1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     sample_datastore.cpp
4 
5   Copyright (C)2009-2012 Nintendo Co., Ltd.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law. They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Rev:$
14  *---------------------------------------------------------------------------*/
15 
16 #include <stdio.h>
17 #include <string.h>
18 #include <nn/fs.h>
19 #include <nn/boss.h>
20 #include <nn/fs/fs_Parameters.h>
21 #include <nn/ac.h>
22 #include <nn/applet.h>
23 #include <nn/ndm.h>
24 #include <nn/friends.h>
25 
26 #define RESULT_SUCCESS_ASSERT NN_UTIL_PANIC_IF_FAILED
27 
28 #define NN_BOSS_RESULT_HANDLING(result, target)            \
29     do {                                                   \
30         if(result.IsFailure())                             \
31         {                                                  \
32             NN_LOG("result is err(%08x).(%s)\n", result.GetPrintableBits(), target);        \
33         }                                                  \
34     } while (0)
35 
36 namespace {
InitializeNetwork(void)37     nn::Result InitializeNetwork(void)
38     {
39         nn::Result result;
40         nn::ac::Config config;
41 
42         nn::fs::Initialize();
43         result = nn::ac::Initialize();
44         NN_UTIL_PANIC_IF_FAILED(result);
45 
46         // Create parameters for connection request
47         result = nn::ac::CreateDefaultConfig( &config );
48         if (result.IsFailure())
49         {
50             NN_UTIL_PANIC_IF_FAILED(nn::ac::Finalize());
51             return result;
52         }
53 
54         // Issue connection request
55         result = nn::ac::Connect( config );
56         if (result.IsFailure())
57         {
58             NN_UTIL_PANIC_IF_FAILED(nn::ac::Finalize());
59             return result;
60         }
61 
62         return nn::ResultSuccess();
63     }
64 
FinalizeNetwork(void)65     nn::Result FinalizeNetwork(void)
66     {
67         nn::Result result;
68 
69         // Create parameters for connection request
70         result = nn::ac::Close();
71         NN_UTIL_RETURN_IF_FAILED(result);
72 
73         result = nn::ac::Finalize();
74         NN_UTIL_PANIC_IF_FAILED(result);
75 
76         return nn::ResultSuccess();
77     }
78 
DumpNsdBody(u8 * pBodyBuf,size_t payloadLength)79     void DumpNsdBody(u8* pBodyBuf, size_t payloadLength)
80     {
81     #ifndef NN_SWITCH_DISABLE_DEBUG_PRINT
82         NN_LOG("---NSD Body Dump(Payload-Length=%d)---\n", payloadLength);
83         int hexCheckCount = 0;
84         for(int i = 0; i < payloadLength; ++i)
85         {
86             NN_LOG("%02x ", pBodyBuf[i]);
87             ++hexCheckCount;
88             if(hexCheckCount == 16)
89             {
90                 NN_LOG("\n");
91                 hexCheckCount = 0;
92             }
93         }
94     #else
95         NN_UNUSED_VAR(pBodyBuf);
96         NN_UNUSED_VAR(payloadLength);
97     #endif
98     }
99 
GetMyPrincipalId(nn::friends::PrincipalId * myPrincipalId)100     nn::Result GetMyPrincipalId(nn::friends::PrincipalId *myPrincipalId)
101     {
102         nn::Result result = nn::ResultSuccess();
103 
104         // Get PrincipalId
105         *myPrincipalId = nn::friends::GetMyPrincipalId();
106 
107         // If PrincipalId does not exist, login and logout of friend server and then try to get again.
108         if (*myPrincipalId == nn::friends::INVALID_PRINCIPAL_ID)
109         {
110             nn::os::Event event(true);
111 
112             // Use the friends library to login to the friend server.
113             result = nn::friends::Login(&event);
114             if (result.IsSuccess())
115             {
116                 // Wait for login process.
117                 event.Wait();
118                 if (nn::friends::GetLastResponseResult().IsSuccess())
119                 {
120                     // Successful login
121                     NN_LOG("[BOSS Sample] Login friend server.");
122                 }
123                 else
124                 {
125                     NN_LOG("[BOSS Sample] Login friend server failed.");
126                 }
127             }
128             else
129             {
130                 NN_LOG("[BOSS Sample] Login friend server failed.");
131             }
132 
133             // Immediately log out (since only getting PrincipalId)
134             nn::friends::Logout();
135 
136             // Re-obtain PrincipalId
137             *myPrincipalId = nn::friends::GetMyPrincipalId();
138         }
139 
140         return result;
141     }
142 }
143 
144 // Expanded save data ID
145 // Take the file used for uploading when registering the DataStore upload task and copy it to BOSS storage (expanded save data region).
146 const bit32 APP_EXT_STORAGE_ID         = 0x00000011;
147 
148 const char extSaveDataMountName[] = "test:";
149 
150 // Parameters required for accessing the DataStore server.
151 const u32     DATASTORE_GAME_ID                                     = 0x0FE00301;   // GameID for accessing the DataStore server (for sample)
152 const wchar_t DATASTORE_ACCESS_KEY[nn::boss::MAX_ACCESS_KEY_LENGTH] = L"871b3169";  // AccessKey for accessing the DataStore server (for sample)
153 
154 
155 /* ------------------------------------------------------------------------
156    Sample code for BOSS DataStore upload task.
157    ------------------------------------------------------------------------ */
sampleDataStoreUploadTask()158 void sampleDataStoreUploadTask()
159 {
160     // Register BOSS storage.
161     // For the DataStore upload task, the file for uploading is copied to BOSS storage.
162     // The copied file is deleted either when the upload process has completed or when the task is deleted.
163     const size_t NADL_STORAGE_SIZE         = 3*1024*1024;   // The amount of the expanded save data region to use as BOSS storage.
164     {
165         nn::Result result = nn::boss::GetStorageInfo();
166         if(result == nn::boss::ResultStorageNotFound())
167         {
168             NN_LOG("[BOSS Sample] RegisterStorage BOSS Storage.\n");
169             result = nn::boss::RegisterStorage(APP_EXT_STORAGE_ID, NADL_STORAGE_SIZE);
170         }
171         NN_BOSS_RESULT_HANDLING(result, "boss::RegisterStorage");
172     }
173 
174     const char8 DATASTORE_UPLOAD_TASK_ID[]      = "DsUpTsk";   /* Please see man pages for details */
175 
176     // Account for the fact that there may still be a task with the same ID and delete it.
177     {
178         nn::boss::Task deleteTargetTask;
179         nn::Result result = deleteTargetTask.Initialize(DATASTORE_UPLOAD_TASK_ID);
180         NN_BOSS_RESULT_HANDLING(result, "Task::Initialize");
181 
182         deleteTargetTask.Cancel(); //UnregisterTask fails if the task is currently running. To ensure that it is deleted, call UnregisterTask after Cancel.
183         NN_BOSS_RESULT_HANDLING(result, "Task::Cancel");
184 
185         result = nn::boss::UnregisterTask(&deleteTargetTask);
186         NN_BOSS_RESULT_HANDLING(result, "boss::UnregisterTask");
187     }
188 
189     // Use the friends library to get your own PrincipalId as the destination for the data.
190     nn::friends::PrincipalId myPrincipalId = nn::friends::INVALID_PRINCIPAL_ID;
191     {
192         nn::Result result = nn::friends::Initialize();
193         NN_BOSS_RESULT_HANDLING(result, "friends::Initialize");
194 
195         result = GetMyPrincipalId(&myPrincipalId);
196         NN_BOSS_RESULT_HANDLING(result, "GetMyPrincipalId");
197 
198         nn::friends::Finalize();
199     }
200 
201     // Register and execute the DataStore upload task.
202     {
203 
204         const u16 DATASTORE_UPLOAD_TASK_EXECUTE_TIME  = 1;  /* Please see man pages for details */
205         const u16 DATASTORE_UPLOAD_TASK_EXECUTE_COUNT = 1;  /* Please see man pages for details */
206 
207         // Register the DataStore upload task.
208         nn::boss::TaskPolicy dPolicy;
209         nn::Result result = dPolicy.Initialize(DATASTORE_UPLOAD_TASK_EXECUTE_TIME, DATASTORE_UPLOAD_TASK_EXECUTE_COUNT);
210         //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.
211         NN_BOSS_RESULT_HANDLING(result, "TaskPolicy::Initialize");
212 
213         // Open the upload target file.
214         // This demo uploads a file made from the data in memory.
215         u8 uploadData[] = {0x01, 0x02, 0x03, 0x04, 0x05};
216         char uploadTargetFilePath[128];
217         snprintf(uploadTargetFilePath, sizeof(uploadTargetFilePath), "%s/test.bin", extSaveDataMountName);
218 
219         // Make a temporary file in the expanded save data region of the data for upload.
220         NN_LOG("[BOSS Sample] Make Upload TempFile(%s).\n", uploadTargetFilePath);
221         nn::fs::TryDeleteFile(uploadTargetFilePath);//Delete old file.
222 
223         result = nn::fs::TryCreateFile(uploadTargetFilePath, sizeof(uploadData));
224         NN_BOSS_RESULT_HANDLING(result, "TryCreateFile");
225 
226         nn::fs::FileOutputStream uploadTempFile;
227         result = uploadTempFile.TryInitialize(uploadTargetFilePath, false);
228         NN_BOSS_RESULT_HANDLING(result, "FileOutputStream::TryInitialize");
229 
230         s32 out;
231         result = uploadTempFile.TryWrite(&out, uploadData, sizeof(uploadData), true);
232         NN_BOSS_RESULT_HANDLING(result, "FileOutputStream::TryWrite");
233         NN_ASSERT(out == sizeof(uploadData));
234         uploadTempFile.Finalize();
235 
236         // Set necessary parameters for DataStore upload.
237         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)
238         const u16               DATASTORE_UPLOAD_TASK_DATA_TYPE    = 12345;                        // The DataStore data DataType.
239         const u16               DATASTORE_UPLOAD_TASK_PERIOD       = 1;                            // The expiration period (in days) of the DataStore data.
240         nn::boss::DataStoreUploadAction dAction;
241         result = dAction.Initialize(DATASTORE_GAME_ID,
242                                     DATASTORE_ACCESS_KEY,
243                                     DATASTORE_UPLOAD_TASK_DST_KIND,
244                                     DATASTORE_UPLOAD_TASK_DATA_TYPE,
245                                     DATASTORE_UPLOAD_TASK_PERIOD);
246         NN_BOSS_RESULT_HANDLING(result, "nn::boss::DataStoreUploadAction::Initialize");
247 
248         // Specify own PrincipalID as destination of transmission.
249         result = dAction.AddDstPrincipalId(myPrincipalId);
250         NN_BOSS_RESULT_HANDLING(result, "nn::boss::DataStoreUploadAction::AddDstPrincipalId");
251 
252         nn::boss::Task dTask;
253         result = dTask.Initialize(DATASTORE_UPLOAD_TASK_ID);
254         NN_BOSS_RESULT_HANDLING(result, "Task::Initialize");
255 
256         // Register the task.
257         NN_LOG("[BOSS Sample] Regist DataStore Upload Task.\n");
258         result = nn::boss::RegisterTask(&dTask, &dPolicy, &dAction, uploadTargetFilePath);
259         if (result == nn::boss::ResultTaskIdAlreadyExist())
260         {
261             NN_LOG("[BOSS Sample] RegisterTask failed. Same name task(%s) already registered.If regist again, need to UnregisterTask before.\n", DATASTORE_UPLOAD_TASK_ID);
262         }
263         NN_BOSS_RESULT_HANDLING(result, "RegisterTask");
264 
265         // Execute task
266         NN_LOG("[BOSS Sample] Start DataStore Upload Task.\n");
267         result = dTask.Start();
268         NN_BOSS_RESULT_HANDLING(result, "Task::Start");
269 
270         //Use StartImmediate if you want to run a task immediately without waiting for the next task execution time.
271         //However, because StartImmediate runs a task in the foreground, the application must make an infrastructure network connection. (BOSS is not used.)
272         /*
273         [Infrastructure network connection with nn::ac::Connect]
274         result = dTask.StartImmediate();
275         NN_BOSS_RESULT_HANDLING(result, "Task::StartImmediate");
276         (NOTE: Use nn::ac::Close to close the infrastructure network connection after the task finishes processing.)
277         */
278 
279         // Polling until completion of execution of DataStore upload task.
280         // If you want to execute immediately, comment out "dPolicy.InitializeWithSecInterval " in the above code.
281         while (1)
282         {
283             nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromMilliSeconds(5000));
284 
285             if (dTask.GetState(true) == nn::boss::TASK_DONE)
286             {
287                 NN_LOG("[BOSS Sample] DataStore Upload Task Done.\n");
288                 break;
289             }
290             else if (dTask.GetState(true) == nn::boss::TASK_ERROR)
291             {
292                 NN_LOG("[BOSS Sample] DataStore Upload Task Error.\n");
293                 break;
294             }
295 
296             NN_LOG("[BOSS Sample] Polling DataStore Upload Task.\n");
297         }
298     }
299 
300 }
301 
302 /* ------------------------------------------------------------------------
303    Sample code for BOSS DataStore download task.
304    ------------------------------------------------------------------------ */
sampleDataStoreDownloadTask()305 void sampleDataStoreDownloadTask()
306 {
307     const char8 DATASTORE_DOWNLOAD_TASK_ID[]      = "DsDlTsk";   /* Please see man pages for details */
308 
309     // Account for the fact that there may still be a task with the same ID and delete it.
310     {
311         nn::boss::Task deleteTargetTask;
312         nn::Result result = deleteTargetTask.Initialize(DATASTORE_DOWNLOAD_TASK_ID);
313         NN_BOSS_RESULT_HANDLING(result, "Task::Initialize");
314 
315         deleteTargetTask.Cancel(); //UnregisterTask fails if the task is currently running. To ensure that it is deleted, call UnregisterTask after Cancel.
316         NN_BOSS_RESULT_HANDLING(result, "Task::Cancel");
317 
318         result = nn::boss::UnregisterTask(&deleteTargetTask);
319         NN_BOSS_RESULT_HANDLING(result, "boss::UnregisterTask");
320     }
321 
322     // Register and execute the DataStore download task.
323     {
324         const u16 DATASTORE_DOWNLOAD_TASK_EXECUTE_TIME  = 1;  /* Please see man pages for details */
325         const u16 DATASTORE_DOWNLOAD_TASK_EXECUTE_COUNT = 1;  /* Please see man pages for details */
326 
327         // Register the DataStore download task.
328         nn::boss::TaskPolicy dPolicy;
329         nn::Result result = dPolicy.Initialize(DATASTORE_DOWNLOAD_TASK_EXECUTE_TIME, DATASTORE_DOWNLOAD_TASK_EXECUTE_COUNT);
330         //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.
331         NN_BOSS_RESULT_HANDLING(result, "TaskPolicy::Initialize");
332 
333         nn::boss::DataStoreDownloadAction dAction;
334         result = dAction.Initialize(DATASTORE_GAME_ID, DATASTORE_ACCESS_KEY);
335         NN_BOSS_RESULT_HANDLING(result, "DataStoreDownloadAction::Initialize");
336 
337         // Set notification to receive at time of DataStore download.
338         const bit32   DATASTORE_DOWNLOAD_NEWS_SERIAL_ID     = 100000;              // SerialID of NS data received as notification.
339         const wchar_t DATASTORE_DOWNLOAD_NEWS_SUBJECT[]     = L"Sample Subject";   // "Subject" part of notification.
340         const wchar_t DATASTORE_DOWNLOAD_NEWS_MESSAGE[]     = L"Sample Message";   // "Message" part of notification.
341         const u8      DATASTORE_DOWNLOAD_NEWS_JUMP_PARAM[nn::boss::MAX_NEWS_JUMP_PARAM] = {1};  // General-purpose notification jump parameters.
342         result = dAction.SetNewsPublication(DATASTORE_DOWNLOAD_NEWS_SERIAL_ID,
343                                             DATASTORE_DOWNLOAD_NEWS_SUBJECT,
344                                             DATASTORE_DOWNLOAD_NEWS_MESSAGE,
345                                             DATASTORE_DOWNLOAD_NEWS_JUMP_PARAM);
346         NN_BOSS_RESULT_HANDLING(result, "DataStoreDownloadAction::SetNewsPublication");
347 
348         nn::boss::Task dTask;
349         result = dTask.Initialize(DATASTORE_DOWNLOAD_TASK_ID);
350         NN_BOSS_RESULT_HANDLING(result, "Task::Initialize");
351 
352         // Register the task.
353         NN_LOG("[BOSS Sample] Regist DataStore Download Task.\n");
354         result = nn::boss::RegisterTask(&dTask, &dPolicy, &dAction);
355         if (result == nn::boss::ResultTaskIdAlreadyExist())
356         {
357             NN_LOG("[BOSS Sample] RegisterTask failed. Same name task(%s) already registered.If regist again, need to UnregisterTask before.\n", DATASTORE_DOWNLOAD_TASK_ID);
358         }
359         NN_BOSS_RESULT_HANDLING(result, "RegisterTask");
360 
361         // Execute task
362         NN_LOG("[BOSS Sample] Start DataStore Download Task.\n");
363         result = dTask.Start();
364         NN_BOSS_RESULT_HANDLING(result, "Task::Start");
365 
366         // Wait for DataStore download data.
367         // Get a data download notification event from the BOSS daemon.
368         nn::os::Event arriveEvent(false);
369         result = nn::boss::RegisterNewArrivalEvent(&arriveEvent);
370         NN_BOSS_RESULT_HANDLING(result, "Task::RegisterNewArrivalEvent");
371 
372         //Use StartImmediate if you want to run a task immediately without waiting for the next task execution time.
373         //However, because StartImmediate runs a task in the foreground, the application must make an infrastructure network connection. (BOSS is not used.)
374         /*
375         [Infrastructure network connection with nn::ac::Connect]
376         result = dTask.StartImmediate();
377         NN_BOSS_RESULT_HANDLING(result, "Task::StartImmediate");
378         (NOTE: Use nn::ac::Close to close the infrastructure network connection after the task finishes processing.)
379         */
380 
381         // Wait for new data to be downloaded.
382         NN_LOG("[BOSS Sample] Wait NSA data arrive....\n");
383         arriveEvent.Wait();
384         NN_LOG("[BOSS Sample] Recognize NSA data arrive\n");
385 
386         //Processing to download data.
387         //// Before you get a list of data IDs, the application's "update indicator flag" is on.
388         {
389             bool arriveFlag = false;
390             NN_UNUSED_VAR(arriveFlag); //Because this variable is exclusively used for output with NN_LOG, configure it to be unusable in Release builds.
391             result = nn::boss::GetNewArrivalFlag(&arriveFlag);
392             NN_BOSS_RESULT_HANDLING(result, "GetNewArrivalFlag");
393             NN_LOG("[BOSS Sample] (Before Read Data, NewArrivalFlag is %d.)\n", arriveFlag);
394         }
395 
396         //// Get a serial ID list for the downloaded data. (You can use the first argument to GetNsDataIdList to filter the data that is obtained.)
397         static const u32 MAX_DATA_ID = 32;
398         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.
399         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.
400         serialIdList.Initialize();
401 
402         u32 getNsDataIdListCount = 0;
403         nn::Result getNsDataIdListResult;
404         do
405         {
406             getNsDataIdListResult = nn::boss::GetNsDataIdList(nn::boss::DATA_TYPE_ALL, &serialIdList);
407             //result = nn::boss::GetNsDataIdList(nn::boss::DATA_TYPE_APPDATA|0xffff, &serialIdList); // When you only want to get extra data for the application.
408 
409             if(getNsDataIdListResult == nn::boss::ResultNsDataListUpdated())
410             {
411                 //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.
412                 serialIdList.Initialize();
413                 getNsDataIdListResult = nn::boss::ResultNsDataListSizeShortage();//Update the value of getNsDataIdListResult so that control does not exit the do-while loop.
414                 continue;
415             }
416             else if((getNsDataIdListResult.IsFailure()) && (getNsDataIdListResult != nn::boss::ResultNsDataListSizeShortage()))
417             {
418                 //Any error other than ResultNsDataListUpdated, ResultNsDataListSizeShortage, or ResultSuccess is an unexpected error.
419                 //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).
420                 NN_BOSS_RESULT_HANDLING(result, "boss::GetNsDataIdList");
421             }
422 
423             //Application-specific processing for downloaded data. (This demo dumps the header information and body data for all downloaded data.)
424             ++getNsDataIdListCount;
425             NN_LOG("[BOSS Sample]Dump NSD datas(%d).(data number = %d)\n\n", getNsDataIdListCount, serialIdList.GetSize());
426             for(int i=0; i < serialIdList.GetSize(); ++i)
427             {
428                 NN_LOG("===NSD No.%d(SerialID = %d)===\n", i, serialIdList.GetNsDataId(i));
429                 nn::boss::NsData contentData;
430                 result = contentData.Initialize(serialIdList.GetNsDataId(i));
431                 NN_BOSS_RESULT_HANDLING(result, "NsData::Initialize");
432 
433                 //Check the header
434                 {
435                     nn::fs::TitleId titlleID = 0;
436                     result = contentData.GetHeaderInfo(nn::boss::NSD_TITLEID, &titlleID, sizeof(titlleID));
437                     NN_BOSS_RESULT_HANDLING(result, "NsData::GetHeaderInfo");
438                     NN_LOG("TitleID = %llx\n",titlleID);
439 
440                     u32 headerInfo = 0;
441                     result = contentData.GetHeaderInfo(nn::boss::NSD_SERIALID, &headerInfo, sizeof(headerInfo));
442                     NN_BOSS_RESULT_HANDLING(result, "NsData::GetHeaderInfo");
443                     NN_LOG("Content SerialID = %d\n",headerInfo);
444 
445                     result = contentData.GetHeaderInfo(nn::boss::NSD_LENGTH, &headerInfo, sizeof(headerInfo));
446                     NN_BOSS_RESULT_HANDLING(result, "NsData::GetHeaderInfo");
447                     NN_LOG("Payload Length = %d\n",headerInfo);
448 
449                     result = contentData.GetHeaderInfo(nn::boss::NSD_VERSION, &headerInfo, sizeof(headerInfo));
450                     NN_BOSS_RESULT_HANDLING(result, "NsData::GetHeaderInfo");
451                     NN_LOG("Content version = %d\n",headerInfo);
452 
453                     result = contentData.GetHeaderInfo(nn::boss::NSD_FLAGS, &headerInfo, sizeof(headerInfo));
454                     NN_BOSS_RESULT_HANDLING(result, "NsData::GetHeaderInfo");
455                     NN_LOG("Content Flags = %d\n",headerInfo);
456 
457                     result = contentData.GetHeaderInfo(nn::boss::NSD_DATATYPE, &headerInfo, sizeof(headerInfo));
458                     RESULT_SUCCESS_ASSERT(result);
459                     NN_BOSS_RESULT_HANDLING(result, "NsData::GetHeaderInfo");
460                     NN_LOG("Content DataType = %d\n",headerInfo);
461                 }
462 
463                 //Check the data
464                 {
465                     NN_LOG("---Dump Data---\n");
466                     u8 dDataBuf[4*1024];
467                     memset(dDataBuf, 0, sizeof(dDataBuf));
468                     size_t readSize = 0;
469                     u32 readCount = 0;
470                     do
471                     {
472                         ++readCount;
473                         NN_LOG("(Read %d)\n", readCount);
474                         readSize = contentData.ReadData(reinterpret_cast<u8*>(dDataBuf), sizeof(dDataBuf));
475                         NN_BOSS_RESULT_HANDLING(result, "NsData::ReadData");
476                         DumpNsdBody(dDataBuf, readSize);
477                     }while(readSize != 0);
478                 }
479 
480                 //Get and set data attribute information
481                 {
482                     u32 setInfo = 0x100;
483                     result = contentData.SetAdditionalInfo(setInfo);
484                     NN_BOSS_RESULT_HANDLING(result, "NsData::SetAdditionalInfo");
485 
486                     u32 getInfo;
487                     result = contentData.GetAdditionalInfo(&getInfo);
488                     NN_BOSS_RESULT_HANDLING(result, "NsData::SetAdditionalInfo");
489                     NN_LOG("[BOSS Sample]AdditionalInfo = %d(Set Info = %d)\n", setInfo, setInfo);
490                 }
491 
492                 /*
493                  * You can also use the data's already-read flag.
494                  * 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.
495                  * The flag is off when data is downloaded and stays off as long as the application does not explicitly turn it on.
496                  * Use this when the application manages unprocessed data.
497                  * 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.
498                  */
499                 {
500                     bool nsdReadFlag = true;
501                     result = contentData.GetReadFlag(&nsdReadFlag);
502                     NN_BOSS_RESULT_HANDLING(result, "NsData::GetReadFlag");
503                     NN_LOG("[BOSS Sample]NSD Read Flag = %d\n", nsdReadFlag);
504 
505                     if(nsdReadFlag == false)
506                     {
507                         //Turn the flag on. (The data's already-read flag does not change unless the application sets it.)
508                         result = contentData.SetReadFlag(true);
509                         NN_BOSS_RESULT_HANDLING(result, "NsData::SetNewFrag");
510                     }
511                 }
512 
513                 //Delete data
514                 /*
515                 {
516                     result = contentData.Delete();
517                     NN_BOSS_RESULT_HANDLING(result, "NsData::Delete");
518                 }
519                 */
520             }
521         }while(getNsDataIdListResult == nn::boss::ResultNsDataListSizeShortage());//If this is ResultNsDataListSizeShortage, run GetNsDataIdList again because all of the IDs have still not been obtained.
522 
523         //After you get a list of data IDs, the application's "update indicator flag" is off.
524         {
525             bool arriveFlag = false;
526             result = nn::boss::GetNewArrivalFlag(&arriveFlag);
527             NN_BOSS_RESULT_HANDLING(result, "GetNewArrivalFlag");
528             NN_LOG("[BOSS Sample](After Read Data, NewArrivalFlag is %d.)\n", arriveFlag);
529         }
530 
531         //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).
532         {
533             result = nn::boss::GetNewDataNsDataIdList(nn::boss::DATA_TYPE_ALL, &serialIdList);
534             NN_BOSS_RESULT_HANDLING(result, "GetNewDataNsDataIdList");
535             NN_LOG("[BOSS Sample](After NSD read flag on, New NSD number is = %d)\n", serialIdList.GetSize());
536         }
537 
538     }
539 }
540 
541 
nnMain()542 extern "C" void nnMain()
543 {
544     // Call only the nn::applet::Enable function to also allow execution from the HOME Menu
545     // HOME Menu transitions, POWER Button presses, and sleep are all unsupported
546     nn::applet::Enable();
547 
548     /* =======================================================================
549            Pre-processing
550                  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.
551        ======================================================================== */
552     //Initialize the BOSS library
553     {
554         nn::Result result = nn::boss::Initialize();
555         NN_BOSS_RESULT_HANDLING(result, "boss::Initialize");
556     }
557 
558     //Change settings so that the application runs BOSS in the background when we are not sleeping
559     {
560         // Initialize the NDM library
561         nn::Result result = nn::ndm::Initialize();
562         NN_BOSS_RESULT_HANDLING(result, "ndm::Initialize()");
563 
564         // Resume BOSS
565         result = nn::ndm::Resume(nn::ndm::DN_BOSS);
566         NN_BOSS_RESULT_HANDLING(result, "ndm::Resume()");
567     }
568 
569     // Create the expanded save data
570     // For DataStore upload tasks the data for uploading is saved to BOSS storage in the expanded save data region.
571     // For DataStore download tasks, the downloaded data is saved to BOSS storage in the expanded save data region.
572     // You thus need an expanded save data region in order to use DataStore tasks.
573     {
574         nn::fs::Initialize();
575         {
576             nn::Result result = nn::fs::MountExtSaveData(extSaveDataMountName, APP_EXT_STORAGE_ID);
577             if(result.IsFailure())
578             {
579                 if(nn::fs::ResultNotFormatted::Includes(result)        ||
580                    nn::fs::ResultBadFormat::Includes(result)           ||
581                    nn::fs::ResultVerificationFailed::Includes(result))
582                 {
583                     nn::fs::DeleteExtSaveData(APP_EXT_STORAGE_ID);
584                 }
585 
586                 if(nn::fs::ResultNotFound::Includes(result)             ||
587                    nn::fs::ResultNotFormatted::Includes(result)         ||
588                    nn::fs::ResultBadFormat::Includes(result)            ||
589                    nn::fs::ResultVerificationFailed::Includes(result))
590                 {
591                     NN_LOG("Create ExtSaveData(ID=%lx)\n", APP_EXT_STORAGE_ID);
592                     //When you create expanded save data, configure the maximum file count so that it also accounts for the number of files downloaded through BOSS.
593                     //Once the maximum number of files already exist in expanded save data, it will no longer be possible to write files downloaded through BOSS.
594 
595                     //In SDK 0.14, icon data of some kind must be specified to fs::CreateExtSaveData.
596                     u8 iconData[] = {0x01};
597                     result = nn::fs::CreateExtSaveData(APP_EXT_STORAGE_ID, iconData, sizeof(iconData), 10, 100);
598                     if(result.IsFailure())
599                     {
600                         NN_LOG_ERROR("CreateExtSaveData failed(%08x)\n", result.GetPrintableBits());
601                         NN_UTIL_PANIC_IF_FAILED(result);
602                     }
603                     result = nn::fs::MountExtSaveData(extSaveDataMountName, APP_EXT_STORAGE_ID);
604                     NN_UTIL_PANIC_IF_FAILED(result);
605                 }
606                 else
607                 {
608                     NN_LOG_ERROR("MountExtSaveData failed(%08x)\n", result.GetPrintableBits());
609                     NN_UTIL_PANIC_IF_FAILED(result);
610                 }
611             }
612         }
613     }
614 
615     NN_LOG("Initializing network.\n");
616 
617     // Connect to the network.
618     {
619         nn::Result result = InitializeNetwork();
620         NN_UTIL_PANIC_IF_FAILED(result);
621     }
622 
623 
624     NN_LOG("BOSS Sample(DataStoreTask) Start\n");
625 
626     // Register and execute the DataStore upload task.
627     sampleDataStoreUploadTask();
628 
629     // If you do not wait for at least around 20 seconds after uploading to DataStore you cannot download the data.
630     // To play it safe, wait for around 25 seconds.
631     NN_LOG("Wait for 25 sec...\n");
632     nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromSeconds(25));
633 
634     // Register and execute the DataStore download task.
635     sampleDataStoreDownloadTask();
636 
637     NN_LOG("BOSS Sample(DataStoreTask) END\n");
638 
639 
640     /* =======================================================================
641            Clean up
642                  Note: Network termination processing and other processing. If BOSS will still be used later, this does not need to be run.
643        ======================================================================== */
644     NN_LOG("Finalizing network.\n");
645     {
646         nn::Result result = FinalizeNetwork();
647         NN_UTIL_PANIC_IF_FAILED(result);
648     }
649 
650     //Unregister BOSS storage if BOSS features will not be used later.
651     //(Note that once it is unregistered, you will no longer be able to run NADL tasks or read data from BOSS storage.)
652     /*
653     {
654         nn::Result result = nn::boss::UnregisterStorage();
655         NN_UTIL_PANIC_IF_FAILED(result);
656     }
657     */
658 
659     //Finalize the NDM library
660     {
661         nn::Result result = nn::ndm::Finalize();
662         NN_BOSS_RESULT_HANDLING(result, "ndm::Finalize()");
663         //When the program exits, pause/resume settings are also restored
664     }
665 
666     //Shut down the library
667     {
668         nn::Result result = nn::boss::Finalize();
669         NN_UTIL_PANIC_IF_FAILED(result);
670     }
671 
672     NN_LOG("END\n");
673 }
674 
675 
676