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