/*---------------------------------------------------------------------------* Project: Horizon File: Exporter.cpp Copyright 2009 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Rev$ *---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include // cfg:norの初期化に必要 #include #include #include #include #include #include #include #include "Exporter.h" #include "CommonLogger.h" #include "SDMountManager.h" #include "ConsoleBackup.h" #include "HeapManager.h" #include "SdReaderWriter.h" #include "FileName.h" #include "FileTransfer.h" #include "common_Types.h" #include "Aes_define.h" #include "PlayHistoryManager.h" #include "VersionDetect.h" namespace ConsoleBackup { namespace { common::SdReaderWriter s_SdWriter; common::NtrNorData s_NtrNorData; common::CfgCountryLanguage s_CountryLanguage; ::std::string s_SysSaveRoot; const size_t EXPORT_THREAD_STACK_SIZE = 0x4000; nn::os::Thread s_ExportThread; nn::os::StackBuffer s_ExportThreadStack; wchar_t s_RootName[256]; } void DeleteTrash(std::wstring currentDirectory) { // TODO: リードオンリーのファイルが消去できない COMMON_LOGGER("Delete Trash.\n"); nn::fs::FileInputStream fis; nn::fs::Directory dir; nn::Result result; std::vector entryList; //カレントディレクトリのエントリ一覧を格納 std::vector::iterator entryIndex; common::SdMountManager::Mount(); result = dir.TryInitialize(currentDirectory.c_str()); if (result.IsSuccess()) { nn::fs::DirectoryEntry entry; s32 numEntry; for (;;) { result = dir.TryRead(&numEntry, &entry, 1); if (result.IsFailure()) { dir.Finalize(); } if (numEntry == 0) { // ルートディレクトリを閉じる dir.Finalize(); // ルートディレクトリの子を開く for (entryIndex = entryList.begin(); entryIndex != entryList.end(); entryIndex++) { // ディレクトリなら削除する if (entryIndex->attributes.isDirectory) { NN_LOG("Try Delete %ls%ls/\n", currentDirectory.c_str(), entryIndex->entryName); result = nn::fs::TryDeleteDirectoryRecursively((currentDirectory + ::std::wstring( entryIndex->entryName)).c_str()); COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); } // ファイルならログとAP設定以外は削除する else { if (std::wcscmp(entryIndex->entryName, common::AP_SETTING_FILENAME) != 0 && std::wcscmp( entryIndex->entryName, common::LOG_FILENAME) != 0) { NN_LOG("Try Delete %ls%ls\n", currentDirectory.c_str(), entryIndex->entryName); result = nn::fs::TryDeleteFile( (currentDirectory + ::std::wstring(entryIndex->entryName)).c_str()); COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); } } } // 削除完了 break; } else { // vectorに保存する entryList.push_back(entry); if (entry.attributes.isDirectory) { NN_LOG("%ls%ls/\n", currentDirectory.c_str(), entry.entryName); } else { NN_LOG("%ls%ls\n", currentDirectory.c_str(), entry.entryName); } } } } else { NN_LOG("failed initialize directory\n"); COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); dir.Finalize(); } common::SdMountManager::Unmount(); } void WriteRegionData() { COMMON_LOGGER("Export Region Data.\n"); nn::cfg::CTR::CfgRegionCode region; region = nn::cfg::CTR::GetRegion(); s_SdWriter.WriteBufWithCmac(common::REGION_DATA_PATHNAME, ®ion, sizeof(nn::cfg::CTR::CfgRegionCode)); } void WriteCountryLanguageData() { COMMON_LOGGER("Export Country and Language Data.\n"); nn::Result result; nn::cfg::nor::CTR::Initialize(); // 国設定 s_CountryLanguage.country = nn::cfg::CTR::GetCountry(); if (s_CountryLanguage.country != nn::cfg::CTR::CFG_COUNTRY_UNKNOWN) { // 言語設定 s_CountryLanguage.language = nn::cfg::CTR::GetLanguage(); s_SdWriter.WriteBufWithCmac(common::COUNTRY_SETTING_PATHNAME, &s_CountryLanguage, sizeof(s_CountryLanguage)); } } void WriteNorData() { COMMON_LOGGER("Export NOR Data.\n"); nn::Result result; nn::cfg::nor::CTR::Initialize(); NN_LOG("Get NTR User Setting\n"); // NTR設定 result = nn::cfg::nor::CTR::GetNtrSetting(&s_NtrNorData.ntrConfig.ncd, &s_NtrNorData.ntrConfig.ncd_ex); COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); // TWL WiFi設定 result = nn::cfg::nor::CTR::ReadTwlWifiSetting(0, s_NtrNorData.TwlWiFiSetting, common::TWL_WIFI_SETTING_SIZE); COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); // NTR WiFi設定 result = nn::cfg::nor::CTR::ReadNtrWifiSetting(0, s_NtrNorData.NtrWiFiSetting, common::NTR_WIFI_SETTING_SIZE); COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); s_SdWriter.WriteBufWithCmac(common::NOR_PATHNAME, &s_NtrNorData, sizeof(common::NtrNorData)); } void WriteSerialNumber() { COMMON_LOGGER("Export Serial Number.\n"); u8* serial; size_t size; GetSerialNumber(&serial, &size); s_SdWriter.WriteBufWithCmac(common::SERIAL_PATHNAME, serial, size); } void WriteDeviceId() { COMMON_LOGGER("Export Device ID.\n"); bit32 deviceId = GetDeviceId(); s_SdWriter.WriteBufWithCmac(common::DEVICE_ID_PATHNAME, &deviceId, sizeof(deviceId)); } void WriteIvs() { COMMON_LOGGER("Export IVS.\n"); void* ivs; size_t size; GetIvs(&ivs, &size); void* enc; nn::Result result; enc = common::HeapManager::GetHeap()->Allocate(size); if(enc != NULL) { // AES暗号化する nn::crypto::Initialize(); nn::crypto::SwAesCtrContext swAesCtrContest; swAesCtrContest.Initialize(common::iv, common::key, sizeof(common::key)); swAesCtrContest.Encrypt(enc, ivs, size); s_SdWriter.WriteBufWithCmac(common::IVS_PATHNAME, enc, size); common::HeapManager::GetHeap()->Free(enc); } } // IVSからセーブデータディレクトリ名を生成する void GetSaveDataDirectoryRoot() { nn::Result result; using namespace nn::dbg; const size_t SEED_SIZE = 16; bit8 hash[nn::crypto::Sha256Context::HASH_SIZE]; const size_t SYS_SAVE_ROOT_LENGTH = 16; char rootHash[SYS_SAVE_ROOT_LENGTH]; char rootStr[SYS_SAVE_ROOT_LENGTH * 2 + 1]; void* addr; size_t size; GetIvs(&addr, &size); // 最後の16バイトのハッシュを使う nn::crypto::CalculateSha256(hash, &reinterpret_cast (addr)[size - SEED_SIZE], SEED_SIZE); for (u8 i = 0; i < SEED_SIZE / 4; i++) { for (u8 j = 0; j < SEED_SIZE / 4; j++) { rootHash[i * 4 + j] = hash[i * 4 + 3 - j]; } } // 得られたハッシュから文字列を生成 for (s32 k = 0; k < SEED_SIZE; k++) { for (s32 i = 6; i < 8; ++i) { bit32 n = (rootHash[k] >> ((7 - i) * 4)) & 0xf; NN_TASSERT_(n < 16); rootStr[i - 6 + k * 2] = static_cast (n < 10 ? '0' + n : 'a' + (n - 10)); } } rootStr[SYS_SAVE_ROOT_LENGTH * 2] = '\0'; // セーブデータディレクトリ名を保存する s_SysSaveRoot = ::std::string(rootStr); NN_LOG("%s\n", s_SysSaveRoot.c_str()); } void CreateTwlDirectory(enum common::TWL_PATH_INDEX path) { NN_ASSERT(path < common::TWL_PATHNAME_MAX); s_SdWriter.CreateDirectory((::std::wstring(common::SDMC_ROOT_DIRECTORY_PATH) + std::wstring(common::SD_TWL_ROOTNAME_TABLE[path])).c_str()); } void WriteTwlData(enum common::TWL_PATH_INDEX path) { NN_ASSERT(path < common::TWL_PATHNAME_MAX); nn::Result result; result = nn::fs::MountSpecialArchive(common::TWL_ARCHIVE_NAME_TABLE[path], common::TWL_FS_ARCHIVE_KIND[path]); COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); result = common::SdMountManager::Mount(); size_t bufSize = common::HeapManager::GetHeap()->GetAllocatableSize(); NN_LOG("AllocatableSize = %d\n", bufSize); u32 fileNum = 0; u32 fileSize = 0; common::CalculateFileNum(::std::wstring(common::NAND_TWL_ROOT_PATHNAME_WITH_SLASH_TABLE[path]), fileNum, fileSize); nn::fs::Unmount(common::NAND_ARCHIVE_NAME); NN_LOG("File Number = %d\n", fileNum); NN_LOG("File Size = %d\n", fileSize); // 進捗表示用 common::InitializeTransferProgress(fileSize); void* buf = common::HeapManager::GetHeap()->Allocate(bufSize, AES_BLOCK_SIZE); if (buf != NULL) { wchar_t archiveName[256]; ::std::mbstowcs(archiveName, common::TWL_ARCHIVE_NAME_TABLE[path], std::strlen(common::TWL_ARCHIVE_NAME_TABLE[path]) + 1); std::wstring archiveString(archiveName); common::CopyDirectory( (archiveString + ::std::wstring(L"/")).c_str(), (common::SDMC_ROOT_DIRECTORY_PATH + ::std::wstring(common::SD_TWL_ROOTNAME_TABLE[path])).c_str(), buf, bufSize, true); common::HeapManager::GetHeap()->Free(buf); } common::SdMountManager::Unmount(); nn::fs::Unmount(common::TWL_ARCHIVE_NAME_TABLE[path]); } void WriteTwlPhotoData() { COMMON_LOGGER("Export Twl Photo Data.\n"); CreateTwlDirectory(common::TWL_PHOTO); WriteTwlData(common::TWL_PHOTO); } void WriteTwlSoundData() { COMMON_LOGGER("Export Twl Sound Data.\n"); CreateTwlDirectory(common::TWL_SOUND); WriteTwlData(common::TWL_SOUND); } void ExportTwlPhotoData() { s_ExportThread.Start(WriteTwlPhotoData, s_ExportThreadStack); } void ExportTwlSoundData() { // 不要なデータを削除する DeleteTrash(common::SDMC_ROOT_DIRECTORY_PATH); s_ExportThread.Start(WriteTwlSoundData, s_ExportThreadStack); } void WriteMcuRtcData() { COMMON_LOGGER("Export RTC Data.\n"); nn::Result result; nn::Handle handle = GetMcuHandle(); if(handle.IsValid()) { nn::mcu::CTR::HwCheck mcu(handle); nn::mcu::CTR::RtcData rtc; result = mcu.GetRtcAll(&rtc); NN_LOG("RTC = 20%02d/%02d/%02d %02d:%02d:%02d\n", rtc.m_Year, rtc.m_Month, rtc.m_Day, rtc.m_Hour, rtc.m_Minute, rtc.m_Second); COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); s_SdWriter.WriteBufWithCmac(common::MCU_RTC_PATHNAME, &rtc, sizeof(rtc)); } else { NN_LOG("invalid handle\n"); } } void ExportThreadFunc() { nn::Result result; result = nn::fs::MountSpecialArchive(common::NAND_ARCHIVE_NAME, nn::fs::CTR::ARCHIVE_TYPE_CTR_NAND); COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); result = common::SdMountManager::Mount(); size_t bufSize = common::HeapManager::GetHeap()->GetAllocatableSize(); NN_LOG("AllocatableSize = %d\n", bufSize); void* buf = common::HeapManager::GetHeap()->Allocate(bufSize, AES_BLOCK_SIZE); if (buf != NULL) { common::CopyDirectory( (::std::wstring(common::NAND_DATA_ROOT_PATHNAME_WITH_SLASH) + ::std::wstring(s_RootName) + ::std::wstring(L"/")).c_str(), (common::SDMC_ROOT_DIRECTORY_PATH + ::std::wstring(common::SD_SAVEDATA_ROOT_NAME) + ::std::wstring(s_RootName) + ::std::wstring(L"/")).c_str(), buf, bufSize, true); common::HeapManager::GetHeap()->Free(buf); } common::SdMountManager::Unmount(); nn::fs::Unmount(common::NAND_ARCHIVE_NAME); NN_LOG("Export Thread Finalize\n"); } nn::Result WriteSaveData() { // NANDからSDカードに書き出し nn::Result result; result = nn::fs::MountSpecialArchive(common::NAND_ARCHIVE_NAME, nn::fs::CTR::ARCHIVE_TYPE_CTR_NAND); COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); u32 fileNum = 0; u32 fileSize = 0; common::CalculateFileNum(::std::wstring(common::NAND_DATA_ROOT_PATHNAME_WITH_SLASH), fileNum, fileSize); nn::fs::Unmount(common::NAND_ARCHIVE_NAME); NN_LOG("File Number = %d\n", fileNum); NN_LOG("File Size = %d\n", fileSize); // 進捗表示用 common::InitializeTransferProgress(fileSize); ::std::mbstowcs(s_RootName, s_SysSaveRoot.c_str(), s_SysSaveRoot.size() + 1); NN_LOG("%ls\n", (::std::wstring(common::NAND_DATA_ROOT_PATHNAME_WITH_SLASH) + ::std::wstring(s_RootName) + ::std::wstring(L"/")).c_str()); // セーブデータディレクトリ以下のデータをSDカードにコピー // コピー用ディレクトリ作成 s_SdWriter.CreateDirectory((::std::wstring(common::SDMC_ROOT_DIRECTORY_PATH) + ::std::wstring( common::SD_SAVEDATA_ROOT_NAME)).c_str()); s_SdWriter.CreateDirectory((::std::wstring(common::SDMC_ROOT_DIRECTORY_PATH) + ::std::wstring( common::SD_SAVEDATA_ROOT_NAME) + ::std::wstring(s_RootName) + ::std::wstring(L"/")).c_str()); COMMON_LOGGER("Export NAND Data Start...\n"); // SDにコピーするためのスレッドの作成 s_ExportThread.Start(ExportThreadFunc, s_ExportThreadStack); return result; } void FinalizeExportThread() { s_ExportThread.Join(); s_ExportThread.Finalize(); } void WriteVersionData() { COMMON_LOGGER("Export Version Data.\n"); common::VerDef versionData; GetVersionData(&versionData); s_SdWriter.WriteBufWithCmac(common::VERSION_DATA_PATHNAME, &versionData, sizeof(common::VerDef)); } void WritePlayHistory() { common::PlayHistoryManager historyManager; COMMON_LOGGER("Export PlayHistory\n"); historyManager.Export(); } void DeleteNimSaveData() { nn::Result result; ::std::wstring nimSaveDataPath = ::std::wstring(common::SDMC_ROOT_DIRECTORY_PATH) + ::std::wstring(common::SD_SAVEDATA_ROOT_NAME) + ::std::wstring(s_RootName) + ::std::wstring(L"/") + std::wstring(common::NIM_SAVEDATA_DIRECTORY_NAME); common::SdMountManager::Mount(); NN_LOG("%ls\n", nimSaveDataPath.c_str()); result = nn::fs::TryDeleteDirectoryRecursively(nimSaveDataPath.c_str()); COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); common::SdMountManager::Unmount(); } void AddShutDownPtmEvent() { nn::pl::CTR::NotifyPlayEvent(nn::pl::CTR::EVENTTYPE_TERMINATE, nn::CTR::INVALID_PROGRAM_ID, nn::fnd::DateTime::GetNow()); } void ExportData() { static bool init = true; if (init) { // リージョンデータをSDに書き込む WriteRegionData(); // 国データと言語データをSDに書き込む WriteCountryLanguageData(); // NORデータをSDカードに書き込む WriteNorData(); // シリアルナンバーをSDカードに書き込む WriteSerialNumber(); // デバイスIDをSDカードに書き込む WriteDeviceId(); // 完全性検証SEEDをSDカードに書き込む WriteIvs(); // IVSからセーブデータディレクトリ名を計算 GetSaveDataDirectoryRoot(); // プレイ履歴をSDに書き出す //WritePlayHistory(); // 電源断の履歴をptmに追加する AddShutDownPtmEvent(); // RTCをSDに書き出す WriteMcuRtcData(); // バージョン情報をSDに書き出す WriteVersionData(); // NANDのセーブデータをSDに書き出す WriteSaveData(); init = false; } } u32 GetProgress() { return common::GetProgress(); } bool IsExportFinished() { return s_ExportThread.IsValid() && !s_ExportThread.IsAlive(); } }