From 0e671a67649898cd9bb3eb80e9801ddff2077c0b Mon Sep 17 00:00:00 2001 From: "giantpune@gmail.com" Date: Thu, 13 Jan 2011 16:43:49 +0000 Subject: [PATCH] * sort titles in nandBinChecker * use unsigned int since HBC's TID wont fit in a signed 32 bit int * add key index fixing in the ticket class --- WiiQt/nusdownloader.cpp | 2 +- WiiQt/tiktmd.cpp | 31 +- WiiQt/tiktmd.h | 8 +- WiiQt/u8.cpp | 1056 +++++++++++++++++++------------------- WiiQt/wad.cpp | 162 +++++- WiiQt/wad.h | 30 +- nandBinCheck/main.cpp | 53 +- nand_dump/mainwindow.cpp | 17 - saveToy/mainwindow.cpp | 1 - 9 files changed, 772 insertions(+), 588 deletions(-) diff --git a/WiiQt/nusdownloader.cpp b/WiiQt/nusdownloader.cpp index 20576ee..11f11c4 100644 --- a/WiiQt/nusdownloader.cpp +++ b/WiiQt/nusdownloader.cpp @@ -13,7 +13,7 @@ NusDownloader::NusDownloader( QObject *parent, const QString &cPath ) : QObject( //change the cache path void NusDownloader::SetCachePath( const QString &cPath ) { - qDebug() << "NusDownloader::SetCachePath" << cPath; + //qDebug() << "NusDownloader::SetCachePath" << cPath; cachePath = cPath; } diff --git a/WiiQt/tiktmd.cpp b/WiiQt/tiktmd.cpp index 1f712c3..26fdbd9 100644 --- a/WiiQt/tiktmd.cpp +++ b/WiiQt/tiktmd.cpp @@ -80,6 +80,14 @@ bool Tmd::SetDiskAccess( bool allow ) return true; } +quint32 Tmd::AccessFlags() +{ + if( !p_tmd ) + return 0; + + return qFromBigEndian( p_tmd->access_rights ); +} + quint16 Tmd::Gid() { if( !p_tmd ) @@ -154,6 +162,13 @@ bool Tmd::SetSize( quint16 cid, quint32 size ) return true; } +quint16 Tmd::BootIndex( quint16 i ) +{ + if( !p_tmd || i > qFromBigEndian( p_tmd->num_contents ) ) + return 0; + return qFromBigEndian( p_tmd->contents[ i ].index ); +} + quint16 Tmd::Type( quint16 i ) { if( !p_tmd || i > qFromBigEndian( p_tmd->num_contents ) ) @@ -234,7 +249,7 @@ bool Tmd::FakeSign() return ret; } -Ticket::Ticket( const QByteArray &stuff ) +Ticket::Ticket( const QByteArray &stuff, bool fixKeyIndex ) { data = stuff; p_tik = NULL; @@ -264,6 +279,20 @@ Ticket::Ticket( const QByteArray &stuff ) data.resize( SignedSize() ); SetPointer(); } + quint8 *keyindex = ((quint8*)p_tik) + 0xb1; + if( fixKeyIndex && ( *keyindex > 0 ) ) + { + if( *keyindex == 1 )//for now, just bail out for korean key + { + qWarning() << "Ticket::Ticket -> ticket uses the korean key. Only titles encrypted with the common key are supported"; + return; + } + qWarning() << "Ticket::Ticket -> key index is" << hex << *keyindex << ". Setting it to 0 and fakesigning"; + + *keyindex = 0;//anything other than 0 or 1 is probably an error. fix it + FakeSign(); + //hexdump( data ); + } } quint64 Ticket::Tid() diff --git a/WiiQt/tiktmd.h b/WiiQt/tiktmd.h index d7f67d3..e15801b 100644 --- a/WiiQt/tiktmd.h +++ b/WiiQt/tiktmd.h @@ -137,7 +137,7 @@ typedef struct _cert_ecdsa { class Ticket { public: - Ticket( const QByteArray &stuff = QByteArray() ); + Ticket( const QByteArray &stuff = QByteArray(), bool fixKeyIndex = true ); //expose the tik data to the rest of the code so it cas read directly from the p_tmd instead of having to add a function to access all the data //the pointer should be good until "data" is changed @@ -184,11 +184,13 @@ public: QByteArray Hash( quint16 i ); //returned in host endian - quint64 Size( quint16 i ); - quint16 Type( quint16 i ); + quint64 Size( quint16 i ); + quint16 Type( quint16 i ); + quint16 BootIndex( quint16 i ); quint64 Tid(); quint64 IOS(); quint16 Gid(); + quint32 AccessFlags(); //gets the number of contents diff --git a/WiiQt/u8.cpp b/WiiQt/u8.cpp index e681400..c632e0e 100644 --- a/WiiQt/u8.cpp +++ b/WiiQt/u8.cpp @@ -1,7 +1,7 @@ #include "u8.h" #include "lz77.h" #include "tools.h" -#include "md5.h" +//#include "md5.h" #include "ash.h" #define U8_HEADER_ALIGNMENT 0x20 //wiibrew says 0x40, but wii.cs is using 0x20 and i havent heard of any bricks because of it @@ -26,7 +26,7 @@ U8::U8( bool initialize, int type, const QStringList &names ) headerType = type; imetNames = names; if( !initialize ) - return; + return; //headerType = type; //imetNames = names; @@ -44,9 +44,9 @@ bool U8::CreateEmptyData() QBuffer buf( &data ); if( !buf.open( QBuffer::WriteOnly ) ) { - qWarning() << "Can't create buffer"; - data.clear(); - return false; + qWarning() << "Can't create buffer"; + data.clear(); + return false; } buf.putChar( 'U' ); buf.putChar( '\xAA' ); @@ -88,64 +88,64 @@ bool U8::RenameEntry( const QString &path, const QString &newName ) qDebug() << "U8::RenameEntry(" << path << "," << newName << ")"; if( !ok ) { - qWarning() << "U8::RenameEntry -> archive has no data"; - return false; + qWarning() << "U8::RenameEntry -> archive has no data"; + return false; } if( newName.contains( "/" ) ) { - qWarning() << "U8::RenameEntry -> newName cannot contain \'/\'"; - return false; + qWarning() << "U8::RenameEntry -> newName cannot contain \'/\'"; + return false; } //check if this is a path to a file in a nested archive QMap::iterator i = nestedU8s.begin(); while( i != nestedU8s.constEnd() ) { - if( path.startsWith( i.key() ) && path != i.key() ) - { - QString subPath = path; - subPath.remove( 0, i.key().size() + 1 );//remove the path of the archive itself + the slash + if( path.startsWith( i.key() ) && path != i.key() ) + { + QString subPath = path; + subPath.remove( 0, i.key().size() + 1 );//remove the path of the archive itself + the slash - bool ret = i.value().RenameEntry( subPath, newName ); - if( !ret ) - { - qWarning() << "U8::RenameEntry -> error replacing data in child U8 archive"; - return false; - } - //qDebug() << "child entry updated, now replacing child archive in this one"; - return ReplaceEntry( i.key(),i.value().GetData() ); - //NOTE - after replacing the entry, "i" is no longer valid. keep that in mind when changing this code. its a bitch to track down this bug - } - i++; + bool ret = i.value().RenameEntry( subPath, newName ); + if( !ret ) + { + qWarning() << "U8::RenameEntry -> error replacing data in child U8 archive"; + return false; + } + //qDebug() << "child entry updated, now replacing child archive in this one"; + return ReplaceEntry( i.key(),i.value().GetData() ); + //NOTE - after replacing the entry, "i" is no longer valid. keep that in mind when changing this code. its a bitch to track down this bug + } + i++; } //make sure the new filename doesnt already exist QString parentPath = path; while( parentPath.startsWith( "/") )//remove leading "/"s - parentPath.remove( 0, 1 ); + parentPath.remove( 0, 1 ); while( parentPath.endsWith( "/") )//remove trailing "/"s - parentPath.resize( parentPath.size() - 1 ); + parentPath.resize( parentPath.size() - 1 ); int slash = parentPath.lastIndexOf( "/" ); if( slash != -1 ) { - parentPath.chop( ( parentPath.size() - slash ) ); - parentPath += "/" + newName; + parentPath.chop( ( parentPath.size() - slash ) ); + parentPath += "/" + newName; } else - parentPath = newName; + parentPath = newName; if( FindEntry( parentPath ) != -1 ) { - qWarning() << "U8::RenameEntry ->" << parentPath << "already exists in the archive"; - return false; + qWarning() << "U8::RenameEntry ->" << parentPath << "already exists in the archive"; + return false; } //find the entry to rename int entryToRename = FindEntry( path ); if( entryToRename == -1 ) { - qWarning() << "U8::RenameEntry" << path << "doesn\'t exists in the archive"; - qWarning() << "choices are" << paths; - return false; + qWarning() << "U8::RenameEntry" << path << "doesn\'t exists in the archive"; + qWarning() << "choices are" << paths; + return false; } quint32 newNameLen = newName.size(); @@ -156,15 +156,15 @@ bool U8::RenameEntry( const QString &path, const QString &newName ) quint32 nFstSize = fstSize + difference; int dataAdjustment = 0; if( RU( nFstSize, U8_HEADER_ALIGNMENT ) < RU( fstSize, U8_HEADER_ALIGNMENT ) ) - dataAdjustment = - RU( ( fstSize - nFstSize ), U8_HEADER_ALIGNMENT ); + dataAdjustment = - RU( ( fstSize - nFstSize ), U8_HEADER_ALIGNMENT ); else if( RU( nFstSize, U8_HEADER_ALIGNMENT ) > RU( fstSize, U8_HEADER_ALIGNMENT ) ) - dataAdjustment = RU( ( nFstSize - fstSize ), U8_HEADER_ALIGNMENT ); + dataAdjustment = RU( ( nFstSize - fstSize ), U8_HEADER_ALIGNMENT ); qDebug() << "old size:" << hex << oldNameLen\ - << "new size:" << hex << newNameLen\ - << "difference:" << hex << difference - << "dataAdjustment:" << hex << dataAdjustment; + << "new size:" << hex << newNameLen\ + << "difference:" << hex << difference + << "dataAdjustment:" << hex << dataAdjustment; QByteArray nFstData( ( qFromBigEndian( fst[ 0 ].FileLength ) ) * 0xc, '\0' ); FEntry *nfst = (FEntry*)( nFstData.data() ); //make the new root entry @@ -182,34 +182,34 @@ bool U8::RenameEntry( const QString &path, const QString &newName ) for( quint32 i = 1; i < cnt; i++ ) { - FEntry *e = &fst[ i ];//old entry - FEntry *ne = &nfst[ i ];//new entry + FEntry *e = &fst[ i ];//old entry + FEntry *ne = &nfst[ i ];//new entry - ne->NameOffset = swap24( nNameTable.size() );//offset to the new entry's name in the string table + ne->NameOffset = swap24( nNameTable.size() );//offset to the new entry's name in the string table - //add the name to the new name table - if( i == (quint32)entryToRename ) - { - nNameTable.append( newName.toLatin1() ); - } - else - nNameTable.append( FstName( e ).toLatin1() ); + //add the name to the new name table + if( i == (quint32)entryToRename ) + { + nNameTable.append( newName.toLatin1() ); + } + else + nNameTable.append( FstName( e ).toLatin1() ); - nNameTable.append( '\0' ); - if( e->Type )//nothing special to change for directories except the name offset - { - ne->Type = 1; - ne->ParentOffset = e->ParentOffset; - ne->NextOffset = e->NextOffset; - } - else//only need to change the name offset & the data offset for files - { - ne->Type = 0; - ne->FileOffset = qFromBigEndian( qFromBigEndian( e->FileOffset ) + dataAdjustment );// + qFromBigEndian( dataAdjustment ); + nNameTable.append( '\0' ); + if( e->Type )//nothing special to change for directories except the name offset + { + ne->Type = 1; + ne->ParentOffset = e->ParentOffset; + ne->NextOffset = e->NextOffset; + } + else//only need to change the name offset & the data offset for files + { + ne->Type = 0; + ne->FileOffset = qFromBigEndian( qFromBigEndian( e->FileOffset ) + dataAdjustment );// + qFromBigEndian( dataAdjustment ); //qFromBigEndian( (quint32)( 0x20 + RU( U8_HEADER_ALIGNMENT, nFstSize ) + nPayload.size() ) ); - ne->FileLength = e->FileLength; - qDebug() << "old offset" << hex << qFromBigEndian( e->FileOffset ) << "new offset" << hex << qFromBigEndian( e->FileOffset ) + dataAdjustment; - } + ne->FileLength = e->FileLength; + qDebug() << "old offset" << hex << qFromBigEndian( e->FileOffset ) << "new offset" << hex << qFromBigEndian( e->FileOffset ) + dataAdjustment; + } } //now put all the parts together to make a new U8 archive @@ -219,8 +219,8 @@ bool U8::RenameEntry( const QString &path, const QString &newName ) QBuffer buf( &data ); if( !buf.open( QBuffer::WriteOnly ) ) { - qWarning() << "U8::AddEntry -> Can't create buffer"; - return -1; + qWarning() << "U8::AddEntry -> Can't create buffer"; + return -1; } buf.putChar( 'U' ); buf.putChar( '\xAA' ); @@ -244,14 +244,14 @@ bool U8::RenameEntry( const QString &path, const QString &newName ) int padding = data_offset - data.size();//pad to 'data_offset' after the fst if( padding ) { - data.append( QByteArray( padding, '\0' ) ); + data.append( QByteArray( padding, '\0' ) ); } data.append( nPayload );//add the actual file data padding = RU( data.size(), 0x20 ) - data.size();//pad the entire thing to 0x20 bytes TOTO: should probably already be done, and this step is not really necessary if( padding ) { - data.append( QByteArray( padding, '\0' ) ); + data.append( QByteArray( padding, '\0' ) ); } @@ -268,46 +268,46 @@ bool U8::ReplaceEntry( const QString &path, const QByteArray &nba, bool autoComp //qDebug() << "U8::ReplaceEntry(" << path << ")"; if( !ok ) { - qWarning() << "U8::ReplaceEntry -> archive has no data"; - return false; + qWarning() << "U8::ReplaceEntry -> archive has no data"; + return false; } //check if this is a path to a file in a nested archive QMap::iterator i = nestedU8s.begin(); while( i != nestedU8s.constEnd() ) { - if( path.startsWith( i.key() ) && path != i.key() ) - { - QString subPath = path; - subPath.remove( 0, i.key().size() + 1 );//remove the path of the archive itself + the slash - //qDebug() << "replacing file" << subPath << "in child U8" << i.key(); + if( path.startsWith( i.key() ) && path != i.key() ) + { + QString subPath = path; + subPath.remove( 0, i.key().size() + 1 );//remove the path of the archive itself + the slash + //qDebug() << "replacing file" << subPath << "in child U8" << i.key(); - U8 ch = i.value(); - QString chPath = i.key(); - //qDebug() << "replacing" << subPath << "in child archive"; - bool ret = ch.ReplaceEntry( subPath, nba, autoCompress ); - if( !ret ) - { - qWarning() << "U8::ReplaceEntry -> error replacing data in child U8 archive"; - return false; - } - //qDebug() << "child entry updated, now replacing child archive in this one"; - return ReplaceEntry( chPath, ch.GetData() ); - //NOTE - after replacing the entry, "i" is no longer valid. keep that in mind when changing this code. its a bitch to track down this bug - } - i++; + U8 ch = i.value(); + QString chPath = i.key(); + //qDebug() << "replacing" << subPath << "in child archive"; + bool ret = ch.ReplaceEntry( subPath, nba, autoCompress ); + if( !ret ) + { + qWarning() << "U8::ReplaceEntry -> error replacing data in child U8 archive"; + return false; + } + //qDebug() << "child entry updated, now replacing child archive in this one"; + return ReplaceEntry( chPath, ch.GetData() ); + //NOTE - after replacing the entry, "i" is no longer valid. keep that in mind when changing this code. its a bitch to track down this bug + } + i++; } //find the entry to replace int entryToReplace = FindEntry( path ); if( entryToReplace == -1 ) { - qWarning() << "U8::ReplaceEntry" << path << "doesn\'t exists in the archive"; - qWarning() << "choices are" << paths; - return false; + qWarning() << "U8::ReplaceEntry" << path << "doesn\'t exists in the archive"; + qWarning() << "choices are" << paths; + return false; } if( qFromBigEndian( fst[ entryToReplace ].Type ) ) { - qWarning() << "U8::ReplaceEntry -> replacing a directory is not supported" << path; - return false; + qWarning() << "U8::ReplaceEntry -> replacing a directory is not supported" << path; + return false; } @@ -317,12 +317,12 @@ bool U8::ReplaceEntry( const QString &path, const QByteArray &nba, bool autoComp QByteArray newData = nba; if( autoCompress ) { - QByteArray oldData = data.mid( qFromBigEndian( fst[ entryToReplace ].FileOffset ), qFromBigEndian( fst[ entryToReplace ].FileLength ) ); - bool oldCompressed = ( LZ77::GetLz77Offset( oldData ) > -1 || IsAshCompressed( oldData ) ); - if( oldCompressed && LZ77::GetLz77Offset( newData ) == -1 && !IsAshCompressed( newData ) ) - { - newData = LZ77::Compress( newData ); - } + QByteArray oldData = data.mid( qFromBigEndian( fst[ entryToReplace ].FileOffset ), qFromBigEndian( fst[ entryToReplace ].FileLength ) ); + bool oldCompressed = ( LZ77::GetLz77Offset( oldData ) > -1 || IsAshCompressed( oldData ) ); + if( oldCompressed && LZ77::GetLz77Offset( newData ) == -1 && !IsAshCompressed( newData ) ) + { + newData = LZ77::Compress( newData ); + } } quint32 newSizePadded = RU( newData.size(), 0x20 ); @@ -333,12 +333,12 @@ bool U8::ReplaceEntry( const QString &path, const QByteArray &nba, bool autoComp QByteArray newPaddedData = newData; if( newSizePadded > (quint32)newPaddedData.size() ) { - newPaddedData.append( QByteArray( newSizePadded - newPaddedData.size(), '\0' ) ); + newPaddedData.append( QByteArray( newSizePadded - newPaddedData.size(), '\0' ) ); } data.insert( qFromBigEndian( fst[ entryToReplace ].FileOffset ), newPaddedData ); if( newSizePadded == oldSizePadded )//the sized match, so nothing else to change - return true; + return true; //point the fst to the new data fst = (FEntry*)( data.data() + rootnode_offset ); @@ -352,12 +352,12 @@ bool U8::ReplaceEntry( const QString &path, const QByteArray &nba, bool autoComp quint32 cnt = qFromBigEndian( fst[ 0 ].FileLength ); for( quint32 i = entryToReplace + 1; i < cnt; i++ ) { - FEntry *e = &fst[ i ];//old entry - if( e->Type )//nothing changes for directories - continue; + FEntry *e = &fst[ i ];//old entry + if( e->Type )//nothing changes for directories + continue; - //qDebug() << "changed" << FstName( e ) << "offset from" << hex << qFromBigEndian( fst[ i ].FileOffset ) << "to" << qFromBigEndian( fst[ i ].FileOffset ) + difference; - e->FileOffset = qFromBigEndian( qFromBigEndian( fst[ i ].FileOffset ) + difference ); + //qDebug() << "changed" << FstName( e ) << "offset from" << hex << qFromBigEndian( fst[ i ].FileOffset ) << "to" << qFromBigEndian( fst[ i ].FileOffset ) + difference; + e->FileOffset = qFromBigEndian( qFromBigEndian( fst[ i ].FileOffset ) + difference ); } CreateEntryList(); @@ -372,39 +372,39 @@ bool U8::RemoveEntry( const QString &path ) //qDebug() << "U8::RemoveEntry(" << path << ")"; if( !ok ) { - qWarning() << "U8::RemoveEntry -> archive has no data"; - return false; + qWarning() << "U8::RemoveEntry -> archive has no data"; + return false; } //check if this is a path to a file in a nested archive QMap::iterator i = nestedU8s.begin(); while( i != nestedU8s.constEnd() ) { - if( path.startsWith( i.key() ) && path != i.key() ) - { - QString subPath = path; - subPath.remove( 0, i.key().size() + 1 );//remove the path of the archive itself + the slash + if( path.startsWith( i.key() ) && path != i.key() ) + { + QString subPath = path; + subPath.remove( 0, i.key().size() + 1 );//remove the path of the archive itself + the slash - U8 ch = i.value(); - QString chPath = i.key(); - //qDebug() << "deleting" << subPath << "in child archive"; + U8 ch = i.value(); + QString chPath = i.key(); + //qDebug() << "deleting" << subPath << "in child archive"; - bool ret = ch.RemoveEntry( subPath ); - if( !ret ) - { - qWarning() << "U8::RemoveEntry -> error replacing data in child U8 archive"; - return false; - } - return ReplaceEntry( chPath, ch.GetData() );//insert the new nested archive in this one - //NOTE - after replacing the entry, "i" is no longer valid. keep that in mind when changing this code. its a bitch to track down this bug - } - i++; + bool ret = ch.RemoveEntry( subPath ); + if( !ret ) + { + qWarning() << "U8::RemoveEntry -> error replacing data in child U8 archive"; + return false; + } + return ReplaceEntry( chPath, ch.GetData() );//insert the new nested archive in this one + //NOTE - after replacing the entry, "i" is no longer valid. keep that in mind when changing this code. its a bitch to track down this bug + } + i++; } //find the entry to delete int entryToDelete = FindEntry( path ); if( entryToDelete == -1 ) { - qWarning() << "U8::RemoveEntry" << path << "doesn\'t exists in the archive"; - return false; + qWarning() << "U8::RemoveEntry" << path << "doesn\'t exists in the archive"; + return false; } //create a list of all the directory indexes that my have their size changed @@ -414,35 +414,35 @@ bool U8::RemoveEntry( const QString &path ) quint32 fstLenToDelete; if( fst[ entryToDelete ].Type )//deleting a directory { - parent = qFromBigEndian( fst[ entryToDelete ].ParentOffset ); - while( parent ) - { - parents << parent; - parent = qFromBigEndian( fst[ parent ].ParentOffset ); - } - numDeletedEntries = qFromBigEndian( fst[ entryToDelete ].NextOffset ) - entryToDelete; - fstLenToDelete = 0; - for( quint32 i = entryToDelete; i < qFromBigEndian( fst[ entryToDelete ].NextOffset ); i ++ ) - { - fstLenToDelete += FstName( i ).size() + 1 + 0xc; - } + parent = qFromBigEndian( fst[ entryToDelete ].ParentOffset ); + while( parent ) + { + parents << parent; + parent = qFromBigEndian( fst[ parent ].ParentOffset ); + } + numDeletedEntries = qFromBigEndian( fst[ entryToDelete ].NextOffset ) - entryToDelete; + fstLenToDelete = 0; + for( quint32 i = entryToDelete; i < qFromBigEndian( fst[ entryToDelete ].NextOffset ); i ++ ) + { + fstLenToDelete += FstName( i ).size() + 1 + 0xc; + } } else//deleting a file { - parent = entryToDelete - 1; - while( parent ) - { - if( fst[ parent ].Type && qFromBigEndian( fst[ parent ].NextOffset ) > (quint32)entryToDelete ) - { - parents << parent; - parent = qFromBigEndian( fst[ parent ].ParentOffset ); - } - else - parent--; - } - numDeletedEntries = 1; - fstLenToDelete = FstName( entryToDelete ).size() + 1 + 0xc; + parent = entryToDelete - 1; + while( parent ) + { + if( fst[ parent ].Type && qFromBigEndian( fst[ parent ].NextOffset ) > (quint32)entryToDelete ) + { + parents << parent; + parent = qFromBigEndian( fst[ parent ].ParentOffset ); + } + else + parent--; + } + numDeletedEntries = 1; + fstLenToDelete = FstName( entryToDelete ).size() + 1 + 0xc; } @@ -468,63 +468,63 @@ bool U8::RemoveEntry( const QString &path ) QList movedDirs;//keep a list of the directories that are shifted back so their children can be shifted too for( quint32 i = 1; i < cnt; i++ ) { - if( i >= (quint32)entryToDelete && i < (quint32)( entryToDelete + numDeletedEntries ) )//skip deleted entries - continue; + if( i >= (quint32)entryToDelete && i < (quint32)( entryToDelete + numDeletedEntries ) )//skip deleted entries + continue; - //copy old entries and adjust as needed - quint8 adj = i < (quint32)entryToDelete ? 0 : numDeletedEntries; - quint32 ni = i - adj; + //copy old entries and adjust as needed + quint8 adj = i < (quint32)entryToDelete ? 0 : numDeletedEntries; + quint32 ni = i - adj; - //qDebug() << "keeping" << FstName( i ) << "in the new archive ( moved from" << hex << i << "to" << hex << ni << ")"; - //if( parents.contains( i ) ) + //qDebug() << "keeping" << FstName( i ) << "in the new archive ( moved from" << hex << i << "to" << hex << ni << ")"; + //if( parents.contains( i ) ) //qDebug() << "\tthis is a parent of the deleted item"; - FEntry *e = &fst[ i ];//old entry - FEntry *ne = &nfst[ ni ];//new entry + FEntry *e = &fst[ i ];//old entry + FEntry *ne = &nfst[ ni ];//new entry - nfst[ ni ].NameOffset = swap24( nNameTable.size() );//offset to the new entry's name in the string table + nfst[ ni ].NameOffset = swap24( nNameTable.size() );//offset to the new entry's name in the string table - //add the name to the new name table - nNameTable.append( FstName( e ).toLatin1() ); - nNameTable.append( '\0' ); + //add the name to the new name table + nNameTable.append( FstName( e ).toLatin1() ); + nNameTable.append( '\0' ); - if( e->Type )//directory - { - ne->Type = 1; - if( !adj )//directories before the new entry - { - ne->ParentOffset = e->ParentOffset; - ne->NextOffset = qFromBigEndian( qFromBigEndian( e->NextOffset ) \ - - ( parents.contains( i ) ? numDeletedEntries : 0 ) ); - } - else//directories after the new entry - { - ne->ParentOffset = qFromBigEndian( qFromBigEndian( e->ParentOffset ) \ - - ( movedDirs.contains( qFromBigEndian( e->ParentOffset ) ) ? numDeletedEntries : 0 ) ); - ne->NextOffset = qFromBigEndian( qFromBigEndian( e->NextOffset ) - numDeletedEntries ); + if( e->Type )//directory + { + ne->Type = 1; + if( !adj )//directories before the new entry + { + ne->ParentOffset = e->ParentOffset; + ne->NextOffset = qFromBigEndian( qFromBigEndian( e->NextOffset ) \ + - ( parents.contains( i ) ? numDeletedEntries : 0 ) ); + } + else//directories after the new entry + { + ne->ParentOffset = qFromBigEndian( qFromBigEndian( e->ParentOffset ) \ + - ( movedDirs.contains( qFromBigEndian( e->ParentOffset ) ) ? numDeletedEntries : 0 ) ); + ne->NextOffset = qFromBigEndian( qFromBigEndian( e->NextOffset ) - numDeletedEntries ); - movedDirs << i; + movedDirs << i; - //qDebug() << "e.parent:" << hex << qFromBigEndian( e->ParentOffset ) << "movedDirs:" << movedDirs; + //qDebug() << "e.parent:" << hex << qFromBigEndian( e->ParentOffset ) << "movedDirs:" << movedDirs; + //hexdump( (const void*)ne, sizeof( FEntry) ); + } + } + else//file + { + ne->Type = 0; + ne->FileOffset = \ + qFromBigEndian( (quint32)( 0x20 + RU( nFstSize, U8_HEADER_ALIGNMENT ) + nPayload.size() ) ); + ne->FileLength = e->FileLength; + nPayload.append( data.mid( qFromBigEndian( e->FileOffset ), qFromBigEndian( e->FileLength ) ) ); + int padding = RU( nPayload.size(), 0x20 ) - nPayload.size();//pad to 0x20 bytes between files + if( padding ) + { + nPayload.append( QByteArray( padding, '\0' ) ); + } + //qDebug() << "writing fileOffset of" << hex << ni << hex << (quint32)( 0x20 + RU( U8_HEADER_ALIGNMENT, nFstSize ) + nPayload.size() ); + } //hexdump( (const void*)ne, sizeof( FEntry) ); - } - } - else//file - { - ne->Type = 0; - ne->FileOffset = \ - qFromBigEndian( (quint32)( 0x20 + RU( nFstSize, U8_HEADER_ALIGNMENT ) + nPayload.size() ) ); - ne->FileLength = e->FileLength; - nPayload.append( data.mid( qFromBigEndian( e->FileOffset ), qFromBigEndian( e->FileLength ) ) ); - int padding = RU( nPayload.size(), 0x20 ) - nPayload.size();//pad to 0x20 bytes between files - if( padding ) - { - nPayload.append( QByteArray( padding, '\0' ) ); - } - //qDebug() << "writing fileOffset of" << hex << ni << hex << (quint32)( 0x20 + RU( U8_HEADER_ALIGNMENT, nFstSize ) + nPayload.size() ); - } - //hexdump( (const void*)ne, sizeof( FEntry) ); } //hexdump( nFstData ); @@ -536,8 +536,8 @@ bool U8::RemoveEntry( const QString &path ) QBuffer buf( &data ); if( !buf.open( QBuffer::WriteOnly ) ) { - qWarning() << "U8::AddEntry -> Can't create buffer"; - return -1; + qWarning() << "U8::AddEntry -> Can't create buffer"; + return -1; } buf.putChar( 'U' ); buf.putChar( '\xAA' ); @@ -561,14 +561,14 @@ bool U8::RemoveEntry( const QString &path ) int padding = data_offset - data.size();//pad to 'data_offset' after the fst if( padding ) { - data.append( QByteArray( padding, '\0' ) ); + data.append( QByteArray( padding, '\0' ) ); } data.append( nPayload );//add the actual file data padding = RU( data.size(), 0x20 ) - data.size();//pad the entire thing to 0x20 bytes TOTO: should probably already be done, and this step is not really necessary if( padding ) { - data.append( QByteArray( padding, '\0' ) ); + data.append( QByteArray( padding, '\0' ) ); } //make sure the pirvate variables are correct @@ -590,70 +590,70 @@ int U8::AddEntry( const QString &path, int type, const QByteArray &newData ) //make sure there is actually data to manipulate if( !ok && !CreateEmptyData() ) { - return -1; + return -1; } //check if this is a path to a file in a nested archive QMap::iterator i = nestedU8s.begin(); while( i != nestedU8s.constEnd() ) { - if( path.startsWith( i.key() ) && path != i.key() ) - { - QString subPath = path; - //make a copy of the string, as "i" will become invalid on replace entry, and create a crash that is a bitch to track down - QString thisPath = i.key(); - U8 chVal = i.value(); - //qDebug() << "adding entry" << subPath << "in child U8" << thisPath; - subPath.remove( 0, thisPath.size() + 1 );//remove the path of the archive itself + the slash - bool ret = chVal.AddEntry( subPath, type, newData ); - if( !ret ) - { - qWarning() << "U8::AddEntry -> error replacing data in child U8 archive"; - return false; - } + if( path.startsWith( i.key() ) && path != i.key() ) + { + QString subPath = path; + //make a copy of the string, as "i" will become invalid on replace entry, and create a crash that is a bitch to track down + QString thisPath = i.key(); + U8 chVal = i.value(); + //qDebug() << "adding entry" << subPath << "in child U8" << thisPath; + subPath.remove( 0, thisPath.size() + 1 );//remove the path of the archive itself + the slash + bool ret = chVal.AddEntry( subPath, type, newData ); + if( !ret ) + { + qWarning() << "U8::AddEntry -> error replacing data in child U8 archive"; + return false; + } - if( !ReplaceEntry( thisPath, chVal.GetData() ) )//insert the new nested archive in this one - return -1; + if( !ReplaceEntry( thisPath, chVal.GetData() ) )//insert the new nested archive in this one + return -1; - //qDebug() << "done replacing the child entry in this archive, finding the index to return (" << thisPath << ")"; - return FindEntry( thisPath );//just return the index of the nested archive - } - i++; + //qDebug() << "done replacing the child entry in this archive, finding the index to return (" << thisPath << ")"; + return FindEntry( thisPath );//just return the index of the nested archive + } + i++; } //make sure this path doesnt already exist if( FindEntry( path ) != -1 ) { - qWarning() << "U8::AddEntry" << path << "already exists in the archive"; - return -1; + qWarning() << "U8::AddEntry" << path << "already exists in the archive"; + return -1; } //find the parent for this new entry QString parentPath = path; while( parentPath.startsWith( "/") )//remove leading "/"s - parentPath.remove( 0, 1 ); + parentPath.remove( 0, 1 ); while( parentPath.endsWith( "/") )//remove trailing "/"s - parentPath.resize( parentPath.size() - 1 ); + parentPath.resize( parentPath.size() - 1 ); int parent = 0; int slash = parentPath.lastIndexOf( "/" ); if( slash != -1 ) { - //parentPath.resize( ( parentPath.size() - slash ) - 2 ); - parentPath.chop( ( parentPath.size() - slash ) ); - parent = FindEntry( parentPath ); - if( parent == -1 )//need to create subfolders for this fucker - { - parent = AddEntry( parentPath, 1 ); - if( parent == -1 ) - { - qWarning() << "U8::AddEntry -> could not create subfolders for" << path; - return -1; - } - } - if( !fst[ parent ].Type )//parent maps to a file, not folder - { - qWarning() << "U8::AddEntry -> can't add child entry to a file"; - return -1; - } + //parentPath.resize( ( parentPath.size() - slash ) - 2 ); + parentPath.chop( ( parentPath.size() - slash ) ); + parent = FindEntry( parentPath ); + if( parent == -1 )//need to create subfolders for this fucker + { + parent = AddEntry( parentPath, 1 ); + if( parent == -1 ) + { + qWarning() << "U8::AddEntry -> could not create subfolders for" << path; + return -1; + } + } + if( !fst[ parent ].Type )//parent maps to a file, not folder + { + qWarning() << "U8::AddEntry -> can't add child entry to a file"; + return -1; + } } //qDebug() << "adding entry to" << FstName( parent ); @@ -664,9 +664,9 @@ int U8::AddEntry( const QString &path, int type, const QByteArray &newData ) QList parents; while( parentPath.contains( "/" ) ) { - parents << FindEntry( parentPath ); - int slash = parentPath.lastIndexOf( "/" ); - parentPath.chop( ( parentPath.size() - slash ) ); + parents << FindEntry( parentPath ); + int slash = parentPath.lastIndexOf( "/" ); + parentPath.chop( ( parentPath.size() - slash ) ); } parents << FindEntry( parentPath ); //qDebug() << "parents" << parents; @@ -678,7 +678,7 @@ int U8::AddEntry( const QString &path, int type, const QByteArray &newData ) int sl = name.lastIndexOf( "/" ); if( sl != -1 ) { - name.remove( 0, sl + 1 ); + name.remove( 0, sl + 1 ); } quint32 nFstSize = fstSize + 0x0C + name.size() + 1;//old fst size + 0xc bytes for the new entry + room for the new name in the string table @@ -699,74 +699,74 @@ int U8::AddEntry( const QString &path, int type, const QByteArray &newData ) QList movedDirs;//keep a list of the directories that are shifted back so their children can be shifted too for( quint32 i = 1; i < cnt; i++ ) { - nfst[ i ].NameOffset = swap24( nNameTable.size() );//offset to the new entry's name in the string table - if( i == (quint32)newEntry )//add the new entry - { - FEntry *ne = &nfst[ i ]; - if( type ) - { - ne->Type = 1; - ne->ParentOffset = qFromBigEndian( parent ); - ne->NextOffset = qFromBigEndian( i + 1 ); - } - else - { - ne->Type = 0; - ne->FileLength = qFromBigEndian( (quint32)newData.size() ); - ne->FileOffset =\ - qFromBigEndian( (quint32)( 0x20 + RU( nFstSize, U8_HEADER_ALIGNMENT ) + nPayload.size() ) ); - nPayload.append( newData ); - int padding = RU( nPayload.size(), 0x20 ) - nPayload.size();//pad to 0x20 bytes between files - if( padding ) + nfst[ i ].NameOffset = swap24( nNameTable.size() );//offset to the new entry's name in the string table + if( i == (quint32)newEntry )//add the new entry { - nPayload.append( QByteArray( padding, '\0' ) ); + FEntry *ne = &nfst[ i ]; + if( type ) + { + ne->Type = 1; + ne->ParentOffset = qFromBigEndian( parent ); + ne->NextOffset = qFromBigEndian( i + 1 ); + } + else + { + ne->Type = 0; + ne->FileLength = qFromBigEndian( (quint32)newData.size() ); + ne->FileOffset =\ + qFromBigEndian( (quint32)( 0x20 + RU( nFstSize, U8_HEADER_ALIGNMENT ) + nPayload.size() ) ); + nPayload.append( newData ); + int padding = RU( nPayload.size(), 0x20 ) - nPayload.size();//pad to 0x20 bytes between files + if( padding ) + { + nPayload.append( QByteArray( padding, '\0' ) ); + } + } + nNameTable.append( name.toLatin1() );//add this entry's name to the table + nNameTable.append( '\0' ); + continue; } - } - nNameTable.append( name.toLatin1() );//add this entry's name to the table - nNameTable.append( '\0' ); - continue; - } - //copy old entries and adjust as needed - quint8 adj = i < (quint32)newEntry ? 0 : 1; - quint32 ni = i - adj; + //copy old entries and adjust as needed + quint8 adj = i < (quint32)newEntry ? 0 : 1; + quint32 ni = i - adj; - FEntry *e = &fst[ ni ];//old entry - FEntry *ne = &nfst[ i ];//new entry + FEntry *e = &fst[ ni ];//old entry + FEntry *ne = &nfst[ i ];//new entry - //add the name to the new name table - nNameTable.append( FstName( e ).toLatin1() ); - nNameTable.append( '\0' ); - if( e->Type ) - { - ne->Type = 1; - if( !adj )//directories before the new entry - { - ne->ParentOffset = e->ParentOffset; - ne->NextOffset = qFromBigEndian( qFromBigEndian( e->NextOffset ) \ - + ( parents.contains( i ) ? 1 : 0 ) ); - } - else//directories after the new entry - { - movedDirs << ni; - ne->ParentOffset = qFromBigEndian( qFromBigEndian( e->ParentOffset ) \ - + ( movedDirs.contains( qFromBigEndian( e->ParentOffset ) ) ? 1 : 0 ) ); - ne->NextOffset = qFromBigEndian( qFromBigEndian( e->NextOffset ) + 1 ); - } - } - else//all file entries - { - ne->Type = 0; - ne->FileOffset = \ - qFromBigEndian( (quint32)( 0x20 + RU( nFstSize, U8_HEADER_ALIGNMENT ) + nPayload.size() ) ); - ne->FileLength = e->FileLength; - nPayload.append( data.mid( qFromBigEndian( e->FileOffset ), qFromBigEndian( e->FileLength ) ) ); - int padding = RU( nPayload.size(), 0x20 ) - nPayload.size();//pad to 0x20 bytes between files - if( padding ) - { - nPayload.append( QByteArray( padding, '\0' ) ); - } - } + //add the name to the new name table + nNameTable.append( FstName( e ).toLatin1() ); + nNameTable.append( '\0' ); + if( e->Type ) + { + ne->Type = 1; + if( !adj )//directories before the new entry + { + ne->ParentOffset = e->ParentOffset; + ne->NextOffset = qFromBigEndian( qFromBigEndian( e->NextOffset ) \ + + ( parents.contains( i ) ? 1 : 0 ) ); + } + else//directories after the new entry + { + movedDirs << ni; + ne->ParentOffset = qFromBigEndian( qFromBigEndian( e->ParentOffset ) \ + + ( movedDirs.contains( qFromBigEndian( e->ParentOffset ) ) ? 1 : 0 ) ); + ne->NextOffset = qFromBigEndian( qFromBigEndian( e->NextOffset ) + 1 ); + } + } + else//all file entries + { + ne->Type = 0; + ne->FileOffset = \ + qFromBigEndian( (quint32)( 0x20 + RU( nFstSize, U8_HEADER_ALIGNMENT ) + nPayload.size() ) ); + ne->FileLength = e->FileLength; + nPayload.append( data.mid( qFromBigEndian( e->FileOffset ), qFromBigEndian( e->FileLength ) ) ); + int padding = RU( nPayload.size(), 0x20 ) - nPayload.size();//pad to 0x20 bytes between files + if( padding ) + { + nPayload.append( QByteArray( padding, '\0' ) ); + } + } } //now put all the parts together to make a new U8 archive @@ -776,8 +776,8 @@ int U8::AddEntry( const QString &path, int type, const QByteArray &newData ) QBuffer buf( &data ); if( !buf.open( QBuffer::WriteOnly ) ) { - qWarning() << "U8::AddEntry -> Can't create buffer"; - return -1; + qWarning() << "U8::AddEntry -> Can't create buffer"; + return -1; } buf.putChar( 'U' ); buf.putChar( '\xAA' ); @@ -801,14 +801,14 @@ int U8::AddEntry( const QString &path, int type, const QByteArray &newData ) int padding = data_offset - data.size();//pad to 'data_offset' after the fst if( padding ) { - data.append( QByteArray( padding, '\0' ) ); + data.append( QByteArray( padding, '\0' ) ); } data.append( nPayload );//add the actual file data padding = RU( data.size(), 0x20 ) - data.size();//pad the entire thing to 0x20 bytes TOTO: should probably already be done, and this step is not really necessary if( padding ) { - data.append( QByteArray( padding, '\0' ) ); + data.append( QByteArray( padding, '\0' ) ); } //make sure the pirvate variables are correct @@ -831,12 +831,12 @@ void U8::CreateEntryList() quint32 cnt = qFromBigEndian( fst[ 0 ].FileLength ); if( ( cnt * 0x0d ) + rootnode_offset > (quint32)data.size() ) { - qWarning() << "U8::CreateEntryList -> invalid archive"; - ok = false; - data.clear(); - paths.clear(); - fst = NULL; - return; + qWarning() << "U8::CreateEntryList -> invalid archive"; + ok = false; + data.clear(); + paths.clear(); + fst = NULL; + return; } NameOff = cnt * 0x0C; bool fixWarn = false;//ony print the warning 1 time @@ -844,79 +844,79 @@ void U8::CreateEntryList() //qDebug() << "cnt" << hex << cnt; for( quint32 i = 1; i < cnt; ++i )//this is not the most effecient way to do things, but it seems to work ok and these archives are small enough that it happens fast anyways { - //start at the beginning of the fst and enter every directory whos "nextoffset" is greater than this index, - //adding the names along the way and hopefully finding "i" - QString path; - quint32 current = 1; - quint32 folder = 0; - while( 1 ) - { - if( !current ) - { - qWarning() << "U8::CreateEntryList -> error parsing the archive"; - break; - } - if( current == i ) - { - path += FstName( i ); - if( fst[ current ].Type ) + //start at the beginning of the fst and enter every directory whos "nextoffset" is greater than this index, + //adding the names along the way and hopefully finding "i" + QString path; + quint32 current = 1; + quint32 folder = 0; + while( 1 ) { - //sanity check: make sure the parent offset is correct - if( folder != qFromBigEndian( fst[ current ].ParentOffset ) ) - { - qWarning() << "U8::CreateEntryList -> error parsing the archive - recursion mismatch in" - << path << "expected:" << hex << folder << "got:" << hex << qFromBigEndian( fst[ current ].ParentOffset )\ - << "(" << FstName( qFromBigEndian( fst[ current ].ParentOffset ) ) << ")"; - - //some tools use "recursion" instead of "parent offset". - //it just coincidentally works for archives such as the opening.bnr and .app since they dont have many folders to mess up - if( qFromBigEndian( fst[ current ].ParentOffset ) == (quint32)path.count( "/" ) ) + if( !current ) { - if( !fixWarn ) - { - qWarning() << "This archive was made by a broken tool such as U8Mii. I'm trying to fix it, but I can't make any promises"; - fixWarn = true; - } - fst[ current ].ParentOffset = qFromBigEndian( folder ); + qWarning() << "U8::CreateEntryList -> error parsing the archive"; + break; } - } - path += "/"; - paths << path; - } - else - { - //add this file to the list of entries - paths << path; - - //check if this file is really a U8 archive, and add it to the list of children - QByteArray chData = data.mid( qFromBigEndian( fst[ current ].FileOffset ), qFromBigEndian( fst[ current ].FileLength ) ); - if( IsU8( chData ) ) - { - U8 child( chData ); - if( child.IsOK() ) + if( current == i ) { - nestedU8s.insert( path, child ); - foreach( QString chPath, child.Entries() ) - { - QString newPath = path + "/" + chPath; - paths << newPath; - } - } - } - } - break; - } - if( fst[ current ].Type && i < qFromBigEndian( fst[ current ].NextOffset ) ) - { - path += FstName( current ) + "/"; - folder = current; - current++; - continue; + path += FstName( i ); + if( fst[ current ].Type ) + { + //sanity check: make sure the parent offset is correct + if( folder != qFromBigEndian( fst[ current ].ParentOffset ) ) + { + qWarning() << "U8::CreateEntryList -> error parsing the archive - recursion mismatch in" + << path << "expected:" << hex << folder << "got:" << hex << qFromBigEndian( fst[ current ].ParentOffset )\ + << "(" << FstName( qFromBigEndian( fst[ current ].ParentOffset ) ) << ")"; - } - current = NextEntryInFolder( current, folder ); - } - //paths << path; + //some tools use "recursion" instead of "parent offset". + //it just coincidentally works for archives such as the opening.bnr and .app since they dont have many folders to mess up + if( qFromBigEndian( fst[ current ].ParentOffset ) == (quint32)path.count( "/" ) ) + { + if( !fixWarn ) + { + qWarning() << "This archive was made by a broken tool such as U8Mii. I'm trying to fix it, but I can't make any promises"; + fixWarn = true; + } + fst[ current ].ParentOffset = qFromBigEndian( folder ); + } + } + path += "/"; + paths << path; + } + else + { + //add this file to the list of entries + paths << path; + + //check if this file is really a U8 archive, and add it to the list of children + QByteArray chData = data.mid( qFromBigEndian( fst[ current ].FileOffset ), qFromBigEndian( fst[ current ].FileLength ) ); + if( IsU8( chData ) ) + { + U8 child( chData ); + if( child.IsOK() ) + { + nestedU8s.insert( path, child ); + foreach( QString chPath, child.Entries() ) + { + QString newPath = path + "/" + chPath; + paths << newPath; + } + } + } + } + break; + } + if( fst[ current ].Type && i < qFromBigEndian( fst[ current ].NextOffset ) ) + { + path += FstName( current ) + "/"; + folder = current; + current++; + continue; + + } + current = NextEntryInFolder( current, folder ); + } + //paths << path; } //qDebug() << "done"; //qDebug() << "paths" << paths; @@ -943,22 +943,22 @@ void U8::Load( const QByteArray &ba ) }*/ data = ba; if( IsAshCompressed( data ) )//decrypt ASH0 files - data = DecryptAsh( data ); + data = DecryptAsh( data ); quint32 tmp; int off = LZ77::GetLz77Offset( data ); int off2 = GetU8Offset( data ); if( off != -1 && ( off2 == -1 || ( off2 != -1 && off < off2 ) ) ) { - isLz77 = true; - data = LZ77::Decompress( data ); - off2 = GetU8Offset( data ); + isLz77 = true; + data = LZ77::Decompress( data ); + off2 = GetU8Offset( data ); } if( off2 == -1 ) { - qDebug() << "This isnt a U8"; - return; + qDebug() << "This isnt a U8"; + return; } data = data.mid( off2 ); @@ -966,8 +966,8 @@ void U8::Load( const QByteArray &ba ) QBuffer buf( &data ); if( !buf.open( QBuffer::ReadOnly ) ) { - qWarning() << "Can't create buffer"; - return; + qWarning() << "Can't create buffer"; + return; } buf.seek( 4 ); @@ -975,9 +975,9 @@ void U8::Load( const QByteArray &ba ) rootnode_offset = qFromBigEndian( tmp ); if( rootnode_offset != 0x20 ) { - qWarning() << "rootnodeOffset" << hex << rootnode_offset; - qWarning() << hex << data.size(); - hexdump( data ); + qWarning() << "rootnodeOffset" << hex << rootnode_offset; + qWarning() << hex << data.size(); + hexdump( data ); } buf.read( ( char*)&tmp, 4 ); @@ -1005,7 +1005,7 @@ QString U8::FstName( const FEntry *entry ) { //qDebug() << "U8::FstName"; if( entry == &fst[ 0 ] ) - return QString(); + return QString(); int nameStart = swap24( entry->NameOffset ) + NameOff + rootnode_offset; return data.mid( nameStart, nameStart + 0x100 ); @@ -1015,8 +1015,8 @@ QString U8::FstName( quint32 i ) { if( i > fst[ 0 ].FileLength ) { - qWarning() << "U8::FstName -> index is out of range"; - return QString(); + qWarning() << "U8::FstName -> index is out of range"; + return QString(); } return FstName( &fst[ i ] ); } @@ -1027,7 +1027,7 @@ quint32 U8::NextEntryInFolder( quint32 current, quint32 directory ) quint32 next = ( fst[ current ].Type ? qFromBigEndian( fst[ current ].FileLength ) : current + 1 ); //qDebug() << "next" << next << "len" << hex << qFromBigEndian( fst[ directory ].FileLength ); if( next < qFromBigEndian( fst[ directory ].FileLength ) ) - return next; + return next; return 0; } @@ -1036,19 +1036,19 @@ int U8::FindEntry( const QString &str, int d ) { //qDebug() << "U8::FindEntry(" << str << "," << d << ")"; if( str.isEmpty() ) - return 0; + return 0; if( str.startsWith( "/" ) ) { - QString r = str; - r.remove( 0, 1 ); - return FindEntry( r, d ); + QString r = str; + r.remove( 0, 1 ); + return FindEntry( r, d ); } if( !fst[ d ].Type ) { - qDebug() << "ERROR!!" << FstName( &fst[ d ] ) << "is not a directory"; - return -1; + qDebug() << "ERROR!!" << FstName( &fst[ d ] ) << "is not a directory"; + return -1; } int next = d + 1; @@ -1056,25 +1056,25 @@ int U8::FindEntry( const QString &str, int d ) while( next ) { - QString item; - int sl = str.indexOf( "/" ); - if( sl > -1 ) - item = str.left( sl ); - else - item = str; + QString item; + int sl = str.indexOf( "/" ); + if( sl > -1 ) + item = str.left( sl ); + else + item = str; - if( FstName( entry ) == item ) - { - if( item == str || item + "/" == str )//this is the item we are looking for - { - return next; - } - //this item is a parent folder of one we are looking for - return FindEntry( str.right( ( str.size() - sl ) - 1 ), next ); - } - //find the next entry in this folder - next = NextEntryInFolder( next, d ); - entry = &fst[ next ]; + if( FstName( entry ) == item ) + { + if( item == str || item + "/" == str )//this is the item we are looking for + { + return next; + } + //this item is a parent folder of one we are looking for + return FindEntry( str.right( ( str.size() - sl ) - 1 ), next ); + } + //find the next entry in this folder + next = NextEntryInFolder( next, d ); + entry = &fst[ next ]; } //no entry with the given path was found @@ -1086,66 +1086,66 @@ const QByteArray U8::GetData( const QString &str, bool onlyPayload ) //qDebug() << "U8::GetData(" << str << ")"; if( str.isEmpty() )//give the data for this whole archive { - if( onlyPayload ) - return data; + if( onlyPayload ) + return data; - //qDebug() << "U8::GetData -> returning all the data for this archive. headerType:" << headerType; + //qDebug() << "U8::GetData -> returning all the data for this archive. headerType:" << headerType; - QByteArray ret = data; + QByteArray ret = data; - switch( headerType ) - { - case U8_Hdr_IMET_app: - case U8_Hdr_IMET_bnr: - ret = AddIMET( headerType );//data is lz77 compressed in this function if it needs to be - break; - case U8_Hdr_IMD5: - { - if( isLz77 ) - ret = LZ77::Compress( ret ); + switch( headerType ) + { + case U8_Hdr_IMET_app: + case U8_Hdr_IMET_bnr: + ret = AddIMET( headerType );//data is lz77 compressed in this function if it needs to be + break; + case U8_Hdr_IMD5: + { + if( isLz77 ) + ret = LZ77::Compress( ret ); - ret = AddIMD5( ret ); + ret = AddIMD5( ret ); - //hexdump( ret, 0, 0x40 ); - } - break; - default: - break; - } - return ret; + //hexdump( ret, 0, 0x40 ); + } + break; + default: + break; + } + return ret; } //check if this is a path to a file in a nested archive QMap::iterator i = nestedU8s.begin(); while( i != nestedU8s.constEnd() ) { - if( str.startsWith( i.key() ) && str != i.key() ) - { - QString subPath = str; - subPath.remove( 0, i.key().size() + 1 );//remove the path of the archive itself + the slash - return i.value().GetData( subPath, onlyPayload ); - } - i++; + if( str.startsWith( i.key() ) && str != i.key() ) + { + QString subPath = str; + subPath.remove( 0, i.key().size() + 1 );//remove the path of the archive itself + the slash + return i.value().GetData( subPath, onlyPayload ); + } + i++; } int index = FindEntry( str ); if( index < 0 ) { - qWarning() << "U8::GetData" << str << "was not found in the archive"; - return QByteArray(); + qWarning() << "U8::GetData" << str << "was not found in the archive"; + return QByteArray(); } if( fst[ index ].Type ) { - qWarning() << "U8::GetData" << str << "is a directory"; - return QByteArray(); + qWarning() << "U8::GetData" << str << "is a directory"; + return QByteArray(); } QByteArray ret = data.mid( qFromBigEndian( fst[ index ].FileOffset ), qFromBigEndian( fst[ index ].FileLength ) ); //hexdump( ret, 0, 0x40 ); if( onlyPayload ) { - if( LZ77::GetLz77Offset( ret ) != -1 ) - ret = LZ77::Decompress( ret ); + if( LZ77::GetLz77Offset( ret ) != -1 ) + ret = LZ77::Decompress( ret ); - else if( IsAshCompressed( ret ) ) - ret = DecryptAsh( ret ); + else if( IsAshCompressed( ret ) ) + ret = DecryptAsh( ret ); } return ret; } @@ -1154,18 +1154,18 @@ quint32 U8::GetSize( const QString &str ) { if( str.isEmpty() ) { - return data.size(); + return data.size(); } int index = FindEntry( str ); if( index < 0 ) { - qWarning() << "U8::GetSize" << str << "was not found in the archive"; - return 0; + qWarning() << "U8::GetSize" << str << "was not found in the archive"; + return 0; } if( fst[ index ].Type ) { - qWarning() << "U8::GetSize" << str << "is a directory"; - return 0; + qWarning() << "U8::GetSize" << str << "is a directory"; + return 0; } return qFromBigEndian( fst[ index ].FileLength ); } @@ -1185,11 +1185,11 @@ bool U8::IsU8( const QByteArray &ba ) { QByteArray data = ba; if( IsAshCompressed( data ) )//decrypt ASH0 files - data = DecryptAsh( data ); + data = DecryptAsh( data ); int off = LZ77::GetLz77Offset( data );//decrypt LZ77 if( off != -1 ) - data = LZ77::Decompress( data ); + data = LZ77::Decompress( data ); QByteArray start = data.left( 5000 ); return start.indexOf( "U\xAA\x38\x2d" ) != -1; @@ -1216,54 +1216,54 @@ void U8::ReadHeader( const QByteArray &ba ) imetNames.clear(); if( ba.startsWith( "IMD5" ) )//dont bother to read any more data { - //qDebug() << "IMD5 header"; - headerType = U8_Hdr_IMD5; - return; + //qDebug() << "IMD5 header"; + headerType = U8_Hdr_IMD5; + return; } QByteArray start = ba.left( sizeof( IMET ) + 0x80 ); QBuffer buf( &start ); if( !buf.open( QBuffer::ReadOnly ) ) { - qWarning() << "U8::ReadHeader Can't create buffer"; - return; + qWarning() << "U8::ReadHeader Can't create buffer"; + return; } int off = start.indexOf( "IMET" ); //qDebug() << "imet offset" << hex << off << "u8 offset" << hex << GetU8Offset( ba ); if( off == 0x40 || off == 0x80 )//read imet header { - if( off > GetU8Offset( ba ) )//in case somebody wants to put a IMET archive inside another U8 for whatever reason - return; + if( off > GetU8Offset( ba ) )//in case somebody wants to put a IMET archive inside another U8 for whatever reason + return; - if( off == 0x40 ) - headerType = U8_Hdr_IMET_bnr; - else - headerType = U8_Hdr_IMET_app; + if( off == 0x40 ) + headerType = U8_Hdr_IMET_bnr; + else + headerType = U8_Hdr_IMET_app; - buf.seek( off ); - IMET imet; - buf.read( (char*)&imet, sizeof( IMET ) ); - buf.close(); + buf.seek( off ); + IMET imet; + buf.read( (char*)&imet, sizeof( IMET ) ); + buf.close(); - for( int h = 0; h < 10; h ++ ) - { - QString name; - for( int i = 0; i < IMET_MAX_NAME_LEN && imet.names[ h ][ i ] != 0; i++ )//no need to switch endian, 0x0000 is a palendrome - name += QChar( qFromBigEndian( imet.names[ h ][ i ] ) ); + for( int h = 0; h < 10; h ++ ) + { + QString name; + for( int i = 0; i < IMET_MAX_NAME_LEN && imet.names[ h ][ i ] != 0; i++ )//no need to switch endian, 0x0000 is a palendrome + name += QChar( qFromBigEndian( imet.names[ h ][ i ] ) ); - imetNames << name; - } - //done - return; + imetNames << name; + } + //done + return; } } QByteArray U8::AddIMD5( QByteArray ba ) { quint32 size = ba.size(); - MD5 hash; + /*MD5 hash; hash.update( ba.data(), size ); - hash.finalize(); + hash.finalize();*/ size = qFromBigEndian( size ); @@ -1271,8 +1271,8 @@ QByteArray U8::AddIMD5( QByteArray ba ) QBuffer buf( &imd5 ); if( !buf.open( QBuffer::WriteOnly ) ) { - qWarning() << "U8::AddIMD5 Can't create buffer"; - return QByteArray(); + qWarning() << "U8::AddIMD5 Can't create buffer"; + return QByteArray(); } buf.putChar( 'I' ); buf.putChar( 'M' ); @@ -1282,7 +1282,8 @@ QByteArray U8::AddIMD5( QByteArray ba ) buf.write( (const char*)&size, 4 ); buf.seek( 0x10 ); - buf.write( (const char*)hash.hexdigestChar(), 16 ); + buf.write( GetMd5( ba ) ); + //buf.write( (const char*)hash.hexdigestChar(), 16 ); buf.close(); //qDebug() << hash.hexdigest().c_str(); //hexdump( (void*)imd5.data(), 0x20 ); @@ -1305,8 +1306,8 @@ QByteArray U8::GetIMET( const QStringList &names, int paddingType, quint32 iconS QBuffer buf( &ret ); if( !buf.open( QBuffer::WriteOnly ) ) { - qWarning() << "U8::GetIMET Can't create buffer"; - return QByteArray(); + qWarning() << "U8::GetIMET Can't create buffer"; + return QByteArray(); } buf.seek( 0 + 0x40 ); buf.putChar( 'I' ); @@ -1329,34 +1330,35 @@ QByteArray U8::GetIMET( const QStringList &names, int paddingType, quint32 iconS int numNames = names.size(); for( int i = 0; i < numNames; i++ ) { - QString name = names.at( i ); - int nameLen = name.size(); - buf.seek( nameOffset + ( IMET_MAX_NAME_LEN * i * 2 ) ); - for( int j = 0; j < nameLen; j++ ) - { - quint16 letter = qFromBigEndian( name.at( j ).unicode() ); - buf.write( ( const char*)&letter, 2 ); - } + QString name = names.at( i ); + int nameLen = name.size(); + buf.seek( nameOffset + ( IMET_MAX_NAME_LEN * i * 2 ) ); + for( int j = 0; j < nameLen; j++ ) + { + quint16 letter = qFromBigEndian( name.at( j ).unicode() ); + buf.write( ( const char*)&letter, 2 ); + } } - MD5 hash; + /*MD5 hash; hash.update( ret.data(), sizeof( IMET ) + 0x40 ); - hash.finalize(); + hash.finalize();*/ buf.seek( 0x5f0 ); - buf.write( (const char*)hash.hexdigestChar(), 16 ); + buf.write( GetMd5( ret ) ); + //buf.write( (const char*)hash.hexdigestChar(), 16 ); buf.close(); switch( paddingType ) { case U8_Hdr_IMET_app: - ret.prepend( QByteArray( 0x40, '\0' ) ); - break; + ret.prepend( QByteArray( 0x40, '\0' ) ); + break; case U8_Hdr_IMET_bnr: - break; + break; default: - ret.remove( 0, 0x40 ); - break; + ret.remove( 0, 0x40 ); + break; } return ret; } @@ -1384,27 +1386,27 @@ const QByteArray U8::AddIMET( int paddingType ) //otherwise write the size - 0x20 ( imd5 header size ) if( LZ77::GetLz77Offset( ret ) != -1 ) - iconSize = LZ77::GetDecompressedSize( ret ); + iconSize = LZ77::GetDecompressedSize( ret ); else - iconSize = ret.size() - 0x20; + iconSize = ret.size() - 0x20; ret = GetData( "meta/banner.bin" ); if( LZ77::GetLz77Offset( ret ) != -1 ) - bannerSize = LZ77::GetDecompressedSize( ret ); + bannerSize = LZ77::GetDecompressedSize( ret ); else - bannerSize = ret.size() - 0x20; + bannerSize = ret.size() - 0x20; ret = GetData( "meta/sound.bin" ); if( LZ77::GetLz77Offset( ret ) != -1 ) - soundSize = LZ77::GetDecompressedSize( ret ); + soundSize = LZ77::GetDecompressedSize( ret ); else - soundSize = ret.size() - 0x20; + soundSize = ret.size() - 0x20; ret = GetIMET( imetNames, paddingType, iconSize, bannerSize, soundSize ); if( isLz77 )//really? can the entire banner be lz77 compressed? - ret += LZ77::Compress( data ); + ret += LZ77::Compress( data ); else - ret += data; + ret += data; return ret; } diff --git a/WiiQt/wad.cpp b/WiiQt/wad.cpp index 3f97e38..96f6500 100644 --- a/WiiQt/wad.cpp +++ b/WiiQt/wad.cpp @@ -7,6 +7,8 @@ static QByteArray globalCert; Wad::Wad( const QByteArray &stuff ) { ok = false; + if( !stuff.size() )//prevent error text when it isnt required + return; if( stuff.size() < 0x80 )//less than this and there is definitely nothing there { Err( "Size is < 0x80" ); @@ -83,6 +85,9 @@ Wad::Wad( const QByteArray &stuff ) Ticket ticket( tikData ); Tmd t( tmdData ); + //the constructor for Ticket may have fixed a bad key index. replace the data just incase it did + tikData = ticket.Data(); + //hexdump( tikData ); //hexdump( tmdData ); @@ -107,8 +112,8 @@ Wad::Wad( const QByteArray &stuff ) { quint32 s = RU( t.Size( i ), 0x40 ); - qDebug() << "content" << i << "is at" << hex << pos - << "with size" << s; + //qDebug() << "content" << i << "is at" << hex << pos + // << "with size" << s; QByteArray encData = stuff.mid( pos, s ); pos += s; @@ -134,6 +139,7 @@ Wad::Wad( const QByteArray &stuff ) Wad::Wad( const QList< QByteArray > &stuff, bool encrypted ) { + ok = false; if( stuff.size() < 3 ) { Err( "Cant treate a wad with < 3 items" ); @@ -173,6 +179,84 @@ Wad::Wad( const QList< QByteArray > &stuff, bool encrypted ) } +Wad::Wad( QDir dir ) +{ + ok = false; + QFileInfoList tmds = dir.entryInfoList( QStringList() << "*.tmd" << "tmd.*", QDir::Files ); + if( tmds.isEmpty() ) + { + Err( "TMD not found" ); + return; + } + tmdData = ReadFile( tmds.at( 0 ).absoluteFilePath() ); + if( tmdData.isEmpty() ) + return; + QFileInfoList tiks = dir.entryInfoList( QStringList() << "*.tik" << "cetk", QDir::Files ); + if( tiks.isEmpty() ) + { + Err( "Ticket not found" ); + return; + } + tikData = ReadFile( tiks.at( 0 ).absoluteFilePath() ); + if( tikData.isEmpty() ) + return; + + Tmd t( tmdData ); + Ticket ticket( tikData ); + + //make sure to only add the tmd & ticket without all the cert mumbo jumbo + tmdData = t.Data(); + tikData = ticket.Data(); + + quint16 cnt = t.Count(); + + bool tmdChanged = false; + for( quint16 i = 0; i < cnt; i++ ) + { + QByteArray appD = ReadFile( dir.absoluteFilePath( t.Cid( i ) + ".app" ) ); + if( appD.isEmpty() ) + { + Err( t.Cid( i ) + ".app not found" ); + return; + } + + if( (quint32)appD.size() != t.Size( i ) ) + { + t.SetSize( i, appD.size() ); + tmdChanged = true; + } + QByteArray realHash = GetSha1( appD ); + if( t.Hash( i ) != realHash ) + { + t.SetHash( i, realHash ); + tmdChanged = true; + } + AesSetKey( ticket.DecryptedKey() ); + appD = PaddedByteArray( appD, 0x40 ); + QByteArray encData = AesEncrypt( i, appD ); + partsEnc << encData; + } + //if something in the tmd changed, fakesign it + if( tmdChanged ) + { + if( !t.FakeSign() ) + { + Err( "Error signing the wad" ); + return; + } + else + { + tmdData = t.Data(); + } + } + QFileInfoList certs = dir.entryInfoList( QStringList() << "*.cert", QDir::Files ); + if( !certs.isEmpty() ) + { + certData = ReadFile( certs.at( 0 ).absoluteFilePath() ); + } + ok = true; +} + void Wad::SetCert( const QByteArray &stuff ) { certData = stuff; @@ -204,6 +288,11 @@ const QByteArray Wad::getTik() return tikData; } +const QByteArray Wad::GetCert() +{ + return certData.isEmpty() ? globalCert : certData; +} + const QByteArray Wad::Content( quint16 i ) { if( tmdData.isEmpty() || tikData.isEmpty() ) @@ -547,7 +636,7 @@ QByteArray Wad::FromDirectory( QDir dir ) return ret; } -bool Wad::SetTid( quint64 tid ) +bool Wad::SetTid( quint64 tid, bool fakeSign ) { if( !tmdData.size() || !tikData.size() ) { @@ -560,12 +649,12 @@ bool Wad::SetTid( quint64 tid ) t.SetTid( tid ); ti.SetTid( tid ); - if( !t.FakeSign() ) + if( fakeSign && !t.FakeSign() ) { Err( "Error signing TMD" ); return false; } - if( !ti.FakeSign() ) + if( fakeSign && !ti.FakeSign() ) { Err( "Error signing ticket" ); return false; @@ -575,7 +664,7 @@ bool Wad::SetTid( quint64 tid ) return true; } -bool Wad::SetIOS( quint32 ios ) +bool Wad::SetIOS( quint32 ios, bool fakeSign ) { if( !tmdData.size() || !tikData.size() ) { @@ -586,7 +675,7 @@ bool Wad::SetIOS( quint32 ios ) t.SetIOS( ios ); - if( !t.FakeSign() ) + if( fakeSign && !t.FakeSign() ) { Err( "Error signing TMD" ); return false; @@ -595,7 +684,27 @@ bool Wad::SetIOS( quint32 ios ) return true; } -bool Wad::SetAhb( bool remove ) +bool Wad::SetVersion( quint16 ver, bool fakeSign ) +{ + if( !tmdData.size() || !tikData.size() ) + { + Err( "Mising parts of the wad" ); + return false; + } + Tmd t( tmdData ); + + t.SetVersion( ver ); + + if( fakeSign && !t.FakeSign() ) + { + Err( "Error signing TMD" ); + return false; + } + tmdData = t.Data(); + return true; +} + +bool Wad::SetAhb( bool remove, bool fakeSign ) { if( !tmdData.size() || !tikData.size() ) { @@ -606,7 +715,7 @@ bool Wad::SetAhb( bool remove ) t.SetAhb( remove ); - if( !t.FakeSign() ) + if( fakeSign && !t.FakeSign() ) { Err( "Error signing TMD" ); return false; @@ -615,7 +724,7 @@ bool Wad::SetAhb( bool remove ) return true; } -bool Wad::SetDiskAccess( bool allow ) +bool Wad::SetDiskAccess( bool allow, bool fakeSign ) { if( !tmdData.size() || !tikData.size() ) { @@ -626,7 +735,7 @@ bool Wad::SetDiskAccess( bool allow ) t.SetDiskAccess( allow ); - if( !t.FakeSign() ) + if( fakeSign && !t.FakeSign() ) { Err( "Error signing TMD" ); return false; @@ -635,6 +744,37 @@ bool Wad::SetDiskAccess( bool allow ) return true; } +bool Wad::FakeSign( bool signTmd, bool signTicket ) +{ + if( !tmdData.size() || !tikData.size() ) + { + Err( "Mising parts of the wad" ); + return false; + } + + if( signTicket ) + { + Ticket ti( tikData ); + if( !ti.FakeSign() ) + { + Err( "Error signing ticket" ); + return false; + } + tikData = ti.Data(); + } + if( signTmd ) + { + Tmd t( tmdData ); + if( !t.FakeSign() ) + { + Err( "Error signing TMD" ); + return false; + } + tmdData = t.Data(); + } + return true; +} + bool Wad::ReplaceContent( quint16 idx, const QByteArray &ba ) { if( idx >= partsEnc.size() || !tmdData.size() || !tikData.size() ) diff --git a/WiiQt/wad.h b/WiiQt/wad.h index 3087e41..137bccd 100644 --- a/WiiQt/wad.h +++ b/WiiQt/wad.h @@ -13,6 +13,13 @@ public: //it will use the global cert unless one is given with SetCert Wad( const QList< QByteArray > &stuff, bool isEncrypted = true ); + //create a wad from the given directory + //! dir should be a directory containing a tmd ( "*.tmd" or "tmd.*" ), a ticket ( "*.tik" or "cetk" ), + //! all the contents ( .app :where cid is the correct cid from the tmd, not the "0, 1, 2, 3..." bullshit some broken wad-unpackers create ) + //! if any of the .apps do not match in size or hash what is in the TMD, then the TMD will be updated and fakesigned + //! a cert ( "*.cert" ) is also supported, but not required + Wad( QDir dir ); + //check if this wad is valid bool IsOk(){ return ok; } @@ -23,16 +30,21 @@ public: quint64 Tid(); //set the tid in the ticket&tmd and fakesign the wad - bool SetTid( quint64 tid ); + bool SetTid( quint64 tid, bool fakeSign = true ); - //set the ios in the ticket&tmd and fakesign the wad - bool SetIOS( quint32 ios ); + //set the ios in the tmd and fakesign the wad + bool SetIOS( quint32 ios, bool fakeSign = true ); + + //set the version in the tmd and fakesign the wad + bool SetVersion( quint16 ver, bool fakeSign = true ); //set the tmd to allow AHBPROT removal - bool SetAhb( bool remove = true ); + bool SetAhb( bool remove = true, bool fakeSign = true ); //set the tmd to allow direct disc access - bool SetDiskAccess( bool allow = true ); + bool SetDiskAccess( bool allow = true, bool fakeSign = true ); + + bool FakeSign( bool signTmd, bool signTicket ); //replace a content of this wad, update the size & hash in the tmd and sign it //ba should be decrypted @@ -51,11 +63,7 @@ public: //pack a wad from the given directory //returns a bytearray containing a wad reading for writing to a file - //or an empty bytearray on error - //! dir should be a directory containing a tmd ( "*.tmd" or "tmd.*" ), a ticket ( "*.tik" or "cetk" ), - //! all the contents ( .app :where cid is the correct cid from the tmd, not the "0, 1, 2, 3..." bullshit some broken wad-unpackers create ) - //! if any of the .apps do not match in size or hash what is in the TMD, then the TMD will be updated and fakesigned - //! a cert ( "*.cert" ) is also supported, but not required + //or an empty bytearray on error static QByteArray FromDirectory( QDir dir ); //get a assembled wad from the list of parts @@ -72,6 +80,8 @@ public: //get the tik for the wad const QByteArray getTik(); + const QByteArray GetCert(); + //get the decrypted data from a content const QByteArray Content( quint16 i ); diff --git a/nandBinCheck/main.cpp b/nandBinCheck/main.cpp index cc5a016..01b90ab 100644 --- a/nandBinCheck/main.cpp +++ b/nandBinCheck/main.cpp @@ -200,9 +200,9 @@ QList< quint64 > InstalledTitles() for( quint16 i = 0; i < subfc; i++ )//check all subfolders of "/ticket" { QTreeWidgetItem *subF = tikFolder->child( i ); - //qDebug() << "checking folder" << subF->text( 0 ); + //qDebug() << "checking folder" << subF->text( 0 ); bool ok = false; - quint32 upper = subF->text( 0 ).toInt( &ok, 16 );//make sure it can be converted to int + quint32 upper = subF->text( 0 ).toUInt( &ok, 16 );//make sure it can be converted to int if ( !ok ) continue; @@ -211,18 +211,18 @@ QList< quint64 > InstalledTitles() { QTreeWidgetItem *tikI = subF->child( j ); QString name = tikI->text( 0 ); - //qDebug() << "checking item" << subF->text( 0 ) + "/" + name; + //qDebug() << "checking item" << subF->text( 0 ) + "/" + name; if( !name.endsWith( ".tik" ) ) { - //qDebug() << "!tik"; + //qDebug() << "!tik"; continue; } name.resize( 8 ); - quint32 lower = name.toInt( &ok, 16 ); + quint32 lower = name.toUInt( &ok, 16 ); if( !ok ) { - //qDebug() << "!ok"; + //qDebug() << "!ok"; continue; } @@ -230,15 +230,16 @@ QList< quint64 > InstalledTitles() QTreeWidgetItem *tmdI = ItemFromPath( "/title/" + subF->text( 0 ) + "/" + name + "/content/title.tmd" ); if( !tmdI ) { - //qDebug() << "no tmd"; + //qDebug() << "no tmd"; continue; } quint64 tid = (( (quint64)upper << 32) | lower ); - //qDebug() << "adding item to list" << TidTxt( tid ); + //qDebug() << "adding item to list" << TidTxt( tid ); ret << tid; } } + qSort( ret.begin(), ret.end() ); return ret; } @@ -301,6 +302,24 @@ void BuildGoodIosList() } } +bool RecurseCheckGidUid( QTreeWidgetItem *item, const QString &uidS, const QString &gidS, const QString &path ) +{ + bool ret = true; + quint16 cnt = item->childCount(); + for( quint16 i = 0; i < cnt; i++ ) + { + QTreeWidgetItem *child = item->child( i ); + if( child->text( 3 ) != uidS || !child->text( 4 ).startsWith( gidS ) ) + { + ret = false; + qDebug() << "\tincorrect uid/gid for" << QString( path + child->text( 0 ) ); + } + if( !RecurseCheckGidUid( child, uidS, gidS, path + child->text( 0 ) + "/" ) ) + ret = false; + } + return ret; +} + bool CheckTitleIntegrity( quint64 tid ) { if( validIoses.contains( tid ) )//this one has already been checked @@ -357,7 +376,7 @@ bool CheckTitleIntegrity( quint64 tid ) } else { - Ticket ticket( ba ); + Ticket ticket( ba, false ); if( ticket.Tid() != tid ) { qDebug() << "the ticket contains the wrong TID"; @@ -378,7 +397,7 @@ bool CheckTitleIntegrity( quint64 tid ) { if( sharedM.GetAppFromHash( t.Hash( i ) ).isEmpty() ) { - qDebug() << "one of the shared contents is missing"; + qDebug() << "\tone of the shared contents is missing"; return false; } } @@ -391,7 +410,7 @@ bool CheckTitleIntegrity( quint64 tid ) QByteArray ba = nand.GetData( pA ); if( ba.isEmpty() ) { - qDebug() << "one of the private contents is missing" << pA; + qDebug() << "\t error reading one of the private contents" << pA; return false; } QByteArray realH = GetSha1( ba ); @@ -432,13 +451,15 @@ bool CheckTitleIntegrity( quint64 tid ) QString gidS = QString( "%1" ).arg( gid, 4, 16, QChar( '0' ) ); if( dataI->text( 3 ) != uidS || !dataI->text( 4 ).startsWith( gidS ) )//dont necessarily fail for this. the title will still be bootable without its data qDebug() << "\tincorrect uid/gid for data folder"; - quint16 cnt = dataI->childCount(); + + RecurseCheckGidUid( dataI, uidS, gidS, "data/" ); + /*quint16 cnt = dataI->childCount(); for( quint16 i = 0; i < cnt; i++ ) { QTreeWidgetItem *item = dataI->child( i ); if( item->text( 3 ) != uidS || !item->text( 4 ).startsWith( gidS ) ) qDebug() << "\tincorrect uid/gid for" << QString( "data/" + item->text( 0 ) ); - } + }*/ } dataP.resize( 25 ); dataP += "content"; @@ -617,8 +638,8 @@ void CheckHmac() int main( int argc, char *argv[] ) { QCoreApplication a( argc, argv ); - QStringList args = QCoreApplication::arguments(); - if( args.size() < 2 ) + QStringList args = QCoreApplication::arguments(); + if( args.size() < 2 ) Usage(); if( !QFile( args.at( 1 ) ).exists() ) @@ -678,8 +699,6 @@ int main( int argc, char *argv[] ) qDebug() << "verifying hmac..."; CheckHmac(); } - - return 0; } diff --git a/nand_dump/mainwindow.cpp b/nand_dump/mainwindow.cpp index 0d602fb..d98c6ca 100644 --- a/nand_dump/mainwindow.cpp +++ b/nand_dump/mainwindow.cpp @@ -37,24 +37,7 @@ MainWindow::MainWindow( QWidget *parent ) : QMainWindow( parent ), ui( new Ui::M connect( &nus, SIGNAL( SendDone() ), this, SLOT( NusIsDone() ) ); connect( &nus, SIGNAL( SendData( NusJob ) ), this, SLOT( ReceiveTitleFromNus( NusJob) ) ); - //TODO, really get these paths from settings - -/*#ifdef Q_WS_WIN - QString cachePath = "../../NUS_cache"; -#else - QString cachePath = "../NUS_cache"; -#endif - QString nandPath = "./dump"; - - ui->lineEdit_cachePath->setText( cachePath ); - ui->lineEdit_nandPath->setText( nandPath ); - ui->lineEdit_extractPath->setText( "./downloaded" );*/ - LoadSettings(); - - - //nand.SetPath( nandPath ); - //nus.SetCachePath( cachePath ); } MainWindow::~MainWindow() diff --git a/saveToy/mainwindow.cpp b/saveToy/mainwindow.cpp index f94f7a1..2927e2d 100644 --- a/saveToy/mainwindow.cpp +++ b/saveToy/mainwindow.cpp @@ -39,7 +39,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi } else if( !pcPath.isEmpty() ) GetSavesFromPC( pcPath ); - } MainWindow::~MainWindow()