720 lines
25 KiB
Objective-C
720 lines
25 KiB
Objective-C
/*****************************************************************************\
|
|
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
|
|
This file is licensed under the Snes9x License.
|
|
For further information, consult the LICENSE file in the root directory.
|
|
\*****************************************************************************/
|
|
|
|
/***********************************************************************************
|
|
SNES9X for Mac OS (c) Copyright John Stiles
|
|
|
|
Snes9x for Mac OS X
|
|
|
|
(c) Copyright 2001 - 2011 zones
|
|
(c) Copyright 2002 - 2005 107
|
|
(c) Copyright 2002 PB1400c
|
|
(c) Copyright 2004 Alexander and Sander
|
|
(c) Copyright 2004 - 2005 Steven Seeger
|
|
(c) Copyright 2005 Ryan Vogt
|
|
(c) Copyright 2019 - 2022 Michael Donald Buckley
|
|
***********************************************************************************/
|
|
|
|
#import <Carbon/Carbon.h>
|
|
#import "AppDelegate.h"
|
|
#import "S9xPreferencesConstants.h"
|
|
#import "S9xCheatFinderViewController.h"
|
|
#import "S9xOpenMultipleViewController.h"
|
|
|
|
NSWindowFrameAutosaveName const kMainWindowIdentifier = @"s9xMainWindow";
|
|
NSWindowFrameAutosaveName const kCheatsWindowIdentifier = @"s9xCheatsWindow";
|
|
NSWindowFrameAutosaveName const kCheatFinderWindowIdentifier = @"s9xCheatFinderWindow";
|
|
|
|
@interface AppDelegate () <S9xCheatFinderDelegate>
|
|
@end
|
|
|
|
@implementation AppDelegate
|
|
|
|
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
|
self.s9xEngine = [S9xEngine new];
|
|
self.s9xEngine.inputDelegate = self;
|
|
[self setupDefaults];
|
|
[self importRecentItems];
|
|
|
|
NSWindow *gameWindow = [[NSWindow alloc] initWithContentRect:s9xView.frame styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskMiniaturizable|NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO];
|
|
|
|
gameWindow.contentView.wantsLayer = YES;
|
|
gameWindow.contentView.layer.backgroundColor = NSColor.blackColor.CGColor;
|
|
|
|
gameWindow.title = @"Snes9x";
|
|
gameWindow.restorationClass = [self class];
|
|
gameWindow.frameAutosaveName = kMainWindowIdentifier;
|
|
gameWindow.releasedWhenClosed = NO;
|
|
gameWindow.backgroundColor = NSColor.clearColor;
|
|
|
|
if ( ![gameWindow setFrameUsingName:kMainWindowIdentifier] )
|
|
{
|
|
[gameWindow center];
|
|
}
|
|
|
|
self.gameWindow = gameWindow;
|
|
|
|
[NSNotificationCenter.defaultCenter addObserverForName:NSWindowWillCloseNotification object:gameWindow queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification *notification)
|
|
{
|
|
[self.s9xEngine quit];
|
|
}];
|
|
|
|
[self resetWindow];
|
|
}
|
|
|
|
- (void)applicationWillTerminate:(NSNotification *)aNotification {
|
|
[self.s9xEngine quit];
|
|
}
|
|
|
|
- (void)setupDefaults
|
|
{
|
|
NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults;
|
|
|
|
NSDictionary *defaultSettings = @{
|
|
kKeyboardPrefs : @{
|
|
@(kUp).stringValue : @(kVK_UpArrow),
|
|
@(kDown).stringValue : @(kVK_DownArrow),
|
|
@(kLeft).stringValue : @(kVK_LeftArrow),
|
|
@(kRight).stringValue : @(kVK_RightArrow),
|
|
@(kY).stringValue : @(kVK_ANSI_X),
|
|
@(kB).stringValue : @(kVK_ANSI_C),
|
|
@(kX).stringValue : @(kVK_ANSI_D),
|
|
@(kA).stringValue : @(kVK_ANSI_V),
|
|
@(kL).stringValue : @(kVK_ANSI_A),
|
|
@(kR).stringValue : @(kVK_ANSI_S),
|
|
@(kStart).stringValue : @(kVK_Space),
|
|
@(kSelect).stringValue : @(kVK_Return),
|
|
|
|
@(kNumButtons + kUp).stringValue : @(kVK_ANSI_Keypad8),
|
|
@(kNumButtons + kDown).stringValue : @(kVK_ANSI_Keypad2),
|
|
@(kNumButtons + kLeft).stringValue : @(kVK_ANSI_Keypad4),
|
|
@(kNumButtons + kRight).stringValue : @(kVK_ANSI_Keypad6),
|
|
@(kNumButtons + kY).stringValue : @(kVK_PageDown),
|
|
@(kNumButtons + kB).stringValue : @(kVK_PageUp),
|
|
@(kNumButtons + kX).stringValue : @(kVK_End),
|
|
@(kNumButtons + kA).stringValue : @(kVK_Home),
|
|
@(kNumButtons + kL).stringValue : @(kVK_ANSI_Keypad0),
|
|
@(kNumButtons + kR).stringValue : @(kVK_ANSI_KeypadDecimal),
|
|
@(kNumButtons + kStart).stringValue : @(kVK_ANSI_KeypadEnter),
|
|
@(kNumButtons + kSelect).stringValue : @(kVK_ANSI_KeypadPlus),
|
|
|
|
@(kKeyFastForward).stringValue : @(kVK_ANSI_Backslash),
|
|
@(kKeyFreeze).stringValue : @(kVK_ANSI_1),
|
|
@(kKeyDefrost).stringValue : @(kVK_ANSI_0),
|
|
@(kKeyScreenshot).stringValue : @(kVK_ANSI_Grave),
|
|
@(kKeySPC).stringValue : @(kVK_ANSI_R),
|
|
@(kKeyScopeTurbo).stringValue : @(kVK_ANSI_B),
|
|
@(kKeyScopePause).stringValue : @(kVK_ANSI_N),
|
|
@(kKeyScopeCursor).stringValue : @(kVK_ANSI_M),
|
|
@(kKeyOffScreen).stringValue : @(kVK_Tab),
|
|
@(kKeyFunction).stringValue : @(kVK_ANSI_Slash),
|
|
@(kKeyAlt).stringValue : @(kVK_ANSI_Period),
|
|
@(kKeyFFDown).stringValue : @(kVK_ANSI_Q),
|
|
@(kKeyFFUp).stringValue : @(kVK_ANSI_W),
|
|
@(kKeyEsc).stringValue : @(kVK_Escape),
|
|
@(kKeyTC).stringValue : @(kVK_ANSI_Comma)
|
|
},
|
|
kShowFPSPref : @(NO),
|
|
kVideoModePref : @(VIDEOMODE_BLOCKY),
|
|
kMacFrameSkipPref : @(macFrameSkip),
|
|
|
|
kSuperFXClockSpeedPercentPref : @(100),
|
|
kSoundInterpolationTypePref: @(2),
|
|
kCPUOverclockPref : @(0),
|
|
|
|
kApplyGameSpecificHacksPref : (@YES),
|
|
kAllowInvalidVRAMAccessPref : @(NO),
|
|
kSeparateEchoBufferFromRAMPref : @(NO),
|
|
kDisableSpriteLimitPref : @(NO),
|
|
};
|
|
|
|
[defaults registerDefaults:defaultSettings];
|
|
[defaults setBool:NO forKey:@"NSWindowAssertWhenDisplayCycleLimitReached"];
|
|
[defaults synchronize];
|
|
|
|
self.keys = [[defaults objectForKey:kKeyboardPrefs] mutableCopy];
|
|
|
|
for (NSString *control in [self.keys copy])
|
|
{
|
|
NSInteger key = control.integerValue;
|
|
NSInteger player = key / kNumButtons;
|
|
S9xButtonCode buttonCode = (S9xButtonCode)(key - (kNumButtons * player));
|
|
[self setButtonCode:buttonCode forKeyCode:self.keys[control].integerValue player:player];
|
|
}
|
|
|
|
NSDictionary *joypadPlayerPrefs = [defaults objectForKey:kJoypadPlayerPrefs];
|
|
|
|
for ( S9xJoypad *joypad in [self listJoypads])
|
|
{
|
|
NSMutableDictionary *joypadPrefs = [[defaults objectForKey:kJoypadInputPrefs] mutableCopy];
|
|
|
|
if (joypadPrefs == nil)
|
|
{
|
|
joypadPrefs = [NSMutableDictionary new];
|
|
[defaults synchronize];
|
|
}
|
|
|
|
NSString *key = [self prefsKeyForVendorID:joypad.vendorID productID:joypad.productID index:joypad.index];
|
|
|
|
for ( NSString *playerString in joypadPlayerPrefs )
|
|
{
|
|
if ( [key isEqualToString:joypadPlayerPrefs[playerString]] )
|
|
{
|
|
[self setPlayer:playerString.intValue forVendorID:joypad.vendorID productID:joypad.productID index:joypad.index];
|
|
}
|
|
}
|
|
|
|
NSMutableDictionary *devicePrefs = [joypadPrefs[key] mutableCopy];
|
|
if (devicePrefs == nil)
|
|
{
|
|
devicePrefs = [NSMutableDictionary new];
|
|
for (S9xJoypadInput *input in [self.s9xEngine getInputsForVendorID:joypad.vendorID productID:joypad.productID index:joypad.index])
|
|
{
|
|
devicePrefs[@(input.buttonCode).stringValue] = [self prefValueForCookie:input.cookie value:input.value];
|
|
}
|
|
|
|
joypadPrefs[key] = devicePrefs;
|
|
[defaults setObject:joypadPrefs forKey:kJoypadInputPrefs];
|
|
[defaults synchronize];
|
|
}
|
|
else
|
|
{
|
|
[self.s9xEngine clearJoypadForVendorID:joypad.vendorID productID:joypad.productID index:joypad.index];
|
|
for (NSString *buttonCodeString in devicePrefs)
|
|
{
|
|
S9xButtonCode buttonCode = (S9xButtonCode)buttonCodeString.intValue;
|
|
NSString *str = devicePrefs[buttonCodeString];
|
|
uint32 cookie = 0;
|
|
int32 value = -1;
|
|
|
|
if ([self getValuesFromString:str cookie:&cookie value:&value])
|
|
{
|
|
[self setButton:buttonCode forVendorID:joypad.vendorID productID:joypad.productID index:joypad.index cookie:cookie value:value];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
self.deviceSetting = Gamepads;
|
|
|
|
[self importKeySettings];
|
|
[self importGraphicsSettings];
|
|
[self applyEmulationSettings];
|
|
|
|
self.s9xEngine.cheatsEnabled = [defaults boolForKey:kEnableCheatsPref];
|
|
|
|
[defaults synchronize];
|
|
}
|
|
|
|
- (void)setButtonCode:(S9xButtonCode)buttonCode forKeyCode:(int16)keyCode player:(int8)player
|
|
{
|
|
if (keyCode < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
self.keys[@(buttonCode + (kNumButtons * player)).stringValue] = @(keyCode);
|
|
|
|
S9xButtonCode oldButton = kNumButtons;
|
|
int8 oldPlayer = -1;
|
|
if ([self.s9xEngine setButton:buttonCode forKey:keyCode player:player oldButton:&oldButton oldPlayer:&oldPlayer oldKey:NULL])
|
|
{
|
|
if (oldButton >= 0 && oldButton < kNumButtons && oldPlayer >= 0 && oldPlayer < MAC_MAX_PLAYERS && (oldPlayer != player || oldButton != buttonCode))
|
|
{
|
|
[self.keys removeObjectForKey:@(oldButton + (kNumButtons * oldPlayer)).stringValue];
|
|
}
|
|
|
|
[NSUserDefaults.standardUserDefaults setObject:[self.keys copy] forKey:kKeyboardPrefs];
|
|
}
|
|
}
|
|
|
|
- (void)clearButton:(S9xButtonCode)button forPlayer:(int8)player
|
|
{
|
|
[self.s9xEngine clearButton:button forPlayer:player];
|
|
NSMutableDictionary *keyDict = [[NSUserDefaults.standardUserDefaults objectForKey:kKeyboardPrefs] mutableCopy];
|
|
[keyDict removeObjectForKey:@(button + (kNumButtons * player)).stringValue];
|
|
[NSUserDefaults.standardUserDefaults setObject:[keyDict copy] forKey:kKeyboardPrefs];
|
|
[NSUserDefaults.standardUserDefaults synchronize];
|
|
}
|
|
|
|
- (NSArray<S9xJoypad *> *)listJoypads
|
|
{
|
|
return [self.s9xEngine listJoypads];
|
|
}
|
|
|
|
- (NSString *)prefsKeyForVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index
|
|
{
|
|
return [NSString stringWithFormat:@"%@:%@:%@", @(vendorID).stringValue, @(productID).stringValue, @(index).stringValue];
|
|
}
|
|
|
|
- (BOOL)getValuesFromString:(NSString *)str vendorID:(uint32 *)vendorID productID:(uint32 *)productID index:(uint32 *)index
|
|
{
|
|
if (vendorID == NULL || productID == NULL || index == NULL)
|
|
{
|
|
return NO;
|
|
|
|
}
|
|
|
|
NSArray<NSString *> *components = [str componentsSeparatedByString:@":"];
|
|
|
|
if (components.count != 3)
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
*vendorID = components[0].intValue;
|
|
*productID = components[1].intValue;
|
|
*index = components[2].intValue;
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (NSString *)prefValueForCookie:(uint32)cookie value:(int32)value
|
|
{
|
|
return [NSString stringWithFormat:@"%@:%@", @(cookie).stringValue, @(value).stringValue];
|
|
}
|
|
|
|
- (BOOL)getValuesFromString:(NSString *)str cookie:(uint32 *)cookie value:(int32 *)value
|
|
{
|
|
if (cookie == NULL || value == NULL)
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
NSArray<NSString *> *components = [str componentsSeparatedByString:@":"];
|
|
|
|
if (components.count != 2)
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
*cookie = components.firstObject.intValue;
|
|
*value = components.lastObject.intValue;
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (void)setPlayer:(int8)player forVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index
|
|
{
|
|
int8 oldPlayer = -1;
|
|
[self.s9xEngine setPlayer:player forVendorID:vendorID productID:productID index:index oldPlayer:&oldPlayer];
|
|
|
|
NSMutableDictionary *playersDict = [[NSUserDefaults.standardUserDefaults objectForKey:kJoypadPlayerPrefs] mutableCopy];
|
|
if (playersDict == nil)
|
|
{
|
|
playersDict = [NSMutableDictionary new];
|
|
}
|
|
|
|
if (oldPlayer >= 0 && player != oldPlayer)
|
|
{
|
|
[playersDict removeObjectForKey:@(oldPlayer).stringValue];
|
|
}
|
|
|
|
playersDict[@(player).stringValue] = [self prefsKeyForVendorID:vendorID productID:productID index:index];
|
|
|
|
[NSUserDefaults.standardUserDefaults setObject:[playersDict copy] forKey:kJoypadPlayerPrefs];
|
|
}
|
|
|
|
- (BOOL)setButton:(S9xButtonCode)button forVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index cookie:(uint32)cookie value:(int32)value
|
|
{
|
|
S9xButtonCode oldButton = (S9xButtonCode)-1;
|
|
BOOL result = [self.s9xEngine setButton:button forVendorID:vendorID productID:productID index:index cookie:cookie value:value oldButton:&oldButton];
|
|
|
|
NSMutableDictionary *prefsDict = [[NSUserDefaults.standardUserDefaults objectForKey:kJoypadInputPrefs] mutableCopy];
|
|
NSString *key = [self prefsKeyForVendorID:vendorID productID:productID index:index];
|
|
NSMutableDictionary *joypadDict = [prefsDict[key] mutableCopy];
|
|
|
|
if (result && button != oldButton)
|
|
{
|
|
[joypadDict removeObjectForKey:@(oldButton).stringValue];
|
|
}
|
|
|
|
joypadDict[@(button).stringValue] = [self prefValueForCookie:cookie value:value];
|
|
|
|
prefsDict[key] = [joypadDict copy];
|
|
|
|
[NSUserDefaults.standardUserDefaults setObject:[prefsDict copy] forKey:kJoypadInputPrefs];
|
|
|
|
return result;
|
|
}
|
|
|
|
- (void)clearJoypadForVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index buttonCode:(S9xButtonCode)buttonCode
|
|
{
|
|
[self.s9xEngine clearJoypadForVendorID:vendorID productID:productID index:index buttonCode:buttonCode];
|
|
NSString *key = [self prefsKeyForVendorID:vendorID productID:productID index:index];
|
|
NSMutableDictionary *joypadsDict = [[NSUserDefaults.standardUserDefaults objectForKey:kJoypadInputPrefs] mutableCopy];
|
|
NSMutableDictionary *deviceDict = [joypadsDict[key] mutableCopy];
|
|
[deviceDict removeObjectForKey:@(buttonCode).stringValue];
|
|
joypadsDict[key] = deviceDict;
|
|
[NSUserDefaults.standardUserDefaults setObject:[joypadsDict copy] forKey:kJoypadInputPrefs];
|
|
[NSUserDefaults.standardUserDefaults synchronize];
|
|
}
|
|
|
|
- (NSString *)labelForVendorID:(uint32)vendorID productID:(uint32)productID cookie:(uint32)cookie value:(int32)value
|
|
{
|
|
return [self.s9xEngine labelForVendorID:vendorID productID:productID cookie:cookie value:value];
|
|
}
|
|
|
|
- (void)importRecentItems
|
|
{
|
|
const NSInteger maxRecents = 20;
|
|
|
|
for (NSInteger i = maxRecents - 1; i >= 0; --i)
|
|
{
|
|
NSString *key = [NSString stringWithFormat:@"RecentItem_%02tu", i];
|
|
NSString *recentItem = [NSUserDefaults.standardUserDefaults objectForKey:key];
|
|
|
|
if (recentItem != nil)
|
|
{
|
|
[NSDocumentController.sharedDocumentController noteNewRecentDocumentURL:[NSURL fileURLWithPath:recentItem]];
|
|
[NSUserDefaults.standardUserDefaults removeObjectForKey:key];
|
|
}
|
|
}
|
|
|
|
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
}
|
|
|
|
- (void)importKeySettings
|
|
{
|
|
NSData *data = [self objectForPrefOSCode:'keyb'];
|
|
|
|
NSUInteger length = data.length;
|
|
char *bytes = (char*)data.bytes;
|
|
for ( NSUInteger i = 0; i < length; ++i )
|
|
{
|
|
// The enum values for controls changed between the Carbon and Cocoa versions.
|
|
// The first 24 enum values are the same, but we have to adjust after that.
|
|
if ( i < 24 )
|
|
{
|
|
[self setButtonCode:(S9xButtonCode)(i - (i / 12)) forKeyCode:bytes[i] player:i / 12];
|
|
}
|
|
else
|
|
{
|
|
[self setButtonCode:(S9xButtonCode)(i - 24 + kKeyFastForward) forKeyCode:bytes[i] player:0];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)importGraphicsSettings
|
|
{
|
|
NSData *data = [self objectForPrefOSCode:'dfps'];
|
|
|
|
if (data != nil)
|
|
{
|
|
[NSUserDefaults.standardUserDefaults setBool:(data.length > 0 && ((char *)data.bytes)[0]) forKey:kShowFPSPref];
|
|
}
|
|
|
|
[self setShowFPS:[NSUserDefaults.standardUserDefaults boolForKey:kShowFPSPref]];
|
|
|
|
data = [self objectForPrefOSCode:'Vmod'];
|
|
|
|
if ( data != nil)
|
|
{
|
|
[NSUserDefaults.standardUserDefaults setInteger:((data.length >= 0 && ((char *)data.bytes)[0]) ? VIDEOMODE_SMOOTH : VIDEOMODE_BLOCKY) forKey:kVideoModePref];
|
|
}
|
|
|
|
[self setVideoMode:(int)[NSUserDefaults.standardUserDefaults integerForKey:kVideoModePref]];
|
|
}
|
|
|
|
- (id)objectForPrefOSCode:(uint32_t)osCode
|
|
{
|
|
NSString *key = [@"Preferences_" stringByAppendingString:[[NSString alloc] initWithBytes:(char *)&osCode length:sizeof(uint32_t) encoding:NSASCIIStringEncoding]];
|
|
|
|
id obj = [NSUserDefaults.standardUserDefaults objectForKey:key];
|
|
|
|
if (obj == nil)
|
|
{
|
|
osCode =CFSwapInt32(osCode);
|
|
key = [@"Preferences_" stringByAppendingString:[[NSString alloc] initWithBytes:(char *)&osCode length:sizeof(uint32_t) encoding:NSASCIIStringEncoding]];
|
|
|
|
obj = [NSUserDefaults.standardUserDefaults objectForKey:key];
|
|
|
|
if (obj != nil)
|
|
{
|
|
[NSUserDefaults.standardUserDefaults removeObjectForKey:key];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
[NSUserDefaults.standardUserDefaults removeObjectForKey:key];
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename
|
|
{
|
|
return [self openURL:[NSURL fileURLWithPath:filename]];
|
|
}
|
|
|
|
- (IBAction)openDocument:(id)sender
|
|
{
|
|
NSOpenPanel* panel = [NSOpenPanel new];
|
|
panel.canChooseDirectories = NO;
|
|
panel.allowsMultipleSelection = NO;
|
|
|
|
NSModalResponse response = [panel runModal];
|
|
|
|
if ( response == NSModalResponseOK )
|
|
{
|
|
[self openURL:panel.URL];
|
|
}
|
|
}
|
|
|
|
- (BOOL)openURL:(NSURL *)url
|
|
{
|
|
if ([self.s9xEngine loadROM:url])
|
|
{
|
|
[self resetWindow];
|
|
[NSDocumentController.sharedDocumentController noteNewRecentDocumentURL:url];
|
|
return YES;
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (IBAction)openMultiple:(id)sender
|
|
{
|
|
S9xOpenMultipleViewController *vc = [[S9xOpenMultipleViewController alloc] initWithNibName:@"S9xOpenMultipleViewController" bundle:nil];
|
|
vc.completionBlock = ^(NSArray<NSURL *> *fileURLs)
|
|
{
|
|
[self openMultipleURLs:fileURLs];
|
|
};
|
|
|
|
NSWindow *window = [NSWindow windowWithContentViewController:vc];
|
|
window.title = NSLocalizedString(@"Open MultiCart", @"");
|
|
window.styleMask = NSWindowStyleMaskTitled;
|
|
[window makeKeyAndOrderFront:self];
|
|
|
|
[NSApp runModalForWindow:window];
|
|
}
|
|
|
|
- (BOOL)openMultipleURLs:(NSArray<NSURL *> *)urls
|
|
{
|
|
if ([self.s9xEngine loadMultiple:urls])
|
|
{
|
|
[self resetWindow];
|
|
return YES;
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (void)resetWindow
|
|
{
|
|
[self.s9xEngine recreateS9xView];
|
|
|
|
NSWindow *gameWindow = self.gameWindow;
|
|
[gameWindow.contentView addSubview:s9xView];
|
|
[s9xView.topAnchor constraintEqualToAnchor:gameWindow.contentView.topAnchor].active = YES;
|
|
[s9xView.bottomAnchor constraintEqualToAnchor:gameWindow.contentView.bottomAnchor].active = YES;
|
|
[s9xView.centerXAnchor constraintEqualToAnchor:gameWindow.contentView.centerXAnchor].active = YES;
|
|
[s9xView.leftAnchor constraintGreaterThanOrEqualToAnchor:gameWindow.contentView.leftAnchor].active = YES;
|
|
[s9xView.rightAnchor constraintLessThanOrEqualToAnchor:gameWindow.contentView.rightAnchor].active = YES;
|
|
|
|
if (self.cheatsWindowController != nil)
|
|
{
|
|
[((S9xCheatsViewController *)self.cheatsWindowController.contentViewController) deselectAll];
|
|
[((S9xCheatsViewController *)self.cheatsWindowController.contentViewController) reloadData];
|
|
}
|
|
|
|
[gameWindow makeKeyAndOrderFront:self];
|
|
}
|
|
|
|
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
|
|
{
|
|
SEL action = menuItem.action;
|
|
if (action == @selector(resume:) || action == @selector(softwareReset:) || action == @selector(hardwareReset:)) {
|
|
return [self.s9xEngine isRunning] && [self.s9xEngine isPaused];
|
|
}
|
|
else if (action == @selector(updateDeviceSetting:)) {
|
|
menuItem.state = (self.deviceSetting == (S9xDeviceSetting)menuItem.tag) ? NSOnState : NSOffState;
|
|
}
|
|
else if (action == @selector(toggleCheats:))
|
|
{
|
|
if (self.s9xEngine.cheatsEnabled)
|
|
{
|
|
menuItem.title = NSLocalizedString(@"Disable Cheats", nil);
|
|
}
|
|
else
|
|
{
|
|
menuItem.title = NSLocalizedString(@"Enable Cheats", nil);
|
|
}
|
|
}
|
|
|
|
return !self.isRunningEmulation;
|
|
}
|
|
|
|
@dynamic isRunningEmulation;
|
|
- (BOOL)isRunningEmulation
|
|
{
|
|
return [self.s9xEngine isRunning] && ![self.s9xEngine isPaused];
|
|
}
|
|
|
|
- (IBAction)terminate:(id)sender
|
|
{
|
|
[self.s9xEngine stop];
|
|
[NSApp terminate:sender];
|
|
}
|
|
|
|
- (IBAction)openPreferencesWindow:(id)sender
|
|
{
|
|
if ( self.preferencesWindowController == nil )
|
|
{
|
|
self.preferencesWindowController = [[S9xPreferencesWindowController alloc] initWithWindowNibName:@"S9xPreferencesWindowController"];
|
|
}
|
|
|
|
[self.preferencesWindowController showWindow:nil];
|
|
[self.preferencesWindowController.window makeKeyAndOrderFront:nil];
|
|
[self.preferencesWindowController.window makeKeyWindow];
|
|
}
|
|
|
|
- (void)setVideoMode:(int)videoMode
|
|
{
|
|
[self.s9xEngine setVideoMode:videoMode];
|
|
[NSUserDefaults.standardUserDefaults setObject:@(videoMode) forKey:kVideoModePref];
|
|
[NSUserDefaults.standardUserDefaults synchronize];
|
|
}
|
|
|
|
- (void)setMacFrameSkip:(int)_macFrameSkip
|
|
{
|
|
[self.s9xEngine setMacFrameSkip:_macFrameSkip];
|
|
[NSUserDefaults.standardUserDefaults setObject:@(_macFrameSkip) forKey:kMacFrameSkipPref];
|
|
[NSUserDefaults.standardUserDefaults synchronize];
|
|
}
|
|
|
|
- (void)setShowFPS:(BOOL)showFPS
|
|
{
|
|
[self.s9xEngine setShowFPS:showFPS];
|
|
[NSUserDefaults.standardUserDefaults setObject:@(showFPS) forKey:kShowFPSPref];
|
|
[NSUserDefaults.standardUserDefaults synchronize];
|
|
}
|
|
|
|
- (void)applyEmulationSettings
|
|
{
|
|
S9xEngine *engine = self.s9xEngine;
|
|
NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults;
|
|
|
|
[engine setSuperFXClockSpeedPercent:(uint32_t)[defaults integerForKey:kSuperFXClockSpeedPercentPref]];
|
|
[engine setSoundInterpolationType:(int)[defaults integerForKey:kSoundInterpolationTypePref]];
|
|
[engine setCPUOverclockMode:(int)[defaults integerForKey:kCPUOverclockPref]];
|
|
|
|
[engine setApplySpecificGameHacks:[defaults boolForKey:kApplyGameSpecificHacksPref]];
|
|
[engine setAllowInvalidVRAMAccess:[defaults boolForKey:kAllowInvalidVRAMAccessPref]];
|
|
[engine setSeparateEchoBufferFromRAM:[defaults boolForKey:kSeparateEchoBufferFromRAMPref]];
|
|
[engine setDisableSpriteLimit:[defaults boolForKey:kDisableSpriteLimitPref]];
|
|
}
|
|
|
|
- (IBAction)resume:(id)sender
|
|
{
|
|
[self.s9xEngine resume];
|
|
}
|
|
|
|
- (IBAction)softwareReset:(id)sender
|
|
{
|
|
[self.s9xEngine softwareReset];
|
|
}
|
|
|
|
- (IBAction)hardwareReset:(id)sender
|
|
{
|
|
[self.s9xEngine hardwareReset];
|
|
}
|
|
|
|
- (IBAction)updateDeviceSetting:(id)sender
|
|
{
|
|
self.deviceSetting = (S9xDeviceSetting)[sender tag];
|
|
}
|
|
|
|
- (void)setDeviceSetting:(S9xDeviceSetting)deviceSetting
|
|
{
|
|
_deviceSetting = deviceSetting;
|
|
[self.s9xEngine setDeviceSetting:deviceSetting];
|
|
}
|
|
|
|
- (BOOL)handleInput:(S9xJoypadInput *)input fromJoypad:(S9xJoypad *)joypad
|
|
{
|
|
if (NSApp.keyWindow != nil && NSApp.keyWindow == self.preferencesWindowController.window)
|
|
{
|
|
return [((S9xPreferencesWindowController *) self.preferencesWindowController) handleInput:input fromJoypad:joypad];
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (void)deviceSettingChanged:(S9xDeviceSetting)deviceSetting
|
|
{
|
|
_deviceSetting = deviceSetting;
|
|
}
|
|
|
|
|
|
- (IBAction)openCheatsWindow:(id)sender
|
|
{
|
|
if (self.cheatsWindowController == nil)
|
|
{
|
|
NSWindow *window = [NSWindow windowWithContentViewController:[[S9xCheatsViewController alloc] initWithNibName:@"S9xCheatsViewController" bundle:nil]];
|
|
self.cheatsWindowController = [[NSWindowController alloc] initWithWindow:window];
|
|
|
|
window = self.cheatsWindowController.window;
|
|
|
|
window.title = NSLocalizedString(@"Cheats", nil);
|
|
window.restorationClass = self.class;
|
|
window.frameAutosaveName = kCheatsWindowIdentifier;
|
|
window.releasedWhenClosed = NO;
|
|
|
|
if ( ![window setFrameUsingName:kCheatsWindowIdentifier] )
|
|
{
|
|
[window center];
|
|
}
|
|
}
|
|
|
|
[self.cheatsWindowController showWindow:nil];
|
|
[self.cheatsWindowController.window makeKeyAndOrderFront:nil];
|
|
[self.cheatsWindowController.window makeKeyWindow];
|
|
}
|
|
|
|
- (IBAction)toggleCheats:(id)sender
|
|
{
|
|
self.s9xEngine.cheatsEnabled = !self.s9xEngine.cheatsEnabled;
|
|
[NSUserDefaults.standardUserDefaults setBool:self.s9xEngine.cheatsEnabled forKey:kEnableCheatsPref];
|
|
}
|
|
|
|
- (IBAction)openCheatFinderWindow:(id)sender
|
|
{
|
|
if ( self.cheatFinderWindowController == nil )
|
|
{
|
|
S9xCheatFinderViewController *vc = [[S9xCheatFinderViewController alloc] initWithNibName:@"S9xCheatFinderViewController" bundle:nil];
|
|
vc.engine = self.s9xEngine;
|
|
vc.delegate = self;
|
|
|
|
NSWindow *window = [NSWindow windowWithContentViewController:vc];
|
|
self.cheatFinderWindowController = [[NSWindowController alloc] initWithWindow:window];
|
|
|
|
window = self.cheatFinderWindowController.window;
|
|
|
|
window.title = NSLocalizedString(@"Cheat Finder", nil);
|
|
window.restorationClass = self.class;
|
|
window.frameAutosaveName = kCheatFinderWindowIdentifier;
|
|
window.releasedWhenClosed = NO;
|
|
|
|
if ( ![window setFrameUsingName:kCheatFinderWindowIdentifier] )
|
|
{
|
|
[window center];
|
|
}
|
|
}
|
|
|
|
[self.cheatFinderWindowController showWindow:nil];
|
|
[self.cheatFinderWindowController.window makeKeyAndOrderFront:nil];
|
|
[self.cheatFinderWindowController.window makeKeyWindow];
|
|
}
|
|
|
|
- (void)addedCheat
|
|
{
|
|
[((S9xCheatsViewController *)self.cheatsWindowController.contentViewController) deselectAll];
|
|
[((S9xCheatsViewController *)self.cheatsWindowController.contentViewController) reloadData];
|
|
}
|
|
|
|
@end
|