ショップ処理を関数にまとめる

TWLタイトルをダウンロードできるように

git-svn-id: file:///Volumes/Transfer/gigaleak_20231201/2020-05-23%20-%20ctr.7z%20+%20svn_v1.068.zip/ctr/svn/ctr_Repair@213 385bec56-5757-e545-9c3a-d8741f4650f1
This commit is contained in:
N2614 2011-04-22 00:13:55 +00:00
parent 08b0b17ee1
commit 19aa839160
6 changed files with 468 additions and 251 deletions

View File

@ -321,6 +321,7 @@ extern "C" void nnMain(void)
bool flip = false;
InitializeState();
for(;;)
{

View File

@ -72,18 +72,15 @@ bool s_ExecuteFgNup = false;
u32 s_FgNupRetryCount = 0;
// 時計同期モードかどうか
bool s_IsSyncClock = false;
// アカウント削除を開始したかどうか
bool s_ExecuteDeleteAccount = false;
// アカウント削除を何回リトライしたか
u32 s_DeleteAccountRetryCount = 0;
// IVS取得を開始したかどうか
bool s_ExecuteGetIvs = false;
// IVS取得を何回リトライしたか
u32 s_GetIvsRetryCount = 0;
// ConnectOnlyを開始したかどうか
bool s_ExecuteConnectOnly = false;
// ConnectOnlyを回リトライしたか
u32 s_ConnectOnlyRetryCount = 0;
// TWLタイトルのダウンロードを開始したかどうか
bool s_ExecuteTitleDownload = false;
// TWLタイトルのダウンロードを何回リトライしたか
u32 s_TitleDownloadRetryCount = 0;
// ショップ処理を開始したかどうか
bool s_ShopOperationExecuted[SHOP_OPERATION_NUM_MAX];
// ショップ処理を何回リトライしたか
u32 s_ShopOperationRetryCount[SHOP_OPERATION_NUM_MAX];
void PutAliveMessage(::std::vector<std::string>& operationMessage, const char* str);
bool CheckAndReadAPSetting(::std::vector<std::string>& operationMessage);
@ -150,7 +147,6 @@ void CheckConsoleInitialized(::std::vector<std::string>& message, bool& goNextSt
void CheckExistsSerialNumber(::std::vector<std::string>& message, bool& goNextStep);
void CheckIvsinSd(::std::vector<std::string>& message, bool& goNextStep);
void CheckRegioinSd(::std::vector<std::string>& message, bool& goNextStep);
void ConnectOnly(::std::vector<std::string>& message, bool& goNextStep, RestoreState nextState);
// SDカード挿入チェック
void CheckSdInserted(::std::vector<std::string>& message, bool& goNextStep)
@ -434,10 +430,15 @@ void CheckRegioinSd(::std::vector<std::string>& message, bool& goNextStep)
}
}
void ConnectOnly(::std::vector<std::string>& message, bool& goNextStep, RestoreState nextState)
void ShopOperationSingleTemplate(
std::vector<std::string>& message,
ShopOperation op,
const char* aliveMessage,
const char* logMesasge,
const char* retryLogMessage,
RestoreState nextState
)
{
NN_UNUSED_VAR(goNextStep);
// ACアダプタが必要か
if (NeedsAcAdater())
{
@ -445,12 +446,12 @@ void ConnectOnly(::std::vector<std::string>& message, bool& goNextStep, RestoreS
}
// アップデートを行う
if (!s_ExecuteConnectOnly)
if (!s_ShopOperationExecuted[op])
{
if (ImportCountryLanguageData().IsSuccess())
{
StartShopOperationSingle(SHOP_OPERATION_CONNECT_ONLY);
s_ExecuteConnectOnly = true;
StartShopOperationSingle(op);
s_ShopOperationExecuted[op] = true;
}
else
{
@ -460,12 +461,12 @@ void ConnectOnly(::std::vector<std::string>& message, bool& goNextStep, RestoreS
// 動いていることを表示
{
PutAliveMessage(message, "Shop Connect");
PutAliveMessage(message, aliveMessage);
}
if (IsShopOperationSingleFinished())
{
FinishShopOperationSingle();
FinalizeShopOperationSingle();
// エラーがあったら表示する
if (GetShopOperationSingleResult().IsFailure())
{
@ -478,20 +479,22 @@ void ConnectOnly(::std::vector<std::string>& message, bool& goNextStep, RestoreS
if (GetShopOperationSingleResult().IsSuccess())
{
COMMON_LOGGER("Shop Connect Finished.\n");
COMMON_LOGGER("%s", logMesasge);
// アカウント削除完了ファイルを作成
CreateDeleteAccountFinishedFile();
s_RestoreState = nextState;
}
else
{
if (s_ConnectOnlyRetryCount++ < RETRY_MAX)
if (s_ShopOperationRetryCount[op]++ < RETRY_MAX)
{
// エラーのためやり直す
COMMON_LOGGER_RESULT_IF_FAILED(GetShopOperationSingleResult());
COMMON_LOGGER("Shop Connect Failed. Retrying... %d\n", s_ConnectOnlyRetryCount);
COMMON_LOGGER("%s %d\n", retryLogMessage, s_ShopOperationRetryCount[op]);
// Unregister用のスレッドを作るとこからやり直し
s_ExecuteConnectOnly = false;
// スレッドを作るとこからやり直し
s_ShopOperationExecuted[op] = false;
}
else
{
@ -804,70 +807,8 @@ void ControlState(::std::vector<std::string>& operationMessage, bool& nextStep)
case DOWNLOAD_IVS:
{
// ACアダプタが必要か
if (NeedsAcAdater())
{
operationMessage.push_back(::std::string("Connect AC Adapter!!"));
}
// アップデートを行う
if (!s_ExecuteGetIvs)
{
if (ImportCountryLanguageData().IsSuccess())
{
StartShopOperationSingle(SHOP_OPERATION_GET_IVS);
s_ExecuteGetIvs = true;
}
else
{
s_RestoreState = FAIL;
}
}
// 動いていることを表示
{
PutAliveMessage(operationMessage, "Get Ivs");
}
if (IsShopOperationSingleFinished())
{
FinishShopOperationSingle();
// エラーがあったら表示する
if (GetShopOperationSingleResult().IsFailure())
{
// APが見つからない
if (GetUpdateResult() == nn::ac::ResultNotFoundAccessPoint())
{
COMMON_LOGGER("No Access Point Found!");
}
}
if (GetShopOperationSingleResult().IsSuccess())
{
COMMON_LOGGER("Get Ivs Finished.\n");
// IVS取得完了ファイルを作成
CreateDownloadIvsFinishedFile();
s_RestoreState = DOWNLOAD_IVS_DONE;
}
else
{
if (s_GetIvsRetryCount++ < RETRY_MAX)
{
// エラーのためやり直す
COMMON_LOGGER_RESULT_IF_FAILED(GetShopOperationSingleResult());
COMMON_LOGGER("Get Ivs Failed. Retrying... %d\n", s_GetIvsRetryCount);
// Unregister用のスレッドを作るとこからやり直し
s_ExecuteGetIvs = false;
}
else
{
s_RestoreState = FAIL;
}
}
}
ShopOperationSingleTemplate(operationMessage, SHOP_OPERATION_GET_IVS, "Get Ivs",
"Get Ivs Finished.\n", "Get Ivs Failed. Retrying...", DOWNLOAD_IVS_DONE);
}
break;
@ -893,70 +834,8 @@ void ControlState(::std::vector<std::string>& operationMessage, bool& nextStep)
case DELETE_ACCOUNT:
{
// ACアダプタが必要か
if (NeedsAcAdater())
{
operationMessage.push_back(::std::string("Connect AC Adapter!!"));
}
// アップデートを行う
if (!s_ExecuteDeleteAccount)
{
if (ImportCountryLanguageData().IsSuccess())
{
StartShopOperationSingle(SHOP_OPERATION_UNREGISTER);
s_ExecuteDeleteAccount = true;
}
else
{
s_RestoreState = FAIL;
}
}
// 動いていることを表示
{
PutAliveMessage(operationMessage, "Deleting Account");
}
if (IsShopOperationSingleFinished())
{
FinishShopOperationSingle();
// エラーがあったら表示する
if (GetShopOperationSingleResult().IsFailure())
{
// APが見つからない
if (GetUpdateResult() == nn::ac::ResultNotFoundAccessPoint())
{
COMMON_LOGGER("No Access Point Found!");
}
}
if (GetShopOperationSingleResult().IsSuccess())
{
COMMON_LOGGER("Delete Account Finished.\n");
// アカウント削除完了ファイルを作成
CreateDeleteAccountFinishedFile();
s_RestoreState = DELETE_ACCOUNT_DONE;
}
else
{
if (s_DeleteAccountRetryCount++ < RETRY_MAX)
{
// エラーのためやり直す
COMMON_LOGGER_RESULT_IF_FAILED(GetShopOperationSingleResult());
COMMON_LOGGER("Delete Account Failed. Retrying... %d\n", s_DeleteAccountRetryCount);
// Unregister用のスレッドを作るとこからやり直し
s_ExecuteDeleteAccount = false;
}
else
{
s_RestoreState = FAIL;
}
}
}
ShopOperationSingleTemplate(operationMessage, SHOP_OPERATION_UNREGISTER, "Deleting Account",
"Delete Account Finished.\n", "Delete Account Failed. Retrying...", DELETE_ACCOUNT_DONE);
}
break;
@ -980,12 +859,6 @@ void ControlState(::std::vector<std::string>& operationMessage, bool& nextStep)
}
break;
case SYNC_TICKET:
{
ConnectOnly(operationMessage, nextStep, DOWNLOAD_TWL);
}
break;
case READ_FILELIST:
{
result = SetupFileList();
@ -1166,13 +1039,20 @@ void ControlState(::std::vector<std::string>& operationMessage, bool& nextStep)
}
break;
case SYNC_TICKET:
{
ShopOperationSingleTemplate(operationMessage, SHOP_OPERATION_CONNECT_ONLY, "Shop Connect",
"Shop Connect Finished.\n", "Shop Connect Failed. Retrying...", DOWNLOAD_TWL);
}
break;
case DOWNLOAD_TWL:
{
static bool init = true;
if (init)
if (!s_ExecuteTitleDownload)
{
COMMON_LOGGER("Download Twl Title\n");
init = false;
s_ExecuteTitleDownload = true;
StartTitleDownload();
}
// 動いていることを表示
@ -1180,16 +1060,27 @@ void ControlState(::std::vector<std::string>& operationMessage, bool& nextStep)
PutAliveMessage(operationMessage, "Download Twl Title");
}
if (TitleDownloader::Finished())
if (DownloadTitleFinished())
{
if (TitleDownloader::Succeeded())
if (DownloadTitleSucceeded())
{
s_IsSyncClock = true;
s_RestoreState = READ_FILELIST;
}
else
{
s_RestoreState = FAIL;
if (s_TitleDownloadRetryCount++ < RETRY_MAX)
{
// エラーのためやり直す
COMMON_LOGGER_RESULT_IF_FAILED(GetShopOperationSingleResult());
COMMON_LOGGER("Download Twl Title Failed. Retrying... %d\n", s_TitleDownloadRetryCount);
s_ExecuteTitleDownload = false;
}
else
{
s_RestoreState = FAIL;
}
}
}
}
@ -1371,6 +1262,12 @@ void InitializeState()
s_RestoreState = STARTUP;
s_RestoreMode = RESTORE_MODE_RESTORE;
for(u32 i = 0; i < SHOP_OPERATION_NUM_MAX; i++)
{
s_ShopOperationExecuted[i] = false;
s_ShopOperationRetryCount[i] = 0;
}
common::InitializeFileCheck();
s_ExistAPSettingAnnotation = false;

View File

@ -20,6 +20,8 @@
#include <nn/ac/private/ac.h>
#include <nn/ac/CTR/private/ac_InternalApi.h>
#include <nn/nim/CTR/private/nim_ShopPrivateApi.h>
#include <nn/CTR/CTR_ProgramId.h>
#include <nn/am.h>
#include "Shop.h"
#include "CommonLogger.h"
@ -27,23 +29,23 @@
using namespace ES_NAMESPACE;
using namespace EC_NAMESPACE;
#define NIM_UNREGISTER_RESULT_CHECK(result) \
#define NIM_SHOP_RESULT_CHECK(result) \
do { \
if (result.IsFailure()) \
{ \
ECCustomerSupportCode csc; \
NN_UTIL_PANIC_IF_FAILED( \
nn::nim::Shop::GetCustomerSupportCode(&csc)); \
nn::nim::Shop::GetCustomerSupportCode(&csc); \
COMMON_LOGGER("CSCode: %d\n", csc); \
nn::dbg::PrintResult(result); \
s_ShopResult = result; \
ShopOperationFinalize(); \
return; \
} \
} while(0)
namespace
{
const char* const TIGER_PROGRAM_ID_STR = "0004001000020900";
nn::Result s_ShopResult = nn::ResultSuccess();
const size_t UNREGISTER_THREAD_STACK_SIZE = 0x1000;
@ -53,6 +55,59 @@ nn::os::StackBuffer<UNREGISTER_THREAD_STACK_SIZE> s_UnregisterThreadStack;
static const size_t EC_BUFFER_SIZE = 128 * 1024;
static u8 s_EcBufffer[EC_BUFFER_SIZE];
struct ShopThreadParam
{
ConsoleRestore::ShopOperation op;
nn::nim::TitleConfig config;
};
// TitleProgress の ==, != を定義
bool operator==(
nn::nim::TitleProgress& lhs,
nn::nim::TitleProgress& rhs)
{
return (lhs.state == rhs.state &&
lhs.lastResult == rhs.lastResult &&
lhs.downloadedSize == rhs.downloadedSize &&
lhs.totalSize == rhs.totalSize);
}
bool operator!=(
nn::nim::TitleProgress& lhs,
nn::nim::TitleProgress& rhs)
{
return (! (lhs == rhs));
}
// TitleState の文字列を取得
const char* GetTitleStateString(nn::nim::TitleState state)
{
switch (state)
{
case nn::nim::TITLE_STATE_NOT_INITIALIZED:
return "NOT_INITIALIZED";
case nn::nim::TITLE_STATE_INITIALIZED:
return "INITIALIZED";
case nn::nim::TITLE_STATE_DOWNLOAD_TMD:
return "DOWNLOAD_TMD";
case nn::nim::TITLE_STATE_PREPARE_SAVE_DATA:
return "PREPARE_SAVE_DATA";
case nn::nim::TITLE_STATE_DOWNLOAD_CONTENTS:
return "DOWNLOAD_CONTENTS";
case nn::nim::TITLE_STATE_WAIT_COMMIT:
return "WAIT_COMMIT";
case nn::nim::TITLE_STATE_COMMITTING:
return "COMMITTING";
case nn::nim::TITLE_STATE_FINISHED:
return "FINISHED";
case nn::nim::TITLE_STATE_VERSION_MISMATCH:
return "VERSION_MISMATCH";
default:
return "UNKNOWN";
}
}
// 空文字列と NULL ポインタをそれぞれ <empty> と NULL として返す
const char* Cstr(const char* p)
{
@ -202,7 +257,7 @@ void ShopOperationConnect();
void ShopOperationFinalize();
void ShopOperationConnect()
void ShopOperationConnect(ECAccountInfo* pAccountInfo)
{
nn::Result result = nn::ResultSuccess();
@ -210,14 +265,44 @@ void ShopOperationConnect()
Connect
-------------------------------------------------------------------- */
NN_LOG("nim::Shop::Connect\n");
ECAccountInfo* pAccountInfo;
result = nn::nim::Shop::Connect(&pAccountInfo, s_EcBufffer, EC_BUFFER_SIZE);
NIM_UNREGISTER_RESULT_CHECK(result);
NIM_SHOP_RESULT_CHECK(result);
PrintECAccountInfo(*pAccountInfo);
NN_LOG("\n");
}
void ShopOperationConnect()
{
nn::Result result = nn::ResultSuccess();
/* -------------------------------------------------------------------
Initialize
-------------------------------------------------------------------- */
NN_LOG("nim::InitializeForShop\n");
result = nn::nim::InitializeForShop();
NIM_SHOP_RESULT_CHECK(result);
/* -------------------------------------------------------------------
SetParameter
-------------------------------------------------------------------- */
NN_LOG("nim::Shop::SetApplication Id\n");
nn::nim::Shop::SetParameter(EC_APP_ID, TIGER_PROGRAM_ID_STR);
NIM_SHOP_RESULT_CHECK(result);
NN_LOG("nim::Shop::SetTIN\n");
result = nn::nim::Shop::SetTin(NIM_TIN);
NIM_SHOP_RESULT_CHECK(result);
/* -------------------------------------------------------------------
Connect
-------------------------------------------------------------------- */
ECAccountInfo* pAccountInfo;
ShopOperationConnect(pAccountInfo);
}
void ShopOperationFinalize()
{
nn::Result result = nn::ResultSuccess();
@ -227,16 +312,22 @@ void ShopOperationFinalize()
-------------------------------------------------------------------- */
NN_LOG("nim::FinalizeForShop\n");
result = nn::nim::FinalizeForShop();
NIM_UNREGISTER_RESULT_CHECK(result);
NIM_SHOP_RESULT_CHECK(result);
NN_LOG("util::ac::Initialize\n");
FinalizeInternal();
NN_LOG("util::ac::Finalize\n");
result = FinalizeInternal();
NIM_SHOP_RESULT_CHECK(result);
}
// メイン関数
void ShopOperationSingleThreadFunc(ShopOperation op)
namespace
{
if(op == SHOP_OPERATION_CLOSE_WITHOUT_CONNECT)
// メイン関数
void ShopOperationSingleThreadFunc(ShopThreadParam param)
{
s_ShopResult = nn::ResultSuccess();
if(param.op == SHOP_OPERATION_CLOSE_WITHOUT_CONNECT)
{
ShopOperationFinalize();
return;
@ -247,21 +338,7 @@ void ShopOperationSingleThreadFunc(ShopOperation op)
nn::Result result = nn::ResultSuccess();
/* -------------------------------------------------------------------
Initialize
-------------------------------------------------------------------- */
NN_LOG("nim::InitializeForShop\n");
result = nn::nim::InitializeForShop();
NIM_UNREGISTER_RESULT_CHECK(result);
/* -------------------------------------------------------------------
SetParameter
-------------------------------------------------------------------- */
NN_LOG("nim::Shop::SetTIN\n");
result = nn::nim::Shop::SetTin(NIM_TIN);
NIM_UNREGISTER_RESULT_CHECK(result);
switch(op)
switch(param.op)
{
case SHOP_OPERATION_CONNECT_WITHOUT_CLOSE:
{
@ -271,17 +348,7 @@ void ShopOperationSingleThreadFunc(ShopOperation op)
case SHOP_OPERATION_CONNECT_ONLY:
{
// Connectするのみ
/* -------------------------------------------------------------------
Connect
-------------------------------------------------------------------- */
NN_LOG("nim::Shop::Connect\n");
ECAccountInfo* pAccountInfo;
result = nn::nim::Shop::Connect(&pAccountInfo, s_EcBufffer, EC_BUFFER_SIZE);
NIM_UNREGISTER_RESULT_CHECK(result);
PrintECAccountInfo(*pAccountInfo);
NN_LOG("\n");
ShopOperationConnect();
}
break;
@ -289,22 +356,14 @@ void ShopOperationSingleThreadFunc(ShopOperation op)
{
// IVSを取得する
result = nn::nim::Shop::ImportIvsFromInfrastructure();
NIM_UNREGISTER_RESULT_CHECK(result);
NIM_SHOP_RESULT_CHECK(result);
}
break;
case SHOP_OPERATION_UNREGISTER:
{
/* -------------------------------------------------------------------
Connect
-------------------------------------------------------------------- */
NN_LOG("nim::Shop::Connect\n");
ECAccountInfo* pAccountInfo;
result = nn::nim::Shop::Connect(&pAccountInfo, s_EcBufffer, EC_BUFFER_SIZE);
NIM_UNREGISTER_RESULT_CHECK(result);
PrintECAccountInfo(*pAccountInfo);
NN_LOG("\n");
ShopOperationConnect(pAccountInfo);
if (pAccountInfo->accountStatus && pAccountInfo->accountStatus[0] == 'R')
{
/* ---------------------------------------------------------------
@ -312,7 +371,7 @@ void ShopOperationSingleThreadFunc(ShopOperation op)
---------------------------------------------------------------- */
NN_LOG("nim::Shop::Unregister\n");
result = nn::nim::Shop::Unregister();
NIM_UNREGISTER_RESULT_CHECK(result);
NIM_SHOP_RESULT_CHECK(result);
}
else
{
@ -320,19 +379,93 @@ void ShopOperationSingleThreadFunc(ShopOperation op)
}
}
break;
case SHOP_OPERATION_DOWNLOAD_TITLE:
{
NN_LOG("Try Download %016llx\n", param.config.titleId);
result = nn::nim::Shop::StartDownload(param.config);
if (result == nn::nim::ResultBusy() || result == nn::nim::ResultAlreadyDone())
{
COMMON_LOGGER("Download Content -> Nim is busy\n");
}
else if (result == nn::nim::ResultInvalidTitle())
{
COMMON_LOGGER("Download Content -> Invalid Title\n");
}
else if (result.IsFailure())
{
COMMON_LOGGER("Download Content -> Failure %x\n", result.GetPrintableBits());
}
/* -------------------------------------------------------------------
GetProgress
-------------------------------------------------------------------- */NN_LOG("nim::Shop::GetProgress()\n");
nn::nim::TitleProgress before;
nn::nim::TitleProgress latest;
while (true)
{
result = nn::nim::Shop::GetProgress(&latest);
// 既にインポート済み
if (result == nn::am::ResultAlreadyExists())
{
break;
}
NIM_SHOP_RESULT_CHECK(result);
// Print progress
if (latest != before)
{
NN_LOG("%8lld / %8lld (%d:%s)\n",
latest.downloadedSize,
latest.totalSize,
latest.state.Get(),
GetTitleStateString(latest.state));
if (latest.state == nn::nim::TITLE_STATE_FINISHED)
{
NN_LOG("Finished Download\n");
break;
}
before = latest;
}
// あまりにも頻繁に GetProgress を呼ぶと、ダウンロード処理の速度に
// 影響が出てしまいます。少なくとも数フレーム以上の間隔をあけて
// GetProgress することを推奨します。
nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromMilliSeconds(50));
}
}
break;
}
ShopOperationFinalize();
}
void StartShopOperationSingle(ShopOperation op)
{
NN_LOG("Start ShopOperationSingle\n");
s_ShopResult = nn::ResultSuccess();
s_UnregisterThread.Start(ShopOperationSingleThreadFunc, op, s_UnregisterThreadStack);
}
void FinishShopOperationSingle()
void StartShopOperationSingle(ShopOperation op, nn::nim::TitleConfig config)
{
NN_LOG("Start ShopOperationSingle\n");
ShopThreadParam param;
param.op = op;
param.config = config;
s_UnregisterThread.Start(ShopOperationSingleThreadFunc, param, s_UnregisterThreadStack);
}
void StartShopOperationSingle(ShopOperation op)
{
ShopThreadParam param;
param.op = op;
NN_LOG("Start ShopOperationSingle\n");
s_UnregisterThread.Start(ShopOperationSingleThreadFunc, param, s_UnregisterThreadStack);
}
void FinalizeShopOperationSingle()
{
NN_LOG("Finalize ShopOperationSingle\n");
s_UnregisterThread.Join();

View File

@ -17,6 +17,7 @@
#define SHOP_H_
#include <nn.h>
#include <nn/nim.h>
#define NIM_TIN "56789"
@ -29,11 +30,15 @@ typedef enum SHOP_OPERATION
SHOP_OPERATION_GET_IVS, // Shop::ImportIvsFromInfrastructureを実行
SHOP_OPERATION_UNREGISTER, // Shop::Unregisterを実行
SHOP_OPERATION_CONNECT_WITHOUT_CLOSE, // Shop::ConnectしてCloseしない
SHOP_OPERATION_CLOSE_WITHOUT_CONNECT // Shop::ConnectせずいきなりCloseする
SHOP_OPERATION_CLOSE_WITHOUT_CONNECT, // Shop::ConnectせずいきなりCloseする
SHOP_OPERATION_DOWNLOAD_TITLE, // Titleをダウンロードする
SHOP_OPERATION_NUM_MAX
} ShopOperation;
void StartShopOperationSingle(ShopOperation op, nn::nim::TitleConfig config);
void StartShopOperationSingle(ShopOperation op);
void FinishShopOperationSingle();
void FinalizeShopOperationSingle();
bool IsShopOperationSingleFinished();
nn::Result GetShopOperationSingleResult();

View File

@ -1,27 +1,137 @@
/*---------------------------------------------------------------------------*
Project: Horizon
File: TitleDownloader.cpp
Project: Horizon
File: TitleDownloader.cpp
Copyright 2009 Nintendo. All rights reserved.
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.
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$
$Rev$
*---------------------------------------------------------------------------*/
#include "TitleDownloader.h"
#include <nn.h>
#include <nn/am.h>
#include <nn/nim.h>
#include <nn/CTR/CTR_ProgramId.h>
#include "CommonLogger.h"
#include "HeapManager.h"
#include "TitleDownloader.h"
#include "Shop.h"
namespace
{
bit8 s_buffer1[400 * 1024];
const size_t TITLE_DOWNLOADER_STACK_SIZE = 0x1000;
nn::os::Thread s_TitleDownloaderThread;
nn::os::StackBuffer<TITLE_DOWNLOADER_STACK_SIZE> s_TitleDownloaderThreadStack;
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"}
};
NN_LOG("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));
*entry = &(titleCatalog->entries[0]);
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);
NN_LOG("titleId : 0x%016llx\n", titleConfig->titleId);
NN_LOG("version : %lld\n" , titleConfig->version);
NN_LOG("ratingAge : %d\n" , titleConfig->ratingAge);
NN_LOG("media : %d\n" , titleConfig->media);
return nn::ResultSuccess();
}
} // namespace <unnamed>
namespace ConsoleRestore
{
TitleDownloader::TitleDownloader() : m_Executing(false)
nn::Result TitleDownloader::m_Result = nn::ResultSuccess();
void TitleDownloaderThreadFunc()
{
TitleDownloader TwlTitleDownloader;
TwlTitleDownloader.Start();
}
void StartTitleDownload()
{
s_TitleDownloaderThread.Start(TitleDownloaderThreadFunc, s_TitleDownloaderThreadStack);
}
bool DownloadTitleFinished()
{
return s_TitleDownloaderThread.IsValid() && !s_TitleDownloaderThread.IsAlive();
}
bool DownloadTitleSucceeded()
{
return TitleDownloader::m_Result.IsSuccess() && GetShopOperationSingleResult().IsSuccess();
}
TitleDownloader::TitleDownloader() : m_TwlTiteNum(0)
{
// TODO 自動生成されたコンストラクター・スタブ
for(u32 i = 0; i < TWL_IMPORTABLE_TITLE_MAX; i++)
{
m_ProgramIdList[i] = 0;
}
}
@ -31,28 +141,88 @@ TitleDownloader::~TitleDownloader()
}
void TitleDownloader::Start(nn::ProgramID* programIdList, size_t listNum)
nn::Result WaitCancelled()
{
if(m_Executing)
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()
{
nn::Result result;
result = ListUp();
if(result.IsFailure())
{
return;
}
m_Executing = true;
for(u8 i = 0; i < m_TwlTiteNum; i++)
{
StartShopOperationSingle(SHOP_OPERATION_CONNECT_WITHOUT_CLOSE);
WaitShopOperationAndFinalize();
nn::nim::TitleConfig config;
result = GetTitleConfig(m_ProgramIdList[i], &config);
if (result.IsSuccess())
{
StartShopOperationSingle(SHOP_OPERATION_DOWNLOAD_TITLE, config);
WaitShopOperationAndFinalize();
}
}
}
bool TitleDownloader::Succeeded()
nn::Result TitleDownloader::ListUp()
{
return true;
}
const u32 AM_TICKET_MAX = 8192;
bool TitleDownloader::Finished()
{
return true;
s32 personalizedTicketNum;
nn::am::TicketInfo *ticketInfoBuffer =
reinterpret_cast<nn::am::TicketInfo*> (common::HeapManager::GetHeap()->Allocate(
sizeof(nn::am::TicketInfo) * AM_TICKET_MAX));
if(ticketInfoBuffer == NULL)
{
COMMON_LOGGER("Failed Allocate Heap!!\n");
m_Result = nn::Result(nn::Result::LEVEL_FATAL, nn::Result::SUMMARY_OUT_OF_RESOURCE, nn::Result::MODULE_COMMON,
nn::Result::DESCRIPTION_OUT_OF_MEMORY);
return m_Result;
}
m_Result=nn::am::GetPersonalizedTicketInfoList(&personalizedTicketNum, ticketInfoBuffer, AM_TICKET_MAX); // 全部
COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(m_Result);
for (u32 i = 0; i < personalizedTicketNum; i++)
{
if (nn::CTR::IsTwlApp(ticketInfoBuffer[i].titleId))
{
m_ProgramIdList[m_TwlTiteNum] = ticketInfoBuffer[i].titleId;
m_TwlTiteNum++;
NN_LOG("%016llx\n", m_ProgramIdList[m_TwlTiteNum - 1]);
}
}
return m_Result;
}

View File

@ -21,19 +21,30 @@
namespace ConsoleRestore
{
void StartTitleDownload();
bool DownloadTitleFinished();
bool DownloadTitleSucceeded();
class TitleDownloader
{
public:
TitleDownloader();
virtual ~TitleDownloader();
void Start(nn::ProgramID* programIdList, size_t listNum);
void Start();
static bool Succeeded();
static bool Finished();
NN_PADDING4;
static nn::Result m_Result;
private:
bool m_Executing;
nn::Result ListUp();
static const size_t TWL_IMPORTABLE_TITLE_MAX = 30;
nn::ProgramID m_ProgramIdList[TWL_IMPORTABLE_TITLE_MAX];
u32 m_TwlTiteNum;
};