2009-06-11 03:16:49 +02:00
using System ;
using System.Windows.Forms ;
using System.IO ;
using System.Net ;
using System.Security.Cryptography ;
using System.Xml ;
2009-07-09 04:11:22 +02:00
using System.Drawing ;
2009-08-04 20:17:21 +02:00
using System.Text.RegularExpressions ;
using System.ComponentModel ;
using System.Threading ;
2009-08-06 04:37:39 +02:00
using System.Text ;
2009-06-11 03:16:49 +02:00
2009-07-17 18:22:58 +02:00
2009-06-11 03:16:49 +02:00
namespace NUS_Downloader
{
public partial class Form1 : Form
{
const string NUSURL = "http://nus.cdn.shop.wii.com/ccs/download/" ;
const string DSiNUSURL = "http://nus.cdn.t.shop.nintendowifi.net/ccs/download/" ;
2009-07-09 04:11:22 +02:00
// TODO: Always remember to change version!
2009-08-06 04:37:39 +02:00
string version = "v1.3" ;
2009-06-11 03:16:49 +02:00
WebClient generalWC = new WebClient ( ) ;
static RijndaelManaged rijndaelCipher ;
static bool dsidecrypt = false ;
2009-07-10 02:56:17 +02:00
const string certs_MD5 = "7677AD47BAA5D6E3E313E72661FBDC16" ;
2009-07-14 21:38:18 +02:00
2009-07-09 04:11:22 +02:00
// Images do not compare unless globalized...
Image green = Properties . Resources . bullet_green ;
Image orange = Properties . Resources . bullet_orange ;
Image redorb = Properties . Resources . bullet_red ;
Image redgreen = Properties . Resources . bullet_redgreen ;
Image redorange = Properties . Resources . bullet_redorange ;
2009-08-07 22:06:34 +02:00
// Certs storage
byte [ ] cert_CA = new byte [ 0x400 ] ;
byte [ ] cert_CACP = new byte [ 0x300 ] ;
byte [ ] cert_CAXS = new byte [ 0x300 ] ;
byte [ ] cert_CA_sha1 = new byte [ 20 ] { 0x5B , 0x7D , 0x3E , 0xE2 , 0x87 , 0x06 , 0xAD , 0x8D , 0xA2 , 0xCB , 0xD5 , 0xA6 , 0xB7 , 0x5C , 0x15 , 0xD0 , 0xF9 , 0xB6 , 0xF3 , 0x18 } ;
byte [ ] cert_CACP_sha1 = new byte [ 20 ] { 0x68 , 0x24 , 0xD6 , 0xDA , 0x4C , 0x25 , 0x18 , 0x4F , 0x0D , 0x6D , 0xAF , 0x6E , 0xDB , 0x9C , 0x0F , 0xC5 , 0x75 , 0x22 , 0xA4 , 0x1C } ;
byte [ ] cert_CAXS_sha1 = new byte [ 20 ] { 0x09 , 0x78 , 0x70 , 0x45 , 0x03 , 0x71 , 0x21 , 0x47 , 0x78 , 0x24 , 0xBC , 0x6A , 0x3E , 0x5E , 0x07 , 0x61 , 0x56 , 0x57 , 0x3F , 0x8A } ;
byte [ ] cert_total_sha1 = new byte [ 20 ] { 0xAC , 0xE0 , 0xF1 , 0x5D , 0x2A , 0x85 , 0x1C , 0x38 , 0x3F , 0xE4 , 0x65 , 0x7A , 0xFC , 0x38 , 0x40 , 0xD6 , 0xFF , 0xE3 , 0x0A , 0xD0 } ;
2009-06-11 03:16:49 +02:00
public struct WADHeader
{
public int HeaderSize ;
public int WadType ;
public int CertChainSize ;
public int Reserved ;
public int TicketSize ;
public int TMDSize ;
public int DataSize ;
public int FooterSize ;
} ;
2009-07-17 18:22:58 +02:00
public struct TitleContent
{
public byte [ ] ContentID ;
public byte [ ] Index ;
public byte [ ] Type ;
public byte [ ] Size ;
public byte [ ] SHAHash ;
} ;
public enum ContentTypes : int {
Shared = 0x8001 , Normal = 0x0001
}
2009-07-10 02:56:17 +02:00
// This is the standard entry to the GUI
2009-06-11 03:16:49 +02:00
public Form1 ( )
{
InitializeComponent ( ) ;
BootChecks ( ) ;
}
2009-07-10 02:56:17 +02:00
// CLI Mode
2009-06-11 03:16:49 +02:00
public Form1 ( string [ ] args )
{
InitializeComponent ( ) ;
2009-08-07 22:06:34 +02:00
Application . DoEvents ( ) ;
2009-06-11 03:16:49 +02:00
// certs.sys / key.bin
if ( BootChecks ( ) = = false )
return ;
// Vars
bool startnow = false ;
2009-07-04 20:32:52 +02:00
bool endafter = false ;
2009-06-11 03:16:49 +02:00
// Fix'd
localuse . Checked = false ;
// Switch through arguments
for ( int i = 0 ; i < args . Length ; i + + )
{
switch ( args [ i ] )
{
case "-t" :
if ( args [ i + 1 ] . Length = = 16 )
titleidbox . Text = args [ i + 1 ] ;
else
{
WriteStatus ( "Title ID: Your Doing It Wrong (c)" ) ;
WriteStatus ( "ex: -t 0000000100000002" ) ;
}
break ;
case "-v" :
titleversion . Text = args [ i + 1 ] ;
break ;
case "-s" :
startnow = true ;
break ;
2009-07-04 20:32:52 +02:00
case "-close" :
endafter = true ;
break ;
2009-06-11 03:16:49 +02:00
case "-d" :
decryptbox . Checked = true ;
break ;
case "-ticket" :
ignoreticket . Checked = true ;
break ;
case "-local" :
localuse . Checked = true ;
break ;
case "-p" :
packbox . Checked = true ;
wadnamebox . Text = args [ i + 1 ] ;
break ;
case "-dsi" :
radioButton2 . Checked = true ;
break ;
default :
break ;
}
}
// Start doing stuff...
if ( ( startnow ) & & ( titleidbox . Text . Length ! = 0 ) )
{
// Prevent mass deletion
if ( ( titleidbox . Text = = "" ) & & ( titleversion . Text = = "" ) )
{
WriteStatus ( "Please enter SOME info..." ) ;
return ;
}
2009-07-10 02:56:17 +02:00
else
{
if ( ! statusbox . Lines [ 0 ] . StartsWith ( " ---" ) )
statusbox . Text = " --- " + titleidbox . Text + " ---" ;
}
2009-06-11 03:16:49 +02:00
// Running Downloads in background so no form freezing
NUSDownloader . RunWorkerAsync ( ) ;
}
2009-07-04 20:32:52 +02:00
// Close if specified
while ( NUSDownloader . IsBusy )
2009-08-07 22:06:34 +02:00
{
Thread . Sleep ( 1000 ) ;
2009-07-04 20:32:52 +02:00
}
if ( ( NUSDownloader . IsBusy = = false ) & & ( endafter = = true ) )
{
Application . Exit ( ) ;
}
2009-06-11 03:16:49 +02:00
}
private void Form1_Load ( object sender , EventArgs e )
{
this . Text = "NUSD - " + version + " - WB3000" ;
2009-06-19 03:57:52 +02:00
this . Size = this . MinimumSize ;
2009-06-11 03:16:49 +02:00
}
private bool BootChecks ( )
{
// Success?
bool result = true ;
// Directory stuff
string currentdir = Application . StartupPath ;
if ( currentdir . EndsWith ( Convert . ToString ( Path . DirectorySeparatorChar ) ) = = false )
currentdir + = Path . DirectorySeparatorChar ;
2009-08-07 22:06:34 +02:00
/ * Check for certs file
2009-06-11 03:16:49 +02:00
if ( File . Exists ( currentdir + "cert.sys" ) = = false )
{
foreach ( Control ctrl in this . Controls )
{
ctrl . Enabled = false ;
}
getcerts . Enabled = true ;
WriteStatus ( "You do not have a certs file. Press the button below to generate a cert file!" ) ;
result = false ;
}
else if ( verifyMd5Hash ( currentdir + "cert.sys" , certs_MD5 ) = = false )
{
foreach ( Control ctrl in this . Controls )
{
ctrl . Enabled = false ;
}
getcerts . Enabled = true ;
WriteStatus ( "Your certs file is corrupted/invalid. Press the button below to generate a cert file!" ) ;
result = false ;
}
else
{
getcerts . Visible = false ;
WriteStatus ( "Certs file is present and intact." ) ;
2009-08-07 22:06:34 +02:00
} * /
2009-06-11 03:16:49 +02:00
// Check for Wii common key bin file...
if ( File . Exists ( currentdir + "key.bin" ) = = false )
{
WriteStatus ( "Common Key (key.bin) missing! Decryption disabled!" ) ;
decryptbox . Visible = false ;
}
else
{
WriteStatus ( "Common Key detected." ) ;
}
2009-06-13 18:04:54 +02:00
// Check for Wii KOR common key bin file...
if ( File . Exists ( currentdir + "kkey.bin" ) = = false )
{
//WriteStatus("Korean Common Key (kkey.bin) missing! Decryption disabled!");
//decryptbox.Visible = false;
}
else
{
WriteStatus ( "Korean Common Key detected." ) ;
}
2009-06-11 03:16:49 +02:00
// Check for DSi common key bin file...
2009-07-09 04:11:22 +02:00
if ( File . Exists ( currentdir + "dsikey.bin" ) = = false )
2009-06-11 03:16:49 +02:00
{
// Do not pester about DSi key
}
else
{
WriteStatus ( "DSi Common Key detected." ) ;
dsidecrypt = true ;
}
// Check for database.xml
if ( File . Exists ( currentdir + "database.xml" ) = = false )
{
WriteStatus ( "Database.xml not found. Title database not usable!" ) ;
databaseButton . Visible = false ;
2009-08-06 04:37:39 +02:00
Extrasbtn . Size = new System . Drawing . Size ( 134 , 20 ) ;
Extrasbtn . Anchor = AnchorStyles . Right ;
2009-06-11 03:16:49 +02:00
}
else
{
2009-08-04 20:17:21 +02:00
string version = GetDatabaseVersion ( "database.xml" ) ;
2009-06-11 03:16:49 +02:00
WriteStatus ( "Database.xml detected." ) ;
2009-08-04 20:17:21 +02:00
WriteStatus ( " - Version: " + version ) ;
2009-06-11 03:16:49 +02:00
// Load it up...
ClearDatabaseStrip ( ) ;
FillDatabaseStrip ( ) ;
LoadRegionCodes ( ) ;
2009-07-09 04:11:22 +02:00
ShowInnerToolTips ( false ) ;
2009-06-11 03:16:49 +02:00
}
return result ;
}
2009-08-04 20:17:21 +02:00
private string GetDatabaseVersion ( string file )
{
// Read version of Database.xml
XmlDocument xDoc = new XmlDocument ( ) ;
if ( file . Contains ( "<" ) )
xDoc . LoadXml ( file ) ;
else
xDoc . Load ( file ) ;
XmlNodeList DatabaseList = xDoc . GetElementsByTagName ( "database" ) ;
XmlAttributeCollection Attributes = DatabaseList [ 0 ] . Attributes ;
return Attributes [ 0 ] . Value ;
}
2009-06-11 03:16:49 +02:00
private void button1_Click ( object sender , EventArgs e )
2009-08-06 04:37:39 +02:00
{
// Show extras menu
extrasStrip . Show ( Extrasbtn , 2 , 2 ) ;
}
private void LoadTitleFromTMD ( )
2009-06-11 03:16:49 +02:00
{
// Show dialog for opening TMD file...
OpenFileDialog opentmd = new OpenFileDialog ( ) ;
opentmd . Filter = "TMD Files|tmd" ;
opentmd . Title = "Open TMD" ;
if ( opentmd . ShowDialog ( ) ! = DialogResult . Cancel )
{
// Read the tmd as a stream...
2009-07-10 02:56:17 +02:00
byte [ ] tmd = FileLocationToByteArray ( opentmd . FileName ) ;
2009-06-11 03:16:49 +02:00
WriteStatus ( "TMD Loaded (" + tmd . Length + " bytes)" ) ;
// Read ID...
for ( int x = 396 ; x < 404 ; x + + )
{
titleidbox . Text + = MakeProperLength ( ConvertToHex ( Convert . ToString ( tmd [ x ] ) ) ) ;
}
WriteStatus ( "Title ID: " + titleidbox . Text ) ;
// Show TitleID Type/likelyhood of NUS existance...
ReadIDType ( titleidbox . Text ) ;
// Read Title Version...
string tmdversion = "" ;
for ( int x = 476 ; x < 478 ; x + + )
{
tmdversion + = MakeProperLength ( ConvertToHex ( Convert . ToString ( tmd [ x ] ) ) ) ;
}
titleversion . Text = Convert . ToString ( int . Parse ( tmdversion , System . Globalization . NumberStyles . HexNumber ) ) ;
// Read System Version (Needed IOS)
2009-07-14 21:38:18 +02:00
string sysversion = IOSNeededFromTMD ( tmd ) ;
2009-06-11 03:16:49 +02:00
if ( sysversion ! = "0" )
WriteStatus ( "Requires: IOS" + sysversion ) ;
// Read Content #...
2009-07-14 21:38:18 +02:00
int nbr_cont = ContentCount ( tmd ) ;
/ * string contentstrnum = "" ;
2009-06-11 03:16:49 +02:00
for ( int x = 478 ; x < 480 ; x + + )
{
contentstrnum + = TrimLeadingZeros ( Convert . ToString ( tmd [ x ] ) ) ;
2009-07-14 21:38:18 +02:00
} * /
WriteStatus ( "Content Count: " + nbr_cont ) ;
2009-06-11 03:16:49 +02:00
2009-07-14 21:38:18 +02:00
string [ ] tmdcontents = GetContentNames ( tmd , nbr_cont ) ;
string [ ] tmdsizes = GetContentSizes ( tmd , nbr_cont ) ;
byte [ ] tmdhashes = GetContentHashes ( tmd , nbr_cont ) ;
byte [ ] tmdindices = GetContentIndices ( tmd , nbr_cont ) ;
2009-07-17 18:22:58 +02:00
int [ ] tmdtypes = GetContentTypes ( tmd , nbr_cont ) ;
2009-06-11 03:16:49 +02:00
// Loop through each content and display name, hash, index
2009-07-14 21:38:18 +02:00
for ( int i = 0 ; i < nbr_cont ; i + + )
2009-06-11 03:16:49 +02:00
{
WriteStatus ( " Content " + ( i + 1 ) + ": " + tmdcontents [ i ] + " (" + Convert . ToString ( int . Parse ( tmdsizes [ i ] , System . Globalization . NumberStyles . HexNumber ) ) + " bytes)" ) ;
byte [ ] hash = new byte [ 20 ] ;
for ( int x = 0 ; x < 20 ; x + + )
{
2009-08-06 04:37:39 +02:00
hash [ x ] = tmdhashes [ ( i * 20 ) + x ] ;
2009-06-11 03:16:49 +02:00
}
2009-07-17 18:22:58 +02:00
WriteStatus ( " - Hash: " + DisplayBytes ( hash , "" ) . Substring ( 0 , 8 ) + "..." ) ;
2009-06-11 03:16:49 +02:00
WriteStatus ( " - Index: " + tmdindices [ i ] ) ;
2009-07-17 18:22:58 +02:00
WriteStatus ( " - Shared: " + ( tmdtypes [ i ] = = 0x8001 ) ) ;
2009-06-11 03:16:49 +02:00
}
}
}
2009-07-14 21:38:18 +02:00
private string IOSNeededFromTMD ( byte [ ] tmd )
{
string sysversion = "" ;
for ( int i = 0 ; i < 8 ; i + + )
{
sysversion + = MakeProperLength ( ConvertToHex ( Convert . ToString ( tmd [ 0x184 + i ] ) ) ) ;
}
sysversion = Convert . ToString ( int . Parse ( sysversion . Substring ( 14 , 2 ) , System . Globalization . NumberStyles . HexNumber ) ) ;
return sysversion ;
}
private int ContentCount ( byte [ ] tmd )
{
// nbr_cont (0xDE) len=0x02
int nbr_cont = 0 ;
nbr_cont = ( tmd [ 0x1DE ] * 256 ) + tmd [ 0x1DF ] ;
return nbr_cont ;
}
2009-07-23 00:22:04 +02:00
private int GetBootIndex ( byte [ ] tmd )
2009-07-14 21:38:18 +02:00
{
// nbr_cont (0xE0) len=0x02
int bootidx = 0 ;
bootidx = ( tmd [ 0x1E0 ] * 256 ) + tmd [ 0x1E1 ] ;
return bootidx ;
}
2009-07-23 00:22:04 +02:00
private byte [ ] SetBootIndex ( byte [ ] tmd , int bootindex )
{
// nbr_cont (0xE0) len=0x02
byte [ ] bootbytes = NewIntegertoByteArray ( bootindex , 2 ) ;
tmd [ 0x1E0 ] = bootbytes [ 0 ] ;
tmd [ 0x1E1 ] = bootbytes [ 1 ] ;
return tmd ;
}
2009-06-11 03:16:49 +02:00
private void WriteStatus ( string Update )
{
2009-07-10 02:56:17 +02:00
// Small function for writing text to the statusbox...
2009-06-11 03:16:49 +02:00
if ( statusbox . Text = = "" )
statusbox . Text = Update ;
else
statusbox . Text + = "\r\n" + Update ;
// Scroll to end of text box.
statusbox . SelectionStart = statusbox . TextLength ;
statusbox . ScrollToCaret ( ) ;
}
/// <summary>
/// Reads data from a stream until the end is reached. The
/// data is returned as a byte array. An IOException is
/// thrown if any of the underlying IO calls fail.
/// </summary>
/// <param name="stream">The stream to read data from</param>
/// <param name="initialLength">The initial buffer length</param>
public static byte [ ] ReadFully ( Stream stream , int initialLength )
{
// If we've been passed an unhelpful initial length, just
// use 32K.
if ( initialLength < 1 )
{
initialLength = 32768 ;
}
byte [ ] buffer = new byte [ initialLength ] ;
int read = 0 ;
int chunk ;
while ( ( chunk = stream . Read ( buffer , read , buffer . Length - read ) ) > 0 )
{
read + = chunk ;
// If we've reached the end of our buffer, check to see if there's
// any more information
if ( read = = buffer . Length )
{
int nextByte = stream . ReadByte ( ) ;
// End of stream? If so, we're done
if ( nextByte = = - 1 )
{
return buffer ;
}
// Nope. Resize the buffer, put in the byte we've just
// read, and continue
byte [ ] newBuffer = new byte [ buffer . Length * 2 ] ;
Array . Copy ( buffer , newBuffer , buffer . Length ) ;
newBuffer [ read ] = ( byte ) nextByte ;
buffer = newBuffer ;
read + + ;
}
}
// Buffer is now too big. Shrink it.
byte [ ] ret = new byte [ read ] ;
Array . Copy ( buffer , ret , read ) ;
return ret ;
}
private string MakeProperLength ( string hex )
{
// If hex is like, 'A', makes it '0A', etc.
if ( hex . Length = = 1 )
hex = "0" + hex ;
return hex ;
}
private string ConvertToHex ( string decval )
{
// Convert text string to unsigned integer
2009-07-10 02:56:17 +02:00
int uiDecimal = System . Convert . ToInt32 ( decval ) ;
return String . Format ( "{0:x2}" , uiDecimal ) ;
2009-06-11 03:16:49 +02:00
}
private void ReadIDType ( string ttlid )
{
/ * Wiibrew TitleID Info . . .
# 3 00000001 : Essential system titles
# 4 00010000 and 00010004 : Disc - based games
# 5 00010001 : Downloaded channels
* 5.1 000010001 - Cxxx : Commodore 64 Games
* 5.2 000010001 - Exxx : NeoGeo Games
* 5.3 000010001 - Fxxx : NES Games
* 5.4 000010001 - Hxxx : Channels
* 5.5 000010001 - Jxxx : SNES Games
* 5.6 000010001 - Nxxx : Nintendo 64 Games
* 5.7 000010001 - Wxxx : WiiWare
# 6 00010002 : System channels
# 7 00010004 : Game channels and games that use them
# 8 00010005 : Downloaded Game Content
# 9 00010008 : "Hidden" channels
* /
if ( ttlid . Substring ( 0 , 8 ) = = "00000001" )
{
WriteStatus ( "ID Type: System Title. BE CAREFUL!" ) ;
}
else if ( ( ttlid . Substring ( 0 , 8 ) = = "00010000" ) | | ( ttlid . Substring ( 0 , 8 ) = = "00010004" ) )
{
WriteStatus ( "ID Type: Disc-Based Game. Unlikely NUS Content!" ) ;
}
else if ( ttlid . Substring ( 0 , 8 ) = = "00010001" )
{
WriteStatus ( "ID Type: Downloaded Channel. Possible NUS Content." ) ;
}
else if ( ttlid . Substring ( 0 , 8 ) = = "00010002" )
{
WriteStatus ( "ID Type: System Channel. BE CAREFUL!" ) ;
}
else if ( ttlid . Substring ( 0 , 8 ) = = "00010004" )
{
WriteStatus ( "ID Type: Game Channel. Unlikely NUS Content!" ) ;
}
else if ( ttlid . Substring ( 0 , 8 ) = = "00010005" )
{
WriteStatus ( "ID Type: Downloaded Game Content. Unlikely NUS Content!" ) ;
}
else if ( ttlid . Substring ( 0 , 8 ) = = "00010008" )
{
WriteStatus ( "ID Type: 'Hidden' Channel. Unlikely NUS Content!" ) ;
}
else
{
WriteStatus ( "ID Type: Unknown. Unlikely NUS Content!" ) ;
}
}
private string TrimLeadingZeros ( string num )
{
int startindex = 0 ;
for ( int i = 0 ; i < num . Length ; i + + )
{
if ( ( num [ i ] = = 0 ) | | ( num [ i ] = = '0' ) )
startindex + = 1 ;
else
break ;
}
return num . Substring ( startindex , ( num . Length - startindex ) ) ;
}
private string [ ] GetContentNames ( byte [ ] tmdfile , int length )
{
string [ ] contentnames = new string [ length ] ;
int startoffset = 484 ;
for ( int i = 0 ; i < length ; i + + )
{
contentnames [ i ] = MakeProperLength ( ConvertToHex ( Convert . ToString ( tmdfile [ startoffset ] ) ) ) +
MakeProperLength ( ConvertToHex ( Convert . ToString ( tmdfile [ startoffset + 1 ] ) ) ) +
MakeProperLength ( ConvertToHex ( Convert . ToString ( tmdfile [ startoffset + 2 ] ) ) ) +
MakeProperLength ( ConvertToHex ( Convert . ToString ( tmdfile [ startoffset + 3 ] ) ) ) ;
startoffset + = 36 ;
}
return contentnames ;
}
private string [ ] GetContentSizes ( byte [ ] tmdfile , int length )
{
string [ ] contentsizes = new string [ length ] ;
int startoffset = 492 ;
for ( int i = 0 ; i < length ; i + + )
{
contentsizes [ i ] = MakeProperLength ( ConvertToHex ( Convert . ToString ( tmdfile [ startoffset ] ) ) ) +
MakeProperLength ( ConvertToHex ( Convert . ToString ( tmdfile [ startoffset + 1 ] ) ) ) +
MakeProperLength ( ConvertToHex ( Convert . ToString ( tmdfile [ startoffset + 2 ] ) ) ) +
MakeProperLength ( ConvertToHex ( Convert . ToString ( tmdfile [ startoffset + 3 ] ) ) ) +
MakeProperLength ( ConvertToHex ( Convert . ToString ( tmdfile [ startoffset + 4 ] ) ) ) +
MakeProperLength ( ConvertToHex ( Convert . ToString ( tmdfile [ startoffset + 5 ] ) ) ) +
MakeProperLength ( ConvertToHex ( Convert . ToString ( tmdfile [ startoffset + 6 ] ) ) ) +
MakeProperLength ( ConvertToHex ( Convert . ToString ( tmdfile [ startoffset + 7 ] ) ) ) ;
contentsizes [ i ] = TrimLeadingZeros ( contentsizes [ i ] ) ;
/ * contentsizes [ i ] = Convert . ToString ( tmdfile [ startoffset ] ) +
Convert . ToString ( tmdfile [ startoffset + 1 ] ) +
Convert . ToString ( tmdfile [ startoffset + 2 ] ) +
Convert . ToString ( tmdfile [ startoffset + 3 ] ) +
Convert . ToString ( tmdfile [ startoffset + 4 ] ) +
Convert . ToString ( tmdfile [ startoffset + 5 ] ) +
Convert . ToString ( tmdfile [ startoffset + 6 ] ) +
Convert . ToString ( tmdfile [ startoffset + 7 ] ) ;
contentsizes [ i ] = TrimLeadingZeros ( contentsizes [ i ] ) ; * /
startoffset + = 36 ;
}
return contentsizes ;
}
private byte [ ] GetContentHashes ( byte [ ] tmdfile , int length )
{
byte [ ] contenthashes = new byte [ length * 20 ] ;
int startoffset = 500 ;
for ( int i = 0 ; i < length ; i + + )
{
for ( int x = 0 ; x < 20 ; x + + )
{
contenthashes [ ( i * 20 ) + x ] = tmdfile [ startoffset + x ] ;
}
startoffset + = 36 ;
}
return contenthashes ;
}
2009-07-17 18:22:58 +02:00
// Returns array of shared/normal values for a tmd...
private int [ ] GetContentTypes ( byte [ ] tmdfile , int length )
{
int [ ] contenttypes = new int [ length ] ;
int startoffset = 0x1EA ;
for ( int i = 0 ; i < length ; i + + )
{
if ( tmdfile [ startoffset ] = = 0x80 )
contenttypes [ i ] = ( int ) ContentTypes . Shared ;
else
contenttypes [ i ] = ( int ) ContentTypes . Normal ;
startoffset + = 36 ;
}
return contenttypes ;
}
2009-06-11 03:16:49 +02:00
private byte [ ] GetContentIndices ( byte [ ] tmdfile , int length )
{
byte [ ] contentindices = new byte [ length ] ;
int startoffset = 0x1E9 ;
for ( int i = 0 ; i < length ; i + + )
{
contentindices [ i ] = tmdfile [ startoffset ] ;
startoffset + = 36 ;
}
return contentindices ;
}
private void button3_Click ( object sender , EventArgs e )
{
// Prevent mass deletion
if ( ( titleidbox . Text = = "" ) & & ( titleversion . Text = = "" ) )
{
WriteStatus ( "Please enter SOME info..." ) ;
return ;
}
2009-07-09 04:11:22 +02:00
else
{
2009-07-14 21:38:18 +02:00
try
{
if ( ! statusbox . Lines [ 0 ] . StartsWith ( " ---" ) )
statusbox . Text = " --- " + titleidbox . Text + " ---" ;
}
catch // No lines present...
{
2009-07-10 02:56:17 +02:00
statusbox . Text = " --- " + titleidbox . Text + " ---" ;
2009-07-14 21:38:18 +02:00
}
2009-07-09 04:11:22 +02:00
}
2009-06-11 03:16:49 +02:00
// Running Downloads in background so no form freezing
NUSDownloader . RunWorkerAsync ( ) ;
}
private void NUSDownloader_DoWork ( object sender , System . ComponentModel . DoWorkEventArgs e )
{
// Preparations for Downloading
Control . CheckForIllegalCrossThreadCalls = false ;
WriteStatus ( "Starting NUS Download. Please be patient!" ) ;
2009-07-08 01:41:38 +02:00
SetEnableforDownload ( false ) ;
2009-06-19 03:57:52 +02:00
downloadstartbtn . Text = "Starting NUS Download!" ;
2009-06-11 03:16:49 +02:00
// Current directory...
string currentdir = Application . StartupPath ;
if ( ! ( currentdir . EndsWith ( @"\" ) ) | | ! ( currentdir . EndsWith ( @"/" ) ) )
currentdir + = @"\" ;
// Prevent crossthread issues
string titleid = titleidbox . Text ;
// Creates the directory
CreateTitleDirectory ( ) ;
// Wii / DSi
bool wiimode = radioButton1 . Checked ;
// Set UserAgent to Wii value
2009-08-01 19:48:32 +02:00
generalWC . Headers . Add ( "User-Agent" , "wii libnup/1.0" ) ;
2009-06-11 03:16:49 +02:00
// Get placement directory early...
string titledirectory ;
if ( titleversion . Text = = "" )
titledirectory = currentdir + titleid + @"\" ;
else
titledirectory = currentdir + titleid + "v" + titleversion . Text + @"\" ;
2009-06-19 03:57:52 +02:00
downloadstartbtn . Text = "Prerequisites: (0/2)" ;
2009-06-11 03:16:49 +02:00
// Download TMD before the rest...
string tmdfull = "tmd" ;
if ( titleversion . Text ! = "" )
tmdfull + = "." + titleversion . Text ;
try
{
DownloadNUSFile ( titleid , tmdfull , titledirectory , 0 , wiimode ) ;
}
catch ( Exception ex )
{
WriteStatus ( "Download Failed: " + tmdfull ) ;
WriteStatus ( " - Reason: " + ex . Message . ToString ( ) . Replace ( "The remote server returned an error: " , "" ) ) ;
2009-07-08 01:41:38 +02:00
SetEnableforDownload ( true ) ;
2009-06-19 03:57:52 +02:00
downloadstartbtn . Text = "Start NUS Download!" ;
2009-06-11 03:16:49 +02:00
dlprogress . Value = 0 ;
DeleteTitleDirectory ( ) ;
return ;
}
2009-06-19 03:57:52 +02:00
downloadstartbtn . Text = "Prerequisites: (1/2)" ;
2009-06-11 03:16:49 +02:00
dlprogress . Value = 50 ;
// Download CETK after tmd...
try
{
DownloadNUSFile ( titleid , "cetk" , titledirectory , 0 , wiimode ) ;
}
catch ( Exception ex )
{
if ( ignoreticket . Checked = = false )
{
WriteStatus ( "Download Failed: cetk" ) ;
WriteStatus ( " - Reason: " + ex . Message . ToString ( ) . Replace ( "The remote server returned an error: " , "" ) ) ;
WriteStatus ( "You may be able to retrieve the contents by Ignoring the Ticket (Check below)" ) ;
2009-07-08 01:41:38 +02:00
SetEnableforDownload ( true ) ;
2009-06-19 03:57:52 +02:00
downloadstartbtn . Text = "Start NUS Download!" ;
2009-06-11 03:16:49 +02:00
dlprogress . Value = 0 ;
DeleteTitleDirectory ( ) ;
return ;
}
else
{
WriteStatus ( "Ticket not found! Continuing, however WAD packing and decryption are not possible!" ) ;
packbox . Checked = false ;
decryptbox . Checked = false ;
}
}
2009-06-19 03:57:52 +02:00
downloadstartbtn . Text = "Prerequisites: (2/2)" ;
2009-06-11 03:16:49 +02:00
dlprogress . Value = 100 ;
2009-08-07 22:06:34 +02:00
// Create ticket file holder
byte [ ] cetkbuf = FileLocationToByteArray ( titledirectory + @"\cetk" ) ;
2009-06-11 03:16:49 +02:00
// Obtain TitleKey
byte [ ] titlekey = new byte [ 16 ] ;
if ( decryptbox . Checked = = true )
{
// Load TitleKey into it's byte[]
// It is currently encrypted...
for ( int i = 0 ; i < 16 ; i + + )
{
titlekey [ i ] = cetkbuf [ 0x1BF + i ] ;
}
// IV (TITLEID+0000s)
byte [ ] iv = new byte [ 16 ] ;
for ( int i = 0 ; i < 8 ; i + + )
{
iv [ i ] = cetkbuf [ 0x1DC + i ] ;
}
for ( int i = 0 ; i < 8 ; i + + )
{
iv [ i + 8 ] = 0x00 ;
}
2009-07-10 02:56:17 +02:00
// Standard/Korea Common Key
byte [ ] keyBytes ;
2009-06-13 18:04:54 +02:00
if ( cetkbuf [ 0x01F1 ] = = 0x01 )
{
WriteStatus ( "Key Type: Korean" ) ;
2009-07-10 02:56:17 +02:00
keyBytes = LoadCommonKey ( @"\kkey.bin" ) ;
2009-06-13 18:04:54 +02:00
}
else
{
WriteStatus ( "Key Type: Standard" ) ;
2009-07-10 02:56:17 +02:00
if ( wiimode )
2009-06-13 18:04:54 +02:00
keyBytes = LoadCommonKey ( @"\key.bin" ) ;
2009-07-10 02:56:17 +02:00
else
keyBytes = LoadCommonKey ( @"\dsikey.bin" ) ;
2009-06-13 18:04:54 +02:00
}
2009-07-10 02:56:17 +02:00
2009-06-11 03:16:49 +02:00
initCrypt ( iv , keyBytes ) ;
2009-07-10 02:56:17 +02:00
WriteStatus ( "Title Key: " + DisplayBytes ( Decrypt ( titlekey ) , "" ) ) ;
2009-06-11 03:16:49 +02:00
titlekey = Decrypt ( titlekey ) ;
}
// Read the tmd as a stream...
2009-07-10 02:56:17 +02:00
byte [ ] tmd = FileLocationToByteArray ( titledirectory + tmdfull ) ;
2009-06-11 03:16:49 +02:00
2009-08-07 22:06:34 +02:00
// Locate Certs **************************************
if ( ! ( CertsValid ( ) ) )
{
WriteStatus ( "Searching for certs..." ) ;
ScanForCerts ( tmd ) ;
ScanForCerts ( cetkbuf ) ;
}
else
WriteStatus ( "Using cached certs..." ) ;
// /Locate Cert **************************************
2009-06-11 03:16:49 +02:00
// Read Title Version...
string tmdversion = "" ;
for ( int x = 476 ; x < 478 ; x + + )
{
tmdversion + = MakeProperLength ( ConvertToHex ( Convert . ToString ( tmd [ x ] ) ) ) ;
}
titleversion . Text = Convert . ToString ( int . Parse ( tmdversion , System . Globalization . NumberStyles . HexNumber ) ) ;
2009-07-14 21:38:18 +02:00
//Read System Version (Needed IOS)
string sysversion = IOSNeededFromTMD ( tmd ) ;
2009-07-09 04:11:22 +02:00
if ( sysversion ! = "0" )
WriteStatus ( "Requires: IOS" + sysversion ) ;
2009-06-11 03:16:49 +02:00
// Renaming would be ideal, but gives too many errors...
/ * if ( ( currentdir + titleid + "v" + titleversion . Text + @"\" ) ! = titledirectory )
{
Directory . Move ( titledirectory , currentdir + titleid + "v" + titleversion . Text + @"\" ) ;
titledirectory = currentdir + titleid + "v" + titleversion . Text + @"\" ;
DirectoryInfo di = new DirectoryInfo ( titledirectory ) ;
} * /
// Read Content #...
string contentstrnum = "" ;
for ( int x = 478 ; x < 480 ; x + + )
{
contentstrnum + = TrimLeadingZeros ( Convert . ToString ( tmd [ x ] ) ) ;
}
WriteStatus ( "Content #: " + contentstrnum ) ;
2009-06-19 03:57:52 +02:00
downloadstartbtn . Text = "Content: (0/" + contentstrnum + ")" ;
2009-06-11 03:16:49 +02:00
dlprogress . Value = 0 ;
// Gather information...
string [ ] tmdcontents = GetContentNames ( tmd , Convert . ToInt32 ( contentstrnum ) ) ;
string [ ] tmdsizes = GetContentSizes ( tmd , Convert . ToInt32 ( contentstrnum ) ) ;
byte [ ] tmdhashes = GetContentHashes ( tmd , Convert . ToInt32 ( contentstrnum ) ) ;
byte [ ] tmdindices = GetContentIndices ( tmd , Convert . ToInt32 ( contentstrnum ) ) ;
// Progress bar total size tally info...
float totalcontentsize = 0 ;
float currentcontentlocation = 0 ;
for ( int i = 0 ; i < tmdsizes . Length ; i + + )
{
totalcontentsize + = int . Parse ( tmdsizes [ i ] , System . Globalization . NumberStyles . HexNumber ) ;
}
WriteStatus ( "Total Size: " + ( long ) totalcontentsize + " bytes" ) ;
for ( int i = 0 ; i < tmdcontents . Length ; i + + )
{
try
{
// If it exists we leave it...
if ( ( localuse . Checked ) & & ( File . Exists ( titledirectory + tmdcontents [ i ] ) ) )
{
WriteStatus ( "Leaving local " + tmdcontents [ i ] + "." ) ;
}
else
{
DownloadNUSFile ( titleid , tmdcontents [ i ] , titledirectory , int . Parse ( tmdsizes [ i ] , System . Globalization . NumberStyles . HexNumber ) , wiimode ) ;
}
}
catch ( Exception ex )
{
WriteStatus ( "Download Failed: " + tmdcontents [ i ] ) ;
WriteStatus ( " - Reason: " + ex . Message . ToString ( ) . Replace ( "The remote server returned an error: " , "" ) ) ;
2009-07-08 01:41:38 +02:00
SetEnableforDownload ( true ) ;
2009-06-19 03:57:52 +02:00
downloadstartbtn . Text = "Start NUS Download!" ;
2009-06-11 03:16:49 +02:00
dlprogress . Value = 0 ;
DeleteTitleDirectory ( ) ;
return ;
}
// Progress reporting advances...
2009-06-19 03:57:52 +02:00
downloadstartbtn . Text = "Content: (" + ( i + 1 ) + @"/" + contentstrnum + ")" ;
2009-06-11 03:16:49 +02:00
currentcontentlocation + = int . Parse ( tmdsizes [ i ] , System . Globalization . NumberStyles . HexNumber ) ;
// Decrypt stuff...
if ( decryptbox . Checked = = true )
{
// Create content file holder
2009-07-10 02:56:17 +02:00
byte [ ] contbuf = FileLocationToByteArray ( titledirectory + @"\" + tmdcontents [ i ] ) ;
2009-06-11 03:16:49 +02:00
// IV (00+IDX+more000)
byte [ ] iv = new byte [ 16 ] ;
2009-07-17 18:22:58 +02:00
for ( int x = 0 ; x < 16 ; x + + )
2009-06-11 03:16:49 +02:00
{
iv [ x ] = 0x00 ;
}
iv [ 1 ] = tmdindices [ i ] ;
initCrypt ( iv , titlekey ) ;
2009-07-17 18:22:58 +02:00
/ * Create decrypted file
2009-06-11 03:16:49 +02:00
string zeros = "000000" ;
2009-07-17 18:22:58 +02:00
FileStream decfs = new FileStream ( titledirectory + @"\" + zeros + i . ToString ( "X2" ) + ".app" , FileMode . Create ) ;
decfs . Write ( Decrypt ( contbuf ) , 0 , int . Parse ( tmdsizes [ i ] , System . Globalization . NumberStyles . HexNumber ) ) ;
decfs . Close ( ) ;
WriteStatus ( " - Decrypted: " + zeros + i . ToString ( "X2" ) + ".app" ) ; * /
FileStream decfs = new FileStream ( titledirectory + @"\" + tmdcontents [ i ] + ".app" , FileMode . Create ) ;
2009-06-11 03:16:49 +02:00
decfs . Write ( Decrypt ( contbuf ) , 0 , int . Parse ( tmdsizes [ i ] , System . Globalization . NumberStyles . HexNumber ) ) ;
decfs . Close ( ) ;
2009-07-17 18:22:58 +02:00
WriteStatus ( " - Decrypted: " + tmdcontents [ i ] + ".app" ) ;
2009-06-11 03:16:49 +02:00
// Hash Check...
byte [ ] hash = new byte [ 20 ] ;
for ( int x = 0 ; x < 20 ; x + + )
{
hash [ x ] = tmdhashes [ ( i * 20 ) + x ] ;
}
byte [ ] deccont = Decrypt ( contbuf ) ;
Array . Resize ( ref deccont , int . Parse ( tmdsizes [ i ] , System . Globalization . NumberStyles . HexNumber ) ) ;
if ( ( Convert . ToBase64String ( ComputeSHA ( deccont ) ) ) = = Convert . ToBase64String ( hash ) )
{
WriteStatus ( " - Hash Check: Pass" ) ;
}
else
{
WriteStatus ( " - Hash Check: Fail" ) ;
2009-07-10 02:56:17 +02:00
WriteStatus ( " - True Hash: " + DisplayBytes ( hash , "" ) ) ;
WriteStatus ( " - You Have: " + DisplayBytes ( ComputeSHA ( Decrypt ( contbuf ) ) , "" ) ) ;
2009-06-11 03:16:49 +02:00
}
}
dlprogress . Value = Convert . ToInt32 ( ( ( currentcontentlocation / totalcontentsize ) * 100 ) ) ;
}
WriteStatus ( "NUS Download Finished." ) ;
2009-06-17 00:25:19 +02:00
// Trucha signing...
if ( ( truchabox . Checked = = true ) & & ( wiimode = = true ) )
{
2009-06-19 03:57:52 +02:00
// Read information from TMD into signing GUI...
requiredIOSbox . Text = Convert . ToString ( tmd [ 0x18B ] ) ;
2009-07-08 01:41:38 +02:00
tmdversiontrucha . Text = Convert . ToString ( ( tmd [ 0x1DC ] * 256 ) + tmd [ 0x1DD ] ) ;
2009-06-19 03:57:52 +02:00
newtitleidbox . Text = titleid ;
2009-07-14 21:38:18 +02:00
// Add contents to contentEdit...
FillContentInfo ( tmd ) ;
2009-07-04 20:32:52 +02:00
// Setup for NO IOS
if ( requiredIOSbox . Text = = "0" )
requiredIOSbox . Enabled = false ;
else
requiredIOSbox . Enabled = true ;
2009-06-19 03:57:52 +02:00
// Read information from TIK into signing GUI...
// Titlekey
for ( int i = 0 ; i < 16 ; i + + )
{
2009-08-07 22:06:34 +02:00
titlekey [ i ] = cetkbuf [ 0x1BF + i ] ;
2009-06-19 03:57:52 +02:00
}
//titlekeybox.Text = DisplayBytes(titlekey).Replace(" ", "");
titlekeybox . Text = System . Text . Encoding . UTF7 . GetString ( titlekey ) ;
// IV (TITLEID+00000000s)
byte [ ] iv = new byte [ 16 ] ;
for ( int i = 0 ; i < 8 ; i + + )
{
2009-08-07 22:06:34 +02:00
iv [ i ] = cetkbuf [ 0x1DC + i ] ;
2009-06-19 03:57:52 +02:00
}
for ( int i = 0 ; i < 8 ; i + + )
{
iv [ i + 8 ] = 0x00 ;
}
2009-07-10 02:56:17 +02:00
titleIDIV . Text = DisplayBytes ( iv , "" ) ;
2009-06-19 03:57:52 +02:00
//DLC
2009-08-07 22:06:34 +02:00
dlcamntbox . Text = Convert . ToString ( ( cetkbuf [ 0x1E6 ] * 256 ) + cetkbuf [ 0x1E7 ] ) ;
2009-06-19 03:57:52 +02:00
//keyindex
2009-08-07 22:06:34 +02:00
if ( cetkbuf [ 0x1F1 ] = = 0x00 )
2009-06-19 03:57:52 +02:00
ckeyindexcb . SelectedIndex = 0 ;
2009-08-07 22:06:34 +02:00
else if ( cetkbuf [ 0x1F1 ] = = 0x01 )
2009-06-19 03:57:52 +02:00
ckeyindexcb . SelectedIndex = 1 ;
else
ckeyindexcb . SelectedIndex = 0 ;
//time enabled
2009-08-07 22:06:34 +02:00
if ( cetkbuf [ 0x247 ] = = 0x00 )
2009-06-19 03:57:52 +02:00
timelimitenabledcb . SelectedIndex = 0 ;
2009-08-07 22:06:34 +02:00
else if ( cetkbuf [ 0x247 ] = = 0x01 )
2009-06-19 03:57:52 +02:00
timelimitenabledcb . SelectedIndex = 1 ;
else
timelimitenabledcb . SelectedIndex = 0 ;
//time in seconds
byte [ ] timelimit = new byte [ 4 ] ;
for ( int i = 0 ; i < timelimit . Length ; i + + )
{
2009-08-07 22:06:34 +02:00
timelimit [ i ] = cetkbuf [ 0x248 + 1 ] ;
2009-06-19 03:57:52 +02:00
}
timelimitsecs . Text = Convert . ToString ( System . BitConverter . ToInt32 ( timelimit , 0 ) ) ;
// Resize form to max to show trucha options...
this . Size = this . MaximumSize ;
2009-07-04 20:32:52 +02:00
shamelessvariablelabel . Text = String . Format ( "{0},{1},{2}" , titledirectory , tmdfull , contentstrnum ) ;
2009-06-19 03:57:52 +02:00
2009-07-08 01:41:38 +02:00
// Loop until user is finished...
2009-06-19 03:57:52 +02:00
while ( this . Size = = this . MaximumSize )
{
System . Threading . Thread . Sleep ( 1000 ) ;
}
2009-07-21 23:53:27 +02:00
// Re-Gather information...
byte [ ] tmdrefresh = FileLocationToByteArray ( titledirectory + tmdfull ) ;
tmdcontents = GetContentNames ( tmd , ContentCount ( tmdrefresh ) ) ;
tmdsizes = GetContentSizes ( tmd , ContentCount ( tmdrefresh ) ) ;
tmdhashes = GetContentHashes ( tmd , ContentCount ( tmdrefresh ) ) ;
tmdindices = GetContentIndices ( tmd , ContentCount ( tmdrefresh ) ) ;
2009-06-19 03:57:52 +02:00
/ *
2009-06-17 00:25:19 +02:00
WriteStatus ( "Trucha Signing TMD..." ) ;
2009-06-19 03:57:52 +02:00
Array . Resize ( ref tmd , 484 + ( Convert . ToInt32 ( contentstrnum ) * 36 ) ) ;
2009-06-17 00:25:19 +02:00
tmd = ZeroSignature ( tmd ) ;
tmd = TruchaSign ( tmd ) ;
FileStream testtmd = new FileStream ( titledirectory + tmdfull , FileMode . Open ) ;
testtmd . Write ( tmd , 0 , tmd . Length ) ;
testtmd . Close ( ) ;
WriteStatus ( "Trucha Signing Ticket..." ) ;
// Create ticket file holder
FileStream cetkf = File . OpenRead ( titledirectory + @"\cetk" ) ;
byte [ ] cetkbuff = ReadFully ( cetkf , 20 ) ;
cetkf . Close ( ) ;
Array . Resize ( ref cetkbuff , 0x2A4 ) ;
2009-06-19 03:57:52 +02:00
2009-06-17 00:25:19 +02:00
cetkbuff = ZeroSignature ( cetkbuff ) ;
cetkbuff = TruchaSign ( cetkbuff ) ;
FileStream testtik = new FileStream ( titledirectory + "cetk" , FileMode . Open ) ;
testtik . Write ( cetkbuff , 0 , cetkbuff . Length ) ;
2009-06-19 03:57:52 +02:00
testtik . Close ( ) ; * /
2009-06-17 00:25:19 +02:00
}
2009-06-11 03:16:49 +02:00
if ( ( packbox . Checked = = true ) & & ( wiimode = = true ) )
{
PackWAD ( titleid , tmdfull , tmdcontents . Length , tmdcontents , tmdsizes , titledirectory ) ;
}
2009-07-08 01:41:38 +02:00
SetEnableforDownload ( true ) ;
2009-06-19 03:57:52 +02:00
downloadstartbtn . Text = "Start NUS Download!" ;
2009-06-11 03:16:49 +02:00
dlprogress . Value = 0 ;
}
private void CreateTitleDirectory ( )
{
// Creates the directory for the downloaded title...
string currentdir = Application . StartupPath ;
if ( currentdir . EndsWith ( Convert . ToString ( Path . DirectorySeparatorChar ) ) = = false )
currentdir + = Path . DirectorySeparatorChar ;
// Get placement directory early...
string titledirectory ;
if ( titleversion . Text = = "" )
titledirectory = Path . Combine ( currentdir , titleidbox . Text + Path . DirectorySeparatorChar ) ;
else
titledirectory = Path . Combine ( currentdir , titleidbox . Text + "v" + titleversion . Text + Path . DirectorySeparatorChar ) ;
// Keep local directory if present and checked out...
if ( ( localuse . Checked ) & & ( Directory . Exists ( titledirectory ) ) )
{
//WriteStatus("Using Local Files");
}
else
{
if ( Directory . Exists ( titledirectory ) )
Directory . Delete ( titledirectory , true ) ;
Directory . CreateDirectory ( titledirectory ) ;
}
}
private void DeleteTitleDirectory ( )
{
string currentdir = Application . StartupPath ;
if ( currentdir . EndsWith ( Convert . ToString ( Path . DirectorySeparatorChar ) ) = = false )
currentdir + = Path . DirectorySeparatorChar ;
// Get placement directory early...
string titledirectory ;
if ( titleversion . Text = = "" )
titledirectory = Path . Combine ( currentdir , titleidbox . Text + Path . DirectorySeparatorChar ) ;
else
titledirectory = Path . Combine ( currentdir , titleidbox . Text + "v" + titleversion . Text + Path . DirectorySeparatorChar ) ;
if ( Directory . Exists ( titledirectory ) )
Directory . Delete ( titledirectory , true ) ;
//Directory.CreateDirectory(currentdir + titleidbox.Text);
}
private void DownloadNUSFile ( string titleid , string filename , string placementdir , int sizeinbytes , bool iswiititle )
{
// Create NUS URL...
string nusfileurl ;
if ( iswiititle )
nusfileurl = NUSURL + titleid + @"/" + filename ;
else
nusfileurl = DSiNUSURL + titleid + @"/" + filename ;
WriteStatus ( "Grabbing " + filename + "..." ) ;
// State size of file...
if ( sizeinbytes ! = 0 )
statusbox . Text + = " (" + Convert . ToString ( sizeinbytes ) + " bytes)" ;
// Download NUS file...
generalWC . DownloadFile ( nusfileurl , placementdir + filename ) ;
}
public void PackWAD ( string titleid , string tmdfilename , int contentcount , string [ ] contentnames , string [ ] contentsizes , string totaldirectory )
{
2009-08-07 22:06:34 +02:00
WriteStatus ( "Beginning WAD Pack..." ) ;
2009-06-11 03:16:49 +02:00
// Directory stuff
string currentdir = Application . StartupPath ;
if ( ! ( currentdir . EndsWith ( @"\" ) ) | | ! ( currentdir . EndsWith ( @"/" ) ) )
currentdir + = @"\" ;
// Create cert file holder
2009-08-07 22:06:34 +02:00
//byte[] certsbuf = FileLocationToByteArray(currentdir + @"\cert.sys");
byte [ ] certsbuf = new byte [ 0xA00 ] ;
if ( ! ( CertsValid ( ) ) )
{
WriteStatus ( "Error: NUSD could not locate cached certs!" ) ;
return ;
}
for ( int c = 0 ; c < cert_CA . Length ; c + + )
{
certsbuf [ c ] = cert_CA [ c ] ;
}
for ( int c = 0 ; c < cert_CACP . Length ; c + + )
{
certsbuf [ c + 0x400 ] = cert_CACP [ c ] ;
}
for ( int c = 0 ; c < cert_CAXS . Length ; c + + )
{
certsbuf [ c + 0x700 ] = cert_CAXS [ c ] ;
}
if ( ! ( TotalCertValid ( certsbuf ) ) )
{
WriteStatus ( "Error: Cert array did not hash properly!" ) ;
return ;
}
2009-06-11 03:16:49 +02:00
// Create ticket file holder
2009-07-10 02:56:17 +02:00
byte [ ] cetkbuf = FileLocationToByteArray ( totaldirectory + @"\cetk" ) ;
2009-06-11 03:16:49 +02:00
// Create tmd file holder
2009-07-10 02:56:17 +02:00
byte [ ] tmdbuf = FileLocationToByteArray ( totaldirectory + @"\" + tmdfilename ) ;
2009-06-11 03:16:49 +02:00
if ( wadnamebox . Text . Contains ( "[v]" ) = = true )
wadnamebox . Text = wadnamebox . Text . Replace ( "[v]" , "v" + titleversion . Text ) ;
// Create wad file
2009-07-17 18:22:58 +02:00
FileStream wadfs = new FileStream ( totaldirectory + @"\" + RemoveIllegalCharacters ( wadnamebox . Text ) , FileMode . Create ) ;
2009-06-11 03:16:49 +02:00
// Add wad stuffs
WADHeader wad = new WADHeader ( ) ;
wad . HeaderSize = 0x20 ;
wad . WadType = 0x49730000 ;
wad . CertChainSize = 0xA00 ;
// Write cert[] to 0x40.
wadfs . Seek ( 0x40 , SeekOrigin . Begin ) ;
wadfs . Write ( certsbuf , 0 , certsbuf . Length ) ;
2009-08-07 22:06:34 +02:00
WriteStatus ( " - Certs wrote (0x" + Convert . ToString ( 64 , 16 ) + ")" ) ;
2009-06-11 03:16:49 +02:00
// Need 64 byte boundary...
wadfs . Seek ( 2624 , SeekOrigin . Begin ) ;
// Cert is 2560
// Write ticket at this point...
wad . TicketSize = 0x2A4 ;
wadfs . Write ( cetkbuf , 0 , wad . TicketSize ) ;
2009-08-07 22:06:34 +02:00
WriteStatus ( " - Ticket wrote (0x" + Convert . ToString ( ( wadfs . Length - 0x2A4 ) , 16 ) + ")" ) ;
2009-06-11 03:16:49 +02:00
// Need 64 byte boundary...
wadfs . Seek ( ByteBoundary ( Convert . ToInt32 ( wadfs . Length ) ) , SeekOrigin . Begin ) ;
// Write TMD at this point...
2009-06-17 00:25:19 +02:00
wad . TMDSize = 484 + ( contentcount * 36 ) ;
2009-06-11 03:16:49 +02:00
wadfs . Write ( tmdbuf , 0 , 484 + ( contentcount * 36 ) ) ;
2009-08-07 22:06:34 +02:00
WriteStatus ( " - TMD wrote (0x" + Convert . ToString ( ( wadfs . Length - ( 484 + ( contentcount * 36 ) ) ) , 16 ) + ")" ) ;
2009-06-11 03:16:49 +02:00
// Preliminary data size of wad file.
wad . DataSize = 0 ;
// Loop n Add contents
for ( int i = 0 ; i < contentcount ; i + + )
{
// Need 64 byte boundary...
wadfs . Seek ( ByteBoundary ( Convert . ToInt32 ( wadfs . Length ) ) , SeekOrigin . Begin ) ;
// Create content file holder
2009-07-10 02:56:17 +02:00
byte [ ] contbuf = FileLocationToByteArray ( totaldirectory + @"\" + contentnames [ i ] ) ;
2009-06-11 03:16:49 +02:00
wadfs . Write ( contbuf , 0 , contbuf . Length ) ;
2009-08-07 22:06:34 +02:00
WriteStatus ( " - " + contentnames [ i ] + " wrote (0x" + Convert . ToString ( ( wadfs . Length - contbuf . Length ) , 16 ) + ")" ) ;
2009-06-11 03:16:49 +02:00
HandleMismatch ( int . Parse ( contentsizes [ i ] , System . Globalization . NumberStyles . HexNumber ) , contbuf . Length ) ;
wad . DataSize + = contbuf . Length ;
}
// Seek the beginning of the WAD...
wadfs . Seek ( 0 , SeekOrigin . Begin ) ;
// Write initial part of header
byte [ ] start = new byte [ 8 ] { 0x00 , 0x00 , 0x00 , 0x20 , 0x49 , 0x73 , 0x00 , 0x00 } ;
wadfs . Write ( start , 0 , start . Length ) ;
// Write CertChainLength
wadfs . Seek ( 0x08 , SeekOrigin . Begin ) ;
2009-06-19 03:57:52 +02:00
byte [ ] chainsize = InttoByteArray ( wad . CertChainSize , 4 ) ;
2009-06-11 03:16:49 +02:00
wadfs . Write ( chainsize , 0 , 4 ) ;
// Write res
byte [ ] reserved = new byte [ 4 ] { 0x00 , 0x00 , 0x00 , 0x00 } ;
wadfs . Seek ( 0x0C , SeekOrigin . Begin ) ;
wadfs . Write ( reserved , 0 , 4 ) ;
// Write ticketsize
byte [ ] ticketsize = new byte [ 4 ] { 0x00 , 0x00 , 0x02 , 0xA4 } ;
wadfs . Seek ( 0x10 , SeekOrigin . Begin ) ;
wadfs . Write ( ticketsize , 0 , 4 ) ;
// Write tmdsize
int strippedtmd = 484 + ( contentcount * 36 ) ;
2009-06-19 03:57:52 +02:00
byte [ ] tmdsize = InttoByteArray ( strippedtmd , 4 ) ;
2009-06-11 03:16:49 +02:00
wadfs . Seek ( 0x14 , SeekOrigin . Begin ) ;
wadfs . Write ( tmdsize , 0 , 4 ) ;
// Write data size
wadfs . Seek ( 0x18 , SeekOrigin . Begin ) ;
2009-06-19 03:57:52 +02:00
byte [ ] datasize = InttoByteArray ( wad . DataSize , 4 ) ;
2009-06-11 03:16:49 +02:00
wadfs . Write ( datasize , 0 , 4 ) ;
// Finished.
WriteStatus ( "WAD Created: " + wadnamebox . Text ) ;
// Close filesystem...
wadfs . Close ( ) ;
}
private long ByteBoundary ( int currentlength )
{
// Gets the next 0x40 offset.
long thelength = currentlength - 1 ;
long remainder = 1 ;
while ( remainder ! = 0 )
{
thelength + = 1 ;
remainder = thelength % 0x40 ;
}
//WriteStatus("Initial Size: " + currentlength);
//WriteStatus("0x40 Size: " + thelength);
return ( long ) thelength ;
}
2009-06-19 03:57:52 +02:00
private byte [ ] InttoByteArray ( int inte , int arraysize )
2009-06-11 03:16:49 +02:00
{
// Take integer and make into byte array
2009-06-19 03:57:52 +02:00
byte [ ] b = new byte [ arraysize ] ;
b = BitConverter . GetBytes ( inte ) ;
2009-06-11 03:16:49 +02:00
if ( BitConverter . IsLittleEndian )
Array . Reverse ( b ) ;
return b ;
}
private void radioButton2_CheckedChanged ( object sender , EventArgs e )
{
if ( radioButton2 . Checked = = true )
{
// Cannot Pack WADs
packbox . Checked = false ;
packbox . Enabled = false ;
// Can decrypt if key exists...lulz
if ( dsidecrypt = = false )
{
decryptbox . Checked = false ;
decryptbox . Enabled = false ;
}
wadnamebox . Enabled = false ;
wadnamebox . Text = "" ;
}
}
private void radioButton1_CheckedChanged ( object sender , EventArgs e )
{
if ( radioButton1 . Checked = = true )
{
// Can pack WADs
// packbox.Checked = true;
packbox . Enabled = true ;
decryptbox . Enabled = true ;
}
}
private void button2_Click ( object sender , EventArgs e )
{
// Display About Text...
statusbox . Text = "" ;
WriteStatus ( "NUS Downloader (NUSD)" ) ;
WriteStatus ( "You are running version: " + version ) ;
2009-07-21 23:53:27 +02:00
WriteStatus ( "This application created by WB3000" ) ;
2009-06-11 03:16:49 +02:00
WriteStatus ( "" ) ;
string currentdir = Application . StartupPath ;
if ( currentdir . EndsWith ( Convert . ToString ( Path . DirectorySeparatorChar ) ) = = false )
currentdir + = Path . DirectorySeparatorChar ;
if ( File . Exists ( currentdir + "key.bin" ) = = false )
WriteStatus ( "Wii Decryption: Need (key.bin)" ) ;
else
WriteStatus ( "Wii Decryption: OK" ) ;
2009-06-13 18:04:54 +02:00
if ( File . Exists ( currentdir + "kkey.bin" ) = = false )
WriteStatus ( "Wii Korea Decryption: Need (kkey.bin)" ) ;
else
WriteStatus ( "Wii Korea Decryption: OK" ) ;
2009-07-09 04:11:22 +02:00
if ( File . Exists ( currentdir + "dsikey.bin" ) = = false )
WriteStatus ( "DSi Decryption: Need (dsikey.bin)" ) ;
2009-06-11 03:16:49 +02:00
else
WriteStatus ( "DSi Decryption: OK" ) ;
2009-06-13 18:04:54 +02:00
if ( File . Exists ( currentdir + "database.xml" ) = = false )
WriteStatus ( "Database: Need (database.xml)" ) ;
else
WriteStatus ( "Database: OK" ) ;
2009-07-17 18:22:58 +02:00
2009-07-21 23:53:27 +02:00
/ * if ( IsWin7 ( ) )
WriteStatus ( "Windows 7 Features: Enabled" ) ; * /
2009-06-11 03:16:49 +02:00
WriteStatus ( "" ) ;
WriteStatus ( "Special thanks to:" ) ;
WriteStatus ( " * Crediar for his wadmaker tool + source, and for the advice!" ) ;
2009-06-19 03:57:52 +02:00
WriteStatus ( " * SquidMan/Galaxy/comex/Xuzz for advice/sources." ) ;
2009-06-13 18:04:54 +02:00
WriteStatus ( " * Pasta for database compilation assistance." ) ;
2009-07-09 04:11:22 +02:00
WriteStatus ( " * #WiiDev for answering the tough questions." ) ;
WriteStatus ( " * Anyone who helped beta test on GBATemp!" ) ;
WriteStatus ( " * Famfamfam for the Silk Icon Set." ) ;
2009-06-11 03:16:49 +02:00
}
2009-07-17 18:22:58 +02:00
2009-06-11 03:16:49 +02:00
private void getcerts_Click ( object sender , EventArgs e )
{
// Get a certs.sys from NUS...
2009-08-07 22:06:34 +02:00
/ *
2009-06-11 03:16:49 +02:00
// Directory stuff
string currentdir = Application . StartupPath ;
if ( currentdir . EndsWith ( Convert . ToString ( Path . DirectorySeparatorChar ) ) = = false )
currentdir + = Path . DirectorySeparatorChar ;
// Create certs file
2009-07-17 18:22:58 +02:00
FileStream certsfs = new FileStream ( currentdir + @"\cert.sys" , FileMode . Create ) ;
2009-06-11 03:16:49 +02:00
// Getting it from SystemMenu 3.2
DownloadNUSFile ( "0000000100000002" , "tmd.289" , currentdir + @"\" , 0 , true ) ;
DownloadNUSFile ( "0000000100000002" , "cetk" , currentdir + @"\" , 0 , true ) ;
// Create ticket file holder
2009-07-10 02:56:17 +02:00
byte [ ] cetkbuf = FileLocationToByteArray ( currentdir + "cetk" ) ;
2009-06-11 03:16:49 +02:00
// Create tmd file holder
2009-07-10 02:56:17 +02:00
byte [ ] tmdbuf = FileLocationToByteArray ( currentdir + "tmd.289" ) ;
2009-06-11 03:16:49 +02:00
// Write CA cert...
certsfs . Seek ( 0 , SeekOrigin . Begin ) ;
certsfs . Write ( tmdbuf , 0x628 , 0x400 ) ;
WriteStatus ( "Added CA Cert!" ) ;
// Write CACP cert...
certsfs . Seek ( 0x400 , SeekOrigin . Begin ) ;
certsfs . Write ( tmdbuf , 0x328 , 0x300 ) ;
WriteStatus ( "Added CACP Cert!" ) ;
// Write CAXS cert...
certsfs . Seek ( 0x700 , SeekOrigin . Begin ) ;
certsfs . Write ( cetkbuf , 0x2A4 , 0x300 ) ;
WriteStatus ( "Added CAXS Cert!" ) ;
certsfs . Close ( ) ;
// Hash check the cert.sys...
if ( verifyMd5Hash ( currentdir + @"\cert.sys" , certs_MD5 ) = = true )
{
WriteStatus ( "Certs File Successfully Created!" ) ;
}
else
{
WriteStatus ( "Error in Creating Certs File!" ) ;
WriteStatus ( "Please report this error if you are sure it is not your internet connection" ) ;
}
// Re-enable controls...
foreach ( Control ctrl in this . Controls )
{
ctrl . Enabled = true ;
}
getcerts . Visible = false ;
wadnamebox . Enabled = false ;
// Cleanup...
File . Delete ( currentdir + "cetk" ) ;
File . Delete ( currentdir + "tmd.289" ) ;
2009-08-07 22:06:34 +02:00
* /
2009-06-11 03:16:49 +02:00
}
2009-08-07 22:06:34 +02:00
/ *
2009-06-11 03:16:49 +02:00
static string getMd5Hash ( string input )
{
System . Text . StringBuilder sb = new System . Text . StringBuilder ( ) ;
FileStream fs = new FileStream ( input , FileMode . Open ) ;
MD5 md5 = new MD5CryptoServiceProvider ( ) ;
byte [ ] hash = md5 . ComputeHash ( fs ) ;
fs . Close ( ) ;
fs . Dispose ( ) ;
foreach ( byte hex in hash )
{
//Returns hash in lower case.
sb . Append ( hex . ToString ( "x2" ) ) ;
}
return sb . ToString ( ) ;
}
// Verify a hash against a string.
static bool verifyMd5Hash ( string input , string hash )
{
// Hash the input.
string hashOfInput = getMd5Hash ( input ) ;
// Create a StringComparer an comare the hashes.
StringComparer comparer = StringComparer . OrdinalIgnoreCase ;
if ( 0 = = comparer . Compare ( hashOfInput , hash ) )
{
return true ;
}
else
{
return false ;
}
}
2009-08-07 22:06:34 +02:00
* /
2009-06-11 03:16:49 +02:00
private void packbox_CheckedChanged ( object sender , EventArgs e )
{
if ( packbox . Checked = = true )
{
wadnamebox . Enabled = true ;
// Change WAD name if applicable
2009-07-10 02:56:17 +02:00
UpdatePackedName ( ) ;
2009-06-11 03:16:49 +02:00
}
else
{
wadnamebox . Enabled = false ;
wadnamebox . Text = "" ;
}
}
private void titleidbox_TextChanged ( object sender , EventArgs e )
{
2009-07-10 02:56:17 +02:00
UpdatePackedName ( ) ;
2009-06-11 03:16:49 +02:00
}
private void titleversion_TextChanged ( object sender , EventArgs e )
{
2009-07-10 02:56:17 +02:00
UpdatePackedName ( ) ;
2009-06-11 03:16:49 +02:00
}
public void initCrypt ( byte [ ] iv , byte [ ] key )
{
rijndaelCipher = new RijndaelManaged ( ) ;
rijndaelCipher . Mode = CipherMode . CBC ;
rijndaelCipher . Padding = PaddingMode . None ;
rijndaelCipher . KeySize = 128 ;
rijndaelCipher . BlockSize = 128 ;
rijndaelCipher . Key = key ;
rijndaelCipher . IV = iv ;
}
public byte [ ] Encrypt ( byte [ ] plainBytes )
{
ICryptoTransform transform = rijndaelCipher . CreateEncryptor ( ) ;
using ( MemoryStream ms = new MemoryStream ( plainBytes ) )
{
using ( CryptoStream cs = new CryptoStream ( ms , transform , CryptoStreamMode . Read ) )
{
return ReadFully ( cs ) ;
}
}
}
public byte [ ] Decrypt ( byte [ ] encryptedData )
{
ICryptoTransform transform = rijndaelCipher . CreateDecryptor ( ) ;
using ( MemoryStream ms = new MemoryStream ( encryptedData ) )
{
using ( CryptoStream cs = new CryptoStream ( ms , transform , CryptoStreamMode . Read ) )
{
return ReadFully ( cs ) ;
}
}
}
public byte [ ] ReadFully ( Stream stream )
{
byte [ ] buffer = new byte [ 32768 ] ;
using ( MemoryStream ms = new MemoryStream ( ) )
{
while ( true )
{
int read = stream . Read ( buffer , 0 , buffer . Length ) ;
if ( read < = 0 )
return ms . ToArray ( ) ;
ms . Write ( buffer , 0 , read ) ;
}
}
}
2009-07-10 02:56:17 +02:00
public string DisplayBytes ( byte [ ] bytes , string spacer )
2009-06-11 03:16:49 +02:00
{
string output = "" ;
for ( int i = 0 ; i < bytes . Length ; + + i )
{
2009-07-10 02:56:17 +02:00
output + = bytes [ i ] . ToString ( "X2" ) + spacer ;
2009-06-11 03:16:49 +02:00
}
return output ;
}
static public byte [ ] ComputeSHA ( byte [ ] data )
{
SHA1 sha = new SHA1CryptoServiceProvider ( ) ;
// This is one implementation of the abstract class SHA1.
return sha . ComputeHash ( data ) ;
}
public byte [ ] LoadCommonKey ( string keyfile )
{
// Directory stuff
string currentdir = Application . StartupPath ;
if ( ! ( currentdir . EndsWith ( @"\" ) ) | | ! ( currentdir . EndsWith ( @"/" ) ) )
currentdir + = @"\" ;
if ( File . Exists ( currentdir + keyfile ) = = true )
{
2009-07-10 02:56:17 +02:00
// Read common key byte[]
return FileLocationToByteArray ( currentdir + keyfile ) ;
2009-06-11 03:16:49 +02:00
}
else
return null ;
}
private void button4_Click ( object sender , EventArgs e )
{
// Open Database button menu...
databaseStrip . Show ( databaseButton , 2 , 2 ) ;
}
private void ClearDatabaseStrip ( )
{
SystemMenuList . DropDownItems . Clear ( ) ;
IOSMenuList . DropDownItems . Clear ( ) ;
WiiWareMenuList . DropDownItems . Clear ( ) ;
// VC Games Sections...
C64MenuList . DropDownItems . Clear ( ) ;
NeoGeoMenuList . DropDownItems . Clear ( ) ;
NESMenuList . DropDownItems . Clear ( ) ;
SNESMenuList . DropDownItems . Clear ( ) ;
N64MenuList . DropDownItems . Clear ( ) ;
TurboGrafx16MenuList . DropDownItems . Clear ( ) ;
TurboGrafxCDMenuList . DropDownItems . Clear ( ) ;
MSXMenuList . DropDownItems . Clear ( ) ;
SegaMSMenuList . DropDownItems . Clear ( ) ;
GenesisMenuList . DropDownItems . Clear ( ) ;
VCArcadeMenuList . DropDownItems . Clear ( ) ;
}
private void FillDatabaseStrip ( )
{
XmlDocument xDoc = new XmlDocument ( ) ;
xDoc . Load ( "database.xml" ) ;
// Variables
string [ ] XMLNodeTypes = new string [ 4 ] { "SYS" , "IOS" , "VC" , "WW" } ;
// Loop through XMLNodeTypes
for ( int i = 0 ; i < XMLNodeTypes . Length ; i + + )
{
XmlNodeList XMLSpecificNodeTypeList = xDoc . GetElementsByTagName ( XMLNodeTypes [ i ] ) ;
for ( int x = 0 ; x < XMLSpecificNodeTypeList . Count ; x + + )
{
ToolStripMenuItem XMLToolStripItem = new ToolStripMenuItem ( ) ;
XmlAttributeCollection XMLAttributes = XMLSpecificNodeTypeList [ x ] . Attributes ;
string titleID = "" ;
string descname = "" ;
2009-07-09 04:11:22 +02:00
bool dangerous = false ;
bool ticket = true ;
2009-06-11 03:16:49 +02:00
// Lol.
XmlNodeList ChildrenOfTheNode = XMLSpecificNodeTypeList [ x ] . ChildNodes ;
for ( int z = 0 ; z < ChildrenOfTheNode . Count ; z + + )
{
switch ( ChildrenOfTheNode [ z ] . Name )
{
case "name" :
descname = ChildrenOfTheNode [ z ] . InnerText ;
break ;
case "titleID" :
titleID = ChildrenOfTheNode [ z ] . InnerText ;
break ;
case "version" :
string [ ] versions = ChildrenOfTheNode [ z ] . InnerText . Split ( ',' ) ;
2009-07-21 23:53:27 +02:00
// Add to region things?
if ( XMLToolStripItem . DropDownItems . Count > 0 )
{
for ( int b = 0 ; b < XMLToolStripItem . DropDownItems . Count ; b + + )
{
if ( ChildrenOfTheNode [ z ] . InnerText ! = "" )
{
ToolStripMenuItem regitem = ( ToolStripMenuItem ) XMLToolStripItem . DropDownItems [ b ] ;
regitem . DropDownItems . Add ( "Latest Version" ) ;
for ( int y = 0 ; y < versions . Length ; y + + )
{
regitem . DropDownItems . Add ( "v" + versions [ y ] ) ;
}
2009-08-01 19:48:32 +02:00
regitem . DropDownItemClicked + = new ToolStripItemClickedEventHandler ( deepitem_clicked ) ;
2009-07-21 23:53:27 +02:00
}
}
}
else
2009-06-11 03:16:49 +02:00
{
2009-07-21 23:53:27 +02:00
XMLToolStripItem . DropDownItems . Add ( "Latest Version" ) ;
if ( ChildrenOfTheNode [ z ] . InnerText ! = "" )
2009-06-11 03:16:49 +02:00
{
2009-07-21 23:53:27 +02:00
for ( int y = 0 ; y < versions . Length ; y + + )
{
XMLToolStripItem . DropDownItems . Add ( "v" + versions [ y ] ) ;
}
2009-06-11 03:16:49 +02:00
}
}
break ;
case "region" :
string [ ] regions = ChildrenOfTheNode [ z ] . InnerText . Split ( ',' ) ;
if ( ChildrenOfTheNode [ z ] . InnerText ! = "" )
{
for ( int y = 0 ; y < regions . Length ; y + + )
{
XMLToolStripItem . DropDownItems . Add ( RegionFromIndex ( Convert . ToInt32 ( regions [ y ] ) , xDoc ) ) ;
}
}
break ;
default :
break ;
case "ticket" :
2009-07-09 04:11:22 +02:00
ticket = Convert . ToBoolean ( ChildrenOfTheNode [ z ] . InnerText ) ;
break ;
case "danger" :
dangerous = true ;
2009-07-10 02:56:17 +02:00
XMLToolStripItem . ToolTipText = ChildrenOfTheNode [ z ] . InnerText ;
2009-06-11 03:16:49 +02:00
break ;
}
2009-07-09 04:11:22 +02:00
XMLToolStripItem . Image = SelectItemImage ( ticket , dangerous ) ;
2009-07-14 21:38:18 +02:00
2009-07-09 04:11:22 +02:00
XMLToolStripItem . Text = String . Format ( "{0} - {1}" , titleID , descname ) ;
2009-06-11 03:16:49 +02:00
}
AddToolStripItemToStrip ( i , XMLToolStripItem , XMLAttributes ) ;
}
}
}
void AddToolStripItemToStrip ( int type , ToolStripMenuItem additionitem , XmlAttributeCollection attributes )
{
// Deal with VC list depth...
if ( type = = 2 )
{
switch ( attributes [ 0 ] . Value )
{
2009-07-09 04:11:22 +02:00
case "C64" :
2009-06-11 03:16:49 +02:00
C64MenuList . DropDownItems . Add ( additionitem ) ;
break ;
2009-07-09 04:11:22 +02:00
case "NEO" :
2009-06-11 03:16:49 +02:00
NeoGeoMenuList . DropDownItems . Add ( additionitem ) ;
break ;
case "NES" :
NESMenuList . DropDownItems . Add ( additionitem ) ;
break ;
case "SNES" :
SNESMenuList . DropDownItems . Add ( additionitem ) ;
break ;
case "N64" :
N64MenuList . DropDownItems . Add ( additionitem ) ;
break ;
2009-07-09 04:11:22 +02:00
case "TG16" :
2009-06-11 03:16:49 +02:00
TurboGrafx16MenuList . DropDownItems . Add ( additionitem ) ;
break ;
2009-07-09 04:11:22 +02:00
case "TGCD" :
2009-06-11 03:16:49 +02:00
TurboGrafxCDMenuList . DropDownItems . Add ( additionitem ) ;
break ;
case "MSX" :
MSXMenuList . DropDownItems . Add ( additionitem ) ;
break ;
case "SMS" :
SegaMSMenuList . DropDownItems . Add ( additionitem ) ;
break ;
2009-07-09 04:11:22 +02:00
case "GEN" :
2009-06-11 03:16:49 +02:00
GenesisMenuList . DropDownItems . Add ( additionitem ) ;
break ;
2009-07-09 04:11:22 +02:00
case "ARC" :
2009-06-11 03:16:49 +02:00
VCArcadeMenuList . DropDownItems . Add ( additionitem ) ;
break ;
default :
break ;
}
additionitem . DropDownItemClicked + = new ToolStripItemClickedEventHandler ( wwitem_regionclicked ) ;
}
else
{
// Add SYS, IOS, WW items
// I thought using index would work in .Items, but I
// guess this switch will have to do...
switch ( type )
{
case 0 :
SystemMenuList . DropDownItems . Add ( additionitem ) ;
break ;
case 1 :
IOSMenuList . DropDownItems . Add ( additionitem ) ;
break ;
case 3 :
WiiWareMenuList . DropDownItems . Add ( additionitem ) ;
break ;
}
additionitem . DropDownItemClicked + = new ToolStripItemClickedEventHandler ( sysitem_versionclicked ) ;
}
}
2009-08-01 19:48:32 +02:00
void deepitem_clicked ( object sender , ToolStripItemClickedEventArgs e )
{
titleidbox . Text = e . ClickedItem . OwnerItem . OwnerItem . Text . Substring ( 0 , 16 ) ;
titleidbox . Text = titleidbox . Text . Replace ( "XX" , e . ClickedItem . OwnerItem . Text . Substring ( 0 , 2 ) ) ;
if ( e . ClickedItem . Text ! = "Latest Version" )
{
if ( e . ClickedItem . Text . Contains ( "v" ) )
{
if ( e . ClickedItem . Text . Contains ( " " ) )
titleversion . Text = e . ClickedItem . Text . Substring ( 1 , e . ClickedItem . Text . IndexOf ( ' ' ) - 1 ) ;
else
titleversion . Text = e . ClickedItem . Text . Substring ( 1 , e . ClickedItem . Text . Length - 1 ) ;
}
}
else
{
titleversion . Text = "" ;
}
// Prepare StatusBox...
string titlename = e . ClickedItem . OwnerItem . OwnerItem . Text . Substring ( 19 , ( e . ClickedItem . OwnerItem . OwnerItem . Text . Length - 19 ) ) ;
statusbox . Text = " --- " + titlename + " ---" ;
// Check if a ticket is present...
if ( ( e . ClickedItem . OwnerItem . OwnerItem . Image ) = = ( orange ) | | ( e . ClickedItem . OwnerItem . OwnerItem . Image ) = = ( redorange ) )
{
ignoreticket . Checked = true ;
WriteStatus ( "Note: This title has no ticket and cannot be packed/decrypted!" ) ;
packbox . Checked = false ;
decryptbox . Checked = false ;
}
else
{
ignoreticket . Checked = false ;
}
// Change WAD name if packed is already checked...
if ( packbox . Checked )
{
if ( titlename . Contains ( "IOS" ) )
wadnamebox . Text = titlename + "-64-[v].wad" ;
else
wadnamebox . Text = titlename + "-NUS-[v].wad" ;
if ( titleversion . Text ! = "" )
wadnamebox . Text = wadnamebox . Text . Replace ( "[v]" , "v" + titleversion . Text ) ;
}
// Check for danger item
if ( ( e . ClickedItem . OwnerItem . OwnerItem . Image ) = = ( redgreen ) | | ( e . ClickedItem . OwnerItem . OwnerItem . Image ) = = ( redorange ) )
{
WriteStatus ( "\r\n" + e . ClickedItem . OwnerItem . OwnerItem . ToolTipText ) ;
}
}
2009-06-11 03:16:49 +02:00
void wwitem_regionclicked ( object sender , ToolStripItemClickedEventArgs e )
{
titleidbox . Text = e . ClickedItem . OwnerItem . Text . Substring ( 0 , 16 ) ;
titleversion . Text = "" ;
titleidbox . Text = titleidbox . Text . Replace ( "XX" , e . ClickedItem . Text . Substring ( 0 , 2 ) ) ;
2009-07-09 04:11:22 +02:00
// Prepare StatusBox...
2009-07-10 02:56:17 +02:00
string titlename = e . ClickedItem . OwnerItem . Text . Substring ( 19 , ( e . ClickedItem . OwnerItem . Text . Length - 19 ) ) ;
statusbox . Text = " --- " + titlename + " ---" ;
2009-07-09 04:11:22 +02:00
// Check if a ticket is present...
if ( ( e . ClickedItem . OwnerItem . Image ) = = ( orange ) | | ( e . ClickedItem . OwnerItem . Image ) = = ( redorange ) )
2009-06-11 03:16:49 +02:00
{
ignoreticket . Checked = true ;
WriteStatus ( "Note: This title has no ticket and cannot be packed/decrypted!" ) ;
packbox . Checked = false ;
decryptbox . Checked = false ;
}
else
{
ignoreticket . Checked = false ;
}
2009-07-09 04:11:22 +02:00
2009-07-10 02:56:17 +02:00
// Change WAD name if packed is already checked...
if ( packbox . Checked )
{
if ( titlename . Contains ( "IOS" ) )
wadnamebox . Text = titlename + "-64-[v].wad" ;
else
wadnamebox . Text = titlename + "-NUS-[v].wad" ;
if ( titleversion . Text ! = "" )
wadnamebox . Text = wadnamebox . Text . Replace ( "[v]" , "v" + titleversion . Text ) ;
}
2009-07-09 04:11:22 +02:00
// Check for danger item
if ( ( e . ClickedItem . OwnerItem . Image ) = = ( redgreen ) | | ( e . ClickedItem . OwnerItem . Image ) = = ( redorange ) )
{
WriteStatus ( "\r\n" + e . ClickedItem . OwnerItem . ToolTipText ) ;
}
2009-06-11 03:16:49 +02:00
}
void sysitem_versionclicked ( object sender , ToolStripItemClickedEventArgs e )
{
titleidbox . Text = e . ClickedItem . OwnerItem . Text . Substring ( 0 , 16 ) ;
2009-07-21 23:53:27 +02:00
2009-06-11 03:16:49 +02:00
if ( e . ClickedItem . Text ! = "Latest Version" )
{
if ( e . ClickedItem . Text . Contains ( "v" ) )
{
if ( e . ClickedItem . Text . Contains ( " " ) )
2009-07-17 18:22:58 +02:00
titleversion . Text = e . ClickedItem . Text . Substring ( 1 , e . ClickedItem . Text . IndexOf ( ' ' ) - 1 ) ;
2009-06-11 03:16:49 +02:00
else
titleversion . Text = e . ClickedItem . Text . Substring ( 1 , e . ClickedItem . Text . Length - 1 ) ;
}
else
{
2009-07-17 18:22:58 +02:00
// Apparently it's a region code..
2009-06-11 03:16:49 +02:00
titleidbox . Text = titleidbox . Text . Replace ( "XX" , e . ClickedItem . Text . Substring ( 0 , 2 ) ) ;
titleversion . Text = "" ;
}
}
else
{
titleversion . Text = "" ;
}
2009-07-09 04:11:22 +02:00
// Prepare StatusBox...
2009-07-10 02:56:17 +02:00
string titlename = e . ClickedItem . OwnerItem . Text . Substring ( 19 , ( e . ClickedItem . OwnerItem . Text . Length - 19 ) ) ;
statusbox . Text = " --- " + titlename + " ---" ;
2009-07-09 04:11:22 +02:00
if ( ( e . ClickedItem . OwnerItem . Image ) = = ( orange ) | | ( e . ClickedItem . OwnerItem . Image ) = = ( redorange ) )
2009-06-11 03:16:49 +02:00
{
ignoreticket . Checked = true ;
WriteStatus ( "Note: This title has no ticket and cannot be packed/decrypted!" ) ;
packbox . Checked = false ;
decryptbox . Checked = false ;
}
else
{
ignoreticket . Checked = false ;
}
2009-07-21 23:53:27 +02:00
2009-07-10 02:56:17 +02:00
// Change WAD name if packed is already checked...
if ( packbox . Checked )
{
if ( titlename . Contains ( "IOS" ) )
wadnamebox . Text = titlename + "-64-[v].wad" ;
else
wadnamebox . Text = titlename + "-NUS-[v].wad" ;
if ( titleversion . Text ! = "" )
wadnamebox . Text = wadnamebox . Text . Replace ( "[v]" , "v" + titleversion . Text ) ;
}
2009-07-09 04:11:22 +02:00
// Check for danger item
if ( ( e . ClickedItem . OwnerItem . Image ) = = ( redgreen ) | | ( e . ClickedItem . OwnerItem . Image ) = = ( redorange ) )
{
WriteStatus ( "\n" + e . ClickedItem . OwnerItem . ToolTipText ) ;
}
2009-06-11 03:16:49 +02:00
}
void HandleMismatch ( int contentsize , int actualsize )
{
if ( contentsize ! = actualsize )
{
if ( ( contentsize - actualsize ) > 16 )
{
2009-07-21 23:53:27 +02:00
statusbox . Text + = " (BAD Mismatch) (Dif: " + ( contentsize - actualsize ) ;
2009-06-11 03:16:49 +02:00
}
else
{
statusbox . Text + = " (Safe Mismatch)" ;
}
}
}
string RegionFromIndex ( int index , XmlDocument databasexml )
{
/ * Typical Region XML
* < REGIONS >
2009-06-13 18:04:54 +02:00
< region index = "0" > 41 ( All / System ) < / region >
2009-06-11 03:16:49 +02:00
< region index = 1 > 44 ( German ) < / region >
< region index = 2 > 45 ( USA / NTSC ) < / region >
< region index = 3 > 46 ( French ) < / region >
< region index = 4 > 4 A ( Japan ) < / region >
< region index = 5 > 4 B ( Korea ) < / region >
< region index = 6 > 4 C ( Japanese Import to Europe / Australia / PAL ) < / region >
< region index = 7 > 4D ( American Import to Europe / Australia / PAL ) < / region >
< region index = 8 > 4 E ( Japanese Import to USA / NTSC ) < / region >
< region index = 9 > 50 ( Europe / PAL ) < / region >
< region index = 10 > 51 ( Korea w / Japanese Language ) < / region >
< region index = 11 > 54 ( Korea w / English Language ) < / region >
< region index = 12 > 58 ( Some Homebrew ) < / region >
< / REGIONS >
* /
XmlNodeList XMLRegionList = databasexml . GetElementsByTagName ( "REGIONS" ) ;
XmlNodeList ChildrenOfTheNode = XMLRegionList [ 0 ] . ChildNodes ;
// For each child node (region node)
for ( int z = 0 ; z < ChildrenOfTheNode . Count ; z + + )
{
// Gather attributes (index='x')
XmlAttributeCollection XMLAttributes = ChildrenOfTheNode [ z ] . Attributes ;
// Return value of node if index matches
if ( Convert . ToInt32 ( XMLAttributes [ 0 ] . Value ) = = index )
return ChildrenOfTheNode [ z ] . InnerText ;
}
return "XX (Error)" ;
}
private void LoadRegionCodes ( )
{
XmlDocument xDoc = new XmlDocument ( ) ;
xDoc . Load ( "database.xml" ) ;
XmlNodeList XMLRegionList = xDoc . GetElementsByTagName ( "REGIONS" ) ;
XmlNodeList ChildrenOfTheNode = XMLRegionList [ 0 ] . ChildNodes ;
// For each child node (region node)
for ( int z = 0 ; z < ChildrenOfTheNode . Count ; z + + )
{
RegionCodesList . DropDownItems . Add ( ChildrenOfTheNode [ z ] . InnerText ) ;
}
}
private void RegionCodesList_DropDownItemClicked ( object sender , ToolStripItemClickedEventArgs e )
{
if ( titleidbox . Text . Length = = 16 )
titleidbox . Text = titleidbox . Text . Substring ( 0 , 14 ) + e . ClickedItem . Text . Substring ( 0 , 2 ) ;
}
2009-06-17 00:25:19 +02:00
2009-07-14 21:38:18 +02:00
private string RemoveIllegalCharacters ( string databasestr )
{
// Database strings must contain filename-legal characters.
foreach ( char illegalchar in System . IO . Path . GetInvalidFileNameChars ( ) )
{
if ( databasestr . Contains ( illegalchar . ToString ( ) ) )
databasestr = databasestr . Replace ( illegalchar , '-' ) ;
}
return databasestr ;
}
2009-06-17 00:25:19 +02:00
private byte [ ] ZeroSignature ( byte [ ] tmdortik )
{
// Write all 0x00 to signature...
// Sig starts at 0x04 in both TMD/TIK
for ( int i = 0 ; i < 256 ; i + + )
{
tmdortik [ i + 4 ] = 0x00 ;
}
WriteStatus ( " - Signature Emptied..." ) ;
return tmdortik ;
}
private byte [ ] TruchaSign ( byte [ ] tmdortik )
{
// Loop through 2 bytes worth of numbers until hash starts with 0x00...
// Padding starts at 0x104 in both TMD/TIK, seems like a good place to me...
byte [ ] payload = new byte [ 2 ] ;
byte [ ] hashobject = new byte [ tmdortik . Length - 0x104 ] ;
for ( int i = 0 ; i < 65535 ; i + + )
{
payload = incrementAtIndex ( payload , 1 ) ;
tmdortik [ 0x104 ] = payload [ 0 ] ;
tmdortik [ 0x105 ] = payload [ 1 ] ;
for ( int x = 0 ; x < ( tmdortik . Length - 0x104 ) ; x + + )
{
hashobject [ x ] = tmdortik [ 0x104 + x ] ;
}
if ( ComputeSHA ( hashobject ) [ 0 ] = = 0x00 )
{
WriteStatus ( " - Successfully Trucha Signed." ) ;
return tmdortik ;
}
}
WriteStatus ( " - Sign FAIL!" ) ;
return tmdortik ;
}
static public byte [ ] incrementAtIndex ( byte [ ] array , int index )
{
if ( array [ index ] = = byte . MaxValue )
{
array [ index ] = 0 ;
if ( index > 0 )
incrementAtIndex ( array , index - 1 ) ;
}
else
{
array [ index ] + + ;
}
return array ;
}
2009-06-19 03:57:52 +02:00
private void button6_Click ( object sender , EventArgs e )
{
// Revert to TMD information...
string [ ] fileinfo = shamelessvariablelabel . Text . Split ( ',' ) ;
// Read the tmd as a stream...
2009-07-10 02:56:17 +02:00
byte [ ] tmd = FileLocationToByteArray ( fileinfo [ 0 ] + fileinfo [ 1 ] ) ;
2009-06-19 03:57:52 +02:00
// Read information from TMD into signing GUI...
requiredIOSbox . Text = Convert . ToString ( tmd [ 0x18B ] ) ;
2009-07-08 01:41:38 +02:00
// Lulzy cheap way of getting version... *256
tmdversiontrucha . Text = Convert . ToString ( ( ( tmd [ 0x1DC ] * 256 ) + tmd [ 0x1DD ] ) ) ;
2009-06-19 03:57:52 +02:00
newtitleidbox . Text = titleidbox . Text ;
2009-07-04 20:32:52 +02:00
// Setup for NO IOS
if ( requiredIOSbox . Text = = "0" )
requiredIOSbox . Enabled = false ;
else
requiredIOSbox . Enabled = true ;
2009-06-19 03:57:52 +02:00
}
private void button5_Click ( object sender , EventArgs e )
{
// Revert to Ticket information...
string [ ] fileinfo = shamelessvariablelabel . Text . Split ( ',' ) ;
// Create ticket file holder
2009-07-10 02:56:17 +02:00
byte [ ] cetkbuff = FileLocationToByteArray ( fileinfo [ 0 ] + @"\cetk" ) ;
2009-06-19 03:57:52 +02:00
// Titlekey
byte [ ] titlekey = new byte [ 16 ] ;
for ( int i = 0 ; i < 16 ; i + + )
{
titlekey [ i ] = cetkbuff [ 0x1BF + i ] ;
}
titlekeybox . Text = System . Text . Encoding . UTF7 . GetString ( titlekey ) ;
// IV (TITLEID+00000000s)
byte [ ] iv = new byte [ 16 ] ;
for ( int i = 0 ; i < 8 ; i + + )
{
iv [ i ] = cetkbuff [ 0x1DC + i ] ;
}
for ( int i = 0 ; i < 8 ; i + + )
{
iv [ i + 8 ] = 0x00 ;
}
2009-07-10 02:56:17 +02:00
titleIDIV . Text = DisplayBytes ( iv , "" ) ;
2009-06-19 03:57:52 +02:00
//DLC
2009-07-08 01:41:38 +02:00
dlcamntbox . Text = Convert . ToString ( ( cetkbuff [ 0x1E6 ] * 256 ) + cetkbuff [ 0x1E7 ] ) ;
2009-06-19 03:57:52 +02:00
//keyindex
if ( cetkbuff [ 0x1F1 ] = = 0x00 )
ckeyindexcb . SelectedIndex = 0 ;
else if ( cetkbuff [ 0x1F1 ] = = 0x01 )
ckeyindexcb . SelectedIndex = 1 ;
else
ckeyindexcb . SelectedIndex = 0 ;
//time enabled
if ( cetkbuff [ 0x247 ] = = 0x00 )
timelimitenabledcb . SelectedIndex = 0 ;
else if ( cetkbuff [ 0x247 ] = = 0x01 )
timelimitenabledcb . SelectedIndex = 1 ;
else
timelimitenabledcb . SelectedIndex = 0 ;
//time in seconds
byte [ ] timelimit = new byte [ 4 ] ;
for ( int i = 0 ; i < timelimit . Length ; i + + )
{
2009-07-04 20:32:52 +02:00
timelimit [ i ] = cetkbuff [ 0x248 + i ] ;
2009-06-19 03:57:52 +02:00
}
timelimitsecs . Text = Convert . ToString ( System . BitConverter . ToInt32 ( timelimit , 0 ) ) ;
}
private void button1_Click_1 ( object sender , EventArgs e )
{
// Write Trucha changes to TMD...
2009-07-04 20:32:52 +02:00
WriteStatus ( "Trucha Signing TMD..." ) ;
// Cheezy file info
string [ ] fileinfo = shamelessvariablelabel . Text . Split ( ',' ) ;
// Read the tmd as a stream...
2009-07-10 02:56:17 +02:00
byte [ ] tmd = FileLocationToByteArray ( fileinfo [ 0 ] + fileinfo [ 1 ] ) ;
2009-07-04 20:32:52 +02:00
// Resize to just TMD...
Array . Resize ( ref tmd , 484 + ( Convert . ToInt32 ( fileinfo [ 2 ] ) * 36 ) ) ;
// Change Required IOS
if ( requiredIOSbox . Text ! = "0" )
{
tmd [ 0x18B ] = Convert . ToByte ( requiredIOSbox . Text ) ;
}
// Change Title Version
byte [ ] version = new byte [ 2 ] ;
version = InttoByteArray ( Convert . ToInt32 ( tmdversiontrucha . Text ) , version . Length ) ;
tmd [ 0x1DC ] = version [ version . Length - 2 ] ;
tmd [ 0x1DD ] = version [ version . Length - 1 ] ;
// Change Title ID
tmd [ 0x18C ] = byte . Parse ( newtitleidbox . Text . Substring ( 0 , 2 ) , System . Globalization . NumberStyles . HexNumber ) ;
tmd [ 0x18D ] = byte . Parse ( newtitleidbox . Text . Substring ( 2 , 2 ) , System . Globalization . NumberStyles . HexNumber ) ;
tmd [ 0x18E ] = byte . Parse ( newtitleidbox . Text . Substring ( 4 , 2 ) , System . Globalization . NumberStyles . HexNumber ) ;
tmd [ 0x18F ] = byte . Parse ( newtitleidbox . Text . Substring ( 6 , 2 ) , System . Globalization . NumberStyles . HexNumber ) ;
tmd [ 0x190 ] = byte . Parse ( newtitleidbox . Text . Substring ( 8 , 2 ) , System . Globalization . NumberStyles . HexNumber ) ;
tmd [ 0x191 ] = byte . Parse ( newtitleidbox . Text . Substring ( 10 , 2 ) , System . Globalization . NumberStyles . HexNumber ) ;
tmd [ 0x192 ] = byte . Parse ( newtitleidbox . Text . Substring ( 12 , 2 ) , System . Globalization . NumberStyles . HexNumber ) ;
tmd [ 0x193 ] = byte . Parse ( newtitleidbox . Text . Substring ( 14 , 2 ) , System . Globalization . NumberStyles . HexNumber ) ;
tmd = ZeroSignature ( tmd ) ;
tmd = TruchaSign ( tmd ) ;
2009-07-17 18:22:58 +02:00
FileStream testtmd = new FileStream ( fileinfo [ 0 ] + fileinfo [ 1 ] , FileMode . Open ) ;
2009-07-04 20:32:52 +02:00
testtmd . Write ( tmd , 0 , tmd . Length ) ;
testtmd . Close ( ) ;
}
private void button4_Click_1 ( object sender , EventArgs e )
{
// Write Trucha changes to Ticket...
WriteStatus ( "Trucha Signing Ticket..." ) ;
// Cheezy file info
string [ ] fileinfo = shamelessvariablelabel . Text . Split ( ',' ) ;
// Create ticket file holder
2009-07-10 02:56:17 +02:00
byte [ ] cetkbuff = FileLocationToByteArray ( fileinfo [ 0 ] + @"\cetk" ) ;
2009-07-04 20:32:52 +02:00
// Resize Ticket to actual size.
Array . Resize ( ref cetkbuff , 0x2A4 ) ;
// TODO: Title Key and IV changes!
2009-08-01 19:48:32 +02:00
WriteStatus ( "Title Key / IV are not available to change in this release :(" ) ;
2009-07-04 20:32:52 +02:00
// Write DLC Amount.
byte [ ] dlcamount = new byte [ 2 ] ;
dlcamount = InttoByteArray ( Convert . ToInt32 ( dlcamntbox . Text ) , dlcamount . Length ) ;
cetkbuff [ 0x1E6 ] = dlcamount [ dlcamount . Length - 2 ] ;
cetkbuff [ 0x1E7 ] = dlcamount [ dlcamount . Length - 1 ] ;
// Common Key index.
if ( ckeyindexcb . SelectedIndex = = 0 )
cetkbuff [ 0x1F1 ] = 0x00 ;
else if ( ckeyindexcb . SelectedIndex = = 1 )
cetkbuff [ 0x1F1 ] = 0x01 ;
else
cetkbuff [ 0x1F1 ] = 0x00 ;
// Time limit enable.
if ( timelimitenabledcb . SelectedIndex = = 0 )
cetkbuff [ 0x247 ] = 0x00 ;
else if ( timelimitenabledcb . SelectedIndex = = 1 )
cetkbuff [ 0x247 ] = 0x01 ;
else
cetkbuff [ 0x247 ] = 0x00 ;
// The amount of time for the limit.
byte [ ] limitseconds = new byte [ 4 ] ;
limitseconds = InttoByteArray ( Convert . ToInt32 ( timelimitsecs . Text ) , 4 ) ;
2009-08-04 02:01:01 +02:00
2009-07-04 20:32:52 +02:00
for ( int i = 0 ; i < 4 ; i + + )
{
cetkbuff [ 0x248 + i ] = limitseconds [ i ] ;
}
// Trucha (Fake) Sign
cetkbuff = ZeroSignature ( cetkbuff ) ;
cetkbuff = TruchaSign ( cetkbuff ) ;
// Write changes to cetk.
2009-07-17 18:22:58 +02:00
FileStream testtik = new FileStream ( fileinfo [ 0 ] + "cetk" , FileMode . Open ) ;
2009-07-04 20:32:52 +02:00
testtik . Write ( cetkbuff , 0 , cetkbuff . Length ) ;
testtik . Close ( ) ;
}
// C# to convert a string to a byte array.
public static byte [ ] StrToByteArray ( string str )
{
System . Text . ASCIIEncoding encoding = new System . Text . ASCIIEncoding ( ) ;
return encoding . GetBytes ( str ) ;
}
private void button7_Click ( object sender , EventArgs e )
{
// Proceed with process (BG worker waits for form to resize)
WriteStatus ( "Trucha modifications complete." ) ;
this . Size = this . MinimumSize ;
2009-06-19 03:57:52 +02:00
}
2009-07-04 20:32:52 +02:00
private void button3_Click_1 ( object sender , EventArgs e )
{
// Clear Statusbox.text
statusbox . Text = "" ;
}
2009-07-08 01:41:38 +02:00
private void SetEnableforDownload ( bool enabled )
{
2009-07-09 04:11:22 +02:00
// Disable things the user should not mess with during download...
2009-07-08 01:41:38 +02:00
downloadstartbtn . Enabled = enabled ;
titleidbox . Enabled = enabled ;
titleversion . Enabled = enabled ;
2009-08-06 04:37:39 +02:00
Extrasbtn . Enabled = enabled ;
2009-07-08 01:41:38 +02:00
databaseButton . Enabled = enabled ;
2009-07-09 04:11:22 +02:00
packbox . Enabled = enabled ;
localuse . Enabled = enabled ;
ignoreticket . Enabled = enabled ;
truchabox . Enabled = enabled ;
decryptbox . Enabled = enabled ;
}
private void ShowInnerToolTips ( bool enabled )
{
// Force tooltips to GTFO in sub menus...
foreach ( ToolStripItem item in databaseStrip . Items )
{
try
{
ToolStripMenuItem menuitem = ( ToolStripMenuItem ) item ;
menuitem . DropDown . ShowItemToolTips = false ;
}
catch ( Exception )
{
// Do nothing, some objects will not cast.
}
}
2009-07-08 01:41:38 +02:00
}
2009-07-09 04:11:22 +02:00
private System . Drawing . Image SelectItemImage ( bool ticket , bool danger )
{
// All is good, go green...
if ( ( ticket ) & & ( ! danger ) )
return green ;
// There's no ticket, but danger is clear...
if ( ( ! ticket ) & & ( ! danger ) )
return orange ;
// DANGER WILL ROBINSON...
if ( ( ticket ) & & ( danger ) )
return redgreen ;
// Double bad...
if ( ( ! ticket ) & & ( danger ) )
return redorange ;
return null ;
}
2009-07-10 02:56:17 +02:00
private byte [ ] FileLocationToByteArray ( string filename )
{
FileStream fs = File . OpenRead ( filename ) ;
byte [ ] filebytearray = ReadFully ( fs , 460 ) ;
fs . Close ( ) ;
return filebytearray ;
}
private void UpdatePackedName ( )
{
// Change WAD name if applicable
if ( ( titleidbox . Enabled = = true ) & & ( packbox . Checked = = true ) )
{
if ( titleversion . Text ! = "" )
{
wadnamebox . Text = titleidbox . Text + "-NUS-v" + titleversion . Text + ".wad" ;
}
else
{
wadnamebox . Text = titleidbox . Text + "-NUS-[v]" + titleversion . Text + ".wad" ;
}
}
2009-07-14 21:38:18 +02:00
wadnamebox . Text = RemoveIllegalCharacters ( wadnamebox . Text ) ;
}
// This is WIP code/theory...
private byte [ ] GenerateTicket ( byte [ ] EncTitleKey , byte [ ] TitleID )
{
byte [ ] Ticket = new byte [ 0x2A4 ] ;
// RSA Signature Heading...
Ticket [ 1 ] = 0x01 ; Ticket [ 3 ] = 0x01 ;
// Signature Issuer... (Root-CA00000001-XS00000003)
byte [ ] SignatureIssuer = new byte [ 0x1A ] { 0x52 , 0x6F , 0x6F , 0x74 , 0x2D , 0x43 , 0x41 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x31 , 0x2D , 0x58 , 0x53 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x33 } ;
for ( int a = 0 ; a < 0x40 ; a + + )
{
Ticket [ 0x140 + a ] = SignatureIssuer [ a ] ;
}
// Encrypted TitleKey...
for ( int b = 0 ; b < 0x10 ; b + + )
{
Ticket [ 0x1BF + b ] = EncTitleKey [ b ] ;
}
// Ticket ID...
for ( int c = 0 ; c < 0x08 ; c + + )
{
Ticket [ 0x1D0 + c ] = 0x49 ;
}
// Title ID...
for ( int d = 0 ; d < 0x08 ; d + + )
{
Ticket [ 0x1DC + d ] = TitleID [ d ] ;
}
// Misc FF...
Ticket [ 0x1E4 ] = 0xFF ; Ticket [ 0x1E5 ] = 0xFF ;
Ticket [ 0x1E6 ] = 0xFF ; Ticket [ 0x1E7 ] = 0xFF ;
// Unknown 0x01...
Ticket [ 0x221 ] = 0x01 ;
// Misc FF...
for ( int e = 0 ; e < 0x20 ; e + + )
{
Ticket [ 0x222 + e ] = 0xFF ;
}
return Ticket ;
}
private void button15_Click ( object sender , EventArgs e )
{
// Read Content info from TMD again (revert)
string [ ] fileinfo = shamelessvariablelabel . Text . Split ( ',' ) ;
// Read the tmd as a stream...
byte [ ] tmd = FileLocationToByteArray ( fileinfo [ 0 ] + fileinfo [ 1 ] ) ;
FillContentInfo ( tmd ) ;
}
private void FillContentInfo ( byte [ ] tmd )
{
// Clear anything existing...
contentsEdit . Items . Clear ( ) ;
// # of Contents and BootIndex
int nbr_cont = ContentCount ( tmd ) ;
2009-08-01 19:48:32 +02:00
int boot_idx = GetBootIndex ( tmd ) ;
2009-07-14 21:38:18 +02:00
string [ ] tmdcontents = GetContentNames ( tmd , nbr_cont ) ;
byte [ ] tmdindices = GetContentIndices ( tmd , nbr_cont ) ;
2009-07-17 18:22:58 +02:00
int [ ] tmdtypes = GetContentTypes ( tmd , nbr_cont ) ;
2009-07-14 21:38:18 +02:00
// Loop and add contents to listbox...
for ( int a = 0 ; a < nbr_cont ; a + + )
{
contentsEdit . Items . Add ( String . Format ( "[{0}] [{1}]" , tmdindices [ a ] , tmdcontents [ a ] ) ) ;
2009-07-17 18:22:58 +02:00
if ( tmdtypes [ a ] = = 0x8001 )
contentsEdit . Items [ a ] + = " [S]" ;
2009-07-14 21:38:18 +02:00
}
2009-07-17 18:22:58 +02:00
2009-07-14 21:38:18 +02:00
// Identify Boot Content...
contentsEdit . Items [ boot_idx ] + = " [BOOT]" ;
}
private void button8_Click ( object sender , EventArgs e )
{
// Move selected content upwards (down an index)...
2009-07-17 20:57:47 +02:00
if ( contentsEdit . SelectedIndex < = 0 )
2009-07-14 21:38:18 +02:00
return ;
2009-07-22 20:07:55 +02:00
int sel_index = contentsEdit . SelectedIndex ;
object sel_item = contentsEdit . SelectedItem ;
contentsEdit . Items . RemoveAt ( sel_index ) ;
contentsEdit . Items . Insert ( sel_index - 1 , sel_item ) ;
string sel_itm = contentsEdit . Items [ sel_index ] . ToString ( ) ;
string other_itm = contentsEdit . Items [ sel_index - 1 ] . ToString ( ) ;
sel_itm = sel_itm . Replace ( "[" , "" ) ; sel_itm = sel_itm . Replace ( "]" , "" ) ;
other_itm = other_itm . Replace ( "[" , "" ) ; other_itm = other_itm . Replace ( "]" , "" ) ;
string [ ] selarray = sel_itm . Split ( ' ' ) ;
string [ ] otherary = other_itm . Split ( ' ' ) ;
contentsEdit . Items [ sel_index ] = String . Format ( "[{0}]" , sel_index ) ;
contentsEdit . Items [ sel_index - 1 ] = String . Format ( "[{0}]" , sel_index - 1 ) ;
for ( int a = 0 ; a < selarray . Length ; a + + )
{
if ( a ! = 0 )
contentsEdit . Items [ sel_index ] + = String . Format ( " [{0}]" , selarray [ a ] ) ;
}
for ( int b = 0 ; b < otherary . Length ; b + + )
{
if ( b ! = 0 )
contentsEdit . Items [ sel_index - 1 ] + = String . Format ( " [{0}]" , otherary [ b ] ) ;
}
/ * int sel_idx = contentsEdit . SelectedIndex ;
2009-07-14 21:38:18 +02:00
string sel_item = contentsEdit . Items [ sel_idx ] . ToString ( ) ;
string lower_item = contentsEdit . Items [ sel_idx - 1 ] . ToString ( ) ;
2009-07-22 20:07:55 +02:00
contentsEdit . Items [ sel_idx ] = String . Format ( "[{0}]{1}" , sel_idx , lower_item . Substring ( contentsEdit . SelectedItem . ToString ( ) . IndexOf ( " [" ) , lower_item . Length - contentsEdit . SelectedItem . ToString ( ) . IndexOf ( " [" ) ) ) ;
contentsEdit . Items [ sel_idx - 1 ] = String . Format ( "[{0}]{1}" , sel_idx - 1 , sel_item . Substring ( contentsEdit . SelectedItem . ToString ( ) . IndexOf ( " [" ) , sel_item . Length - contentsEdit . SelectedItem . ToString ( ) . IndexOf ( " [" ) ) ) ;
* /
contentsEdit . SelectedIndex = sel_index - 1 ;
2009-07-14 21:38:18 +02:00
}
private void button9_Click ( object sender , EventArgs e )
{
// Move selected content down (up an index)...
if ( contentsEdit . SelectedIndex > = contentsEdit . Items . Count - 1 )
return ;
2009-07-22 20:07:55 +02:00
int sel_index = contentsEdit . SelectedIndex ;
object sel_item = contentsEdit . SelectedItem ;
contentsEdit . Items . RemoveAt ( sel_index ) ;
contentsEdit . Items . Insert ( sel_index + 1 , sel_item ) ;
string sel_itm = contentsEdit . Items [ sel_index ] . ToString ( ) ;
string other_itm = contentsEdit . Items [ sel_index + 1 ] . ToString ( ) ;
sel_itm = sel_itm . Replace ( "[" , "" ) ; sel_itm = sel_itm . Replace ( "]" , "" ) ;
other_itm = other_itm . Replace ( "[" , "" ) ; other_itm = other_itm . Replace ( "]" , "" ) ;
string [ ] selarray = sel_itm . Split ( ' ' ) ;
string [ ] otherary = other_itm . Split ( ' ' ) ;
contentsEdit . Items [ sel_index ] = String . Format ( "[{0}]" , sel_index ) ;
contentsEdit . Items [ sel_index + 1 ] = String . Format ( "[{0}]" , sel_index + 1 ) ;
for ( int a = 0 ; a < selarray . Length ; a + + )
{
if ( a ! = 0 )
contentsEdit . Items [ sel_index ] + = String . Format ( " [{0}]" , selarray [ a ] ) ;
}
for ( int b = 0 ; b < otherary . Length ; b + + )
{
if ( b ! = 0 )
contentsEdit . Items [ sel_index + 1 ] + = String . Format ( " [{0}]" , otherary [ b ] ) ;
}
/ * int sel_idx = contentsEdit . SelectedIndex ;
2009-07-14 21:38:18 +02:00
string sel_item = contentsEdit . Items [ sel_idx ] . ToString ( ) ;
string upper_item = contentsEdit . Items [ sel_idx + 1 ] . ToString ( ) ;
2009-07-22 20:07:55 +02:00
contentsEdit . Items [ sel_idx ] = String . Format ( "[{0}]{1}" , sel_idx , upper_item . Substring ( contentsEdit . SelectedItem . ToString ( ) . IndexOf ( " [" ) , upper_item . Length - contentsEdit . SelectedItem . ToString ( ) . IndexOf ( " [" ) ) ) ;
contentsEdit . Items [ sel_idx + 1 ] = String . Format ( "[{0}]{1}" , sel_idx + 1 , sel_item . Substring ( contentsEdit . SelectedItem . ToString ( ) . IndexOf ( " [" ) , sel_item . Length - contentsEdit . SelectedItem . ToString ( ) . IndexOf ( " [" ) ) ) ;
* /
contentsEdit . SelectedIndex = sel_index + 1 ;
2009-07-14 21:38:18 +02:00
}
private void button12_Click ( object sender , EventArgs e )
{
// Set a new boot index...
2009-07-22 20:07:55 +02:00
// Handle help info first...
if ( Control . ModifierKeys = = Keys . Shift )
{
WriteStatus ( "[HELP INFO] Select a content, and press this to make it the 'boot' content. The boot content is the" +
" DOL file which is ran when the channel is started." ) ;
return ;
}
2009-07-17 18:22:58 +02:00
if ( contentsEdit . SelectedIndex < 0 )
2009-07-14 21:38:18 +02:00
return ;
for ( int a = 0 ; a < contentsEdit . Items . Count ; a + + )
{
if ( contentsEdit . Items [ a ] . ToString ( ) . Contains ( " [BOOT]" ) )
contentsEdit . Items [ a ] = contentsEdit . Items [ a ] . ToString ( ) . Substring ( 0 , contentsEdit . Items [ a ] . ToString ( ) . Length - 7 ) ;
}
contentsEdit . Items [ contentsEdit . SelectedIndex ] + = " [BOOT]" ;
}
private void button11_Click ( object sender , EventArgs e )
{
// Add a file to the contents...
2009-07-22 20:07:55 +02:00
// Handle help info first...
if ( Control . ModifierKeys = = Keys . Shift )
{
WriteStatus ( "[HELP INFO] This button will allow you to add a content to the title. You can browse " +
"and select an .app file (a decrypted content) to insert into the contents list." ) ;
return ;
}
2009-07-14 21:38:18 +02:00
OpenFileDialog opencont = new OpenFileDialog ( ) ;
2009-07-17 18:22:58 +02:00
opencont . Filter = "Decrypted Contents|*.app|All Files|*" ;
2009-07-14 21:38:18 +02:00
opencont . Multiselect = false ;
opencont . Title = "Locate a Content" ;
if ( opencont . ShowDialog ( ) ! = DialogResult . Cancel )
{
2009-07-22 20:07:55 +02:00
// OK WE MUST PREVENT:
// - NON HEX NAMING
// - FILE EXISTING WITH THE SAME NAME && THAT FILE != NEW FILE
// -
2009-07-17 18:22:58 +02:00
if ( ( OnlyHexInString ( opencont . SafeFileName . Substring ( 0 , 8 ) ) = = false ) )
2009-07-14 21:38:18 +02:00
{
2009-07-17 18:22:58 +02:00
MessageBox . Show ( "Please locate/rename a file to be (8 HEX CHARACTERS) long + (.app) extention!" , "Bad!" , MessageBoxButtons . OK ) ;
2009-07-14 21:38:18 +02:00
return ;
}
for ( int i = 0 ; i < contentsEdit . Items . Count ; i + + )
{
if ( contentsEdit . Items [ i ] . ToString ( ) . Contains ( opencont . SafeFileName ) )
{
MessageBox . Show ( "A file already exists in the title with that filename!" , "Bad!" , MessageBoxButtons . OK ) ;
return ;
}
}
// D: TODO?
string [ ] fileinfo = shamelessvariablelabel . Text . Split ( ',' ) ;
2009-07-17 18:22:58 +02:00
if ( File . Exists ( fileinfo [ 0 ] + opencont . SafeFileName ) )
{
MessageBox . Show ( "Rename the file you are adding, it already exists in the title directory!" , "Bad!" , MessageBoxButtons . OK ) ;
return ;
}
2009-07-14 21:38:18 +02:00
if ( fileinfo [ 0 ] + opencont . SafeFileName ! = opencont . FileName )
{
// Move the file into the directory...
File . Copy ( opencont . FileName , fileinfo [ 0 ] + opencont . SafeFileName ) ;
}
contentsEdit . Items . Add ( String . Format ( "[{0}] [{1}]" , contentsEdit . Items . Count , opencont . SafeFileName ) ) ;
}
}
public bool OnlyHexInString ( string test )
{
return System . Text . RegularExpressions . Regex . IsMatch ( test , @"\A\b[0-9a-fA-F]+\b\Z" ) ;
}
private void button10_Click ( object sender , EventArgs e )
{
// Remove a content from the list...
2009-07-22 20:07:55 +02:00
// Handle help info first...
if ( Control . ModifierKeys = = Keys . Shift )
{
WriteStatus ( "[HELP INFO] This button will allow you to remove a content from the title. Be careful, " +
"as most contents are necessary for proper channel usage!" ) ;
return ;
}
2009-07-17 18:22:58 +02:00
if ( ( contentsEdit . SelectedIndex < 0 ) | | ( contentsEdit . Items . Count < = 1 ) )
2009-07-14 21:38:18 +02:00
return ;
string [ ] fileinfo = shamelessvariablelabel . Text . Split ( ',' ) ;
DialogResult question = MessageBox . Show ( "Delete the actual file as well?" , "Delete content?" , MessageBoxButtons . YesNoCancel ) ;
if ( question = = DialogResult . Yes )
File . Delete ( fileinfo [ 0 ] + contentsEdit . SelectedItem . ToString ( ) . Substring ( contentsEdit . SelectedItem . ToString ( ) . IndexOf ( "] [" ) + 3 , 8 ) ) ;
if ( question ! = DialogResult . Cancel )
contentsEdit . Items . RemoveAt ( contentsEdit . SelectedIndex ) ;
2009-07-22 20:07:55 +02:00
RecalculateIndices ( ) ;
2009-07-10 02:56:17 +02:00
}
2009-07-14 21:38:18 +02:00
2009-07-17 18:22:58 +02:00
private void button14_Click ( object sender , EventArgs e )
2009-08-02 20:12:05 +02:00
{
UpdateTMDContents ( ) ;
}
private void UpdateTMDContents ( )
2009-07-17 18:22:58 +02:00
{
// Write changes to TMD of contents...
WriteStatus ( "Updating TMD with content information..." ) ;
string [ ] fileinfo = shamelessvariablelabel . Text . Split ( ',' ) ;
2009-07-18 05:00:09 +02:00
WriteStatus ( " - Loading Title Key from Ticket..." ) ;
2009-07-17 18:22:58 +02:00
byte [ ] ticket = FileLocationToByteArray ( fileinfo [ 0 ] + "cetk" ) ;
byte [ ] etitlekey = new byte [ 16 ] ;
for ( int a = 0 ; a < 16 ; a + + )
{
etitlekey [ a ] = ticket [ 0x1BF + a ] ;
}
// TODO: Add more key support
byte [ ] commonkey = LoadCommonKey ( @"\key.bin" ) ;
// IV (TITLEID00000000)
byte [ ] iv = new byte [ 16 ] ;
for ( int b = 0 ; b < 8 ; b + + )
2009-08-02 20:12:05 +02:00
{
2009-07-17 18:22:58 +02:00
iv [ b ] = ticket [ 0x1DC + b ] ;
2009-08-02 20:12:05 +02:00
}
2009-07-17 18:22:58 +02:00
for ( int c = 0 ; c < 8 ; c + + )
2009-08-02 20:12:05 +02:00
{
iv [ c + 8 ] = 0x00 ;
}
2009-07-17 18:22:58 +02:00
initCrypt ( iv , commonkey ) ;
byte [ ] dtitlekey = Decrypt ( etitlekey ) ;
// Holds all the content data...
TitleContent [ ] contents = new TitleContent [ contentsEdit . Items . Count ] ;
// Previous TMD for analysis
byte [ ] tmd = FileLocationToByteArray ( fileinfo [ 0 ] + fileinfo [ 1 ] ) ;
for ( int c = 0 ; c < contentsEdit . Items . Count ; c + + )
{
string itemstr = contentsEdit . Items [ c ] . ToString ( ) ;
contents [ c ] = new TitleContent ( ) ;
2009-07-23 00:22:04 +02:00
// Set boot index...
if ( itemstr . Contains ( " [BOOT]" ) )
{
tmd = SetBootIndex ( tmd , c ) ;
}
2009-07-17 18:22:58 +02:00
if ( itemstr . Contains ( ".app" ) )
{
// This is already decrypted, we're going to add it to the TMD...
2009-07-22 20:07:55 +02:00
string filename = itemstr . Substring ( itemstr . IndexOf ( "] [" ) + 3 , 12 ) ;
2009-07-17 18:22:58 +02:00
byte [ ] contentbytes = FileLocationToByteArray ( fileinfo [ 0 ] + filename ) ;
2009-07-18 05:00:09 +02:00
WriteStatus ( filename + " is a decrypted file..." ) ;
2009-07-17 18:22:58 +02:00
WriteStatus ( " - Encrypting " + filename + "..." ) ;
// Gather the contentID (crappy way to do it)...
contents [ c ] . ContentID = new byte [ 4 ] ;
contents [ c ] . ContentID [ 0 ] = byte . Parse ( filename . Substring ( 0 , 2 ) , System . Globalization . NumberStyles . HexNumber ) ;
contents [ c ] . ContentID [ 1 ] = byte . Parse ( filename . Substring ( 2 , 2 ) , System . Globalization . NumberStyles . HexNumber ) ;
contents [ c ] . ContentID [ 2 ] = byte . Parse ( filename . Substring ( 4 , 2 ) , System . Globalization . NumberStyles . HexNumber ) ;
contents [ c ] . ContentID [ 3 ] = byte . Parse ( filename . Substring ( 6 , 2 ) , System . Globalization . NumberStyles . HexNumber ) ;
// Grab SHA1/size of file
contents [ c ] . SHAHash = new byte [ 20 ] ;
contents [ c ] . SHAHash = ComputeSHA ( contentbytes ) ;
contents [ c ] . Size = new byte [ 8 ] ;
// TODOCHECK THIS OVER
2009-07-21 23:53:27 +02:00
contents [ c ] . Size = NewIntegertoByteArray ( contentbytes . Length , 8 ) ;
2009-08-02 20:12:05 +02:00
2009-07-17 18:22:58 +02:00
contents [ c ] . Index = new byte [ 2 ] ;
2009-07-21 23:53:27 +02:00
contents [ c ] . Index = NewIntegertoByteArray ( c , 2 ) ;
2009-07-17 18:22:58 +02:00
contents [ c ] . Type = new byte [ 2 ] ;
contents [ c ] . Type [ 1 ] = 0x01 ;
if ( contentsEdit . Items [ c ] . ToString ( ) . Contains ( " [S]" ) )
contents [ c ] . Type [ 0 ] = 0x80 ;
else
contents [ c ] . Type [ 0 ] = 0x00 ;
// Pad to be 16 byte aligned
2009-07-17 20:57:47 +02:00
contentbytes = PadToMultipleOf ( contentbytes , 16 ) ;
2009-07-17 18:22:58 +02:00
// Encrypt with correct index IV/titlekey
byte [ ] ivindex = new byte [ 16 ] ;
for ( int d = 0 ; d < ivindex . Length ; d + + )
{
ivindex [ d ] = 0x00 ;
}
ivindex [ 0 ] = contents [ c ] . Index [ 0 ] ;
ivindex [ 1 ] = contents [ c ] . Index [ 1 ] ;
initCrypt ( ivindex , dtitlekey ) ;
FileStream encryptwrite = new FileStream ( fileinfo [ 0 ] + filename . Substring ( 0 , 8 ) , FileMode . Create ) ;
encryptwrite . Write ( Encrypt ( contentbytes ) , 0 , contentbytes . Length ) ;
encryptwrite . Close ( ) ;
WriteStatus ( " - " + filename . Substring ( 0 , 8 ) + " written!" ) ;
}
else
2009-08-02 20:12:05 +02:00
{
2009-07-18 05:00:09 +02:00
// An encrypted content...it was from the original TMD
2009-07-22 20:07:55 +02:00
string filename = itemstr . Substring ( itemstr . IndexOf ( "] [" ) + 3 , 8 ) ;
2009-07-17 18:22:58 +02:00
byte [ ] contentbytes = FileLocationToByteArray ( fileinfo [ 0 ] + filename ) ;
2009-07-18 05:00:09 +02:00
WriteStatus ( filename + " is encrypted and from the original TMD..." ) ;
WriteStatus ( " - Gathering " + filename + " information..." ) ;
2009-07-17 18:22:58 +02:00
// Grab previous values from TMD...
int nbr_cont = ContentCount ( tmd ) ;
string [ ] tmdoldcontents = GetContentNames ( tmd , nbr_cont ) ;
string [ ] tmdsizes = GetContentSizes ( tmd , nbr_cont ) ;
byte [ ] tmdhashes = GetContentHashes ( tmd , nbr_cont ) ;
int thiscontentidx = 0 ;
for ( int f = 0 ; f < nbr_cont ; f + + )
{
if ( tmdoldcontents [ f ] = = filename )
thiscontentidx = f ;
}
2009-07-18 05:00:09 +02:00
// if index has been changed...
2009-07-17 18:22:58 +02:00
if ( thiscontentidx ! = c )
{
// We have to decrypt the content, and then encrypt to keep IV in line...
2009-07-18 05:00:09 +02:00
WriteStatus ( " - Index altered. Must change IV..." ) ;
2009-07-17 18:22:58 +02:00
byte [ ] ivindex = new byte [ 16 ] ;
for ( int d = 0 ; d < ivindex . Length ; d + + )
{
ivindex [ d ] = 0x00 ;
}
// TODO: Complete this...
ivindex [ 0 ] = 0x00 ;
ivindex [ 1 ] = ( byte ) thiscontentidx ;
2009-07-14 21:38:18 +02:00
2009-07-17 18:22:58 +02:00
initCrypt ( ivindex , dtitlekey ) ;
byte [ ] hash = new byte [ 20 ] ;
for ( int x = 0 ; x < 20 ; x + + )
{
hash [ x ] = tmdhashes [ ( thiscontentidx * 20 ) + x ] ;
}
byte [ ] decContent = Decrypt ( contentbytes ) ;
2009-07-17 20:57:47 +02:00
Array . Resize ( ref decContent , int . Parse ( tmdsizes [ thiscontentidx ] , System . Globalization . NumberStyles . HexNumber ) ) ;
2009-07-18 05:00:09 +02:00
contents [ c ] . Size = NewIntegertoByteArray ( decContent . Length , 8 ) ;
2009-07-17 18:22:58 +02:00
if ( ( Convert . ToBase64String ( ComputeSHA ( decContent ) ) ) = = Convert . ToBase64String ( hash ) )
{
2009-07-17 20:57:47 +02:00
WriteStatus ( " - Hash Check: Content is Unchanged..." ) ;
2009-07-17 18:22:58 +02:00
contents [ c ] . SHAHash = hash ;
2009-07-18 05:00:09 +02:00
//WriteStatus("HASH: " + DisplayBytes(hash, ""));
2009-07-17 18:22:58 +02:00
}
else
{
2009-07-18 05:00:09 +02:00
WriteStatus ( " - Hash Check: Content changed (did you add an encrypted file from another title?)..." ) ;
2009-07-17 18:22:58 +02:00
contents [ c ] . SHAHash = ComputeSHA ( decContent ) ;
}
// Re-encrypt
byte [ ] newiv = new byte [ 16 ] ;
for ( int g = 0 ; g < newiv . Length ; g + + )
{
newiv [ g ] = 0x00 ;
}
2009-07-18 05:00:09 +02:00
byte [ ] smallix = NewIntegertoByteArray ( c , 2 ) ;
ivindex [ 0 ] = smallix [ 0 ] ;
ivindex [ 1 ] = smallix [ 1 ] ;
//WriteStatus(" - Old Index: " + thiscontentidx + "; New Index: " + c);
2009-07-17 18:22:58 +02:00
// Pad back to 0x16 alignment
2009-07-17 20:57:47 +02:00
//AlignByteArray(decContent, 0x16
decContent = PadToMultipleOf ( decContent , 16 ) ;
2009-07-17 18:22:58 +02:00
initCrypt ( newiv , dtitlekey ) ;
byte [ ] encContent = Encrypt ( decContent ) ;
File . Delete ( fileinfo [ 0 ] + filename . Substring ( 0 , 8 ) ) ;
FileStream encryptwrite = new FileStream ( fileinfo [ 0 ] + filename . Substring ( 0 , 8 ) , FileMode . OpenOrCreate ) ;
encryptwrite . Write ( encContent , 0 , encContent . Length ) ;
encryptwrite . Close ( ) ;
WriteStatus ( " - Encrypted Content Again!" ) ;
}
else
2009-08-02 20:12:05 +02:00
{
2009-07-17 18:22:58 +02:00
// Hopefully this content has not been touched...
2009-07-18 05:00:09 +02:00
WriteStatus ( " - Content has not changed Index." ) ;
byte [ ] hash = new byte [ 20 ] ;
for ( int x = 0 ; x < 20 ; x + + )
{
hash [ x ] = tmdhashes [ ( c * 20 ) + x ] ;
}
contents [ c ] . SHAHash = hash ;
contents [ c ] . Size = NewIntegertoByteArray ( int . Parse ( tmdsizes [ c ] , System . Globalization . NumberStyles . HexNumber ) , 8 ) ;
2009-07-17 18:22:58 +02:00
}
contents [ c ] . ContentID = new byte [ 4 ] ;
contents [ c ] . ContentID [ 0 ] = byte . Parse ( filename . Substring ( 0 , 2 ) , System . Globalization . NumberStyles . HexNumber ) ;
contents [ c ] . ContentID [ 1 ] = byte . Parse ( filename . Substring ( 2 , 2 ) , System . Globalization . NumberStyles . HexNumber ) ;
contents [ c ] . ContentID [ 2 ] = byte . Parse ( filename . Substring ( 4 , 2 ) , System . Globalization . NumberStyles . HexNumber ) ;
contents [ c ] . ContentID [ 3 ] = byte . Parse ( filename . Substring ( 6 , 2 ) , System . Globalization . NumberStyles . HexNumber ) ;
2009-08-02 20:12:05 +02:00
2009-07-17 18:22:58 +02:00
contents [ c ] . Index = new byte [ 2 ] ;
2009-07-17 20:57:47 +02:00
contents [ c ] . Index = NewIntegertoByteArray ( c , 2 ) ;
2009-08-02 20:12:05 +02:00
2009-07-17 18:22:58 +02:00
contents [ c ] . Type = new byte [ 2 ] ;
2009-07-17 20:57:47 +02:00
contents [ c ] . Type [ 1 ] = 0x01 ;
2009-07-17 18:22:58 +02:00
if ( contentsEdit . Items [ c ] . ToString ( ) . Contains ( " [S]" ) )
contents [ c ] . Type [ 0 ] = 0x80 ;
else
2009-08-02 20:12:05 +02:00
contents [ c ] . Type [ 0 ] = 0x00 ;
2009-07-17 18:22:58 +02:00
}
2009-07-17 20:57:47 +02:00
}
2009-08-02 20:12:05 +02:00
2009-07-23 00:22:04 +02:00
// Collect everything into a single byte[]...
byte [ ] contentSection = new byte [ contents . Length * 36 ] ;
for ( int h = 0 ; h < contents . Length ; h + + )
{
for ( int i = 0 ; i < contents [ h ] . ContentID . Length ; i + + )
2009-07-17 18:22:58 +02:00
{
2009-07-23 00:22:04 +02:00
contentSection [ ( h * 36 ) + i ] = contents [ h ] . ContentID [ i ] ;
}
2009-07-17 18:22:58 +02:00
2009-07-23 00:22:04 +02:00
for ( int j = 0 ; j < contents [ h ] . Index . Length ; j + + )
{
contentSection [ ( h * 36 ) + ( contents [ h ] . ContentID . Length + j ) ] = contents [ h ] . Index [ j ] ;
}
2009-07-17 18:22:58 +02:00
2009-07-23 00:22:04 +02:00
for ( int k = 0 ; k < contents [ h ] . Type . Length ; k + + )
{
contentSection [ ( h * 36 ) + ( contents [ h ] . ContentID . Length + contents [ h ] . Index . Length + k ) ] = contents [ h ] . Type [ k ] ;
}
2009-07-17 18:22:58 +02:00
2009-07-23 00:22:04 +02:00
for ( int l = 0 ; l < contents [ h ] . Size . Length ; l + + )
{
contentSection [ ( h * 36 ) + ( contents [ h ] . ContentID . Length + contents [ h ] . Index . Length + contents [ h ] . Type . Length + l ) ] = contents [ h ] . Size [ l ] ;
2009-07-17 18:22:58 +02:00
}
2009-07-23 00:22:04 +02:00
for ( int m = 0 ; m < contents [ h ] . SHAHash . Length ; m + + )
2009-07-17 18:22:58 +02:00
{
2009-07-23 00:22:04 +02:00
contentSection [ ( h * 36 ) + ( contents [ h ] . ContentID . Length + contents [ h ] . Index . Length + contents [ h ] . Type . Length + contents [ h ] . Size . Length + m ) ] = contents [ h ] . SHAHash [ m ] ;
2009-07-17 18:22:58 +02:00
}
2009-07-23 00:22:04 +02:00
}
2009-07-17 18:22:58 +02:00
2009-07-23 00:22:04 +02:00
for ( int n = 0 ; n < contentSection . Length ; n + + )
{
tmd [ 0x1E4 + n ] = contentSection [ n ] ;
}
2009-07-17 18:22:58 +02:00
2009-07-23 00:22:04 +02:00
// Fakesign the TMD again...
tmd = ZeroSignature ( tmd ) ;
tmd = TruchaSign ( tmd ) ;
// Write all this stuff to the TMD...
FileStream testtmd = new FileStream ( fileinfo [ 0 ] + fileinfo [ 1 ] , FileMode . Open ) ;
testtmd . Write ( tmd , 0 , tmd . Length ) ;
testtmd . Close ( ) ;
2009-07-17 18:22:58 +02:00
}
2009-07-17 20:57:47 +02:00
/ * Pad Byte [ ] to specific alignment . . .
2009-07-17 18:22:58 +02:00
private byte [ ] AlignByteArray ( byte [ ] content , int alignto )
{
long thelength = content . Length - 1 ;
2009-07-17 20:57:47 +02:00
long remainder = thelength % alignto ;
2009-07-17 18:22:58 +02:00
while ( remainder ! = 0 )
{
thelength + = 1 ;
remainder = thelength % alignto ;
}
Array . Resize ( ref content , ( int ) thelength ) ;
return content ;
2009-07-17 20:57:47 +02:00
} * /
private byte [ ] PadToMultipleOf ( byte [ ] src , int pad )
{
int len = ( src . Length + pad - 1 ) / pad * pad ;
Array . Resize ( ref src , len ) ;
return src ;
2009-07-17 18:22:58 +02:00
}
2009-07-17 20:57:47 +02:00
2009-07-17 18:22:58 +02:00
private void button17_Click ( object sender , EventArgs e )
{
// Move groupbox to display title modder...
2009-08-01 19:48:32 +02:00
if ( button17 . Text = = "Modify Individual Contents..." )
{
contentModBox . Location = new Point ( 278 , 12 ) ;
contentModBox . Visible = true ;
contentModBox . BringToFront ( ) ;
button17 . Text = "Trucha Sign Title..." ;
}
else if ( button17 . Text = = "Trucha Sign Title..." )
{
//contentModBox.Location = new Point(300, 300);
contentModBox . Visible = false ;
contentModBox . SendToBack ( ) ;
button17 . Text = "Modify Individual Contents..." ;
}
2009-07-17 18:22:58 +02:00
}
private void button13_Click ( object sender , EventArgs e )
{
// Share/Unshare Contents in the list...
2009-07-22 20:07:55 +02:00
// Handle help info first...
if ( Control . ModifierKeys = = Keys . Shift )
{
WriteStatus ( "[HELP INFO] Toggles the shared state of the content." ) ;
return ;
}
2009-07-17 18:22:58 +02:00
if ( contentsEdit . SelectedIndex < 0 )
return ;
if ( contentsEdit . Items [ contentsEdit . SelectedIndex ] . ToString ( ) . Contains ( " [S]" ) )
contentsEdit . Items [ contentsEdit . SelectedIndex ] = contentsEdit . SelectedItem . ToString ( ) . Replace ( " [S]" , "" ) ;
else
{
if ( contentsEdit . Items [ contentsEdit . SelectedIndex ] . ToString ( ) . Contains ( " [BOOT]" ) )
contentsEdit . Items [ contentsEdit . SelectedIndex ] = contentsEdit . SelectedItem . ToString ( ) . Replace ( " [BOOT]" , "" ) + " [S] [BOOT]" ;
else
contentsEdit . Items [ contentsEdit . SelectedIndex ] = contentsEdit . SelectedItem . ToString ( ) + " [S]" ;
}
}
private bool IsWin7 ( )
{
return ( Environment . OSVersion . VersionString . Contains ( "6.1" ) = = true ) ;
}
2009-07-17 20:57:47 +02:00
private byte [ ] NewIntegertoByteArray ( int theInt , int arrayLen )
{
byte [ ] resultArray = new byte [ arrayLen ] ;
for ( int i = arrayLen - 1 ; i > = 0 ; i - - )
{
resultArray [ i ] = ( byte ) ( ( theInt > > ( 8 * i ) ) & 0xFF ) ;
}
Array . Reverse ( resultArray ) ;
// Fix duplication, rewrite extra to 0x00;
if ( arrayLen > 4 )
{
for ( int i = 0 ; i < ( arrayLen - 4 ) ; i + + )
{
resultArray [ i ] = 0x00 ;
}
}
return resultArray ;
}
2009-07-21 23:53:27 +02:00
private void button16_Click ( object sender , EventArgs e )
{
// add trucha bug to content...
2009-07-22 20:07:55 +02:00
// Handle help info first...
if ( Control . ModifierKeys = = Keys . Shift )
{
WriteStatus ( "[HELP INFO] Inserts the trucha bug into the selected content, if the bug was fixed previously." ) ;
return ;
}
2009-07-21 23:53:27 +02:00
if ( contentsEdit . SelectedIndex < 0 )
return ;
WriteStatus ( "Attempting to add 'bugs' back into content..." ) ;
string [ ] fileinfo = shamelessvariablelabel . Text . Split ( ',' ) ;
byte [ ] new_hash_check = new byte [ ] { 0x20 , 0x07 , 0x4B , 0x0B } ;
byte [ ] old_hash_check = new byte [ ] { 0x20 , 0x07 , 0x23 , 0xA2 } ;
// If decrypted...
// - check right away for bug...
// - add bug, then end...
// if encrypted...
// - decrypt content...
// - find bug/patch...
// - recalculate hash...
// - write back into TMD...
// - trucha sign content...
if ( contentsEdit . Items [ contentsEdit . SelectedIndex ] . ToString ( ) . Contains ( ".app" ) )
{
// Content is decrypted/new to the title...
string filename = contentsEdit . Items [ contentsEdit . SelectedIndex ] . ToString ( ) . Substring ( contentsEdit . Items [ contentsEdit . SelectedIndex ] . ToString ( ) . IndexOf ( "] [" ) + 3 , 12 ) ;
byte [ ] contentbt = FileLocationToByteArray ( fileinfo [ 0 ] + filename ) ;
2009-08-04 02:01:01 +02:00
byte [ ] newvalues = new byte [ 4 ] ;
newvalues [ 1 ] = 0x00 ;
2009-07-21 23:53:27 +02:00
int [ ] oldresults = ByteArrayContainsByteArray ( contentbt , old_hash_check ) ;
int [ ] newresults = ByteArrayContainsByteArray ( contentbt , new_hash_check ) ;
if ( oldresults [ 0 ] ! = 0 )
{
WriteStatus ( String . Format ( " - {0} Old-school ES Signing Fix(es) Found..." , oldresults [ 0 ] ) ) ;
for ( int s = 1 ; s < oldresults . Length - 1 ; s + + )
{
2009-08-04 02:01:01 +02:00
contentbt = PatchBinary ( contentbt , oldresults [ s ] , newvalues ) ;
2009-07-21 23:53:27 +02:00
WriteStatus ( String . Format ( " - Bug restored at 0x{0}" , int . Parse ( oldresults [ s ] . ToString ( ) , System . Globalization . NumberStyles . HexNumber ) ) ) ;
}
}
2009-08-04 02:01:01 +02:00
2009-07-21 23:53:27 +02:00
if ( newresults [ 0 ] ! = 0 )
{
WriteStatus ( String . Format ( " - {0} New-school ES Signing Fix(es) Found..." , newresults [ 0 ] ) ) ;
for ( int s = 1 ; s < newresults . Length - 1 ; s + + )
{
2009-08-04 02:01:01 +02:00
contentbt = PatchBinary ( contentbt , newresults [ s ] , newvalues ) ;
2009-07-21 23:53:27 +02:00
WriteStatus ( String . Format ( " + Bug restored at 0x{0}." , int . Parse ( newresults [ s ] . ToString ( ) , System . Globalization . NumberStyles . HexNumber ) ) ) ;
}
}
}
else
2009-08-04 02:01:01 +02:00
{
WriteStatus ( " - The file you selected was encrypted, attempting to decrypt and patch..." ) ;
2009-07-21 23:53:27 +02:00
string filename = contentsEdit . Items [ contentsEdit . SelectedIndex ] . ToString ( ) . Substring ( contentsEdit . Items [ contentsEdit . SelectedIndex ] . ToString ( ) . IndexOf ( "] [" ) + 3 , 8 ) ;
byte [ ] ticket = FileLocationToByteArray ( fileinfo [ 0 ] + "cetk" ) ;
2009-08-02 20:12:05 +02:00
byte [ ] tmd = FileLocationToByteArray ( fileinfo [ 0 ] + fileinfo [ 1 ] ) ;
2009-07-21 23:53:27 +02:00
byte [ ] etitlekey = new byte [ 16 ] ;
for ( int a = 0 ; a < 16 ; a + + )
{
etitlekey [ a ] = ticket [ 0x1BF + a ] ;
}
// TODO: Add more key support
byte [ ] commonkey = LoadCommonKey ( @"\key.bin" ) ;
// IV (TITLEID00000000)
byte [ ] iv = new byte [ 16 ] ;
for ( int b = 0 ; b < 8 ; b + + )
{
iv [ b ] = ticket [ 0x1DC + b ] ;
}
for ( int c = 0 ; c < 8 ; c + + )
{
iv [ c + 8 ] = 0x00 ;
}
initCrypt ( iv , commonkey ) ;
2009-08-02 20:12:05 +02:00
byte [ ] dtitlekey = Decrypt ( etitlekey ) ;
// Decrypt this content (determine index)
string [ ] tmdcontents = GetContentNames ( tmd , ContentCount ( tmd ) ) ;
byte [ ] tmdindices = GetContentIndices ( tmd , ContentCount ( tmd ) ) ;
byte [ ] tmdhashes = GetContentHashes ( tmd , ContentCount ( tmd ) ) ;
string [ ] tmdsizes = GetContentSizes ( tmd , ContentCount ( tmd ) ) ;
iv = new byte [ 16 ] ;
2009-08-04 02:01:01 +02:00
for ( int f = 0 ; f < 16 ; f + + )
2009-08-02 20:12:05 +02:00
{
2009-08-04 02:01:01 +02:00
iv [ f ] = 0x00 ;
2009-08-02 20:12:05 +02:00
}
byte [ ] hash = new byte [ 20 ] ;
for ( int d = 0 ; d < tmdcontents . Length ; d + + )
{
if ( tmdcontents [ d ] = = filename )
{
iv [ 0 ] = 0x00 ; // TODO: Add double index byte support
iv [ 1 ] = tmdindices [ d ] ;
for ( int x = 0 ; x < 20 ; x + + )
{
hash [ x ] = tmdhashes [ ( d * 20 ) + x ] ;
}
}
}
initCrypt ( iv , commonkey ) ;
2009-08-04 02:01:01 +02:00
//DEBUG
WriteStatus ( DisplayBytes ( iv , " " ) ) ;
2009-08-02 20:12:05 +02:00
byte [ ] decContent = Decrypt ( FileLocationToByteArray ( fileinfo [ 0 ] + filename ) ) ;
2009-08-04 02:01:01 +02:00
Array . Resize ( ref decContent , int . Parse ( tmdsizes [ 14 ] , System . Globalization . NumberStyles . HexNumber ) ) ;
2009-08-02 20:12:05 +02:00
if ( ( Convert . ToBase64String ( ComputeSHA ( decContent ) ) ) = = Convert . ToBase64String ( hash ) )
{
2009-08-04 02:01:01 +02:00
WriteStatus ( " - Hash Check: Content is Unchanged..." ) ;
2009-08-02 20:12:05 +02:00
}
else
{
2009-08-04 02:01:01 +02:00
WriteStatus ( " - Hash Check: Content changed (did you add an encrypted file from another title?)..." ) ;
WriteStatus ( " - Content Hash: " + DisplayBytes ( ComputeSHA ( decContent ) , "" ) ) ;
WriteStatus ( " - TMD Hash: " + DisplayBytes ( hash , "" ) ) ;
2009-08-02 20:12:05 +02:00
}
2009-08-04 02:01:01 +02:00
if ( File . Exists ( fileinfo [ 0 ] + filename + ".app" ) )
2009-08-02 20:12:05 +02:00
{
2009-08-04 02:01:01 +02:00
if ( MessageBox . Show ( fileinfo [ 0 ] + filename + ".app Exists! Delete the current file so we can move on?" , "File Conflict" , MessageBoxButtons . YesNo ) = = DialogResult . Yes )
File . Delete ( fileinfo [ 0 ] + filename + ".app" ) ;
else
return ;
2009-08-02 20:12:05 +02:00
}
2009-08-04 02:01:01 +02:00
byte [ ] newvalues = new byte [ 4 ] ;
newvalues [ 1 ] = 0x00 ;
2009-08-02 20:12:05 +02:00
2009-08-04 02:01:01 +02:00
int [ ] oldresults = ByteArrayContainsByteArray ( decContent , old_hash_check ) ;
int [ ] newresults = ByteArrayContainsByteArray ( decContent , new_hash_check ) ;
2009-08-02 20:12:05 +02:00
2009-08-04 02:01:01 +02:00
if ( oldresults [ 0 ] ! = 0 )
{
WriteStatus ( String . Format ( " - {0} Old-school ES Signing Fix(es) Found..." , oldresults [ 0 ] ) ) ;
for ( int s = 1 ; s < oldresults . Length - 1 ; s + + )
{
decContent = PatchBinary ( decContent , oldresults [ s ] , newvalues ) ;
WriteStatus ( String . Format ( " - Bug restored at 0x{0}" , int . Parse ( oldresults [ s ] . ToString ( ) , System . Globalization . NumberStyles . HexNumber ) ) ) ;
}
}
2009-08-01 19:48:32 +02:00
2009-08-04 02:01:01 +02:00
if ( newresults [ 0 ] ! = 0 )
{
WriteStatus ( String . Format ( " - {0} New-school ES Signing Fix(es) Found..." , newresults [ 0 ] ) ) ;
for ( int s = 1 ; s < newresults . Length - 1 ; s + + )
{
decContent = PatchBinary ( decContent , newresults [ s ] , newvalues ) ;
WriteStatus ( String . Format ( " + Bug restored at 0x{0}." , int . Parse ( newresults [ s ] . ToString ( ) , System . Globalization . NumberStyles . HexNumber ) ) ) ;
}
}
2009-08-02 20:12:05 +02:00
2009-08-04 02:01:01 +02:00
File . WriteAllBytes ( fileinfo [ 0 ] + filename + ".app" , decContent ) ;
2009-08-02 20:12:05 +02:00
2009-08-04 02:01:01 +02:00
contentsEdit . Items [ contentsEdit . SelectedIndex ] = contentsEdit . Items [ contentsEdit . SelectedIndex ] . ToString ( ) . Replace ( filename , filename + ".app" ) ;
2009-08-02 20:12:05 +02:00
2009-08-04 02:01:01 +02:00
UpdateTMDContents ( ) ;
2009-08-02 20:12:05 +02:00
2009-08-04 02:01:01 +02:00
WriteStatus ( "Trucha signing complete!" ) ;
2009-07-21 23:53:27 +02:00
}
}
private int [ ] ByteArrayContainsByteArray ( byte [ ] bigboy , byte [ ] littleman )
{
// bigboy.Contains(littleman);
// returns offset { cnt , ofst };
int [ ] offset = new int [ 5 ] ;
for ( int a = 0 ; a < ( bigboy . Length - littleman . Length ) ; a + + )
{
int matches = 0 ;
for ( int b = 0 ; b < littleman . Length ; b + + )
{
if ( bigboy [ a + b ] = = littleman [ b ] )
matches + = 1 ;
}
if ( matches = = littleman . Length )
{
offset [ offset [ 0 ] + 1 ] = a ;
offset [ 0 ] + = 1 ;
}
}
return offset ;
}
2009-08-04 02:01:01 +02:00
private byte [ ] PatchBinary ( byte [ ] content , int offset , byte [ ] newvalues )
2009-07-21 23:53:27 +02:00
{
2009-08-04 02:01:01 +02:00
for ( int a = 0 ; a < newvalues . Length ; a + + )
2009-07-21 23:53:27 +02:00
{
2009-08-04 02:01:01 +02:00
if ( newvalues [ a ] > = 0 )
content [ offset + a ] = newvalues [ a ] ;
2009-07-21 23:53:27 +02:00
}
return content ;
}
2009-07-22 20:07:55 +02:00
private void RecalculateIndices ( )
{
for ( int a = 0 ; a < contentsEdit . Items . Count ; a + + )
{
string item = contentsEdit . Items [ a ] . ToString ( ) ;
item = item . Replace ( "[" , "" ) ; item = item . Replace ( "]" , "" ) ;
string [ ] itemparts = item . Split ( ' ' ) ;
contentsEdit . Items [ a ] = String . Format ( "[{0}]" , a ) ;
for ( int b = 0 ; b < itemparts . Length ; b + + )
{
if ( b ! = 0 )
contentsEdit . Items [ a ] + = String . Format ( " [{0}]" , itemparts [ b ] ) ;
}
}
}
2009-08-04 20:17:21 +02:00
private string RetrieveNewDatabase ( )
{
// Retrieve Wiibrew database page source code
WebClient databasedl = new WebClient ( ) ;
2009-08-06 04:37:39 +02:00
statusbox . Refresh ( ) ;
2009-08-04 20:17:21 +02:00
string wiibrewsource = databasedl . DownloadString ( "http://www.wiibrew.org/wiki/NUS_Downloader/database" ) ;
2009-08-06 04:37:39 +02:00
statusbox . Refresh ( ) ;
2009-08-04 20:17:21 +02:00
// Strip out HTML
wiibrewsource = Regex . Replace ( wiibrewsource , @"<(.|\n)*?>" , "" ) ;
// Shrink to fix only the database
string startofdatabase = "<database v" ;
string endofdatabase = "</database>" ;
wiibrewsource = wiibrewsource . Substring ( wiibrewsource . IndexOf ( startofdatabase ) , wiibrewsource . Length - wiibrewsource . IndexOf ( startofdatabase ) ) ;
wiibrewsource = wiibrewsource . Substring ( 0 , wiibrewsource . IndexOf ( endofdatabase ) + endofdatabase . Length ) ;
2009-08-06 04:37:39 +02:00
// Fix ", <, >, and spaces
2009-08-04 20:17:21 +02:00
wiibrewsource = wiibrewsource . Replace ( "<" , "<" ) ;
wiibrewsource = wiibrewsource . Replace ( ">" , ">" ) ;
wiibrewsource = wiibrewsource . Replace ( """ , '"' . ToString ( ) ) ;
2009-08-06 04:37:39 +02:00
wiibrewsource = wiibrewsource . Replace ( " " , " " ) ; // Shouldn't occur, but they happen...
2009-08-04 20:17:21 +02:00
// Return parsed xml database...
return wiibrewsource ;
}
private void updateDatabaseToolStripMenuItem_Click ( object sender , EventArgs e )
{
statusbox . Text = "" ;
WriteStatus ( "Updating your database.xml from Wiibrew.org" ) ;
string database = RetrieveNewDatabase ( ) ;
string currentversion = GetDatabaseVersion ( "database.xml" ) ;
string onlineversion = GetDatabaseVersion ( database ) ;
2009-08-06 04:37:39 +02:00
WriteStatus ( " - Database successfully parsed!" ) ;
WriteStatus ( " - Current Database Version: " + currentversion ) ;
WriteStatus ( " - Online Database Version: " + onlineversion ) ;
2009-08-04 20:17:21 +02:00
if ( currentversion = = onlineversion )
{
WriteStatus ( " - You have the latest database version!" ) ;
return ;
}
WriteStatus ( " - Overwriting your current database.xml..." ) ;
WriteStatus ( " - The old database will become 'olddatabase.xml' in case the new one is faulty." ) ;
string olddatabase = File . ReadAllText ( "database.xml" ) ;
File . WriteAllText ( "olddatabase.xml" , olddatabase ) ;
File . Delete ( "database.xml" ) ;
File . WriteAllText ( "database.xml" , database ) ;
WriteStatus ( "Database successfully updated!" ) ;
}
2009-08-06 04:37:39 +02:00
private void loadInfoFromTMDToolStripMenuItem_Click ( object sender , EventArgs e )
{
// Extras menu -> Load TMD...
LoadTitleFromTMD ( ) ;
}
public string SendSOAPRequest ( string soap_xml )
{
System . Net . HttpWebRequest req = ( System . Net . HttpWebRequest ) System . Net . HttpWebRequest . Create ( "http://nus.shop.wii.com:80/nus/services/NetUpdateSOAP" ) ;
req . Method = "POST" ;
req . UserAgent = "wii libnup/1.0" ;
req . Headers . Add ( "SOAPAction" , '"' + "urn:nus.wsapi.broadon.com/" + '"' ) ;
Stream writeStream = req . GetRequestStream ( ) ;
UTF8Encoding encoding = new UTF8Encoding ( ) ;
byte [ ] bytes = encoding . GetBytes ( soap_xml ) ;
req . ContentType = "text/xml; charset=utf-8" ;
//req.ContentLength = bytes.Length;
writeStream . Write ( bytes , 0 , bytes . Length ) ;
writeStream . Close ( ) ;
Application . DoEvents ( ) ;
try
{
string result ;
System . Net . HttpWebResponse resp = ( System . Net . HttpWebResponse ) req . GetResponse ( ) ;
using ( Stream responseStream = resp . GetResponseStream ( ) )
{
using ( StreamReader readStream = new StreamReader ( responseStream , Encoding . UTF8 ) )
{
result = readStream . ReadToEnd ( ) ;
}
}
req . Abort ( ) ;
Application . DoEvents ( ) ;
return result ;
}
catch ( Exception ex )
{
req . Abort ( ) ;
return ex . Message . ToString ( ) ;
}
}
private void emulateUpdate_DropDownItemClicked ( object sender , ToolStripItemClickedEventArgs e )
{
// Begin Wii System Update
WriteStatus ( "Starting Wii System Update..." ) ;
extrasStrip . Close ( ) ;
string deviceID = "4362227770" ;
string messageID = "13198105123219138" ;
string attr = "2" ;
string RegionID = "USA" ;
string CountryCode = "US" ;
// TODO: Specific region/ccodes
string soap_req = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"" +
" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n" +
"<soapenv:Body>\n<GetSystemUpdateRequest xmlns=\"urn:nus.wsapi.broadon.com\">\n" +
"<Version>1.0</Version>\n<MessageId>" + messageID + "</MessageId>\n<DeviceId>" + deviceID + "</DeviceId>\n" +
"<RegionId>" + RegionID + "</RegionId>\n<CountryCode>" + CountryCode + "</CountryCode>\n<TitleVersion>\n<TitleId>0000000100000001</TitleId>\n" +
"<Version>2</Version>\n</TitleVersion>\n<TitleVersion>\n<TitleId>0000000100000002</TitleId>\n" +
"<Version>33</Version>\n</TitleVersion>\n<TitleVersion>\n<TitleId>0000000100000009</TitleId>\n" +
"<Version>516</Version>\n</TitleVersion>\n<Attribute>" + attr + "</Attribute>\n<AuditData></AuditData>\n" +
"</GetSystemUpdateRequest>\n</soapenv:Body>\n</soapenv:Envelope>" ;
WriteStatus ( " - Sending SOAP Request to NUS..." ) ;
string update_xml = SendSOAPRequest ( soap_req ) ;
if ( update_xml ! = null )
WriteStatus ( " - Recieved Update Info!" ) ;
else
{
WriteStatus ( " - Fail." ) ;
return ;
}
XmlDocument xDoc = new XmlDocument ( ) ;
xDoc . LoadXml ( update_xml ) ;
XmlNodeList TitleList = xDoc . GetElementsByTagName ( "TitleVersion" ) ;
for ( int a = 0 ; a < TitleList . Count ; a + + )
{
XmlNodeList TitleInfo = TitleList [ a ] . ChildNodes ;
string TitleID = "" ;
string Version = "" ;
for ( int b = 0 ; b < TitleInfo . Count ; b + + )
{
switch ( TitleInfo [ b ] . Name )
{
case "TitleId" :
TitleID = TitleInfo [ b ] . InnerText ;
break ;
case "Version" :
Version = TitleInfo [ b ] . InnerText ;
break ;
default :
break ;
}
}
WriteStatus ( String . Format ( "{0}v{1}" , TitleID , Version ) ) ;
}
}
2009-08-07 22:06:34 +02:00
private void ScanForCerts ( byte [ ] tmdortik )
{
// For some reason a few 00s are cut off, so pad it up to be safe.
tmdortik = PadToMultipleOf ( tmdortik , 16 ) ;
// Search for cert_CACP
if ( ! ( tmdortik . Length < 0x300 ) )
for ( int a = 0 ; a < ( tmdortik . Length - 0x300 ) ; a + + )
{
byte [ ] chunk = new byte [ 0x300 ] ;
for ( int b = 0 ; b < 0x300 ; b + + )
{
chunk [ b ] = tmdortik [ a + b ] ;
}
if ( Convert . ToBase64String ( ComputeSHA ( chunk ) ) = = Convert . ToBase64String ( cert_CACP_sha1 ) )
{
cert_CACP = chunk ;
WriteStatus ( " - Cert CA-CP Located!" ) ;
break ;
}
}
// Search for cert_CAXS
if ( ! ( tmdortik . Length < 0x300 ) )
for ( int a = 0 ; a < ( tmdortik . Length - 0x300 ) ; a + + )
{
byte [ ] chunk = new byte [ 0x300 ] ;
for ( int b = 0 ; b < 0x300 ; b + + )
{
chunk [ b ] = tmdortik [ a + b ] ;
}
if ( Convert . ToBase64String ( ComputeSHA ( chunk ) ) = = Convert . ToBase64String ( cert_CAXS_sha1 ) )
{
cert_CAXS = chunk ;
WriteStatus ( " - Cert CA-XS Located!" ) ;
break ;
}
}
// Search for cert_CA
if ( ( ! ( tmdortik . Length < 0x400 ) ) & & ( ( Convert . ToBase64String ( cert_CA ) ! = Convert . ToBase64String ( cert_CA_sha1 ) ) ) )
{
for ( int a = 0 ; a < ( tmdortik . Length - 0x400 ) ; a + + )
{
byte [ ] chunk = new byte [ 0x400 ] ;
for ( int b = 0 ; b < 0x400 ; b + + )
{
chunk [ b ] = tmdortik [ a + b ] ;
}
if ( Convert . ToBase64String ( ComputeSHA ( chunk ) ) = = Convert . ToBase64String ( cert_CA_sha1 ) )
{
cert_CA = chunk ;
WriteStatus ( " - Cert CA Located!" ) ;
break ;
}
}
}
}
private bool CertsValid ( )
{
if ( Convert . ToBase64String ( ComputeSHA ( cert_CA ) ) ! = Convert . ToBase64String ( cert_CA_sha1 ) )
return false ;
if ( Convert . ToBase64String ( ComputeSHA ( cert_CACP ) ) ! = Convert . ToBase64String ( cert_CACP_sha1 ) )
return false ;
if ( Convert . ToBase64String ( ComputeSHA ( cert_CAXS ) ) ! = Convert . ToBase64String ( cert_CAXS_sha1 ) )
return false ;
return true ;
}
private bool TotalCertValid ( byte [ ] cert_sys )
{
if ( Convert . ToBase64String ( ComputeSHA ( cert_sys ) ) ! = Convert . ToBase64String ( cert_total_sha1 ) )
return false ;
return true ;
}
2009-06-11 03:16:49 +02:00
}
}