Compare commits

...

4 Commits
1.7.0 ... 1.7.1

Author SHA1 Message Date
Matt Nadareski
5716143168 Bump version 2024-11-13 20:48:55 -05:00
Matt Nadareski
2a59b23149 Add more extensions to N3DS wrapper 2024-11-13 20:47:25 -05:00
Matt Nadareski
bdbec4ed02 Update Models to 1.5.1 2024-11-13 20:41:13 -05:00
Matt Nadareski
25193f1805 Start making fixes to N3DS 2024-11-13 20:21:32 -05:00
5 changed files with 418 additions and 209 deletions

View File

@@ -27,7 +27,7 @@
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="SabreTools.Models" Version="1.5.0" />
<PackageReference Include="SabreTools.Models" Version="1.5.1" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -83,7 +83,7 @@ namespace SabreTools.Serialization.Deserializers
#region Extended Headers
// Create the extended header table
cart.ExtendedHeaders = new NCCHExtendedHeader[8];
cart.ExtendedHeaders = new NCCHExtendedHeader?[8];
// Iterate and build the extended headers
for (int i = 0; i < 8; i++)
@@ -115,7 +115,7 @@ namespace SabreTools.Serialization.Deserializers
#region ExeFS Headers
// Create the ExeFS header table
cart.ExeFSHeaders = new ExeFSHeader[8];
cart.ExeFSHeaders = new ExeFSHeader?[8];
// Iterate and build the ExeFS headers
for (int i = 0; i < 8; i++)
@@ -149,7 +149,7 @@ namespace SabreTools.Serialization.Deserializers
#region RomFS Headers
// Create the RomFS header table
cart.RomFSHeaders = new RomFSHeader[8];
cart.RomFSHeaders = new RomFSHeader?[8];
// Iterate and build the RomFS headers
for (int i = 0; i < 8; i++)
@@ -264,25 +264,20 @@ namespace SabreTools.Serialization.Deserializers
header.WritableAddressMediaUnits = data.ReadUInt32();
header.CardInfoBitmask = data.ReadUInt32();
header.Reserved3 = data.ReadBytes(0x108);
header.Reserved1 = data.ReadBytes(0xF8);
header.FilledSize = data.ReadUInt32();
header.Reserved2 = data.ReadBytes(0x0C);
header.TitleVersion = data.ReadUInt16();
header.CardRevision = data.ReadUInt16();
header.Reserved3 = data.ReadBytes(0x0C);
header.CVerTitleID = data.ReadBytes(0x08);
header.CVerVersionNumber = data.ReadUInt16();
header.Reserved4 = data.ReadBytes(0xCD6);
header.InitialData = ParseInitialData(data);
return header;
}
/// <summary>
/// Parse a Stream into a development card info header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled development card info header on success, null on error</returns>
public static DevelopmentCardInfoHeader? ParseDevelopmentCardInfoHeader(Stream data)
{
return data.ReadType<DevelopmentCardInfoHeader>();
}
/// <summary>
/// Parse a Stream into initial data
/// </summary>
@@ -303,6 +298,16 @@ namespace SabreTools.Serialization.Deserializers
return id;
}
/// <summary>
/// Parse a Stream into a development card info header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled development card info header on success, null on error</returns>
public static DevelopmentCardInfoHeader? ParseDevelopmentCardInfoHeader(Stream data)
{
return data.ReadType<DevelopmentCardInfoHeader>();
}
/// <summary>
/// Parse a Stream into an NCCH header
/// </summary>

View File

