This commit is contained in:
shadowninja108 2018-11-14 19:34:09 -07:00
commit a8f7e7fb0f
17 changed files with 798 additions and 14 deletions

View file

@ -5,7 +5,7 @@ namespace LibHac
{
public class Nacp
{
public NacpLang[] Languages { get; } = new NacpLang[0x10];
public NacpDescription[] Descriptions { get; } = new NacpDescription[0x10];
public string Isbn { get; }
public byte StartupUserAccount { get; }
public byte TouchScreenUsageMode { get; }
@ -54,20 +54,21 @@ namespace LibHac
public byte RepairFlag { get; }
public byte ProgramIndex { get; }
public long TotalSaveDataSize { get; }
public long UserTotalSaveDataSize { get; }
public long DeviceTotalSaveDataSize { get; }
public Nacp() { }
public Nacp(BinaryReader reader)
public Nacp(Stream file)
{
long start = reader.BaseStream.Position;
long start = file.Position;
BinaryReader reader = new BinaryReader(file);
for (int i = 0; i < 16; i++)
{
Languages[i] = new NacpLang(reader);
Descriptions[i] = new NacpDescription(reader, i);
}
Isbn = reader.ReadUtf8Z(37);
@ -146,15 +147,18 @@ namespace LibHac
}
}
public class NacpLang
public class NacpDescription
{
public string Title { get; }
public string Developer { get; }
public NacpLang() { }
public TitleLanguage Language;
public NacpLang(BinaryReader reader)
public NacpDescription() { }
public NacpDescription(BinaryReader reader, int index)
{
Language = (TitleLanguage)index;
long start = reader.BaseStream.Position;
Title = reader.ReadUtf8Z();
reader.BaseStream.Position = start + 0x200;
@ -162,4 +166,23 @@ namespace LibHac
reader.BaseStream.Position = start + 0x300;
}
}
public enum TitleLanguage
{
AmericanEnglish = 0,
BritishEnglish,
Japanese,
French,
German,
LatinAmericanSpanish,
Spanish,
Italian,
Dutch,
CanadianFrench,
Portuguese,
Russian,
Korean,
Taiwanese,
Chinese
}
}

52
LibHac/Npdm/Aci0.cs Normal file
View file

@ -0,0 +1,52 @@
using System;
using System.IO;
namespace LibHac.Npdm
{
public class Aci0
{
public string Magic;
public long TitleId { get; }
public int FsVersion { get; }
public ulong FsPermissionsBitmask { get; }
public ServiceAccessControl ServiceAccess { get; }
public KernelAccessControl KernelAccess { get; }
public Aci0(Stream stream, int offset)
{
stream.Seek(offset, SeekOrigin.Begin);
var reader = new BinaryReader(stream);
Magic = reader.ReadAscii(0x4);
if (Magic != "ACI0")
{
throw new Exception("ACI0 Stream doesn't contain ACI0 section!");
}
stream.Seek(0xc, SeekOrigin.Current);
TitleId = reader.ReadInt64();
//Reserved.
stream.Seek(8, SeekOrigin.Current);
int fsAccessHeaderOffset = reader.ReadInt32();
int fsAccessHeaderSize = reader.ReadInt32();
int serviceAccessControlOffset = reader.ReadInt32();
int serviceAccessControlSize = reader.ReadInt32();
int kernelAccessControlOffset = reader.ReadInt32();
int kernelAccessControlSize = reader.ReadInt32();
var accessHeader = new FsAccessHeader(stream, offset + fsAccessHeaderOffset);
FsVersion = accessHeader.Version;
FsPermissionsBitmask = accessHeader.PermissionsBitmask;
ServiceAccess = new ServiceAccessControl(stream, offset + serviceAccessControlOffset, serviceAccessControlSize);
KernelAccess = new KernelAccessControl(stream, offset + kernelAccessControlOffset, kernelAccessControlSize);
}
}
}

61
LibHac/Npdm/Acid.cs Normal file
View file

