aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.HLE/Loaders/Npdm
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.HLE/Loaders/Npdm')
-rw-r--r--src/Ryujinx.HLE/Loaders/Npdm/ACI0.cs53
-rw-r--r--src/Ryujinx.HLE/Loaders/Npdm/ACID.cs61
-rw-r--r--src/Ryujinx.HLE/Loaders/Npdm/FsAccessControl.cs28
-rw-r--r--src/Ryujinx.HLE/Loaders/Npdm/FsAccessHeader.cs37
-rw-r--r--src/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs23
-rw-r--r--src/Ryujinx.HLE/Loaders/Npdm/Npdm.cs72
-rw-r--r--src/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs42
7 files changed, 316 insertions, 0 deletions
diff --git a/src/Ryujinx.HLE/Loaders/Npdm/ACI0.cs b/src/Ryujinx.HLE/Loaders/Npdm/ACI0.cs
new file mode 100644
index 00000000..209e79d1
--- /dev/null
+++ b/src/Ryujinx.HLE/Loaders/Npdm/ACI0.cs
@@ -0,0 +1,53 @@
+using Ryujinx.HLE.Exceptions;
+using System.IO;
+
+namespace Ryujinx.HLE.Loaders.Npdm
+{
+ public class Aci0
+ {
+ private const int Aci0Magic = 'A' << 0 | 'C' << 8 | 'I' << 16 | '0' << 24;
+
+ public ulong TitleId { get; set; }
+
+ public int FsVersion { get; private set; }
+ public ulong FsPermissionsBitmask { get; private set; }
+
+ public ServiceAccessControl ServiceAccessControl { get; private set; }
+ public KernelAccessControl KernelAccessControl { get; private set; }
+
+ public Aci0(Stream stream, int offset)
+ {
+ stream.Seek(offset, SeekOrigin.Begin);
+
+ BinaryReader reader = new BinaryReader(stream);
+
+ if (reader.ReadInt32() != Aci0Magic)
+ {
+ throw new InvalidNpdmException("ACI0 Stream doesn't contain ACI0 section!");
+ }
+
+ stream.Seek(0xc, SeekOrigin.Current);
+
+ TitleId = reader.ReadUInt64();
+
+ // 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();
+
+ FsAccessHeader fsAccessHeader = new FsAccessHeader(stream, offset + fsAccessHeaderOffset, fsAccessHeaderSize);
+
+ FsVersion = fsAccessHeader.Version;
+ FsPermissionsBitmask = fsAccessHeader.PermissionsBitmask;
+
+ ServiceAccessControl = new ServiceAccessControl(stream, offset + serviceAccessControlOffset, serviceAccessControlSize);
+
+ KernelAccessControl = new KernelAccessControl(stream, offset + kernelAccessControlOffset, kernelAccessControlSize);
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/Loaders/Npdm/ACID.cs b/src/Ryujinx.HLE/Loaders/Npdm/ACID.cs
new file mode 100644
index 00000000..365495c6
--- /dev/null
+++ b/src/Ryujinx.HLE/Loaders/Npdm/ACID.cs
@@ -0,0 +1,61 @@
+using Ryujinx.HLE.Exceptions;
+using System.IO;
+
+namespace Ryujinx.HLE.Loaders.Npdm
+{
+ public class Acid
+ {
+ private const int AcidMagic = 'A' << 0 | 'C' << 8 | 'I' << 16 | 'D' << 24;
+
+ public byte[] Rsa2048Signature { get; private set; }
+ public byte[] Rsa2048Modulus { get; private set; }
+ public int Unknown1 { get; private set; }
+ public int Flags { get; private set; }
+
+ public long TitleIdRangeMin { get; private set; }
+ public long TitleIdRangeMax { get; private set; }
+
+ public FsAccessControl FsAccessControl { get; private set; }
+ public ServiceAccessControl ServiceAccessControl { get; private set; }
+ public KernelAccessControl KernelAccessControl { get; private set; }
+
+ public Acid(Stream stream, int offset)
+ {
+ stream.Seek(offset, SeekOrigin.Begin);
+
+ BinaryReader reader = new BinaryReader(stream);
+
+ Rsa2048Signature = reader.ReadBytes(0x100);
+ Rsa2048Modulus = reader.ReadBytes(0x100);
+
+ if (reader.ReadInt32() != AcidMagic)
+ {
+ throw new InvalidNpdmException("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();
+
+ FsAccessControl = new FsAccessControl(stream, offset + fsAccessControlOffset, fsAccessControlSize);
+
+ ServiceAccessControl = new ServiceAccessControl(stream, offset + serviceAccessControlOffset, serviceAccessControlSize);
+
+ KernelAccessControl = new KernelAccessControl(stream, offset + kernelAccessControlOffset, kernelAccessControlSize);
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/Loaders/Npdm/FsAccessControl.cs b/src/Ryujinx.HLE/Loaders/Npdm/FsAccessControl.cs
new file mode 100644
index 00000000..d0f349ea
--- /dev/null
+++ b/src/Ryujinx.HLE/Loaders/Npdm/FsAccessControl.cs
@@ -0,0 +1,28 @@
+using System.IO;
+
+namespace Ryujinx.HLE.Loaders.Npdm
+{
+ public class FsAccessControl
+ {
+ public int Version { get; private set; }
+ public ulong PermissionsBitmask { get; private set; }
+ public int Unknown1 { get; private set; }
+ public int Unknown2 { get; private set; }
+ public int Unknown3 { get; private set; }
+ public int Unknown4 { get; private set; }
+
+ public FsAccessControl(Stream stream, int offset, int size)
+ {
+ stream.Seek(offset, SeekOrigin.Begin);
+
+ BinaryReader reader = new BinaryReader(stream);
+
+ Version = reader.ReadInt32();
+ PermissionsBitmask = reader.ReadUInt64();
+ Unknown1 = reader.ReadInt32();
+ Unknown2 = reader.ReadInt32();
+ Unknown3 = reader.ReadInt32();
+ Unknown4 = reader.ReadInt32();
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/Loaders/Npdm/FsAccessHeader.cs b/src/Ryujinx.HLE/Loaders/Npdm/FsAccessHeader.cs
new file mode 100644
index 00000000..564b8dc3
--- /dev/null
+++ b/src/Ryujinx.HLE/Loaders/Npdm/FsAccessHeader.cs
@@ -0,0 +1,37 @@
+using Ryujinx.HLE.Exceptions;
+using System;
+using System.IO;
+
+namespace Ryujinx.HLE.Loaders.Npdm
+{
+ class FsAccessHeader
+ {
+ public int Version { get; private set; }
+ public ulong PermissionsBitmask { get; private set; }
+
+ public FsAccessHeader(Stream stream, int offset, int size)
+ {
+ stream.Seek(offset, SeekOrigin.Begin);
+
+ BinaryReader reader = new BinaryReader(stream);
+
+ Version = reader.ReadInt32();
+ PermissionsBitmask = reader.ReadUInt64();
+
+ int dataSize = reader.ReadInt32();
+
+ if (dataSize != 0x1c)
+ {
+ throw new InvalidNpdmException("FsAccessHeader is corrupted!");
+ }
+
+ int contentOwnerIdSize = reader.ReadInt32();
+ int dataAndContentOwnerIdSize = reader.ReadInt32();
+
+ if (dataAndContentOwnerIdSize != 0x1c)
+ {
+ throw new NotImplementedException("ContentOwnerId section is not implemented!");
+ }
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs b/src/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs
new file mode 100644
index 00000000..39803642
--- /dev/null
+++ b/src/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs
@@ -0,0 +1,23 @@
+using System.IO;
+
+namespace Ryujinx.HLE.Loaders.Npdm
+{
+ public class KernelAccessControl
+ {
+ public int[] Capabilities { get; private set; }
+
+ public KernelAccessControl(Stream stream, int offset, int size)
+ {
+ stream.Seek(offset, SeekOrigin.Begin);
+
+ Capabilities = new int[size / 4];
+
+ BinaryReader reader = new BinaryReader(stream);
+
+ for (int index = 0; index < Capabilities.Length; index++)
+ {
+ Capabilities[index] = reader.ReadInt32();
+ }
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/Loaders/Npdm/Npdm.cs b/src/Ryujinx.HLE/Loaders/Npdm/Npdm.cs
new file mode 100644
index 00000000..29a2b0fc
--- /dev/null
+++ b/src/Ryujinx.HLE/Loaders/Npdm/Npdm.cs
@@ -0,0 +1,72 @@
+using Ryujinx.HLE.Exceptions;
+using System.IO;
+using System.Text;
+
+namespace Ryujinx.HLE.Loaders.Npdm
+{
+ // 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
+ {
+ private const int MetaMagic = 'M' << 0 | 'E' << 8 | 'T' << 16 | 'A' << 24;
+
+ public byte ProcessFlags { get; private set; }
+ public bool Is64Bit { get; private set; }
+ public byte MainThreadPriority { get; private set; }
+ public byte DefaultCpuId { get; private set; }
+ public int PersonalMmHeapSize { get; private set; }
+ public int Version { get; private set; }
+ public int MainThreadStackSize { get; private set; }
+ public string TitleName { get; set; }
+ public byte[] ProductCode { get; private set; }
+
+ public Aci0 Aci0 { get; private set; }
+ public Acid Acid { get; private set; }
+
+ public Npdm(Stream stream)
+ {
+ BinaryReader reader = new BinaryReader(stream);
+
+ if (reader.ReadInt32() != MetaMagic)
+ {
+ throw new InvalidNpdmException("NPDM Stream doesn't contain NPDM file!");
+ }
+
+ reader.ReadInt64();
+
+ ProcessFlags = reader.ReadByte();
+
+ Is64Bit = (ProcessFlags & 1) != 0;
+
+ reader.ReadByte();
+
+ MainThreadPriority = reader.ReadByte();
+ DefaultCpuId = reader.ReadByte();
+
+ reader.ReadInt32();
+
+ PersonalMmHeapSize = reader.ReadInt32();
+
+ Version = reader.ReadInt32();
+
+ MainThreadStackSize = reader.ReadInt32();
+
+ byte[] tempTitleName = reader.ReadBytes(0x10);
+
+ TitleName = Encoding.UTF8.GetString(tempTitleName, 0, tempTitleName.Length).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);
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs b/src/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs
new file mode 100644
index 00000000..54012b8a
--- /dev/null
+++ b/src/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs
@@ -0,0 +1,42 @@
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Text;
+
+namespace Ryujinx.HLE.Loaders.Npdm
+{
+ public class ServiceAccessControl
+ {
+ public IReadOnlyDictionary<string, bool> Services { get; private set; }
+
+ public ServiceAccessControl(Stream stream, int offset, int size)
+ {
+ stream.Seek(offset, SeekOrigin.Begin);
+
+ BinaryReader reader = new BinaryReader(stream);
+
+ int bytesRead = 0;
+
+ Dictionary<string, bool> services = new Dictionary<string, bool>();
+
+ while (bytesRead != size)
+ {
+ byte controlByte = reader.ReadByte();
+
+ if (controlByte == 0)
+ {
+ break;
+ }
+
+ int length = (controlByte & 0x07) + 1;
+ bool registerAllowed = (controlByte & 0x80) != 0;
+
+ services[Encoding.ASCII.GetString(reader.ReadBytes(length))] = registerAllowed;
+
+ bytesRead += length + 1;
+ }
+
+ Services = new ReadOnlyDictionary<string, bool>(services);
+ }
+ }
+}