/*---------------------------------------------------------------------------* Project: Horizon File: Shop.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 #include #include #include #include "Shop.h" #include "CommonLogger.h" using namespace ES_NAMESPACE; using namespace EC_NAMESPACE; #define NIM_SHOP_RESULT_CHECK(result) \ do { \ if (result.IsFailure()) \ { \ ECCustomerSupportCode csc; \ nn::nim::Shop::GetCustomerSupportCode(&csc); \ COMMON_LOGGER("CSCode: %d\n", csc); \ nn::dbg::PrintResult(result); \ s_ShopResult = result; \ 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; nn::os::Thread s_UnregisterThread; nn::os::StackBuffer 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 ポインタをそれぞれ と NULL として返す const char* Cstr(const char* p) { return p ? (p[0] ? p : "") : "NULL"; } // ECTicketVersions を出力 void PrintECTicketVersions(const ECTicketVersions& ticketVersions) { if (ticketVersions.nTicketVersions == 0) { NN_LOG("No TicketVersions\n"); return; } NN_LOG("----- ECTicketVersions -----\n"); for (u32 i = 0; i < ticketVersions.nTicketVersions; i++) { ECTicketVersion version = ticketVersions.ticketVersions[i]; NN_LOG("%03d: 0x%016llx v%d\n", i, version.ticketId, version.version); } NN_LOG("---------------------------\n"); } // ECAccountInfo の情報を出力 void PrintECAccountInfo(const ECAccountInfo& accountInfo) { NN_LOG("========== ECAccountInfo ==========\n"); NN_LOG("accountId\n %s\n", Cstr(accountInfo.accountId)); NN_LOG("accountStatus\n %s\n", Cstr(accountInfo.accountStatus)); if (accountInfo.accountBalance == NULL) { NN_LOG("accountBalance\n NULL\n"); } else { NN_LOG("accountBalance->amount\n %s\n", Cstr(accountInfo.accountBalance->amount)); NN_LOG("accountBalance->currency\n %s\n", Cstr(accountInfo.accountBalance->currency)); } if (accountInfo.agreedEULAVersion == NULL) { NN_LOG("agreedEULAVersion\n NULL\n"); } else { NN_LOG("agreedEULAVersion\n %lld\n", *accountInfo.agreedEULAVersion); } if (accountInfo.latestEULAVersion == NULL) { NN_LOG("latestEULAVersion\n NULL\n"); } else { NN_LOG("latestEULAVersion\n %lld\n", *accountInfo.latestEULAVersion); } NN_LOG("country\n %s\n", Cstr(accountInfo.country)); NN_LOG("extAccountId\n %s\n", Cstr(accountInfo.extAccountId)); NN_LOG("deviceToken\n %s\n", Cstr(accountInfo.deviceToken)); NN_LOG("weakToken\n %s\n", Cstr(accountInfo.weakToken)); NN_LOG("isStandbyMode\n %d\n", accountInfo.isStandbyMode); if (accountInfo.owned == NULL) { NN_LOG("owned\n NULL\n"); } else { PrintECTicketVersions(*(accountInfo.owned)); } } nn::Result PrintNetworkSetting() { nn::ac::NetworkSetting networkSetting; NN_UTIL_RETURN_IF_FAILED(nn::ac::LoadNetworkSetting(0, networkSetting)); COMMON_LOGGER("SSID: %s\n", networkSetting.wireless.essidSecurity.ssid); COMMON_LOGGER("DNS : %d.%d.%d.%d\n", networkSetting.ip.dns[0][0], networkSetting.ip.dns[0][1], networkSetting.ip.dns[0][2], networkSetting.ip.dns[0][3]); return nn::ResultSuccess(); } nn::Result ConnectNetwork() { nn::Result result = nn::ResultSuccess(); nn::ac::Config config; result = nn::ac::CreateDefaultConfig(&config); NN_UTIL_RETURN_IF_FAILED(result); result = nn::ac::ConnectWithoutEula(config); NN_UTIL_RETURN_IF_FAILED(result); NN_LOG("Success nn::ac::ConnectWithoutEula\n"); NN_UTIL_RETURN_IF_FAILED(PrintNetworkSetting()); return nn::ResultSuccess(); } nn::Result InitializeInternal() { nn::Result result = nn::ResultSuccess(); result = nn::ac::InitializeInternal(); NN_UTIL_RETURN_IF_FAILED(result); result = ConnectNetwork(); NN_UTIL_RETURN_IF_FAILED(result); return nn::ResultSuccess(); } nn::Result FinalizeInternal() { nn::Result result = nn::ResultSuccess(); nn::ac::CloseAll(); result = nn::ac::FinalizeInternal(); NN_UTIL_RETURN_IF_FAILED(result); return nn::ResultSuccess(); } } namespace ConsoleRestore{ void ShopOperationConnect(); void ShopOperationFinalize(); void ShopOperationConnect(ECAccountInfo* pAccountInfo) { nn::Result result = nn::ResultSuccess(); /* ------------------------------------------------------------------- Connect -------------------------------------------------------------------- */ NN_LOG("nim::Shop::Connect\n"); result = nn::nim::Shop::Connect(&pAccountInfo, s_EcBufffer, EC_BUFFER_SIZE); 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(); /* ------------------------------------------------------------------- Finalize -------------------------------------------------------------------- */ NN_LOG("nim::FinalizeForShop\n"); result = nn::nim::FinalizeForShop(); NIM_SHOP_RESULT_CHECK(result); NN_LOG("util::ac::Finalize\n"); result = FinalizeInternal(); NIM_SHOP_RESULT_CHECK(result); } namespace { // メイン関数 void ShopOperationSingleThreadFunc(ShopThreadParam param) { s_ShopResult = nn::ResultSuccess(); if(param.op == SHOP_OPERATION_CLOSE_WITHOUT_CONNECT) { ShopOperationFinalize(); return; } NN_LOG("util::ac::Initialize\n"); InitializeInternal(); nn::Result result = nn::ResultSuccess(); switch(param.op) { case SHOP_OPERATION_CONNECT_WITHOUT_CLOSE: { ShopOperationConnect(); return; } case SHOP_OPERATION_CONNECT_ONLY: { ShopOperationConnect(); } break; case SHOP_OPERATION_GET_IVS: { ShopOperationConnect(); // IVSを取得する result = nn::nim::Shop::ImportIvsFromInfrastructure(); NIM_SHOP_RESULT_CHECK(result); } break; case SHOP_OPERATION_UNREGISTER: { ECAccountInfo* pAccountInfo; ShopOperationConnect(pAccountInfo); if (pAccountInfo->accountStatus && pAccountInfo->accountStatus[0] == 'R') { /* --------------------------------------------------------------- Unregister ---------------------------------------------------------------- */ NN_LOG("nim::Shop::Unregister\n"); result = nn::nim::Shop::Unregister(); NIM_SHOP_RESULT_CHECK(result); } else { NN_LOG("Not registered.\n"); } } 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::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(); s_UnregisterThread.Finalize(); } bool IsShopOperationSingleFinished() { return s_UnregisterThread.IsValid() && !s_UnregisterThread.IsAlive(); } nn::Result GetShopOperationSingleResult() { return s_ShopResult; } }