/*---------------------------------------------------------------------------* Project: Horizon File: sample_nsalist.cpp Copyright (C)2009-2012 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 #include #include #include #include #include #include #include #include #define RESULT_SUCCESS_ASSERT NN_UTIL_PANIC_IF_FAILED #define NN_BOSS_RESULT_HANDLING(result, target) \ do { \ if(result.IsFailure()) \ { \ NN_LOG("result is err(%08x).(%s)\n", result.GetPrintableBits(), target); \ } \ } while (0) namespace { nn::Result InitializeNetwork(void) { nn::Result result; nn::ac::Config config; nn::fs::Initialize(); result = nn::ac::Initialize(); NN_UTIL_PANIC_IF_FAILED(result); // Create parameters for connection request result = nn::ac::CreateDefaultConfig( &config ); if (result.IsFailure()) { NN_UTIL_PANIC_IF_FAILED(nn::ac::Finalize()); return result; } // Issue connection request result = nn::ac::Connect( config ); if (result.IsFailure()) { NN_UTIL_PANIC_IF_FAILED(nn::ac::Finalize()); return result; } return nn::ResultSuccess(); } nn::Result FinalizeNetwork(void) { nn::Result result; // Create parameters for connection request result = nn::ac::Close(); NN_UTIL_RETURN_IF_FAILED(result); result = nn::ac::Finalize(); NN_UTIL_PANIC_IF_FAILED(result); return nn::ResultSuccess(); } void DumpNsdBody(u8* pBodyBuf, size_t payloadLength) { #ifndef NN_SWITCH_DISABLE_DEBUG_PRINT NN_LOG("---NSD Body Dump(Payload-Length=%d)---\n", payloadLength); int hexCheckCount = 0; for(int i = 0; i < payloadLength; ++i) { NN_LOG("%02x ", pBodyBuf[i]); ++hexCheckCount; if(hexCheckCount == 16) { NN_LOG("\n"); hexCheckCount = 0; } } #else NN_UNUSED_VAR(pBodyBuf); NN_UNUSED_VAR(payloadLength); #endif } } // Expanded save data ID // Expanded save data is required to use NADL tasks. The expanded save data ID specifies which expanded save data to use and is unique across all applications. // The expanded save data region is also used to store downloaded NSA lists. const bit32 APP_EXT_STORAGE_ID = 0x00000011; // Task ID // Both a BOSS code and a task ID must be specified when you get an NSA list. const char8 DOWNLOAD_TASK_ID[] = "flist"; /* Please see man pages for details */ const char BOSS_CODE[] = "9x4m4dJwyBBlUc3g"; /* BOSS code */ /* ------------------------------------------------------------------------ This sample code gets a task's NSA list from the server, and then uses an NADL task to download the files in that list. For more details, see sample_nadl_simple, which uses the same process of downloading files through an NADL task. ------------------------------------------------------------------------ */ void sampleNSAListExecute() { // Create an instance of nn::boss::NsaList nn::boss::NsaList nsaList("test:/filelist.txt"); // Download the NSA list { // Specify a BOSS code and a task ID to download the NSA list. nn::Result result = nsaList.Download(BOSS_CODE, DOWNLOAD_TASK_ID); if (result.IsFailure()) { NN_LOG_ERROR("nn::boss::NsaList::Download failed(0x%08x)\n", result.GetPrintableBits()); NN_UTIL_PANIC_IF_FAILED(result); } } // Check for errors related to downloading an NSA list { if (nsaList.GetResult() != nn::boss::TASK_SUCCESS) { NN_LOG_ERROR("nn::boss::NsaList::Download failed\n"); NN_LOG_ERROR("nn::boss::NsaList::GetResult(%d)\n", nsaList.GetResult()); } } // Get the NSA list's Digest value /* { const u32 DIGEST_BUF_SIZE = 40+1; u8 digestBuf[DIGEST_BUF_SIZE] = {0}; nn::Result result = nsaList.GetDigest(digestBuf, DIGEST_BUF_SIZE); if(result.IsFailure()) { // Error NN_LOG_ERROR("nn::boss::NsaList::GetDigest failed(0x%08x)\n", result.GetPrintableBits()); NN_UTIL_PANIC_IF_FAILED(result); } NN_LOG("NSA List Digest : %s\n", (char *)digestBuf); } */ // Determine whether the NSA list is valid const u32 WORK_BUF_SIZE = 256; u8 workBuf[WORK_BUF_SIZE]; { memset(workBuf, 0, WORK_BUF_SIZE); if (!nsaList.CheckValidity((void *)workBuf, WORK_BUF_SIZE)) { // Error NN_LOG_ERROR("nn::boss::NsaList::CheckValidity failed\n"); } } // Parse the downloaded NSA list u32 numNsaList = 0; const u32 NSA_LIST_LENGTH = 10; nn::boss::NsaList::NsaInformation nsaListInfo[NSA_LIST_LENGTH]; { memset(workBuf, 0, WORK_BUF_SIZE); s32 parseResult = nsaList.Parse(&numNsaList, nsaListInfo, NSA_LIST_LENGTH, (void *)workBuf, WORK_BUF_SIZE); if (parseResult < 0) { // Error NN_LOG_ERROR("nn::boss::NsaList::Parse failed(%d)\n", parseResult); } } // Display the content of the downloaded NSA list for (int i = 0; i < numNsaList; i++) { NN_LOG("----- NSA List %d -----\n", i+1); NN_LOG(" FileName : %s\n", nsaListInfo[i].fileName); NN_LOG(" FileSize : %d\n", nsaListInfo[i].fileSize); NN_LOG(" UpdateEpochTime : %d\n", nsaListInfo[i].updateEpochTime); NN_LOG(" Attribute1 : %s\n", nsaListInfo[i].attribute1); NN_LOG(" Attribute2 : %s\n", nsaListInfo[i].attribute2); NN_LOG(" Attribute2 : %s\n", nsaListInfo[i].attribute3); NN_LOG(" Caption : %s\n", nsaListInfo[i].caption); NN_LOG("----------------------\n"); } //////////////////////////////////////////////////////////////////////////// // // Download the (NSA) file in the obtained NSA list // This uses the same basic processing as sample_nadl_simple. // //////////////////////////////////////////////////////////////////////////// //Register BOSS storage. const size_t NADL_STORAGE_SIZE = 3*1024*1024; //The amount of the expanded save data region to use as BOSS storage. { nn::Result result = nn::boss::GetStorageInfo(); if(result == nn::boss::ResultStorageNotFound()) { NN_LOG("[BOSS Sample]RegisterStorage BOSS Storage.\n"); result = nn::boss::RegisterStorage(APP_EXT_STORAGE_ID, NADL_STORAGE_SIZE); } NN_BOSS_RESULT_HANDLING(result, "boss::RegisterStorage"); } //Opt-out settings (The following code sets and accesses the local opt-out flag.) /* { //Set the opt-out flag bool optoutValue = true; nn::Result result = nn::boss::SetOptoutFlag(optoutValue); NN_BOSS_RESULT_HANDLING(result, "nn::boss::SetOptoutFlag"); // Access the opt-out flag result = nn::boss::GetOptoutFlag(&optoutValue); NN_LOG("[BOSS Sample]Optout value is %d\n", optoutValue); NN_BOSS_RESULT_HANDLING(result, "nn::boss::GetOptoutFlag"); } */ // Register and run the NADL task // This downloads the files in the NSA list using an immediate execution task. { const char8 TASK_TARGET_URL[] = "https://npdl.cdn.nintendowifi.net/p01/nsa/9x4m4dJwyBBlUc3g/flist/file1"; /* Please see man pages for details */ //const char8 TASK_TARGET_URL[] = "https://npdl.cdn.nintendowifi.net/p01/nsa/9x4m4dJwyBBlUc3g/flist/file2"; /**< The task's target URL. This downloads file2 from the files in the NSA list. */ //const char8 TASK_TARGET_URL[] = "https://npdl.cdn.nintendowifi.net/p01/nsa/9x4m4dJwyBBlUc3g/flist/file3"; /**< The task's target URL. This downloads file3 from the files in the NSA list. */ // Register the NADL task // You don't need to specify an execution interval or execution count for immediate execution tasks. nn::boss::TaskPolicy dPolicy; nn::boss::NsaDownloadAction dAction; nn::Result result = dAction.Initialize(TASK_TARGET_URL); NN_BOSS_RESULT_HANDLING(result, "NsaDownloadAction::Initialize"); //result = dAction.SetApInfo( nn::boss::APINFOTYPE_APGROUP|nn::boss::APINFOTYPE_APAREA|nn::boss::APINFOTYPE_AP ); // Configure AP information to be given to HTTP queries, as necessary. //result = dAction.AddHeaderField(pLabel, pValue); // Set a unique HTTP request header, as necessary. // Task for immediate execution // Because the task for immediate execution uses the dedicated task ID "FGONLYT," there is no need to set the task ID with Initialize. // nn::boss::FgOnlyTask dFgOnlyTask; // Register a task for immediate execution. NN_LOG("[BOSS Sample]Regist FG Only Task.\n"); result = nn::boss::RegisterImmediateTask(&dFgOnlyTask, &dAction, &dPolicy); NN_BOSS_RESULT_HANDLING(result, "RegisterImmediateTask"); //Wait for NADL download data //// Get a data download notification event from the BOSS daemon nn::os::Event arriveEvent(false); result = nn::boss::RegisterNewArrivalEvent(&arriveEvent); NN_BOSS_RESULT_HANDLING(result, "Task::RegisterNewArrivalEvent"); // Immediate task execution // FgOnlyTask can only use immediate execution in the foreground. NN_LOG("[BOSS Sample]Start FG Only Task.\n"); result = dFgOnlyTask.StartImmediate(); NN_BOSS_RESULT_HANDLING(result, "FgOnlyTask::StartImmediate"); //// Wait for new data to be downloaded. NN_LOG("[BOSS Sample]Wait NSA data arrive....\n"); arriveEvent.Wait(); NN_LOG("[BOSS Sample]Recognize NSA data arrive\n"); //// You can also wait for a task to complete rather than for data to be downloaded. (By "waiting for a task to complete," we mean "waiting for the next task to finish executing." This does not refer to running for the full execution count.) //// When waiting for data download, even if a task is executed, if there is no connection with the server, if the data already exists, //// or if the downloaded data is invalid (decoding process or signature verification fails), the data is discarded and the process cannot return from Wait. //// If you run a task and wait for it to complete, WaitFinish will exit when the task completes even if it had an error. //// Use each method where it suits the purpose. //result = dTask.WaitFinish(); // Wait for the task to complete. (Without a timeout. This will wait indefinitely until processing is complete.) //result = dTask.WaitFinish(nn::fnd::TimeSpan::FromSeconds(60)); // Wait for the task to complete. (With a timeout. ResultWaitfinishTimeout returns when processing does not complete within the specified time.) //// Another way to wait for a task to complete is to poll on the task state using the Task::GetState function. //// After you have detected that the task has completed, you can use the following functions to get information on the completed task. //// Task::GetResult for the execution result //// Task::GetCommErrorCode for the HTTP communication status code run by the task //// Task::GetError for detailed information on task errors /* ------------------------------------------------------------------------ * When a file in the NSA list is downloaded, often that data is processed by the application immediately, but if there are multiple instances of NS data existing in BOSS storage already, there is no way for the application to determine which data was just downloaded. * * * For that reason, process the NS data in BOSS storage before getting the file and clear out BOSS storage. * * The following code assumes that the BOSS storage is empty. * * To download a file from the NSA list while leaving the existing NS data in BOSS storage, set the data version and private data type when registering the NSA list file (data) so that it can be distinguished from other NS data when getting NS data. * * * ------------------------------------------------------------------------ */ //Processing to download data. //// Before you get a list of data IDs, the application's "update indicator flag" is on. { bool arriveFlag = false; NN_UNUSED_VAR(arriveFlag);//Because this variable is exclusively used for output with NN_LOG, configure it to be unusable in Release builds. result = nn::boss::GetNewArrivalFlag(&arriveFlag); NN_BOSS_RESULT_HANDLING(result, "GetNewArrivalFlag"); NN_LOG("[BOSS Sample](Before Read Data, NewArrivalFlag is %d.)\n", arriveFlag); } //// Get a serial ID list for the downloaded data. (You can use the first argument to GetNsDataIdList to filter the data that is obtained.) u32 idBuf; // Buffer for storing serial IDs. This is prepared by the application. nn::boss::NsDataIdList serialIdList(&idBuf, 1); // Download a file in the NSA list and get a single instance of NS data. serialIdList.Initialize(); while (1) { result = nn::boss::GetNsDataIdList(nn::boss::DATA_TYPE_APPDATA|0xffff, &serialIdList); //If you only want to get extra data for the application. if (result == nn::boss::ResultNsDataListUpdated()) { // If ResultNsDataListUpdated is returned, this indicates that NS data is added to or deleted from BOSS storage during the process of getting a list of IDs. Initialize the list and attempt to get a list of serial IDs from the beginning. serialIdList.Initialize(); continue; } break; } if ((result.IsFailure()) && (result != nn::boss::ResultNsDataListSizeShortage())) { //Any error other than ResultNsDataListUpdated, ResultNsDataListSizeShortage, or ResultSuccess is an unexpected error. //ResultNsDataListSizeShortage() indicates that a list of IDs was obtained successfully, but the IDs could not all be stored in an NsDataIdList (the operation must be repeated). ResultSuccess() indicates that a list of IDs was obtained successfully (all of the IDs were obtained). NN_BOSS_RESULT_HANDLING(result, "boss::GetNsDataIdList"); } //Application-specific processing for downloaded data. (This demo dumps the header information and body data for all downloaded data.) NN_LOG("[BOSS Sample]Dump NSD data.\n\n"); NN_LOG("===NSD (SerialID = %d)===\n", serialIdList.GetNsDataId(0)); nn::boss::NsData contentData; result = contentData.Initialize(serialIdList.GetNsDataId(0)); NN_BOSS_RESULT_HANDLING(result, "NsData::Initialize"); //Check the header { nn::fs::TitleId titlleID = 0; result = contentData.GetHeaderInfo(nn::boss::NSD_TITLEID, &titlleID, sizeof(titlleID)); NN_BOSS_RESULT_HANDLING(result, "NsData::GetHeaderInfo"); NN_LOG("TitleID = %llx\n",titlleID); u32 headerInfo = 0; result = contentData.GetHeaderInfo(nn::boss::NSD_SERIALID, &headerInfo, sizeof(headerInfo)); NN_BOSS_RESULT_HANDLING(result, "NsData::GetHeaderInfo"); NN_LOG("Content SerialID = %d\n",headerInfo); result = contentData.GetHeaderInfo(nn::boss::NSD_LENGTH, &headerInfo, sizeof(headerInfo)); NN_BOSS_RESULT_HANDLING(result, "NsData::GetHeaderInfo"); NN_LOG("Payload Length = %d\n",headerInfo); result = contentData.GetHeaderInfo(nn::boss::NSD_VERSION, &headerInfo, sizeof(headerInfo)); NN_BOSS_RESULT_HANDLING(result, "NsData::GetHeaderInfo"); NN_LOG("Content version = %d\n",headerInfo); result = contentData.GetHeaderInfo(nn::boss::NSD_FLAGS, &headerInfo, sizeof(headerInfo)); NN_BOSS_RESULT_HANDLING(result, "NsData::GetHeaderInfo"); NN_LOG("Content Flags = %d\n",headerInfo); result = contentData.GetHeaderInfo(nn::boss::NSD_DATATYPE, &headerInfo, sizeof(headerInfo)); RESULT_SUCCESS_ASSERT(result); NN_BOSS_RESULT_HANDLING(result, "NsData::GetHeaderInfo"); NN_LOG("Content DataType = %d\n",headerInfo); } //Check the data { NN_LOG("---Dump Data---\n"); u8 dDataBuf[4*1024]; memset(dDataBuf, 0, sizeof(dDataBuf)); size_t readSize = 0; u32 readCount = 0; do { ++readCount; NN_LOG("(Read %d)\n", readCount); readSize = contentData.ReadData(reinterpret_cast(dDataBuf), sizeof(dDataBuf)); NN_BOSS_RESULT_HANDLING(result, "NsData::ReadData"); DumpNsdBody(dDataBuf, readSize); }while(readSize != 0); } //Get and set data attribute information { u32 setInfo = 0x100; result = contentData.SetAdditionalInfo(setInfo); NN_BOSS_RESULT_HANDLING(result, "NsData::SetAdditionalInfo"); u32 getInfo; result = contentData.GetAdditionalInfo(&getInfo); NN_BOSS_RESULT_HANDLING(result, "NsData::SetAdditionalInfo"); NN_LOG("[BOSS Sample]AdditionalInfo = %d(Set Info = %d)\n", setInfo, setInfo); } /* * You can also use the data's already-read flag. * Unlike the application's "update indicator flag" obtained by the RegisterNewArrivalEvent function, the data's already-read flag is used for the application to manage unprocessed data. * The flag is off when data is downloaded and stays off as long as the application does not explicitly turn it on. * Use this when the application manages unprocessed data. * NOTE: The GetNewDataNsDataIdList function returns a list of data for which this flag is off. You can use this for different purposes than GetNsDataIdList, which returns a list of all data regardless of their flags. */ { bool nsdReadFlag = true; result = contentData.GetReadFlag(&nsdReadFlag); NN_BOSS_RESULT_HANDLING(result, "NsData::GetReadFlag"); NN_LOG("[BOSS Sample]NSD Read Flag = %d\n", nsdReadFlag); if(nsdReadFlag == false) { //Turn the flag on. (The data's already-read flag does not change unless the application sets it.) result = contentData.SetReadFlag(true); NN_BOSS_RESULT_HANDLING(result, "NsData::SetNewFrag"); } } //Delete data /* { result = contentData.Delete(); NN_BOSS_RESULT_HANDLING(result, "NsData::Delete"); } */ //After you get a list of data IDs, the application's "update indicator flag" is off. { bool arriveFlag = false; result = nn::boss::GetNewArrivalFlag(&arriveFlag); NN_BOSS_RESULT_HANDLING(result, "GetNewArrivalFlag"); NN_LOG("[BOSS Sample](After Read Data, NewArrivalFlag is %d.)\n", arriveFlag); } //If you turn on the already-read flag for all data, the GetNewDataNsDataIdList function will get an empty list (because it gets a list of data for which the already-read flag is off). { result = nn::boss::GetNewDataNsDataIdList(nn::boss::DATA_TYPE_ALL, &serialIdList); NN_BOSS_RESULT_HANDLING(result, "GetNewDataNsDataIdList"); NN_LOG("[BOSS Sample](After NSD read flag on, New NSD number is = %d)\n", serialIdList.GetSize()); } } } extern "C" void nnMain() { // Call only the nn::applet::Enable function to also allow execution from the HOME Menu // HOME Menu transitions, POWER Button presses, and sleep are all unsupported nn::applet::Enable(); /* =======================================================================     Pre-processing Note: Network configuration process or other processes. You do not need to implement this if the process has already been run when BOSS is used. ======================================================================== */ //Initialize the BOSS library { nn::Result result = nn::boss::Initialize(); NN_BOSS_RESULT_HANDLING(result, "boss::Initialize"); } //Change settings so that the application runs BOSS in the background when we are not sleeping { // Initialize the NDM library nn::Result result = nn::ndm::Initialize(); NN_BOSS_RESULT_HANDLING(result, "ndm::Initialize()"); // Resume BOSS result = nn::ndm::Resume(nn::ndm::DN_BOSS); NN_BOSS_RESULT_HANDLING(result, "ndm::Resume()"); } // Create the expanded save data // Save the downloaded NSA list in the expanded save data region. { const char extSaveDataMountName[] = "test:"; nn::fs::Initialize(); { nn::Result result = nn::fs::MountExtSaveData(extSaveDataMountName, APP_EXT_STORAGE_ID); if(result.IsFailure()) { if(nn::fs::ResultNotFormatted::Includes(result) || nn::fs::ResultBadFormat::Includes(result) || nn::fs::ResultVerificationFailed::Includes(result)) { nn::fs::DeleteExtSaveData(APP_EXT_STORAGE_ID); } if(nn::fs::ResultNotFound::Includes(result) || nn::fs::ResultNotFormatted::Includes(result) || nn::fs::ResultBadFormat::Includes(result) || nn::fs::ResultVerificationFailed::Includes(result)) { NN_LOG("Create ExtSaveData(ID=%lx)\n", APP_EXT_STORAGE_ID); //In SDK 0.14, icon data of some kind must be specified to fs::CreateExtSaveData. u8 iconData[] = {0x01}; result = nn::fs::CreateExtSaveData(APP_EXT_STORAGE_ID, iconData, sizeof(iconData), 10, 100); if(result.IsFailure()) { NN_LOG_ERROR("CreateExtSaveData failed(%08x)\n", result.GetPrintableBits()); NN_UTIL_PANIC_IF_FAILED(result); } result = nn::fs::MountExtSaveData(extSaveDataMountName, APP_EXT_STORAGE_ID); NN_UTIL_PANIC_IF_FAILED(result); } else { NN_LOG_ERROR("MountExtSaveData failed(%08x)\n", result.GetPrintableBits()); NN_UTIL_PANIC_IF_FAILED(result); } } } } NN_LOG("Initializing network.\n"); // Connect to the network. // The NSA list is downloaded in the foreground, so you must connect to the network beforehand. { nn::Result result = InitializeNetwork(); NN_UTIL_PANIC_IF_FAILED(result); } NN_LOG("BOSS Sample (NSA List Simple) Start\n"); // Get the NSA list and download the files in the list sampleNSAListExecute(); NN_LOG("BOSS Sample (NSA List Simple) END\n"); /* =======================================================================     Clean up Note: Network termination processing and other processing. If BOSS will still be used later, this does not need to be run. ======================================================================== */ NN_LOG("Finalizing network.\n"); { nn::Result result = FinalizeNetwork(); NN_UTIL_PANIC_IF_FAILED(result); } //Unregister BOSS storage if BOSS features will not be used later. //(Note that once it is unregistered, you will no longer be able to run NADL tasks or read data from BOSS storage.) /* { nn::Result result = nn::boss::UnregisterStorage(); NN_UTIL_PANIC_IF_FAILED(result); } */ //Finalize the NDM library { nn::Result result = nn::ndm::Finalize(); NN_BOSS_RESULT_HANDLING(result, "ndm::Finalize()"); //When the program exits, pause/resume settings are also restored } //Shut down the library { nn::Result result = nn::boss::Finalize(); NN_UTIL_PANIC_IF_FAILED(result); } NN_LOG("END\n"); }