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