mirror of
https://github.com/martravi/wiiqt.git
synced 2024-11-22 00:59:18 +01:00
* fix bug in nand.bin class: when creating a new nand.bin, initialize fst_pos in fst entries
* insert new entries in nand.bin at the start of the chain instead of at the end ( to closer mimic IOS FS behavior ) * oneswanzenegger: create /sys/cert.sys when creating a new nand.bin
This commit is contained in:
parent
fe3ec0de0a
commit
8e3d07daad
@ -90,6 +90,9 @@ bool NandBin::CreateNew( const QString &path, const QByteArray &keys, const QByt
|
||||
fats.clear();
|
||||
memset( &fsts, 0, sizeof( fst_t ) * 0x17ff );
|
||||
|
||||
for( quint16 i = 0; i < 0x17ff; i++ )
|
||||
fsts[ i ].fst_pos = i;
|
||||
|
||||
//reserve blocks 0 - 7
|
||||
for( quint16 i = 0; i < 0x40; i++ )
|
||||
{
|
||||
@ -1193,7 +1196,12 @@ quint16 NandBin::CreateEntry( const QString &path, quint32 uid, quint16 gid, qui
|
||||
if( !ret )
|
||||
return 0;
|
||||
|
||||
fsts[ entryNo ].sib = ret;
|
||||
//method 1: this works, and the nand is bootable. but doesnt mimic the IOS FS driver. ( my entries appear in reversed order when walking the FS )
|
||||
//fsts[ entryNo ].sib = ret;
|
||||
|
||||
//method 2: trying to mimic the IOS FS driver ( insert new entries at the start of the chain, instead of the end )
|
||||
fsts[ ret ].sib = fsts[ parIdx ].sub;
|
||||
fsts[ parIdx ].sub = ret;
|
||||
}
|
||||
QTreeWidgetItem *child = CreateItem( par, name, 0, ret, uid, gid, 0, fsts[ ret ].attr, 0 );
|
||||
if( attr == NAND_FILE )
|
||||
|
@ -56,13 +56,13 @@ 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( ' ' ) );
|
||||
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 );
|
||||
.arg( job.tid, 16, 16, QChar( '0' ) )
|
||||
.arg( job.version ).arg( job.decrypt ? "decrypted" : "encrypted" )
|
||||
.arg( dataStuff );
|
||||
|
||||
|
||||
ui->plainTextEdit_log->appendHtml( str );
|
||||
@ -92,44 +92,44 @@ void MainWindow::NusIsDone()
|
||||
QTreeWidgetItem *item = ItemFromPath( "/title/00000001/00000002/data/setting.txt" );
|
||||
if( !item )
|
||||
{
|
||||
quint8 reg = SETTING_TXT_UNK;
|
||||
if( ui->lineEdit_tid->text().endsWith( "e", Qt::CaseInsensitive ) && ui->lineEdit_tid->text().size() == 4 )
|
||||
reg = SETTING_TXT_PAL;
|
||||
if( ui->lineEdit_tid->text().endsWith( "j", Qt::CaseInsensitive ) && ui->lineEdit_tid->text().size() == 4 )
|
||||
reg = SETTING_TXT_JAP;
|
||||
if( ui->lineEdit_tid->text().endsWith( "k", Qt::CaseInsensitive ) && ui->lineEdit_tid->text().size() == 4 )
|
||||
reg = SETTING_TXT_KOR;
|
||||
QByteArray ba = SettingTxtDialog::Edit( this, QByteArray(), reg ); //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) )
|
||||
quint8 reg = SETTING_TXT_UNK;
|
||||
if( ui->lineEdit_tid->text().endsWith( "e", Qt::CaseInsensitive ) && ui->lineEdit_tid->text().size() == 4 )
|
||||
reg = SETTING_TXT_PAL;
|
||||
if( ui->lineEdit_tid->text().endsWith( "j", Qt::CaseInsensitive ) && ui->lineEdit_tid->text().size() == 4 )
|
||||
reg = SETTING_TXT_JAP;
|
||||
if( ui->lineEdit_tid->text().endsWith( "k", Qt::CaseInsensitive ) && ui->lineEdit_tid->text().size() == 4 )
|
||||
reg = SETTING_TXT_KOR;
|
||||
QByteArray ba = SettingTxtDialog::Edit( this, QByteArray(), reg ); //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
|
||||
{
|
||||
ShowMessage( "<b>Error writing data for setting.txt.</b>" );
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
if( !FlushNand() )
|
||||
{
|
||||
ShowMessage( "<b>Error flushing nand. Maybe you used too much TP?</b>" );
|
||||
}
|
||||
nandDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,7 +142,7 @@ void MainWindow::ReceiveTitleFromNus( NusJob job )
|
||||
|
||||
//do something with the data we got
|
||||
if( InstallNUSItem( job ) )
|
||||
nandDirty = true;
|
||||
nandDirty = true;
|
||||
|
||||
}
|
||||
|
||||
@ -150,7 +150,7 @@ void MainWindow::ReceiveTitleFromNus( NusJob job )
|
||||
void MainWindow::on_pushButton_GetTitle_clicked()
|
||||
{
|
||||
if( !nandInited && !InitNand( ui->lineEdit_nandPath->text() ) )
|
||||
return;
|
||||
return;
|
||||
|
||||
bool ok = false;
|
||||
bool wholeUpdate = false;
|
||||
@ -158,46 +158,46 @@ void MainWindow::on_pushButton_GetTitle_clicked()
|
||||
quint32 ver = 0;
|
||||
if( ui->lineEdit_tid->text().size() == 4 )
|
||||
{
|
||||
wholeUpdate = true;
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
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 );
|
||||
nus.Get( tid, decrypt, ver );
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,7 +206,7 @@ void MainWindow::on_pushButton_nandPath_clicked()
|
||||
{
|
||||
QString f = QFileDialog::getOpenFileName( this, tr( "Select nand.bin" ) );
|
||||
if( f.isEmpty() )
|
||||
return;
|
||||
return;
|
||||
|
||||
ui->lineEdit_nandPath->setText( f );
|
||||
}
|
||||
@ -215,7 +215,7 @@ void MainWindow::on_pushButton_CachePathBrowse_clicked()
|
||||
{
|
||||
QString f = QFileDialog::getExistingDirectory( this, tr( "Select NUS Cache base folder" ) );
|
||||
if( f.isEmpty() )
|
||||
return;
|
||||
return;
|
||||
|
||||
ui->lineEdit_cachePath->setText( f );
|
||||
nus.SetCachePath( ui->lineEdit_cachePath->text() );
|
||||
@ -225,26 +225,26 @@ void MainWindow::on_pushButton_CachePathBrowse_clicked()
|
||||
void MainWindow::on_actionSetting_txt_triggered()
|
||||
{
|
||||
if( !nandInited )
|
||||
return;
|
||||
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;
|
||||
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.SetData( "/title/00000001/00000002/data/setting.txt", ba );
|
||||
}
|
||||
|
||||
//nand-dump -> flush
|
||||
void MainWindow::on_actionFlush_triggered()
|
||||
{
|
||||
if( !nandInited )
|
||||
FlushNand();
|
||||
FlushNand();
|
||||
}
|
||||
|
||||
//nand-dump -> ImportWad
|
||||
@ -252,23 +252,23 @@ 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;
|
||||
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;
|
||||
return;
|
||||
|
||||
QByteArray data = ReadFile( fn );
|
||||
if( data.isEmpty() )
|
||||
return;
|
||||
return;
|
||||
|
||||
Wad wad( data );
|
||||
if( !wad.IsOk() )
|
||||
{
|
||||
ShowMessage( tr( "Wad data not ok for \"%1\"" ).arg( fn ) );
|
||||
return;
|
||||
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
|
||||
@ -282,7 +282,7 @@ void MainWindow::on_actionImportWad_triggered()
|
||||
quint16 cnt = t.Count();
|
||||
for( quint16 i = 0; i < cnt; i++ )
|
||||
{
|
||||
job.data << wad.Content( i );
|
||||
job.data << wad.Content( i );
|
||||
}
|
||||
|
||||
job.decrypt = true;
|
||||
@ -295,7 +295,7 @@ void MainWindow::on_actionNew_nand_from_keys_triggered()
|
||||
{
|
||||
QString path = NewNandBin::GetNewNandPath( this );
|
||||
if( path.isEmpty() )
|
||||
return;
|
||||
return;
|
||||
InitNand( path );
|
||||
ui->lineEdit_nandPath->setText( path );
|
||||
}
|
||||
@ -304,8 +304,8 @@ void MainWindow::on_pushButton_initNand_clicked()
|
||||
{
|
||||
if( ui->lineEdit_nandPath->text().isEmpty() )
|
||||
{
|
||||
ShowMessage( "<b>Please enter a path for nand.bin<\b>" );
|
||||
return;
|
||||
ShowMessage( "<b>Please enter a path for nand.bin<\b>" );
|
||||
return;
|
||||
}
|
||||
InitNand( ui->lineEdit_nandPath->text() );
|
||||
}
|
||||
@ -316,45 +316,45 @@ bool MainWindow::InitNand( const QString &path )
|
||||
sharedDirty = false;
|
||||
nandDirty = false;
|
||||
if( !nand.SetPath( path ) || !nand.InitNand() )
|
||||
return false;
|
||||
return false;
|
||||
|
||||
if( !UpdateTree() )
|
||||
return false;
|
||||
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;
|
||||
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 );
|
||||
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;
|
||||
}
|
||||
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>" );
|
||||
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" );
|
||||
|
||||
@ -372,15 +372,15 @@ bool MainWindow::FlushNand()
|
||||
bool r1 = true;
|
||||
bool r2 = true;
|
||||
if( uidDirty && !nand.SetData( "/sys/uid.sys", uid.Data() ) )
|
||||
r1 = false;
|
||||
r1 = false;
|
||||
else
|
||||
uidDirty = false;
|
||||
uidDirty = false;
|
||||
|
||||
|
||||
if( sharedDirty && !nand.SetData( "/shared1/content.map", shared.Data() ) )
|
||||
r2 = false;
|
||||
r2 = false;
|
||||
else
|
||||
sharedDirty = false;
|
||||
sharedDirty = false;
|
||||
|
||||
return ( nand.WriteMetaData() && r1 && r2 );
|
||||
}
|
||||
@ -390,11 +390,11 @@ 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;
|
||||
}
|
||||
QTreeWidgetItem *r = parent->child( i );
|
||||
if( r->text( 0 ) == s )
|
||||
{
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -404,20 +404,20 @@ QTreeWidgetItem *MainWindow::ItemFromPath( const QString &path )
|
||||
QTreeWidgetItem *item = root;
|
||||
if( !path.startsWith( "/" ) || path.contains( "//" ))
|
||||
{
|
||||
return NULL;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
@ -427,10 +427,10 @@ 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;
|
||||
ret.prepend( "/" + item->text( 0 ) );
|
||||
item = item->parent();
|
||||
if( item->text( 0 ) == "/" )// dont add the root
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -439,12 +439,12 @@ 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;
|
||||
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;
|
||||
ShowMessage( "The nand FS is seriously broken. I Couldn't even find a correct root" );
|
||||
return false;
|
||||
}
|
||||
root = r->takeChild( 0 );
|
||||
delete r;
|
||||
@ -454,14 +454,14 @@ bool MainWindow::UpdateTree()
|
||||
|
||||
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;
|
||||
// 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;
|
||||
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();
|
||||
@ -472,13 +472,13 @@ bool MainWindow::InstallSharedContent( const QByteArray stuff, const QByteArray
|
||||
//qDebug() << "MainWindow::InstallSharedContent";
|
||||
QByteArray h;
|
||||
if( hash.isEmpty() )
|
||||
h = GetSha1( stuff );
|
||||
h = GetSha1( stuff );
|
||||
else
|
||||
h = hash;
|
||||
h = hash;
|
||||
|
||||
QString cid = shared.GetAppFromHash( hash );
|
||||
if( !cid.isEmpty() ) //this one is already installed
|
||||
return true;
|
||||
return true;
|
||||
//qDebug() << "will create new";
|
||||
|
||||
//get next available cid in the shared map
|
||||
@ -490,7 +490,7 @@ bool MainWindow::InstallSharedContent( const QByteArray stuff, const QByteArray
|
||||
//create the file
|
||||
quint16 r = CreateIfNeeded( "/shared1/" + cid + ".app", 0, 0, NAND_FILE, NAND_RW, NAND_RW, 0 );
|
||||
if( !r )
|
||||
return false;
|
||||
return false;
|
||||
|
||||
//write the data to the file
|
||||
return nand.SetData( r, stuff );
|
||||
@ -509,9 +509,9 @@ bool MainWindow::InstallNUSItem( NusJob job )
|
||||
QTreeWidgetItem *content;
|
||||
if( !job.tid || !job.data.size() > 2 )
|
||||
{
|
||||
qWarning() << "bad sizes";
|
||||
ShowMessage( "<b>Error installing title " + title + " to nand</b>" );
|
||||
return false;
|
||||
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 );
|
||||
@ -521,19 +521,19 @@ bool MainWindow::InstallNUSItem( NusJob job )
|
||||
Ticket ticket( job.data.takeFirst() );
|
||||
if( t.Tid() != job.tid || ticket.Tid() != job.tid )
|
||||
{
|
||||
qWarning() << "bad tid";
|
||||
goto error;
|
||||
qWarning() << "bad tid";
|
||||
goto error;
|
||||
}
|
||||
cnt = t.Count();
|
||||
if( job.data.size() != cnt )
|
||||
{
|
||||
qWarning() << "content count";
|
||||
goto error;
|
||||
qWarning() << "content count";
|
||||
goto error;
|
||||
}
|
||||
//qDebug() << "uidDirty" << uidDirty;
|
||||
if( !uidDirty )
|
||||
{
|
||||
uidDirty = !uid.GetUid( job.tid, false );
|
||||
uidDirty = !uid.GetUid( job.tid, false );
|
||||
}
|
||||
//qDebug() << "uidDirty" << uidDirty;
|
||||
_uid = uid.GetUid( job.tid );
|
||||
@ -541,8 +541,8 @@ bool MainWindow::InstallNUSItem( NusJob job )
|
||||
_gid = t.Gid();
|
||||
if( !_uid )
|
||||
{
|
||||
qWarning() << "no uid";
|
||||
goto error;
|
||||
qWarning() << "no uid";
|
||||
goto error;
|
||||
}
|
||||
|
||||
//create all the folders
|
||||
@ -566,15 +566,15 @@ bool MainWindow::InstallNUSItem( NusJob job )
|
||||
cnt = content->childCount();
|
||||
for ( quint16 i = 0; i < cnt; i++ )
|
||||
{
|
||||
if( !nand.Delete( "/title/" + upper + "/" + lower + "/content/" + content->child( i )->text( 0 ) ) )
|
||||
if( !nand.Delete( "/title/" + upper + "/" + lower + "/content/" + content->child( i )->text( 0 ) ) )
|
||||
{ qWarning() << "error deleting old title"; goto error; }
|
||||
deleted = true;
|
||||
deleted = true;
|
||||
}
|
||||
if( deleted )
|
||||
{
|
||||
//nand.WriteMetaData();
|
||||
UpdateTree();
|
||||
ShowMessage( tr( "Deleted old TMD and private contents for<br>%1" ).arg( title ) );
|
||||
//nand.WriteMetaData();
|
||||
UpdateTree();
|
||||
ShowMessage( tr( "Deleted old TMD and private contents for<br>%1" ).arg( title ) );
|
||||
}
|
||||
|
||||
cnt = t.Count();
|
||||
@ -597,64 +597,64 @@ bool MainWindow::InstallNUSItem( NusJob job )
|
||||
//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 ) );
|
||||
}
|
||||
//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;
|
||||
}
|
||||
//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;
|
||||
}
|
||||
//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" );
|
||||
@ -662,7 +662,7 @@ bool MainWindow::InstallNUSItem( NusJob job )
|
||||
//nand.Delete( "/title/" + upper );
|
||||
//UpdateTree();
|
||||
return true;
|
||||
error:
|
||||
error:
|
||||
ShowMessage( "<b>Error installing title " + title + " to nand</b>" );
|
||||
return false;
|
||||
}
|
||||
@ -671,9 +671,9 @@ error:
|
||||
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" );
|
||||
"<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 );
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ NewNandBin::NewNandBin( QWidget *parent, QList<quint16> badBlocks ) : QDialog(pa
|
||||
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 );
|
||||
QString txt = QString( "%1" ).arg( block );
|
||||
if( !ui->listWidget_badBlocks->findItems( txt, Qt::MatchExactly ).isEmpty() )
|
||||
ui->listWidget_badBlocks->addItem( txt );
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ void NewNandBin::on_pushButton_keys_clicked()
|
||||
{
|
||||
QString f = QFileDialog::getOpenFileName( this, tr( "Select Keys.bin" ), dir );
|
||||
if( f.isEmpty() )
|
||||
return;
|
||||
return;
|
||||
ui->lineEdit_keys->setText( f );
|
||||
dir = QFileInfo( f ).canonicalPath();
|
||||
}
|
||||
@ -32,7 +32,7 @@ void NewNandBin::on_pushButton_boot_clicked()
|
||||
{
|
||||
QString f = QFileDialog::getOpenFileName( this, tr( "Select Boot 1 & 2" ), dir );
|
||||
if( f.isEmpty() )
|
||||
return;
|
||||
return;
|
||||
ui->lineEdit_boot->setText( f );
|
||||
dir = QFileInfo( f ).canonicalPath();
|
||||
}
|
||||
@ -41,7 +41,7 @@ void NewNandBin::on_pushButton_dest_clicked()
|
||||
{
|
||||
QString f = QFileDialog::getSaveFileName( this, tr( "Output file" ), dir );
|
||||
if( f.isEmpty() )
|
||||
return;
|
||||
return;
|
||||
ui->lineEdit_dest->setText( f );
|
||||
dir = QFileInfo( f ).canonicalPath();
|
||||
}
|
||||
@ -52,10 +52,10 @@ QList<quint16> NewNandBin::BadBlocks()
|
||||
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;
|
||||
bool ok = false;
|
||||
quint16 num = ui->listWidget_badBlocks->item( i )->text().toInt( &ok );
|
||||
if( ok )
|
||||
ret << num;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -65,7 +65,7 @@ void NewNandBin::on_pushButton_bb_add_clicked()
|
||||
quint16 val = ui->spinBox->value();
|
||||
if( !BadBlocks().contains( val ) )
|
||||
{
|
||||
ui->listWidget_badBlocks->addItem( QString( "%1" ).arg( val ) );
|
||||
ui->listWidget_badBlocks->addItem( QString( "%1" ).arg( val ) );
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,8 +74,8 @@ 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;
|
||||
ui->listWidget_badBlocks->removeItemWidget( item );
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,35 +84,48 @@ 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;
|
||||
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;
|
||||
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() << "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() )
|
||||
|| !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;
|
||||
qWarning() << "NewNandBin::on_buttonBox_accepted -> error creating directories in the new nand";
|
||||
return;
|
||||
}
|
||||
//add cert.sys
|
||||
quint16 handle = nand.CreateEntry( "/sys/cert.sys", 0, 0, NAND_FILE, NAND_RW, NAND_RW, NAND_READ );
|
||||
if( !handle || !nand.SetData( "/sys/cert.sys", QByteArray( (const char*)&certs_dat, CERTS_DAT_SIZE ) ) )
|
||||
{
|
||||
qWarning() << "NewNandBin::on_buttonBox_accepted -> error creating cert in the new nand";
|
||||
return;
|
||||
}
|
||||
//commit changes to metadata
|
||||
if( !nand.WriteMetaData() )
|
||||
{
|
||||
qWarning() << "NewNandBin::on_buttonBox_accepted -> error writing metadata";
|
||||
return;
|
||||
}
|
||||
|
||||
ret = ui->lineEdit_dest->text();
|
||||
}
|
||||
@ -121,7 +134,7 @@ QString NewNandBin::GetNewNandPath( QWidget *parent, QList<quint16> badBlocks )
|
||||
{
|
||||
NewNandBin d( parent, badBlocks );
|
||||
if( !d.exec() )
|
||||
return QString();
|
||||
return QString();
|
||||
return d.ret;
|
||||
}
|
||||
|
||||
@ -130,13 +143,13 @@ void NewNandBin::on_pushButton_badBlockFile_clicked()
|
||||
{
|
||||
QString f = QFileDialog::getOpenFileName( this, tr( "Select File with Bad Block List" ), dir );
|
||||
if( f.isEmpty() )
|
||||
return;
|
||||
return;
|
||||
dir = QFileInfo( f ).canonicalPath();
|
||||
QString str = QString( ReadFile( f ) );
|
||||
if( str.isEmpty() )
|
||||
{
|
||||
qWarning() << "NewNandBin::on_pushButton_badBlockFile_clicked -> error reading file";
|
||||
return;
|
||||
qWarning() << "NewNandBin::on_pushButton_badBlockFile_clicked -> error reading file";
|
||||
return;
|
||||
}
|
||||
ui->listWidget_badBlocks->clear();
|
||||
|
||||
@ -144,17 +157,17 @@ void NewNandBin::on_pushButton_badBlockFile_clicked()
|
||||
QStringList lines = str.split( "\n", QString::SkipEmptyParts );
|
||||
foreach( QString line, lines )
|
||||
{
|
||||
if( line.size() > 5 )
|
||||
continue;
|
||||
bool ok = false;
|
||||
if( line.size() > 5 )
|
||||
continue;
|
||||
bool ok = false;
|
||||
|
||||
if( ui->listWidget_badBlocks->findItems( line, Qt::MatchExactly ).size() )//this one is already in the list
|
||||
continue;
|
||||
if( ui->listWidget_badBlocks->findItems( line, Qt::MatchExactly ).size() )//this one is already in the list
|
||||
continue;
|
||||
|
||||
quint16 bb = line.toInt( &ok );
|
||||
if( !ok || bb < 8 || bb > 4079 )
|
||||
continue;
|
||||
quint16 bb = line.toInt( &ok );
|
||||
if( !ok || bb < 8 || bb > 4079 )
|
||||
continue;
|
||||
|
||||
ui->listWidget_badBlocks->addItem( line );
|
||||
ui->listWidget_badBlocks->addItem( line );
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,11 @@
|
||||
this is a tool to list saves in an extracted nand dump
|
||||
this is a tool to list/extract/delete saves to/from an extracted nand dump. saves are extracted, packed into a data.bin, zipped up with a txt file
|
||||
that says a bit about the save, and then stored in a folder. you must designate a folder and provide the necessary keys before extracting saves
|
||||
will work. i chose to go with data.bin as it is a way to store the entire save in 1 file and also allow the save to be installed using the
|
||||
system menu. and i went with the zip format 1) to save space, and 2) to keep a save and the description all in 1 compact file.
|
||||
|
||||
saves are extracted to "<the folder you specify>/<TID upper>/<TID lower>/#.zip". the # is chosen by the lowest available number starting with 1.
|
||||
there is no need for the saves to be in numerical order, if you have 10 zips for the same game and you decide to delete "3.zip", it will cause
|
||||
no issues, and next time you extract a save for this game, it will use the filename "3.zip".
|
||||
|
||||
TODO:
|
||||
add some sort of extract/delete/install functionality
|
||||
installing saves back to sneek nand still isnt done
|
||||
|
Loading…
Reference in New Issue
Block a user