ctr_Repair/trunk/ConsoleDataMigration/sources/ConsoleRestore/PreorderTitleRestorer.cpp
N2614 344cd574a6 暗号式予約販売タイトルの修理ロジックを追加
タイトルタグの設定のテストは現状失敗するので無効化

git-svn-id: file:///Volumes/Transfer/gigaleak_20231201/2020-05-23%20-%20ctr.7z%20+%20svn_v1.068.zip/ctr/svn/ctr_Repair@842 385bec56-5757-e545-9c3a-d8741f4650f1
2015-03-19 02:49:26 +00:00

382 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/fs_ApiExternalSeeds.h>
#include <nn/fs/fs_ApiTitleTags.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()
{
}
PreorderTitleRestorer::~PreorderTitleRestorer()
{
}
nn::Result PreorderTitleRestorer::Execute(nn::ProgramId list[], size_t size, nn::cfg::CTR::CfgCountryCode country, nn::cfg::CTR::CfgRegionCode region)
{
for(s32 i = 0; i < size; ++i)
{
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));
}
return nn::ResultSuccess();
}
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 */