@ -0,0 +1,61 @@
using System;
using System.IO;
namespace LibHac.Npdm
{
public class Acid
{
public string Magic;
public byte[] Rsa2048Signature { get; }
public byte[] Rsa2048Modulus { get; }
public int Unknown1 { get; }
public int Flags { get; }
public long TitleIdRangeMin { get; }
public long TitleIdRangeMax { get; }
public FsAccessControl FsAccess { get; }
public ServiceAccessControl ServiceAccess { get; }
public KernelAccessControl KernelAccess { get; }
public Acid(Stream stream, int offset)
{
stream.Seek(offset, SeekOrigin.Begin);
var reader = new BinaryReader(stream);
Rsa2048Signature = reader.ReadBytes(0x100);
Rsa2048Modulus = reader.ReadBytes(0x100);
Magic = reader.ReadAscii(0x4);
if (Magic != "ACID")
{
throw new Exception("ACID Stream doesn't contain ACID section!");
}
//Size field used with the above signature (?).
Unknown1 = reader.ReadInt32();
reader.ReadInt32();
//Bit0 must be 1 on retail, on devunit 0 is also allowed. Bit1 is unknown.
Flags = reader.ReadInt32();
TitleIdRangeMin = reader.ReadInt64();
TitleIdRangeMax = reader.ReadInt64();
int fsAccessControlOffset = reader.ReadInt32();
int fsAccessControlSize = reader.ReadInt32();
int serviceAccessControlOffset = reader.ReadInt32();
int serviceAccessControlSize = reader.ReadInt32();
int kernelAccessControlOffset = reader.ReadInt32();
int kernelAccessControlSize = reader.ReadInt32();
FsAccess = new FsAccessControl(stream, offset + fsAccessControlOffset);
ServiceAccess = new ServiceAccessControl(stream, offset + serviceAccessControlOffset, serviceAccessControlSize);
KernelAccess = new KernelAccessControl(stream, offset + kernelAccessControlOffset, kernelAccessControlSize);
}
}
}

View file

@ -0,0 +1,9 @@
namespace LibHac.Npdm
{
internal enum ApplicationType
{
SystemModule,
Application,
Applet
}
}

View file

@ -0,0 +1,28 @@
using System.IO;
namespace LibHac.Npdm
{
public class FsAccessControl
{
public int Version { get; }
public ulong PermissionsBitmask { get; }
public int Unknown1 { get; }
public int Unknown2 { get; }
public int Unknown3 { get; }
public int Unknown4 { get; }
public FsAccessControl(Stream stream, int offset)
{
stream.Seek(offset, SeekOrigin.Begin);
var reader = new BinaryReader(stream);
Version = reader.ReadInt32();
PermissionsBitmask = reader.ReadUInt64();
Unknown1 = reader.ReadInt32();
Unknown2 = reader.ReadInt32();
Unknown3 = reader.ReadInt32();
Unknown4 = reader.ReadInt32();
}
}
}

View file

@ -0,0 +1,36 @@
using System;
using System.IO;
namespace LibHac.Npdm
{
public class FsAccessHeader
{
public int Version { get; }
public ulong PermissionsBitmask { get; }
public FsAccessHeader(Stream stream, int offset)
{
stream.Seek(offset, SeekOrigin.Begin);
var reader = new BinaryReader(stream);
Version = reader.ReadInt32();
PermissionsBitmask = reader.ReadUInt64();
int dataSize = reader.ReadInt32();
if (dataSize != 0x1c)
{
throw new Exception("FsAccessHeader is corrupted!");
}
int contentOwnerIdSize = reader.ReadInt32();
int dataAndContentOwnerIdSize = reader.ReadInt32();
if (dataAndContentOwnerIdSize != 0x1c)
{
throw new NotImplementedException("ContentOwnerId section is not implemented!");
}
}
}
}

View file

