1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - WM - demos - dataShare-Model
3   File:     main.c
4 
5   Copyright 2007-2008 Nintendo. 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   $Date:: 2009-08-28#$
14   $Rev: 11024 $
15   $Author: tominaga_masafumi $
16  *---------------------------------------------------------------------------*/
17 
18 /*
19   dataShare-Model
20 
21   Although this is not in game format, this sample program actively implements the features that are necessary (that seem necessary) for an actual game application.
22 
23   Specific features implemented include those listed below.
24 
25   1) Lobby screen
26   2) Parent selection screen
27   3) Measures against process slow-down (synchronous)
28   4) Error display and return
29 
30 
31   For normal controls, the +Control Pad makes a menu selection, the A Button confirms, and the B Button cancels. For additional debug controls, the L Button and R Button respectively increase and decrease the load count.
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 */
44 
45 #ifdef SDK_TWL
46 #include <twl.h>
47 #else
48 #include <nitro.h>
49 #endif
50 
51 #include <nitro/wm.h>
52 #include <string.h>
53 
54 #include "main.h"
55 #include "font.h"
56 #include "print.h"
57 #include "key.h"
58 #include "graphics.h"
59 #include "wh.h"
60 #include "menu.h"
61 #include "unicode.h"
62 #include "ball.h"
63 
64 /* The GGID used in this demo */
65 #define WH_GGID           SDK_MAKEGGID_SYSTEM(0x13)
66 #define GRAPH_TOTAL_FRAME 60
67 
68 typedef struct GraphData_
69 {
70     u16     level;
71     s16     data;
72 }
73 GraphData;
74 
75 static u16 sForcedChannel = 0;
76 static u16 sTgid = 0;
77 static int sSysMode = 0;
78 static int sSysModeStep = 0;
79 
80 static s32 sFrame = 0;
81 static u8 sSendBuf[512] ATTRIBUTE_ALIGN(32);
82 static u8 sRecvBuf[512] ATTRIBUTE_ALIGN(32);
83 static BOOL sRecvFlag[WM_NUM_MAX_CHILD + 1];
84 static OSOwnerInfo sMyInfo;
85 static MyGameInfo sGameInfo;
86 
87 static WMKeySet sKeySet;
88 
89 static GraphData sGraphData[1 + WM_NUM_MAX_CHILD][GRAPH_TOTAL_FRAME];
90 static PRScreen sInfoScreen ATTRIBUTE_ALIGN(32);
91 static PRScreen sDebugScreen ATTRIBUTE_ALIGN(32);
92 static GXOamAttr oamBak[BALL_PLAYER_MAX];
93 static int sBeaconCount = 0;
94 static int sNeedWait = 0;
95 static BOOL sGraphEnabled = 0;
96 
97 static WMBssDesc sBssDesc[ITEM_NUM_MAX];
98 static Window sRoleMenuWindow;
99 static Window sSelectChannelWindow;
100 static Window sSelectParentWindow;
101 static Window sLobbyWindow;
102 static Window sErrorWindow;
103 static Window sBusyWindow;
104 static Window sOptionWindow;
105 static Window sWaitWindow;
106 
107 extern const unsigned char wlicon_image[];
108 extern const unsigned short wlicon_palette[];
109 
TraceWH(const char * fmt,...)110 static void TraceWH(const char *fmt, ...)
111 {
112     va_list vlist;
113     va_start(vlist, fmt);
114     PR_VPrintString(&sDebugScreen, fmt, vlist);
115     va_end(vlist);
116 }
117 
printString(const char * fmt,...)118 static void printString(const char *fmt, ...)
119 {
120     va_list vlist;
121     va_start(vlist, fmt);
122     PR_VPrintString(&sInfoScreen, fmt, vlist);
123     va_end(vlist);
124 }
125 
isDataReceived(int index)126 BOOL isDataReceived(int index)
127 {
128     return sRecvFlag[index];
129 }
130 
getRecvData(int index)131 ShareData *getRecvData(int index)
132 {
133     return (ShareData *) (&(sRecvBuf[index * sizeof(ShareData)]));
134 }
135 
getSendData(void)136 ShareData *getSendData(void)
137 {
138     return (ShareData *) sSendBuf;
139 }
140 
getInfoScreen(void)141 PRScreen *getInfoScreen(void)
142 {
143     return &sInfoScreen;
144 }
145 
changeSysMode(int s)146 void changeSysMode(int s)
147 {
148     if (sSysMode == s)
149     {
150         return;
151     }
152 
153     OS_Printf("sysmode = %d\n", s);
154     sSysMode = s;
155     sSysModeStep = 0;
156 }
157 
indicateCallback(void * arg)158 static void indicateCallback(void *arg)
159 {
160     WMIndCallback *cb;
161     cb = (WMIndCallback *)arg;
162     if (cb->state == WM_STATECODE_BEACON_RECV)
163     {
164         sBeaconCount = 2;
165     }
166 }
167 
scanCallback(WMBssDesc * bssdesc)168 static void scanCallback(WMBssDesc *bssdesc)
169 {
170     char    buf[ITEM_LENGTH_MAX];
171     u16     nickName[10 + 1];
172     char    sjisNickName[10 * 2 + 1];
173     u8      entryCount;
174     int     i;
175 
176     (void)STD_CopyLString(sjisNickName, "unknown", sizeof(sjisNickName));
177     entryCount = 0;
178 
179     // Check whether the userGameInfo is an expected value
180     if (bssdesc->gameInfoLength != 0 && bssdesc->gameInfo.userGameInfoLength == sizeof(MyGameInfo))
181     {
182         const MyGameInfo *pMyGameInfo = (MyGameInfo *) bssdesc->gameInfo.userGameInfo;
183         u32 nickNameLength = pMyGameInfo->nickNameLength;
184 
185         // Do not trust data obtained via communications. Always check that the values are appropriate.
186         if (nickNameLength <= 10)
187         {
188             MI_CpuCopy8(pMyGameInfo->nickName, nickName, nickNameLength * 2);
189             nickName[nickNameLength] = 0;
190             MI_CpuFill8(sjisNickName, 0, sizeof(sjisNickName));
191             ExUTF16_LEtoSJIS_BE((u8*)sjisNickName, (u16*)nickName, (u16)nickNameLength);
192 
193             entryCount = pMyGameInfo->entryCount;
194         }
195     }
196 
197     // Investigate the detected parents
198     for (i = 0; i < sSelectParentWindow.itemnum; ++i)
199     {
200         if (WM_IsBssidEqual(sBssDesc[i].bssid, bssdesc->bssid))
201         {
202             break;
203         }
204     }
205 
206     // Create the menu item for parent selection
207     (void)OS_SNPrintf(buf, ITEM_LENGTH_MAX,
208                       "[%d]channel%d %s %d/%d", i + 1, bssdesc->channel, sjisNickName, entryCount,
209                       WH_CHILD_MAX);
210 
211     if (i < sSelectParentWindow.itemnum)
212     {
213         // Overwrite because it is already registered in the menu
214         setItemToWindow(&sSelectParentWindow, buf, i);
215     }
216     else
217     {
218         // The information is stored and added to the menu because a new parent was detected
219         sBssDesc[sSelectParentWindow.itemnum] = *bssdesc;
220         addItemToWindow(&sSelectParentWindow, buf);
221 
222         WH_PrintBssDesc(bssdesc);
223     }
224 }
225 
gameinfoCallback(void * arg)226 static void gameinfoCallback(void *arg)
227 {
228     WMCallback *cb = (WMCallback *)arg;
229 }
230 
updateGameInfo(BOOL isEntry)231 static void updateGameInfo(BOOL isEntry)
232 {
233     ++sGameInfo.periodicalCount;
234     sGameInfo.entryCount = (u8)(MATH_CountPopulation(WH_GetBitmap()) - 1);
235 
236     (void)WM_SetGameInfo(gameinfoCallback, (u16 *)&sGameInfo, sizeof(sGameInfo), WH_GGID, sTgid,
237                          (u8)(isEntry ? WM_ATTR_FLAG_ENTRY : 0));
238 }
239 
forceSpinWait(void)240 static void forceSpinWait(void)
241 {
242     // Process to create the process failure state at will using OS_SpinWait
243     //
244 
245     static int waitcycle = 0;
246 
247     if (getKeyInfo()->cnt & PAD_BUTTON_L)
248     {
249         waitcycle += 4000;
250         // OS_Printf("wait = %d\n", waitcycle);
251 
252     }
253     else if (getKeyInfo()->cnt & PAD_BUTTON_R)
254     {
255         waitcycle -= 4000;
256         if (waitcycle < 0)
257         {
258             waitcycle = 0;
259         }
260         // OS_Printf("wait = %d\n", waitcycle);
261     }
262 
263     OS_SpinWait((u32)waitcycle);
264 }
265 
ModeSelectRole(void)266 static void ModeSelectRole(void)
267 {
268     static const char *menuitems[] = {
269         "Start (Parent mode)",
270         "Start (Child mode)",
271         "Option",
272         NULL
273     };
274 
275     if (sSysModeStep == 0)
276     {
277         sRoleMenuWindow.selected = 0;
278         setupWindow(&sRoleMenuWindow, 16, 16, WIN_FLAG_SELECTABLE, 24, 24, 16);
279         if (sRoleMenuWindow.itemnum == 0)
280         {
281             int     i;
282             for (i = 0; menuitems[i] != NULL; ++i)
283             {
284                 addItemToWindow(&sRoleMenuWindow, menuitems[i]);
285             }
286         }
287         openWindow(&sRoleMenuWindow);
288     }
289 
290     if (sRoleMenuWindow.state == WIN_STATE_CLOSED)
291     {
292         if (sRoleMenuWindow.selected < 0)
293         {
294             openWindow(&sRoleMenuWindow);
295             return;
296         }
297 
298         switch (sRoleMenuWindow.selected)
299         {
300         case 0:
301             // WH initialization
302             if (!WH_Initialize())
303             {
304                 OS_Panic("WH_Initiailze failed.");
305             }
306             else
307             {
308                 // Wait for initialization to complete
309                 while(WH_GetSystemState()==WH_SYSSTATE_BUSY){}
310             }
311 
312             if (sForcedChannel == 0)
313             {
314                 // Get the best channel based on the usage rate and makes a connection
315                 (void)WH_StartMeasureChannel();
316 
317             }
318             else
319             {
320                 sTgid++;
321 
322                 // Update userGameInfo while in the entry reception state
323                 updateGameInfo(TRUE);
324 
325                 // Delete cached parent information
326                 MI_CpuClear8(sBssDesc, sizeof(sBssDesc));
327 
328                 // Start the connection using the manually-selected channel
329                 (void)WH_ParentConnect(WH_CONNECTMODE_DS_PARENT, sTgid, sForcedChannel);
330             }
331             InitBall(NULL, 13, 2, 4);
332             changeSysMode(SYSMODE_LOBBY);
333             break;
334 
335         case 1:
336             // WH initialization
337             if (!WH_Initialize())
338             {
339                 OS_Panic("WH_Initiailze failed.");
340             }
341             else
342             {
343                 // Wait for initialization to complete
344                 while(WH_GetSystemState()==WH_SYSSTATE_BUSY){}
345             }
346 
347             {
348                 // Search for a parent
349                 static const u8 ANY_PARENT[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
350                 enum
351                 { ALL_CHANNEL = 0 };
352 
353                 initWindow(&sSelectParentWindow);
354                 setupWindow(&sSelectParentWindow, 16, 16, WIN_FLAG_SELECTABLE, 8 * 2, 8, 16);
355                 (void)WH_StartScan(scanCallback, ANY_PARENT, ALL_CHANNEL);
356                 InitBall(NULL, 13, 2, 4);
357                 changeSysMode(SYSMODE_SCAN_PARENT);
358             }
359             break;
360 
361         case 2:
362             // To the option screen
363             changeSysMode(SYSMODE_OPTION);
364             break;
365 
366         default:
367             break;
368         }
369     }
370 }
371 
ModeOption(void)372 static void ModeOption(void)
373 {
374     // Option screen process
375 
376     if (sSysModeStep == 0)
377     {
378         initWindow(&sOptionWindow);
379         setupWindow(&sOptionWindow, 16, 16, WIN_FLAG_SELECTABLE, 8, 8, 16);
380         addItemToWindow(&sOptionWindow, "Select channel (parent)");
381 
382         if (sGraphEnabled)
383         {
384             addItemToWindow(&sOptionWindow, "Disable beacon graph");
385         }
386         else
387         {
388             addItemToWindow(&sOptionWindow, "Enable beacon graph");
389         }
390 
391         openWindow(&sOptionWindow);
392         return;
393     }
394 
395     if (sOptionWindow.state == WIN_STATE_CLOSED)
396     {
397         if (sOptionWindow.selected < 0)
398         {
399             // Cancelled
400             changeSysMode(SYSMODE_SELECT_ROLE);
401             return;
402         }
403 
404         if (sOptionWindow.selected == 0)
405         {
406             // To the channel selection screen
407             changeSysMode(SYSMODE_SELECT_CHANNEL);
408 
409         }
410         else if (sOptionWindow.selected == 1)
411         {
412             sGraphEnabled = sGraphEnabled ? FALSE : TRUE;
413             changeSysMode(SYSMODE_SELECT_ROLE);
414         }
415     }
416 }
417 
ModeLobby(void)418 static void ModeLobby(void)
419 {
420     // Lobby screen process
421 
422     u16     bmap;
423     int     i;
424 
425     updateGameInfo(TRUE);
426 
427     if (sSysModeStep == 0)
428     {
429         initWindow(&sLobbyWindow);
430         setupWindow(&sLobbyWindow, 16, 16, WIN_FLAG_NONE, 8, 8, 16);
431 
432         for (i = 0; i < WH_CHILD_MAX; ++i)
433         {
434             addItemToWindow(&sLobbyWindow, "");
435         }
436 
437         addItemToWindow(&sLobbyWindow, "");
438         addItemToWindow(&sLobbyWindow, "Push A to start");
439 
440         openWindow(&sLobbyWindow);
441         return;
442     }
443 
444     bmap = WH_GetBitmap();
445     for (i = 0; i < WH_CHILD_MAX; ++i)
446     {
447         if (bmap & (1 << i))
448         {
449             (void)OS_SNPrintf(sLobbyWindow.item[i], ITEM_LENGTH_MAX, "[%02d] - entry", i);
450         }
451         else
452         {
453             (void)OS_SNPrintf(sLobbyWindow.item[i], ITEM_LENGTH_MAX, "[%02d] - waiting", i);
454         }
455     }
456 
457     if (sLobbyWindow.state == WIN_STATE_CLOSED)
458     {
459         getSendData()->command = SHARECMD_NONE;
460         if (sLobbyWindow.selected < 0)
461         {
462             // WH shutdown
463             WH_Finalize();
464             while(WH_GetSystemState()==WH_SYSSTATE_BUSY){}
465             (void)WH_End();
466             while(WH_GetSystemState()==WH_SYSSTATE_BUSY){}
467 
468             changeSysMode(SYSMODE_SELECT_ROLE);
469             return;
470         }
471 
472         changeSysMode(SYSMODE_PARENT);
473     }
474 }
475 
ModeLobbyWait(void)476 static void ModeLobbyWait(void)
477 {
478     // Process during child device lobby standby
479     // Wait until a parent sends a start signal
480 
481     if (sSysModeStep == 0)
482     {
483         initWindow(&sWaitWindow);
484         setupWindow(&sWaitWindow, 32, 56, WIN_FLAG_NOCONTROL, 8, 8, 8);
485         addItemToWindow(&sWaitWindow, "\\2Accepted.");
486         addItemToWindow(&sWaitWindow, "\\2Waiting for parent...");
487         openWindow(&sWaitWindow);
488         return;
489     }
490 
491     if (getRecvData(0)->command == SHARECMD_EXITLOBBY)
492     {
493         closeWindow(&sWaitWindow);
494         changeSysMode(SYSMODE_CHILD);
495     }
496 }
497 
ModeSelectChannel(void)498 static void ModeSelectChannel(void)
499 {
500     static u16 channelList[15];        // Auto select + maximum 14 channels
501     // Channel selection screen
502     if (sSysModeStep == 0)
503     {
504         setupWindow(&sSelectChannelWindow, 16, 16, WIN_FLAG_SELECTABLE, 16, 12, 16);
505 
506         if (sSelectChannelWindow.itemnum == 0)
507         {
508             u16     pattern;
509             int     i, j;
510             for (i = 0; i < 14; i++)
511             {
512                 channelList[i] = 0;
513             }
514 
515             // WH initialization
516             if (!WH_Initialize())
517             {
518                 OS_Panic("WH_Initiailze failed.");
519             }
520             else
521             {
522                 // Wait for initialization to complete
523                 while(WH_GetSystemState()==WH_SYSSTATE_BUSY){}
524             }
525 
526             pattern = WH_GetAllowedChannel();
527 
528             // WH shutdown
529             WH_Finalize();
530             while(WH_GetSystemState()==WH_SYSSTATE_BUSY){}
531             (void)WH_End();
532             while(WH_GetSystemState()==WH_SYSSTATE_BUSY){}
533 
534             addItemToWindow(&sSelectChannelWindow, "Auto select");
535             for (i = 1, j = 1; i <= 14; ++i)
536             {
537                 if (pattern & (1 << (i - 1)))
538                 {
539                     char    buf[ITEM_LENGTH_MAX];
540                     (void)OS_SNPrintf(buf, ITEM_LENGTH_MAX, "Channel %d", i);
541                     channelList[j] = (u16)i;
542                     ++j;
543                     addItemToWindow(&sSelectChannelWindow, buf);
544                 }
545             }
546         }
547 
548         openWindow(&sSelectChannelWindow);
549     }
550 
551     if (sSelectChannelWindow.state == WIN_STATE_CLOSED)
552     {
553         if (sSelectChannelWindow.selected >= 0)
554         {
555             sForcedChannel = channelList[sSelectChannelWindow.selected];
556         }
557 
558         // Return to the role selection screen
559         changeSysMode(SYSMODE_SELECT_ROLE);
560     }
561 }
562 
ModeSelectParent(void)563 static void ModeSelectParent(void)
564 {
565     // Display parent on the list and select it.
566     // In this state, WH is scanning a parent, and if a new parent is found during scanning, it is immediately added/reflected to the menu.
567     //
568 
569     if (WH_GetSystemState() == WH_SYSSTATE_CONNECT_FAIL)
570     {
571         // Because the internal WM state is invalid if the WM_StartConnect function fails, WM_Reset must be called once to reset the state to IDLE
572         //
573         WH_Reset();
574         return;
575     }
576 
577     if (sSysModeStep == 0)
578     {
579         openWindow(&sSelectParentWindow);
580     }
581 
582     // Did the user close the parent search screen?
583     if ((sSelectParentWindow.state == WIN_STATE_CLOSED))
584     {
585         if (WH_GetSystemState() == WH_SYSSTATE_SCANNING)
586         {
587             // If the parent is currently scanning, scan is finished for a moment
588             (void)WH_EndScan();
589             return;
590         }
591 
592         if (WH_GetSystemState() == WH_SYSSTATE_IDLE)
593         {
594             if (sSelectParentWindow.selected < 0)
595             {
596                 // WH shutdown
597                 WH_Finalize();
598                 while(WH_GetSystemState()==WH_SYSSTATE_BUSY){}
599                 (void)WH_End();
600                 while(WH_GetSystemState()==WH_SYSSTATE_BUSY){}
601 
602                 changeSysMode(SYSMODE_SELECT_ROLE);
603                 return;
604             }
605 
606             // When connecting to a parent, set the local host's nickname in the SSID user region and send a notification
607             WH_SetSsid(sMyInfo.nickName, (u32)(sMyInfo.nickNameLength * 2));
608 
609             // If not scanning, and if the user has selected a parent, data sharing starts
610             (void)WH_ChildConnect(WH_CONNECTMODE_DS_CHILD,
611                                   &(sBssDesc[sSelectParentWindow.selected]));
612             changeSysMode(SYSMODE_LOBBYWAIT);
613         }
614     }
615 }
616 
ModeError(void)617 static void ModeError(void)
618 {
619     // Error state
620     if (sSysModeStep == 0)
621     {
622         initWindow(&sErrorWindow);
623         setupWindow(&sErrorWindow, 16, 16, WIN_FLAG_NONE, 8, 8, 16);
624 
625         addItemToWindow(&sErrorWindow, "\\1Error has occured!");
626 
627         if (WH_GetLastError() == WM_ERRCODE_OVER_MAX_ENTRY)
628         {
629             addItemToWindow(&sErrorWindow, "\\4Rejected\n");
630         }
631 
632         if (WH_GetLastError() == WH_ERRCODE_DISCONNECTED)
633         {
634             addItemToWindow(&sErrorWindow, "\\4Disconnected by parent\n");
635         }
636 
637         if (WH_GetLastError() == WH_ERRCODE_PARENT_NOT_FOUND)
638         {
639             addItemToWindow(&sErrorWindow, "\\4Parent not found\n");
640         }
641 
642         if (WH_GetLastError() == WH_ERRCODE_LOST_PARENT)
643         {
644             addItemToWindow(&sErrorWindow, "\\4Lost parent\n");
645         }
646 
647         addItemToWindow(&sErrorWindow, "");
648         addItemToWindow(&sErrorWindow, "\\fPush A to reset");
649 
650         closeAllWindow();
651         openWindow(&sErrorWindow);
652     }
653 
654     if (sErrorWindow.state == WIN_STATE_CLOSED)
655     {
656         // WH shutdown
657         WH_Finalize();
658         while(WH_GetSystemState()==WH_SYSSTATE_BUSY){}
659         (void)WH_End();
660         while(WH_GetSystemState()==WH_SYSSTATE_BUSY){}
661 
662         getRecvData(0)->command = SHARECMD_NONE;
663         changeSysMode(SYSMODE_SELECT_ROLE);
664     }
665 }
666 
printShareData(void)667 static void printShareData(void)
668 {
669     s32     i;
670     ShareData *sd;
671 
672     sd = getSendData();
673     printString("\\2Send:     0x%04x 0x%04x\n", sd->key, sd->count & 0xffff);
674 
675     printString("\\4Receive:\n");
676     for (i = 1; i < (WM_NUM_MAX_CHILD + 1); i++)
677     {
678         if (sRecvFlag[i])
679         {
680             sd = getRecvData(i);
681             printString("\\4Child%02d:  0x%04x 0x%04x\n", i, sd->key, sd->count & 0xffff);
682 
683         }
684         else
685         {
686             printString("No child\n");
687         }
688     }
689 }
690 
ModeParent(void)691 static void ModeParent(void)
692 {
693     printString("\n  \\fParent mode\n\n");
694     printShareData();
695 }
696 
ModeChild(void)697 static void ModeChild(void)
698 {
699     ShareData *sd;
700 
701     printString("\n  \\fChild mode\n\n");
702     printShareData();
703 
704     sd = (ShareData *) getRecvData(0);
705     printString("\\4Parent:   0x%04x 0x%04x\n", sd->key, sd->count & 0xffff);
706 }
707 
VBlankIntr(void)708 static void VBlankIntr(void)
709 {
710     /*
711        Some other samples use StepDataSharing here (the VBlankIntr function).
712        This sample also performed StepDataSharing here before.
713 
714 
715        It is this way because WM_StepDataSharing (and WM_GetKeySet) have to be called before the start of MP communications in a frame for communications to be stable.
716        The SDK prepares a VCount of 240 for child devices and 260 for parent devices with default MP communications, so the fewest problems are presented by setting a value immediately after the VBlank starts.
717        (Although not implemented when this document was written, there are plans to allow the VCount for the start of MP communications to be set during initialization.)
718 
719        For 30-fps (and other) games, code is normally required to limit the number of StepDS calls to one every two frames.
720        In that case, wait only a single frame (instead of two) when WM_ERRCODE_NO_DATASET is returned (if processing was lost).
721        Otherwise, when there is one frame difference between the parent and child, it cannot be corrected.
722 
723 
724 
725 
726 
727 
728      */
729 
730     updateKeys();
731     OS_SetIrqCheckFlag(OS_IE_V_BLANK);
732 }
733 
initAllocSystem(void)734 static void initAllocSystem(void)
735 {
736     void   *tempLo;
737     OSHeapHandle hh;
738 
739     tempLo = OS_InitAlloc(OS_ARENA_MAIN, OS_GetMainArenaLo(), OS_GetMainArenaHi(), 1);
740     OS_SetArenaLo(OS_ARENA_MAIN, tempLo);
741     hh = OS_CreateHeap(OS_ARENA_MAIN, OS_GetMainArenaLo(), OS_GetMainArenaHi());
742     if (hh < 0)
743     {
744         OS_Panic("ARM9: Fail to create heap...\n");
745     }
746     hh = OS_SetCurrentHeap(OS_ARENA_MAIN, hh);
747 }
748 
drawPowerGraph(void)749 static void drawPowerGraph(void)
750 {
751     static const GXRgb linecolor[4] = {
752         GX_RGB(15, 0, 0),              // Dark red (dead)
753         GX_RGB(31, 31, 0),             // Yellow
754         GX_RGB(0, 31, 0),              // Green
755         GX_RGB(20, 31, 20),            // Light green
756     };
757 
758     int     midx, ringidx;
759 
760     ringidx = (sFrame % GRAPH_TOTAL_FRAME);
761 
762     for (midx = 0; midx < WM_NUM_MAX_CHILD + 1; ++midx)
763     {
764         sGraphData[midx][ringidx].data = (s16)getRecvData(midx)->data;
765         sGraphData[midx][ringidx].level = (u16)getRecvData(midx)->level;
766     }
767 
768     G3_PolygonAttr(GX_LIGHTMASK_NONE,
769                    GX_POLYGONMODE_MODULATE, GX_CULL_NONE, 0, 31, GX_POLYGON_ATTR_MISC_DISP_1DOT);
770     G3_TexImageParam(GX_TEXFMT_NONE,
771                      GX_TEXGEN_NONE,
772                      GX_TEXSIZE_S16,
773                      GX_TEXSIZE_T16, GX_TEXREPEAT_NONE, GX_TEXFLIP_NONE, GX_TEXPLTTCOLOR0_USE, 0);
774 
775     G3_MtxMode(GX_MTXMODE_POSITION_VECTOR);
776     G3_PushMtx();
777     G3_Identity();
778     G3_Translate(0, 0, -FX16_ONE * 4);
779 
780     G3_Begin(GX_BEGIN_TRIANGLES);
781 
782     for (midx = 1; midx < WM_NUM_MAX_CHILD + 1; ++midx)
783     {
784         int     basey, ys, ye, gi, x, level;
785         basey = ((WM_NUM_MAX_CHILD / 2 - midx) * 9 + 6) * FX16_ONE / 64;
786 
787         for (gi = 0; gi < GRAPH_TOTAL_FRAME; ++gi)
788         {
789             int     ri;
790             ri = (ringidx - gi);
791             if (ri < 0)
792             {
793                 ri += GRAPH_TOTAL_FRAME;
794             }
795 
796             ys = sGraphData[midx][ri].data;
797             level = sGraphData[midx][ri].level;
798 
799             ++ri;
800             if (ri >= GRAPH_TOTAL_FRAME)
801             {
802                 ri -= GRAPH_TOTAL_FRAME;
803             }
804 
805             ye = sGraphData[midx][ri].data;
806 
807             x = -(gi - GRAPH_TOTAL_FRAME / 2) * 3;
808             x *= FX16_ONE / 64;
809             ys = ys * FX16_ONE / 64 + basey;
810             ye = ye * FX16_ONE / 64 + basey;
811 
812             G3_Color(linecolor[level]);
813 
814             G3_Vtx((fx16)x, (fx16)ys, 0);
815             G3_Vtx((fx16)(x + FX16_ONE / 64 / 2), (fx16)(ys + FX16_ONE / 64), 0);
816             G3_Vtx((fx16)(x + 3 * FX16_ONE / 64), (fx16)ye, 0);
817         }
818     }
819 
820     G3_End();
821     G3_PopMtx(1);
822 }
823 
drawPowerIcon(void)824 static void drawPowerIcon(void)
825 {
826     // GUIDELINE
827     // Display the radio reception strength icon
828     setupPseudo2DCamera();
829 
830     G3_PolygonAttr(GX_LIGHTMASK_NONE, GX_POLYGONMODE_DECAL, GX_CULL_NONE, 0, 31, 0);
831 
832     G3_TexImageParam(GX_TEXFMT_PLTT16,
833                      GX_TEXGEN_TEXCOORD,
834                      GX_TEXSIZE_S16,
835                      GX_TEXSIZE_T16,
836                      GX_TEXREPEAT_NONE,
837                      GX_TEXFLIP_NONE,
838                      GX_TEXPLTTCOLOR0_USE, (u32)(0x2000 + WM_GetLinkLevel() * 16 * 16 / 2));
839     G3_TexPlttBase(0x2000, GX_TEXFMT_PLTT16);
840     G3_MtxMode(GX_MTXMODE_POSITION_VECTOR);
841     drawPseudo2DTexQuad(224, 160, 16, 16, 16, 16);
842 }
843 
drawRadioIcon(void)844 static void drawRadioIcon(void)
845 {
846     // GUIDELINE
847     // Display the radio wave strength icon
848     int     i;
849 
850     G3_PolygonAttr(GX_LIGHTMASK_NONE, GX_POLYGONMODE_DECAL, GX_CULL_NONE, 0, 31, 0);
851 
852     G3_TexPlttBase(0x2000, GX_TEXFMT_PLTT16);
853     G3_TexImageParam(GX_TEXFMT_PLTT16,
854                      GX_TEXGEN_TEXCOORD,
855                      GX_TEXSIZE_S16,
856                      GX_TEXSIZE_T16,
857                      GX_TEXREPEAT_NONE,
858                      GX_TEXFLIP_NONE, GX_TEXPLTTCOLOR0_USE, 0x2000 + 4 * 16 * 16 / 2);
859 
860     for (i = 0; i < 2; ++i)
861     {
862         drawPseudo2DTexQuad(16,
863                             12 + i * 24 + ((i == sRoleMenuWindow.selected) ? (sFrame / 15 & 1) : 0),
864                             16, 16, 16, 16);
865     }
866 }
867 
updateShareData(void)868 static void updateShareData(void)
869 {
870     if (WH_GetSystemState() == WH_SYSSTATE_DATASHARING)
871     {
872         if (WH_StepDS(sSendBuf))
873         {
874             u16     i;
875             for (i = 0; i < WH_CHILD_MAX + 1; ++i)
876             {
877                 u8     *adr;
878                 ShareData *sd;
879 
880                 adr = (u8 *)WH_GetSharedDataAdr(i);
881                 sd = (ShareData *) & (sRecvBuf[i * sizeof(ShareData)]);
882 
883                 if (adr != NULL)
884                 {
885                     MI_CpuCopy8(adr, sd, sizeof(ShareData));
886                     sRecvFlag[i] = TRUE;
887 
888                 }
889                 else
890                 {
891                     sd->level = 0;
892                     sd->data = 0;
893                     sRecvFlag[i] = FALSE;
894                 }
895             }
896             for (; i < WM_NUM_MAX_CHILD + 1; ++i)
897             {
898                 ShareData *sd;
899 
900                 sd = (ShareData *) & (sRecvBuf[i * sizeof(ShareData)]);
901 
902                 sd->level = 0;
903                 sd->data = 0;
904                 sRecvFlag[i] = FALSE;
905             }
906             sNeedWait = FALSE;
907 
908         }
909         else
910         {
911             u16     i;
912             for (i = 0; i < WM_NUM_MAX_CHILD + 1; ++i)
913             {
914                 sRecvFlag[i] = FALSE;
915             }
916 
917             sNeedWait = TRUE;
918         }
919 
920     }
921     else
922     {
923         u16     i;
924         for (i = 0; i < WM_NUM_MAX_CHILD + 1; ++i)
925         {
926             sRecvFlag[i] = FALSE;
927         }
928 
929         sNeedWait = FALSE;
930     }
931 }
932 
packSendData(void)933 static void packSendData(void)
934 {
935     ShareData *senddata;
936 
937     if (sNeedWait)
938     {
939         return;
940     }
941 
942     senddata = getSendData();
943     senddata->command = (sSysMode == SYSMODE_LOBBY) ? SHARECMD_NONE : SHARECMD_EXITLOBBY;
944 
945     senddata->level = (u16)WM_GetLinkLevel();
946 
947     senddata->data = 0;
948     senddata->key = getKeyInfo()->cnt;
949     senddata->count = sFrame & 0xffff;
950 
951     if (sBeaconCount != 0)
952     {
953         senddata->data += sBeaconCount * senddata->level;
954 
955         if (sBeaconCount > 0)
956         {
957             sBeaconCount = -sBeaconCount + 1;
958         }
959         else
960         {
961             sBeaconCount = -sBeaconCount - 1;
962         }
963     }
964 }
965 
mainLoop(void)966 static void mainLoop(void)
967 {
968     int retry = 0;
969     enum {
970         MAX_RETRY = 5
971     };
972 
973     for (sFrame = 0; TRUE; sFrame++)
974     {
975         int     whstate;
976         int     prevmode;
977 
978         whstate = WH_GetSystemState();
979         prevmode = sSysMode;
980 
981         switch (whstate)
982         {
983         case WH_SYSSTATE_CONNECT_FAIL:
984             // Retry several times when a connection fails
985             if (sSysMode == SYSMODE_LOBBYWAIT && retry < MAX_RETRY)
986             {
987                 changeSysMode(SYSMODE_SELECT_PARENT);
988                 sSysModeStep = 1; // So that window does not open...
989                 retry++;
990                 break;
991             }
992             // When a retry fails, transition unchanged to the error state
993 
994         case WH_SYSSTATE_ERROR:
995             // WH state has the priority if an error occurred
996             changeSysMode(SYSMODE_ERROR);
997             break;
998 
999         case WH_SYSSTATE_MEASURECHANNEL:
1000             {
1001                 u16     channel = WH_GetMeasureChannel();
1002                 sTgid++;
1003                 (void)WH_ParentConnect(WH_CONNECTMODE_DS_PARENT, sTgid, channel);
1004             }
1005             break;
1006 
1007         default:
1008             break;
1009         }
1010 
1011         PR_ClearScreen(&sInfoScreen);
1012 
1013         // Load test
1014         forceSpinWait();
1015 
1016         switch (sSysMode)
1017         {
1018         case SYSMODE_SELECT_ROLE:
1019             // Role (parent or child) selection screen
1020             ModeSelectRole();
1021             retry = 0;
1022             break;
1023 
1024         case SYSMODE_SELECT_CHANNEL:
1025             // Channel selection screen
1026             ModeSelectChannel();
1027             break;
1028 
1029         case SYSMODE_LOBBY:
1030             // Lobby screen
1031             ModeLobby();
1032             break;
1033 
1034         case SYSMODE_LOBBYWAIT:
1035             // Lobby standby screen
1036             ModeLobbyWait();
1037             break;
1038 
1039         case SYSMODE_OPTION:
1040             // Option screen
1041             ModeOption();
1042             break;
1043 
1044         case SYSMODE_SCAN_PARENT:
1045         case SYSMODE_SELECT_PARENT:
1046             // Parent selection screen
1047             ModeSelectParent();
1048             break;
1049 
1050         case SYSMODE_ERROR:
1051             // Error report screen
1052             ModeError();
1053             break;
1054 
1055         case SYSMODE_PARENT:
1056             // Parent mode screen
1057             ModeParent();
1058             break;
1059 
1060         case SYSMODE_CHILD:
1061             // Child mode screen
1062             ModeChild();
1063             break;
1064 
1065         default:
1066             break;
1067         }
1068 
1069         if ((whstate == WH_SYSSTATE_BUSY)
1070             || ((whstate == WH_SYSSTATE_SCANNING) && (sSelectParentWindow.itemnum == 0)))
1071         {
1072             sBusyWindow.state = WIN_STATE_OPENED;
1073 
1074         }
1075         else
1076         {
1077             sBusyWindow.state = WIN_STATE_CLOSED;
1078         }
1079 
1080         updateAllWindow();
1081         drawAllWindow();
1082 
1083         if( (sSysMode != SYSMODE_SELECT_ROLE) && (sSysMode != SYSMODE_OPTION) && (sSysMode != SYSMODE_SELECT_CHANNEL) )
1084         {
1085             drawPowerIcon();
1086         }
1087 
1088         if ((sSysMode == SYSMODE_PARENT) || (sSysMode == SYSMODE_CHILD))
1089         {
1090             if (sGraphEnabled)
1091             {
1092                 drawPowerGraph();
1093             }
1094         }
1095 
1096         if ((sSysMode == SYSMODE_SELECT_ROLE) && (sRoleMenuWindow.state == WIN_STATE_OPENED))
1097         {
1098             drawRadioIcon();
1099         }
1100 
1101         G3_SwapBuffers(GX_SORTMODE_AUTO, GX_BUFFERMODE_W);
1102 
1103         if (!sNeedWait && ((sSysMode == SYSMODE_PARENT) || (sSysMode == SYSMODE_CHILD)))
1104         {
1105             // ... In an actual game, the update processing for characters and so on is performed here
1106             //
1107             int     i;
1108             for (i = 0; i < BALL_PLAYER_MAX; ++i)
1109             {
1110                 if (sRecvFlag[i])
1111                 {
1112                     ShareData *sd = getRecvData(i);
1113                     InputBallKey((int)i, (int)sd->key);
1114                 }
1115             }
1116 
1117             // Update the balls' state using data that could be shared.
1118             //   Note that if this is run in a frame during which WH_StepDS failed, synchronization between the parent and child game state will be lost.
1119             //
1120             UpdateBalls(WH_GetCurrentAid());
1121 
1122             // Prepare the display data
1123             for (i = 0; i < BALL_PLAYER_MAX; ++i)
1124             {
1125                 G2_SetOBJAttr(&oamBak[i],
1126                               shared->ball[i].x, shared->ball[i].y, 0,
1127                               GX_OAM_MODE_NORMAL, FALSE, GX_OAM_EFFECT_NONE,
1128                               GX_OAM_SHAPE_8x8, GX_OAM_COLOR_16,
1129                               shared->ball[i].chr, shared->ball[i].plt, 0);
1130             }
1131         }
1132 
1133         DC_FlushRange(sInfoScreen.screen, sizeof(u16) * PR_SCREEN_SIZE);
1134         /* I/O register is accessed using DMA operation, so cache wait is not needed */
1135 
1136         // DC_WaitWriteBufferEmpty();
1137         GX_LoadBG2Scr(sInfoScreen.screen, 0, sizeof(u16) * PR_SCREEN_SIZE);
1138         DC_FlushRange(sDebugScreen.screen, sizeof(u16) * PR_SCREEN_SIZE);
1139         /* I/O register is accessed using DMA operation, so cache wait is not needed */
1140 
1141         // DC_WaitWriteBufferEmpty();
1142         GXS_LoadBG2Scr(sDebugScreen.screen, 0, sizeof(u16) * PR_SCREEN_SIZE);
1143         DC_FlushRange(&oamBak, sizeof(oamBak));
1144         if ((sSysMode == SYSMODE_PARENT) || (sSysMode == SYSMODE_CHILD))
1145         {
1146             GX_LoadOAM(&oamBak, 0, sizeof(oamBak));
1147         }
1148         else
1149         {
1150             // No display of entire OAM
1151             MI_CpuFillFast((void *)HW_OAM, 192, HW_OAM_SIZE);
1152         }
1153 
1154         OS_WaitVBlankIntr();
1155 
1156         packSendData();
1157         updateShareData();
1158 
1159         if (prevmode == sSysMode)
1160         {
1161             ++sSysModeStep;
1162         }
1163     }
1164 }
1165 
1166 #ifdef SDK_TWL
TwlMain(void)1167 void TwlMain(void)
1168 #else
1169 void NitroMain(void)
1170 #endif
1171 {
1172     OS_Init();
1173     FX_Init();
1174 
1175     initGraphics();
1176 
1177     GX_SetBankForBG(GX_VRAM_BG_256_AB);
1178     GX_SetBankForBGExtPltt(GX_VRAM_BGEXTPLTT_01_F);
1179     G2_SetBG2ControlText(GX_BG_SCRSIZE_TEXT_256x256,
1180                          GX_BG_COLORMODE_16, GX_BG_SCRBASE_0x0000, GX_BG_CHARBASE_0x04000);
1181 
1182     GX_BeginLoadTex();
1183     GX_LoadTex(wlicon_image, 0x2000, 16 * 16 * 5);
1184     GX_EndLoadTex();
1185 
1186     GX_BeginLoadTexPltt();
1187     GX_LoadTexPltt(wlicon_palette, 0x2000, 32);
1188     GX_EndLoadTexPltt();
1189 
1190     GX_LoadBG2Char(d_CharData, 0, sizeof(d_CharData));
1191     GX_LoadBGPltt(d_PaletteData, 0, sizeof(d_PaletteData));
1192 
1193     GX_SetBankForSubBG(GX_VRAM_SUB_BG_128_C);
1194     GX_SetBankForSubBGExtPltt(GX_VRAM_SUB_BGEXTPLTT_0123_H);
1195     G2S_SetBG2ControlText(GX_BG_SCRSIZE_TEXT_256x256,
1196                           GX_BG_COLORMODE_16, GX_BG_SCRBASE_0x0000, GX_BG_CHARBASE_0x04000);
1197 
1198     GXS_SetGraphicsMode(GX_BGMODE_0);
1199     GXS_SetVisiblePlane(GX_PLANEMASK_BG2);
1200     G2S_SetBG2Priority(0);
1201     G2S_BG2Mosaic(FALSE);
1202 
1203     GXS_LoadBG2Char(d_CharData, 0, sizeof(d_CharData));
1204     GXS_LoadBGPltt(d_PaletteData, 0, sizeof(d_PaletteData));
1205 
1206     GX_SetBankForOBJ(GX_VRAM_OBJ_16_G);
1207     GX_LoadOBJ(d_CharData, 0, sizeof(d_CharData));
1208     GX_LoadOBJPltt(d_PaletteData, 0, sizeof(d_PaletteData));
1209     GX_SetVisiblePlane(GX_GetVisiblePlane() | GX_PLANEMASK_OBJ);
1210 
1211     initAllocSystem();
1212 
1213     OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
1214     (void)OS_EnableIrqMask(OS_IE_V_BLANK);
1215     (void)GX_VBlankIntr(TRUE);
1216     (void)OS_EnableIrq();
1217     (void)OS_EnableInterrupts();
1218 
1219     sInfoScreen.scroll = FALSE;
1220     sDebugScreen.scroll = TRUE;
1221 
1222     // Load owner information
1223     OS_GetOwnerInfo(&sMyInfo);
1224     sGameInfo.nickNameLength = (u8)sMyInfo.nickNameLength;
1225     MI_CpuCopy8(sMyInfo.nickName, sGameInfo.nickName, MATH_MIN(sMyInfo.nickNameLength * 2, sizeof(sGameInfo.nickName)));
1226 
1227     // WH initialization settings
1228     WH_SetGgid(WH_GGID);
1229     WH_SetDebugOutput(TraceWH);
1230 
1231     // Set the callback specified in WM_SetIndCallback, which is called within WH
1232     WH_SetIndCallback(indicateCallback);
1233 
1234     GX_DispOn();
1235     GXS_DispOn();
1236 
1237     initWindow(&sRoleMenuWindow);
1238     initWindow(&sSelectChannelWindow);
1239     initWindow(&sSelectParentWindow);
1240     initWindow(&sLobbyWindow);
1241     initWindow(&sErrorWindow);
1242     initWindow(&sOptionWindow);
1243     initWindow(&sWaitWindow);
1244     initWindow(&sBusyWindow);
1245 
1246     setupWindow(&sBusyWindow, 64, 80, WIN_FLAG_NOCONTROL, 8, 8, 16);
1247     addItemToWindow(&sBusyWindow, "\\2Working...");
1248 
1249     // Countermeasure for pressing A Button in IPL
1250     updateKeys();
1251 
1252     // initCharacter();
1253     changeSysMode(SYSMODE_SELECT_ROLE);
1254     mainLoop();
1255 }
1256