From cc7d47f3ab2e382da683ec944f88e462c1613812 Mon Sep 17 00:00:00 2001 From: "giantpune@gmail.com" Date: Sat, 11 Dec 2010 10:12:50 +0000 Subject: [PATCH] * add access functions to the nandBin class to get the cluster map for whole nand and for a given file * change the nandExtract program a bit. move nand extraction to a thread to keep it from hanging the main thread * add pretty block map git-svn-id: http://wiiqt.googlecode.com/svn/trunk@15 389f4c8b-5dfe-645f-db0e-df882bc27289 --- WiiQt/nandbin.cpp | 56 +++++++-- WiiQt/nandbin.h | 15 +++ nandExtract/black.png | Bin 0 -> 2913 bytes nandExtract/blue.png | Bin 0 -> 2979 bytes nandExtract/green.png | Bin 0 -> 2917 bytes nandExtract/grey.png | Bin 0 -> 2926 bytes nandExtract/includes.h | 35 ------ nandExtract/main.cpp | 2 + nandExtract/nandExtract.pro | 9 +- nandExtract/nandwindow.cpp | 236 ++++++++++++++++++++++++++++++++---- nandExtract/nandwindow.h | 29 +++-- nandExtract/nandwindow.ui | 50 ++++++++ nandExtract/pink.png | Bin 0 -> 2939 bytes nandExtract/rc.qrc | 9 ++ 14 files changed, 362 insertions(+), 79 deletions(-) create mode 100755 nandExtract/black.png create mode 100755 nandExtract/blue.png create mode 100755 nandExtract/green.png create mode 100755 nandExtract/grey.png delete mode 100755 nandExtract/includes.h create mode 100755 nandExtract/pink.png create mode 100644 nandExtract/rc.qrc diff --git a/WiiQt/nandbin.cpp b/WiiQt/nandbin.cpp index 914ee19..cd39ac4 100755 --- a/WiiQt/nandbin.cpp +++ b/WiiQt/nandbin.cpp @@ -5,6 +5,7 @@ NandBin::NandBin( QObject * parent, const QString &path ) : QObject( parent ) { type = -1; fatNames = false; + fstInited = false; root = NULL; if( !path.isEmpty() ) @@ -22,6 +23,7 @@ NandBin::~NandBin() bool NandBin::SetPath( const QString &path ) { + fstInited = false; //nandPath = path; if( f.isOpen() ) f.close(); @@ -160,7 +162,10 @@ bool NandBin::ExtractDir( fst_t fst, QString parent ) QFileInfo fi( parent + "/" + filename ); if( filename != "/" && !fi.exists() && !QDir().mkpath( fi.absoluteFilePath() ) ) + { + emit SendError( tr( "Can\'t create directory \"%1\"" ).arg( fi.absoluteFilePath() ) ); return false; + } if( fst.sub != 0xffff && !ExtractFST( fst.sub, fi.absoluteFilePath() ) ) return false; @@ -179,22 +184,18 @@ bool NandBin::ExtractFile( fst_t fst, QString parent ) if( fst.size && !data.size() )//dont worry if files dont have anything in them anyways return false; - QFile out( fi.absoluteFilePath() ); - if( out.exists() )// !! delete existing files - out.remove(); - - if( !out.open( QIODevice::WriteOnly ) ) + if( !WriteFile( fi.absoluteFilePath(), data ) ) { - emit SendError( tr( "Can't open \"%1\" for writing" ).arg( fi.absoluteFilePath() ) ); + emit SendError( tr( "Error writing \"%1\"" ).arg( fi.absoluteFilePath() ) ); return false; } - out.write( data ); - out.close(); return true; } bool NandBin::InitNand( QIcon dirs, QIcon files ) { + fstInited = false; + fats.clear(); type = GetDumpType( f.size() ); if( type < 0 || type > 3 ) return false; @@ -211,6 +212,16 @@ bool NandBin::InitNand( QIcon dirs, QIcon files ) loc_fat = loc_super; loc_fst = loc_fat + 0x0C + n_fatlen[ type ]; + //cache all the entries + for( quint16 i = 0; i < 0x17ff; i++ ) + fsts[ i ] = GetFST( i ); + + //cache all the fats + for( quint16 i = 0; i < 0x8000; i++ ) + fats << GetFAT( i ); + + fstInited = true; + if( root ) delete root; @@ -220,7 +231,7 @@ bool NandBin::InitNand( QIcon dirs, QIcon files ) root = new QTreeWidgetItem( QStringList() << nandPath ); AddChildren( root, 0 ); - ShowInfo(); + //ShowInfo(); return true; } @@ -349,6 +360,11 @@ fst_t NandBin::GetFST( quint16 entry ) fst.filename[ 0 ] = '\0'; return fst; } + if( fstInited )//we've already read this once, just give back the one we already know + { + //qDebug() << "reading from cache" << hex << entry; + return fsts[ entry ]; + } // compensate for 64 bytes of ecc data every 64 fst entries quint32 n_fst[] = { 0, 2, 2 }; int loc_entry = ( ( ( entry / 0x40 ) * n_fst[ type ] ) + entry ) * 0x20; @@ -390,6 +406,8 @@ fst_t NandBin::GetFST( quint16 entry ) quint16 NandBin::GetFAT( quint16 fat_entry ) { + if( fstInited ) + return fats.at( fat_entry ); /* * compensate for "off-16" storage at beginning of superblock * 53 46 46 53 XX XX XX XX 00 00 00 00 @@ -508,6 +526,26 @@ QByteArray NandBin::GetFile( fst_t fst ) return data; } +const QList NandBin::GetFatsForFile( quint16 i ) +{ + //qDebug() << "NandBin::GetFatsForFile" << i; + QList ret; + fst_t fst = GetFST( i ); + + if( fst.filename[ 0 ] == '\0' ) + return ret; + + quint16 fat = fst.sub; + quint16 j = 0;//just to make sure a broken nand doesnt lead to an endless loop + while ( fat < 0x8000 && fat > 0 && ++j ) + { + ret << fat; + fat = GetFAT( fat ); + } + + return ret; +} + void NandBin::SetFixNamesForFAT( bool fix ) { fatNames = fix; diff --git a/WiiQt/nandbin.h b/WiiQt/nandbin.h index 4f90a1c..2a19781 100755 --- a/WiiQt/nandbin.h +++ b/WiiQt/nandbin.h @@ -78,6 +78,12 @@ public: //ie... /title/00000001/00000002/data/setting.txt const QByteArray GetData( const QString &path ); + //returns the fats for this nand. + const QList GetFats() { return fats; } + + //get the fats for a given file + const QList GetFatsForFile( quint16 i ); + private: QByteArray key; @@ -93,6 +99,15 @@ private: QIcon groupIcon; QIcon keyIcon; + //read all the fst and remember them rather than seeking back and forth in the file all the time + // uses ~120KiB RAM + bool fstInited; + fst_t fsts[ 0x17ff ]; + + //cache the fat to keep from having to look them up repeatedly + // uses ~64KiB + QListfats; + int GetDumpType( quint64 fileSize ); bool GetKey( int type ); QByteArray ReadKeyfile( QString path ); diff --git a/nandExtract/black.png b/nandExtract/black.png new file mode 100755 index 0000000000000000000000000000000000000000..060039de976dca86cffd8d198494e634970d94b6 GIT binary patch literal 2913 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 LNkvXXu0mjfHBM`T literal 0 HcmV?d00001 diff --git a/nandExtract/blue.png b/nandExtract/blue.png new file mode 100755 index 0000000000000000000000000000000000000000..974f56ee0d29eb67373813a45c0162661a4566ce GIT binary patch literal 2979 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 Ze*iRar^C$_Vw?Z~002ovPDHLkV1jM!jcot` literal 0 HcmV?d00001 diff --git a/nandExtract/green.png b/nandExtract/green.png new file mode 100755 index 0000000000000000000000000000000000000000..e83e3b96e707d125316e02f6d32abd026103e655 GIT binary patch literal 2917 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 Y00V}q_P_FfSO5S307*qoM6N<$g7dz2WdHyG literal 0 HcmV?d00001 diff --git a/nandExtract/includes.h b/nandExtract/includes.h deleted file mode 100755 index 8723e2e..0000000 --- a/nandExtract/includes.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef INCLUDES_H -#define INCLUDES_H - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -//#include -//#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -//#include -//#include - - -#endif // INCLUDES_H diff --git a/nandExtract/main.cpp b/nandExtract/main.cpp index dd679f9..e207aa8 100755 --- a/nandExtract/main.cpp +++ b/nandExtract/main.cpp @@ -3,6 +3,8 @@ int main(int argc, char *argv[]) { + Q_INIT_RESOURCE( rc ); + QApplication a(argc, argv); NandWindow w; w.show(); diff --git a/nandExtract/nandExtract.pro b/nandExtract/nandExtract.pro index 5d4798d..0d95f71 100755 --- a/nandExtract/nandExtract.pro +++ b/nandExtract/nandExtract.pro @@ -9,10 +9,15 @@ SOURCES += main.cpp \ ../WiiQt/tools.cpp \ ../WiiQt/savebanner.cpp \ ../WiiQt/aes.c \ - ../WiiQt/sha1.c + ../WiiQt/sha1.c \ + nandthread.cpp HEADERS += nandwindow.h \ ../WiiQt/nandbin.h \ - ../WiiQt/tools.h + ../WiiQt/tools.h \ + nandthread.h FORMS += nandwindow.ui + +RESOURCES += \ + rc.qrc diff --git a/nandExtract/nandwindow.cpp b/nandExtract/nandwindow.cpp index acb4772..3c00a2a 100755 --- a/nandExtract/nandwindow.cpp +++ b/nandExtract/nandwindow.cpp @@ -1,12 +1,38 @@ #include "nandwindow.h" #include "ui_nandwindow.h" -NandWindow::NandWindow(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::NandWindow), - nandBin( this ) +NandWindow::NandWindow( QWidget *parent ) : QMainWindow( parent ), ui( new Ui::NandWindow ), nThread( this ) { - ui->setupUi(this); + ui->setupUi( this ); + freeSpace = 0; + + //setup the block map + ui->graphicsView_blocks->setScene( &gv ); + ui->graphicsView_blocks->setAlignment( Qt::AlignRight ); + ui->graphicsView_blocks->setRenderHint( QPainter::Antialiasing ); + + QPixmap blue( ":/blue.png" ); + QPixmap green( ":/green.png" ); + QPixmap pink( ":/pink.png" ); + QPixmap grey( ":/grey.png" ); + + quint16 i = 0; + for( quint16 y = 0; y < 352; y += 11 ) + { + for( quint16 x = 0; x < 1408; x += 11 ) + { + pmi[ i ] = new QGraphicsPixmapItem( grey ); + pmi[ i ]->setPos( x, y ); + gv.addItem( pmi[ i ] );//items belong to this now. no need to delete them + + i++; + } + } + + //put the progressbar on the status bar + ui->progressBar->setVisible( false ); + ui->statusBar->addPermanentWidget( ui->progressBar, 0 ); + //ui->treeWidget->header()->resizeSection( 0, 300 );//name QFontMetrics fm( fontMetrics() ); ui->treeWidget->header()->resizeSection( 0, fm.width( QString( 22, 'W' ) ) );//name @@ -19,8 +45,12 @@ NandWindow::NandWindow(QWidget *parent) : ui->treeWidget->header()->resizeSection( 7, fm.width( "WWWWW" ) );//attr - connect( &nandBin, SIGNAL( SendError( QString ) ), this, SLOT( GetError( QString ) ) ); - connect( &nandBin, SIGNAL( SendText( QString ) ), this, SLOT( GetStatusUpdate( QString ) ) ); + //connect( &nandBin, SIGNAL( SendError( QString ) ), this, SLOT( GetError( QString ) ) ); + //connect( &nandBin, SIGNAL( SendText( QString ) ), this, SLOT( GetStatusUpdate( QString ) ) ); + connect( &nThread, SIGNAL( SendError( QString ) ), this, SLOT( GetError( QString ) ) ); + connect( &nThread, SIGNAL( SendText( QString ) ), this, SLOT( GetStatusUpdate( QString ) ) ); + connect( &nThread, SIGNAL( SendExtractDone() ), this, SLOT( ThreadIsDone() ) ); + connect( &nThread, SIGNAL( SendProgress( int ) ), ui->progressBar, SLOT( setValue( int ) ) ); } NandWindow::~NandWindow() @@ -28,6 +58,13 @@ NandWindow::~NandWindow() delete ui; } +void NandWindow::ThreadIsDone() +{ + qDebug() << "NandWindow::ThreadIsDone"; + ui->progressBar->setVisible( false ); + ui->statusBar->showMessage( "Done extracting", 5000 ); +} + void NandWindow::changeEvent(QEvent *e) { QMainWindow::changeEvent(e); @@ -47,17 +84,10 @@ void NandWindow::GetStatusUpdate( QString s ) void NandWindow::GetError( QString str ) { + QMessageBox::warning( this, tr( "Error" ), str, QMessageBox::Ok ); qWarning() << str; } -void NandWindow::ExtractShit() -{ - ui->statusBar->showMessage( "Trying to extract..." ); - nandBin.ExtractToDir( exItem, exPath );//who cares if it returns false? not me. thats what the qDebug() info is for - ui->statusBar->showMessage( "Done", 5000 ); - -} - //nand window right-clicked void NandWindow::on_treeWidget_customContextMenuRequested( QPoint pos ) { @@ -84,11 +114,8 @@ void NandWindow::on_treeWidget_customContextMenuRequested( QPoint pos ) if( path.isEmpty() ) return; - exPath = path; - exItem = item; - - //ghetto, but gives the dialog box time to dissappear before the gui freezes as it extracts the nand - QTimer::singleShot( 250, this, SLOT( ExtractShit() ) ); + ui->progressBar->setVisible( true ); + nThread.Extract( item, path ); } } } @@ -100,7 +127,8 @@ void NandWindow::on_actionOpen_Nand_triggered() if( path.isEmpty() ) return; - if( !nandBin.SetPath( path ) ) + blocks.clear(); + if( !nThread.SetPath( path ) ) { qDebug() << " error in nandBin.SetPath"; ui->statusBar->showMessage( "Error setting path to " + path ); @@ -112,7 +140,8 @@ void NandWindow::on_actionOpen_Nand_triggered() groupIcon.addPixmap( style()->standardPixmap( QStyle::SP_DirClosedIcon ), QIcon::Normal, QIcon::Off ); groupIcon.addPixmap( style()->standardPixmap( QStyle::SP_DirOpenIcon ), QIcon::Normal, QIcon::On ); keyIcon.addPixmap( style()->standardPixmap( QStyle::SP_FileIcon ) ); - if( !nandBin.InitNand( groupIcon, keyIcon ) ) + + if( !nThread.InitNand( groupIcon, keyIcon ) ) { qDebug() << " error in nandBin.InitNand()"; ui->statusBar->showMessage( "Error reading " + path ); @@ -120,9 +149,12 @@ void NandWindow::on_actionOpen_Nand_triggered() } ui->treeWidget->clear(); + GetBlocksfromNand(); + DrawBlockMap(); //get an item holding a tree with all the items of the nand - QTreeWidgetItem* tree = nandBin.GetTree(); + //QTreeWidgetItem* tree = nandBin.GetTree(); + QTreeWidgetItem* tree = nThread.GetTree(); //take the actual contents of the nand from the made up root and add them to the gui widget ui->treeWidget->addTopLevelItems( tree->takeChildren() ); @@ -135,6 +167,160 @@ void NandWindow::on_actionOpen_Nand_triggered() ui->treeWidget->topLevelItem( 0 )->setExpanded( true ); ui->statusBar->showMessage( "Loaded " + path, 5000 ); - - //nandBin.GetData( "/title/00000001/00000002/data/setting.txt" );//testing 1,2,1,2 +} + +void NandWindow::GetBlocksfromNand() +{ + blocks.clear(); + freeSpace = 0; + + QList clusters = nThread.GetFats(); + if( !clusters.size() == 0x8000 ) + { + QMessageBox::warning( this, tr( "Error" ), tr( "Expected 0x8000 clusters from the nand, but got %1 instead!" ).arg( clusters.size(), 0, 16 ), QMessageBox::Ok ); + return; + } + //QString str; + for( quint16 i = 0; i < 0x8000; i += 8 )//first cluster of each block. + { + quint16 thisBlock = clusters.at( i ); + + //str += QString( "%1 " ).arg( thisBlock, 4, 16 ); + if( thisBlock == 0xFFFC + || thisBlock == 0xFFFD ) + { + //qDebug() << "adding" << hex << thisBlock; + blocks << thisBlock; + continue; + } + bool used = 0; + for( quint16 j = i; j < i + 8; j++ )//each individual cluster + { + thisBlock = clusters.at( i ); + if( thisBlock == 0xFFFE ) + freeSpace += 0x800; + + else used = true; + } + blocks << ( used ? 1 : 0xfffe ); // just put 1 for used blocks + } + +} + +QList NandWindow::ToBlocks( QList clusters ) +{ + //qDebug() << "NandWindow::ToBlocks" << clusters; + QList ret; + quint16 size = clusters.size(); + for( quint16 i = 0; i < size; i++ ) + { + quint16 block = ( clusters.at( i ) / 8 ); + if( !ret.contains( block ) ) + ret << block; + } + return ret; +} + +//expects blocks, not clusters!! +void NandWindow::DrawBlockMap( QList newFile ) +{ + if( blocks.size() != 0x1000 ) + { + qWarning() << "NandWindow::DrawBlockMap -> current blocks are fucked up, son" << hex << blocks.size(); + return; + } + QPixmap blue( ":/blue.png" ); + QPixmap green( ":/green.png" ); + QPixmap pink( ":/pink.png" ); + QPixmap grey( ":/grey.png" ); + QPixmap black( ":/black.png" ); + + for( quint16 i = 0; i < 0x1000; i++ ) + { + quint16 thisBlock; + if( !newFile.contains( i ) ) + { + thisBlock = blocks.at( i ); + } + else + { + thisBlock = 2; + } + switch( thisBlock ) + { + default: + case 1://used, but not in this file + pmi[ i ]->setPixmap( green ); + break; + case 2://used in this file + pmi[ i ]->setPixmap( pink ); + break; + case 0xFFFE://free block + pmi[ i ]->setPixmap( grey ); + break; + case 0xFFFC://reserved + pmi[ i ]->setPixmap( blue ); + break; + case 0xFFFD: // bad block + pmi[ i ]->setPixmap( black ); + break; + } + } + + +} + +//show nand usage +void NandWindow::on_actionShow_Usage_triggered() +{ + //128x32 + /*QList clusters = nThread.GetFats();//each of these is 0x4000 bytes ( neglecting the ecc ) + quint16 badBlocks = 0; + quint16 reserved = 0; + quint16 freeBlocks = 0; + QListbadOnes; + for( quint16 i = 0; i < 0x8000; i++ ) + { + quint16 fat = GetFAT( i ); + if( 0xfffc == fat ) + reserved++; + else if( 0xfffd == fat ) + { + badBlocks++; + if( i % 8 == 0 ) + { + badOnes << ( i / 8 ); + } + } + else if( 0xfffe == fat ) + freeBlocks++; + } + if( badBlocks ) + badBlocks /= 8; + + if( reserved ) + reserved /= 8; + + if( freeBlocks ) + freeBlocks /= 8;*/ +} + +//some item in the nand tree was clicked +void NandWindow::on_treeWidget_currentItemChanged( QTreeWidgetItem* current, QTreeWidgetItem* previous ) +{ + Q_UNUSED( previous ); + + if( !current || current->text( 6 ) == "00" ) + return; + bool ok = false; + quint16 entry = current->text( 1 ).toInt( &ok ); + if( !ok ) + { + qDebug() << "NandWindow::on_treeWidget_currentItemChanged ->" << current->text( 1 ) << "isnt a decimal number"; + return; + } + + QList blocks = ToBlocks( nThread.GetFatsForFile( entry ) ); + //qDebug() << "blocks for" << current->text( 0 ) << blocks; + DrawBlockMap( blocks ); } diff --git a/nandExtract/nandwindow.h b/nandExtract/nandwindow.h index 6a2509c..8df0f59 100755 --- a/nandExtract/nandwindow.h +++ b/nandExtract/nandwindow.h @@ -1,8 +1,10 @@ #ifndef NANDWINDOW_H #define NANDWINDOW_H -#include "includes.h" -#include "../WiiQt/nandbin.h" +#include "../WiiQt/includes.h" +//#include "../WiiQt/nandbin.h" + +#include "nandthread.h" namespace Ui { class NandWindow; @@ -20,21 +22,32 @@ protected: private: Ui::NandWindow *ui; - NandBin nandBin; + NandThread nThread; + + //changes the blocks in the blockmap with different colors + void DrawBlockMap( QList newFile = QList() ); + QGraphicsScene gv;//scene to hold the blockmap images + QGraphicsPixmapItem *pmi[ 0x1000 ];//pointers to all the blocks in the display + //quint16 ClusterToBlock( quint16 cluster ); + + QList blocks; + quint32 freeSpace; + void GetBlocksfromNand(); + + QList ToBlocks( QList clusters ); + - //just put these here and create a slot to make the GUI not - //look so ugly when it hangs while stuff is extracting - QString exPath; - QTreeWidgetItem* exItem; public slots: void GetError( QString ); void GetStatusUpdate( QString ); + void ThreadIsDone(); private slots: + void on_treeWidget_currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous); + void on_actionShow_Usage_triggered(); void on_actionOpen_Nand_triggered(); void on_treeWidget_customContextMenuRequested(QPoint pos); - void ExtractShit(); }; #endif // NANDWINDOW_H diff --git a/nandExtract/nandwindow.ui b/nandExtract/nandwindow.ui index 3d101ef..a1d6b94 100755 --- a/nandExtract/nandwindow.ui +++ b/nandExtract/nandwindow.ui @@ -15,6 +15,12 @@ + + 2 + + + 2 + @@ -62,6 +68,35 @@ + + + + false + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + QPainter::Antialiasing|QPainter::HighQualityAntialiasing|QPainter::SmoothPixmapTransform|QPainter::TextAntialiasing + + + QGraphicsView::CacheBackground + + + + + + + + 150 + 16777215 + + + + 24 + + + @@ -79,7 +114,14 @@ + + + Info + + + + @@ -98,6 +140,14 @@ Ctrl+O + + + Show Usage... + + + Ctrl+U + + diff --git a/nandExtract/pink.png b/nandExtract/pink.png new file mode 100755 index 0000000000000000000000000000000000000000..36afc61701f15260a450fcdcd9b8999a0cdde2e0 GIT binary patch literal 2939 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 literal 0 HcmV?d00001 diff --git a/nandExtract/rc.qrc b/nandExtract/rc.qrc new file mode 100644 index 0000000..a654d98 --- /dev/null +++ b/nandExtract/rc.qrc @@ -0,0 +1,9 @@ + + + blue.png + green.png + grey.png + pink.png + black.png + +