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:: 2008-09-17#$
14   $Rev: 8556 $
15   $Author: okubata_ryoma $
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   Specifically, one of the following will be called: 1) Lobby screen; ,2) Parent selection screen; ,3) Measures against processing failure (synchronized); or ,4) Error display and return.
24 
25 
26 
27 
28 
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             if (sForcedChannel == 0)
302             {
303                 // Get the best channel based on the usage rate and makes a connection
304                 (void)WH_StartMeasureChannel();
305 
306             }
307             else
308             {
309                 sTgid++;
310 
311                 // Update userGameInfo while in the entry reception state
312                 updateGameInfo(TRUE);
313 
314                 // Delete cached parent information
315                 MI_CpuClear8(sBssDesc, sizeof(sBssDesc));
316 
317                 // Start the connection using the manually-selected channel
318                 (void)WH_ParentConnect(WH_CONNECTMODE_DS_PARENT, sTgid, sForcedChannel);
319             }
320             InitBall(NULL, 13, 2, 4);
321             changeSysMode(SYSMODE_LOBBY);
322             break;
323 
324         case 1:
325             {
326                 // Search for a parent
327                 static const u8 ANY_PARENT[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
328                 enum
329                 { ALL_CHANNEL = 0 };
330 
331                 initWindow(&sSelectParentWindow);
332                 setupWindow(&sSelectParentWindow, 16, 16, WIN_FLAG_SELECTABLE, 8 * 2, 8, 16);
333                 (void)WH_StartScan(scanCallback, ANY_PARENT, ALL_CHANNEL);
334                 InitBall(NULL, 13, 2, 4);
335                 changeSysMode(SYSMODE_SCAN_PARENT);
336             }
337             break;
338 
339         case 2:
340             // To the option screen
341             changeSysMode(SYSMODE_OPTION);
342             break;
343 
344         default:
345             break;
346         }
347     }
348 }
349 
ModeOption(void)350 static void ModeOption(void)
351 {
352     // Option screen process
353 
354     if (sSysModeStep == 0)
355     {
356         initWindow(&sOptionWindow);
357         setupWindow(&sOptionWindow, 16, 16, WIN_FLAG_SELECTABLE, 8, 8, 16);
358         addItemToWindow(&sOptionWindow, "Select channel (parent)");
359 
360         if (sGraphEnabled)
361         {
362             addItemToWindow(&sOptionWindow, "Disable beacon graph");
363         }
364         else
365         {
366             addItemToWindow(&sOptionWindow, "Enable beacon graph");
367         }
368 
369         openWindow(&sOptionWindow);
370         return;
371     }
372 
373     if (sOptionWindow.state == WIN_STATE_CLOSED)
374     {
375         if (sOptionWindow.selected < 0)
376         {
377             // Cancelled
378             changeSysMode(SYSMODE_SELECT_ROLE);
379             return;
380         }
381 
382         if (sOptionWindow.selected == 0)
383         {
384             // To the channel selection screen
385             changeSysMode(SYSMODE_SELECT_CHANNEL);
386 
387         }
388         else if (sOptionWindow.selected == 1)
389         {
390             sGraphEnabled = sGraphEnabled ? FALSE : TRUE;
391             changeSysMode(SYSMODE_SELECT_ROLE);
392         }
393     }
394 }
395 
ModeLobby(void)396 static void ModeLobby(void)
397 {
398     // Lobby screen process
399 
400     u16     bmap;
401     int     i;
402 
403     updateGameInfo(TRUE);
404 
405     if (sSysModeStep == 0)
406     {
407         initWindow(&sLobbyWindow);
408         setupWindow(&sLobbyWindow, 16, 16, WIN_FLAG_NONE, 8, 8, 16);
409 
410         for (i = 0; i < WH_CHILD_MAX; ++i)
411         {
412             addItemToWindow(&sLobbyWindow, "");
413         }
414 
415         addItemToWindow(&sLobbyWindow, "");
416         addItemToWindow(&sLobbyWindow, "Push A to start");
417 
418         openWindow(&sLobbyWindow);
419         return;
420     }
421 
422     bmap = WH_GetBitmap();
423     for (i = 0; i < WH_CHILD_MAX; ++i)
424     {
425         if (bmap & (1 << i))
426         {
427             (void)OS_SNPrintf(sLobbyWindow.item[i], ITEM_LENGTH_MAX, "[%02d] - entry", i);
428         }
429         else
430         {
431             (void)OS_SNPrintf(sLobbyWindow.item[i], ITEM_LENGTH_MAX, "[%02d] - waiting", i);
432         }
433     }
434 
435     if (sLobbyWindow.state == WIN_STATE_CLOSED)
436     {
437         getSendData()->command = SHARECMD_NONE;
438         if (sLobbyWindow.selected < 0)
439         {
440             WH_Finalize();
441             changeSysMode(SYSMODE_SELECT_ROLE);
442             return;
443         }
444 
445         changeSysMode(SYSMODE_PARENT);
446     }
447 }
448 
ModeLobbyWait(void)449 static void ModeLobbyWait(void)
450 {
451     // Process during child device lobby standby.
452     // Wait until a parent sends a start signal.
453 
454     if (sSysModeStep == 0)
455     {
456         initWindow(&sWaitWindow);
457         setupWindow(&sWaitWindow, 32, 56, WIN_FLAG_NOCONTROL, 8, 8, 8);
458         addItemToWindow(&sWaitWindow, "\\2Accepted.");
459         addItemToWindow(&sWaitWindow, "\\2Waiting for parent...");
460         openWindow(&sWaitWindow);
461         return;
462     }
463 
464     if (getRecvData(0)->command == SHARECMD_EXITLOBBY)
465     {
466         closeWindow(&sWaitWindow);
467         changeSysMode(SYSMODE_CHILD);
468     }
469 }
470 
ModeSelectChannel(void)471 static void ModeSelectChannel(void)
472 {
473     static u16 channelList[15];        // Auto select + maximum 14 channels
474     // Channel selection screen
475     if (sSysModeStep == 0)
476     {
477         setupWindow(&sSelectChannelWindow, 16, 16, WIN_FLAG_SELECTABLE, 16, 12, 16);
478 
479         if (sSelectChannelWindow.itemnum == 0)
480         {
481             u16     pattern;
482             int     i, j;
483             for (i = 0; i < 14; i++)
484             {
485                 channelList[i] = 0;
486             }
487             pattern = WH_GetAllowedChannel();
488             addItemToWindow(&sSelectChannelWindow, "Auto select");
489             for (i = 1, j = 1; i <= 14; ++i)
490             {
491                 if (pattern & (1 << (i - 1)))
492                 {
493                     char    buf[ITEM_LENGTH_MAX];
494                     (void)OS_SNPrintf(buf, ITEM_LENGTH_MAX, "Channel %d", i);
495                     channelList[j] = (u16)i;
496                     ++j;
497                     addItemToWindow(&sSelectChannelWindow, buf);
498                 }
499             }
500         }
501 
502         openWindow(&sSelectChannelWindow);
503     }
504 
505     if (sSelectChannelWindow.state == WIN_STATE_CLOSED)
506     {
507         if (sSelectChannelWindow.selected >= 0)
508         {
509             sForcedChannel = channelList[sSelectChannelWindow.selected];
510         }
511 
512         // Return to the role selection screen
513         changeSysMode(SYSMODE_SELECT_ROLE);
514     }
515 }
516 
ModeSelectParent(void)517 static void ModeSelectParent(void)
518 {
519     // Display parent on the list and select it.
520     // 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.
521     //
522 
523     if (WH_GetSystemState() == WH_SYSSTATE_CONNECT_FAIL)
524     {
525         // If WM_StartConnect() fails, the WM internal state is invalid. Use WM_Reset to reset the state to the IDLE state.
526         //
527         WH_Reset();
528         return;
529     }
530 
531     if (sSysModeStep == 0)
532     {
533         openWindow(&sSelectParentWindow);
534     }
535 
536     // Did the user close the parent search screen?
537     if ((sSelectParentWindow.state == WIN_STATE_CLOSED))
538     {
539         if (WH_GetSystemState() == WH_SYSSTATE_SCANNING)
540         {
541             // If the parent is currently scanning, scan is finished for a moment
542             (void)WH_EndScan();
543             return;
544         }
545 
546         if (WH_GetSystemState() == WH_SYSSTATE_IDLE)
547         {
548             if (sSelectParentWindow.selected < 0)
549             {
550                 WH_Finalize();
551                 changeSysMode(SYSMODE_SELECT_ROLE);
552                 return;
553             }
554 
555             // When connecting to the parent device, set this device's nickname in the SSID user region and send a notification
556             WH_SetSsid(sMyInfo.nickName, (u32)(sMyInfo.nickNameLength * 2));
557 
558             // If not scanning, and if the user has selected a parent, data sharing starts
559             (void)WH_ChildConnect(WH_CONNECTMODE_DS_CHILD,
560                                   &(sBssDesc[sSelectParentWindow.selected]));
561             changeSysMode(SYSMODE_LOBBYWAIT);
562         }
563     }
564 }
565 
ModeError(void)566 static void ModeError(void)
567 {
568     // Error state
569     if (sSysModeStep == 0)
570     {
571         initWindow(&sErrorWindow);
572         setupWindow(&sErrorWindow, 16, 16, WIN_FLAG_NONE, 8, 8, 16);
573 
574         addItemToWindow(&sErrorWindow, "\\1Error has occured!");
575 
576         if (WH_GetLastError() == WM_ERRCODE_OVER_MAX_ENTRY)
577         {
578             addItemToWindow(&sErrorWindow, "\\4Rejected\n");
579         }
580 
581         if (WH_GetLastError() == WH_ERRCODE_DISCONNECTED)
582         {
583             addItemToWindow(&sErrorWindow, "\\4Disconnected by parent\n");
584         }
585 
586         if (WH_GetLastError() == WH_ERRCODE_PARENT_NOT_FOUND)
587         {
588             addItemToWindow(&sErrorWindow, "\\4Parent not found\n");
589         }
590 
591         if (WH_GetLastError() == WH_ERRCODE_LOST_PARENT)
592         {
593             addItemToWindow(&sErrorWindow, "\\4Lost parent\n");
594         }
595 
596         addItemToWindow(&sErrorWindow, "");
597         addItemToWindow(&sErrorWindow, "\\fPush A to reset");
598 
599         closeAllWindow();
600         openWindow(&sErrorWindow);
601     }
602 
603     if (sErrorWindow.state == WIN_STATE_CLOSED)
604     {
605         WH_Finalize();
606         getRecvData(0)->command = SHARECMD_NONE;
607         changeSysMode(SYSMODE_SELECT_ROLE);
608     }
609 }
610 
printShareData(void)611 static void printShareData(void)
612 {
613     s32     i;
614     ShareData *sd;
615 
616     sd = getSendData();
617     printString("\\2Send:     0x%04x 0x%04x\n", sd->key, sd->count & 0xffff);
618 
619     printString("\\4Receive:\n");
620     for (i = 1; i < (WM_NUM_MAX_CHILD + 1); i++)
621     {
622         if (sRecvFlag[i])
623         {
624             sd = getRecvData(i);
625             printString("\\4Child%02d:  0x%04x 0x%04x\n", i, sd->key, sd->count & 0xffff);
626 
627         }
628         else
629         {
630             printString("No child\n");
631         }
632     }
633 }
634 
ModeParent(void)635 static void ModeParent(void)
636 {
637     printString("\n  \\fParent mode\n\n");
638     printShareData();
639 }
640 
ModeChild(void)641 static void ModeChild(void)
642 {
643     ShareData *sd;
644 
645     printString("\n  \\fChild mode\n\n");
646     printShareData();
647 
648     sd = (ShareData *) getRecvData(0);
649     printString("\\4Parent:   0x%04x 0x%04x\n", sd->key, sd->count & 0xffff);
650 }
651 
VBlankIntr(void)652 static void VBlankIntr(void)
653 {
654     /*
655        Some other samples use StepDataSharing here (the VBlankIntr function).
656 
657        This sample also performed StepDataSharing here before.
658 
659        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.
660 
661 
662        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.
663 
664        (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.)
665 
666 
667        For 30-fps (and other) games, code is normally required to limit the number of StepDS calls to one every two frames.
668 
669        In that case, wait only a single frame (instead of two) when WM_ERRCODE_NO_DATASET is returned (if processing was lost).
670 
671        Otherwise, when there is one frame difference between the parent and child, it cannot be corrected.
672      */
673 
674     updateKeys();
675     OS_SetIrqCheckFlag(OS_IE_V_BLANK);
676 }
677 
initAllocSystem(void)678 static void initAllocSystem(void)
679 {
680     void   *tempLo;
681     OSHeapHandle hh;
682 
683     tempLo = OS_InitAlloc(OS_ARENA_MAIN, OS_GetMainArenaLo(), OS_GetMainArenaHi(), 1);
684     OS_SetArenaLo(OS_ARENA_MAIN, tempLo);
685     hh = OS_CreateHeap(OS_ARENA_MAIN, OS_GetMainArenaLo(), OS_GetMainArenaHi());
686     if (hh < 0)
687     {
688         OS_Panic("ARM9: Fail to create heap...\n");
689     }
690     hh = OS_SetCurrentHeap(OS_ARENA_MAIN, hh);
691 }
692 
drawPowerGraph(void)693 static void drawPowerGraph(void)
694 {
695     static const GXRgb linecolor[4] = {
696         GX_RGB(15, 0, 0),              // Dark red (dead)
697         GX_RGB(31, 31, 0),             // Yellow
698         GX_RGB(0, 31, 0),              // Green
699         GX_RGB(20, 31, 20),            // Light green
700     };
701 
702     int     midx, ringidx;
703 
704     ringidx = (sFrame % GRAPH_TOTAL_FRAME);
705 
706     for (midx = 0; midx < WM_NUM_MAX_CHILD + 1; ++midx)
707     {
708         sGraphData[midx][ringidx].data = (s16)getRecvData(midx)->data;
709         sGraphData[midx][ringidx].level = (u16)getRecvData(midx)->level;
710     }
711 
712     G3_PolygonAttr(GX_LIGHTMASK_NONE,
713                    GX_POLYGONMODE_MODULATE, GX_CULL_NONE, 0, 31, GX_POLYGON_ATTR_MISC_DISP_1DOT);
714     G3_TexImageParam(GX_TEXFMT_NONE,
715                      GX_TEXGEN_NONE,
716                      GX_TEXSIZE_S16,
717                      GX_TEXSIZE_T16, GX_TEXREPEAT_NONE, GX_TEXFLIP_NONE, GX_TEXPLTTCOLOR0_USE, 0);
718 
719     G3_MtxMode(GX_MTXMODE_POSITION_VECTOR);
720     G3_PushMtx();
721     G3_Identity();
722     G3_Translate(0, 0, -FX16_ONE * 4);
723 
724     G3_Begin(GX_BEGIN_TRIANGLES);
725 
726     for (midx = 1; midx < WM_NUM_MAX_CHILD + 1; ++midx)
727     {
728         int     basey, ys, ye, gi, x, level;
729         basey = ((WM_NUM_MAX_CHILD / 2 - midx) * 9 + 6) * FX16_ONE / 64;
730 
731         for (gi = 0; gi < GRAPH_TOTAL_FRAME; ++gi)
732         {
733             int     ri;
734             ri = (ringidx - gi);
735             if (ri < 0)
736             {
737                 ri += GRAPH_TOTAL_FRAME;
738             }
739 
740             ys = sGraphData[midx][ri].data;
741             level = sGraphData[midx][ri].level;
742 
743             ++ri;
744             if (ri >= GRAPH_TOTAL_FRAME)
745             {
746                 ri -= GRAPH_TOTAL_FRAME;
747             }
748 
749             ye = sGraphData[midx][ri].data;
750 
751             x = -(gi - GRAPH_TOTAL_FRAME / 2) * 3;
752             x *= FX16_ONE / 64;
753             ys = ys * FX16_ONE / 64 + basey;
754             ye = ye * FX16_ONE / 64 + basey;
755 
756             G3_Color(linecolor[level]);
757 
758             G3_Vtx((fx16)x, (fx16)ys, 0);
759             G3_Vtx((fx16)(x + FX16_ONE / 64 / 2), (fx16)(ys + FX16_ONE / 64), 0);
760             G3_Vtx((fx16)(x + 3 * FX16_ONE / 64), (fx16)ye, 0);
761         }
762     }
763 
764     G3_End();
765     G3_PopMtx(1);
766 }
767 
drawPowerIcon(void)768 static void drawPowerIcon(void)
769 {
770     // GUIDELINE
771     // Display the radio reception strength icon
772     setupPseudo2DCamera();
773 
774     G3_PolygonAttr(GX_LIGHTMASK_NONE, GX_POLYGONMODE_DECAL, GX_CULL_NONE, 0, 31, 0);
775     G3_PushMtx();
776     G3_MtxMode(GX_MTXMODE_TEXTURE);
777     G3_Identity();
778 
779     G3_TexImageParam(GX_TEXFMT_PLTT16,
780                      GX_TEXGEN_TEXCOORD,
781                      GX_TEXSIZE_S16,
782                      GX_TEXSIZE_T16,
783                      GX_TEXREPEAT_NONE,
784                      GX_TEXFLIP_NONE,
785                      GX_TEXPLTTCOLOR0_USE, (u32)(0x2000 + WM_GetLinkLevel() * 16 * 16 / 2));
786     G3_TexPlttBase(0x2000, GX_TEXFMT_PLTT16);
787     G3_MtxMode(GX_MTXMODE_POSITION_VECTOR);
788     drawPseudo2DTexQuad(224, 160, 16, 16, 16, 16);
789 }
790 
drawRadioIcon(void)791 static void drawRadioIcon(void)
792 {
793     // GUIDELINE
794     // Display the radio wave strength icon
795     int     i;
796     G3_TexPlttBase(0x2000, GX_TEXFMT_PLTT16);
797     G3_TexImageParam(GX_TEXFMT_PLTT16,
798                      GX_TEXGEN_TEXCOORD,
799                      GX_TEXSIZE_S16,
800                      GX_TEXSIZE_T16,
801                      GX_TEXREPEAT_NONE,
802                      GX_TEXFLIP_NONE, GX_TEXPLTTCOLOR0_USE, 0x2000 + 4 * 16 * 16 / 2);
803 
804     for (i = 0; i < 2; ++i)
805     {
806         drawPseudo2DTexQuad(16,
807                             12 + i * 24 + ((i == sRoleMenuWindow.selected) ? (sFrame / 15 & 1) : 0),
808                             16, 16, 16, 16);
809     }
810 }
811 
updateShareData(void)812 static void updateShareData(void)
813 {
814     if (WH_GetSystemState() == WH_SYSSTATE_DATASHARING)
815     {
816         if (WH_StepDS(sSendBuf))
817         {
818             u16     i;
819             for (i = 0; i < WH_CHILD_MAX + 1; ++i)
820             {
821                 u8     *adr;
822                 ShareData *sd;
823 
824                 adr = (u8 *)WH_GetSharedDataAdr(i);
825                 sd = (ShareData *) & (sRecvBuf[i * sizeof(ShareData)]);
826 
827                 if (adr != NULL)
828                 {
829                     MI_CpuCopy8(adr, sd, sizeof(ShareData));
830                     sRecvFlag[i] = TRUE;
831 
832                 }
833                 else
834                 {
835                     sd->level = 0;
836                     sd->data = 0;
837                     sRecvFlag[i] = FALSE;
838                 }
839             }
840             for (; i < WM_NUM_MAX_CHILD + 1; ++i)
841             {
842                 ShareData *sd;
843 
844                 sd = (ShareData *) & (sRecvBuf[i * sizeof(ShareData)]);
845 
846                 sd->level = 0;
847                 sd->data = 0;
848                 sRecvFlag[i] = FALSE;
849             }
850             sNeedWait = FALSE;
851 
852         }
853         else
854         {
855             u16     i;
856             for (i = 0; i < WM_NUM_MAX_CHILD + 1; ++i)
857             {
858                 sRecvFlag[i] = FALSE;
859             }
860 
861             sNeedWait = TRUE;
862         }
863 
864     }
865     else
866     {
867         u16     i;
868         for (i = 0; i < WM_NUM_MAX_CHILD + 1; ++i)
869         {
870             sRecvFlag[i] = FALSE;
871         }
872 
873         sNeedWait = FALSE;
874     }
875 }
876 
packSendData(void)877 static void packSendData(void)
878 {
879     ShareData *senddata;
880 
881     if (sNeedWait)
882     {
883         return;
884     }
885 
886     senddata = getSendData();
887     senddata->command = (sSysMode == SYSMODE_LOBBY) ? SHARECMD_NONE : SHARECMD_EXITLOBBY;
888 
889     senddata->level = (u16)WM_GetLinkLevel();
890 
891     senddata->data = 0;
892     senddata->key = getKeyInfo()->cnt;
893     senddata->count = sFrame & 0xffff;
894 
895     if (sBeaconCount != 0)
896     {
897         senddata->data += sBeaconCount * senddata->level;
898 
899         if (sBeaconCount > 0)
900         {
901             sBeaconCount = -sBeaconCount + 1;
902         }
903         else
904         {
905             sBeaconCount = -sBeaconCount - 1;
906         }
907     }
908 }
909 
mainLoop(void)910 static void mainLoop(void)
911 {
912     int retry = 0;
913     enum {
914         MAX_RETRY = 5
915     };
916 
917     for (sFrame = 0; TRUE; sFrame++)
918     {
919         int     whstate;
920         int     prevmode;
921 
922         whstate = WH_GetSystemState();
923         prevmode = sSysMode;
924 
925         switch (whstate)
926         {
927         case WH_SYSSTATE_CONNECT_FAIL:
928             // Retry several times when a connection fails
929             if (sSysMode == SYSMODE_LOBBYWAIT && retry < MAX_RETRY)
930             {
931                 changeSysMode(SYSMODE_SELECT_PARENT);
932                 sSysModeStep = 1; // So that window does not open...
933                 retry++;
934                 break;
935             }
936             // When a retry fails, transition unchanged to the error state
937 
938         case WH_SYSSTATE_ERROR:
939             // WH state has the priority if an error occurred
940             changeSysMode(SYSMODE_ERROR);
941             break;
942 
943         case WH_SYSSTATE_MEASURECHANNEL:
944             {
945                 u16     channel = WH_GetMeasureChannel();
946                 sTgid++;
947                 (void)WH_ParentConnect(WH_CONNECTMODE_DS_PARENT, sTgid, channel);
948             }
949             break;
950 
951         default:
952             break;
953         }
954 
955         PR_ClearScreen(&sInfoScreen);
956 
957         // Load test
958         forceSpinWait();
959 
960         switch (sSysMode)
961         {
962         case SYSMODE_SELECT_ROLE:
963             // Role (parent or child) selection screen
964             ModeSelectRole();
965             retry = 0;
966             break;
967 
968         case SYSMODE_SELECT_CHANNEL:
969             // Channel selection screen
970             ModeSelectChannel();
971             break;
972 
973         case SYSMODE_LOBBY:
974             // Lobby screen
975             ModeLobby();
976             break;
977 
978         case SYSMODE_LOBBYWAIT:
979             // Lobby standby screen
980             ModeLobbyWait();
981             break;
982 
983         case SYSMODE_OPTION:
984             // Option screen
985             ModeOption();
986             break;
987 
988         case SYSMODE_SCAN_PARENT:
989         case SYSMODE_SELECT_PARENT:
990             // Parent selection screen
991             ModeSelectParent();
992             break;
993 
994         case SYSMODE_ERROR:
995             // Error report screen
996             ModeError();
997             break;
998 
999         case SYSMODE_PARENT:
1000             // Parent mode screen
1001             ModeParent();
1002             break;
1003 
1004         case SYSMODE_CHILD:
1005             // Child mode screen
1006             ModeChild();
1007             break;
1008 
1009         default:
1010             break;
1011         }
1012 
1013         if ((whstate == WH_SYSSTATE_BUSY)
1014             || ((whstate == WH_SYSSTATE_SCANNING) && (sSelectParentWindow.itemnum == 0)))
1015         {
1016             sBusyWindow.state = WIN_STATE_OPENED;
1017 
1018         }
1019         else
1020         {
1021             sBusyWindow.state = WIN_STATE_CLOSED;
1022         }
1023 
1024         updateAllWindow();
1025         drawAllWindow();
1026 
1027         drawPowerIcon();
1028 
1029         if ((sSysMode == SYSMODE_PARENT) || (sSysMode == SYSMODE_CHILD))
1030         {
1031             if (sGraphEnabled)
1032             {
1033                 drawPowerGraph();
1034             }
1035         }
1036 
1037         if ((sSysMode == SYSMODE_SELECT_ROLE) && (sRoleMenuWindow.state == WIN_STATE_OPENED))
1038         {
1039             drawRadioIcon();
1040         }
1041 
1042         G3_SwapBuffers(GX_SORTMODE_AUTO, GX_BUFFERMODE_W);
1043 
1044         if (!sNeedWait && ((sSysMode == SYSMODE_PARENT) || (sSysMode == SYSMODE_CHILD)))
1045         {
1046             // ... In an actual game, the update processing for characters and so on is performed here
1047             //
1048             int     i;
1049             for (i = 0; i < BALL_PLAYER_MAX; ++i)
1050             {
1051                 if (sRecvFlag[i])
1052                 {
1053                     ShareData *sd = getRecvData(i);
1054                     InputBallKey((int)i, (int)sd->key);
1055                 }
1056             }
1057 
1058             // Update the balls' state using data that could be shared.
1059             //   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.
1060             //
1061             UpdateBalls(WH_GetCurrentAid());
1062 
1063             // Prepare the display data
1064             for (i = 0; i < BALL_PLAYER_MAX; ++i)
1065             {
1066                 G2_SetOBJAttr(&oamBak[i],
1067                               shared->ball[i].x, shared->ball[i].y, 0,
1068                               GX_OAM_MODE_NORMAL, FALSE, GX_OAM_EFFECT_NONE,
1069                               GX_OAM_SHAPE_8x8, GX_OAM_COLOR_16,
1070                               shared->ball[i].chr, shared->ball[i].plt, 0);
1071             }
1072         }
1073 
1074         DC_FlushRange(sInfoScreen.screen, sizeof(u16) * PR_SCREEN_SIZE);
1075         /* I/O register is accessed using DMA operation, so cache wait is not needed */
1076 
1077         // DC_WaitWriteBufferEmpty();
1078         GX_LoadBG2Scr(sInfoScreen.screen, 0, sizeof(u16) * PR_SCREEN_SIZE);
1079         DC_FlushRange(sDebugScreen.screen, sizeof(u16) * PR_SCREEN_SIZE);
1080         /* I/O register is accessed using DMA operation, so cache wait is not needed */
1081 
1082         // DC_WaitWriteBufferEmpty();
1083         GXS_LoadBG2Scr(sDebugScreen.screen, 0, sizeof(u16) * PR_SCREEN_SIZE);
1084         DC_FlushRange(&oamBak, sizeof(oamBak));
1085         if ((sSysMode == SYSMODE_PARENT) || (sSysMode == SYSMODE_CHILD))
1086         {
1087             GX_LoadOAM(&oamBak, 0, sizeof(oamBak));
1088         }
1089         else
1090         {
1091             // No display of entire OAM
1092             MI_CpuFillFast((void *)HW_OAM, 192, HW_OAM_SIZE);
1093         }
1094 
1095         OS_WaitVBlankIntr();
1096 
1097         packSendData();
1098         updateShareData();
1099 
1100         if (prevmode == sSysMode)
1101         {
1102             ++sSysModeStep;
1103         }
1104     }
1105 }
1106 
1107 #ifdef SDK_TWL
TwlMain(void)1108 void TwlMain(void)
1109 #else
1110 void NitroMain(void)
1111 #endif
1112 {
1113     OS_Init();
1114     FX_Init();
1115 
1116     initGraphics();
1117 
1118     GX_SetBankForBG(GX_VRAM_BG_256_AB);
1119     GX_SetBankForBGExtPltt(GX_VRAM_BGEXTPLTT_01_F);
1120     G2_SetBG2ControlText(GX_BG_SCRSIZE_TEXT_256x256,
1121                          GX_BG_COLORMODE_16, GX_BG_SCRBASE_0x0000, GX_BG_CHARBASE_0x04000);
1122 
1123     GX_BeginLoadTex();
1124     GX_LoadTex(wlicon_image, 0x2000, 16 * 16 * 5);
1125     GX_EndLoadTex();
1126 
1127     GX_BeginLoadTexPltt();
1128     GX_LoadTexPltt(wlicon_palette, 0x2000, 32);
1129     GX_EndLoadTexPltt();
1130 
1131     GX_LoadBG2Char(d_CharData, 0, sizeof(d_CharData));
1132     GX_LoadBGPltt(d_PaletteData, 0, sizeof(d_PaletteData));
1133 
1134     GX_SetBankForSubBG(GX_VRAM_SUB_BG_128_C);
1135     GX_SetBankForSubBGExtPltt(GX_VRAM_SUB_BGEXTPLTT_0123_H);
1136     G2S_SetBG2ControlText(GX_BG_SCRSIZE_TEXT_256x256,
1137                           GX_BG_COLORMODE_16, GX_BG_SCRBASE_0x0000, GX_BG_CHARBASE_0x04000);
1138 
1139     GXS_SetGraphicsMode(GX_BGMODE_0);
1140     GXS_SetVisiblePlane(GX_PLANEMASK_BG2);
1141     G2S_SetBG2Priority(0);
1142     G2S_BG2Mosaic(FALSE);
1143 
1144     GXS_LoadBG2Char(d_CharData, 0, sizeof(d_CharData));
1145     GXS_LoadBGPltt(d_PaletteData, 0, sizeof(d_PaletteData));
1146 
1147     GX_SetBankForOBJ(GX_VRAM_OBJ_16_G);
1148     GX_LoadOBJ(d_CharData, 0, sizeof(d_CharData));
1149     GX_LoadOBJPltt(d_PaletteData, 0, sizeof(d_PaletteData));
1150     GX_SetVisiblePlane(GX_GetVisiblePlane() | GX_PLANEMASK_OBJ);
1151 
1152     initAllocSystem();
1153 
1154     OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
1155     (void)OS_EnableIrqMask(OS_IE_V_BLANK);
1156     (void)GX_VBlankIntr(TRUE);
1157     (void)OS_EnableIrq();
1158     (void)OS_EnableInterrupts();
1159 
1160     sInfoScreen.scroll = FALSE;
1161     sDebugScreen.scroll = TRUE;
1162 
1163     // Load owner information
1164     OS_GetOwnerInfo(&sMyInfo);
1165     sGameInfo.nickNameLength = (u8)sMyInfo.nickNameLength;
1166     MI_CpuCopy8(sMyInfo.nickName, sGameInfo.nickName, MATH_MIN(sMyInfo.nickNameLength * 2, sizeof(sGameInfo.nickName)));
1167 
1168     // Initialize wireless
1169     // Set callback specified in the WM_SetIndCallback called within wh
1170     WH_SetIndCallback(indicateCallback);
1171     if (!WH_Initialize())
1172     {
1173         OS_Panic("WH_Initiailze failed.");
1174     }
1175 
1176     // WH initialization settings
1177     WH_SetGgid(WH_GGID);
1178     WH_SetDebugOutput(TraceWH);
1179 
1180     GX_DispOn();
1181     GXS_DispOn();
1182 
1183     initWindow(&sRoleMenuWindow);
1184     initWindow(&sSelectChannelWindow);
1185     initWindow(&sSelectParentWindow);
1186     initWindow(&sLobbyWindow);
1187     initWindow(&sErrorWindow);
1188     initWindow(&sOptionWindow);
1189     initWindow(&sWaitWindow);
1190     initWindow(&sBusyWindow);
1191 
1192     setupWindow(&sBusyWindow, 64, 80, WIN_FLAG_NOCONTROL, 8, 8, 16);
1193     addItemToWindow(&sBusyWindow, "\\2Working...");
1194 
1195     // Countermeasure for pressing A Button in IPL
1196     updateKeys();
1197 
1198     // initCharacter();
1199     changeSysMode(SYSMODE_SELECT_ROLE);
1200     mainLoop();
1201 }
1202