@@ -44,26 +44,26 @@ namespace SabreTools.Serialization.Printers
builder.AppendLine(header.PartitionsCryptType, " Partitions crypt type");
builder.AppendLine();
builder.AppendLine(" Partition table:");
builder.AppendLine(" -------------------------");
builder.AppendLine(" Partition table:");
builder.AppendLine(" -------------------------");
if (header.PartitionsTable == null || header.PartitionsTable.Length == 0)
{
builder.AppendLine(" No partition table entries");
builder.AppendLine(" No partition table entries");
}
else
{
for (int i = 0; i < header.PartitionsTable.Length; i++)
{
var partitionTableEntry = header.PartitionsTable[i];
builder.AppendLine($" Partition table entry {i}");
builder.AppendLine($" Partition table entry {i}");
if (partitionTableEntry == null)
{
builder.AppendLine(" [NULL]");
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(partitionTableEntry.Offset, " Offset");
builder.AppendLine(partitionTableEntry.Length, " Length");
builder.AppendLine(partitionTableEntry.Offset, " Offset");
builder.AppendLine(partitionTableEntry.Length, " Length");
}
}
builder.AppendLine();
@@ -77,17 +77,17 @@ namespace SabreTools.Serialization.Printers
builder.AppendLine(header.PartitionFlags, " Partition flags");
builder.AppendLine();
builder.AppendLine(" Partition ID table:");
builder.AppendLine(" -------------------------");
builder.AppendLine(" Partition ID table:");
builder.AppendLine(" -------------------------");
if (header.PartitionIdTable == null || header.PartitionIdTable.Length == 0)
{
builder.AppendLine(" No partition ID table entries");
builder.AppendLine(" No partition ID table entries");
}
else
{
for (int i = 0; i < header.PartitionIdTable.Length; i++)
{
builder.AppendLine(header.PartitionIdTable[i], $" Partition {i} ID");
builder.AppendLine(header.PartitionIdTable[i], $" Partition {i} ID");
}
}
builder.AppendLine();
@@ -146,62 +146,6 @@ namespace SabreTools.Serialization.Printers
return;
}
builder.AppendLine();
builder.AppendLine(" Initial Data:");
builder.AppendLine(" -------------------------");
if (header.InitialData == null)
{
builder.AppendLine(" No initial data");
}
else
{
builder.AppendLine(header.InitialData.CardSeedKeyY, " Card seed keyY");
builder.AppendLine(header.InitialData.EncryptedCardSeed, " Encrypted card seed");
builder.AppendLine(header.InitialData.CardSeedAESMAC, " Card seed AES-MAC");
builder.AppendLine(header.InitialData.CardSeedNonce, " Card seed nonce");
builder.AppendLine(header.InitialData.Reserved, " Reserved");
builder.AppendLine();
builder.AppendLine(" Backup Header:");
builder.AppendLine(" -------------------------");
if (header.InitialData.BackupHeader == null)
{
builder.AppendLine(" No backup header");
}
else
{
builder.AppendLine(header.InitialData.BackupHeader.MagicID, " Magic ID");
builder.AppendLine(header.InitialData.BackupHeader.ContentSizeInMediaUnits, " Content size in media units");
builder.AppendLine(header.InitialData.BackupHeader.PartitionId, " Partition ID");
builder.AppendLine(header.InitialData.BackupHeader.MakerCode, " Maker code");
builder.AppendLine(header.InitialData.BackupHeader.Version, " Version");
builder.AppendLine(header.InitialData.BackupHeader.VerificationHash, " Verification hash");
builder.AppendLine(header.InitialData.BackupHeader.ProgramId, " Program ID");
builder.AppendLine(header.InitialData.BackupHeader.Reserved1, " Reserved 1");
builder.AppendLine(header.InitialData.BackupHeader.LogoRegionHash, " Logo region SHA-256 hash");
builder.AppendLine(header.InitialData.BackupHeader.ProductCode, " Product code");
builder.AppendLine(header.InitialData.BackupHeader.ExtendedHeaderHash, " Extended header SHA-256 hash");
builder.AppendLine(header.InitialData.BackupHeader.ExtendedHeaderSizeInBytes, " Extended header size in bytes");
builder.AppendLine(header.InitialData.BackupHeader.Reserved2, " Reserved 2");
builder.AppendLine($" Flags: {header.InitialData.BackupHeader.Flags} (0x{header.InitialData.BackupHeader.Flags:X})");
builder.AppendLine(header.InitialData.BackupHeader.PlainRegionOffsetInMediaUnits, " Plain region offset, in media units");
builder.AppendLine(header.InitialData.BackupHeader.PlainRegionSizeInMediaUnits, " Plain region size, in media units");
builder.AppendLine(header.InitialData.BackupHeader.LogoRegionOffsetInMediaUnits, " Logo region offset, in media units");
builder.AppendLine(header.InitialData.BackupHeader.LogoRegionSizeInMediaUnits, " Logo region size, in media units");
builder.AppendLine(header.InitialData.BackupHeader.ExeFSOffsetInMediaUnits, " ExeFS offset, in media units");
builder.AppendLine(header.InitialData.BackupHeader.ExeFSSizeInMediaUnits, " ExeFS size, in media units");
builder.AppendLine(header.InitialData.BackupHeader.ExeFSHashRegionSizeInMediaUnits, " ExeFS hash region size, in media units");
builder.AppendLine(header.InitialData.BackupHeader.Reserved3, " Reserved 3");
builder.AppendLine(header.InitialData.BackupHeader.RomFSOffsetInMediaUnits, " RomFS offset, in media units");
builder.AppendLine(header.InitialData.BackupHeader.RomFSSizeInMediaUnits, " RomFS size, in media units");
builder.AppendLine(header.InitialData.BackupHeader.RomFSHashRegionSizeInMediaUnits, " RomFS hash region size, in media units");
builder.AppendLine(header.InitialData.BackupHeader.Reserved4, " Reserved 4");
builder.AppendLine(header.InitialData.BackupHeader.ExeFSSuperblockHash, " ExeFS superblock SHA-256 hash");
builder.AppendLine(header.InitialData.BackupHeader.RomFSSuperblockHash, " RomFS superblock SHA-256 hash");
}
}
builder.AppendLine();
builder.AppendLine(header.CardDeviceReserved1, " Card device reserved 1");
builder.AppendLine(header.TitleKey, " Title key");
builder.AppendLine(header.CardDeviceReserved2, " Card device reserved 2");

