// WIIDisc.cpp: implementation of the CWIIDisc class.
//
//////////////////////////////////////////////////////////////////////
#include "misc.h"
#include <stdio.h>
#include <iostream>
#include <string>
#include <ctype.h>
#include <sys/types.h>
#include <fcntl.h>
#include <openssl/ssl.h>
#include "util.h"
#include "WIIDisc.h"
//#include "ResizePartition.h"
//#include "BootMode.h"
//suk
#define AfxMessageBox(...) {printf (__VA_ARGS__); printf ("\n");}
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#define new DEBUG_NEW
#endif
using namespace std;
/* Trucha signature */
u_int8_t trucha_signature[256] = {
0x57, 0x61, 0x4E, 0x69, 0x4E, 0x4B, 0x6F, 0x4B,
0x4F, 0x57, 0x61, 0x53, 0x48, 0x65, 0x52, 0x65,
0x21, 0x8A, 0xB5, 0xBC, 0x89, 0x00, 0x8E, 0x5C,
0x2B, 0xB6, 0x3E, 0x4D, 0x0A, 0xD7, 0xD2, 0xC4,
0x97, 0x36, 0x82, 0xDF, 0x57, 0x06, 0x37, 0x27,
0x96, 0xF1, 0x40, 0xD6, 0xCD, 0x36, 0xE4, 0xEE,
0xC0, 0x99, 0xAA, 0x49, 0x99, 0x38, 0xA5, 0xC5,
0xEE, 0xE3, 0x12, 0xF8, 0xBB, 0xE4, 0xBC, 0x52,
0x1A, 0x3F, 0x31, 0x71, 0x45, 0x68, 0x98, 0xDB,
0x5A, 0xD9, 0xB2, 0x27, 0x0F, 0x96, 0x15, 0xCF,
0x2F, 0xBF, 0x18, 0xC8, 0xF7, 0xBD, 0x8D, 0xE5,
0xA1, 0x9F, 0xDE, 0x5C, 0x83, 0x9A, 0xAE, 0x9D,
0xD9, 0xDF, 0x0F, 0x1E, 0x47, 0xA7, 0xFA, 0xA1,
0x80, 0xAC, 0xC8, 0x8F, 0x42, 0xDD, 0x5E, 0x71,
0x9C, 0x76, 0x39, 0x93, 0x34, 0xC7, 0x79, 0xD5,
0x66, 0x57, 0x31, 0xEA, 0xF1, 0xDF, 0x87, 0xCB,
0xBE, 0x96, 0xE9, 0x05, 0x3E, 0xE3, 0xA7, 0xBE,
0x8F, 0x6F, 0x4E, 0xD1, 0x4D, 0xAC, 0x42, 0xE9,
0x23, 0x7C, 0x7D, 0x57, 0x43, 0xF6, 0x2C, 0xA9,
0x4D, 0x5D, 0x93, 0x3E, 0x3C, 0x1B, 0x09, 0xFA,
0xB1, 0xF3, 0xFF, 0xEF, 0xD6, 0xA6, 0xAE, 0x66,
0x16, 0xFC, 0x37, 0x63, 0xA8, 0x7A, 0x4C, 0xCB,
0xF6, 0xC9, 0x22, 0x39, 0xBF, 0x4E, 0xE2, 0x0C,
0xAB, 0x76, 0x4B, 0xE7, 0x91, 0x54, 0xE1, 0x42,
0x47, 0xE1, 0x32, 0x1E, 0x87, 0xE0, 0x84, 0x9D,
0xDC, 0xBB, 0x00, 0x84, 0x35, 0x4D, 0x50, 0x2B,
0x16, 0x72, 0x64, 0xD6, 0xC1, 0x47, 0xE2, 0x6C,
0xBD, 0x2D, 0x54, 0x4E, 0x82, 0x35, 0x90, 0xC9,
0x16, 0xC2, 0xE7, 0x9E, 0xA2, 0x6B, 0x3B, 0x7E,
0x27, 0x3C, 0x03, 0x5C, 0x89, 0x53, 0x88, 0x9F,
0xC5, 0xEC, 0x75, 0x86, 0x33, 0x58, 0xF3, 0xF0,
0x85, 0x47, 0x3E, 0x07, 0x7C, 0xCF, 0xD1, 0x93
};
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CWIIDisc::CWIIDisc (void) {
// Create and blank the Wii blank table
tableLen = (u_int64_t) (4699979776LL) / (u_int64_t) (0x8000) * 2;
pFreeTable = (u_int8_t *) malloc (tableLen);
memset (pFreeTable, 0, tableLen);
MarkAsUsed (0, 0x40000); // Set the header size to used
pBlankSector = (u_int8_t *) malloc (0x8000);
memset (pBlankSector, 0xFF, 0x8000);
pBlankSector0 = (u_int8_t *) malloc (0x8000);
memset (pBlankSector0, 0, 0x8000);
}
CWIIDisc::~CWIIDisc () {
// Free up the memory
free (pFreeTable);
free (pBlankSector);
free (pBlankSector0);
}
#define BUFSIZE ((u_int64_t) 0x8000)
/* This is the function that performs the actual scrubbing */
bool CWIIDisc::CleanupISO (char *csFileIn, char *csFileOut, char *csFileDiff, scrubHeadersMode nHeaderMode) {
FILE *fIn = NULL;
FILE *fOut = NULL;
FILE *fOutDif = NULL;
bool bStatus = true; // Used for error checking on read/write
u_int8_t inData[BUFSIZE];
if (csFileOut) {
if (!(fOut = fopen (csFileOut, "wb"))) {
AddToLog ("Unable to create save filename");
return false;
}
}
if (csFileDiff) {
// now open the dif file as well
fOutDif = fopen (csFileDiff, "wb");
if (NULL == fOutDif) {
AddToLog ("Unable to create dif filename");
// close the other output file
fclose (fOut);
return false;
}
}
if ((fIn = fopen (csFileIn, "rb"))) {
// open the in and out files
// read the inblock of 32K
// check to see if we have to write it - allow for bigger discs now
// as well as smaller ones
u_int64_t i;
for (i = 0; ((i < (nImageSize / BUFSIZE)) && (!feof (fIn))); i++) {
bStatus *= (fread (inData, 1, BUFSIZE, fIn) == BUFSIZE);
if (0x01 == pFreeTable[i]) {
// block is marked as used so...
if (fOut)
bStatus *= (BUFSIZE == fwrite (inData, 1, BUFSIZE, fOut));
if (fOutDif)
bStatus *= (BUFSIZE == fwrite (pBlankSector0, 1, 1, fOutDif));
} else {
// empty block so...
if (fOut) {
if (nHeaderMode == SCRUB_REMOVE_HEADERS) {
// change back to 1.0 version.
// As it was pretty trivial for N to check the SHA tables then it seems
// pointless including them at the cost of 1k per sector
bStatus *= (BUFSIZE == fwrite (pBlankSector, 1, BUFSIZE, fOut));
} else {
// 1.0a version
bStatus *= (0x0400 == fwrite (inData, 1, 0x0400, fOut));
bStatus *= (0x7c00 == fwrite (pBlankSector, 1, 0x7c00, fOut));
}
}
if (fOutDif) {
bStatus *= (0x0001 == fwrite (pBlankSector, 1, 1, fOutDif));
bStatus *= (BUFSIZE == fwrite (inData, 1, BUFSIZE, fOutDif));
}
}
}
if (!bStatus) {
// error in read or write - don't care where, just exit with error
if (fOutDif)
fclose (fOutDif);
if (fOut)
fclose (fOut);
fclose (fIn);
return false;
}
}
if (fOutDif)
fclose (fOutDif);
if (fOut)
fclose (fOut);
fclose (fIn);
return true;
}
#define HEADER_BUFSIZE 0x440
/* First function to be called */
struct image_file *CWIIDisc::image_init (char *filename, bool force_wii) {
printf ("--- %s\n", __FUNCTION__);
FILE *fp;
struct image_file *image;
struct part_header *header;
u_int8_t buffer[HEADER_BUFSIZE];
if ((fp = fopen (filename, "rb")) == NULL) {
AfxMessageBox (filename);
return NULL;
}
// get the filesize and set the range accordingly for future operations
if (my_fseek (fp, 0L, SEEK_END) == -1) {
AfxMessageBox ("Unable to seek to EOF");
fclose (fp);
return NULL;
}
this -> nImageSize = my_ftell (fp);
if (!(image = (struct image_file *) malloc (sizeof (struct image_file)))) {
AfxMessageBox ("out of memory");
fclose (fp);
return NULL;
}
memset (image, 0, sizeof (struct image_file));
image -> fp = fp;
if (!io_read (buffer, HEADER_BUFSIZE, image, 0)) {
AfxMessageBox ("reading header");
fclose (fp);
free (image);
return NULL;
}
if (!(header = (struct part_header *) (malloc (sizeof (struct part_header))))) {
AfxMessageBox ("out of memory");
fclose (fp);
free (image);
return NULL;
}
// OK, parse the image header
image_parse_header (header, buffer, false); // FIXME Always false...
if (!header -> is_gc && !header -> is_wii) {
AfxMessageBox ("unknown type for file: %s", filename);
fclose (fp);
free (header);
free (image);
return NULL;
}
if (!header -> has_magic) {
AddToLog ("image has an invalid magic");
}
image -> is_wii = header -> is_wii;
if (header -> is_wii) {
/* Init key */
if (!CheckAndLoadKey (image)) {
fclose (fp);
free (image);
return NULL;
}
}
// Runtime crash fixed :)
// Identified by Juster over on GBATemp.net
// the free was occuring before in the wrong location
// As a free was being carried out and the next line was checking
// a value it was pointing to
free (header);
printf ("--- %s return\n", __FUNCTION__);
return image;
};
u_int8_t CWIIDisc::image_parse_header (struct part_header *header, u_int8_t *buffer, bool forceWii) {
printf ("--- %s\n", __FUNCTION__);
memset (header, 0, sizeof (struct part_header));
header->console = buffer[0];
// account for the Team Twizlers gotcha
if (!forceWii) {
header->is_gc = (header->console == 'G') ||
(header->console == 'D') ||
(header->console == 'P') || (header->console == 'U');
header->is_wii = (header->console == 'R') ||
(header->console == '_') ||
(header->console == 'H') ||
(header->console == '0') || (header->console == '4');
} else {
header->is_gc = false;
header->is_wii = true;
}
header->gamecode[0] = buffer[1];
header->gamecode[1] = buffer[2];
header->region = buffer[3];
header->publisher[0] = buffer[4];
header->publisher[1] = buffer[5];
header->has_magic = be32 (&buffer[0x18]) == 0x5d1c9ea3;
strncpy (header->name, (char *) (&buffer[0x20]), 0x60);
header->dol_offset = be32 (&buffer[0x420]);
header->fst_offset = be32 (&buffer[0x424]);
header->fst_size = be32 (&buffer[0x428]);
if (header->is_wii) {
header->dol_offset *= 4;
header->fst_offset *= 4;
header->fst_size *= 4;
}
printf ("--- %s return\n", __FUNCTION__);
return header->is_gc || header->is_wii;
}
/* Second function to be called */
int CWIIDisc::image_parse (struct image_file *image) {
printf ("--- %s\n", __FUNCTION__);
u_int8_t buffer[HEADER_BUFSIZE];
u_int8_t *fst;
u_int8_t i;
u_int8_t j, valid, nvp;
u_int32_t nfiles;
// string csText;
if (image->is_wii) {
AddToLog ("wii image detected");
get_partitions (image);
} else {
AddToLog ("gamecube image detected");
image->parts = (struct partition *)
malloc (sizeof (struct partition));
memset (&image->parts[0], 0, sizeof (struct partition));
image->PartitionCount = 1;
image->ChannelCount = 0;
image->part_tbl_offset = 0;
image->chan_tbl_offset = 0;
image->parts[0].type = PART_DATA;
}
fstat (fileno (image->fp), &image->st);
nvp = 0;
for (i = 0; i < image->PartitionCount + 1; ++i) {
AddToLog ("------------------------------------------------------------------------------");
AddToLog ("Partition:", i);
switch (image->parts[i].type) {
case PART_DATA:
AfxMessageBox ("Partition:%d - DATA", i);
break;
case PART_UPDATE:
AfxMessageBox ("Partition:%d - UPDATE", i);
break;
case PART_INSTALLER:
AfxMessageBox ("Partition:%d - INSTALLER", i);
break;
case PART_VC:
AfxMessageBox ("Partition:%d - VC GAME [%s]", i, image->parts[i].chan_id);
break;
default:
if (0 != i) {
AfxMessageBox ("Partition:%d - UNKNOWN", i);
} else {
AfxMessageBox ("Partition:0 - PART INFO");
}
break;
}
if (!io_read_part (buffer, HEADER_BUFSIZE, image, i, 0)) {
AfxMessageBox ("partition header");
return 1;
}
valid = 1;
for (j = 0; j < 6; ++j) {
if (!isprint (buffer[j])) {
valid = 0;
break;
}
}
if (!valid) {
AddToLog ("invalid header for partition:", i);
continue;
}
nvp++;
/* Parse partition header */
cout << "reparse header" << endl;
image_parse_header (&image->parts[i].header, buffer, false); // FIXME Always false
cout << "reparse header end" << endl;
if (PART_UNKNOWN != image->parts[i].type) {
AddToLog ("\\partition.bin", image->parts[i].offset, image->parts[i].data_offset);
MarkAsUsed (image->parts[i].offset, image->parts[i].data_offset);
// add on the boot.bin
AddToLog ("\\boot.bin",
image->parts[i].offset +
image->parts[i].data_offset,
(u_int64_t) HEADER_BUFSIZE);
MarkAsUsedDC (image->parts[i].offset +
image->parts[i].data_offset, 0,
(u_int64_t) HEADER_BUFSIZE,
image->parts[i].is_encrypted);
// add on the bi2.bin
AddToLog ("\\bi2.bin",
image->parts[i].offset +
image->parts[i].data_offset + HEADER_BUFSIZE,
(u_int64_t) 0x2000);
MarkAsUsedDC (image->parts[i].offset +
image->parts[i].data_offset, HEADER_BUFSIZE,
(u_int64_t) 0x2000,
image->parts[i].is_encrypted);
}
io_read_part (buffer, 8, image, i, 0x2440 + 0x14);
image->parts[i].appldr_size =
be32 (buffer) + be32 (&buffer[4]);
if (image->parts[i].appldr_size > 0)
image->parts[i].appldr_size += 32;
if (image->parts[i].appldr_size > 0) {
AddToLog ("\\apploader.img",
image->parts[i].offset +
image->parts[i].data_offset + 0x2440,
image->parts[i].appldr_size);
MarkAsUsedDC (image->parts[i].offset +
image->parts[i].data_offset, 0x2440,
image->parts[i].appldr_size,
image->parts[i].is_encrypted);
} else {
AddToLog ("apploader.img not present");
}
if (image->parts[i].header.dol_offset > 0) {
io_read_part (buffer, 0x100, image, i,
image->parts[i].header.dol_offset);
image->parts[i].header.dol_size =
get_dol_size (buffer);
// now check for error condition with bad main.dol
if (image->parts[i].header.dol_size >=
image->parts[i].data_size) {
// almost certainly an error as it's bigger than the partition
image->parts[i].header.dol_size = 0;
}
MarkAsUsedDC (image->parts[i].offset +
image->parts[i].data_offset,
image->parts[i].header.dol_offset,
image->parts[i].header.dol_size,
image->parts[i].is_encrypted);
AddToLog ("\\main.dol ",
image->parts[i].offset +
image->parts[i].data_offset +
image->parts[i].header.dol_offset,
image->parts[i].header.dol_size);
} else {
AddToLog ("partition has no main.dol");
}
if (image->parts[i].is_encrypted) {
// Now add the TMD.BIN and cert.bin files - as these are part of partition.bin
// we don't need to mark them as used - we do put them undr partition.bin in the
// tree though
AddToLog ("\\tmd.bin",
image->parts[i].offset +
image->parts[i].tmd_offset,
image->parts[i].tmd_size);
AddToLog ("\\cert.bin",
image->parts[i].offset +
image->parts[i].cert_offset,
image->parts[i].cert_size);
// add on the H3
AddToLog ("\\h3.bin",
image->parts[i].offset +
image->parts[i].h3_offset,
(u_int64_t) 0x18000);
MarkAsUsedDC (image->parts[i].offset,
image->parts[i].h3_offset,
(u_int64_t) 0x18000, false);
}
if (image->parts[i].header.fst_offset > 0 &&
image->parts[i].header.fst_size > 0) {
AddToLog ("\\fst.bin ",
image->parts[i].offset +
image->parts[i].data_offset +
image->parts[i].header.fst_offset,
image->parts[i].header.fst_size);
MarkAsUsedDC (image->parts[i].offset +
image->parts[i].data_offset,
image->parts[i].header.fst_offset,
image->parts[i].header.fst_size,
image->parts[i].is_encrypted);
fst = (u_int8_t *) (malloc ((u_int32_t)
(image->parts[i].header.
fst_size)));
if (io_read_part
(fst,
(u_int32_t) (image->parts[i].header.fst_size),
image, i,
image->parts[i].header.fst_offset) !=
image->parts[i].header.fst_size) {
AfxMessageBox ("fst.bin");
free (fst);
return 1;
}
nfiles = be32 (fst + 8);
if (12 * nfiles > image->parts[i].header.fst_size) {
AddToLog ("invalid fst for partition", i);
} else {
m_csText = "\\";
parse_fst (fst, (char *) (fst + 12 * nfiles), 0, NULL, image, i);
}
free (fst);
} else {
AddToLog ("partition has no fst");
}
}
if (!nvp) {
AddToLog ("no valid partition were found, exiting");
return 1;
}
printf ("--- %s return\n", __FUNCTION__);
return 0;
}
u_int8_t CWIIDisc::get_partitions (struct image_file * image) {
u_int8_t buffer[16];
u_int8_t i, total_parts;
u_int8_t title_key[16];
u_int8_t iv[16];
u_int8_t partition_key[16];
// clear out the old memory allocated
if (image -> parts) {
free (image -> parts);
image -> parts = NULL;
}
/* Read the partition table (?) */
io_read (buffer, 16, image, 0x40000);
// store the values for later bit twiddling
image -> PartitionCount = be32 (&buffer[0]); // number of partitions is out by one
image -> ChannelCount = be32 (&buffer[8]);
AddToLog ("number of partitions:", image -> PartitionCount + 1);
AddToLog ("number of channels:", image -> ChannelCount);
image -> part_tbl_offset = (u_int64_t) (be32 (&buffer[4])) * ((u_int64_t) (4));
image -> chan_tbl_offset = (u_int64_t) (be32 (&buffer[12])) * ((u_int64_t) (4));
AddToLog ("partition table offset: ", image -> part_tbl_offset);
AddToLog ("channel table offset: ", image -> chan_tbl_offset);
total_parts = image -> PartitionCount + image -> ChannelCount + 1;
image -> parts = (struct partition *) malloc (total_parts * sizeof (struct partition));
memset (image -> parts, 0, total_parts * sizeof (struct partition));
for (i = 1; i < total_parts; i++) {
AddToLog ("--------------------------------------------------------------------------");
AddToLog ("Partition:", i);
if (i < image -> PartitionCount + 1) {
io_read (buffer, 8, image, image -> part_tbl_offset + (i - 1) * 8);
switch (be32 (&buffer[4])) {
case 0:
image -> parts[i].type = PART_DATA;
break;
case 1:
image -> parts[i].type = PART_UPDATE;
break;
case 2:
image -> parts[i].type = PART_INSTALLER;
break;
default:
AddToLog ("WARNING: Unknown partition type");
image -> parts[i].type = PART_UNKNOWN;
break;
}
} else {
AddToLog ("Virtual console");
// error in WiiFuse as it 'assumes' there are only two partitions before VC games
// changed to a generic version
io_read (buffer, 8, image, image -> chan_tbl_offset + (i - image -> PartitionCount - 1) * 8);
image -> parts[i].type = PART_VC;
image -> parts[i].chan_id[0] = buffer[4];
image -> parts[i].chan_id[1] = buffer[5];
image -> parts[i].chan_id[2] = buffer[6];
image -> parts[i].chan_id[3] = buffer[7];
}
image -> parts[i].offset = (u_int64_t) (be32 (buffer)) * ((u_int64_t) (4));
AddToLog ("partition offset: ", image -> parts[i].offset);
// mark the block as used
MarkAsUsed (image -> parts[i].offset, 0x8000);
io_read (buffer, 8, image, image -> parts[i].offset + 0x2b8);
image -> parts[i].data_offset = (u_int64_t) (be32 (&buffer[0])) * 4;
image -> parts[i].data_size = (u_int64_t) (be32 (&buffer[4])) * 4;
// now get the H3 offset
io_read (buffer, 4, image, image -> parts[i].offset + 0x2b4);
image -> parts[i].h3_offset = (u_int64_t) (be32 (&buffer[0])) * 4;
AddToLog ("partition data offset:", image -> parts[i].data_offset);
AddToLog ("partition data size:", image -> parts[i].data_size);
AddToLog ("H3 offset:", image -> parts[i].h3_offset);
tmd_load (image, i);
if (image -> parts[i].tmd == NULL) {
AddToLog ("partition has no valid tmd");
} else {
sprintf (image->parts[i].title_id_str, "%016llx", image->parts[i].tmd->title_id);
image -> parts[i].is_encrypted = 1;
image -> parts[i].cached_block = 0xffffffff;
memset (title_key, 0, 16);
memset (iv, 0, 16);
io_read (title_key, 16, image, image->parts[i].offset + 0x1bf);
io_read (iv, 8, image, image->parts[i].offset + 0x1dc);
AES_cbc_encrypt (title_key, partition_key, 16, &image->key, iv, AES_DECRYPT);
memcpy (image->parts[i].title_key, partition_key, 16);
AES_set_decrypt_key (partition_key, 128, &image->parts[i].key);
sprintf (image->parts[i].key_c, "0x"
"%02x%02x%02x%02x%02x%02x%02x%02x"
"%02x%02x%02x%02x%02x%02x%02x%02x",
partition_key[0], partition_key[1],
partition_key[2], partition_key[3],
partition_key[4], partition_key[5],
partition_key[6], partition_key[7],
partition_key[8], partition_key[9],
partition_key[10], partition_key[11],
partition_key[12], partition_key[13],
partition_key[14], partition_key[15]);
}
}
return image -> PartitionCount == 0;
}
int CWIIDisc::io_read (u_int8_t *ptr, size_t size, struct image_file *image, u_int64_t offset) {
size_t bytes;
if (my_fseek (image -> fp, offset, SEEK_SET) != 0) {
AfxMessageBox ("fseek failed to %llu\n", offset);
bytes = -1;
} else {
MarkAsUsed (offset, size);
bytes = fread (ptr, 1, size, image->fp);
if (bytes != size)
AfxMessageBox ("io_read");
}
return (bytes);
}
bool CWIIDisc::CheckAndLoadKey (struct image_file *image) {
FILE *fp_key;
static u_int8_t LoadedKey[KEY_LENGTH];
bool ret;
if (!(fp_key = fopen (KEYFILE, "rb"))) {
AfxMessageBox ("Unable to open key.bin");
ret = false;
} else if (fread (LoadedKey, 1, KEY_LENGTH, fp_key) != KEY_LENGTH) {
AfxMessageBox ("key.bin not 16 bytes in size");
ret = false;
} else {
// now check to see if it's the right key
// as we don't want to embed the key value in here then lets cheat a little ;)
// by checking the xor'd difference values
if ((0x0F != (LoadedKey[0] ^ LoadedKey[1])) ||
(0xCE != (LoadedKey[1] ^ LoadedKey[2])) ||
(0x08 != (LoadedKey[2] ^ LoadedKey[3])) ||
(0x7C != (LoadedKey[3] ^ LoadedKey[4])) ||
(0xDB != (LoadedKey[4] ^ LoadedKey[5])) ||
(0x16 != (LoadedKey[5] ^ LoadedKey[6])) ||
(0x77 != (LoadedKey[6] ^ LoadedKey[7])) ||
(0xAC != (LoadedKey[7] ^ LoadedKey[8])) ||
(0x91 != (LoadedKey[8] ^ LoadedKey[9])) ||
(0x1C != (LoadedKey[9] ^ LoadedKey[10])) ||
(0x80 != (LoadedKey[10] ^ LoadedKey[11])) ||
(0x36 != (LoadedKey[11] ^ LoadedKey[12])) ||
(0xF2 != (LoadedKey[12] ^ LoadedKey[13])) ||
(0x2B != (LoadedKey[13] ^ LoadedKey[14])) ||
(0x5D != (LoadedKey[14] ^ LoadedKey[15]))) {
/* No check for the last byte?! :( */
AddToLog ("WARNING: Key does not look correct"); // Try using it anyway
}
AES_set_decrypt_key (LoadedKey, 128, &image->key);
ret = true;
}
if (fp_key)
fclose (fp_key);
return ret;
}
//////////////////////////////////////////////////////////////////////
// Recreate the original data from a DIF file and a compress file //
//////////////////////////////////////////////////////////////////////
bool CWIIDisc::RecreateOriginalFile (char *scrubbed, char *diff, char *output) {
FILE *fInCompress = NULL;
FILE *fInDif = NULL;
FILE *fOut = NULL;
u_int64_t nFileSize;
unsigned long nRead = 0L;
int i = 0;
unsigned char inData[0x8000];
unsigned char inDifData[0x8000];
fInCompress = fopen (scrubbed, "rb");
fInDif = fopen (diff, "rb");
if ((NULL == fInCompress) || (NULL == fInDif)) {
// error opning one of the files
if (NULL != fInCompress) {
fclose (fInCompress);
}
if (NULL != fInDif) {
fclose (fInDif);
}
return false;
}
// try and create the output file
if (NULL == (fOut = fopen (output, "wb"))) {
// error - close open and return
fclose (fInDif);
fclose (fInCompress);
return false;
}
// get the input filesize
nFileSize = my_fseek (fInCompress, 0L, SEEK_END);
my_fseek (fInCompress, 0L, SEEK_SET);
i = 0;
while (!feof (fInCompress)) {
// read in a block of data
nRead = fread (inData, 1, 0x8000, fInCompress);
if (0x8000 == nRead) {
// read in a byte from the DIFF file
fread (inDifData, 1, 1, fInDif);
// if DIFF file flagged as 'change data' then
if (0xFF == inDifData[0]) {
// read in block of data
fread (inDifData, 1, 0x8000, fInDif);
// ouput it
fwrite (inDifData, 1, 0x8000, fOut);
} else {
// just need to send the input from the compressed to the output
fwrite (inData, 1, 0x8000, fOut);
}
} else {
// we've read to the end
}
i++;
}
fclose (fInDif);
fclose (fInCompress);
fclose (fOut);
return true;
}
//////////////////////////////////////////////////////////////////////////////////////////
u_int32_t CWIIDisc::parse_fst (u_int8_t * fst, const char *names, u_int32_t i,
struct tree * tree, struct image_file * image,
u_int32_t part)
{
u_int64_t offset;
u_int32_t size;
const char *name;
u_int32_t j;
// string m_csText; // This is a sort of global varm should be removed. suk
name = names + (be32 (fst + 12 * i) & 0x00ffffff);
size = be32 (fst + 12 * i + 8);
if (i == 0) {
// directory so need to go through the directory entries
for (j = 1; j < size;) {
j = parse_fst (fst, names, j, tree, image, part);
}
return size;
}
if (fst[12 * i]) {
m_csText += name;
m_csText += "\\";
AddToLog (m_csText);
//suk pParent = m_pParent->AddItemToTree(name, pParent);
for (j = i + 1; j < size;)
j = parse_fst (fst, names, j, NULL, image, part);
// now remove the directory name we just added
// m_csText = m_csText.Left (m_csText.GetLength () - strlen (name) - 1);
m_csText = m_csText.substr (0, m_csText.length () - strlen (name) - 1);
return size;
} else {
offset = be32 (fst + 12 * i + 4);
if (image->parts[part].header.is_wii)
offset *= 4;
// string csTemp;
// csTemp.Format ("%s [0x%lX] [0x%I64X] [0x%lX] [%d]", name,
// part, offset, size, i);
//suk m_pParent->AddItemToTree(csTemp, pParent);
std::string csTemp = m_csText + name;
// csTemp.Format ("%s%s", m_csText, name);
AddToLog (csTemp, image->parts[part].offset + offset, size);
MarkAsUsedDC (image->parts[part].offset +
image->parts[part].data_offset, offset, size,
image->parts[part].is_encrypted);
image->nfiles++;
image->nbytes += size;
return i + 1;
}
}
u_int32_t CWIIDisc::parse_fst_and_save (u_int8_t * fst, const char *names,
u_int32_t i, struct image_file * image,
u_int32_t part)
{
u_int64_t offset;
u_int32_t size;
const char *name;
u_int32_t j;
string csTemp;
// string m_csText;
name = names + (be32 (fst + 12 * i) & 0x00ffffff);
size = be32 (fst + 12 * i + 8);
if (i == 0) {
// directory so need to go through the directory entries
for (j = 1; j < size;) {
j = parse_fst_and_save (fst, names, j, image, part);
}
if (j != 0xFFFFFFFF) {
return size;
} else {
return 0xFFFFFFFF;
}
}
if (fst[12 * i]) {
// directory so...
// create a directory and change to it
//suk mkdir?!
mkdir (name, 0770);
chdir (name);
for (j = i + 1; j < size;) {
j = parse_fst_and_save (fst, names, j, image, part);
}
// now remove the directory name we just added
//m_csText = m_csText.Left (m_csText.GetLength () - strlen (name) - 1);
m_csText = m_csText.substr (0, m_csText.length () - strlen (name) - 1);
chdir ("..");
if (j != 0xFFFFFFFF) {
return size;
} else {
return 0xFFFFFFFF;
}
} else {
// it's a file so...
// create a filename and then save it out
offset = be32 (fst + 12 * i + 4);
if (image->parts[part].header.is_wii) {
offset *= 4;
}
// now save it
if (true ==
SaveDecryptedFile (name, image, part, offset, size)) {
return i + 1;
} else {
// Error writing file
return 0xFFFFFFFF;
}
}
}
//suk This should be merged with Reset
void CWIIDisc::image_deinit (struct image_file *image) {
int32_t i;
if (!image)
return;
if (image -> PartitionCount + 1 > 0) {
for (i = 0; i < image -> PartitionCount + 1; ++i)
if (image -> parts[i].tmd)
tmd_free (image -> parts[i].tmd);
free (image -> parts);
}
fclose (image -> fp);
free (image);
}
void CWIIDisc::tmd_load (struct image_file *image, u_int32_t part) {
struct tmd *tmd;
u_int32_t tmd_offset, tmd_size;
enum tmd_sig sig = SIG_UNKNOWN;
u_int64_t off, cert_size, cert_off;
u_int8_t buffer[64];
u_int16_t i, s;
off = image->parts[part].offset;
io_read (buffer, 16, image, off + 0x2a4);
tmd_size = be32 (buffer);
tmd_offset = be32 (&buffer[4]) * 4;
cert_size = be32 (&buffer[8]);
cert_off = be32 (&buffer[12]) * 4;
// TODO: ninty way?
/*
* if (cert_size)
* image->parts[part].tmd_size =
* cert_off - image->parts[part].tmd_offset + cert_size;
*/
off += tmd_offset;
io_read (buffer, 4, image, off);
off += 4;
switch (be32 (buffer)) {
case 0x00010001:
sig = SIG_RSA_2048;
s = 0x100;
break;
case 0x00010000:
sig = SIG_RSA_4096;
s = 0x200;
break;
}
if (sig == SIG_UNKNOWN)
return;
tmd = (struct tmd *) malloc (sizeof (struct tmd));
memset (tmd, 0, sizeof (struct tmd));
tmd->sig_type = sig;
image->parts[part].tmd = tmd;
image->parts[part].tmd_offset = tmd_offset;
image->parts[part].tmd_size = tmd_size;
image->parts[part].cert_offset = cert_off;
image->parts[part].cert_size = cert_size;
tmd->sig = (unsigned char *) malloc (s);
io_read (tmd->sig, s, image, off);
off += s;
off = ROUNDUP64B (off);
io_read ((unsigned char *) &tmd->issuer[0], 0x40, image, off);
off += 0x40;
io_read (buffer, 26, image, off);
off += 26;
tmd->version = buffer[0];
tmd->ca_crl_version = buffer[1];
tmd->signer_crl_version = buffer[2];
tmd->sys_version = be64 (&buffer[4]);
tmd->title_id = be64 (&buffer[12]);
tmd->title_type = be32 (&buffer[20]);
tmd->group_id = be16 (&buffer[24]);
off += 62;
io_read (buffer, 10, image, off);
off += 10;
tmd->access_rights = be32 (buffer);
tmd->title_version = be16 (&buffer[4]);
tmd->num_contents = be16 (&buffer[6]);
tmd->boot_index = be16 (&buffer[8]);
off += 2;
if (tmd->num_contents < 1)
return;
tmd->contents = (struct tmd_content *) malloc (sizeof (struct tmd_content) * tmd->num_contents);
for (i = 0; i < tmd->num_contents; ++i) {
io_read (buffer, 0x30, image, off);
off += 0x30;
tmd->contents[i].cid = be32 (buffer);
tmd->contents[i].index = be16 (&buffer[4]);
tmd->contents[i].type = be16 (&buffer[6]);
tmd->contents[i].size = be64 (&buffer[8]);
memcpy (tmd->contents[i].hash, &buffer[16], 20);
}
return;
}
void CWIIDisc::tmd_free (struct tmd *tmd)
{
if (tmd == NULL)
return;
if (tmd->sig)
free (tmd->sig);
if (tmd->contents)
free (tmd->contents);
free (tmd);
}
int CWIIDisc::decrypt_block (struct image_file *image, u_int32_t part,
u_int32_t block)
{
if (block == image->parts[part].cached_block)
return 0;
if (io_read (image->parts[part].dec_buffer, 0x8000, image,
image->parts[part].offset +
image->parts[part].data_offset +
(u_int64_t) (0x8000) * (u_int64_t) (block))
!= 0x8000) {
AfxMessageBox ("decrypt read");
return -1;
}
AES_cbc_encrypt (&image->parts[part].dec_buffer[0x400],
image->parts[part].cache, 0x7c00,
&image->parts[part].key,
&image->parts[part].dec_buffer[0x3d0], AES_DECRYPT);
image->parts[part].cached_block = block;
return 0;
}
size_t CWIIDisc::io_read_part (unsigned char *ptr, size_t size,
struct image_file * image, u_int32_t part,
u_int64_t offset)
{
u_int32_t block = (u_int32_t) (offset / (u_int64_t) (0x7c00));
u_int32_t cache_offset = (u_int32_t) (offset % (u_int64_t) (0x7c00));
u_int32_t cache_size;
unsigned char *dst = ptr;
if (!image->parts[part].is_encrypted)
return io_read (ptr, size, image,
image->parts[part].offset + offset);
while (size) {
if (decrypt_block (image, part, block))
return (dst - ptr);
cache_size = size;
if (cache_size + cache_offset > 0x7c00)
cache_size = 0x7c00 - cache_offset;
memcpy (dst, image->parts[part].cache + cache_offset,
cache_size);
dst += cache_size;
size -= cache_size;
cache_offset = 0;
block++;
}
return dst - ptr;
}
unsigned int CWIIDisc::CountBlocksUsed ()
{
unsigned int nRetVal = 0;;
u_int64_t nBlock = 0;
u_int64_t i = 0;
unsigned char cLastBlock = 0x01;
AddToLog ("------------------------------------------------------------------------------");
for (i = 0; i < (nImageSize / (u_int64_t) (0x8000)); i++) {
nRetVal += pFreeTable[i];
if (cLastBlock != pFreeTable[i]) {
// change so show
if (1 == cLastBlock) {
AddToLog ("Marked Content",
nBlock * (u_int64_t) (0x8000),
i * (u_int64_t) (0x8000) - 1,
(i - nBlock) * 32);
} else {
AddToLog ("Empty Content",
nBlock * (u_int64_t) (0x8000),
i * (u_int64_t) (0x8000) - 1,
(i - nBlock) * 32);
}
nBlock = i;
cLastBlock = pFreeTable[i];
}
}
// output the final range
if (1 == cLastBlock) {
AddToLog ("Marked Content", nBlock * (u_int64_t) (0x8000),
nImageSize - 1, (i - nBlock) * 32);
} else {
AddToLog ("Empty Content", nBlock * (u_int64_t) (0x8000),
nImageSize - 1, (i - nBlock) * 32);
}
AddToLog ("------------------------------------------------------------------------------");
return nRetVal;
}
void CWIIDisc::MarkAsUsed (u_int64_t nOffset, u_int64_t nSize)
{
u_int64_t nStartValue = nOffset;
u_int64_t nEndValue = nOffset + nSize;
while ((nStartValue < nEndValue) &&
(nStartValue < ((u_int64_t) (4699979776LL) * (u_int64_t) (2))))
{
pFreeTable[nStartValue / (u_int64_t) (0x8000)] = 1;
nStartValue = nStartValue + ((u_int64_t) (0x8000));
}
}
void CWIIDisc::MarkAsUsedDC (u_int64_t nPartOffset, u_int64_t nOffset,
u_int64_t nSize, bool bIsEncrypted)
{
u_int64_t nTempOffset;
u_int64_t nTempSize;
if (true == bIsEncrypted) {
// the offset and size relate to the decrypted file so.........
// we need to change the values to accomodate the 0x400 bytes of crypto data
nTempOffset = nOffset / (u_int64_t) (0x7c00);
nTempOffset = nTempOffset * ((u_int64_t) (0x8000));
nTempOffset += nPartOffset;
nTempSize = nSize / (u_int64_t) (0x7c00);
nTempSize = (nTempSize + 1) * ((u_int64_t) (0x8000));
// add on the offset in the first nblock for the case where data straddles blocks
nTempSize += nOffset % (u_int64_t) (0x7c00);
} else {
// unencrypted - we use the actual offsets
nTempOffset = nPartOffset + nOffset;
nTempSize = nSize;
}
MarkAsUsed (nTempOffset, nTempSize);
}
void CWIIDisc::AddToLog (string csText) {
// m_pParent->AddToLog (csText); //suk
fprintf (stderr, "[LOG] %s\n", csText.c_str ());
}
void CWIIDisc::AddToLog (string csText, u_int64_t nValue) {
// m_pParent->AddToLog (csText, nValue);
fprintf (stderr, "[LOG] %s %llu\n", csText.c_str (), nValue);
}
void CWIIDisc::AddToLog (string csText, u_int64_t nValue1, u_int64_t nValue2) {
// string csText1;
// csText1.Format ("%s [0x%I64X], [0x%I64X]", csText, nValue1, nValue2);
// m_pParent->AddToLog (csText1);
fprintf (stderr, "[LOG] %s [%llX] [%llX] \n", csText.c_str (), nValue1, nValue2);
}
void CWIIDisc::AddToLog (string csText, u_int64_t nValue1, u_int64_t nValue2, u_int64_t nValue3) {
// string csText1;
// csText1.Format ("%s [0x%I64X], [0x%I64X] [%I64d K]", csText, nValue1,
// nValue2, nValue3);
// m_pParent->AddToLog (csText1);
fprintf (stderr, "[LOG] %s [0x%llX], [0x%llX] [%llX K]\n", csText.c_str (), nValue1, nValue2, nValue3);
}
void CWIIDisc::Reset ()
{
//set them all to clear first
memset (pFreeTable, 0, ((4699979776LL / 32768LL) * 2L));
// then set the header size to used
MarkAsUsed (0, 0x50000);
#if 0 //suk
hDisc = NULL;
for (int i = 0; i < 20; i++) {
hPartition[i] = NULL;
}
#endif
// then clear the decrypt key
u_int8_t key[16];
memset (key, 0, 16);
AES_KEY nKey;
memset (&nKey, 0, sizeof (AES_KEY));
AES_set_decrypt_key (key, 128, &nKey);
}
bool CWIIDisc::SaveDecryptedFile (string csDestinationFilename,
struct image_file *image, u_int32_t part,
u_int64_t nFileOffset, u_int64_t nFileSize,
bool bOverrideEncrypt)
{
FILE *fOut;
u_int32_t block = (u_int32_t) (nFileOffset / (u_int64_t) (0x7c00));
u_int32_t cache_offset =
(u_int32_t) (nFileOffset % (u_int64_t) (0x7c00));
u_int64_t cache_size;
unsigned char cBuffer[0x8000];
fOut = fopen (csDestinationFilename.c_str (), "wb");
if ((!image->parts[part].is_encrypted) || (true == bOverrideEncrypt)) {
if (-1 == my_fseek (image->fp, nFileOffset, SEEK_SET)) {
AfxMessageBox ("io_seek");
return -1;
}
while (nFileSize) {
cache_size = nFileSize;
if (cache_size > 0x8000)
cache_size = 0x8000;
fread (&cBuffer[0], 1, (u_int32_t) (cache_size), image->fp);
fwrite (cBuffer, 1, (u_int32_t) (cache_size), fOut);
nFileSize -= cache_size;
}
} else {
while (nFileSize) {
if (decrypt_block (image, part, block)) {
fclose (fOut);
return false;
}
cache_size = nFileSize;
if (cache_size + cache_offset > 0x7c00)
cache_size = 0x7c00 - cache_offset;
if (cache_size !=
fwrite (image->parts[part].cache + cache_offset,
1, (u_int32_t) (cache_size), fOut)) {
AddToLog ("Error writing file");
fclose (fOut);
return false;
}
nFileSize -= cache_size;
cache_offset = 0;
block++;
}
}
fclose (fOut);
return true;
}
bool CWIIDisc::LoadDecryptedFile (string csDestinationFilename,
struct image_file * image, u_int32_t part,
u_int64_t nFileOffset, u_int64_t nFileSize,
int nFSTReference)
{
FILE *fIn;
u_int32_t nImageSize;
u_int64_t nfImageSize;
u_int8_t *pBootBin = (unsigned char *) calloc (0x440, 1);
u_int8_t *pPartData;
u_int64_t nFreeSpaceStart;
u_int32_t nExtraData;
u_int32_t nExtraDataBlocks;
// create a 64 cluster buffer for the file
fIn = fopen (csDestinationFilename.c_str (), "rb");
if (NULL == fIn) {
AddToLog ("Error opening file");
return false;
}
// get the size of the file we are trying to load
nfImageSize = my_fseek (fIn, 0L, SEEK_END);
nImageSize = (u_int32_t) nfImageSize;
// pointer back to the start
my_fseek (fIn, 0L, SEEK_SET);
// now get the filesize we are trying to load and make sure it is smaller
// or the same size as the one we are trying to replace if so then a simple replace
if (nFileSize >= nImageSize) {
// simple replace
// now need to change the boot.bin if one if fst.bin or main.dol were changed
if (nFileSize != nfImageSize) {
// we have a different sized file being put in
// this is obviously smaller but will require a tweak to one of the file
// entries
if (0 < nFSTReference) {
// normal file so change the FST.BIN
u_int8_t *pFSTBin = (unsigned char *)
calloc ((u_int32_t)
(image->parts[part].header.
fst_size), 1);
io_read_part (pFSTBin,
(u_int32_t) (image->parts[part].
header.fst_size),
image, part,
image->parts[part].header.
fst_offset);
// alter the entry for the passed FST Reference
nFSTReference = nFSTReference * 0x0c;
// update the length for the file
Write32 (pFSTBin + nFSTReference + 0x08L,
nImageSize);
// write out the FST.BIN
wii_write_data_file (image, part,
image->parts[part].
header.fst_offset,
(u_int32_t) (image->
parts[part].
header.
fst_size),
pFSTBin);
// write it out
wii_write_data_file (image, part, nFileOffset,
nImageSize, NULL, fIn);
} else {
switch (nFSTReference) {
case 0:
// - one of the files that should ALWAYS be the correct size
AfxMessageBox
("Error as file sizes do not match and they MUST for boot.bin and bi2.bin");
fclose (fIn);
free (pBootBin);
return false;
break;
case -1:
// FST
io_read_part (pBootBin, 0x440, image,
part, 0);
// update the settings for the FST.BIN entry
// this has to be rounded to the nearest 4 so.....
if (0 != (nImageSize % 4)) {
nImageSize =
nImageSize + (4 -
nImageSize
% 4);
}
Write32 (pBootBin + 0x428L,
(u_int32_t) (nImageSize >>
2));
Write32 (pBootBin + 0x42CL,
(u_int32_t) (nImageSize >>
2));
// now write it out
wii_write_data_file (image, part, 0,
0x440, pBootBin);
break;
case -2:
// main.dol - don't have to do anything
break;
case -3:
// apploader - don't have to do anything
break;
case -4:
// partition.bin
AfxMessageBox
("Error as partition.bin MUST be 0x20000 bytes in size");
fclose (fIn);
free (pBootBin);
return false;
break;
case -5:
AfxMessageBox
("Error as tmd.bin MUST be same size");
fclose (fIn);
free (pBootBin);
return false;
break;
case -6:
AfxMessageBox
("Error as cert.bin MUST be same size");
fclose (fIn);
free (pBootBin);
return false;
break;
case -7:
AfxMessageBox
("Error as h3.bin MUST be 0x18000 bytes in size");
fclose (fIn);
free (pBootBin);
return false;
break;
default:
AfxMessageBox
("Unknown file reference passed");
fclose (fIn);
free (pBootBin);
return false;
break;
}
// now write it out
wii_write_data_file (image, part, nFileOffset,
nImageSize, NULL, fIn);
}
} else {
// Equal sized file so need to check for the special cases
if (0 > nFSTReference) {
switch (nFSTReference) {
case -1:
case -2:
case -3:
// simple write as files are the same size
wii_write_data_file (image, part,
nFileOffset,
nImageSize, NULL,
fIn);
break;
case -4:
// Partition.bin
// it's a direct write
pPartData =
(u_int8_t *) calloc (1,
(unsigned
int)
nFileSize);
fread (pPartData, 1,
(unsigned int) nFileSize, fIn);
DiscWriteDirect (image,
image->parts[part].
offset, pPartData,
(unsigned int)
nFileSize);
free (pPartData);
break;
case -5:
// tmd.bin;
case -6:
// cert.bin
case -7:
// h3.bin
// same for all 3
pPartData =
(u_int8_t *) calloc (1,
(unsigned
int)
nFileSize);
fread (pPartData, 1,
(unsigned int) nFileSize, fIn);
DiscWriteDirect (image,
image->parts[part].
offset + nFileOffset,
pPartData,
(unsigned int)
nFileSize);
free (pPartData);
break;
default:
AddToLog ("Unknown file reference passed");
break;
}
} else {
// simple write as files are the same size
wii_write_data_file (image, part, nFileOffset,
nImageSize, NULL, fIn);
}
}
} else {
// Alternatively just have to update the FST or boot.bin depending on the file we want to change
// this will depend on whether the passed index is
// -ve = Partition data,
// 0 = given by boot.bin,
// +ve = normal file
// need to find some free space in the partition first
nFreeSpaceStart =
FindRequiredFreeSpaceInPartition (image, part,
nImageSize);
if (0 == nFreeSpaceStart) {
// no free space - so cant do it
AfxMessageBox
("Unable to find free space to add the file :(");
fclose (fIn);
free (pBootBin);
return false;
}
// depending on the passed offset we then need to modify either the
// fst.bin or the boot.bin
if (0 < nFSTReference) {
// normal one - so read out the fst and change the values for the relevant pointer
// before writing it out
u_int8_t *pFSTBin = (unsigned char *)
calloc ((u_int32_t)
(image->parts[part].header.fst_size),
1);
io_read_part (pFSTBin,
(u_int32_t) (image->parts[part].header.
fst_size), image, part,
image->parts[part].header.fst_offset);
// alter the entry for the passed FST Reference
nFSTReference = nFSTReference * 0x0c;
// update the offset for this file
Write32 (pFSTBin + nFSTReference + 0x04L,
u_int32_t (nFreeSpaceStart >> 2));
// update the length for the file
Write32 (pFSTBin + nFSTReference + 0x08L, nImageSize);
// write out the FST.BIN
wii_write_data_file (image, part,
image->parts[part].header.
fst_offset,
(u_int32_t) (image->parts[part].
header.fst_size),
pFSTBin);
// now write data file out
wii_write_data_file (image, part, nFreeSpaceStart,
nImageSize, NULL, fIn);
} else {
switch (nFSTReference) {
case -1: // FST.BIN
// change the boot.bin file too and write that out
io_read_part (pBootBin, 0x440, image, part,
0);
// update the settings for the FST.BIN entry
// this has to be rounded to the nearest 4 so.....
if (0 != (nImageSize % 4)) {
nImageSize =
nImageSize + (4 -
nImageSize % 4);
}
// update the settings for the FST.BIN entry
Write32 (pBootBin + 0x424L,
u_int32_t (nFreeSpaceStart >> 2));
Write32 (pBootBin + 0x428L,
(u_int32_t) (nImageSize >> 2));
Write32 (pBootBin + 0x42CL,
(u_int32_t) (nImageSize >> 2));
// now write it out
wii_write_data_file (image, part, 0, 0x440,
pBootBin);
// now write it out
wii_write_data_file (image, part,
nFreeSpaceStart,
nImageSize, NULL, fIn);
break;
case -2: // Main.DOL
// change the boot.bin file too and write that out
io_read_part (pBootBin, 0x440, image, part,
0);
// update the settings for the main.dol entry
Write32 (pBootBin + 0x420L,
u_int32_t (nFreeSpaceStart >> 2));
// now write it out
wii_write_data_file (image, part, 0, 0x440,
pBootBin);
// now write main.dol out
wii_write_data_file (image, part,
nFreeSpaceStart,
nImageSize, NULL, fIn);
break;
case -3: // Apploader.img - now this is fun! as we have to
// move the main.dol and potentially fst.bin too too otherwise they would be overwritten
// also what happens if the data for those two has already been moved
// aaaargh
// check to see what we have to move
// by calculating the amount of extra data we are trying to stuff in
nExtraData =
(u_int32_t) (nImageSize -
image->parts[part].
appldr_size);
nExtraDataBlocks =
1 +
((nImageSize -
(u_int32_t) (image->parts[part].
appldr_size)) /
0x7c00);
// check to see if we have that much free at the end of the area
// or do we need to try and overwrite
if (true ==
CheckForFreeSpace (image, part,
image->parts[part].
appldr_size + 0x2440,
nExtraDataBlocks)) {
// we have enough space after the current apploader - already moved the main.dol?
// so just need to write it out.
wii_write_data_file (image, part,
0x2440,
nImageSize, NULL,
fIn);
} else {
// check if we can get by with moving the main.dol
if (nExtraData >
image->parts[part].header.
dol_size) {
// don't really want to be playing around here as we potentially can get
// overwrites of all sorts of data
AfxMessageBox
("Cannot guarantee writing data correctly\nI blame nargels");
AddToLog ("Cannot guarantee a good write of apploader");
fclose (fIn);
free (pBootBin);
return false;
} else {
// "just" need to move main.dol
u_int8_t *pMainDol =
(u_int8_t *)
calloc ((u_int32_t)
(image->
parts[part].
header.
dol_size),
1);
io_read_part (pMainDol,
(u_int32_t)
(image->
parts[part].
header.
dol_size),
image, part,
image->
parts[part].
header.
dol_offset);
// try and get some free space for it
nFreeSpaceStart =
FindRequiredFreeSpaceInPartition
(image, part,
(u_int32_t) (image->
parts
[part].
header.
dol_size));
// now unmark the original dol area
MarkAsUnused (image->
parts[part].
offset +
image->
parts[part].
data_offset +
(((image->
parts[part].
header.
dol_offset) /
0x7c00) *
0x8000),
image->
parts[part].
header.
dol_size);
if ((0 != nFreeSpaceStart) &&
(true ==
CheckForFreeSpace (image,
part,
image->
parts
[part].
appldr_size
+
0x2440,
nExtraDataBlocks)))
{
// got space so write it out
wii_write_data_file
(image, part,
nFreeSpaceStart,
(u_int32_t)
(image->
parts[part].
header.
dol_size),
pMainDol);
// now do the boot.bin file too
io_read_part
(pBootBin,
0x440, image,
part, 0);
// update the settings for the boot.BIN entry
Write32 (pBootBin +
0x420L,
u_int32_t
(nFreeSpaceStart
>> 2));
// now write it out
wii_write_data_file
(image, part,
0, 0x440,
pBootBin);
// now write out the apploader - we don't need to change any other data
// as the size is inside the apploader
wii_write_data_file
(image, part,
0x2440,
nImageSize,
NULL, fIn);
} else {
// cannot do it :(
AfxMessageBox
("Unable to move the main.dol and find enough space for the apploader.");
AddToLog ("Unable to add larger apploader");
free (pMainDol);
free (pBootBin);
fclose (fIn);
return false;
}
}
}
break;
default:
// Unable to do these as they are set sizes and lengths
// boot.bin and bi2.bin
// partition.bin
AfxMessageBox
("Unable to change that file as it is a set size\nin the disc image");
AddToLog ("Unable to change set size file");
free (pBootBin);
fclose (fIn);
return false;
break;
}
}
}
// free the memory we allocated
free (pBootBin);
fclose (fIn);
return true;
}
////////////////////////////////////////////////////////////////////////
// here we find the free space, modify the fst.bin and do some other //
// bit buggering to add 5 files to the image //
// As these are filled with blanks then they compress well even after //
// encrypting //
////////////////////////////////////////////////////////////////////////
bool CWIIDisc::TruchaScrub (image_file * image, unsigned int nPartition)
{
unsigned char *pFST = NULL;
unsigned char *pFSTCopy = NULL;
unsigned char *pBootBin = NULL;
unsigned char *pFSTDummy = NULL;
FILE *fOut;
int z = 0;
u_int64_t nFSTOldSize;
u_int64_t nFSTNewSize;
u_int32_t nFSTNewDiscSize;
//// find the free space size
u_int64_t nOffset;
u_int64_t nSize;
u_int64_t nSizeForFiles;
FindFreeSpaceInPartition (image->parts[nPartition].offset, &nOffset,
&nSize);
// now need to subtract the partition info from the start
nOffset =
nOffset - (image->parts[nPartition].offset +
image->parts[nPartition].data_offset);
nFSTOldSize = (image->parts[nPartition].header.fst_size);
nFSTNewSize = (image->parts[nPartition].header.fst_size) + (0x0c + 0x0c) * 5; //5 extra entries plus extra strings
nFSTNewDiscSize = u_int32_t (nFSTNewSize >> 2);
// now follows an example bit of code on how to put whatever you like
// onto a TRUCHA disc by a three stage process
/////////////////////////////////////////////////////////////////////////
// Step 1 = prepare a modified boot.bin that points to where the new
// fst.bin will live
/////////////////////////////////////////////////////////////////////////
// get the boot.bin file out and then save it
// with modified values for a 'fixed' FST
pBootBin = (unsigned char *) malloc (0x440);
io_read_part (pBootBin, 0x440, image, nPartition, 0);
Write32 (pBootBin + 0x424L, u_int32_t (nOffset >> 2));
Write32 (pBootBin + 0x428L, nFSTNewDiscSize);
Write32 (pBootBin + 0x42CL, nFSTNewDiscSize);
// also update the offset to point to the new FST location after the second write
// save the boot.bin
fOut = fopen ("ReplacementBoot.bin", "wb");
fwrite (pBootBin, 1, 0x440, fOut);
fclose (fOut);
/////////////////////////////////////////////////////////////////////////
// Step 2 = create a modified fst.bin that points to where we are going
// to store the fst.bin we really want to use in the new disc image
/////////////////////////////////////////////////////////////////////////
// now create the fst.bin we use to chuck our data into the correct place
// only two entries plus a file name
pFSTDummy = (unsigned char *) malloc ((0x0C * 2) + 0x10);
// create the dummy FST
memset (pFSTDummy, 0, 0x28);
// standard start record
Write32 (pFSTDummy, 0x01000000);
Write32 (pFSTDummy + 0x04L, 0x00000000);
Write32 (pFSTDummy + 0x08L, 0x00000002);
// now the data entry
Write32 (pFSTDummy + 0x0C, 0x00000000); // file , first string table entry
Write32 (pFSTDummy + 0x10, u_int32_t (nOffset >> 2)); // start location
Write32 (pFSTDummy + 0x14, (u_int32_t) (nFSTNewSize)); // size of new FST
// now the string table entry
memcpy (pFSTDummy + 0x18L, "FakeFSTGoesHere", 0x0F);
fOut = fopen ("FakeFST.bin", "wb");
fwrite (pFSTDummy, 1, 0x28, fOut);
fclose (fOut);
/////////////////////////////////////////////////////////////////////////
// Step 3 = create the new fst.bin we really want to use
// in this case we are going to add 5 files into the image that will be
// filled with blank space as even when encrypted the data will compress
/////////////////////////////////////////////////////////////////////////
// get the fst.bin from the image file
pFST = (unsigned char *) malloc ((u_int32_t) (nFSTOldSize));
io_read_part (pFST, (u_int32_t) (nFSTOldSize), image, nPartition,
image->parts[nPartition].header.fst_offset);
// allocate enough for the altered version
pFSTCopy = (unsigned char *) (malloc ((u_int32_t) (nFSTNewSize))); // extra entry plus string chars
// clear it out first
memset (pFSTCopy, 0, (u_int32_t) (nFSTNewSize));
// modify it to add extra files in the root directory using the name SCRUBPT1 to SCRUBPT5
unsigned int nFiles = be32 (pFST + 8);
unsigned int nStringTableOffset = (nFiles * 0x0c);
// copy the existing data
// Table first
memcpy (pFSTCopy, pFST, nStringTableOffset);
// then the string table
memcpy (pFSTCopy + (nFiles + 5) * 0x0c, pFST + (nFiles * 0x0c),
(u_int32_t) (nFSTOldSize) - (nFiles * 0x0c));
// update the number of files in the copy by adding 5
Write32 (pFSTCopy + 8, nFiles + 5);
// available space is now what was available minus the size of the new FST.BIN rounded up
// to nearest block
u_int64_t nFSTTweak =
(((nFSTNewSize / (u_int64_t) (0x7C00)) +
1) * (u_int64_t) (0x7C00));
nSize = nSize - nFSTTweak;
// save the number for later file creation
nSizeForFiles = nSize;
nOffset = nOffset + nFSTTweak;
// now loop around adding the correct data for each of the table entries
u_int32_t nFSTStringTableSize;
nFSTStringTableSize =
(u_int32_t) (nFSTNewSize) - ((nFiles + 5) * 0x0C);
// now find where the existing data really ends by moving backwards from
// the end until we find a non 0 character
// this should stop fstreader getting it's knickers in a twist
u_int32_t nStringPad = (u_int32_t) (nFSTNewSize) - 1;
while (0 == pFSTCopy[nStringPad]) {
nStringPad--;
}
// we are now at the first non 0 char
// so add two to give us the correct offset
nStringPad += 2;
for (z = 0; z < 5; z++) {
// string pointer at end of table minus 5 entries plus whatever entry we are at
Write32 (pFSTCopy + (nFiles + z) * 0x0C,
nStringPad - ((nFiles + 5) * 0x0C) + (z * 0x0C));
// then offset - value needs to be divided by 4 after adding on the block modified
// fst.bin ammendment
Write32 (pFSTCopy + (nFiles + z) * 0x0C + 0x04,
(u_int32_t) (nOffset >> 2));
// then length - this will be either the max data size or
// the actual size depending on the data we have left to pad
if (nSize >= 0x3FFFFC00) {
Write32 (pFSTCopy + (nFiles + z) * 0x0C + 0x08,
0x3FFFFC00);
nSize = nSize - 0x3FFFFC00;
nOffset = nOffset + 0x3FFFFC00;
} else {
u_int32_t nTempSize;
nTempSize = (u_int32_t) (nSize);
Write32 (pFSTCopy + (nFiles + z) * 0x0C + 0x08,
nTempSize);
nSize = 0;
}
// add the string entry for the name "SCRUBPT1 to 5"
memcpy (&pFSTCopy[nStringPad + (z * 0x0C)], "SCRUBPX.DAT",
0x0B);
// then add the part number
pFSTCopy[nStringPad + (z * 0x0C) + 0x06] = '1' + z;
}
// save the fst.bin
fOut = fopen ("SCRUBBEDFST.bin", "wb");
fwrite (pFSTCopy, 1, (u_int32_t) (nFSTNewSize), fOut);
fclose (fOut);
/////////////////////////////////////////////////////////////////////////
// now we need to create the blank files
/////////////////////////////////////////////////////////////////////////
#if 0 //suk
// create the progress bar etc.....
CProgressBox *pProgressBox;
MSG msg;
pProgressBox = new CProgressBox (m_pParent);
pProgressBox->Create (IDD_PROGRESSBOX);
pProgressBox->ShowWindow (SW_SHOW);
pProgressBox->SetRange (0,
(int) ((nSizeForFiles /
(u_int64_t) (0x7c00))));
pProgressBox->SetPosition (0);
pProgressBox->SetWindowMessage ("Saving blank files");
#endif
int nPosition = 0;
for (z = 1; z < 6; z++) {
// create a file of the right size to pad it
string csfName;
// FIXME
// csfName.Format ("SCRUBP%d.DAT", z);
if (0 != nSizeForFiles) {
if ((u_int64_t) 0x3FFFFC00 <= nSizeForFiles) {
if (1 == z) // only create the first full file if we need to
{
fOut = fopen (csfName.c_str (), "wb");
// full block
// output in 0x7c00 blocks as the figure must be a multiple of this
for (u_int32_t x = 0; x < 33825; x++) {
fwrite (pBlankSector, 1, 0x7c00, fOut);
nPosition++;
#if 0 //suk
pProgressBox->
SetPosition
(nPosition);
// do the message pump thang
if (PeekMessage (&msg,
NULL,
0,
0,
PM_REMOVE)) {
// PeekMessage has found a message--process it
if (msg.message !=
WM_CANCELLED) {
TranslateMessage (&msg); // Translate virt. key codes
DispatchMessage (&msg); // Dispatch msg. to window
} else {
// quit message received - simply exit
AddToLog ("Save cancelled");
delete pProgressBox;
if (NULL !=
fOut) {
fclose (fOut);
}
free (pBootBin);
free (pFST);
free (pFSTCopy);
free (pFSTDummy);
return false;
}
}
#endif
}
fclose (fOut);
} else {
nPosition += 33825L;
// pProgressBox->SetPosition (nPosition);
}
nSizeForFiles =
nSizeForFiles -
(u_int64_t) 0x3FFFFC00;
} else {
fOut = fopen (csfName.c_str (), "wb");
// partially filled block - must be at the end
// output in 0x7c00 blocks as the figure must be a multiple of this
for (u_int32_t x = 0; x < nSizeForFiles;
x += 0x7C00) {
fwrite (pBlankSector, 1, 0x7c00,
fOut);
nPosition++;
#if 0 //suk
pProgressBox->SetPosition (nPosition);
// do the message pump thang
if (PeekMessage (&msg,
NULL,
0, 0, PM_REMOVE)) {
// PeekMessage has found a message--process it
if (msg.message !=
WM_CANCELLED) {
TranslateMessage (&msg); // Translate virt. key codes
DispatchMessage (&msg); // Dispatch msg. to window
} else {
// quit message received - simply exit
AddToLog ("Save cancelled");
delete pProgressBox;
if (NULL != fOut) {
fclose (fOut);
}
free (pBootBin);
free (pFST);
free (pFSTCopy);
free (pFSTDummy);
return false;
}
}
#endif
}
nSizeForFiles = 0;
fclose (fOut);
}
}
}
// delete pProgressBox;
// Phew! - now free up the memory
free (pBootBin);
free (pFST);
free (pFSTCopy);
free (pFSTDummy);
return true;
}
void CWIIDisc::FindFreeSpaceInPartition (int64_t nPartOffset,
u_int64_t *pStart,
u_int64_t *pSize)
{
// go through the data block table to find the first unused block starting
// at the offest provided and use that to search to the end of the data or
// the first marked.
// As WII games all use a ( << 2) implementation of size then we just need to
// used the supplied values divided by 4 for the returns
char cLastBlock = 1; // assume (!) that we have data at the partition start
*pSize = 0;
*pStart = 0;
for (u_int64_t i = (nPartOffset / (u_int64_t) (0x8000));
i < (nImageSize / (u_int64_t) (0x8000)); i++) {
if (cLastBlock != pFreeTable[i]) {
// change
if (1 == cLastBlock) {
// we have the first empty space
*pStart = i * (u_int64_t) (0x8000);
*pSize = (u_int64_t) (0x7c00);
} else {
// now found the end so simply can return as we have switched from a free to a used
// we have to tweak the start though as we need to remove a few values
return;
}
} else {
// same so if we have a potential run
if (0 == cLastBlock) {
// add the block to the size
*pSize += (u_int64_t) (0x7c00);
}
}
cLastBlock = pFreeTable[i];
}
// if we get here then there is no free space OR we have passed the end of the
// image after starting
if (0 == cLastBlock) {
// all okay and values are correct
} else {
// no free space
}
}
////////////////////////////////////////////////////////////
// Inverse of the be32 function - writes a 32 bit value //
// high to low //
////////////////////////////////////////////////////////////
void CWIIDisc::Write32 (u_int8_t * p, u_int32_t nVal)
{
p[0] = (nVal >> 24) & 0xFF;
p[1] = (nVal >> 16) & 0xFF;
p[2] = (nVal >> 8) & 0xFF;
p[3] = (nVal) & 0xFF;
}
////////////////////////////////////////////////////////////////////////////////////////
// This function takes two FSTs and merges them using a passed offset as the start //
// location for the new data. //
// UNFINISHED - UNFINISHED - UNFINISHED ETC...........................................//
////////////////////////////////////////////////////////////////////////////////////////
bool CWIIDisc::MergeAndRelocateFSTs (unsigned char *pFST1,
u_int32_t nSizeofFST1,
unsigned char *pFST2,
u_int32_t nSizeofFST2,
unsigned char *pNewFST,
u_int32_t * nSizeofNewFST,
u_int64_t nNewOffset,
u_int64_t nOldOffset)
{
u_int32_t nFilesFST1 = 0;
u_int32_t nFilesFST2 = 0;
//s u_int32_t nFilesNewFST = 0;
u_int32_t nStringTableOffset;
//s u_int64_t nOffsetCalc = 0;
u_int32_t nStringPad = 0;
// extract the data for FST 1
nFilesFST1 = be32 (pFST1 + 8);
// extract the data for FST 2
nFilesFST2 = be32 (pFST1 + 8);
// merge the two entry offset tables (as we then will know where the string table starts)
// copy the first one over
memcpy (pNewFST, pFST1, nFilesFST1 * 0x0C);
memcpy (pNewFST + (nFilesFST1 * 0x0C), pFST2 + 0x0C,
(nFilesFST2 - 1) * 0x0C);
nStringTableOffset = (nFilesFST1 + nFilesFST2 - 1) * 0x0c;
// now copy the string tables
memcpy (pNewFST + nStringTableOffset, pFST1 + (nFilesFST1 * 0x0C),
nSizeofFST1 - (nFilesFST1 * 0x0C));
// now search back to find the first non 0 character
nStringPad = nSizeofFST1 + ((nFilesFST2 - 1) * 0x0C);
while (0 == pNewFST[nStringPad]) {
nStringPad--;
}
nStringPad += 2;
// so that we then know the real positional offset to write to
memcpy (pNewFST + nStringPad, pFST2 + (nFilesFST2 * 0x0C),
nSizeofFST2 - (nFilesFST2 * 0x0C));
// we then need to go through both sets of data in the copied FST2 data to mark
// them correctly
// HOW TO HANDLE DUPLICATE FILENAMES???
//
// TO BE DONE - BUT NOT IN THIS APPLICATION ;)
return true;
}
//////////////////////////////////////////////////////////////////////////////
// Invert of the mark as used - to allow for //
// creation of a DIF file for a specific area e.g. mariokart partition 3 //
// Not really used these days //
//////////////////////////////////////////////////////////////////////////////
void CWIIDisc::MarkAsUnused (u_int64_t nOffset, u_int64_t nSize)
{
u_int64_t nStartValue = nOffset;
u_int64_t nEndValue = nOffset + nSize;
while ((nStartValue < nEndValue) &&
(nStartValue < ((u_int64_t) (4699979776LL) * (u_int64_t) (2))))
{
pFreeTable[nStartValue / (u_int64_t) (0x8000)] = 0;
nStartValue = nStartValue + ((u_int64_t) (0x8000));
}
}
bool CWIIDisc::DiscWriteDirect (struct image_file *image, u_int64_t nOffset,
u_int8_t * pData, unsigned int nSize)
{
int64_t nSeek;
// Simply seek to the right place
nSeek = my_fseek (image->fp, nOffset, SEEK_SET);
if (-1 == nSeek) {
// m_pParent->AddToLog ("Seek error for write");
AfxMessageBox ("io_seek");
return false;
}
if (nSize != fwrite (pData, 1, nSize, image->fp)) {
// m_pParent->AddToLog ("Write error");
AfxMessageBox ("Write error");
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// The infamous TRUCHA signing bug //
// where we change the reserved bytes in the ticket until the SHA has a //
// 0 in the first location //
////////////////////////////////////////////////////////////////////