/*---------------------------------------------------------------------------* Project: Horizon File: PreinstallImporter.cpp Copyright 2009-2011 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 "CommonLogger.h" #include "HeapManager.h" #include "PreinstallImporter.h" #include "XmlCreator.h" #include "SimpleXmlPreprocessor.h" #include #include #include namespace ConsoleRestore { PreinstallImporter::PreinstallImporter() { // TODO 自動生成されたコンストラクター・スタブ } PreinstallImporter::~PreinstallImporter() { // TODO Auto-generated destructor stub } nn::Result PreinstallImporter::SetupSd(bool* isAlreadyAvailable) { // SDカードがインポート可能状態かどうかチェック NN_UTIL_RETURN_IF_FAILED( nn::am::QueryAvailableExternalTitleDatabase(isAlreadyAvailable) ); // タイトルデータベースを作成する if (!*isAlreadyAvailable) { NN_UTIL_RETURN_IF_FAILED( nn::am::InitializeExternalTitleDatabase() ); } return nn::ResultSuccess(); } nn::Result PreinstallImporter::ListTitles(nn::ProgramId* list, size_t* num, bit64 deviceId, u8* serialNo) { // 送信用のXMLデータを構築する XmlCreator sendXml; sendXml.Exec(deviceId, serialNo); // 構築したXMLデータを使ってBGSと通信する BgsCommunicator comm; if(!comm.Execute(sendXml.GetData().c_str(), sendXml.GetData().size())) { return comm.GetLastResult(); } // 通信結果をパースしてタイトルリストを作成する NN_UTIL_RETURN_IF_FAILED( GetHtmlBodyAndParseXmlData(comm, list, num) ); return nn::ResultSuccess(); } nn::Result PreinstallImporter::GetHtmlBodyAndParseXmlData(BgsCommunicator& comm, nn::ProgramId* list, size_t* num) { // 通信結果を取得する size_t bodySize; if(!comm.GetBodySize(&bodySize)) { return comm.GetLastResult(); } if(common::GetAllocatableSize() < bodySize + 1) { // 巨大な通信結果のためFATAL。1回で受信しきれるはず return nn::Result(nn::Result::LEVEL_FATAL, nn::Result::SUMMARY_OUT_OF_RESOURCE, nn::Result::MODULE_COMMON, nn::Result::DESCRIPTION_OUT_OF_MEMORY); } common::HeapManager heap(bodySize + 1); void* buf = heap.GetAddr(); if(!comm.GetBody(reinterpret_cast(buf), bodySize + 1)) { return comm.GetLastResult(); } // NULL終端する reinterpret_cast(buf)[bodySize] = '\0'; if(!comm.Finalize()) { return comm.GetLastResult(); } // XMLを解析してタイトルリストを設定する NN_UTIL_RETURN_IF_FAILED(ParseXmlData(buf, list, num)); return nn::ResultSuccess(); } nn::Result PreinstallImporter::ParseXmlData(void* buf, nn::ProgramId* list, size_t* num) { // 通信結果をパースする // SimpleXmlParserに食わせるために変換する SimpleXmlPreprocessor pp; // システムヒープ(8MB)を超えたら正常に動かない std::string xmlResult(reinterpret_cast(buf)); pp.Canonicalize(xmlResult); // XMLをパースしてタイトルリストを取得する nn::fnd::IAllocator* pAllocator = nn::init::GetAllocator(); nn::xml::simple::SimpleXmlParser simpleXmlParser(pAllocator); const nn::xml::simple::SimpleXmlParser::Node* pTaskIdNode; NN_UTIL_RETURN_IF_FAILED( SetNodetoTitleIds(simpleXmlParser, xmlResult, &pTaskIdNode) ); *num = 0; // プリインストールタイトルが無ければ終了 if(!pTaskIdNode) { return nn::MakePermanentResult(nn::Result::SUMMARY_NOT_FOUND, nn::Result::MODULE_APPLICATION, nn::Result::DESCRIPTION_NOT_FOUND); } // タイトルIDリストをコピーする common::HeapManager xmlHeap(pTaskIdNode->contentSize); void* titleIdBuffer = xmlHeap.GetAddr(); std::memcpy(titleIdBuffer, pTaskIdNode->content, pTaskIdNode->contentSize); SplitTextAndSetupList(list, num, reinterpret_cast(titleIdBuffer)); return nn::ResultSuccess(); } nn::Result PreinstallImporter::SetNodetoTitleIds(nn::xml::simple::SimpleXmlParser& parser, std::string& xmlData, const nn::xml::simple::SimpleXmlParser::Node** pNode) { /* * SimpleXmlPreprocessorを通したXMLデータはこんな形になっている 1326939225587 0 1 17179924184 EJA20305940 JPN 1324339327000 000400000FEEB400,000400000FEEB000 */ parser.parse(reinterpret_cast(const_cast(xmlData.c_str())), xmlData.size()); if(parser.isError()) { COMMON_LOGGER("invalid xml Data\n"); return nn::Result(nn::Result::LEVEL_STATUS, nn::Result::SUMMARY_INVALID_STATE, nn::Result::MODULE_COMMON, nn::Result::DESCRIPTION_INVALID_RESULT_VALUE); } // 欲しい情報がある場所まで階層を掘り下げる const nn::xml::simple::SimpleXmlParser::Node* pRootNode = parser.getRootNode(); const nn::xml::simple::SimpleXmlParser::Node* pTargetNode = pRootNode->firstChild; const nn::xml::simple::SimpleXmlParser::Node* pPriorityNode = nn::xml::simple::SimpleXmlParser::FindNextNode( pTargetNode, "Body"); pPriorityNode = nn::xml::simple::SimpleXmlParser::FindNextNode(pPriorityNode->firstChild, "GetPreInstalledInfoResponse"); pPriorityNode = nn::xml::simple::SimpleXmlParser::FindNextNode(pPriorityNode->firstChild, "GetPreInstalledInfoResponse"); pPriorityNode = nn::xml::simple::SimpleXmlParser::FindNextNode(pPriorityNode->firstChild, "PreinstalledInfo"); *pNode = nn::xml::simple::SimpleXmlParser::FindNextNode(pPriorityNode->firstChild, "TitleIds"); return nn::ResultSuccess(); } void PreinstallImporter::SplitTextAndSetupList(nn::ProgramId* list, size_t* num, char* text) { const char* token = ","; char* cutout; cutout = std::strtok(text, token); if(!cutout) { return; } list[*num] = std::strtoll(cutout, NULL, 16); (*num)++; while( cutout ) { cutout = std::strtok(NULL, token); if(cutout) { list[*num] = std::strtoll(cutout, NULL, 16); (*num)++; } } } } /* namespace ConsoleRestore */