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@803 385bec56-5757-e545-9c3a-d8741f4650f1
454 lines
14 KiB
C++
454 lines
14 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];
|
|
// ダウンロード済みのタイトルの個数
|
|
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;
|
|
|
|
s_Progress = 0;
|
|
TitleDownloader::m_Result = ListUpTwlTitles(s_ProgramIdList, &s_ProgramIdNum);
|
|
COMMON_LOGGER_RETURN_VOID_IF_FAILED(TitleDownloader::m_Result);
|
|
TwlTitleDownloader.SetupTitleList(s_ProgramIdList, s_ProgramIdNum, s_FinishedTitleNum);
|
|
|
|
TitleDownloader::m_Result = nn::am::InitializeForNetworkImporter();
|
|
COMMON_LOGGER_RETURN_VOID_IF_FAILED(TitleDownloader::m_Result);
|
|
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::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;
|
|
}
|
|
|
|
}
|