diff --git a/SabreTools.Serialization/Wrappers/N3DS.cs b/SabreTools.Serialization/Wrappers/N3DS.cs
index 9cb9f13f..19dee6d9 100644
--- a/SabreTools.Serialization/Wrappers/N3DS.cs
+++ b/SabreTools.Serialization/Wrappers/N3DS.cs
@@ -22,76 +22,10 @@ namespace SabreTools.Serialization.Wrappers
public NCCHHeader? BackupHeader => Model.CardInfoHeader?.InitialData?.BackupHeader;
///
- /// Backup Write Wait Time (The time to wait to write save to backup after the card is recognized (0-255
- /// seconds)). NATIVE_FIRM loads this flag from the gamecard NCSD header starting with 6.0.0-11.
+ /// ExeFS headers
///
- public byte BackupWriteWaitTime
- {
- get
- {
- if (Model.Header?.PartitionFlags == null)
- return default;
-
- return Model.Header.PartitionFlags[(int)NCSDFlags.BackupWriteWaitTime];
- }
- }
-
- ///
- /// Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (Only SDK 2.X)
- ///
- public MediaCardDeviceType MediaCardDevice2X
- {
- get
- {
- if (Model.Header?.PartitionFlags == null)
- return default;
-
- return (MediaCardDeviceType)Model.Header.PartitionFlags[(int)NCSDFlags.MediaCardDevice2X];
- }
- }
-
- ///
- /// Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (SDK 3.X+)
- ///
- public MediaCardDeviceType MediaCardDevice3X
- {
- get
- {
- if (Model.Header?.PartitionFlags == null)
- return default;
-
- return (MediaCardDeviceType)Model.Header.PartitionFlags[(int)NCSDFlags.MediaCardDevice3X];
- }
- }
-
- ///
- /// Media Platform Index (1 = CTR)
- ///
- public MediaPlatformIndex MediaPlatformIndex
- {
- get
- {
- if (Model.Header?.PartitionFlags == null)
- return default;
-
- return (MediaPlatformIndex)Model.Header.PartitionFlags[(int)NCSDFlags.MediaPlatformIndex];
- }
- }
-
- ///
- /// Media Type Index (0 = Inner Device, 1 = Card1, 2 = Card2, 3 = Extended Device)
- ///
- public MediaTypeIndex MediaTypeIndex
- {
- get
- {
- if (Model.Header?.PartitionFlags == null)
- return default;
-
- return (MediaTypeIndex)Model.Header.PartitionFlags[(int)NCSDFlags.MediaTypeIndex];
- }
- }
-
+ public ExeFSHeader?[] ExeFSHeaders => Model.ExeFSHeaders ?? [];
+
///
/// Media unit size in bytes
///
@@ -106,7 +40,17 @@ namespace SabreTools.Serialization.Wrappers
}
}
- #region Partition Entries
+ ///
+ /// Partitions data table
+ ///
+ public NCCHHeader?[] Partitions => Model.Partitions ?? [];
+
+ ///
+ /// Partitions header table
+ ///
+ public PartitionTableEntry?[] PartitionsTable => Model.Header?.PartitionsTable ?? [];
+
+ #region Named Partition Entries
///
/// Partition table entry for Executable Content (CXI)
@@ -115,10 +59,10 @@ namespace SabreTools.Serialization.Wrappers
{
get
{
- if (Model.Header?.PartitionsTable == null)
+ if (PartitionsTable == null || PartitionsTable.Length == 0)
return null;
- return Model.Header.PartitionsTable[0];
+ return PartitionsTable[0];
}
}
@@ -129,10 +73,10 @@ namespace SabreTools.Serialization.Wrappers
{
get
{
- if (Model.Header?.PartitionsTable == null)
+ if (PartitionsTable == null || PartitionsTable.Length == 0)
return null;
- return Model.Header.PartitionsTable[1];
+ return PartitionsTable[1];
}
}
@@ -143,10 +87,10 @@ namespace SabreTools.Serialization.Wrappers
{
get
{
- if (Model.Header?.PartitionsTable == null)
+ if (PartitionsTable == null || PartitionsTable.Length == 0)
return null;
- return Model.Header.PartitionsTable[2];
+ return PartitionsTable[2];
}
}
@@ -157,10 +101,10 @@ namespace SabreTools.Serialization.Wrappers
{
get
{
- if (Model.Header?.PartitionsTable == null)
+ if (PartitionsTable == null || PartitionsTable.Length == 0)
return null;
- return Model.Header.PartitionsTable[6];
+ return PartitionsTable[6];
}
}
@@ -171,10 +115,90 @@ namespace SabreTools.Serialization.Wrappers
{
get
{
- if (Model.Header?.PartitionsTable == null)
+ if (PartitionsTable == null || PartitionsTable.Length == 0)
return null;
- return Model.Header.PartitionsTable[7];
+ return PartitionsTable[7];
+ }
+ }
+
+ #endregion
+
+ ///
+ /// Partitions flags
+ ///
+ public byte[] PartitionFlags => Model.Header?.PartitionFlags ?? [];
+
+ #region Partition Flags
+
+ ///
+ /// Backup Write Wait Time (The time to wait to write save to backup after the card is recognized (0-255
+ /// seconds)). NATIVE_FIRM loads this flag from the gamecard NCSD header starting with 6.0.0-11.
+ ///
+ public byte BackupWriteWaitTime
+ {
+ get
+ {
+ if (PartitionFlags == null || PartitionFlags.Length == 0)
+ return default;
+
+ return PartitionFlags[(int)NCSDFlags.BackupWriteWaitTime];
+ }
+ }
+
+ ///
+ /// Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (Only SDK 2.X)
+ ///
+ public MediaCardDeviceType MediaCardDevice2X
+ {
+ get
+ {
+ if (PartitionFlags == null || PartitionFlags.Length == 0)
+ return default;
+
+ return (MediaCardDeviceType)PartitionFlags[(int)NCSDFlags.MediaCardDevice2X];
+ }
+ }
+
+ ///
+ /// Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (SDK 3.X+)
+ ///
+ public MediaCardDeviceType MediaCardDevice3X
+ {
+ get
+ {
+ if (PartitionFlags == null || PartitionFlags.Length == 0)
+ return default;
+
+ return (MediaCardDeviceType)PartitionFlags[(int)NCSDFlags.MediaCardDevice3X];
+ }
+ }
+
+ ///
+ /// Media Platform Index (1 = CTR)
+ ///
+ public MediaPlatformIndex MediaPlatformIndex
+ {
+ get
+ {
+ if (PartitionFlags == null || PartitionFlags.Length == 0)
+ return default;
+
+ return (MediaPlatformIndex)PartitionFlags[(int)NCSDFlags.MediaPlatformIndex];
+ }
+ }
+
+ ///
+ /// Media Type Index (0 = Inner Device, 1 = Card1, 2 = Card2, 3 = Extended Device)
+ ///
+ public MediaTypeIndex MediaTypeIndex
+ {
+ get
+ {
+ if (PartitionFlags == null || PartitionFlags.Length == 0)
+ return default;
+
+ return (MediaTypeIndex)PartitionFlags[(int)NCSDFlags.MediaTypeIndex];
}
}
@@ -251,31 +275,48 @@ namespace SabreTools.Serialization.Wrappers
///
/// Get the bit masks for a partition
///
- public BitMasks GetBitMasks(int partitionIndex)
+ public BitMasks GetBitMasks(int index)
{
- if (Model.Partitions == null)
+ if (Partitions == null)
return 0;
- if (partitionIndex < 0 || partitionIndex >= Model.Partitions.Length)
+ if (index < 0 || index >= Partitions.Length)
return 0;
- var partition = Model.Partitions[partitionIndex];
+ var partition = Partitions[index];
if (partition?.Flags == null)
return 0;
return partition.Flags.BitMasks;
}
+ ///
+ /// Get the crypto method for a partition
+ ///
+ public CryptoMethod GetCryptoMethod(int index)
+ {
+ if (Partitions == null)
+ return 0;
+ if (index < 0 || index >= Partitions.Length)
+ return 0;
+
+ var partition = Partitions[index];
+ if (partition?.Flags == null)
+ return 0;
+
+ return partition.Flags.CryptoMethod;
+ }
+
///
/// Determines if a file header represents a CODE block
///
public bool IsCodeBinary(int fsIndex, int headerIndex)
{
- if (Model.ExeFSHeaders == null)
+ if (ExeFSHeaders == null)
return false;
- if (fsIndex < 0 || fsIndex >= Model.ExeFSHeaders.Length)
+ if (fsIndex < 0 || fsIndex >= ExeFSHeaders.Length)
return false;
- var fsHeader = Model.ExeFSHeaders[fsIndex];
+ var fsHeader = ExeFSHeaders[fsIndex];
if (fsHeader?.FileHeaders == null)
return false;
@@ -292,15 +333,15 @@ namespace SabreTools.Serialization.Wrappers
///
/// Get the initial value for the plain counter
///
- public byte[] PlainIV(int partitionIndex)
+ public byte[] PlainIV(int index)
{
- if (Model.Partitions == null)
+ if (Partitions == null)
return [];
- if (partitionIndex < 0 || partitionIndex >= Model.Partitions.Length)
+ if (index < 0 || index >= Partitions.Length)
return [];
- var header = Model.Partitions[partitionIndex];
- if (header == null)
+ var header = Partitions[index];
+ if (header == null || header.MagicID != NCCHMagicNumber)
return [];
byte[] partitionIdBytes = BitConverter.GetBytes(header.PartitionId);
@@ -311,15 +352,15 @@ namespace SabreTools.Serialization.Wrappers
///
/// Get the initial value for the ExeFS counter
///
- public byte[] ExeFSIV(int partitionIndex)
+ public byte[] ExeFSIV(int index)
{
- if (Model.Partitions == null)
+ if (Partitions == null)
return [];
- if (partitionIndex < 0 || partitionIndex >= Model.Partitions.Length)
+ if (index < 0 || index >= Partitions.Length)
return [];
- var header = Model.Partitions[partitionIndex];
- if (header == null)
+ var header = Partitions[index];
+ if (header == null || header.MagicID != NCCHMagicNumber)
return [];
byte[] partitionIdBytes = BitConverter.GetBytes(header.PartitionId);
@@ -330,15 +371,15 @@ namespace SabreTools.Serialization.Wrappers
///
/// Get the initial value for the RomFS counter
///
- public byte[] RomFSIV(int partitionIndex)
+ public byte[] RomFSIV(int index)
{
- if (Model.Partitions == null)
+ if (Partitions == null)
return [];
- if (partitionIndex < 0 || partitionIndex >= Model.Partitions.Length)
+ if (index < 0 || index >= Partitions.Length)
return [];
- var header = Model.Partitions[partitionIndex];
- if (header == null)
+ var header = Partitions[index];
+ if (header == null || header.MagicID != NCCHMagicNumber)
return [];
byte[] partitionIdBytes = BitConverter.GetBytes(header.PartitionId);
@@ -351,20 +392,11 @@ namespace SabreTools.Serialization.Wrappers
///
public bool PossiblyDecrypted(int index)
{
- if (Model.Partitions == null)
- return false;
-
- if (index < 0 || index >= Model.Partitions.Length)
- return false;
-
- var partition = Model.Partitions[index];
- if (partition?.Flags == null)
- return false;
-
+ var bitMasks = GetBitMasks(index);
#if NET20 || NET35
- return (partition.Flags.BitMasks & BitMasks.NoCrypto) != 0;
+ return (bitMasks & BitMasks.NoCrypto) != 0;
#else
- return partition.Flags.BitMasks.HasFlag(BitMasks.NoCrypto);
+ return bitMasks.HasFlag(BitMasks.NoCrypto);
#endif
}
@@ -378,24 +410,20 @@ namespace SabreTools.Serialization.Wrappers
/// Offset to the ExeFS of the partition, 0 on error
public uint GetExeFSOffset(int index)
{
- // Empty partitions table means no size is available
- var partitionsTable = Model.Header?.PartitionsTable;
- if (partitionsTable == null)
+ // No partitions means no size is available
+ if (PartitionsTable == null || Partitions == null)
+ return 0;
+ if (index < 0 || index >= Partitions.Length)
return 0;
// Invalid partition table entry means no size is available
- var entry = partitionsTable[index];
+ var entry = PartitionsTable[index];
if (entry == null)
return 0;
- // Empty partitions array means no size is available
- var partitions = Model.Partitions;
- if (partitions == null)
- return 0;
-
// Invalid partition means no size is available
- var header = partitions[index];
- if (header == null)
+ var header = Partitions[index];
+ if (header == null || header.MagicID != NCCHMagicNumber)
return 0;
// If the offset is 0, return 0
@@ -414,13 +442,14 @@ namespace SabreTools.Serialization.Wrappers
/// Offset to the partition, 0 on error
public uint GetPartitionOffset(int index)
{
- // Empty partitions table means no size is available
- var partitionsTable = Model.Header?.PartitionsTable;
- if (partitionsTable == null)
+ // No partitions means no size is available
+ if (PartitionsTable == null)
+ return 0;
+ if (index < 0 || index >= PartitionsTable.Length)
return 0;
// Invalid partition table entry means no size is available
- var entry = partitionsTable[index];
+ var entry = PartitionsTable[index];
if (entry == null)
return 0;
@@ -439,24 +468,20 @@ namespace SabreTools.Serialization.Wrappers
/// Offset to the RomFS of the partition, 0 on error
public uint GetRomFSOffset(int index)
{
- // Empty partitions table means no size is available
- var partitionsTable = Model.Header?.PartitionsTable;
- if (partitionsTable == null)
+ // No partitions means no size is available
+ if (PartitionsTable == null || Partitions == null)
+ return 0;
+ if (index < 0 || index >= Partitions.Length)
return 0;
// Invalid partition table entry means no size is available
- var entry = partitionsTable[index];
+ var entry = PartitionsTable[index];
if (entry == null)
return 0;
- // Empty partitions array means no size is available
- var partitions = Model.Partitions;
- if (partitions == null)
- return 0;
-
// Invalid partition means no size is available
- var header = partitions[index];
- if (header == null)
+ var header = Partitions[index];
+ if (header == null || header.MagicID != NCCHMagicNumber)
return 0;
// If the offset is 0, return 0
@@ -480,12 +505,13 @@ namespace SabreTools.Serialization.Wrappers
public uint GetExeFSSize(int index)
{
// Empty partitions array means no size is available
- var partitions = Model.Partitions;
- if (partitions == null)
+ if (Partitions == null)
+ return 0;
+ if (index < 0 || index >= Partitions.Length)
return 0;
// Invalid partition header means no size is available
- var header = partitions[index];
+ var header = Partitions[index];
if (header == null)
return 0;
@@ -500,12 +526,13 @@ namespace SabreTools.Serialization.Wrappers
public uint GetExtendedHeaderSize(int index)
{
// Empty partitions array means no size is available
- var partitions = Model.Partitions;
- if (partitions == null)
+ if (Partitions == null)
+ return 0;
+ if (index < 0 || index >= Partitions.Length)
return 0;
// Invalid partition header means no size is available
- var header = partitions[index];
+ var header = Partitions[index];
if (header == null)
return 0;
@@ -520,12 +547,13 @@ namespace SabreTools.Serialization.Wrappers
public uint GetRomFSSize(int index)
{
// Empty partitions array means no size is available
- var partitions = Model.Partitions;
- if (partitions == null)
+ if (Partitions == null)
+ return 0;
+ if (index < 0 || index >= Partitions.Length)
return 0;
// Invalid partition header means no size is available
- var header = partitions[index];
+ var header = Partitions[index];
if (header == null)
return 0;