View File

@@ -10,7 +10,7 @@
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>1.7.0</Version>
<Version>1.7.1</Version>
<!-- Package Properties -->
<Authors>Matt Nadareski</Authors>
@@ -52,7 +52,7 @@
<PackageReference Include="SabreTools.ASN1" Version="1.4.0" />
<PackageReference Include="SabreTools.Hashing" Version="1.4.0" />
<PackageReference Include="SabreTools.IO" Version="1.5.0" />
<PackageReference Include="SabreTools.Models" Version="1.5.0" />
<PackageReference Include="SabreTools.Models" Version="1.5.1" />
</ItemGroup>
</Project>

View File

@@ -1,10 +1,11 @@
using System;
using System.IO;
using SabreTools.Models.N3DS;
using static SabreTools.Models.N3DS.Constants;
namespace SabreTools.Serialization.Wrappers
{
public class N3DS : WrapperBase<Models.N3DS.Cart>
public class N3DS : WrapperBase<Cart>
{
#region Descriptive Properties
@@ -13,17 +14,180 @@ namespace SabreTools.Serialization.Wrappers
#endregion
#region Extension Properties
/// <summary>
/// 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.
/// </summary>
public byte BackupWriteWaitTime
{
get
{
if (Model.Header?.PartitionFlags == null)
return default;
return Model.Header.PartitionFlags[(int)NCSDFlags.BackupWriteWaitTime];
}
}
/// <summary>
/// Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (Only SDK 2.X)
/// </summary>
public MediaCardDeviceType MediaCardDevice2X
{
get
{
if (Model.Header?.PartitionFlags == null)
return default;
return (MediaCardDeviceType)Model.Header.PartitionFlags[(int)NCSDFlags.MediaCardDevice2X];
}
}
/// <summary>
/// Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (SDK 3.X+)
/// </summary>
public MediaCardDeviceType MediaCardDevice3X
{
get
{
if (Model.Header?.PartitionFlags == null)
return default;
return (MediaCardDeviceType)Model.Header.PartitionFlags[(int)NCSDFlags.MediaCardDevice3X];
}
}
/// <summary>
/// Media Platform Index (1 = CTR)
/// </summary>
public MediaPlatformIndex MediaPlatformIndex
{
get
{
if (Model.Header?.PartitionFlags == null)
return default;
return (MediaPlatformIndex)Model.Header.PartitionFlags[(int)NCSDFlags.MediaPlatformIndex];
}
}
/// <summary>
/// Media Type Index (0 = Inner Device, 1 = Card1, 2 = Card2, 3 = Extended Device)
/// </summary>
public MediaTypeIndex MediaTypeIndex
{
get
{
if (Model.Header?.PartitionFlags == null)
return default;
return (MediaTypeIndex)Model.Header.PartitionFlags[(int)NCSDFlags.MediaTypeIndex];
}
}
/// <summary>
/// Media unit size in bytes
/// </summary>
public uint MediaUnitSize
{
get
{
if (Model.Header?.PartitionFlags == null)
return default;
return (uint)(0x200 * Math.Pow(2, Model.Header.PartitionFlags[(int)NCSDFlags.MediaUnitSize]));
}
}
#region Partition Entries
/// <summary>
/// Partition table entry for Executable Content (CXI)
/// </summary>
public PartitionTableEntry? ExecutableContentEntry
{
get
{
if (Model.Header?.PartitionsTable == null)
return null;
return Model.Header.PartitionsTable[0];
}
}
/// <summary>
/// Partition table entry for E-Manual (CFA)
/// </summary>
public PartitionTableEntry? EManualEntry
{
get
{
if (Model.Header?.PartitionsTable == null)
return null;
return Model.Header.PartitionsTable[1];
}
}
/// <summary>
/// Partition table entry for Download Play Child container (CFA)
/// </summary>
public PartitionTableEntry? DownloadPlayChildContainerEntry
{
get
{
if (Model.Header?.PartitionsTable == null)
return null;
return Model.Header.PartitionsTable[2];
}
}
/// <summary>
/// Partition table entry for New3DS Update Data (CFA)
/// </summary>
public PartitionTableEntry? New3DSUpdateDataEntry
{
get
{
if (Model.Header?.PartitionsTable == null)
return null;
return Model.Header.PartitionsTable[6];
}
}
/// <summary>
/// Partition table entry for Update Data (CFA)
/// </summary>
public PartitionTableEntry? UpdateDataEntry
{
get
{
if (Model.Header?.PartitionsTable == null)
return null;
return Model.Header.PartitionsTable[7];
}
}
#endregion
#endregion
#region Constructors
/// <inheritdoc/>
public N3DS(Models.N3DS.Cart? model, byte[]? data, int offset)
public N3DS(Cart? model, byte[]? data, int offset)
: base(model, data, offset)
{
// All logic is handled by the base class
}
/// <inheritdoc/>
public N3DS(Models.N3DS.Cart? model, Stream? data)
public N3DS(Cart? model, Stream? data)
: base(model, data)
{
// All logic is handled by the base class
@@ -77,177 +241,273 @@ namespace SabreTools.Serialization.Wrappers
#endregion
// TODO: Hook these up for external use
#region Currently Unused Extensions
#region ExeFSFileHeader
#region Data
/// <summary>
/// Determines if a file header represents a CODE block
/// </summary>
public static bool IsCodeBinary(ExeFSFileHeader? header)
public bool IsCodeBinary(int fsIndex, int headerIndex)
{
if (header == null)
if (Model.ExeFSHeaders == null)
return false;
if (fsIndex < 0 || fsIndex >= Model.ExeFSHeaders.Length)
return false;
return header.FileName == ".code\0\0\0";
var fsHeader = Model.ExeFSHeaders[fsIndex];
if (fsHeader?.FileHeaders == null)
return false;
if (headerIndex < 0 || headerIndex >= fsHeader.FileHeaders.Length)
return false;
var fileHeader = fsHeader.FileHeaders[headerIndex];
if (fileHeader == null)
return false;
return fileHeader.FileName == ".code\0\0\0";
}
#endregion
/// <summary>
/// Get the initial value for the plain counter
/// </summary>
public byte[] PlainIV(int partitionIndex)
{
if (Model.Partitions == null)
return [];
if (partitionIndex < 0 || partitionIndex >= Model.Partitions.Length)
return [];
#region NCCHHeaderFlags
var header = Model.Partitions[partitionIndex];
if (header == null)
return [];
byte[] partitionIdBytes = BitConverter.GetBytes(header.PartitionId);
return [.. partitionIdBytes, .. PlainCounter];
}
/// <summary>
/// Get the initial value for the ExeFS counter
/// </summary>
public byte[] ExeFSIV(int partitionIndex)
{
if (Model.Partitions == null)
return [];
if (partitionIndex < 0 || partitionIndex >= Model.Partitions.Length)
return [];
var header = Model.Partitions[partitionIndex];
if (header == null)
return [];
byte[] partitionIdBytes = BitConverter.GetBytes(header.PartitionId);
return [.. partitionIdBytes, .. ExefsCounter];
}
/// <summary>
/// Get the initial value for the RomFS counter
/// </summary>
public byte[] RomFSIV(int partitionIndex)
{
if (Model.Partitions == null)
return [];
if (partitionIndex < 0 || partitionIndex >= Model.Partitions.Length)
return [];
var header = Model.Partitions[partitionIndex];
if (header == null)
return [];
byte[] partitionIdBytes = BitConverter.GetBytes(header.PartitionId);
return [.. partitionIdBytes, .. RomfsCounter];
}
/// <summary>
/// Get if the NoCrypto bit is set
/// </summary>
public static bool PossiblyDecrypted(NCCHHeaderFlags flags)
public bool PossiblyDecrypted(int index)
{
if (flags == null)
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;
#if NET20 || NET35
return (flags.BitMasks & BitMasks.NoCrypto) != 0;
return (partition.Flags.BitMasks & BitMasks.NoCrypto) != 0;
#else
return flags.BitMasks.HasFlag(BitMasks.NoCrypto);
return partition.Flags.BitMasks.HasFlag(BitMasks.NoCrypto);
#endif
}
#endregion
#region NCSDHeader
#region Offsets
/// <summary>
/// Partition table entry for Executable Content (CXI)
/// Get the offset of a partition ExeFS
/// </summary>
public static PartitionTableEntry? ExecutableContent(NCSDHeader? header)
/// <returns>Offset to the ExeFS of the partition, 0 on error</returns>
public uint GetExeFSOffset(int index)
{
if (header?.PartitionsTable == null)
return null;
// Empty partitions table means no size is available
var partitionsTable = Model.Header?.PartitionsTable;
if (partitionsTable == null)
return 0;
return header.PartitionsTable[0];
// Invalid partition table entry means no size is available
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)
return 0;
// If the offset is 0, return 0
uint exeFsOffsetMU = header.ExeFSOffsetInMediaUnits;
if (exeFsOffsetMU == 0)
return 0;
// Return the adjusted offset
uint partitionOffsetMU = entry.Offset;
return (partitionOffsetMU + exeFsOffsetMU) * MediaUnitSize;
}
/// <summary>
/// Partition table entry for E-Manual (CFA)
/// Get the offset of a partition
/// </summary>
public static PartitionTableEntry? EManual(NCSDHeader? header)
/// <returns>Offset to the partition, 0 on error</returns>
public uint GetPartitionOffset(int index)
{
if (header?.PartitionsTable == null)
return null;
// Empty partitions table means no size is available
var partitionsTable = Model.Header?.PartitionsTable;
if (partitionsTable == null)
return 0;
return header.PartitionsTable[1];
// Invalid partition table entry means no size is available
var entry = partitionsTable[index];
if (entry == null)
return 0;
// Return the adjusted offset
uint partitionOffsetMU = entry.Offset;
if (entry.Offset == 0)
return 0;
// Return the adjusted offset
return partitionOffsetMU * MediaUnitSize;
}
/// <summary>
/// Partition table entry for Download Play Child container (CFA)
/// Get the offset of a partition RomFS
/// </summary>
public static PartitionTableEntry? DownloadPlayChildContainer(NCSDHeader? header)
/// <returns>Offset to the RomFS of the partition, 0 on error</returns>
public uint GetRomFSOffset(int index)
{
if (header?.PartitionsTable == null)
return null;
// Empty partitions table means no size is available
var partitionsTable = Model.Header?.PartitionsTable;
if (partitionsTable == null)
return 0;
return header.PartitionsTable[2];
}
// Invalid partition table entry means no size is available
var entry = partitionsTable[index];
if (entry == null)
return 0;
/// <summary>
/// Partition table entry for New3DS Update Data (CFA)
/// </summary>
public static PartitionTableEntry? New3DSUpdateData(NCSDHeader? header)
{
if (header?.PartitionsTable == null)
return null;
// Empty partitions array means no size is available
var partitions = Model.Partitions;
if (partitions == null)
return 0;
return header.PartitionsTable[6];
}
// Invalid partition means no size is available
var header = partitions[index];
if (header == null)
return 0;
/// <summary>
/// Partition table entry for Update Data (CFA)
/// </summary>
public static PartitionTableEntry? UpdateData(NCSDHeader? header)
{
if (header?.PartitionsTable == null)
return null;
// If the offset is 0, return 0
uint romFsOffsetMU = header.RomFSOffsetInMediaUnits;
if (romFsOffsetMU == 0)
return 0;
return header.PartitionsTable[7];
}
/// <summary>
/// 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.
/// </summary>
public static byte BackupWriteWaitTime(NCSDHeader? header)
{
if (header?.PartitionFlags == null)
return default;
return header.PartitionFlags[(int)NCSDFlags.BackupWriteWaitTime];
}
/// <summary>
/// Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (SDK 3.X+)
/// </summary>
public static MediaCardDeviceType MediaCardDevice3X(NCSDHeader? header)
{
if (header?.PartitionFlags == null)
return default;
return (MediaCardDeviceType)header.PartitionFlags[(int)NCSDFlags.MediaCardDevice3X];
}
/// <summary>
/// Media Platform Index (1 = CTR)
/// </summary>
public static MediaPlatformIndex MediaPlatformIndex(NCSDHeader? header)
{
if (header?.PartitionFlags == null)
return default;
return (MediaPlatformIndex)header.PartitionFlags[(int)NCSDFlags.MediaPlatformIndex];
}
/// <summary>
/// Media Type Index (0 = Inner Device, 1 = Card1, 2 = Card2, 3 = Extended Device)
/// </summary>
public static MediaTypeIndex MediaTypeIndex(NCSDHeader? header)
{
if (header?.PartitionFlags == null)
return default;
return (MediaTypeIndex)header.PartitionFlags[(int)NCSDFlags.MediaTypeIndex];
}
/// <summary>
/// Media Unit Size i.e. u32 MediaUnitSize = 0x200*2^flags[6];
/// </summary>
public static uint MediaUnitSize(Cart cart)
{
return MediaUnitSize(cart.Header);
}
/// <summary>
/// Media Unit Size i.e. u32 MediaUnitSize = 0x200*2^flags[6];
/// </summary>
public static uint MediaUnitSize(NCSDHeader? header)
{
if (header?.PartitionFlags == null)
return default;
return (uint)(0x200 * Math.Pow(2, header.PartitionFlags[(int)NCSDFlags.MediaUnitSize]));
}
/// <summary>
/// Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (Only SDK 2.X)
/// </summary>
public static MediaCardDeviceType MediaCardDevice2X(NCSDHeader? header)
{
if (header?.PartitionFlags == null)
return default;
return (MediaCardDeviceType)header.PartitionFlags[(int)NCSDFlags.MediaCardDevice2X];
// Return the adjusted offset
uint partitionOffsetMU = entry.Offset;
return (partitionOffsetMU + romFsOffsetMU) * MediaUnitSize;
}
#endregion
#region Sizes
/// <summary>
/// Get the size of a partition ExeFS
/// </summary>
/// <returns>Size of the partition ExeFS in bytes, 0 on error</returns>
public uint GetExeFSSize(int index)
{
// Empty partitions array means no size is available
var partitions = Model.Partitions;
if (partitions == null)
return 0;
// Invalid partition header means no size is available
var header = partitions[index];
if (header == null)
return 0;
// Return the adjusted size
return header.ExeFSSizeInMediaUnits * MediaUnitSize;
}
/// <summary>
/// Get the size of a partition extended header
/// </summary>
/// <returns>Size of the partition extended header in bytes, 0 on error</returns>
public uint GetExtendedHeaderSize(int index)
{
// Empty partitions array means no size is available
var partitions = Model.Partitions;
if (partitions == null)
return 0;
// Invalid partition header means no size is available
var header = partitions[index];
if (header == null)
return 0;
// Return the adjusted size
return header.ExtendedHeaderSizeInBytes;
}
/// <summary>
/// Get the size of a partition RomFS
/// </summary>
/// <returns>Size of the partition RomFS in bytes, 0 on error</returns>
public uint GetRomFSSize(int index)
{
// Empty partitions array means no size is available
var partitions = Model.Partitions;
if (partitions == null)
return 0;
// Invalid partition header means no size is available
var header = partitions[index];
if (header == null)
return 0;
// Return the adjusted size
return header.RomFSSizeInMediaUnits * MediaUnitSize;
}
#endregion
}
}