diff --git a/.gitattributes b/.gitattributes index 6706d81..558052d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -33,12 +33,17 @@ WiiQt/uidmap.cpp -text WiiQt/uidmap.h -text WiiQt/wad.cpp -text WiiQt/wad.h -text -nandExtract/includes.h -text +nandExtract/black.png -text +nandExtract/blue.png -text +nandExtract/green.png -text +nandExtract/grey.png -text nandExtract/main.cpp -text nandExtract/nandExtract.pro -text nandExtract/nandwindow.cpp -text nandExtract/nandwindow.h -text nandExtract/nandwindow.ui -text +nandExtract/pink.png -text +nandExtract/rc.qrc -text nandExtract/readmii.txt -text nand_dump/main.cpp -text nand_dump/mainwindow.cpp -text 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 0000000..060039d Binary files /dev/null and b/nandExtract/black.png differ diff --git a/nandExtract/blue.png b/nandExtract/blue.png new file mode 100755 index 0000000..974f56e Binary files /dev/null and b/nandExtract/blue.png differ diff --git a/nandExtract/green.png b/nandExtract/green.png new file mode 100755 index 0000000..e83e3b9 Binary files /dev/null and b/nandExtract/green.png differ diff --git a/nandExtract/grey.png b/nandExtract/grey.png new file mode 100755 index 0000000..803096b Binary files /dev/null and b/nandExtract/grey.png differ 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 0000000..36afc61 Binary files /dev/null and b/nandExtract/pink.png differ 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 + +