@ -0,0 +1,33 @@
namespace LibHac.Npdm
{
public enum FsPermissionBool : ulong
{
BisCache = 0x8000000000000080,
EraseMmc = 0x8000000000000080,
GameCardCertificate = 0x8000000000000010,
GameCardIdSet = 0x8000000000000010,
GameCardDriver = 0x8000000000000200,
GameCardAsic = 0x8000000000000200,
SaveDataCreate = 0x8000000000002020,
SaveDataDelete0 = 0x8000000000000060,
SystemSaveDataCreate0 = 0x8000000000000028,
SystemSaveDataCreate1 = 0x8000000000000020,
SaveDataDelete1 = 0x8000000000004028,
SaveDataIterators0 = 0x8000000000000060,
SaveDataIterators1 = 0x8000000000004020,
SaveThumbnails = 0x8000000000020000,
PosixTime = 0x8000000000000400,
SaveDataExtraData = 0x8000000000004060,
GlobalMode = 0x8000000000080000,
SpeedEmulation = 0x8000000000080000,
NULL = 0,
PaddingFiles = 0xC000000000800000,
SaveData_Debug = 0xC000000001000000,
SaveData_SystemManagement = 0xC000000002000000,
Unknown0x16 = 0x8000000004000000,
Unknown0x17 = 0x8000000008000000,
Unknown0x18 = 0x8000000010000000,
Unknown0x19 = 0x8000000000000800,
Unknown0x1A = 0x8000000000004020
}
}

View file

@ -0,0 +1,45 @@
namespace LibHac.Npdm
{
public enum FsPermissionRw : ulong
{
MountContentType2 = 0x8000000000000801,
MountContentType5 = 0x8000000000000801,
MountContentType3 = 0x8000000000000801,
MountContentType4 = 0x8000000000000801,
MountContentType6 = 0x8000000000000801,
MountContentType7 = 0x8000000000000801,
Unknown0x6 = 0x8000000000000000,
ContentStorageAccess = 0x8000000000000800,
ImageDirectoryAccess = 0x8000000000001000,
MountBisType28 = 0x8000000000000084,
MountBisType29 = 0x8000000000000080,
MountBisType30 = 0x8000000000008080,
MountBisType31 = 0x8000000000008080,
Unknown0xD = 0x8000000000000080,
SdCardAccess = 0xC000000000200000,
GameCardUser = 0x8000000000000010,
SaveDataAccess0 = 0x8000000000040020,
SystemSaveDataAccess0 = 0x8000000000000028,
SaveDataAccess1 = 0x8000000000000020,
SystemSaveDataAccess1 = 0x8000000000000020,
BisPartition0 = 0x8000000000010082,
BisPartition10 = 0x8000000000010080,
BisPartition20 = 0x8000000000010080,
BisPartition21 = 0x8000000000010080,
BisPartition22 = 0x8000000000010080,
BisPartition23 = 0x8000000000010080,
BisPartition24 = 0x8000000000010080,
BisPartition25 = 0x8000000000010080,
BisPartition26 = 0x8000000000000080,
BisPartition27 = 0x8000000000000084,
BisPartition28 = 0x8000000000000084,
BisPartition29 = 0x8000000000000080,
BisPartition30 = 0x8000000000000080,
BisPartition31 = 0x8000000000000080,
BisPartition32 = 0x8000000000000080,
Unknown0x23 = 0xC000000000200000,
GameCard_System = 0x8000000000000100,
MountContent_System = 0x8000000000100008,
HostAccess = 0xC000000000400000
}
}

View file

