mirror of
https://github.com/rvtr/ctr_Repair.git
synced 2025-10-31 13:51:08 -04:00
git-svn-id: file:///Volumes/Transfer/gigaleak_20231201/2020-05-23%20-%20ctr.7z%20+%20svn_v1.068.zip/ctr/svn/ctr_Repair@325 385bec56-5757-e545-9c3a-d8741f4650f1
933 lines
30 KiB
C++
933 lines
30 KiB
C++
/*---------------------------------------------------------------------------*
|
|
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 <vector>
|
|
#include <cstdlib>
|
|
#include <cwchar>
|
|
#include <string>
|
|
#include <cstdlib>
|
|
#include <nn/nstd.h>
|
|
#include <nn/fs/CTR/fs_ArchiveTypesForSystem.h>
|
|
#include <nn/fs/CTR/MPCore/fs_FileSystemBasePrivate.h>
|
|
#include <nn/cfg/CTR/cfg_Api.h>
|
|
#include <nn/cfg/CTR/cfg_ApiNor.h> // cfg:norの初期化に必要
|
|
#include <nn/cfg/CTR/cfg_NtrSettings.h>
|
|
#include <nn/ps/CTR/ps_API.h>
|
|
#include <nn/drivers/aes/CTR/ARM946ES/driverAes_Types.h>
|
|
#include <nn/crypto/crypto_AesCmac.h>
|
|
#include <nn/crypto/crypto_SwAesCtrContext.h>
|
|
#include <nn/crypto/crypto_Sha256.h>
|
|
#include <nn/crypto/crypto_SwAesCmac.h>
|
|
#include <nn/mcu.h>
|
|
#include <nn/am.h>
|
|
#include <nn/pl/CTR/pl_PlayHistoryApi.h>
|
|
#include <nn/pl/CTR/pl_PlayHistoryApiSysmenu.h>
|
|
|
|
#include "Exporter.h"
|
|
#include "CommonLogger.h"
|
|
#include "SDMountManager.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"
|
|
#include "Util.h"
|
|
|
|
namespace ConsoleBackup
|
|
{
|
|
|
|
namespace
|
|
{
|
|
|
|
common::SdReaderWriter s_SdWriter;
|
|
common::NtrNorData s_NtrNorData;
|
|
common::CfgCountryLanguage s_CountryLanguage;
|
|
|
|
const size_t EXPORT_THREAD_STACK_SIZE = 0x4000;
|
|
nn::os::Thread s_ExportThread;
|
|
nn::os::StackBuffer<EXPORT_THREAD_STACK_SIZE> s_ExportThreadStack;
|
|
|
|
wchar_t s_RootName[256];
|
|
|
|
nn::crypto::Sha256Context s_FileListContext;
|
|
|
|
}
|
|
|
|
void AddCmac(nn::fs::FileOutputStream* file, nn::crypto::Sha256Context* context);
|
|
|
|
void DeleteTrash(std::wstring currentDirectory)
|
|
{
|
|
// TODO: リードオンリーのファイルが消去できない
|
|
|
|
COMMON_LOGGER("Delete Trash.\n");
|
|
|
|
nn::fs::FileInputStream fis;
|
|
nn::fs::Directory dir;
|
|
nn::Result result;
|
|
std::vector<nn::fs::DirectoryEntry> entryList; //カレントディレクトリのエントリ一覧を格納
|
|
std::vector<nn::fs::DirectoryEntry>::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 WriteTwlTitleList(std::vector<std::wstring>& programIdList)
|
|
{
|
|
COMMON_LOGGER("Export TwlTitle List.\n");
|
|
|
|
size_t heapSize = common::HeapManager::GetHeap()->GetAllocatableSize();
|
|
char* titleListBuf = reinterpret_cast<char*> (common::HeapManager::GetHeap()->Allocate(heapSize));
|
|
|
|
size_t writeSize = 0;
|
|
if (titleListBuf != NULL)
|
|
{
|
|
for (std::vector<std::wstring>::iterator it = programIdList.begin(); it != programIdList.end(); it++)
|
|
{
|
|
nn::nstd::TSNPrintf(titleListBuf + writeSize, heapSize - writeSize, "%s\n", common::GetCharStr(it->c_str()));
|
|
NN_LOG("%ls\n", it->c_str());
|
|
writeSize += it->size() + sizeof('\n');
|
|
}
|
|
}
|
|
|
|
nn::Result result = s_SdWriter.WriteBufWithCmac(common::TWL_TITLELIST_PATHNAME, titleListBuf, writeSize);
|
|
COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result);
|
|
common::HeapManager::GetHeap()->Free(titleListBuf);
|
|
}
|
|
|
|
|
|
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::HardwareStateManager& manager)
|
|
{
|
|
COMMON_LOGGER("Export Serial Number.\n");
|
|
|
|
u8* serial;
|
|
size_t size;
|
|
manager.GetSerialNumber(&serial, &size);
|
|
|
|
s_SdWriter.WriteBufWithCmac(common::SERIAL_PATHNAME, serial, size);
|
|
}
|
|
|
|
void WriteDeviceId(common::HardwareStateManager& manager)
|
|
{
|
|
COMMON_LOGGER("Export Device ID.\n");
|
|
|
|
bit32 deviceId = manager.GetDeviceId();
|
|
|
|
s_SdWriter.WriteBufWithCmac(common::DEVICE_ID_PATHNAME, &deviceId, sizeof(deviceId));
|
|
}
|
|
|
|
void WriteIvs(common::HardwareStateManager& manager)
|
|
{
|
|
COMMON_LOGGER("Export IVS.\n");
|
|
|
|
void* ivs;
|
|
size_t size;
|
|
manager.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);
|
|
}
|
|
}
|
|
|
|
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 ListTwlSaveData(std::wstring currentDirectory, std::vector<common::SavePathInfo>* list)
|
|
{
|
|
nn::fs::Directory dir;
|
|
nn::fs::DirectoryEntry entry;
|
|
s32 numEntry;
|
|
|
|
nn::Result result = dir.TryInitialize(currentDirectory.c_str());
|
|
if(result.IsSuccess())
|
|
{
|
|
for(;;)
|
|
{
|
|
result = dir.TryRead(&numEntry, &entry, 1);
|
|
if(result.IsFailure())
|
|
{
|
|
dir.Finalize();
|
|
break;
|
|
}
|
|
|
|
if(numEntry == 0)
|
|
{
|
|
dir.Finalize();
|
|
}
|
|
else
|
|
{
|
|
common::SavePathInfo pathInfo;
|
|
pathInfo.name = currentDirectory + std::wstring(L"/") + std::wstring(entry.entryName);
|
|
pathInfo.isDirectory = false;
|
|
list->push_back(pathInfo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AddCurrentProgramIdPath(std::vector<std::wstring>* programIdList, std::wstring currentDir)
|
|
{
|
|
std::wstring currentPath(currentDir);
|
|
std::wstring token(common::NAND_TWL_DATA_ROOT_PATHNAME_WITHOUT_SLASH);
|
|
|
|
std::wstring::size_type pos;
|
|
pos = currentPath.find(token);
|
|
if(pos != std::wstring::npos)
|
|
{
|
|
std::wstring subStr = currentPath.substr(token.size());
|
|
|
|
std::wstring slash(L"/");
|
|
pos = subStr.find(slash);
|
|
while(pos != std::wstring::npos)
|
|
{
|
|
subStr.erase(pos, slash.size());
|
|
pos = subStr.find(slash);
|
|
}
|
|
|
|
std::wstring ctrProgramIdHi(L"00048");
|
|
subStr.replace(0, ctrProgramIdHi.size(), ctrProgramIdHi);
|
|
|
|
programIdList->push_back(subStr);
|
|
}
|
|
else
|
|
{
|
|
NN_LOG("Can't find %ls\n", common::NAND_TWL_DATA_ROOT_PATHNAME_WITHOUT_SLASH);
|
|
}
|
|
|
|
}
|
|
|
|
void AddCurrentDirectory(std::vector<common::SavePathInfo>* list, std::wstring currentDir, wchar_t* currentEntry)
|
|
{
|
|
common::SavePathInfo pathInfo;
|
|
|
|
pathInfo.name = currentDir + std::wstring(L"/") + std::wstring(currentEntry) + std::wstring(L"/");
|
|
pathInfo.isDirectory = true;
|
|
list->push_back(pathInfo);
|
|
}
|
|
|
|
|
|
bool ListTwlSaveDataDirectory(std::wstring currentDirectory, u32 level, std::vector<common::SavePathInfo>* list,
|
|
std::vector<std::wstring>* programIdList)
|
|
{
|
|
nn::fs::FileInputStream fis;
|
|
nn::fs::Directory dir;
|
|
nn::Result result;
|
|
std::vector<nn::fs::DirectoryEntry> entryList; //カレントディレクトリのエントリ一覧を格納
|
|
std::vector<nn::fs::DirectoryEntry>::iterator entryIndex;
|
|
const u8 TWL_SAVEDATA_DIRECTORY_LEVEL = 2; // data ディレクトリまでの階層
|
|
const wchar_t* const TWL_SAVEDATA_DIRECTORY_NAME = L"data";
|
|
|
|
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();
|
|
|
|
bool retValue = false;
|
|
for (entryIndex = entryList.begin(); entryIndex != entryList.end(); entryIndex++)
|
|
{
|
|
// レベル2未満のディレクトリなら再帰的に開く
|
|
if(level < TWL_SAVEDATA_DIRECTORY_LEVEL)
|
|
{
|
|
if (entryIndex->attributes.isDirectory)
|
|
{
|
|
if (ListTwlSaveDataDirectory(
|
|
currentDirectory + std::wstring(L"/") + std::wstring(entryIndex->entryName),
|
|
level + 1, list, programIdList))
|
|
{
|
|
NN_LOG("%ls/%ls has data directory.\n", currentDirectory.c_str(), entryIndex->entryName);
|
|
retValue = true;
|
|
|
|
AddCurrentDirectory(list, currentDirectory, entryIndex->entryName);
|
|
}
|
|
}
|
|
}
|
|
// レベル2のディレクトリなら data かどうかチェック
|
|
else if(level == TWL_SAVEDATA_DIRECTORY_LEVEL && entryIndex->attributes.isDirectory)
|
|
{
|
|
if (std::wcscmp(entryIndex->entryName, TWL_SAVEDATA_DIRECTORY_NAME) == 0)
|
|
{
|
|
// ファイル一覧を取得する
|
|
retValue = true;
|
|
ListTwlSaveData(currentDirectory + std::wstring(L"/") + std::wstring(entryIndex->entryName), list);
|
|
|
|
AddCurrentDirectory(list, currentDirectory, entryIndex->entryName);
|
|
AddCurrentProgramIdPath(programIdList, currentDirectory);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(level != 0)
|
|
{
|
|
return retValue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// vectorに保存する
|
|
entryList.push_back(entry);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NN_LOG("failed initialize directory\n");
|
|
COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result);
|
|
dir.Finalize();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
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;
|
|
s64 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)
|
|
{
|
|
nn::fs::FileOutputStream list;
|
|
result = list.TryInitialize(common::FILE_LIST_PATHNAME, true);
|
|
COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result);
|
|
if (result.IsSuccess())
|
|
{
|
|
result = list.TryGetSize(&fileSize);
|
|
if (result.IsSuccess())
|
|
{
|
|
// 末尾に移動
|
|
result = list.TrySetPosition(fileSize);
|
|
if (result.IsSuccess())
|
|
{
|
|
wchar_t archiveName[nn::fs::MAX_FILE_PATH_LENGTH];
|
|
::std::mbstowcs(archiveName, common::TWL_ARCHIVE_NAME_TABLE[path],
|
|
std::strlen(common::TWL_ARCHIVE_NAME_TABLE[path]) + 1);
|
|
std::wstring archiveString(archiveName);
|
|
common::CopyDirectory(
|
|
NULL,
|
|
(archiveString + ::std::wstring(L"/")).c_str(),
|
|
(common::SDMC_ROOT_DIRECTORY_PATH + ::std::wstring(common::SD_TWL_ROOTNAME_TABLE[path])).c_str(),
|
|
buf, bufSize, true, &list, &s_FileListContext);
|
|
}
|
|
|
|
list.TryFlush();
|
|
list.Finalize();
|
|
}
|
|
}
|
|
common::HeapManager::GetHeap()->Free(buf);
|
|
}
|
|
|
|
common::SdMountManager::Unmount();
|
|
nn::fs::Unmount(common::TWL_ARCHIVE_NAME_TABLE[path]);
|
|
}
|
|
|
|
void CalculateTwlSaveData(std::vector<common::SavePathInfo>* fileList, s64* fileSize)
|
|
{
|
|
NN_NULL_ASSERT(fileList);
|
|
NN_NULL_ASSERT(fileSize);
|
|
|
|
*fileSize = 0;
|
|
nn::Result result;
|
|
|
|
for (std::vector<common::SavePathInfo>::iterator it = fileList->begin(); it != fileList->end(); it++)
|
|
{
|
|
if(!it->isDirectory)
|
|
{
|
|
nn::fs::FileInputStream file;
|
|
result = file.TryInitialize(it->name.c_str());
|
|
if(result.IsSuccess())
|
|
{
|
|
s64 size;
|
|
result = file.TryGetSize(&size);
|
|
if(result.IsSuccess())
|
|
{
|
|
*fileSize += size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void WriteTwlSaveData()
|
|
{
|
|
nn::Result result;
|
|
std::vector<common::SavePathInfo> fileList;
|
|
std::vector<std::wstring> programIdList;
|
|
|
|
COMMON_LOGGER("Export Twl Save Data.\n");
|
|
|
|
// ディレクトリ作成
|
|
s_SdWriter.CreateDirectory((::std::wstring(common::SDMC_ROOT_DIRECTORY_PATH) +
|
|
std::wstring(common::SD_SAVEDATA_TWL_ROOT_NAME)).c_str());
|
|
|
|
// セーブデータを含むディレクトリ一覧を生成
|
|
result = nn::fs::MountSpecialArchive(common::NAND_TWL_ARCHIVE_NAME, nn::fs::CTR::ARCHIVE_TYPE_TWL_NAND);
|
|
COMMON_LOGGER_RESULT_IF_FAILED(result);
|
|
ListTwlSaveDataDirectory(std::wstring(common::NAND_TWL_DATA_ROOT_PATHNAME_WITHOUT_SLASH), 0, &fileList, & programIdList);
|
|
|
|
NN_LOG("No Twl Savedata\n");
|
|
if(fileList.size() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
NN_LOG("listup Twl Savedata Directory\n");
|
|
for (std::vector<common::SavePathInfo>::reverse_iterator it = fileList.rbegin(); it != fileList.rend(); it++)
|
|
{
|
|
NN_LOG("%ls\n", it->name.c_str());
|
|
}
|
|
|
|
WriteTwlTitleList(programIdList);
|
|
|
|
// 合計サイズ取得
|
|
s64 fileSize;
|
|
CalculateTwlSaveData(&fileList, &fileSize);
|
|
common::InitializeTransferProgress(fileSize);
|
|
|
|
NN_LOG("\n");
|
|
// SDに書き出し
|
|
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)
|
|
{
|
|
nn::fs::FileOutputStream list;
|
|
result = list.TryInitialize(common::FILE_LIST_PATHNAME, true);
|
|
COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result);
|
|
if (result.IsSuccess())
|
|
{
|
|
result = list.TryGetSize(&fileSize);
|
|
if (result.IsSuccess())
|
|
{
|
|
// 末尾に移動
|
|
result = list.TrySetPosition(fileSize);
|
|
if (result.IsSuccess())
|
|
{
|
|
wchar_t archiveName[nn::fs::MAX_FILE_PATH_LENGTH];
|
|
::std::mbstowcs(archiveName, common::NAND_TWL_ARCHIVE_NAME,
|
|
std::strlen(common::NAND_TWL_ARCHIVE_NAME) + 1);
|
|
std::wstring archiveString(archiveName);
|
|
|
|
for (std::vector<common::SavePathInfo>::reverse_iterator it = fileList.rbegin(); it
|
|
!= fileList.rend(); it++)
|
|
{
|
|
// twln:/title/をsdmc:/CTR_Console_Repair/TWLBackup/に置換
|
|
std::wstring toPath(it->name.c_str());
|
|
toPath.replace(
|
|
0,
|
|
std::wcslen(common::NAND_TWL_DATA_ROOT_PATHNAME_WITHOUT_SLASH) + 1,
|
|
std::wstring(common::SDMC_ROOT_DIRECTORY_PATH) + std::wstring(
|
|
common::SD_SAVEDATA_TWL_ROOT_NAME));
|
|
|
|
if(it->isDirectory)
|
|
{
|
|
common::ExportTwlSaveDirectory(toPath.c_str(), &list, &s_FileListContext);
|
|
}
|
|
else
|
|
{
|
|
common::ExportTwlSaveFile(it->name.c_str(), toPath.c_str(), buf, bufSize, &list,
|
|
&s_FileListContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
list.TryFlush();
|
|
list.Finalize();
|
|
}
|
|
}
|
|
common::HeapManager::GetHeap()->Free(buf);
|
|
}
|
|
|
|
common::SdMountManager::Unmount();
|
|
|
|
nn::fs::Unmount( common::NAND_TWL_ARCHIVE_NAME);
|
|
}
|
|
|
|
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 InitializeFileListContext()
|
|
{
|
|
nn::crypto::Initialize();
|
|
s_FileListContext.Initialize();
|
|
}
|
|
|
|
void ExportTwlSaveData()
|
|
{
|
|
// 不要なデータを削除する
|
|
DeleteTrash((std::wstring(common::LOG_ROOT_DIRECTORY_PATH) + std::wstring(L"/")).c_str());
|
|
|
|
s_ExportThread.Start(WriteTwlSaveData, s_ExportThreadStack);
|
|
}
|
|
|
|
void ExportTwlPhotoData()
|
|
{
|
|
s_ExportThread.Start(WriteTwlPhotoData, s_ExportThreadStack);
|
|
}
|
|
|
|
void ExportTwlSoundData()
|
|
{
|
|
s_ExportThread.Start(WriteTwlSoundData, s_ExportThreadStack);
|
|
}
|
|
|
|
|
|
void WriteMcuRtcData(common::HardwareStateManager& manager)
|
|
{
|
|
COMMON_LOGGER("Export RTC Data.\n");
|
|
nn::Result result;
|
|
nn::Handle handle = manager.GetMcuHandle();
|
|
|
|
if(handle.IsValid())
|
|
{
|
|
nn::mcu::CTR::HwCheck mcu(handle);
|
|
|
|
nn::mcu::CTR::RtcData rtc;
|
|
result = mcu.GetRtcAll(&rtc);
|
|
const u8 RETRY = 10;
|
|
for (u8 i = 0; i < RETRY; i++)
|
|
{
|
|
if (result.IsSuccess())
|
|
{
|
|
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));
|
|
break;
|
|
}
|
|
nn::os::Thread::Sleep(
|
|
nn::fnd::TimeSpan::FromMilliSeconds(
|
|
nn::os::Tick::GetSystemCurrent().ToTimeSpan().GetMilliSeconds() % 100));
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
nn::fs::FileOutputStream list;
|
|
result = list.TryInitialize(common::FILE_LIST_PATHNAME, true);
|
|
COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result);
|
|
if (result.IsSuccess())
|
|
{
|
|
s64 fileSize;
|
|
result = list.TryGetSize(&fileSize);
|
|
if (result.IsSuccess())
|
|
{
|
|
// 末尾に移動
|
|
result = list.TrySetPosition(fileSize);
|
|
if (result.IsSuccess())
|
|
{
|
|
common::CopyDirectory(
|
|
NULL,
|
|
::std::wstring(common::NAND_DATA_ROOT_PATHNAME_WITH_SLASH).c_str(),
|
|
(std::wstring(common::SDMC_ROOT_DIRECTORY_PATH) + ::std::wstring(
|
|
common::SD_SAVEDATA_ROOT_NAME)).c_str(), buf, bufSize, true, &list,
|
|
&s_FileListContext);
|
|
|
|
AddCmac(&list, &s_FileListContext);
|
|
list.TryFlush();
|
|
list.Finalize();
|
|
}
|
|
}
|
|
}
|
|
common::HeapManager::GetHeap()->Free(buf);
|
|
}
|
|
|
|
common::SdMountManager::Unmount();
|
|
nn::fs::Unmount(common::NAND_ARCHIVE_NAME);
|
|
|
|
NN_LOG("Export Thread Finalize\n");
|
|
}
|
|
|
|
nn::Result WriteSaveData(::std::string& sysSaveRoot)
|
|
{
|
|
// 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;
|
|
s64 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, sysSaveRoot.c_str(), 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());
|
|
|
|
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::HardwareStateManager& manager)
|
|
{
|
|
COMMON_LOGGER("Export Version Data.\n");
|
|
|
|
common::VerDef versionData;
|
|
manager.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(common::HardwareStateManager& manager)
|
|
{
|
|
static bool init = true;
|
|
|
|
if (init)
|
|
{
|
|
// リージョンデータをSDに書き込む
|
|
WriteRegionData();
|
|
|
|
// 国データと言語データをSDに書き込む
|
|
WriteCountryLanguageData();
|
|
|
|
// NORデータをSDカードに書き込む
|
|
WriteNorData();
|
|
|
|
// シリアルナンバーをSDカードに書き込む
|
|
WriteSerialNumber(manager);
|
|
|
|
// デバイスIDをSDカードに書き込む
|
|
WriteDeviceId(manager);
|
|
|
|
// 完全性検証SEEDをSDカードに書き込む
|
|
WriteIvs(manager);
|
|
|
|
::std::string systemSaveRoot;
|
|
void* ivs;
|
|
size_t size;
|
|
manager.GetIvs(&ivs, &size);
|
|
// IVSからセーブデータディレクトリ名を計算
|
|
common::Util::GetSaveDataDirectoryRoot(systemSaveRoot, ivs, size);
|
|
|
|
// プレイ履歴をSDに書き出す
|
|
//WritePlayHistory();
|
|
|
|
// 電源断の履歴をptmに追加する
|
|
AddShutDownPtmEvent();
|
|
|
|
// RTCをSDに書き出す
|
|
WriteMcuRtcData(manager);
|
|
|
|
// バージョン情報をSDに書き出す
|
|
WriteVersionData(manager);
|
|
|
|
// NANDのセーブデータをSDに書き出す
|
|
WriteSaveData(systemSaveRoot);
|
|
|
|
init = false;
|
|
}
|
|
}
|
|
|
|
u32 GetProgress()
|
|
{
|
|
return common::GetProgress();
|
|
}
|
|
|
|
bool IsExportThreadFinished()
|
|
{
|
|
return s_ExportThread.IsValid() && !s_ExportThread.IsAlive();
|
|
}
|
|
|
|
//!@ brief ファイルにSHA256から計算したAES-CMACを付加します
|
|
//!@ param[in] file CMACを付加したいInitialize済みのファイル
|
|
//!@ param[in] context CMAC計算元のSHA256コンテキスト
|
|
void AddCmac(nn::fs::FileOutputStream* file, nn::crypto::Sha256Context* context)
|
|
{
|
|
nn::Result result;
|
|
|
|
bit8 sha256Hash[nn::crypto::Sha256Context::HASH_SIZE];
|
|
context->GetHash(sha256Hash);
|
|
|
|
bit8 cmac[nn::crypto::AES_CMAC_MAC_SIZE];
|
|
result = nn::crypto::CalculateAesCmacSw(cmac, sha256Hash, nn::crypto::Sha256Context::HASH_SIZE, common::cmacKey);
|
|
COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result);
|
|
|
|
s32 writeSize;
|
|
result = file->TryWrite(&writeSize, cmac, sizeof(cmac), false);
|
|
COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result);
|
|
|
|
}
|
|
|
|
}
|