* fix bugs in the nandBin delete

* fiix bug when writing supercluster
* add macros for nand attributes
* adding another example program for creating nand.bin and adding wads.  it SEEMS to work as expected, but still, use it with EXTREME CAUTION
* changed default NUS_Cache path in NUS downloader to parent directly so the 2 example programs can share the same cache
* added "-all" argument for the nandBinchecker
This commit is contained in:
giantpune@gmail.com 2010-12-21 20:01:39 +00:00
parent 9afc40b70f
commit d0490cce4c
17 changed files with 1520 additions and 160 deletions

8
.gitattributes vendored
View File

@ -63,6 +63,14 @@ nand_dump/mainwindow.h -text
nand_dump/mainwindow.ui -text
nand_dump/nand.pro -text
nand_dump/readmii.txt -text
ohneschwanzenegger/main.cpp -text
ohneschwanzenegger/mainwindow.cpp -text
ohneschwanzenegger/mainwindow.h -text
ohneschwanzenegger/mainwindow.ui -text
ohneschwanzenegger/newnandbin.cpp -text
ohneschwanzenegger/newnandbin.h -text
ohneschwanzenegger/newnandbin.ui -text
ohneschwanzenegger/refleurii.pro -text
saveToy/includes.h -text
saveToy/main.cpp -text
saveToy/mainwindow.cpp -text

View File

