1 /*---------------------------------------------------------------------------*
2   Project:  Revolution MPDS demo
3   File:     mpdsmodel.c
4 
5   Copyright 2007 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   $Log: mpdsmodel.c,v $
14   Revision 1.19  2007/11/29 03:20:27  seiki_masashi
15   Added the const modifier.
16 
17   Revision 1.18  2007/11/21 06:53:14  yosizaki
18   Reapplied the rolled-back content.
19 
20   Revision 1.16  2007/10/27 11:20:46  seiki_masashi
21   Small fix
22 
23   Revision 1.15  2007/10/26 12:15:00  yosizaki
24   Added ball display.
25 
26   Revision 1.14  2007/10/26 09:31:17  seiki_masashi
27   Cleaned up the code
28 
29   Revision 1.13  2007/10/26 07:29:56  seiki_masashi
30   Cleaned up the code
31 
32   Revision 1.12  2007/10/26 07:05:28  seiki_masashi
33   Revised the link-level display
34 
35   Revision 1.10  2007/10/25 03:20:53  seiki_masashi
36   Support for transformations that correspond to the ROM font encoding used by child device nicknames when they are displayed.
37 
38   Revision 1.8  2007/10/24 13:50:49  seiki_masashi
39   Added a feature to display nicknames in the Lobby.
40 
41   Revision 1.5  2007/10/24 01:38:51  seiki_masashi
42   Revised the method by which connection status is displayed.
43 
44   Revision 1.3  2007/10/23 13:43:34  seiki_masashi
45   Revised so that pad input is sent.
46 
47   Revision 1.2  2007/10/23 13:08:49  seiki_masashi
48   Changed to be multithreaded.
49 
50   Revision 1.1  2007/10/23 00:05:57  seiki_masashi
51   Added the mpdsmodel demo.
52 
53   $NoKeywords: $
54  *---------------------------------------------------------------------------*/
55 
56 //
57 // This sample performs communications with the demos/wm/dataShare-Model demo in the NITRO-SDK.
58 //
59 // This is a demo that shows the sequence of accepting a new connection from a child at the lobby screen, and then starting a battle once the connection is closed.
60 //
61 //
62 
63 #include <string.h>
64 #include <revolution.h>
65 #include <revolution/mp.h>
66 #include <revolution/mpds.h>
67 #include <revolution/mem.h>
68 #include <revolution/enc.h>
69 
70 #include "rexdemo/demokpad.h"
71 #include "rexdemo/graphic.h"
72 
73 #include "ball.h"
74 
75 #define     USERHEAP_SIZE ( 65536 )
76 
77 #define     MY_NICKNAME         L"Wii" // the nickname to display in the parent device selection screen on the child device
78 #define     MY_NICKNAME_LENGTH  3      // the number of UTF16BE characters in MY_NICKNAME (maximum of 10)
79 
80 #define     MY_MAX_NODES    8   // maximum number of child devices that can be connected (up to a maximum of 8 devices are guaranteed in Wii Sequential communications)
81 #define     MY_DS_NODES     16  // the number of nodes, including parent devices, that perform DataSharing (matches the dataShare-Model demo in the NITRO-SDK)
82                                 // for a normal application (MY_MAX_NODES+1)
83 
84 #define     MY_DS_LENGTH    12  // the shared data size per device for DataSharing
85 #define     MY_DS_PORT      13  // the port number to use for DataSharing (matches the dataShare-Model demo in the NITRO-SDK)
86 
87 // When DataSharing is performed, the required transmission size is the shared size per device times the number of devices, plus 4 bytes.
88 #define     MY_PARENT_MAX_SIZE ( (MY_DS_LENGTH * MY_DS_NODES) + MPDS_HEADER_SIZE )
89 
90 #define     STACK_SIZE      8192 // thread stack size; you must also consider the amount used by interrupt processing
91 #define     THREAD_PRIORITY 14   // thread priority; specify a higher priority than the main thread (16)
92 
93 /*===========================================================================*/
94 
95 // application-specific UserGameInfo
96 // note that processing must be done in Little Endian to match the DS
97 #pragma pack(push,1)
98 typedef struct MyGameInfo
99 {
100     u8      nickName[10 * 2];
101     u8      nickNameLength;
102     u8      entryCount;
103     u16     periodicalCount;
104 }
105 MyGameInfo;
106 #pragma pack(pop)
107 
108 // shared data structure
109 // note that processing must be done in Little Endian to match the DS
110 #pragma pack(push,1)
111 typedef struct ShareData
112 {
113     u8 data:3;               // For graphing
114     u8 level:2;              // Radio reception strength
115     u8 command:3;            // Instruction (used for switching the game states at once, etc.)
116     u16 key;                 // Key data
117     u32 count:24;            // Frame count
118 }
119 ShareData;
120 #pragma pack(pop)
121 
122 enum
123 {
124     SHARECMD_NONE = 0,                 // Nothing specific (normal)
125     SHARECMD_EXITLOBBY,                // Signal for ending the lobby screen
126     SHARECMD_NUM
127 };
128 
129 enum
130 {
131     STATE_IDLE = 0,
132     STATE_GOING_TO_LOBBY,
133     STATE_LOBBY,
134     STATE_GOING_TO_COMMUNICATION,
135     STATE_COMMUNICATION,
136     STATE_GOING_TO_IDLE
137 };
138 
139 enum
140 {
141     RESULT_OK = 0,
142     RESULT_ERROR = -1,
143     RESULT_QUIT = -2
144 };
145 
146 /*===========================================================================*/
147 
148 static const GXColor white = { 0xFF, 0xFF, 0xFF, };
149 static const GXColor yellow = { 0xFF, 0xFF, 0x00, };
150 static const GXColor gray = { 0x80, 0x80, 0x80, };
151 static const GXColor black = { 0x00, 0x00, 0x00, };
152 static const GXColor red = { 0xFF, 0x00, 0x18 };
153 static const GXColor green = { 0x00, 0xFF, 0x00 };
154 
155 static MEMHeapHandle    sUserHeap;
156 
157 static ShareData sRecvData[MY_MAX_NODES+1];
158 static BOOL sRecvFlag[MY_MAX_NODES+1];
159 static BOOL sConnectedFlag[MY_MAX_NODES+1];
160 static u8 sConnectedMacAddress[MY_MAX_NODES+1][MP_SIZE_MACADDRESS];
161 static u16 sConnectedNickName[MY_MAX_NODES+1][10 + 1];
162 
163 static u32 sFrameCount = 0;
164 static s32 sState = STATE_IDLE;
165 static OSThreadQueue sStateChangeThreadQueue;
166 static BOOL sQuitFlag = FALSE;
167 
168 static OSMutex sGlobalMutex;
169 
170 static OSThread sStateManagementThread;
171 static OSThread sDataSharingThread;
172 static OSThread sUpdateGameInfoThread;
173 
174 static u8 sStateManagementThreadStack[STACK_SIZE] ATTRIBUTE_ALIGN(32);
175 static u8 sDataSharingThreadStack[STACK_SIZE] ATTRIBUTE_ALIGN(32);
176 static u8 sUpdateGameInfoThreadStack[STACK_SIZE] ATTRIBUTE_ALIGN(32);
177 
178 static MPDSContext sDSContext;
179 static MyGameInfo sMyGameInfo;
180 static ENCContext sEncContext;
181 
182 /*===========================================================================*/
183 
184 static void* StateManagementThreadFunc( void* arg );
185 static void* DataSharingThreadFunc( void* arg );
186 static void* UpdateGameInfoThreadFunc( void* arg );
187 
188 static void Initialize( void );
189 static void MainLoop( void );
190 
191 static void Draw( void );
192 static void SetState( s32 state );
193 static inline s32 GetState( void );
194 static s32 WaitForStateChange( void );
195 static void QuitMP( void );
196 static BOOL IsQuitting( void );
197 static s32 TransToLobby( void );
198 static s32 TransToCommunication( void );
199 static s32 TransToIdle( void );
200 static s32 DoLobbyMode( void );
201 static s32 DoCommunicationMode( void );
202 static void DataSharingCallback( s32 type, MPPortCallbackInfo* info );
203 
204 static void* mpAlloc( u32 size );
205 static void mpFree( void* ptr );
206 
207 static u16 ConvKPadKeyToDSPad(u32 mixedKey);
208 
EnterCriticalSection(void)209 static inline void EnterCriticalSection( void )
210 {
211     (void)OSLockMutex( &sGlobalMutex );
212 }
213 
LeaveCriticalSection(void)214 static void LeaveCriticalSection( void )
215 {
216     (void)OSUnlockMutex( &sGlobalMutex );
217 }
218 
219 /*===========================================================================*/
220 
221 // The GGID is a unique ID for MP communications assigned to each application.
222 //   You must receive a GGID assignment from Nintendo for an actual application.
223 //   Here, the GGID is used that is identical to that of the dataShare-Model demo.
224 #define MY_GGID     0x003fff13
225 
226 // MP communication settings
227 static MPConfig sMpConfig =
228 {
229     mpAlloc,
230     mpFree,
231 
232     8,                  // threadPriority; set to a priority that is higher than that of the main thread by 4 or more
233 
234     MP_MODE_PARENT,     // mode
235 
236     MY_GGID,            // ggid
237     MP_TGID_AUTO,       // tgid
238 
239     MP_CHANNEL_AUTO,    // channel; normally MP_CHANNEL_AUTO
240 
241     MP_LIFETIME_DEFAULT,// lifeTime; usually equal to MP_LIFETIME_DEFAULT
242 
243     MP_BEACON_PERIOD_AUTO,  // beaconPeriod; normally MP_BEACON_PERIOD_AUTO
244     MY_MAX_NODES,       // maxNodes
245     MY_PARENT_MAX_SIZE, // parentMaxSize
246     MY_DS_LENGTH,       // childMaxSize
247     TRUE,               // entryFlag
248     FALSE,              // multiBootFlag; usually FALSE
249 
250     1,                  // frequency
251 
252     0,                  // userGameInfoLength
253     { 0, },             // userGameInfo; set MyGameInfo in the program
254 
255     NULL,               // indicationCallbackFunction
256 
257     /// ...             // port configuration (can be set up using MPSetPortConfig)
258 };
259 
260 // DataSharing configuration
261 static MPDSConfig sMpdsConfig =
262 {
263     MY_DS_LENGTH,       // dataLength
264 
265     MY_DS_PORT,         // port (must use one of sequential communications ports 12-15)
266     MP_PRIORITY_HIGH,   // priority
267 
268     TRUE,               // isParent; always TRUE
269     (1 << MY_DS_NODES)-1,   // aidBits (a binary number that is a row of 1's. The number of 1's is equal to MY_DS_NODES)
270     TRUE,               // isDoubleMode
271     TRUE,               // isAutoStart; always TRUE
272     DataSharingCallback // mpdsCallback; NULL if unnecessary
273 };
274 
275 
main(void)276 void main(void)
277 {
278     OSReport("test program started.\n");
279 
280     Initialize();
281 
282     MainLoop();
283 }
284 
Initialize(void)285 void Initialize( void )
286 {
287     /* Initialize OS and memory heap */
288     REXDEMOKPadInit();
289     REXDEMOInitScreen( FALSE );
290     REXDEMOSetGroundColor( black );
291     REXDEMOSetFontSize( 10, 20 );
292     REXDEMOBeginRender();
293     REXDEMOWaitRetrace();
294     (void)PADInit();
295 
296     /* Initialize heap for MP library */
297     {
298         void*   heapAddress;
299 
300         heapAddress = OSGetMEM2ArenaLo();
301         OSSetMEM2ArenaLo( (void*)OSRoundUp32B( (u32)heapAddress + USERHEAP_SIZE ) );
302         sUserHeap   = MEMCreateExpHeapEx( heapAddress, USERHEAP_SIZE, MEM_HEAP_OPT_THREAD_SAFE );
303         if( sUserHeap == NULL )
304         {
305             OSHalt( "Could not create heap.\n" );
306         }
307     }
308 
309     OSInitMutex( &sGlobalMutex );
310     OSInitThreadQueue( &sStateChangeThreadQueue );
311 
312     // Prepare ENCContext to match the ROM font's encoding, since string display uses the ROM font
313     //
314     (void)ENCInitContext(&sEncContext);
315     (void)ENCSetExternalEncoding(&sEncContext,
316                                  ( OSGetFontEncode() == OS_FONT_ENCODE_SJIS )
317                                  ? (const u8*)"Shift_JIS" : (const u8*)"ISO-8859-1");
318     (void)ENCSetBreakType(&sEncContext, ENC_BR_KEEP);
319     (void)ENCSetAlternativeCharacter(&sEncContext, L'?', L'?');
320 
321     sState = STATE_IDLE;
322 
323     // Create a status management thread
324     (void)OSCreateThread( &sStateManagementThread, StateManagementThreadFunc, NULL,
325                           (void*)((u32)sStateManagementThreadStack + STACK_SIZE), STACK_SIZE,
326                           THREAD_PRIORITY, OS_THREAD_ATTR_DETACH );
327     (void)OSResumeThread( &sStateManagementThread );
328 }
329 
MainLoop(void)330 void MainLoop( void )
331 {
332     while (TRUE)
333     {
334         // Use exclusive access when touching global variables shared with other threads.
335         EnterCriticalSection();
336         REXDEMOKPadRead();
337         LeaveCriticalSection();
338 
339         if ( OSIsThreadTerminated(&sStateManagementThread) )
340         {
341             // Exit the main loop because all threads have terminated.
342             OSReport("All threads are terminated; exit from MainLoop\n");
343             break;
344         }
345 
346         Draw();
347 
348         REXDEMOWaitRetrace();
349         sFrameCount++;
350     }
351 }
352 
Draw(void)353 void Draw( void )
354 {
355     s32 state;
356     s32 i;
357 
358     REXDEMOBeginRender();
359 
360     {
361         // Use exclusive access when touching global variables shared with other threads.
362         EnterCriticalSection(); // Begin exclusive access
363         state = GetState();
364 
365         switch ( state )
366         {
367         case STATE_LOBBY:
368             REXDEMOSetTextColor(green);
369             REXDEMOPrintf(40, 8, 0, "Lobby mode");
370             REXDEMOSetTextColor(white);
371             REXDEMOPrintf(4, 400, 0, "push <A> button to start.");
372 
373             for (i = 0; i < MY_MAX_NODES+1; i++)
374             {
375                 if (sConnectedFlag[i])
376                 {
377                     char nickName[20+1];
378                     {
379                         ENCContext convCtx;
380                         s32 dstlen, srclen;
381                         dstlen = sizeof(nickName) - 1 /* NULL termination*/;
382                         srclen = -1; // Convert up to the NULL terminator
383                         (void)NETMemSet(nickName, 0, sizeof(nickName));
384                         (void)ENCDuplicateContext(&convCtx, &sEncContext);
385                         (void)ENCConvertFromInternalEncoding(&convCtx, (u8*)nickName, &dstlen, sConnectedNickName[i], &srclen);
386                     }
387                     REXDEMOSetTextColor(yellow);
388                     REXDEMOPrintf(40, (s16)(80 + i * 20), 0,
389                                   "AID(%02d): entry %02X:%02X:%02X:%02X:%02X:%02X %s",
390                                   i,
391                                   sConnectedMacAddress[i][0],
392                                   sConnectedMacAddress[i][1],
393                                   sConnectedMacAddress[i][2],
394                                   sConnectedMacAddress[i][3],
395                                   sConnectedMacAddress[i][4],
396                                   sConnectedMacAddress[i][5],
397                                   nickName);
398                 }
399                 else
400                 {
401                     REXDEMOSetTextColor(gray);
402                     REXDEMOPrintf(40, (s16)(80 + i * 20), 0, "AID(%02d): --------", i);
403                 }
404             }
405             break;
406 
407         case STATE_COMMUNICATION:
408             REXDEMOSetTextColor(green);
409             REXDEMOPrintf(40, 8, 0, "Parent mode");
410             REXDEMOSetTextColor(white);
411             REXDEMOPrintf(4, 400, 0, "push <B> button to restart.");
412 
413             REXDEMOSetTextColor(yellow);
414             REXDEMOPrintf(4, 30, 0, "Send:     0x%04X-0x%04X", 0, sFrameCount & 0xffff);
415             REXDEMOPrintf(4, 52, 0, "Receive:");
416 
417             for (i = 0; i < MY_MAX_NODES+1; i++)
418             {
419                 if (sRecvFlag[i])
420                 {
421                     REXDEMOSetTextColor(yellow);
422                     REXDEMOPrintf(40, (s16)(80 + i * 20), 0,
423                                   "AID(%02d): %04X-%04X",
424                                   i,
425                                   MPMPToH16((u16)sRecvData[i].key), MPMPToH16((u16)(sRecvData[i].count & 0xffff)) );
426                 }
427                 else
428                 {
429                     REXDEMOSetTextColor(red);
430                     REXDEMOPrintf(40, (s16)(80 + i * 20), 0, "No Data");
431                 }
432             }
433             {
434                 static const GXColor    paletteTable[] =
435                 {
436                     { 0x80, 0x80, 0x80, },  // gray
437                     { 0x00, 0xFF, 0x00, },  // green
438                     { 0xFF, 0xFF, 0x00, },  // yellow
439                 };
440                 s16     x   = 320;
441                 s16     y   = 10;
442                 s16     z   = -1023;
443                 for (i = 0; i < BALL_PLAYER_MAX; ++i)
444                 {
445                     REXDEMOSetTextColor(paletteTable[shared->ball[i].plt]);
446                     REXDEMOPrintf(x + shared->ball[i].x, y + shared->ball[i].y, 0, "%c", shared->ball[i].chr);
447                 }
448                 {
449                     static const u32 font[] ATTRIBUTE_ALIGN(32) =
450                     {
451                         0xFFFFFFFF,
452                         0xFFFFFFFF,
453                         0xFFFFFFFF,
454                         0xFFFFFFFF,
455                         0xFFFFFFFF,
456                         0xFFFFFFFF,
457                         0xFFFFFFFF,
458                         0xFFFFFFFF,
459                     };
460                     static const u16 pal[16] ATTRIBUTE_ALIGN(32) =
461                     {
462                     GXPackedRGB5A3(0x00, 0x00, 0x00, 0xFF), /* 0 : black */
463                     GXPackedRGB5A3(0x80, 0x00, 0x00, 0xFF), /* 1 : dark red*/
464                     GXPackedRGB5A3(0x00, 0x80, 0x00, 0xFF), /* 2 : dark green*/
465                     GXPackedRGB5A3(0x80, 0x80, 0x00, 0xFF), /* 3 : dark yellow*/
466                     GXPackedRGB5A3(0x00, 0x00, 0x80, 0xFF), /* 4 : dark blue*/
467                     GXPackedRGB5A3(0x80, 0x00, 0x80, 0xFF), /* 5 : dark purple*/
468                     GXPackedRGB5A3(0x00, 0x80, 0x80, 0x00), /* 6 : (clear) */
469                     GXPackedRGB5A3(0x80, 0x80, 0x80, 0xFF), /* 7 : gray */
470                     GXPackedRGB5A3(0xC0, 0xC0, 0xC0, 0xFF), /* 8 : light gray*/
471                     GXPackedRGB5A3(0xFF, 0x00, 0x00, 0xFF), /* 9 : red */
472                     GXPackedRGB5A3(0x00, 0xFF, 0x00, 0xFF), /* A : green*/
473                     GXPackedRGB5A3(0xFF, 0xFF, 0x00, 0xFF), /* B : yellow*/
474                     GXPackedRGB5A3(0x00, 0x00, 0xFF, 0xFF), /* C : blue*/
475                     GXPackedRGB5A3(0xFF, 0x00, 0xFF, 0xFF), /* D : purple*/
476                     GXPackedRGB5A3(0x00, 0xFF, 0xFF, 0xFF), /* E : cyan*/
477                     GXPackedRGB5A3(0xFF, 0xFF, 0xFF, 0xFF), /* F : white*/
478                     };
479                     static GXTlutObj tlut;
480                     Mtx mtx;
481                     GXTexObj obj;
482                     GXInitTlutObj(&tlut, (void *)pal, GX_TL_RGB5A3, 16);
483                     GXLoadTlut(&tlut, GX_TLUT0);
484                     GXInitTexObjCI(&obj, (void *)font,
485                                    8, 8, GX_TF_C4,
486                                    GX_CLAMP, GX_CLAMP, GX_FALSE, GX_TLUT0);
487                     GXInitTexObjLOD(&obj, GX_NEAR, GX_NEAR,
488                                     0.0f, 0.0f, 0.0f, GX_DISABLE,
489                                     GX_FALSE, GX_ANISO_1);
490                     GXLoadTexObj(&obj, GX_TEXMAP4);
491                     MTXScale(mtx, 1.0f / (float)8, 1.0f / (float)8, 1.0f);
492                     GXLoadTexMtxImm(mtx, GX_TEXMTX4, GX_MTX2x4);
493                     // Temporarily change the REXDEMOBeginRender setting
494                     GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_TEXMTX4);
495                     GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP4, GX_COLOR0A0);
496                     GXBegin(GX_QUADS, GX_VTXFMT0, 4);
497                     {
498                         GXPosition3s16((s16) (x), (s16) (y), (s16) z);
499                         GXTexCoord2s16((s16) (0), (s16) (0));
500                         GXPosition3s16((s16) (x + BALL_FIELD_WIDTH), (s16) (y), (s16) z);
501                         GXTexCoord2s16((s16) (8), (s16) (0));
502                         GXPosition3s16((s16) (x + BALL_FIELD_WIDTH), (s16) (y + BALL_FIELD_HEIGHT), (s16) z);
503                         GXTexCoord2s16((s16) (8), (s16) (8));
504                         GXPosition3s16((s16) (x), (s16) (y + BALL_FIELD_HEIGHT), (s16) z);
505                         GXTexCoord2s16((s16) (0), (s16) (8));
506                     }
507                     GXEnd();
508                     // Restore the REXDEMOBeginRender setting
509                     GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_TEXMTX0);
510                     GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
511                 }
512             }
513 
514             break;
515 
516         default:
517             REXDEMOSetTextColor(green);
518             REXDEMOPrintf(40, 8, 0, "Working...");
519             break;
520         }
521 
522         // The link-level display is not required in the Wii Guidelines.
523         REXDEMOSetTextColor(green);
524         {
525             s32 linkLevel = MPGetLinkLevel();
526             REXDEMOPrintf(480, 400, 0, "Link Level: %d", (linkLevel >= 0) ? linkLevel : 0);
527         }
528 
529         REXDEMOSetTextColor(white);
530 
531         LeaveCriticalSection(); // End exclusive access
532     }
533 }
534 
SetState(s32 state)535 void SetState( s32 state )
536 {
537     // Use exclusive access when touching global variables shared with other threads.
538     EnterCriticalSection(); // Begin exclusive access
539     sState = state;
540     LeaveCriticalSection(); // End exclusive access
541     OSWakeupThread( &sStateChangeThreadQueue );
542 }
543 
GetState(void)544 inline s32 GetState( void )
545 {
546     return sState;
547 }
548 
WaitForStateChange(void)549 s32 WaitForStateChange( void )
550 {
551     OSSleepThread( &sStateChangeThreadQueue );
552     // Note that with high-priority threads, in situations such as when SetState is called multiple times, only the final change to sState will be detected.
553     //
554     return GetState();
555 }
556 
QuitMP(void)557 void QuitMP( void )
558 {
559     sQuitFlag = TRUE;
560 }
561 
IsQuitting(void)562 BOOL IsQuitting( void )
563 {
564     return sQuitFlag;
565 }
566 
567 /////////////////////////////////////////////////////////////////////
568 // Management thread for game state
StateManagementThreadFunc(void * arg)569 void* StateManagementThreadFunc( void* arg )
570 {
571 #pragma unused( arg )
572     s32 result;
573 
574     ///////////////////////////////////////////////////////
575     // STATE_IDLE
576     ///////////////////////////////////////////////////////
577 state_idle:
578     SetState( STATE_IDLE );
579 
580     ///////////////////////////////////////////////////////
581     // STATE_GOING_TO_LOBBY
582     ///////////////////////////////////////////////////////
583 state_going_to_lobby:
584     SetState( STATE_GOING_TO_LOBBY );
585 
586     result = TransToLobby();
587 
588     switch ( result )
589     {
590     case RESULT_OK:
591         // go to next state
592         break;
593 
594     case RESULT_ERROR:
595         goto state_error;
596 
597     case RESULT_QUIT:
598         goto state_going_to_idle;
599     }
600 
601     ///////////////////////////////////////////////////////
602     // STATE_LOBBY
603     ///////////////////////////////////////////////////////
604 state_lobby:
605     SetState( STATE_LOBBY );
606 
607     result = DoLobbyMode();
608 
609     switch ( result )
610     {
611     case RESULT_OK:
612         // go to next state
613         break;
614 
615     case RESULT_ERROR:
616         goto state_error;
617 
618     case RESULT_QUIT:
619         goto state_going_to_idle;
620     }
621 
622     ///////////////////////////////////////////////////////
623     // STATE_GOING_TO_COMMUNICATION
624     ///////////////////////////////////////////////////////
625 state_going_to_communication:
626     SetState( STATE_GOING_TO_COMMUNICATION );
627 
628     (void)TransToCommunication();
629 
630     ///////////////////////////////////////////////////////
631     // STATE_COMMUNICATION
632     ///////////////////////////////////////////////////////
633 state_communication:
634     SetState( STATE_COMMUNICATION );
635 
636     result = DoCommunicationMode();
637 
638     switch ( result )
639     {
640     case RESULT_OK:
641         // go to next state
642         goto state_going_to_idle;
643 
644     case RESULT_ERROR:
645         goto state_error;
646 
647     case RESULT_QUIT:
648         goto state_going_to_idle;
649     }
650 
651     ///////////////////////////////////////////////////////
652     // STATE_GOING_TO_IDLE
653     ///////////////////////////////////////////////////////
654 state_going_to_idle:
655     SetState( STATE_GOING_TO_IDLE );
656 
657     result = TransToIdle();
658 
659     switch ( result )
660     {
661     case RESULT_ERROR:
662         goto state_error;
663     }
664 
665     goto state_idle;
666 
667 state_error:
668     OSReport("Error occurred.\n");
669 
670     return NULL;
671 }
672 
673 ////////////////////////////////////////////
674 // Transition from the idle state to lobby mode
TransToLobby(void)675 s32 TransToLobby( void )
676 {
677     s32 result;
678     int i;
679 
680     // There are no other threads touching these global variables because MP communications have not yet started.
681     // Initialization without exclusive access can be performed.
682     for ( i = 0; i < MY_MAX_NODES+1; i++ )
683     {
684         sRecvFlag[i] = FALSE;
685         sConnectedFlag[i] = FALSE;
686         (void)NETMemSet(sConnectedMacAddress[i], 0, MP_SIZE_MACADDRESS);
687         (void)NETMemSet(sConnectedNickName[i], 0, sizeof(sConnectedNickName[0]));
688     }
689     // Configuration for this device
690     sConnectedFlag[0] = TRUE;
691     (void)NETGetWirelessMacAddress(sConnectedMacAddress[0]);
692     (void)NETMemSet(sConnectedNickName[0], 0, sizeof(sConnectedNickName[0]));
693     (void)NETMemCpy(sConnectedNickName[0], MY_NICKNAME, MY_NICKNAME_LENGTH * 2);
694     ASSERT(MY_NICKNAME_LENGTH * 2 <= sizeof(sConnectedNickName[0]) - 2);
695 
696     sQuitFlag = FALSE;
697 
698 
699     // All session members should be maintained locally, so initialize the game information
700     // (Convert the input data through MP communications, and start updating the content using the same rules)
701     InitBall(NULL, 0, 1, 2);
702 
703 
704     // Initialize UserGameInfo
705     (void)NETMemSet(&sMyGameInfo, 0, sizeof(sMyGameInfo));
706     sMyGameInfo.nickNameLength = MY_NICKNAME_LENGTH;
707     /* Note that endian conversion must be performed for the data being sent to the DS*/
708     NETSwapAndCopyMemory16(sMyGameInfo.nickName, MY_NICKNAME, MY_NICKNAME_LENGTH*sizeof(u16));
709     sMyGameInfo.entryCount = 0;
710     sMyGameInfo.periodicalCount = 0;
711     (void)NETMemCpy( sMpConfig.userGameInfo, &sMyGameInfo, sizeof(sMyGameInfo) );
712     sMpConfig.userGameInfoLength = sizeof(MyGameInfo);
713 
714     result = MPDSInit( &sDSContext, &sMpdsConfig );
715     if ( result < MP_RESULT_OK )
716     {
717         OSReport( "MPDSInit returns %08x\n", result );
718         return RESULT_ERROR;
719     }
720 
721     result = MPDSSetupPortConfig( &sDSContext, &sMpConfig );
722     if ( result < MP_RESULT_OK )
723     {
724         OSReport( "MPDSSetupPortConfig returns %08x\n", result );
725         return RESULT_ERROR;
726     }
727 
728     // Start the MP communication state according to config
729     //   Block in order to wait for the MP state to occur.
730     OSReport("MPStartup()\n");
731     result = MPStartup( &sMpConfig );
732     if( result != MP_RESULT_OK )
733     {
734         OSReport( "MPStartup returns %08x\n", result );
735         return RESULT_ERROR;
736     }
737 
738     // Create a thread for communications
739     (void)OSCreateThread( &sDataSharingThread, DataSharingThreadFunc, NULL,
740                           (void*)((u32)sDataSharingThreadStack + STACK_SIZE), STACK_SIZE,
741                           THREAD_PRIORITY, 0 );
742     (void)OSResumeThread( &sDataSharingThread );
743     (void)OSCreateThread( &sUpdateGameInfoThread, UpdateGameInfoThreadFunc, NULL,
744                           (void*)((u32)sUpdateGameInfoThreadStack + STACK_SIZE), STACK_SIZE,
745                           THREAD_PRIORITY, 0 );
746     (void)OSResumeThread( &sUpdateGameInfoThread );
747 
748     return IsQuitting() ? RESULT_QUIT : RESULT_OK;
749 }
750 
751 ////////////////////////////////////////////
752 // Transition from lobby mode to communications mode
TransToCommunication(void)753 s32 TransToCommunication( void )
754 {
755     s32 result;
756     int i;
757 
758     // Stop accepting new connections
759     result = MPSetEntryFlag( FALSE );
760     if ( result < MP_RESULT_OK )
761     {
762         OSReport( "MPSetEntryFlag returns %08x\n", result );
763         return RESULT_ERROR;
764     }
765 
766     // Update the beacon and send a notification that connections will no longer be accepted.
767     //   Block for a long period of time, until the next beacon is sent.
768     result = MPUpdateBeacon();
769     if ( result < MP_RESULT_OK )
770     {
771         OSReport( "MPUpdateBeacon returns %08x\n", result );
772         return RESULT_ERROR;
773     }
774 
775     // Use exclusive access when touching global variables shared with other threads.
776     EnterCriticalSection();
777     for ( i = 0; i < MY_MAX_NODES+1; i++ )
778     {
779         sRecvFlag[i] = FALSE;
780     }
781     LeaveCriticalSection();
782 
783     return IsQuitting() ? RESULT_QUIT : RESULT_OK;
784 }
785 
786 ////////////////////////////////////////////
787 // Transition to the idle state
TransToIdle(void)788 s32 TransToIdle( void )
789 {
790     s32 result;
791 
792     // Send a termination command to the thread
793     QuitMP();
794 
795     // Ends the MP communication state
796     //   Block for a long period of time to wait for all child devices to disconnect.
797     OSReport("MPCleanup()\n");
798     result = MPCleanup();
799     if ( result < MP_RESULT_OK )
800     {
801         OSReport( "MPCleanup returns %08x\n", result );
802         return RESULT_ERROR;
803     }
804 
805     (void)OSJoinThread( &sDataSharingThread, NULL );
806     (void)OSJoinThread( &sUpdateGameInfoThread, NULL );
807 
808     return RESULT_OK;
809 }
810 
811 ////////////////////////////////////////////
812 // Conditions for state transitions in lobby mode
DoLobbyMode(void)813 s32 DoLobbyMode( void )
814 {
815     BOOL fBreak = FALSE;
816 
817     // Accept new connections from child devices
818     {
819         while ( !fBreak )
820         {
821             // Use exclusive access when touching global variables shared with other threads.
822             EnterCriticalSection();
823             if( REXDEMOGetAnyMixedPadTrigger() & (KPAD_BUTTON_A | (PAD_BUTTON_A << 16)) )
824             {
825                 // Using the A Button, transition to the next state
826                 fBreak = TRUE;
827             }
828             LeaveCriticalSection();
829 
830             VIWaitForRetrace();
831         }
832     }
833 
834     return RESULT_OK;
835 }
836 
837 ////////////////////////////////////////////
838 // Conditions for state transitions in communication mode
DoCommunicationMode(void)839 s32 DoCommunicationMode( void )
840 {
841     BOOL fBreak = FALSE;
842 
843     // Communicating
844     {
845         while ( !fBreak )
846         {
847             // Use exclusive access when touching global variables shared with other threads.
848             EnterCriticalSection();
849             if( REXDEMOGetAnyMixedPadTrigger() & (KPAD_BUTTON_B | (PAD_BUTTON_B << 16)) )
850             {
851                 // Using the B Button, transition to the next state
852                 fBreak = TRUE;
853             }
854             LeaveCriticalSection();
855 
856             VIWaitForRetrace();
857         }
858     }
859 
860     return RESULT_OK;
861 }
862 
863 
864 /////////////////////////////////////////////////////////////////////
865 // Thread to process DataSharing
DataSharingThreadFunc(void * arg)866 void* DataSharingThreadFunc( void* arg )
867 {
868 #pragma unused( arg )
869     s32 result;
870     ShareData sendData;
871     MPDSDataSet recvDataSet;
872     BOOL fError;
873     s32 i;
874     s32 state;
875 
876     // Because the V-Blank periods are different for the Wii and the DS, the period for MP communications carried out between DS systems will not match the Wii's V-Blank period.
877     //
878     // Consequently, we need a reserved thread that runs at the MP communications period for DataSharing (the period with which the MPDSStep function succeeds), which is separate from the main rendering loop that is running at the Wii's V-Blank period.
879     //
880     //
881 
882     // It is also possible to perform DataSharing by using the MPDSTryStep function within the main loop, without creating a thread. However, in this case, the timing with which data is set will not match between the parent and children, and DataSharing will fail with high frequency.
883     //
884     //
885     //
886 
887     fError = FALSE;
888 
889     while ( !IsQuitting() && fError == FALSE )
890     {
891         // Use exclusive access when touching global variables shared with other threads.
892         EnterCriticalSection(); // Begin exclusive access
893 
894         state = GetState();
895 
896         switch ( state )
897         {
898         case STATE_LOBBY:
899         case STATE_COMMUNICATION:
900             // When in a state in which DataSharing is running:
901 
902             // Create data to send from the current game state
903             //   Note that the Wii and DS have a different endianness.
904             (void)NETMemSet( &sendData, 0, sizeof(sendData) );
905             sendData.count = MPHToMP16( (u16)sFrameCount ); // Endian conversion
906             if ( state == STATE_LOBBY )
907             {
908                 sendData.command = SHARECMD_NONE;
909             }
910             else
911             {
912                 sendData.command = SHARECMD_EXITLOBBY;
913             }
914             sendData.key = MPHToMP16(ConvKPadKeyToDSPad(REXDEMOGetAnyMixedPadHold()));
915             sendData.level = MPGetLinkLevel();
916 
917             // We must not block while maintaining exclusive access
918             LeaveCriticalSection(); // End exclusive access
919 
920             // Share data using DataSharing
921             //   The MPDSStep function blocks for a long period of time until the data is complete.
922             result = MPDSStep( &sDSContext, &sendData, &recvDataSet );
923 
924             // Parse the received data and update the game state
925             {
926                 // Use exclusive access when touching global variables shared with other threads.
927                 EnterCriticalSection(); // Begin exclusive access
928 
929                 if ( state != GetState() )
930                 {
931                     // The state changed while calling the MPDS[Try]Step function.
932                     //   Ignore because regardless of the state, there is no disparity in data parsing at this time.
933                     //
934                     //   The data that was shared must be parsed in the same way by all parents and children, so depending on the extent of the processing, it is probably best to include the state in the shared data, and break up the processing depending on the state that is included in the shared data.
935                     //
936                     //
937                 }
938 
939                 for ( i = 0; i < MY_MAX_NODES+1; i++ )
940                 {
941                     sRecvFlag[i] = FALSE;
942                 }
943 
944                 if( result == MP_RESULT_OK )
945                 {
946                     // Successfully received shared data
947                     const u8* data;
948 
949                     // Parse the contents of the shared data
950                     for ( i = 0; i < MY_MAX_NODES+1; i++ )
951                     {
952                         data = MPDSGetData( &sDSContext, &recvDataSet, (u32)i );
953                         if ( data != NULL )
954                         {
955                             // Was able to share data from the i-th sharing partner
956                             (void)NETMemCpy( (void*)&sRecvData[i], data, MY_DS_LENGTH );
957                             sRecvFlag[i] = TRUE;
958                         }
959                         else
960                         {
961                             // There are a number of cases in which NULL is returned.
962                             // 1. Empty data is read immediately after DataSharing started
963                             // 2. The child device with the corresponding AID is not connected
964                             // (3. Some kind of internal error has occurred)
965 
966                             // You can use this information to check if a child device is connected or disconnected.
967                             // However, in states in which children can connect freely, it is not possible to detect when a child disconnects and a separate child makes a connection immediately afterward.
968                             //
969                             //
970                             // Immediately after the start of the game, use the MPSetEntryFlag and MPUpdateBeacon functions to cut off new child connections.
971                             //
972                         }
973                     }
974 
975                     //
976                     //
977                     // Data could have been shared between the child and the parent, so update the in-game state.
978                     // The logic for using the shared data to update the in-game state is placed here.
979                     //
980                     // If using DataSharing, you can completely synchronize the parent and children by updating the in-game state using only the data that could be obtained from the MPDS[Try]Step function.
981                     //
982                     // If the local data for the current pad input, etc. is used directly, the synchronization will be lost, so be sure to pass even your own data to the MPDS[Try]Step function once.
983                     //
984                     //
985                     //
986 
987                     for (i = 0; i < BALL_PLAYER_MAX; ++i)
988                     {
989                         if ((i < MY_MAX_NODES+1) && sRecvFlag[i])
990                         {
991                             InputBallKey((int)i, (int)MPMPToH16((u16)sRecvData[i].key));
992                         }
993                     }
994                     UpdateBalls(0);
995 
996                 }
997                 else if ( result == MP_RESULT_NO_DATA )
998                 {
999                     // This is an error that occurs normally if the MPDSTryStep function was used and the data was not complete due to the communication status being wrong.
1000                     //
1001                     //
1002                     // This will not occur if the MPDSStep function was used.
1003 
1004                     //
1005                     // Data sharing could not be achieved between the parent and children, so wait for the next frame without updating the in-game state.
1006                     //
1007                     //
1008                     // How long this state will continue depends on the participants in the communications, so you must not carry out any operations that change the program's internal state, such as updating the coordinate data using the velocity data, or reading in random values.
1009                     //
1010                     //
1011                     //
1012                     //
1013                 }
1014                 else
1015                 {
1016                     // error
1017                     OSReport("MPDSStep failed: %08x\n", result);
1018                     fError = TRUE;
1019                 }
1020 
1021                 LeaveCriticalSection(); // End exclusive access
1022             }
1023             break;
1024 
1025         default:
1026             // if not in a communication state
1027             LeaveCriticalSection(); // End exclusive access
1028 
1029             // block and wait until the state changes
1030             (void)WaitForStateChange();
1031             break;
1032         }
1033     }
1034 
1035     return NULL;
1036 }
1037 
1038 /////////////////////////////////////////////////////////////////////
1039 // Thread for beacon updates
UpdateGameInfoThreadFunc(void * arg)1040 void* UpdateGameInfoThreadFunc( void* arg )
1041 {
1042 #pragma unused( arg )
1043     while ( !IsQuitting() )
1044     {
1045         switch ( GetState() )
1046         {
1047         case STATE_LOBBY:
1048         case STATE_COMMUNICATION:
1049             // Update beacon contents periodically during communication
1050             sMyGameInfo.entryCount = MPCountPopulation(MPGetConnectedAIDs());
1051             sMyGameInfo.periodicalCount++;
1052             (void)MPSetUserGameInfo(&sMyGameInfo, sizeof(sMyGameInfo));
1053 
1054             // Apply the configured UserGameInfo to the beacon
1055             //   Block for a long period of time, until the updated beacon is sent.
1056             (void)MPUpdateBeacon();
1057             break;
1058 
1059         default:
1060             (void)WaitForStateChange();
1061             break;
1062         }
1063     }
1064 
1065     return NULL;
1066 }
1067 
1068 /////////////////////////////////////////////////////////////////////
1069 // Event notification callback
DataSharingCallback(s32 type,MPPortCallbackInfo * info)1070 void DataSharingCallback( s32 type, MPPortCallbackInfo* info )
1071 {
1072     // The MPDS library forwards notifications from the MP library.
1073     // In this demo, callbacks are used to obtain detailed information about the children that have connected, but if only DataSharing is being used, there is no particular necessarily to prepare a callback function.
1074     //
1075     //
1076 
1077     // This function is called from a thread for MP library callbacks.
1078     // Because it is not an interrupt callback, it is also possible to call a thread syncrhonization function, but if blocking occurs for a long period of time, MP communications will be affected.
1079     //
1080     // Limit blocking to the minimum required to implement exclusive access.
1081     // Depending on the circumstances, you may also want to look into a method of receiving and passing data in a way that doesn't block, using OSMessageQueue or other such functions.
1082     //
1083 
1084     u32 aid;
1085 
1086     switch ( type )
1087     {
1088     case MP_PORT_CB_TYPE_CONNECTED:
1089         // a child device has connected
1090         aid = info->connected.fromAid;
1091 
1092         EnterCriticalSection();
1093         (void)NETMemCpy(sConnectedMacAddress[aid], info->connected.macAddress, MP_SIZE_MACADDRESS);
1094         (void)NETMemSet(sConnectedNickName[aid], 0, sizeof(sConnectedNickName[0]));
1095         /* Note that data from the DS has a different endianness.*/
1096         NETSwapAndCopyMemory16(sConnectedNickName[aid], info->connected.ssidUserData, info->connected.ssidUserDataLength);
1097         sConnectedFlag[aid] = TRUE;
1098         LeaveCriticalSection();
1099 
1100         break;
1101 
1102     case MP_PORT_CB_TYPE_DISCONNECTED:
1103         // a child device has disconnected
1104         aid = info->disconnected.fromAid;
1105 
1106         EnterCriticalSection();
1107         sConnectedFlag[aid] = FALSE;
1108         (void)NETMemSet(sConnectedMacAddress[aid], 0, MP_SIZE_MACADDRESS);
1109         (void)NETMemSet(sConnectedNickName[aid], 0, sizeof(sConnectedNickName[0]));
1110         LeaveCriticalSection();
1111 
1112         break;
1113 
1114     case MP_PORT_CB_TYPE_STARTUP:
1115     case MP_PORT_CB_TYPE_CLEANUP:
1116     case MPDS_PORT_CB_TYPE_DATASET_RECEIVED:
1117     default:
1118         // there is nothing to do
1119         break;
1120     }
1121 }
1122 
1123 /*===========================================================================*/
1124 
mpAlloc(u32 size)1125 void* mpAlloc( u32 size )
1126 {
1127     return MEMAllocFromExpHeapEx( sUserHeap, size, 32 );
1128 }
1129 
mpFree(void * ptr)1130 void mpFree( void* ptr )
1131 {
1132     MEMFreeToExpHeap( sUserHeap, ptr );
1133 }
1134 
1135 /*===========================================================================*/
1136 
1137 // PAD constants for the DS
1138 #define DSPAD_BUTTON_A            0x0001 // A
1139 #define DSPAD_BUTTON_B            0x0002 // B
1140 #define DSPAD_BUTTON_SELECT       0x0004 // SELECT
1141 #define DSPAD_BUTTON_START        0x0008 // START
1142 #define DSPAD_KEY_RIGHT           0x0010 // +Control Pad, RIGHT
1143 #define DSPAD_KEY_LEFT            0x0020 // +Control Pad, LEFT
1144 #define DSPAD_KEY_UP              0x0040 // +Control Pad, UP
1145 #define DSPAD_KEY_DOWN            0x0080 // +Control Pad, DOWN
1146 #define DSPAD_BUTTON_R            0x0100 // R
1147 #define DSPAD_BUTTON_L            0x0200 // L
1148 #define DSPAD_BUTTON_X            0x0400 // X
1149 #define DSPAD_BUTTON_Y            0x0800 // Y
1150 
ConvKPadKeyToDSPad(u32 mixedKey)1151 u16 ConvKPadKeyToDSPad(u32 mixedKey)
1152 {
1153     u16 key = 0;
1154 
1155     if ( mixedKey & (KPAD_BUTTON_LEFT | (PAD_BUTTON_LEFT << 16)) )
1156     {
1157         key |= DSPAD_KEY_LEFT;
1158     }
1159     if ( mixedKey & (KPAD_BUTTON_RIGHT | (PAD_BUTTON_RIGHT << 16)) )
1160     {
1161         key |= DSPAD_KEY_RIGHT;
1162     }
1163     if ( mixedKey & (KPAD_BUTTON_UP | (PAD_BUTTON_UP << 16)) )
1164     {
1165         key |= DSPAD_KEY_UP;
1166     }
1167     if ( mixedKey & (KPAD_BUTTON_DOWN | (PAD_BUTTON_DOWN << 16)) )
1168     {
1169         key |= DSPAD_KEY_DOWN;
1170     }
1171     if( mixedKey & (KPAD_BUTTON_A | (PAD_BUTTON_A << 16)) )
1172     {
1173         key |= DSPAD_BUTTON_A;
1174     }
1175     if( mixedKey & (KPAD_BUTTON_B | (PAD_BUTTON_B << 16)) )
1176     {
1177         key |= DSPAD_BUTTON_A;
1178     }
1179     if( mixedKey & (KPAD_BUTTON_1 | (PAD_BUTTON_X << 16)) )
1180     {
1181         key |= DSPAD_BUTTON_X;
1182     }
1183     if( mixedKey & (KPAD_BUTTON_2 | (PAD_BUTTON_Y << 16)) )
1184     {
1185         key |= DSPAD_BUTTON_Y;
1186     }
1187     if( mixedKey & (KPAD_BUTTON_PLUS | (PAD_TRIGGER_R << 16)) )
1188     {
1189         key |= DSPAD_BUTTON_R;
1190     }
1191     if( mixedKey & (KPAD_BUTTON_MINUS | (PAD_TRIGGER_L << 16)) )
1192     {
1193         key |= DSPAD_BUTTON_L;
1194     }
1195     if( mixedKey & (KPAD_BUTTON_HOME | (PAD_BUTTON_START << 16)) )
1196     {
1197         key |= DSPAD_BUTTON_START;
1198     }
1199 
1200     return key;
1201 }
1202