mirror of
https://github.com/martravi/wiiqt.git
synced 2024-11-22 00:59:18 +01:00
* add wad packing to NUS tool
* add more "known updates" to the NUS class. now your can specify "3.4u" or similar instead of a TID and it will try to download all the titles that would have been in that update. the lists of titles were taken from wiimpersonator logs and when those were missing, from disc update partitions. thanks to markhemus and rduke for helping make the lists * NUS tool will now make sure there is a valid setting.txt in the nand after it is done installing titles to it
This commit is contained in:
parent
fe18155a2a
commit
b0db7bf382
@ -6,10 +6,12 @@
|
||||
#include "tiktmd.h"
|
||||
#include "tools.h"
|
||||
#include "aes.h"
|
||||
#include "wad.h"
|
||||
|
||||
MainWindow::MainWindow( QWidget *parent ) : QMainWindow( parent ), ui( new Ui::MainWindow ), nus ( this )
|
||||
{
|
||||
ui->setupUi(this);
|
||||
Wad::SetGlobalCert( QByteArray( (const char*)&certs_dat, CERTS_DAT_SIZE ) );
|
||||
|
||||
//connect to the nus object so we can respond to what it is saying with pretty stuff in the gui
|
||||
connect( &nus, SIGNAL( SendDownloadProgress( int ) ), ui->progressBar_dl, SLOT( setValue( int ) ) );
|
||||
@ -23,8 +25,10 @@ MainWindow::MainWindow( QWidget *parent ) : QMainWindow( parent ), ui( new Ui::M
|
||||
//TODO, really get these paths from settings
|
||||
ui->lineEdit_cachePath->setText( cachePath );
|
||||
ui->lineEdit_nandPath->setText( nandPath );
|
||||
nand.SetPath( nandPath );
|
||||
//nand.SetPath( nandPath );
|
||||
nus.SetCachePath( cachePath );
|
||||
|
||||
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
@ -70,6 +74,18 @@ void MainWindow::NusIsDone()
|
||||
{
|
||||
ui->lineEdit_nandPath->setEnabled( true );
|
||||
ui->pushButton_nandPath->setEnabled( true );
|
||||
|
||||
//write the uid.sys and content.map to disc
|
||||
nand.Flush();
|
||||
|
||||
//make sure there is a setting.txt
|
||||
QByteArray set = nand.GetSettingTxt();
|
||||
if( set.isEmpty() )
|
||||
{
|
||||
set = SettingTxtDialog::Edit( this );
|
||||
if( !set.isEmpty() )
|
||||
nand.SetSettingTxt( set );
|
||||
}
|
||||
}
|
||||
else if( ui->radioButton_wad->isChecked() )
|
||||
{
|
||||
@ -84,20 +100,11 @@ void MainWindow::NusIsDone()
|
||||
|
||||
void MainWindow::ReceiveTitleFromNus( NusJob job )
|
||||
{
|
||||
//QString dataStuff = QString( "%1 items:" ).arg( job.data.size() );
|
||||
//for( int i = 0; i < job.data.size(); i++ )
|
||||
//dataStuff += QString( " %1" ).arg( job.data.at( i ).size(), 0, 16, QChar( ' ' ) );
|
||||
|
||||
QString str = tr( "Received a completed download from NUS" );
|
||||
QString title = QString( "%1v%2" ).arg( job.tid, 16, 16, QChar( '0' ) ).arg( job.version );
|
||||
/*QString j = QString( "NusJob( %1, %2, %3, %4 )<br>" )
|
||||
.arg( job.tid, 16, 16, QChar( '0' ) )
|
||||
.arg( job.version ).arg( job.decrypt ? "decrypted" : "encrypted" )
|
||||
.arg( dataStuff );
|
||||
|
||||
ui->plainTextEdit_log->appendHtml( j );*/
|
||||
ui->plainTextEdit_log->appendHtml( str );
|
||||
return;
|
||||
|
||||
//do something with the data we got
|
||||
if( ui->radioButton_folder->isChecked() )
|
||||
{
|
||||
@ -113,6 +120,53 @@ return;
|
||||
}
|
||||
else if( ui->radioButton_wad->isChecked() )
|
||||
{
|
||||
Wad wad( job.data );
|
||||
if( !wad.IsOk() )
|
||||
{
|
||||
ShowMessage( "<b>Error making a wad from " + title + "<\b>" );
|
||||
return;
|
||||
}
|
||||
QFileInfo fi( ui->lineEdit_wad->text() );
|
||||
if( fi.isFile() )
|
||||
{
|
||||
ShowMessage( "<b>" + ui->lineEdit_wad->text() + " is a file. I need a folder<\b>" );
|
||||
return;
|
||||
}
|
||||
if( !fi.exists() )
|
||||
{
|
||||
ShowMessage( "<b>" + fi.absoluteFilePath() + " is not a folder!\nTrying to create it...<\b>" );
|
||||
if( !QDir().mkpath( ui->lineEdit_wad->text() ) )
|
||||
{
|
||||
ShowMessage( "<b>Failed to make the directory!<\b>" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
QByteArray w = wad.Data();
|
||||
if( w.isEmpty() )
|
||||
{
|
||||
ShowMessage( "<b>Error creating wad<br>" + wad.LastError() + "<\b>" );
|
||||
return;
|
||||
}
|
||||
|
||||
QString name = wad.WadName( fi.absoluteFilePath() );
|
||||
if( name.isEmpty() )
|
||||
{
|
||||
name = QFileDialog::getSaveFileName( this, tr( "Filename for %1" ).arg( title ), fi.absoluteFilePath() );
|
||||
if( name.isEmpty() )
|
||||
{
|
||||
ShowMessage( "<b>No save name given, aborting<\b>" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
QFile file( fi.absoluteFilePath() + "/" + name );
|
||||
if( !file.open( QIODevice::WriteOnly ) )
|
||||
{
|
||||
ShowMessage( "<b>Cant open " + fi.absoluteFilePath() + "/" + name + " for writing<\b>" );
|
||||
return;
|
||||
}
|
||||
file.write( w );
|
||||
file.close();
|
||||
ShowMessage( "Saved " + title + " to " + fi.absoluteFilePath() + "/" + name );
|
||||
}
|
||||
|
||||
//bool r = nand.InstallNusItem( job );
|
||||
@ -123,25 +177,35 @@ return;
|
||||
void MainWindow::on_pushButton_GetTitle_clicked()
|
||||
{
|
||||
bool ok = false;
|
||||
quint64 tid = ui->lineEdit_tid->text().toLongLong( &ok, 16 );
|
||||
if( !ok )
|
||||
bool wholeUpdate = false;
|
||||
quint64 tid = 0;
|
||||
quint32 ver = 0;
|
||||
if( ui->lineEdit_tid->text().size() == 4 )
|
||||
{
|
||||
ShowMessage( "<b>Error converting \"" + ui->lineEdit_tid->text() + "\" to a hex number.</b>" );
|
||||
return;
|
||||
wholeUpdate = true;
|
||||
}
|
||||
quint32 ver = TITLE_LATEST_VERSION;
|
||||
if( !ui->lineEdit_version->text().isEmpty() )
|
||||
else
|
||||
{
|
||||
ver = ui->lineEdit_version->text().toInt( &ok, 10 );
|
||||
tid = ui->lineEdit_tid->text().toLongLong( &ok, 16 );
|
||||
if( !ok )
|
||||
{
|
||||
ShowMessage( "<b>Error converting \"" + ui->lineEdit_version->text() + "\" to a decimal number.</b>" );
|
||||
ShowMessage( "<b>Error converting \"" + ui->lineEdit_tid->text() + "\" to a hex number.</b>" );
|
||||
return;
|
||||
}
|
||||
if( ver > 0xffff )
|
||||
ver = TITLE_LATEST_VERSION;
|
||||
if( !ui->lineEdit_version->text().isEmpty() )
|
||||
{
|
||||
ShowMessage( tr( "<b>Version %1 is too high. Max is 65535</b>" ).arg( ver ) );
|
||||
return;
|
||||
ver = ui->lineEdit_version->text().toInt( &ok, 10 );
|
||||
if( !ok )
|
||||
{
|
||||
ShowMessage( "<b>Error converting \"" + ui->lineEdit_version->text() + "\" to a decimal number.</b>" );
|
||||
return;
|
||||
}
|
||||
if( ver > 0xffff )
|
||||
{
|
||||
ShowMessage( tr( "<b>Version %1 is too high. Max is 65535</b>" ).arg( ver ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
//decide how we want nus to give us the title
|
||||
@ -187,7 +251,18 @@ void MainWindow::on_pushButton_GetTitle_clicked()
|
||||
//ui->progressBar_title->setValue( 0 );
|
||||
//ui->progressBar_whole->setValue( 0 );
|
||||
nus.SetCachePath( ui->lineEdit_cachePath->text() );
|
||||
nus.Get( tid, decrypt, ver );
|
||||
if( wholeUpdate )
|
||||
{
|
||||
if( !nus.GetUpdate( ui->lineEdit_tid->text(), decrypt ) )
|
||||
{
|
||||
ShowMessage( tr( "<b>I dont know the titles that were in the %1 update</b>" ).arg( ui->lineEdit_tid->text() ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nus.Get( tid, decrypt, ver );
|
||||
}
|
||||
}
|
||||
|
||||
//ratio buttons toggled
|
||||
@ -233,10 +308,42 @@ void MainWindow::on_pushButton_decFolder_clicked()
|
||||
|
||||
void MainWindow::on_pushButton_wad_clicked()
|
||||
{
|
||||
QString path = ui->lineEdit_wad->text().isEmpty() ? "/media" : ui->lineEdit_wad->text();
|
||||
QString path = ui->lineEdit_wad->text().isEmpty() ? QDir::currentPath() : ui->lineEdit_wad->text();
|
||||
QString f = QFileDialog::getExistingDirectory( this, tr( "Select folder to save wads to" ), path );
|
||||
if( f.isEmpty() )
|
||||
return;
|
||||
|
||||
ui->lineEdit_wad->setText( f );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
3.4j-
|
||||
Error getting title from NUS: Error downloading part of the title.
|
||||
NusJob( 0001000248414b4a, 2, decrypted, 0 items: )
|
||||
|
||||
Error getting title from NUS: Error downloading part of the title.
|
||||
NusJob( 0001000248414c4a, 2, decrypted, 0 items: )
|
||||
|
||||
Received a completed download from NUS
|
||||
Installed 0001000248415941v2 title to nand
|
||||
|
||||
Received a completed download from NUS
|
||||
Error 000100084843434av0 title to nand
|
||||
|
||||
4.0j
|
||||
Error getting title from NUS: Error downloading part of the title.
|
||||
NusJob( 0001000248414b4a, 2, decrypted, 0 items: )
|
||||
|
||||
Error getting title from NUS: Error downloading part of the title.
|
||||
NusJob( 0001000248414c4a, 2, decrypted, 0 items: )
|
||||
|
||||
Received a completed download from NUS
|
||||
Installed 0001000248415941v3 title to nand
|
||||
|
||||
Error getting title from NUS: Error downloading part of the title.
|
||||
NusJob( 000100024843434a, 1, decrypted, 0 items: )
|
||||
|
||||
Received a completed download from NUS
|
||||
Error 000100084843434av0 title to nand
|
||||
*/
|
||||
|
@ -25,6 +25,8 @@ private:
|
||||
|
||||
void ShowMessage( const QString& mes );
|
||||
|
||||
|
||||
|
||||
public slots:
|
||||
//slots for getting info from the NUS downloader
|
||||
void GetError( const QString &message, NusJob job );
|
||||
|
@ -298,7 +298,7 @@ bool NandDump::RecurseDeleteFolder( const QString &path )
|
||||
|
||||
bool NandDump::InstallNusItem( NusJob job )
|
||||
{
|
||||
if( !job.tid || !job.version || job.data.size() < 3 )
|
||||
if( !job.tid || job.data.size() < 3 )
|
||||
{
|
||||
qWarning() << "NandDump::InstallNusItem -> invalid item";
|
||||
return false;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -53,7 +53,7 @@ public:
|
||||
|
||||
//TODO! this function is far from complete
|
||||
//starts getting all the titles involved for a given update
|
||||
//expects strings like "4.2u" and "3.3e"
|
||||
//expects strings like "4.2u" and "3.4e" ( any of the updates listed below should be working )
|
||||
//returns false if it doesnt know the IOS for the given string
|
||||
// The boot2 update is not uncluded in this. ask for it separately if you want it
|
||||
bool GetUpdate( const QString & upd, bool decrypt = true );
|
||||
@ -63,6 +63,7 @@ public:
|
||||
//to keep people from bulk DLing and installing and messing something up, any boot2 upudate will not be included
|
||||
//in the list, ask for it specifically
|
||||
//lists are created from wiimpersonator logs when available. otherwise they come from examining game update partitions
|
||||
static QMap< quint64, quint16 > List20u();
|
||||
static QMap< quint64, quint16 > List30u();
|
||||
static QMap< quint64, quint16 > List31u();
|
||||
static QMap< quint64, quint16 > List32u();
|
||||
@ -73,25 +74,32 @@ public:
|
||||
static QMap< quint64, quint16 > List42u();
|
||||
static QMap< quint64, quint16 > List43u();
|
||||
|
||||
/* static QMap< quint64, quint16 > List30e();
|
||||
static QMap< quint64, quint16 > List21e();
|
||||
static QMap< quint64, quint16 > List30e();
|
||||
static QMap< quint64, quint16 > List31e();
|
||||
static QMap< quint64, quint16 > List32e();
|
||||
static QMap< quint64, quint16 > List33e();
|
||||
//static QMap< quint64, quint16 > List32e();
|
||||
//static QMap< quint64, quint16 > List33e();
|
||||
static QMap< quint64, quint16 > List34e();
|
||||
static QMap< quint64, quint16 > List40e();
|
||||
static QMap< quint64, quint16 > List41e();
|
||||
static QMap< quint64, quint16 > List42e();
|
||||
static QMap< quint64, quint16 > List43e();
|
||||
|
||||
static QMap< quint64, quint16 > List30j();
|
||||
static QMap< quint64, quint16 > List35k();
|
||||
static QMap< quint64, quint16 > List41k();
|
||||
static QMap< quint64, quint16 > List42k();
|
||||
static QMap< quint64, quint16 > List43k();
|
||||
|
||||
static QMap< quint64, quint16 > List20j();
|
||||
//static QMap< quint64, quint16 > List30j();
|
||||
static QMap< quint64, quint16 > List31j();
|
||||
static QMap< quint64, quint16 > List32j();
|
||||
static QMap< quint64, quint16 > List33j();
|
||||
//static QMap< quint64, quint16 > List32j();
|
||||
//static QMap< quint64, quint16 > List33j();
|
||||
static QMap< quint64, quint16 > List34j();
|
||||
static QMap< quint64, quint16 > List40j();
|
||||
static QMap< quint64, quint16 > List41j();
|
||||
static QMap< quint64, quint16 > List42j();
|
||||
static QMap< quint64, quint16 > List43j();*/
|
||||
static QMap< quint64, quint16 > List43j();
|
||||
|
||||
private:
|
||||
|
||||
|
@ -24,35 +24,53 @@ quint64 Tmd::Tid()
|
||||
|
||||
QString Tmd::Cid( quint16 i )
|
||||
{
|
||||
if( p_tmd && i > qFromBigEndian( p_tmd->num_contents ) )
|
||||
if( !p_tmd || i > qFromBigEndian( p_tmd->num_contents ) )
|
||||
return QString();
|
||||
return QString( "%1" ).arg( qFromBigEndian( p_tmd->contents[ i ].cid ), 8, 16, QChar( '0' ) );
|
||||
}
|
||||
|
||||
QByteArray Tmd::Hash( quint16 i )
|
||||
{
|
||||
if( p_tmd && i > qFromBigEndian( p_tmd->num_contents ) )
|
||||
if( !p_tmd || i > qFromBigEndian( p_tmd->num_contents ) )
|
||||
return QByteArray();
|
||||
|
||||
return QByteArray( (const char*)&p_tmd->contents[ i ].hash, 20 );
|
||||
}
|
||||
|
||||
quint16 Tmd::Count()
|
||||
{
|
||||
if( !p_tmd )
|
||||
return 0;
|
||||
|
||||
return qFromBigEndian( p_tmd->num_contents );
|
||||
}
|
||||
|
||||
quint16 Tmd::Version()
|
||||
{
|
||||
if( !p_tmd )
|
||||
return 0;
|
||||
|
||||
return qFromBigEndian( p_tmd->title_version );
|
||||
}
|
||||
|
||||
quint64 Tmd::Size( quint16 i )
|
||||
{
|
||||
if( p_tmd && i > qFromBigEndian( p_tmd->num_contents ) )
|
||||
if( !p_tmd || i > qFromBigEndian( p_tmd->num_contents ) )
|
||||
return 0;
|
||||
return qFromBigEndian( p_tmd->contents[ i ].size );
|
||||
}
|
||||
|
||||
quint16 Tmd::Type( quint16 i )
|
||||
{
|
||||
if( p_tmd && i > qFromBigEndian( p_tmd->num_contents ) )
|
||||
if( !p_tmd || i > qFromBigEndian( p_tmd->num_contents ) )
|
||||
return 0;
|
||||
return qFromBigEndian( p_tmd->contents[ i ].type );
|
||||
}
|
||||
|
||||
quint32 Tmd::SignedSize()
|
||||
{
|
||||
if( !p_tmd )
|
||||
return 0;
|
||||
return payLoadOffset + sizeof( tmd ) + ( sizeof( tmd_content ) * qFromBigEndian( p_tmd->num_contents ) );
|
||||
}
|
||||
|
||||
|
@ -38,19 +38,7 @@ typedef struct _tiklimit {
|
||||
quint32 tag;
|
||||
quint32 value;
|
||||
} __attribute__((packed)) tiklimit;
|
||||
/*
|
||||
typedef struct _tikview {
|
||||
quint32 view;
|
||||
quint64 ticketid;
|
||||
quint32 devicetype;
|
||||
quint64 titleid;
|
||||
quint16 access_mask;
|
||||
quint8 reserved[0x3c];
|
||||
quint8 cidx_mask[0x40];
|
||||
quint16 padding;
|
||||
tiklimit limits[8];
|
||||
} __attribute__((packed)) tikview;
|
||||
*/
|
||||
|
||||
typedef struct _tik {
|
||||
sig_issuer issuer;
|
||||
quint8 fill[63]; //TODO: not really fill
|
||||
@ -108,20 +96,6 @@ typedef struct _tmd_view_content
|
||||
quint64 size;
|
||||
} __attribute__((packed)) tmd_view_content;
|
||||
|
||||
typedef struct _tmdview
|
||||
{
|
||||
quint8 version; // 0x0000;
|
||||
quint8 filler[3];
|
||||
quint64 sys_version; //0x0004
|
||||
quint64 title_id; // 0x00c
|
||||
quint32 title_type; //0x0014
|
||||
quint16 group_id; //0x0018
|
||||
quint8 reserved[0x3e]; //0x001a this is the same reserved 0x3e bytes from the tmd
|
||||
quint16 title_version; //0x0058
|
||||
quint16 num_contents; //0x005a
|
||||
tmd_view_content contents[]; //0x005c
|
||||
}__attribute__((packed)) tmd_view;
|
||||
|
||||
typedef struct _cert_header {
|
||||
sig_issuer issuer;
|
||||
quint32 cert_type;
|
||||
@ -213,6 +187,12 @@ public:
|
||||
quint16 Type( quint16 i );
|
||||
quint64 Tid();
|
||||
|
||||
//gets the number of contents
|
||||
quint16 Count();
|
||||
|
||||
//title version
|
||||
quint16 Version();
|
||||
|
||||
quint32 SignedSize();
|
||||
private:
|
||||
quint32 payLoadOffset;
|
||||
|
@ -104,6 +104,18 @@ QByteArray AesDecrypt( quint16 index, const QByteArray source )
|
||||
return ret;
|
||||
}
|
||||
|
||||
QByteArray AesEncrypt( quint16 index, const QByteArray source )
|
||||
{
|
||||
static quint8 iv[ 16 ];
|
||||
|
||||
quint16 beidx = qFromBigEndian( index );
|
||||
memset( iv, 0, 16 );
|
||||
memcpy( iv, &beidx, 2 );
|
||||
QByteArray ret( source.size(), '\0' );
|
||||
aes_encrypt( iv, (const quint8*)source.data(), (quint8*)ret.data(), source.size() );
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AesSetKey( const QByteArray key )
|
||||
{
|
||||
aes_set_key( (const quint8*)key.data() );
|
||||
@ -129,3 +141,217 @@ QByteArray GetSha1( QByteArray stuff )
|
||||
//hexdump( ret );
|
||||
return ret;
|
||||
}
|
||||
|
||||
QByteArray ReadFile( const QString &path )
|
||||
{
|
||||
QFile file( path );
|
||||
if( !file.exists() || !file.open( QIODevice::ReadOnly ) )
|
||||
{
|
||||
qWarning() << "ReadFile -> can't open" << path;
|
||||
return QByteArray();
|
||||
}
|
||||
QByteArray ret = file.readAll();
|
||||
file.close();
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define CERTS_DAT_SIZE 2560
|
||||
const quint8 certs_dat[ CERTS_DAT_SIZE ] = {
|
||||
0x00, 0x01, 0x00, 0x01, 0x7D, 0x9D, 0x5E, 0xBA, 0x52, 0x81, 0xDC, 0xA7, 0x06, 0x5D, 0x2F, 0x08,
|
||||
0x68, 0xDB, 0x8A, 0xC7, 0x3A, 0xCE, 0x7E, 0xA9, 0x91, 0xF1, 0x96, 0x9F, 0xE1, 0xD0, 0xF2, 0xC1,
|
||||
0x1F, 0xAE, 0xC0, 0xC3, 0xF0, 0x1A, 0xDC, 0xB4, 0x46, 0xAD, 0xE5, 0xCA, 0x03, 0xB6, 0x25, 0x21,
|
||||
0x94, 0x62, 0xC6, 0xE1, 0x41, 0x0D, 0xB9, 0xE6, 0x3F, 0xDE, 0x98, 0xD1, 0xAF, 0x26, 0x3B, 0x4C,
|
||||
0xB2, 0x87, 0x84, 0x27, 0x82, 0x72, 0xEF, 0x27, 0x13, 0x4B, 0x87, 0xC2, 0x58, 0xD6, 0x7B, 0x62,
|
||||
0xF2, 0xB5, 0xBF, 0x9C, 0xB6, 0xBA, 0x8C, 0x89, 0x19, 0x2E, 0xC5, 0x06, 0x89, 0xAC, 0x74, 0x24,
|
||||
0xA0, 0x22, 0x09, 0x40, 0x03, 0xEE, 0x98, 0xA4, 0xBD, 0x2F, 0x01, 0x3B, 0x59, 0x3F, 0xE5, 0x66,
|
||||
0x6C, 0xD5, 0xEB, 0x5A, 0xD7, 0xA4, 0x93, 0x10, 0xF3, 0x4E, 0xFB, 0xB4, 0x3D, 0x46, 0xCB, 0xF1,
|
||||
0xB5, 0x23, 0xCF, 0x82, 0xF6, 0x8E, 0xB5, 0x6D, 0xB9, 0x04, 0xA7, 0xC2, 0xA8, 0x2B, 0xE1, 0x1D,
|
||||
0x78, 0xD3, 0x9B, 0xA2, 0x0D, 0x90, 0xD3, 0x07, 0x42, 0xDB, 0x5E, 0x7A, 0xC1, 0xEF, 0xF2, 0x21,
|
||||
0x51, 0x09, 0x62, 0xCF, 0xA9, 0x14, 0xA8, 0x80, 0xDC, 0xF4, 0x17, 0xBA, 0x99, 0x93, 0x0A, 0xEE,
|
||||
0x08, 0xB0, 0xB0, 0xE5, 0x1A, 0x3E, 0x9F, 0xAF, 0xCD, 0xC2, 0xD7, 0xE3, 0xCB, 0xA1, 0x2F, 0x3A,
|
||||
0xC0, 0x07, 0x90, 0xDE, 0x44, 0x7A, 0xC3, 0xC5, 0x38, 0xA8, 0x67, 0x92, 0x38, 0x07, 0x8B, 0xD4,
|
||||
0xC4, 0xB2, 0x45, 0xAC, 0x29, 0x16, 0x88, 0x6D, 0x2A, 0x0E, 0x59, 0x4E, 0xED, 0x5C, 0xC8, 0x35,
|
||||
0x69, 0x8B, 0x4D, 0x62, 0x38, 0xDF, 0x05, 0x72, 0x4D, 0xCC, 0xF6, 0x81, 0x80, 0x8A, 0x70, 0x74,
|
||||
0x06, 0x59, 0x30, 0xBF, 0xF8, 0x51, 0x41, 0x37, 0xE8, 0x15, 0xFA, 0xBA, 0xA1, 0x72, 0xB8, 0xE0,
|
||||
0x69, 0x6C, 0x61, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x52, 0x6F, 0x6F, 0x74, 0x2D, 0x43, 0x41, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x58, 0x53, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xF1, 0xB8, 0x9F, 0xD1, 0xAD, 0x07, 0xA9, 0x37, 0x8A, 0x7B, 0x10, 0x0C,
|
||||
0x7D, 0xC7, 0x39, 0xBE, 0x9E, 0xDD, 0xB7, 0x32, 0x00, 0x89, 0xAB, 0x25, 0xB1, 0xF8, 0x71, 0xAF,
|
||||
0x5A, 0xA9, 0xF4, 0x58, 0x9E, 0xD1, 0x83, 0x02, 0x32, 0x8E, 0x81, 0x1A, 0x1F, 0xEF, 0xD0, 0x09,
|
||||
0xC8, 0x06, 0x36, 0x43, 0xF8, 0x54, 0xB9, 0xE1, 0x3B, 0xBB, 0x61, 0x3A, 0x7A, 0xCF, 0x87, 0x14,
|
||||
0x85, 0x6B, 0xA4, 0x5B, 0xAA, 0xE7, 0xBB, 0xC6, 0x4E, 0xB2, 0xF7, 0x5D, 0x87, 0xEB, 0xF2, 0x67,
|
||||
0xED, 0x0F, 0xA4, 0x41, 0xA9, 0x33, 0x66, 0x5E, 0x57, 0x7D, 0x5A, 0xDE, 0xAB, 0xFB, 0x46, 0x2E,
|
||||
0x76, 0x00, 0xCA, 0x9C, 0xE9, 0x4D, 0xC4, 0xCB, 0x98, 0x39, 0x92, 0xAB, 0x7A, 0x2F, 0xB3, 0xA3,
|
||||
0x9E, 0xA2, 0xBF, 0x9C, 0x53, 0xEC, 0xD0, 0xDC, 0xFA, 0x6B, 0x8B, 0x5E, 0xB2, 0xCB, 0xA4, 0x0F,
|
||||
0xFA, 0x40, 0x75, 0xF8, 0xF2, 0xB2, 0xDE, 0x97, 0x38, 0x11, 0x87, 0x2D, 0xF5, 0xE2, 0xA6, 0xC3,
|
||||
0x8B, 0x2F, 0xDC, 0x8E, 0x57, 0xDD, 0xBD, 0x5F, 0x46, 0xEB, 0x27, 0xD6, 0x19, 0x52, 0xF6, 0xAE,
|
||||
0xF8, 0x62, 0xB7, 0xEE, 0x9A, 0xC6, 0x82, 0xA2, 0xB1, 0x9A, 0xA9, 0xB5, 0x58, 0xFB, 0xEB, 0xB3,
|
||||
0x89, 0x2F, 0xBD, 0x50, 0xC9, 0xF5, 0xDC, 0x4A, 0x6E, 0x9C, 0x9B, 0xFE, 0x45, 0x80, 0x34, 0xA9,
|
||||
0x42, 0x18, 0x2D, 0xDE, 0xB7, 0x5F, 0xE0, 0xD1, 0xB3, 0xDF, 0x0E, 0x97, 0xE3, 0x99, 0x80, 0x87,
|
||||
0x70, 0x18, 0xC2, 0xB2, 0x83, 0xF1, 0x35, 0x75, 0x7C, 0x5A, 0x30, 0xFC, 0x3F, 0x30, 0x84, 0xA4,
|
||||
0x9A, 0xAA, 0xC0, 0x1E, 0xE7, 0x06, 0x69, 0x4F, 0x8E, 0x14, 0x48, 0xDA, 0x12, 0x3A, 0xCC, 0x4F,
|
||||
0xFA, 0x26, 0xAA, 0x38, 0xF7, 0xEF, 0xBF, 0x27, 0x8F, 0x36, 0x97, 0x79, 0x77, 0x5D, 0xB7, 0xC5,
|
||||
0xAD, 0xC7, 0x89, 0x91, 0xDC, 0xF8, 0x43, 0x8D, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01, 0x00, 0x00, 0xB3, 0xAD, 0xB3, 0x22, 0x6B, 0x3C, 0x3D, 0xFF, 0x1B, 0x4B, 0x40, 0x77,
|
||||
0x16, 0xFF, 0x4F, 0x7A, 0xD7, 0x64, 0x86, 0xC8, 0x95, 0xAC, 0x56, 0x2D, 0x21, 0xF1, 0x06, 0x01,
|
||||
0xD4, 0xF6, 0x64, 0x28, 0x19, 0x1C, 0x07, 0x76, 0x8F, 0xDF, 0x1A, 0xE2, 0xCE, 0x7B, 0x27, 0xC9,
|
||||
0x0F, 0xBC, 0x0A, 0xD0, 0x31, 0x25, 0x78, 0xEC, 0x07, 0x79, 0xB6, 0x57, 0xD4, 0x37, 0x24, 0x13,
|
||||
0xA7, 0xF8, 0x6F, 0x0C, 0x14, 0xC0, 0xEF, 0x6E, 0x09, 0x41, 0xED, 0x2B, 0x05, 0xEC, 0x39, 0x57,
|
||||
0x36, 0x07, 0x89, 0x00, 0x4A, 0x87, 0x8D, 0x2E, 0x9D, 0xF8, 0xC7, 0xA5, 0xA9, 0xF8, 0xCA, 0xB3,
|
||||
0x11, 0xB1, 0x18, 0x79, 0x57, 0xBB, 0xF8, 0x98, 0xE2, 0xA2, 0x54, 0x02, 0xCF, 0x54, 0x39, 0xCF,
|
||||
0x2B, 0xBF, 0xA0, 0xE1, 0xF8, 0x5C, 0x06, 0x6E, 0x83, 0x9A, 0xE0, 0x94, 0xCA, 0x47, 0xE0, 0x15,
|
||||
0x58, 0xF5, 0x6E, 0x6F, 0x34, 0xE9, 0x2A, 0xA2, 0xDC, 0x38, 0x93, 0x7E, 0x37, 0xCD, 0x8C, 0x5C,
|
||||
0x4D, 0xFD, 0x2F, 0x11, 0x4F, 0xE8, 0x68, 0xC9, 0xA8, 0xD9, 0xFE, 0xD8, 0x6E, 0x0C, 0x21, 0x75,
|
||||
0xA2, 0xBD, 0x7E, 0x89, 0xB9, 0xC7, 0xB5, 0x13, 0xF4, 0x1A, 0x79, 0x61, 0x44, 0x39, 0x10, 0xEF,
|
||||
0xF9, 0xD7, 0xFE, 0x57, 0x22, 0x18, 0xD5, 0x6D, 0xFB, 0x7F, 0x49, 0x7A, 0xA4, 0xCB, 0x90, 0xD4,
|
||||
0xF1, 0xAE, 0xB1, 0x76, 0xE4, 0x68, 0x5D, 0xA7, 0x94, 0x40, 0x60, 0x98, 0x2F, 0x04, 0x48, 0x40,
|
||||
0x1F, 0xCF, 0xC6, 0xBA, 0xEB, 0xDA, 0x16, 0x30, 0xB4, 0x73, 0xB4, 0x15, 0x23, 0x35, 0x08, 0x07,
|
||||
0x0A, 0x9F, 0x4F, 0x89, 0x78, 0xE6, 0x2C, 0xEC, 0x5E, 0x92, 0x46, 0xA5, 0xA8, 0xBD, 0xA0, 0x85,
|
||||
0x78, 0x68, 0x75, 0x0C, 0x3A, 0x11, 0x2F, 0xAF, 0x95, 0xE8, 0x38, 0xC8, 0x99, 0x0E, 0x87, 0xB1,
|
||||
0x62, 0xCD, 0x10, 0xDA, 0xB3, 0x31, 0x96, 0x65, 0xEF, 0x88, 0x9B, 0x54, 0x1B, 0xB3, 0x36, 0xBB,
|
||||
0x67, 0x53, 0x9F, 0xAF, 0xC2, 0xAE, 0x2D, 0x0A, 0x2E, 0x75, 0xC0, 0x23, 0x74, 0xEA, 0x4E, 0xAC,
|
||||
0x8D, 0x99, 0x50, 0x7F, 0x59, 0xB9, 0x53, 0x77, 0x30, 0x5F, 0x26, 0x35, 0xC6, 0x08, 0xA9, 0x90,
|
||||
0x93, 0xAC, 0x8F, 0xC6, 0xDE, 0x23, 0xB9, 0x7A, 0xEA, 0x70, 0xB4, 0xC4, 0xCF, 0x66, 0xB3, 0x0E,
|
||||
0x58, 0x32, 0x0E, 0xC5, 0xB6, 0x72, 0x04, 0x48, 0xCE, 0x3B, 0xB1, 0x1C, 0x53, 0x1F, 0xCB, 0x70,
|
||||
0x28, 0x7C, 0xB5, 0xC2, 0x7C, 0x67, 0x4F, 0xBB, 0xFD, 0x8C, 0x7F, 0xC9, 0x42, 0x20, 0xA4, 0x73,
|
||||
0x23, 0x1D, 0x58, 0x7E, 0x5A, 0x1A, 0x1A, 0x82, 0xE3, 0x75, 0x79, 0xA1, 0xBB, 0x82, 0x6E, 0xCE,
|
||||
0x01, 0x71, 0xC9, 0x75, 0x63, 0x47, 0x4B, 0x1D, 0x46, 0xE6, 0x79, 0xB2, 0x82, 0x37, 0x62, 0x11,
|
||||
0xCD, 0xC7, 0x00, 0x2F, 0x46, 0x87, 0xC2, 0x3C, 0x6D, 0xC0, 0xD5, 0xB5, 0x78, 0x6E, 0xE1, 0xF2,
|
||||
0x73, 0xFF, 0x01, 0x92, 0x50, 0x0F, 0xF4, 0xC7, 0x50, 0x6A, 0xEE, 0x72, 0xB6, 0xF4, 0x3D, 0xF6,
|
||||
0x08, 0xFE, 0xA5, 0x83, 0xA1, 0xF9, 0x86, 0x0F, 0x87, 0xAF, 0x52, 0x44, 0x54, 0xBB, 0x47, 0xC3,
|
||||
0x06, 0x0C, 0x94, 0xE9, 0x9B, 0xF7, 0xD6, 0x32, 0xA7, 0xC8, 0xAB, 0x4B, 0x4F, 0xF5, 0x35, 0x21,
|
||||
0x1F, 0xC1, 0x80, 0x47, 0xBB, 0x7A, 0xFA, 0x5A, 0x2B, 0xD7, 0xB8, 0x84, 0xAD, 0x8E, 0x56, 0x4F,
|
||||
0x5B, 0x89, 0xFF, 0x37, 0x97, 0x37, 0xF1, 0xF5, 0x01, 0x3B, 0x1F, 0x9E, 0xC4, 0x18, 0x6F, 0x92,
|
||||
0x2A, 0xD5, 0xC4, 0xB3, 0xC0, 0xD5, 0x87, 0x0B, 0x9C, 0x04, 0xAF, 0x1A, 0xB5, 0xF3, 0xBC, 0x6D,
|
||||
0x0A, 0xF1, 0x7D, 0x47, 0x08, 0xE4, 0x43, 0xE9, 0x73, 0xF7, 0xB7, 0x70, 0x77, 0x54, 0xBA, 0xF3,
|
||||
0xEC, 0xD2, 0xAC, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x52, 0x6F, 0x6F, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x43, 0x41, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x5B, 0xFA, 0x7D, 0x5C, 0xB2, 0x79, 0xC9, 0xE2, 0xEE, 0xE1, 0x21, 0xC6,
|
||||
0xEA, 0xF4, 0x4F, 0xF6, 0x39, 0xF8, 0x8F, 0x07, 0x8B, 0x4B, 0x77, 0xED, 0x9F, 0x95, 0x60, 0xB0,
|
||||
0x35, 0x82, 0x81, 0xB5, 0x0E, 0x55, 0xAB, 0x72, 0x11, 0x15, 0xA1, 0x77, 0x70, 0x3C, 0x7A, 0x30,
|
||||
0xFE, 0x3A, 0xE9, 0xEF, 0x1C, 0x60, 0xBC, 0x1D, 0x97, 0x46, 0x76, 0xB2, 0x3A, 0x68, 0xCC, 0x04,
|
||||
0xB1, 0x98, 0x52, 0x5B, 0xC9, 0x68, 0xF1, 0x1D, 0xE2, 0xDB, 0x50, 0xE4, 0xD9, 0xE7, 0xF0, 0x71,
|
||||
0xE5, 0x62, 0xDA, 0xE2, 0x09, 0x22, 0x33, 0xE9, 0xD3, 0x63, 0xF6, 0x1D, 0xD7, 0xC1, 0x9F, 0xF3,
|
||||
0xA4, 0xA9, 0x1E, 0x8F, 0x65, 0x53, 0xD4, 0x71, 0xDD, 0x7B, 0x84, 0xB9, 0xF1, 0xB8, 0xCE, 0x73,
|
||||
0x35, 0xF0, 0xF5, 0x54, 0x05, 0x63, 0xA1, 0xEA, 0xB8, 0x39, 0x63, 0xE0, 0x9B, 0xE9, 0x01, 0x01,
|
||||
0x1F, 0x99, 0x54, 0x63, 0x61, 0x28, 0x70, 0x20, 0xE9, 0xCC, 0x0D, 0xAB, 0x48, 0x7F, 0x14, 0x0D,
|
||||
0x66, 0x26, 0xA1, 0x83, 0x6D, 0x27, 0x11, 0x1F, 0x20, 0x68, 0xDE, 0x47, 0x72, 0x14, 0x91, 0x51,
|
||||
0xCF, 0x69, 0xC6, 0x1B, 0xA6, 0x0E, 0xF9, 0xD9, 0x49, 0xA0, 0xF7, 0x1F, 0x54, 0x99, 0xF2, 0xD3,
|
||||
0x9A, 0xD2, 0x8C, 0x70, 0x05, 0x34, 0x82, 0x93, 0xC4, 0x31, 0xFF, 0xBD, 0x33, 0xF6, 0xBC, 0xA6,
|
||||
0x0D, 0xC7, 0x19, 0x5E, 0xA2, 0xBC, 0xC5, 0x6D, 0x20, 0x0B, 0xAF, 0x6D, 0x06, 0xD0, 0x9C, 0x41,
|
||||
0xDB, 0x8D, 0xE9, 0xC7, 0x20, 0x15, 0x4C, 0xA4, 0x83, 0x2B, 0x69, 0xC0, 0x8C, 0x69, 0xCD, 0x3B,
|
||||
0x07, 0x3A, 0x00, 0x63, 0x60, 0x2F, 0x46, 0x2D, 0x33, 0x80, 0x61, 0xA5, 0xEA, 0x6C, 0x91, 0x5C,
|
||||
0xD5, 0x62, 0x35, 0x79, 0xC3, 0xEB, 0x64, 0xCE, 0x44, 0xEF, 0x58, 0x6D, 0x14, 0xBA, 0xAA, 0x88,
|
||||
0x34, 0x01, 0x9B, 0x3E, 0xEB, 0xEE, 0xD3, 0x79, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01, 0x00, 0x01, 0x4E, 0x00, 0x5F, 0xF1, 0x3F, 0x86, 0x75, 0x8D, 0xB6, 0x9C, 0x45, 0x63,
|
||||
0x0F, 0xD4, 0x9B, 0xF4, 0xCC, 0x5D, 0x54, 0xCF, 0xCC, 0x22, 0x34, 0x72, 0x57, 0xAB, 0xA4, 0xBA,
|
||||
0x53, 0xD2, 0xB3, 0x3D, 0xE6, 0xEC, 0x9E, 0xA1, 0x57, 0x54, 0x53, 0xAE, 0x5F, 0x93, 0x3D, 0x96,
|
||||
0xBF, 0xF7, 0xCC, 0x7A, 0x79, 0x56, 0x6E, 0x84, 0x7B, 0x1B, 0x60, 0x77, 0xC2, 0xA9, 0x38, 0x71,
|
||||
0x30, 0x1A, 0x8C, 0xD3, 0xC9, 0x3D, 0x4D, 0xB3, 0x26, 0xE9, 0x87, 0x92, 0x66, 0xE9, 0xD3, 0xBA,
|
||||
0x9F, 0x79, 0xBC, 0x46, 0x38, 0xFA, 0x2D, 0x20, 0xA0, 0x3A, 0x70, 0x67, 0xA4, 0x11, 0xA7, 0xA0,
|
||||
0xB7, 0xD9, 0x12, 0xAD, 0x11, 0x6A, 0x3A, 0xC4, 0x6E, 0x32, 0x42, 0x47, 0xC2, 0x08, 0xBA, 0xB4,
|
||||
0x94, 0x9C, 0xC5, 0x2E, 0xD0, 0x2F, 0x19, 0xF6, 0x51, 0xE0, 0xDF, 0x2E, 0x36, 0x53, 0xAA, 0xAF,
|
||||
0x97, 0xA6, 0x92, 0xBB, 0xA9, 0x1D, 0xD8, 0x6E, 0x24, 0x2E, 0xB3, 0x08, 0x77, 0x55, 0x11, 0xCE,
|
||||
0x98, 0xF6, 0xA2, 0xF4, 0x26, 0xC9, 0x27, 0x04, 0xD0, 0xFC, 0x8D, 0xD4, 0x80, 0x9E, 0xD7, 0x61,
|
||||
0xBD, 0x11, 0xB7, 0x85, 0x94, 0x8C, 0xD6, 0xD0, 0x7A, 0xDB, 0xA4, 0x08, 0xD0, 0xF0, 0x86, 0xF6,
|
||||
0x5A, 0xAE, 0x19, 0x14, 0xB2, 0x88, 0x9A, 0xA8, 0xAE, 0x4A, 0xA2, 0xAA, 0xC7, 0x61, 0xA9, 0x0D,
|
||||
0x41, 0x2C, 0xB1, 0x50, 0x09, 0xAB, 0x3E, 0x93, 0xFC, 0xA9, 0x24, 0xDE, 0xCE, 0x4F, 0x7C, 0x06,
|
||||
0xAB, 0xDC, 0x2E, 0x60, 0x9D, 0x68, 0xBE, 0x00, 0x73, 0xFA, 0x80, 0x57, 0x6A, 0x14, 0x5E, 0xED,
|
||||
0xC4, 0x8B, 0x74, 0x32, 0x87, 0x07, 0x93, 0xC8, 0xFC, 0xA6, 0xD8, 0x3E, 0x09, 0x6E, 0xC5, 0xF2,
|
||||
0xA9, 0xC4, 0x21, 0xE7, 0x48, 0xB3, 0x73, 0x40, 0x5B, 0xE2, 0xFA, 0x8A, 0xE1, 0x58, 0x78, 0xE9,
|
||||
0xD5, 0x23, 0x88, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x52, 0x6F, 0x6F, 0x74, 0x2D, 0x43, 0x41, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x43, 0x50, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x34, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xF1, 0xB8, 0xA0, 0x64, 0xC1, 0x6D, 0xF3, 0x83, 0x29, 0x55, 0xC3, 0x29,
|
||||
0x5B, 0x72, 0xF0, 0x33, 0x2E, 0x97, 0xEF, 0x14, 0x84, 0x8A, 0x68, 0x04, 0x9C, 0xA6, 0x8E, 0xAC,
|
||||
0xDE, 0x14, 0x50, 0x33, 0xB8, 0x6C, 0x10, 0x8D, 0x48, 0x33, 0x5C, 0x5D, 0x0C, 0xAB, 0x77, 0x04,
|
||||
0x62, 0x54, 0x47, 0x55, 0x45, 0x2A, 0x90, 0x00, 0x70, 0xB1, 0x56, 0x92, 0x5C, 0x17, 0x86, 0xE2,
|
||||
0xCD, 0x20, 0x6D, 0xCC, 0xDC, 0x2C, 0x2E, 0x37, 0x6E, 0x27, 0xFC, 0xB4, 0x20, 0x66, 0xCC, 0x0A,
|
||||
0x8C, 0xE9, 0xFE, 0xE8, 0x57, 0x04, 0xE6, 0xCA, 0x63, 0x1A, 0x2E, 0x7E, 0x91, 0x7E, 0x94, 0x7C,
|
||||
0x39, 0x91, 0x77, 0x36, 0x29, 0xD1, 0x55, 0x61, 0x85, 0xBB, 0xD7, 0xB7, 0x73, 0xCA, 0x37, 0x47,
|
||||
0x9E, 0x5F, 0xAA, 0xA3, 0xB6, 0x05, 0xE0, 0x01, 0xE1, 0xAC, 0xE5, 0x8D, 0xD8, 0xF8, 0x47, 0x82,
|
||||
0xD6, 0x45, 0xFC, 0xE3, 0xA1, 0xCD, 0x03, 0xAB, 0x36, 0xF0, 0xF3, 0x86, 0xB1, 0xA2, 0xD1, 0x37,
|
||||
0x40, 0xA1, 0x94, 0x8A, 0x53, 0xBA, 0x1B, 0x0D, 0x8C, 0x48, 0x63, 0xCD, 0x6B, 0x2C, 0x2E, 0x20,
|
||||
0x64, 0x94, 0x80, 0x4C, 0x62, 0xFA, 0xA9, 0x3A, 0x7E, 0x33, 0xA9, 0xEA, 0x78, 0x6B, 0x59, 0xCA,
|
||||
0xE3, 0xAB, 0x36, 0x45, 0xF4, 0xCB, 0x8F, 0xD7, 0x90, 0x6B, 0x82, 0x68, 0xCD, 0xAC, 0xF1, 0x7B,
|
||||
0x3A, 0xEC, 0x46, 0x83, 0x1B, 0x91, 0xF6, 0xDE, 0x18, 0x61, 0x83, 0xBC, 0x4B, 0x32, 0x67, 0x93,
|
||||
0xC7, 0x2E, 0x50, 0xD9, 0x1E, 0x36, 0xA0, 0xDC, 0xE2, 0xB9, 0x7D, 0xA0, 0x21, 0x3E, 0x46, 0x96,
|
||||
0x02, 0x1F, 0x33, 0x1C, 0xBE, 0xAE, 0x8D, 0xFC, 0x92, 0x87, 0x32, 0xAA, 0x44, 0xDC, 0x78, 0xE7,
|
||||
0x19, 0x9A, 0x3D, 0xDD, 0x57, 0x22, 0x7E, 0x9E, 0x77, 0xDE, 0x32, 0x63, 0x86, 0x93, 0x6C, 0x11,
|
||||
0xAC, 0xA7, 0x0F, 0x81, 0x19, 0xD3, 0x3A, 0x99, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
// public root cert
|
||||
const quint8 root_dat[] = {
|
||||
0xF8, 0x24, 0x6C, 0x58, 0xBA, 0xE7, 0x50, 0x03, 0x01, 0xFB, 0xB7, 0xC2, 0xEB, 0xE0, 0x01, 0x05,
|
||||
0x71, 0xDA, 0x92, 0x23, 0x78, 0xF0, 0x51, 0x4E, 0xC0, 0x03, 0x1D, 0xD0, 0xD2, 0x1E, 0xD3, 0xD0,
|
||||
0x7E, 0xFC, 0x85, 0x20, 0x69, 0xB5, 0xDE, 0x9B, 0xB9, 0x51, 0xA8, 0xBC, 0x90, 0xA2, 0x44, 0x92,
|
||||
0x6D, 0x37, 0x92, 0x95, 0xAE, 0x94, 0x36, 0xAA, 0xA6, 0xA3, 0x02, 0x51, 0x0C, 0x7B, 0x1D, 0xED,
|
||||
0xD5, 0xFB, 0x20, 0x86, 0x9D, 0x7F, 0x30, 0x16, 0xF6, 0xBE, 0x65, 0xD3, 0x83, 0xA1, 0x6D, 0xB3,
|
||||
0x32, 0x1B, 0x95, 0x35, 0x18, 0x90, 0xB1, 0x70, 0x02, 0x93, 0x7E, 0xE1, 0x93, 0xF5, 0x7E, 0x99,
|
||||
0xA2, 0x47, 0x4E, 0x9D, 0x38, 0x24, 0xC7, 0xAE, 0xE3, 0x85, 0x41, 0xF5, 0x67, 0xE7, 0x51, 0x8C,
|
||||
0x7A, 0x0E, 0x38, 0xE7, 0xEB, 0xAF, 0x41, 0x19, 0x1B, 0xCF, 0xF1, 0x7B, 0x42, 0xA6, 0xB4, 0xED,
|
||||
0xE6, 0xCE, 0x8D, 0xE7, 0x31, 0x8F, 0x7F, 0x52, 0x04, 0xB3, 0x99, 0x0E, 0x22, 0x67, 0x45, 0xAF,
|
||||
0xD4, 0x85, 0xB2, 0x44, 0x93, 0x00, 0x8B, 0x08, 0xC7, 0xF6, 0xB7, 0xE5, 0x6B, 0x02, 0xB3, 0xE8,
|
||||
0xFE, 0x0C, 0x9D, 0x85, 0x9C, 0xB8, 0xB6, 0x82, 0x23, 0xB8, 0xAB, 0x27, 0xEE, 0x5F, 0x65, 0x38,
|
||||
0x07, 0x8B, 0x2D, 0xB9, 0x1E, 0x2A, 0x15, 0x3E, 0x85, 0x81, 0x80, 0x72, 0xA2, 0x3B, 0x6D, 0xD9,
|
||||
0x32, 0x81, 0x05, 0x4F, 0x6F, 0xB0, 0xF6, 0xF5, 0xAD, 0x28, 0x3E, 0xCA, 0x0B, 0x7A, 0xF3, 0x54,
|
||||
0x55, 0xE0, 0x3D, 0xA7, 0xB6, 0x83, 0x26, 0xF3, 0xEC, 0x83, 0x4A, 0xF3, 0x14, 0x04, 0x8A, 0xC6,
|
||||
0xDF, 0x20, 0xD2, 0x85, 0x08, 0x67, 0x3C, 0xAB, 0x62, 0xA2, 0xC7, 0xBC, 0x13, 0x1A, 0x53, 0x3E,
|
||||
0x0B, 0x66, 0x80, 0x6B, 0x1C, 0x30, 0x66, 0x4B, 0x37, 0x23, 0x31, 0xBD, 0xC4, 0xB0, 0xCA, 0xD8,
|
||||
0xD1, 0x1E, 0xE7, 0xBB, 0xD9, 0x28, 0x55, 0x48, 0xAA, 0xEC, 0x1F, 0x66, 0xE8, 0x21, 0xB3, 0xC8,
|
||||
0xA0, 0x47, 0x69, 0x00, 0xC5, 0xE6, 0x88, 0xE8, 0x0C, 0xCE, 0x3C, 0x61, 0xD6, 0x9C, 0xBB, 0xA1,
|
||||
0x37, 0xC6, 0x60, 0x4F, 0x7A, 0x72, 0xDD, 0x8C, 0x7B, 0x3E, 0x3D, 0x51, 0x29, 0x0D, 0xAA, 0x6A,
|
||||
0x59, 0x7B, 0x08, 0x1F, 0x9D, 0x36, 0x33, 0xA3, 0x46, 0x7A, 0x35, 0x61, 0x09, 0xAC, 0xA7, 0xDD,
|
||||
0x7D, 0x2E, 0x2F, 0xB2, 0xC1, 0xAE, 0xB8, 0xE2, 0x0F, 0x48, 0x92, 0xD8, 0xB9, 0xF8, 0xB4, 0x6F,
|
||||
0x4E, 0x3C, 0x11, 0xF4, 0xF4, 0x7D, 0x8B, 0x75, 0x7D, 0xFE, 0xFE, 0xA3, 0x89, 0x9C, 0x33, 0x59,
|
||||
0x5C, 0x5E, 0xFD, 0xEB, 0xCB, 0xAB, 0xE8, 0x41, 0x3E, 0x3A, 0x9A, 0x80, 0x3C, 0x69, 0x35, 0x6E,
|
||||
0xB2, 0xB2, 0xAD, 0x5C, 0xC4, 0xC8, 0x58, 0x45, 0x5E, 0xF5, 0xF7, 0xB3, 0x06, 0x44, 0xB4, 0x7C,
|
||||
0x64, 0x06, 0x8C, 0xDF, 0x80, 0x9F, 0x76, 0x02, 0x5A, 0x2D, 0xB4, 0x46, 0xE0, 0x3D, 0x7C, 0xF6,
|
||||
0x2F, 0x34, 0xE7, 0x02, 0x45, 0x7B, 0x02, 0xA4, 0xCF, 0x5D, 0x9D, 0xD5, 0x3C, 0xA5, 0x3A, 0x7C,
|
||||
0xA6, 0x29, 0x78, 0x8C, 0x67, 0xCA, 0x08, 0xBF, 0xEC, 0xCA, 0x43, 0xA9, 0x57, 0xAD, 0x16, 0xC9,
|
||||
0x4E, 0x1C, 0xD8, 0x75, 0xCA, 0x10, 0x7D, 0xCE, 0x7E, 0x01, 0x18, 0xF0, 0xDF, 0x6B, 0xFE, 0xE5,
|
||||
0x1D, 0xDB, 0xD9, 0x91, 0xC2, 0x6E, 0x60, 0xCD, 0x48, 0x58, 0xAA, 0x59, 0x2C, 0x82, 0x00, 0x75,
|
||||
0xF2, 0x9F, 0x52, 0x6C, 0x91, 0x7C, 0x6F, 0xE5, 0x40, 0x3E, 0xA7, 0xD4, 0xA5, 0x0C, 0xEC, 0x3B,
|
||||
0x73, 0x84, 0xDE, 0x88, 0x6E, 0x82, 0xD2, 0xEB, 0x4D, 0x4E, 0x42, 0xB5, 0xF2, 0xB1, 0x49, 0xA8,
|
||||
0x1E, 0xA7, 0xCE, 0x71, 0x44, 0xDC, 0x29, 0x94, 0xCF, 0xC4, 0x4E, 0x1F, 0x91, 0xCB, 0xD4, 0x95,
|
||||
0x00, 0x01, 0x00, 0x01
|
||||
};
|
||||
|
@ -16,12 +16,16 @@ void hexdump12( const QByteArray &d, int from = 0, int len = -1 );
|
||||
//simplified functions for crypto shit
|
||||
void AesSetKey( const QByteArray key );
|
||||
QByteArray AesDecrypt( quint16 index, const QByteArray source );
|
||||
QByteArray AesEncrypt( quint16 index, const QByteArray source );
|
||||
|
||||
QByteArray GetSha1( QByteArray stuff );
|
||||
|
||||
//get a padded version of the given buffer
|
||||
QByteArray PaddedByteArray( const QByteArray &orig, quint32 padTo );
|
||||
|
||||
//read a file into a bytearray
|
||||
QByteArray ReadFile( const QString &path );
|
||||
|
||||
//keep track of the last folder browsed to when looking for files
|
||||
extern QString currentDir;
|
||||
|
||||
@ -31,4 +35,8 @@ extern QString cachePath;
|
||||
//folder to use as the base path for the nand
|
||||
extern QString nandPath;
|
||||
|
||||
#define CERTS_DAT_SIZE 2560
|
||||
extern const quint8 certs_dat[ CERTS_DAT_SIZE ];
|
||||
extern const quint8 root_dat[];
|
||||
|
||||
#endif // TOOLS_H
|
||||
|
@ -8,7 +8,10 @@ Wad::Wad( const QByteArray stuff )
|
||||
{
|
||||
ok = false;
|
||||
if( stuff.size() < 0x80 )//less than this and there is definitely nothing there
|
||||
{
|
||||
Err( "Size is < 0x80" );
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray copy = stuff;
|
||||
QBuffer b( © );
|
||||
@ -27,6 +30,7 @@ Wad::Wad( const QByteArray stuff )
|
||||
if( tmp != 0x49730000 && tmp != 0x69620000 && tmp != 0x426b0000 )
|
||||
{
|
||||
b.close();
|
||||
hexdump( stuff, 0, 0x40 );
|
||||
Err( "Bad file magic word" );
|
||||
return;
|
||||
}
|
||||
@ -71,11 +75,14 @@ Wad::Wad( const QByteArray stuff )
|
||||
Ticket ticket( tikData );
|
||||
Tmd t( tmdData );
|
||||
|
||||
//hexdump( tikData );
|
||||
//hexdump( tmdData );
|
||||
|
||||
if( ticket.Tid() != t.Tid() )
|
||||
qWarning() << "wad contains 2 different TIDs";
|
||||
|
||||
quint32 cnt = qFromBigEndian( t.payload()->num_contents );
|
||||
qDebug() << "Wad contains" << hex << cnt << "contents";
|
||||
quint32 cnt = t.Count();
|
||||
//qDebug() << "Wad contains" << hex << cnt << "contents";
|
||||
|
||||
//another quick sanity check
|
||||
quint32 totalSize = 0;
|
||||
@ -87,42 +94,395 @@ Wad::Wad( const QByteArray stuff )
|
||||
Err( "Size of all the apps in the tmd is greater than the size in the wad header" );
|
||||
return;
|
||||
}
|
||||
/*
|
||||
void AesSetKey( const QByteArray key );
|
||||
QByteArray AesDecrypt( quint16 index, const QByteArray source );
|
||||
*/
|
||||
//read all the contents, check the hash, and remember the data ( still encrypted )
|
||||
for( quint32 i = 0; i < cnt; i++ )
|
||||
{
|
||||
|
||||
quint32 s = RU( 0x40, t.Size( i ) );
|
||||
//qDebug() << "content" << i << "is at" << hex << pos << "with size" << s;
|
||||
QByteArray encData = stuff.mid( pos, s );
|
||||
pos += s;
|
||||
|
||||
//doing this here in case there is some other object that is using the AES that would change the key on us
|
||||
AesSetKey( ticket.DecryptedKey() );
|
||||
|
||||
|
||||
QByteArray decData = AesDecrypt( i, encData );
|
||||
decData.resize( t.Size( i ) );
|
||||
QByteArray realHash = GetSha1( decData );
|
||||
if( realHash != t.Hash( i ) )
|
||||
{
|
||||
Err( QString( "hash doesnt match for content %1" ).arg( i ) );
|
||||
}
|
||||
partsEnc << encData;
|
||||
}
|
||||
|
||||
//Data = stuff.mid( pos, Size );
|
||||
//pos += RU( 0x40, Size );
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//wtf? some VC titles have a full banner as the footer? maybe somebody's wad packer is busted
|
||||
//QByteArray footer = stuff.mid( pos, stuff.size() - pos );
|
||||
//qDebug() << "footer";
|
||||
//hexdump( footer );
|
||||
ok = true;
|
||||
|
||||
}
|
||||
|
||||
Wad::Wad( QList< QByteArray > stuff, bool encrypted )
|
||||
{
|
||||
if( stuff.size() < 3 )
|
||||
{
|
||||
Err( "Cant treate a wad with < 3 items" );
|
||||
return;
|
||||
}
|
||||
|
||||
tmdData = stuff.at( 0 );
|
||||
tikData = stuff.at( 1 );
|
||||
|
||||
Ticket ticket( tikData );
|
||||
Tmd t( tmdData );
|
||||
|
||||
quint16 cnt = stuff.size() - 2;
|
||||
if( cnt != t.Count() )
|
||||
{
|
||||
Err( "The number of items given doesnt match the number in the tmd" );
|
||||
return;
|
||||
}
|
||||
for( quint16 i = 0; i < cnt; i++ )
|
||||
{
|
||||
QByteArray encData;
|
||||
|
||||
if( encrypted )
|
||||
{
|
||||
encData = stuff.at( i + 2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
QByteArray decDataPadded = PaddedByteArray( stuff.at( i + 2 ), 0x40 );
|
||||
//doing this here in case there is some other object that is using the AES that would change the key on us
|
||||
AesSetKey( ticket.DecryptedKey() );
|
||||
encData = AesEncrypt( i, decDataPadded );
|
||||
}
|
||||
partsEnc << encData;
|
||||
}
|
||||
ok = true;
|
||||
|
||||
}
|
||||
|
||||
void Wad::SetCert( const QByteArray stuff )
|
||||
{
|
||||
certData = stuff;
|
||||
}
|
||||
|
||||
quint64 Wad::Tid()
|
||||
{
|
||||
if( !tmdData.size() )
|
||||
{
|
||||
Err( "There is no data in the TMD" );
|
||||
return 0;
|
||||
}
|
||||
Tmd t( tmdData );
|
||||
return t.Tid();
|
||||
}
|
||||
|
||||
void Wad::SetGlobalCert( const QByteArray &stuff )
|
||||
{
|
||||
globalCert = stuff;
|
||||
}
|
||||
|
||||
const QByteArray Wad::Content( quint16 i )
|
||||
{
|
||||
if( tmdData.isEmpty() || tikData.isEmpty() )
|
||||
{
|
||||
Err( "Can't decryte data without a TMD and ticket" );
|
||||
return QByteArray();
|
||||
}
|
||||
Ticket ticket( tikData );
|
||||
Tmd t( tmdData );
|
||||
if( partsEnc.size() != t.Count() || i >= partsEnc.size() )
|
||||
{
|
||||
Err( "I dont know whats going on some number is out of range and i dont like it" );
|
||||
return QByteArray();
|
||||
}
|
||||
QByteArray encData = partsEnc.at( i );
|
||||
|
||||
AesSetKey( ticket.DecryptedKey() );
|
||||
|
||||
QByteArray decData = AesDecrypt( i, encData );
|
||||
decData.resize( t.Size( i ) );
|
||||
QByteArray realHash = GetSha1( decData );
|
||||
if( realHash != t.Hash( i ) )
|
||||
{
|
||||
Err( QString( "hash doesnt match for content %1" ).arg( i ) );
|
||||
return QByteArray();
|
||||
}
|
||||
return decData;
|
||||
}
|
||||
|
||||
void Wad::Err( QString str )
|
||||
{
|
||||
ok = false;
|
||||
errStr = str;
|
||||
qWarning() << "Wad::Error" << str;
|
||||
}
|
||||
|
||||
const QByteArray Wad::Data( quint32 magicWord, const QByteArray footer )
|
||||
{
|
||||
//qDebug() << "Wad::Data" << hex << magicWord << footer.size();
|
||||
if( !partsEnc.size() || tmdData.isEmpty() || tikData.isEmpty() || ( certData.isEmpty() && globalCert.isEmpty() ) )
|
||||
{
|
||||
Err( "Dont have all the parts to make a wad" );
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
//do some brief checks to make sure that wad is good
|
||||
Ticket ticket( tikData );
|
||||
Tmd t( tmdData );
|
||||
if( t.Tid() != ticket.Tid() )
|
||||
{
|
||||
Err( "Ticket and TMD have different TID" );
|
||||
return QByteArray();
|
||||
}
|
||||
if( partsEnc.size() != t.Count() )
|
||||
{
|
||||
Err( "Dont have enough contents according to the TMD" );
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
//everything seems in order, try to make the thing
|
||||
QByteArray cert = certData.isEmpty() ? globalCert : certData;
|
||||
quint32 certSize = cert.size();
|
||||
quint32 tikSize = ticket.SignedSize();
|
||||
quint32 tmdSize = t.SignedSize();
|
||||
quint32 appSize = 0;
|
||||
quint32 footerSize = footer.size();
|
||||
|
||||
//add all the app sizes together and check that they match the TMD
|
||||
quint16 cnt = t.Count();
|
||||
for( quint16 i = 0; i < cnt; i++ )
|
||||
{
|
||||
quint32 s = RU( 0x40, partsEnc.at( i ).size() );
|
||||
if( RU( 0x40, t.Size( i ) ) != s )
|
||||
{
|
||||
Err( QString( "Size of content %1 is bad ( %2, %3, %4 )" ).arg( i )
|
||||
.arg( t.Size( i ), 0, 16 )
|
||||
.arg( RU( 0x40, t.Size( i ) ), 0, 16 )
|
||||
.arg( s, 0, 16 ) );
|
||||
return QByteArray();
|
||||
}
|
||||
appSize += s;
|
||||
}
|
||||
|
||||
QByteArray header( 0x20, '\0' );
|
||||
QBuffer buf( &header );
|
||||
buf.open( QIODevice::WriteOnly );
|
||||
|
||||
quint32 tmp = qFromBigEndian( 0x20 );//header size
|
||||
buf.write( (const char*)&tmp, 4 );
|
||||
tmp = qFromBigEndian( magicWord );//magic word
|
||||
buf.write( (const char*)&tmp, 4 );
|
||||
tmp = qFromBigEndian( certSize );
|
||||
buf.write( (const char*)&tmp, 4 );
|
||||
buf.seek( 0x10 );
|
||||
tmp = qFromBigEndian( tikSize );
|
||||
buf.write( (const char*)&tmp, 4 );
|
||||
tmp = qFromBigEndian( tmdSize );
|
||||
buf.write( (const char*)&tmp, 4 );
|
||||
tmp = qFromBigEndian( appSize );
|
||||
buf.write( (const char*)&tmp, 4 );
|
||||
tmp = qFromBigEndian( footerSize );
|
||||
buf.write( (const char*)&tmp, 4 );
|
||||
buf.close();
|
||||
//hexdump( header, 0, 0x20 );
|
||||
|
||||
QByteArray ret = PaddedByteArray( header, 0x40 ) + PaddedByteArray( cert, 0x40 );
|
||||
//make sure we dont have the huge ticket and TMD that come when downloading from NUS
|
||||
QByteArray tik = tikData;
|
||||
tik.resize( tikSize );
|
||||
tik = PaddedByteArray( tik, 0x40 );
|
||||
|
||||
QByteArray tm = tmdData;
|
||||
tm.resize( tmdSize );
|
||||
tm = PaddedByteArray( tm, 0x40 );
|
||||
|
||||
//hexdump( tik );
|
||||
//hexdump( tm );
|
||||
|
||||
ret += tik + tm;
|
||||
for( quint16 i = 0; i < cnt; i++ )
|
||||
{
|
||||
ret += PaddedByteArray( partsEnc.at( i ), 0x40 );
|
||||
}
|
||||
ret += footer;
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString Wad::WadName( QString path )
|
||||
{
|
||||
if( !tmdData.size() )
|
||||
{
|
||||
Err( "There is no data in the TMD" );
|
||||
return QString();
|
||||
}
|
||||
Tmd t( tmdData );
|
||||
return WadName( t.Tid(), t.Version(), path );
|
||||
}
|
||||
|
||||
QString Wad::WadName( quint64 tid, quint16 version, QString path )
|
||||
{
|
||||
quint32 type = (quint32)( ( tid >> 32 ) & 0xffffffff );
|
||||
quint32 base = (quint32)( tid & 0xffffffff );
|
||||
quint8 reg = (quint8)( tid & 0xff );
|
||||
quint32 regionFree = ( ( base >> 8 ) & 0xffffff ) << 8;
|
||||
QString region;
|
||||
switch( reg )
|
||||
{
|
||||
case 0x45:
|
||||
region = "US";
|
||||
break;
|
||||
case 0x50:
|
||||
region = "EU";
|
||||
break;
|
||||
case 0x4A:
|
||||
region = "JP";
|
||||
break;
|
||||
case 0x4B:
|
||||
region = "KO";//is this correct?? i have no korean games
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
QString name;
|
||||
switch( type )
|
||||
{
|
||||
case 1:
|
||||
switch( base )
|
||||
{
|
||||
case 1:
|
||||
name = QString( "BOOT2-v%1-64" ).arg( version );
|
||||
break;
|
||||
case 2:
|
||||
name = QString( "RVL-WiiSystemmenu-v%1" ).arg( version );
|
||||
break;
|
||||
case 0x100:
|
||||
name = QString( "RVL-bc-v%1" ).arg( version );
|
||||
break;
|
||||
case 0x101:
|
||||
name = QString( "RVL-mios-v%1" ).arg( version );
|
||||
break;
|
||||
default:
|
||||
if( base > 0xff )
|
||||
break;
|
||||
name = QString( "IOS%1-64-v%2" ).arg( base ).arg( version );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x10002:
|
||||
switch( base )
|
||||
{
|
||||
case 0x48414141://HAAA
|
||||
name = QString( "RVL-photo-v%1" ).arg( version );
|
||||
break;
|
||||
case 0x48415941://HAYA
|
||||
name = QString( "RVL-photo2-v%1" ).arg( version );
|
||||
break;
|
||||
case 0x48414241://HABA
|
||||
name = QString( "RVL-Shopping-v%1" ).arg( version );
|
||||
break;
|
||||
case 0x48414341://HACA
|
||||
name = QString( "RVL-NigaoeNR-v%1" ).arg( version );//mii channel
|
||||
break;
|
||||
case 0x48414741://HAGA
|
||||
name = QString( "RVL-News-v%1" ).arg( version );
|
||||
break;
|
||||
case 0x48414641://HAFA
|
||||
name = QString( "RVL-Weather-v%1" ).arg( version );
|
||||
break;
|
||||
default:
|
||||
switch( regionFree )
|
||||
{
|
||||
case 0x48414600://HAF?
|
||||
name = QString( "RVL-Forecast_%1-v%2" ).arg( region ).arg( version );
|
||||
break;
|
||||
case 0x48414700://HAG?
|
||||
name = QString( "RVL-News_%1-v%2" ).arg( region ).arg( version );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x10008:
|
||||
switch( regionFree )
|
||||
{
|
||||
case 0x48414b00://HAK?
|
||||
name = QString( "RVL-Eulav_%1-v%2" ).arg( region ).arg( version );
|
||||
break;
|
||||
case 0x48414c00://HAL?
|
||||
name = QString( "RVL-Rgnsel_%1-v%2" ).arg( region ).arg( version );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if( name.isEmpty() )
|
||||
return QString();
|
||||
|
||||
if( path.isEmpty() )
|
||||
return name + ".wad.out.wad";
|
||||
|
||||
QString ret = name + ".wad.out.wad";
|
||||
int i = 1;
|
||||
while( QFile::exists( path + "/" + ret ) )
|
||||
{
|
||||
ret = name + QString( "(copy %1)" ).arg( i ) + ".wad.out.wad";
|
||||
i++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
QByteArray Wad::FromDirectory( QDir dir )
|
||||
{
|
||||
QFileInfoList tmds = dir.entryInfoList( QStringList() << "*.tmd", QDir::Files );
|
||||
if( tmds.isEmpty() )
|
||||
{
|
||||
qWarning() << "Wad::FromDirectory -> no tmd found in" << dir.absolutePath();
|
||||
return QByteArray();
|
||||
}
|
||||
QByteArray tmdD = ReadFile( tmds.at( 0 ).absoluteFilePath() );
|
||||
if( tmdD.isEmpty() )
|
||||
return QByteArray();
|
||||
QFileInfoList tiks = dir.entryInfoList( QStringList() << "*.tik", QDir::Files );
|
||||
if( tiks.isEmpty() )
|
||||
{
|
||||
qWarning() << "Wad::FromDirectory -> no tik found in" << dir.absolutePath();
|
||||
return QByteArray();
|
||||
}
|
||||
QByteArray tikD = ReadFile( tiks.at( 0 ).absoluteFilePath() );
|
||||
if( tikD.isEmpty() )
|
||||
return QByteArray();
|
||||
Tmd t(tmdD );
|
||||
QList<QByteArray> datas = QList<QByteArray>()<< tmdD << tikD;
|
||||
|
||||
quint16 cnt = t.Count();
|
||||
for( quint16 i = 0; i < cnt; i++ )
|
||||
{
|
||||
QByteArray appD = ReadFile( dir.absoluteFilePath( t.Cid( i ) + ".app" ) );
|
||||
if( appD.isEmpty() )
|
||||
return QByteArray();
|
||||
|
||||
datas << appD;
|
||||
}
|
||||
Wad wad( datas, false );
|
||||
if( !wad.IsOk() )
|
||||
return QByteArray();
|
||||
|
||||
QFileInfoList certs = dir.entryInfoList( QStringList() << "*.cert", QDir::Files );
|
||||
if( !certs.isEmpty() )
|
||||
{
|
||||
QByteArray certD = ReadFile( certs.at( 0 ).absoluteFilePath() );
|
||||
if( !certD.isEmpty() )
|
||||
wad.SetCert( certD );
|
||||
}
|
||||
|
||||
QByteArray ret = wad.Data();
|
||||
return ret;
|
||||
}
|
||||
|
@ -37,8 +37,7 @@ public:
|
||||
static void SetGlobalCert( const QByteArray &stuff );
|
||||
|
||||
//pack a wad from the given directory
|
||||
// use footer to decide if a footer will be added
|
||||
static QByteArray FromDirectory( QDir dir, bool footer = false );
|
||||
static QByteArray FromDirectory( QDir dir );
|
||||
|
||||
//get a assembled wad from the list of parts
|
||||
//the first one should be the tmd, the second one the ticket, and the rest are the contents, in order
|
||||
@ -46,7 +45,7 @@ public:
|
||||
static QByteArray FromPartList( QList< QByteArray > stuff, bool isEncrypted = true );
|
||||
|
||||
//get all the parts of this wad put together in a wad ready for writing to disc or whatever
|
||||
const QByteArray Data();
|
||||
const QByteArray Data( quint32 magicWord = 0x49730000, const QByteArray footer = QByteArray() );
|
||||
|
||||
//get the decrypted data from a content
|
||||
const QByteArray Content( quint16 i );
|
||||
@ -54,6 +53,14 @@ public:
|
||||
//get the last error encountered while trying to do something
|
||||
const QString LastError(){ return errStr; }
|
||||
|
||||
//get a name for a wad as it would be seen in a wii disc update partition
|
||||
//if a path is given, it will check that path for existing wads with the name and append a number to the end "(1)" to avoid duplicate files
|
||||
//returns an empty string if it cant guess the title based on TID
|
||||
static QString WadName( quint64 tid, quint16 version, QString path = QString() );
|
||||
|
||||
QString WadName( QString path = QString() );
|
||||
|
||||
|
||||
private:
|
||||
bool ok;
|
||||
QString errStr;
|
||||
|
Loading…
Reference in New Issue
Block a user