diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 26ab98f803..9b1094dd48 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -150,7 +150,7 @@ add_library(core HW/WiimoteEmu/Encryption.cpp HW/WiimoteEmu/Speaker.cpp HW/WiimoteReal/WiimoteReal.cpp - HW/WiiSaveCrypted.cpp + HW/WiiSave.cpp IOS/Device.cpp IOS/DeviceStub.cpp IOS/IOS.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 3a1aeee2ea..ad815d88a4 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -179,7 +179,7 @@ - + @@ -437,7 +437,7 @@ - + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 25a151d371..a16e67b8e1 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -561,7 +561,7 @@ HW %28Flipper/Hollywood%29 - + HW %28Flipper/Hollywood%29 @@ -1242,7 +1242,7 @@ HW %28Flipper/Hollywood%29 - + HW %28Flipper/Hollywood%29 @@ -1578,4 +1578,4 @@ - \ No newline at end of file + diff --git a/Source/Core/Core/HW/WiiSaveCrypted.cpp b/Source/Core/Core/HW/WiiSave.cpp similarity index 68% rename from Source/Core/Core/HW/WiiSaveCrypted.cpp rename to Source/Core/Core/HW/WiiSave.cpp index 28c9150b34..5909c00e7f 100644 --- a/Source/Core/Core/HW/WiiSaveCrypted.cpp +++ b/Source/Core/Core/HW/WiiSave.cpp @@ -7,7 +7,7 @@ // Licensed under the terms of the GNU GPL, version 2 // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt -#include "Core/HW/WiiSaveCrypted.h" +#include "Core/HW/WiiSave.h" #include #include @@ -29,35 +29,30 @@ #include "Common/MsgHandler.h" #include "Common/NandPaths.h" #include "Common/StringUtil.h" -#include "Common/Swap.h" -const u8 CWiiSaveCrypted::s_sd_key[16] = {0xAB, 0x01, 0xB9, 0xD8, 0xE1, 0x62, 0x2B, 0x08, - 0xAF, 0xBA, 0xD8, 0x4D, 0xBF, 0xC2, 0xA5, 0x5D}; -const u8 CWiiSaveCrypted::s_md5_blanker[16] = {0x0E, 0x65, 0x37, 0x81, 0x99, 0xBE, 0x45, 0x17, - 0xAB, 0x06, 0xEC, 0x22, 0x45, 0x1A, 0x57, 0x93}; -const u32 CWiiSaveCrypted::s_ng_id = 0x0403AC68; +using Md5 = std::array; -bool CWiiSaveCrypted::ImportWiiSave(const std::string& filename) +constexpr std::array s_sd_initial_iv{{0x21, 0x67, 0x12, 0xE6, 0xAA, 0x1F, 0x68, 0x9F, + 0x95, 0xC5, 0xA2, 0x23, 0x24, 0xDC, 0x6A, 0x98}}; +constexpr std::array s_sd_key{{0xAB, 0x01, 0xB9, 0xD8, 0xE1, 0x62, 0x2B, 0x08, 0xAF, 0xBA, + 0xD8, 0x4D, 0xBF, 0xC2, 0xA5, 0x5D}}; +constexpr Md5 s_md5_blanker{{0x0E, 0x65, 0x37, 0x81, 0x99, 0xBE, 0x45, 0x17, 0xAB, 0x06, 0xEC, 0x22, + 0x45, 0x1A, 0x57, 0x93}}; +constexpr u32 s_ng_id = 0x0403AC68; + +bool WiiSave::Import(std::string filename) { - CWiiSaveCrypted save_file(filename); - return save_file.m_valid; + WiiSave save_file{std::move(filename)}; + return save_file.Import(); } -bool CWiiSaveCrypted::ExportWiiSave(u64 title_id) +bool WiiSave::Export(u64 title_id, std::string export_path) { - CWiiSaveCrypted export_save("", title_id); - if (export_save.m_valid) - { - SuccessAlertT("Successfully exported file to %s", export_save.m_encrypted_save_path.c_str()); - } - else - { - PanicAlertT("Export failed"); - } - return export_save.m_valid; + WiiSave export_save{title_id, std::move(export_path)}; + return export_save.Export(); } -void CWiiSaveCrypted::ExportAllSaves() +size_t WiiSave::ExportAll(std::string export_path) { std::string title_folder = File::GetUserPath(D_WIIROOT_IDX) + "/title"; std::vector titles; @@ -85,56 +80,50 @@ void CWiiSaveCrypted::ExportAllSaves() } } } - SuccessAlertT("Found %zu save file(s)", titles.size()); - u32 success = 0; + size_t exported_save_count = 0; for (const u64& title : titles) { - CWiiSaveCrypted export_save{"", title}; - if (export_save.m_valid) - success++; + WiiSave export_save{title, export_path}; + if (export_save.Export()) + ++exported_save_count; } - SuccessAlertT("Successfully exported %u save(s) to %s", success, - (File::GetUserPath(D_USER_IDX) + "private/wii/title/").c_str()); + return exported_save_count; } -CWiiSaveCrypted::CWiiSaveCrypted(const std::string& filename, u64 title_id) - : m_encrypted_save_path(filename), m_title_id(title_id) +WiiSave::WiiSave(std::string filename) + : m_sd_iv{s_sd_initial_iv}, m_encrypted_save_path(std::move(filename)), m_valid{true} { - memcpy(m_sd_iv, "\x21\x67\x12\xE6\xAA\x1F\x68\x9F\x95\xC5\xA2\x23\x24\xDC\x6A\x98", 0x10); - - if (!title_id) // Import - { - mbedtls_aes_setkey_dec(&m_aes_ctx, s_sd_key, 128); - m_valid = true; - ReadHDR(); - ReadBKHDR(); - ImportWiiSaveFiles(); - // TODO: check_sig() - if (m_valid) - { - SuccessAlertT("Successfully imported save file(s)"); - } - else - { - PanicAlertT("Import failed"); - } - } - else - { - mbedtls_aes_setkey_enc(&m_aes_ctx, s_sd_key, 128); - - if (getPaths(true)) - { - m_valid = true; - WriteHDR(); - WriteBKHDR(); - ExportWiiSaveFiles(); - do_sig(); - } - } + mbedtls_aes_setkey_dec(&m_aes_ctx, s_sd_key.data(), 128); } -void CWiiSaveCrypted::ReadHDR() +bool WiiSave::Import() +{ + ReadHDR(); + ReadBKHDR(); + ImportWiiSaveFiles(); + // TODO: check_sig() + return m_valid; +} + +WiiSave::WiiSave(u64 title_id, std::string export_path) + : m_sd_iv{s_sd_initial_iv}, m_encrypted_save_path(std::move(export_path)), m_title_id{title_id} +{ + mbedtls_aes_setkey_enc(&m_aes_ctx, s_sd_key.data(), 128); + + if (getPaths(true)) + m_valid = true; +} + +bool WiiSave::Export() +{ + WriteHDR(); + WriteBKHDR(); + ExportWiiSaveFiles(); + do_sig(); + return m_valid; +} + +void WiiSave::ReadHDR() { File::IOFile data_file(m_encrypted_save_path, "rb"); if (!data_file) @@ -151,9 +140,9 @@ void CWiiSaveCrypted::ReadHDR() } data_file.Close(); - mbedtls_aes_crypt_cbc(&m_aes_ctx, MBEDTLS_AES_DECRYPT, HEADER_SZ, m_sd_iv, + mbedtls_aes_crypt_cbc(&m_aes_ctx, MBEDTLS_AES_DECRYPT, HEADER_SZ, m_sd_iv.data(), (const u8*)&m_encrypted_header, (u8*)&m_header); - u32 banner_size = Common::swap32(m_header.hdr.BannerSize); + u32 banner_size = m_header.hdr.banner_size; if ((banner_size < FULL_BNR_MIN) || (banner_size > FULL_BNR_MAX) || (((banner_size - BNR_SZ) % ICON_SZ) != 0)) { @@ -161,18 +150,17 @@ void CWiiSaveCrypted::ReadHDR() m_valid = false; return; } - m_title_id = Common::swap64(m_header.hdr.SaveGameTitle); + m_title_id = m_header.hdr.save_game_title; - u8 md5_file[16]; - u8 md5_calc[16]; - memcpy(md5_file, m_header.hdr.Md5, 0x10); - memcpy(m_header.hdr.Md5, s_md5_blanker, 0x10); - mbedtls_md5((u8*)&m_header, HEADER_SZ, md5_calc); - if (memcmp(md5_file, md5_calc, 0x10)) + Md5 md5_file = m_header.hdr.md5; + m_header.hdr.md5 = s_md5_blanker; + Md5 md5_calc; + mbedtls_md5((u8*)&m_header, HEADER_SZ, md5_calc.data()); + if (md5_file == md5_calc) { ERROR_LOG(CONSOLE, "MD5 mismatch\n %016" PRIx64 "%016" PRIx64 " != %016" PRIx64 "%016" PRIx64, - Common::swap64(md5_file), Common::swap64(md5_file + 8), Common::swap64(md5_calc), - Common::swap64(md5_calc + 8)); + Common::swap64(md5_file.data()), Common::swap64(md5_file.data() + 8), + Common::swap64(md5_calc.data()), Common::swap64(md5_calc.data() + 8)); m_valid = false; } @@ -189,7 +177,7 @@ void CWiiSaveCrypted::ReadHDR() { INFO_LOG(CONSOLE, "Creating file %s", banner_file_path.c_str()); File::IOFile banner_file(banner_file_path, "wb"); - banner_file.WriteBytes(m_header.BNR, banner_size); + banner_file.WriteBytes(m_header.banner, banner_size); } else { @@ -197,7 +185,7 @@ void CWiiSaveCrypted::ReadHDR() } } -void CWiiSaveCrypted::WriteHDR() +void WiiSave::WriteHDR() { if (!m_valid) return; @@ -205,28 +193,28 @@ void CWiiSaveCrypted::WriteHDR() std::string banner_file_path = m_wii_title_path + "/banner.bin"; u32 banner_size = static_cast(File::GetSize(banner_file_path)); - m_header.hdr.BannerSize = Common::swap32(banner_size); + m_header.hdr.banner_size = banner_size; - m_header.hdr.SaveGameTitle = Common::swap64(m_title_id); - memcpy(m_header.hdr.Md5, s_md5_blanker, 0x10); - m_header.hdr.Permissions = 0x3C; + m_header.hdr.save_game_title = m_title_id; + m_header.hdr.md5 = s_md5_blanker; + m_header.hdr.permissions = 0x3C; File::IOFile banner_file(banner_file_path, "rb"); - if (!banner_file.ReadBytes(m_header.BNR, banner_size)) + if (!banner_file.ReadBytes(m_header.banner, banner_size)) { ERROR_LOG(CONSOLE, "Failed to read banner.bin"); m_valid = false; return; } // remove nocopy flag - m_header.BNR[7] &= ~1; + m_header.banner[7] &= ~1; - u8 md5_calc[16]; - mbedtls_md5((u8*)&m_header, HEADER_SZ, md5_calc); - memcpy(m_header.hdr.Md5, md5_calc, 0x10); + Md5 md5_calc; + mbedtls_md5((u8*)&m_header, HEADER_SZ, md5_calc.data()); + m_header.hdr.md5 = std::move(md5_calc); - mbedtls_aes_crypt_cbc(&m_aes_ctx, MBEDTLS_AES_ENCRYPT, HEADER_SZ, m_sd_iv, (const u8*)&m_header, - (u8*)&m_encrypted_header); + mbedtls_aes_crypt_cbc(&m_aes_ctx, MBEDTLS_AES_ENCRYPT, HEADER_SZ, m_sd_iv.data(), + (const u8*)&m_header, (u8*)&m_encrypted_header); File::IOFile data_file(m_encrypted_save_path, "wb"); if (!data_file.WriteBytes(&m_encrypted_header, HEADER_SZ)) @@ -236,7 +224,7 @@ void CWiiSaveCrypted::WriteHDR() } } -void CWiiSaveCrypted::ReadBKHDR() +void WiiSave::ReadBKHDR() { if (!m_valid) return; @@ -257,47 +245,41 @@ void CWiiSaveCrypted::ReadBKHDR() } fpData_bin.Close(); - if (m_bk_hdr.size != Common::swap32(BK_LISTED_SZ) || - m_bk_hdr.magic != Common::swap32(BK_HDR_MAGIC)) + if (m_bk_hdr.size != BK_LISTED_SZ || m_bk_hdr.magic != BK_HDR_MAGIC) { - ERROR_LOG(CONSOLE, "Invalid Size(%x) or Magic word (%x)", m_bk_hdr.size, m_bk_hdr.magic); + ERROR_LOG(CONSOLE, "Invalid Size(%x) or Magic word (%x)", u32(m_bk_hdr.size), + u32(m_bk_hdr.magic)); m_valid = false; return; } - m_files_list_size = Common::swap32(m_bk_hdr.numberOfFiles); - m_size_of_files = Common::swap32(m_bk_hdr.sizeOfFiles); - m_total_size = Common::swap32(m_bk_hdr.totalSize); - - if (m_size_of_files + FULL_CERT_SZ != m_total_size) + if (m_bk_hdr.size_of_files + FULL_CERT_SZ != m_bk_hdr.total_size) { - WARN_LOG(CONSOLE, "Size(%x) + cert(%x) does not equal totalsize(%x)", m_size_of_files, - FULL_CERT_SZ, m_total_size); + WARN_LOG(CONSOLE, "Size(%x) + cert(%x) does not equal totalsize(%x)", + u32(m_bk_hdr.size_of_files), FULL_CERT_SZ, u32(m_bk_hdr.total_size)); } - if (m_title_id != Common::swap64(m_bk_hdr.SaveGameTitle)) + if (m_title_id != m_bk_hdr.save_game_title) { WARN_LOG(CONSOLE, "Encrypted title (%" PRIx64 ") does not match unencrypted title (%" PRIx64 ")", - m_title_id, Common::swap64(m_bk_hdr.SaveGameTitle)); + m_title_id, u64(m_bk_hdr.save_game_title)); } } -void CWiiSaveCrypted::WriteBKHDR() +void WiiSave::WriteBKHDR() { if (!m_valid) return; - m_files_list_size = 0; - m_size_of_files = 0; - - ScanForFiles(m_wii_title_path, m_files_list, &m_files_list_size, &m_size_of_files); + u32 number_of_files = 0, size_of_files = 0; + ScanForFiles(m_wii_title_path, m_files_list, &number_of_files, &size_of_files); memset(&m_bk_hdr, 0, BK_SZ); - m_bk_hdr.size = Common::swap32(BK_LISTED_SZ); - m_bk_hdr.magic = Common::swap32(BK_HDR_MAGIC); - m_bk_hdr.NGid = s_ng_id; - m_bk_hdr.numberOfFiles = Common::swap32(m_files_list_size); - m_bk_hdr.sizeOfFiles = Common::swap32(m_size_of_files); - m_bk_hdr.totalSize = Common::swap32(m_size_of_files + FULL_CERT_SZ); - m_bk_hdr.SaveGameTitle = Common::swap64(m_title_id); + m_bk_hdr.size = BK_LISTED_SZ; + m_bk_hdr.magic = BK_HDR_MAGIC; + m_bk_hdr.ngid = s_ng_id; + m_bk_hdr.number_of_files = number_of_files; + m_bk_hdr.size_of_files = size_of_files; + m_bk_hdr.total_size = size_of_files + FULL_CERT_SZ; + m_bk_hdr.save_game_title = m_title_id; File::IOFile data_file(m_encrypted_save_path, "ab"); if (!data_file.WriteBytes(&m_bk_hdr, BK_SZ)) @@ -307,7 +289,7 @@ void CWiiSaveCrypted::WriteBKHDR() } } -void CWiiSaveCrypted::ImportWiiSaveFiles() +void WiiSave::ImportWiiSaveFiles() { if (!m_valid) return; @@ -324,11 +306,10 @@ void CWiiSaveCrypted::ImportWiiSaveFiles() FileHDR file_hdr_tmp; - for (u32 i = 0; i < m_files_list_size; ++i) + for (u32 i = 0; i < m_bk_hdr.number_of_files; ++i) { memset(&file_hdr_tmp, 0, FILE_HDR_SZ); - memset(m_iv, 0, 0x10); - u32 file_size = 0; + m_iv.fill(0); if (!data_file.ReadBytes(&file_hdr_tmp, FILE_HDR_SZ)) { @@ -336,7 +317,7 @@ void CWiiSaveCrypted::ImportWiiSaveFiles() m_valid = false; } - if (Common::swap32(file_hdr_tmp.magic) != FILE_HDR_MAGIC) + if (file_hdr_tmp.magic != FILE_HDR_MAGIC) { ERROR_LOG(CONSOLE, "Bad File Header"); break; @@ -345,15 +326,14 @@ void CWiiSaveCrypted::ImportWiiSaveFiles() { // Allows files in subfolders to be escaped properly (ex: "nocopy/data00") // Special characters in path components will be escaped such as /../ - std::string file_path = Common::EscapePath(reinterpret_cast(file_hdr_tmp.name)); + std::string file_path = Common::EscapePath(file_hdr_tmp.name.data()); std::string file_path_full = m_wii_title_path + '/' + file_path; File::CreateFullPath(file_path_full); const File::FileInfo file_info(file_path_full); if (file_hdr_tmp.type == 1) { - file_size = Common::swap32(file_hdr_tmp.size); - u32 file_size_rounded = Common::AlignUp(file_size, BLOCK_SZ); + u32 file_size_rounded = Common::AlignUp(file_hdr_tmp.size, BLOCK_SZ); std::vector file_data(file_size_rounded); std::vector file_data_enc(file_size_rounded); @@ -364,14 +344,14 @@ void CWiiSaveCrypted::ImportWiiSaveFiles() break; } - memcpy(m_iv, file_hdr_tmp.IV, 0x10); - mbedtls_aes_crypt_cbc(&m_aes_ctx, MBEDTLS_AES_DECRYPT, file_size_rounded, m_iv, + m_iv = file_hdr_tmp.iv; + mbedtls_aes_crypt_cbc(&m_aes_ctx, MBEDTLS_AES_DECRYPT, file_size_rounded, m_iv.data(), static_cast(file_data_enc.data()), file_data.data()); INFO_LOG(CONSOLE, "Creating file %s", file_path_full.c_str()); File::IOFile raw_save_file(file_path_full, "wb"); - raw_save_file.WriteBytes(file_data.data(), file_size); + raw_save_file.WriteBytes(file_data.data(), file_hdr_tmp.size); } else if (file_hdr_tmp.type == 2) { @@ -391,12 +371,12 @@ void CWiiSaveCrypted::ImportWiiSaveFiles() } } -void CWiiSaveCrypted::ExportWiiSaveFiles() +void WiiSave::ExportWiiSaveFiles() { if (!m_valid) return; - for (u32 i = 0; i < m_files_list_size; i++) + for (u32 i = 0; i < m_bk_hdr.number_of_files; i++) { FileHDR file_hdr_tmp; memset(&file_hdr_tmp, 0, FILE_HDR_SZ); @@ -414,9 +394,9 @@ void CWiiSaveCrypted::ExportWiiSaveFiles() } u32 file_size_rounded = Common::AlignUp(file_size, BLOCK_SZ); - file_hdr_tmp.magic = Common::swap32(FILE_HDR_MAGIC); - file_hdr_tmp.size = Common::swap32(file_size); - file_hdr_tmp.Permissions = 0x3c; + file_hdr_tmp.magic = FILE_HDR_MAGIC; + file_hdr_tmp.size = file_size; + file_hdr_tmp.permissions = 0x3c; std::string name = Common::UnescapeFileName(m_files_list[i].substr(m_wii_title_path.length() + 1)); @@ -428,7 +408,7 @@ void CWiiSaveCrypted::ExportWiiSaveFiles() m_valid = false; return; } - strncpy((char*)file_hdr_tmp.name, name.c_str(), sizeof(file_hdr_tmp.name)); + std::strncpy(file_hdr_tmp.name.data(), name.c_str(), file_hdr_tmp.name.size()); { File::IOFile fpData_bin(m_encrypted_save_path, "ab"); @@ -459,8 +439,9 @@ void CWiiSaveCrypted::ExportWiiSaveFiles() m_valid = false; } - mbedtls_aes_crypt_cbc(&m_aes_ctx, MBEDTLS_AES_ENCRYPT, file_size_rounded, file_hdr_tmp.IV, - static_cast(file_data.data()), file_data_enc.data()); + mbedtls_aes_crypt_cbc(&m_aes_ctx, MBEDTLS_AES_ENCRYPT, file_size_rounded, + file_hdr_tmp.iv.data(), static_cast(file_data.data()), + file_data_enc.data()); File::IOFile fpData_bin(m_encrypted_save_path, "ab"); if (!fpData_bin.WriteBytes(file_data_enc.data(), file_size_rounded)) @@ -471,7 +452,7 @@ void CWiiSaveCrypted::ExportWiiSaveFiles() } } -void CWiiSaveCrypted::do_sig() +void WiiSave::do_sig() { if (!m_valid) return; @@ -514,7 +495,7 @@ void CWiiSaveCrypted::do_sig() generate_ecdsa(ap_sig, ap_sig + 30, ng_priv, hash); make_ec_cert(ap_cert, ap_sig, signer, name, ap_priv, 0); - data_size = Common::swap32(m_bk_hdr.sizeOfFiles) + 0x80; + data_size = m_bk_hdr.size_of_files + 0x80; File::IOFile data_file(m_encrypted_save_path, "rb"); if (!data_file) @@ -550,8 +531,8 @@ void CWiiSaveCrypted::do_sig() m_valid = data_file.IsGood(); } -void CWiiSaveCrypted::make_ec_cert(u8* cert, const u8* sig, const char* signer, const char* name, - const u8* priv, const u32 key_id) +void WiiSave::make_ec_cert(u8* cert, const u8* sig, const char* signer, const char* name, + const u8* priv, const u32 key_id) { memset(cert, 0, 0x180); *(u32*)cert = Common::swap32(0x10002); @@ -564,7 +545,7 @@ void CWiiSaveCrypted::make_ec_cert(u8* cert, const u8* sig, const char* signer, ec_priv_to_pub(priv, cert + 0x108); } -bool CWiiSaveCrypted::getPaths(bool for_export) +bool WiiSave::getPaths(bool for_export) { if (m_title_id) { @@ -591,12 +572,7 @@ bool CWiiSaveCrypted::getPaths(bool for_export) ERROR_LOG(CONSOLE, "No banner file found for title %s", game_id); return false; } - if (m_encrypted_save_path.length() == 0) - { - // If no path was passed, use User folder - m_encrypted_save_path = File::GetUserPath(D_USER_IDX); - } - m_encrypted_save_path += StringFromFormat("private/wii/title/%s/data.bin", game_id); + m_encrypted_save_path += StringFromFormat("/private/wii/title/%s/data.bin", game_id); File::CreateFullPath(m_encrypted_save_path); } else @@ -606,9 +582,8 @@ bool CWiiSaveCrypted::getPaths(bool for_export) return true; } -void CWiiSaveCrypted::ScanForFiles(const std::string& save_directory, - std::vector& file_list, u32* num_files, - u32* size_files) +void WiiSave::ScanForFiles(const std::string& save_directory, std::vector& file_list, + u32* num_files, u32* size_files) { std::vector directories; directories.push_back(save_directory); @@ -653,6 +628,6 @@ void CWiiSaveCrypted::ScanForFiles(const std::string& save_directory, *size_files = size; } -CWiiSaveCrypted::~CWiiSaveCrypted() +WiiSave::~WiiSave() { } diff --git a/Source/Core/Core/HW/WiiSave.h b/Source/Core/Core/HW/WiiSave.h new file mode 100644 index 0000000000..995082eebd --- /dev/null +++ b/Source/Core/Core/HW/WiiSave.h @@ -0,0 +1,135 @@ +// Copyright 2010 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/Swap.h" + +class WiiSave +{ +public: + /// Import a save into the NAND from a .bin file. + static bool Import(std::string filename); + /// Export a save to a .bin file. + static bool Export(u64 title_id, std::string export_path); + /// Export all saves that are in the NAND. Returns the number of exported saves. + static size_t ExportAll(std::string export_path); + +private: + explicit WiiSave(std::string filename); + explicit WiiSave(u64 title_id, std::string export_path); + ~WiiSave(); + + bool Import(); + bool Export(); + + void ReadHDR(); + void ReadBKHDR(); + void WriteHDR(); + void WriteBKHDR(); + void ImportWiiSaveFiles(); + void ExportWiiSaveFiles(); + void do_sig(); + void make_ec_cert(u8* cert, const u8* sig, const char* signer, const char* name, const u8* priv, + const u32 key_id); + bool getPaths(bool for_export = false); + void ScanForFiles(const std::string& save_directory, std::vector& file_list, + u32* num_files, u32* size_files); + + mbedtls_aes_context m_aes_ctx; + std::array m_sd_iv; + std::vector m_files_list; + + std::string m_encrypted_save_path; + + std::string m_wii_title_path; + + std::array m_iv; + + u64 m_title_id; + + bool m_valid; + + enum + { + BLOCK_SZ = 0x40, + HDR_SZ = 0x20, + ICON_SZ = 0x1200, + BNR_SZ = 0x60a0, + FULL_BNR_MIN = 0x72a0, // BNR_SZ + 1*ICON_SZ + FULL_BNR_MAX = 0xF0A0, // BNR_SZ + 8*ICON_SZ + HEADER_SZ = 0xF0C0, // HDR_SZ + FULL_BNR_MAX + BK_LISTED_SZ = 0x70, // Size before rounding to nearest block + BK_SZ = 0x80, + FILE_HDR_SZ = 0x80, + + SIG_SZ = 0x40, + NG_CERT_SZ = 0x180, + AP_CERT_SZ = 0x180, + FULL_CERT_SZ = 0x3C0, // SIG_SZ + NG_CERT_SZ + AP_CERT_SZ + 0x80? + + BK_HDR_MAGIC = 0x426B0001, + FILE_HDR_MAGIC = 0x03adf17e + }; + +#pragma pack(push, 1) + + struct DataBinHeader // encrypted + { + Common::BigEndianValue save_game_title; + Common::BigEndianValue banner_size; // (0x72A0 or 0xF0A0, also seen 0xBAA0) + u8 permissions; + u8 unk1; // maybe permissions is a be16 + std::array md5; // md5 of plaintext header with md5 blanker applied + Common::BigEndianValue unk2; + }; + + struct Header + { + DataBinHeader hdr; + u8 banner[FULL_BNR_MAX]; + }; + + struct BkHeader // Not encrypted + { + Common::BigEndianValue size; // 0x00000070 + // u16 magic; // 'Bk' + // u16 magic2; // or version (0x0001) + Common::BigEndianValue magic; // 0x426B0001 + Common::BigEndianValue ngid; + Common::BigEndianValue number_of_files; + Common::BigEndianValue size_of_files; + Common::BigEndianValue unk1; + Common::BigEndianValue unk2; + Common::BigEndianValue total_size; + std::array unk3; + Common::BigEndianValue save_game_title; + std::array mac_address; + std::array padding; + }; + + struct FileHDR // encrypted + { + Common::BigEndianValue magic; // 0x03adf17e + Common::BigEndianValue size; + u8 permissions; + u8 attrib; + u8 type; // (1=file, 2=directory) + std::array name; + std::array iv; + std::array unk; + }; +#pragma pack(pop) + + Header m_header; + Header m_encrypted_header; + BkHeader m_bk_hdr; +}; diff --git a/Source/Core/Core/HW/WiiSaveCrypted.h b/Source/Core/Core/HW/WiiSaveCrypted.h deleted file mode 100644 index bd4f74838c..0000000000 --- a/Source/Core/Core/HW/WiiSaveCrypted.h +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2010 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include - -#include "Common/CommonTypes.h" - -class CWiiSaveCrypted -{ -public: - bool static ImportWiiSave(const std::string& filename); - bool static ExportWiiSave(u64 title_id); - void static ExportAllSaves(); - -private: - CWiiSaveCrypted(const std::string& filename, u64 title_id = 0); - ~CWiiSaveCrypted(); - void ReadHDR(); - void ReadBKHDR(); - void WriteHDR(); - void WriteBKHDR(); - void Extract() {} - void ImportWiiSaveFiles(); - void ExportWiiSaveFiles(); - void do_sig(); - void make_ec_cert(u8* cert, const u8* sig, const char* signer, const char* name, const u8* priv, - const u32 key_id); - bool getPaths(bool for_export = false); - void ScanForFiles(const std::string& save_directory, std::vector& file_list, - u32* num_files, u32* size_files); - - static const u8 s_sd_key[16]; - static const u8 s_md5_blanker[16]; - static const u32 s_ng_id; - - mbedtls_aes_context m_aes_ctx; - u8 m_sd_iv[0x10]; - std::vector m_files_list; - - std::string m_encrypted_save_path; - - std::string m_wii_title_path; - - u8 m_iv[0x10]; - - u32 m_files_list_size; - u32 m_size_of_files; - u32 m_total_size; - - u64 m_title_id; - - bool m_valid; - - enum - { - BLOCK_SZ = 0x40, - HDR_SZ = 0x20, - ICON_SZ = 0x1200, - BNR_SZ = 0x60a0, - FULL_BNR_MIN = 0x72a0, // BNR_SZ + 1*ICON_SZ - FULL_BNR_MAX = 0xF0A0, // BNR_SZ + 8*ICON_SZ - HEADER_SZ = 0xF0C0, // HDR_SZ + FULL_BNR_MAX - BK_LISTED_SZ = 0x70, // Size before rounding to nearest block - BK_SZ = 0x80, - FILE_HDR_SZ = 0x80, - - SIG_SZ = 0x40, - NG_CERT_SZ = 0x180, - AP_CERT_SZ = 0x180, - FULL_CERT_SZ = 0x3C0, // SIG_SZ + NG_CERT_SZ + AP_CERT_SZ + 0x80? - - BK_HDR_MAGIC = 0x426B0001, - FILE_HDR_MAGIC = 0x03adf17e - }; - -#pragma pack(push, 1) - - struct Data_Bin_HDR // encrypted - { - u64 SaveGameTitle; - u32 BannerSize; // (0x72A0 or 0xF0A0, also seen 0xBAA0) - u8 Permissions; - u8 unk1; // maybe permissions is a be16 - u8 Md5[0x10]; // md5 of plaintext header with md5 blanker applied - u16 unk2; - }; - - struct HEADER - { - Data_Bin_HDR hdr; - u8 BNR[FULL_BNR_MAX]; - }; - - struct BK_Header // Not encrypted - { - u32 size; // 0x00000070 - // u16 magic; // 'Bk' - // u16 magic2; // or version (0x0001) - u32 magic; // 0x426B0001 - u32 NGid; - u32 numberOfFiles; - u32 sizeOfFiles; - u32 unk1; - u32 unk2; - u32 totalSize; - u8 unk3[64]; - u64 SaveGameTitle; - u8 MACaddress[6]; - u8 padding[0x12]; - }; - - struct FileHDR // encrypted - { - u32 magic; // 0x03adf17e - u32 size; - u8 Permissions; - u8 attrib; - u8 type; // (1=file, 2=directory) - u8 name[0x45]; - u8 IV[0x10]; - u8 unk[0x20]; - }; -#pragma pack(pop) - - HEADER m_header; - HEADER m_encrypted_header; - BK_Header m_bk_hdr; -}; diff --git a/Source/Core/DolphinQt2/GameList/GameList.cpp b/Source/Core/DolphinQt2/GameList/GameList.cpp index 76aef93609..00e30abace 100644 --- a/Source/Core/DolphinQt2/GameList/GameList.cpp +++ b/Source/Core/DolphinQt2/GameList/GameList.cpp @@ -20,7 +20,7 @@ #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/HW/DVD/DVDInterface.h" -#include "Core/HW/WiiSaveCrypted.h" +#include "Core/HW/WiiSave.h" #include "Core/WiiUtils.h" #include "DiscIO/Blob.h" #include "DiscIO/Enums.h" @@ -258,14 +258,16 @@ void GameList::OpenProperties() void GameList::ExportWiiSave() { - QMessageBox result_dialog(this); + const QString export_dir = QFileDialog::getExistingDirectory( + this, tr("Select Export Directory"), QString::fromStdString(File::GetUserPath(D_USER_IDX)), + QFileDialog::ShowDirsOnly); + if (export_dir.isEmpty()) + return; - const bool success = CWiiSaveCrypted::ExportWiiSave(GetSelectedGame()->GetTitleID()); - - result_dialog.setIcon(success ? QMessageBox::Information : QMessageBox::Critical); - result_dialog.setText(success ? tr("Successfully exported save files") : - tr("Failed to export save files!")); - result_dialog.exec(); + if (WiiSave::Export(GetSelectedGame()->GetTitleID(), export_dir.toStdString())) + QMessageBox::information(this, tr("Save Export"), tr("Successfully exported save files")); + else + QMessageBox::critical(this, tr("Save Export"), tr("Failed to export save files.")); } void GameList::OpenWiki() diff --git a/Source/Core/DolphinQt2/MenuBar.cpp b/Source/Core/DolphinQt2/MenuBar.cpp index 7d4e46ceba..5f939fb087 100644 --- a/Source/Core/DolphinQt2/MenuBar.cpp +++ b/Source/Core/DolphinQt2/MenuBar.cpp @@ -26,7 +26,7 @@ #include "Core/Core.h" #include "Core/Debugger/RSO.h" #include "Core/HLE/HLE.h" -#include "Core/HW/WiiSaveCrypted.h" +#include "Core/HW/WiiSave.h" #include "Core/HW/Wiimote.h" #include "Core/Host.h" #include "Core/IOS/ES/ES.h" @@ -896,13 +896,26 @@ void MenuBar::ImportWiiSave() tr("Wii save files (*.bin);;" "All Files (*)")); - if (!file.isEmpty()) - CWiiSaveCrypted::ImportWiiSave(file.toStdString()); + if (file.isEmpty()) + return; + + if (WiiSave::Import(file.toStdString())) + QMessageBox::information(this, tr("Save Import"), tr("Successfully imported save files.")); + else + QMessageBox::critical(this, tr("Save Import"), tr("Failed to import save files.")); } void MenuBar::ExportWiiSaves() { - CWiiSaveCrypted::ExportAllSaves(); + const QString export_dir = QFileDialog::getExistingDirectory( + this, tr("Select Export Directory"), QString::fromStdString(File::GetUserPath(D_USER_IDX)), + QFileDialog::ShowDirsOnly); + if (export_dir.isEmpty()) + return; + + const size_t count = WiiSave::ExportAll(export_dir.toStdString()); + QMessageBox::information(this, tr("Save Export"), + tr("Exported %n save(s)", "", static_cast(count))); } void MenuBar::CheckNAND() diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp index d123d3ccca..ea6f504e7c 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -45,7 +45,7 @@ #include "Core/HW/GCPad.h" #include "Core/HW/ProcessorInterface.h" #include "Core/HW/SI/SI_Device.h" -#include "Core/HW/WiiSaveCrypted.h" +#include "Core/HW/WiiSave.h" #include "Core/HW/Wiimote.h" #include "Core/Host.h" #include "Core/HotkeyManager.h" @@ -1204,7 +1204,7 @@ void CFrame::OnLoadGameCubeIPLEUR(wxCommandEvent&) void CFrame::OnExportAllSaves(wxCommandEvent& WXUNUSED(event)) { - CWiiSaveCrypted::ExportAllSaves(); + WiiSave::ExportAll(File::GetUserPath(D_USER_IDX)); } void CFrame::OnImportSave(wxCommandEvent& WXUNUSED(event)) @@ -1215,7 +1215,7 @@ void CFrame::OnImportSave(wxCommandEvent& WXUNUSED(event)) wxFD_OPEN | wxFD_PREVIEW | wxFD_FILE_MUST_EXIST, this); if (!path.IsEmpty()) - CWiiSaveCrypted::ImportWiiSave(WxStrToStr(path)); + WiiSave::Import(WxStrToStr(path)); } void CFrame::OnShowCheatsWindow(wxCommandEvent& WXUNUSED(event)) diff --git a/Source/Core/DolphinWX/GameListCtrl.cpp b/Source/Core/DolphinWX/GameListCtrl.cpp index 30c973eb42..fcf0aa3d0f 100644 --- a/Source/Core/DolphinWX/GameListCtrl.cpp +++ b/Source/Core/DolphinWX/GameListCtrl.cpp @@ -45,7 +45,7 @@ #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/HW/DVD/DVDInterface.h" -#include "Core/HW/WiiSaveCrypted.h" +#include "Core/HW/WiiSave.h" #include "Core/Movie.h" #include "Core/SysConf.h" #include "Core/TitleDatabase.h" @@ -988,7 +988,7 @@ void GameListCtrl::OnExportSave(wxCommandEvent& WXUNUSED(event)) { const UICommon::GameFile* iso = GetSelectedISO(); if (iso) - CWiiSaveCrypted::ExportWiiSave(iso->GetTitleID()); + WiiSave::Export(iso->GetTitleID(), File::GetUserPath(D_USER_IDX)); } // Save this file as the default file