wiiqt6/WiiQt/nandbin.cpp

1165 lines
28 KiB
C++
Raw Normal View History

#include "nandbin.h"
#include "tools.h"
NandBin::NandBin( QObject * parent, const QString &path ) : QObject( parent )
{
type = -1;
fatNames = false;
fstInited = false;
root = NULL;
if( !path.isEmpty() )
SetPath( path );
}
NandBin::~NandBin()
{
if( f.isOpen() )
f.close();
if( root )
delete root;
}
bool NandBin::SetPath( const QString &path )
{
fstInited = false;
nandPath = path;
bootBlocks = Blocks0to7();
if( f.isOpen() )
f.close();
f.setFileName( path );
bool ret = !f.exists() || !f.open( QIODevice::ReadOnly );
if( ret )
{
emit SendError( tr( "Cant open %1" ).arg( path ) );
}
return !ret;
}
QTreeWidgetItem *NandBin::GetTree()
{
return root->clone();
}
bool NandBin::ExtractToDir( QTreeWidgetItem *item, const QString &path )
{
if( !item )
return false;
bool ok = false;
quint16 entry = item->text( 1 ).toInt( &ok );
if( !ok )
{
emit SendError( tr( "Error converting entry(%1) to a number" ).arg( item->text( 1 ) ) );
return false;
}
return ExtractFST( entry, path, true );//dont bother extracting this item's siblings
return true;
}
bool NandBin::AddChildren( QTreeWidgetItem *parent, quint16 entry )
{
if( entry >= 0x17ff )
{
emit SendError( tr( "entry is above 0x17ff mmmmkay [ 0x%1 ]" ).arg( entry, 0, 16 ) );
return false;
}
fst_t fst = GetFST( entry );
if( !fst.filename[ 0 ] )//something is amiss, better quit now
return false;
if( fst.sib != 0xffff )
{
if( !AddChildren( parent, fst.sib ) )
return false;
}
QStringList text;
QString name = FstName( fst );
QString en = QString( "%1" ).arg( entry );
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
if( fst.mode )
{
child->setIcon( 0, keyIcon );
}
else
{
child->setIcon( 0, groupIcon );
//try to add subfolder contents to the tree
if( fst.sub != 0xffff && !AddChildren( child, fst.sub ) )
return false;
}
return true;
}
QString NandBin::FstName( fst_t fst )
{
QByteArray ba( (char*)fst.filename, 0xc );
QString ret = QString( ba );
if( fatNames )
ret.replace( ":", "-" );
return ret;
}
bool NandBin::ExtractFST( quint16 entry, const QString &path, bool singleFile )
{
//qDebug() << "NandBin::ExtractFST(" << hex << entry << "," << path << ")";
fst_t fst = GetFST( entry );
if( !fst.filename[ 0 ] )//something is amiss, better quit now
return false;
if( !singleFile && fst.sib != 0xffff && !ExtractFST( fst.sib, path ) )
return false;
switch( fst.mode )
{
case 0:
if( !ExtractDir( fst, path ) )
return false;
break;
case 1:
if( !ExtractFile( fst, path ) )
return false;
break;
default://wtf
emit SendError( tr( "Unknown fst mode. Bailing out" ) );
return false;
break;
}
return true;
}
bool NandBin::ExtractDir( fst_t fst, QString parent )
{
//qDebug() << "NandBin::ExtractDir(" << parent << ")";
QByteArray ba( (char*)fst.filename, 0xc );
QString filename( ba );
QFileInfo fi( parent );
if( filename != "/" )
{
fi.setFile( parent + "/" + filename );
if( !fi.exists() && !QDir().mkpath( fi.absoluteFilePath() ) )
{
emit SendError( tr( "Can\'t create directory \"%1\"" ).arg( fi.absoluteFilePath() ) );
return false;
}
}
if( fst.sub != 0xffff && !ExtractFST( fst.sub, fi.absoluteFilePath() ) )
return false;
return true;
}
bool NandBin::ExtractFile( fst_t fst, QString parent )
{
QByteArray ba( (char*)fst.filename, 0xc );
QString filename( ba );
QFileInfo fi( parent + "/" + filename );
qDebug() << "extract" << fi.absoluteFilePath();
emit SendText( tr( "Extracting \"%1\"" ).arg( fi.absoluteFilePath() ) );
QByteArray data = GetFile( fst );
if( fst.size && !data.size() )//dont worry if files dont have anything in them anyways
return false;
if( !WriteFile( fi.absoluteFilePath(), data ) )
{
emit SendError( tr( "Error writing \"%1\"" ).arg( fi.absoluteFilePath() ) );
return false;
}
return true;
}
bool NandBin::InitNand( QIcon dirs, QIcon files )
{
fstInited = false;
fats.clear();
type = GetDumpType( f.size() );
if( type < 0 || type > 3 )
return false;
//qDebug() << "dump type:" << type;
if( !GetKey( type ) )
return false;
loc_super = FindSuperblock();
if( loc_super < 0 )
return false;
quint32 n_fatlen[] = { 0x010000, 0x010800, 0x010800 };
loc_fat = loc_super;
loc_fst = loc_fat + 0x0C + n_fatlen[ type ];
//cache all the entries
for( quint16 i = 0; i < 0x17ff; i++ )
fsts[ i ] = GetFST( i );
//cache all the fats
for( quint16 i = 0; i < 0x8000; i++ )
fats << GetFAT( i );
fstInited = true;
if( root )
delete root;
groupIcon = dirs;
keyIcon = files;
root = new QTreeWidgetItem( QStringList() << nandPath );
AddChildren( root, 0 );
//checkout the blocks for boot1&2
QList<QByteArray>blocks;
for( quint16 i = 0; i < 8; i++ )
{
QByteArray block;
for( quint16 j = 0; j < 8; j++ )
{
block += GetCluster( ( i * 8 ) + j, false );
}
if( block.size() != 0x4000 * 8 )
{
qDebug() << "wrong block size" << i;
return false;
}
blocks << block;
}
//debug shitzz
//ecc
/*int blNo = 7;
for( int b = 0; b < 8; b++ )
{
qDebug() << "cluster" << b << "of block" << hex << blNo;
int clNo = ( blNo * 8 ) + b;
for( int i = 0; i < 8; i++ )
{
qDebug() << "page" << i << "of cluster" << hex << ( clNo - ( blNo * 8 ) ) << "(" << clNo << ")";
QByteArray whole = GetPage( ( clNo * 8 ) + i, true );
if( whole.size() != 0x840 )
{
qDebug() << "wrong size" << hex << whole.size();
continue;
}
QByteArray eccR = whole.mid( 0x800, 0x40 );
QByteArray eccC = NandSpare::CalcEcc( whole.left( 0x800 ) );
hexdump( eccR );
hexdump( eccC );
}
}*/
//this makes correct hmac data for the superblocks
/*int blNo = 0xff0;//first superblock
//int blNo = 0xffe;//last superblock
for( int b = blNo; b < 16 + blNo; b += 2 )
{
//if( !( b % 2 ) )
//continue;
QByteArray clData;
for( int c = 0; c < 16; c++ )
{
for( int p = 0; p < 8; p++ )
{
QByteArray whole = GetPage( ( ( b * 64 ) + ( c * 8 ) + p ), true );
clData += whole.left( 0x800 );
}
}
qDebug() << "hmac:";
QByteArray hmR = spare.Get_hmac_meta( clData, ( b * 8 ) );
hexdump( hmR );
}*/
//GetFile( 369 );
if( !bootBlocks.SetBlocks( blocks ) )
return false;
return true;
}
int NandBin::GetDumpType( quint64 fileSize )
{
quint64 sizes[] = { 536870912, // type 0 | 536870912 == no ecc
553648128, // type 1 | 553648128 == ecc
553649152 }; // type 2 | 553649152 == old bootmii
for( int i = 0; i < 3; i++ )
{
if( sizes[ i ] == fileSize )
return i;
}
emit SendError( tr( "Can't tell what type of nand dump this is" ) );
return -1;
}
const QList<Boot2Info> NandBin::Boot2Infos()
{
if( !bootBlocks.IsOk() )
return QList<Boot2Info>();
return bootBlocks.Boot2Infos();
}
quint8 NandBin::Boot1Version()
{
if( !bootBlocks.IsOk() )
return 0;
return bootBlocks.Boot1Version();
}
bool NandBin::GetKey( int type )
{
QByteArray hmacKey;
switch( type )
{
case 0:
case 1:
{
QString keyPath = nandPath;
int sl = keyPath.lastIndexOf( "/" );
if( sl == -1 )
{
emit SendError( tr( "Error getting path of keys.bin" ) );
return false;
}
keyPath.resize( sl + 1 );
keyPath += "keys.bin";
key = ReadKeyfile( keyPath, 0 );
if( key.isEmpty() )
return false;
hmacKey = ReadKeyfile( keyPath, 1 );
if( hmacKey.isEmpty() )
return false;
}
break;
case 2:
{
if( !f.isOpen() )
{
emit SendError( tr( "Tried to read keys from unopened file" ) );
return false;
}
f.seek( 0x21000144 );
hmacKey = f.read( 20 );
f.seek( 0x21000158 );
key = f.read( 16 );
}
break;
default:
emit SendError( tr( "Tried to read keys for unknown dump type" ) );
return false;
break;
}
spare.SetHMacKey( hmacKey );//set the hmac key for calculating spare data
//hexdump( hmacKey );
return true;
}
QByteArray NandBin::ReadKeyfile( QString path, quint8 type )
{
QByteArray retval;
QFile f( path );
if( !f.exists() || !f.open( QIODevice::ReadOnly ) )
{
emit SendError( tr( "Can't open %1!" ).arg( path ) );
return retval;
}
if( f.size() < 0x400 )
{
f.close();
emit SendError( tr( "keys.bin is too small!" ) );
return retval;
}
if( type == 0 )
{
f.seek( 0x158 );
retval = f.read( 16 );
}
else if( type == 1 )
{
f.seek( 0x144 );
retval = f.read( 20 );
}
f.close();
return retval;
}
qint32 NandBin::FindSuperblock()
{
if( type < 0 || type > 3 )
{
emit SendError( tr( "Tried to get superblock of unknown dump type" ) );
return -1;
}
if( !f.isOpen() )
{
emit SendError( tr( "Tried to get superblock of unopened dump" ) );
return -1;
}
quint32 loc = 0, current = 0, last = 0;
quint32 n_start[] = { 0x1FC00000, 0x20BE0000, 0x20BE0000 },
n_end[] = { 0x20000000, 0x21000000, 0x21000000 },
n_len[] = { 0x40000, 0x42000, 0x42000 };
for( loc = n_start[ type ]; loc < n_end[ type ]; loc += n_len[ type ] )
{
f.read( (char*)&current, 4 );
current = qFromBigEndian( current );
//qDebug() << "superblock" << hex << current;
if( current > last )
last = current;
else
{
//qDebug() << "superblock loc" << hex << loc - n_len[ type ];
return loc - n_len[ type ];
}
f.seek( n_len[ type ] - 4 );
}
return -1;//hmmmm what happens if the last supercluster is the latest one? seems like a bug to fix at a later date...
}
fst_t NandBin::GetFST( quint16 entry )
{
//qDebug() << "NandBin::GetFST(" << hex << entry << ")";
fst_t fst;
if( entry >= 0x17FF )
{
emit SendError( tr( "Tried to get entry above 0x17ff [ 0x%1 ]" ).arg( entry, 0, 16 ) );
fst.filename[ 0 ] = '\0';
return fst;
}
if( fstInited )//we've already read this once, just give back the one we already know
{
//qDebug() << "reading from cache" << hex << entry;
return fsts[ entry ];
}
// compensate for 64 bytes of ecc data every 64 fst entries
quint32 n_fst[] = { 0, 2, 2 };
int loc_entry = ( ( ( entry / 0x40 ) * n_fst[ type ] ) + entry ) * 0x20;
if( (quint32)f.size() < loc_fst + loc_entry + sizeof( fst_t ) )
{
emit SendError( tr( "Tried to read fst_t beyond size of nand.bin" ) );
fst.filename[ 0 ] = '\0';
return fst;
}
f.seek( loc_fst + loc_entry );
f.read( (char*)&fst.filename, 0xc );
f.read( (char*)&fst.mode, 1 );
f.read( (char*)&fst.attr, 1 );
f.read( (char*)&fst.sub, 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
{
f.read( (char*)&fst.size, 2 );
f.seek( f.pos() + 0x40 );
f.read( (char*)(&fst.size) + 2, 2 );
}
else
f.read( (char*)&fst.size, 4 );
f.read( (char*)&fst.uid, 4 );
f.read( (char*)&fst.gid, 2 );
f.read( (char*)&fst.x3, 4 );
fst.sub = qFromBigEndian( fst.sub );
fst.sib = qFromBigEndian( fst.sib );
fst.size = qFromBigEndian( fst.size );
fst.uid = qFromBigEndian( fst.uid );
fst.gid = qFromBigEndian( fst.gid );
fst.x3 = qFromBigEndian( fst.x3 );
fst.fst_pos = entry;
fst.mode &= 1;
return fst;
}
quint16 NandBin::GetFAT( quint16 fat_entry )
{
if( fstInited )
return fats.at( fat_entry );
/*
* compensate for "off-16" storage at beginning of superblock
* 53 46 46 53 XX XX XX XX 00 00 00 00
* S F F S "version" padding?
* 1 2 3 4 5 6*/
fat_entry += 6;
// location in fat of cluster chain
quint32 n_fat[] = { 0, 0x20, 0x20 };
int loc = loc_fat + ((((fat_entry / 0x400) * n_fat[type]) + fat_entry) * 2);
if( (quint32)f.size() < loc + sizeof( quint16 ) )
{
emit SendError( tr( "Tried to read FAT entry beyond size of nand.bin" ) );
return 0;
}
f.seek( loc );
quint16 ret;
f.read( (char*)&ret, 2 );
ret = qFromBigEndian( ret );
return ret;
}
QByteArray NandBin::GetPage( quint32 pageNo, bool withEcc )
{
//qDebug() << "NandBin::GetPage( " << hex << pageNo << ", " << withEcc << " )";
quint32 n_pagelen[] = { 0x800, 0x840, 0x840 };
if( f.size() < ( pageNo + 1 ) * n_pagelen[ type ] )
{
emit SendError( tr( "Tried to read page past size of nand.bin" ) );
return QByteArray();
}
f.seek( pageNo * n_pagelen[ type ] ); //seek to the beginning of the page to read
QByteArray page = f.read( ( type && withEcc ) ? n_pagelen[ type ] : 0x800 );
return page;
}
QByteArray NandBin::GetCluster( quint16 cluster_entry, bool decrypt )
{
//qDebug() << "NandBin::GetCluster" << hex << cluster_entry;
quint32 n_clusterlen[] = { 0x4000, 0x4200, 0x4200 };
quint32 n_pagelen[] = { 0x800, 0x840, 0x840 };
if( f.size() < ( cluster_entry * n_clusterlen[ type ] ) + ( 8 * n_pagelen[ type ] ) )
{
emit SendError( tr( "Tried to read cluster past size of nand.bin" ) );
return QByteArray();
}
QByteArray cluster;
for( int i = 0; i < 8; i++ )
{
f.seek( ( cluster_entry * n_clusterlen[ type ] ) + ( i * n_pagelen[ type ] ) ); //seek to the beginning of the page to read
//QByteArray page = f.read( n_pagelen[ type ] ); //read the page, with ecc
QByteArray page = f.read( 0x800 ); //read the page, skip the ecc
//hexdump( page.mid( 0x800, 0x40 ) );//just here for debugging purposes
//cluster += page.left( 0x800 );
cluster += page;
}
if( cluster.size() != 0x4000 )
{
qDebug() << "actual cluster size" << hex << cluster.size();
emit SendError( tr( "Error reading cluster" ) );
return QByteArray();
}
if( !decrypt )
return cluster;
//really redundant to do this for ever AES decryption, but the AES code only lets
//1 key set at a time and it may be changed if some other object is decrypting something else
AesSetKey( key );
QByteArray ret = AesDecrypt( 0, cluster );
return ret;
}
QByteArray NandBin::GetFile( quint16 entry )
{
fst_t fst = GetFST( entry );
if( !fst.filename[ 0 ] )//something is amiss, better quit now
return QByteArray();
return GetFile( fst );
}
QByteArray NandBin::GetFile( fst_t fst_ )
{
qDebug() << "NandBin::GetFile" << (const char*)fst_.filename;
if( !fst_.size )
return QByteArray();
quint16 fat = fst_.sub;
//int cluster_span = (int)( fst.size / 0x4000) + 1;
QByteArray data;
//int idx = 0;
for (int i = 0; fat < 0xFFF0; i++)
{
QByteArray cluster = GetCluster( fat );
if( cluster.size() != 0x4000 )
return QByteArray();
//debug shit... am i creating correct hmac data?
//WriteDecryptedCluster( 0, cluster, fst_, idx++ );
data += cluster;
fat = GetFAT( fat );
}
//this check doesnt really seem to matter, it always appears to be 1 extra cluster added to the end
//of the file and that extra bit is dropped in this function before the data is returned.
/*if( data.size() != cluster_span * 0x4000 )
{
qDebug() << "data.size() != cluster_span * 0x4000 :: "
<< hex << data.size()
<< cluster_span
<< ( cluster_span * 0x4000 )
<< "expected size:" << hex << fst.size;
emit SendError( tr( "Error reading file [ block size is not a as expected ] %1" ).arg( FstName( fst ) ) );
}*/
if( (quint32)data.size() < fst_.size )
{
qDebug() << "(quint32)data.size() < fst.size :: "
<< hex << data.size()
<< "expected size:" << hex << fst_.size;
emit SendError( tr( "Error reading file [ returned data size is less that the size in the fst ]" ) );
return QByteArray();
}
if( (quint32)data.size() > fst_.size )
data.resize( fst_.size );//dont need to give back all the data, only up to the expected size
return data;
}
const QList<quint16> NandBin::GetFatsForFile( quint16 i )
{
//qDebug() << "NandBin::GetFatsForFile" << i;
QList<quint16> ret;
fst_t fst = GetFST( i );
if( fst.filename[ 0 ] == '\0' )
return ret;
quint16 fat = fst.sub;
quint16 j = 0;//just to make sure a broken nand doesnt lead to an endless loop
while ( fat < 0x8000 && fat > 0 && ++j )
{
ret << fat;
fat = GetFAT( fat );
}
return ret;
}
void NandBin::SetFixNamesForFAT( bool fix )
{
fatNames = fix;
}
const QByteArray NandBin::GetData( const QString &path )
{
QTreeWidgetItem *item = ItemFromPath( path );
if( !item )
return QByteArray();
if( !item->text( 6 ).contains( "1" ) )
{
qDebug() << "NandBin::GetData -> can't get data for a folder" << item->text( 0 );
return QByteArray();
}
bool ok = false;
quint16 entry = item->text( 1 ).toInt( &ok, 10 );
if( !ok )
return QByteArray();
return GetFile( entry );
}
QTreeWidgetItem *NandBin::ItemFromPath( const QString &path )
{
if( !root || !root->childCount() )
return NULL;
QTreeWidgetItem *item = root->child( 0 );
if( item->text( 0 ) != "/" )
{
qWarning() << "NandBin::ItemFromPath -> root is not \"/\"" << item->text( 0 );
return NULL;
}
if( !path.startsWith( "/" ) || path.contains( "//" ))
{
qWarning() << "NandBin::ItemFromPath -> invalid path";
return NULL;
}
int slash = 1;
while( slash )
{
int nextSlash = path.indexOf( "/", slash + 1 );
QString lookingFor = path.mid( slash, nextSlash - slash );
item = FindItem( lookingFor, item );
if( !item )
{
qWarning() << "NandBin::ItemFromPath ->item not found" << path;
return NULL;
}
slash = nextSlash + 1;
}
return item;
}
QTreeWidgetItem *NandBin::FindItem( const QString &s, QTreeWidgetItem *parent )
{
int cnt = parent->childCount();
for( int i = 0; i <cnt; i++ )
{
QTreeWidgetItem *r = parent->child( i );
if( r->text( 0 ) == s )
{
return r;
}
}
return NULL;
}
void NandBin::ShowInfo()
{
quint16 badBlocks = 0;
quint16 reserved = 0;
quint16 freeBlocks = 0;
QList<quint16>badOnes;
for( quint16 i = 0; i < 0x8000; i++ )
{
quint16 fat = GetFAT( i );
if( 0xfffc == fat )
reserved++;
else if( 0xfffd == fat )
{
badBlocks++;
if( i % 8 == 0 )
{
badOnes << ( i / 8 );
}
}
else if( 0xfffe == fat )
freeBlocks++;
}
if( badBlocks )
badBlocks /= 8;
if( reserved )
reserved /= 8;
if( freeBlocks )
freeBlocks /= 8;
qDebug() << "free blocks:" << hex << freeBlocks
<< "\nbadBlocks:" << hex << badBlocks << badOnes
<< "\nreserved:" << hex << reserved;
}
bool NandBin::WriteCluster( quint32 pageNo, const QByteArray data, const QByteArray hmac )
{
if( data.size() != 0x4000 )
{
qWarning() << "NandBin::WriteCluster -> size:" << hex << data.size();
return false;
}
for( int i = 0; i < 8; i++ )
{
QByteArray spareData( 0x40, '\0' );
quint8* sp = (quint8*)spareData.data();
QByteArray ecc = spare.CalcEcc( data.mid( i * 0x800, 0x800 ) );
memcpy( sp + 0x30, ecc.data(), 0x14 );
sp[ 0 ] = 0xff; // good block
if( !hmac.isEmpty() )
{
if( i == 6 )
{
memcpy( (char*)sp + 1, hmac.data(), 20 );
memcpy( (char*)sp + 21, hmac.data(), 12 );
}
else if( i == 7 )
{
memcpy( (char*)sp + 1, hmac.data() + 12, 8 );
}
}
if( !WritePage( pageNo + i, data.mid( i * 0x800, 0x800 ) + spareData ) )
return false;
}
return true;
}
bool NandBin::WriteDecryptedCluster( quint32 pageNo, const QByteArray data, fst_t fst, quint16 idx )
{
qDebug() << "NandBin::WriteDecryptedCluster";
QByteArray hmac = spare.Get_hmac_data( data, fst.uid, (const unsigned char *)&fst.filename, fst.fst_pos, fst.x3, idx );
hexdump( hmac );
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 );
QByteArray encData = AesEncrypt( 0, data );
return WriteCluster( pageNo, encData, hmac );
}
bool NandBin::WritePage( quint32 pageNo, const QByteArray data )
{
//qDebug() << "NandBin::WritePage(" << hex << pageNo << ")";
return true;
quint32 n_pagelen[] = { 0x800, 0x840, 0x840 };
if( (quint32)data.size() != n_pagelen[ type ] )
{
qWarning() << "data is wrong size" << hex << data.size();
return false;
}
if( f.size() < ( pageNo + 1 ) * n_pagelen[ type ] )
{
emit SendError( tr( "Tried to write page past size of nand.bin" ) );
return false;
}
f.seek( pageNo * n_pagelen[ type ] ); //seek to the beginning of the page to write
return f.write( data );
}
/*
structure of blocks 0 - 7
block 0 ( boot 1 )
0 - 0x4320
a bunch of encrypted gibberish.
the rest of the block as all 0s
sha1 of the whole block is
2cdd5affd2e78c537616a119a7a2e1c568e91f22 with boot1b
f01e8aca029ee0cb5287f5055da1a0bed2a533fa with boot1c
<sven> {1, {0x4a, 0x7c, 0x6f, 0x30, 0x38, 0xde, 0xea, 0x7a, 0x07, 0xd3, 0x32, 0x32, 0x02, 0x4b, 0xe9, 0x5a, 0xfb, 0x56, 0xbf, 0x65}},
<sven> {1, {0x2c, 0xdd, 0x5a, 0xff, 0xd2, 0xe7, 0x8c, 0x53, 0x76, 0x16, 0xa1, 0x19, 0xa7, 0xa2, 0xe1, 0xc5, 0x68, 0xe9, 0x1f, 0x22}},
<sven> {0, {0xf0, 0x1e, 0x8a, 0xca, 0x02, 0x9e, 0xe0, 0xcb, 0x52, 0x87, 0xf5, 0x05, 0x5d, 0xa1, 0xa0, 0xbe, 0xd2, 0xa5, 0x33, 0xfa}},
<sven> {0, {0x8d, 0x9e, 0xcf, 0x2f, 0x8f, 0x98, 0xa3, 0xc1, 0x07, 0xf1, 0xe5, 0xe3, 0x6f, 0xf2, 0x4d, 0x57, 0x7e, 0xac, 0x36, 0x08}},
block 1 ( boot2 ) //just guessing at these for now
u32 0x20 //header size
u32 0xf00 //data offset
u32 0xa00 //cert size
u32 0x2a4 //ticket size
u32 0x208 //tmd size
u32[ 3 ] 0s //padding till 0x20
0x20 - 0x9f0
cert
root ca00000001
Root-CA00000001 CP00000004
Root-CA00000001 XS00000003
round up to 0xa20
ticket for boot2
0xcc4
tmd?
0xecc - 0xf00
gibberish padding
0xf00
start of boot2 contents? ( mine is 0x00023E91 bytes in the TMD for v2 )
there is room for 0x1F100 bytes of it left in this block with 0x4D91 leftover
block 2
bunch of gibberish till 0x4da0
probably the rest of the contents of boot2, padded with gibberish
0x4da0
0s till 0x5000
0x5000 - 0x1f800
bunch of 0xffff
maybe this is untouched nand. never been written to
0x1f800 - 0x1f8e4 blockmap?
26F29A401EE684CF0000000201000000
00000000010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
010101010101010101010101
26F29A401EE684CF0000000201000000
00000000010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
010101010101010101010101
26F29A401EE684CF0000000201000000
00000000010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
010101010101010101010101
0x1f8e4 - 0x20000 ( end of block )
more 0s
block 2 ( looks a lot like block 1 )
wad type header again with the same values
same cert looking doodad again
same ticket again
tmd is different ( zero'd RSA )
1 content, 0x00027B5D bytes
block 4 ( the rest of bootmii )
0 - 0x8a60
gibberish - rest of bootmii content still encrypted
0x8a60 - 0x1f800
0s
0x1f800
26F29A401EE684CF0000000501010100
00000000010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
010101010101010101010101
26F29A401EE684CF0000000501010100
00000000010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
010101010101010101010101
26F29A401EE684CF0000000501010100
00000000010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
010101010101010101010101
then 0s till 0x20000 ( end of block )
block 5
all 0xff
block 6
identical to block 2 except this only difference is the 0x02 is 0x03
26F29A401EE684CF0000000301000000
00000000010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
010101010101010101010101
26F29A401EE684CF0000000301000000
00000000010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
010101010101010101010101
26F29A401EE684CF0000000301000000
00000000010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
010101010101010101010101
block 7
identical to block 1
///////////
nand #2 ( no bootmii )
///////////
block 0 ( boot 1c )
gibberish till 0x43f0. then 0s for the rest of the block
block 1
same wad header as before, with the same values
cert doodad and ticket are the same
tmd is v4 and the content is 0x00027BE8 bytes
block 2
0 - 0x8af0
the rest of the content from boot2 ( padded to 0x40 )
0s till 0x9000
0xff till 0x18f00
26F29A401EE684CF0000000201000000
00000000010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
010101010101010101010101
26F29A401EE684CF0000000201000000
00000000010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
010101010101010101010101
26F29A401EE684CF0000000201000000
00000000010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
010101010101010101010101
block 3 - all 0xff
block 4 - all 0xff
block 5 - all 0xff
block 6 - identical to block 2 except the blockmap ( generation 3 ? )
26F29A401EE684CF0000000301000000
00000000010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
010101010101010101010101
26F29A401EE684CF0000000301000000
00000000010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
010101010101010101010101
26F29A401EE684CF0000000301000000
00000000010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
010101010101010101010101
block 7 matches block 1
/////////////
nand #3
/////////////
block 2
26F29A401EE684CF
00000004
01000000000000000101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
block 4
26F29A401EE684CF
0000000F
01010100000000000101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
block 6
26F29A401EE684CF
00000005
01000000000000000101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
26F29A401EE684CF
00000002
01000000000000000101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
26F29A401EE684CF
00000005
01010100000000000101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
01010101010101010101010101010101
*/