ctr_Repair/trunk/ConsoleDataMigration/sources/ConsoleRestore/TitleDownloader.cpp
N2614 e693f4b3d9 セーブデータが存在して、Personalized eTicketの存在するDSiWareのみダウンロードするように
git-svn-id: file:///Volumes/Transfer/gigaleak_20231201/2020-05-23%20-%20ctr.7z%20+%20svn_v1.068.zip/ctr/svn/ctr_Repair@827 385bec56-5757-e545-9c3a-d8741f4650f1
2014-11-14 07:38:28 +00:00

489 lines
16 KiB
C++

/*---------------------------------------------------------------------------*
Project: Horizon
File: TitleDownloader.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 <nn.h>
#include <nn/am.h>
#include <nn/nim.h>
#include <nn/CTR/CTR_ProgramId.h>
#include <nn/cfg/CTR/cfg_ApiSys.h>
#include "SdMountManager.h"
#include "common_Types.h"
#include "FileName.h"
#include "CommonLogger.h"
#include "HeapManager.h"
#include "TitleDownloader.h"
#include "Shop.h"
#include "SdReaderWriter.h"
#include "PreinstallImporter.h"
namespace
{
bit8 s_buffer1[400 * 1024];
// ダウンロードするタイトルの個数
size_t s_ProgramIdNum = 0;
// ダウンロードするタイトルのProgramIdの配列
nn::ProgramId s_ProgramIdList[256];
// SDカードに吸い出されたDSiWareのタイトルの個数
size_t s_SdDSiWareProgramIdNum = 0;
// SDカードに吸い出されたDSiWareのProgramIdの配列
nn::ProgramId s_SdDSiWareList[32];
// ダウンロード済みのタイトルの個数
size_t s_FinishedTitleNum = 0;
const size_t TITLE_DOWNLOADER_STACK_SIZE = 0x2000;
nn::os::Thread s_TitleDownloaderThread;
nn::os::StackBuffer<TITLE_DOWNLOADER_STACK_SIZE> s_TitleDownloaderThreadStack;
u64 s_Progress;
nn::fs::MediaType GetMediaType(const ES_NAMESPACE::ESTitleId titleId)
{
return (nn::CTR::IsTwlApp(titleId)) ?
nn::fs::MEDIA_TYPE_NAND : nn::fs::MEDIA_TYPE_SDMC;
}
const char *GetAttribute(EC_NAMESPACE::ECNameValuePair *attributes, u32 nAttributes, const char *attributeName)
{
for(int i=0; i<nAttributes; i++)
{
if(std::strcmp(attributes[i].name, attributeName)==0)
{
return attributes[i].value;
}
}
return NULL;
}
nn::Result GetEntry(ES_NAMESPACE::ESTitleId titleId, EC_NAMESPACE::ECTitleCatalogEntry **entry)
{
nn::nim::AttributeName returnAttribute[] =
{"Version", "TitleName", "TitleType", "TitleDescription", "Category", "Publisher", "MaxUserFileSize", "ReleaseDate"};
nn::nim::AttributeFilter attributeFilter[] = {
{"==", "string", "PricingSelection", "RELEASED"}
};
COMMON_LOGGER_DETAIL("ID: %016llx\n", titleId);
EC_NAMESPACE::ECTitleCatalog *titleCatalog;
nn::Result result = nn::nim::Shop::ListTitles(0, 1,
returnAttribute, sizeof(returnAttribute)/sizeof(returnAttribute[0]),
attributeFilter, sizeof(attributeFilter)/sizeof(attributeFilter[0]),
&(titleId), 1,
NULL, 0,
&titleCatalog, s_buffer1, sizeof(s_buffer1));
if(result.IsSuccess())
{
if(titleCatalog->nEntries == 1)
{
*entry = &(titleCatalog->entries[0]);
}
else
{
return nn::MakeStatusResult(nn::Result::SUMMARY_NOT_FOUND, nn::Result::MODULE_COMMON, nn::Result::DESCRIPTION_NOT_FOUND);
}
}
return result;
}
nn::Result GetTitleConfig(const ES_NAMESPACE::ESTitleId titleId, nn::nim::TitleConfig *titleConfig, s64* requiredSize)
{
EC_NAMESPACE::ECTitleCatalogEntry *entry;
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(GetEntry(titleId, &entry));
titleConfig->titleId =titleId;
titleConfig->version=std::strtoull(GetAttribute(entry->attributes, entry->nAttributes, "Version"), NULL, 10);
titleConfig->ratingAge=0;
titleConfig->media=GetMediaType(titleId);
*requiredSize=std::strtoull(GetAttribute(entry->attributes, entry->nAttributes, "MaxUserFileSize"), NULL, 10);
COMMON_LOGGER_DETAIL("titleId : 0x%016llx\n", titleConfig->titleId);
COMMON_LOGGER_DETAIL("version : %lld\n" , titleConfig->version);
COMMON_LOGGER_DETAIL("ratingAge : %d\n" , titleConfig->ratingAge);
COMMON_LOGGER_DETAIL("media : %d\n" , titleConfig->media);
COMMON_LOGGER_DETAIL("size : %lld\n" , *requiredSize);
return nn::ResultSuccess();
}
} // namespace <unnamed>
namespace ConsoleRestore
{
nn::Result TitleDownloader::m_Result = nn::ResultSuccess();
void TwlTitleDownloaderThreadFunc()
{
TitleDownloader TwlTitleDownloader;
PreinstallImporter importer;
s_Progress = 0;
TitleDownloader::m_Result = ListUpTwlTitles(s_SdDSiWareList, &s_SdDSiWareProgramIdNum);
COMMON_LOGGER_RETURN_VOID_IF_FAILED(TitleDownloader::m_Result);
TitleDownloader::m_Result = nn::am::InitializeForNetworkImporter();
COMMON_LOGGER_RETURN_VOID_IF_FAILED(TitleDownloader::m_Result);
TwlTitleDownloader.m_Result = importer.ListTitlesBasedOnTickets(s_ProgramIdList, &s_ProgramIdNum);
COMMON_LOGGER_RETURN_VOID_IF_FAILED(TwlTitleDownloader.m_Result);
TwlTitleDownloader.SetupTitleListForTWL(s_ProgramIdList, s_ProgramIdNum, s_FinishedTitleNum, s_SdDSiWareList, s_SdDSiWareProgramIdNum);
TwlTitleDownloader.Start();
nn::am::FinalizeForNetworkImporter();
s_FinishedTitleNum = TwlTitleDownloader.GetFinishedTitleNum();
}
void PreinstallTitleDownloaderThreadFunc()
{
TitleDownloader PreinstallTitleDownloader;
s_Progress = 0;
s_FinishedTitleNum = 0;
PreinstallTitleDownloader.SetupTitleList(s_ProgramIdList, s_ProgramIdNum, s_FinishedTitleNum);
TitleDownloader::m_Result = nn::am::InitializeForNetworkImporter();
COMMON_LOGGER_RETURN_VOID_IF_FAILED(TitleDownloader::m_Result);
PreinstallTitleDownloader.Start();
nn::am::FinalizeForNetworkImporter();
s_FinishedTitleNum = PreinstallTitleDownloader.GetFinishedTitleNum();
}
void StartTwlTitleDownload()
{
s_TitleDownloaderThread.Start(TwlTitleDownloaderThreadFunc, s_TitleDownloaderThreadStack);
}
void PreparePreinstallTitleDownloadThreadFunc(PreinstallListupParam param)
{
PreinstallImporter importer;
bool isAlreadyAvailable = false;
TitleDownloader preinstallTitleDownloader;
TitleDownloader::m_Result = nn::am::InitializeForNetworkImporter();
COMMON_LOGGER_RETURN_VOID_IF_FAILED(TitleDownloader::m_Result);
preinstallTitleDownloader.m_Result = importer.ListTitlesBasedOnTickets(s_ProgramIdList, &s_ProgramIdNum);
COMMON_LOGGER_RETURN_VOID_IF_FAILED(preinstallTitleDownloader.m_Result);
preinstallTitleDownloader.SetupTitleList(s_ProgramIdList, s_ProgramIdNum, s_FinishedTitleNum);
s64 requiredSize;
preinstallTitleDownloader.CalculateRequiredSize(&requiredSize);
COMMON_LOGGER_RETURN_VOID_IF_FAILED(preinstallTitleDownloader.m_Result);
s64 total;
s64 free;
preinstallTitleDownloader.m_Result= common::SdMountManager::Mount();
COMMON_LOGGER_RETURN_VOID_IF_FAILED(preinstallTitleDownloader.m_Result);
preinstallTitleDownloader.m_Result = nn::fs::GetSdmcSize(&total, &free);
NN_LOG("total = %lld, free = %lld\n", total, free);
COMMON_LOGGER_RETURN_VOID_IF_FAILED(preinstallTitleDownloader.m_Result);
preinstallTitleDownloader.m_Result= common::SdMountManager::Unmount();
COMMON_LOGGER_RETURN_VOID_IF_FAILED(preinstallTitleDownloader.m_Result);
NN_LOG("requiredSize = %lld\n", requiredSize + 8 * 1024 * 1024);
// TODO: SDデータベースの正確なサイズを反映する
if(free < requiredSize + 8 * 1024 * 1024)
{
preinstallTitleDownloader.m_Result = nn::MakePermanentResult(nn::Result::SUMMARY_OUT_OF_RESOURCE,
nn::Result::MODULE_APPLICATION, nn::Result::DESCRIPTION_OUT_OF_MEMORY);
return;
}
preinstallTitleDownloader.m_Result = importer.SetupSd(&isAlreadyAvailable, param.forcePreinstall);
if(isAlreadyAvailable)
{
preinstallTitleDownloader.m_Result = nn::MakePermanentResult(nn::Result::SUMMARY_INVALID_STATE,
nn::Result::MODULE_APPLICATION, nn::Result::DESCRIPTION_ALREADY_INITIALIZED);
return;
}
nn::am::FinalizeForNetworkImporter();
}
void StartPreparePreinstallTitleDownload(bool forcePreinstall)
{
PreinstallListupParam param;
param.forcePreinstall = forcePreinstall;
s_TitleDownloaderThread.Start(PreparePreinstallTitleDownloadThreadFunc, param, s_TitleDownloaderThreadStack);
}
void StartPreinstallTitleDownload()
{
s_TitleDownloaderThread.Start(PreinstallTitleDownloaderThreadFunc, s_TitleDownloaderThreadStack);
}
bool IsDownloadTitleFinished()
{
return s_TitleDownloaderThread.IsValid() && !s_TitleDownloaderThread.IsAlive();
}
void FinalizeTitleDownload()
{
s_TitleDownloaderThread.Join();
s_TitleDownloaderThread.Finalize();
}
bool DownloadTitleSucceeded()
{
return TitleDownloader::m_Result.IsSuccess();
}
nn::Result GetTitleDownloadResult()
{
return TitleDownloader::m_Result;
}
u32 GetTitleDownloadProgress()
{
return s_Progress;
}
TitleDownloader::TitleDownloader() : m_TiteNum(0), m_FinishedTitleNum(0)
{
for(u32 i = 0; i < IMPORTABLE_TITLE_MAX; i++)
{
m_ProgramIdList[i] = 0;
}
}
TitleDownloader::~TitleDownloader()
{
}
nn::Result ListUpTwlTitles(nn::ProgramId* list, size_t* num)
{
nn::Result result;
COMMON_LOGGER("Read TwlTitle List.\n");
*num = 0;
size_t heapSize = common::GetAllocatableSize();
if(heapSize > common::FILE_COPY_HEAP_SIZE)
{
heapSize = common::FILE_COPY_HEAP_SIZE;
}
common::HeapManager heap(heapSize);
char* titleListBuf = reinterpret_cast<char*> (heap.GetAddr());
size_t readSize = 0;
if (titleListBuf != NULL)
{
common::SdReaderWriter sdReader;
result = sdReader.ReadBufWithCmac(common::TWL_TITLELIST_PATHNAME, titleListBuf, heapSize, &readSize);
COMMON_LOGGER_RESULT_IF_FAILED(result);
if (result.IsSuccess())
{
u32 listHead = 0;
for (u32 i = 0; i < readSize; i++)
{
if (titleListBuf[i] == '\n')
{
char ProgramIdStr[32];
char *error;
std::memcpy(ProgramIdStr, &titleListBuf[listHead], i - listHead);
list[*num] = std::strtoull(ProgramIdStr, &error, 16);
(*num)++;
COMMON_LOGGER_DETAIL("%016llx\n", list[*num - 1]);
listHead = i + 1;
}
}
}
COMMON_LOGGER("%d Title(s) found.\n", *num);
}
else
{
result = nn::Result(nn::Result::LEVEL_FATAL, nn::Result::SUMMARY_OUT_OF_RESOURCE, nn::Result::MODULE_COMMON,
nn::Result::DESCRIPTION_OUT_OF_MEMORY);
}
return result;
}
nn::Result WaitCancelled()
{
nn::nim::TitleProgress progress;
while(true)
{
// キャンセルがResultとして返ってくる / ダウンロード終了まで待つ
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(nn::nim::Shop::GetProgress(&progress));
if(progress.lastResult==nn::nim::ResultCancelRequested() || progress.state==nn::nim::TITLE_STATE_FINISHED)
{
break;
}
nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromMilliSeconds(100));
}
return nn::ResultSuccess();
}
void WaitShopOperationAndFinalize()
{
while (!IsShopOperationSingleFinished())
{
nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromMilliSeconds(100));
}
FinalizeShopOperationSingle();
}
void StartShopOperationSingleRetry(ShopOperation op, u32 retryCount)
{
for(u32 i = 0; i < retryCount; i++)
{
StartShopOperationSingle(op);
WaitShopOperationAndFinalize();
if(GetShopOperationSingleResult().IsSuccess())
{
break;
}
}
}
void TitleDownloader::Start()
{
COMMON_LOGGER("Download %d Title(s).\n", m_TiteNum);
for(u8 i = m_FinishedTitleNum; i < m_TiteNum; i++)
{
s_Progress = i * 100 / m_TiteNum;
nn::am::ProgramInfo info;
// 既にインポート済みならスキップ
if (nn::CTR::IsTwlApp(m_ProgramIdList[i]))
{
m_Result = nn::am::GetProgramInfos(&info, nn::fs::MEDIA_TYPE_NAND, &m_ProgramIdList[i], 1);
if (m_Result.IsSuccess())
{
m_FinishedTitleNum++;
continue;
}
}
else if(nn::CTR::IsCtr(m_ProgramIdList[i]))
{
m_Result = nn::am::GetProgramInfos(&info, nn::fs::MEDIA_TYPE_SDMC, &m_ProgramIdList[i], 1);
if (m_Result.IsSuccess())
{
m_FinishedTitleNum++;
continue;
}
}
StartShopOperationSingle(SHOP_OPERATION_CONNECT_WITHOUT_CLOSE);
WaitShopOperationAndFinalize();
m_Result = GetShopOperationSingleResult();
COMMON_LOGGER_RETURN_VOID_IF_FAILED(m_Result);
nn::nim::TitleConfig config;
s64 requiredSize;
m_Result = GetTitleConfig(m_ProgramIdList[i], &config, &requiredSize);
COMMON_LOGGER_RETURN_VOID_IF_FAILED(m_Result);
StartShopOperationSingle(SHOP_OPERATION_DOWNLOAD_TITLE, config);
WaitShopOperationAndFinalize();
m_Result = GetShopOperationSingleResult();
// ダウンロード済みならスキップ
if(m_Result == nn::nim::ResultAlreadyDownloaded())
{
m_FinishedTitleNum++;
continue;
}
COMMON_LOGGER_RETURN_VOID_IF_FAILED(m_Result);
m_FinishedTitleNum++;
}
// 成功にセットしておく
m_Result = nn::ResultSuccess();
}
void TitleDownloader::SetupTitleList(nn::ProgramId* list, size_t num, u32 index)
{
const size_t listNum = nn::math::Min(num, IMPORTABLE_TITLE_MAX);
for(u32 i = 0; i < listNum; i++)
{
m_ProgramIdList[i] = list[i];
}
m_TiteNum = listNum;
m_FinishedTitleNum = index;
}
void TitleDownloader::SetupTitleListForTWL(nn::ProgramId* eTicketList, size_t eTicketNum, u32 index, nn::ProgramId* sdDSiWareList, size_t sdDSiWareNum)
{
const size_t listNum = nn::math::Min(eTicketNum, IMPORTABLE_TITLE_MAX);
u32 twlAppNum = 0;
// 8thNUPからのショップアカウント削除後もセーブデータが削除されない仕様と、MSETでのSDカードへのDSiWareのバックアップ機能があるため、
// eTicketが存在する、かつセーブデータが存在するタイトルのみダウンロードする
for(u32 i = 0; i < listNum; i++)
{
if(nn::CTR::IsTwlApp(eTicketList[i]))
{
for(u32 j = 0; j < sdDSiWareNum; j++)
{
if(eTicketList[i] == sdDSiWareList[j])
{
m_ProgramIdList[twlAppNum++] = eTicketList[i];
}
}
}
}
m_TiteNum = twlAppNum;
m_FinishedTitleNum = index;
}
void TitleDownloader::CalculateRequiredSize(s64* requiredSize)
{
*requiredSize = 0;
for(u8 i = 0; i < m_TiteNum; i++)
{
s_Progress = i * 100 / m_TiteNum;
StartShopOperationSingleRetry(SHOP_OPERATION_CONNECT_WITHOUT_CLOSE, 3);
m_Result = GetShopOperationSingleResult();
COMMON_LOGGER_RETURN_VOID_IF_FAILED(m_Result);
nn::nim::TitleConfig config;
s64 size;
for(u8 j = 0; j < 3; j++)
{
m_Result = GetTitleConfig(m_ProgramIdList[i], &config, &size);
if(m_Result.IsSuccess())
{
break;
}
}
COMMON_LOGGER_RETURN_VOID_IF_FAILED(m_Result);
*requiredSize += size;
}
}
u32 TitleDownloader::GetFinishedTitleNum()
{
return m_FinishedTitleNum;
}
}