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#import "snes9x.h"
196#import "memmap.h"
197#import "apu.h"
198#import "controls.h"
199#import "crosshairs.h"
200#import "cheats.h"
201#import "movie.h"
202#import "snapshot.h"
203#import "display.h"
204#import "blit.h"
205
206#ifdef DEBUGGER
207#import "debug.h"
208#endif
209
210#import <Cocoa/Cocoa.h>
211#import <QuickTime/QuickTime.h>
212#import <pthread.h>
213
214#import "mac-prefix.h"
215#import "mac-appleevent.h"
216#import "mac-audio.h"
217#import "mac-cheat.h"
218#import "mac-cheatfinder.h"
219#import "mac-client.h"
220#import "mac-cocoatools.h"
221#import "mac-controls.h"
222#import "mac-coreimage.h"
223#import "mac-dialog.h"
224#import "mac-file.h"
225#import "mac-gworld.h"
226#import "mac-joypad.h"
227#import "mac-keyboard.h"
228#import "mac-multicart.h"
229#import "mac-musicbox.h"
230#import "mac-netplay.h"
231#import "mac-prefs.h"
232#import "mac-quicktime.h"
233#import "mac-render.h"
234#import "mac-screenshot.h"
235#import "mac-server.h"
236#import "mac-snes9x.h"
237#import "mac-stringtools.h"
238#import "mac-os.h"
239
240#define	kRecentMenu_MAX		20
241#define KeyIsPressed(km, k)	(1 & (((unsigned char *) km) [(k) >> 3] >> ((k) & 7)))
242
243volatile bool8		running             = false;
244volatile bool8		s9xthreadrunning    = false;
245
246volatile bool8		eventQueued         = false;
247
248volatile int		windowResizeCount   = 1;
249volatile bool8		windowExtend        = true;
250
251SInt32				systemVersion;
252
253uint32				controlPad[MAC_MAX_PLAYERS];
254
255uint8				romDetect           = 0,
256					interleaveDetect    = 0,
257					videoDetect         = 0,
258					headerDetect        = 0;
259
260WindowRef			gWindow             = NULL;
261HIRect				gWindowRect;
262int					glScreenW,
263					glScreenH;
264CGRect				glScreenBounds;
265Point				windowPos[kWindowCount];
266CGSize				windowSize[kWindowCount];
267
268CGImageRef			macIconImage[118];
269int					macPadIconIndex,
270					macLegendIconIndex,
271					macMusicBoxIconIndex,
272					macFunctionIconIndex;
273
274int					macFrameSkip        = -1;
275int32				skipFrames          = 3;
276int64				lastFrame           = 0;
277
278int					macFastForwardRate  = 5,
279					macFrameAdvanceRate = 1000000;
280
281unsigned long		spcFileCount        = 0,
282					pngFileCount        = 0;
283
284bool8				finished            = false,
285					cartOpen            = false,
286					autofire            = false,
287					hidExist            = true,
288					directDisplay       = false;
289
290bool8				fullscreen          = false,
291					autoRes             = false,
292					glstretch           = true,
293					gl32bit             = true,
294					vsync               = true,
295					drawoverscan        = false,
296					screencurvature     = false,
297					multiprocessor      = false,
298					ciFilterEnable      = false;
299long				drawingMethod       = kDrawingOpenGL;
300int					videoMode           = VIDEOMODE_SMOOTH;
301
302SInt32				macSoundVolume      = 80;	// %
303uint32				macSoundBuffer_ms   = 100;	// ms
304uint32				macSoundInterval_ms = 16;   // ms
305bool8				macSoundLagEnable   = false;
306uint16				aueffect            = 0;
307
308uint8				saveInROMFolder     = 2;	// 0 : Snes9x  1 : ROM  2 : Application Support
309CFStringRef			saveFolderPath;
310
311int					macCurvatureWarp    = 15,
312					macAspectRatio      = 0;
313
314bool8				startopendlog       = false,
315					showtimeinfrz       = true,
316					enabletoggle        = true,
317					savewindowpos       = false,
318					onscreeninfo        = true;
319int					inactiveMode        = 2;
320int					musicboxmode        = kMBXSoundEmulation;
321
322bool8				applycheat          = false;
323int					padSetting          = 1,
324					deviceSetting       = 1,
325					deviceSettingMaster = 1;
326int					macControllerOption = SNES_JOYPAD;
327AutoFireState		autofireRec[MAC_MAX_PLAYERS];
328
329bool8				macQTRecord         = false;
330uint16				macQTMovFlag        = 0;
331
332uint16				macRecordFlag       = 0x3,
333					macPlayFlag         = 0x1;
334wchar_t				macRecordWChar[MOVIE_MAX_METADATA];
335
336char				npServerIP[256],
337					npName[256];
338
339bool8				lastoverscan        = false;
340
341CGPoint				unlimitedCursor;
342
343ExtraOption			extraOptions;
344
345CFStringRef			multiCartPath[2];
346
347#ifdef MAC_PANTHER_SUPPORT
348IconRef				macIconRef[118];
349#endif
350
351enum
352{
353	mApple          = 128,
354	iAbout          = 1,
355
356	mFile           = 129,
357	iOpen           = 1,
358	iOpenMulti      = 2,
359	iOpenRecent     = 3,
360	iClose          = 5,
361	iRomInfo        = 7,
362
363	mControl        = 134,
364	iKeyboardLayout = 1,
365	iISpLayout      = 2,
366	iAutoFire       = 4,
367	iISpPreset      = 6,
368
369	mEdit           = 130,
370
371	mEmulation      = 131,
372	iResume         = 1,
373	iSoftReset      = 3,
374	iReset          = 4,
375	iDevice         = 6,
376
377	mCheat          = 132,
378	iApplyCheats    = 1,
379	iGameGenie      = 3,
380	iCheatFinder    = 4,
381
382	mOption         = 133,
383	iFreeze         = 1,
384	iDefrost        = 2,
385	iFreezeTo       = 4,
386	iDefrostFrom    = 5,
387	iRecordMovie    = 7,
388	iPlayMovie      = 8,
389	iQTMovie        = 10,
390	iSaveSPC        = 12,
391	iSaveSRAM       = 13,
392	iCIFilter       = 15,
393	iMusicBox       = 17,
394
395	mNetplay        = 135,
396	iServer         = 1,
397	iClient         = 2,
398
399	mPresets        = 201,
400
401	mDevice         = 202,
402	iPad            = 1,
403	iMouse			= 2,
404	iMouse2         = 3,
405	iSuperScope     = 4,
406	iMultiPlayer5   = 5,
407	iMultiPlayer5_2 = 6,
408	iJustifier1     = 7,
409	iJustifier2     = 8,
410
411	mRecentItem     = 203
412};
413
414enum
415{
416	kmF1Key    = 0x7A,
417	kmF2Key    = 0x78,
418	kmF3Key	   = 0x63,
419	kmF4Key	   = 0x76,
420	kmF5Key	   = 0x60,
421	kmF6Key    = 0x61,
422	km0Key     = 0x1D,
423	km1Key     = 0x12,
424	km2Key     = 0x13,
425	km3Key     = 0x14,
426	km4Key     = 0x15,
427	km5Key     = 0x17,
428	km6Key     = 0x16,
429	km7Key     = 0x1A,
430	km8Key     = 0x1C,
431	km9Key     = 0x19,
432	kmAKey     = 0x00,
433	kmBKey     = 0x0B,
434	kmCKey     = 0x08,
435	kmEscKey   = 0x35,
436	kmCtrKey   = 0x3B,
437	kmMinusKey = 0x1B,
438	kmQKey     = 0x0C,
439	kmWKey     = 0x0D,
440	kmOKey     = 0x1F,
441	kmPKey     = 0x23
442};
443
444struct ButtonCommand
445{
446	char	command[16];
447	uint8	keycode;
448	bool8	held;
449};
450
451struct GameViewInfo
452{
453	int		globalLeft;
454	int		globalTop;
455	int		width;
456	int		height;
457};
458
459static volatile bool8	rejectinput     = false;
460
461static bool8			pauseEmulation  = false,
462						frameAdvance    = false;
463
464static pthread_t		s9xthread;
465
466static MenuRef			recentMenu;
467static CFStringRef		recentItem[kRecentMenu_MAX + 1];
468
469static EventHandlerUPP	gameWindowUPP,
470						gameWUPaneUPP;
471static EventHandlerRef	gameWindowEventRef,
472						gameWUPaneEventRef;
473
474static int				windowZoomCount = 0;
475
476static int				frameCount      = 0;
477
478static bool8			frzselecting    = false;
479
480static uint16			changeAuto[2] = { 0x0000, 0x0000 };
481
482static GameViewInfo		scopeViewInfo;
483
484static ButtonCommand	btncmd[] =
485{
486	{ "ToggleBG0",       kmF1Key,    false },
487	{ "ToggleBG1",       kmF2Key,    false },
488	{ "ToggleBG2",       kmF3Key,    false },
489	{ "ToggleBG3",       kmF4Key,    false },
490	{ "ToggleSprites",   kmF5Key,    false },
491	{ "SwapJoypads",     kmF6Key,    false },
492	{ "SoundChannel0",   km1Key,     false },
493	{ "SoundChannel1",   km2Key,     false },
494	{ "SoundChannel2",   km3Key,     false },
495	{ "SoundChannel3",   km4Key,     false },
496	{ "SoundChannel4",   km5Key,     false },
497	{ "SoundChannel5",   km6Key,     false },
498	{ "SoundChannel6",   km7Key,     false },
499	{ "SoundChannel7",   km8Key,     false },
500	{ "SoundChannelsOn", km9Key,     false },
501	{ "_mac1",           km0Key,     false },
502	{ "_mac2",           kmMinusKey, false },
503	{ "_mac3",           kmQKey,     false },
504	{ "_mac4",           kmWKey,     false },
505	{ "_mac5",           kmOKey,     false },
506	{ "_mac6",           kmPKey,     false }
507};
508
509#define	kCommandListSize	(sizeof(btncmd) / sizeof(btncmd[0]))
510
511static void Initialize (void);
512static void Deinitialize (void);
513static void InitAutofire (void);
514static void InitRecentItems (void);
515static void DeinitRecentItems (void);
516static void ClearRecentItems (void);
517static void InitRecentMenu (void);
518static void DeinitRecentMenu (void);
519static void ProcessInput (void);
520static void ResizeGameWindow (void);
521static void ChangeAutofireSettings (int, int);
522static void ChangeTurboRate (int);
523static void ForceChangingKeyScript (void);
524static void CFTimerCallback (CFRunLoopTimerRef, void *);
525static void UpdateFreezeDefrostScreen (int, CGImageRef, uint8 *, CGContextRef);
526static void * MacSnes9xThread (void *);
527static OSStatus HandleMenuChoice (UInt32, Boolean *);
528static inline void EmulationLoop (void);
529static pascal OSStatus MainEventHandler (EventHandlerCallRef, EventRef, void *);
530static pascal OSStatus SubEventHandler (EventHandlerCallRef, EventRef, void *);
531static pascal OSStatus GameWindowEventHandler (EventHandlerCallRef, EventRef, void *);
532static pascal OSStatus GameWindowUserPaneEventHandler (EventHandlerCallRef, EventRef, void *);
533
534
535int main (int argc, char **argv)
536{
537#ifdef MAC_PANTHER_SUPPORT
538	NSAutoreleasePool	*pool;
539#endif
540	OSStatus			err;
541	EventHandlerRef		eref;
542	EventHandlerUPP		eUPP;
543	EventTypeSpec		mEvents[] = { { kEventClassCommand, kEventCommandProcess      },
544									  { kEventClassCommand, kEventCommandUpdateStatus } },
545						sEvents[] = { { kEventClassCommand, kEventCommandProcess      },
546									  { kEventClassCommand, kEventCommandUpdateStatus },
547									  { kEventClassMouse,   kEventMouseUp             },
548									  { kEventClassMouse,   kEventMouseMoved          },
549									  { kEventClassMouse,   kEventMouseDragged        } };
550
551#ifdef MAC_PANTHER_SUPPORT
552	pool = [[NSAutoreleasePool alloc] init];
553#endif
554	eUPP = NewEventHandlerUPP(MainEventHandler);
555	err = InstallApplicationEventHandler(eUPP, GetEventTypeCount(mEvents), mEvents, NULL, &eref);
556
557	Initialize();
558
559	while (!finished)
560	{
561		if (cartOpen && running)
562		{
563		#ifdef DEBUGGER
564			CPU.Flags |= DEBUG_MODE_FLAG;
565			S9xDoDebug();
566		#endif
567
568			eventQueued = false;
569
570			Microseconds((UnsignedWide *) &lastFrame);
571			frameCount = 0;
572			if (macFrameSkip < 0)
573				skipFrames = 3;
574			else
575				skipFrames = macFrameSkip;
576
577			err = RemoveEventHandler(eref);
578			DisposeEventHandlerUPP(eUPP);
579		#ifdef MAC_PANTHER_SUPPORT
580			[pool release];
581
582			pool = [[NSAutoreleasePool alloc] init];
583		#endif
584			eUPP = NewEventHandlerUPP(SubEventHandler);
585			err = InstallApplicationEventHandler(eUPP, GetEventTypeCount(sEvents), sEvents, NULL, &eref);
586
587			S9xInitDisplay(NULL, NULL);
588			ClearGFXScreen();
589
590			if (!fullscreen)
591				ForceChangingKeyScript();
592
593			pthread_create(&s9xthread, NULL, MacSnes9xThread, NULL);
594
595			CFRunLoopTimerRef		cftimer    = NULL;
596			CFRunLoopTimerContext	cftimerctx = { 0, NULL, NULL, NULL, NULL };
597
598			if (!fullscreen)
599			{
600				cftimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent(), 30, 0, 0, CFTimerCallback, &cftimerctx);
601				if (cftimer)
602					CFRunLoopAddTimer(CFRunLoopGetCurrent(), cftimer, kCFRunLoopCommonModes);
603			}
604
605			AdjustMenus();
606
607			RunApplicationEventLoop();
608
609			if (!fullscreen)
610			{
611				if (cftimer)
612				{
613					CFRunLoopTimerInvalidate(cftimer);
614					CFRelease(cftimer);
615					cftimer = NULL;
616				}
617			}
618
619			pthread_join(s9xthread, NULL);
620
621			if (!Settings.NetPlay || Settings.NetPlayServer)
622			{
623				SNES9X_SaveSRAM();
624				S9xResetSaveTimer(false);
625				S9xSaveCheatFile(S9xGetFilename(".cht", CHEAT_DIR));
626			}
627
628			S9xDeinitDisplay();
629
630			if (Settings.NetPlay)
631			{
632				if (!Settings.NetPlayServer)
633				{
634					DeinitGameWindow();
635					cartOpen = false;
636				}
637
638				Settings.NetPlay = false;
639				Settings.NetPlayServer = false;
640			}
641
642			err = RemoveEventHandler(eref);
643			DisposeEventHandlerUPP(eUPP);
644		#ifdef MAC_PANTHER_SUPPORT
645			[pool release];
646
647			pool = [[NSAutoreleasePool alloc] init];
648		#endif
649			eUPP = NewEventHandlerUPP(MainEventHandler);
650			err = InstallApplicationEventHandler(eUPP, GetEventTypeCount(mEvents), mEvents, NULL, &eref);
651		}
652
653		if (!finished)
654		{
655			AdjustMenus();
656			RunApplicationEventLoop();
657		}
658	}
659
660	Deinitialize();
661
662	err = RemoveEventHandler(eref);
663	DisposeEventHandlerUPP(eUPP);
664#ifdef MAC_PANTHER_SUPPORT
665	[pool release];
666#endif
667
668	return (0);
669}
670
671static void CFTimerCallback (CFRunLoopTimerRef timer, void *info)
672{
673	OSStatus	err;
674
675	err = UpdateSystemActivity(OverallAct);
676}
677
678static void * MacSnes9xThread (void *)
679{
680	Settings.StopEmulation = false;
681	s9xthreadrunning = true;
682
683	EmulationLoop();
684
685	s9xthreadrunning = false;
686	Settings.StopEmulation = true;
687
688	return (NULL);
689}
690
691static inline void EmulationLoop (void)
692{
693	bool8	olddisplayframerate = false;
694	int		storedMacFrameSkip  = macFrameSkip;
695
696	pauseEmulation = false;
697	frameAdvance   = false;
698
699	if (macQTRecord)
700	{
701		olddisplayframerate = Settings.DisplayFrameRate;
702		Settings.DisplayFrameRate = false;
703	}
704
705	MacStartSound();
706
707	if (Settings.NetPlay)
708	{
709		if (Settings.NetPlayServer)
710		{
711			NPServerDetachNetPlayThread();
712			NPServerStartClients();
713
714			while (running)
715			{
716				NPServerProcessInput();
717				S9xMainLoop();
718			}
719
720			NPServerStopNetPlayThread();
721			NPServerStopServer();
722		}
723		else
724		{
725			NPClientDetachNetPlayThread();
726			NPClientNetPlayWaitStart();
727
728			while (running)
729			{
730				NPClientProcessInput();
731				S9xMainLoop();
732			}
733
734			NPClientStopNetPlayThread();
735			NPClientDisconnect();
736
737			NPClientRestoreConfig();
738		}
739	}
740	else
741	{
742		while (running)
743		{
744			ProcessInput();
745
746			if (!pauseEmulation)
747				S9xMainLoop();
748			else
749			{
750				if (frameAdvance)
751				{
752					macFrameSkip = 1;
753					skipFrames = 1;
754					frameAdvance = false;
755					S9xMainLoop();
756					macFrameSkip = storedMacFrameSkip;
757				}
758
759				usleep(Settings.FrameTime);
760			}
761		}
762	}
763
764	MacStopSound();
765
766	if (macQTRecord)
767	{
768		MacQTStopRecording();
769		macQTRecord = false;
770
771		Settings.DisplayFrameRate = olddisplayframerate;
772	}
773
774	S9xMovieShutdown();
775}
776
777static pascal OSStatus MainEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
778{
779    OSStatus	err, result = eventNotHandledErr;
780	Boolean		done = false;
781
782	if (frzselecting)
783		return (result);
784
785	switch (GetEventClass(inEvent))
786	{
787		case kEventClassCommand:
788			switch (GetEventKind(inEvent))
789			{
790				HICommand	cmd;
791
792				case kEventCommandUpdateStatus:
793					err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &cmd);
794					if (err == noErr && cmd.commandID == 'clos')
795					{
796						UpdateMenuCommandStatus(false);
797						result = noErr;
798					}
799
800					break;
801
802				case kEventCommandProcess:
803					err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &cmd);
804					if (err == noErr)
805					{
806						UInt32	modifierkey;
807
808						err = GetEventParameter(inEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifierkey);
809						if (err == noErr)
810						{
811							if ((cmd.commandID == 'pref') && (modifierkey & optionKey))
812								cmd.commandID = 'EXTR';
813
814							result = HandleMenuChoice(cmd.commandID, &done);
815
816							if (done)
817								QuitApplicationEventLoop();
818						}
819					}
820
821					break;
822			}
823
824			break;
825	}
826
827	return (result);
828}
829
830static pascal OSStatus SubEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
831{
832	OSStatus	err, result = eventNotHandledErr;
833
834	if (frzselecting)
835		return (result);
836
837	switch (GetEventClass(inEvent))
838	{
839		case kEventClassCommand:
840			switch (GetEventKind(inEvent))
841			{
842				HICommand	cmd;
843
844				case kEventCommandUpdateStatus:
845					err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &cmd);
846					if (err == noErr && cmd.commandID == 'clos')
847					{
848						UpdateMenuCommandStatus(false);
849						result = noErr;
850					}
851
852					break;
853
854				case kEventCommandProcess:
855					err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &cmd);
856					if (err == noErr)
857					{
858						switch (cmd.commandID)
859						{
860							case 'Erun':	// Pause
861							case 'SubQ':	// Queue from emulation thread
862								running = false;
863								while (s9xthreadrunning)
864									sleep(0);
865								QuitApplicationEventLoop();
866								result = noErr;
867								break;
868
869							case 'Ocif':	// Core Image Filter
870								HiliteMenu(0);
871								ConfigureCoreImageFilter();
872								result = noErr;
873								break;
874						}
875					}
876
877					break;
878			}
879
880			break;
881
882		case kEventClassMouse:
883			if (fullscreen)
884			{
885				if ((macControllerOption == SNES_JOYPAD) || (macControllerOption == SNES_MULTIPLAYER5) || (macControllerOption == SNES_MULTIPLAYER5_2))
886				{
887					if (!(Settings.NetPlay && !Settings.NetPlayServer))
888					{
889						switch (GetEventKind(inEvent))
890						{
891							case kEventMouseUp:
892								HIPoint	hipt;
893
894								err = GetEventParameter(inEvent, kEventParamMouseLocation, typeHIPoint, NULL, sizeof(HIPoint), NULL, &hipt);
895								if (err == noErr)
896								{
897									if (CGRectContainsPoint(glScreenBounds, hipt))
898									{
899										running = false;
900										while (s9xthreadrunning)
901											sleep(0);
902										QuitApplicationEventLoop();
903										result = noErr;
904									}
905								}
906
907								break;
908						}
909					}
910				}
911				else
912				if ((macControllerOption == SNES_MOUSE) || (macControllerOption == SNES_MOUSE_SWAPPED))
913				{
914					switch (GetEventKind(inEvent))
915					{
916						case kEventMouseMoved:
917						case kEventMouseDragged:
918							HIPoint	hipt;
919
920							err = GetEventParameter(inEvent, kEventParamMouseDelta, typeHIPoint, NULL, sizeof(HIPoint), NULL, &hipt);
921							if (err == noErr)
922							{
923								unlimitedCursor.x += hipt.x;
924								unlimitedCursor.y += hipt.y;
925							}
926
927							break;
928					}
929				}
930			}
931
932			break;
933	}
934
935	return (result);
936}
937
938void PostQueueToSubEventLoop (void)
939{
940	OSStatus	err;
941	EventRef	event;
942
943	err = CreateEvent(kCFAllocatorDefault, kEventClassCommand, kEventCommandProcess, 0, kEventAttributeUserEvent, &event);
944	if (err == noErr)
945	{
946		HICommand	cmd;
947
948		cmd.commandID          = 'SubQ';
949		cmd.attributes         = kEventAttributeUserEvent;
950		cmd.menu.menuRef       = NULL;
951		cmd.menu.menuItemIndex = 0;
952
953		err = SetEventParameter(event, kEventParamDirectObject, typeHICommand, sizeof(HICommand), &cmd);
954		if (err == noErr)
955			err = PostEventToQueue(GetMainEventQueue(), event, kEventPriorityStandard);
956
957		ReleaseEvent(event);
958	}
959}
960
961void InitGameWindow (void)
962{
963	OSStatus			err;
964	IBNibRef			nibRef;
965	WindowAttributes	attr;
966	CFStringRef			ref;
967	HIViewRef			ctl;
968	HIViewID			cid = { 'Pict', 0 };
969	Rect				rct;
970	char				drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1];
971	EventTypeSpec		wupaneEvents[] = { { kEventClassControl, kEventControlClick            },
972										   { kEventClassControl, kEventControlDraw             } },
973						windowEvents[] = { { kEventClassWindow,  kEventWindowDeactivated       },
974										   { kEventClassWindow,  kEventWindowActivated         },
975										   { kEventClassWindow,  kEventWindowBoundsChanging    },
976										   { kEventClassWindow,  kEventWindowBoundsChanged     },
977										   { kEventClassWindow,  kEventWindowZoom              },
978										   { kEventClassWindow,  kEventWindowToolbarSwitchMode } };
979
980	if (gWindow)
981		return;
982
983	err = CreateNibReference(kMacS9XCFString, &nibRef);
984	if (err)
985		QuitWithFatalError(err, "os 02");
986
987	err = CreateWindowFromNib(nibRef, CFSTR("GameWindow"), &gWindow);
988	if (err)
989		QuitWithFatalError(err, "os 03");
990
991	DisposeNibReference(nibRef);
992
993	HIViewFindByID(HIViewGetRoot(gWindow), cid, &ctl);
994
995	gameWindowUPP = NewEventHandlerUPP(GameWindowEventHandler);
996	err = InstallWindowEventHandler(gWindow, gameWindowUPP, GetEventTypeCount(windowEvents), windowEvents, (void *) gWindow, &gameWindowEventRef);
997
998	gameWUPaneUPP = NewEventHandlerUPP(GameWindowUserPaneEventHandler);
999	err = InstallControlEventHandler(ctl, gameWUPaneUPP, GetEventTypeCount(wupaneEvents), wupaneEvents, (void *) gWindow, &gameWUPaneEventRef);
1000
1001	_splitpath(Memory.ROMFilename, drive, dir, fname, ext);
1002	ref = CFStringCreateWithCString(kCFAllocatorDefault, fname, kCFStringEncodingUTF8);
1003	if (ref)
1004	{
1005		SetWindowTitleWithCFString(gWindow, ref);
1006		CFRelease(ref);
1007	}
1008
1009	attr = kWindowFullZoomAttribute | kWindowResizableAttribute | kWindowLiveResizeAttribute;
1010	err = ChangeWindowAttributes(gWindow, attr, kWindowNoAttributes);
1011
1012	attr = kWindowToolbarButtonAttribute;
1013	if (!drawoverscan)
1014		err = ChangeWindowAttributes(gWindow, attr, kWindowNoAttributes);
1015	else
1016		err = ChangeWindowAttributes(gWindow, kWindowNoAttributes, attr);
1017
1018	if (savewindowpos)
1019	{
1020		MoveWindow(gWindow, windowPos[kWindowScreen].h, windowPos[kWindowScreen].v, false);
1021
1022		if ((windowSize[kWindowScreen].width <= 0) || (windowSize[kWindowScreen].height <= 0))
1023		{
1024			windowExtend = true;
1025			windowSize[kWindowScreen].width  = 512;
1026			windowSize[kWindowScreen].height = kMacWindowHeight;
1027		}
1028
1029		if (!lastoverscan && !windowExtend && drawoverscan)
1030		{
1031			windowExtend = true;
1032			windowSize[kWindowScreen].height = (int) ((float) (windowSize[kWindowScreen].height + 0.5) * SNES_HEIGHT_EXTENDED / SNES_HEIGHT);
1033		}
1034
1035		SizeWindow(gWindow, (short) windowSize[kWindowScreen].width, (short) windowSize[kWindowScreen].height, false);
1036	}
1037	else
1038	{
1039		if (drawoverscan)
1040			windowExtend = true;
1041
1042		SizeWindow(gWindow, 512, (windowExtend ? kMacWindowHeight : (SNES_HEIGHT << 1)), false);
1043		RepositionWindow(gWindow, NULL, kWindowCenterOnMainScreen);
1044	}
1045
1046	windowZoomCount = 0;
1047
1048	GetWindowBounds(gWindow, kWindowContentRgn, &rct);
1049	gWindowRect = CGRectMake((float) rct.left, (float) rct.top, (float) (rct.right - rct.left), (float) (rct.bottom - rct.top));
1050
1051	ActivateWindow(gWindow, true);
1052}
1053
1054void UpdateGameWindow (void)
1055{
1056	OSStatus	err;
1057	HIViewRef	ctl;
1058	HIViewID	cid = { 'Pict', 0 };
1059
1060	if (!gWindow)
1061		return;
1062
1063	HIViewFindByID(HIViewGetRoot(gWindow), cid, &ctl);
1064	err = HIViewSetNeedsDisplay(ctl, true);
1065}
1066
1067static void ResizeGameWindow (void)
1068{
1069	Rect	rct;
1070	int		ww, wh;
1071
1072	if (!gWindow)
1073		return;
1074
1075	GetWindowBounds(gWindow, kWindowContentRgn, &rct);
1076
1077	wh = (windowExtend ? SNES_HEIGHT_EXTENDED : SNES_HEIGHT) * ((windowZoomCount >> 1) + 1);
1078
1079	if (windowZoomCount % 2)
1080		ww = SNES_NTSC_OUT_WIDTH(SNES_WIDTH) * ((windowZoomCount >> 1) + 1) / 2;
1081	else
1082		ww = SNES_WIDTH * ((windowZoomCount >> 1) + 1);
1083
1084	rct.right  = rct.left + ww;
1085	rct.bottom = rct.top  + wh;
1086
1087	SetWindowBounds(gWindow, kWindowContentRgn, &rct);
1088
1089	printf("Window Size: %d, %d\n", ww, wh);
1090
1091	windowZoomCount++;
1092	if (windowZoomCount == 8)
1093		windowZoomCount = 0;
1094}
1095
1096void DeinitGameWindow (void)
1097{
1098	OSStatus	err;
1099
1100	if (!gWindow)
1101		return;
1102
1103	SaveWindowPosition(gWindow, kWindowScreen);
1104	lastoverscan = drawoverscan;
1105
1106	err = RemoveEventHandler(gameWUPaneEventRef);
1107	DisposeEventHandlerUPP(gameWUPaneUPP);
1108
1109	err = RemoveEventHandler(gameWindowEventRef);
1110	DisposeEventHandlerUPP(gameWindowUPP);
1111
1112	CFRelease(gWindow);
1113	gWindow = NULL;
1114}
1115
1116static pascal OSStatus GameWindowEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
1117{
1118	OSStatus	err, result = eventNotHandledErr;
1119	HIRect		rct;
1120	Rect		r;
1121	UInt32		attr;
1122
1123	switch (GetEventClass(inEvent))
1124	{
1125		case kEventClassWindow:
1126			switch (GetEventKind(inEvent))
1127			{
1128				case kEventWindowDeactivated:
1129					if (running)
1130					{
1131						if (!(Settings.NetPlay && !Settings.NetPlayServer))
1132						{
1133							if (inactiveMode == 3)
1134							{
1135								running = false;
1136								while (s9xthreadrunning)
1137									sleep(0);
1138								QuitApplicationEventLoop();
1139								result = noErr;
1140							}
1141							else
1142							if (inactiveMode == 2)
1143							{
1144								rejectinput = true;
1145								result = noErr;
1146							}
1147						}
1148					}
1149
1150					break;
1151
1152				case kEventWindowActivated:
1153					if (running)
1154					{
1155						if (!(Settings.NetPlay && !Settings.NetPlayServer))
1156						{
1157							ForceChangingKeyScript();
1158
1159							if (inactiveMode == 2)
1160							{
1161								rejectinput = false;
1162								result = noErr;
1163							}
1164						}
1165					}
1166
1167					break;
1168
1169				case kEventWindowBoundsChanging:
1170					windowResizeCount = 0x7FFFFFFF;
1171
1172					err = GetEventParameter(inEvent, kEventParamAttributes, typeUInt32, NULL, sizeof(UInt32), NULL, &attr);
1173					if ((err == noErr) && (attr & kWindowBoundsChangeSizeChanged))
1174					{
1175						err = GetEventParameter(inEvent, kEventParamCurrentBounds, typeHIRect, NULL, sizeof(HIRect), NULL, &rct);
1176						if (err == noErr)
1177						{
1178							if (GetCurrentEventKeyModifiers() & shiftKey)
1179							{
1180								HIRect	origRct;
1181
1182								err = GetEventParameter(inEvent, kEventParamOriginalBounds, typeHIRect, NULL, sizeof(HIRect), NULL, &origRct);
1183								if (err == noErr)
1184								{
1185									rct.size.width = (float) (int) (origRct.size.width * rct.size.height / origRct.size.height);
1186									err = SetEventParameter(inEvent, kEventParamCurrentBounds, typeHIRect, sizeof(HIRect), &rct);
1187								}
1188							}
1189
1190							gWindowRect = rct;
1191						}
1192					}
1193
1194					result = noErr;
1195					break;
1196
1197				case kEventWindowBoundsChanged:
1198					windowResizeCount = 3;
1199					result = noErr;
1200					break;
1201
1202				case kEventWindowZoom:
1203					ResizeGameWindow();
1204					result = noErr;
1205					break;
1206
1207				case kEventWindowToolbarSwitchMode:
1208					windowExtend = !windowExtend;
1209
1210					GetWindowBounds(gWindow, kWindowContentRgn, &r);
1211
1212					if (windowExtend)
1213						r.bottom = r.top + (int) (((float) (r.bottom - r.top) + 0.5) * SNES_HEIGHT_EXTENDED / SNES_HEIGHT);
1214					else
1215						r.bottom = r.top + (int) (((float) (r.bottom - r.top) + 0.5) * SNES_HEIGHT / SNES_HEIGHT_EXTENDED);
1216
1217					SetWindowBounds(gWindow, kWindowContentRgn, &r);
1218
1219					result = noErr;
1220					break;
1221			}
1222
1223			break;
1224	}
1225
1226	return (result);
1227}
1228
1229static pascal OSStatus GameWindowUserPaneEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
1230{
1231    OSStatus	err, result = eventNotHandledErr;
1232
1233	switch (GetEventClass(inEvent))
1234	{
1235		case kEventClassControl:
1236			switch (GetEventKind(inEvent))
1237			{
1238				case kEventControlClick:
1239					if (running)
1240					{
1241						if ((macControllerOption == SNES_JOYPAD) || (macControllerOption == SNES_MULTIPLAYER5) || (macControllerOption == SNES_MULTIPLAYER5_2))
1242						{
1243							if (!(Settings.NetPlay && !Settings.NetPlayServer))
1244							{
1245								if (!frzselecting)
1246								{
1247									running = false;
1248									while (s9xthreadrunning)
1249										sleep(0);
1250									QuitApplicationEventLoop();
1251									result = noErr;
1252								}
1253							}
1254						}
1255					}
1256					else
1257					{
1258						UInt32	count;
1259
1260						err = GetEventParameter(inEvent, kEventParamClickCount, typeUInt32, NULL, sizeof(UInt32), NULL, &count);
1261						if ((err == noErr) && (count == 2))
1262						{
1263							SNES9X_Go();
1264							QuitApplicationEventLoop();
1265							result = noErr;
1266						}
1267					}
1268
1269					break;
1270
1271				case kEventControlDraw:
1272					CGContextRef	ctx;
1273					HIViewRef		view;
1274					HIRect			bounds;
1275
1276					err = GetEventParameter(inEvent, kEventParamDirectObject, typeControlRef, NULL, sizeof(ControlRef), NULL, &view);
1277					if (err == noErr)
1278					{
1279						err = GetEventParameter(inEvent, kEventParamCGContextRef, typeCGContextRef, NULL, sizeof(CGContextRef), NULL, &ctx);
1280						if (err == noErr)
1281						{
1282							if (!running)
1283							{
1284								HIViewGetBounds(view, &bounds);
1285								CGContextTranslateCTM(ctx, 0, bounds.size.height);
1286								CGContextScaleCTM(ctx, 1.0f, -1.0f);
1287								DrawPauseScreen(ctx, bounds);
1288							}
1289						}
1290					}
1291
1292					result = noErr;
1293					break;
1294			}
1295
1296			break;
1297	}
1298
1299	return (result);
1300}
1301
1302static void InitRecentItems (void)
1303{
1304	CFStringRef	keyRef, pathRef;
1305	int			count;
1306	char		key[32];
1307
1308	count = 0;
1309
1310	for (int i = 0; i <= kRecentMenu_MAX; i++)
1311		recentItem[i] = NULL;
1312
1313	for (int i = 0; i <  kRecentMenu_MAX; i++)
1314	{
1315		sprintf(key, "RecentItem_%02d", i);
1316		keyRef = CFStringCreateWithCString(kCFAllocatorDefault, key, CFStringGetSystemEncoding());
1317		if (keyRef)
1318		{
1319			pathRef = (CFStringRef) CFPreferencesCopyAppValue(keyRef, kCFPreferencesCurrentApplication);
1320			if (pathRef)
1321			{
1322				recentItem[count] = pathRef;
1323				count++;
1324			}
1325
1326			CFRelease(keyRef);
1327		}
1328	}
1329}
1330
1331static void DeinitRecentItems (void)
1332{
1333	CFStringRef	keyRef;
1334	char		key[32];
1335
1336	for (int i = 0; i < kRecentMenu_MAX; i++)
1337	{
1338		sprintf(key, "RecentItem_%02d", i);
1339		keyRef = CFStringCreateWithCString(kCFAllocatorDefault, key, CFStringGetSystemEncoding());
1340		if (keyRef)
1341		{
1342			if (recentItem[i])
1343			{
1344				CFPreferencesSetAppValue(keyRef, recentItem[i], kCFPreferencesCurrentApplication);
1345				CFRelease(recentItem[i]);
1346				recentItem[i] = NULL;
1347			}
1348			else
1349				CFPreferencesSetAppValue(keyRef, NULL, kCFPreferencesCurrentApplication);
1350
1351			CFRelease(keyRef);
1352		}
1353	}
1354
1355	CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
1356}
1357
1358static void ClearRecentItems (void)
1359{
1360	for (int i = 0; i < kRecentMenu_MAX; i++)
1361	{
1362		if (recentItem[i])
1363		{
1364			CFRelease(recentItem[i]);
1365			recentItem[i] = NULL;
1366		}
1367	}
1368}
1369
1370void AddRecentItem (FSRef *ref)
1371{
1372	OSStatus	err;
1373	char		path[PATH_MAX + 1];
1374
1375	err = FSRefMakePath(ref, (unsigned char *) path, PATH_MAX);
1376	if (err == noErr)
1377	{
1378		CFStringRef	pathRef;
1379
1380		pathRef = CFStringCreateWithCString(kCFAllocatorDefault, path, kCFStringEncodingUTF8);
1381		if (pathRef)
1382		{
1383			int	i, j;
1384
1385			for (i = 0; i < kRecentMenu_MAX; i++)
1386				if (recentItem[i] && (CFStringCompare(pathRef, recentItem[i], 0) == 0))
1387					break;
1388
1389			if (i == kRecentMenu_MAX)
1390			{
1391				for (j = kRecentMenu_MAX - 1; j >= 0; j--)
1392					recentItem[j + 1] = recentItem[j];
1393
1394				if (recentItem[kRecentMenu_MAX])
1395				{
1396					CFRelease(recentItem[kRecentMenu_MAX]);
1397					recentItem[kRecentMenu_MAX] = NULL;
1398				}
1399
1400				recentItem[0] = pathRef;
1401			}
1402			else
1403			{
1404				CFRelease(pathRef);
1405
1406				if (i > 0)
1407				{
1408					CFStringRef	temp;
1409
1410					temp = recentItem[i];
1411
1412					for (j = i - 1; j >= 0; j--)
1413						recentItem[j + 1] = recentItem[j];
1414
1415					recentItem[0] = temp;
1416				}
1417			}
1418		}
1419	}
1420}
1421
1422static void InitRecentMenu (void)
1423{
1424	OSStatus	err;
1425
1426	err = CreateNewMenu(mRecentItem, 0, &recentMenu);
1427	err = SetMenuItemHierarchicalMenu(GetMenuRef(mFile), iOpenRecent, recentMenu);
1428}
1429
1430static void DeinitRecentMenu (void)
1431{
1432	CFRelease(recentMenu);
1433}
1434
1435void BuildRecentMenu (void)
1436{
1437	OSStatus	err;
1438	CFStringRef	str;
1439
1440	err = DeleteMenuItems(recentMenu, 1, CountMenuItems(recentMenu));
1441
1442	for (int i = 0; i < kRecentMenu_MAX; i++)
1443	{
1444		if (!recentItem[i])
1445			break;
1446
1447		Boolean	r;
1448		char	path[PATH_MAX + 1];
1449
1450		r = CFStringGetCString(recentItem[i], path, PATH_MAX, kCFStringEncodingUTF8);
1451		if (r)
1452		{
1453			CFStringRef	nameRef;
1454			char		drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1];
1455
1456			_splitpath(path, drive, dir, fname, ext);
1457			snprintf(path, PATH_MAX + 1, "%s%s", fname, ext);
1458			nameRef = CFStringCreateWithCString(kCFAllocatorDefault, path, kCFStringEncodingUTF8);
1459			if (nameRef)
1460			{
1461				err = AppendMenuItemTextWithCFString(recentMenu, nameRef, 0, 'FRe0' + i, NULL);
1462				CFRelease(nameRef);
1463			}
1464		}
1465	}
1466
1467	err = AppendMenuItemTextWithCFString(recentMenu, NULL, kMenuItemAttrSeparator, 'FR__', NULL);
1468
1469	str = CFCopyLocalizedString(CFSTR("ClearMenu"), "ClearMenu");
1470	if (str)
1471	{
1472		err = AppendMenuItemTextWithCFString(recentMenu, str, 0, 'FRcr', NULL);
1473		CFRelease(str);
1474	}
1475}
1476
1477void AdjustMenus (void)
1478{
1479	OSStatus	err;
1480	MenuRef		menu;
1481	CFStringRef	str;
1482
1483	if (running)
1484	{
1485		menu = GetMenuRef(mApple);
1486		DisableMenuItem(menu, iAbout);
1487		DisableMenuCommand(NULL, kHICommandPreferences);
1488		DisableMenuCommand(NULL, kHICommandQuit);
1489
1490		menu = GetMenuRef(mFile);
1491		DisableMenuItem(menu, iOpen);
1492		DisableMenuItem(menu, iOpenMulti);
1493		DisableMenuItem(menu, iOpenRecent);
1494		DisableMenuItem(menu, iRomInfo);
1495
1496		menu = GetMenuRef(mControl);
1497		DisableMenuItem(menu, iKeyboardLayout);
1498		DisableMenuItem(menu, iISpLayout);
1499		DisableMenuItem(menu, iAutoFire);
1500		DisableMenuItem(menu, iISpPreset);
1501
1502		menu = GetMenuRef(mEmulation);
1503		str = CFCopyLocalizedString(CFSTR("PauseMenu"), "pause");
1504		err = SetMenuItemTextWithCFString(menu, iResume, str);
1505		CFRelease(str);
1506		DisableMenuItem(menu, iSoftReset);
1507		DisableMenuItem(menu, iReset);
1508		DisableMenuItem(menu, iDevice);
1509
1510		if (Settings.NetPlay)
1511		{
1512			if (Settings.NetPlayServer)
1513				EnableMenuItem(menu, iResume);
1514			else
1515				DisableMenuItem(menu, iResume);
1516		}
1517		else
1518			EnableMenuItem(menu, iResume);
1519
1520		menu = GetMenuRef(mCheat);
1521		DisableMenuItem(menu, iApplyCheats);
1522		DisableMenuItem(menu, iGameGenie);
1523		DisableMenuItem(menu, iCheatFinder);
1524
1525		menu = GetMenuRef(mOption);
1526		DisableMenuItem(menu, iFreeze);
1527		DisableMenuItem(menu, iDefrost);
1528		DisableMenuItem(menu, iFreezeTo);
1529		DisableMenuItem(menu, iDefrostFrom);
1530		DisableMenuItem(menu, iRecordMovie);
1531		DisableMenuItem(menu, iPlayMovie);
1532		DisableMenuItem(menu, iQTMovie);
1533		DisableMenuItem(menu, iSaveSPC);
1534		DisableMenuItem(menu, iSaveSRAM);
1535		DisableMenuItem(menu, iMusicBox);
1536		if (ciFilterEnable)
1537			EnableMenuItem(menu, iCIFilter);
1538		else
1539			DisableMenuItem(menu, iCIFilter);
1540
1541		menu = GetMenuRef(mNetplay);
1542		DisableMenuItem(menu, iServer);
1543		DisableMenuItem(menu, iClient);
1544	}
1545	else
1546	{
1547		menu = GetMenuRef(mApple);
1548		EnableMenuItem(menu, iAbout);
1549		EnableMenuCommand(NULL, kHICommandPreferences);
1550		EnableMenuCommand(NULL, kHICommandQuit);
1551
1552		menu = GetMenuRef(mFile);
1553		EnableMenuItem(menu, iOpen);
1554		EnableMenuItem(menu, iOpenMulti);
1555		EnableMenuItem(menu, iOpenRecent);
1556		if (cartOpen)
1557			EnableMenuItem(menu, iRomInfo);
1558		else
1559			DisableMenuItem(menu, iRomInfo);
1560
1561		menu = GetMenuRef(mControl);
1562		EnableMenuItem(menu, iKeyboardLayout);
1563		EnableMenuItem(menu, iAutoFire);
1564		if (hidExist)
1565		{
1566			EnableMenuItem(menu, iISpLayout);
1567			EnableMenuItem(menu, iISpPreset);
1568		}
1569		else
1570		{
1571			DisableMenuItem(menu, iISpLayout);
1572			DisableMenuItem(menu, iISpPreset);
1573		}
1574
1575		menu = GetMenuRef(mEmulation);
1576		str = CFCopyLocalizedString(CFSTR("RunMenu"), "run");
1577		err = SetMenuItemTextWithCFString(menu, iResume, str);
1578		CFRelease(str);
1579		EnableMenuItem(menu, iDevice);
1580		if (cartOpen)
1581		{
1582			EnableMenuItem(menu, iResume);
1583			EnableMenuItem(menu, iSoftReset);
1584			EnableMenuItem(menu, iReset);
1585		}
1586		else
1587		{
1588			DisableMenuItem(menu, iResume);
1589			DisableMenuItem(menu, iSoftReset);
1590			DisableMenuItem(menu, iReset);
1591		}
1592
1593		menu = GetMenuRef(mCheat);
1594		if (cartOpen)
1595		{
1596			EnableMenuItem(menu, iApplyCheats);
1597			EnableMenuItem(menu, iGameGenie);
1598			EnableMenuItem(menu, iCheatFinder);
1599		}
1600		else
1601		{
1602			DisableMenuItem(menu, iApplyCheats);
1603			DisableMenuItem(menu, iGameGenie);
1604			DisableMenuItem(menu, iCheatFinder);
1605		}
1606
1607		menu = GetMenuRef(mOption);
1608		DisableMenuItem(menu, iCIFilter);
1609		if (cartOpen)
1610		{
1611			EnableMenuItem(menu, iFreeze);
1612			EnableMenuItem(menu, iDefrost);
1613			EnableMenuItem(menu, iFreezeTo);
1614			EnableMenuItem(menu, iDefrostFrom);
1615			EnableMenuItem(menu, iRecordMovie);
1616			EnableMenuItem(menu, iPlayMovie);
1617			EnableMenuItem(menu, iQTMovie);
1618			EnableMenuItem(menu, iSaveSPC);
1619			EnableMenuItem(menu, iSaveSRAM);
1620			EnableMenuItem(menu, iMusicBox);
1621		}
1622		else
1623		{
1624			DisableMenuItem(menu, iFreeze);
1625			DisableMenuItem(menu, iDefrost);
1626			DisableMenuItem(menu, iFreezeTo);
1627			DisableMenuItem(menu, iDefrostFrom);
1628			DisableMenuItem(menu, iRecordMovie);
1629			DisableMenuItem(menu, iPlayMovie);
1630			DisableMenuItem(menu, iQTMovie);
1631			DisableMenuItem(menu, iSaveSPC);
1632			DisableMenuItem(menu, iSaveSRAM);
1633			DisableMenuItem(menu, iMusicBox);
1634		}
1635
1636		menu = GetMenuRef(mNetplay);
1637		EnableMenuItem(menu, iClient);
1638		if (cartOpen)
1639			EnableMenuItem(menu, iServer);
1640		else
1641			DisableMenuItem(menu, iServer);
1642	}
1643
1644	DrawMenuBar();
1645}
1646
1647void UpdateMenuCommandStatus (Boolean closeMenu)
1648{
1649	if (closeMenu)
1650		EnableMenuItem(GetMenuRef(mFile), iClose);
1651	else
1652		DisableMenuItem(GetMenuRef(mFile), iClose);
1653}
1654
1655static OSStatus HandleMenuChoice (UInt32 command, Boolean *done)
1656{
1657	OSStatus	err, result = noErr;
1658	MenuRef		mh;
1659	int			item;
1660	bool8		isok = true;
1661
1662	if ((command & 0xFFFFFF00) == 'FRe\0')
1663	{
1664		Boolean	r;
1665		int		index;
1666		char	path[PATH_MAX + 1];
1667
1668		index = (int) (command & 0x000000FF) - (int) '0';
1669		r = CFStringGetCString(recentItem[index], path, PATH_MAX, kCFStringEncodingUTF8);
1670		if (r)
1671		{
1672			FSRef	ref;
1673
1674			err = FSPathMakeRef((unsigned char *) path, &ref, NULL);
1675			if (err == noErr)
1676			{
1677				if (SNES9X_OpenCart(&ref))
1678				{
1679					SNES9X_Go();
1680					*done = true;
1681				}
1682				else
1683					AdjustMenus();
1684			}
1685		}
1686	}
1687	else
1688	{
1689		switch (command)
1690		{
1691			case 'abou':	// About SNES9X
1692				StartCarbonModalDialog();
1693				AboutDialog();
1694				FinishCarbonModalDialog();
1695
1696				break;
1697
1698			case 'pref':	// Preferences...
1699				StartCarbonModalDialog();
1700				ConfigurePreferences();
1701				FinishCarbonModalDialog();
1702
1703				break;
1704
1705			case 'EXTR':	// Extra Options...
1706				StartCarbonModalDialog();
1707				ConfigureExtraOptions();
1708				FinishCarbonModalDialog();
1709
1710				break;
1711
1712			case 'quit':	// Quit SNES9X
1713				SNES9X_Quit();
1714				*done = true;
1715
1716				break;
1717
1718			case 'open':	// Open ROM Image...
1719				if (SNES9X_OpenCart(NULL))
1720				{
1721					SNES9X_Go();
1722					*done = true;
1723				}
1724				else
1725					AdjustMenus();
1726
1727				break;
1728
1729			case 'Mult':	// Open Multiple ROM Images...
1730				if (SNES9X_OpenMultiCart())
1731				{
1732					SNES9X_Go();
1733					*done = true;
1734				}
1735				else
1736					AdjustMenus();
1737
1738				break;
1739
1740			case 'FRcr':	// Clear Menu
1741				ClearRecentItems();
1742				BuildRecentMenu();
1743
1744				break;
1745
1746			case 'Finf':	// ROM Information
1747				StartCarbonModalDialog();
1748				RomInfoDialog();
1749				FinishCarbonModalDialog();
1750
1751				break;
1752
1753			case 'Ckey':	// Configure Keyboard...
1754				StartCarbonModalDialog();
1755				ConfigureKeyboard();
1756				FinishCarbonModalDialog();
1757
1758				break;
1759
1760			case 'Cpad':	// Configure Controllers...
1761				StartCarbonModalDialog();
1762				ConfigureHID();
1763				FinishCarbonModalDialog();
1764
1765				break;
1766
1767			case 'Caut':	// Automatic Fire...
1768				StartCarbonModalDialog();
1769				ConfigureAutofire();
1770				FinishCarbonModalDialog();
1771
1772				break;
1773
1774			case 'Hapl':	// Apply Cheat Entries
1775				mh = GetMenuRef(mCheat);
1776				applycheat = !applycheat;
1777				CheckMenuItem(mh, iApplyCheats, applycheat);
1778				Settings.ApplyCheats = applycheat;
1779
1780				if (!Settings.ApplyCheats)
1781					S9xRemoveCheats();
1782				else
1783					S9xApplyCheats();
1784
1785				break;
1786
1787			case 'Hent':	// Cheat Entry...
1788				StartCarbonModalDialog();
1789				ConfigureCheat();
1790				FinishCarbonModalDialog();
1791
1792				break;
1793
1794			case 'Hfnd':	// Cheat Finder...
1795				StartCarbonModalDialog();
1796				CheatFinder();
1797				FinishCarbonModalDialog();
1798
1799				break;
1800
1801			case 'Erun':	// Run
1802				SNES9X_Go();
1803				*done = true;
1804
1805				break;
1806
1807			case 'Esrs':	// Software Reset
1808				SNES9X_SoftReset();
1809				SNES9X_Go();
1810				*done = true;
1811
1812				break;
1813
1814			case 'Erst':	// Hardware Reset
1815				SNES9X_Reset();
1816				SNES9X_Go();
1817				*done = true;
1818
1819				break;
1820
1821			case 'Ofrz':	// Freeze State
1822				isok = SNES9X_Freeze();
1823				*done = true;
1824
1825				break;
1826
1827			case 'Odfr':	// Defrost state
1828				isok = SNES9X_Defrost();
1829				*done = true;
1830
1831				break;
1832
1833			case 'Ofrd':	// Freeze State to...
1834				StartCarbonModalDialog();
1835				isok = SNES9X_FreezeTo();
1836				FinishCarbonModalDialog();
1837
1838				break;
1839
1840			case 'Odfd':	// Defrost State From...
1841				StartCarbonModalDialog();
1842				isok = SNES9X_DefrostFrom();
1843				if (gWindow)
1844					ActivateWindow(gWindow, true);
1845				FinishCarbonModalDialog();
1846				*done = true;
1847
1848				break;
1849
1850			case 'MVrc':	// Record Movie...
1851				StartCarbonModalDialog();
1852				isok = SNES9X_RecordMovie();
1853				if (gWindow)
1854					ActivateWindow(gWindow, true);
1855				FinishCarbonModalDialog();
1856				*done = true;
1857
1858				break;
1859
1860			case 'MVpl':	// Play Movie...
1861				StartCarbonModalDialog();
1862				isok = SNES9X_PlayMovie();
1863				if (isok && (macPlayFlag & 0x2))
1864				{
1865					running = false;
1866					isok = SNES9X_QTMovieRecord();
1867					running = true;
1868				}
1869
1870				if (gWindow)
1871					ActivateWindow(gWindow, true);
1872				FinishCarbonModalDialog();
1873				*done = true;
1874
1875				break;
1876
1877			case 'QTmv':	// Record QuickTime Movie...
1878				StartCarbonModalDialog();
1879				isok = SNES9X_QTMovieRecord();
1880				if (gWindow)
1881					ActivateWindow(gWindow, true);
1882				FinishCarbonModalDialog();
1883				*done = true;
1884
1885				break;
1886
1887			case 'Ospc':	// Save SPC File at Next Note-on
1888				S9xDumpSPCSnapshot();
1889
1890				break;
1891
1892			case 'Osrm':	// Save SRAM Now
1893				SNES9X_SaveSRAM();
1894
1895				break;
1896
1897			case 'Ombx':	// Music Box
1898				StartCarbonModalDialog();
1899				MusicBoxDialog();
1900				FinishCarbonModalDialog();
1901
1902				break;
1903
1904			case 'Nser':	// Server...
1905				bool8	sr;
1906
1907				Settings.NetPlay = false;
1908				Settings.NetPlayServer = false;
1909
1910				NPServerInit();
1911
1912				if (!NPServerStartServer(NP_PORT))
1913				{
1914					NPServerStopServer();
1915					break;
1916				}
1917
1918				StartCarbonModalDialog();
1919				sr = NPServerDialog();
1920				FinishCarbonModalDialog();
1921
1922				if (sr)
1923				{
1924					SNES9X_Reset();
1925					SNES9X_Go();
1926					Settings.NetPlay = true;
1927					Settings.NetPlayServer = true;
1928
1929					*done = true;
1930				}
1931				else
1932					NPServerStopServer();
1933
1934				break;
1935
1936			case 'Ncli':	// Client...
1937				bool8	cr;
1938
1939				Settings.NetPlay = false;
1940				Settings.NetPlayServer = false;
1941
1942				NPClientInit();
1943
1944				StartCarbonModalDialog();
1945				cr = NPClientDialog();
1946				FinishCarbonModalDialog();
1947
1948				if (cr)
1949				{
1950					SNES9X_Go();
1951					Settings.NetPlay = true;
1952					Settings.NetPlayServer = false;
1953
1954					*done = true;
1955				}
1956				else
1957					AdjustMenus();
1958
1959				break;
1960
1961			case 'CPr1':	// Controller Preset
1962			case 'CPr2':
1963			case 'CPr3':
1964			case 'CPr4':
1965			case 'CPr5':
1966				item = (int) (command & 0x000000FF) - (int) '0';
1967				err = GetMenuItemHierarchicalMenu(GetMenuRef(mControl), iISpPreset, &mh);
1968				CheckMenuItem(mh, padSetting, false);
1969				padSetting = item;
1970				CheckMenuItem(mh, padSetting, true);
1971				ClearPadSetting();
1972				LoadControllerSettings();
1973
1974				break;
1975
1976			case 'EIp1':	// Input Device
1977			case 'EIp2':
1978			case 'EIp3':
1979			case 'EIp4':
1980			case 'EIp5':
1981			case 'EIp6':
1982			case 'EIp7':
1983			case 'EIp8':
1984				item = (int) (command & 0x000000FF) - (int) '0';
1985				err = GetMenuItemHierarchicalMenu(GetMenuRef(mEmulation), iDevice, &mh);
1986				CheckMenuItem(mh, deviceSetting, false);
1987				deviceSetting = item;
1988				deviceSettingMaster = deviceSetting;
1989				CheckMenuItem(mh, deviceSetting, true);
1990				ChangeInputDevice();
1991
1992				break;
1993
1994			default:
1995				result = eventNotHandledErr;
1996				break;
1997		}
1998	}
1999
2000	return (result);
2001}
2002
2003void ChangeInputDevice (void)
2004{
2005	switch (deviceSetting)
2006	{
2007		case iPad:
2008			S9xSetController(0, CTL_JOYPAD,     0, 0, 0, 0);
2009			S9xSetController(1, CTL_JOYPAD,     1, 0, 0, 0);
2010			macControllerOption = SNES_JOYPAD;
2011			break;
2012
2013		case iMouse:
2014			S9xSetController(0, CTL_MOUSE,      0, 0, 0, 0);
2015			S9xSetController(1, CTL_JOYPAD,     1, 0, 0, 0);
2016			macControllerOption = SNES_MOUSE;
2017			break;
2018
2019		case iMouse2:
2020			S9xSetController(0, CTL_JOYPAD,     0, 0, 0, 0);
2021			S9xSetController(1, CTL_MOUSE,      1, 0, 0, 0);
2022			macControllerOption = SNES_MOUSE_SWAPPED;
2023			break;
2024
2025		case iSuperScope:
2026			S9xSetController(0, CTL_JOYPAD,     0, 0, 0, 0);
2027			S9xSetController(1, CTL_SUPERSCOPE, 0, 0, 0, 0);
2028			macControllerOption = SNES_SUPERSCOPE;
2029			break;
2030
2031		case iMultiPlayer5:
2032			S9xSetController(0, CTL_JOYPAD,     0, 0, 0, 0);
2033			S9xSetController(1, CTL_MP5,        1, 2, 3, 4);
2034			macControllerOption = SNES_MULTIPLAYER5;
2035			break;
2036
2037		case iMultiPlayer5_2:
2038			S9xSetController(0, CTL_MP5,        0, 1, 2, 3);
2039			S9xSetController(1, CTL_MP5,        4, 5, 6, 7);
2040			macControllerOption = SNES_MULTIPLAYER5_2;
2041			break;
2042
2043		case iJustifier1:
2044			S9xSetController(0, CTL_JOYPAD,     0, 0, 0, 0);
2045			S9xSetController(1, CTL_JUSTIFIER,  0, 0, 0, 0);
2046			macControllerOption = SNES_JUSTIFIER;
2047			break;
2048
2049		case iJustifier2:
2050			S9xSetController(0, CTL_JOYPAD,     0, 0, 0, 0);
2051			S9xSetController(1, CTL_JUSTIFIER,  1, 0, 0, 0);
2052			macControllerOption = SNES_JUSTIFIER_2;
2053			break;
2054	}
2055}
2056
2057void ApplyNSRTHeaderControllers (void)
2058{
2059	OSStatus	err;
2060	MenuRef		menu;
2061
2062	err = GetMenuItemHierarchicalMenu(GetMenuRef(mEmulation), iDevice, &menu);
2063	if (err)
2064		return;
2065
2066	for (int i = 1; i <= CountMenuItems(menu); i++)
2067	{
2068		CheckMenuItem(menu, i, false);
2069		SetItemStyle(menu, i, normal);
2070	}
2071
2072	deviceSetting = deviceSettingMaster;
2073
2074	uint32	valid = 0;
2075
2076	if (!strncmp((const char *) Memory.NSRTHeader + 24, "NSRT", 4))
2077	{
2078		switch (Memory.NSRTHeader[29])
2079		{
2080			case 0x00: // Everything goes
2081				deviceSetting = iPad;
2082				valid = (1 << iPad);
2083				break;
2084
2085			case 0x10: // Mouse in Port 0
2086				deviceSetting = iMouse;
2087				valid = (1 << iMouse);
2088				break;
2089
2090			case 0x01: // Mouse in Port 1
2091				deviceSetting = iMouse2;
2092				valid = (1 << iMouse2);
2093				break;
2094
2095			case 0x03: // Super Scope in Port 1
2096				deviceSetting = iSuperScope;
2097				valid = (1 << iSuperScope);
2098				break;
2099
2100			case 0x06: // Multitap in Port 1
2101				deviceSetting = iMultiPlayer5;
2102				valid = (1 << iPad) | (1 << iMultiPlayer5);
2103				break;
2104
2105			case 0x66: // Multitap in Ports 0 and 1
2106				deviceSetting = iMultiPlayer5_2;
2107				valid = (1 << iPad) | (1 << iMultiPlayer5) | (1 << iMultiPlayer5_2);
2108				break;
2109
2110			case 0x08: // Multitap in Port 1, Mouse in new Port 1
2111				deviceSetting = iMouse2;
2112				valid = (1 << iPad) | (1 << iMouse2) | (1 << iMultiPlayer5);
2113				break;
2114
2115			case 0x04: // Pad or Super Scope in Port 1
2116				deviceSetting = iSuperScope;
2117				valid = (1 << iPad) | (1 << iSuperScope);
2118				break;
2119
2120			case 0x05: // Justifier - Must ask user...
2121				deviceSetting = iJustifier1;
2122				valid = (1 << iJustifier1) | (1 << iJustifier2);
2123				break;
2124
2125			case 0x20: // Pad or Mouse in Port 0
2126				deviceSetting = iMouse;
2127				valid = (1 << iPad) | (1 << iMouse);
2128				break;
2129
2130			case 0x22: // Pad or Mouse in Port 0 & 1
2131				deviceSetting = iMouse;
2132				valid = (1 << iPad) | (1 << iMouse) | (1 << iMouse2);
2133				break;
2134
2135			case 0x24: // Pad or Mouse in Port 0, Pad or Super Scope in Port 1
2136				deviceSetting = iSuperScope;
2137				valid = (1 << iPad) | (1 << iMouse) | (1 << iSuperScope);
2138				break;
2139
2140			case 0x27: // Pad or Mouse in Port 0, Pad or Mouse or Super Scope in Port 1
2141				deviceSetting = iSuperScope;
2142				valid = (1 << iPad) | (1 << iMouse) | (1 << iMouse2) | (1 << iSuperScope);
2143				break;
2144
2145			case 0x99: // Lasabirdie
2146				break;
2147
2148			case 0x0A: // Barcode Battler
2149				break;
2150
2151			default:
2152				break;
2153		}
2154	}
2155
2156	CheckMenuItem(menu, deviceSetting, true);
2157
2158	for (int i = 1; i <= CountMenuItems(menu); i++)
2159	{
2160		if (valid & (1 << i))
2161			SetItemStyle(menu, i, underline);
2162	}
2163
2164	ChangeInputDevice();
2165}
2166
2167int PromptFreezeDefrost (Boolean freezing)
2168{
2169	OSStatus			err;
2170	CGContextRef		ctx;
2171	CGColorSpaceRef		color;
2172	CGDataProviderRef	prov;
2173	CGImageRef			image;
2174	CGRect				rct;
2175	CGPoint				pt;
2176	CFURLRef			url;
2177	FSCatalogInfo		info;
2178	FSRef				ref;
2179	KeyMap				keys;
2180	UInt64				newestDate, currentDate;
2181	UInt32				startTime;
2182	float				x, y, textw;
2183	int					result, newestIndex, current_selection, oldInactiveMode;
2184	char				dateC[256];
2185	uint8				*back, *draw;
2186
2187	const UInt32		repeatDelay = 10;
2188	const int			w = SNES_WIDTH << 1, h = kMacWindowHeight;
2189	const char			letters[] = "123456789ABC", *filename;
2190	const uint8			keyCheck[] = { kmEscKey, km1Key, km2Key, km3Key, km4Key, km5Key, km6Key, km7Key, km8Key, km9Key, kmAKey, kmBKey, kmCKey };
2191
2192	if (!directDisplay)
2193	{
2194		S9xInitDisplay(NULL, NULL);
2195		SNES9X_Go();
2196	}
2197
2198	frzselecting = true;
2199	oldInactiveMode = inactiveMode;
2200	if (inactiveMode == 3)
2201		inactiveMode = 2;
2202
2203	S9xSetSoundMute(true);
2204
2205	back = (uint8 *) malloc(w * h * 2);
2206	draw = (uint8 *) malloc(w * h * 2);
2207	if (!back || !draw)
2208		QuitWithFatalError(0, "os 04");
2209
2210	color = CGColorSpaceCreateDeviceRGB();
2211	if (!color)
2212		QuitWithFatalError(0, "os 05");
2213
2214	ctx = CGBitmapContextCreate(back, w, h, 5, w * 2, color, kCGImageAlphaNoneSkipFirst | ((systemVersion >= 0x1040) ? kCGBitmapByteOrder16Host : 0));
2215	if (!ctx)
2216		QuitWithFatalError(0, "os 06");
2217
2218	rct = CGRectMake(0.0f, 0.0f, (float) w, (float) h);
2219	CGContextClearRect(ctx, rct);
2220
2221	image = NULL;
2222
2223	if (freezing)
2224		url = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("logo_freeze"),  CFSTR("png"), NULL);
2225	else
2226		url = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("logo_defrost"), CFSTR("png"), NULL);
2227	if (url)
2228	{
2229		prov = CGDataProviderCreateWithURL(url);
2230		if (prov)
2231		{
2232			image = CGImageCreateWithPNGDataProvider(prov, NULL, true, kCGRenderingIntentDefault);
2233			CGDataProviderRelease(prov);
2234		}
2235
2236		CFRelease(url);
2237	}
2238
2239	if (image)
2240	{
2241		rct = CGRectMake(0.0f, (float) h - 118.0f, 512.0f, 118.0f);
2242		CGContextDrawImage(ctx, rct, image);
2243		CGImageRelease(image);
2244	}
2245
2246	newestDate  = 0;
2247	newestIndex = -1;
2248
2249	CGContextSetLineJoin(ctx, kCGLineJoinRound);
2250
2251	rct = CGRectMake(0.0f, (float) h - 238.0f, 128.0f, 120.0f);
2252
2253	for (int count = 0; count < 12; count++)
2254	{
2255		filename = S9xGetFreezeFilename(count);
2256		err = FSPathMakeRef((unsigned char *) filename, &ref, NULL);
2257		if (err == noErr)
2258		{
2259			err = FSGetCatalogInfo(&ref, kFSCatInfoContentMod, &info, NULL, NULL, NULL);
2260			currentDate = (((UInt64) info.contentModDate.highSeconds << 48) | ((UInt64) info.contentModDate.lowSeconds << 16) | (UInt64) info.contentModDate.fraction);
2261
2262			if (currentDate > newestDate)
2263			{
2264				newestIndex = count;
2265				newestDate  = currentDate;
2266			}
2267
2268			DrawThumbnailResource(&ref, ctx, rct);
2269
2270			CGContextSetShouldAntialias(ctx, false);
2271			CGContextSetLineWidth(ctx, 1.0f);
2272
2273			CGContextSetRGBStrokeColor(ctx, 0.0f, 0.0f, 0.0f, 1.0f);
2274			x = rct.origin.x + 127.0f;
2275			y = rct.origin.y + 119.0f;
2276			CGContextBeginPath(ctx);
2277			CGContextMoveToPoint(ctx, x, y);
2278			CGContextAddLineToPoint(ctx, x,          y - 119.0f);
2279			CGContextAddLineToPoint(ctx, x - 127.0f, y - 119.0f);
2280			CGContextStrokePath(ctx);
2281
2282			CGContextSetShouldAntialias(ctx, true);
2283			CGContextSetLineWidth(ctx, 3.0f);
2284
2285			CGContextSelectFont(ctx, "Helvetica", 12.0f, kCGEncodingMacRoman);
2286			x = rct.origin.x +   5.0f;
2287			y = rct.origin.y + 107.0f;
2288			CGContextSetTextDrawingMode(ctx, kCGTextStroke);
2289			CGContextSetRGBStrokeColor(ctx, 0.0f, 0.0f, 0.0f, 0.8f);
2290			CGContextShowTextAtPoint(ctx, x, y, &letters[count], 1);
2291			CGContextSetTextDrawingMode(ctx, kCGTextFill);
2292			CGContextSetRGBFillColor(ctx, 1.0f, 0.7f, 0.7f, 1.0f);
2293			CGContextShowTextAtPoint(ctx, x, y, &letters[count], 1);
2294
2295			if (showtimeinfrz)
2296			{
2297				CFAbsoluteTime		at;
2298				CFDateFormatterRef	format;
2299				CFLocaleRef			locale;
2300				CFStringRef			datstr;
2301				Boolean				r;
2302
2303				err = UCConvertUTCDateTimeToCFAbsoluteTime(&(info.contentModDate), &at);
2304				locale = CFLocaleCopyCurrent();
2305				format = CFDateFormatterCreate(kCFAllocatorDefault, locale, kCFDateFormatterShortStyle, kCFDateFormatterMediumStyle);
2306				datstr = CFDateFormatterCreateStringWithAbsoluteTime(kCFAllocatorDefault, format, at);
2307				r = CFStringGetCString(datstr, dateC, sizeof(dateC), CFStringGetSystemEncoding());
2308				CFRelease(datstr);
2309				CFRelease(format);
2310				CFRelease(locale);
2311
2312				CGContextSelectFont(ctx, "Helvetica", 10.0f, kCGEncodingMacRoman);
2313				x = rct.origin.x +  20.0f;
2314				y = rct.origin.y + 107.0f;
2315				CGContextSetTextDrawingMode(ctx, kCGTextInvisible);
2316				CGContextShowTextAtPoint(ctx, x, y, dateC, strlen(dateC));
2317				pt = CGContextGetTextPosition(ctx);
2318				textw = pt.x - x;
2319				x = rct.origin.x + 122.0f - textw;
2320				CGContextSetTextDrawingMode(ctx, kCGTextStroke);
2321				CGContextSetRGBStrokeColor(ctx, 0.0f, 0.0f, 0.0f, 0.8f);
2322				CGContextShowTextAtPoint(ctx, x, y, dateC, strlen(dateC));
2323				CGContextSetTextDrawingMode(ctx, kCGTextFill);
2324				CGContextSetRGBFillColor(ctx, 1.0f, 1.0f, 1.0f, 1.0f);
2325				CGContextShowTextAtPoint(ctx, x, y, dateC, strlen(dateC));
2326			}
2327		}
2328		else
2329		{
2330			CGContextSelectFont(ctx, "Helvetica", 12.0f, kCGEncodingMacRoman);
2331			x = rct.origin.x +   5.0f;
2332			y = rct.origin.y + 107.0f;
2333			CGContextSetTextDrawingMode(ctx, kCGTextFill);
2334			CGContextSetRGBFillColor(ctx, 0.7f, 0.7f, 0.7f, 1.0f);
2335			CGContextShowTextAtPoint(ctx, x, y, &letters[count], 1);
2336		}
2337
2338		if ((count % 4) == 3)
2339			rct = CGRectOffset(rct, -128.0f * 3.0f, -120.0f);
2340		else
2341			rct = CGRectOffset(rct, 128.0f, 0.0f);
2342	}
2343
2344	if (newestIndex < 0)
2345		newestIndex = 0;
2346
2347	CGContextRelease(ctx);
2348
2349	image = NULL;
2350
2351	prov = CGDataProviderCreateWithData(NULL, back, w * h * 2, NULL);
2352	if (prov)
2353	{
2354		image = CGImageCreate(w, h, 5, 16, w * 2, color, kCGImageAlphaNoneSkipFirst | ((systemVersion >= 0x1040) ? kCGBitmapByteOrder16Host : 0), prov, NULL, 0, kCGRenderingIntentDefault);
2355		CGDataProviderRelease(prov);
2356	}
2357
2358	if (!image)
2359		QuitWithFatalError(0, "os 07");
2360
2361	ctx = CGBitmapContextCreate(draw, w, h, 5, w * 2, color, kCGImageAlphaNoneSkipFirst | ((systemVersion >= 0x1040) ? kCGBitmapByteOrder16Host : 0));
2362	if (!ctx)
2363		QuitWithFatalError(0, "os 08");
2364
2365	CGContextSetShouldAntialias(ctx, false);
2366
2367	UpdateFreezeDefrostScreen(newestIndex, image, draw, ctx);
2368
2369	CocoaPlayFreezeDefrostSound();
2370
2371	result = -2;
2372	current_selection = newestIndex;
2373
2374	do
2375	{
2376		if (!rejectinput)
2377		{
2378			GetKeys(keys);
2379
2380			for (int count = 0; count <= 12; count++)
2381			{
2382				while (KeyIsPressed(keys, keyCheck[count]))
2383				{
2384					result = count - 1;
2385					GetKeys(keys);
2386				}
2387			}
2388
2389			while (KeyIsPressed(keys, keyCode[k1PRight]))
2390			{
2391				startTime = TickCount();
2392				current_selection += 1;
2393				if (current_selection > 11)
2394					current_selection -= 12;
2395				UpdateFreezeDefrostScreen(current_selection, image, draw, ctx);
2396				while (KeyIsPressed(keys, keyCode[k1PRight]) && (TickCount() < (startTime + repeatDelay)))
2397					GetKeys(keys);
2398			}
2399
2400			while (KeyIsPressed(keys, keyCode[k1PLeft]))
2401			{
2402				startTime = TickCount();
2403				current_selection -= 1;
2404				if (current_selection < 0)
2405					current_selection += 12;
2406				UpdateFreezeDefrostScreen(current_selection, image, draw, ctx);
2407				while (KeyIsPressed(keys, keyCode[k1PLeft])  && (TickCount() < (startTime + repeatDelay)))
2408					GetKeys(keys);
2409			}
2410
2411			while (KeyIsPressed(keys, keyCode[k1PDown]))
2412			{
2413				startTime = TickCount();
2414				current_selection += 4;
2415				if (current_selection > 11)
2416					current_selection -= 12;
2417				UpdateFreezeDefrostScreen(current_selection, image, draw, ctx);
2418				while (KeyIsPressed(keys, keyCode[k1PDown])  && (TickCount() < (startTime + repeatDelay)))
2419					GetKeys(keys);
2420			}
2421
2422			while (KeyIsPressed(keys, keyCode[k1PUp]))
2423			{
2424				startTime = TickCount();
2425				current_selection -= 4;
2426				if (current_selection < 0)
2427					current_selection += 12;
2428				UpdateFreezeDefrostScreen(current_selection, image, draw, ctx);
2429				while (KeyIsPressed(keys, keyCode[k1PUp])    && (TickCount() < (startTime + repeatDelay)))
2430					GetKeys(keys);
2431			}
2432
2433			while (KeyIsPressed(keys, keyCode[k1PA]     ) ||
2434				   KeyIsPressed(keys, keyCode[k2PA]     ) ||
2435				   KeyIsPressed(keys, keyCode[k1PB]     ) ||
2436				   KeyIsPressed(keys, keyCode[k2PB]     ) ||
2437				   KeyIsPressed(keys, keyCode[k1PX]     ) ||
2438				   KeyIsPressed(keys, keyCode[k2PX]     ) ||
2439				   KeyIsPressed(keys, keyCode[k1PY]     ) ||
2440				   KeyIsPressed(keys, keyCode[k2PY]     ) ||
2441				   KeyIsPressed(keys, keyCode[k1PStart] ) ||
2442				   KeyIsPressed(keys, keyCode[k2PStart] ) ||
2443				   KeyIsPressed(keys, keyCode[k1PSelect]) ||
2444				   KeyIsPressed(keys, keyCode[k2PSelect]))
2445			{
2446				GetKeys(keys);
2447				result = current_selection;
2448			}
2449
2450			uint32	pad1, pad2;
2451
2452			while (ISpKeyIsPressed(kISpEsc    ) ||
2453				   ISpKeyIsPressed(kISp1PStart) ||
2454				   ISpKeyIsPressed(kISp2PStart))
2455				result = -1;
2456
2457			pad1 = pad2 = 0;
2458			JoypadScanDirection(0, &pad1);
2459			JoypadScanDirection(1, &pad2);
2460			while ((pad1 & 0x0100) || (pad2 & 0x0100))	// Rt
2461			{
2462				startTime = TickCount();
2463				current_selection += 1;
2464				if (current_selection > 11)
2465					current_selection -= 12;
2466				UpdateFreezeDefrostScreen(current_selection, image, draw, ctx);
2467				do
2468				{
2469					pad1 = pad2 = 0;
2470					JoypadScanDirection(0, &pad1);
2471					JoypadScanDirection(1, &pad2);
2472				} while (((pad1 & 0x0100) || (pad2 & 0x0100)) && (TickCount() < (startTime + repeatDelay)));
2473			}
2474
2475			pad1 = pad2 = 0;
2476			JoypadScanDirection(0, &pad1);
2477			JoypadScanDirection(1, &pad2);
2478			while ((pad1 & 0x0200) || (pad2 & 0x0200))	// Lf
2479			{
2480				startTime = TickCount();
2481				current_selection -= 1;
2482				if (current_selection < 0)
2483					current_selection += 12;
2484				UpdateFreezeDefrostScreen(current_selection, image, draw, ctx);
2485				do
2486				{
2487					pad1 = pad2 = 0;
2488					JoypadScanDirection(0, &pad1);
2489					JoypadScanDirection(1, &pad2);
2490				} while (((pad1 & 0x0200) || (pad2 & 0x0200)) && (TickCount() < (startTime + repeatDelay)));
2491			}
2492
2493			pad1 = pad2 = 0;
2494			JoypadScanDirection(0, &pad1);
2495			JoypadScanDirection(1, &pad2);
2496			while ((pad1 & 0x0800) || (pad2 & 0x0800))	// Up
2497			{
2498				startTime = TickCount();
2499				current_selection -= 4;
2500				if (current_selection < 0)
2501					current_selection += 12;
2502				UpdateFreezeDefrostScreen(current_selection, image, draw, ctx);
2503				do
2504				{
2505					pad1 = pad2 = 0;
2506					JoypadScanDirection(0, &pad1);
2507					JoypadScanDirection(1, &pad2);
2508				} while (((pad1 & 0x0800) || (pad2 & 0x0800)) && (TickCount() < (startTime + repeatDelay)));
2509			}
2510
2511			pad1 = pad2 = 0;
2512			JoypadScanDirection(0, &pad1);
2513			JoypadScanDirection(1, &pad2);
2514			while ((pad1 & 0x0400) || (pad2 & 0x0400))	// Dn
2515			{
2516				startTime = TickCount();
2517				current_selection += 4;
2518				if (current_selection > 11)
2519					current_selection -= 12;
2520				UpdateFreezeDefrostScreen(current_selection, image, draw, ctx);
2521				do
2522				{
2523					pad1 = pad2 = 0;
2524					JoypadScanDirection(0, &pad1);
2525					JoypadScanDirection(1, &pad2);
2526				} while (((pad1 & 0x0400) || (pad2 & 0x0400)) && (TickCount() < (startTime + repeatDelay)));
2527			}
2528
2529			while (ISpKeyIsPressed(kISp1PA) ||
2530				   ISpKeyIsPressed(kISp2PA) ||
2531				   ISpKeyIsPressed(kISp1PB) ||
2532				   ISpKeyIsPressed(kISp2PB) ||
2533				   ISpKeyIsPressed(kISp1PX) ||
2534				   ISpKeyIsPressed(kISp2PX) ||
2535				   ISpKeyIsPressed(kISp1PY) ||
2536				   ISpKeyIsPressed(kISp2PY))
2537				result = current_selection;
2538		}
2539
2540		usleep(30000);
2541
2542		windowResizeCount = 2;
2543		UpdateFreezeDefrostScreen(current_selection, image, draw, ctx);
2544	} while (result == -2);
2545
2546	CocoaPlayFreezeDefrostSound();
2547
2548	CGContextRelease(ctx);
2549	CGImageRelease(image);
2550	CGColorSpaceRelease(color);
2551	free(draw);
2552	free(back);
2553
2554	S9xSetSoundMute(false);
2555
2556	inactiveMode = oldInactiveMode;
2557	frzselecting = false;
2558
2559	windowResizeCount = 2;
2560
2561	return (result);
2562}
2563
2564static void UpdateFreezeDefrostScreen (int newIndex, CGImageRef image, uint8 *draw, CGContextRef ctx)
2565{
2566	if (newIndex >= 0 && newIndex < 12)
2567	{
2568		CGRect		rct;
2569		const int	w = SNES_WIDTH << 1, h = kMacWindowHeight;
2570
2571		CGContextSetLineWidth(ctx, 1.0f);
2572
2573		rct = CGRectMake(0.0f, 0.0f, (float) w, (float) h);
2574		CGContextDrawImage(ctx, rct, image);
2575
2576		rct = CGRectMake(0.0f, (float) h - 238.0f, 128.0f, 120.0f);
2577		rct = CGRectOffset(rct, (float) (128 * (newIndex % 4)), (float) (-120 * (newIndex / 4)));
2578		rct.size.width  -= 1.0f;
2579		rct.size.height -= 1.0f;
2580
2581		CGContextSetRGBStrokeColor(ctx, 1.0f, 1.0f, 0.0f, 1.0f);
2582		CGContextStrokeRect(ctx, rct);
2583		rct = CGRectInset(rct, 1.0f, 1.0f);
2584		CGContextSetRGBStrokeColor(ctx, 0.0f, 0.0f, 0.0f, 1.0f);
2585		CGContextStrokeRect(ctx, rct);
2586	}
2587
2588	DrawFreezeDefrostScreen(draw);
2589}
2590
2591static void ProcessInput (void)
2592{
2593	KeyMap			myKeys;
2594	bool8			isok, fnbtn, altbtn, tcbtn;
2595	static bool8	toggleff = false, lastTimeTT = false, lastTimeFn = false, ffUp = false, ffDown = false, ffUpSp = false, ffDownSp = false;
2596
2597	if (rejectinput)
2598		return;
2599
2600	if (ISpKeyIsPressed(kISpEsc))
2601	{
2602		if (s9xthreadrunning)
2603		{
2604			if (!eventQueued)
2605			{
2606				PostQueueToSubEventLoop();
2607				eventQueued = true;
2608			}
2609		}
2610		else
2611			running = false;
2612
2613		return;
2614	}
2615
2616	if (ISpKeyIsPressed(kISpFreeze))
2617	{
2618		MacStopSound();
2619		while (ISpKeyIsPressed(kISpFreeze));
2620
2621		isok = SNES9X_Freeze();
2622		ClearGFXScreen();
2623		return;
2624	}
2625
2626	if (ISpKeyIsPressed(kISpDefrost))
2627	{
2628		MacStopSound();
2629		while (ISpKeyIsPressed(kISpDefrost));
2630
2631		isok = SNES9X_Defrost();
2632		ClearGFXScreen();
2633		return;
2634	}
2635
2636	if (ISpKeyIsPressed(kISpScreenshot))
2637	{
2638		Settings.TakeScreenshot = true;
2639		while (ISpKeyIsPressed(kISpScreenshot));
2640	}
2641
2642	if (ISpKeyIsPressed(kISpSPC))
2643	{
2644		S9xDumpSPCSnapshot();
2645		while (ISpKeyIsPressed(kISpSPC));
2646	}
2647
2648	if (ISpKeyIsPressed(kISpFFUp))
2649	{
2650		if (!ffUpSp)
2651		{
2652			ChangeTurboRate(+1);
2653			ffUpSp = true;
2654		}
2655	}
2656	else
2657		ffUpSp = false;
2658
2659	if (ISpKeyIsPressed(kISpFFDown))
2660	{
2661		if (!ffDownSp)
2662		{
2663			ChangeTurboRate(-1);
2664			ffDownSp = true;
2665		}
2666	}
2667	else
2668		ffDownSp = false;
2669
2670	controlPad[0] = controlPad[1] = 0;
2671
2672	JoypadScanDirection(0, &(controlPad[0]));
2673	if (ISpKeyIsPressed(kISp1PR     ))	controlPad[0] |= 0x0010;
2674	if (ISpKeyIsPressed(kISp1PL     ))	controlPad[0] |= 0x0020;
2675	if (ISpKeyIsPressed(kISp1PX     ))	controlPad[0] |= 0x0040;
2676	if (ISpKeyIsPressed(kISp1PA     ))	controlPad[0] |= 0x0080;
2677	if (ISpKeyIsPressed(kISp1PStart ))	controlPad[0] |= 0x1000;
2678	if (ISpKeyIsPressed(kISp1PSelect))	controlPad[0] |= 0x2000;
2679	if (ISpKeyIsPressed(kISp1PY     ))	controlPad[0] |= 0x4000;
2680	if (ISpKeyIsPressed(kISp1PB     ))	controlPad[0] |= 0x8000;
2681
2682	JoypadScanDirection(1, &(controlPad[1]));
2683	if (ISpKeyIsPressed(kISp2PR     ))	controlPad[1] |= 0x0010;
2684	if (ISpKeyIsPressed(kISp2PL     ))	controlPad[1] |= 0x0020;
2685	if (ISpKeyIsPressed(kISp2PX     ))	controlPad[1] |= 0x0040;
2686	if (ISpKeyIsPressed(kISp2PA     ))	controlPad[1] |= 0x0080;
2687	if (ISpKeyIsPressed(kISp2PStart ))	controlPad[1] |= 0x1000;
2688	if (ISpKeyIsPressed(kISp2PSelect))	controlPad[1] |= 0x2000;
2689	if (ISpKeyIsPressed(kISp2PY     ))	controlPad[1] |= 0x4000;
2690	if (ISpKeyIsPressed(kISp2PB     ))	controlPad[1] |= 0x8000;
2691
2692	if (((macControllerOption == SNES_MULTIPLAYER5) || (macControllerOption == SNES_MULTIPLAYER5_2)) && Settings.MultiPlayer5Master)
2693	{
2694		controlPad[2] = controlPad[3] = controlPad[4] = 0;
2695
2696		JoypadScanDirection(2, &(controlPad[2]));
2697		if (ISpKeyIsPressed(kISp3PR     ))	controlPad[2] |= 0x0010;
2698		if (ISpKeyIsPressed(kISp3PL     ))	controlPad[2] |= 0x0020;
2699		if (ISpKeyIsPressed(kISp3PX     ))	controlPad[2] |= 0x0040;
2700		if (ISpKeyIsPressed(kISp3PA     ))	controlPad[2] |= 0x0080;
2701		if (ISpKeyIsPressed(kISp3PStart ))	controlPad[2] |= 0x1000;
2702		if (ISpKeyIsPressed(kISp3PSelect))	controlPad[2] |= 0x2000;
2703		if (ISpKeyIsPressed(kISp3PY     ))	controlPad[2] |= 0x4000;
2704		if (ISpKeyIsPressed(kISp3PB     ))	controlPad[2] |= 0x8000;
2705
2706		JoypadScanDirection(3, &(controlPad[3]));
2707		if (ISpKeyIsPressed(kISp4PR     ))	controlPad[3] |= 0x0010;
2708		if (ISpKeyIsPressed(kISp4PL     ))	controlPad[3] |= 0x0020;
2709		if (ISpKeyIsPressed(kISp4PX     ))	controlPad[3] |= 0x0040;
2710		if (ISpKeyIsPressed(kISp4PA     ))	controlPad[3] |= 0x0080;
2711		if (ISpKeyIsPressed(kISp4PStart ))	controlPad[3] |= 0x1000;
2712		if (ISpKeyIsPressed(kISp4PSelect))	controlPad[3] |= 0x2000;
2713		if (ISpKeyIsPressed(kISp4PY     ))	controlPad[3] |= 0x4000;
2714		if (ISpKeyIsPressed(kISp4PB     ))	controlPad[3] |= 0x8000;
2715
2716		JoypadScanDirection(4, &(controlPad[4]));
2717		if (ISpKeyIsPressed(kISp5PR     ))	controlPad[4] |= 0x0010;
2718		if (ISpKeyIsPressed(kISp5PL     ))	controlPad[4] |= 0x0020;
2719		if (ISpKeyIsPressed(kISp5PX     ))	controlPad[4] |= 0x0040;
2720		if (ISpKeyIsPressed(kISp5PA     ))	controlPad[4] |= 0x0080;
2721		if (ISpKeyIsPressed(kISp5PStart ))	controlPad[4] |= 0x1000;
2722		if (ISpKeyIsPressed(kISp5PSelect))	controlPad[4] |= 0x2000;
2723		if (ISpKeyIsPressed(kISp5PY     ))	controlPad[4] |= 0x4000;
2724		if (ISpKeyIsPressed(kISp5PB     ))	controlPad[4] |= 0x8000;
2725
2726		ControlPadFlagsToS9xReportButtons(2, controlPad[2]);
2727		ControlPadFlagsToS9xReportButtons(3, controlPad[3]);
2728		ControlPadFlagsToS9xReportButtons(4, controlPad[4]);
2729
2730		if (macControllerOption == SNES_MULTIPLAYER5_2)
2731		{
2732			controlPad[5] = controlPad[6] = controlPad[7] = 0;
2733
2734			JoypadScanDirection(5, &(controlPad[5]));
2735			if (ISpKeyIsPressed(kISp6PR     ))	controlPad[5] |= 0x0010;
2736			if (ISpKeyIsPressed(kISp6PL     ))	controlPad[5] |= 0x0020;
2737			if (ISpKeyIsPressed(kISp6PX     ))	controlPad[5] |= 0x0040;
2738			if (ISpKeyIsPressed(kISp6PA     ))	controlPad[5] |= 0x0080;
2739			if (ISpKeyIsPressed(kISp6PStart ))	controlPad[5] |= 0x1000;
2740			if (ISpKeyIsPressed(kISp6PSelect))	controlPad[5] |= 0x2000;
2741			if (ISpKeyIsPressed(kISp6PY     ))	controlPad[5] |= 0x4000;
2742			if (ISpKeyIsPressed(kISp6PB     ))	controlPad[5] |= 0x8000;
2743
2744			JoypadScanDirection(6, &(controlPad[6]));
2745			if (ISpKeyIsPressed(kISp7PR     ))	controlPad[6] |= 0x0010;
2746			if (ISpKeyIsPressed(kISp7PL     ))	controlPad[6] |= 0x0020;
2747			if (ISpKeyIsPressed(kISp7PX     ))	controlPad[6] |= 0x0040;
2748			if (ISpKeyIsPressed(kISp7PA     ))	controlPad[6] |= 0x0080;
2749			if (ISpKeyIsPressed(kISp7PStart ))	controlPad[6] |= 0x1000;
2750			if (ISpKeyIsPressed(kISp7PSelect))	controlPad[6] |= 0x2000;
2751			if (ISpKeyIsPressed(kISp7PY     ))	controlPad[6] |= 0x4000;
2752			if (ISpKeyIsPressed(kISp7PB     ))	controlPad[6] |= 0x8000;
2753
2754			JoypadScanDirection(7, &(controlPad[7]));
2755			if (ISpKeyIsPressed(kISp8PR     ))	controlPad[7] |= 0x0010;
2756			if (ISpKeyIsPressed(kISp8PL     ))	controlPad[7] |= 0x0020;
2757			if (ISpKeyIsPressed(kISp8PX     ))	controlPad[7] |= 0x0040;
2758			if (ISpKeyIsPressed(kISp8PA     ))	controlPad[7] |= 0x0080;
2759			if (ISpKeyIsPressed(kISp8PStart ))	controlPad[7] |= 0x1000;
2760			if (ISpKeyIsPressed(kISp8PSelect))	controlPad[7] |= 0x2000;
2761			if (ISpKeyIsPressed(kISp8PY     ))	controlPad[7] |= 0x4000;
2762			if (ISpKeyIsPressed(kISp8PB     ))	controlPad[7] |= 0x8000;
2763
2764			ControlPadFlagsToS9xReportButtons(5, controlPad[5]);
2765			ControlPadFlagsToS9xReportButtons(6, controlPad[6]);
2766			ControlPadFlagsToS9xReportButtons(7, controlPad[7]);
2767		}
2768	}
2769
2770	GetKeys(myKeys);
2771
2772	fnbtn  = (KeyIsPressed(myKeys, keyCode[kKeyFunction]) || ISpKeyIsPressed(kISpFunction));
2773	altbtn = (KeyIsPressed(myKeys, keyCode[kKeyAlt]     ) || ISpKeyIsPressed(kISpAlt)     );
2774
2775	if (fnbtn)
2776	{
2777		if (!lastTimeFn)
2778		{
2779			for (unsigned int i = 0; i < kCommandListSize; i++)
2780				btncmd[i].held = false;
2781		}
2782
2783		lastTimeFn = true;
2784		lastTimeTT = false;
2785		ffUp = ffDown = false;
2786
2787		for (unsigned int i = 0; i < kCommandListSize; i++)
2788		{
2789			if (KeyIsPressed(myKeys, btncmd[i].keycode))
2790			{
2791				if (!(btncmd[i].held))
2792				{
2793					btncmd[i].held = true;
2794
2795					if (strncmp(btncmd[i].command, "_mac", 4) == 0)
2796					{
2797						static char	msg[64];
2798
2799						switch (btncmd[i].command[4] - '0')
2800						{
2801							case 1:
2802								Settings.DisplayPressedKeys = !Settings.DisplayPressedKeys;
2803								break;
2804
2805							case 2:
2806								if (S9xMovieActive())
2807									Settings.DisplayMovieFrame = !Settings.DisplayMovieFrame;
2808								break;
2809
2810							case 3:
2811								if (macFrameAdvanceRate < 5000000)
2812									macFrameAdvanceRate += 100000;
2813								sprintf(msg, "Emulation Speed: 100/%d", macFrameAdvanceRate / 10000);
2814								S9xSetInfoString(msg);
2815								break;
2816
2817							case 4:
2818								if (macFrameAdvanceRate > 500000)
2819									macFrameAdvanceRate -= 100000;
2820								sprintf(msg, "Emulation Speed: 100/%d", macFrameAdvanceRate / 10000);
2821								S9xSetInfoString(msg);
2822								break;
2823
2824							case 5:
2825								pauseEmulation = !pauseEmulation;
2826								break;
2827
2828							case 6:
2829								frameAdvance = true;
2830								break;
2831						}
2832					}
2833					else
2834					{
2835						s9xcommand_t	s9xcmd;
2836
2837						s9xcmd = S9xGetCommandT(btncmd[i].command);
2838						S9xApplyCommand(s9xcmd, 1, 0);
2839					}
2840				}
2841			}
2842			else
2843				btncmd[i].held = false;
2844		}
2845	}
2846	else
2847	{
2848		lastTimeFn = false;
2849
2850		if (KeyIsPressed(myKeys, keyCode[kKeyEsc]))
2851		{
2852			if (s9xthreadrunning)
2853			{
2854				if (!eventQueued)
2855				{
2856					PostQueueToSubEventLoop();
2857					eventQueued = true;
2858				}
2859			}
2860			else
2861				running = false;
2862
2863			return;
2864		}
2865
2866		if (KeyIsPressed(myKeys, keyCode[kKeyFreeze]))
2867		{
2868			MacStopSound();
2869			while (KeyIsPressed(myKeys, keyCode[kKeyFreeze]))
2870				GetKeys(myKeys);
2871
2872			isok = SNES9X_Freeze();
2873			ClearGFXScreen();
2874			return;
2875		}
2876
2877		if (KeyIsPressed(myKeys, keyCode[kKeyDefrost]))
2878		{
2879			MacStopSound();
2880			while (KeyIsPressed(myKeys, keyCode[kKeyDefrost]))
2881				GetKeys(myKeys);
2882
2883			isok = SNES9X_Defrost();
2884			ClearGFXScreen();
2885			return;
2886		}
2887
2888		if (KeyIsPressed(myKeys, keyCode[kKeyScreenshot]))
2889		{
2890			Settings.TakeScreenshot = true;
2891			while (KeyIsPressed(myKeys, keyCode[kKeyScreenshot]))
2892				GetKeys(myKeys);
2893		}
2894
2895		if (KeyIsPressed(myKeys, keyCode[kKeySPC]))
2896		{
2897			S9xDumpSPCSnapshot();
2898			while (KeyIsPressed(myKeys, keyCode[kKeySPC]))
2899				GetKeys(myKeys);
2900		}
2901
2902		if (KeyIsPressed(myKeys, keyCode[kKeyFFUp]))
2903		{
2904			if (!ffUp)
2905			{
2906				ChangeTurboRate(+1);
2907				ffUp = true;
2908			}
2909		}
2910		else
2911			ffUp = false;
2912
2913		if (KeyIsPressed(myKeys, keyCode[kKeyFFDown]))
2914		{
2915			if (!ffDown)
2916			{
2917				ChangeTurboRate(-1);
2918				ffDown = true;
2919			}
2920		}
2921		else
2922			ffDown = false;
2923
2924		if (KeyIsPressed(myKeys, keyCode[k1PR]     ))	controlPad[0] |= 0x0010;
2925		if (KeyIsPressed(myKeys, keyCode[k1PL]     ))	controlPad[0] |= 0x0020;
2926		if (KeyIsPressed(myKeys, keyCode[k1PX]     ))	controlPad[0] |= 0x0040;
2927		if (KeyIsPressed(myKeys, keyCode[k1PA]     ))	controlPad[0] |= 0x0080;
2928		if (KeyIsPressed(myKeys, keyCode[k1PRight] ))	controlPad[0] |= 0x0100;
2929		if (KeyIsPressed(myKeys, keyCode[k1PLeft]  ))	controlPad[0] |= 0x0200;
2930		if (KeyIsPressed(myKeys, keyCode[k1PDown]  ))	controlPad[0] |= 0x0400;
2931		if (KeyIsPressed(myKeys, keyCode[k1PUp]    ))	controlPad[0] |= 0x0800;
2932		if (KeyIsPressed(myKeys, keyCode[k1PStart] ))	controlPad[0] |= 0x1000;
2933		if (KeyIsPressed(myKeys, keyCode[k1PSelect]))	controlPad[0] |= 0x2000;
2934		if (KeyIsPressed(myKeys, keyCode[k1PY]     ))	controlPad[0] |= 0x4000;
2935		if (KeyIsPressed(myKeys, keyCode[k1PB]     ))	controlPad[0] |= 0x8000;
2936
2937		if (KeyIsPressed(myKeys, keyCode[k2PR]     ))	controlPad[1] |= 0x0010;
2938		if (KeyIsPressed(myKeys, keyCode[k2PL]     ))	controlPad[1] |= 0x0020;
2939		if (KeyIsPressed(myKeys, keyCode[k2PX]     ))	controlPad[1] |= 0x0040;
2940		if (KeyIsPressed(myKeys, keyCode[k2PA]     ))	controlPad[1] |= 0x0080;
2941		if (KeyIsPressed(myKeys, keyCode[k2PRight] ))	controlPad[1] |= 0x0100;
2942		if (KeyIsPressed(myKeys, keyCode[k2PLeft]  ))	controlPad[1] |= 0x0200;
2943		if (KeyIsPressed(myKeys, keyCode[k2PDown]  ))	controlPad[1] |= 0x0400;
2944		if (KeyIsPressed(myKeys, keyCode[k2PUp]    ))	controlPad[1] |= 0x0800;
2945		if (KeyIsPressed(myKeys, keyCode[k2PStart] ))	controlPad[1] |= 0x1000;
2946		if (KeyIsPressed(myKeys, keyCode[k2PSelect]))	controlPad[1] |= 0x2000;
2947		if (KeyIsPressed(myKeys, keyCode[k2PY]     ))	controlPad[1] |= 0x4000;
2948		if (KeyIsPressed(myKeys, keyCode[k2PB]     ))	controlPad[1] |= 0x8000;
2949
2950		if (altbtn)
2951		{
2952			if (!lastTimeTT)
2953				changeAuto[0] = changeAuto[1] = 0;
2954
2955			for (int i = 0; i < 2; i++)
2956			{
2957				for (int j = 0; j < 12; j++)
2958				{
2959					uint16	mask = 0x0010 << j;
2960
2961					if (controlPad[i] & mask & autofireRec[i].toggleMask)
2962					{
2963						controlPad[i] &= ~mask;
2964
2965						if (!(changeAuto[i] & mask))
2966						{
2967							changeAuto[i] |= mask;
2968							ChangeAutofireSettings(i, j);
2969						}
2970					}
2971					else
2972						changeAuto[i] &= ~mask;
2973				}
2974			}
2975
2976			lastTimeTT = true;
2977		}
2978		else
2979			lastTimeTT = false;
2980	}
2981
2982	if (enabletoggle)
2983	{
2984		if ((ISpKeyIsPressed(kISpFastForward) || KeyIsPressed(myKeys, keyCode[kKeyFastForward])) && !fnbtn)
2985		{
2986			if (!toggleff)
2987			{
2988				toggleff = true;
2989				Settings.TurboMode = !Settings.TurboMode;
2990				S9xSetInfoString(Settings.TurboMode ? "Turbo mode on" : "Turbo mode off");
2991				if (!Settings.TurboMode)
2992					S9xClearSamples();
2993			}
2994		}
2995		else
2996			toggleff = false;
2997	}
2998	else
2999	{
3000		bool8	old = Settings.TurboMode;
3001		Settings.TurboMode = ((ISpKeyIsPressed(kISpFastForward) || KeyIsPressed(myKeys, keyCode[kKeyFastForward])) && !fnbtn) ? true : false;
3002		if (!Settings.TurboMode && old)
3003			S9xClearSamples();
3004	}
3005
3006	for (int i = 0; i < 2; i++)
3007		controlPad[i] ^= autofireRec[i].invertMask;
3008
3009	if (autofire)
3010	{
3011		long long	currentTime;
3012		uint16		changeMask;
3013
3014		Microseconds((UnsignedWide *) &currentTime);
3015		tcbtn = (KeyIsPressed(myKeys, keyCode[kKeyTC]) || ISpKeyIsPressed(kISpTC));
3016
3017		for (int i = 0; i < 2; i++)
3018		{
3019			changeMask = (lastTimeTT ? (~changeAuto[i]) : 0xFFFF);
3020
3021			for (int j = 0; j < 12; j++)
3022			{
3023				uint16	mask = (0x0010 << j) & changeMask;
3024
3025				if (autofireRec[i].tcMask & mask)
3026				{
3027					if (!tcbtn)
3028						continue;
3029				}
3030
3031				if (autofireRec[i].buttonMask & mask)
3032				{
3033					if (controlPad[i] & mask)
3034					{
3035						if (currentTime > autofireRec[i].nextTime[j])
3036						{
3037							if (Settings.TurboMode)
3038								autofireRec[i].nextTime[j] = currentTime + (long long) ((1.0 / (float) autofireRec[i].frequency) * 1000000.0 / macFastForwardRate);
3039							else
3040								autofireRec[i].nextTime[j] = currentTime + (long long) ((1.0 / (float) autofireRec[i].frequency) * 1000000.0);
3041						}
3042						else
3043							controlPad[i] &= ~mask;
3044					}
3045				}
3046			}
3047		}
3048	}
3049
3050	ControlPadFlagsToS9xReportButtons(0, controlPad[0]);
3051	ControlPadFlagsToS9xReportButtons(1, controlPad[1]);
3052
3053	if (macControllerOption == SNES_JUSTIFIER_2)
3054		ControlPadFlagsToS9xPseudoPointer(controlPad[1]);
3055}
3056
3057static void ChangeAutofireSettings (int player, int btn)
3058{
3059	static char	msg[64];
3060	uint16		mask, m;
3061
3062	mask = 0x0010 << btn;
3063	autofireRec[player].buttonMask ^= mask;
3064	autofire = (autofireRec[0].buttonMask || autofireRec[1].buttonMask);
3065
3066	m = autofireRec[player].buttonMask;
3067	if (m)
3068		snprintf(msg, sizeof(msg), "Autofire %d:%s%s%s%s%s%s%s%s%s%s%s%s%s", player + 1,
3069			(m & 0xC0F0 ?   " " : ""),
3070			(m & 0x0080 ?   "A" : ""),
3071			(m & 0x8000 ?   "B" : ""),
3072			(m & 0x0040 ?   "X" : ""),
3073			(m & 0x4000 ?   "Y" : ""),
3074			(m & 0x0020 ?   "L" : ""),
3075			(m & 0x0010 ?   "R" : ""),
3076			(m & 0x0800 ? " Up" : ""),
3077			(m & 0x0400 ? " Dn" : ""),
3078			(m & 0x0200 ? " Lf" : ""),
3079			(m & 0x0100 ? " Rt" : ""),
3080			(m & 0x1000 ? " St" : ""),
3081			(m & 0x2000 ? " Se" : ""));
3082	else
3083		snprintf(msg, sizeof(msg), "Autofire %d: Off", player + 1);
3084
3085	S9xSetInfoString(msg);
3086}
3087
3088static void ChangeTurboRate (int d)
3089{
3090	static char	msg[64];
3091
3092	macFastForwardRate += d;
3093	if (macFastForwardRate < 1)
3094		macFastForwardRate = 1;
3095	else
3096	if (macFastForwardRate > 15)
3097		macFastForwardRate = 15;
3098
3099	snprintf(msg, sizeof(msg), "Turbo Rate: %d", macFastForwardRate);
3100	S9xSetInfoString(msg);
3101}
3102
3103void GetGameScreenPointer (int16 *x, int16 *y, bool fullmouse)
3104{
3105	int	ph;
3106
3107	ph = !drawoverscan ? ((IPPU.RenderedScreenHeight > 256) ? IPPU.RenderedScreenHeight : (IPPU.RenderedScreenHeight << 1)) : (SNES_HEIGHT_EXTENDED << 1);
3108
3109	if (fullscreen)
3110	{
3111		if (glstretch)
3112		{
3113			float   fpw = (float) glScreenH / (float) ph * 512.0f;
3114
3115			scopeViewInfo.width      = (int) (fpw + ((float) glScreenW - fpw) * (float) macAspectRatio / 10000.0);
3116			scopeViewInfo.height     = glScreenH;
3117			scopeViewInfo.globalLeft = (int) glScreenBounds.origin.x + ((glScreenW - scopeViewInfo.width) >> 1);
3118			scopeViewInfo.globalTop  = (int) glScreenBounds.origin.y;
3119		}
3120		else
3121		{
3122			scopeViewInfo.width      = 512;
3123			scopeViewInfo.height     = ph;
3124			scopeViewInfo.globalLeft = (int) glScreenBounds.origin.x + ((glScreenW - 512) >> 1);
3125			scopeViewInfo.globalTop  = (int) glScreenBounds.origin.y + ((glScreenH - ph ) >> 1);
3126		}
3127	}
3128	else
3129	{
3130		Rect	rct;
3131
3132		GetWindowBounds(gWindow, kWindowContentRgn, &rct);
3133
3134		int	ww = rct.right  - rct.left,
3135			wh = rct.bottom - rct.top;
3136
3137		scopeViewInfo.width      = ww;
3138		scopeViewInfo.globalLeft = rct.left;
3139
3140		if (windowExtend)
3141		{
3142			scopeViewInfo.height    = ph * wh / kMacWindowHeight;
3143			scopeViewInfo.globalTop = rct.top + ((kMacWindowHeight - ph) >> 1) * wh / kMacWindowHeight;
3144		}
3145		else
3146		{
3147			scopeViewInfo.height    = wh;
3148			scopeViewInfo.globalTop = rct.top;
3149		}
3150	}
3151
3152	if (!fullmouse)
3153	{
3154		Point	pos;
3155
3156		GetGlobalMouse(&pos);
3157
3158		*x = (int16) (((float) (pos.h - scopeViewInfo.globalLeft)) / ((float) scopeViewInfo.width ) * (float) IPPU.RenderedScreenWidth);
3159		*y = (int16) (((float) (pos.v - scopeViewInfo.globalTop )) / ((float) scopeViewInfo.height) * (float) (!drawoverscan ? IPPU.RenderedScreenHeight : SNES_HEIGHT_EXTENDED));
3160	}
3161	else
3162	{
3163		*x = (int16) (unlimitedCursor.x / (float) scopeViewInfo.width  * (float) IPPU.RenderedScreenWidth);
3164		*y = (int16) (unlimitedCursor.y / (float) scopeViewInfo.height * (float) (!drawoverscan ? IPPU.RenderedScreenHeight : SNES_HEIGHT_EXTENDED));
3165	}
3166}
3167
3168static void Initialize (void)
3169{
3170	OSStatus	err;
3171	IBNibRef	menuNibRef;
3172	MenuRef		menu;
3173	SInt32		qtVersion;
3174
3175	printf("\nSnes9x for Mac OS X %s (%s), ", VERSION, MAC_VERSION);
3176#ifdef __BIG_ENDIAN__
3177	printf("PowerPC\n\n");
3178#else
3179	printf("Intel\n\n");
3180#endif
3181
3182	err = Gestalt(gestaltSystemVersion, &systemVersion);
3183	err = Gestalt(gestaltQuickTimeVersion, &qtVersion);
3184
3185	if ((systemVersion < 0x1039) || (qtVersion < 0x07008000))
3186	{
3187		AppearanceAlert(kAlertStopAlert, kS9xMacAlertRequiredSystem, kS9xMacAlertRequiredSystemHint);
3188		QuitWithFatalError(0, "os 09");
3189	}
3190
3191	printf("OS: %x  QuickTime: %x\n\n", (unsigned) systemVersion, (unsigned) qtVersion);
3192
3193#ifdef ZLIB
3194	printf("zlib header version: %s\n\n", ZLIB_VERSION);
3195#endif
3196
3197	NSApplicationLoad();
3198
3199	ZeroMemory(&Settings, sizeof(Settings));
3200	Settings.MouseMaster = true;
3201	Settings.SuperScopeMaster = true;
3202	Settings.JustifierMaster = true;
3203	Settings.MultiPlayer5Master = true;
3204	Settings.FrameTimePAL = 20000;
3205	Settings.FrameTimeNTSC = 16667;
3206	Settings.SixteenBitSound = true;
3207	Settings.Stereo = true;
3208	Settings.SoundPlaybackRate = 32000;
3209	Settings.SoundInputRate = 32000;
3210	Settings.SupportHiRes = true;
3211	Settings.Transparency = true;
3212	Settings.AutoDisplayMessages = true;
3213	Settings.InitialInfoStringTimeout = 120;
3214	Settings.HDMATimingHack = 100;
3215	Settings.BlockInvalidVRAMAccessMaster = true;
3216	Settings.StopEmulation = true;
3217	Settings.WrongMovieStateProtection = true;
3218	Settings.DumpStreamsMaxFrames = -1;
3219	Settings.StretchScreenshots = 1;
3220	Settings.SnapshotScreenshots = true;
3221	Settings.OpenGLEnable = true;
3222
3223	for (int a = 0; a < kWindowCount; a++)
3224	{
3225		windowPos[a].h = 40;
3226		windowPos[a].v = 80;
3227		windowSize[a].width  = -1.0f;
3228		windowSize[a].height = -1.0f;
3229	}
3230
3231	extraOptions.benchmark = false;
3232	extraOptions.glForceNoTextureRectangle = false;
3233	extraOptions.glUseClientStrageApple = true;
3234	extraOptions.glUseTexturePriority = false;
3235	extraOptions.glStorageHint = 2;
3236
3237	npServerIP[0] = 0;
3238	npName[0] = 0;
3239
3240	saveFolderPath = NULL;
3241
3242	CreateIconImages();
3243
3244	InitAppleEvents();
3245	InitKeyboard();
3246	InitAutofire();
3247	InitCheatFinder();
3248
3249	LoadPrefs();
3250
3251	InitGraphics();
3252	InitMacSound();
3253	SetUpHID();
3254
3255	RegisterHelpBook();
3256
3257	if (systemVersion < 0x1040)
3258		ciFilterEnable = false;
3259
3260	err = CreateNibReference(kMacS9XCFString, &menuNibRef);
3261	err = SetMenuBarFromNib(menuNibRef, CFSTR("MenuBar"));
3262	DisposeNibReference(menuNibRef);
3263
3264	EnableMenuCommand(NULL, kHICommandPreferences);
3265
3266	DisableMenuItem(GetMenuRef(mEdit), 0);
3267
3268	CheckMenuItem(GetMenuRef(mCheat), iApplyCheats, applycheat);
3269	Settings.ApplyCheats = applycheat;
3270
3271	err = GetMenuItemHierarchicalMenu(GetMenuRef(mControl), iISpPreset, &menu);
3272	CheckMenuItem(menu, padSetting, true);
3273
3274	err = GetMenuItemHierarchicalMenu(GetMenuRef(mEmulation), iDevice, &menu);
3275	CheckMenuItem(menu, deviceSetting, true);
3276	deviceSettingMaster = deviceSetting;
3277
3278	DisableMenuItem(GetMenuRef(mOption), iCIFilter);
3279
3280	InitRecentItems();
3281	InitRecentMenu();
3282	BuildRecentMenu();
3283
3284	InitMultiCart();
3285
3286	DrawMenuBar();
3287
3288	autofire = (autofireRec[0].buttonMask || autofireRec[1].buttonMask) ? true : false;
3289	for (int a = 0; a < MAC_MAX_PLAYERS; a++)
3290		for (int b = 0; b < 12; b++)
3291			autofireRec[a].nextTime[b] = 0;
3292
3293	S9xMovieInit();
3294
3295	S9xUnmapAllControls();
3296	S9xSetupDefaultKeymap();
3297	ChangeInputDevice();
3298
3299	err = EnterMovies();
3300
3301	if (!Memory.Init() || !S9xInitAPU() || !S9xGraphicsInit())
3302		QuitWithFatalError(err, "os 01");
3303
3304	frzselecting = false;
3305
3306	S9xSetControllerCrosshair(X_MOUSE1, 0, NULL, NULL);
3307	S9xSetControllerCrosshair(X_MOUSE2, 0, NULL, NULL);
3308
3309	if (systemVersion >= 0x1040)
3310	{
3311		InitCoreImage();
3312		InitCoreImageFilter();
3313	}
3314}
3315
3316static void Deinitialize (void)
3317{
3318	if (systemVersion >= 0x1040)
3319	{
3320		DeinitCoreImageFilter();
3321		DeinitCoreImage();
3322	}
3323
3324	deviceSetting = deviceSettingMaster;
3325
3326	ExitMovies();
3327
3328	DeinitMultiCart();
3329	DeinitRecentMenu();
3330	DeinitRecentItems();
3331	SavePrefs();
3332	ReleaseHID();
3333	DeinitCheatFinder();
3334	DeinitGraphics();
3335	DeinitKeyboard();
3336	DeinitMacSound();
3337	DeinitAppleEvents();
3338	ReleaseIconImages();
3339
3340	S9xGraphicsDeinit();
3341	S9xDeinitAPU();
3342	Memory.Deinit();
3343}
3344
3345static void InitAutofire (void)
3346{
3347	autofire = false;
3348
3349	for (int i = 0; i < 2; i++)
3350	{
3351		for (int j = 0; j < 12; j++)
3352			autofireRec[i].nextTime[j] = 0;
3353
3354		autofireRec[i].buttonMask = 0x0000;
3355		autofireRec[i].toggleMask = 0xFFF0;
3356		autofireRec[i].tcMask     = 0x0000;
3357		autofireRec[i].invertMask = 0x0000;
3358		autofireRec[i].frequency  = 10;
3359	}
3360}
3361
3362static void ForceChangingKeyScript (void)
3363{
3364	if (systemVersion >= 0x1050)
3365	{
3366		OSStatus			err;
3367		TISInputSourceRef	tis;
3368
3369		tis = TISCopyCurrentASCIICapableKeyboardInputSource();
3370		err = TISSelectInputSource(tis);
3371		CFRelease(tis);
3372	}
3373#ifdef MAC_TIGER_PANTHER_SUPPORT
3374	else
3375	{
3376		long	script;
3377
3378		script = GetScriptManagerVariable(smKeyScript);
3379		if (script == smJapanese)
3380			KeyScript(smRoman | smKeyForceKeyScriptMask);
3381	}
3382#endif
3383}
3384
3385void S9xSyncSpeed (void)
3386{
3387	long long	currentFrame, adjustment;
3388
3389	if (directDisplay)
3390	{
3391		if (extraOptions.benchmark)
3392			IPPU.RenderThisFrame = true;
3393		else
3394		{
3395			if (Settings.SoundSync)
3396			{
3397				while (!S9xSyncSound())
3398					usleep(0);
3399			}
3400
3401			if (!macQTRecord)
3402			{
3403				if (macFrameSkip < 0)	// auto skip
3404				{
3405					skipFrames--;
3406
3407					if (skipFrames <= 0)
3408					{
3409						adjustment = (Settings.TurboMode ? (macFrameAdvanceRate / macFastForwardRate) : macFrameAdvanceRate) / Memory.ROMFramesPerSecond;
3410						Microseconds((UnsignedWide *) &currentFrame);
3411
3412						skipFrames = (int32) ((currentFrame - lastFrame) / adjustment);
3413						lastFrame += frameCount * adjustment;
3414
3415						if (skipFrames < 1)
3416							skipFrames = 1;
3417						else
3418						if (skipFrames > 7)
3419						{
3420							skipFrames = 7;
3421							Microseconds((UnsignedWide *) &lastFrame);
3422						}
3423
3424						frameCount = skipFrames;
3425
3426						if (lastFrame > currentFrame)
3427							usleep((useconds_t) (lastFrame - currentFrame));
3428
3429						IPPU.RenderThisFrame = true;
3430					}
3431					else
3432						IPPU.RenderThisFrame = false;
3433				}
3434				else					// constant
3435				{
3436					skipFrames--;
3437
3438					if (skipFrames <= 0)
3439					{
3440						adjustment = macFrameAdvanceRate * macFrameSkip / Memory.ROMFramesPerSecond;
3441						Microseconds((UnsignedWide *) &currentFrame);
3442
3443						if (currentFrame - lastFrame < adjustment)
3444						{
3445							usleep((useconds_t) (adjustment + lastFrame - currentFrame));
3446							Microseconds((UnsignedWide *) &currentFrame);
3447						}
3448
3449						lastFrame = currentFrame;
3450						skipFrames = macFrameSkip;
3451						if (Settings.TurboMode)
3452							skipFrames *= macFastForwardRate;
3453
3454						IPPU.RenderThisFrame = true;
3455					}
3456					else
3457						IPPU.RenderThisFrame = false;
3458				}
3459			}
3460			else
3461			{
3462				MacQTRecordFrame(IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight);
3463
3464				adjustment = macFrameAdvanceRate / Memory.ROMFramesPerSecond;
3465				Microseconds((UnsignedWide *) &currentFrame);
3466
3467				if (currentFrame - lastFrame < adjustment)
3468					usleep((useconds_t) (adjustment + lastFrame - currentFrame));
3469
3470				lastFrame = currentFrame;
3471
3472				IPPU.RenderThisFrame = true;
3473			}
3474		}
3475	}
3476	else
3477		IPPU.RenderThisFrame = false;
3478}
3479
3480void S9xAutoSaveSRAM (void)
3481{
3482    SNES9X_SaveSRAM();
3483}
3484
3485void S9xMessage (int type, int number, const char *message)
3486{
3487	static char	mes[256];
3488
3489	if (!onscreeninfo)
3490	{
3491		printf("%s\n", message);
3492
3493		if ((type == S9X_INFO) && (number == S9X_ROM_INFO))
3494			if (strstr(message, "checksum ok") == NULL)
3495				AppearanceAlert(kAlertCautionAlert, kS9xMacAlertkBadRom, kS9xMacAlertkBadRomHint);
3496	}
3497	else
3498	{
3499		strncpy(mes, message, 255);
3500		S9xSetInfoString(mes);
3501	}
3502}
3503
3504const char * S9xStringInput (const char *s)
3505{
3506	return (NULL);
3507}
3508
3509void S9xToggleSoundChannel (int c)
3510{
3511    static int	channel_enable = 255;
3512
3513	if (c == 8)
3514		channel_enable = 255;
3515    else
3516		channel_enable ^= 1 << c;
3517
3518	S9xSetSoundControl(channel_enable);
3519}
3520
3521void S9xExit (void)
3522{
3523	PlayAlertSound();
3524
3525	running = false;
3526	cartOpen = false;
3527
3528	QuitApplicationEventLoop();
3529}
3530
3531void QuitWithFatalError (OSStatus err, const char *msg)
3532{
3533	printf("Quit. %s  err: %ld\n", msg, err);
3534	ExitToShell();
3535}
3536