diff --git a/SabreTools.Serialization.Test/SabreTools.Serialization.Test.csproj b/SabreTools.Serialization.Test/SabreTools.Serialization.Test.csproj
index f2175cfb..38efe1c0 100644
--- a/SabreTools.Serialization.Test/SabreTools.Serialization.Test.csproj
+++ b/SabreTools.Serialization.Test/SabreTools.Serialization.Test.csproj
@@ -27,7 +27,7 @@
all
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/SabreTools.Serialization/Deserializers/N3DS.cs b/SabreTools.Serialization/Deserializers/N3DS.cs
index 98935e55..f0d1a46f 100644
--- a/SabreTools.Serialization/Deserializers/N3DS.cs
+++ b/SabreTools.Serialization/Deserializers/N3DS.cs
@@ -259,7 +259,18 @@ namespace SabreTools.Serialization.Deserializers
/// Filled card info header on success, null on error
public static CardInfoHeader? ParseCardInfoHeader(Stream data)
{
- return data.ReadType();
+ // TODO: Use marshalling here instead of building
+ var header = new CardInfoHeader();
+
+ header.WritableAddressMediaUnits = data.ReadUInt32();
+ header.CardInfoBitmask = data.ReadUInt32();
+ header.Reserved3 = data.ReadBytes(0x108);
+ header.TitleVersion = data.ReadUInt16();
+ header.CardRevision = data.ReadUInt16();
+ header.Reserved4 = data.ReadBytes(0xCD6);
+ header.InitialData = ParseInitialData(data);
+
+ return header;
}
///
@@ -272,6 +283,26 @@ namespace SabreTools.Serialization.Deserializers
return data.ReadType();
}
+ ///
+ /// Parse a Stream into initial data
+ ///
+ /// Stream to parse
+ /// Filled initial data on success, null on error
+ public static InitialData? ParseInitialData(Stream data)
+ {
+ // TODO: Use marshalling here instead of building
+ var id = new InitialData();
+
+ id.CardSeedKeyY = data.ReadBytes(0x10);
+ id.EncryptedCardSeed = data.ReadBytes(0x10);
+ id.CardSeedAESMAC = data.ReadBytes(0x10);
+ id.CardSeedNonce = data.ReadBytes(0x0C);
+ id.Reserved = data.ReadBytes(0xC4);
+ id.BackupHeader = ParseNCCHHeader(data, skipSignature: true);
+
+ return id;
+ }
+
///
/// Parse a Stream into an NCCH header
///
diff --git a/SabreTools.Serialization/Printers/N3DS.cs b/SabreTools.Serialization/Printers/N3DS.cs
index 970e71fe..604e74b4 100644
--- a/SabreTools.Serialization/Printers/N3DS.cs
+++ b/SabreTools.Serialization/Printers/N3DS.cs
@@ -131,6 +131,8 @@ namespace SabreTools.Serialization.Printers
builder.AppendLine(header.CVerVersionNumber, " Version number of CVer in included update partition");
builder.AppendLine(header.Reserved4, " Reserved 4");
builder.AppendLine();
+
+ Print(builder, header.InitialData);
}
private static void Print(StringBuilder builder, DevelopmentCardInfoHeader? header)
@@ -227,6 +229,96 @@ namespace SabreTools.Serialization.Printers
builder.AppendLine();
}
+ private static void Print(StringBuilder builder, InitialData? id)
+ {
+ builder.AppendLine(" Initial Data Information:");
+ builder.AppendLine(" -------------------------");
+ if (id == null)
+ {
+ builder.AppendLine(" No initial data");
+ builder.AppendLine();
+ return;
+ }
+
+ builder.AppendLine(id.CardSeedKeyY, " Card seed KeyY");
+ builder.AppendLine(id.EncryptedCardSeed, " Encrypted card seed");
+ builder.AppendLine(id.CardSeedAESMAC, " Card seed AES-MAC");
+ builder.AppendLine(id.CardSeedNonce, " Card seed nonce");
+ builder.AppendLine(id.Reserved, " Reserved");
+ builder.AppendLine();
+
+ PrintBackup(builder, id.BackupHeader);
+ }
+
+ private static void PrintBackup(StringBuilder builder, NCCHHeader? header)
+ {
+ builder.AppendLine(" Backup NCCH Header Information:");
+ builder.AppendLine(" -------------------------");
+ if (header == null)
+ {
+ builder.AppendLine(" No backup NCCH header");
+ builder.AppendLine();
+ return;
+ }
+
+ if (header.MagicID == string.Empty)
+ {
+ builder.AppendLine(" Empty backup header, no data can be parsed");
+ }
+ else if (header.MagicID != Constants.NCCHMagicNumber)
+ {
+ builder.AppendLine(" Unrecognized backup header, no data can be parsed");
+ }
+ else
+ {
+ // Backup header omits RSA signature
+ builder.AppendLine(header.MagicID, " Magic ID");
+ builder.AppendLine(header.ContentSizeInMediaUnits, " Content size in media units");
+ builder.AppendLine(header.PartitionId, " Partition ID");
+ builder.AppendLine(header.MakerCode, " Maker code");
+ builder.AppendLine(header.Version, " Version");
+ builder.AppendLine(header.VerificationHash, " Verification hash");
+ builder.AppendLine(header.ProgramId, " Program ID");
+ builder.AppendLine(header.Reserved1, " Reserved 1");
+ builder.AppendLine(header.LogoRegionHash, " Logo region SHA-256 hash");
+ builder.AppendLine(header.ProductCode, " Product code");
+ builder.AppendLine(header.ExtendedHeaderHash, " Extended header SHA-256 hash");
+ builder.AppendLine(header.ExtendedHeaderSizeInBytes, " Extended header size in bytes");
+ builder.AppendLine(header.Reserved2, " Reserved 2");
+ builder.AppendLine(" Flags:");
+ if (header.Flags == null)
+ {
+ builder.AppendLine(" [NULL]");
+ }
+ else
+ {
+ builder.AppendLine(header.Flags.Reserved0, " Reserved 0");
+ builder.AppendLine(header.Flags.Reserved1, " Reserved 1");
+ builder.AppendLine(header.Flags.Reserved2, " Reserved 2");
+ builder.AppendLine($" Crypto method: {header.Flags.CryptoMethod} (0x{header.Flags.CryptoMethod:X})");
+ builder.AppendLine($" Content platform: {header.Flags.ContentPlatform} (0x{header.Flags.ContentPlatform:X})");
+ builder.AppendLine($" Content type: {header.Flags.MediaPlatformIndex} (0x{header.Flags.MediaPlatformIndex:X})");
+ builder.AppendLine(header.Flags.ContentUnitSize, " Content unit size");
+ builder.AppendLine($" Bitmasks: {header.Flags.BitMasks} (0x{header.Flags.BitMasks:X})");
+ }
+ builder.AppendLine(header.PlainRegionOffsetInMediaUnits, " Plain region offset, in media units");
+ builder.AppendLine(header.PlainRegionSizeInMediaUnits, " Plain region size, in media units");
+ builder.AppendLine(header.LogoRegionOffsetInMediaUnits, " Logo region offset, in media units");
+ builder.AppendLine(header.LogoRegionSizeInMediaUnits, " Logo region size, in media units");
+ builder.AppendLine(header.ExeFSOffsetInMediaUnits, " ExeFS offset, in media units");
+ builder.AppendLine(header.ExeFSSizeInMediaUnits, " ExeFS size, in media units");
+ builder.AppendLine(header.ExeFSHashRegionSizeInMediaUnits, " ExeFS hash region size, in media units");
+ builder.AppendLine(header.Reserved3, " Reserved 3");
+ builder.AppendLine(header.RomFSOffsetInMediaUnits, " RomFS offset, in media units");
+ builder.AppendLine(header.RomFSSizeInMediaUnits, " RomFS size, in media units");
+ builder.AppendLine(header.RomFSHashRegionSizeInMediaUnits, " RomFS hash region size, in media units");
+ builder.AppendLine(header.Reserved4, " Reserved 4");
+ builder.AppendLine(header.ExeFSSuperblockHash, " ExeFS superblock SHA-256 hash");
+ builder.AppendLine(header.RomFSSuperblockHash, " RomFS superblock SHA-256 hash");
+ }
+ builder.AppendLine();
+ }
+
private static void Print(StringBuilder builder, NCCHHeader?[]? entries)
{
builder.AppendLine(" NCCH Partition Header Information:");
@@ -455,7 +547,7 @@ namespace SabreTools.Serialization.Printers
}
else
{
- builder.AppendLine(entry.ACI.ARM9AccessControl.Descriptors, " Descriptors");
+ builder.AppendLine($" Descriptors: {entry.ACI.ARM9AccessControl.Descriptors} (0x{entry.ACI.ARM9AccessControl.Descriptors:X})");
builder.AppendLine(entry.ACI.ARM9AccessControl.DescriptorVersion, " Descriptor version");
}
@@ -523,7 +615,7 @@ namespace SabreTools.Serialization.Printers
}
else
{
- builder.AppendLine(entry.ACIForLimitations.ARM9AccessControl.Descriptors, " Descriptors");
+ builder.AppendLine($" Descriptors: {entry.ACIForLimitations.ARM9AccessControl.Descriptors} (0x{entry.ACIForLimitations.ARM9AccessControl.Descriptors:X})");
builder.AppendLine(entry.ACIForLimitations.ARM9AccessControl.DescriptorVersion, " Descriptor version");
}
}
diff --git a/SabreTools.Serialization/SabreTools.Serialization.csproj b/SabreTools.Serialization/SabreTools.Serialization.csproj
index 485d3df6..60f3af90 100644
--- a/SabreTools.Serialization/SabreTools.Serialization.csproj
+++ b/SabreTools.Serialization/SabreTools.Serialization.csproj
@@ -31,7 +31,7 @@
-
+