mirror of
https://github.com/martravi/wiiqt.git
synced 2024-11-22 09:09:18 +01:00
* NUS downloader - add 2 more full updates to the list
* fix bug in creating the jap lists * change the saveDialog to openFileDialog when opening a wad * remove default text in the NUS downloader * nandBin class - add the beginnings of writing... adding & deleting files & directories apparently is working. writing data to a file appears to work * right now, its not writing to the nand.bin, so dont use these functions * probably some other stuff i forgot
This commit is contained in:
parent
6ce5ffc566
commit
8eac10ea9e
@ -60,6 +60,48 @@ bool NandBin::ExtractToDir( QTreeWidgetItem *item, const QString &path )
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QTreeWidgetItem *NandBin::CreateItem( QTreeWidgetItem *parent, const QString &name, quint32 size, quint16 entry, quint32 uid, quint32 gid, quint32 x3, quint8 attr, quint8 wtf)
|
||||||
|
{
|
||||||
|
if( !parent )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
QStringList text;
|
||||||
|
|
||||||
|
QString enStr = QString( "%1" ).arg( entry );
|
||||||
|
QString sizeStr = QString( "%1" ).arg( size, 0, 16 );
|
||||||
|
QString uidStr = QString( "%1" ).arg( uid, 8, 16, QChar( '0' ) );
|
||||||
|
QString gidStr = QString( "%1 (\"%2%3\")" ).arg( gid, 4, 16, QChar( '0' ) )
|
||||||
|
.arg( QChar( ascii( (char)( (gid >> 8) & 0xff ) ) ) )
|
||||||
|
.arg( QChar( ascii( (char)( (gid) & 0xff ) ) ) );
|
||||||
|
QString x3Str = QString( "%1" ).arg( x3, 8, 16, QChar( '0' ) );
|
||||||
|
QString wtfStr = QString( "%1" ).arg( wtf, 2, 16, QChar( '0' ) );
|
||||||
|
QString attrStr = QString( "%1 " ).arg( ( attr & 3 ), 2, 16, QChar( '0' ) );
|
||||||
|
|
||||||
|
quint8 m = attr;
|
||||||
|
const char perm[ 3 ] = {'-','r','w'};
|
||||||
|
for( quint8 i = 0; i < 3; i++ )
|
||||||
|
{
|
||||||
|
attrStr += perm[ ( m >> 6 ) & 1 ];
|
||||||
|
attrStr += perm[ ( m >> 6 ) & 2 ];
|
||||||
|
attrStr += ' ';
|
||||||
|
m <<= 2;
|
||||||
|
}
|
||||||
|
attrStr += QString( "[%1]" ).arg( attr, 2, 16, QChar( '0' ) );
|
||||||
|
|
||||||
|
text << name << enStr << sizeStr << uidStr << gidStr << x3Str << wtfStr << attrStr;
|
||||||
|
//qDebug() << "adding" << name << en << size << uid << gid << x3 << mode << attr;
|
||||||
|
QTreeWidgetItem *child = new QTreeWidgetItem( parent, text );
|
||||||
|
child->setTextAlignment( 1, Qt::AlignRight | Qt::AlignVCenter );//align to the right
|
||||||
|
child->setTextAlignment( 2, Qt::AlignRight | Qt::AlignVCenter );
|
||||||
|
child->setTextAlignment( 3, Qt::AlignRight | Qt::AlignVCenter );
|
||||||
|
child->setTextAlignment( 4, Qt::AlignRight | Qt::AlignVCenter );
|
||||||
|
child->setTextAlignment( 5, Qt::AlignRight | Qt::AlignVCenter );
|
||||||
|
child->setTextAlignment( 6, Qt::AlignRight | Qt::AlignVCenter );
|
||||||
|
//child->setTextAlignment( 7, Qt::AlignRight | Qt::AlignVCenter );
|
||||||
|
child->setFont( 7, QFont( "Courier New", 10, 5 ) );
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
bool NandBin::AddChildren( QTreeWidgetItem *parent, quint16 entry )
|
bool NandBin::AddChildren( QTreeWidgetItem *parent, quint16 entry )
|
||||||
{
|
{
|
||||||
if( entry >= 0x17ff )
|
if( entry >= 0x17ff )
|
||||||
@ -78,36 +120,18 @@ bool NandBin::AddChildren( QTreeWidgetItem *parent, quint16 entry )
|
|||||||
if( !AddChildren( parent, fst.sib ) )
|
if( !AddChildren( parent, fst.sib ) )
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
QStringList text;
|
|
||||||
QString name = FstName( fst );
|
|
||||||
|
|
||||||
QString en = QString( "%1" ).arg( entry );
|
QTreeWidgetItem *child = CreateItem( parent, FstName( fst ), fst.size, entry, fst.uid, fst.gid, fst.x3, fst.attr, fst.wtf );
|
||||||
QString size = QString( "%1" ).arg( fst.size, 0, 16 );
|
|
||||||
QString uid = QString( "%1" ).arg( fst.uid, 8, 16, QChar( '0' ) );
|
|
||||||
QString gid = QString( "%1 (\"%2%3\")" ).arg( fst.gid, 4, 16, QChar( '0' ) )
|
|
||||||
.arg( QChar( ascii( (char)( (fst.gid >> 8) & 0xff ) ) ) )
|
|
||||||
.arg( QChar( ascii( (char)( (fst.gid) & 0xff ) ) ) );
|
|
||||||
QString x3 = QString( "%1" ).arg( fst.x3, 8, 16, QChar( '0' ) );
|
|
||||||
QString mode = QString( "%1" ).arg( fst.mode, 2, 16, QChar( '0' ) );
|
|
||||||
QString attr = QString( "%1" ).arg( fst.attr, 2, 16, QChar( '0' ) );
|
|
||||||
|
|
||||||
text << name << en << size << uid << gid << x3 << mode << attr;
|
|
||||||
QTreeWidgetItem *child = new QTreeWidgetItem( parent, text );
|
|
||||||
child->setTextAlignment( 1, Qt::AlignRight | Qt::AlignVCenter );//align to the right
|
|
||||||
child->setTextAlignment( 2, Qt::AlignRight | Qt::AlignVCenter );
|
|
||||||
child->setTextAlignment( 3, Qt::AlignRight | Qt::AlignVCenter );
|
|
||||||
child->setTextAlignment( 4, Qt::AlignRight | Qt::AlignVCenter );
|
|
||||||
child->setTextAlignment( 5, Qt::AlignRight | Qt::AlignVCenter );
|
|
||||||
child->setTextAlignment( 6, Qt::AlignRight | Qt::AlignVCenter );
|
|
||||||
child->setTextAlignment( 7, Qt::AlignRight | Qt::AlignVCenter );
|
|
||||||
|
|
||||||
//set some icons
|
//set some icons
|
||||||
if( fst.mode )
|
if( ( fst.attr & 3 ) == 1 )
|
||||||
{
|
{
|
||||||
|
//qDebug() << "is a file";
|
||||||
child->setIcon( 0, keyIcon );
|
child->setIcon( 0, keyIcon );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
//qDebug() << "is a folder" << (fst.attr & 3);
|
||||||
child->setIcon( 0, groupIcon );
|
child->setIcon( 0, groupIcon );
|
||||||
//try to add subfolder contents to the tree
|
//try to add subfolder contents to the tree
|
||||||
if( fst.sub != 0xffff && !AddChildren( child, fst.sub ) )
|
if( fst.sub != 0xffff && !AddChildren( child, fst.sub ) )
|
||||||
@ -137,9 +161,9 @@ bool NandBin::ExtractFST( quint16 entry, const QString &path, bool singleFile )
|
|||||||
if( !singleFile && fst.sib != 0xffff && !ExtractFST( fst.sib, path ) )
|
if( !singleFile && fst.sib != 0xffff && !ExtractFST( fst.sib, path ) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
switch( fst.mode )
|
switch( fst.attr & 3 )
|
||||||
{
|
{
|
||||||
case 0:
|
case 2:
|
||||||
if( !ExtractDir( fst, path ) )
|
if( !ExtractDir( fst, path ) )
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
@ -300,8 +324,26 @@ bool NandBin::InitNand( QIcon dirs, QIcon files )
|
|||||||
}*/
|
}*/
|
||||||
//GetFile( 369 );
|
//GetFile( 369 );
|
||||||
|
|
||||||
|
//testing creating new items
|
||||||
|
/*quint16 n = CreateEntry( "/test1", 0x4444, 0x5555, 2, 1, 2, 0 );
|
||||||
|
qDebug() << "created item" << n;
|
||||||
|
|
||||||
|
n = CreateEntry( "/test1/test2", 0x4444, 0x5555, 1, 1, 2, 3 );
|
||||||
|
qDebug() << "created item" << n;
|
||||||
|
if( n )
|
||||||
|
{
|
||||||
|
bool dd = SetData( n, QByteArray( 0x3000000, '\x69') );
|
||||||
|
qDebug() << "added data" << dd;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*ShowLostClusters();
|
||||||
|
bool x= Delete( "/ticket" );
|
||||||
|
qDebug() << "delete" << x;
|
||||||
|
|
||||||
|
ShowLostClusters();*/
|
||||||
|
//delete root;
|
||||||
|
//root = new QTreeWidgetItem( QStringList() << nandPath );
|
||||||
|
//AddChildren( root, 0 );
|
||||||
|
|
||||||
|
|
||||||
if( !bootBlocks.SetBlocks( blocks ) )
|
if( !bootBlocks.SetBlocks( blocks ) )
|
||||||
@ -310,6 +352,39 @@ bool NandBin::InitNand( QIcon dirs, QIcon files )
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NandBin::ShowLostClusters()
|
||||||
|
{
|
||||||
|
QList<quint16> u = GetFatsForEntry( 0 );
|
||||||
|
quint16 ss = fats.size();
|
||||||
|
qDebug() << "total used clusters" << u.size() << "of" << ss << "total";
|
||||||
|
quint16 lost = 0;
|
||||||
|
QList<quint16> ffs;
|
||||||
|
QList<quint16> frs;
|
||||||
|
for( quint16 i = 0; i < ss; i++ )
|
||||||
|
{
|
||||||
|
if( u.contains( fats.at( i ) ) )//this cluster is really used
|
||||||
|
continue;
|
||||||
|
switch( fats.at( i ) )
|
||||||
|
{
|
||||||
|
case 0xFFFB:
|
||||||
|
case 0xFFFC:
|
||||||
|
case 0xFFFD:
|
||||||
|
break;
|
||||||
|
case 0xFFFE:
|
||||||
|
frs << i;
|
||||||
|
break;
|
||||||
|
case 0xFFFF:
|
||||||
|
ffs << i;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
lost++;
|
||||||
|
qDebug() << hex << i << fats.at( i );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qDebug() << "found" << lost << "lost clusters\n0xffffs" << hex << ffs.size() << ffs << "\nfree" << frs.size();
|
||||||
|
}
|
||||||
|
|
||||||
int NandBin::GetDumpType( quint64 fileSize )
|
int NandBin::GetDumpType( quint64 fileSize )
|
||||||
{
|
{
|
||||||
quint64 sizes[] = { 536870912, // type 0 | 536870912 == no ecc
|
quint64 sizes[] = { 536870912, // type 0 | 536870912 == no ecc
|
||||||
@ -464,7 +539,7 @@ fst_t NandBin::GetFST( quint16 entry )
|
|||||||
fst_t fst;
|
fst_t fst;
|
||||||
if( entry >= 0x17FF )
|
if( entry >= 0x17FF )
|
||||||
{
|
{
|
||||||
emit SendError( tr( "Tried to get entry above 0x17ff [ 0x%1 ]" ).arg( entry, 0, 16 ) );
|
emit SendError( tr( "Tried to get entry above 0x17fe [ 0x%1 ]" ).arg( entry, 0, 16 ) );
|
||||||
fst.filename[ 0 ] = '\0';
|
fst.filename[ 0 ] = '\0';
|
||||||
return fst;
|
return fst;
|
||||||
}
|
}
|
||||||
@ -485,8 +560,8 @@ fst_t NandBin::GetFST( quint16 entry )
|
|||||||
f.seek( loc_fst + loc_entry );
|
f.seek( loc_fst + loc_entry );
|
||||||
|
|
||||||
f.read( (char*)&fst.filename, 0xc );
|
f.read( (char*)&fst.filename, 0xc );
|
||||||
f.read( (char*)&fst.mode, 1 );
|
|
||||||
f.read( (char*)&fst.attr, 1 );
|
f.read( (char*)&fst.attr, 1 );
|
||||||
|
f.read( (char*)&fst.wtf, 1 );
|
||||||
f.read( (char*)&fst.sub, 2 );
|
f.read( (char*)&fst.sub, 2 );
|
||||||
f.read( (char*)&fst.sib, 2 );
|
f.read( (char*)&fst.sib, 2 );
|
||||||
if( type && ( entry + 1 ) % 64 == 0 )//bug in other nand.bin extracterizers. the entry for every 64th fst item is inturrupeted by some spare shit
|
if( type && ( entry + 1 ) % 64 == 0 )//bug in other nand.bin extracterizers. the entry for every 64th fst item is inturrupeted by some spare shit
|
||||||
@ -509,7 +584,7 @@ fst_t NandBin::GetFST( quint16 entry )
|
|||||||
fst.x3 = qFromBigEndian( fst.x3 );
|
fst.x3 = qFromBigEndian( fst.x3 );
|
||||||
fst.fst_pos = entry;
|
fst.fst_pos = entry;
|
||||||
|
|
||||||
fst.mode &= 1;
|
//fst.mode &= 1;
|
||||||
return fst;
|
return fst;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -668,11 +743,39 @@ const QList<quint16> NandBin::GetFatsForFile( quint16 i )
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
quint16 fat = fst.sub;
|
quint16 fat = fst.sub;
|
||||||
|
//qDebug() << hex << fat;
|
||||||
quint16 j = 0;//just to make sure a broken nand doesnt lead to an endless loop
|
quint16 j = 0;//just to make sure a broken nand doesnt lead to an endless loop
|
||||||
while ( fat < 0x8000 && fat > 0 && ++j )
|
while ( fat < 0x8000 && fat > 0 && ++j )
|
||||||
{
|
{
|
||||||
ret << fat;
|
ret << fat;
|
||||||
fat = GetFAT( fat );
|
fat = GetFAT( fat );
|
||||||
|
|
||||||
|
//qDebug() << hex << fat;
|
||||||
|
}
|
||||||
|
//qDebug() << hex << ret;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QList<quint16> NandBin::GetFatsForEntry( quint16 i )
|
||||||
|
{
|
||||||
|
//qDebug() << "NandBin::GetFatsForEntry" << i;
|
||||||
|
fst_t fst = GetFST( i );
|
||||||
|
|
||||||
|
QList<quint16> ret;
|
||||||
|
if( fst.sib != 0xffff )
|
||||||
|
{
|
||||||
|
ret.append( GetFatsForEntry( fst.sib ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ( fst.attr & 3 ) == 1 )
|
||||||
|
{
|
||||||
|
ret.append( GetFatsForFile( i ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( fst.sub != 0xffff )
|
||||||
|
ret.append( GetFatsForEntry( fst.sub ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -689,7 +792,7 @@ const QByteArray NandBin::GetData( const QString &path )
|
|||||||
if( !item )
|
if( !item )
|
||||||
return QByteArray();
|
return QByteArray();
|
||||||
|
|
||||||
if( !item->text( 6 ).contains( "1" ) )
|
if( !item->text( 7 ).startsWith( "01" ) )
|
||||||
{
|
{
|
||||||
qDebug() << "NandBin::GetData -> can't get data for a folder" << item->text( 0 );
|
qDebug() << "NandBin::GetData -> can't get data for a folder" << item->text( 0 );
|
||||||
return QByteArray();
|
return QByteArray();
|
||||||
@ -735,6 +838,22 @@ QTreeWidgetItem *NandBin::ItemFromPath( const QString &path )
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QTreeWidgetItem *NandBin::GetParent( const QString &path )
|
||||||
|
{
|
||||||
|
if( !path.startsWith( "/" ) || !root || !root->childCount() )//invalid entry
|
||||||
|
return NULL;
|
||||||
|
if( path.count( "/" ) < 2 )//this will be an entry in the root
|
||||||
|
return root->child( 0 );
|
||||||
|
|
||||||
|
QString parent = path;
|
||||||
|
if( parent.endsWith( "/" ) )
|
||||||
|
parent.resize( parent.size() - 1 );
|
||||||
|
int sl = parent.lastIndexOf( "/" );
|
||||||
|
parent.resize( sl );
|
||||||
|
|
||||||
|
return ItemFromPath( parent );
|
||||||
|
}
|
||||||
|
|
||||||
QTreeWidgetItem *NandBin::FindItem( const QString &s, QTreeWidgetItem *parent )
|
QTreeWidgetItem *NandBin::FindItem( const QString &s, QTreeWidgetItem *parent )
|
||||||
{
|
{
|
||||||
int cnt = parent->childCount();
|
int cnt = parent->childCount();
|
||||||
@ -785,6 +904,34 @@ void NandBin::ShowInfo()
|
|||||||
<< "\nreserved:" << hex << reserved;
|
<< "\nreserved:" << hex << reserved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QTreeWidgetItem *NandBin::ItemFromEntry( quint16 i, QTreeWidgetItem *parent )
|
||||||
|
{
|
||||||
|
return ItemFromEntry( QString( "%1" ).arg( i ), parent );
|
||||||
|
}
|
||||||
|
|
||||||
|
QTreeWidgetItem *NandBin::ItemFromEntry( const QString &i, QTreeWidgetItem *parent )
|
||||||
|
{
|
||||||
|
if( !parent )
|
||||||
|
return NULL;
|
||||||
|
//qDebug() << "NandBin::ItemFromEntry" << i << parent->text( 0 );
|
||||||
|
|
||||||
|
|
||||||
|
quint32 cnt = parent->childCount();
|
||||||
|
for( quint32 j = 0; j < cnt; j++ )
|
||||||
|
{
|
||||||
|
QTreeWidgetItem *child = parent->child( j );
|
||||||
|
if( child->text( 1 ) == i )
|
||||||
|
return child;
|
||||||
|
|
||||||
|
//qDebug() << child->text( 2 ) << i;
|
||||||
|
|
||||||
|
QTreeWidgetItem *r = ItemFromEntry( i, child );
|
||||||
|
if( r )
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
bool NandBin::WriteCluster( quint32 pageNo, const QByteArray data, const QByteArray hmac )
|
bool NandBin::WriteCluster( quint32 pageNo, const QByteArray data, const QByteArray hmac )
|
||||||
{
|
{
|
||||||
if( data.size() != 0x4000 )
|
if( data.size() != 0x4000 )
|
||||||
@ -825,15 +972,7 @@ bool NandBin::WriteDecryptedCluster( quint32 pageNo, const QByteArray data, fst_
|
|||||||
|
|
||||||
hexdump( hmac );
|
hexdump( hmac );
|
||||||
return true;
|
return true;
|
||||||
/*fs_hmac_data(
|
|
||||||
buffer,
|
|
||||||
fp->node->uid,
|
|
||||||
(const unsigned char *)fp->node->name,
|
|
||||||
fp->idx,
|
|
||||||
fp->node->dummy,
|
|
||||||
fp->cluster_idx,
|
|
||||||
hmac
|
|
||||||
);*/
|
|
||||||
AesSetKey( key );
|
AesSetKey( key );
|
||||||
QByteArray encData = AesEncrypt( 0, data );
|
QByteArray encData = AesEncrypt( 0, data );
|
||||||
return WriteCluster( pageNo, encData, hmac );
|
return WriteCluster( pageNo, encData, hmac );
|
||||||
@ -859,306 +998,358 @@ bool NandBin::WritePage( quint32 pageNo, const QByteArray data )
|
|||||||
return f.write( data );
|
return f.write( data );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
quint16 NandBin::CreateNode( const QString &name, quint32 uid, quint16 gid, quint8 attr, quint8 user_perm, quint8 group_perm, quint8 other_perm )
|
||||||
structure of blocks 0 - 7
|
{
|
||||||
|
attr = attr | ( ( user_perm & 3 ) << 6 ) | ( ( group_perm & 3 ) << 4 ) | ( ( other_perm & 3 ) << 2 );
|
||||||
block 0 ( boot 1 )
|
|
||||||
|
quint32 i;//TODO: maybe add in some sort of wear-leveling emulation so all new entries arent created in sequential order
|
||||||
0 - 0x4320
|
for( i = 1; i < 0x17ff; i++ )//cant be entry 0 because that is the root
|
||||||
a bunch of encrypted gibberish.
|
{
|
||||||
the rest of the block as all 0s
|
fst_t fst = fsts[ i ];
|
||||||
|
if( !fst.filename[ 0 ] )//this one doesnt have a filename, it cant be used already
|
||||||
|
break;
|
||||||
sha1 of the whole block is
|
}
|
||||||
|
if( i == 0x17ff )
|
||||||
2cdd5affd2e78c537616a119a7a2e1c568e91f22 with boot1b
|
{
|
||||||
f01e8aca029ee0cb5287f5055da1a0bed2a533fa with boot1c
|
return 0;
|
||||||
|
}
|
||||||
<sven> {1, {0x4a, 0x7c, 0x6f, 0x30, 0x38, 0xde, 0xea, 0x7a, 0x07, 0xd3, 0x32, 0x32, 0x02, 0x4b, 0xe9, 0x5a, 0xfb, 0x56, 0xbf, 0x65}},
|
QByteArray n = name.toLatin1().data();
|
||||||
<sven> {1, {0x2c, 0xdd, 0x5a, 0xff, 0xd2, 0xe7, 0x8c, 0x53, 0x76, 0x16, 0xa1, 0x19, 0xa7, 0xa2, 0xe1, 0xc5, 0x68, 0xe9, 0x1f, 0x22}},
|
n.resize( 12 );
|
||||||
<sven> {0, {0xf0, 0x1e, 0x8a, 0xca, 0x02, 0x9e, 0xe0, 0xcb, 0x52, 0x87, 0xf5, 0x05, 0x5d, 0xa1, 0xa0, 0xbe, 0xd2, 0xa5, 0x33, 0xfa}},
|
memcpy( &fsts[ i ].filename, n, 12 );
|
||||||
<sven> {0, {0x8d, 0x9e, 0xcf, 0x2f, 0x8f, 0x98, 0xa3, 0xc1, 0x07, 0xf1, 0xe5, 0xe3, 0x6f, 0xf2, 0x4d, 0x57, 0x7e, 0xac, 0x36, 0x08}},
|
fsts[ i ].attr = attr;
|
||||||
|
fsts[ i ].wtf = 0;
|
||||||
|
if( ( attr & 3 ) == 2 )
|
||||||
block 1 ( boot2 ) //just guessing at these for now
|
{
|
||||||
|
fsts[ i ].sub = 0xffff;
|
||||||
u32 0x20 //header size
|
}
|
||||||
u32 0xf00 //data offset
|
else
|
||||||
u32 0xa00 //cert size
|
{
|
||||||
u32 0x2a4 //ticket size
|
fsts[ i ].sub = 0xfffb;
|
||||||
u32 0x208 //tmd size
|
}
|
||||||
u32[ 3 ] 0s //padding till 0x20
|
fsts[ i ].sib = 0xffff;
|
||||||
|
fsts[ i ].size = 0;
|
||||||
0x20 - 0x9f0
|
fsts[ i ].uid = uid;
|
||||||
cert
|
fsts[ i ].gid = gid;
|
||||||
root ca00000001
|
fsts[ i ].x3 = 0;
|
||||||
Root-CA00000001 CP00000004
|
|
||||||
Root-CA00000001 XS00000003
|
return i;
|
||||||
|
}
|
||||||
round up to 0xa20
|
|
||||||
ticket for boot2
|
quint16 NandBin::CreateEntry( const QString &path, quint32 uid, quint16 gid, quint8 attr, quint8 user_perm, quint8 group_perm, quint8 other_perm )
|
||||||
|
{
|
||||||
0xcc4
|
qDebug() << "NandBin::CreateEntry" << path;
|
||||||
tmd?
|
|
||||||
|
quint16 ret = 0;
|
||||||
0xecc - 0xf00
|
QTreeWidgetItem *par = GetParent( path );
|
||||||
gibberish padding
|
if( !par )
|
||||||
|
{
|
||||||
0xf00
|
qDebug() << "NandBin::CreateEntry -> parent doesnt exist for" << path;
|
||||||
start of boot2 contents? ( mine is 0x00023E91 bytes in the TMD for v2 )
|
return ret;
|
||||||
there is room for 0x1F100 bytes of it left in this block with 0x4D91 leftover
|
}
|
||||||
|
|
||||||
|
QString name = path;
|
||||||
block 2
|
name.remove( 0, name.lastIndexOf( "/" ) + 1 );
|
||||||
bunch of gibberish till 0x4da0
|
if( name.size() > 12 )
|
||||||
probably the rest of the contents of boot2, padded with gibberish
|
return 0;
|
||||||
|
//QTreeWidgetItem *cur = NULL;
|
||||||
|
quint32 cnt = par->childCount();
|
||||||
0x4da0
|
for( quint32 i = 0; i < cnt; i++ )
|
||||||
0s till 0x5000
|
{
|
||||||
|
if( par->child( i )->text( 0 ) == name )
|
||||||
0x5000 - 0x1f800
|
{
|
||||||
bunch of 0xffff
|
qDebug() << "NandBin::CreateEntry ->" << path << "already exists";
|
||||||
maybe this is untouched nand. never been written to
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ok = false;
|
||||||
0x1f800 - 0x1f8e4 blockmap?
|
quint16 parIdx = par->text( 1 ).toInt( &ok );
|
||||||
|
if( !ok || parIdx > 0x17fe )
|
||||||
26F29A401EE684CF0000000201000000
|
return 0;//wtf
|
||||||
00000000010101010101010101010101
|
|
||||||
01010101010101010101010101010101
|
//fst_t parFst = fsts[ parIdx ];
|
||||||
01010101010101010101010101010101
|
if( fsts[ parIdx ].sub == 0xffff )//directory has no entries yet
|
||||||
010101010101010101010101
|
{
|
||||||
|
ret = CreateNode( name, uid, gid, attr, user_perm, group_perm, other_perm );
|
||||||
26F29A401EE684CF0000000201000000
|
if( !ret )
|
||||||
00000000010101010101010101010101
|
return 0;
|
||||||
01010101010101010101010101010101
|
fsts[ parIdx ].sub = ret;
|
||||||
01010101010101010101010101010101
|
}
|
||||||
010101010101010101010101
|
else//find the last entry in this directory
|
||||||
|
{
|
||||||
26F29A401EE684CF0000000201000000
|
quint16 entryNo = 0;
|
||||||
00000000010101010101010101010101
|
for( quint32 i = cnt; i > 0; i-- )
|
||||||
01010101010101010101010101010101
|
{
|
||||||
01010101010101010101010101010101
|
entryNo = par->child( i - 1 )->text( 1 ).toInt( &ok );
|
||||||
010101010101010101010101
|
if( !ok || entryNo > 0x17fe )
|
||||||
|
return 0;//wtf
|
||||||
0x1f8e4 - 0x20000 ( end of block )
|
|
||||||
more 0s
|
if( fsts[ entryNo ].sib == 0xffff )
|
||||||
|
break;
|
||||||
|
|
||||||
block 2 ( looks a lot like block 1 )
|
if( i == 1 )//wtf
|
||||||
wad type header again with the same values
|
return 0;
|
||||||
|
}
|
||||||
same cert looking doodad again
|
if( !entryNo )//something is busted, none of the child entries is marked as the last one
|
||||||
same ticket again
|
return 0;
|
||||||
|
|
||||||
tmd is different ( zero'd RSA )
|
ret = CreateNode( name, uid, gid, attr, user_perm, group_perm, other_perm );
|
||||||
1 content, 0x00027B5D bytes
|
if( !ret )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fsts[ entryNo ].sib = ret;
|
||||||
|
}
|
||||||
|
QTreeWidgetItem *child = CreateItem( par, name, 0, ret, uid, gid, 0, fsts[ ret ].attr, 0 );
|
||||||
block 4 ( the rest of bootmii )
|
if( attr == 1 )
|
||||||
0 - 0x8a60
|
{
|
||||||
gibberish - rest of bootmii content still encrypted
|
child->setIcon( 0, keyIcon );
|
||||||
|
}
|
||||||
0x8a60 - 0x1f800
|
else
|
||||||
0s
|
{
|
||||||
|
child->setIcon( 0, groupIcon );
|
||||||
0x1f800
|
}
|
||||||
26F29A401EE684CF0000000501010100
|
return ret;
|
||||||
00000000010101010101010101010101
|
}
|
||||||
01010101010101010101010101010101
|
|
||||||
01010101010101010101010101010101
|
bool NandBin::Delete( const QString &path )
|
||||||
010101010101010101010101
|
{
|
||||||
|
QTreeWidgetItem *i = ItemFromPath( path );
|
||||||
26F29A401EE684CF0000000501010100
|
return DeleteItem( i );
|
||||||
00000000010101010101010101010101
|
}
|
||||||
01010101010101010101010101010101
|
|
||||||
01010101010101010101010101010101
|
bool NandBin::DeleteItem( QTreeWidgetItem *item )
|
||||||
010101010101010101010101
|
{
|
||||||
|
qDebug() << "NandBin::DeleteItem" << item->text( 0 );
|
||||||
26F29A401EE684CF0000000501010100
|
if( !item )
|
||||||
00000000010101010101010101010101
|
{
|
||||||
01010101010101010101010101010101
|
qWarning() << "cant delete a null item";
|
||||||
01010101010101010101010101010101
|
return false;
|
||||||
010101010101010101010101
|
}
|
||||||
|
|
||||||
then 0s till 0x20000 ( end of block )
|
bool ok = false;
|
||||||
|
quint16 idx = item->text( 1 ).toInt( &ok );//get the index of the entry to remove
|
||||||
|
if( !ok || idx > 0x17fe )
|
||||||
|
{
|
||||||
block 5
|
qWarning() << "wtf1";
|
||||||
all 0xff
|
return false;//wtf
|
||||||
|
}
|
||||||
block 6
|
|
||||||
identical to block 2 except this only difference is the 0x02 is 0x03
|
//find the entry that is this one's previous sibling
|
||||||
26F29A401EE684CF0000000301000000
|
quint16 pId = 0xffff;//this is the index of the item in relation to its parent
|
||||||
00000000010101010101010101010101
|
quint16 prevSib = 0;
|
||||||
01010101010101010101010101010101
|
QTreeWidgetItem *par = item->parent();
|
||||||
01010101010101010101010101010101
|
if( !par )
|
||||||
010101010101010101010101
|
{
|
||||||
|
qWarning() << "wtf2";
|
||||||
26F29A401EE684CF0000000301000000
|
return false;//trying to delete the root item
|
||||||
00000000010101010101010101010101
|
}
|
||||||
01010101010101010101010101010101
|
quint16 parIdx = par->text( 1 ).toInt( &ok );
|
||||||
01010101010101010101010101010101
|
if( !ok || parIdx > 0x17fe )
|
||||||
010101010101010101010101
|
{
|
||||||
|
qWarning() << "wtf1a";
|
||||||
26F29A401EE684CF0000000301000000
|
return false;//wtf
|
||||||
00000000010101010101010101010101
|
}
|
||||||
01010101010101010101010101010101
|
if( fsts[ parIdx ].sub == idx ) //this is the first item in the folder, point the parent to this items first sibling
|
||||||
01010101010101010101010101010101
|
fsts[ parIdx ].sub = fsts[ idx ].sib;
|
||||||
010101010101010101010101
|
|
||||||
|
else //point the previous entry to the next one
|
||||||
|
{
|
||||||
block 7
|
|
||||||
identical to block 1
|
quint16 cnt = par->childCount();
|
||||||
|
for( quint16 i = 0; i < cnt; i++ )
|
||||||
|
{
|
||||||
|
//qDebug() << i << par->child( i )->text( 0 ) << pId << prevSib;
|
||||||
|
quint16 r = par->child( i )->text( 1 ).toInt( &ok );
|
||||||
|
if( !ok || r > 0x17fe )
|
||||||
|
{
|
||||||
///////////
|
qWarning() << "wtf3";
|
||||||
nand #2 ( no bootmii )
|
return false;//wtf
|
||||||
///////////
|
}
|
||||||
|
|
||||||
block 0 ( boot 1c )
|
if( fsts[ r ].sib == idx )//this is the one we want
|
||||||
gibberish till 0x43f0. then 0s for the rest of the block
|
prevSib = r;
|
||||||
|
|
||||||
block 1
|
if( par->child( i )->text( 0 ) == item->text( 0 ) )
|
||||||
same wad header as before, with the same values
|
{
|
||||||
cert doodad and ticket are the same
|
pId = i;
|
||||||
tmd is v4 and the content is 0x00027BE8 bytes
|
//qDebug() << "found the item";
|
||||||
|
}
|
||||||
block 2
|
|
||||||
0 - 0x8af0
|
if( pId != 0xffff && prevSib )
|
||||||
the rest of the content from boot2 ( padded to 0x40 )
|
break;
|
||||||
|
|
||||||
0s till 0x9000
|
if( i == cnt - 1 )//not found
|
||||||
0xff till 0x18f00
|
{
|
||||||
|
qWarning() << "wtf4" << pId << prevSib;
|
||||||
26F29A401EE684CF0000000201000000
|
return false;
|
||||||
00000000010101010101010101010101
|
}
|
||||||
01010101010101010101010101010101
|
}
|
||||||
01010101010101010101010101010101
|
fsts[ prevSib ].sib = fsts[ idx ].sib;
|
||||||
010101010101010101010101
|
}
|
||||||
|
|
||||||
26F29A401EE684CF0000000201000000
|
switch( fsts[ idx ].attr & 3 )
|
||||||
00000000010101010101010101010101
|
{
|
||||||
01010101010101010101010101010101
|
case 1:
|
||||||
01010101010101010101010101010101
|
{
|
||||||
010101010101010101010101
|
int q = 0;
|
||||||
|
qDebug() << "deleting clusters of" << item->text( 0 ) << idx;
|
||||||
26F29A401EE684CF0000000201000000
|
quint16 fat = fsts[ idx ].sub;//delete all this file's clusters
|
||||||
00000000010101010101010101010101
|
//fats.replace( fat, 0xfffe );
|
||||||
01010101010101010101010101010101
|
do
|
||||||
01010101010101010101010101010101
|
{
|
||||||
010101010101010101010101
|
fats.replace( fat, 0xfffe );
|
||||||
|
//qDebug() << "fat" << hex << fat;
|
||||||
|
fat = GetFAT( fat );
|
||||||
|
//fats.replace( fat, 0xfffe );
|
||||||
block 3 - all 0xff
|
//qDebug() << "deleting cluster" << hex << fat << "from table";
|
||||||
block 4 - all 0xff
|
q++;
|
||||||
block 5 - all 0xff
|
}
|
||||||
|
while( fat < 0x17ff );
|
||||||
block 6 - identical to block 2 except the blockmap ( generation 3 ? )
|
qDebug() << "delete loop done. freed" << q << "clusters";
|
||||||
|
}
|
||||||
26F29A401EE684CF0000000301000000
|
break;
|
||||||
00000000010101010101010101010101
|
case 2:
|
||||||
01010101010101010101010101010101
|
{
|
||||||
01010101010101010101010101010101
|
qDebug() << "deleting children of" << item->text( 0 );
|
||||||
010101010101010101010101
|
quint32 cnt = item->childCount();//delete all the children of this item
|
||||||
|
for( quint32 i = cnt - 1; i > 0; i-- )
|
||||||
26F29A401EE684CF0000000301000000
|
{
|
||||||
00000000010101010101010101010101
|
if( !DeleteItem( item->child( i - 1 ) ) )
|
||||||
01010101010101010101010101010101
|
{
|
||||||
01010101010101010101010101010101
|
qWarning() << "wtf6";
|
||||||
010101010101010101010101
|
return false;
|
||||||
|
}
|
||||||
26F29A401EE684CF0000000301000000
|
}
|
||||||
00000000010101010101010101010101
|
}
|
||||||
01010101010101010101010101010101
|
break;
|
||||||
01010101010101010101010101010101
|
|
||||||
010101010101010101010101
|
}
|
||||||
|
memset( &fsts[ idx ], 0, sizeof( fst_t ) ); //clear this entry
|
||||||
block 7 matches block 1
|
QTreeWidgetItem *d = par->takeChild( pId );
|
||||||
|
delete d;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/////////////
|
bool NandBin::SetData( const QString &path, const QByteArray data )
|
||||||
nand #3
|
{
|
||||||
/////////////
|
QTreeWidgetItem *i = ItemFromPath( path );
|
||||||
|
if( !i )
|
||||||
block 2
|
return false;
|
||||||
26F29A401EE684CF
|
|
||||||
00000004
|
bool ok = false;
|
||||||
01000000000000000101010101010101
|
quint16 idx = i->text( 1 ).toInt( &ok );//find the entry
|
||||||
01010101010101010101010101010101
|
if( !ok || idx > 0x17fe )
|
||||||
01010101010101010101010101010101
|
return false;
|
||||||
01010101010101010101010101010101
|
|
||||||
|
return SetData( idx, data );
|
||||||
block 4
|
}
|
||||||
26F29A401EE684CF
|
|
||||||
0000000F
|
bool NandBin::SetData( quint16 idx, const QByteArray data )
|
||||||
01010100000000000101010101010101
|
{
|
||||||
01010101010101010101010101010101
|
fst_t fst = fsts[ idx ];
|
||||||
01010101010101010101010101010101
|
if( ( fst.attr & 3 ) != 1 )
|
||||||
01010101010101010101010101010101
|
return false;
|
||||||
|
|
||||||
block 6
|
QList<quint16> fts = GetFatsForFile( idx ); //get the currently used fats and overwrite them. this doesnt serve much purpose, but it seems cleaner
|
||||||
26F29A401EE684CF
|
QByteArray pData = PaddedByteArray( data, 0x4000 );//actual data that must be written to the nand
|
||||||
00000005
|
quint32 size = pData.size(); //the actual space used in the nand for this file
|
||||||
01000000000000000101010101010101
|
quint16 clCnt = size / 0x4000; //the number of clusters needed to hold this file
|
||||||
01010101010101010101010101010101
|
|
||||||
01010101010101010101010101010101
|
//if this new data will take more clusters than the old data, create the new ones
|
||||||
01010101010101010101010101010101
|
if( clCnt > fts.size() )
|
||||||
|
{
|
||||||
|
//list all the free clusters
|
||||||
|
QList<quint16>freeClusters;
|
||||||
|
for( quint16 i = 0; i < 0x8000; i++ )
|
||||||
|
{
|
||||||
|
if( fats.at( i ) == 0xfffe )
|
||||||
|
freeClusters << i;
|
||||||
|
}
|
||||||
|
if( freeClusters.size() < clCnt - fts.size() )
|
||||||
|
{
|
||||||
|
qWarning() << "not enough free clusters to write the file";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//setup random number stuff to emulate wear leveling
|
||||||
|
QTime midnight( 0, 0, 0 );
|
||||||
|
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";
|
||||||
|
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
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//grab a random cluster from the list
|
||||||
|
quint16 idx = qrand() % freeClusters.size();
|
||||||
26F29A401EE684CF
|
quint16 cl = freeClusters.takeAt( idx ); //remove this number from the list
|
||||||
00000002
|
fts << cl; //add this one to the clusters that will be used to hold the data
|
||||||
|
quint16 block = cl / 8; //try to find other clusters in the same block
|
||||||
01000000000000000101010101010101
|
for( quint16 i = block * 8; i < ( ( block + 1 ) * 8 ) && fts.size() < clCnt; i++ )
|
||||||
01010101010101010101010101010101
|
{
|
||||||
01010101010101010101010101010101
|
if( cl == i ) //this one is already added to the list
|
||||||
01010101010101010101010101010101
|
continue;
|
||||||
|
|
||||||
|
if( fats.at( i ) == 0xfffe ) //theres more free clusters in this same block, grab them
|
||||||
26F29A401EE684CF
|
{
|
||||||
00000005
|
fts << i;
|
||||||
01010100000000000101010101010101
|
freeClusters.removeAt( freeClusters.indexOf( i, MAX( cl - 8, 0 ) ) );
|
||||||
01010101010101010101010101010101
|
}
|
||||||
01010101010101010101010101010101
|
}
|
||||||
01010101010101010101010101010101
|
//read the spare data to see that the cluster is good - removed for now. but its probably not a bad idea to do this
|
||||||
|
/*if( type )//if the dump doesnt have spare data, dont try to read it, just assume the cluster is good
|
||||||
|
{
|
||||||
*/
|
QByteArray page = GetPage( cl * 8, true );
|
||||||
|
if( page.isEmpty() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QByteArray spr = page.right( 0x40 );
|
||||||
|
if( !spr.startsWith( 0xff ) )
|
||||||
|
{
|
||||||
|
qWarning() << "page" << hex << ( cl * 8 ) << "is bad??";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//qDebug() << "about to writing shit" << clCnt << fts.size();
|
||||||
|
//qDebug() << "file will be on clusters" << hex << fts;
|
||||||
|
for( quint32 i = 0; i < clCnt; i++ )
|
||||||
|
{
|
||||||
|
QByteArray cluster = pData.mid( i * 0x4000, 0x4000 );
|
||||||
|
if( !WriteDecryptedCluster( ( fts.at( i ) * 8 ), cluster, fst, i ) )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//qDebug() << "done writing shit, fix the fats now" << clCnt << fts.size();
|
||||||
|
//all the data has been written, now make sure the fats are correct
|
||||||
|
fsts[ idx ].sub = fts.at( 0 );
|
||||||
|
|
||||||
|
for( quint16 i = 0; i < clCnt - 1; i++ )
|
||||||
|
{
|
||||||
|
//qDebug() << "replacing fat" << hex << fts.at( 0 );
|
||||||
|
fats.replace( fts.at( 0 ), fts.at( 1 ) );
|
||||||
|
fts.takeFirst();
|
||||||
|
}
|
||||||
|
//qDebug() << "loop is done";
|
||||||
|
fats.replace( fts.at( 0 ), 0xfffb );//last cluster in chain
|
||||||
|
fts.takeFirst();
|
||||||
|
//qDebug() << "fixed the last one" << hex << fts;
|
||||||
|
// if the new data uses less clusters than the previous data, mark the extra ones as free
|
||||||
|
while( !fts.isEmpty() )
|
||||||
|
{
|
||||||
|
fats.replace( fts.at( 0 ), 0xfffe );
|
||||||
|
fts.takeFirst();
|
||||||
|
}
|
||||||
|
//qDebug() << "2nd loop is done";
|
||||||
|
|
||||||
|
fsts[ idx ].size = data.size();
|
||||||
|
|
||||||
|
QTreeWidgetItem *i = ItemFromEntry( idx, root );
|
||||||
|
if( !i )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
i->setText( 2, QString( "%1" ).arg( data.size(), 0, 16 ) );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
struct fst_t
|
struct fst_t
|
||||||
{
|
{
|
||||||
quint8 filename[ 0xc ];
|
quint8 filename[ 0xc ];
|
||||||
quint8 mode;
|
|
||||||
quint8 attr;
|
quint8 attr;
|
||||||
|
quint8 wtf;
|
||||||
quint16 sub;
|
quint16 sub;
|
||||||
quint16 sib;
|
quint16 sib;
|
||||||
quint32 size;
|
quint32 size;
|
||||||
@ -20,6 +20,8 @@ struct fst_t
|
|||||||
// class to deal with an encrypted wii nand dump
|
// 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()
|
// 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()
|
// 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
|
||||||
|
|
||||||
//once InitNand() is called, you can get the contents of the nand in a nice QTreeWidgetItem* with GetTree()
|
//once InitNand() is called, you can get the contents of the nand in a nice QTreeWidgetItem* with GetTree()
|
||||||
class NandBin : public QObject
|
class NandBin : public QObject
|
||||||
@ -87,12 +89,33 @@ public:
|
|||||||
//get the fats for a given file
|
//get the fats for a given file
|
||||||
const QList<quint16> GetFatsForFile( quint16 i );
|
const QList<quint16> GetFatsForFile( quint16 i );
|
||||||
|
|
||||||
|
//recurse folders and files and get all fats used for them
|
||||||
|
//! this is probably a more expensive function than you want to use
|
||||||
|
//! it was added only to aid in checking for bugs and lost clusters
|
||||||
|
const QList<quint16> GetFatsForEntry( quint16 i );
|
||||||
|
|
||||||
|
//use the above function to search and display lost clusters
|
||||||
|
void ShowLostClusters();
|
||||||
|
|
||||||
const Blocks0to7 BootBlocks(){ return bootBlocks; }
|
const Blocks0to7 BootBlocks(){ return bootBlocks; }
|
||||||
const QList<Boot2Info> Boot2Infos();
|
const QList<Boot2Info> Boot2Infos();
|
||||||
quint8 Boot1Version();
|
quint8 Boot1Version();
|
||||||
|
|
||||||
QByteArray GetPage( quint32 pageNo, bool withEcc = false );
|
QByteArray GetPage( quint32 pageNo, bool withEcc = false );
|
||||||
|
|
||||||
|
//create new entry
|
||||||
|
//returns the index of the entry on success, or 0 on error
|
||||||
|
quint16 CreateEntry( const QString &path, quint32 uid, quint16 gid, quint8 attr, quint8 user_perm, quint8 group_perm, quint8 other_perm );
|
||||||
|
|
||||||
|
//delete a file/folder
|
||||||
|
bool Delete( const QString &path );
|
||||||
|
|
||||||
|
//sets the data for a given file ( overwrites existing data )
|
||||||
|
bool SetData( quint16 idx, const QByteArray data );
|
||||||
|
|
||||||
|
//overloads the above function
|
||||||
|
bool SetData( const QString &path, const QByteArray data );
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QByteArray key;
|
QByteArray key;
|
||||||
@ -133,6 +156,8 @@ private:
|
|||||||
bool ExtractDir( fst_t fst, QString parent );
|
bool ExtractDir( fst_t fst, QString parent );
|
||||||
bool ExtractFile( fst_t fst, QString parent );
|
bool ExtractFile( fst_t fst, QString parent );
|
||||||
|
|
||||||
|
QTreeWidgetItem *CreateItem( QTreeWidgetItem *parent, const QString &name, quint32 size, quint16 entry, quint32 uid, quint32 gid, quint32 x3, quint8 attr, quint8 wtf);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
QTreeWidgetItem *root;
|
QTreeWidgetItem *root;
|
||||||
@ -147,6 +172,15 @@ private:
|
|||||||
bool WriteDecryptedCluster( quint32 pageNo, const QByteArray data, fst_t fst, quint16 idx );
|
bool WriteDecryptedCluster( quint32 pageNo, const QByteArray data, fst_t fst, quint16 idx );
|
||||||
bool WritePage( quint32 pageNo, const QByteArray data );
|
bool WritePage( quint32 pageNo, const QByteArray data );
|
||||||
|
|
||||||
|
quint16 CreateNode( const QString &name, quint32 uid, quint16 gid, quint8 attr, quint8 user_perm, quint8 group_perm, quint8 other_perm );
|
||||||
|
|
||||||
|
bool DeleteItem( QTreeWidgetItem *item );
|
||||||
|
//find a parent entry for a path to be created - "/title/00000001" should give the entry for "/title"
|
||||||
|
QTreeWidgetItem *GetParent( const QString &path );
|
||||||
|
|
||||||
|
QTreeWidgetItem *ItemFromEntry( quint16 i, QTreeWidgetItem *parent = NULL );
|
||||||
|
QTreeWidgetItem *ItemFromEntry( const QString &i, QTreeWidgetItem *parent = NULL );
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
//connect to these to receive messages from this object
|
//connect to these to receive messages from this object
|
||||||
void SendError( QString );
|
void SendError( QString );
|
||||||
|
@ -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 )
|
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, 12 ) << entry_n << x3 << blk;
|
//qDebug() << "NandSpare::Get_hmac_data" << hex << cluster.size() << uid << QString( (const char*)name ) << entry_n << x3 << blk;
|
||||||
if( hmacKey.size() != 0x14 || cluster.size() != 0x4000 )
|
if( hmacKey.size() != 0x14 || cluster.size() != 0x4000 )
|
||||||
return QByteArray();
|
return QByteArray();
|
||||||
|
|
||||||
|
@ -539,6 +539,7 @@ bool NusDownloader::GetUpdate( const QString & upd, bool decrypt )
|
|||||||
if( s == "2.1e" ) titles = List21e();
|
if( s == "2.1e" ) titles = List21e();
|
||||||
else if( s == "3.0e" ) titles = List30e();
|
else if( s == "3.0e" ) titles = List30e();
|
||||||
else if( s == "3.1e" ) titles = List31e();
|
else if( s == "3.1e" ) titles = List31e();
|
||||||
|
else if( s == "3.3e" ) titles = List33e();
|
||||||
else if( s == "3.4e" ) titles = List34e();
|
else if( s == "3.4e" ) titles = List34e();
|
||||||
else if( s == "4.0e" ) titles = List40e();
|
else if( s == "4.0e" ) titles = List40e();
|
||||||
else if( s == "4.1e" ) titles = List41e();
|
else if( s == "4.1e" ) titles = List41e();
|
||||||
@ -563,6 +564,7 @@ bool NusDownloader::GetUpdate( const QString & upd, bool decrypt )
|
|||||||
|
|
||||||
else if( s == "2.0j" ) titles = List20j();
|
else if( s == "2.0j" ) titles = List20j();
|
||||||
else if( s == "3.1j" ) titles = List31j();
|
else if( s == "3.1j" ) titles = List31j();
|
||||||
|
else if( s == "3.3j" ) titles = List33j();
|
||||||
else if( s == "3.4j" ) titles = List34j();
|
else if( s == "3.4j" ) titles = List34j();
|
||||||
else if( s == "4.0j" ) titles = List40j();
|
else if( s == "4.0j" ) titles = List40j();
|
||||||
else if( s == "4.1j" ) titles = List41j();
|
else if( s == "4.1j" ) titles = List41j();
|
||||||
@ -592,8 +594,8 @@ QMap< quint64, quint16 > NusDownloader::List20j()
|
|||||||
titles.insert( 0x100000023ull, 0xc10 ); // IOS35 - not really part of this update, but needed for sneek
|
titles.insert( 0x100000023ull, 0xc10 ); // IOS35 - not really part of this update, but needed for sneek
|
||||||
titles.insert( 0x100000100ull, 0x2 );//bcv2
|
titles.insert( 0x100000100ull, 0x2 );//bcv2
|
||||||
titles.insert( 0x100000101ull, 0x4 );//miosv4
|
titles.insert( 0x100000101ull, 0x4 );//miosv4
|
||||||
titles.insert( 0x1000848414B45ull, 0 );//EULA - HAKE
|
titles.insert( 0x1000848414B4aull, 0 );//EULA - HAKJ
|
||||||
titles.insert( 0x1000848414C45ull, 0x2 );//regsel //region select isnt in the paper mario update, but putting it here just to be safe
|
titles.insert( 0x1000848414C4aull, 0x2 );//regsel //region select isnt in the paper mario update, but putting it here just to be safe
|
||||||
titles.insert( 0x1000248414341ull, 0x2 );//nigaoeNRv2 - MII
|
titles.insert( 0x1000248414341ull, 0x2 );//nigaoeNRv2 - MII
|
||||||
titles.insert( 0x1000248414141ull, 0x1 );//photov1
|
titles.insert( 0x1000248414141ull, 0x1 );//photov1
|
||||||
titles.insert( 0x1000248414241ull, 0x4 );//shoppingv4
|
titles.insert( 0x1000248414241ull, 0x4 );//shoppingv4
|
||||||
@ -602,9 +604,8 @@ QMap< quint64, quint16 > NusDownloader::List20j()
|
|||||||
|
|
||||||
QMap< quint64, quint16 > NusDownloader::List31j()
|
QMap< quint64, quint16 > NusDownloader::List31j()
|
||||||
{
|
{
|
||||||
QMap< quint64, quint16 > titles = List20u();//TODO - missing a few in here
|
QMap< quint64, quint16 > titles = List20j();//TODO - missing a few in here
|
||||||
titles.insert( 0x100000002ull, 256 );//sys menu
|
titles.insert( 0x100000002ull, 256 );//sys menu
|
||||||
titles.insert( 0x10000000eull, 262 );//14v262 - should actually be 14v257 but that version isnt available on NUS
|
|
||||||
titles.insert( 0x100000014ull, 12 );//20v12
|
titles.insert( 0x100000014ull, 12 );//20v12
|
||||||
titles.insert( 0x100000015ull, 514 );//21v514
|
titles.insert( 0x100000015ull, 514 );//21v514
|
||||||
titles.insert( 0x100000016ull, 777 );//22v777 - should be v772
|
titles.insert( 0x100000016ull, 777 );//22v777 - should be v772
|
||||||
@ -614,7 +615,7 @@ QMap< quint64, quint16 > NusDownloader::List31j()
|
|||||||
titles.insert( 0x100000021ull, 1040 );//33v1040
|
titles.insert( 0x100000021ull, 1040 );//33v1040
|
||||||
titles.insert( 0x100000022ull, 1039 );//34v1039
|
titles.insert( 0x100000022ull, 1039 );//34v1039
|
||||||
titles.insert( 0x100000023ull, 1040 );//35v1040
|
titles.insert( 0x100000023ull, 1040 );//35v1040
|
||||||
titles.insert( 0x100000024ull, 1042 );//36v1040
|
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( 0x1000248415941ull, 0x1 );//photo2v1
|
||||||
titles.insert( 0x1000848414B4aull, 0 );//EULA - HAKJ
|
titles.insert( 0x1000848414B4aull, 0 );//EULA - HAKJ
|
||||||
@ -628,6 +629,27 @@ QMap< quint64, quint16 > NusDownloader::List31j()
|
|||||||
return titles;
|
return titles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QMap< quint64, quint16 > NusDownloader::List33j()
|
||||||
|
{
|
||||||
|
QMap< quint64, quint16 > titles = List31j();//TODO - missing 3.2j
|
||||||
|
titles.insert( 0x100000002ull, 352 );//sys menu
|
||||||
|
titles.insert( 0x10000000bull, 10 );//11v10
|
||||||
|
titles.insert( 0x10000000cull, 6 );//12v6
|
||||||
|
titles.insert( 0x10000000dull, 10 );//13v10
|
||||||
|
titles.insert( 0x10000000full, 257 );//15v257
|
||||||
|
titles.insert( 0x100000011ull, 512 );//17v512
|
||||||
|
titles.insert( 0x10000001eull, 2576 );//30v2576
|
||||||
|
titles.insert( 0x10000001full, 2576 );//31v2576
|
||||||
|
titles.insert( 0x100000025ull, 2070 );//37v2070
|
||||||
|
titles.insert( 0x100000100ull, 0x4 );//bcv4
|
||||||
|
titles.insert( 0x1000248415941ull, 0x1 );//photo2v1
|
||||||
|
titles.insert( 0x1000848414B4aull, 2 );//EULA - HAKJ
|
||||||
|
titles.insert( 0x100000101ull, 8 );//miosv8
|
||||||
|
titles.insert( 0x1000248414341ull, 5 );//nigaoeNRv5 - MII
|
||||||
|
titles.insert( 0x1000248414241ull, 10 );//shoppingv10
|
||||||
|
return titles;
|
||||||
|
}
|
||||||
|
|
||||||
QMap< quint64, quint16 > NusDownloader::List34j()
|
QMap< quint64, quint16 > NusDownloader::List34j()
|
||||||
{
|
{
|
||||||
QMap< quint64, quint16 > titles;
|
QMap< quint64, quint16 > titles;
|
||||||
@ -851,20 +873,31 @@ QMap< quint64, quint16 > NusDownloader::List31e()
|
|||||||
titles.insert( 0x100000023ull, 1040 );//35v1040
|
titles.insert( 0x100000023ull, 1040 );//35v1040
|
||||||
titles.insert( 0x100000024ull, 1042 );//36v1042
|
titles.insert( 0x100000024ull, 1042 );//36v1042
|
||||||
titles.insert( 0x100000002ull, 258 );//sys menu
|
titles.insert( 0x100000002ull, 258 );//sys menu
|
||||||
titles.insert( 0x100000100ull, 0x2 );//bcv2
|
|
||||||
titles.insert( 0x10000001eull, 1039 );//30v1039
|
|
||||||
titles.insert( 0x10000001full, 1039 );//31v1039
|
|
||||||
titles.insert( 0x100000101ull, 5 );//miosv5
|
|
||||||
titles.insert( 0x1000848414B50ull, 2 );//EULA - HAKP
|
|
||||||
titles.insert( 0x1000248414650ull, 0x7 ); // forcast v7 HAFP
|
|
||||||
titles.insert( 0x1000248414750ull, 0x7 ); // news v7 HAGP
|
|
||||||
titles.insert( 0x1000848414C50ull, 0x2 );//regsel
|
|
||||||
titles.insert( 0x1000248414341ull, 4 );//nigaoeNRv4 - MII
|
|
||||||
titles.insert( 0x1000248415941ull, 0x2 ); // photo channel 1.1 HAYA
|
titles.insert( 0x1000248415941ull, 0x2 ); // photo channel 1.1 HAYA
|
||||||
titles.insert( 0x1000248414141ull, 0x1 );//photov1
|
return titles;
|
||||||
titles.insert( 0x1000248414241ull, 7 );//shoppingv7
|
}
|
||||||
titles.insert( 0x1000248414741ull, 0x3 );//news channel HAGA
|
|
||||||
titles.insert( 0x1000248414641ull, 0x3 );//Weather Channel HAFA
|
QMap< quint64, quint16 > NusDownloader::List33e()
|
||||||
|
{
|
||||||
|
QMap< quint64, quint16 > titles = List31e();
|
||||||
|
titles.insert( 0x100000002ull, 354 );//RVL-WiiSystemmenu-v354.wad
|
||||||
|
titles.insert( 0x10000000bull, 10 );//11v10
|
||||||
|
titles.insert( 0x10000000cull, 6 );//12v6
|
||||||
|
titles.insert( 0x10000000dull, 10 );//13v10
|
||||||
|
titles.insert( 0x10000000eull, 262 );//14v262 - should actually be 14v257 but that version isnt available on NUS
|
||||||
|
titles.insert( 0x10000000full, 257 );//15v257
|
||||||
|
titles.insert( 0x100000011ull, 512 );//17v512
|
||||||
|
titles.insert( 0x100000014ull, 12 );//20v12
|
||||||
|
titles.insert( 0x100000015ull, 514 );//21v514
|
||||||
|
titles.insert( 0x100000016ull, 777 );//22v777 - should be v772
|
||||||
|
titles.insert( 0x10000001cull, 1292 );//28v1292 - should be 1228
|
||||||
|
titles.insert( 0x10000001eull, 2576 );//30v2576
|
||||||
|
titles.insert( 0x10000001full, 2576 );//31v2576
|
||||||
|
titles.insert( 0x100000025ull, 2070 );//37v2070
|
||||||
|
titles.insert( 0x100000100ull, 4 );//bcv4
|
||||||
|
titles.insert( 0x100000101ull, 8 );//miosv8
|
||||||
|
titles.insert( 0x1000248414341ull, 5 );//nigaoeNRv5 - MII
|
||||||
|
titles.insert( 0x1000248414241ull, 10 );//shoppingv10
|
||||||
return titles;
|
return titles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1401,4 +1434,3 @@ QMap< quint64, quint16 > NusDownloader::List43k()
|
|||||||
titles.insert( 0x100000002ull, 0x206 ); // SystemMenu 4.3K
|
titles.insert( 0x100000002ull, 0x206 ); // SystemMenu 4.3K
|
||||||
return titles;
|
return titles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ public:
|
|||||||
static QMap< quint64, quint16 > List30e();
|
static QMap< quint64, quint16 > List30e();
|
||||||
static QMap< quint64, quint16 > List31e();
|
static QMap< quint64, quint16 > List31e();
|
||||||
//static QMap< quint64, quint16 > List32e();
|
//static QMap< quint64, quint16 > List32e();
|
||||||
//static QMap< quint64, quint16 > List33e();
|
static QMap< quint64, quint16 > List33e();
|
||||||
static QMap< quint64, quint16 > List34e();
|
static QMap< quint64, quint16 > List34e();
|
||||||
static QMap< quint64, quint16 > List40e();
|
static QMap< quint64, quint16 > List40e();
|
||||||
static QMap< quint64, quint16 > List41e();
|
static QMap< quint64, quint16 > List41e();
|
||||||
@ -98,7 +98,7 @@ public:
|
|||||||
//static QMap< quint64, quint16 > List30j();
|
//static QMap< quint64, quint16 > List30j();
|
||||||
static QMap< quint64, quint16 > List31j();
|
static QMap< quint64, quint16 > List31j();
|
||||||
//static QMap< quint64, quint16 > List32j();
|
//static QMap< quint64, quint16 > List32j();
|
||||||
//static QMap< quint64, quint16 > List33j();
|
static QMap< quint64, quint16 > List33j();
|
||||||
static QMap< quint64, quint16 > List34j();
|
static QMap< quint64, quint16 > List34j();
|
||||||
static QMap< quint64, quint16 > List40j();
|
static QMap< quint64, quint16 > List40j();
|
||||||
static QMap< quint64, quint16 > List41j();
|
static QMap< quint64, quint16 > List41j();
|
||||||
|
@ -152,7 +152,7 @@ void NandThread::run()
|
|||||||
|
|
||||||
quint32 NandThread::FileCount( QTreeWidgetItem *item )
|
quint32 NandThread::FileCount( QTreeWidgetItem *item )
|
||||||
{
|
{
|
||||||
if( item->text( 6 ) == "00" )//its a folder, recurse through it and count all the files
|
if( item->text( 7 ).startsWith( "02" ) )//its a folder, recurse through it and count all the files
|
||||||
{
|
{
|
||||||
quint32 ret = 0;
|
quint32 ret = 0;
|
||||||
quint16 cnt = item->childCount();
|
quint16 cnt = item->childCount();
|
||||||
|
@ -365,7 +365,7 @@ void NandWindow::on_treeWidget_currentItemChanged( QTreeWidgetItem* current, QTr
|
|||||||
{
|
{
|
||||||
Q_UNUSED( previous );
|
Q_UNUSED( previous );
|
||||||
|
|
||||||
if( !current || current->text( 6 ) == "00" )
|
if( !current || current->text( 7 ).startsWith( "02" ) )
|
||||||
return;
|
return;
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
quint16 entry = current->text( 1 ).toInt( &ok );
|
quint16 entry = current->text( 1 ).toInt( &ok );
|
||||||
|
@ -322,30 +322,16 @@ void MainWindow::on_actionImportWad_triggered()
|
|||||||
ShowMessage( tr( "<b>Error setting the basepath of the nand to %1</b>" ).arg( QFileInfo( ui->lineEdit_nandPath->text() ).absoluteFilePath() ) );
|
ShowMessage( tr( "<b>Error setting the basepath of the nand to %1</b>" ).arg( QFileInfo( ui->lineEdit_nandPath->text() ).absoluteFilePath() ) );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QString fn = QFileDialog::getSaveFileName(this,
|
QString fn = QFileDialog::getOpenFileName( this,
|
||||||
tr("Wad files(*.wad)"),
|
tr("Wad files(*.wad)"),
|
||||||
QCoreApplication::applicationDirPath(),
|
QCoreApplication::applicationDirPath(),
|
||||||
tr("WadFiles (*.wad)"));
|
tr("WadFiles (*.wad)"));
|
||||||
if(fn == "") return;
|
if(fn == "") return;
|
||||||
|
|
||||||
QFile f(fn);
|
QByteArray data = ReadFile( fn );
|
||||||
if(!f.open( QIODevice::ReadOnly)) {
|
if( data.isEmpty() )
|
||||||
ShowMessage( tr( "Error opening %1" ).arg( fn ) );
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
qint64 len = f.size();
|
|
||||||
char* buffer = new char[len];
|
|
||||||
qint64 read = f.read(buffer, len);
|
|
||||||
f.close();
|
|
||||||
hexdump(buffer, 0x40);
|
|
||||||
if(read != len) {
|
|
||||||
ShowMessage( tr( "Error reading %1" ).arg( fn ) );
|
|
||||||
free(buffer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QByteArray data(buffer, len);
|
|
||||||
free(buffer);
|
|
||||||
Wad wad(data);
|
Wad wad(data);
|
||||||
if( !wad.IsOk() ) {
|
if( !wad.IsOk() ) {
|
||||||
ShowMessage( tr( "Wad data not ok" ) );;
|
ShowMessage( tr( "Wad data not ok" ) );;
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="lineEdit_tid">
|
<widget class="QLineEdit" name="lineEdit_tid">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>0000000100000002</string>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="maxLength">
|
<property name="maxLength">
|
||||||
<number>16</number>
|
<number>16</number>
|
||||||
@ -43,7 +43,7 @@
|
|||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>513</string>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="maxLength">
|
<property name="maxLength">
|
||||||
<number>5</number>
|
<number>5</number>
|
||||||
@ -218,7 +218,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<addaction name="actionSetting_txt"/>
|
<addaction name="actionSetting_txt"/>
|
||||||
<addaction name="actionFlush"/>
|
<addaction name="actionFlush"/>
|
||||||
<addaction name="actionImportWad"/>
|
<addaction name="actionImportWad"/>
|
||||||
</widget>
|
</widget>
|
||||||
<addaction name="menuNand_Dump"/>
|
<addaction name="menuNand_Dump"/>
|
||||||
</widget>
|
</widget>
|
||||||
|
Loading…
Reference in New Issue
Block a user