2009-08-18 07:02:23 +02:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
|
|
|
|
namespace NUS_Downloader
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Class for handling WAD Packaging.
|
|
|
|
|
/// </summary>
|
|
|
|
|
class WADPacker
|
|
|
|
|
{
|
|
|
|
|
// WAD Component Variables
|
|
|
|
|
private byte[] Certsys;
|
|
|
|
|
public byte[] Certs { get { return Certsys;} set { Certsys = value; CertChainSize = Certsys.Length; } }
|
|
|
|
|
private byte[] tmd;
|
|
|
|
|
public byte[] TMD { get { return tmd; }
|
2009-08-18 07:16:04 +02:00
|
|
|
|
set
|
|
|
|
|
{
|
2009-08-18 07:02:23 +02:00
|
|
|
|
tmd = value;
|
|
|
|
|
TMDContentCount = ContentCount(TMD);
|
|
|
|
|
TMDSize = 484 + (TMDContentCount * 36);
|
|
|
|
|
} }
|
2009-08-18 07:16:04 +02:00
|
|
|
|
public byte[] Ticket;
|
|
|
|
|
private int TMDContentCount;
|
2009-08-18 07:02:23 +02:00
|
|
|
|
|
|
|
|
|
// WAD Contents
|
|
|
|
|
private byte[][] TMDContents;
|
|
|
|
|
public byte[][] Contents { get { return TMDContents; }
|
|
|
|
|
set {
|
|
|
|
|
TMDContents = value;
|
|
|
|
|
for (int a = 0; a < TMDContents.Length; a++)
|
|
|
|
|
{
|
|
|
|
|
DataSize += TMDContents[a].Length;
|
|
|
|
|
}
|
|
|
|
|
} }
|
|
|
|
|
|
|
|
|
|
// WAD Saving Variables
|
2009-08-18 07:16:04 +02:00
|
|
|
|
public string Directory;
|
|
|
|
|
public string FileName;
|
2009-08-18 07:02:23 +02:00
|
|
|
|
|
|
|
|
|
// TMD Informations
|
2009-08-18 07:16:04 +02:00
|
|
|
|
public string[] tmdnames;
|
|
|
|
|
public string[] tmdsizes;
|
2009-08-18 07:02:23 +02:00
|
|
|
|
|
|
|
|
|
// WAD Header Variables
|
|
|
|
|
private const int HeaderSize = 0x20;
|
2009-08-18 07:16:04 +02:00
|
|
|
|
private int CertChainSize;
|
2009-08-18 07:02:23 +02:00
|
|
|
|
private const int TicketSize = 0x2A4;
|
2009-08-18 07:16:04 +02:00
|
|
|
|
private int TMDSize;
|
|
|
|
|
private int DataSize;
|
2009-08-18 07:02:23 +02:00
|
|
|
|
private byte[] WADMagic = new byte[8] { 0x00, 0x00, 0x00, 0x20, 0x49, 0x73, 0x00, 0x00 };
|
|
|
|
|
private byte[] RESERVED_CONST = new byte[4] { 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
|
private byte[] TIKSIZE_CONST = new byte[4] { 0x00, 0x00, 0x02, 0xA4 };
|
|
|
|
|
|
|
|
|
|
// Report Status back in EventHandler
|
|
|
|
|
public delegate void StatusChangedEventHandler(string status);
|
|
|
|
|
public event StatusChangedEventHandler StatusChanged;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Pads byte[].
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="src">The byte[] or binary to be padded.</param>
|
|
|
|
|
/// <param name="pad">How much to pad by.</param>
|
|
|
|
|
/// <returns>Padded byte[]</returns>
|
|
|
|
|
private long PadToMultipleOf(long src, int pad)
|
|
|
|
|
{
|
|
|
|
|
long len = (src + pad - 1) / pad * pad;
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Converts an integer into its equivilant byte array.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="theInt">The integer</param>
|
|
|
|
|
/// <param name="arrayLen">Length you desire the byte[] to be.</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
private byte[] ConvertInttoByteArray(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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Handles the size mismatch.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="contentsize">The contentsize.</param>
|
|
|
|
|
/// <param name="actualsize">The actualsize.</param>
|
|
|
|
|
void HandleMismatch(int contentsize, int actualsize)
|
|
|
|
|
{
|
|
|
|
|
if (contentsize != actualsize)
|
|
|
|
|
if ((contentsize - actualsize) > 16)
|
|
|
|
|
StatusChanged(String.Format(" (BAD Mismatch) (Dif: {0}", (contentsize - actualsize)));
|
|
|
|
|
//else
|
|
|
|
|
//statusbox.Text += " (Safe Mismatch)";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns content count of TMD
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="tmd">The TMD.</param>
|
|
|
|
|
/// <returns>int Count of Contents</returns>
|
|
|
|
|
private int ContentCount(byte[] tmd)
|
|
|
|
|
{
|
|
|
|
|
return (tmd[0x1DE] * 256) + tmd[0x1DF];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Packs the WAD file, saves it to specified location.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void PackWAD()
|
|
|
|
|
{
|
|
|
|
|
if ((String.IsNullOrEmpty(Directory)) || (String.IsNullOrEmpty(FileName)))
|
|
|
|
|
{
|
|
|
|
|
StatusChanged("ERROR: No Directory/FileName provided!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FileStream wadfs = new FileStream((Directory + FileName), FileMode.Create);
|
|
|
|
|
|
|
|
|
|
// Seek the beginning of the WAD...
|
|
|
|
|
wadfs.Seek(0, SeekOrigin.Begin);
|
|
|
|
|
|
|
|
|
|
// Write initial part of header (WADType)
|
|
|
|
|
wadfs.Write(WADMagic, 0, WADMagic.Length);
|
|
|
|
|
|
|
|
|
|
// Write CertChainLength
|
|
|
|
|
wadfs.Seek(0x08, SeekOrigin.Begin);
|
|
|
|
|
byte[] chainsize = ConvertInttoByteArray(CertChainSize, 4);
|
|
|
|
|
wadfs.Write(chainsize, 0, chainsize.Length);
|
|
|
|
|
|
|
|
|
|
// Write res
|
|
|
|
|
wadfs.Seek(0x0C, SeekOrigin.Begin);
|
|
|
|
|
wadfs.Write(RESERVED_CONST, 0, RESERVED_CONST.Length);
|
|
|
|
|
|
|
|
|
|
// Write ticketsize
|
|
|
|
|
wadfs.Seek(0x10, SeekOrigin.Begin);
|
|
|
|
|
wadfs.Write(TIKSIZE_CONST, 0, TIKSIZE_CONST.Length);
|
|
|
|
|
|
|
|
|
|
// Write tmdsize
|
|
|
|
|
wadfs.Seek(0x14, SeekOrigin.Begin);
|
|
|
|
|
byte[] tmdsize = ConvertInttoByteArray(TMDSize, 4);
|
|
|
|
|
wadfs.Write(tmdsize, 0, tmdsize.Length);
|
|
|
|
|
|
|
|
|
|
// Write data size
|
|
|
|
|
wadfs.Seek(0x18, SeekOrigin.Begin);
|
|
|
|
|
wadfs.Write(ConvertInttoByteArray(DataSize, 4), 0, 4);
|
2009-08-18 07:16:04 +02:00
|
|
|
|
StatusChanged(" - Header wrote (0x00)");
|
2009-08-18 07:02:23 +02:00
|
|
|
|
|
|
|
|
|
// Write cert[] to 0x40.
|
|
|
|
|
wadfs.Seek(0x40, SeekOrigin.Begin);
|
2009-08-18 07:16:04 +02:00
|
|
|
|
wadfs.Write(Certsys, 0, Certsys.Length);
|
2009-08-18 07:02:23 +02:00
|
|
|
|
StatusChanged(String.Format(" - Certs wrote (0x{0})", Convert.ToString(64, 16)));
|
|
|
|
|
|
|
|
|
|
// Pad to next 64 byte boundary.
|
|
|
|
|
wadfs.Seek(2624, SeekOrigin.Begin);
|
|
|
|
|
|
|
|
|
|
// Write ticket at this point...
|
|
|
|
|
wadfs.Write(Ticket, 0, TicketSize);
|
|
|
|
|
StatusChanged(String.Format(" - Ticket wrote (0x{0})", Convert.ToString((wadfs.Length - 0x2A4), 16)));
|
|
|
|
|
|
|
|
|
|
// Pad to next 64 byte boundary.
|
|
|
|
|
wadfs.Seek(PadToMultipleOf(wadfs.Length, 64), SeekOrigin.Begin);
|
|
|
|
|
|
|
|
|
|
// Write TMD at this point...
|
2009-08-18 07:16:04 +02:00
|
|
|
|
wadfs.Write(tmd, 0, TMDSize);
|
2009-08-18 07:02:23 +02:00
|
|
|
|
StatusChanged(String.Format(" - TMD wrote (0x{0})", Convert.ToString((wadfs.Length - TMDSize), 16)));
|
|
|
|
|
|
|
|
|
|
// Add the individual contents
|
|
|
|
|
for (int a = 0; a < TMDContentCount; a++)
|
|
|
|
|
{
|
|
|
|
|
// Pad to next 64 byte boundary...
|
|
|
|
|
wadfs.Seek(PadToMultipleOf(wadfs.Length, 64), SeekOrigin.Begin);
|
|
|
|
|
|
2009-08-18 07:16:04 +02:00
|
|
|
|
wadfs.Write(TMDContents[a], 0, Contents[a].Length);
|
2009-08-18 07:02:23 +02:00
|
|
|
|
|
2009-08-18 07:16:04 +02:00
|
|
|
|
StatusChanged(String.Format(" - {0} wrote (0x{1})", tmdnames[a], Convert.ToString((wadfs.Length - TMDContents[a].Length), 16)));
|
|
|
|
|
HandleMismatch(int.Parse(tmdsizes[a], System.Globalization.NumberStyles.HexNumber), TMDContents[a].Length);
|
2009-08-18 07:02:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Close filesystem...
|
|
|
|
|
wadfs.Close();
|
|
|
|
|
|
|
|
|
|
// Finished.
|
|
|
|
|
StatusChanged("WAD Created: " + FileName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|