@ -15,7 +15,10 @@ NandBin::NandBin( QObject * parent, const QString &path ) : QObject( parent )
NandBin::~NandBin()
{
if( f.isOpen() )
{
f.flush();
f.close();
}
if( root )
delete root;
@ -147,6 +150,7 @@ bool NandBin::CreateNew( const QString &path, QByteArray keys, QByteArray first8
QTreeWidgetItem *NandBin::GetTree()
{
//qDebug() << "NandBin::GetTree()";
return root->clone();
}
@ -371,7 +375,17 @@ bool NandBin::InitNand( QIcon dirs, QIcon files )
root = new QTreeWidgetItem( QStringList() << nandPath );
AddChildren( root, 0 );
/*#ifdef NAND_BIN_CAN_WRITE
CreateEntry( "/testDir", 0, 0, NAND_DIR, NAND_RW, NAND_RW, NAND_RW );
quint16 pp = CreateEntry( "/testDir/testFile", 0, 0, NAND_FILE, NAND_RW, NAND_RW, NAND_RW );
qDebug() << "created entry" << pp;
Delete( "/testDir/testFile" );
pp = CreateEntry( "/testDir/testFile", 0, 0, NAND_FILE, NAND_RW, NAND_RW, NAND_RW );
qDebug() << "created entry" << pp;
SetData( pp, QByteArray( 0x10000, '\x0' ) );
WriteMetaData();
#endif*/
//checkout the blocks for boot1&2
QList<QByteArray>blocks;
@ -390,120 +404,10 @@ bool NandBin::InitNand( QIcon dirs, QIcon files )
blocks << block;
}
//debug shitzz
/*QList<quint16> fffcs;
QList<quint16> ffffs;
QList<quint16> fffds;
for( quint16 i = 0; i < 0x8000; i++ )
{
switch( fats.at( i ) )
{
case 0xFFFB:
break;
case 0xFFFC:
fffcs << i;
break;
case 0xFFFD:
fffds << i;
break;
case 0xFFFE:
break;
case 0xFFFF:
ffffs << i;
break;
default:
break;
}
}*/
/*foreach( quint16 cl, fffds )
{
qDebug() << "bad cluster" << hex << cl;
for( quint16 i = 0; i < 8; i++ )
{
QByteArray page = GetPage( (cl * 8 ) + i, true );
for( quint16 j = 0; j < page.size(); j++ )
{
if( page.at( j ) != '\0' )
{
hexdump( page );
break;
}
}
//hexdump( page );
}
//break;
}*/
/*qDebug() << "total ffff clusters:" << ffffs.size();
quint16 u = 0;
while( ffffs.size() )
{
quint16 fc = ffffs.takeFirst();
if( fc < 0x40 || fc >= 0x7F00 )
continue;
QByteArray cl = GetCluster( fc, false );
while( ffffs.size() && ( ( fc / 8 ) == ( ffffs.at( 0 ) / 8 ) ) )
{
fc = ffffs.takeFirst();
if( fc < 0x40 || fc >= 0x7F00 )
continue;
cl += GetCluster( fc, false );
}
WriteFile( QString("./aaaa_ff_%1_%2.bin" ).arg( u++).arg( fc ), cl );
}*/
/*QList<quint16> fcBlocks;
quint16 cnt = fffcs.size();
quint16 rr = 0;
for( quint16 i = 0; i < cnt; i++ )
{
quint16 block = fffcs.at( i )/8;
if( !fcBlocks.contains( block ) )
fcBlocks << block;
if( fffcs.at( i ) < 0x40 || fffcs.at( i ) >= 0x7F00 )
continue;
rr++;
}
qDebug() << "fcBlocks:" << fcBlocks << "total reserved clusters:" << fffcs.size() << "reserved not used for superblock/boot" << rr;*/
/*quint16 ppp = 0;
for( quint16 i = 0; i < fcBlocks.size(); i++ )
{
for( quint16 j = 0; j < 8; j++ )
{
if( !fffcs.contains( ( i * 8 ) + j ) )
{
ppp++;
break;
}
}
}
qDebug() << "partial reserved blocks:" << ppp;*/
/*quint16 u = 0;
while( fffcs.size() )
{
quint16 fc = fffcs.takeFirst();
if( fc < 0x40 || fc >= 0x7F00 )
continue;
QByteArray cl = GetCluster( fc );
while( fffcs.size() && ( ( fc / 8 ) == ( fffcs.at( 0 ) / 8 ) ) )
{
fc = fffcs.takeFirst();
if( fc < 0x40 || fc >= 0x7F00 )
continue;
cl += GetCluster( fc );
}
WriteFile( QString("./aaaa_fc_%1_%2.bin" ).arg( u++).arg( fc ), cl );
}*/
if( !bootBlocks.SetBlocks( blocks ) )
return false;
ShowInfo();
//ShowInfo();
return true;
}
@ -764,11 +668,7 @@ 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
* S F F S "version" padding?
* 1 2 3 4 5 6*/
fat_entry += 6;
// location in fat of cluster chain
@ -1127,7 +1027,7 @@ bool NandBin::WriteCluster( quint32 pageNo, const QByteArray data, const QByteAr
QByteArray spareData( 0x40, '\0' );
quint8* sp = (quint8*)spareData.data();
QByteArray ecc = spare.CalcEcc( data.mid( i * 0x800, 0x800 ) );
memcpy( sp + 0x30, ecc.data(), 0x14 );
memcpy( sp + 0x30, ecc.data(), 0x10 );
sp[ 0 ] = 0xff; // good block
if( !hmac.isEmpty() )
{
@ -1138,7 +1038,7 @@ bool NandBin::WriteCluster( quint32 pageNo, const QByteArray data, const QByteAr
}
else if( i == 7 )
{
memcpy( (char*)sp + 1, hmac.data() + 12, 8 );
memcpy( (char*)sp + 1, (char*)(hmac.data()) + 12, 8 );
}
}
if( !WritePage( pageNo + i, data.mid( i * 0x800, 0x800 ) + spareData ) )
@ -1192,10 +1092,11 @@ quint16 NandBin::CreateNode( const QString &name, quint32 uid, quint16 gid, quin
attr = attr | ( ( user_perm & 3 ) << 6 ) | ( ( group_perm & 3 ) << 4 ) | ( ( other_perm & 3 ) << 2 );
quint32 i;
//qDebug() << "looking for first empty node";
for( i = 1; i < 0x17ff; i++ )//cant be entry 0 because that is the root
{
fst_t fst = fsts[ i ];
if( !fst.filename[ 0 ] )//this one doesnt have a filename, it cant be used already
//qDebug() << hex << i << FstName( fsts[ i ] );
if( !fsts[ i ].filename[ 0 ] )//this one doesnt have a filename, it cant be used already
break;
}
if( i == 0x17ff )
@ -1291,7 +1192,7 @@ quint16 NandBin::CreateEntry( const QString &path, quint32 uid, quint16 gid, qui
fsts[ entryNo ].sib = ret;
}
QTreeWidgetItem *child = CreateItem( par, name, 0, ret, uid, gid, 0, fsts[ ret ].attr, 0 );
if( attr == 1 )
if( attr == NAND_FILE )
{
child->setIcon( 0, keyIcon );
}
@ -1310,13 +1211,13 @@ bool NandBin::Delete( const QString &path )
bool NandBin::DeleteItem( QTreeWidgetItem *item )
{
qDebug() << "NandBin::DeleteItem" << item->text( 0 );
if( !item )
{
qWarning() << "cant delete a null item";
return false;
}
qDebug() << "NandBin::DeleteItem" << item->text( 0 );
bool ok = false;
quint16 idx = item->text( 1 ).toInt( &ok );//get the index of the entry to remove
if( !ok || idx > 0x17fe )
@ -1341,7 +1242,24 @@ bool NandBin::DeleteItem( QTreeWidgetItem *item )
return false;//wtf
}
if( fsts[ parIdx ].sub == idx ) //this is the first item in the folder, point the parent to this items first sibling
{
fsts[ parIdx ].sub = fsts[ idx ].sib;
quint16 cnt = par->childCount();
for( quint16 i = 0; i < cnt; i++ )
{
if( par->child( i )->text( 0 ) == item->text( 0 ) )
{
pId = i;
//qDebug() << "found the item";
break;
}
if( i == cnt - 1 )//not found
{
qWarning() << "wtf 15" << pId << i << cnt;
return false;
}
}
}
else //point the previous entry to the next one
{
@ -1382,28 +1300,22 @@ bool NandBin::DeleteItem( QTreeWidgetItem *item )
{
case 1:
{
int q = 0;
//int q = 0;
qDebug() << "deleting clusters of" << item->text( 0 ) << idx;
quint16 fat = fsts[ idx ].sub;//delete all this file's clusters
//fats.replace( fat, 0xfffe );
do
QList<quint16> toFree = GetFatsForFile( idx );
foreach( quint16 cl, toFree )
{
fats.replace( fat, 0xfffe );
//qDebug() << "fat" << hex << fat;
fat = GetFAT( fat );
//fats.replace( fat, 0xfffe );
//qDebug() << "deleting cluster" << hex << fat << "from table";
q++;
fats.replace( cl, 0xfffe );
}
while( fat < 0x17ff );
qDebug() << "delete loop done. freed" << q << "clusters";
qDebug() << "delete loop done. freed" << toFree.size() << "clusters";
}
break;
case 2:
{
qDebug() << "deleting children of" << item->text( 0 );
quint32 cnt = item->childCount();//delete all the children of this item
for( quint32 i = cnt - 1; i > 0; i-- )
qDebug() << cnt << "childern";
for( quint32 i = cnt; i > 0; i-- )
{
if( !DeleteItem( item->child( i - 1 ) ) )
{
@ -1416,7 +1328,9 @@ bool NandBin::DeleteItem( QTreeWidgetItem *item )
}
memset( &fsts[ idx ], 0, sizeof( fst_t ) ); //clear this entry
fsts[ idx ].fst_pos = idx; //reset this
QTreeWidgetItem *d = par->takeChild( pId );
qDebug() << "deleting tree item" << d->text( 0 );
delete d;
return true;
}
@ -1425,12 +1339,18 @@ bool NandBin::SetData( const QString &path, const QByteArray data )
{
QTreeWidgetItem *i = ItemFromPath( path );
if( !i )
{
qDebug() << "!item" << path;
return false;
}
bool ok = false;
quint16 idx = i->text( 1 ).toInt( &ok );//find the entry
if( !ok || idx > 0x17fe )
{
qDebug() << "out of range" << path;
return false;
}
return SetData( idx, data );
}
@ -1438,8 +1358,12 @@ bool NandBin::SetData( const QString &path, const QByteArray data )
bool NandBin::SetData( quint16 idx, const QByteArray data )
{
fst_t fst = fsts[ idx ];
qDebug() << "NandBin::SetData" << FstName( fst );
if( ( fst.attr & 3 ) != 1 )
{
qDebug() << idx << "is a folder";
return false;
}
QList<quint16> fts = GetFatsForFile( idx ); //get the currently used fats and overwrite them. this doesnt serve much purpose, but it seems cleaner
QByteArray pData = PaddedByteArray( data, 0x4000 );//actual data that must be written to the nand
@ -1467,7 +1391,7 @@ bool NandBin::SetData( quint16 idx, const QByteArray data )
qsrand( midnight.secsTo( QTime::currentTime() ) );
//now grab the clusters that will be used from the list
qDebug() << "trying to find" << ( clCnt - fts.size() ) << "free clusters";
//qDebug() << "trying to find" << ( clCnt - fts.size() ) << "free clusters";
while( fts.size() < clCnt )
{
if( !freeClusters.size() )//avoid endless loop in case there are some clusters that should be free, but the spare data says theyre bad
@ -1601,9 +1525,13 @@ bool NandBin::SetData( quint16 idx, const QByteArray data )
QTreeWidgetItem *i = ItemFromEntry( idx, root );
if( !i )
{
qDebug() << "!ItemFromEntry";
return false;
}
i->setText( 2, QString( "%1" ).arg( data.size(), 0, 16 ) );
//f.flush();
return true;
}
@ -1671,7 +1599,7 @@ bool NandBin::WriteMetaData()
//qDebug() << "done adding shit" << hex << (quint32)b.pos();
b.close();
QByteArray hmR = spare.Get_hmac_meta( scl, nextSuperCluster );
//qDebug() << "about to write the meta block" << hex << nextSuperCluster << nextClusterVersion << "to page" << (quint32)( nextSuperCluster * 8 );
qDebug() << "about to write the meta block" << hex << nextSuperCluster << nextClusterVersion << "to page" << (quint32)( nextSuperCluster * 8 );
for( quint8 i = 0; i < 0x10; i++ )
{
@ -1685,6 +1613,9 @@ bool NandBin::WriteMetaData()
currentSuperCluster = nextSuperCluster;
superClusterVersion = nextClusterVersion; //probably need to put some magic here in case the version wraps around back to 0
//make sure all the data is really written
f.flush();
return true;
}

View File

@ -27,8 +27,8 @@ struct fst_t
// class to deal with an encrypted wii nand dump
// basic usage... create an object, set a path, call InitNand. then you can get the detailed list of entries with GetTree()
// extract files with GetFile()
//! so far, all functions for writing to the nand are just dummies. i have only run a few test with them, but now actually used them to write to a nand
//! dont try to use them yet
//! so far, all functions for writing to the nand are highly untested. it is not recommended to try to use this code productively!!
//! you should verify anything written with this code before attempting to install it on you wii
//once InitNand() is called, you can get the contents of the nand in a nice QTreeWidgetItem* with GetTree()
class NandBin : public QObject
@ -71,7 +71,6 @@ public:
// 7 attr
QTreeWidgetItem *GetTree();
//extracts an item( and all its children ) to a directory
//this function is BLOCKING and will block the current thread, so if done in the gui thread, it will freeze your GUI till it returns
bool ExtractToDir( QTreeWidgetItem *item, const QString &path );
@ -130,6 +129,7 @@ public:
bool SetData( const QString &path, const QByteArray data );
//write the current changes to the metadata( if you dont do this, then none of the other stuff youve done wont be saved )
// but at the same time, you probably dont need to overuse this. ( no need to write metadata every time you make a single change )
bool WriteMetaData();
//functions to verify spare data
@ -207,10 +207,9 @@ private:
QTreeWidgetItem *ItemFromEntry( quint16 i, QTreeWidgetItem *parent = NULL );
QTreeWidgetItem *ItemFromEntry( const QString &i, QTreeWidgetItem *parent = NULL );
signals:
//connect to these to receive messages from this object
//so far, many errors are only outputting to qDebug() and qWarning().
void SendError( QString );
void SendText( QString );
};

View File

@ -185,7 +185,7 @@ void fs_hmac_data( const unsigned char *data, quint32 uid, const unsigned char *
QByteArray NandSpare::Get_hmac_data( const QByteArray cluster, quint32 uid, const unsigned char *name, quint32 entry_n, quint32 x3, quint16 blk )
{
//qDebug() << "NandSpare::Get_hmac_data" << hex << cluster.size() << uid << QString( (const char*)name ) << entry_n << x3 << blk;
//qDebug() << "NandSpare::Get_hmac_data" << hex << cluster.size() << uid << QString( QByteArray( (const char*)name, 12 ) ) << entry_n << x3 << blk;
if( hmacKey.size() != 0x14 || cluster.size() != 0x4000 )
return QByteArray();

View File

@ -616,7 +616,7 @@ QMap< quint64, quint16 > NusDownloader::List31j()
titles.insert( 0x100000022ull, 1039 );//34v1039
titles.insert( 0x100000023ull, 1040 );//35v1040
titles.insert( 0x100000024ull, 1042 );//36v1042
//titles.insert( 0x100000025ull, 2070 );//37v2070//3.1u has this one but not 3.1j??
//titles.insert( 0x100000025ull, 2070 );//37v2070 //3.1u has this one but not 3.1j??
titles.insert( 0x1000248415941ull, 0x1 );//photo2v1
titles.insert( 0x1000848414B4aull, 0 );//EULA - HAKJ
titles.insert( 0x100024841464aull, 0x7 );// forcast v7 HAFJ

View File

@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
<string>Setting.txt</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
@ -96,7 +96,7 @@
<item row="5" column="1">
<widget class="QLineEdit" name="lineEdit_serno">
<property name="text">
<string>123456789</string>
<string>622011873</string>
</property>
<property name="maxLength">
<number>9</number>

View File

@ -51,17 +51,17 @@ public:
//get all the parts of this wad put together in a wad ready for writing to disc or whatever
const QByteArray Data( quint32 magicWord = 0x49730000, const QByteArray footer = QByteArray() );
//get the tmd for the wad
const QByteArray getTmd();
//get the tmd for the wad
const QByteArray getTmd();
//get the tik for the wad
const QByteArray getTik();
//get the tik for the wad
const QByteArray getTik();
//get the decrypted data from a content
const QByteArray Content( quint16 i );
//get the number of contents
quint32 content_count();
//get the number of contents
quint32 content_count();
//get the last error encountered while trying to do something
const QString LastError(){ return errStr; }

View File

@ -30,6 +30,7 @@ void Usage()
qDebug() << " -clInfo shows free, used, and lost ( marked used, but dont belong to any file ) clusters";
qDebug() << " -spare calculate & compare ecc for all pages in the nand";
qDebug() << " calculate & compare hmac signatures for all files and superblocks";
qDebug() << " -all does all of the above";
exit( 1 );
}
@ -340,7 +341,7 @@ bool CheckTitleIntegrity( quint64 tid )
//return false; //maye in the future this will be true, but for now, this doesnt mean it wont boot
break;
case ERROR_RSA_FAKESIGNED:
// qDebug() << "\t" << it << "fakesigned";
qDebug() << "\t" << it << "fakesigned";
break;
default:
break;
@ -408,13 +409,13 @@ bool CheckTitleIntegrity( quint64 tid )
quint64 ios = t.IOS();
if( ios && !validIoses.contains( ios ) )
{
qDebug() << "the IOS for this title is not bootable";
qDebug() << "\tthe IOS for this title is not bootable\n\t" << TidTxt( ios ).insert( 8, "-" ) ;
return false;
}
quint32 uid = uidM.GetUid( tid, false );
if( !uid )
{
qDebug() << "this title has no UID entry";
qDebug() << "\tthis title has no UID entry";
return false;
}
@ -629,13 +630,13 @@ int main( int argc, char *argv[] )
root = NULL;
//these only serve to show info. no action is taken
if( args.contains( "-boot", Qt::CaseInsensitive ) )
if( args.contains( "-boot", Qt::CaseInsensitive ) || args.contains( "-all", Qt::CaseInsensitive ) )
{
qDebug() << "checking boot1 & 2...";
ShowBootInfo( nand.Boot1Version(), nand.Boot2Infos() );
}
if( args.contains( "-fs", Qt::CaseInsensitive ) )
if( args.contains( "-fs", Qt::CaseInsensitive ) || args.contains( "-all", Qt::CaseInsensitive ) )
{
qDebug() << "checking uid.sys...";
QByteArray ba = nand.GetData( "/sys/uid.sys" );
@ -662,13 +663,13 @@ int main( int argc, char *argv[] )
}
}
if( args.contains( "-clInfo", Qt::CaseInsensitive ) )
if( args.contains( "-clInfo", Qt::CaseInsensitive ) || args.contains( "-all", Qt::CaseInsensitive ) )
{
qDebug() << "checking for lost clusters...";
CheckLostClusters();
}
if( args.contains( "-spare", Qt::CaseInsensitive ) )
if( args.contains( "-spare", Qt::CaseInsensitive ) || args.contains( "-all", Qt::CaseInsensitive ) )
{
qDebug() << "verifying ecc...";
CheckEcc();

View File

@ -22,7 +22,7 @@ MainWindow::MainWindow( QWidget *parent ) : QMainWindow( parent ), ui( new Ui::M
//TODO, really get these paths from settings
QString cachePath = "./NUS_cache";
QString cachePath = "../NUS_cache";
QString nandPath = "./dump";

View File

@ -0,0 +1,11 @@
#include <QtGui/QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

View File

@ -0,0 +1,662 @@
#include "mainwindow.h"
#include "newnandbin.h"
#include "ui_mainwindow.h"
#include "../WiiQt/settingtxtdialog.h"
#include "../WiiQt/tiktmd.h"
#include "../WiiQt/tools.h"
#include "../WiiQt/wad.h"
MainWindow::MainWindow( QWidget *parent ) : QMainWindow( parent ), ui( new Ui::MainWindow ), nus ( this ), nand ( this )
{
ui->setupUi(this);
nandInited = false;
root = NULL;
uidDirty = false;
sharedDirty = false;
nandDirty = false;
Wad::SetGlobalCert( QByteArray( (const char*)&certs_dat, CERTS_DAT_SIZE ) );
//connect to the nus object so we can respond to what it is saying with pretty stuff in the gui
connect( &nus, SIGNAL( SendDownloadProgress( int ) ), ui->progressBar_dl, SLOT( setValue( int ) ) );
connect( &nus, SIGNAL( SendTitleProgress( int ) ), ui->progressBar_title, SLOT( setValue( int ) ) );
connect( &nus, SIGNAL( SendTotalProgress( int ) ), ui->progressBar_whole, SLOT( setValue( int ) ) );
connect( &nus, SIGNAL( SendText( QString ) ), ui->statusBar, SLOT( showMessage( QString ) ) );
connect( &nus, SIGNAL( SendError( const QString &, NusJob ) ), this, SLOT( GetError( const QString &, NusJob ) ) );
connect( &nus, SIGNAL( SendDone() ), this, SLOT( NusIsDone() ) );
connect( &nus, SIGNAL( SendData( NusJob ) ), this, SLOT( ReceiveTitleFromNus( NusJob) ) );
//connect to the nand.bin to get text and crap from it
connect( &nand, SIGNAL( SendError( const QString & ) ), this, SLOT( GetError( const QString & ) ) );
connect( &nand, SIGNAL( SendText( QString ) ), ui->statusBar, SLOT( showMessage( QString ) ) );
//TODO, really get these paths from settings
QString cachePath = "../NUS_cache";
QString nandPath = "./testNand.bin";
//QString keyPath = "../keys.bin";
ui->lineEdit_cachePath->setText( cachePath );
ui->lineEdit_nandPath->setText( nandPath );
//ui->lineEdit_keys->setText( keyPath );
nus.SetCachePath( cachePath );
}
MainWindow::~MainWindow()
{
delete ui;
}
//some slots to respond to the NUS downloader
void MainWindow::GetError( const QString &message, NusJob job )
{
QString dataStuff = QString( "%1 items:" ).arg( job.data.size() );
for( int i = 0; i < job.data.size(); i++ )
dataStuff += QString( " %1" ).arg( job.data.at( i ).size(), 0, 16, QChar( ' ' ) );
QString str = tr( "<b>Error getting title from NUS: %1</b>" ).arg( message );
QString j = QString( "NusJob( %1, %2, %3, %4 )<br>" )
.arg( job.tid, 16, 16, QChar( '0' ) )
.arg( job.version ).arg( job.decrypt ? "decrypted" : "encrypted" )
.arg( dataStuff );
ui->plainTextEdit_log->appendHtml( str );
ui->plainTextEdit_log->appendHtml( j );
}
//get error from nand.bin
void MainWindow::GetError( const QString &message )
{
QString str = tr( "<b>Nand object Error: %1</b>" ).arg( message );
ui->plainTextEdit_log->appendHtml( str );
}
void MainWindow::ShowMessage( const QString& mes )
{
QString str = mes + "<br>";
ui->plainTextEdit_log->appendHtml( str );
}
void MainWindow::NusIsDone()
{
QString str = tr( "NUS object is done working<br>" );
ui->plainTextEdit_log->appendHtml( str );
ui->statusBar->showMessage( tr( "Done" ), 5000 );
//make sure there is a setting.txt
QTreeWidgetItem *item = ItemFromPath( "/title/00000001/00000002/data/setting.txt" );
if( !item )
{
QByteArray ba = SettingTxtDialog::Edit( this ); //call a dialog to create a new setting.txt
if( !ba.isEmpty() ) //if the dialog returned anything ( cancel wasnt pressed ) write that new setting.txt to the nand
{
quint16 r = nand.CreateEntry( "/title/00000001/00000002/data/setting.txt", 0x1000, 1, NAND_FILE, NAND_READ, NAND_READ, NAND_READ );
if( !r )
{
ShowMessage( "<b>Error creating setting.txt. maybe some folders are missing?</b>" );
}
else
{
if( !nand.SetData( r, ba) )
{
ShowMessage( "<b>Error writing data for setting.txt.</b>" );
}
else
{
nandDirty = true;
UpdateTree();
}
}
}
}
//nand.Delete( "/title/00000001/00000002/content/title.tmd" );
if( nandDirty )
{
if( !FlushNand() )
{
ShowMessage( "<b>Error flushing nand. Maybe you used too much TP?</b>" );
}
nandDirty = false;
}
}
void MainWindow::ReceiveTitleFromNus( NusJob job )
{
QString str = tr( "Received a completed download from NUS" );
//QString title = QString( "%1v%2" ).arg( job.tid, 16, 16, QChar( '0' ) ).arg( job.version );
ui->plainTextEdit_log->appendHtml( str );
//do something with the data we got
if( InstallNUSItem( job ) )
nandDirty = true;
}
//clicked the button to get a title
void MainWindow::on_pushButton_GetTitle_clicked()
{
if( !nandInited && !InitNand( ui->lineEdit_nandPath->text() ) )
return;
bool ok = false;
bool wholeUpdate = false;
quint64 tid = 0;
quint32 ver = 0;
if( ui->lineEdit_tid->text().size() == 4 )
{
wholeUpdate = true;
}
else
{
tid = ui->lineEdit_tid->text().toLongLong( &ok, 16 );
if( !ok )
{
ShowMessage( "<b>Error converting \"" + ui->lineEdit_tid->text() + "\" to a hex number.</b>" );
return;
}
ver = TITLE_LATEST_VERSION;
if( !ui->lineEdit_version->text().isEmpty() )
{
ver = ui->lineEdit_version->text().toInt( &ok, 10 );
if( !ok )
{
ShowMessage( "<b>Error converting \"" + ui->lineEdit_version->text() + "\" to a decimal number.</b>" );
return;
}
if( ver > 0xffff )
{
ShowMessage( tr( "<b>Version %1 is too high. Max is 65535</b>" ).arg( ver ) );
return;
}
}
}
//decide how we want nus to give us the title
bool decrypt = true;
nus.SetCachePath( ui->lineEdit_cachePath->text() );
if( wholeUpdate )
{
if( !nus.GetUpdate( ui->lineEdit_tid->text(), decrypt ) )
{
ShowMessage( tr( "<b>I dont know the titles that were in the %1 update</b>" ).arg( ui->lineEdit_tid->text() ) );
return;
}
}
else
{
nus.Get( tid, decrypt, ver );
}
}
//search for a path to use as the nand basepath
void MainWindow::on_pushButton_nandPath_clicked()
{
QString f = QFileDialog::getOpenFileName( this, tr( "Select nand.bin" ) );
if( f.isEmpty() )
return;
ui->lineEdit_nandPath->setText( f );
nus.SetCachePath( ui->lineEdit_cachePath->text() );
}
//nand-dump -> setting.txt
void MainWindow::on_actionSetting_txt_triggered()
{
if( !nandInited )
return;
QTreeWidgetItem *it = ItemFromPath( "/title/00000001/00000002/data/setting.txt" );
if( !it )
{
ShowMessage( tr( "<b>There is no setting.txt found in %1</b>" )
.arg( QFileInfo( ui->lineEdit_nandPath->text() ).absoluteFilePath() ) );
return;
}
QByteArray ba = nand.GetData( "/title/00000001/00000002/data/setting.txt" ); //read the current setting.txt
ba = SettingTxtDialog::Edit( this, ba ); //call a dialog to edit that existing file and store the result in the same bytearray
if( !ba.isEmpty() ) //if the dialog returned anything ( cancel wasnt pressed ) write that new setting.txt to the nand dump
nand.SetData( "/title/00000001/00000002/data/setting.txt", ba );
}
//nand-dump -> flush
void MainWindow::on_actionFlush_triggered()
{
if( !nandInited )
FlushNand();
}
//nand-dump -> ImportWad
void MainWindow::on_actionImportWad_triggered()
{
if( !nandInited && !InitNand( ui->lineEdit_nandPath->text() ) )
{
ShowMessage( tr( "<b>Error setting the basepath of the nand to %1</b>" ).arg( QFileInfo( ui->lineEdit_nandPath->text() ).absoluteFilePath() ) );
return;
}
QString fn = QFileDialog::getOpenFileName( this, tr("Wad files(*.wad)"), QCoreApplication::applicationDirPath(), tr("WadFiles (*.wad)") );
if( fn.isEmpty() )
return;
QByteArray data = ReadFile( fn );
if( data.isEmpty() )
return;
Wad wad( data );
if( !wad.IsOk() )
{
ShowMessage( tr( "Wad data not ok for \"%1\"" ).arg( fn ) );
return;
}
//work smart, not hard... just turn the wad into a NUSJob and reused the same code to install it
NusJob job;
job.tid = wad.Tid();
job.data << wad.getTmd();
job.data << wad.getTik();
Tmd t( wad.getTmd() );
job.version = t.Version();
quint16 cnt = t.Count();
for( quint16 i = 0; i < cnt; i++ )
{
job.data << wad.Content( i );
}
job.decrypt = true;
ShowMessage( tr( "Installing %1 to nand" ).arg( fn ) );
InstallNUSItem( job );
}
void MainWindow::on_actionNew_nand_from_keys_triggered()
{
QString path = NewNandBin::GetNewNandPath( this );
if( path.isEmpty() )
return;
InitNand( path );
ui->lineEdit_nandPath->setText( path );
}
void MainWindow::on_pushButton_initNand_clicked()
{
if( ui->lineEdit_nandPath->text().isEmpty() )
{
ShowMessage( "<b>Please enter a path for nand.bin<\b>" );
return;
}
InitNand( ui->lineEdit_nandPath->text() );
}
bool MainWindow::InitNand( const QString &path )
{
nandInited = false;
sharedDirty = false;
nandDirty = false;
if( !nand.SetPath( path ) || !nand.InitNand() )
return false;
if( !UpdateTree() )
return false;
//setup the uid
QTreeWidgetItem *it = ItemFromPath( "/sys/uid.sys" );
if( !it )
{
uid.CreateNew( true );
if( !nand.CreateEntry( "/sys/uid.sys", 0, 0, NAND_FILE, NAND_RW, NAND_RW, 0 ) )
{
ShowMessage( "<b>Error creating new uid.sys</b>" );
return false;
}
uidDirty = true;
}
else
{
QByteArray ba = nand.GetData( "/sys/uid.sys" );
uid = UIDmap( ba );
}
//set up the shared map
it = ItemFromPath( "/shared1/content.map" );
if( !it )
{
if( !nand.CreateEntry( "/shared1/content.map", 0, 0, NAND_FILE, NAND_RW, NAND_RW, 0 ) )
{
sharedDirty = true;
ShowMessage( "<b>Error creating new content.map</b>" );
return false;
}
}
else
{
QByteArray ba = nand.GetData( "/shared1/content.map" );
shared = SharedContentMap( ba );
if( !shared.Check() )//i really dont want to create a new one and rewrite all the contents to match it
ShowMessage( "<b>Something about the shared map isnt right, but im using it anyways</b>" );
}
//nand.Delete( "/title/00000001/00000002/content/title.tmd" );
nandInited = true;
ShowMessage( "Set path to nand as " + path );
return true;
}
//this one is kinda important
// it is in charge of writing the uid, content map, and all metadata to the nand.
//failing to do this will result in any changes not being applied
bool MainWindow::FlushNand()
{
//qDebug() << "MainWindow::FlushNand()";
bool r1 = true;
bool r2 = true;
if( uidDirty && !nand.SetData( "/sys/uid.sys", uid.Data() ) )
r1 = false;
else
uidDirty = false;
if( sharedDirty && !nand.SetData( "/shared1/content.map", shared.Data() ) )
r2 = false;
else
sharedDirty = false;
return ( nand.WriteMetaData() && r1 && r2 );
}
QTreeWidgetItem *MainWindow::FindItem( const QString &s, QTreeWidgetItem *parent )
{
int cnt = parent->childCount();
for( int i = 0; i <cnt; i++ )
{
QTreeWidgetItem *r = parent->child( i );
if( r->text( 0 ) == s )
{
return r;
}
}
return NULL;
}
QTreeWidgetItem *MainWindow::ItemFromPath( const QString &path )
{
QTreeWidgetItem *item = root;
if( !path.startsWith( "/" ) || path.contains( "//" ))
{
return NULL;
}
int slash = 1;
while( slash )
{
int nextSlash = path.indexOf( "/", slash + 1 );
QString lookingFor = path.mid( slash, nextSlash - slash );
item = FindItem( lookingFor, item );
if( !item )
{
//qWarning() << "ItemFromPath ->item not found" << path;
return NULL;
}
slash = nextSlash + 1;
}
return item;
}
QString MainWindow::PathFromItem( QTreeWidgetItem *item )
{
QString ret;
while( item )
{
ret.prepend( "/" + item->text( 0 ) );
item = item->parent();
if( item->text( 0 ) == "/" )// dont add the root
break;
}
return ret;
}
bool MainWindow::UpdateTree()
{
//set up the tree so we know what all is in the nand without asking for it every time
if( root )
delete root;
QTreeWidgetItem *r = nand.GetTree();
if( r->childCount() != 1 || r->child( 0 )->text( 0 ) != "/" )
{
ShowMessage( "The nand FS is seriously broken. I Couldn't even find a correct root" );
return false;
}
root = r->takeChild( 0 );
delete r;
return true;
}
quint16 MainWindow::CreateIfNeeded( const QString &path, quint32 uid, quint16 gid, quint8 attr, quint8 user_perm, quint8 group_perm, quint8 other_perm )
{
// qDebug() << "MainWindow::CreateIfNeeded" << path;
QTreeWidgetItem *item = ItemFromPath( path );
if( !item )
{
quint16 ret = nand.CreateEntry( path, uid, gid, attr, user_perm, group_perm, other_perm );
if( ret && UpdateTree() )
return ret;
return 0;
}
//TODO - if the item already exists, check that its attributes match the expected ones
return item->text( 1 ).toInt();
}
bool MainWindow::InstallSharedContent( const QByteArray stuff, const QByteArray hash )
{
//qDebug() << "MainWindow::InstallSharedContent";
QByteArray h;
if( hash.isEmpty() )
h = GetSha1( stuff );
else
h = hash;
QString cid = shared.GetAppFromHash( hash );
if( !cid.isEmpty() ) //this one is already installed
return true;
//qDebug() << "will create new";
//get next available cid in the shared map
cid = shared.GetNextEmptyCid();
shared.AddEntry( cid, h );
sharedDirty = true;
//qDebug() << "next cid" << cid;
//create the file
quint16 r = CreateIfNeeded( "/shared1/" + cid + ".app", 0, 0, NAND_FILE, NAND_RW, NAND_RW, 0 );
if( !r )
return false;
//write the data to the file
return nand.SetData( r, stuff );
}
bool MainWindow::InstallNUSItem( NusJob job )
{
QString title = QString( "%1 v%2" ).arg( job.tid, 16, 16, QChar( '0' ) ).arg( job.version );
qDebug() << "MainWindow::InstallNUSItem" << title;
quint16 r;
quint32 _uid;
quint16 _gid;
quint16 cnt;
bool deleted = false;
QTreeWidgetItem *content;
if( !job.tid || !job.data.size() > 2 )
{
qWarning() << "bad sizes";
ShowMessage( "<b>Error installing title " + title + " to nand</b>" );
return false;
}
QString tid = QString( "%1" ).arg( job.tid, 16, 16, QChar( '0' ) );
QString upper = tid.left( 8 );
QString lower = tid.right( 8 );
Tmd t( job.data.takeFirst() );
Ticket ticket( job.data.takeFirst() );
if( t.Tid() != job.tid || ticket.Tid() != job.tid )
{
qWarning() << "bad tid";
goto error;
}
cnt = t.Count();
if( job.data.size() != cnt )
{
qWarning() << "content count";
goto error;
}
//qDebug() << "uidDirty" << uidDirty;
if( !uidDirty )
{
uidDirty = !uid.GetUid( job.tid, false );
}
//qDebug() << "uidDirty" << uidDirty;
_uid = uid.GetUid( job.tid );
_gid = t.Gid();
if( !_uid )
{
qWarning() << "no uid";
goto error;
}
//create all the folders
if( !CreateIfNeeded( "/ticket/" + upper, 0, 0, NAND_DIR, NAND_RW, NAND_RW, 0 ) )
{ qWarning() << "can't create ticket+upper folder";goto error;}
if( !CreateIfNeeded( "/title/" + upper, 0, 0, NAND_DIR, NAND_RW, NAND_RW, NAND_READ ) )
{ qWarning() << "can't create title+upper folder";goto error;}
if( !CreateIfNeeded( "/title/" + upper + "/" + lower, 0, 0, NAND_DIR, NAND_RW, NAND_RW, NAND_READ ) )
{ qWarning() << "can't create title+upper+lower folder";goto error;}
if( !CreateIfNeeded( "/title/" + upper + "/" + lower + "/content", 0, 0, NAND_DIR, NAND_RW, NAND_RW, 0 ) )
{ qWarning() << "can't create content folder";goto error;}
if( !CreateIfNeeded( "/title/" + upper + "/" + lower + "/data", _uid, _gid, NAND_DIR, NAND_RW, 0, 0 ) )
{ qWarning() << "can't create data folder";goto error;}
//delete old tmd/.apps and whatever else in the content folder
content = ItemFromPath( "/title/" + upper + "/" + lower + "/content" );
cnt = content->childCount();
for ( quint16 i = 0; i < cnt; i++ )
{
if( !nand.Delete( "/title/" + upper + "/" + lower + "/content/" + content->child( i )->text( 0 ) ) )
{ qWarning() << "error deleting old title"; goto error; }
deleted = true;
}
if( deleted )
{
//nand.WriteMetaData();
UpdateTree();
ShowMessage( tr( "Deleted old TMD and private contents for\n%1" ).arg( title ) );
}
cnt = t.Count();
//install ticket
r = CreateIfNeeded( "/ticket/" + upper + "/" + lower + ".tik", 0, 0, NAND_FILE, NAND_RW, NAND_RW, 0 );
if( !r )
{ qWarning() << "can't create ticket";goto error;}
if( !nand.SetData( r, ticket.Data() ) )
{ qWarning() << "can't write to ticket";goto error;}
//install tmd
r = CreateIfNeeded( "/title/" + upper + "/" + lower + "/content/title.tmd", 0, 0, NAND_FILE, NAND_RW, NAND_RW, 0 );
if( !r )
{ qWarning() << "can't create tmd";goto error;}
if( !nand.SetData( r, t.Data() ) )
{ qWarning() << "can't write to tmd";goto error;}
//install contents
//qDebug() << "will install" << cnt << "contents";
for( quint16 i = 0; i < cnt; i++ )
{
//qDebug() << "installing" << i;
//make sure the data is decrypted
QByteArray decData;
QByteArray hash;
if( job.decrypt )
{
decData = job.data.takeFirst();
if( (quint32)decData.size() != t.Size( i ) )
{
qDebug() << "wtf - size";
decData.resize( t.Size( i ) );
}
}
else
{
//decrypt the data
QByteArray encData = job.data.takeFirst();
AesSetKey( ticket.DecryptedKey() );
decData = AesDecrypt( i, encData );
decData.resize( t.Size( i ) );
}
//check the hash
hash = GetSha1( decData );
if( hash != t.Hash( i ) )
{
qWarning() << "hash" << i << "\n" << hash.toHex() << "\n" << t.Hash( i ).toHex();
goto error;
}
//install the content
if( t.Type( i ) == 1 )//private
{
r = CreateIfNeeded( "/title/" + upper + "/" + lower + "/content/" + t.Cid( i ) + ".app" , 0, 0, NAND_FILE, NAND_RW, NAND_RW, 0 );
if( !r )
{
qWarning() << "cant create content" << i;
goto error;
}
if ( !nand.SetData( r, decData ) )
{
qWarning() << "cant write content" << i;
goto error;
}
}
else if( t.Type( i ) == 0x8001 )//shared
{
if ( !InstallSharedContent( decData, hash ) )
{
qWarning() << "error installing shared" << i << hash.toHex();
goto error;
}
}
else
{
qWarning() << "type" << hex << t.Type( i );
goto error;
}
}
qDebug() << "done installing";
ShowMessage( "Installed title " + title + " to nand" );
//nand.Delete( "/title/" + upper );
//UpdateTree();
return true;
error:
ShowMessage( "<b>Error installing title " + title + " to nand</b>" );
return false;
}
//help -> about
void MainWindow::on_actionAbout_triggered()
{
QString txt = tr( "This is an example program from WiiQt. It is designed to write titles to a nand.bin and even create one from scratch."
"<br><br>PLEASE BE AWARE, THIS IS NOT VERY WELL TESTED AND AS OF RIGHT NOW."
" IT SHOULD ONLY BE USED BY PEOPLE THAT KNOW HOW TO VERIFY THE FILES IT PRODUCES. AND HAVE A WAY TO FIX A BRICKED WII SHOULD THIS PROGRAM HAVE BUGS"
"<br><br>YOU HAVE BEEN WARNED"
"<br>giantpune" );
QMessageBox::critical( this, tr( "About" ), txt );
}

View File

@ -0,0 +1,67 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "../WiiQt/includes.h"
#include "../WiiQt/nusdownloader.h"
#include "../WiiQt/nandbin.h"
#include "../WiiQt/uidmap.h"
#include "../WiiQt/sharedcontentmap.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow( QWidget *parent = 0 );
~MainWindow();
private:
Ui::MainWindow *ui;
QTreeWidgetItem *root;
bool nandInited;
bool uidDirty;
bool sharedDirty;
bool nandDirty;
NusDownloader nus;
NandBin nand;
UIDmap uid;
SharedContentMap shared;
bool FlushNand();
void ShowMessage( const QString& mes );
bool InitNand( const QString &path );
bool UpdateTree();
QString PathFromItem( QTreeWidgetItem *item );
QTreeWidgetItem *ItemFromPath( const QString &path );
QTreeWidgetItem *FindItem( const QString &s, QTreeWidgetItem *parent );
bool InstallNUSItem( NusJob job );
quint16 CreateIfNeeded( const QString &path, quint32 uid, quint16 gid, quint8 attr, quint8 user_perm, quint8 group_perm, quint8 other_perm );
bool InstallSharedContent( const QByteArray stuff, const QByteArray hash = QByteArray() );
public slots:
//slots for getting info from the NUS downloader
void GetError( const QString &message, NusJob job );
void GetError( const QString &message );
void NusIsDone();
void ReceiveTitleFromNus( NusJob job );
private slots:
void on_actionAbout_triggered();
void on_pushButton_initNand_clicked();
void on_actionNew_nand_from_keys_triggered();
void on_actionFlush_triggered();
void on_actionSetting_txt_triggered();
void on_actionImportWad_triggered();
void on_pushButton_nandPath_clicked();
void on_pushButton_GetTitle_clicked();
};
#endif // MAINWINDOW_H

View File

@ -0,0 +1,237 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>654</width>
<height>507</height>
</rect>
</property>
<property name="windowTitle">
<string>NUS NandBuilder</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QGridLayout" name="gridLayout_2">
<property name="margin">
<number>2</number>
</property>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="lineEdit_tid">
<property name="text">
<string/>
</property>
<property name="maxLength">
<number>16</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4_version">
<property name="text">
<string>v</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_version">
<property name="maximumSize">
<size>
<width>107</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="maxLength">
<number>5</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_GetTitle">
<property name="text">
<string>Get It!</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="lineEdit_cachePath"/>
</item>
<item>
<widget class="QPushButton" name="pushButton_CachePathBrowse">
<property name="text">
<string>Local Cache</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_progDl">
<property name="text">
<string>Download</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QProgressBar" name="progressBar_dl">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_progTitle">
<property name="text">
<string>Title</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QProgressBar" name="progressBar_title">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_progTotal">
<property name="text">
<string>Total</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QProgressBar" name="progressBar_whole">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>nand.bin</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_nandPath">
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="pushButton_nandPath">
<property name="text">
<string>Search...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="pushButton_initNand">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Init Nand</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QPlainTextEdit" name="plainTextEdit_log"/>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>654</width>
<height>27</height>
</rect>
</property>
<widget class="QMenu" name="menuNand_Dump">
<property name="title">
<string>Nand Dump</string>
</property>
<addaction name="actionSetting_txt"/>
<addaction name="actionFlush"/>
<addaction name="actionImportWad"/>
<addaction name="actionNew_nand_from_keys"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
</property>
<addaction name="actionAbout"/>
</widget>
<addaction name="menuNand_Dump"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
<action name="actionSetting_txt">
<property name="text">
<string>Setting.txt...</string>
</property>
</action>
<action name="actionFlush">
<property name="text">
<string>Flush</string>
</property>
</action>
<action name="actionImportWad">
<property name="text">
<string>Import Wad</string>
</property>
</action>
<action name="actionNew_nand_from_keys">
<property name="text">
<string>New nand...</string>
</property>
<property name="shortcut">
<string>Ctrl+N</string>
</property>
</action>
<action name="actionAbout">
<property name="text">
<string>About</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,123 @@
#include "newnandbin.h"
#include "ui_newnandbin.h"
#include "../WiiQt/tools.h"
NewNandBin::NewNandBin( QWidget *parent, QList<quint16> badBlocks ) : QDialog(parent), ui(new Ui::NewNandBin), nand( this )
{
ui->setupUi(this);
foreach( quint16 block, badBlocks )
{
QString txt = QString( "%1" ).arg( block );
if( !ui->listWidget_badBlocks->findItems( txt, Qt::MatchExactly ).isEmpty() )
ui->listWidget_badBlocks->addItem( txt );
}
}
NewNandBin::~NewNandBin()
{
delete ui;
}
void NewNandBin::on_pushButton_keys_clicked()
{
QString f = QFileDialog::getOpenFileName( this, tr( "Select Keys.bin" ) );
if( f.isEmpty() )
return;
ui->lineEdit_keys->setText( f );
}
void NewNandBin::on_pushButton_boot_clicked()
{
QString f = QFileDialog::getOpenFileName( this, tr( "Select Boot 1 & 2" ) );
if( f.isEmpty() )
return;
ui->lineEdit_boot->setText( f );
}
void NewNandBin::on_pushButton_dest_clicked()
{
QString f = QFileDialog::getSaveFileName( this, tr( "Output file" ) );
if( f.isEmpty() )
return;
ui->lineEdit_dest->setText( f );
}
QList<quint16> NewNandBin::BadBlocks()
{
QList<quint16> ret;
quint16 cnt = ui->listWidget_badBlocks->count();
for( quint16 i = 0; i < cnt; i++ )
{
bool ok = false;
quint16 num = ui->listWidget_badBlocks->item( i )->text().toInt( &ok );
if( ok )
ret << num;
}
return ret;
}
void NewNandBin::on_pushButton_bb_add_clicked()
{
quint16 val = ui->spinBox->value();
if( !BadBlocks().contains( val ) )
{
ui->listWidget_badBlocks->addItem( QString( "%1" ).arg( val ) );
//QListWidgetItem *i = new QListWidgetItem( QString( "%1" ).arg( val ), ui->listWidget_badBlocks );
}
}
void NewNandBin::on_pushButton_bb_rm_clicked()
{
QList<QListWidgetItem *> items = ui->listWidget_badBlocks->selectedItems();
foreach( QListWidgetItem *item, items )
{
ui->listWidget_badBlocks->removeItemWidget( item );
delete item;
}
}
//ok clicked
void NewNandBin::on_buttonBox_accepted()
{
if( ui->lineEdit_keys->text().isEmpty() || ui->lineEdit_boot->text().isEmpty() || ui->lineEdit_dest->text().isEmpty() )
{
QMessageBox::warning( this, tr( "Error" ), tr( "Required feilds are empty" ) );
return;
}
QByteArray keys = ReadFile( ui->lineEdit_keys->text() );
QByteArray boots = ReadFile( ui->lineEdit_boot->text() );
if( keys.size() != 0x400 || boots.size() != 0x108000 )
{
QMessageBox::warning( this, tr( "Error" ), tr( "The keys or boot1/2 is not correct" ) );
return;
}
if( !nand.CreateNew( ui->lineEdit_dest->text(), keys, boots, BadBlocks() ) )
{
qDebug() << "error creating nand.bin";
return;
}
//qDebug() << "created nand, trying to add default entries";
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 the new nand";
return;
}
ret = ui->lineEdit_dest->text();
}
QString NewNandBin::GetNewNandPath( QWidget *parent, QList<quint16> badBlocks )
{
NewNandBin d( parent, badBlocks );
if( !d.exec() )
return QString();
return d.ret;
}

View File

@ -0,0 +1,38 @@
#ifndef NEWNANDBIN_H
#define NEWNANDBIN_H
#include "../WiiQt/includes.h"
#include "../WiiQt/nandbin.h"
namespace Ui {
class NewNandBin;
}
class NewNandBin : public QDialog
{
Q_OBJECT
public:
explicit NewNandBin( QWidget *parent = 0, QList<quint16>badBlocks = QList<quint16>() );
~NewNandBin();
static QString GetNewNandPath( QWidget *parent = 0, QList<quint16>badBlocks = QList<quint16>() );
private:
Ui::NewNandBin *ui;
QList<quint16> BadBlocks();
NandBin nand;
QString ret;
private slots:
void on_buttonBox_accepted();
void on_pushButton_bb_rm_clicked();
void on_pushButton_bb_add_clicked();
void on_pushButton_dest_clicked();
void on_pushButton_boot_clicked();
void on_pushButton_keys_clicked();
};
#endif // NEWNANDBIN_H

View File

@ -0,0 +1,234 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NewNandBin</class>
<widget class="QDialog" name="NewNandBin">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>427</width>
<height>290</height>
</rect>
</property>
<property name="windowTitle">
<string>New Nand</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0" colspan="2">
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QPushButton" name="pushButton_keys">
<property name="text">
<string>Keys</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_keys">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="pushButton_boot">
<property name="text">
<string>Boot 1 &amp;&amp; 2</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEdit_boot">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="pushButton_dest">
<property name="text">
<string>Destination</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineEdit_dest">
<property name="text">
<string>./testNand.bin</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox_bad">
<property name="title">
<string>Bad Blocks</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="margin">
<number>2</number>
</property>
<property name="spacing">
<number>2</number>
</property>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>2</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>2</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>2</number>
</property>
<item>
<widget class="QSpinBox" name="spinBox">
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>8</number>
</property>
<property name="maximum">
<number>4079</number>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>2</number>
</property>
<item>
<widget class="QPushButton" name="pushButton_bb_add">
<property name="maximumSize">
<size>
<width>37</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>&gt;&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_bb_rm">
<property name="maximumSize">
<size>
<width>37</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>&lt;&lt;</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QListWidget" name="listWidget_badBlocks">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>149</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>NewNandBin</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>NewNandBin</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,49 @@
#-------------------------------------------------
#
# Project created by QtCreator 2010-12-02T23:30:12
#
#-------------------------------------------------
QT += core gui\
network
TARGET = ohneschwanzenegger
TEMPLATE = app
DEFINES += NAND_BIN_CAN_WRITE
SOURCES += main.cpp\
mainwindow.cpp \
../WiiQt/tools.cpp \
../WiiQt/sharedcontentmap.cpp \
../WiiQt/tiktmd.cpp \
../WiiQt/nusdownloader.cpp \
../WiiQt/uidmap.cpp \
../WiiQt/nanddump.cpp \
../WiiQt/settingtxtdialog.cpp \
../WiiQt/wad.cpp \
../WiiQt/aes.c \
../WiiQt/sha1.c \
newnandbin.cpp \
../WiiQt/nandbin.cpp \
../WiiQt/nandspare.cpp\
../WiiQt/blocks0to7.cpp
HEADERS += mainwindow.h \
../WiiQt/tools.h \
../WiiQt/uidmap.h \
../WiiQt/sharedcontentmap.h \
../WiiQt/tiktmd.h \
../WiiQt/nusdownloader.h \
../WiiQt/uidmap.h \
../WiiQt/nanddump.h \
../WiiQt/settingtxtdialog.h \
../WiiQt/wad.h \
newnandbin.h \
../WiiQt/nandbin.h \
../WiiQt/nandspare.h\
../WiiQt/blocks0to7.h
FORMS += mainwindow.ui \
../WiiQt/settingtxtdialog.ui \
newnandbin.ui