@ -0,0 +1,173 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace LibHac.Npdm
{
public class KernelAccessControl
{
public List<KernelAccessControlItem> Items { get; }
public KernelAccessControl(Stream stream, int offset, int size)
{
stream.Seek(offset, SeekOrigin.Begin);
var reader = new BinaryReader(stream);
var items = new KernelAccessControlItem[size / 4];
for (int index = 0; index < size / 4; index++)
{
uint descriptor = reader.ReadUInt32();
//Ignore the descriptor.
if (descriptor == 0xffffffff)
{
continue;
}
items[index] = new KernelAccessControlItem();
int lowBits = 0;
while ((descriptor & 1) != 0)
{
descriptor >>= 1;
lowBits++;
}
descriptor >>= 1;
switch (lowBits)
{
//Kernel flags.
case 3:
{
items[index].HasKernelFlags = true;
items[index].HighestThreadPriority = (descriptor >> 0) & 0x3f;
items[index].LowestThreadPriority = (descriptor >> 6) & 0x3f;
items[index].LowestCpuId = (descriptor >> 12) & 0xff;
items[index].HighestCpuId = (descriptor >> 20) & 0xff;
break;
}
//Syscall mask.
case 4:
{
items[index].HasSvcFlags = true;
items[index].AllowedSvcs = new bool[0x80];
int sysCallBase = (int)(descriptor >> 24) * 0x18;
for (int sysCall = 0; sysCall < 0x18 && sysCallBase + sysCall < 0x80; sysCall++)
{
Items[index].AllowedSvcs[sysCallBase + sysCall] = (descriptor & 1) != 0;
descriptor >>= 1;
}
break;
}
//Map IO/Normal.
case 6:
{
ulong address = (descriptor & 0xffffff) << 12;
bool isRo = (descriptor >> 24) != 0;
if (index == size / 4 - 1)
{
throw new Exception("Invalid Kernel Access Control Descriptors!");
}
descriptor = reader.ReadUInt32();
if ((descriptor & 0x7f) != 0x3f)
{
throw new Exception("Invalid Kernel Access Control Descriptors!");
}
descriptor >>= 7;
ulong mmioSize = (descriptor & 0xffffff) << 12;
bool isNormal = (descriptor >> 24) != 0;
items[index].NormalMmio.Add(new KernelAccessControlMmio(address, mmioSize, isRo, isNormal));
index++;
break;
}
//Map Normal Page.
case 7:
{
ulong address = descriptor << 12;
items[index].PageMmio.Add(new KernelAccessControlMmio(address, 0x1000, false, false));
break;
}
//IRQ Pair.
case 11:
{
items[index].Irq.Add(new KernelAccessControlIrq(
(descriptor >> 0) & 0x3ff,
(descriptor >> 10) & 0x3ff));
break;
}
//Application Type.
case 13:
{
items[index].HasApplicationType = true;
items[index].ApplicationType = (int)descriptor & 7;
break;
}
//Kernel Release Version.
case 14:
{
items[index].HasKernelVersion = true;
items[index].KernelVersionRelease = (int)descriptor;
break;
}
//Handle Table Size.
case 15:
{
items[index].HasHandleTableSize = true;
items[index].HandleTableSize = (int)descriptor;
break;
}
//Debug Flags.
case 16:
{
items[index].HasDebugFlags = true;
items[index].AllowDebug = ((descriptor >> 0) & 1) != 0;
items[index].ForceDebug = ((descriptor >> 1) & 1) != 0;
break;
}
}
}
Items = items.ToList();
}
}
}

View file

@ -0,0 +1,14 @@
namespace LibHac.Npdm
{
public struct KernelAccessControlIrq
{
public uint Irq0 { get; }
public uint Irq1 { get; }
public KernelAccessControlIrq(uint irq0, uint irq1)
{
Irq0 = irq0;
Irq1 = irq1;
}
}
}

View file

@ -0,0 +1,22 @@
namespace LibHac.Npdm
{
public struct KernelAccessControlMmio
{
public ulong Address { get; }
public ulong Size { get; private set; }
public bool IsRo { get; private set; }
public bool IsNormal { get; private set; }
public KernelAccessControlMmio(
ulong address,
ulong size,
bool isro,
bool isnormal)
{
Address = address;
Size = size;
IsRo = isro;
IsNormal = isnormal;
}
}
}

View file

@ -0,0 +1,33 @@
using System.Collections.Generic;
namespace LibHac.Npdm
{
public struct KernelAccessControlItem
{
public bool HasKernelFlags { get; set; }
public uint LowestThreadPriority { get; set; }
public uint HighestThreadPriority { get; set; }
public uint LowestCpuId { get; set; }
public uint HighestCpuId { get; set; }
public bool HasSvcFlags { get; set; }
public bool[] AllowedSvcs { get; set; }
public List<KernelAccessControlMmio> NormalMmio { get; set; }
public List<KernelAccessControlMmio> PageMmio { get; set; }
public List<KernelAccessControlIrq> Irq { get; set; }
public bool HasApplicationType { get; set; }
public int ApplicationType { get; set; }
public bool HasKernelVersion { get; set; }
public int KernelVersionRelease { get; set; }
public bool HasHandleTableSize { get; set; }
public int HandleTableSize { get; set; }
public bool HasDebugFlags { get; set; }
public bool AllowDebug { get; set; }
public bool ForceDebug { get; set; }
}
}

76
LibHac/Npdm/Npdm.cs Normal file
View file

