2010-12-21 21:01:39 +01:00
|
|
|
#include "newnandbin.h"
|
|
|
|
#include "ui_newnandbin.h"
|
2011-01-18 04:03:08 +01:00
|
|
|
#include "../WiiQt/uidmap.h"
|
2010-12-21 21:01:39 +01:00
|
|
|
#include "../WiiQt/tools.h"
|
|
|
|
|
2011-05-28 08:05:34 +02:00
|
|
|
NewNandBin::NewNandBin( QWidget *parent, const QList<quint16> &badBlocks ) : QDialog(parent), ui(new Ui::NewNandBin), nand( this )
|
2010-12-21 21:01:39 +01:00
|
|
|
{
|
2010-12-22 01:25:08 +01:00
|
|
|
dir = QDir::currentPath();
|
2010-12-21 21:01:39 +01:00
|
|
|
ui->setupUi(this);
|
|
|
|
foreach( quint16 block, badBlocks )
|
|
|
|
{
|
2011-05-17 23:18:45 +02:00
|
|
|
QString txt = QString( "%1" ).arg( block );
|
|
|
|
if( ui->listWidget_badBlocks->findItems( txt, Qt::MatchExactly ).isEmpty() )
|
|
|
|
ui->listWidget_badBlocks->addItem( txt );
|
2010-12-21 21:01:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NewNandBin::~NewNandBin()
|
|
|
|
{
|
|
|
|
delete ui;
|
|
|
|
}
|
|
|
|
|
|
|
|
void NewNandBin::on_pushButton_keys_clicked()
|
|
|
|
{
|
2010-12-22 01:25:08 +01:00
|
|
|
QString f = QFileDialog::getOpenFileName( this, tr( "Select Keys.bin" ), dir );
|
2010-12-21 21:01:39 +01:00
|
|
|
if( f.isEmpty() )
|
2011-05-17 23:18:45 +02:00
|
|
|
return;
|
2010-12-21 21:01:39 +01:00
|
|
|
ui->lineEdit_keys->setText( f );
|
2010-12-22 01:25:08 +01:00
|
|
|
dir = QFileInfo( f ).canonicalPath();
|
2010-12-21 21:01:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void NewNandBin::on_pushButton_boot_clicked()
|
|
|
|
{
|
2010-12-22 01:25:08 +01:00
|
|
|
QString f = QFileDialog::getOpenFileName( this, tr( "Select Boot 1 & 2" ), dir );
|
2010-12-21 21:01:39 +01:00
|
|
|
if( f.isEmpty() )
|
2011-05-17 23:18:45 +02:00
|
|
|
return;
|
2010-12-21 21:01:39 +01:00
|
|
|
ui->lineEdit_boot->setText( f );
|
2010-12-22 01:25:08 +01:00
|
|
|
dir = QFileInfo( f ).canonicalPath();
|
2010-12-21 21:01:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void NewNandBin::on_pushButton_dest_clicked()
|
|
|
|
{
|
2010-12-22 01:25:08 +01:00
|
|
|
QString f = QFileDialog::getSaveFileName( this, tr( "Output file" ), dir );
|
2010-12-21 21:01:39 +01:00
|
|
|
if( f.isEmpty() )
|
2011-05-17 23:18:45 +02:00
|
|
|
return;
|
2010-12-21 21:01:39 +01:00
|
|
|
ui->lineEdit_dest->setText( f );
|
2010-12-22 01:25:08 +01:00
|
|
|
dir = QFileInfo( f ).canonicalPath();
|
2010-12-21 21:01:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QList<quint16> NewNandBin::BadBlocks()
|
|
|
|
{
|
|
|
|
QList<quint16> ret;
|
|
|
|
quint16 cnt = ui->listWidget_badBlocks->count();
|
|
|
|
for( quint16 i = 0; i < cnt; i++ )
|
|
|
|
{
|
2011-05-17 23:18:45 +02:00
|
|
|
bool ok = false;
|
|
|
|
quint16 num = ui->listWidget_badBlocks->item( i )->text().toInt( &ok );
|
|
|
|
if( ok )
|
|
|
|
ret << num;
|
2010-12-21 21:01:39 +01:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void NewNandBin::on_pushButton_bb_add_clicked()
|
|
|
|
{
|
|
|
|
quint16 val = ui->spinBox->value();
|
|
|
|
if( !BadBlocks().contains( val ) )
|
|
|
|
{
|
2011-05-17 23:18:45 +02:00
|
|
|
ui->listWidget_badBlocks->addItem( QString( "%1" ).arg( val ) );
|
2010-12-21 21:01:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void NewNandBin::on_pushButton_bb_rm_clicked()
|
|
|
|
{
|
|
|
|
QList<QListWidgetItem *> items = ui->listWidget_badBlocks->selectedItems();
|
|
|
|
foreach( QListWidgetItem *item, items )
|
|
|
|
{
|
2011-05-17 23:18:45 +02:00
|
|
|
ui->listWidget_badBlocks->removeItemWidget( item );
|
|
|
|
delete item;
|
2010-12-21 21:01:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-14 18:48:38 +01:00
|
|
|
bool NewNandBin::CreateDefaultEntries()
|
2010-12-21 21:01:39 +01:00
|
|
|
{
|
2011-05-17 23:18:45 +02:00
|
|
|
if( !nand.CreateEntry( "/sys", 0, 0, NAND_DIR, NAND_RW, NAND_RW, 0 )
|
|
|
|
|| !nand.CreateEntry( "/ticket", 0, 0, NAND_DIR, NAND_RW, NAND_RW, 0 )
|
|
|
|
|| !nand.CreateEntry( "/title", 0, 0, NAND_DIR, NAND_RW, NAND_RW, NAND_READ )
|
|
|
|
|| !nand.CreateEntry( "/shared1", 0, 0, NAND_DIR, NAND_RW, NAND_RW, 0 )
|
|
|
|
|| !nand.CreateEntry( "/shared2", 0, 0, NAND_DIR, NAND_RW, NAND_RW, NAND_RW )
|
|
|
|
|| !nand.CreateEntry( "/import", 0, 0, NAND_DIR, NAND_RW, NAND_RW, 0 )
|
|
|
|
|| !nand.CreateEntry( "/meta", 0x1000, 1, NAND_DIR, NAND_RW, NAND_RW, NAND_RW )
|
|
|
|
|| !nand.CreateEntry( "/tmp", 0, 0, NAND_DIR, NAND_RW, NAND_RW, NAND_RW )
|
|
|
|
|| !nand.WriteMetaData() )
|
|
|
|
{
|
|
|
|
qWarning() << "NewNandBin::on_buttonBox_accepted -> error creating directories in the new nand";
|
|
|
|
QMessageBox::warning( this, tr( "Error" ), \
|
|
|
|
tr( "Can't create base folders in the new nand." ) );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
//add cert.sys
|
|
|
|
quint16 handle = nand.CreateEntry( "/sys/cert.sys", 0, 0, NAND_FILE, NAND_RW, NAND_RW, NAND_READ );
|
|
|
|
if( !handle || !nand.SetData( handle, QByteArray( (const char*)&certs_dat, CERTS_DAT_SIZE ) ) )
|
|
|
|
{
|
|
|
|
qWarning() << "NewNandBin::on_buttonBox_accepted -> error creating cert in the new nand";
|
|
|
|
QMessageBox::warning( this, tr( "Error" ), \
|
|
|
|
tr( "Can't create cert.sys in the new nand." ) );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
//create uid.sys
|
|
|
|
switch( ui->comboBox_uid->currentIndex() )
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
uidSys.clear();
|
|
|
|
break;
|
|
|
|
case 1://jap
|
|
|
|
{
|
|
|
|
UIDmap uid;
|
|
|
|
uid.CreateNew( 0x4a );
|
|
|
|
uidSys = uid.Data();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2://usa
|
|
|
|
{
|
|
|
|
UIDmap uid;
|
|
|
|
uid.CreateNew( 0x45 );
|
|
|
|
uidSys = uid.Data();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 3://eur
|
|
|
|
{
|
|
|
|
UIDmap uid;
|
|
|
|
uid.CreateNew( 0x50 );
|
|
|
|
uidSys = uid.Data();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 4://kor
|
|
|
|
{
|
|
|
|
UIDmap uid;
|
|
|
|
uid.CreateNew( 0x4b );
|
|
|
|
uidSys = uid.Data();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if( !uidSys.isEmpty() )
|
|
|
|
{
|
|
|
|
//hexdump( uidSys );
|
|
|
|
quint16 fd = nand.CreateEntry( "/sys/uid.sys", 0, 0, NAND_FILE, NAND_RW, NAND_RW, 0 );
|
|
|
|
if( !fd || !nand.SetData( fd, uidSys ) )
|
|
|
|
{
|
|
|
|
qWarning() << "NewNandBin::on_buttonBox_accepted -> error creating cert in the new nand";
|
|
|
|
QMessageBox::warning( this, tr( "Error" ), \
|
|
|
|
tr( "Can't create uid.sys in the new nand." ) );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2011-01-18 04:03:08 +01:00
|
|
|
|
2011-05-17 23:18:45 +02:00
|
|
|
//commit changes to metadata
|
|
|
|
if( !nand.WriteMetaData() )
|
|
|
|
{
|
|
|
|
qWarning() << "NewNandBin::on_buttonBox_accepted -> error writing metadata";
|
|
|
|
QMessageBox::warning( this, tr( "Error" ), \
|
|
|
|
tr( "Can't write metadata in the new nand." ) );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2011-01-14 18:48:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//ok clicked
|
|
|
|
void NewNandBin::on_buttonBox_accepted()
|
|
|
|
{
|
|
|
|
if( ui->lineEdit_keys->text().isEmpty() || ui->lineEdit_boot->text().isEmpty() || ui->lineEdit_dest->text().isEmpty() )
|
|
|
|
{
|
2011-05-17 23:18:45 +02:00
|
|
|
QMessageBox::warning( this, tr( "Error" ), tr( "Required feilds are empty" ) );
|
|
|
|
return;
|
2011-01-14 18:48:38 +01:00
|
|
|
}
|
2011-05-17 23:18:45 +02:00
|
|
|
if( keys.isEmpty() )
|
|
|
|
keys = ReadFile( ui->lineEdit_keys->text() );
|
|
|
|
if( boots.isEmpty() )
|
|
|
|
boots = ReadFile( ui->lineEdit_boot->text() );
|
2011-01-14 18:48:38 +01:00
|
|
|
if( keys.size() != 0x400 || boots.size() != 0x108000 )
|
|
|
|
{
|
2011-05-17 23:18:45 +02:00
|
|
|
QMessageBox::warning( this, tr( "Error" ), tr( "The keys or boot1/2 is not correct" ) );
|
|
|
|
keys.clear();
|
|
|
|
boots.clear();
|
|
|
|
return;
|
2011-01-14 18:48:38 +01:00
|
|
|
}
|
2011-05-29 03:01:11 +02:00
|
|
|
setCursor( Qt::BusyCursor );
|
2011-01-14 18:48:38 +01:00
|
|
|
if( !nand.CreateNew( ui->lineEdit_dest->text(), keys, boots, BadBlocks() ) )
|
|
|
|
{
|
2011-05-17 23:18:45 +02:00
|
|
|
qDebug() << "error creating nand.bin";
|
2011-05-29 03:01:11 +02:00
|
|
|
unsetCursor();
|
2011-05-17 23:18:45 +02:00
|
|
|
keys.clear();
|
|
|
|
boots.clear();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
//qDebug() << "created nand, trying to add default entries";
|
|
|
|
if( !CreateDefaultEntries() )
|
|
|
|
{
|
2011-05-29 03:01:11 +02:00
|
|
|
unsetCursor();
|
2011-05-17 23:18:45 +02:00
|
|
|
keys.clear();
|
|
|
|
boots.clear();
|
|
|
|
return;
|
2011-01-14 18:48:38 +01:00
|
|
|
}
|
2011-05-29 03:01:11 +02:00
|
|
|
unsetCursor();
|
2010-12-21 21:01:39 +01:00
|
|
|
|
|
|
|
ret = ui->lineEdit_dest->text();
|
|
|
|
}
|
|
|
|
|
2011-05-28 08:05:34 +02:00
|
|
|
QString NewNandBin::GetNewNandPath( QWidget *parent, const QList<quint16> &badBlocks )
|
2010-12-21 21:01:39 +01:00
|
|
|
{
|
|
|
|
NewNandBin d( parent, badBlocks );
|
|
|
|
if( !d.exec() )
|
2011-05-17 23:18:45 +02:00
|
|
|
return QString();
|
2010-12-21 21:01:39 +01:00
|
|
|
return d.ret;
|
|
|
|
}
|
2010-12-22 01:25:08 +01:00
|
|
|
|
|
|
|
//read bad blocks from a txt file
|
|
|
|
void NewNandBin::on_pushButton_badBlockFile_clicked()
|
|
|
|
{
|
|
|
|
QString f = QFileDialog::getOpenFileName( this, tr( "Select File with Bad Block List" ), dir );
|
|
|
|
if( f.isEmpty() )
|
2011-05-17 23:18:45 +02:00
|
|
|
return;
|
2010-12-22 01:25:08 +01:00
|
|
|
dir = QFileInfo( f ).canonicalPath();
|
|
|
|
QString str = QString( ReadFile( f ) );
|
|
|
|
if( str.isEmpty() )
|
|
|
|
{
|
2011-05-17 23:18:45 +02:00
|
|
|
qWarning() << "NewNandBin::on_pushButton_badBlockFile_clicked -> error reading file";
|
|
|
|
return;
|
2010-12-22 01:25:08 +01:00
|
|
|
}
|
|
|
|
ui->listWidget_badBlocks->clear();
|
|
|
|
|
|
|
|
str.replace( "\r\n", "\n" );
|
|
|
|
QStringList lines = str.split( "\n", QString::SkipEmptyParts );
|
2011-05-17 23:18:45 +02:00
|
|
|
foreach( const QString &line, lines )
|
2010-12-22 01:25:08 +01:00
|
|
|
{
|
2011-05-17 23:18:45 +02:00
|
|
|
if( line.size() > 5 )
|
|
|
|
continue;
|
|
|
|
bool ok = false;
|
2010-12-22 01:25:08 +01:00
|
|
|
|
2011-05-17 23:18:45 +02:00
|
|
|
if( ui->listWidget_badBlocks->findItems( line, Qt::MatchExactly ).size() )//this one is already in the list
|
|
|
|
continue;
|
2010-12-22 01:25:08 +01:00
|
|
|
|
2011-05-17 23:18:45 +02:00
|
|
|
quint16 bb = line.toInt( &ok );
|
|
|
|
if( !ok || bb < 8 || bb > 4079 )
|
|
|
|
continue;
|
2010-12-22 01:25:08 +01:00
|
|
|
|
2011-05-17 23:18:45 +02:00
|
|
|
ui->listWidget_badBlocks->addItem( line );
|
2010-12-22 01:25:08 +01:00
|
|
|
}
|
|
|
|
}
|
2011-01-14 18:48:38 +01:00
|
|
|
|
|
|
|
//read info from existing nand.bin
|
|
|
|
void NewNandBin::on_pushButton_oldNand_clicked()
|
|
|
|
{
|
2011-05-17 23:18:45 +02:00
|
|
|
QString f = QFileDialog::getOpenFileName( this, tr( "Select Old nand.bin" ), dir );
|
|
|
|
if( f.isEmpty() )
|
|
|
|
return;
|
2011-01-14 18:48:38 +01:00
|
|
|
|
|
|
|
|
2011-05-17 23:18:45 +02:00
|
|
|
QFileInfo fi( f );
|
|
|
|
QFile file( fi.absoluteFilePath() );
|
|
|
|
if( !file.exists() || !file.open( QIODevice::ReadOnly ) )
|
|
|
|
{
|
|
|
|
QMessageBox::warning( this, tr( "Error" ), \
|
|
|
|
tr( "Can't open %1!" ).arg( fi.absoluteFilePath() ) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ui->listWidget_badBlocks->clear();
|
2011-01-14 18:48:38 +01:00
|
|
|
|
2011-05-17 23:18:45 +02:00
|
|
|
switch( fi.size() )
|
|
|
|
{
|
|
|
|
case 0x21000000:// w/ ecc, keys in different file
|
|
|
|
{
|
|
|
|
boots = file.read( 0x108000 );
|
|
|
|
//file.seek();
|
|
|
|
keys = ReadFile( fi.absoluteDir().absoluteFilePath( "keys.bin" ) );
|
2011-01-14 18:48:38 +01:00
|
|
|
|
2011-05-17 23:18:45 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x21000400:// w/ ecc, keys at end of nand dump
|
|
|
|
{
|
|
|
|
boots = file.read( 0x108000 );
|
|
|
|
file.seek( 0x21000000 );
|
|
|
|
keys = file.read( 0x400 );
|
|
|
|
//keys = ReadFile( fi.absoluteDir().absoluteFilePath( "keys.bin" ) );
|
2011-01-14 18:48:38 +01:00
|
|
|
|
2011-05-17 23:18:45 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default://unsupported for this
|
|
|
|
QMessageBox::warning( this, tr( "Error" ), tr( "I need a nand dump with ecc to create a new nand from.<br>Accepted sizes are 0x21000000 and 0x21000400." ) );
|
|
|
|
file.close();
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
file.close();
|
2011-01-14 18:48:38 +01:00
|
|
|
|
2011-05-17 23:18:45 +02:00
|
|
|
//create nandBin object to get the list of bad blocks
|
|
|
|
NandBin old( this );
|
|
|
|
if( !old.SetPath( fi.absoluteFilePath() ) || !old.InitNand() )
|
|
|
|
{
|
|
|
|
QMessageBox::warning( this, tr( "Error" ), \
|
|
|
|
tr( "Error reading %1." ).arg( fi.absoluteFilePath() ) );
|
|
|
|
keys.clear();
|
|
|
|
boots.clear();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
QList<quint16> clusters = old.GetFats();
|
|
|
|
QList<quint16> badBlacks;
|
|
|
|
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 );
|
|
|
|
keys.clear();
|
|
|
|
boots.clear();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for( quint16 i = 0; i < 0x8000; i += 8 )//first cluster of each block.
|
|
|
|
{
|
|
|
|
//qDebug() << hex << i << clusters.at( i );
|
|
|
|
if( clusters.at( i ) == 0xFFFD )
|
|
|
|
{
|
|
|
|
quint16 block = ( i / 8 );
|
|
|
|
badBlacks << block;
|
|
|
|
QString txt = QString( "%1" ).arg( block );
|
|
|
|
//qDebug() << "bad cluster" << hex << i << block << txt;
|
|
|
|
//if( ui->listWidget_badBlocks->findItems( txt, Qt::MatchExactly ).isEmpty() )//just in case, but this should always be true
|
|
|
|
ui->listWidget_badBlocks->addItem( txt );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
uidSys = old.GetData( "/sys/uid.sys" );
|
|
|
|
if( !uidSys.isEmpty() )
|
|
|
|
{
|
|
|
|
uidSys = GetCleanUid( uidSys );
|
|
|
|
ui->comboBox_uid->setCurrentIndex( 5 );
|
|
|
|
//hexdump( uidSys );
|
|
|
|
}
|
|
|
|
ui->lineEdit_boot->setText( tr( "<From old nand>" ) );
|
|
|
|
ui->lineEdit_keys->setText( tr( "<From old nand>" ) );
|
2011-01-18 04:03:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//remove all entries of a uid.sys from after the user has started doing stuff
|
|
|
|
QByteArray NewNandBin::GetCleanUid( QByteArray old )
|
|
|
|
{
|
2011-05-17 23:18:45 +02:00
|
|
|
QBuffer buf( &old );
|
2011-05-28 08:05:34 +02:00
|
|
|
buf.open( QIODevice::ReadOnly );
|
2011-01-14 18:48:38 +01:00
|
|
|
|
2011-05-17 23:18:45 +02:00
|
|
|
quint64 tid;
|
|
|
|
quint16 titles = 0;
|
|
|
|
quint32 cnt = old.size() / 12;
|
|
|
|
for( quint32 i = 0; i < cnt; i++ )
|
|
|
|
{
|
|
|
|
buf.seek( i * 12 );
|
|
|
|
buf.read( (char*)&tid, 8 );
|
|
|
|
tid = qFromBigEndian( tid );
|
|
|
|
quint32 upper = ( ( tid >> 32 ) & 0xffffffff );
|
|
|
|
quint32 lower = ( tid & 0xffffffff );
|
|
|
|
//qDebug() << QString( "%1" ).arg( tid, 16, 16, QChar( '0' ) ) << hex << upper << lower << ( ( lower >> 24 ) & 0xff ) << ( lower & 0xffff00 );
|
|
|
|
if( ( upper == 0x10001 && ( ( lower >> 24 ) & 0xff ) != 0x48 ) || //a channel, not starting with 'H'
|
|
|
|
lower == 0x48415858 || //original HBC
|
|
|
|
tid == 0x100000000ull || //bannerbomb -> ATD ( or any other program that uses the SU tid )
|
|
|
|
( upper == 0x10000 && ( ( lower & 0xffffff00 ) == 0x555000 ) ) ) //a disc update partition
|
|
|
|
break;
|
2011-01-18 04:03:08 +01:00
|
|
|
|
2011-05-17 23:18:45 +02:00
|
|
|
titles++;
|
|
|
|
}
|
|
|
|
buf.close();
|
|
|
|
return old.left( 12 * titles );
|
2011-01-14 18:48:38 +01:00
|
|
|
}
|