diff --git a/BurnOutSharp.Builders/BFPK.cs b/BurnOutSharp.Builders/BFPK.cs
index 10e7b5f0..48f6efe6 100644
--- a/BurnOutSharp.Builders/BFPK.cs
+++ b/BurnOutSharp.Builders/BFPK.cs
@@ -1,5 +1,4 @@
using System.IO;
-using System.Linq;
using System.Text;
using BurnOutSharp.Models.BFPK;
using BurnOutSharp.Utilities;
diff --git a/BurnOutSharp.Builders/Nitro.cs b/BurnOutSharp.Builders/Nitro.cs
new file mode 100644
index 00000000..242a81d2
--- /dev/null
+++ b/BurnOutSharp.Builders/Nitro.cs
@@ -0,0 +1,192 @@
+using System.IO;
+using System.Text;
+using BurnOutSharp.Models.Nitro;
+using BurnOutSharp.Utilities;
+
+namespace BurnOutSharp.Builders
+{
+ public class Nitro
+ {
+ #region Byte Data
+
+ ///
+ /// Parse a byte array into a NDS cart image
+ ///
+ /// Byte array to parse
+ /// Offset into the byte array
+ /// Filled cart image on success, null on error
+ public static Cart ParseCart(byte[] data, int offset)
+ {
+ // If the data is invalid
+ if (data == null)
+ return null;
+
+ // If the offset is out of bounds
+ if (offset < 0 || offset >= data.Length)
+ return null;
+
+ // Create a memory stream and parse that
+ MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
+ return ParseCart(dataStream);
+ }
+
+ #endregion
+
+ #region Stream Data
+
+ ///
+ /// Parse a Stream into a NDS cart image
+ ///
+ /// Stream to parse
+ /// Filled cart image on success, null on error
+ public static Cart ParseCart(Stream data)
+ {
+ // If the data is invalid
+ if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
+ return null;
+
+ // If the offset is out of bounds
+ if (data.Position < 0 || data.Position >= data.Length)
+ return null;
+
+ // Cache the current offset
+ int initialOffset = (int)data.Position;
+
+ // Create a new cart image to fill
+ var cart = new Cart();
+
+ #region Header
+
+ // Try to parse the header
+ var header = ParseHeader(data);
+ if (header == null)
+ return null;
+
+ // Set the cart image header
+ cart.Header = header;
+
+ #endregion
+
+ #region Secure Area
+
+ // Try to get the secure area offset
+ int secureAreaOffset = 0x4000;
+ if (secureAreaOffset > data.Length)
+ return null;
+
+ // Seek to the secure area
+ data.Seek(secureAreaOffset, SeekOrigin.Begin);
+
+ // Read the secure area without processing
+ cart.SecureArea = data.ReadBytes(0x800);
+
+ #endregion
+
+ return cart;
+ }
+
+ ///
+ /// Parse a Stream into a header
+ ///
+ /// Stream to parse
+ /// Filled header on success, null on error
+ private static Header ParseHeader(Stream data)
+ {
+ // TODO: Use marshalling here instead of building
+ Header header = new Header();
+
+ byte[] gameTitle = data.ReadBytes(0x0C);
+ header.GameTitle = Encoding.ASCII.GetString(gameTitle);
+ header.Gamecode = data.ReadUInt32();
+ byte[] makerCode = data.ReadBytes(2);
+ header.Makercode = Encoding.ASCII.GetString(bytes: makerCode);
+ header.Unitcode = (Unitcode)data.ReadByteValue();
+ header.EncryptionSeedSelect = data.ReadByteValue();
+ header.Devicecapacity = data.ReadByteValue();
+ header.Reserved1 = data.ReadBytes(7);
+ header.GameRevision = data.ReadUInt16();
+ header.RomVersion = data.ReadByteValue();
+ header.InternalFlags = data.ReadByteValue();
+ header.ARM9RomOffset = data.ReadUInt32();
+ header.ARM9EntryAddress = data.ReadUInt32();
+ header.ARM9LoadAddress = data.ReadUInt32();
+ header.ARM9Size = data.ReadUInt32();
+ header.ARM7RomOffset = data.ReadUInt32();
+ header.ARM7EntryAddress = data.ReadUInt32();
+ header.ARM7LoadAddress = data.ReadUInt32();
+ header.ARM7Size = data.ReadUInt32();
+ header.FileNameTableOffset = data.ReadUInt32();
+ header.FileNameTableLength = data.ReadUInt32();
+ header.FileAllocationTableOffset = data.ReadUInt32();
+ header.FileAllocationTableLength = data.ReadUInt32();
+ header.ARM9OverlayOffset = data.ReadUInt32();
+ header.ARM9OverlayLength = data.ReadUInt32();
+ header.ARM7OverlayOffset = data.ReadUInt32();
+ header.ARM7OverlayLength = data.ReadUInt32();
+ header.SecureDisable = data.ReadBytes(8);
+ header.NTRRegionRomSize = data.ReadUInt32();
+ header.HeaderSize = data.ReadUInt32();
+ header.Reserved2 = data.ReadBytes(56);
+ header.NintendoLogo = data.ReadBytes(156);
+ header.NintendoLogoCRC = data.ReadUInt16();
+ header.DebuggerReserved = data.ReadBytes(0x20);
+
+ // If we have a DSi compatible title
+ if (header.Unitcode == Unitcode.NDSPlusDSi || header.Unitcode == Unitcode.DSi)
+ {
+ header.GlobalMBK15Settings = data.ReadBytes(20);
+ header.LocalMBK68SettingsARM9 = data.ReadBytes(12);
+ header.LocalMBK68SettingsARM7 = data.ReadBytes(12);
+ header.GlobalMBK9Setting = data.ReadBytes(4);
+ header.RegionFlags = data.ReadBytes(4);
+ header.AccessControl = data.ReadBytes(4);
+ header.ARM7SCFGEXTMask = data.ReadBytes(4);
+ header.ReservedFlags = data.ReadBytes(4);
+ header.ARM9iRomOffset = data.ReadUInt32();
+ header.Reserved3 = data.ReadBytes(4);
+ header.ARM9iLoadAddress = data.ReadUInt32();
+ header.ARM9iSize = data.ReadUInt32();
+ header.ARM7iRomOffset = data.ReadUInt32();
+ header.Reserved4 = data.ReadBytes(4);
+ header.ARM7iLoadAddress = data.ReadUInt32();
+ header.ARM7iSize = data.ReadUInt32();
+ header.DigestNTRRegionOffset = data.ReadUInt32();
+ header.DigestNTRRegionLength = data.ReadUInt32();
+ header.DigestTWLRegionOffset = data.ReadUInt32();
+ header.DigestTWLRegionLength = data.ReadUInt32();
+ header.DigestSectorHashtableRegionOffset = data.ReadUInt32();
+ header.DigestSectorHashtableRegionLength = data.ReadUInt32();
+ header.DigestBlockHashtableRegionOffset = data.ReadUInt32();
+ header.DigestBlockHashtableRegionLength = data.ReadUInt32();
+ header.DigestSectorSize = data.ReadUInt32();
+ header.DigestBlockSectorCount = data.ReadUInt32();
+ header.IconBannerSize = data.ReadUInt32();
+ header.Unknown1 = data.ReadBytes(4);
+ header.ModcryptArea1Offset = data.ReadUInt32();
+ header.ModcryptArea1Size = data.ReadUInt32();
+ header.ModcryptArea2Offset = data.ReadUInt32();
+ header.ModcryptArea2Size = data.ReadUInt32();
+ header.TitleID = data.ReadBytes(8);
+ header.DSiWarePublicSavSize = data.ReadUInt32();
+ header.DSiWarePrivateSavSize = data.ReadUInt32();
+ header.ReservedZero = data.ReadBytes(176);
+ header.Unknown2 = data.ReadBytes(0x10);
+ header.ARM9WithSecureAreaSHA1HMACHash = data.ReadBytes(20);
+ header.ARM7SHA1HMACHash = data.ReadBytes(20);
+ header.DigestMasterSHA1HMACHash = data.ReadBytes(20);
+ header.BannerSHA1HMACHash = data.ReadBytes(20);
+ header.ARM9iDecryptedSHA1HMACHash = data.ReadBytes(20);
+ header.ARM7iDecryptedSHA1HMACHash = data.ReadBytes(20);
+ header.Reserved5 = data.ReadBytes(40);
+ header.ARM9NoSecureAreaSHA1HMACHash = data.ReadBytes(20);
+ header.Reserved6 = data.ReadBytes(2636);
+ header.ReservedAndUnchecked = data.ReadBytes(0x180);
+ header.RSASignature = data.ReadBytes(0x80);
+ }
+
+ return header;
+ }
+
+ #endregion
+ }
+}
diff --git a/BurnOutSharp.Models/Nitro/Cart.cs b/BurnOutSharp.Models/Nitro/Cart.cs
new file mode 100644
index 00000000..18ae46ae
--- /dev/null
+++ b/BurnOutSharp.Models/Nitro/Cart.cs
@@ -0,0 +1,18 @@
+namespace BurnOutSharp.Models.Nitro
+{
+ ///
+ /// Represents a DS cart image
+ ///
+ public class Cart
+ {
+ ///
+ /// DS cart header
+ ///
+ public Header Header { get; set; }
+
+ ///
+ /// Secure area, may be encrypted or decrypted
+ ///
+ public byte[] SecureArea { get; set; }
+ }
+}
\ No newline at end of file