1 /*---------------------------------------------------------------------------*
2   Project:  Revolution MPDS simple demo
3   File:     mpdssimple.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: mpdssimple.c,v $
14   Revision 1.12  2007/11/29 03:20:27  seiki_masashi
15   Added the const modifier.
16 
17   Revision 1.11  2007/10/26 09:31:17  seiki_masashi
18   Cleaned up the code
19 
20   Revision 1.10  2007/10/26 07:29:56  seiki_masashi
21   Cleaned up the code
22 
23   Revision 1.9  2007/10/26 07:05:28  seiki_masashi
24   Revised the link-level display
25 
26   Revision 1.6  2007/10/24 09:30:41  okubata_ryoma
27   Small fix
28 
29   Revision 1.5  2007/10/22 09:50:11  seiki_masashi
30   Made the MPDSStep function into a synchronous function, and added the new function MPDSTryStep
31 
32   Revision 1.1  2007/10/10 08:38:45  seiki_masashi
33   Added the MPDS library
34 
35   $NoKeywords: $
36  *---------------------------------------------------------------------------*/
37 
38 //
39 // This sample performs communications with the demos/wm/dataShare-1 demo in the NITRO-SDK.
40 //
41 // Only the minimum amount of processing to check the state of communications is being done, so if creating an actual application, refer to demos like mpdsmodel.
42 //
43 //
44 
45 #include <string.h>
46 #include <revolution.h>
47 #include <revolution/mp.h>
48 #include <revolution/mpds.h>
49 #include <revolution/mem.h>
50 
51 #include "rexdemo/demokpad.h"
52 #include "rexdemo/graphic.h"
53 
54 #define     USERHEAP_SIZE ( 65536 )
55 
56 #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)
57 #define     MY_DS_NODES     16  // the number of nodes, including parent devices, that perform DataSharing (matches the dataShare-1 demo in the NITRO-SDK)
58                                 // for a normal application (MY_MAX_NODES+1)
59 
60 #define     MY_DS_LENGTH    8   // the shared data size per device for DataSharing
61 
62 // When DataSharing is performed, the required transmission size is the shared size per device times the number of devices, plus 4 bytes.
63 #define     MY_PARENT_MAX_SIZE ( (MY_DS_LENGTH * MY_DS_NODES) + MPDS_HEADER_SIZE )
64 
65 /*===========================================================================*/
66 
67 static MEMHeapHandle    sUserHeap;
68 
69 static u16 sRecvBuf[MY_MAX_NODES+1][MY_DS_LENGTH/sizeof(u16)];
70 static BOOL sRecvFlag[MY_MAX_NODES+1];
71 
72 static MPDSContext sDSContext;
73 
74 /*===========================================================================*/
75 
76 static const GXColor white = { 0xFF, 0xFF, 0xFF, };
77 static const GXColor yellow = { 0xFF, 0xFF, 0x00, };
78 static const GXColor gray = { 0x80, 0x80, 0x80, };
79 static const GXColor black = { 0x00, 0x00, 0x00, };
80 static const GXColor red = { 0xFF, 0x00, 0x18 };
81 static const GXColor green = { 0x00, 0xFF, 0x00 };
82 
83 /*===========================================================================*/
84 
85 static void DoMPCommunication( void );
86 
87 static void* mpAlloc( u32 size );
88 static void mpFree( void* ptr );
89 static void PortCallbackFunction( s32 type, MPPortCallbackInfo* info );
90 
91 /*===========================================================================*/
92 
93 // Toggles whether to use doubleMode for DataSharing.
94 //   Using doubleMode makes it possible to achieve synchronization of data at 60 fps, even when the MP frequency is set to once per frame.
95 //
96 //   The dataShare-1 demo in NITRO-SDK does not use doubleMode by default.
97 //   If trying out doubleMode, change from 2 to 1 in the dataShare-1 demo. PICTURE_FRAME_PER_GAME_FRAME:
98 //
99 //#define DATA_SHARING_DOUBLE_MODE 1
100 
101 // The GGID is a unique ID for MP communications assigned to each application.
102 //   You must receive a GGID assignment from Nintendo for an actual application.
103 //   Here, the GGID is used that is identical to that of the dataShare-1 demo.
104 #define MY_GGID     0x003fff11
105 
106 // MP communication settings
107 static MPConfig sMpConfig =
108 {
109     mpAlloc,
110     mpFree,
111 
112     8,                  // threadPriority; set to a priority that is higher than that of the main thread by 4 or more
113 
114     MP_MODE_PARENT,     // mode
115 
116     MY_GGID,            // ggid
117     MP_TGID_AUTO,       // tgid
118 
119     1,                  // channel
120                         // Because the dataShare-1 demo of NITRO-SDK is fixed to ch1, the parent device is also fixed to ch1
121                         //
122                         // Normally applications should use automatic settings by specifying MP_CHANNEL_AUTO
123 
124     MP_LIFETIME_DEFAULT,// lifeTime; usually equal to MP_LIFETIME_DEFAULT
125 
126     MP_BEACON_PERIOD_AUTO,  // beaconPeriod; normally MP_BEACON_PERIOD_AUTO
127     MY_MAX_NODES,       // maxNodes
128     MY_PARENT_MAX_SIZE, // parentMaxSize
129     MY_DS_LENGTH,       // childMaxSize
130     TRUE,               // entryFlag
131     FALSE,              // multiBootFlag; usually FALSE
132 
133     1,                  // frequency
134 
135     0,                  // userGameInfoLength
136     { 0, },             // userGameInfo
137 
138     NULL,               // indicationCallbackFunction
139 
140     /// ...             // port configuration (can be set up using MPSetPortConfig)
141 };
142 
143 // DataSharing configuration
144 static MPDSConfig sMpdsConfig =
145 {
146     MY_DS_LENGTH,       // dataLength
147 
148     12,                 // port (must use one of sequential communications ports 12-15)
149     MP_PRIORITY_HIGH,   // priority
150 
151     TRUE,               // isParent; always TRUE
152     (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)
153 #ifdef DATA_SHARING_DOUBLE_MODE
154     TRUE,               // isDoubleMode
155 #else
156     FALSE,              // isDoubleMode
157 #endif
158     TRUE,               // isAutoStart; always TRUE
159     NULL                // mpdsCallback
160 };
161 
main(void)162 void main(void)
163 {
164     /* Initialize OS and memory heap */
165     OSReport("test program started.\n");
166     REXDEMOKPadInit();
167     REXDEMOInitScreen( FALSE );
168     REXDEMOSetGroundColor( black );
169     REXDEMOSetFontSize( 10, 20 );
170     REXDEMOBeginRender();
171     REXDEMOWaitRetrace();
172 
173     /* Initialize heap for MP library */
174     {
175         void*   heapAddress;
176 
177         heapAddress = OSGetMEM2ArenaLo();
178         OSSetMEM2ArenaLo( (void*)OSRoundUp32B( (u32)heapAddress + USERHEAP_SIZE ) );
179         sUserHeap   = MEMCreateExpHeapEx( heapAddress, USERHEAP_SIZE, MEM_HEAP_OPT_THREAD_SAFE );
180         if( sUserHeap == NULL )
181         {
182             OSHalt( "Could not create heap.\n" );
183         }
184     }
185 
186     (void)PADInit();
187 
188     OSReport("push <A> button to restart.\n");
189     while (TRUE)
190     {
191         DoMPCommunication();
192     }
193 }
194 
DoMPCommunication(void)195 void DoMPCommunication( void )
196 {
197     s32 result;
198 
199     result = MPDSInit( &sDSContext, &sMpdsConfig );
200     if ( result < 0 )
201     {
202         OSReport( "MPDSInit returns %08x\n", result );
203         OSHalt("** panic **");
204     }
205 
206     result = MPDSSetupPortConfig( &sDSContext, &sMpConfig );
207     if ( result < 0 )
208     {
209         OSReport( "MPDSSetupPortConfig returns %08x\n", result );
210         OSHalt("** panic **");
211     }
212 
213     OSReport("MPStartup()\n");
214     result = MPStartup( &sMpConfig );
215     if( result != MP_RESULT_OK )
216     {
217         OSReport( "MPStartup returns %08x\n", result );
218         OSHalt( "** panic **\n" );
219     }
220 
221     {
222         static u16 frameData[MY_DS_LENGTH/sizeof(u16)];
223         u32 counter1 = 0;
224         u32 counter2 = 0;
225 
226 #ifndef DATA_SHARING_DOUBLE_MODE
227         // If performing DataSharing without using doubleMode when the MP frequency set to once per frame, updates to the game state can only be made at 30 fps at maximum.
228         //
229         // In this case, a flag will be required for determining whether the current frame coincides with the timing of a game state update.
230         //
231         BOOL updateFlag = TRUE;
232 #endif
233 
234         for( counter1 = 0; TRUE; counter1++ )
235         {
236             s32 i;
237             MPDSDataSet recvDataSet;
238 
239             REXDEMOKPadRead();
240 
241             if( REXDEMOGetAnyMixedPadTrigger() & (KPAD_BUTTON_A | (PAD_BUTTON_A << 16)) )
242             {
243                 break;
244             }
245 
246 #ifndef DATA_SHARING_DOUBLE_MODE
247             // Update the game state at a frequency of once every two frames
248             if ( updateFlag )
249 #else
250             // When using doubleMode, it is possible to update the game state every frame
251 #endif
252             {
253                 // Note that the Wii and DS have a different endianness.
254                 // The data order is being made to match the dataShare-1 demo in NITRO-SDK
255                 frameData[0] = MPHToMP16((u16)(counter1 >> 16));
256                 frameData[1] = MPHToMP16((u16)(counter1 & 0xffff));
257                 frameData[2] = MPHToMP16((u16)(counter2 >> 16));
258                 frameData[3] = MPHToMP16((u16)(counter2 & 0xffff));
259 
260                 // Increment counter2 to match the dataShare-1 demo in NITRO-SDK
261                 counter2++;
262 
263                 for ( i = 0; i < MY_MAX_NODES+1; i++ )
264                 {
265                     sRecvFlag[i] = FALSE;
266                 }
267 
268                 // Share data using DataSharing
269                 result = MPDSTryStep( &sDSContext, frameData, &recvDataSet );
270 
271                 if( result == MP_RESULT_OK )
272                 {
273                     // Successfully received shared data
274                     const u8* data;
275 
276                     // Parse the contents of the shared data
277                     for ( i = 0; i < MY_MAX_NODES+1; i++ )
278                     {
279                         data = MPDSGetData( &sDSContext, &recvDataSet, (u32)i );
280                         if ( data != NULL )
281                         {
282                             // Was able to share data from the i-th sharing partner
283                             (void)NETMemCpy( sRecvBuf[i], data, MY_DS_LENGTH );
284                             sRecvFlag[i] = TRUE;
285                         }
286                         else
287                         {
288                             // There are a number of cases in which NULL is returned.
289                             // 1. Empty data is read immediately after DataSharing started
290                             // 2. The child device with the corresponding AID is not connected
291                             // (3. Some kind of internal error has occurred)
292 
293                             // You can use this information to check if a child device is connected or disconnected.
294                             // 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.
295                             //
296                             //
297                             // Immediately after the start of the game, use the MPSetEntryFlag and MPUpdateBeacon functions to cut off new child connections.
298                             //
299                         }
300                     }
301 
302 #ifndef DATA_SHARING_DOUBLE_MODE
303                     // The game state was updated successfully, so do not update at the next frame
304                     //
305                     updateFlag = FALSE;
306 #endif
307 
308                     //
309                     //
310                     // Data could have been shared between the child and the parent, so update the in-game state.
311                     // The logic for using the shared data to update the in-game state is placed here.
312                     //
313                     // 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.
314                     //
315                     // 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.
316                     //
317                     //
318                     //
319 
320                 }
321                 else if ( result == MP_RESULT_NO_DATA )
322                 {
323                     // This is an error that occurs normally if the data was not complete due to the communication status being wrong.
324                     //
325 
326 #ifndef DATA_SHARING_DOUBLE_MODE
327                     // Try updating again at the next frame
328                     updateFlag = TRUE;
329 #endif
330 
331                     // Decrement counter1 in order to match the dataShare-1 demo in NITRO-SDK
332                     counter1--;
333 
334                     //
335                     // Data sharing could not be achieved between the parent and children, so wait for the next frame without updating the in-game state.
336                     //
337                     //
338                     // 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.
339                     //
340                     //
341                     //
342                     //
343                 }
344                 else
345                 {
346                     // error
347                     OSReport("MPDSTryStep failed: %08x\n", result);
348                     break;
349                 }
350             }
351 #ifndef DATA_SHARING_DOUBLE_MODE
352             else
353             {
354                 // An update did not occur at this frame, so update at the next frame
355                 updateFlag = TRUE;
356             }
357 #endif
358 
359             REXDEMOBeginRender();
360 
361             REXDEMOSetTextColor(green);
362             REXDEMOPrintf(40, 8, 0, "Parent mode");
363             REXDEMOSetTextColor(white);
364             REXDEMOPrintf(4, 400, 0, "push <A> button to restart.");
365 
366             REXDEMOSetTextColor(green);
367             {
368                 s32 linkLevel = MPGetLinkLevel();
369                 REXDEMOPrintf(480, 400, 0, "Link Level: %d", (linkLevel >= 0) ? linkLevel : 0);
370             }
371 
372             REXDEMOSetTextColor(yellow);
373             REXDEMOPrintf(4, 30, 0, "Send:     %08X-%08X", counter1, counter2);
374             REXDEMOPrintf(4, 52, 0, "Receive:");
375             {
376                 s32     i;
377 
378                 for (i = 0; i < MY_MAX_NODES+1; i++)
379                 {
380                     if (sRecvFlag[i])
381                     {
382                         REXDEMOSetTextColor(yellow);
383                         REXDEMOPrintf(40, (s16)(80 + i * 20), 0,
384                                       "AID(%02d): %04X%04X-%04X%04X",
385                                       i,
386                                       MPMPToH16(sRecvBuf[i][0]), MPMPToH16(sRecvBuf[i][1]),
387                                       MPMPToH16(sRecvBuf[i][2]), MPMPToH16(sRecvBuf[i][3]) );
388                     }
389                     else
390                     {
391                         REXDEMOSetTextColor(red);
392                         REXDEMOPrintf(40, (s16)(80 + i * 20), 0, "No Data");
393                     }
394                 }
395             }
396 
397             REXDEMOSetTextColor(white);
398             REXDEMOWaitRetrace();
399         }
400     }
401 
402     OSReport("MPCleanup()\n");
403     (void)MPCleanup();
404 }
405 
406 /*===========================================================================*/
407 
mpAlloc(u32 size)408 void* mpAlloc( u32 size )
409 {
410     return MEMAllocFromExpHeapEx( sUserHeap, size, 32 );
411 }
412 
mpFree(void * ptr)413 void mpFree( void* ptr )
414 {
415     MEMFreeToExpHeap( sUserHeap, ptr );
416 }
417