diff --git a/3DSDecrypt/Headers/NCCHHeader.cs b/3DSDecrypt/Headers/NCCHHeader.cs
index bb0b35e..55e7829 100644
--- a/3DSDecrypt/Headers/NCCHHeader.cs
+++ b/3DSDecrypt/Headers/NCCHHeader.cs
@@ -160,14 +160,16 @@ namespace ThreeDS.Headers
/// Read from a stream and get an NCCH header, if possible
///
/// BinaryReader representing the input stream
+ /// True if the RSA signature is read, false otherwise
/// NCCH header object, null on error
- public static NCCHHeader Read(BinaryReader reader)
+ public static NCCHHeader Read(BinaryReader reader, bool readSignature)
{
NCCHHeader header = new NCCHHeader();
try
{
- header.RSA2048Signature = reader.ReadBytes(0x100);
+ if (readSignature)
+ header.RSA2048Signature = reader.ReadBytes(0x100);
if (new string(reader.ReadChars(4)) != NCCHMagicNumber)
return null;
diff --git a/3DSDecrypt/Headers/NCSDHeader.cs b/3DSDecrypt/Headers/NCSDHeader.cs
index 71dcc74..78a7292 100644
--- a/3DSDecrypt/Headers/NCSDHeader.cs
+++ b/3DSDecrypt/Headers/NCSDHeader.cs
@@ -40,9 +40,34 @@ namespace ThreeDS.Headers
///
public PartitionTableEntry[] PartitionsTable { get; private set; }
+ ///
+ /// Partition table entry for Executable Content (CXI)
+ ///
+ public PartitionTableEntry ExecutableContent { get { return PartitionsTable[0]; } }
+
+ ///
+ /// Partition table entry for E-Manual (CFA)
+ ///
+ public PartitionTableEntry EManual { get { return PartitionsTable[1]; } }
+
+ ///
+ /// Partition table entry for Download Play Child container (CFA)
+ ///
+ public PartitionTableEntry DownloadPlayChildContainer { get { return PartitionsTable[2]; } }
+
+ ///
+ /// Partition table entry for New3DS Update Data (CFA)
+ ///
+ public PartitionTableEntry New3DSUpdateData { get { return PartitionsTable[6]; } }
+
+ ///
+ /// Partition table entry for Update Data (CFA)
+ ///
+ public PartitionTableEntry UpdateData { get { return PartitionsTable[7]; } }
+
#endregion
- #region For carts
+ #region CTR Cart Image (CCI) Specific
///
/// Exheader SHA-256 hash
@@ -62,13 +87,38 @@ namespace ThreeDS.Headers
///
/// Partition Flags
///
- private byte[] partitionFlags;
- public byte BackupWriteWaitTime { get { return partitionFlags[(int)NCSDFlags.BackupWriteWaitTime]; } }
- public MediaCardDeviceType MediaCardDevice3X { get { return (MediaCardDeviceType)partitionFlags[(int)NCSDFlags.MediaCardDevice3X]; } }
- public MediaPlatformIndex MediaPlatformIndex { get { return (MediaPlatformIndex)partitionFlags[(int)NCSDFlags.MediaPlatformIndex]; } }
- public MediaTypeIndex MediaTypeIndex { get { return (MediaTypeIndex)partitionFlags[(int)NCSDFlags.MediaTypeIndex]; } }
- public uint SectorSize { get { return (uint)(0x200 * Math.Pow(2, partitionFlags[(int)NCSDFlags.MediaUnitSize])); } }
- public MediaCardDeviceType MediaCardDevice2X { get { return (MediaCardDeviceType)partitionFlags[(int)NCSDFlags.MediaCardDevice2X]; } }
+ public byte[] PartitionFlags { get; private set; }
+
+ ///
+ /// 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 { return PartitionFlags[(int)NCSDFlags.BackupWriteWaitTime]; } }
+
+ ///
+ /// Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (SDK 3.X+)
+ ///
+ public MediaCardDeviceType MediaCardDevice3X { get { return (MediaCardDeviceType)PartitionFlags[(int)NCSDFlags.MediaCardDevice3X]; } }
+
+ ///
+ /// Media Platform Index (1 = CTR)
+ ///
+ public MediaPlatformIndex MediaPlatformIndex { get { return (MediaPlatformIndex)PartitionFlags[(int)NCSDFlags.MediaPlatformIndex]; } }
+
+ ///
+ /// Media Type Index (0 = Inner Device, 1 = Card1, 2 = Card2, 3 = Extended Device)
+ ///
+ public MediaTypeIndex MediaTypeIndex { get { return (MediaTypeIndex)PartitionFlags[(int)NCSDFlags.MediaTypeIndex]; } }
+
+ ///
+ /// Media Unit Size i.e. u32 MediaUnitSize = 0x200*2^flags[6];
+ ///
+ public uint MediaUnitSize { get { return (uint)(0x200 * Math.Pow(2, PartitionFlags[(int)NCSDFlags.MediaUnitSize])); } }
+
+ ///
+ /// Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (Only SDK 2.X)
+ ///
+ public MediaCardDeviceType MediaCardDevice2X { get { return (MediaCardDeviceType)PartitionFlags[(int)NCSDFlags.MediaCardDevice2X]; } }
///
/// Partition ID table
@@ -96,11 +146,11 @@ namespace ThreeDS.Headers
///
/// Support for this was implemented with 9.6.0-X FIRM, see below regarding save crypto.
///
- public byte FIrmUpdateByte2 { get; private set; }
+ public byte FirmUpdateByte2 { get; private set; }
#endregion
- #region For NAND
+ #region Raw NAND Format Specific
///
/// Unknown
@@ -114,12 +164,95 @@ namespace ThreeDS.Headers
#endregion
+ #region Card Info Header
+
+ ///
+ /// CARD2: Writable Address In Media Units (For 'On-Chip' Savedata). CARD1: Always 0xFFFFFFFF.
+ ///
+ public byte[] CARD2WritableAddressMediaUnits { get; private set; }
+
+ ///
+ /// Card Info Bitmask
+ ///
+ public byte[] CardInfoBytemask { get; private set; }
+
+ ///
+ /// Reserved1
+ ///
+ public byte[] Reserved3 { get; private set; }
+
+ ///
+ /// Title version
+ ///
+ public ushort TitleVersion { get; private set; }
+
+ ///
+ /// Card revision
+ ///
+ public ushort CardRevision { get; private set; }
+
+ ///
+ /// Reserved2
+ ///
+ public byte[] Reserved4 { get; private set; }
+
+ ///
+ /// Card seed keyY (first u64 is Media ID (same as first NCCH partitionId))
+ ///
+ public byte[] CardSeedKeyY { get; private set; }
+
+ ///
+ /// Encrypted card seed (AES-CCM, keyslot 0x3B for retail cards, see CTRCARD_SECSEED) ///
+ public byte[] EncryptedCardSeed { get; private set; }
+
+ ///
+ /// Card seed AES-MAC
+ ///
+ public byte[] CardSeedAESMAC { get; private set; }
+
+ ///
+ /// Card seed nonce
+ ///
+ public byte[] CardSeedNonce { get; private set; }
+
+ ///
+ /// Reserved3
+ ///
+ public byte[] Reserved5 { get; private set; }
+
+ ///
+ /// Copy of first NCCH header (excluding RSA signature)
+ ///
+ public NCCHHeader BackupHeader { get; private set; }
+
+ #endregion
+
+ #region Development Card Info Header Extension
+
+ ///
+ /// CardDeviceReserved1
+ ///
+ public byte[] CardDeviceReserved1 { get; private set; }
+
+ ///
+ /// TitleKey
+ ///
+ public byte[] TitleKey { get; private set; }
+
+ ///
+ /// CardDeviceReserved2
+ ///
+ public byte[] CardDeviceReserved2 { get; private set; }
+
+ #endregion
+
///
/// Read from a stream and get an NCSD header, if possible
///
/// BinaryReader representing the input stream
+ /// True if development cart, false otherwise
/// NCSD header object, null on error
- public static NCSDHeader Read(BinaryReader reader)
+ public static NCSDHeader Read(BinaryReader reader, bool development)
{
NCSDHeader header = new NCSDHeader();
@@ -145,7 +278,7 @@ namespace ThreeDS.Headers
header.ExheaderHash = reader.ReadBytes(0x20);
header.AdditionalHeaderSize = reader.ReadUInt32();
header.SectorZeroOffset = reader.ReadUInt32();
- header.partitionFlags = reader.ReadBytes(8);
+ header.PartitionFlags = reader.ReadBytes(8);
header.PartitionIdTable = new byte[8][];
for (int i = 0; i < 8; i++)
@@ -154,7 +287,27 @@ namespace ThreeDS.Headers
header.Reserved1 = reader.ReadBytes(0x20);
header.Reserved2 = reader.ReadBytes(0xE);
header.FirmUpdateByte1 = reader.ReadByte();
- header.FIrmUpdateByte2 = reader.ReadByte();
+ header.FirmUpdateByte2 = reader.ReadByte();
+
+ header.CARD2WritableAddressMediaUnits = reader.ReadBytes(4);
+ header.CardInfoBytemask = reader.ReadBytes(4);
+ header.Reserved3 = reader.ReadBytes(0x108);
+ header.TitleVersion = reader.ReadUInt16();
+ header.CardRevision = reader.ReadUInt16();
+ header.Reserved4 = reader.ReadBytes(0xCEE);
+ header.CardSeedKeyY = reader.ReadBytes(0x10);
+ header.EncryptedCardSeed = reader.ReadBytes(0x10);
+ header.CardSeedAESMAC = reader.ReadBytes(0x10);
+ header.CardSeedNonce = reader.ReadBytes(0x10);
+ header.Reserved5 = reader.ReadBytes(0xC4);
+ header.BackupHeader = NCCHHeader.Read(reader, false);
+
+ if (development)
+ {
+ header.CardDeviceReserved1 = reader.ReadBytes(0x200);
+ header.TitleKey = reader.ReadBytes(0x10);
+ header.CardDeviceReserved2 = reader.ReadBytes(0xF0);
+ }
}
else if (header.PartitionsFSType == FilesystemType.FIRM)
{
diff --git a/3DSDecrypt/ThreeDSTool.cs b/3DSDecrypt/ThreeDSTool.cs
index 7351e04..fc7fac8 100644
--- a/3DSDecrypt/ThreeDSTool.cs
+++ b/3DSDecrypt/ThreeDSTool.cs
@@ -66,7 +66,7 @@ namespace ThreeDS
using (BinaryReader f = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
using (BinaryWriter g = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)))
{
- NCSDHeader header = NCSDHeader.Read(f);
+ NCSDHeader header = NCSDHeader.Read(f, development);
if (header == null)
{
Console.WriteLine("Error: Not a 3DS Rom!");
@@ -83,9 +83,9 @@ namespace ThreeDS
}
// Seek to the beginning of the NCCH partition
- f.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize), SeekOrigin.Begin);
+ f.BaseStream.Seek((header.PartitionsTable[p].Offset * header.MediaUnitSize), SeekOrigin.Begin);
- NCCHHeader partitionHeader = NCCHHeader.Read(f);
+ NCCHHeader partitionHeader = NCCHHeader.Read(f, true);
if (partitionHeader == null)
{
Console.WriteLine($"Partition {p} Unable to read NCCH header");
@@ -108,12 +108,12 @@ namespace ThreeDS
ProcessRomFS(f, g, header, p, partitionHeader, false);
// Write the new CryptoMethod
- g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x18B, SeekOrigin.Begin);
+ g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.MediaUnitSize) + 0x18B, SeekOrigin.Begin);
g.Write((byte)CryptoMethod.Original);
g.Flush();
// Write the new BitMasks flag
- g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x18F, SeekOrigin.Begin);
+ g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.MediaUnitSize) + 0x18F, SeekOrigin.Begin);
BitMasks flag = partitionHeader.Flags.BitMasks;
flag = flag & (BitMasks)((byte)(BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) ^ 0xFF);
flag = (flag | BitMasks.NoCrypto);
@@ -139,17 +139,13 @@ namespace ThreeDS
using (BinaryReader f = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
using (BinaryWriter g = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)))
{
- NCSDHeader header = NCSDHeader.Read(f);
+ NCSDHeader header = NCSDHeader.Read(f, development);
if (header == null)
{
Console.WriteLine("Error: Not a 3DS Rom!");
return;
}
- // Get the backup flags
- f.BaseStream.Seek(0x1188, SeekOrigin.Begin);
- NCCHHeaderFlags backupFlags = NCCHHeaderFlags.Read(f);
-
// Iterate over all 8 NCCH partitions
for (int p = 0; p < 8; p++)
{
@@ -160,9 +156,9 @@ namespace ThreeDS
}
// Seek to the beginning of the NCCH partition
- f.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize), SeekOrigin.Begin);
+ f.BaseStream.Seek((header.PartitionsTable[p].Offset * header.MediaUnitSize), SeekOrigin.Begin);
- NCCHHeader partitionHeader = NCCHHeader.Read(f);
+ NCCHHeader partitionHeader = NCCHHeader.Read(f, true);
if (partitionHeader == null)
{
Console.WriteLine($"Partition {p} Unable to read NCCH header");
@@ -177,15 +173,15 @@ namespace ThreeDS
}
// Determine the Keys to be used
- SetEncryptionKeys(partitionHeader.RSA2048Signature, backupFlags.BitMasks, backupFlags.CryptoMethod, p);
+ SetEncryptionKeys(partitionHeader.RSA2048Signature, header.BackupHeader.Flags.BitMasks, header.BackupHeader.Flags.CryptoMethod, p);
// Encrypt each of the pieces if they exist
ProcessExtendedHeader(f, g, header, p, partitionHeader, true);
ProcessExeFS(f, g, header, p, partitionHeader, true);
- ProcessRomFS(f, g, header, p, partitionHeader, true, backupFlags);
+ ProcessRomFS(f, g, header, p, partitionHeader, true, header.BackupHeader.Flags);
// Write the new CryptoMethod
- g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x18B, SeekOrigin.Begin);
+ g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.MediaUnitSize) + 0x18B, SeekOrigin.Begin);
if (p > 0)
{
g.Write((byte)CryptoMethod.Original); // For partitions 1 and up, set crypto-method to 0x00
@@ -193,15 +189,15 @@ namespace ThreeDS
}
else
{
- g.Write((byte)backupFlags.CryptoMethod); // If partition 0, restore crypto-method from backup flags
+ g.Write((byte)header.BackupHeader.Flags.CryptoMethod); // If partition 0, restore crypto-method from backup flags
g.Flush();
}
// Write the new BitMasks flag
- g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x18F, SeekOrigin.Begin);
+ g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.MediaUnitSize) + 0x18F, SeekOrigin.Begin);
BitMasks flag = partitionHeader.Flags.BitMasks;
flag = (flag & ((BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator | BitMasks.NoCrypto) ^ (BitMasks)0xFF));
- flag = (flag | (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) & backupFlags.BitMasks);
+ flag = (flag | (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) & header.BackupHeader.Flags.BitMasks);
g.Write((byte)flag);
g.Flush();
}
@@ -352,8 +348,8 @@ namespace ThreeDS
{
if (partitionHeader.ExtendedHeaderSizeInBytes > 0)
{
- reader.BaseStream.Seek((header.PartitionsTable[partitionNumber].Offset * header.SectorSize) + 0x200, SeekOrigin.Begin);
- writer.BaseStream.Seek((header.PartitionsTable[partitionNumber].Offset * header.SectorSize) + 0x200, SeekOrigin.Begin);
+ reader.BaseStream.Seek((header.PartitionsTable[partitionNumber].Offset * header.MediaUnitSize) + 0x200, SeekOrigin.Begin);
+ writer.BaseStream.Seek((header.PartitionsTable[partitionNumber].Offset * header.MediaUnitSize) + 0x200, SeekOrigin.Begin);
Console.WriteLine($"Partition {partitionNumber} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + ": ExHeader");
@@ -387,7 +383,7 @@ namespace ThreeDS
// For all but the original crypto method, process each of the files in the table
if (partitionHeader.Flags.CryptoMethod != CryptoMethod.Original)
{
- reader.BaseStream.Seek((header.PartitionsTable[partitionNumber].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
+ reader.BaseStream.Seek((header.PartitionsTable[partitionNumber].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.MediaUnitSize, SeekOrigin.Begin);
ExeFSHeader exefsHeader = ExeFSHeader.Read(reader);
if (exefsHeader != null)
{
@@ -399,15 +395,15 @@ namespace ThreeDS
uint datalenM = ((fileHeader.FileSize) / (1024 * 1024));
uint datalenB = ((fileHeader.FileSize) % (1024 * 1024));
- uint ctroffset = ((fileHeader.FileOffset + header.SectorSize) / 0x10);
+ uint ctroffset = ((fileHeader.FileOffset + header.MediaUnitSize) / 0x10);
byte[] exefsIVWithOffsetForHeader = AddToByteArray(partitionHeader.ExeFSIV, (int)ctroffset);
var firstCipher = CreateAESCipher(NormalKey, exefsIVWithOffsetForHeader, encrypt);
var secondCipher = CreateAESCipher(NormalKey2C, exefsIVWithOffsetForHeader, !encrypt);
- reader.BaseStream.Seek((((header.PartitionsTable[partitionNumber].Offset + partitionHeader.ExeFSOffsetInMediaUnits) + 1) * header.SectorSize) + fileHeader.FileOffset, SeekOrigin.Begin);
- writer.BaseStream.Seek((((header.PartitionsTable[partitionNumber].Offset + partitionHeader.ExeFSOffsetInMediaUnits) + 1) * header.SectorSize) + fileHeader.FileOffset, SeekOrigin.Begin);
+ reader.BaseStream.Seek((((header.PartitionsTable[partitionNumber].Offset + partitionHeader.ExeFSOffsetInMediaUnits) + 1) * header.MediaUnitSize) + fileHeader.FileOffset, SeekOrigin.Begin);
+ writer.BaseStream.Seek((((header.PartitionsTable[partitionNumber].Offset + partitionHeader.ExeFSOffsetInMediaUnits) + 1) * header.MediaUnitSize) + fileHeader.FileOffset, SeekOrigin.Begin);
if (datalenM > 0)
{
@@ -435,16 +431,16 @@ namespace ThreeDS
ProcessExeFSFilenameTable(reader, writer, header, partitionNumber, partitionHeader, encrypt);
// Process the ExeFS
- int exefsSizeM = (int)((partitionHeader.ExeFSSizeInMediaUnits - 1) * header.SectorSize) / (1024 * 1024);
- int exefsSizeB = (int)((partitionHeader.ExeFSSizeInMediaUnits - 1) * header.SectorSize) % (1024 * 1024);
- int ctroffsetE = (int)(header.SectorSize / 0x10);
+ int exefsSizeM = (int)((partitionHeader.ExeFSSizeInMediaUnits - 1) * header.MediaUnitSize) / (1024 * 1024);
+ int exefsSizeB = (int)((partitionHeader.ExeFSSizeInMediaUnits - 1) * header.MediaUnitSize) % (1024 * 1024);
+ int ctroffsetE = (int)(header.MediaUnitSize / 0x10);
byte[] exefsIVWithOffset = AddToByteArray(partitionHeader.ExeFSIV, ctroffsetE);
var exeFS = CreateAESCipher(NormalKey2C, exefsIVWithOffset, encrypt);
- reader.BaseStream.Seek((header.PartitionsTable[partitionNumber].Offset + partitionHeader.ExeFSOffsetInMediaUnits + 1) * header.SectorSize, SeekOrigin.Begin);
- writer.BaseStream.Seek((header.PartitionsTable[partitionNumber].Offset + partitionHeader.ExeFSOffsetInMediaUnits + 1) * header.SectorSize, SeekOrigin.Begin);
+ reader.BaseStream.Seek((header.PartitionsTable[partitionNumber].Offset + partitionHeader.ExeFSOffsetInMediaUnits + 1) * header.MediaUnitSize, SeekOrigin.Begin);
+ writer.BaseStream.Seek((header.PartitionsTable[partitionNumber].Offset + partitionHeader.ExeFSOffsetInMediaUnits + 1) * header.MediaUnitSize, SeekOrigin.Begin);
if (exefsSizeM > 0)
{
for (int i = 0; i < exefsSizeM; i++)
@@ -479,13 +475,13 @@ namespace ThreeDS
/// True if we want to encrypt the extended header, false otherwise
private void ProcessExeFSFilenameTable(BinaryReader reader, BinaryWriter writer, NCSDHeader header, int partitionNumber, NCCHHeader partitionHeader, bool encrypt)
{
- reader.BaseStream.Seek((header.PartitionsTable[partitionNumber].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
- writer.BaseStream.Seek((header.PartitionsTable[partitionNumber].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
+ reader.BaseStream.Seek((header.PartitionsTable[partitionNumber].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.MediaUnitSize, SeekOrigin.Begin);
+ writer.BaseStream.Seek((header.PartitionsTable[partitionNumber].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.MediaUnitSize, SeekOrigin.Begin);
Console.WriteLine($"Partition {partitionNumber} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": ExeFS Filename Table");
var exeFSFilenameTable = CreateAESCipher(NormalKey2C, partitionHeader.ExeFSIV, encrypt);
- writer.Write(exeFSFilenameTable.ProcessBytes(reader.ReadBytes((int)header.SectorSize)));
+ writer.Write(exeFSFilenameTable.ProcessBytes(reader.ReadBytes((int)header.MediaUnitSize)));
writer.Flush();
}
@@ -503,8 +499,8 @@ namespace ThreeDS
{
if (partitionHeader.RomFSOffsetInMediaUnits != 0)
{
- int romfsSizeM = (int)(partitionHeader.RomFSSizeInMediaUnits * header.SectorSize) / (1024 * 1024);
- int romfsSizeB = (int)(partitionHeader.RomFSSizeInMediaUnits * header.SectorSize) % (1024 * 1024);
+ int romfsSizeM = (int)(partitionHeader.RomFSSizeInMediaUnits * header.MediaUnitSize) / (1024 * 1024);
+ int romfsSizeB = (int)(partitionHeader.RomFSSizeInMediaUnits * header.MediaUnitSize) % (1024 * 1024);
// Encrypting RomFS for partitions 1 and up always use Key0x2C
if (encrypt && partitionNumber > 0)
@@ -529,8 +525,8 @@ namespace ThreeDS
var cipher = CreateAESCipher(NormalKey, partitionHeader.RomFSIV, encrypt);
- reader.BaseStream.Seek((header.PartitionsTable[partitionNumber].Offset + partitionHeader.RomFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
- writer.BaseStream.Seek((header.PartitionsTable[partitionNumber].Offset + partitionHeader.RomFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
+ reader.BaseStream.Seek((header.PartitionsTable[partitionNumber].Offset + partitionHeader.RomFSOffsetInMediaUnits) * header.MediaUnitSize, SeekOrigin.Begin);
+ writer.BaseStream.Seek((header.PartitionsTable[partitionNumber].Offset + partitionHeader.RomFSOffsetInMediaUnits) * header.MediaUnitSize, SeekOrigin.Begin);
if (romfsSizeM > 0)
{
for (int i = 0; i < romfsSizeM; i++)