mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 21:37:52 -07:00
Wii Save Export can now successfully create data.bin files that the wii will accept as long as the user has their wii's private keys.
requires NG-id, NG-key-id, NG-priv, NG-sig sorry for not fixing this sooner, I forgot that I cleared my wiis private keys before the initial commit git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@7642 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
parent
46b95db6c7
commit
f95bcfc1c2
@ -33,18 +33,16 @@ const u8 SDKey[16] = {0xAB, 0x01, 0xB9, 0xD8, 0xE1, 0x62, 0x2B, 0x08,
|
||||
const u8 MD5_BLANKER[0x10] = {0x0E, 0x65, 0x37, 0x81, 0x99, 0xBE, 0x45, 0x17,
|
||||
0xAB, 0x06, 0xEC, 0x22, 0x45, 0x1A, 0x57, 0x93};
|
||||
|
||||
CWiiSaveCrypted::CWiiSaveCrypted(const char* FileName, u64 title)
|
||||
: _saveGameTitle(title)
|
||||
CWiiSaveCrypted::CWiiSaveCrypted(const char* FileName, u64 TitleID)
|
||||
: m_TitleID(TitleID)
|
||||
{
|
||||
Common::ReadReplacements(replacements);
|
||||
strcpy(pathData_bin, FileName);
|
||||
memcpy(SD_IV, "\x21\x67\x12\xE6\xAA\x1F\x68\x9F\x95\xC5\xA2\x23\x24\xDC\x6A\x98", 0x10);
|
||||
|
||||
if (!title)
|
||||
if (!TitleID) // Import
|
||||
{
|
||||
AES_set_decrypt_key(SDKey, 128, &m_AES_KEY);
|
||||
|
||||
|
||||
do
|
||||
{
|
||||
b_valid = true;
|
||||
@ -66,6 +64,33 @@ CWiiSaveCrypted::CWiiSaveCrypted(const char* FileName, u64 title)
|
||||
|
||||
if (getPaths(true))
|
||||
{
|
||||
memset(&keys, 0, sizeof(_keys));
|
||||
std::string keysdir = File::GetSysDirectory() + "/Wii/";
|
||||
std::string NG_id = keysdir + "NG-id";
|
||||
std::string NG_key_id = keysdir + "NG-key-id";
|
||||
std::string NG_priv = keysdir + "NG-priv";
|
||||
std::string NG_sig = keysdir + "NG-sig";
|
||||
if (!File::Exists(NG_id) || !File::Exists(NG_key_id) ||
|
||||
!File::Exists(NG_priv) ||!File::Exists(NG_sig))
|
||||
{
|
||||
if (!AskYesNoT("Wii Private Keys not found, the exported save will only work with dolphin\n Continue?"))
|
||||
{
|
||||
PanicAlertT("Key files not found, save will not copy to wii\nPlace NG-id, NG-key-id, NG-priv, and NG-sig in the folder\n%s", keysdir.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
File::IOFile fp_key(NG_id, "rb");
|
||||
fp_key.ReadBytes(keys.NG_id, 4);
|
||||
fp_key.Open(NG_key_id, "rb");
|
||||
fp_key.ReadBytes(keys.NG_key_id, 4);
|
||||
fp_key.Open(NG_priv, "rb");
|
||||
fp_key.ReadBytes(keys.NG_priv, 30);
|
||||
fp_key.Open(NG_sig, "rb");
|
||||
fp_key.ReadBytes(keys.NG_sig, 60);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
b_valid = true;
|
||||
@ -110,7 +135,7 @@ void CWiiSaveCrypted::ReadHDR()
|
||||
b_valid = false;
|
||||
return;
|
||||
}
|
||||
_saveGameTitle = Common::swap64(_header.hdr.SaveGameTitle);
|
||||
m_TitleID = Common::swap64(_header.hdr.SaveGameTitle);
|
||||
|
||||
memcpy(md5_file, _header.hdr.Md5, 0x10);
|
||||
memcpy(_header.hdr.Md5, MD5_BLANKER, 0x10);
|
||||
@ -126,10 +151,10 @@ void CWiiSaveCrypted::ReadHDR()
|
||||
b_valid = false;
|
||||
return;
|
||||
}
|
||||
if (!File::Exists(pathBanner_bin) || AskYesNoT("%s already exists, overwrite?", pathBanner_bin))
|
||||
if (!File::Exists(BannerFilePath) || AskYesNoT("%s already exists, overwrite?", BannerFilePath.c_str()))
|
||||
{
|
||||
INFO_LOG(CONSOLE, "creating file %s", pathBanner_bin);
|
||||
File::IOFile fpBanner_bin(pathBanner_bin, "wb");
|
||||
INFO_LOG(CONSOLE, "creating file %s", BannerFilePath.c_str());
|
||||
File::IOFile fpBanner_bin(BannerFilePath, "wb");
|
||||
fpBanner_bin.WriteBytes(_header.BNR, _bannerSize);
|
||||
}
|
||||
}
|
||||
@ -139,14 +164,15 @@ void CWiiSaveCrypted::WriteHDR()
|
||||
if (!b_valid) return;
|
||||
memset(&_header, 0, HEADER_SZ);
|
||||
|
||||
_header.hdr.BannerSize = Common::swap32(File::GetSize(pathBanner_bin));
|
||||
u32 bannerSize = File::GetSize(BannerFilePath);
|
||||
_header.hdr.BannerSize = Common::swap32(bannerSize);
|
||||
|
||||
_header.hdr.SaveGameTitle = Common::swap64(_saveGameTitle);
|
||||
_header.hdr.SaveGameTitle = Common::swap64(m_TitleID);
|
||||
memcpy(_header.hdr.Md5, MD5_BLANKER, 0x10);
|
||||
_header.hdr.Permissions = 0x3C;//0x35;
|
||||
_header.hdr.Permissions = 0x35;
|
||||
|
||||
File::IOFile fpBanner_bin(pathBanner_bin, "rb");
|
||||
if (!fpBanner_bin.ReadBytes(_header.BNR, Common::swap32(_header.hdr.BannerSize)))
|
||||
File::IOFile fpBanner_bin(BannerFilePath, "rb");
|
||||
if (!fpBanner_bin.ReadBytes(_header.BNR, bannerSize))
|
||||
{
|
||||
PanicAlertT("Failed to read banner.bin");
|
||||
b_valid = false;
|
||||
@ -202,8 +228,8 @@ void CWiiSaveCrypted::ReadBKHDR()
|
||||
|
||||
if (_sizeOfFiles + FULL_CERT_SZ != _totalSize)
|
||||
WARN_LOG(CONSOLE, "Size(%x) + cert(%x) does not equal totalsize(%x)", _sizeOfFiles, FULL_CERT_SZ, _totalSize);
|
||||
if (_saveGameTitle != Common::swap64(bkhdr.SaveGameTitle))
|
||||
WARN_LOG(CONSOLE, "encrypted title (%llx) does not match unencrypted title (%llx)", _saveGameTitle, Common::swap64(bkhdr.SaveGameTitle));
|
||||
if (m_TitleID != Common::swap64(bkhdr.SaveGameTitle))
|
||||
WARN_LOG(CONSOLE, "encrypted title (%llx) does not match unencrypted title (%llx)", m_TitleID, Common::swap64(bkhdr.SaveGameTitle));
|
||||
}
|
||||
|
||||
void CWiiSaveCrypted::WriteBKHDR()
|
||||
@ -212,21 +238,15 @@ void CWiiSaveCrypted::WriteBKHDR()
|
||||
_numberOfFiles = 0;
|
||||
_sizeOfFiles = 0;
|
||||
|
||||
ScanForFiles(pathSavedir, FilesList, &_numberOfFiles, &_sizeOfFiles);
|
||||
ScanForFiles(WiiTitlePath, FilesList, &_numberOfFiles, &_sizeOfFiles);
|
||||
memset(&bkhdr, 0, BK_SZ);
|
||||
bkhdr.size = Common::swap32(BK_LISTED_SZ);
|
||||
bkhdr.magic = Common::swap32(BK_HDR_MAGIC);
|
||||
//customize this
|
||||
bkhdr.NGid = Common::swap32(1);
|
||||
//
|
||||
bkhdr.NGid = *(u32*)(keys.NG_id);
|
||||
bkhdr.numberOfFiles = Common::swap32(_numberOfFiles);
|
||||
bkhdr.sizeOfFiles = Common::swap32(_sizeOfFiles);
|
||||
bkhdr.totalSize = Common::swap32(_sizeOfFiles + FULL_CERT_SZ);
|
||||
bkhdr.SaveGameTitle = Common::swap64(_saveGameTitle);
|
||||
// customize this
|
||||
const u8 MAC[6] = {0};
|
||||
//
|
||||
memcpy(bkhdr.MACaddress, MAC, 6);
|
||||
bkhdr.SaveGameTitle = Common::swap64(m_TitleID);
|
||||
|
||||
File::IOFile fpData_bin(pathData_bin, "ab");
|
||||
if (!fpData_bin.WriteBytes(&bkhdr, BK_SZ))
|
||||
@ -279,8 +299,8 @@ void CWiiSaveCrypted::ImportWiiSaveFiles()
|
||||
fileName.replace(j, 1, iter->second);
|
||||
}
|
||||
|
||||
sprintf(pathRawSave, "%s%s", pathSavedir, fileName.c_str());
|
||||
File::CreateFullPath(pathRawSave);
|
||||
std::string fullFilePath = WiiTitlePath + fileName;
|
||||
File::CreateFullPath(fullFilePath);
|
||||
if (_tmpFileHDR.type == 1)
|
||||
{
|
||||
_fileSize = Common::swap32(_tmpFileHDR.size);
|
||||
@ -299,11 +319,11 @@ void CWiiSaveCrypted::ImportWiiSaveFiles()
|
||||
AES_cbc_encrypt((const unsigned char *)_encryptedData, _data, RoundedFileSize, &m_AES_KEY, IV, AES_DECRYPT);
|
||||
delete []_encryptedData;
|
||||
|
||||
if (!File::Exists(pathRawSave) || AskYesNoT("%s already exists, overwrite?", pathRawSave))
|
||||
if (!File::Exists(fullFilePath) || AskYesNoT("%s already exists, overwrite?", fullFilePath.c_str()))
|
||||
{
|
||||
INFO_LOG(CONSOLE, "creating file %s", pathRawSave);
|
||||
INFO_LOG(CONSOLE, "creating file %s", fullFilePath.c_str());
|
||||
|
||||
File::IOFile fpRawSaveFile(pathRawSave, "wb");
|
||||
File::IOFile fpRawSaveFile(fullFilePath, "wb");
|
||||
fpRawSaveFile.WriteBytes(_data, _fileSize);
|
||||
}
|
||||
delete []_data;
|
||||
@ -330,7 +350,7 @@ void CWiiSaveCrypted::ExportWiiSaveFiles()
|
||||
|
||||
tmpFileHDR.magic = Common::swap32(FILE_HDR_MAGIC);
|
||||
tmpFileHDR.size = Common::swap32(_fileSize);
|
||||
tmpFileHDR.Permissions = 0x3C;
|
||||
tmpFileHDR.Permissions = 0x35;
|
||||
tmpFileHDR.type = File::IsDirectory(FilesList[i]) ? 2 : 1;
|
||||
|
||||
SplitPath(FilesList[i], NULL, &__name, &__ext);
|
||||
@ -342,8 +362,6 @@ void CWiiSaveCrypted::ExportWiiSaveFiles()
|
||||
|
||||
for (size_t j = 0; (j = __name.find(iter->second, j)) != __name.npos; ++j)
|
||||
{
|
||||
/*std::string tmp = __name.substr(0, j) + iter->first +__name.substr(j+iter->second.length(), __name.length());
|
||||
__name = tmp;*/
|
||||
__name.replace(j, iter->second.length(), 1, iter->first);
|
||||
}
|
||||
}
|
||||
@ -409,16 +427,10 @@ void CWiiSaveCrypted::do_sig()
|
||||
char name[64];
|
||||
u8 *data;
|
||||
u32 data_size;
|
||||
//allow customization
|
||||
u32 ng_id = Common::swap32(1);
|
||||
u32 ng_key_id = Common::swap32(2);
|
||||
u8 ng_sig[0x3C] = {0};
|
||||
u8 ng_priv[0x1E] = {0};
|
||||
//allow customization
|
||||
|
||||
sprintf(signer, "Root-CA00000001-MS00000002");
|
||||
sprintf(name, "NG%08x", ng_id);
|
||||
make_ec_cert(ng_cert, ng_sig, signer, name, ng_priv, ng_key_id);
|
||||
sprintf(name, "NG%08x", Common::swap32(keys.NG_id));
|
||||
make_ec_cert(ng_cert, keys.NG_sig, signer, name, keys.NG_priv, Common::swap32(keys.NG_key_id));
|
||||
|
||||
|
||||
memset(ap_priv, 0, sizeof ap_priv);
|
||||
@ -426,12 +438,12 @@ void CWiiSaveCrypted::do_sig()
|
||||
|
||||
memset(ap_sig, 81, sizeof ap_sig); // temp
|
||||
|
||||
sprintf(signer, "Root-CA00000001-MS00000002-NG%08x", ng_id);
|
||||
sprintf(signer, "Root-CA00000001-MS00000002-NG%08x", Common::swap32(keys.NG_id));
|
||||
sprintf(name, "AP%08x%08x", 1, 2);
|
||||
make_ec_cert(ap_cert, ap_sig, signer, name, ap_priv, 0);
|
||||
|
||||
sha1(ap_cert + 0x80, 0x100, hash);
|
||||
generate_ecdsa(ap_sig, ap_sig + 30, ng_priv, hash);
|
||||
generate_ecdsa(ap_sig, ap_sig + 30, keys.NG_priv, hash);
|
||||
make_ec_cert(ap_cert, ap_sig, signer, name, ap_priv, 0);
|
||||
|
||||
data_size = Common::swap32(bkhdr.sizeOfFiles) + 0x80;
|
||||
@ -446,7 +458,10 @@ void CWiiSaveCrypted::do_sig()
|
||||
|
||||
fpData_bin.Seek(0xf0c0, SEEK_SET);
|
||||
if (!fpData_bin.ReadBytes(data, data_size))
|
||||
PanicAlert("read data for sig check");
|
||||
{
|
||||
b_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
sha1(data, data_size, hash);
|
||||
sha1(hash, 20, hash);
|
||||
@ -461,12 +476,11 @@ void CWiiSaveCrypted::do_sig()
|
||||
generate_ecdsa(sig, sig + 30, ap_priv, hash);
|
||||
*(u32*)(sig + 60) = Common::swap32(0x2f536969);
|
||||
|
||||
if (!fpData_bin.WriteArray(sig, sizeof(sig)))
|
||||
PanicAlert("write sig");
|
||||
if (!fpData_bin.WriteArray(ng_cert, sizeof(ng_cert)))
|
||||
PanicAlert("write NG cert");
|
||||
if (!fpData_bin.WriteArray(ap_cert, sizeof(ap_cert)))
|
||||
PanicAlert("write AP cert");
|
||||
fpData_bin.WriteArray(sig, sizeof(sig));
|
||||
fpData_bin.WriteArray(ng_cert, sizeof(ng_cert));
|
||||
fpData_bin.WriteArray(ap_cert, sizeof(ap_cert));
|
||||
|
||||
b_valid = fpData_bin.IsGood();
|
||||
}
|
||||
|
||||
|
||||
@ -485,40 +499,40 @@ void CWiiSaveCrypted::make_ec_cert(u8 *cert, u8 *sig, char *signer, char *name,
|
||||
|
||||
bool CWiiSaveCrypted::getPaths(bool forExport)
|
||||
{
|
||||
if (_saveGameTitle)
|
||||
if (m_TitleID)
|
||||
{
|
||||
sprintf(pathSavedir, "%stitle/%08x/%08x/data/",
|
||||
File::GetUserPath(D_WIIUSER_IDX).c_str(),
|
||||
(u32)(_saveGameTitle>>32), (u32)_saveGameTitle);
|
||||
sprintf(pathBanner_bin, "%sbanner.bin", pathSavedir);
|
||||
sprintf(_saveGameString, "%c%c%c%c",
|
||||
(u8)(_saveGameTitle >> 24) & 0xFF, (u8)(_saveGameTitle >> 16) & 0xFF,
|
||||
(u8)(_saveGameTitle >> 8) & 0xFF, (u8)_saveGameTitle & 0xFF);
|
||||
WiiTitlePath = Common::GetTitleDataPath(m_TitleID);
|
||||
BannerFilePath = WiiTitlePath + "banner.bin";
|
||||
}
|
||||
if (forExport)
|
||||
{
|
||||
if(!File::IsDirectory(pathSavedir))
|
||||
char GameID[5];
|
||||
sprintf(GameID, "%c%c%c%c",
|
||||
(u8)(m_TitleID >> 24) & 0xFF, (u8)(m_TitleID >> 16) & 0xFF,
|
||||
(u8)(m_TitleID >> 8) & 0xFF, (u8)m_TitleID & 0xFF);
|
||||
|
||||
if(!File::IsDirectory(WiiTitlePath))
|
||||
{
|
||||
b_valid = false;
|
||||
PanicAlertT("No save folder found for title %s", _saveGameString);
|
||||
PanicAlertT("No save folder found for title %s", GameID);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!File::Exists(pathBanner_bin))
|
||||
if(!File::Exists(BannerFilePath))
|
||||
{
|
||||
b_valid = false;
|
||||
PanicAlertT("No banner file found for title %s", _saveGameString);
|
||||
PanicAlertT("No banner file found for title %s", GameID);
|
||||
return false;
|
||||
}
|
||||
if (strlen(pathData_bin) == 0)
|
||||
strcpy(pathData_bin, "."); // If no path was passed, use current dir
|
||||
sprintf(pathData_bin, "%s/private/wii/title/%s/data.bin", pathData_bin, _saveGameString);
|
||||
sprintf(pathData_bin, "%s/private/wii/title/%s/data.bin", pathData_bin, GameID);
|
||||
File::CreateFullPath(pathData_bin);
|
||||
}
|
||||
else
|
||||
{
|
||||
File::CreateFullPath(pathSavedir);
|
||||
if (!AskYesNoT("Warning! it is advised to backup all files in the folder:\n%s\nDo you wish to continue?", pathSavedir))
|
||||
File::CreateFullPath(WiiTitlePath);
|
||||
if (!AskYesNoT("Warning! it is advised to backup all files in the folder:\n%s\nDo you wish to continue?", WiiTitlePath.c_str()))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -30,7 +30,7 @@
|
||||
class CWiiSaveCrypted
|
||||
{
|
||||
public:
|
||||
CWiiSaveCrypted(const char* FileName, u64 title = 0);
|
||||
CWiiSaveCrypted(const char* FileName, u64 TitleID = 0);
|
||||
~CWiiSaveCrypted();
|
||||
void ReadHDR();
|
||||
void ReadBKHDR();
|
||||
@ -50,11 +50,10 @@ private:
|
||||
u8 SD_IV[0x10];
|
||||
std::vector<std::string> FilesList;
|
||||
|
||||
char pathData_bin[2048],
|
||||
pathSavedir[2048],
|
||||
pathBanner_bin[2048], //should always be FULL_WII_USER_DIR "title/%08x/%08x/data/"
|
||||
pathRawSave[2048],
|
||||
_saveGameString[5];
|
||||
char pathData_bin[2048];
|
||||
|
||||
std::string BannerFilePath,
|
||||
WiiTitlePath;
|
||||
|
||||
u8 IV[0x10],
|
||||
*_encryptedData,
|
||||
@ -62,6 +61,14 @@ private:
|
||||
md5_file[16],
|
||||
md5_calc[16];
|
||||
|
||||
struct _keys
|
||||
{
|
||||
u8 NG_priv[0x1E],
|
||||
NG_sig[0x3C],
|
||||
NG_id[4],
|
||||
NG_key_id[4];
|
||||
}keys;
|
||||
|
||||
u32 _bannerSize,
|
||||
_numberOfFiles,
|
||||
_sizeOfFiles,
|
||||
@ -69,7 +76,7 @@ private:
|
||||
_fileSize,
|
||||
_roundedfileSize;
|
||||
|
||||
u64 _saveGameTitle;
|
||||
u64 m_TitleID;
|
||||
|
||||
bool b_valid,
|
||||
b_tryAgain;
|
||||
|
Loading…
Reference in New Issue
Block a user