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