/*---------------------------------------------------------------------------* Project: Horizon File: sample_datastore.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 } nn::Result GetMyPrincipalId(nn::friends::PrincipalId *myPrincipalId) { nn::Result result = nn::ResultSuccess(); // Get PrincipalId *myPrincipalId = nn::friends::GetMyPrincipalId(); // If PrincipalId does not exist, login and logout of friend server and then try to get again. if (*myPrincipalId == nn::friends::INVALID_PRINCIPAL_ID) { nn::os::Event event(true); // Use the friends library to login to the friend server. result = nn::friends::Login(&event); if (result.IsSuccess()) { // Wait for login process. event.Wait(); if (nn::friends::GetLastResponseResult().IsSuccess()) { // Successful login NN_LOG("[BOSS Sample] Login friend server."); } else { NN_LOG("[BOSS Sample] Login friend server failed."); } } else { NN_LOG("[BOSS Sample] Login friend server failed."); } // Immediately log out (since only getting PrincipalId) nn::friends::Logout(); // Re-obtain PrincipalId *myPrincipalId = nn::friends::GetMyPrincipalId(); } return result; } } // Expanded save data ID // Take the file used for uploading when registering the DataStore upload task and copy it to BOSS storage (expanded save data region). const bit32 APP_EXT_STORAGE_ID = 0x00000011; const char extSaveDataMountName[] = "test:"; // Parameters required for accessing the DataStore server. const u32 DATASTORE_GAME_ID = 0x0FE00301; // GameID for accessing the DataStore server (for sample) const wchar_t DATASTORE_ACCESS_KEY[nn::boss::MAX_ACCESS_KEY_LENGTH] = L"871b3169"; // AccessKey for accessing the DataStore server (for sample) /* ------------------------------------------------------------------------ Sample code for BOSS DataStore upload task. ------------------------------------------------------------------------ */ void sampleDataStoreUploadTask() { // Register BOSS storage. // For the DataStore upload task, the file for uploading is copied to BOSS storage. // The copied file is deleted either when the upload process has completed or when the task is deleted. 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"); } const char8 DATASTORE_UPLOAD_TASK_ID[] = "DsUpTsk"; /* Please see man pages for details */ // Account for the fact that there may still be a task with the same ID and delete it. { nn::boss::Task deleteTargetTask; nn::Result result = deleteTargetTask.Initialize(DATASTORE_UPLOAD_TASK_ID); NN_BOSS_RESULT_HANDLING(result, "Task::Initialize"); deleteTargetTask.Cancel(); //UnregisterTask fails if the task is currently running. To ensure that it is deleted, call UnregisterTask after Cancel. NN_BOSS_RESULT_HANDLING(result, "Task::Cancel"); result = nn::boss::UnregisterTask(&deleteTargetTask); NN_BOSS_RESULT_HANDLING(result, "boss::UnregisterTask"); } // Use the friends library to get your own PrincipalId as the destination for the data. nn::friends::PrincipalId myPrincipalId = nn::friends::INVALID_PRINCIPAL_ID; { nn::Result result = nn::friends::Initialize(); NN_BOSS_RESULT_HANDLING(result, "friends::Initialize"); result = GetMyPrincipalId(&myPrincipalId); NN_BOSS_RESULT_HANDLING(result, "GetMyPrincipalId"); nn::friends::Finalize(); } // Register and execute the DataStore upload task. { const u16 DATASTORE_UPLOAD_TASK_EXECUTE_TIME = 1; /* Please see man pages for details */ const u16 DATASTORE_UPLOAD_TASK_EXECUTE_COUNT = 1; /* Please see man pages for details */ // Register the DataStore upload task. nn::boss::TaskPolicy dPolicy; nn::Result result = dPolicy.Initialize(DATASTORE_UPLOAD_TASK_EXECUTE_TIME, DATASTORE_UPLOAD_TASK_EXECUTE_COUNT); //nn::Result result = dPolicy.InitializeWithSecInterval(DATASTORE_UPLOAD_TASK_EXECUTE_TIME, DATASTORE_UPLOAD_TASK_EXECUTE_COUNT);// You can also specify an execution interval in seconds for testing. NN_BOSS_RESULT_HANDLING(result, "TaskPolicy::Initialize"); // Open the upload target file. // This demo uploads a file made from the data in memory. u8 uploadData[] = {0x01, 0x02, 0x03, 0x04, 0x05}; char uploadTargetFilePath[128]; snprintf(uploadTargetFilePath, sizeof(uploadTargetFilePath), "%s/test.bin", extSaveDataMountName); // Make a temporary file in the expanded save data region of the data for upload. NN_LOG("[BOSS Sample] Make Upload TempFile(%s).\n", uploadTargetFilePath); nn::fs::TryDeleteFile(uploadTargetFilePath);//Delete old file. result = nn::fs::TryCreateFile(uploadTargetFilePath, sizeof(uploadData)); NN_BOSS_RESULT_HANDLING(result, "TryCreateFile"); nn::fs::FileOutputStream uploadTempFile; result = uploadTempFile.TryInitialize(uploadTargetFilePath, false); NN_BOSS_RESULT_HANDLING(result, "FileOutputStream::TryInitialize"); s32 out; result = uploadTempFile.TryWrite(&out, uploadData, sizeof(uploadData), true); NN_BOSS_RESULT_HANDLING(result, "FileOutputStream::TryWrite"); NN_ASSERT(out == sizeof(uploadData)); uploadTempFile.Finalize(); // Set necessary parameters for DataStore upload. const nn::boss::DstKind DATASTORE_UPLOAD_TASK_DST_KIND = nn::boss::DSTKIND_SPECIFIED; // The target for sending of DateStore data (data is sent to specified peer) const u16 DATASTORE_UPLOAD_TASK_DATA_TYPE = 12345; // The DataStore data DataType. const u16 DATASTORE_UPLOAD_TASK_PERIOD = 1; // The expiration period (in days) of the DataStore data. nn::boss::DataStoreUploadAction dAction; result = dAction.Initialize(DATASTORE_GAME_ID, DATASTORE_ACCESS_KEY, DATASTORE_UPLOAD_TASK_DST_KIND, DATASTORE_UPLOAD_TASK_DATA_TYPE, DATASTORE_UPLOAD_TASK_PERIOD); NN_BOSS_RESULT_HANDLING(result, "nn::boss::DataStoreUploadAction::Initialize"); // Specify own PrincipalID as destination of transmission. result = dAction.AddDstPrincipalId(myPrincipalId); NN_BOSS_RESULT_HANDLING(result, "nn::boss::DataStoreUploadAction::AddDstPrincipalId"); nn::boss::Task dTask; result = dTask.Initialize(DATASTORE_UPLOAD_TASK_ID); NN_BOSS_RESULT_HANDLING(result, "Task::Initialize"); // Register the task. NN_LOG("[BOSS Sample] Regist DataStore Upload Task.\n"); result = nn::boss::RegisterTask(&dTask, &dPolicy, &dAction, uploadTargetFilePath); if (result == nn::boss::ResultTaskIdAlreadyExist()) { NN_LOG("[BOSS Sample] RegisterTask failed. Same name task(%s) already registered.If regist again, need to UnregisterTask before.\n", DATASTORE_UPLOAD_TASK_ID); } NN_BOSS_RESULT_HANDLING(result, "RegisterTask"); // Execute task NN_LOG("[BOSS Sample] Start DataStore Upload Task.\n"); result = dTask.Start(); NN_BOSS_RESULT_HANDLING(result, "Task::Start"); //Use StartImmediate if you want to run a task immediately without waiting for the next task execution time. //However, because StartImmediate runs a task in the foreground, the application must make an infrastructure network connection. (BOSS is not used.) /* [Infrastructure network connection with nn::ac::Connect] result = dTask.StartImmediate(); NN_BOSS_RESULT_HANDLING(result, "Task::StartImmediate"); (NOTE: Use nn::ac::Close to close the infrastructure network connection after the task finishes processing.) */ // Polling until completion of execution of DataStore upload task. // If you want to execute immediately, comment out "dPolicy.InitializeWithSecInterval " in the above code. while (1) { nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromMilliSeconds(5000)); if (dTask.GetState(true) == nn::boss::TASK_DONE) { NN_LOG("[BOSS Sample] DataStore Upload Task Done.\n"); break; } else if (dTask.GetState(true) == nn::boss::TASK_ERROR) { NN_LOG("[BOSS Sample] DataStore Upload Task Error.\n"); break; } NN_LOG("[BOSS Sample] Polling DataStore Upload Task.\n"); } } } /* ------------------------------------------------------------------------ Sample code for BOSS DataStore download task. ------------------------------------------------------------------------ */ void sampleDataStoreDownloadTask() { const char8 DATASTORE_DOWNLOAD_TASK_ID[] = "DsDlTsk"; /* Please see man pages for details */ // Account for the fact that there may still be a task with the same ID and delete it. { nn::boss::Task deleteTargetTask; nn::Result result = deleteTargetTask.Initialize(DATASTORE_DOWNLOAD_TASK_ID); NN_BOSS_RESULT_HANDLING(result, "Task::Initialize"); deleteTargetTask.Cancel(); //UnregisterTask fails if the task is currently running. To ensure that it is deleted, call UnregisterTask after Cancel. NN_BOSS_RESULT_HANDLING(result, "Task::Cancel"); result = nn::boss::UnregisterTask(&deleteTargetTask); NN_BOSS_RESULT_HANDLING(result, "boss::UnregisterTask"); } // Register and execute the DataStore download task. { const u16 DATASTORE_DOWNLOAD_TASK_EXECUTE_TIME = 1; /* Please see man pages for details */ const u16 DATASTORE_DOWNLOAD_TASK_EXECUTE_COUNT = 1; /* Please see man pages for details */ // Register the DataStore download task. nn::boss::TaskPolicy dPolicy; nn::Result result = dPolicy.Initialize(DATASTORE_DOWNLOAD_TASK_EXECUTE_TIME, DATASTORE_DOWNLOAD_TASK_EXECUTE_COUNT); //nn::Result result = dPolicy.InitializeWithSecInterval(DATASTORE_DOWNLOAD_TASK_EXECUTE_TIME, DATASTORE_DOWNLOAD_TASK_EXECUTE_COUNT);// You can also specify an execution interval in seconds for testing. NN_BOSS_RESULT_HANDLING(result, "TaskPolicy::Initialize"); nn::boss::DataStoreDownloadAction dAction; result = dAction.Initialize(DATASTORE_GAME_ID, DATASTORE_ACCESS_KEY); NN_BOSS_RESULT_HANDLING(result, "DataStoreDownloadAction::Initialize"); // Set notification to receive at time of DataStore download. const bit32 DATASTORE_DOWNLOAD_NEWS_SERIAL_ID = 100000; // SerialID of NS data received as notification. const wchar_t DATASTORE_DOWNLOAD_NEWS_SUBJECT[] = L"Sample Subject"; // "Subject" part of notification. const wchar_t DATASTORE_DOWNLOAD_NEWS_MESSAGE[] = L"Sample Message"; // "Message" part of notification. const u8 DATASTORE_DOWNLOAD_NEWS_JUMP_PARAM[nn::boss::MAX_NEWS_JUMP_PARAM] = {1}; // General-purpose notification jump parameters. result = dAction.SetNewsPublication(DATASTORE_DOWNLOAD_NEWS_SERIAL_ID, DATASTORE_DOWNLOAD_NEWS_SUBJECT, DATASTORE_DOWNLOAD_NEWS_MESSAGE, DATASTORE_DOWNLOAD_NEWS_JUMP_PARAM); NN_BOSS_RESULT_HANDLING(result, "DataStoreDownloadAction::SetNewsPublication"); nn::boss::Task dTask; result = dTask.Initialize(DATASTORE_DOWNLOAD_TASK_ID); NN_BOSS_RESULT_HANDLING(result, "Task::Initialize"); // Register the task. NN_LOG("[BOSS Sample] Regist DataStore Download Task.\n"); result = nn::boss::RegisterTask(&dTask, &dPolicy, &dAction); if (result == nn::boss::ResultTaskIdAlreadyExist()) { NN_LOG("[BOSS Sample] RegisterTask failed. Same name task(%s) already registered.If regist again, need to UnregisterTask before.\n", DATASTORE_DOWNLOAD_TASK_ID); } NN_BOSS_RESULT_HANDLING(result, "RegisterTask"); // Execute task NN_LOG("[BOSS Sample] Start DataStore Download Task.\n"); result = dTask.Start(); NN_BOSS_RESULT_HANDLING(result, "Task::Start"); // Wait for DataStore 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"); //Use StartImmediate if you want to run a task immediately without waiting for the next task execution time. //However, because StartImmediate runs a task in the foreground, the application must make an infrastructure network connection. (BOSS is not used.) /* [Infrastructure network connection with nn::ac::Connect] result = dTask.StartImmediate(); NN_BOSS_RESULT_HANDLING(result, "Task::StartImmediate"); (NOTE: Use nn::ac::Close to close the infrastructure network connection after the task finishes processing.) */ // 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"); //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.) static const u32 MAX_DATA_ID = 32; u32 idBuf[MAX_DATA_ID];//Buffer for storing serial IDs. This is prepared by the application. By providing a large buffer, you can get a large number of serial IDs at one time. nn::boss::NsDataIdList serialIdList(idBuf, MAX_DATA_ID);//The number of serial IDs that can be obtained at one time is the same as the number of elements in the array buffer. serialIdList.Initialize(); u32 getNsDataIdListCount = 0; nn::Result getNsDataIdListResult; do { getNsDataIdListResult = nn::boss::GetNsDataIdList(nn::boss::DATA_TYPE_ALL, &serialIdList); //result = nn::boss::GetNsDataIdList(nn::boss::DATA_TYPE_APPDATA|0xffff, &serialIdList); // When you only want to get extra data for the application. if(getNsDataIdListResult == 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(); getNsDataIdListResult = nn::boss::ResultNsDataListSizeShortage();//Update the value of getNsDataIdListResult so that control does not exit the do-while loop. continue; } else if((getNsDataIdListResult.IsFailure()) && (getNsDataIdListResult != 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.) ++getNsDataIdListCount; NN_LOG("[BOSS Sample]Dump NSD datas(%d).(data number = %d)\n\n", getNsDataIdListCount, serialIdList.GetSize()); for(int i=0; i < serialIdList.GetSize(); ++i) { NN_LOG("===NSD No.%d(SerialID = %d)===\n", i, serialIdList.GetNsDataId(i)); nn::boss::NsData contentData; result = contentData.Initialize(serialIdList.GetNsDataId(i)); 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"); } */ } }while(getNsDataIdListResult == nn::boss::ResultNsDataListSizeShortage());//If this is ResultNsDataListSizeShortage, run GetNsDataIdList again because all of the IDs have still not been obtained. //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 // For DataStore upload tasks the data for uploading is saved to BOSS storage in the expanded save data region. // For DataStore download tasks, the downloaded data is saved to BOSS storage in the expanded save data region. // You thus need an expanded save data region in order to use DataStore tasks. { 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); //When you create expanded save data, configure the maximum file count so that it also accounts for the number of files downloaded through BOSS. //Once the maximum number of files already exist in expanded save data, it will no longer be possible to write files downloaded through BOSS. //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. { nn::Result result = InitializeNetwork(); NN_UTIL_PANIC_IF_FAILED(result); } NN_LOG("BOSS Sample(DataStoreTask) Start\n"); // Register and execute the DataStore upload task. sampleDataStoreUploadTask(); // If you do not wait for at least around 20 seconds after uploading to DataStore you cannot download the data. // To play it safe, wait for around 25 seconds. NN_LOG("Wait for 25 sec...\n"); nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromSeconds(25)); // Register and execute the DataStore download task. sampleDataStoreDownloadTask(); NN_LOG("BOSS Sample(DataStoreTask) 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"); }