athena/src/athena/WiiSaveReader.cpp

330 lines
8.3 KiB
C++
Raw Normal View History

2016-03-04 15:00:12 -08:00
#include "athena/WiiSaveReader.hpp"
#include "athena/WiiSave.hpp"
#include "athena/WiiFile.hpp"
#include "athena/WiiImage.hpp"
#include "athena/WiiBanner.hpp"
#include "athena/Utility.hpp"
#include "athena/FileWriter.hpp"
2013-02-15 20:22:16 -08:00
#include "md5.h"
#include "aes.hpp"
2013-02-15 20:22:16 -08:00
#include "ec.h"
#include "sha1.h"
#include <iostream>
#include <iomanip>
#include <string.h>
2016-03-04 15:00:12 -08:00
namespace athena
2013-07-20 20:57:20 -07:00
{
2013-02-15 20:22:16 -08:00
namespace io
{
WiiSaveReader::WiiSaveReader(const atUint8* data, atUint64 length)
: base(data, length)
2013-02-15 20:22:16 -08:00
{
2014-04-20 02:14:15 -07:00
setEndian(Endian::BigEndian);
2013-02-15 20:22:16 -08:00
}
WiiSaveReader::WiiSaveReader(const std::string& filename)
: base(filename)
2013-02-15 20:22:16 -08:00
{
2014-04-20 02:14:15 -07:00
setEndian(Endian::BigEndian);
2013-02-15 20:22:16 -08:00
}
WiiSave* WiiSaveReader::readSave()
{
WiiSave* ret = new WiiSave;
2015-05-18 20:24:56 -07:00
2015-08-29 01:34:22 -07:00
if (length() < 0xF0C0)
2014-02-26 21:46:26 -08:00
{
2015-08-29 01:34:22 -07:00
atError("Not a valid WiiSave");
return nullptr;
}
2013-02-15 20:22:16 -08:00
2015-08-29 01:34:22 -07:00
WiiBanner* banner = this->readBanner();
2013-02-15 20:22:16 -08:00
2015-08-29 01:34:22 -07:00
if (!banner)
{
atError("Invalid banner");
return nullptr;
}
2015-05-18 20:24:56 -07:00
2015-08-29 01:34:22 -07:00
ret->setBanner(banner);
atUint32 bkVer = base::readUint32();
2013-02-15 20:22:16 -08:00
2015-08-29 01:34:22 -07:00
if (bkVer != 0x00000070)
{
atError("Invalid BacKup header size");
return nullptr;
}
2013-02-15 20:22:16 -08:00
2015-08-29 01:34:22 -07:00
atUint32 bkMagic = base::readUint32();
2013-02-15 20:22:16 -08:00
2015-08-29 01:34:22 -07:00
if (bkMagic != 0x426B0001)
{
atError("Invalid BacKup header magic");
return nullptr;
}
2014-02-26 21:46:26 -08:00
2015-08-29 01:34:22 -07:00
/*atUint32 ngId =*/ base::readUint32();
atUint32 numFiles = base::readUint32();
2015-05-18 20:24:56 -07:00
2015-08-29 01:34:22 -07:00
/*int fileSize =*/ base::readUint32();
base::seek(8); // skip unknown data;
2015-05-18 20:24:56 -07:00
2015-08-29 01:34:22 -07:00
atUint32 totalSize = base::readUint32();
base::seek(64); // Unknown (Most likely padding)
base::seek(8);
base::seek(6);
base::seek(2);
base::seek(0x10);
2013-02-15 20:22:16 -08:00
2015-08-29 01:34:22 -07:00
std::vector<WiiFile*> files;
2014-02-26 21:46:26 -08:00
2015-08-29 01:34:22 -07:00
for (atUint32 i = 0; i < numFiles; ++i)
2013-02-15 20:22:16 -08:00
{
2015-08-29 01:34:22 -07:00
WiiFile* file = readFile();
if (file)
files.push_back(file);
2013-02-15 20:22:16 -08:00
}
2015-08-29 01:34:22 -07:00
ret->setRoot(buildTree(files));
readCerts(totalSize);
2013-02-15 20:22:16 -08:00
return ret;
}
WiiBanner* WiiSaveReader::readBanner()
{
atUint8* dec = new atUint8[0xF0C0];
2013-02-15 20:22:16 -08:00
memset(dec, 0, 0xF0C0);
std::unique_ptr<atUint8[]> data = base::readUBytes(0xF0C0);
atUint8* oldData = base::data();
atUint64 oldPos = base::position();
atUint64 oldLen = base::length();
atUint64 gameId;
atUint32 bannerSize;
atUint8 permissions;
atUint8 md5[16];
atUint8 md5Calc[16];
atUint8 tmpIV[16];
2013-02-15 20:22:16 -08:00
memcpy(tmpIV, SD_IV, 16);
2013-09-28 08:14:13 -07:00
std::cout << "Decrypting: banner.bin...";
std::unique_ptr<IAES> aes = NewAES();
aes->setKey(SD_KEY);
aes->decrypt(tmpIV, data.get(), dec, 0xF0C0);
2013-09-28 08:14:13 -07:00
std::cout << "done" << std::endl;
2013-02-15 20:22:16 -08:00
memset(md5, 0, 16);
memset(md5Calc, 0, 16);
// Read in the MD5 sum
memcpy(md5, (dec + 0x0E), 0x10);
// Write the blanker to the buffer
memcpy((dec + 0x0E), MD5_BLANKER, 0x10);
2013-09-28 08:14:13 -07:00
MD5Hash::MD5(md5Calc, dec, 0xF0C0);
2013-02-15 20:22:16 -08:00
// Compare the Calculated MD5 to the one from the file.
// This needs to be done incase the file is corrupted.
if (memcmp(md5, md5Calc, 0x10))
{
std::cerr << "MD5 Mismatch" << std::endl;
// Make sure to reset m_reader values back to the old ones.
std::cerr << "MD5 provided: ";
2015-05-18 20:24:56 -07:00
2013-02-15 20:22:16 -08:00
for (int i = 0; i < 16; ++i)
std::cerr << std::setw(2) << std::setfill('0') << std::hex << (int)(md5[i]);
2015-05-18 20:24:56 -07:00
2013-02-15 20:22:16 -08:00
std::cerr << std::endl;
std::cerr << "MD5 Calculated: ";
2015-05-18 20:24:56 -07:00
2013-02-15 20:22:16 -08:00
for (int i = 0; i < 16; ++i)
std::cerr << std::hex << (int)(md5Calc[i]);
2015-05-18 20:24:56 -07:00
2013-02-15 20:22:16 -08:00
std::cerr << std::endl;
base::setData(oldData, oldLen);
2014-04-20 02:14:15 -07:00
base::seek(oldPos, SeekOrigin::Begin);
2015-07-21 22:37:22 -07:00
atError("MD5 Mismatch");
return nullptr;
2013-02-15 20:22:16 -08:00
}
2013-02-15 20:22:16 -08:00
// Set the binary reader buffer;
base::setData(dec, 0xF0C0);
2013-02-15 20:22:16 -08:00
// Start reading the header
2014-04-20 02:14:15 -07:00
gameId = base::readUint64();
bannerSize = base::readUint32();
permissions = base::readByte();
2014-02-26 21:46:26 -08:00
/* unk =*/ base::readByte();
base::seek(0x10);
2013-02-15 20:22:16 -08:00
// skip padding
base::seek(2);
2013-02-15 20:22:16 -08:00
int magic;
int flags;
short animSpeed;
std::string gameTitle;
std::string subTitle;
2014-04-20 02:14:15 -07:00
magic = base::readUint32();
2013-02-15 20:22:16 -08:00
// Ensure that the header magic is valid.
if (magic != 0x5749424E)
{
// Make sure to reset m_reader values back to the old ones.
base::setData(oldData, oldLen);
2014-04-20 02:14:15 -07:00
base::seek(oldPos, SeekOrigin::Begin);
2015-07-21 22:37:22 -07:00
atError("Invalid Header Magic");
return nullptr;
2013-02-15 20:22:16 -08:00
}
2014-04-20 02:14:15 -07:00
flags = base::readUint32();
animSpeed = base::readUint16();
base::seek(22);
2013-02-15 20:22:16 -08:00
gameTitle = base::readWStringAsString();
2015-05-18 20:24:56 -07: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
subTitle = base::readWStringAsString();
2015-05-18 20:24:56 -07: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
WiiBanner* banner = new WiiBanner;
banner->setGameID(gameId);
banner->setTitle(gameTitle);
banner->setSubtitle(subTitle);
banner->setBannerSize(bannerSize);
WiiImage* bannerImage = readImage(192, 64);
banner->setBannerImage(bannerImage);
banner->setAnimationSpeed(animSpeed);
banner->setPermissions(permissions);
banner->setFlags(flags);
if (banner->bannerSize() == 0x72a0)
{
WiiImage* icon = readImage(48, 48);
2015-05-18 20:24:56 -07:00
2013-02-15 20:22:16 -08:00
if (icon)
banner->addIcon(icon);
else
std::cerr << "Warning: Icon empty, skipping" << std::endl;
}
else
{
2015-05-18 20:24:56 -07:00
for (int i = 0; i < 8; i++)
2013-02-15 20:22:16 -08:00
{
WiiImage* icon = readImage(48, 48);
2015-05-18 20:24:56 -07:00
2013-02-15 20:22:16 -08:00
if (icon)
banner->addIcon(icon);
else
std::cerr << "Warning: Icon empty, skipping" << std::endl;
}
}
base::setData(oldData, oldLen);
2014-04-20 02:14:15 -07:00
base::seek(oldPos, SeekOrigin::Begin);
2013-02-15 20:22:16 -08:00
return banner;
}
WiiImage* WiiSaveReader::readImage(atUint32 width, atUint32 height)
2013-02-15 20:22:16 -08:00
{
std::unique_ptr<atUint8[]> image = base::readUBytes(width * height * 2);
2013-02-15 20:22:16 -08:00
if (!utility::isEmpty((atInt8*)image.get(), width * height * 2))
return new WiiImage(width, height, std::move(image));
2013-02-15 20:22:16 -08:00
return NULL;
}
WiiFile* WiiSaveReader::readFile()
{
atUint32 fileLen;
atUint8 permissions;
atUint8 attributes;
atUint8 type;
2013-02-15 20:22:16 -08:00
std::string name;
WiiFile* ret;
atUint32 magic = base::readUint32();
2015-05-18 20:24:56 -07:00
2013-02-15 20:22:16 -08:00
if (magic != 0x03adf17e)
{
std::cerr << "Not a valid File entry header: 0x" << std::hex << magic << std::endl;
return NULL;
}
2014-04-20 02:14:15 -07:00
fileLen = base::readUint32();
permissions = base::readByte();
attributes = base::readByte();
type = (WiiFile::Type)base::readByte();
name = std::string((const char*)base::readBytes(0x45).get());
2013-02-15 20:22:16 -08:00
ret = new WiiFile(std::string(name));
ret->setPermissions(permissions);
ret->setAttributes(attributes);
ret->setType((WiiFile::Type)type);
std::unique_ptr<atUint8[]> iv = base::readUBytes(0x10);
base::seek(0x20);
2013-02-15 20:22:16 -08:00
if (type == WiiFile::File)
{
// Read file data
int roundedLen = (fileLen + 63) & ~63;
std::unique_ptr<atUint8[]> filedata = base::readUBytes(roundedLen);
2013-02-15 20:22:16 -08:00
// Decrypt file
2013-09-28 08:14:13 -07:00
std::cout << "Decrypting: " << ret->filename() << "...";
atUint8* decData = new atUint8[roundedLen];
std::unique_ptr<IAES> aes = NewAES();
aes->setKey(SD_KEY);
aes->decrypt(iv.get(), filedata.get(), decData, roundedLen);
2013-02-15 20:22:16 -08:00
ret->setData(decData);
ret->setLength(fileLen);
2013-09-28 08:14:13 -07:00
std::cout << "done" << std::endl;
2013-02-15 20:22:16 -08:00
}
return ret;
}
void WiiSaveReader::readCerts(atUint32 totalSize)
2013-02-15 20:22:16 -08:00
{
2013-09-28 08:14:13 -07:00
std::cout << "Reading certs..." << std::endl;
atUint32 dataSize = totalSize - 0x340;
std::unique_ptr<atUint8[]> sig = base::readUBytes(0x40);
std::unique_ptr<atUint8[]> ngCert = base::readUBytes(0x180);
std::unique_ptr<atUint8[]> apCert = base::readUBytes(0x180);
2014-04-20 02:14:15 -07:00
base::seek(0xF0C0, SeekOrigin::Begin);
std::unique_ptr<atUint8[]> data = base::readUBytes(dataSize);
atUint8* hash;
2013-02-15 20:22:16 -08:00
hash = getSha1(data.get(), dataSize);
atUint8* hash2 = getSha1(hash, 20);
#if 0
2013-09-28 08:14:13 -07:00
std::cout << "validating..." << std::endl;
std::cout << (check_ec(ngCert.get(), apCert.get(), sig.get(), hash2) ? "ok" : "invalid") << "...";
2013-09-28 08:14:13 -07:00
std::cout << "done" << std::endl;
#endif
2013-02-15 20:22:16 -08:00
}
2013-07-20 20:57:20 -07:00
WiiFile* WiiSaveReader::buildTree(std::vector<WiiFile*> files)
{
// This is simply a virtual root that will contain all the other nodes
WiiFile* root = new WiiFile("");
root->setType(WiiFile::Directory);
for (WiiFile* f : files)
root->addChild(f);
return root;
}
} // io
2013-07-20 20:57:20 -07:00
} // zelda