Read KIP1 files

This commit is contained in:
Alex Barney 2018-09-17 19:18:28 -05:00
parent 4bfaafc301
commit 8b8aa3277f
5 changed files with 189 additions and 3 deletions

164
LibHac/Kip.cs Normal file
View file

@ -0,0 +1,164 @@
using System;
using System.IO;
using LibHac.Streams;
namespace LibHac
{
public class Kip
{
private const int HeaderSize = 0x100;
public KipHeader Header { get; }
public int[] SectionOffsets { get; } = new int[6];
public int Size { get; }
private SharedStreamSource StreamSource { get; }
public Kip(Stream stream)
{
StreamSource = new SharedStreamSource(stream);
Header = new KipHeader(StreamSource.CreateStream());
Size = HeaderSize;
for (int index = 0; index < Header.Sections.Length; index++)
{
int sectionSize = Header.Sections[index].CompressedSize;
SectionOffsets[index] = Size;
Size += sectionSize;
}
}
public Stream OpenSection(int index)
{
if (index < 0 || index > 5)
{
throw new ArgumentOutOfRangeException(nameof(index), "Section index must be between 0-5");
}
return StreamSource.CreateStream(SectionOffsets[index], Header.Sections[index].CompressedSize);
}
public byte[] DecompressSection(int index)
{
Stream compStream = OpenSection(index);
var compressed = new byte[compStream.Length];
compStream.Read(compressed, 0, compressed.Length);
return DecompressBlz(compressed);
}
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);
var 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(Stream stream)
{
var reader = new BinaryReader(stream);
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();
}
}
}

View file

@ -46,6 +46,8 @@ namespace LibHac
{ {
SharedStream encStream = StreamSource.CreateStream(0x110, 0xF0); SharedStream encStream = StreamSource.CreateStream(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.
var counter = new byte[0x10]; var counter = new byte[0x10];
Array.Copy(Header.Counter, counter, 0x10); Array.Copy(Header.Counter, counter, 0x10);
Util.IncrementByteArray(counter); Util.IncrementByteArray(counter);

View file

@ -48,7 +48,8 @@ namespace hactoolnet
Save, Save,
Keygen, Keygen,
Pk11, Pk11,
Pk21 Pk21,
Kip1
} }
internal class Context internal class Context

16
hactoolnet/ProcessKip.cs Normal file
View file

@ -0,0 +1,16 @@
using System.IO;
using LibHac;
namespace hactoolnet
{
internal static class ProcessKip
{
public static void ProcessKip1(Context ctx)
{
using (var file = new FileStream(ctx.Options.InFile, FileMode.Open, FileAccess.Read))
{
var kip = new Kip(file);
}
}
}
}

View file

@ -56,6 +56,9 @@ namespace hactoolnet
case FileType.Pk21: case FileType.Pk21:
ProcessPackage.ProcessPk21(ctx); ProcessPackage.ProcessPk21(ctx);
break; break;
case FileType.Kip1:
ProcessKip.ProcessKip1(ctx);
break;
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
@ -155,8 +158,8 @@ namespace hactoolnet
// For running random stuff // For running random stuff
// ReSharper disable once UnusedParameter.Local // ReSharper disable once UnusedParameter.Local
private static void CustomTask(Context ctx) private static void CustomTask(Context ctx)
{ {
} }
} }
} }