/*---------------------------------------------------------------------------* 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 #include #include #include #include #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]; const size_t TITLE_DOWNLOADER_STACK_SIZE = 0x2000; nn::os::Thread s_TitleDownloaderThread; nn::os::StackBuffer 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; inEntries == 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) { EC_NAMESPACE::ECTitleCatalogEntry *entry; NN_UTIL_RETURN_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); 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); return nn::ResultSuccess(); } } // namespace namespace ConsoleRestore { nn::Result TitleDownloader::m_Result = nn::ResultSuccess(); void TwlTitleDownloaderThreadFunc() { TitleDownloader TwlTitleDownloader; s_Progress = 0; size_t num = 0; nn::ProgramId list[256]; TitleDownloader::m_Result = ListUpTwlTitles(list, &num); COMMON_LOGGER_RETURN_VOID_IF_FAILED(TitleDownloader::m_Result); TwlTitleDownloader.SetupTitleList(list, num); TwlTitleDownloader.Start(); } void PreinstallTitleDownloaderThreadFunc(PreinstallListupParam param) { TitleDownloader PreinstallTitleDownloader; PreinstallImporter importer; s_Progress = 0; size_t num = 0; nn::ProgramId list[256]; TitleDownloader::m_Result = importer.ListTitles(list, &num, param.deviceId, param.serialNo); COMMON_LOGGER_RETURN_VOID_IF_FAILED(TitleDownloader::m_Result); PreinstallTitleDownloader.SetupTitleList(list, num); PreinstallTitleDownloader.Start(); } void StartTwlTitleDownload() { s_TitleDownloaderThread.Start(TwlTitleDownloaderThreadFunc, s_TitleDownloaderThreadStack); } bool PreparePreinstallTitleDownload() { PreinstallImporter importer; bool isAlreadyAvailable = false; TitleDownloader::m_Result = importer.SetupSd(&isAlreadyAvailable); if(isAlreadyAvailable) { return false; } return TitleDownloader::m_Result.IsSuccess(); } void StartPreinstallTitleDownload(bit64 deviceId, u8* serialNo) { PreinstallListupParam param; param.deviceId = deviceId; std::memcpy(param.serialNo, serialNo, nn::cfg::CTR::CFG_SECURE_INFO_SERIAL_NO_LEN); s_TitleDownloaderThread.Start(PreinstallTitleDownloaderThreadFunc, param, 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() && GetShopOperationSingleResult().IsSuccess(); } u32 GetTitleDownloadProgress() { return s_Progress; } TitleDownloader::TitleDownloader() : m_TiteNum(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 (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_WITH_LINE(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として返ってくる / ダウンロード終了まで待つ NN_UTIL_RETURN_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 TitleDownloader::Start() { for(u8 i = 0; i < m_TiteNum; i++) { s_Progress = i * 100 / m_TiteNum; StartShopOperationSingle(SHOP_OPERATION_CONNECT_WITHOUT_CLOSE); WaitShopOperationAndFinalize(); nn::nim::TitleConfig config; m_Result = GetTitleConfig(m_ProgramIdList[i], &config); COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(m_Result); if (m_Result.IsSuccess()) { StartShopOperationSingle(SHOP_OPERATION_DOWNLOAD_TITLE, config); WaitShopOperationAndFinalize(); } } } void TitleDownloader::SetupTitleList(nn::ProgramId* list, size_t num) { 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; } }