1 /*****************************************************************************\
2 Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3 This file is licensed under the Snes9x License.
4 For further information, consult the LICENSE file in the root directory.
5 \*****************************************************************************/
6
7 /***********************************************************************************
8 SNES9X for Mac OS (c) Copyright John Stiles
9
10 Snes9x for Mac OS X
11
12 (c) Copyright 2001 - 2011 zones
13 (c) Copyright 2002 - 2005 107
14 (c) Copyright 2002 PB1400c
15 (c) Copyright 2004 Alexander and Sander
16 (c) Copyright 2004 - 2005 Steven Seeger
17 (c) Copyright 2005 Ryan Vogt
18 ***********************************************************************************/
19
20
21 #include "snes9x.h"
22 #include "memmap.h"
23 #include "apu.h"
24 #include "snapshot.h"
25 #include "cheats.h"
26 #include "display.h"
27
28 #include <arpa/inet.h>
29 #include <pthread.h>
30 #include <semaphore.h>
31
32 #include "mac-prefix.h"
33 #include "mac-cart.h"
34 #include "mac-cheatfinder.h"
35 #include "mac-controls.h"
36 #include "mac-dialog.h"
37 #include "mac-file.h"
38 #include "mac-joypad.h"
39 #include "mac-keyboard.h"
40 #include "mac-os.h"
41 #include "mac-snes9x.h"
42 #include "mac-stringtools.h"
43 #include "mac-netplay.h"
44 #include "mac-client.h"
45
46 #ifdef SELF_TEST
47 #include <sys/un.h>
48 #endif
49
50 #define KeyIsPressed(km, k) (1 & (((unsigned char *) km) [(k) >> 3] >> ((k) & 7)))
51
52 enum
53 {
54 kNPCDialogNone,
55 kNPCDialogInit,
56 kNPCDialogConnect,
57 kNPCDialogConnectFailed,
58 kNPCDialogOpenBegin,
59 kNPCDialogOpenEnd,
60 kNPCDialogPrepare,
61 kNPCDialogPrepareFailed,
62 kNPCDialogShowList,
63 kNPCDialogDone,
64 kNPCDialogCancel
65 };
66
67 typedef struct
68 {
69 volatile bool8 padloop;
70 volatile bool8 exitsgn;
71 volatile uint32 phasecount;
72 volatile uint32 phasespan;
73 volatile uint8 header;
74 bool8 online;
75 int socket;
76 int numplayers;
77 char name[256];
78 char serverIP[256];
79
80 int savedDeviceSetting;
81 int savedAutoSaveDelay;
82
83 bool8 configsaved;
84 bool8 dialogcancel;
85 bool8 dialogsheet;
86 int dialogprocess;
87 } clientState;
88
89 typedef struct
90 {
91 bool8 ready;
92 int player;
93 char name[256];
94 } clientsInfo;
95
96 typedef struct
97 {
98 uint32 crc32;
99 int input;
100 int length;
101 char fname[PATH_MAX + 1];
102 } cROMInfo;
103
104 static char n_csememu[] = "/tmp/s9x_c_emu_semaphore",
105 n_csempad[] = "/tmp/s9x_c_pad_semaphore";
106
107 static clientState npclient;
108 static clientsInfo npcinfo[NP_MAX_PLAYERS];
109
110 static cROMInfo nprominfo;
111
112 static uint32 npcactvpad[NP_MAX_PLAYERS][64], // [player number]
113 npcrecvpad[NP_MAX_PLAYERS][64], // [player number]
114 npcsendpad[64],
115 npccachpad[64];
116
117 static WindowRef mRef, sRef;
118 static sem_t *csememu, *csempad;
119 static pthread_t connectthread, preparethread, gamepadthread;
120
121 static int NPClientGetMesFromServer (void);
122 static void NPClientDetachConnectThread (void);
123 static void NPClientDetachPrepareThread (void);
124 static void NPClientBeginPlayerListSheet (void);
125 static void NPClientEndPlayerListSheet (void);
126 static bool8 NPClientConnectToServer (int);
127 static bool8 NPClientSendMesToServer (int);
128 static bool8 NPClientSendNameToServer (void);
129 static bool8 NPClientGetROMInfoFromServer (void);
130 static bool8 NPClientBeginOpenROMImage (WindowRef);
131 static bool8 NPClientEndOpenROMImage (void);
132 static bool8 NPClientROMReadyToServer (void);
133 static bool8 NPClientGetSRAMFromServer (void);
134 static bool8 NPClientGetPlayerListFromServer (void);
135 static bool8 NPClientReplyPhaseSpanTest (void);
136 static void * NPClientConnectThread (void *);
137 static void * NPClientPrepareThread (void *);
138 static void * NPClientNetPlayThread (void *);
139 static pascal void NPClientDialogTimerHandler (EventLoopTimerRef, void *);
140 static pascal OSStatus NPClientDialogEventHandler (EventHandlerCallRef, EventRef, void *);
141 static pascal OSStatus NPClientSheetEventHandler (EventHandlerCallRef, EventRef, void *);
142
143
NPClientDialog(void)144 bool8 NPClientDialog (void)
145 {
146 OSStatus err;
147 IBNibRef nibRef;
148
149 npclient.dialogcancel = true;
150 npclient.dialogsheet = false;
151 npclient.configsaved = false;
152
153 err = CreateNibReference(kMacS9XCFString, &nibRef);
154 if (err == noErr)
155 {
156 err = CreateWindowFromNib(nibRef, CFSTR("Connect"), &mRef);
157 if (err == noErr)
158 {
159 err = CreateWindowFromNib(nibRef, CFSTR("PlayerList"), &sRef);
160 if (err == noErr)
161 {
162 EventHandlerRef eref, seref;
163 EventLoopTimerRef tref;
164 EventHandlerUPP eventUPP, sheetUPP;
165 EventLoopTimerUPP timerUPP;
166 EventTypeSpec windowEvents[] = { { kEventClassCommand, kEventCommandProcess },
167 { kEventClassCommand, kEventCommandUpdateStatus } };
168 CFStringRef ref;
169 HIViewRef ctl, root;
170 HIViewID cid;
171
172 npclient.dialogprocess = kNPCDialogInit;
173
174 eventUPP = NewEventHandlerUPP(NPClientDialogEventHandler);
175 err = InstallWindowEventHandler(mRef, eventUPP, GetEventTypeCount(windowEvents), windowEvents, (void *) mRef, &eref);
176
177 timerUPP = NewEventLoopTimerUPP(NPClientDialogTimerHandler);
178 err = InstallEventLoopTimer(GetCurrentEventLoop(), 0.0f, 0.1f, timerUPP, (void *) mRef, &tref);
179
180 sheetUPP = NewEventHandlerUPP(NPClientSheetEventHandler);
181 err = InstallWindowEventHandler(sRef, sheetUPP, GetEventTypeCount(windowEvents), windowEvents, (void *) sRef, &seref);
182
183 root = HIViewGetRoot(mRef);
184 cid.id = 0;
185
186 cid.signature = 'CHAS';
187 HIViewFindByID(root, cid, &ctl);
188 HIViewSetVisible(ctl, false);
189
190 cid.signature = 'SVIP';
191 HIViewFindByID(root, cid, &ctl);
192 SetEditTextCStr(ctl, npServerIP, false);
193
194 cid.signature = 'CLNM';
195 HIViewFindByID(root, cid, &ctl);
196 ref = CFStringCreateWithCString(kCFAllocatorDefault, npName, kCFStringEncodingUTF8);
197 if (ref)
198 {
199 SetEditTextCFString(ctl, ref, false);
200 CFRelease(ref);
201 }
202 else
203 SetEditTextCFString(ctl, CFSTR("unknown"), false);
204
205 MoveWindowPosition(mRef, kWindowClient, false);
206 ShowWindow(mRef);
207 err = HIViewAdvanceFocus(root, 0);
208 err = RunAppModalLoopForWindow(mRef);
209 HideWindow(mRef);
210 SaveWindowPosition(mRef, kWindowClient);
211
212 err = RemoveEventHandler(seref);
213 DisposeEventHandlerUPP(sheetUPP);
214
215 err = RemoveEventLoopTimer(tref);
216 DisposeEventLoopTimerUPP(timerUPP);
217
218 err = RemoveEventHandler(eref);
219 DisposeEventHandlerUPP(eventUPP);
220
221 CFRelease(sRef);
222 }
223
224 CFRelease(mRef);
225 }
226
227 DisposeNibReference(nibRef);
228 }
229
230 return (!npclient.dialogcancel);
231 }
232
NPClientDialogEventHandler(EventHandlerCallRef inHandlerRef,EventRef inEvent,void * inUserData)233 static pascal OSStatus NPClientDialogEventHandler (EventHandlerCallRef inHandlerRef, EventRef inEvent, void *inUserData)
234 {
235 OSStatus err, result = eventNotHandledErr;
236
237 switch (GetEventClass(inEvent))
238 {
239 case kEventClassCommand:
240 switch (GetEventKind(inEvent))
241 {
242 HICommand tHICommand;
243
244 case kEventCommandUpdateStatus:
245 err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand);
246 if (err == noErr && tHICommand.commandID == 'clos')
247 {
248 UpdateMenuCommandStatus(false);
249 result = noErr;
250 }
251
252 break;
253
254 case kEventCommandProcess:
255 err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand);
256 if (err == noErr)
257 {
258 switch (tHICommand.commandID)
259 {
260 case 'OK__':
261 CFStringRef ref;
262 HIViewRef ctl, root;
263 HIViewID cid;
264
265 root = HIViewGetRoot(mRef);
266 cid.id = 0;
267
268 cid.signature = 'SVIP';
269 HIViewFindByID(root, cid, &ctl);
270 GetEditTextCStr(ctl, npclient.serverIP);
271 DeactivateControl(ctl);
272 if (npclient.serverIP[0] == 0)
273 strcpy(npclient.serverIP, "127.0.0.1");
274 strcpy(npServerIP, npclient.serverIP);
275 printf("%s\n", npServerIP);
276
277 cid.signature = 'CLNM';
278 HIViewFindByID(root, cid, &ctl);
279 CopyEditTextCFString(ctl, &ref);
280 DeactivateControl(ctl);
281 if (ref)
282 {
283 Boolean r;
284
285 r = CFStringGetCString(ref, npclient.name, 256, kCFStringEncodingUTF8);
286 if (!r)
287 strcpy(npclient.name, "unknown");
288 else
289 if (npclient.name[0] == 0)
290 strcpy(npclient.name, "Guest");
291
292 CFRelease(ref);
293 }
294 else
295 strcpy(npclient.name, "unknown");
296 strcpy(npName, npclient.name);
297 printf("%s\n", npName);
298
299 cid.signature = 'OK__';
300 HIViewFindByID(root, cid, &ctl);
301 DeactivateControl(ctl);
302
303 cid.signature = 'NOT_';
304 HIViewFindByID(root, cid, &ctl);
305 DeactivateControl(ctl);
306
307 npclient.dialogcancel = false;
308 npclient.dialogprocess = kNPCDialogConnect;
309
310 result = noErr;
311 break;
312
313 case 'NOT_':
314 npclient.dialogcancel = true;
315 npclient.dialogprocess = kNPCDialogCancel;
316
317 result = noErr;
318 break;
319
320 case 'NvDn':
321 npclient.dialogcancel = false;
322 npclient.dialogprocess = kNPCDialogOpenEnd;
323
324 result = noErr;
325 break;
326 }
327 }
328
329 break;
330 }
331
332 break;
333 }
334
335 return (result);
336 }
337
NPClientDialogTimerHandler(EventLoopTimerRef inTimer,void * userData)338 static pascal void NPClientDialogTimerHandler (EventLoopTimerRef inTimer, void *userData)
339 {
340 WindowRef window = (WindowRef) userData;
341 HIViewRef ctl;
342 HIViewID cid = { 'CHAS', 0 };
343
344 HIViewFindByID(HIViewGetRoot(mRef), cid, &ctl);
345
346 switch (npclient.dialogprocess)
347 {
348 case kNPCDialogNone:
349 break;
350
351 case kNPCDialogCancel:
352 NPNotification(" kNPCDialogCancel", -1);
353 npclient.dialogprocess = kNPCDialogNone;
354 npclient.dialogcancel = true;
355 QuitAppModalLoopForWindow(mRef);
356 break;
357
358 case kNPCDialogInit:
359 NPNotification(" kNPCDialogInit", -1);
360 npclient.dialogprocess = kNPCDialogNone;
361 break;
362
363 case kNPCDialogConnect:
364 NPNotification(" kNPCDialogConnect", -1);
365 npclient.dialogprocess = kNPCDialogNone;
366 HIViewSetVisible(ctl, true);
367 NPClientDetachConnectThread();
368 break;
369
370 case kNPCDialogConnectFailed:
371 NPNotification(" kNPCDialogConnectFailed", -1);
372 npclient.dialogprocess = kNPCDialogNone;
373 npclient.dialogcancel = true;
374 QuitAppModalLoopForWindow(mRef);
375 break;
376
377 case kNPCDialogOpenBegin:
378 NPNotification(" kNPCDialogOpenBegin", -1);
379 npclient.dialogprocess = kNPCDialogNone;
380 HIViewSetVisible(ctl, false);
381 NPClientStoreConfig();
382 if (!NPClientBeginOpenROMImage(window))
383 {
384 NPClientDisconnect();
385 NPClientRestoreConfig();
386 npclient.dialogprocess = kNPCDialogCancel;
387 }
388
389 break;
390
391 case kNPCDialogOpenEnd:
392 NPNotification(" kNPCDialogOpenEnd", -1);
393 npclient.dialogprocess = kNPCDialogNone;
394 if (!NPClientEndOpenROMImage())
395 {
396 NPClientDisconnect();
397 NPClientRestoreConfig();
398 npclient.dialogprocess = kNPCDialogCancel;
399 }
400 else
401 npclient.dialogprocess = kNPCDialogPrepare;
402
403 break;
404
405 case kNPCDialogPrepare:
406 NPNotification(" kNPCDialogPrepare", -1);
407 npclient.dialogprocess = kNPCDialogNone;
408 HIViewSetVisible(ctl, true);
409 NPClientDetachPrepareThread();
410 break;
411
412 case kNPCDialogPrepareFailed:
413 NPNotification(" kNPCDialogPrepareFailed", -1);
414 npclient.dialogprocess = kNPCDialogNone;
415 NPClientRestoreConfig();
416 npclient.dialogcancel = true;
417 QuitAppModalLoopForWindow(mRef);
418 break;
419
420 case kNPCDialogShowList:
421 NPNotification(" kNPCDialogShowList", -1);
422 npclient.dialogprocess = kNPCDialogNone;
423 HIViewSetVisible(ctl, false);
424 npclient.dialogsheet = true;
425 NPClientBeginPlayerListSheet();
426 break;
427
428 case kNPCDialogDone:
429 NPNotification(" kNPCDialogDone", -1);
430 npclient.dialogprocess = kNPCDialogNone;
431 NPClientEndPlayerListSheet();
432 npclient.dialogsheet = false;
433 npclient.dialogcancel = false;
434 QuitAppModalLoopForWindow(mRef);
435 break;
436 }
437 }
438
NPClientDetachConnectThread(void)439 static void NPClientDetachConnectThread (void)
440 {
441 pthread_create(&connectthread, NULL, NPClientConnectThread, NULL);
442 pthread_detach(connectthread);
443 }
444
NPClientConnectThread(void *)445 static void * NPClientConnectThread (void *)
446 {
447 NPNotification("Client: Entered connection thread.", -1);
448
449 if ((NPClientConnectToServer(NP_PORT) == false) ||
450 (NPClientSendNameToServer() == false) ||
451 (NPClientGetROMInfoFromServer() == false))
452 {
453 NPClientDisconnect();
454 npclient.dialogprocess = kNPCDialogConnectFailed;
455 return (NULL);
456 }
457
458 npclient.dialogprocess = kNPCDialogOpenBegin;
459 NPNotification("Client: Exited connection thread.", -1);
460 return (NULL);
461 }
462
NPClientInit(void)463 void NPClientInit (void)
464 {
465 npclient.padloop = false;
466 npclient.exitsgn = false;
467 npclient.phasecount = 0;
468 npclient.phasespan = 0;
469 npclient.header = 0;
470 npclient.online = false;
471 npclient.socket = -1;
472 npclient.numplayers = 0;
473 npclient.name[0] = 0;
474 npclient.serverIP[0] = 0;
475
476 nprominfo.crc32 = 0;
477 nprominfo.input = 0;
478 nprominfo.length = 0;
479 nprominfo.fname[0] = 0;
480
481 for (int i = 0; i < NP_MAX_PLAYERS; i++)
482 {
483 for (int j = 0; j < 64; j++)
484 {
485 npcactvpad[i][j] = 0;
486 npcrecvpad[i][j] = 0;
487 }
488 }
489
490 for (int j = 0; j < 64; j++)
491 {
492 npcsendpad[j] = 0;
493 npccachpad[j] = 0;
494 }
495
496 for (int c = 0; c < NP_MAX_PLAYERS; c++)
497 {
498 npcinfo[c].ready = false;
499 npcinfo[c].player = 0;
500 npcinfo[c].name[0] = 0;
501 }
502 }
503
NPClientConnectToServer(int port)504 static bool8 NPClientConnectToServer (int port)
505 {
506 #ifndef SELF_TEST
507 struct sockaddr_in address;
508 #else
509 struct sockaddr_un address;
510 #endif
511
512 NPNotification("Client: Connecting to server...", -1);
513
514 memset(&address, 0, sizeof(address));
515 #ifndef SELF_TEST
516 address.sin_family = AF_INET;
517 address.sin_addr.s_addr = inet_addr(npclient.serverIP);
518 address.sin_port = htons(port);
519 #else
520 address.sun_family = AF_UNIX;
521 strcpy(address.sun_path, SOCK_NAME);
522 #endif
523
524 #ifndef SELF_TEST
525 if (address.sin_addr.s_addr == INADDR_NONE)
526 {
527 NPError("Client: Server IP is invalid.", 5001);
528 return (false);
529 }
530 #endif
531
532 #ifndef SELF_TEST
533 if ((npclient.socket = socket(PF_INET, SOCK_STREAM, 0)) < 0)
534 #else
535 if ((npclient.socket = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
536 #endif
537 {
538 NPError("Client: Failed to create socket.", 5002);
539 return (false);
540 }
541
542 if (connect(npclient.socket, (struct sockaddr *) &address, sizeof(address)) < 0)
543 {
544 NPError("Client: Failed to connect to server.", 5003);
545 return (false);
546 }
547
548 npclient.online = true;
549
550 NPNotification("Client: Connected to server.", -1);
551 return (true);
552 }
553
NPClientDisconnect(void)554 void NPClientDisconnect (void)
555 {
556 if (npclient.socket != -1)
557 {
558 NPNotification("Client: Disconnecting from server...", -1);
559
560 close(npclient.socket);
561 npclient.socket = -1;
562
563 NPNotification("Client: Disconnected from server.", -1);
564 }
565
566 npclient.online = false;
567 npclient.name[0] = 0;
568 npclient.serverIP[0] = 0;
569 }
570
NPClientSendMesToServer(int num)571 static bool8 NPClientSendMesToServer (int num)
572 {
573 uint8 mes[2];
574
575 mes[0] = NP_CLIENT_MAGIC;
576 mes[1] = num;
577
578 if (socket_write(npclient.socket, mes, 2) != 2)
579 return (false);
580
581 return (true);
582 }
583
NPClientGetMesFromServer(void)584 static int NPClientGetMesFromServer (void)
585 {
586 uint8 mes[2];
587
588 if (socket_read(npclient.socket, mes, 2) != 2)
589 return (-1);
590
591 if (mes[0] != NP_SERVER_MAGIC)
592 return (-1);
593
594 return ((int) mes[1]);
595 }
596
NPClientSendNameToServer(void)597 static bool8 NPClientSendNameToServer (void)
598 {
599 if (!npclient.online)
600 return (false);
601
602 NPNotification("Client: Sending player name to server...", -1);
603
604 if (NPClientGetMesFromServer() != kNPServerNameRequest)
605 {
606 NPError("Client: Failed to receive messsage from server.", 5101);
607 return (false);
608 }
609
610 uint8 mes[4];
611 uint32 l;
612
613 l = strlen(npclient.name);
614 WRITE_LONG(mes + 0, l);
615
616 if (socket_write(npclient.socket, mes, 4) != 4)
617 {
618 NPError("Client: Failed to send name size to server.", 5102);
619 return (false);
620 }
621
622 if (socket_write(npclient.socket, (uint8 *) npclient.name, l) != (int) l)
623 {
624 NPError("Client: Failed to send name to server.", 5103);
625 return (false);
626 }
627
628 if (NPClientGetMesFromServer() != kNPServerNameReceived)
629 {
630 NPError("Client: Failed to receive messsage from server.", 5104);
631 return (false);
632 }
633
634 if (NPClientSendMesToServer(kNPClientNameSent) == false)
635 {
636 NPError("Client: Failed to send messsage to server.", 5105);
637 return (false);
638 }
639
640 NPNotification("Client: Sent player name to server.", -1);
641 return (true);
642 }
643
NPClientGetROMInfoFromServer(void)644 static bool8 NPClientGetROMInfoFromServer (void)
645 {
646 if (!npclient.online)
647 return (false);
648
649 NPNotification("Client: Receiving ROM information from server...", -1);
650
651 if (NPClientGetMesFromServer() != kNPServerROMInfoWillSend)
652 {
653 NPError("Client: Failed to receive messsage from server.", 5201);
654 return (false);
655 }
656
657 if (NPClientSendMesToServer(kNPClientROMInfoWaiting) == false)
658 {
659 NPError("Client: Failed to send messsage to server.", 5202);
660 return (false);
661 }
662
663 uint8 mes[16];
664 uint32 l;
665
666 if (socket_read(npclient.socket, mes, 16) != 16)
667 {
668 NPError("Client: Failed to receive ROM information from server.", 5203);
669 return (false);
670 }
671
672 nprominfo.crc32 = READ_LONG(mes + 0);
673 nprominfo.input = READ_LONG(mes + 4);
674
675 l = READ_LONG(mes + 12);
676
677 if (socket_read(npclient.socket, (uint8 *) nprominfo.fname, l) != (int) l)
678 {
679 NPError("Client: Failed to receive ROM name from server.", 5204);
680 return (false);
681 }
682
683 nprominfo.fname[l] = 0;
684 nprominfo.length = l;
685
686 NPNotification("Client: Received ROM information from server.", -1);
687 return (true);
688 }
689
NPClientStoreConfig(void)690 void NPClientStoreConfig (void)
691 {
692 npclient.savedDeviceSetting = deviceSetting;
693 npclient.savedAutoSaveDelay = Settings.AutoSaveDelay;
694
695 npclient.configsaved = true;
696
697 deviceSetting = nprominfo.input;
698 Settings.AutoSaveDelay = 0;
699
700 ChangeInputDevice();
701 }
702
NPClientRestoreConfig(void)703 void NPClientRestoreConfig (void)
704 {
705 if (npclient.configsaved)
706 {
707 deviceSetting = npclient.savedDeviceSetting;
708 Settings.AutoSaveDelay = npclient.savedAutoSaveDelay;
709
710 npclient.configsaved = false;
711
712 ChangeInputDevice();
713 }
714 }
715
NPClientBeginOpenROMImage(WindowRef window)716 static bool8 NPClientBeginOpenROMImage (WindowRef window)
717 {
718 CFStringRef numRef, romRef, baseRef;
719 CFMutableStringRef mesRef;
720 SInt32 replaceAt;
721 bool8 r;
722
723 DeinitGameWindow();
724
725 if (cartOpen)
726 {
727 SNES9X_SaveSRAM();
728 S9xResetSaveTimer(false);
729 S9xSaveCheatFile(S9xGetFilename(".cht", CHEAT_DIR));
730 }
731
732 cartOpen = false;
733
734 ResetCheatFinder();
735
736 romRef = CFStringCreateWithCString(kCFAllocatorDefault, nprominfo.fname, kCFStringEncodingUTF8);
737 numRef = CFCopyLocalizedString(CFSTR("NPROMNamePos"), "1");
738 baseRef = CFCopyLocalizedString(CFSTR("NPROMNameMes"), "NPROM");
739 mesRef = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, baseRef);
740 replaceAt = CFStringGetIntValue(numRef);
741 CFStringReplace(mesRef, CFRangeMake(replaceAt - 1, 1), romRef);
742
743 r = NavBeginOpenROMImageSheet(window, mesRef);
744
745 CFRelease(mesRef);
746 CFRelease(baseRef);
747 CFRelease(numRef);
748 CFRelease(romRef);
749
750 return (r);
751 }
752
NPClientEndOpenROMImage(void)753 static bool8 NPClientEndOpenROMImage (void)
754 {
755 OSStatus err;
756 FSRef cartRef;
757 char filename[PATH_MAX + 1];
758 bool8 r;
759
760 r = NavEndOpenROMImageSheet(&cartRef);
761 if (!r)
762 {
763 cartOpen = false;
764 return (false);
765 }
766
767 CheckSaveFolder(&cartRef);
768
769 Settings.ForceLoROM = (romDetect == kLoROMForce );
770 Settings.ForceHiROM = (romDetect == kHiROMForce );
771 Settings.ForceHeader = (headerDetect == kHeaderForce );
772 Settings.ForceNoHeader = (headerDetect == kNoHeaderForce );
773 Settings.ForceInterleaved = (interleaveDetect == kInterleaveForce );
774 Settings.ForceInterleaved2 = (interleaveDetect == kInterleave2Force );
775 Settings.ForceInterleaveGD24 = (interleaveDetect == kInterleaveGD24 );
776 Settings.ForceNotInterleaved = (interleaveDetect == kNoInterleaveForce);
777 Settings.ForcePAL = (videoDetect == kPALForce );
778 Settings.ForceNTSC = (videoDetect == kNTSCForce );
779
780 GFX.InfoString = NULL;
781 GFX.InfoStringTimeout = 0;
782
783 S9xResetSaveTimer(true);
784
785 err = FSRefMakePath(&cartRef, (unsigned char *) filename, PATH_MAX);
786
787 SNES9X_InitSound();
788
789 if (Memory.LoadROM(filename) /*&& (Memory.ROMCRC32 == nprominfo.crc32)*/)
790 {
791 ChangeTypeAndCreator(filename, 'CART', '~9X~');
792 cartOpen = true;
793 return (true);
794 }
795 else
796 {
797 cartOpen = false;
798 return (false);
799 }
800 }
801
NPClientDetachPrepareThread(void)802 static void NPClientDetachPrepareThread (void)
803 {
804 pthread_create(&preparethread, NULL, NPClientPrepareThread, NULL);
805 pthread_detach(preparethread);
806 }
807
NPClientPrepareThread(void *)808 static void * NPClientPrepareThread (void *)
809 {
810 NPNotification("Client: Entered preparing thread.", -1);
811
812 if ((NPClientROMReadyToServer() == false) ||
813 (NPClientGetSRAMFromServer() == false) ||
814 (NPClientGetPlayerListFromServer() == false) ||
815 (NPClientReplyPhaseSpanTest() == false))
816 {
817 NPClientDisconnect();
818 npclient.dialogprocess = kNPCDialogPrepareFailed;
819 return (NULL);
820 }
821
822 npclient.dialogprocess = kNPCDialogShowList;
823 NPNotification("Client: Exited preparing thread.", -1);
824 return (NULL);
825 }
826
NPClientROMReadyToServer(void)827 static bool8 NPClientROMReadyToServer (void)
828 {
829 if (!npclient.online)
830 return (false);
831
832 NPNotification("Client: Sending ROM ready sign to server...", -1);
833
834 if (NPClientSendMesToServer(kNPClientROMOpened) == false)
835 {
836 NPError("Client: Failed to send messsage to server.", 5401);
837 return (false);
838 }
839
840 NPNotification("Client: Sent ROM ready sign to server.", -1);
841 return (true);
842 }
843
NPClientGetSRAMFromServer(void)844 static bool8 NPClientGetSRAMFromServer (void)
845 {
846 if (!npclient.online)
847 return (false);
848
849 NPNotification("Client: Receiving SRAM from server...", -1);
850
851 if (NPClientGetMesFromServer() != kNPServerSRAMWillSend)
852 {
853 NPError("Client: Failed to receive messsage from server.", 5501);
854 return (false);
855 }
856
857 if (NPClientSendMesToServer(kNPClientSRAMWaiting) == false)
858 {
859 NPError("Client: Failed to send messsage to server.", 5502);
860 return (false);
861 }
862
863 uint8 mes[4];
864 uint32 sramsize;
865
866 if (socket_read(npclient.socket, mes, 4) != 4)
867 {
868 NPError("Client: Failed to receive SRAM size from server.", 5503);
869 return (false);
870 }
871
872 sramsize = READ_LONG(mes + 0);
873
874 if (sramsize != (uint32) (Memory.SRAMSize ? (1 << (Memory.SRAMSize + 3)) * 128 : 0))
875 {
876 NPError("Client: SRAM size mismatch.", 5504);
877 return (false);
878 }
879
880 if (sramsize && (socket_read(npclient.socket, Memory.SRAM, sramsize) != (int) sramsize))
881 {
882 NPError("Server: Failed to receive SRAM from server.", 5505);
883 return (false);
884 }
885
886 if (NPClientSendMesToServer(kNPClientSRAMLoaded) == false)
887 {
888 NPError("Client: Failed to send messsage to server.", 5506);
889 return (false);
890 }
891
892 NPNotification("Client: Received SRAM from server.", -1);
893 return (true);
894 }
895
NPClientGetPlayerListFromServer(void)896 static bool8 NPClientGetPlayerListFromServer (void)
897 {
898 if (!npclient.online)
899 return (false);
900
901 NPNotification("Client: Receiving player list from server...", -1);
902
903 if (NPClientGetMesFromServer() != kNPServerPlayerWillSend)
904 {
905 NPError("Client: Failed to receive messsage from server.", 5701);
906 return (false);
907 }
908
909 if (NPClientSendMesToServer(kNPClientPlayerWaiting) == false)
910 {
911 NPError("Client: Failed to send messsage to server.", 5702);
912 return (false);
913 }
914
915 for (int i = 0; i < NP_MAX_PLAYERS; i++)
916 {
917 uint8 mes[10];
918 uint32 l;
919
920 if (socket_read(npclient.socket, mes, 10) != 10)
921 {
922 NPError("Client: Failed to receive messsage from server.", 5703);
923 return (false);
924 }
925
926 npcinfo[i].ready = READ_BYTE(mes + 1);
927 npcinfo[i].player = READ_LONG(mes + 2);
928
929 l = READ_LONG(mes + 6);
930
931 if (l && (socket_read(npclient.socket, (uint8 *) npcinfo[i].name, l) != (int) l))
932 {
933 NPError("Client: Failed to receive messsage from server.", 5704);
934 return (false);
935 }
936
937 npcinfo[i].name[l] = 0;
938 }
939
940 npclient.numplayers = 0;
941 for (int i = 0; i < NP_MAX_PLAYERS; i++)
942 if (npcinfo[i].ready)
943 npclient.numplayers++;
944
945 NPNotification("Client: Received player list from server.", -1);
946 NPNotification("Client: Number of players: %d", npclient.numplayers);
947
948 return (true);
949 }
950
NPClientReplyPhaseSpanTest(void)951 static bool8 NPClientReplyPhaseSpanTest (void)
952 {
953 uint8 mes[21];
954 int l = npclient.numplayers * 4 + 1;
955
956 NPNotification("Client: Replying sending / receiving pad states test...", -1);
957
958 for (int n = 0; n < 5; n++)
959 {
960 if (socket_read(npclient.socket, mes, l) != l)
961 return (false);
962
963 if (socket_write(npclient.socket, mes, 5) != 5)
964 return (false);
965 }
966
967 NPNotification("Client: Replied sending / receiving pad states test.", -1);
968
969 NPNotification("Client: Receiving phase span value from server...", -1);
970
971 if (socket_read(npclient.socket, mes, 4) != 4)
972 return (false);
973
974 npclient.phasespan = READ_LONG(mes + 0);
975
976 NPNotification(" phase span: %d (frames)", npclient.phasespan);
977
978 NPNotification("Client: Received phase span value from server.", -1);
979
980 return (true);
981 }
982
NPClientBeginPlayerListSheet(void)983 static void NPClientBeginPlayerListSheet (void)
984 {
985 OSStatus err;
986 CFStringRef ref;
987 HIViewRef ctl, root;
988 HIViewID cid;
989
990 root = HIViewGetRoot(sRef);
991 cid.signature = 'PLNM';
992
993 for (int i = 0; i < NP_MAX_PLAYERS; i++)
994 {
995 if (npcinfo[i].ready)
996 {
997 cid.id = npcinfo[i].player;
998 HIViewFindByID(root, cid, &ctl);
999 ref = CFStringCreateWithCString(kCFAllocatorDefault, npcinfo[i].name, kCFStringEncodingUTF8);
1000 if (ref)
1001 {
1002 SetStaticTextCFString(ctl, ref, false);
1003 CFRelease(ref);
1004 }
1005 else
1006 SetStaticTextCFString(ctl, CFSTR("unknown"), false);
1007 }
1008 }
1009
1010 err = ShowSheetWindow(sRef, mRef);
1011 }
1012
NPClientEndPlayerListSheet(void)1013 static void NPClientEndPlayerListSheet (void)
1014 {
1015 OSStatus err;
1016
1017 err = HideSheetWindow(sRef);
1018 }
1019
NPClientSheetEventHandler(EventHandlerCallRef inHandlerRef,EventRef inEvent,void * inUserData)1020 static pascal OSStatus NPClientSheetEventHandler (EventHandlerCallRef inHandlerRef, EventRef inEvent, void *inUserData)
1021 {
1022 if (!npclient.dialogsheet)
1023 return (eventNotHandledErr);
1024
1025 OSStatus err, result = eventNotHandledErr;
1026
1027 switch (GetEventClass(inEvent))
1028 {
1029 case kEventClassCommand:
1030 switch (GetEventKind(inEvent))
1031 {
1032 HICommand tHICommand;
1033
1034 case kEventCommandUpdateStatus:
1035 err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand);
1036 if (err == noErr && tHICommand.commandID == 'clos')
1037 {
1038 UpdateMenuCommandStatus(false);
1039 result = noErr;
1040 }
1041
1042 break;
1043
1044 case kEventCommandProcess:
1045 err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand);
1046 if (err == noErr)
1047 {
1048 switch (tHICommand.commandID)
1049 {
1050 case 'ok ':
1051 npclient.dialogprocess = kNPCDialogDone;
1052 result = noErr;
1053 break;
1054 }
1055 }
1056
1057 break;
1058 }
1059
1060 break;
1061 }
1062
1063 return (result);
1064 }
1065
NPClientDetachNetPlayThread(void)1066 void NPClientDetachNetPlayThread (void)
1067 {
1068 NPNotification("Client: Detaching pad thread...", -1);
1069
1070 npclient.padloop = true;
1071 npclient.exitsgn = false;
1072
1073 csememu = sem_open(n_csememu, O_CREAT, 0600, 0);
1074 csempad = sem_open(n_csempad, O_CREAT, 0600, 0);
1075
1076 pthread_create(&gamepadthread, NULL, NPClientNetPlayThread, NULL);
1077 pthread_detach(gamepadthread);
1078
1079 NPNotification("Client: Detached pad thread.", -1);
1080 }
1081
NPClientStopNetPlayThread(void)1082 void NPClientStopNetPlayThread (void)
1083 {
1084 NPNotification("Client: Stopping pad thread...", -1);
1085
1086 npclient.padloop = false;
1087 sem_post(csempad);
1088 sem_post(csememu);
1089
1090 while (!npclient.exitsgn)
1091 sleep(0);
1092
1093 sem_unlink(n_csememu);
1094 sem_unlink(n_csempad);
1095 sem_close(csememu);
1096 sem_close(csempad);
1097
1098 NPNotification("Client: Stopped pad thread.", -1);
1099 }
1100
NPClientNetPlayWaitStart(void)1101 bool8 NPClientNetPlayWaitStart (void)
1102 {
1103 NPNotification("Client: Waiting start flag...", -1);
1104
1105 if (NPClientSendMesToServer(kNPClientStartWait) == false)
1106 {
1107 NPError("Client: Failed to send messsage to server.", 5801);
1108 return (false);
1109 }
1110
1111 if (NPClientGetMesFromServer() != kNPServerStart)
1112 {
1113 NPError("Client: Failed to send messsage to server.", 5802);
1114 return (false);
1115 }
1116
1117 npclient.phasecount = 0;
1118 npclient.header = 0;
1119
1120 sem_post(csempad);
1121
1122 NPNotification("Client: Netplay started.", -1);
1123 return (true);
1124 }
1125
NPClientNetPlayThread(void *)1126 static void * NPClientNetPlayThread (void *)
1127 {
1128 uint8 mes[NP_MAX_PLAYERS * 64 * 4 + 1];
1129 uint8 count = 0;
1130 int l;
1131
1132 NPNotification("Client: Entered pad thread.", -1);
1133
1134 while (npclient.padloop)
1135 {
1136 sem_wait(csempad);
1137
1138 l = npclient.numplayers * npclient.phasespan * 4 + 1;
1139 if (socket_read(npclient.socket, mes, l) != l)
1140 {
1141 npclient.exitsgn = true;
1142 sem_post(csememu);
1143 pthread_exit(NULL);
1144 }
1145
1146 if ((mes[0] & 0xF) != count)
1147 NPNotification("Client: Warning: Failed to synchronize server.", -1);
1148
1149 npclient.header = mes[0] & 0xF0;
1150
1151 for (int i = 0; i < npclient.numplayers; i++)
1152 for (uint32 j = 0; j < npclient.phasespan; j++)
1153 npcrecvpad[i][j] = READ_LONG(mes + (i * npclient.phasespan + j) * 4 + 1);
1154
1155 WRITE_BYTE(mes + 0, count);
1156
1157 for (uint32 j = 0; j < npclient.phasespan; j++)
1158 WRITE_LONG(mes + j * 4 + 1, npcsendpad[j]);
1159
1160 l = npclient.phasespan * 4 + 1;
1161 if (socket_write(npclient.socket, mes, l) != l)
1162 {
1163 npclient.exitsgn = true;
1164 sem_post(csememu);
1165 pthread_exit(NULL);
1166 }
1167
1168 count = (count + 1) & 0xF;
1169
1170 sem_post(csememu);
1171 }
1172
1173 npclient.exitsgn = true;
1174
1175 NPNotification("Client: Exited pad thread.", -1);
1176 return (NULL);
1177 }
1178
NPClientProcessInput(void)1179 void NPClientProcessInput (void)
1180 {
1181 static uint32 pos = 0;
1182 KeyMap myKeys;
1183
1184 if (npclient.exitsgn)
1185 {
1186 if (s9xthreadrunning)
1187 {
1188 if (!eventQueued)
1189 {
1190 PostQueueToSubEventLoop();
1191 eventQueued = true;
1192 }
1193 }
1194 else
1195 running = false;
1196
1197 return;
1198 }
1199
1200 if (npclient.phasecount == 0)
1201 {
1202 sem_wait(csememu);
1203
1204 for (int i = 0; i < npclient.numplayers; i++)
1205 for (uint32 j = 0; j < npclient.phasespan; j++)
1206 npcactvpad[i][j] = npcrecvpad[i][j];
1207
1208 for (uint32 j = 0; j < npclient.phasespan; j++)
1209 npcsendpad[j] = npccachpad[j];
1210
1211 if (npclient.header & 0x80)
1212 {
1213 npcsendpad[npclient.phasespan] = 0;
1214 for (int i = 0; i < npclient.numplayers; i++)
1215 npcactvpad[i][npclient.phasespan] = 0;
1216
1217 npclient.phasespan++;
1218 if (npclient.phasespan > (uint32) Memory.ROMFramesPerSecond)
1219 npclient.phasespan = (uint32) Memory.ROMFramesPerSecond;
1220
1221 char str[256];
1222 sprintf(str, "delay: %d", npclient.phasespan);
1223 S9xMessage(0, 0, str);
1224 }
1225 else
1226 if (npclient.header & 0x40)
1227 {
1228 npclient.phasespan--;
1229 if (npclient.phasespan == 0)
1230 npclient.phasespan = 1;
1231
1232 char str[256];
1233 sprintf(str, "delay: %d", npclient.phasespan);
1234 S9xMessage(0, 0, str);
1235 }
1236
1237 npclient.header = 0;
1238 pos = 0;
1239 }
1240
1241 for (int i = 0; i < npclient.numplayers; i++)
1242 {
1243 controlPad[i] = npcactvpad[i][pos];
1244 ControlPadFlagsToS9xReportButtons(i, controlPad[i]);
1245 }
1246
1247 GetKeys(myKeys);
1248
1249 uint32 pad = 0;
1250
1251 JoypadScanDirection(0, &pad);
1252 if (ISpKeyIsPressed(kISp1PR )) pad |= 0x0010;
1253 if (ISpKeyIsPressed(kISp1PL )) pad |= 0x0020;
1254 if (ISpKeyIsPressed(kISp1PX )) pad |= 0x0040;
1255 if (ISpKeyIsPressed(kISp1PA )) pad |= 0x0080;
1256 if (ISpKeyIsPressed(kISp1PStart )) pad |= 0x1000;
1257 if (ISpKeyIsPressed(kISp1PSelect)) pad |= 0x2000;
1258 if (ISpKeyIsPressed(kISp1PY )) pad |= 0x4000;
1259 if (ISpKeyIsPressed(kISp1PB )) pad |= 0x8000;
1260
1261 if (KeyIsPressed(myKeys, keyCode[k1PR] )) pad |= 0x0010;
1262 if (KeyIsPressed(myKeys, keyCode[k1PL] )) pad |= 0x0020;
1263 if (KeyIsPressed(myKeys, keyCode[k1PX] )) pad |= 0x0040;
1264 if (KeyIsPressed(myKeys, keyCode[k1PA] )) pad |= 0x0080;
1265 if (KeyIsPressed(myKeys, keyCode[k1PRight] )) pad |= 0x0100;
1266 if (KeyIsPressed(myKeys, keyCode[k1PLeft] )) pad |= 0x0200;
1267 if (KeyIsPressed(myKeys, keyCode[k1PDown] )) pad |= 0x0400;
1268 if (KeyIsPressed(myKeys, keyCode[k1PUp] )) pad |= 0x0800;
1269 if (KeyIsPressed(myKeys, keyCode[k1PStart] )) pad |= 0x1000;
1270 if (KeyIsPressed(myKeys, keyCode[k1PSelect])) pad |= 0x2000;
1271 if (KeyIsPressed(myKeys, keyCode[k1PY] )) pad |= 0x4000;
1272 if (KeyIsPressed(myKeys, keyCode[k1PB] )) pad |= 0x8000;
1273
1274 npccachpad[pos] = pad;
1275
1276 if (npclient.phasecount == 0)
1277 {
1278 npclient.phasecount = npclient.phasespan;
1279 sem_post(csempad);
1280 }
1281
1282 npclient.phasecount--;
1283 pos++;
1284 }
1285