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