1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     cec_Main.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: 47558 $
14  *---------------------------------------------------------------------------*/
15 
16 #include <nn.h>
17 #include <nn/fs.h>
18 #include <nn/os.h>
19 #include <nn/dbg.h>
20 #include <ctype.h>
21 
22 #include <nn/cec.h>
23 #include <nn/ndm.h>
24 #include <nn/applet.h>
25 #include "message.h"
26 #include "sleep.h"
27 #include "demo.h"
28 
29 using namespace nn;
30 using namespace nn::fnd;
31 using namespace nn::os;
32 
33 static bool  threadAlive = false;
34 static Event recvEvent;
35 
36 const u32       FONT_SIZE   =  8;    // Font size
37 const f32       LINE_WIDTH  =  0.f;  // Line width
38 
39 // Render color definition
40 #define WHITE_COLOR     1.f, 1.f, 1.f
41 #define RED_COLOR       1.f, 0.f, 0.f
42 #define GREEN_COLOR     0.f, 1.f, 0.f
43 
44 #define INBOX_MAXNUM            25
45 #define OUTBOX_MAXNUM            1
46 #define BOTTOM_DISP             29
47 
48 #define TITLE_LENGTH            26
49 #define BODY_LENGTH           1024
50 
51 NN_DEFINE_RESULT_CONST(ResultApplicationSuccess,\
52                    nn::Result::LEVEL_SUCCESS,\
53                    nn::Result::SUMMARY_SUCCESS,\
54                    nn::Result::MODULE_APPLICATION,\
55                    nn::Result::DESCRIPTION_SUCCESS);
56 
57 enum MODE
58 {
59     CREATE_MODE = 0,
60     WRITE_MODE,
61     READ_MODE
62 };
63 
64 enum DISP
65 {
66     NON_DISP = 0,
67     CREATE_DISP,
68     WRITE_DISP,
69     READ_DISP
70 };
71 
72 struct Data
73 {
74     nn::fnd::DateTimeParameters date;
75     wchar_t   title[TITLE_LENGTH];
76     wchar_t   body[BODY_LENGTH];
77 };
78 
79 struct BoxBuffer
80 {
81     s32                 inMessNum;
82     u32                 outMessNum;
83     struct Data         inMessData[INBOX_MAXNUM];
84     struct Data         outMessData[OUTBOX_MAXNUM];
85     s32                 sentCount;
86 };
87 
88 static enum MODE s_Mode;
89 static enum DISP s_Disp;
90 static BoxBuffer s_Box = {0};
91 
92 static nn::os::CriticalSection s_CriticalSection;
93 
94 //Settings for MessageBox information
95 static u32     TITLEID = nn::cec::MakeCecTitleId(0x00026);     // StreetPass ID (a fixed value)
96 static u32     PRIVATEID = 0xabababab;   // Card-specific ID (a different value for each card and for each instance of save data)
97 static char    HMACKEY[] = "12345678901234567890123456789012";
98 
99 //Settings for Message information
100 static wchar_t s_Title[TITLE_LENGTH] = TITLE_0;
101 static wchar_t s_Body[BODY_LENGTH]   = BODY_0;
102 static const wchar_t *s_ContentTitle[CONTENT_NUM] = {
103                             TITLE_0, TITLE_1, TITLE_2, TITLE_3, TITLE_4,
104                             TITLE_5, TITLE_6, TITLE_7, TITLE_8, TITLE_9};
105 static const wchar_t *s_ContentBody[CONTENT_NUM] = {
106                             BODY_0, BODY_1, BODY_2, BODY_3, BODY_4,
107                             BODY_5, BODY_6, BODY_7, BODY_8, BODY_9};
108 
109 //------------------------------------------------------------
110 // Allocator used by the CEC library
111 class CecTestAllocator : public nn::fnd::IAllocator
112 {
113 private:
114     nn::fnd::ExpHeap expHeap;
115     bool            isInitialized;
116     NN_PADDING3;
117 
118 public:
CecTestAllocator()119     CecTestAllocator(){isInitialized = false;};
~CecTestAllocator()120     virtual ~CecTestAllocator(){};
121 
InitExpHeap()122     void InitExpHeap()
123     {
124         if(isInitialized == false)
125         {
126             expHeap.Initialize(
127                         nn::os::GetDeviceMemoryAddress(),
128                         1024 * 1024 );
129             isInitialized = true;
130         }
131     }
132 
133     /* Please see man pages for details
134 
135     */
Allocate(size_t size,s32 alignment=4)136     virtual void* Allocate(size_t size, s32 alignment = 4)
137     {
138         if(isInitialized == true)
139         {
140             return expHeap.Allocate(size, alignment);
141         }
142         return NULL;
143     }
144 
145     /* Please see man pages for details
146 
147     */
Free(void * p)148     virtual void Free(void* p)
149     {
150         if(isInitialized == true)
151         {
152             expHeap.Free(p);
153         }
154     }
155 
156 };
157 
158 CecTestAllocator allocator;
159 
SaveSendMessage(nn::cec::MessageBox * pCecMessBox)160 nn::Result SaveSendMessage(nn::cec::MessageBox* pCecMessBox)
161 {
162     // Try to open a box
163     nn::Result result = pCecMessBox->OpenMessageBox(TITLEID, PRIVATEID);
164     if(result.IsFailure()) return result;
165 
166     // Save messages to the buffer from which they will be sent
167     s_Box.outMessNum= pCecMessBox->GetBoxMessageNum(nn::cec::CEC_BOXTYPE_OUTBOX);
168     for(int i=0; i<s_Box.outMessNum; i++)
169     {
170         u8 *buf;
171         nn::cec::MessageId messId;
172         nn::cec::Message cecMess;
173 
174         // Get the message ID
175         pCecMessBox->GetMessageId(&messId, nn::cec::CEC_BOXTYPE_OUTBOX, i);
176 
177         // Get the size
178         size_t messSize = pCecMessBox->GetMessageSize(nn::cec::CEC_BOXTYPE_OUTBOX, i);
179 
180         // Allocate a buffer for reading data
181         buf = reinterpret_cast<u8*>(allocator.Allocate(messSize));
182         if(buf==NULL) NN_LOG("Malloc Error\n");
183 
184         // Read the message
185         result = pCecMessBox->ReadMessage(cecMess, buf, messSize, nn::cec::CEC_BOXTYPE_OUTBOX, messId);
186         if(result.IsFailure())
187         {
188             NN_LOG("### [Message %d]ReadMessage Error\n", i-1);
189             allocator.Free(buf);
190             continue;
191         }
192 
193         //InfoText
194         const wchar_t*    pInfoText;
195         size_t      infoTextLen;
196         if(cecMess.GetInfoText(&pInfoText, &infoTextLen).IsSuccess())
197         {
198             std::memset(s_Box.outMessData[i].title,0,TITLE_LENGTH*sizeof(wchar_t));
199             std::memcpy(&s_Box.outMessData[i].title, pInfoText, infoTextLen);
200         }
201 
202         //body
203         void*       pBody;
204         size_t      bodyLen;
205         cecMess.GetMessageBodyPointer(&pBody,&bodyLen);
206         std::memcpy(&s_Box.outMessData[i].body, pBody, bodyLen);
207 
208         //CreateDate
209         s_Box.outMessData[i].date = cecMess.GetCreateDate();
210 
211         allocator.Free(buf);
212     }
213     pCecMessBox->CloseMessageBox();
214     return result;
215 }
216 
CreateBox(nn::cec::MessageBox & cecMessBox)217 nn::Result CreateBox(nn::cec::MessageBox& cecMessBox)
218 {
219     nn::Result  result;
220     /* --------------------------------------------------------
221       Create a box
222       --------------------------------------------------------*/
223     // Try to open a box
224     result = cecMessBox.OpenMessageBox(TITLEID, PRIVATEID);
225     if(result.IsFailure())
226     {
227         if(result != nn::cec::MakeResultNoData())return result;
228 
229         // Set the BoxName
230         wchar_t boxName[64] = L"CEC_DEMO_BOX"; //Box Name
231 
232         // Set the icon
233         u8 boxIcon[48*48*2];
234         std::memset(boxIcon, 0x55, sizeof(boxIcon));//Fill it in randomly
235 
236         //Create a box
237         result = cecMessBox.CreateMessageBox(
238                                     TITLEID,\
239                                     PRIVATEID,\
240                                     HMACKEY,\
241                                     boxIcon,\
242                                     sizeof(boxIcon),\
243                                     boxName,\
244                                     sizeof(boxName),\
245                                     nn::cec::CEC_INBOX_SIZE_DEFAULT,\
246                                     nn::cec::CEC_OUTBOX_SIZE_DEFAULT,\
247                                     INBOX_MAXNUM,\
248                                     OUTBOX_MAXNUM,\
249                                     nn::cec::CEC_MESSSIZEMAX_DEFAULT);
250     }
251     cecMessBox.CloseMessageBox();
252     return result;
253 }
254 
DeleteBox(nn::cec::MessageBox * pCecMessBox)255 void DeleteBox(nn::cec::MessageBox* pCecMessBox)
256 {
257     // Try to open a box
258     if(pCecMessBox->OpenMessageBox(TITLEID, PRIVATEID).IsSuccess())
259     {
260         pCecMessBox->DeleteMessageBox();
261     }
262     else
263     {
264         pCecMessBox->DeleteMessageBox(TITLEID);
265     }
266 }
267 
DeleteMessage(nn::cec::MessageBox * pCecMessBox,nn::cec::CecBoxType cecBoxType)268 nn::Result DeleteMessage(nn::cec::MessageBox* pCecMessBox, nn::cec::CecBoxType cecBoxType)
269 {
270     nn::Result result = pCecMessBox->OpenMessageBox(TITLEID, PRIVATEID);
271     if(result.IsFailure())return result;
272     //Delete messages
273     result=pCecMessBox->DeleteAllMessages(cecBoxType);
274     if(cecBoxType == nn::cec::CEC_BOXTYPE_INBOX)
275     {
276         //Delete the inbox buffer
277         std::memset(&s_Box,0,sizeof(s_Box));
278     }
279     pCecMessBox->CloseMessageBox();
280     return result;
281 }
282 
SetSendMessage(nn::cec::MessageBox * pCecMessBox)283 nn::Result SetSendMessage(nn::cec::MessageBox* pCecMessBox)
284 {
285     nn::Result         result;
286     nn::cec::Message   newMess;
287     u8                 icon[40*40*2] = {0};
288     nn::cec::MessageId mssid;
289 
290     // Try to open a box
291     result = pCecMessBox->OpenMessageBox(TITLEID, PRIVATEID);
292     if(result.IsFailure()) return result;
293 
294     /* --------------------------------------------------------
295       Create a message
296       --------------------------------------------------------*/
297     newMess.NewMessage( TITLEID,                                 // CecTitleId
298                         0,                                       // Group ID
299                         nn::cec::MESSAGE_TYPEFLAG_NON_FRIEND\
300                         |nn::cec::MESSAGE_TYPEFLAG_FRIEND,       // Message Type Flag
301                         nn::cec::CEC_SENDMODE_SENDRECV,          // Send Mode
302                         nn::cec::MESSAGE_SENDCOUNT_ONCE,         // Send Count
303                         nn::cec::MESSAGE_PROPAGATIONCOUNT_ONCE   // Propagation Count
304                         );
305 
306     // Set the data itself (the body)
307     newMess.SetMessageBody(s_Body, sizeof(s_Body));
308 
309     // Set the icon
310     std::memset(icon, 0x55, sizeof(icon));//Fill it in randomly
311     newMess.SetIcon(icon, sizeof(icon));
312 
313     // Set the InfoText
314     newMess.SetInfoText(s_Title, sizeof(s_Title));
315 
316     // Save the message in the outbox
317     result = pCecMessBox->WriteMessage(newMess, nn::cec::CEC_BOXTYPE_OUTBOX, mssid);
318     if(result.IsFailure())
319     {
320         //The messages in the box have exceeded the maximum value
321         if(result.GetDescription() == nn::cec::DESCRIPTION_BOXMESSNUM_FULL)
322         {
323             //Delete all messages to be sent
324             pCecMessBox->DeleteAllMessages(nn::cec::CEC_BOXTYPE_OUTBOX);
325         }
326         pCecMessBox->CloseMessageBox();
327         return result;
328     }
329     pCecMessBox->CloseMessageBox();
330 
331     //Save messages to the buffer from which they will be sent
332     return SaveSendMessage(pCecMessBox);
333 }
334 
ReceiveEventThread(u32 * pIsExistNewMess)335 static void ReceiveEventThread(u32* pIsExistNewMess)
336 {
337     nn::cec::CecNotificationData notificationData;
338     nn::cec::CecControl::GetCecRecvEventHandle(&recvEvent);
339 
340     do{
341         recvEvent.Wait();
342 
343         NN_LOG("============= Receive Check!!!!!! ============= \n");
344 
345         // Get information received from the daemon
346         nn::cec::CTR::CecControl::GetCecInfoBuffer(TITLEID, reinterpret_cast<u8*>(&notificationData), sizeof(notificationData));
347         NN_LOG(" Recv Count:[%d] Num:[%d] \n", notificationData.count, notificationData.num);
348 
349         // Determine whether information has been received
350         if(notificationData.num > 0)
351         {
352             nn::os::CriticalSection::ScopedLock locker(s_CriticalSection);
353             nn::cec::MessageBox messBox;
354 
355             // Open the box
356             nn::cec::CecControl::StopScanning();
357             nn::Result result = messBox.OpenMessageBox(TITLEID, PRIVATEID);
358             if(result.IsFailure())
359             {
360                 NN_LOG(" ### Cannot Open Box(Deleted?) [0x%08x/0x%08x]\n", TITLEID, PRIVATEID);
361                 NN_DBG_PRINT_RESULT(result);
362                 continue;
363             }
364 
365             u32 inboxMessNum = messBox.GetBoxMessageNum(nn::cec::CEC_BOXTYPE_INBOX);
366             if(inboxMessNum == 0)
367             {
368                 NN_LOG(" ### No Message(Deleted?) [0x%08x/0x%08x]\n", TITLEID, PRIVATEID);
369                 messBox.CloseMessageBox();
370                 continue;
371             }
372 
373             nn::cec::MessageId recvMessId;
374 
375             // Determine whether the inbox has messages with the MessageId values that were received
376             NN_LOG("%s(%d): notificationData check\n",__FUNCTION__,__LINE__);
377             for(int i=0; i < notificationData.num; i++)
378             {
379                 // Determine whether there is data in the inbox
380                 nn::cec::MessageId inboxMessId;
381                 for(int j=0; j<inboxMessNum; j++)
382                 {
383                     messBox.GetMessageId(&inboxMessId, nn::cec::CEC_BOXTYPE_INBOX, j);
384                     if(inboxMessId.IsEqual(notificationData.param[i].messageId))
385                     {
386                         NN_LOG(" <<< New Message >>>\n");   // There is a message in the inbox
387 
388                         //Set a flag indicating that there is a new message
389                         *pIsExistNewMess = 1;
390                         break;
391                     }
392                 }
393 
394             }
395             messBox.CloseMessageBox();
396 
397         }
398         NN_LOG("=============================================== \n");
399         /*
400             With StreetPass, to use StreetPass again with a partner with whom you have used StreetPass before, takes an average of 4 hours (maximum of 8 hours).
401             To restart communications in a short time interval, you must enable StartScanning(true).
402             To enable the StartScanning(true) included in this demo program, use the executable file that is used for the Development build.
403 
404         */
405         nn::cec::CecControl::StartScanning(true);
406     }while(threadAlive);
407     recvEvent.Finalize();
408 }
409 
410 // Update the box
UpdateCreation(nn::hid::PadStatus * pPadStatus,nn::cec::MessageBox * pCecMessBox)411 nn::Result UpdateCreation(nn::hid::PadStatus* pPadStatus, nn::cec::MessageBox* pCecMessBox)
412 {
413     nn::Result result;
414     if(pPadStatus->trigger & nn::hid::BUTTON_A)
415     {
416         // Create a box
417         result = CreateBox(*pCecMessBox);
418         if(result.IsSuccess())
419         {
420             //Enter a mode in which outgoing messages are set
421             s_Mode = WRITE_MODE;
422             s_Disp = WRITE_DISP;
423         }
424         else
425         {
426             //Display error information
427             s_Disp = CREATE_DISP;
428         }
429         return result;
430     }
431     return ResultApplicationSuccess();
432 }
433 
434 // Display box information
DisplayCreation(demo::RenderSystemDrawing * pRenderSystem,nn::Result * result)435 void DisplayCreation(demo::RenderSystemDrawing* pRenderSystem, nn::Result* result)
436 {
437     u16 x,y;
438 
439     //Display a message prompting the user to create a box
440     x = 0; y = 0;
441     pRenderSystem->SetRenderTarget(NN_GX_DISPLAY0);
442     pRenderSystem->Clear();
443     pRenderSystem->SetColor(WHITE_COLOR);
444     pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "PRESS A TO CREATE MESSAGEBOX");
445 
446     //Display error information
447     if(result->IsFailure())
448     {
449         pRenderSystem->SetColor(RED_COLOR);
450         if(*result == nn::cec::MakeResultBoxNumFull())   //The maximum box count has been reached
451         {
452             pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "ERROR:MESSAGEBOX NUM IS FULL");
453         }
454         else if(*result == nn::cec::MakeResultNotAuthorized())   //The private IDs do not match
455         {
456             pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "ERROR:PRIVATEID DO NOT MATCH");
457         }
458         else if(result->GetDescription() == Result::DESCRIPTION_BUSY)
459         {
460             pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "ERROR:BUSY");
461         }
462         else
463         {
464             pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "ERROR:");
465         }
466     }
467     pRenderSystem->SwapBuffers();
468 
469     //Display controls
470     x = 0; y = BOTTOM_DISP;
471     pRenderSystem->SetRenderTarget(NN_GX_DISPLAY1);
472     pRenderSystem->Clear();
473     pRenderSystem->SetColor(WHITE_COLOR);
474     pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y, "A:CREATE_BOX");
475     pRenderSystem->SwapBuffers();
476 }
477 
478 // Set messages to be sent
UpdateWriting(nn::hid::PadStatus * pPadStatus,nn::cec::MessageBox * pCecMessBox,u8 * pSelected)479 nn::Result UpdateWriting(nn::hid::PadStatus* pPadStatus, nn::cec::MessageBox* pCecMessBox, u8* pSelected)
480 {
481     nn::Result result;
482     if(pPadStatus->trigger & nn::hid::BUTTON_A)
483     {
484         // Create a message to send
485         result = SetSendMessage(pCecMessBox);
486         if(result.IsSuccess())
487         {
488             //Enter a mode in which outgoing messages are set
489             s_Mode = READ_MODE;
490             s_Disp = READ_DISP;
491         }
492         else
493         {
494             //Display error information
495             s_Disp = WRITE_DISP;
496         }
497         return result;
498     }
499     //Return to the previous screen
500     if(pPadStatus->trigger & nn::hid::BUTTON_B)
501     {
502         DeleteBox(pCecMessBox);
503         s_Mode = CREATE_MODE;
504         s_Disp = CREATE_DISP;
505     }
506     //Choose the message content
507     if(pPadStatus->trigger & nn::hid::BUTTON_RIGHT)
508     {
509         if(*pSelected<CONTENT_NUM-1)
510             *pSelected = *pSelected + 1;
511     }
512     if(pPadStatus->trigger & nn::hid::BUTTON_LEFT)
513     {
514         if(0<*pSelected)
515             *pSelected = *pSelected - 1;
516     }
517     if(pPadStatus->trigger & nn::hid::BUTTON_RIGHT\
518      ||pPadStatus->trigger & nn::hid::BUTTON_LEFT)
519     {
520         //Set the content of the message to be sent
521         std::memcpy(&s_Title,s_ContentTitle[*pSelected],sizeof(s_Title));
522         std::memcpy(&s_Body,s_ContentBody[*pSelected], sizeof(s_Body));
523         s_Disp = WRITE_DISP;
524     }
525     return ResultApplicationSuccess();
526 }
527 
528 // Display a screen for setting the message to be sent
DisplayWriting(demo::RenderSystemDrawing * pRenderSystem,nn::Result * result)529 void DisplayWriting(demo::RenderSystemDrawing* pRenderSystem, nn::Result* result)
530 {
531     u16 x;
532     u16 y;
533 
534     x = 0; y = 0;
535     pRenderSystem->SetRenderTarget(NN_GX_DISPLAY0);
536     pRenderSystem->Clear();
537     pRenderSystem->SetColor(WHITE_COLOR);
538     pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "AT FIRST,  PRESS LEFT/RIGHT TO SELECT CONTENT");
539     pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "AT SECOND, PRESS A TO SET SEND MESSAGE");
540 
541     if(result->IsFailure())   //Display error information
542     {
543         pRenderSystem->SetColor(RED_COLOR);
544         if(result->GetDescription() == nn::cec::DESCRIPTION_NOT_AGREE_EULA)
545         {
546             pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "ERROR:YOU DID NOT AGREE EULA");
547         }
548         if(result->GetDescription() == nn::cec::DESCRIPTION_PARENTAL_CONTROL_CEC)
549         {
550             pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "ERROR:PARENTAL CONTROL BANED");
551         }
552         if(result->GetDescription() == nn::cec::DESCRIPTION_BOXMESSNUM_FULL)
553         {
554             pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "ERROR:SEND MESSAGENUM IS FULL");
555             pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "APPLICATION DELETED ALL OUTBOX MESSAGE");
556         }
557     }
558 
559     pRenderSystem->SwapBuffers();
560 
561     //Display the message box
562     x = 0; y = 0;
563     pRenderSystem->SetRenderTarget(NN_GX_DISPLAY1);
564     pRenderSystem->Clear();
565     pRenderSystem->SetColor(WHITE_COLOR);
566     pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "TITLE:%ls",s_Title);
567     pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "------------------BODY------------------");
568     wchar_t *line, *ptr, body[BODY_LENGTH];
569     std::memcpy(body,s_Body,sizeof(s_Body));
570     line=std::wcstok(body,L"\n",&ptr);
571     while(line!=NULL)
572     {
573         pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "%ls",line);
574         line=std::wcstok(NULL,L"\n",&ptr);
575     }
576     pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * (BOTTOM_DISP-1), "A:SET_MESSAGE B:DELETE_BOX");
577     pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * BOTTOM_DISP, "LEFT/RIGHT:SELECT_CONTENT");
578     pRenderSystem->SwapBuffers();
579 }
580 
GetRecvMessage(nn::cec::MessageBox * pCecMessBox)581 nn::Result GetRecvMessage(nn::cec::MessageBox* pCecMessBox){
582     nn::Result result ;
583     // Try to open a box
584     result = pCecMessBox->OpenMessageBox(TITLEID, PRIVATEID);
585     if(result.IsFailure()) return result;
586 
587     //----------------------------------
588     {
589         // Get the number of messages in the inbox
590         nn::cec::MessageId messId[INBOX_MAXNUM];  // Message ID
591 
592         u32 messNum = pCecMessBox->GetBoxMessageNum(nn::cec::CEC_BOXTYPE_INBOX);
593 
594         // Create a list of message IDs for the inbox
595         for(u32 i=messNum; i!=0; i--)
596         {
597             pCecMessBox->GetMessageId(&messId[i-1], nn::cec::CEC_BOXTYPE_INBOX, i-1);
598         }
599 
600         // Read messages from the inbox
601         for(u32 i=messNum; i!=0; i--)
602         {
603             u8 *buf;
604             nn::cec::Message cecMess;
605 
606             // Get the size
607             size_t messSize = pCecMessBox->GetMessageSize(nn::cec::CEC_BOXTYPE_INBOX, i-1);
608 
609             // Allocate a buffer for reading data
610             buf = reinterpret_cast<u8*>(allocator.Allocate(messSize));
611             if(buf==NULL) NN_LOG("Malloc Error\n");
612 
613             // Read the message
614             result = pCecMessBox->ReadMessage(cecMess, buf, messSize, nn::cec::CEC_BOXTYPE_INBOX, messId[i-1]);
615             if(result.IsFailure())
616             {
617                 NN_LOG("### [Message %d]ReadMessage Error\n", i-1);
618                 allocator.Free(buf);
619                 continue;
620             }
621 
622             //------------------------------
623             //Save all kinds of messages in the application
624             if(s_Box.inMessNum < INBOX_MAXNUM)
625             {
626                 //InfoText
627                 const wchar_t*    pInfoText;
628                 size_t      infoTextLen;
629                 if(cecMess.GetInfoText(&pInfoText, &infoTextLen).IsSuccess())
630                 {
631                     std::memset(s_Box.inMessData[s_Box.inMessNum].title,0,TITLE_LENGTH*sizeof(wchar_t));
632                     std::memcpy(&s_Box.inMessData[s_Box.inMessNum].title, pInfoText, infoTextLen);
633                 }
634 
635                 //body
636                 void*       pBody;
637                 size_t      bodyLen;
638                 cecMess.GetMessageBodyPointer(&pBody,&bodyLen);
639                 std::memcpy(&s_Box.inMessData[s_Box.inMessNum].body, pBody, bodyLen);
640 
641                 //ReceiveData
642                 s_Box.inMessData[s_Box.inMessNum].date = cecMess.GetRecvDate();
643 
644                 s_Box.inMessNum++;
645             }
646             else
647             {
648                 // Exceeded the number of messages that can be saved
649                 NN_LOG("### MailBox Full.\n");
650             }
651             allocator.Free(buf);
652         }
653         pCecMessBox->DeleteAllMessages(nn::cec::CEC_BOXTYPE_INBOX);
654         NN_LOG("Delete Inbox Messages.\n");
655     }
656 
657     //----------------------------------
658     {
659         // Get the number of messages in the outbox
660         s_Box.outMessNum = pCecMessBox->GetBoxMessageNum(nn::cec::CEC_BOXTYPE_OUTBOX);
661         nn::cec::MessageId messId[OUTBOX_MAXNUM]; // Message SID
662         u32 sendCount[OUTBOX_MAXNUM];             // Send Count
663 
664         // Create a list of message IDs for the outbox
665         for(u32 i=0; i<s_Box.outMessNum; i++)
666         {
667             pCecMessBox->GetMessageId(&messId[i], nn::cec::CEC_BOXTYPE_OUTBOX, i);
668             sendCount[i] = pCecMessBox->GetMessageSendCount(nn::cec::CEC_BOXTYPE_OUTBOX, i);
669         }
670 
671         // Delete messages that have already been sent
672         for(u32 i=0; i<s_Box.outMessNum; i++)
673         {
674             if(sendCount[i] == 0)
675             {
676                 s_Box.sentCount++;    // Send count
677                 s_Box.outMessNum--;
678                 pCecMessBox->DeleteMessage(nn::cec::CEC_BOXTYPE_OUTBOX, messId[i]);
679                 NN_LOG("Delete Sent Message [%s].\n",nn::cec::MessageId(messId[i]).GetString());
680             }
681         }
682     }
683     pCecMessBox->CloseMessageBox();
684     //Save messages to the buffer from which they will be sent
685     return SaveSendMessage(pCecMessBox);
686 }
687 
688 //Update messages
UpdateReading(nn::hid::PadStatus * pPadStatus,nn::cec::MessageBox * pCecMessBox,u8 * pSelected,u32 * pIsExistNewMess)689 void UpdateReading(nn::hid::PadStatus* pPadStatus, nn::cec::MessageBox* pCecMessBox, u8* pSelected, u32* pIsExistNewMess)
690 {
691     nn::Result result;
692     //Delete all messages that have been received
693     if(pPadStatus->trigger & nn::hid::BUTTON_X)
694     {
695         DeleteMessage(pCecMessBox, nn::cec::CEC_BOXTYPE_INBOX);
696     }
697     //Get messages that have been received
698     if(pPadStatus->trigger & nn::hid::BUTTON_Y)
699     {
700         GetRecvMessage(pCecMessBox);
701         *pIsExistNewMess = 0;
702     }
703     //Go to the previous screen
704     if(pPadStatus->trigger & nn::hid::BUTTON_B)
705     {
706         DeleteMessage(pCecMessBox, nn::cec::CEC_BOXTYPE_OUTBOX);
707         s_Mode = WRITE_MODE;
708         s_Disp = WRITE_DISP;
709     }
710     //Select a received message
711     if(pPadStatus->trigger & nn::hid::BUTTON_UP)
712     {
713         if(0<*pSelected)*pSelected = *pSelected - 1;
714     }
715     if(pPadStatus->trigger & nn::hid::BUTTON_DOWN)
716     {
717         if(*pSelected<s_Box.inMessNum-1)*pSelected = *pSelected + 1;
718     }
719     //Update the screen if there was a valid key event
720     if(pPadStatus->trigger & nn::hid::BUTTON_X\
721      ||pPadStatus->trigger & nn::hid::BUTTON_Y\
722      ||pPadStatus->trigger & nn::hid::BUTTON_UP\
723      ||pPadStatus->trigger & nn::hid::BUTTON_DOWN)
724     {
725         s_Disp = READ_DISP;
726     }
727 }
728 
GetWeekString(nn::fnd::Week week)729 char* GetWeekString(nn::fnd::Week week)
730 {
731     switch(week)
732     {
733     case nn::fnd::WEEK_SUNDAY:
734         return "SUN";
735     case nn::fnd::WEEK_MONDAY:
736         return "MON";
737     case nn::fnd::WEEK_TUESDAY:
738         return "TUE";
739     case nn::fnd::WEEK_WEDNESDAY:
740         return "WED";
741     case nn::fnd::WEEK_THURSDAY:
742         return "THR";
743     case nn::fnd::WEEK_FRIDAY:
744         return "FRI";
745     case nn::fnd::WEEK_SATURDAY:
746         return "SAT";
747     }
748     return "NUL";
749 }
750 
751 //Display messages
DisplayReading(demo::RenderSystemDrawing * pRenderSystem,u8 selected,u32 * pIsExistNewMess)752 void DisplayReading(demo::RenderSystemDrawing* pRenderSystem, u8 selected, u32* pIsExistNewMess)
753 {
754     u16 x,y;
755 
756     //------------------------------------------------------------
757     //Display a list of messages
758     //------------------------------------------------------------
759     //Display received messages
760     x = 0; y = 0;
761     pRenderSystem->SetRenderTarget(NN_GX_DISPLAY0);
762     pRenderSystem->Clear();
763     pRenderSystem->SetColor(WHITE_COLOR);
764     pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "---------------------------------------------INBOX");
765     pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "TITLE                      ReceiveDate");
766     for(int i=0; i<s_Box.inMessNum; i++)
767     {
768         if(selected==y-2&&selected<INBOX_MAXNUM)pRenderSystem->SetColor(GREEN_COLOR);
769         else pRenderSystem->SetColor(WHITE_COLOR);
770         pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y, "%ls",s_Box.inMessData[i].title);
771         pRenderSystem->DrawText(FONT_SIZE * (x+TITLE_LENGTH+1), FONT_SIZE * y++, "%04d/%02d/%02d %02d:%02d %02d.%03d",\
772             s_Box.inMessData[i].date.year,s_Box.inMessData[i].date.month,s_Box.inMessData[i].date.day,s_Box.inMessData[i].date.hour,\
773             s_Box.inMessData[i].date.minute,s_Box.inMessData[i].date.second,s_Box.inMessData[i].date.milliSecond);
774     }pRenderSystem->SetColor(WHITE_COLOR);
775 
776     y = INBOX_MAXNUM+2;
777     pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "--------------------------------------------OUTBOX");
778     //Display messages to be sent
779     pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "TITLE                      CreateDate");
780     for(int i=0; i<s_Box.outMessNum; i++)
781     {
782         if(selected==y&&selected>OUTBOX_MAXNUM)pRenderSystem->SetColor(GREEN_COLOR);
783         else pRenderSystem->SetColor(WHITE_COLOR);
784         pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y, "%ls",s_Box.outMessData[i].title);
785         pRenderSystem->DrawText(FONT_SIZE * (x+TITLE_LENGTH+1), FONT_SIZE * y++, "%04d/%02d/%02d %02d:%02d %02d.%03d",\
786             s_Box.outMessData[i].date.year,s_Box.outMessData[i].date.month,s_Box.outMessData[i].date.day,\
787             s_Box.outMessData[i].date.hour,s_Box.outMessData[i].date.minute,s_Box.outMessData[i].date.second,\
788             s_Box.outMessData[i].date.milliSecond);
789     }
790     pRenderSystem->SwapBuffers();
791 
792     //----------------------------------------------------------------
793     //Display the message box
794     x = 0; y = 0;
795     pRenderSystem->SetRenderTarget(NN_GX_DISPLAY1);
796     pRenderSystem->Clear();
797     pRenderSystem->SetColor(WHITE_COLOR);
798     pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "TITLE:%ls",s_Box.inMessData[selected].title);
799     pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "------------------BODY------------------");
800     if(*pIsExistNewMess)   //Display an alert message informing to the user that there is a new message
801     {
802         switch(*pIsExistNewMess%3)
803         {
804         case 0:
805             pRenderSystem->DrawText(FONT_SIZE * 9, FONT_SIZE * 10,"////NEW MESSAGE////");
806             break;
807         case 1:
808             pRenderSystem->DrawText(FONT_SIZE * 9, FONT_SIZE * 10,"----NEW MESSAGE----");
809             break;
810         case 2:
811             pRenderSystem->DrawText(FONT_SIZE * 9, FONT_SIZE * 10,"\\\\\\\\NEW MESSAGE\\\\\\\\");
812             break;
813         }
814         pRenderSystem->DrawText(FONT_SIZE * 6, FONT_SIZE * 11,"PRESS Y TO RECEIVE MESSAGE");
815         *pIsExistNewMess = *pIsExistNewMess+1;
816     }
817     else   //Display the content of the message
818     {
819         wchar_t *line, *ptr, body[BODY_LENGTH];
820         std::memcpy(body,s_Box.inMessData[selected].body,sizeof(body));
821         line=std::wcstok(body,L"\n",&ptr);
822         while(line!=NULL)
823         {
824             pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "%ls",line);
825             line=std::wcstok(NULL,L"\n",&ptr);
826         }
827     }
828     pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * (BOTTOM_DISP-1),"Y:RECEIVE_MESSAGE X:DELETE_MESSAGE");
829     pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * BOTTOM_DISP,    "B:BACK UP/DOWN:SELECT_MESSAGE");
830     pRenderSystem->SwapBuffers();
831 }
832 
nnMain(void)833 extern "C" void nnMain( void )
834 {
835     nn::Result result;
836     nn::os::Thread thread;
837 
838     // Use the FS library to get the EULA version to agree to for the application
839     nn::fs::Initialize();
840 
841     // Initialize hid
842     result = nn::hid::Initialize();
843     NN_UTIL_PANIC_IF_FAILED(result);
844 
845     // Initialize the CriticalSection
846     s_CriticalSection.Initialize();
847 
848     // Initialize applets
849     SleepHandler::Initialize();
850 
851     // Prepare pad
852     nn::hid::PadReader padReader;
853     nn::hid::PadStatus padStatus;
854 
855     // Wait for HID input to stabilize
856     nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromSeconds(1));
857 
858     // Allocator
859     allocator.InitExpHeap();
860 
861     // Access the daemon
862     NN_LOG("Cec: nn::cec::Initialize()\n");
863     result = nn::cec::Initialize(allocator);
864     NN_UTIL_PANIC_IF_FAILED(result);
865 
866     //Flag indicating whether there is a new message
867     u32 isExistNewMess = 0;
868     {
869         threadAlive = true;
870         result = thread.TryStartUsingAutoStack<u32*, u32*>(ReceiveEventThread, &isExistNewMess, 1024);
871         if(result.IsFailure())
872         {
873             NN_LOG("%s(%d):### TryStartUsingAutoStack Failed.\n",__FUNCTION__,__LINE__);
874         }
875     }
876     /*
877         With StreetPass, to use StreetPass again with a partner with whom you have used StreetPass before, takes an average of 4 hours (maximum of 8 hours).
878         To restart communications in a short time interval, you must enable StartScanning(true).
879         To enable the StartScanning(true) included in this demo program, use the executable file that is used for the Development build.
880 
881     */
882     nn::cec::CecControl::StartScanning(true);
883 
884     // Allocate heap
885     nn::fnd::ExpHeap appHeap;
886     appHeap.Initialize(nn::os::GetDeviceMemoryAddress()+(1024*1024), nn::os::GetDeviceMemorySize() );
887 
888     // Prepare RenderSystem
889     uptr heapForGx = reinterpret_cast<uptr> (appHeap.Allocate(0x800000));
890     demo::RenderSystemDrawing renderSystem;
891     renderSystem.Initialize(heapForGx, 0x800000);
892     renderSystem.SetClearColor(NN_GX_DISPLAY_BOTH, 0.f, 0.f, 0.f, 0.f);
893     renderSystem.SetFontSize(FONT_SIZE);
894     renderSystem.SetLineWidth(LINE_WIDTH);
895 
896     nn::cec::MessageBox cecMessBox;
897     u8 selectedInBox    = 0;
898     u8 selectedOutBox   = 0;
899 
900     // Try to open a box
901     result = cecMessBox.OpenMessageBox(TITLEID, PRIVATEID);
902     if(result.IsFailure())
903     {
904         //Go to the StreetPass box creation screen
905         s_Mode = CREATE_MODE;
906         s_Disp = CREATE_DISP;
907     }
908     else
909     {
910         //When there is a message in the inbox
911         if(cecMessBox.GetBoxMessageNum(nn::cec::CEC_BOXTYPE_INBOX))
912         {
913             //Set the new message flag
914             isExistNewMess = 1;
915         }
916         if(cecMessBox.GetBoxMessageNum(nn::cec::CEC_BOXTYPE_OUTBOX)==0)
917         {
918             //Go to the screen that writes messages to send
919             s_Mode = WRITE_MODE;
920             s_Disp = WRITE_DISP;
921         }
922         else
923         {
924             //Save messages to the buffer from which they will be sent
925             SaveSendMessage(&cecMessBox);
926 
927             //Screen that reads received messages
928             s_Mode = READ_MODE;
929             s_Disp = READ_DISP;
930         }
931         cecMessBox.CloseMessageBox();
932     }
933     while(1)
934     {
935         // Get input
936         padReader.ReadLatest(&padStatus);
937         switch(s_Mode){
938         case CREATE_MODE: //StreetPass box creation screen
939             result = UpdateCreation(&padStatus, &cecMessBox);
940             if(s_Disp==CREATE_DISP)
941             {
942                 DisplayCreation(&renderSystem,&result);
943                 s_Disp = NON_DISP;
944             }
945             break;
946 
947         case WRITE_MODE: //Screen that writes messages to send
948             result = UpdateWriting(&padStatus, &cecMessBox, &selectedOutBox);
949             if(s_Disp==WRITE_DISP)
950             {
951                 DisplayWriting(&renderSystem, &result);
952                 s_Disp = NON_DISP;
953             }
954             break;
955 
956         case READ_MODE: //Screen that reads received messages
957             UpdateReading(&padStatus, &cecMessBox, &selectedInBox, &isExistNewMess);
958             if(s_Disp==READ_DISP||(isExistNewMess&&s_Disp==NON_DISP))
959             {
960                 DisplayReading(&renderSystem,selectedInBox, &isExistNewMess);
961                 s_Disp = NON_DISP;
962             }
963             break;
964         }
965 
966         // Determination for System Sleep Mode
967         if ( SleepHandler::IsSleepRequested() )
968         {
969             NN_LOG("<<SLEEP>>\n");
970             s_CriticalSection.Enter();
971             SleepHandler::SleepSystem();
972             s_CriticalSection.Leave();
973             NN_LOG("<<AWAKE>>\n");
974             nn::gx::StartLcdDisplay();
975         }
976         nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromMilliSeconds(100));
977     }
978 }
979