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