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@843 385bec56-5757-e545-9c3a-d8741f4650f1
515 lines
16 KiB
C++
515 lines
16 KiB
C++
/*---------------------------------------------------------------------------*
|
|
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 <nn/os.h>
|
|
#include <nn/Handle.h>
|
|
#include <nn/dbg.h>
|
|
#include <nn/nim.h>
|
|
#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"
|
|
#include "Util.h"
|
|
|
|
using namespace ES_NAMESPACE;
|
|
using namespace EC_NAMESPACE;
|
|
|
|
#define NIM_SHOP_RESULT_CHECK(result) \
|
|
do { \
|
|
::nn::Result shopResult = (result); \
|
|
if (shopResult.IsFailure()) \
|
|
{ \
|
|
ECCustomerSupportCode csc; \
|
|
nn::nim::Shop::GetCustomerSupportCode(&csc); \
|
|
if(csc != 0)\
|
|
{\
|
|
COMMON_LOGGER("CSCode: %d\n", csc); \
|
|
}\
|
|
COMMON_LOGGER_RESULT_IF_FAILED(shopResult); \
|
|
NN_DBG_PRINT_RESULT(shopResult); \
|
|
COMMON_LOGGER_DETAIL("Result = %08X\n", shopResult.GetPrintableBits()); \
|
|
s_ShopResult = shopResult; \
|
|
return; \
|
|
} \
|
|
} while(0)
|
|
namespace
|
|
{
|
|
|
|
nn::Result s_ShopResult = nn::ResultSuccess();
|
|
|
|
const size_t SHOP_OPERATION_THREAD_STACK_SIZE = 0x1000;
|
|
nn::os::Thread s_ShopOperationThread;
|
|
nn::os::StackBuffer<SHOP_OPERATION_THREAD_STACK_SIZE> s_ShopOperationThreadStack;
|
|
|
|
const size_t EC_BUFFER_SIZE = 128 * 1024;
|
|
u8 s_EcBufffer[EC_BUFFER_SIZE];
|
|
bool s_IsNimShopInitialized = false;
|
|
nn::cfg::CTR::CfgCountryCode s_CfgCountryCode = nn::cfg::CTR::CFG_COUNTRY_UNDEFINED;
|
|
|
|
struct ShopThreadParam
|
|
{
|
|
ConsoleRestore::ShopOperation op;
|
|
NN_PADDING3;
|
|
NN_PADDING4;
|
|
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 の文字列を取得
|
|
#ifdef COMMON_LOGGER_DETAIL_ENABLE
|
|
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";
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
// 空文字列と NULL ポインタをそれぞれ <empty> と NULL として返す
|
|
#ifdef COMMON_LOGGER_DETAIL_ENABLE
|
|
const char* Cstr(const char* p)
|
|
{
|
|
return p ? (p[0] ? p : "<empty>") : "NULL";
|
|
}
|
|
#endif
|
|
|
|
// ECTicketVersions を出力
|
|
void PrintECTicketVersions(const ECTicketVersions& ticketVersions)
|
|
{
|
|
if (ticketVersions.nTicketVersions == 0)
|
|
{
|
|
COMMON_LOGGER_DETAIL("No TicketVersions\n");
|
|
return;
|
|
}
|
|
|
|
COMMON_LOGGER_DETAIL("----- ECTicketVersions -----\n");
|
|
for (u32 i = 0; i < ticketVersions.nTicketVersions; i++)
|
|
{
|
|
#ifdef COMMON_LOGGER_DETAIL_ENABLE
|
|
ECTicketVersion version = ticketVersions.ticketVersions[i];
|
|
#endif
|
|
COMMON_LOGGER_DETAIL("%03d: 0x%016llx v%d\n", i, version.ticketId, version.version);
|
|
}
|
|
COMMON_LOGGER_DETAIL("---------------------------\n");
|
|
}
|
|
|
|
// ECAccountInfo の情報を出力
|
|
void PrintECAccountInfo(const ECAccountInfo& accountInfo)
|
|
{
|
|
COMMON_LOGGER_DETAIL("========== ECAccountInfo ==========\n");
|
|
|
|
COMMON_LOGGER_DETAIL("accountId\n %s\n", Cstr(accountInfo.accountId));
|
|
|
|
COMMON_LOGGER_DETAIL("accountStatus\n %s\n", Cstr(accountInfo.accountStatus));
|
|
|
|
if (accountInfo.accountBalance == NULL)
|
|
{
|
|
COMMON_LOGGER_DETAIL("accountBalance\n NULL\n");
|
|
}
|
|
else
|
|
{
|
|
COMMON_LOGGER_DETAIL("accountBalance->amount\n %s\n",
|
|
Cstr(accountInfo.accountBalance->amount));
|
|
COMMON_LOGGER_DETAIL("accountBalance->currency\n %s\n",
|
|
Cstr(accountInfo.accountBalance->currency));
|
|
}
|
|
|
|
if (accountInfo.agreedEULAVersion == NULL)
|
|
{
|
|
COMMON_LOGGER_DETAIL("agreedEULAVersion\n NULL\n");
|
|
}
|
|
else
|
|
{
|
|
COMMON_LOGGER_DETAIL("agreedEULAVersion\n %lld\n",
|
|
*accountInfo.agreedEULAVersion);
|
|
}
|
|
|
|
if (accountInfo.latestEULAVersion == NULL)
|
|
{
|
|
COMMON_LOGGER_DETAIL("latestEULAVersion\n NULL\n");
|
|
}
|
|
else
|
|
{
|
|
COMMON_LOGGER_DETAIL("latestEULAVersion\n %lld\n",
|
|
*accountInfo.latestEULAVersion);
|
|
}
|
|
|
|
COMMON_LOGGER_DETAIL("country\n %s\n", Cstr(accountInfo.country));
|
|
|
|
COMMON_LOGGER_DETAIL("extAccountId\n %s\n", Cstr(accountInfo.extAccountId));
|
|
|
|
COMMON_LOGGER_DETAIL("deviceToken\n %s\n", Cstr(accountInfo.deviceToken));
|
|
|
|
COMMON_LOGGER_DETAIL("weakToken\n %s\n", Cstr(accountInfo.weakToken));
|
|
|
|
COMMON_LOGGER_DETAIL("isStandbyMode\n %d\n", accountInfo.isStandbyMode);
|
|
|
|
if (accountInfo.owned == NULL)
|
|
{
|
|
COMMON_LOGGER_DETAIL("owned\n NULL\n");
|
|
}
|
|
else
|
|
{
|
|
PrintECTicketVersions(*(accountInfo.owned));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
namespace ConsoleRestore{
|
|
|
|
nn::Result ShopOperationConnect();
|
|
nn::Result ShopOperationFinalize();
|
|
|
|
|
|
nn::Result CheckStandbyMode(s32 isStandbyMode)
|
|
{
|
|
if(isStandbyMode)
|
|
{
|
|
COMMON_LOGGER("Shop is Standby Mode\n");
|
|
return nn::MakePermanentResult(nn::Result::SUMMARY_INVALID_STATE, nn::Result::MODULE_COMMON,
|
|
nn::Result::DESCRIPTION_NOT_AUTHORIZED);
|
|
}
|
|
else
|
|
{
|
|
return nn::ResultSuccess();
|
|
}
|
|
}
|
|
|
|
nn::Result ShopOperationConnect(ECAccountInfo** pAccountInfo)
|
|
{
|
|
nn::Result result = nn::ResultSuccess();
|
|
|
|
/* -------------------------------------------------------------------
|
|
Connect
|
|
-------------------------------------------------------------------- */
|
|
COMMON_LOGGER_DETAIL("nim::Shop::Connect\n");
|
|
result = nn::nim::Shop::Connect(pAccountInfo, s_EcBufffer, EC_BUFFER_SIZE);
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(result);
|
|
result = CheckStandbyMode((*pAccountInfo)->isStandbyMode);
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(result);
|
|
|
|
PrintECAccountInfo(**pAccountInfo);
|
|
COMMON_LOGGER_DETAIL("\n");
|
|
// 国コードを保持
|
|
if((*pAccountInfo)->country)
|
|
{
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
nn::cfg::ConvertIso3166a2ToCountryCode(&s_CfgCountryCode, (*pAccountInfo)->country));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
nn::Result ShopOperationInitialize()
|
|
{
|
|
nn::Result result = nn::ResultSuccess();
|
|
|
|
/* -------------------------------------------------------------------
|
|
Initialize
|
|
-------------------------------------------------------------------- */
|
|
if (!s_IsNimShopInitialized)
|
|
{
|
|
COMMON_LOGGER_DETAIL("nim::InitializeForShop\n");
|
|
result = nn::nim::InitializeForShop();
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(result);
|
|
s_IsNimShopInitialized = true;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------
|
|
SetParameter
|
|
-------------------------------------------------------------------- */
|
|
COMMON_LOGGER_DETAIL("nim::Shop::SetApplication Id\n");
|
|
|
|
nn::nim::Shop::SetApplicationId();
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(result);
|
|
|
|
COMMON_LOGGER_DETAIL("nim::Shop::SetTIN\n");
|
|
result = nn::nim::Shop::SetTin(CONSOLE_RESTORE_TIN);
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
nn::Result ShopOperationConnect()
|
|
{
|
|
nn::Result result;
|
|
result = ShopOperationInitialize();
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(result);
|
|
|
|
/* -------------------------------------------------------------------
|
|
Connect
|
|
-------------------------------------------------------------------- */
|
|
ECAccountInfo* pAccountInfo;
|
|
result = ShopOperationConnect(&pAccountInfo);
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
nn::Result ShopOperationFinalize()
|
|
{
|
|
nn::Result result = nn::ResultSuccess();
|
|
|
|
/* -------------------------------------------------------------------
|
|
Finalize
|
|
-------------------------------------------------------------------- */
|
|
COMMON_LOGGER_DETAIL("nim::FinalizeForShop\n");
|
|
result = nn::nim::FinalizeForShop();
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(result);
|
|
s_IsNimShopInitialized = false;
|
|
|
|
return result;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
|
|
void ShopOperationUnregisterCore(bool force)
|
|
{
|
|
nn::Result result;
|
|
result = ShopOperationInitialize();
|
|
NIM_SHOP_RESULT_CHECK(result);
|
|
ECAccountInfo* pAccountInfo;
|
|
result = ShopOperationConnect(&pAccountInfo);
|
|
NIM_SHOP_RESULT_CHECK(result);
|
|
if (force)
|
|
{
|
|
if (pAccountInfo->accountStatus && (pAccountInfo->accountStatus[0] == 'R' || pAccountInfo->accountStatus[0]
|
|
== 'T'))
|
|
{
|
|
COMMON_LOGGER_DETAIL("nim::Shop::Unregister\n");
|
|
result = nn::nim::Shop::Unregister();
|
|
NIM_SHOP_RESULT_CHECK(result);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pAccountInfo->accountStatus && (pAccountInfo->accountStatus[0] == 'R'))
|
|
{
|
|
COMMON_LOGGER_DETAIL("nim::Shop::Unregister\n");
|
|
result = nn::nim::Shop::Unregister();
|
|
NIM_SHOP_RESULT_CHECK(result);
|
|
}
|
|
else
|
|
{
|
|
COMMON_LOGGER_DETAIL("Not registered.\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
// メイン関数
|
|
void ShopOperationSingleThreadFunc(ShopThreadParam param)
|
|
{
|
|
nn::Result result;
|
|
|
|
s_ShopResult = nn::ResultSuccess();
|
|
|
|
if(!nn::ac::IsConnected())
|
|
{
|
|
COMMON_LOGGER_DETAIL("util::ac::Initialize\n");
|
|
result = common::InitializeNetwork();
|
|
NIM_SHOP_RESULT_CHECK(result);
|
|
}
|
|
|
|
switch(param.op)
|
|
{
|
|
case SHOP_OPERATION_CONNECT:
|
|
{
|
|
result = ShopOperationConnect();
|
|
NIM_SHOP_RESULT_CHECK(result);
|
|
}
|
|
break;
|
|
|
|
case SHOP_OPERATION_GET_IVS:
|
|
{
|
|
result = ShopOperationInitialize();
|
|
NIM_SHOP_RESULT_CHECK(result);
|
|
// IVSを取得する
|
|
result = nn::nim::Shop::ImportIvsFromInfrastructure();
|
|
NIM_SHOP_RESULT_CHECK(result);
|
|
}
|
|
break;
|
|
|
|
case SHOP_OPERATION_UNREGISTER:
|
|
{
|
|
ShopOperationUnregisterCore(false);
|
|
}
|
|
break;
|
|
|
|
case SHOP_OPERATION_FORCE_UNREGISTER:
|
|
{
|
|
ShopOperationUnregisterCore(true);
|
|
}
|
|
break;
|
|
|
|
case SHOP_OPERATION_CONNECT_WITHOUT_CLOSE:
|
|
{
|
|
result = ShopOperationConnect();
|
|
NIM_SHOP_RESULT_CHECK(result);
|
|
return;
|
|
}
|
|
|
|
|
|
{
|
|
case SHOP_OPERATION_DOWNLOAD_TITLE:
|
|
COMMON_LOGGER_DETAIL("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
|
|
-------------------------------------------------------------------- */
|
|
|
|
COMMON_LOGGER_DETAIL("nim::Shop::GetProgress()\n");
|
|
nn::nim::TitleProgress before;
|
|
nn::nim::TitleProgress latest;
|
|
while (true)
|
|
{
|
|
result = nn::nim::Shop::GetProgress(&latest);
|
|
|
|
NIM_SHOP_RESULT_CHECK(result);
|
|
NIM_SHOP_RESULT_CHECK(latest.lastResult);
|
|
|
|
// Print progress
|
|
if (latest != before)
|
|
{
|
|
COMMON_LOGGER_DETAIL("%8lld / %8lld (%d:%s)\n",
|
|
latest.downloadedSize,
|
|
latest.totalSize,
|
|
latest.state.Get(),
|
|
GetTitleStateString(latest.state));
|
|
|
|
if (latest.state == nn::nim::TITLE_STATE_FINISHED)
|
|
{
|
|
COMMON_LOGGER_DETAIL("Finished Download\n");
|
|
break;
|
|
}
|
|
|
|
before = latest;
|
|
}
|
|
|
|
// あまりにも頻繁に GetProgress を呼ぶと、ダウンロード処理の速度に
|
|
// 影響が出てしまいます。少なくとも数フレーム以上の間隔をあけて
|
|
// GetProgress することを推奨します。
|
|
nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromMilliSeconds(100));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
result = ShopOperationFinalize();
|
|
NIM_SHOP_RESULT_CHECK(result);
|
|
}
|
|
|
|
}
|
|
|
|
void StartShopOperationSingle(ShopOperation op, nn::nim::TitleConfig config)
|
|
{
|
|
COMMON_LOGGER_DETAIL("Start ShopOperationSingle, %s\n", SHOP_OPERATION_STR[op]);
|
|
|
|
ShopThreadParam param;
|
|
param.op = op;
|
|
param.config = config;
|
|
s_ShopOperationThread.Start(ShopOperationSingleThreadFunc, param, s_ShopOperationThreadStack);
|
|
}
|
|
|
|
void StartShopOperationSingle(ShopOperation op)
|
|
{
|
|
ShopThreadParam param;
|
|
param.op = op;
|
|
|
|
COMMON_LOGGER_DETAIL("Start ShopOperationSingle, %s\n", SHOP_OPERATION_STR[op]);
|
|
s_ShopOperationThread.Start(ShopOperationSingleThreadFunc, param, s_ShopOperationThreadStack);
|
|
}
|
|
|
|
void FinalizeShopOperationSingle()
|
|
{
|
|
COMMON_LOGGER_DETAIL("Finalize ShopOperationSingle\n");
|
|
s_ShopOperationThread.Join();
|
|
s_ShopOperationThread.Finalize();
|
|
}
|
|
|
|
bool IsShopOperationSingleFinished()
|
|
{
|
|
return s_ShopOperationThread.IsValid() && !s_ShopOperationThread.IsAlive();
|
|
}
|
|
|
|
nn::Result GetShopOperationSingleResult()
|
|
{
|
|
return s_ShopResult;
|
|
}
|
|
|
|
nn::cfg::CTR::CfgCountryCode GetCountryCodeFromEci()
|
|
{
|
|
return s_CfgCountryCode;
|
|
}
|
|
}
|