2016-03-30 20:07:55 +00:00
# import "AppDelegate.h"
2017-01-24 19:00:56 +00:00
# include "GBButtons.h"
2020-03-26 18:54:18 +00:00
# include "GBView.h"
2017-10-12 21:02:02 +00:00
# include < Core / gb . h >
2017-01-24 19:00:56 +00:00
# import < Carbon / Carbon . h >
2019-10-19 16:26:04 +00:00
# import < JoyKit / JoyKit . h >
2021-04-25 19:28:24 +00:00
# import < WebKit / WebKit . h >
2022-07-03 19:23:35 +00:00
# import < mach - o / dyld . h >
2021-04-25 19:28:24 +00:00
# define UPDATE_SERVER "https://sameboy.github.io"
static uint32_t color_to _int ( NSColor * color )
{
color = [ color colorUsingColorSpace : [ NSColorSpace deviceRGBColorSpace ] ] ;
return ( ( ( unsigned ) ( color . redComponent * 0 xFF ) ) < < 16 ) |
( ( ( unsigned ) ( color . greenComponent * 0 xFF ) ) < < 8 ) |
( ( unsigned ) ( color . blueComponent * 0 xFF ) ) ;
}
2016-03-30 20:07:55 +00:00
@ implementation AppDelegate
2016-04-13 19:43:16 +00:00
{
NSWindow * preferences_window ;
2018-12-01 14:08:59 +00:00
NSArray < NSView * > * preferences_tabs ;
2021-04-25 19:28:24 +00:00
NSString * _lastVersion ;
NSString * _updateURL ;
NSURLSessionDownloadTask * _updateTask ;
enum {
UPDATE_DOWNLOADING ,
UPDATE_EXTRACTING ,
UPDATE_WAIT _INSTALL ,
UPDATE_INSTALLING ,
UPDATE_FAILED ,
} _updateState ;
NSString * _downloadDirectory ;
2022-07-03 19:23:35 +00:00
AuthorizationRef _auth ;
2016-04-13 19:43:16 +00:00
}
- ( void ) applicationDidFinishLaunching : ( NSNotification * ) notification
{
2022-07-02 18:00:40 +00:00
// Refresh icon if launched via a software update
[ NSApplication sharedApplication ] . applicationIconImage = [ NSImage imageNamed : @ "AppIcon" ] ;
2017-01-24 19:00:56 +00:00
NSUserDefaults * defaults = [ NSUserDefaults standardUserDefaults ] ;
for ( unsigned i = 0 ; i < GBButtonCount ; i + + ) {
2018-12-04 22:00:16 +00:00
if ( [ [ defaults objectForKey : button_to _preference _name ( i , 0 ) ] isKindOfClass : [ NSString class ] ] ) {
[ defaults removeObjectForKey : button_to _preference _name ( i , 0 ) ] ;
2017-01-24 19:00:56 +00:00
}
}
2016-04-13 19:43:16 +00:00
[ [ NSUserDefaults standardUserDefaults ] registerDefaults : @ {
2017-01-24 19:00:56 +00:00
@ "GBRight" : @ ( kVK_RightArrow ) ,
@ "GBLeft" : @ ( kVK_LeftArrow ) ,
@ "GBUp" : @ ( kVK_UpArrow ) ,
@ "GBDown" : @ ( kVK_DownArrow ) ,
2016-04-13 19:43:16 +00:00
2017-01-24 19:00:56 +00:00
@ "GBA" : @ ( kVK_ANSI _X ) ,
@ "GBB" : @ ( kVK_ANSI _Z ) ,
@ "GBSelect" : @ ( kVK_Delete ) ,
@ "GBStart" : @ ( kVK_Return ) ,
2016-04-13 19:43:16 +00:00
2017-01-24 19:00:56 +00:00
@ "GBTurbo" : @ ( kVK_Space ) ,
2018-02-10 12:42:14 +00:00
@ "GBRewind" : @ ( kVK_Tab ) ,
2018-02-10 21:30:30 +00:00
@ "GBSlow-Motion" : @ ( kVK_Shift ) ,
2016-04-28 20:07:05 +00:00
@ "GBFilter" : @ "NearestNeighbor" ,
2017-10-12 14:22:22 +00:00
@ "GBColorCorrection" : @ ( GB_COLOR _CORRECTION _EMULATE _HARDWARE ) ,
2018-02-10 12:42:14 +00:00
@ "GBHighpassFilter" : @ ( GB_HIGHPASS _REMOVE _DC _OFFSET ) ,
2018-12-01 15:16:50 +00:00
@ "GBRewindLength" : @ ( 10 ) ,
2020-03-26 18:54:18 +00:00
@ "GBFrameBlendingMode" : @ ( [ defaults boolForKey : @ "DisableFrameBlending" ] ? GB_FRAME _BLENDING _MODE _DISABLED : GB_FRAME _BLENDING _MODE _ACCURATE ) ,
2018-12-01 15:16:50 +00:00
@ "GBDMGModel" : @ ( GB_MODEL _DMG _B ) ,
@ "GBCGBModel" : @ ( GB_MODEL _CGB _E ) ,
@ "GBSGBModel" : @ ( GB_MODEL _SGB2 ) ,
2020-04-29 13:50:31 +00:00
@ "GBRumbleMode" : @ ( GB_RUMBLE _CARTRIDGE _ONLY ) ,
2021-05-21 15:12:29 +00:00
@ "GBVolume" : @ ( 1.0 ) ,
2021-11-14 19:27:12 +00:00
@ "GBMBC7JoystickOverride" : @ NO ,
@ "GBMBC7AllowMouse" : @ YES ,
2022-03-14 22:37:05 +00:00
2022-05-21 18:27:02 +00:00
// Default themes
@ "GBThemes" : @ {
@ "Desert" : @ {
@ "BrightnessBias" : @ 0.0 ,
@ "Colors" : @ [ @ 0 xff302f3e , @ 0 xff576674 , @ 0 xff839ba4 , @ 0 xffb1d0d2 , @ 0 xffb7d7d8 ] ,
@ "DisabledLCDColor" : @ YES ,
@ "HueBias" : @ 0.10087773904382469 ,
@ "HueBiasStrength" : @ 0.062142056772908363 ,
@ "Manual" : @ NO ,
} ,
@ "Evening" : @ {
@ "BrightnessBias" : @ -0.10168700106441975 ,
@ "Colors" : @ [ @ 0 xff362601 , @ 0 xff695518 , @ 0 xff899853 , @ 0 xffa6e4ae , @ 0 xffa9eebb ] ,
@ "DisabledLCDColor" : @ YES ,
@ "HueBias" : @ 0.60027079191058874 ,
@ "HueBiasStrength" : @ 0.33816297305747867 ,
@ "Manual" : @ NO ,
} ,
@ "Fog" : @ {
@ "BrightnessBias" : @ 0.0 ,
@ "Colors" : @ [ @ 0 xff373c34 , @ 0 xff737256 , @ 0 xff9da386 , @ 0 xffc3d2bf , @ 0 xffc7d8c6 ] ,
@ "DisabledLCDColor" : @ YES ,
@ "HueBias" : @ 0.55750435756972117 ,
@ "HueBiasStrength" : @ 0.18424738545816732 ,
@ "Manual" : @ NO ,
} ,
@ "Magic Eggplant" : @ {
@ "BrightnessBias" : @ 0.0 ,
@ "Colors" : @ [ @ 0 xff3c2136 , @ 0 xff942e84 , @ 0 xffc7699d , @ 0 xfff1e4b0 , @ 0 xfff6f9b2 ] ,
@ "DisabledLCDColor" : @ YES ,
@ "HueBias" : @ 0.87717878486055778 ,
@ "HueBiasStrength" : @ 0.65018052788844627 ,
@ "Manual" : @ NO ,
} ,
@ "Radioactive Pea" : @ {
@ "BrightnessBias" : @ -0.48079556772908372 ,
@ "Colors" : @ [ @ 0 xff215200 , @ 0 xff1f7306 , @ 0 xff169e34 , @ 0 xff03ceb8 , @ 0 xff00d4d1 ] ,
@ "DisabledLCDColor" : @ YES ,
@ "HueBias" : @ 0.3795131972111554 ,
@ "HueBiasStrength" : @ 0.34337649402390436 ,
@ "Manual" : @ NO ,
} ,
@ "Seaweed" : @ {
@ "BrightnessBias" : @ -0.28532744023904377 ,
@ "Colors" : @ [ @ 0 xff3f0015 , @ 0 xff426532 , @ 0 xff58a778 , @ 0 xff95e0df , @ 0 xffa0e7ee ] ,
@ "DisabledLCDColor" : @ YES ,
@ "HueBias" : @ 0.2694067480079681 ,
@ "HueBiasStrength" : @ 0.51565612549800799 ,
@ "Manual" : @ NO ,
} ,
@ "Twilight" : @ {
@ "BrightnessBias" : @ -0.091789093625498031 ,
@ "Colors" : @ [ @ 0 xff3f0015 , @ 0 xff461286 , @ 0 xff6254bd , @ 0 xff97d3e9 , @ 0 xffa0e7ee ] ,
@ "DisabledLCDColor" : @ YES ,
@ "HueBias" : @ 0.0 ,
@ "HueBiasStrength" : @ 0.49710532868525897 ,
@ "Manual" : @ NO ,
} ,
} ,
2022-03-14 22:37:05 +00:00
@ "NSToolbarItemForcesStandardSize" : @ YES , // Forces Monterey to resepect toolbar item sizes
2016-04-13 19:43:16 +00:00
} ] ;
2019-10-19 16:26:04 +00:00
[ JOYController startOnRunLoop : [ NSRunLoop currentRunLoop ] withOptions : @ {
JOYAxes2DEmulateButtonsKey : @ YES ,
JOYHatsEmulateButtonsKey : @ YES ,
} ] ;
2020-05-23 11:50:54 +00:00
2020-08-21 21:56:12 +00:00
if ( [ [ NSUserDefaults standardUserDefaults ] boolForKey : @ "GBNotificationsUsed" ] ) {
[ NSUserNotificationCenter defaultUserNotificationCenter ] . delegate = self ;
}
2021-04-25 19:28:24 +00:00
[ self askAutoUpdates ] ;
if ( [ [ NSUserDefaults standardUserDefaults ] boolForKey : @ "GBAutoUpdatesEnabled" ] ) {
[ self checkForUpdates ] ;
}
if ( [ [ NSProcessInfo processInfo ] . arguments containsObject : @ "--update-launch" ] ) {
2021-10-23 10:36:58 +00:00
[ NSApp activateIgnoringOtherApps : true ] ;
2021-04-25 19:28:24 +00:00
}
2016-04-13 19:43:16 +00:00
}
2016-03-30 20:07:55 +00:00
2017-01-24 19:00:56 +00:00
- ( IBAction ) toggleDeveloperMode : ( id ) sender
{
2016-04-08 10:54:34 +00:00
NSUserDefaults * defaults = [ NSUserDefaults standardUserDefaults ] ;
[ defaults setBool : ! [ defaults boolForKey : @ "DeveloperMode" ] forKey : @ "DeveloperMode" ] ;
2016-03-30 20:07:55 +00:00
}
2018-12-01 14:08:59 +00:00
- ( IBAction ) switchPreferencesTab : ( id ) sender
{
for ( NSView * view in preferences_tabs ) {
[ view removeFromSuperview ] ;
}
NSView * tab = preferences_tabs [ [ sender tag ] ] ;
NSRect old = [ _preferencesWindow frame ] ;
NSRect new = [ _preferencesWindow frameRectForContentRect : tab . frame ] ;
new . origin . x = old . origin . x ;
new . origin . y = old . origin . y + ( old . size . height - new . size . height ) ;
2021-10-23 10:36:58 +00:00
[ _preferencesWindow setFrame : new display : true animate : _preferencesWindow . visible ] ;
2018-12-01 14:08:59 +00:00
[ _preferencesWindow . contentView addSubview : tab ] ;
}
2016-04-08 10:54:34 +00:00
- ( BOOL ) validateMenuItem : ( NSMenuItem * ) anItem
{
if ( [ anItem action ] = = @ selector ( toggleDeveloperMode : ) ) {
2018-12-01 14:08:59 +00:00
[ ( NSMenuItem * ) anItem setState : [ [ NSUserDefaults standardUserDefaults ] boolForKey : @ "DeveloperMode" ] ] ;
2016-04-08 10:54:34 +00:00
}
2020-11-13 21:07:35 +00:00
if ( anItem = = self . linkCableMenuItem ) {
return [ [ NSDocumentController sharedDocumentController ] documents ] . count > 1 ;
}
2016-04-13 19:43:16 +00:00
return true ;
2016-03-30 20:07:55 +00:00
}
2020-11-13 21:07:35 +00:00
- ( void ) menuNeedsUpdate : ( NSMenu * ) menu
{
NSMutableArray * items = [ NSMutableArray array ] ;
NSDocument * currentDocument = [ [ NSDocumentController sharedDocumentController ] currentDocument ] ;
for ( NSDocument * document in [ [ NSDocumentController sharedDocumentController ] documents ] ) {
if ( document = = currentDocument ) continue ;
NSMenuItem * item = [ [ NSMenuItem alloc ] initWithTitle : document . displayName action : @ selector ( connectLinkCable : ) keyEquivalent : @ "" ] ;
item . representedObject = document ;
item . image = [ [ NSWorkspace sharedWorkspace ] iconForFile : document . fileURL . path ] ;
[ item . image setSize : NSMakeSize ( 16 , 16 ) ] ;
[ items addObject : item ] ;
}
menu . itemArray = items ;
}
2016-04-13 19:43:16 +00:00
- ( IBAction ) showPreferences : ( id ) sender
{
NSArray * objects ;
if ( ! _preferencesWindow ) {
[ [ NSBundle mainBundle ] loadNibNamed : @ "Preferences" owner : self topLevelObjects : & objects ] ;
2018-12-01 14:08:59 +00:00
NSToolbarItem * first_toolbar _item = [ _preferencesWindow . toolbar . items firstObject ] ;
_preferencesWindow . toolbar . selectedItemIdentifier = [ first_toolbar _item itemIdentifier ] ;
2021-04-25 19:28:24 +00:00
preferences_tabs = @ [ self . emulationTab , self . graphicsTab , self . audioTab , self . controlsTab , self . updatesTab ] ;
2018-12-01 14:08:59 +00:00
[ self switchPreferencesTab : first_toolbar _item ] ;
[ _preferencesWindow center ] ;
2021-04-25 19:28:24 +00:00
# ifndef UPDATE_SUPPORT
[ _preferencesWindow . toolbar removeItemAtIndex : 4 ] ;
# endif
2022-03-12 23:14:29 +00:00
if ( @ available ( macOS 11.0 , * ) ) {
[ _preferencesWindow . toolbar insertItemWithItemIdentifier : NSToolbarFlexibleSpaceItemIdentifier atIndex : 0 ] ;
[ _preferencesWindow . toolbar insertItemWithItemIdentifier : NSToolbarFlexibleSpaceItemIdentifier atIndex : _preferencesWindow . toolbar . items . count ] ;
}
2016-04-13 19:43:16 +00:00
}
[ _preferencesWindow makeKeyAndOrderFront : self ] ;
}
2016-09-10 16:46:42 +00:00
2016-10-01 21:10:09 +00:00
- ( BOOL ) applicationOpenUntitledFile : ( NSApplication * ) sender
2016-09-10 16:46:42 +00:00
{
2021-04-25 19:28:24 +00:00
[ self askAutoUpdates ] ;
2021-02-25 15:12:01 +00:00
/ * Bring an existing panel to the foreground * /
for ( NSWindow * window in [ [ NSApplication sharedApplication ] windows ] ) {
if ( [ window isKindOfClass : [ NSOpenPanel class ] ] ) {
[ ( NSOpenPanel * ) window makeKeyAndOrderFront : nil ] ;
return true ;
}
}
2016-10-01 21:10:09 +00:00
[ [ NSDocumentController sharedDocumentController ] openDocument : self ] ;
2021-02-25 15:12:01 +00:00
return true ;
2016-09-10 16:46:42 +00:00
}
2016-10-01 21:10:09 +00:00
2020-05-23 11:50:54 +00:00
- ( void ) userNotificationCenter : ( NSUserNotificationCenter * ) center didActivateNotification : ( NSUserNotification * ) notification
{
2021-10-23 10:36:58 +00:00
[ [ NSDocumentController sharedDocumentController ] openDocumentWithContentsOfFile : notification . identifier display : true ] ;
2020-05-23 11:50:54 +00:00
}
2020-11-13 21:07:35 +00:00
2021-04-25 19:28:24 +00:00
- ( void ) updateFound
{
[ [ [ NSURLSession sharedSession ] dataTaskWithURL : [ NSURL URLWithString : @ UPDATE_SERVER "/raw_changes" ] completionHandler : ^ ( NSData * data , NSURLResponse * response , NSError * error ) {
NSColor * linkColor = [ NSColor colorWithRed : 0.125 green : 0.325 blue : 1.0 alpha : 1.0 ] ;
if ( @ available ( macOS 10.10 , * ) ) {
linkColor = [ NSColor linkColor ] ;
}
NSString * changes = [ [ NSString alloc ] initWithData : data encoding : NSUTF8StringEncoding ] ;
2021-05-30 17:55:04 +00:00
NSRange cutoffRange = [ changes rangeOfString : @ "<!--(" GB_VERSION ")-->" ] ;
2021-04-25 19:28:24 +00:00
if ( cutoffRange . location ! = NSNotFound ) {
changes = [ changes substringToIndex : cutoffRange . location ] ;
}
NSString * html = [ NSString stringWithFormat : @ "<!DOCTYPE html><html><head><title></title>"
"<style>html {background-color:transparent; color: #%06x; line-height:1.5} a:link, a:visited{color:#%06x; text-decoration:none}</style>"
"</head><body>%@</body></html>" ,
color_to _int ( [ NSColor textColor ] ) ,
color_to _int ( linkColor ) ,
changes ] ;
if ( [ ( NSHTTPURLResponse * ) response statusCode ] = = 200 ) {
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
NSArray * objects ;
[ [ NSBundle mainBundle ] loadNibNamed : @ "UpdateWindow" owner : self topLevelObjects : & objects ] ;
self . updateChanges . preferences . standardFontFamily = [ NSFont systemFontOfSize : 0 ] . familyName ;
self . updateChanges . preferences . fixedFontFamily = @ "Menlo" ;
self . updateChanges . drawsBackground = false ;
[ self . updateChanges . mainFrame loadHTMLString : html baseURL : nil ] ;
} ) ;
}
} ] resume ] ;
}
- ( NSArray * ) webView : ( WebView * ) sender contextMenuItemsForElement : ( NSDictionary * ) element defaultMenuItems : ( NSArray * ) defaultMenuItems
{
// Disable reload context menu
if ( [ defaultMenuItems count ] <= 2 ) {
return nil ;
}
return defaultMenuItems ;
}
- ( void ) webView : ( WebView * ) sender didFinishLoadForFrame : ( WebFrame * ) frame
{
static dispatch_once _t onceToken ;
dispatch_once ( & onceToken , ^ {
sender . mainFrame . frameView . documentView . enclosingScrollView . drawsBackground = true ;
sender . mainFrame . frameView . documentView . enclosingScrollView . backgroundColor = [ NSColor textBackgroundColor ] ;
sender . policyDelegate = self ;
[ self . updateWindow center ] ;
[ self . updateWindow makeKeyAndOrderFront : nil ] ;
} ) ;
}
- ( void ) webView : ( WebView * ) webView decidePolicyForNavigationAction : ( NSDictionary * ) actionInformation request : ( NSURLRequest * ) request frame : ( WebFrame * ) frame decisionListener : ( id < WebPolicyDecisionListener > ) listener
{
[ listener ignore ] ;
[ [ NSWorkspace sharedWorkspace ] openURL : [ request URL ] ] ;
}
- ( void ) checkForUpdates
{
# ifdef UPDATE_SUPPORT
[ [ [ NSURLSession sharedSession ] dataTaskWithURL : [ NSURL URLWithString : @ UPDATE_SERVER "/latest_version" ] completionHandler : ^ ( NSData * data , NSURLResponse * response , NSError * error ) {
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ self . updatesSpinner stopAnimation : nil ] ;
2021-10-23 10:36:58 +00:00
[ self . updatesButton setEnabled : true ] ;
2021-04-25 19:28:24 +00:00
} ) ;
if ( [ ( NSHTTPURLResponse * ) response statusCode ] = = 200 ) {
NSString * string = [ [ NSString alloc ] initWithData : data encoding : NSUTF8StringEncoding ] ;
NSArray < NSString * > * components = [ string componentsSeparatedByString : @ "|" ] ;
if ( components . count ! = 2 ) return ;
_lastVersion = components [ 0 ] ;
_updateURL = components [ 1 ] ;
2021-05-30 17:55:04 +00:00
if ( ! [ @ GB_VERSION isEqualToString : _lastVersion ] &&
2021-04-25 19:28:24 +00:00
! [ [ [ NSUserDefaults standardUserDefaults ] stringForKey : @ "GBSkippedVersion" ] isEqualToString : _lastVersion ] ) {
[ self updateFound ] ;
}
}
} ] resume ] ;
# endif
}
- ( IBAction ) userCheckForUpdates : ( id ) sender
{
if ( self . updateWindow ) {
[ self . updateWindow makeKeyAndOrderFront : sender ] ;
}
else {
[ [ NSUserDefaults standardUserDefaults ] setObject : nil forKey : @ "GBSkippedVersion" ] ;
[ self checkForUpdates ] ;
[ sender setEnabled : false ] ;
[ self . updatesSpinner startAnimation : sender ] ;
}
}
- ( void ) askAutoUpdates
{
# ifdef UPDATE_SUPPORT
if ( ! [ [ NSUserDefaults standardUserDefaults ] boolForKey : @ "GBAskedAutoUpdates" ] ) {
NSAlert * alert = [ [ NSAlert alloc ] init ] ;
alert . messageText = @ "Should SameBoy check for updates when launched?" ;
alert . informativeText = @ "SameBoy is frequently updated with new features, accuracy improvements, and bug fixes. This setting can always be changed in the preferences window." ;
[ alert addButtonWithTitle : @ "Check on Launch" ] ;
[ alert addButtonWithTitle : @ "Don't Check on Launch" ] ;
[ [ NSUserDefaults standardUserDefaults ] setBool : [ alert runModal ] = = NSAlertFirstButtonReturn forKey : @ "GBAutoUpdatesEnabled" ] ;
[ [ NSUserDefaults standardUserDefaults ] setBool : true forKey : @ "GBAskedAutoUpdates" ] ;
}
# endif
}
- ( IBAction ) skipVersion : ( id ) sender
{
[ [ NSUserDefaults standardUserDefaults ] setObject : _lastVersion forKey : @ "GBSkippedVersion" ] ;
[ self . updateWindow performClose : sender ] ;
}
2022-07-03 19:23:35 +00:00
- ( bool ) executePath : ( NSString * ) path withArguments : ( NSArray < NSString * > * ) arguments
{
if ( ! _auth ) {
NSTask * task = [ [ NSTask alloc ] init ] ;
task . launchPath = path ;
task . arguments = arguments ;
[ task launch ] ;
[ task waitUntilExit ] ;
return task . terminationStatus = = 0 && task . terminationReason = = NSTaskTerminationReasonExit ;
}
char * argv [ arguments . count + 1 ] ;
argv [ arguments . count ] = NULL ;
for ( unsigned i = 0 ; i < arguments . count ; i + + ) {
argv [ i ] = ( char * ) arguments [ i ] . UTF8String ;
}
return AuthorizationExecuteWithPrivileges ( _auth , path . UTF8String , kAuthorizationFlagDefaults , argv , NULL ) = = errAuthorizationSuccess ;
}
- ( void ) deauthorize
{
if ( _auth ) {
AuthorizationFree ( _auth , kAuthorizationFlagDefaults ) ;
_auth = nil ;
}
}
2021-04-25 19:28:24 +00:00
- ( IBAction ) installUpdate : ( id ) sender
{
2022-07-03 19:23:35 +00:00
bool needsAuthorization = false ;
if ( [ self executePath : @ "/usr/sbin/spctl" withArguments : @ [ @ "--status" ] ] ) { // Succeeds when GateKeeper is on
// GateKeeper is on , we need to - - add ourselves as root , else we might get a GateKeeper crash
needsAuthorization = true ;
}
else if ( access ( _dyld _get _image _name ( 0 ) , W_OK ) ) {
// We don ' t have write access , so we need authorization to update as root
needsAuthorization = true ;
}
if ( needsAuthorization && ! _auth ) {
AuthorizationCreate ( NULL , kAuthorizationEmptyEnvironment , kAuthorizationFlagPreAuthorize | kAuthorizationFlagInteractionAllowed , & _auth ) ;
// Make sure we can modify the bundle
if ( ! [ self executePath : @ "/usr/sbin/chown" withArguments : @ [ @ "-R" , [ NSString stringWithFormat : @ "%d:%d" , getuid ( ) , getgid ( ) ] , [ NSBundle mainBundle ] . bundlePath ] ] ) {
[ self deauthorize ] ;
return ;
}
}
2021-04-25 19:28:24 +00:00
[ self . updateProgressSpinner startAnimation : nil ] ;
self . updateProgressButton . title = @ "Cancel" ;
self . updateProgressButton . enabled = true ;
self . updateProgressLabel . stringValue = @ "Downloading update..." ;
_updateState = UPDATE_DOWNLOADING ;
_updateTask = [ [ NSURLSession sharedSession ] downloadTaskWithURL : [ NSURL URLWithString : _updateURL ] completionHandler : ^ ( NSURL * location , NSURLResponse * response , NSError * error ) {
_updateTask = nil ;
dispatch_sync ( dispatch_get _main _queue ( ) , ^ {
self . updateProgressButton . enabled = false ;
self . updateProgressLabel . stringValue = @ "Extracting update..." ;
_updateState = UPDATE_EXTRACTING ;
} ) ;
_downloadDirectory = [ [ [ NSFileManager defaultManager ] URLForDirectory : NSItemReplacementDirectory
inDomain : NSUserDomainMask
appropriateForURL : [ [ NSBundle mainBundle ] bundleURL ]
2021-10-23 10:36:58 +00:00
create : true
2021-04-25 19:28:24 +00:00
error : nil ] path ] ;
if ( ! _downloadDirectory ) {
2022-07-03 19:23:35 +00:00
[ self deauthorize ] ;
2021-04-25 19:28:24 +00:00
dispatch_sync ( dispatch_get _main _queue ( ) , ^ {
self . updateProgressButton . enabled = false ;
self . updateProgressLabel . stringValue = @ "Failed to extract update." ;
_updateState = UPDATE_FAILED ;
self . updateProgressButton . title = @ "Close" ;
self . updateProgressButton . enabled = true ;
[ self . updateProgressSpinner stopAnimation : nil ] ;
} ) ;
2022-01-03 14:51:24 +00:00
return ;
2021-04-25 19:28:24 +00:00
}
2022-07-03 19:23:35 +00:00
NSTask * unzipTask ;
2021-04-25 19:28:24 +00:00
unzipTask = [ [ NSTask alloc ] init ] ;
unzipTask . launchPath = @ "/usr/bin/unzip" ;
unzipTask . arguments = @ [ location . path , @ "-d" , _downloadDirectory ] ;
[ unzipTask launch ] ;
[ unzipTask waitUntilExit ] ;
if ( unzipTask . terminationStatus ! = 0 || unzipTask . terminationReason ! = NSTaskTerminationReasonExit ) {
2022-07-03 19:23:35 +00:00
[ self deauthorize ] ;
2021-04-25 19:28:24 +00:00
[ [ NSFileManager defaultManager ] removeItemAtPath : _downloadDirectory error : nil ] ;
dispatch_sync ( dispatch_get _main _queue ( ) , ^ {
self . updateProgressButton . enabled = false ;
self . updateProgressLabel . stringValue = @ "Failed to extract update." ;
_updateState = UPDATE_FAILED ;
self . updateProgressButton . title = @ "Close" ;
self . updateProgressButton . enabled = true ;
[ self . updateProgressSpinner stopAnimation : nil ] ;
} ) ;
return ;
}
dispatch_sync ( dispatch_get _main _queue ( ) , ^ {
self . updateProgressButton . enabled = false ;
self . updateProgressLabel . stringValue = @ "Update ready, save your game progress and click Install." ;
_updateState = UPDATE_WAIT _INSTALL ;
self . updateProgressButton . title = @ "Install" ;
self . updateProgressButton . enabled = true ;
[ self . updateProgressSpinner stopAnimation : nil ] ;
} ) ;
} ] ;
[ _updateTask resume ] ;
self . updateProgressWindow . preventsApplicationTerminationWhenModal = false ;
[ self . updateWindow beginSheet : self . updateProgressWindow completionHandler : ^ ( NSModalResponse returnCode ) {
[ self . updateWindow close ] ;
} ] ;
}
- ( void ) performUpgrade
{
self . updateProgressButton . enabled = false ;
self . updateProgressLabel . stringValue = @ "Instaling update..." ;
_updateState = UPDATE_INSTALLING ;
self . updateProgressButton . enabled = false ;
[ self . updateProgressSpinner startAnimation : nil ] ;
dispatch_async ( dispatch_get _global _queue ( DISPATCH_QUEUE _PRIORITY _DEFAULT , 0 ) , ^ {
NSString * executablePath = [ [ NSBundle mainBundle ] executablePath ] ;
NSString * contentsPath = [ [ [ NSBundle mainBundle ] bundlePath ] stringByAppendingPathComponent : @ "Contents" ] ;
NSString * contentsTempPath = [ [ [ NSBundle mainBundle ] bundlePath ] stringByAppendingPathComponent : @ "TempContents" ] ;
NSString * updateContentsPath = [ _downloadDirectory stringByAppendingPathComponent : @ "SameBoy.app/Contents" ] ;
NSError * error = nil ;
[ [ NSFileManager defaultManager ] moveItemAtPath : contentsPath toPath : contentsTempPath error : & error ] ;
if ( error ) {
2022-07-03 19:23:35 +00:00
[ self deauthorize ] ;
2021-04-25 19:28:24 +00:00
[ [ NSFileManager defaultManager ] removeItemAtPath : _downloadDirectory error : nil ] ;
_downloadDirectory = nil ;
dispatch_sync ( dispatch_get _main _queue ( ) , ^ {
self . updateProgressButton . enabled = false ;
self . updateProgressLabel . stringValue = @ "Failed to install update." ;
_updateState = UPDATE_FAILED ;
self . updateProgressButton . title = @ "Close" ;
self . updateProgressButton . enabled = true ;
[ self . updateProgressSpinner stopAnimation : nil ] ;
} ) ;
return ;
}
[ [ NSFileManager defaultManager ] moveItemAtPath : updateContentsPath toPath : contentsPath error : & error ] ;
if ( error ) {
2022-07-03 19:23:35 +00:00
[ self deauthorize ] ;
2021-04-25 19:28:24 +00:00
[ [ NSFileManager defaultManager ] moveItemAtPath : contentsTempPath toPath : contentsPath error : nil ] ;
[ [ NSFileManager defaultManager ] removeItemAtPath : _downloadDirectory error : nil ] ;
_downloadDirectory = nil ;
dispatch_sync ( dispatch_get _main _queue ( ) , ^ {
self . updateProgressButton . enabled = false ;
self . updateProgressLabel . stringValue = @ "Failed to install update." ;
_updateState = UPDATE_FAILED ;
self . updateProgressButton . title = @ "Close" ;
self . updateProgressButton . enabled = true ;
[ self . updateProgressSpinner stopAnimation : nil ] ;
} ) ;
return ;
}
[ [ NSFileManager defaultManager ] removeItemAtPath : _downloadDirectory error : nil ] ;
[ [ NSFileManager defaultManager ] removeItemAtPath : contentsTempPath error : nil ] ;
_downloadDirectory = nil ;
atexit_b ( ^ {
execl ( executablePath . UTF8String , executablePath . UTF8String , "--update-launch" , NULL ) ;
} ) ;
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ NSApp terminate : nil ] ;
} ) ;
} ) ;
}
- ( IBAction ) updateAction : ( id ) sender
{
switch ( _updateState ) {
case UPDATE_DOWNLOADING :
[ _updateTask cancelByProducingResumeData : nil ] ;
_updateTask = nil ;
[ self . updateProgressWindow close ] ;
break ;
case UPDATE_WAIT _INSTALL :
[ self performUpgrade ] ;
break ;
case UPDATE_EXTRACTING :
case UPDATE_INSTALLING :
break ;
case UPDATE_FAILED :
[ self . updateProgressWindow close ] ;
break ;
}
}
2022-06-18 19:36:08 +00:00
- ( void ) orderFrontAboutPanel : ( id ) sender
{
// NSAboutPanelOptionApplicationIcon is not available prior to 10.13 , but the key is still there and working .
[ [ NSApplication sharedApplication ] orderFrontStandardAboutPanelWithOptions : @ {
@ "ApplicationIcon" : [ NSImage imageNamed : @ "Icon" ]
} ] ;
}
2021-04-25 19:28:24 +00:00
- ( void ) dealloc
{
if ( _downloadDirectory ) {
[ [ NSFileManager defaultManager ] removeItemAtPath : _downloadDirectory error : nil ] ;
}
}
2020-11-13 21:07:35 +00:00
- ( IBAction ) nop : ( id ) sender
{
}
2016-03-30 20:07:55 +00:00
@ end