@ -0,0 +1,76 @@
using System;
using System.IO;
namespace LibHac.Npdm
{
//https://github.com/Ryujinx/Ryujinx/blob/master/Ryujinx.HLE/Loaders/Npdm/Npdm.cs
//https://github.com/SciresM/hactool/blob/master/npdm.c
//https://github.com/SciresM/hactool/blob/master/npdm.h
//http://switchbrew.org/index.php?title=NPDM
public class Npdm
{
public string Magic;
public bool Is64Bits { get; }
public int AddressSpaceWidth { get; }
public byte MainThreadPriority { get; }
public byte DefaultCpuId { get; }
public int SystemResourceSize { get; }
public int ProcessCategory { get; }
public int MainEntrypointStackSize { get; }
public string TitleName { get; }
public byte[] ProductCode { get; }
public Aci0 Aci0 { get; }
public Acid AciD { get; }
public Npdm(Stream stream)
{
var reader = new BinaryReader(stream);
Magic = reader.ReadAscii(0x4);
if (Magic != "META")
{
throw new Exception("NPDM Stream doesn't contain NPDM file!");
}
reader.ReadInt64();
//MmuFlags, bit0: 64-bit instructions, bits1-3: address space width (1=64-bit, 2=32-bit). Needs to be <= 0xF.
byte mmuflags = reader.ReadByte();
Is64Bits = (mmuflags & 1) != 0;
AddressSpaceWidth = (mmuflags >> 1) & 7;
reader.ReadByte();
MainThreadPriority = reader.ReadByte(); //(0-63).
DefaultCpuId = reader.ReadByte();
reader.ReadInt32();
//System resource size (max size as of 5.x: 534773760).
SystemResourceSize = Util.Swap32(reader.ReadInt32());
//ProcessCategory (0: regular title, 1: kernel built-in). Should be 0 here.
ProcessCategory = Util.Swap32(reader.ReadInt32());
//Main entrypoint stack size.
MainEntrypointStackSize = reader.ReadInt32();
TitleName = reader.ReadUtf8(0x10).Trim('\0');
ProductCode = reader.ReadBytes(0x10);
stream.Seek(0x30, SeekOrigin.Current);
int aci0Offset = reader.ReadInt32();
int aci0Size = reader.ReadInt32();
int acidOffset = reader.ReadInt32();
int acidSize = reader.ReadInt32();
Aci0 = new Aci0(stream, aci0Offset);
AciD = new Acid(stream, acidOffset);
}
}
}

View file

@ -0,0 +1,36 @@
using System.Collections.Generic;
using System.IO;
namespace LibHac.Npdm
{
public class ServiceAccessControl
{
public Dictionary<string, bool> Services { get; } = new Dictionary<string, bool>();
public ServiceAccessControl(Stream stream, int offset, int size)
{
stream.Seek(offset, SeekOrigin.Begin);
var reader = new BinaryReader(stream);
int bytesRead = 0;
while (bytesRead != size)
{
byte controlByte = reader.ReadByte();
if (controlByte == 0)
{
break;
}
int length = ((controlByte & 0x07)) + 1;
bool registerAllowed = ((controlByte & 0x80) != 0);
Services.Add(reader.ReadAscii(length), registerAllowed);
bytesRead += length + 1;
}
}
}
}

134
LibHac/Npdm/SvcName.cs Normal file
View file

