private void ScanForBadBlocks(bool throwException = true, uint blocksize = 0x4000, uint nextBlock = 0x41F0) { if (!HasSpare || MetaType == NANDSpare.MetaType.MetaTypeUnInitialized) { if (throwException) { throw new NotSupportedException(); } return; } _badBlocks.Clear(); RawSeek(0x200, SeekOrigin.Begin); // Seek to first page spare data... var tBlocks = Length / blocksize; for (uint block = 0; block < tBlocks; block++) { var meta = NANDSpare.GetMetaData(RawReadBytes(0x10), MetaType); if (NANDSpare.CheckIsBadBlock(meta)) { _badBlocks.Add(block); if (Main.VerifyVerbosityLevel(Main.VerbosityLevels.High)) { Main.SendInfo("{1}BadBlock Marker detected @ block 0x{0:X}", block, Environment.NewLine); } } RawSeek(nextBlock, SeekOrigin.Current); } _badBlocksScanned = true; }
public SmartNANDReader(string file, bool fastScan = false) { if (Main.VerifyVerbosityLevel(Main.VerbosityLevels.Debug)) { Main.SendInfo("Creating SmartNANDReader for {0}{1}", file, Environment.NewLine); } _binaryReader = new BinaryReader(File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read)); if (Main.VerifyVerbosityLevel(Main.VerbosityLevels.Debug)) { Main.SendInfo("Verifying Magic Bytes... "); } if (!VerifyMagic()) { if (Main.VerifyVerbosityLevel(Main.VerbosityLevels.Debug)) { Main.SendInfo("Failed!{0}", Environment.NewLine); } throw new X360UtilsException(X360UtilsException.X360UtilsErrors.BadMagic); } if (Main.VerifyVerbosityLevel(Main.VerbosityLevels.Debug)) { Main.SendInfo("OK!{0}", Environment.NewLine); } if (Main.VerifyVerbosityLevel(Main.VerbosityLevels.Debug)) { Main.SendInfo("Checking for spare... "); } HasSpare = CheckForSpare(); if (Main.VerifyVerbosityLevel(Main.VerbosityLevels.Debug)) { Main.SendInfo(HasSpare ? "Spare detected!{0}" : "No Spare detected!{0}", Environment.NewLine); } if (HasSpare) { Main.SendMaxBlocksChanged((int)(_binaryReader.BaseStream.Length / 0x4200)); _doSendPosition = true; if (Main.VerifyVerbosityLevel(Main.VerbosityLevels.Debug)) { Main.SendInfo("Detecting spare type... "); } MetaType = NANDSpare.DetectSpareType(this); if (Main.VerifyVerbosityLevel(Main.VerbosityLevels.Debug)) { Main.SendInfo("{0}{1}", MetaType, Environment.NewLine); } if (fastScan) { return; } if (Main.VerifyVerbosityLevel(Main.VerbosityLevels.Debug)) { Main.SendInfo("Scanning for FSRoot/Mobile entries"); } ScanForFsRootAndMobiles(); return; } Main.SendMaxBlocksChanged((int)(_binaryReader.BaseStream.Length / 0x4000)); _doSendPosition = true; MetaType = NANDSpare.MetaType.MetaTypeNone; }
internal MobileEntry(long offset, ref NANDSpare.MetaData meta) { Offset = offset; _rawOffset = (offset / 0x200) * 0x210; Version = NANDSpare.GetFsSequence(ref meta); MobileType = NANDSpare.GetBlockType(ref meta); Size = NANDSpare.GetFsSize(ref meta); }
private static long GetBaseBlockForMeta2(ref NANDReader reader) { reader.RawSeek(reader.FsRoot.Offset / 0x200 * 0x210 + 0x200, SeekOrigin.Begin); var meta = NANDSpare.GetMetaData(reader.RawReadBytes(0x10)); var reserved = 0x1E0; reserved -= meta.Meta2.FsPageCount; reserved -= meta.Meta2.FsSize0 << 2; return(reserved * 8); }
private bool CheckForSpare() { RawSeek(0, SeekOrigin.Begin); var tmp = _binaryReader.ReadBytes(0x630); RawSeek(0, SeekOrigin.Begin); var ret = true; for (var i = 0; i < tmp.Length; i += 0x210) { if (!NANDSpare.CheckPageEcd(ref tmp, i)) { ret = false; } } return(ret); }
public void SeekToLbaEx(uint lba) { Lba = lba; if (_badBlocks.Contains(lba)) { var block = MetaType == NANDSpare.MetaType.MetaType2 ? 0xFFF : 0x3FF; while (true) { NANDSpare.MetaData meta; switch (MetaType) { case NANDSpare.MetaType.MetaType0: case NANDSpare.MetaType.MetaType1: Seek(block * 0x4000, SeekOrigin.Begin); RawSeek(0x200, SeekOrigin.Current); meta = NANDSpare.GetMetaData(RawReadBytes(0x10), MetaType); if (NANDSpare.GetLba(ref meta) == lba) { Seek(block * 0x4000, SeekOrigin.Begin); return; } break; case NANDSpare.MetaType.MetaType2: Seek(block * 0x4000, SeekOrigin.Begin); RawSeek(0x200, SeekOrigin.Current); meta = NANDSpare.GetMetaData(RawReadBytes(0x10), MetaType); if (NANDSpare.GetLba(ref meta) == lba / 8) { Seek(block * 0x4000 + ((lba % 8) * 0x4000), SeekOrigin.Begin); return; } break; default: Seek(lba * 0x4000, SeekOrigin.Begin); return; } block--; } } Seek(MetaType == NANDSpare.MetaType.MetaType2 ? ((lba / 8) * 0x20000) + ((lba % 8) * 0x4000) : lba * 0x4000, SeekOrigin.Begin); }
public long[] FindBadBlocks(bool forceSb = false) { if (!HasSpare || MetaType == NANDSpare.MetaType.MetaTypeUnInitialized) { throw new NotSupportedException(); } if (_forcedSb && !forceSb || !_forcedSb && forceSb) { _badBlocks.Clear(); } if (_badBlocks.Count > 0) { return(_badBlocks.ToArray()); } _forcedSb = forceSb; _badBlocks.Clear(); RawSeek(0x200, SeekOrigin.Begin); // Seek to first page spare data... var totalBlocks = Length / (MetaType == NANDSpare.MetaType.MetaType2 ? (!forceSb ? 0x20000 : 0x4000) : 0x4000); for (var block = 0; block < totalBlocks; block++) { var spare = RawReadBytes(0x10); if (NANDSpare.CheckIsBadBlockSpare(ref spare, MetaType)) { if (Main.VerifyVerbosityLevel(1)) { Main.SendInfo("{1}BadBlock Marker detected @ block 0x{0:X}", block, Environment.NewLine); } _badBlocks.Add(block); } RawSeek(MetaType == NANDSpare.MetaType.MetaType2 ? (!forceSb ? 0x20FF0 : 0x41F0) : 0x41F0, SeekOrigin.Current); } if (Main.VerifyVerbosityLevel(1)) { Main.SendInfo(Environment.NewLine); } if (_badBlocks.Count > 0) { return(_badBlocks.ToArray()); } throw new X360UtilsException(X360UtilsException.X360UtilsErrors.DataNotFound); }
public void SeekToLba(uint lba) { if (_badBlocks.Contains(lba)) { var block = 0; while (true) { NANDSpare.MetaData meta; switch (MetaType) { case NANDSpare.MetaType.MetaType0: case NANDSpare.MetaType.MetaType1: Seek((lba * 0x4000) - block * 0x4000, SeekOrigin.Begin); RawSeek(0x200, SeekOrigin.Current); meta = NANDSpare.GetMetaData(RawReadBytes(0x10), MetaType); if (NANDSpare.GetLba(ref meta) == lba) { Seek((lba * 0x4000) - block * 0x4000, SeekOrigin.Begin); return; } break; case NANDSpare.MetaType.MetaType2: Seek((lba * 0x20000) - block * 0x20000, SeekOrigin.Begin); RawSeek(0x200, SeekOrigin.Current); meta = NANDSpare.GetMetaData(RawReadBytes(0x10), MetaType); if (NANDSpare.GetLba(ref meta) == lba) { Seek((lba * 0x20000) - block * 0x20000, SeekOrigin.Begin); return; } break; default: Seek(lba * 0x4000, SeekOrigin.Begin); return; } block++; } } Seek(lba * (MetaType != NANDSpare.MetaType.MetaType2 ? 0x4000 : 0x20000), SeekOrigin.Begin); }
public void ScanForFsRootAndMobile() { if (FsRootEntries.Count > 0) { return; } if (!HasSpare) { #region MMC (No Spare) if (Length < 0x2FF0000) { throw new X360UtilsException(X360UtilsException.X360UtilsErrors.DataTooSmall); } Seek(0x2FE8018, SeekOrigin.Begin); // Seek to MMC Anchor number offset var ver1 = BitOperations.Swap(BitConverter.ToUInt32(ReadBytes(4), 0)); Seek(0x2FEC018, SeekOrigin.Begin); // Seek to MMC Anchor number offset var ver2 = BitOperations.Swap(BitConverter.ToUInt32(ReadBytes(4), 0)); if (ver1 == 0 || ver2 == 0) { throw new X360UtilsException(X360UtilsException.X360UtilsErrors.DataNotFound); } Seek(ver1 > ver2 ? 0x2FE8000 : 0x2FEC000, SeekOrigin.Begin); // Seek to MMC Anchor Block Offset var buf = ReadBytes(0x4000); // We want the first anchor buffer FsRootEntries.Add(new FsRootEntry(NANDSpare.GetMmcMobileBlock(ref buf, 0) * 0x4000, 0, true)); for (byte i = 0x31; i < 0x3F; i++) { var size = NANDSpare.GetMmcMobileSize(ref buf, i); MobileEntries.Add(new MobileEntry(NANDSpare.GetMmcMobileBlock(ref buf, i) * 0x4000, 0, size > 0 ? size : 0x4000, i)); } #endregion } else { #region NAND (With Spare) var maximumOffset = BitOperations.GetSmallest(_binaryReader.BaseStream.Length, 0x4200000); // Only read the filesystem area of BB NANDs (for faster processing) #region FSRoot RawSeek(0x8600, SeekOrigin.Begin); //Seek to block 3 page 0 on small block for (; _binaryReader.BaseStream.Position < maximumOffset - 0x10;) { var meta = NANDSpare.GetMetaData(_binaryReader.ReadBytes(0x10), MetaType); if (NANDSpare.PageIsFsRoot(ref meta)) { Debug.SendDebug("FSRoot found @ 0x{0:X} version: {1}", Position - 0x200, NANDSpare.GetFsSequence(ref meta)); FsRootEntries.Add(new FsRootEntry(Position - 0x200, NANDSpare.GetFsSequence(ref meta))); RawSeek(0x41f0, SeekOrigin.Current); // Seek to the next small block } else { if (NANDSpare.IsMobilePage(ref meta)) { Debug.SendDebug("Mobile found @ 0x{0:X} version: {1}", Position - 0x200, NANDSpare.GetFsSequence(ref meta)); MobileEntries.Add(new MobileEntry(Position - 0x200, ref meta)); } for (var i = 0; i < 31; i++) { RawSeek(0x200, SeekOrigin.Current); meta = NANDSpare.GetMetaData(_binaryReader.ReadBytes(0x10), MetaType); if (NANDSpare.IsMobilePage(ref meta)) { Debug.SendDebug("Mobile found @ 0x{0:X} version: {1}", Position - 0x200, NANDSpare.GetFsFreePages(ref meta)); MobileEntries.Add(new MobileEntry(Position - 0x200, ref meta)); } } RawSeek(0x200, SeekOrigin.Current); } } #endregion #region Mobile*.dat RawSeek(0x8600, SeekOrigin.Begin); //Seek to block 3 page 0 on small block for (; _binaryReader.BaseStream.Position < maximumOffset - 0x10;) { var meta = NANDSpare.GetMetaData(_binaryReader.ReadBytes(0x10), MetaType); if (NANDSpare.PageIsFsRoot(ref meta)) { RawSeek(0x41f0, SeekOrigin.Current); // Seek to the next small block continue; // Skip this one } if (NANDSpare.IsMobilePage(ref meta)) { Debug.SendDebug("Mobile found @ 0x{0:X} version: {1}", Position - 0x200, NANDSpare.GetFsSequence(ref meta)); MobileEntries.Add(new MobileEntry(Position - 0x200, ref meta)); var size = NANDSpare.GetFsSize(ref meta); RawSeek(size / 0x200 * 0x210 - 0x10, SeekOrigin.Current); if (size % 0x200 > 0) // There's data still to be saved... { RawSeek(0x210, SeekOrigin.Current); // Seek 1 page } while (Position % 0x800 > 0) // We want to have an even 4 pages! { RawSeek(0x210, SeekOrigin.Current); // Seek 1 page } } else { RawSeek(0x830, SeekOrigin.Current); // Skip 4 pages } } #endregion #endregion } RawSeek(0, SeekOrigin.Begin); //Reset the stream if (FsRootEntries.Count <= 0) { throw new X360UtilsException(X360UtilsException.X360UtilsErrors.DataNotFound); } FindLatestFsRoot(); FillMobileArray(); }
public NANDReader(string file, bool fastScan = false) { Debug.SendDebug("Creating NANDReader for: {0}", file); _binaryReader = new BinaryReader(File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read)); if (!VerifyMagic()) { throw new Exception("Bad Magic"); } if (Main.VerifyVerbosityLevel(1)) { Main.SendInfo("\r\nChecking for spare... "); } HasSpare = CheckForSpare(); if (HasSpare) { if (Main.VerifyVerbosityLevel(1)) { Main.SendInfo("Image has Spare..."); } Main.SendMaxBlocksChanged((int)(_binaryReader.BaseStream.Length / 0x4200)); _doSendPosition = true; if (Main.VerifyVerbosityLevel(1)) { Main.SendInfo("\r\nChecking for MetaType..."); } MetaType = NANDSpare.DetectSpareType(this); if (Main.VerifyVerbosityLevel(1)) { Main.SendInfo("\r\nMetaType: {0}\r\n", MetaType); } if (fastScan) { return; } if (Main.VerifyVerbosityLevel(1)) { Main.SendInfo("Checking for bad blocks..."); } try { FindBadBlocks(); } catch (X360UtilsException ex) { if (ex.ErrorCode != X360UtilsException.X360UtilsErrors.DataNotFound) { throw; } } } else { if (Main.VerifyVerbosityLevel(1)) { Main.SendInfo("Image does NOT have Spare..."); } if (Main.VerifyVerbosityLevel(1)) { Main.SendInfo("\r\n"); } Main.SendMaxBlocksChanged((int)(_binaryReader.BaseStream.Length / 0x4000)); _doSendPosition = true; MetaType = NANDSpare.MetaType.MetaTypeNone; } }
private void SeekToSmallBlock(uint blockLba) { if (MetaType == NANDSpare.MetaType.MetaTypeNone) { RawSeek(blockLba * 0x4000, SeekOrigin.Begin); return; } if (MetaType == NANDSpare.MetaType.MetaTypeUnInitialized) { RawSeek(blockLba * 0x4200, SeekOrigin.Begin); return; } RawSeek(0x200 + (blockLba * 0x4200), SeekOrigin.Begin); // Seek to the first page spare of the block we're looking for var meta = NANDSpare.GetMetaData(RawReadBytes(0x10), MetaType); switch (MetaType) { case NANDSpare.MetaType.MetaType2: if (NANDSpare.GetLba(ref meta) == blockLba / 8) { RawSeek(blockLba * 0x4200, SeekOrigin.Begin); return; } break; default: if (NANDSpare.GetLba(ref meta) == blockLba) { RawSeek(blockLba * 0x4200, SeekOrigin.Begin); return; } break; } #region Find the block starting from the end... var block = LastBlock(); while (true) { RawSeek(0x200 + (block * 0x4200), SeekOrigin.Begin); meta = NANDSpare.GetMetaData(RawReadBytes(0x10), MetaType); switch (MetaType) { case NANDSpare.MetaType.MetaType2: if (NANDSpare.GetLba(ref meta) == blockLba / 8) { RawSeek(block * 0x4200, SeekOrigin.Begin); return; } break; default: if (NANDSpare.GetLba(ref meta) == blockLba) { RawSeek(block * 0x4200, SeekOrigin.Begin); return; } break; } block--; } #endregion }
private void ScanForFsRootAndMobiles() { if (FsRoot != null) { return; } var mobiles = new List <MobileEntry>(); var fsroots = new List <FsRootEntry>(); if (!HasSpare) { #region MMC if (Length < 0x2FF0000) { throw new X360UtilsException(X360UtilsException.X360UtilsErrors.DataTooSmall); } Seek(0x2FE8018, SeekOrigin.Begin); // Seek to MMC Anchor number offset var ver1 = BitOperations.Swap(BitConverter.ToUInt32(ReadBytes(4), 0)); Seek(0x2FEC018, SeekOrigin.Begin); // Seek to MMC Anchor number offset var ver2 = BitOperations.Swap(BitConverter.ToUInt32(ReadBytes(4), 0)); if (ver1 == 0 || ver2 == 0) { throw new X360UtilsException(X360UtilsException.X360UtilsErrors.DataNotFound); } Seek(ver1 > ver2 ? 0x2FE8000 : 0x2FEC000, SeekOrigin.Begin); // Seek to MMC Anchor Block Offset var buf = ReadBytes(0x4000); // We want the first anchor buffer fsroots.Add(new FsRootEntry(NANDSpare.GetMmcMobileBlock(ref buf, 0) * 0x4000, 0, true)); for (byte i = 0x31; i < 0x3F; i++) { var size = NANDSpare.GetMmcMobileSize(ref buf, i); mobiles.Add(new MobileEntry(NANDSpare.GetMmcMobileBlock(ref buf, i) * 0x4000, 0, size > 0 ? size : 0x4000, i)); } #endregion } else { #region NAND var maximumOffset = BitOperations.GetSmallest(_binaryReader.BaseStream.Length, 0x4200000); // Only read the filesystem area of BB NANDs (for faster processing) RawSeek(0x8600, SeekOrigin.Begin); // Seek to Page 0 on Block 3 (SB) Nothing before this will be valid FSRoot... for (; RawPosition < maximumOffset - 0x10;) { var meta = NANDSpare.GetMetaData(RawReadBytes(0x10), MetaType); if (!NANDSpare.PageIsFsRoot(ref meta)) { continue; } if (Main.VerifyVerbosityLevel(Main.VerbosityLevels.Debug)) { Main.SendInfo("FSRoot found @ 0x{0:X} Version: {1}{2}", Position - 0x200, NANDSpare.GetFsSequence(ref meta)); } fsroots.Add(new FsRootEntry(Position - 0x200, NANDSpare.GetFsSequence(ref meta))); } #region Mobile RawSeek(0x8600, SeekOrigin.Begin); //Seek to block 3 page 0 on small block for (; _binaryReader.BaseStream.Position < maximumOffset - 0x10;) { var meta = NANDSpare.GetMetaData(_binaryReader.ReadBytes(0x10), MetaType); if (NANDSpare.PageIsFsRoot(ref meta)) { RawSeek(0x41f0, SeekOrigin.Current); // Seek to the next small block continue; // Skip this one } if (NANDSpare.IsMobilePage(ref meta)) { if (Main.VerifyVerbosityLevel(Main.VerbosityLevels.Debug)) { Main.SendInfo("Mobile found @ 0x{0:X} Version: {1}{2}", Position - 0x200, NANDSpare.GetFsSequence(ref meta)); } mobiles.Add(new MobileEntry(Position - 0x200, ref meta)); var size = NANDSpare.GetFsSize(ref meta); RawSeek(size / 0x200 * 0x210 - 0x10, SeekOrigin.Current); if (size % 0x200 > 0) // There's data still to be saved... { RawSeek(0x210, SeekOrigin.Current); // Seek 1 page } while (Position % 0x800 > 0) // We want to have an even 4 pages! { RawSeek(0x210, SeekOrigin.Current); // Seek 1 page } } else { RawSeek(0x830, SeekOrigin.Current); // Skip 4 pages } } #endregion #endregion } FileSystemEntries = fsroots.ToArray(); FindLatestFsRoot(); FindLatestMobiles(mobiles); }