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