@ -0,0 +1,134 @@
namespace LibHac.Npdm
{
public enum SvcName
{
Reserved0,
SetHeapSize,
SetMemoryPermission,
SetMemoryAttribute,
MapMemory,
UnmapMemory,
QueryMemory,
ExitProcess,
CreateThread,
StartThread,
ExitThread,
SleepThread,
GetThreadPriority,
SetThreadPriority,
GetThreadCoreMask,
SetThreadCoreMask,
GetCurrentProcessorNumber,
SignalEvent,
ClearEvent,
MapSharedMemory,
UnmapSharedMemory,
CreateTransferMemory,
CloseHandle,
ResetSignal,
WaitSynchronization,
CancelSynchronization,
ArbitrateLock,
ArbitrateUnlock,
WaitProcessWideKeyAtomic,
SignalProcessWideKey,
GetSystemTick,
ConnectToNamedPort,
SendSyncRequestLight,
SendSyncRequest,
SendSyncRequestWithUserBuffer,
SendAsyncRequestWithUserBuffer,
GetProcessId,
GetThreadId,
Break,
OutputDebugString,
ReturnFromException,
GetInfo,
FlushEntireDataCache,
FlushDataCache,
MapPhysicalMemory,
UnmapPhysicalMemory,
GetFutureThreadInfo,
GetLastThreadInfo,
GetResourceLimitLimitValue,
GetResourceLimitCurrentValue,
SetThreadActivity,
GetThreadContext3,
WaitForAddress,
SignalToAddress,
Reserved1,
Reserved2,
Reserved3,
Reserved4,
Reserved5,
Reserved6,
DumpInfo,
DumpInfoNew,
Reserved7,
Reserved8,
CreateSession,
AcceptSession,
ReplyAndReceiveLight,
ReplyAndReceive,
ReplyAndReceiveWithUserBuffer,
CreateEvent,
Reserved9,
Reserved10,
MapPhysicalMemoryUnsafe,
UnmapPhysicalMemoryUnsafe,
SetUnsafeLimit,
CreateCodeMemory,
ControlCodeMemory,
SleepSystem,
ReadWriteRegister,
SetProcessActivity,
CreateSharedMemory,
MapTransferMemory,
UnmapTransferMemory,
CreateInterruptEvent,
QueryPhysicalAddress,
QueryIoMapping,
CreateDeviceAddressSpace,
AttachDeviceAddressSpace,
DetachDeviceAddressSpace,
MapDeviceAddressSpaceByForce,
MapDeviceAddressSpaceAligned,
MapDeviceAddressSpace,
UnmapDeviceAddressSpace,
InvalidateProcessDataCache,
StoreProcessDataCache,
FlushProcessDataCache,
DebugActiveProcess,
BreakDebugProcess,
TerminateDebugProcess,
GetDebugEvent,
ContinueDebugEvent,
GetProcessList,
GetThreadList,
GetDebugThreadContext,
SetDebugThreadContext,
QueryDebugProcessMemory,
ReadDebugProcessMemory,
WriteDebugProcessMemory,
SetHardwareBreakPoint,
GetDebugThreadParam,
Reserved11,
GetSystemInfo,
CreatePort,
ManageNamedPort,
ConnectToPort,
SetProcessMemoryPermission,
MapProcessMemory,
UnmapProcessMemory,
QueryProcessMemory,
MapProcessCodeMemory,
UnmapProcessCodeMemory,
CreateProcess,
StartProcess,
TerminateProcess,
GetProcessInfo,
CreateResourceLimit,
SetResourceLimitLimitValue,
CallSecureMonitor
}
}

View file

@ -189,16 +189,15 @@ namespace LibHac
foreach (Title title in Titles.Values.Where(x => x.ControlNca != null))
{
var romfs = new Romfs(title.ControlNca.OpenSection(0, false, IntegrityCheckLevel.ErrorOnInvalid));
byte[] control = romfs.GetFile("/control.nacp");
Stream control = romfs.OpenFile("/control.nacp");
var reader = new BinaryReader(new MemoryStream(control));
title.Control = new Nacp(reader);
title.Control = new Nacp(control);
foreach (NacpLang lang in title.Control.Languages)
foreach (NacpDescription desc in title.Control.Descriptions)
{
if (!string.IsNullOrWhiteSpace(lang.Title))
if (!string.IsNullOrWhiteSpace(desc.Title))
{
title.Name = lang.Title;
title.Name = desc.Title;
break;
}
}

View file

@ -409,6 +409,16 @@ namespace LibHac
bool isOutOfRange = startIndex < 0 || startIndex > length || subLength < 0 || startIndex > length - subLength;
return !isOutOfRange;
}
public static int Swap32(int value)
{
uint uintVal = (uint)value;
return (int)(((uintVal >> 24) & 0x000000ff) |
((uintVal >> 8) & 0x0000ff00) |
((uintVal << 8) & 0x00ff0000) |
((uintVal << 24) & 0xff000000));
}
}
public class ByteArray128BitComparer : EqualityComparer<byte[]>