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@851 385bec56-5757-e545-9c3a-d8741f4650f1
398 lines
12 KiB
C++
398 lines
12 KiB
C++
/*---------------------------------------------------------------------------*
|
|
Project: Horizon
|
|
File: PreorderTitleRestorer.cpp
|
|
|
|
Copyright (C)2015 Nintendo Co., Ltd. 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 "PreorderTitleRestorer.h"
|
|
#include "NinjaCommunicator.h"
|
|
#include "NinjaXmlReader.h"
|
|
#include "CommonLogger.h"
|
|
#include "HeapManager.h"
|
|
|
|
#include <nn/fs.h>
|
|
#include <nn/fs/fs_ApiExternalSeeds.h>
|
|
#include <nn/fs/fs_ApiTitleTags.h>
|
|
#include <nn/nim.h>
|
|
#include <nn/nim/CTR/nim_PrepurchaseTitleTag.h>
|
|
#include <nn/nim/CTR/nim_ShopApi.h>
|
|
|
|
|
|
namespace ConsoleRestore
|
|
{
|
|
|
|
namespace
|
|
{
|
|
|
|
using namespace nn::cfg;
|
|
CfgCountryCode s_TypicalCountryCode[] = {
|
|
CFG_COUNTRY_JAPAN,
|
|
CFG_COUNTRY_UNITED_STATES,
|
|
CFG_COUNTRY_CANADA,
|
|
CFG_COUNTRY_MEXICO,
|
|
CFG_COUNTRY_AUSTRALIA,
|
|
CFG_COUNTRY_FRANCE,
|
|
CFG_COUNTRY_GERMANY,
|
|
CFG_COUNTRY_ITALY,
|
|
CFG_COUNTRY_NETHERLANDS,
|
|
CFG_COUNTRY_SPAIN,
|
|
CFG_COUNTRY_UNITED_KINGDOM,
|
|
CFG_COUNTRY_TAIWAN,
|
|
CFG_COUNTRY_SOUTH_KOREA,
|
|
CFG_COUNTRY_HONG_KONG,
|
|
CFG_COUNTRY_MACAU,
|
|
CFG_COUNTRY_CHINA
|
|
};
|
|
const size_t s_TypicalCountryCodeSize = sizeof(s_TypicalCountryCode) / sizeof(s_TypicalCountryCode[0]);
|
|
|
|
}
|
|
|
|
PreorderTitleRestorer::PreorderTitleRestorer() : m_Progress(0)
|
|
{
|
|
|
|
}
|
|
|
|
PreorderTitleRestorer::~PreorderTitleRestorer()
|
|
{
|
|
}
|
|
|
|
nn::Result PreorderTitleRestorer::Execute(nn::ProgramId list[], size_t size, nn::cfg::CTR::CfgCountryCode country, nn::cfg::CTR::CfgRegionCode region)
|
|
{
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
nn::nim::InitializeForShop());
|
|
nn::fs::Initialize();
|
|
|
|
for(s32 i = 0; i < size; ++i)
|
|
{
|
|
NN_LOG("progress=%llu\n", m_Progress);
|
|
std::string xml;
|
|
nn::cfg::CfgCountryCode deliveredCountry;
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
GetExternalSeedInfo(&deliveredCountry, xml, list[i], country, region));
|
|
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
SetExternalSeedOrPlayableDate(list[i], deliveredCountry, xml));
|
|
m_Progress = (i + 1) * 100 / size;
|
|
}
|
|
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
nn::nim::FinalizeForShop());
|
|
|
|
return nn::ResultSuccess();
|
|
}
|
|
|
|
u64 PreorderTitleRestorer::GetProgress()
|
|
{
|
|
return m_Progress;
|
|
}
|
|
|
|
nn::Result PreorderTitleRestorer::GetExternalSeedInfo(
|
|
nn::cfg::CTR::CfgCountryCode* deliveredCountry,
|
|
std::string& xml,
|
|
nn::ProgramId programId,
|
|
nn::cfg::CTR::CfgCountryCode country,
|
|
nn::cfg::CTR::CfgRegionCode region)
|
|
{
|
|
std::string nsUid;
|
|
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
GetNsUid(nsUid, programId));
|
|
|
|
if(country == nn::cfg::CFG_COUNTRY_UNDEFINED)
|
|
{
|
|
// 代表的な国で外部鍵の取得を試みた後、全ての国に対して繰り返しEcInfoを取得する
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
GetEcInfoAndCountryRepeatedly(xml, deliveredCountry, nsUid, region));
|
|
}
|
|
else
|
|
{
|
|
*deliveredCountry = country;
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
GetEcInfo(xml, nsUid, country));
|
|
}
|
|
|
|
return nn::ResultSuccess();
|
|
}
|
|
|
|
nn::Result PreorderTitleRestorer::GetNsUid(std::string& nsUid, nn::ProgramId programId)
|
|
{
|
|
NinjaCommunicator ninja;
|
|
|
|
// ns_uidの取得
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
ninja.GetNsUid("https://ninja.ctr.shop.nintendo.net/ninja/ws/", programId));
|
|
|
|
size_t bufSize = 1024 * 1024; // 1MB
|
|
common::HeapManager heap(bufSize);
|
|
heap.GetAddr();
|
|
void* buf = heap.GetAddr();
|
|
if(!buf)
|
|
{
|
|
return nn::Result(nn::Result::LEVEL_FATAL, nn::Result::SUMMARY_OUT_OF_RESOURCE, nn::Result::MODULE_COMMON,
|
|
nn::Result::DESCRIPTION_OUT_OF_MEMORY);
|
|
}
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
ninja.GetBody(reinterpret_cast<u8*>(buf), bufSize));
|
|
|
|
NN_LOG("%s\n", buf);
|
|
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
ninja.Finalize());
|
|
|
|
NinjaXmlReader reader;
|
|
reader.GetNsUid(nsUid, reinterpret_cast<char*>(buf));
|
|
|
|
return nn::ResultSuccess();
|
|
}
|
|
|
|
nn::Result PreorderTitleRestorer::GetEcInfo(std::string& ecInfo, std::string nsUid, nn::cfg::CfgCountryCode country)
|
|
{
|
|
NinjaCommunicator ninja;
|
|
|
|
char iso3166a2[3];
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
nn::cfg::ConvertCountryCodeToIso3166a2(iso3166a2, country));
|
|
|
|
// ec_infoの取得
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
ninja.GetEcInfo("https://ninja.ctr.shop.nintendo.net/ninja/ws/", nsUid.c_str(), iso3166a2));
|
|
|
|
size_t bufSize = 1024 * 1024; // 1MB
|
|
common::HeapManager heap(bufSize);
|
|
heap.GetAddr();
|
|
void* buf = heap.GetAddr();
|
|
if(!buf)
|
|
{
|
|
return nn::Result(nn::Result::LEVEL_FATAL, nn::Result::SUMMARY_OUT_OF_RESOURCE, nn::Result::MODULE_COMMON,
|
|
nn::Result::DESCRIPTION_OUT_OF_MEMORY);
|
|
}
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
ninja.GetBody(reinterpret_cast<u8*>(buf), bufSize));
|
|
|
|
NN_LOG("%s\n", buf);
|
|
ecInfo = reinterpret_cast<char*>(buf);
|
|
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
ninja.Finalize());
|
|
|
|
return nn::ResultSuccess();
|
|
}
|
|
|
|
nn::Result PreorderTitleRestorer::GetEcInfoAndCountryRepeatedly(std::string& ecInfo, nn::cfg::CfgCountryCode* deliveredCountry, std::string nsUid, nn::cfg::CTR::CfgRegionCode region)
|
|
{
|
|
// 既に接続ダウンロード済みの国があればそれを試す
|
|
for(std::map<nn::cfg::CTR::CfgCountryCode, bool>::iterator it = m_SuccessfulCountryMap.begin(); it != m_SuccessfulCountryMap.end(); ++it)
|
|
{
|
|
if(IsValidCountry(it->first, region))
|
|
{
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
GetEcInfo(ecInfo, nsUid, it->first));
|
|
|
|
if (HasExternal_Seed(ecInfo))
|
|
{
|
|
*deliveredCountry = it->first;
|
|
return nn::ResultSuccess();
|
|
}
|
|
}
|
|
}
|
|
|
|
// 代表的な国からダウンロードを試す
|
|
for(s32 i = 0; i < s_TypicalCountryCodeSize; ++i)
|
|
{
|
|
if(IsValidCountry(s_TypicalCountryCode[i], region))
|
|
{
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
GetEcInfo(ecInfo, nsUid, s_TypicalCountryCode[i]));
|
|
|
|
if (HasExternal_Seed(ecInfo))
|
|
{
|
|
*deliveredCountry = s_TypicalCountryCode[i];
|
|
if(m_SuccessfulCountryMap.count(s_TypicalCountryCode[i]) == 0)
|
|
{
|
|
m_SuccessfulCountryMap.insert(std::make_pair(s_TypicalCountryCode[i], true));
|
|
}
|
|
return nn::ResultSuccess();
|
|
}
|
|
}
|
|
}
|
|
|
|
// 全ての国からダウンロードを試す
|
|
for(s32 i = CFG_COUNTRY_JAPAN; i < CFG_COUNTRY_UNKNOWN; ++i)
|
|
{
|
|
CfgCountryCode country = static_cast<CfgCountryCode>(i);
|
|
if(IsValidCountry(country, region))
|
|
{
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
GetEcInfo(ecInfo, nsUid, country));
|
|
|
|
if (HasExternal_Seed(ecInfo))
|
|
{
|
|
*deliveredCountry = country;
|
|
if(m_SuccessfulCountryMap.count(country) == 0)
|
|
{
|
|
m_SuccessfulCountryMap.insert(std::make_pair(country, true));
|
|
}
|
|
return nn::ResultSuccess();
|
|
}
|
|
}
|
|
}
|
|
|
|
return nn::ResultSuccess();
|
|
}
|
|
|
|
nn::Result PreorderTitleRestorer::SetExternalSeedOrPlayableDate(nn::ProgramId programId, nn::cfg::CTR::CfgCountryCode country, std::string& xml)
|
|
{
|
|
// content_lockが無ければ何もしない
|
|
if (IsLocked(xml))
|
|
{
|
|
// external_seedが有れば外部鍵をインポート
|
|
if (HasExternal_Seed(xml))
|
|
{
|
|
NN_LOG("Importing External Seed for %016llx\n", programId);
|
|
COMMON_LOGGER_RETURN_RESULT_IF_FAILED(
|
|
SetExternalKey(programId, country));
|
|
}
|
|
// external_seedが無ければタイトルタグを登録
|
|
else
|
|
{
|
|
NN_LOG("SetTitleTag for %x\n", programId);
|
|
SetTitleTag(programId, country, xml);
|
|
}
|
|
}
|
|
|
|
return nn::ResultSuccess();
|
|
}
|
|
|
|
bool PreorderTitleRestorer::IsLocked(std::string xml)
|
|
{
|
|
NinjaXmlReader reader;
|
|
return reader.HasContentLock(xml);
|
|
}
|
|
|
|
bool PreorderTitleRestorer::HasExternal_Seed(std::string xml)
|
|
{
|
|
NinjaXmlReader reader;
|
|
return reader.HasExternalSeed(xml);
|
|
}
|
|
|
|
nn::Result PreorderTitleRestorer::SetExternalKey(nn::ProgramId programId, nn::cfg::CTR::CfgCountryCode country)
|
|
{
|
|
nn::nim::ResultError resultError;
|
|
resultError = nn::nim::CTR::Shop::ImportExternalSeed(programId, country);
|
|
if(resultError.IsFailure())
|
|
{
|
|
COMMON_LOGGER("ErrorCode:%d\n", resultError.GetErrorCode());
|
|
return resultError.GetResult();
|
|
}
|
|
|
|
return nn::ResultSuccess();
|
|
}
|
|
|
|
void PreorderTitleRestorer::SetTitleTag(nn::ProgramId programId, nn::cfg::CTR::CfgCountryCode country, std::string xml)
|
|
{
|
|
NinjaXmlReader reader;
|
|
nn::fnd::DateTime playable = reader.GetPlayableDate(xml);
|
|
|
|
nn::fs::CTR::TitleTag titleTag;
|
|
nn::nim::CTR::MakePrepurchaseTitleTag(&titleTag, playable.GetYear(), playable.GetMonth(), playable.GetDay(), country);
|
|
nn::fs::SetTitleTag(programId, titleTag);
|
|
}
|
|
|
|
|
|
// Horizon\sources\tools\Config\UserInfoSetting.cppからのコピペ
|
|
bool PreorderTitleRestorer::IsValidCountry(nn::cfg::CTR::CfgCountryCode countryCode, nn::cfg::CTR::CfgRegionCode region)
|
|
{
|
|
using namespace nn::cfg::CTR;
|
|
|
|
if (countryCode == 0xFF)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// 対象リージョンで指定国が有効かどうかを判定
|
|
if (region == nn::cfg::CTR::CFG_REGION_JAPAN)
|
|
{
|
|
if (countryCode == nn::cfg::CTR::CFG_COUNTRY_JAPAN)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else if (region == nn::cfg::CTR::CFG_REGION_AMERICA)
|
|
{
|
|
if ((CFG_COUNTRY_MAPPING_AMERICA >> 16) <= countryCode && countryCode <= (CFG_COUNTRY_MAPPING_AMERICA & 0xFF))
|
|
{
|
|
return true;
|
|
}
|
|
switch (countryCode)
|
|
{
|
|
case CFG_COUNTRY_SINGAPORE:
|
|
case CFG_COUNTRY_MALAYSIA:
|
|
case CFG_COUNTRY_UAE:
|
|
case CFG_COUNTRY_SAUDI_ARABIA:
|
|
return true;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
else if (region == nn::cfg::CTR::CFG_REGION_EUROPE)
|
|
{
|
|
if ((CFG_COUNTRY_MAPPING_EUROPE >> 16) <= countryCode && countryCode <= (CFG_COUNTRY_MAPPING_EUROPE & 0xFF))
|
|
{
|
|
return true;
|
|
}
|
|
switch (countryCode)
|
|
{
|
|
case CFG_COUNTRY_INDIA:
|
|
case CFG_COUNTRY_SAN_MARINO:
|
|
case CFG_COUNTRY_VATICAN_CITY:
|
|
return true;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
else if (region == nn::cfg::CTR::CFG_REGION_CHINA)
|
|
{
|
|
switch (countryCode)
|
|
{
|
|
case CFG_COUNTRY_CHINA:
|
|
return true;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
else if (region == nn::cfg::CTR::CFG_REGION_KOREA)
|
|
{
|
|
switch (countryCode)
|
|
{
|
|
case CFG_COUNTRY_SOUTH_KOREA:
|
|
return true;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
else if (region == nn::cfg::CTR::CFG_REGION_TAIWAN)
|
|
{
|
|
switch (countryCode)
|
|
{
|
|
case CFG_COUNTRY_TAIWAN:
|
|
case CFG_COUNTRY_HONG_KONG:
|
|
return true;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} /* namespace ConsoleRestore */
|