athena/src/athena/WiiSaveWriter.cpp

254 lines
6.5 KiB
C++
Raw Normal View History

2016-03-04 15:00:12 -08:00
#include "athena/WiiSaveWriter.hpp"
#include "athena/WiiSave.hpp"
#include "athena/WiiFile.hpp"
#include "athena/WiiBanner.hpp"
#include "athena/WiiImage.hpp"
#include "athena/WiiSave.hpp"
#include "athena/WiiFile.hpp"
#include "athena/WiiBanner.hpp"
#include "athena/MemoryWriter.hpp"
#include "athena/Utility.hpp"
2014-04-20 02:14:15 -07:00
#include "aes.hpp"
2013-02-15 20:22:16 -08:00
#include "ec.h"
#include "md5.h"
#include "sha1.h"
#include <stdio.h>
#include <vector>
#include <string.h>
#include <sys/stat.h>
#include <iostream>
#include <iomanip>
2016-03-04 15:00:12 -08:00
namespace athena
2013-07-20 20:57:20 -07:00
{
namespace io
{
2015-05-18 20:24:56 -07:00
WiiSaveWriter::WiiSaveWriter(const std::string& filename)
: base(filename)
2013-02-15 20:22:16 -08:00
{
2014-04-20 02:14:15 -07:00
base::setEndian(Endian::BigEndian);
2013-02-15 20:22:16 -08:00
}
2015-05-18 20:24:56 -07:00
bool WiiSaveWriter::writeSave(WiiSave* save, atUint8* macAddress, atUint32 ngId, atUint8* ngPriv, atUint8* ngSig, atUint32 ngKeyId, const std::string& filepath)
2013-02-15 20:22:16 -08:00
{
2014-04-20 02:14:15 -07:00
if (!save)
2015-07-21 22:37:22 -07:00
{
atError("save cannot be NULL");
return false;
}
2015-05-18 20:24:56 -07:00
2013-02-15 20:22:16 -08:00
if (filepath != "")
m_filepath = filepath;
writeBanner(save->banner());
2014-04-20 02:14:15 -07:00
base::writeUint32(0x70);
base::writeUint32(0x426B0001);
base::writeUint32(ngId); // NG-ID
base::writeUint32(save->fileCount());
2014-04-20 02:14:15 -07:00
base::writeUint32(0); // Size of files;
base::seek(8);
2014-04-20 02:14:15 -07:00
base::writeUint32(0); // totalSize
base::seek(64);
2014-04-20 02:14:15 -07:00
base::writeUint64(save->banner()->gameID());
base::writeBytes((atInt8*)macAddress, 6);
base::seek(2); // unknown;
base::seek(0x10); // padding;
atUint32 totalSize = 0;
2015-05-18 20:24:56 -07:00
for (WiiFile* file : save->allFiles())
2013-02-15 20:22:16 -08:00
{
totalSize += writeFile(file);
2013-02-15 20:22:16 -08:00
}
2015-05-18 20:24:56 -07:00
atUint64 pos = base::position();
2013-02-15 20:22:16 -08:00
// Write size data
2014-04-20 02:14:15 -07:00
base::seek(0xF0C0 + 0x10, SeekOrigin::Begin);
base::writeUint32(totalSize);
base::seek(0xF0C0 + 0x1C, SeekOrigin::Begin);
base::writeUint32(totalSize + 0x3c0);
base::seek(pos, SeekOrigin::Begin);
2013-02-15 20:22:16 -08:00
writeCerts(totalSize, ngId, ngPriv, ngSig, ngKeyId);
base::save();
2013-02-15 20:22:16 -08:00
return true;
}
2015-05-18 20:24:56 -07:00
void WiiSaveWriter::writeBanner(WiiBanner* banner)
2013-02-15 20:22:16 -08:00
{
2014-04-20 02:14:15 -07:00
base::setEndian(Endian::BigEndian);
base::writeInt64(banner->gameID());
2015-05-18 20:24:56 -07:00
base::writeInt32((0x60a0 + 0x1200) * (atUint32)banner->icons().size());
base::writeByte((atInt8)banner->permissions());
base::seek(1);
base::writeBytes((atInt8*)MD5_BLANKER, 16);
base::seek(2);
base::writeInt32(0x5749424E); // WIBN
base::writeInt32(banner->flags());
base::writeInt16(banner->animationSpeed());
base::seek(22);
2013-02-15 20:22:16 -08:00
base::writeStringAsWString(banner->title());
2013-02-15 20:22:16 -08:00
if (base::position() != 0x0080)
2014-04-20 02:14:15 -07:00
base::seek(0x0080, SeekOrigin::Begin);
2013-02-15 20:22:16 -08:00
base::writeStringAsWString(banner->subtitle());
2013-02-15 20:22:16 -08:00
if (base::position() != 0x00C0)
2014-04-20 02:14:15 -07:00
base::seek(0x00C0, SeekOrigin::Begin);
2013-02-15 20:22:16 -08:00
WiiImage* bannerImage = banner->bannerImage();
2015-05-18 20:24:56 -07:00
base::writeBytes((atInt8*)bannerImage->data(), bannerImage->width()*bannerImage->height() * 2);
2013-02-15 20:22:16 -08:00
// For empty icons
2015-05-18 20:24:56 -07:00
atUint8* tmpIcon = new atUint8[48 * 48 * 2];
memset(tmpIcon, 0, 48 * 48 * 2);
for (atUint32 i = 0; i < 8; ++i)
2013-02-15 20:22:16 -08:00
{
if (i < banner->icons().size())
{
writeImage(banner->icons()[i]);
}
else
{
2015-05-18 20:24:56 -07:00
base::writeBytes((atInt8*)tmpIcon, 48 * 48 * 2);
2013-02-15 20:22:16 -08:00
}
}
delete[] tmpIcon; // delete tmp buffer;
atUint8* hash = new atUint8[0x10];
MD5Hash::MD5(hash, (atUint8*)base::data(), 0xF0C0);
2014-04-20 02:14:15 -07:00
base::seek(0x0E, SeekOrigin::Begin);
base::writeBytes((atInt8*)hash, 0x10);
2013-02-15 20:22:16 -08:00
std::unique_ptr<IAES> aes = NewAES();
aes->setKey(SD_KEY);
atUint8 data[0xF0C0];
memcpy(data, base::data(), 0xF0C0);
atUint8 tmpIV[26];
2013-02-15 20:22:16 -08:00
memcpy(tmpIV, SD_IV, 16);
aes->encrypt(tmpIV, data, data, 0xF0C0);
2013-02-15 20:22:16 -08:00
2014-04-20 02:14:15 -07:00
base::seek(0, SeekOrigin::Begin);
base::writeBytes((atInt8*)data, 0xF0C0);
2014-04-20 02:14:15 -07:00
base::seek(0xF0C0, SeekOrigin::Begin);
2013-02-15 20:22:16 -08:00
}
2015-05-18 20:24:56 -07:00
atUint32 WiiSaveWriter::writeFile(WiiFile* file)
2013-02-15 20:22:16 -08:00
{
atUint32 ret = 0x80;
2013-02-15 20:22:16 -08:00
// Write the File magic
2014-04-20 02:14:15 -07:00
base::writeUint32(0x03ADF17E);
base::writeUint32(file->length());
base::writeByte(file->permissions());
base::writeByte(file->attributes());
base::writeByte(file->type());
2013-02-15 20:22:16 -08:00
atUint8 name[0x45];
utility::fillRandom(name, 0x45);
memcpy(name, file->fullpath().c_str(), file->fullpath().size());
name[file->fullpath().size()] = '\0';
base::writeBytes((atInt8*)name, 0x45);
atUint8 iv[16];
utility::fillRandom(iv, 0x10);
base::writeBytes((atInt8*)iv, 0x10);
atUint8 crap[0x20];
utility::fillRandom(crap, 0x20);
base::writeBytes((atInt8*)crap, 0x20);
2013-02-15 20:22:16 -08:00
if (file->type() == WiiFile::File)
{
int roundedSize = (file->length() + 63) & ~63;
atUint8* data = new atUint8[roundedSize];
2013-02-15 20:22:16 -08:00
memset(data, 0, roundedSize);
std::unique_ptr<IAES> aes = NewAES();
aes->setKey(SD_KEY);
aes->encrypt(iv, file->data(), data, roundedSize);
2013-02-15 20:22:16 -08:00
base::writeBytes((atInt8*)data, roundedSize);
2013-02-15 20:22:16 -08:00
ret += roundedSize;
delete[] data;
}
return ret;
}
void WiiSaveWriter::writeImage(WiiImage* image)
{
atInt8* data = (atInt8*)image->data();
base::writeBytes(data, image->width() * image->height() * 2);
2013-02-15 20:22:16 -08:00
}
2015-05-18 20:24:56 -07:00
void WiiSaveWriter::writeCerts(atUint32 filesSize, atUint32 ngId, atUint8* ngPriv, atUint8* ngSig, atUint32 ngKeyId)
2013-02-15 20:22:16 -08:00
{
#if 0
atUint8 sig[0x40];
atUint8 ngCert[0x180];
atUint8 apCert[0x180];
atUint8* hash;
atUint8 apPriv[30];
atUint8 apSig[60];
2013-02-15 20:22:16 -08:00
char signer[64];
char name[64];
atUint8* data;
atUint32 dataSize;
2013-02-15 20:22:16 -08:00
sprintf(signer, "Root-CA00000001-MS00000002");
sprintf(name, "NG%08x", ngId);
make_ec_cert(ngCert, ngSig, signer, name, ngPriv, ngKeyId);
memset(apPriv, 0, 30);
apPriv[10] = 1;
memset(apSig, 81, 30);
sprintf(signer, "Root-CA00000001-MS00000002-NG%08x", ngId);
sprintf(name, "AP%08x%08x", 1, 2);
make_ec_cert(apCert, apSig, signer, name, apPriv, 0);
hash = getSha1(apCert + 0x80, 0x100);
2015-05-18 20:24:56 -07:00
generate_ecdsa(apSig, apSig + 30, ngPriv, hash);
2013-02-15 20:22:16 -08:00
make_ec_cert(apCert, apSig, signer, name, apPriv, 0);
delete[] hash;
dataSize = filesSize + 0x80;
data = new atUint8[dataSize];
atUint8* rawData = base::data();
2013-02-15 20:22:16 -08:00
memcpy(data, rawData + 0xF0C0, dataSize);
hash = getSha1(data, dataSize);
atUint8* hash2 = getSha1(hash, 20);
2013-02-15 20:22:16 -08:00
delete[] hash;
delete[] data;
2015-05-18 20:24:56 -07:00
generate_ecdsa(sig, sig + 30, apPriv, hash2);
2013-02-15 20:22:16 -08:00
int stuff = 0x2f536969;
2015-05-18 20:24:56 -07:00
if (!utility::isSystemBigEndian())
stuff = utility::swap32(stuff);
2013-02-15 20:22:16 -08:00
2015-05-18 20:24:56 -07:00
*(atUint32*)(sig + 60) = stuff;
2013-02-15 20:22:16 -08:00
delete[] hash2;
base::writeBytes((atInt8*)sig, 0x40);
base::writeBytes((atInt8*)ngCert, 0x180);
base::writeBytes((atInt8*)apCert, 0x180);
#endif
2013-02-15 20:22:16 -08:00
}
2013-07-20 20:57:20 -07:00
} // io
2013-07-20 20:57:20 -07:00
} // zelda