2015-10-21 22:13:26 -07:00
/ *
* Apple // emulator for * ix
*
* This software package is subject to the GNU General Public License
* version 3 or later ( your choice ) as published by the Free Software
* Foundation .
*
* Copyright 2014 , 2015 Aaron Culliney
*
* /
2014-10-18 12:02:02 -07:00
# import "EmulatorDiskController.h"
2014-10-19 12:56:30 -07:00
# import "EmulatorPrefsController.h"
# import "common.h"
2014-10-18 12:02:02 -07:00
# define READONLY_CHOICE _INDEX 0
# define NO_DISK _INSERTED @ "(No Disk Inserted)"
# define DSK_PROPERTIES @ ".dsk 143360 bytes"
# define NIB_PROPERTIES @ ".nib 232960 bytes"
# define kApple2DisksURL @ "kApple2DisksURL"
@ interface EmulatorDiskController ( )
@ property ( assign ) IBOutlet NSWindow * disksWindow ;
@ property ( assign ) IBOutlet NSTextField * diskInA ;
@ property ( assign ) IBOutlet NSTextField * diskInB ;
@ property ( assign ) IBOutlet NSTextField * diskAProperties ;
@ property ( assign ) IBOutlet NSTextField * diskBProperties ;
@ property ( assign ) IBOutlet NSMatrix * diskAProtection ;
@ property ( assign ) IBOutlet NSMatrix * diskBProtection ;
@ property ( assign ) IBOutlet NSButton * chooseDiskA ;
@ property ( assign ) IBOutlet NSButton * chooseDiskB ;
2014-10-25 13:52:46 -07:00
@ property ( assign ) IBOutlet NSButton * startupLoadDiskA ;
@ property ( assign ) IBOutlet NSButton * startupLoadDiskB ;
2018-11-04 14:07:30 -08:00
@ property ( assign ) IBOutlet NSButton * okButton ;
- ( void ) loadPrefsForDomain : ( const char * ) domain ;
2014-10-18 12:02:02 -07:00
@ end
2018-11-04 14:07:30 -08:00
static EmulatorDiskController * diskInstance = nil ;
static void prefsChangeCallback ( const char * domain )
{
( void ) domain ;
assert ( diskInstance ) ;
[ diskInstance loadPrefsForDomain : domain ] ;
}
2014-10-18 12:02:02 -07:00
@ implementation EmulatorDiskController
- ( void ) awakeFromNib
{
2014-11-29 13:31:21 -08:00
# if CRASH_APP _ON _LOAD _BECAUSE _YAY _GJ _APPLE
glGetError ( ) ;
# endif
2018-11-04 14:07:30 -08:00
assert ( ! diskInstance ) ;
diskInstance = self ;
prefs_registerListener ( PREF_DOMAIN _VM , prefsChangeCallback ) ;
2014-10-18 12:02:02 -07:00
[ self . diskInA setStringValue : NO_DISK _INSERTED ] ;
[ self . diskAProperties setStringValue : @ "" ] ;
[ self . diskInB setStringValue : NO_DISK _INSERTED ] ;
[ self . diskBProperties setStringValue : @ "" ] ;
[ self . chooseDiskA setKeyEquivalent : @ "\r" ] ;
[ self . chooseDiskA setBezelStyle : NSRoundedBezelStyle ] ;
2014-10-25 13:52:46 -07:00
[ self . startupLoadDiskA setState : NSOffState ] ;
[ self . startupLoadDiskB setState : NSOffState ] ;
2018-11-04 14:07:30 -08:00
}
- ( void ) loadPrefsForDomain : ( const char * ) domain
{
assert ( strcmp ( domain , PREF_DOMAIN _VM ) = = 0 ) ;
( void ) domain ;
2014-10-19 12:56:30 -07:00
{
2018-11-04 14:07:30 -08:00
NSString * startupDiskA = nil ;
char * pathA = NULL ;
startupDiskA = prefs_copyStringValue ( PREF_DOMAIN _VM , PREF_DISK _PATH _A , & pathA ) ? [ NSString stringWithUTF8String : pathA ] : nil ;
FREE ( pathA ) ;
bool bVal = false ;
BOOL readOnlyA = prefs_parseBoolValue ( PREF_DOMAIN _VM , PREF_DISK _PATH _A _RO , & bVal ) ? bVal : true ;
2018-11-22 10:14:46 -08:00
if ( startupDiskA && [ startupDiskA length ] )
2014-10-19 12:56:30 -07:00
{
2017-09-13 20:27:42 -07:00
const char * path = [ startupDiskA UTF8String ] ;
int fdA = -1 ;
TEMP_FAILURE _RETRY ( fdA = open ( path , readOnlyA ? O_RDONLY : O_RDWR ) ) ;
2019-06-28 20:17:37 -07:00
if ( fdA = = -1 ) {
LOG ( "OOPS, open failed for path %s (%s)" , path , strerror ( errno ) ) ;
}
2017-09-13 20:27:42 -07:00
const char * err = disk6_insert ( fdA , 0 , path , readOnlyA ) ;
if ( fdA >= 0 ) {
TEMP_FAILURE _RETRY ( close ( fdA ) ) ;
}
if ( ! err )
{
[ self . diskInA setStringValue : [ [ startupDiskA pathComponents ] lastObject ] ] ;
[ self . startupLoadDiskA setState : NSOnState ] ;
[ self . diskAProtection setState : ( readOnlyA ? NSOnState : NSOffState ) atRow : 0 column : 0 ] ;
[ self . diskAProtection setState : ( ! readOnlyA ? NSOnState : NSOffState ) atRow : 0 column : 1 ] ;
}
2014-10-19 12:56:30 -07:00
}
}
{
2018-11-04 14:07:30 -08:00
NSString * startupDiskB = nil ;
char * pathB = NULL ;
startupDiskB = prefs_copyStringValue ( PREF_DOMAIN _VM , PREF_DISK _PATH _B , & pathB ) ? [ NSString stringWithUTF8String : pathB ] : nil ;
FREE ( pathB ) ;
bool bVal = false ;
BOOL readOnlyB = prefs_parseBoolValue ( PREF_DOMAIN _VM , PREF_DISK _PATH _B _RO , & bVal ) ? bVal : true ;
2018-11-22 10:14:46 -08:00
if ( startupDiskB && [ startupDiskB length ] )
2014-10-19 12:56:30 -07:00
{
2017-09-13 20:27:42 -07:00
const char * path = [ startupDiskB UTF8String ] ;
int fdB = -1 ;
TEMP_FAILURE _RETRY ( fdB = open ( path , readOnlyB ? O_RDONLY : O_RDWR ) ) ;
2019-06-28 20:17:37 -07:00
if ( fdB = = -1 ) {
LOG ( "OOPS, open failed for path %s (%s)" , path , strerror ( errno ) ) ;
}
2017-09-13 20:27:42 -07:00
const char * err = disk6_insert ( fdB , 1 , path , readOnlyB ) ;
if ( fdB >= 0 ) {
TEMP_FAILURE _RETRY ( close ( fdB ) ) ;
}
if ( ! err )
{
[ self . diskInB setStringValue : [ [ startupDiskB pathComponents ] lastObject ] ] ;
[ self . startupLoadDiskB setState : NSOnState ] ;
[ self . diskBProtection setState : ( readOnlyB ? NSOnState : NSOffState ) atRow : 0 column : 0 ] ;
[ self . diskBProtection setState : ( ! readOnlyB ? NSOnState : NSOffState ) atRow : 0 column : 1 ] ;
}
2014-10-19 12:56:30 -07:00
}
}
2014-10-18 12:02:02 -07:00
}
2014-10-25 13:52:46 -07:00
- ( void ) _savePrefs
2014-10-18 12:02:02 -07:00
{
2018-11-04 14:07:30 -08:00
if ( ( [ self . startupLoadDiskA state ] = = NSOnState ) && ( disk6 . disk [ 0 ] . fd >= 0 ) )
2014-10-25 13:52:46 -07:00
{
2018-11-04 14:07:30 -08:00
prefs_setStringValue ( PREF_DOMAIN _VM , PREF_DISK _PATH _A , disk6 . disk [ 0 ] . file_name ) ;
NSButtonCell * readOnlyChoice = ( NSButtonCell * ) [ [ [ self diskAProtection ] cells ] firstObject ] ;
prefs_setBoolValue ( PREF_DOMAIN _VM , PREF_DISK _PATH _A _RO , ( [ readOnlyChoice state ] = = NSOnState ) ) ;
}
else
{
prefs_setStringValue ( PREF_DOMAIN _VM , PREF_DISK _PATH _A , "" ) ;
prefs_setBoolValue ( PREF_DOMAIN _VM , PREF_DISK _PATH _A _RO , true ) ;
2014-10-25 13:52:46 -07:00
}
2018-11-04 14:07:30 -08:00
if ( ( [ self . startupLoadDiskB state ] = = NSOnState ) && ( disk6 . disk [ 1 ] . fd >= 0 ) )
2014-10-25 13:52:46 -07:00
{
2018-11-04 14:07:30 -08:00
prefs_setStringValue ( PREF_DOMAIN _VM , PREF_DISK _PATH _B , disk6 . disk [ 1 ] . file_name ) ;
NSButtonCell * readOnlyChoice = ( NSButtonCell * ) [ [ [ self diskBProtection ] cells ] firstObject ] ;
prefs_setBoolValue ( PREF_DOMAIN _VM , PREF_DISK _PATH _B _RO , ( [ readOnlyChoice state ] = = NSOnState ) ) ;
}
else
{
prefs_setStringValue ( PREF_DOMAIN _VM , PREF_DISK _PATH _B , "" ) ;
prefs_setBoolValue ( PREF_DOMAIN _VM , PREF_DISK _PATH _B _RO , true ) ;
2014-10-25 13:52:46 -07:00
}
2018-11-04 14:07:30 -08:00
2018-11-22 10:14:46 -08:00
// prefs_sync ( PREF_DOMAIN _VM ) ; - - do not sync here since that will trigger reload of startup disks
2018-11-04 14:07:30 -08:00
prefs_save ( ) ;
2014-10-18 12:02:02 -07:00
}
2014-10-25 13:52:46 -07:00
- ( void ) _protectionChangedForDrive : ( int ) drive
2014-10-18 12:02:02 -07:00
{
2015-11-13 23:13:21 -08:00
if ( disk6 . disk [ drive ] . fd < 0 )
2014-10-18 12:02:02 -07:00
{
return ;
}
// HACK NOTE : dispatch so that state of outlet property is set properly
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
2015-11-13 23:13:21 -08:00
NSButtonCell * readOnlyChoice = ( NSButtonCell * ) [ [ ( drive = = 0 ? [ self diskAProtection ] : [ self diskBProtection ] ) cells ] firstObject ] ;
2014-10-25 13:52:46 -07:00
NSString * path = [ NSString stringWithUTF8String : disk6 . disk [ drive ] . file_name ] ;
[ self _insertDisketteInDrive : drive path : path type : [ EmulatorDiskController extensionForPath : path ] readOnly : ( [ readOnlyChoice state ] = = NSOnState ) ] ;
[ self _savePrefs ] ;
2014-10-18 12:02:02 -07:00
} ) ;
}
2014-10-25 13:52:46 -07:00
- ( IBAction ) diskAProtectionChanged : ( id ) sender
{
[ self _protectionChangedForDrive : 0 ] ;
}
2014-10-18 12:02:02 -07:00
- ( IBAction ) diskBProtectionChanged : ( id ) sender
{
2014-10-25 13:52:46 -07:00
[ self _protectionChangedForDrive : 1 ] ;
2014-10-18 12:02:02 -07:00
}
- ( BOOL ) _insertDisketteInDrive : ( int ) drive path : ( NSString * ) path type : ( NSString * ) type readOnly : ( BOOL ) readOnly
{
2015-11-13 23:13:21 -08:00
disk6_eject ( drive ) ;
2014-10-18 12:02:02 -07:00
2017-09-13 20:27:42 -07:00
int fd = -1 ;
2019-06-28 20:17:37 -07:00
const char * cPath = [ path UTF8String ] ;
TEMP_FAILURE _RETRY ( fd = open ( cPath , readOnly ? O_RDONLY : O_RDWR ) ) ;
if ( fd = = -1 ) {
LOG ( "OOPS, open failed for path %s (%s)" , cPath , strerror ( errno ) ) ;
}
const char * errMsg = disk6_insert ( fd , drive , cPath , readOnly ) ;
2017-09-13 20:27:42 -07:00
if ( fd >= 0 ) {
TEMP_FAILURE _RETRY ( close ( fd ) ) ;
}
2014-10-18 12:02:02 -07:00
if ( errMsg )
{
2017-09-13 20:27:42 -07:00
NSAlert * alert = [ NSAlert alertWithError : [ NSError errorWithDomain : [ NSString stringWithUTF8String : errMsg ] code : -1 userInfo : nil ] ] ;
[ alert beginSheetModalForWindow : [ self disksWindow ] completionHandler : nil ] ;
if ( ! drive )
2014-10-18 12:02:02 -07:00
{
2017-09-13 20:27:42 -07:00
[ [ self diskInA ] setStringValue : NO_DISK _INSERTED ] ;
[ [ self diskAProperties ] setStringValue : @ "" ] ;
}
else
{
[ [ self diskInB ] setStringValue : NO_DISK _INSERTED ] ;
[ [ self diskBProperties ] setStringValue : @ "" ] ;
2014-10-18 12:02:02 -07:00
}
2017-09-13 20:27:42 -07:00
return NO ;
2014-10-18 12:02:02 -07:00
}
2014-10-25 13:52:46 -07:00
path = [ NSString stringWithUTF8String : disk6 . disk [ drive ] . file_name ] ;
2014-10-18 12:02:02 -07:00
NSString * imageName = [ [ path pathComponents ] lastObject ] ;
2018-11-04 14:07:30 -08:00
2014-10-25 13:52:46 -07:00
if ( drive = = 0 )
2014-10-18 12:02:02 -07:00
{
[ [ self diskInA ] setStringValue : imageName ] ;
2018-11-04 14:07:30 -08:00
bool isStartupDiskA = false ;
{
char * pathA = NULL ;
if ( prefs_copyStringValue ( PREF_DOMAIN _VM , PREF_DISK _PATH _A , & pathA ) ) {
isStartupDiskA = ( strcmp ( pathA , disk6 . disk [ drive ] . file_name ) = = 0 ) ;
}
FREE ( pathA ) ;
}
if ( isStartupDiskA )
2014-10-25 13:52:46 -07:00
{
[ self . startupLoadDiskA setState : NSOnState ] ;
}
2014-10-18 12:02:02 -07:00
}
else
{
[ [ self diskInB ] setStringValue : imageName ] ;
2018-11-04 14:07:30 -08:00
bool isStartupDiskB = false ;
{
char * pathB = NULL ;
if ( prefs_copyStringValue ( PREF_DOMAIN _VM , PREF_DISK _PATH _B , & pathB ) ) {
isStartupDiskB = ( strcmp ( pathB , disk6 . disk [ drive ] . file_name ) = = 0 ) ;
}
FREE ( pathB ) ;
}
if ( isStartupDiskB )
2014-10-25 13:52:46 -07:00
{
[ self . startupLoadDiskB setState : NSOnState ] ;
}
2014-10-18 12:02:02 -07:00
}
if ( [ type isEqualToString : @ "dsk" ] || [ type isEqualToString : @ "do" ] || [ type isEqualToString : @ "po" ] )
{
if ( ! drive )
{
[ [ self diskAProperties ] setStringValue : DSK_PROPERTIES ] ;
}
else
{
[ [ self diskBProperties ] setStringValue : DSK_PROPERTIES ] ;
}
}
else
{
if ( ! drive )
{
[ [ self diskAProperties ] setStringValue : NIB_PROPERTIES ] ;
}
else
{
[ [ self diskBProperties ] setStringValue : NIB_PROPERTIES ] ;
}
}
return YES ;
}
2014-10-19 12:56:30 -07:00
+ ( NSSet * ) emulatorFileTypes
{
static NSSet * set = nil ;
static dispatch_once _t onceToken = 0 L ;
dispatch_once ( & onceToken , ^ {
2014-10-25 13:52:46 -07:00
set = [ [ NSSet alloc ] initWithObjects : @ "dsk" , @ "nib" , @ "do" , @ "po" , @ "gz" , nil ] ;
2014-10-19 12:56:30 -07:00
} ) ;
return [ [ set retain ] autorelease ] ;
}
+ ( NSString * ) extensionForPath : ( NSString * ) path
2014-10-18 12:02:02 -07:00
{
NSString * extension0 = [ path pathExtension ] ;
NSString * extension1 = [ [ path stringByDeletingPathExtension ] pathExtension ] ;
2014-10-25 13:52:46 -07:00
if ( [ extension0 isEqualToString : @ "gz" ] )
2014-10-18 12:02:02 -07:00
{
extension0 = extension1 ;
}
2018-11-11 07:30:35 -08:00
return [ extension0 lowercaseString ] ;
2014-10-18 12:02:02 -07:00
}
2014-10-19 12:56:30 -07:00
+ ( void ) chooseDiskForWindow : ( NSWindow * ) window completionHandler : ( DiskCompletionHandler ) handler
2014-10-18 12:02:02 -07:00
{
2014-10-19 12:56:30 -07:00
NSOpenPanel * openPanel = [ [ NSOpenPanel openPanel ] retain ] ;
2014-10-18 12:02:02 -07:00
[ openPanel setTitle : @ "Choose a disk image" ] ;
NSURL * url = [ [ NSUserDefaults standardUserDefaults ] URLForKey : kApple2DisksURL ] ;
if ( ! url )
{
2014-12-06 17:07:58 -08:00
url = [ [ NSBundle mainBundle ] URLForResource : @ "images" withExtension : nil ] ;
2014-10-18 12:02:02 -07:00
}
[ openPanel setDirectoryURL : url ] ;
[ openPanel setShowsResizeIndicator : YES ] ;
[ openPanel setShowsHiddenFiles : NO ] ;
[ openPanel setCanChooseFiles : YES ] ;
[ openPanel setCanChooseDirectories : NO ] ;
[ openPanel setCanCreateDirectories : NO ] ;
[ openPanel setAllowsMultipleSelection : NO ] ;
// NOTE : Doesn ' t appear to be a way to specify ".dsk.gz" . . . so we may inadvertently allow files of ".foo.gz" here
2014-10-19 12:56:30 -07:00
NSSet * fileTypes = [ EmulatorDiskController emulatorFileTypes ] ;
2014-10-18 12:02:02 -07:00
[ openPanel setAllowedFileTypes : [ fileTypes allObjects ] ] ;
2014-10-19 12:56:30 -07:00
handler = Block_copy ( handler ) ;
[ openPanel beginSheetModalForWindow : window completionHandler : ^ ( NSInteger result ) {
handler ( openPanel , result ) ;
Block_release ( handler ) ;
[ openPanel autorelease ] ;
} ] ;
}
- ( void ) _chooseDisk : ( int ) drive readOnly : ( BOOL ) readOnly
{
[ EmulatorDiskController chooseDiskForWindow : [ self disksWindow ] completionHandler : ^ ( NSOpenPanel * openPanel , NSInteger result ) {
2014-10-18 12:02:02 -07:00
if ( result = = NSOKButton )
{
NSURL * selection = [ [ openPanel URLs ] firstObject ] ;
NSString * path = [ [ selection path ] stringByResolvingSymlinksInPath ] ;
2014-10-19 12:56:30 -07:00
NSString * extension = [ EmulatorDiskController extensionForPath : path ] ;
NSSet * fileTypes = [ EmulatorDiskController emulatorFileTypes ] ;
NSString * directory = [ path stringByDeletingLastPathComponent ] ;
NSUserDefaults * defaults = [ NSUserDefaults standardUserDefaults ] ;
[ defaults setURL : [ NSURL URLWithString : directory ] forKey : kApple2DisksURL ] ;
2014-10-18 12:02:02 -07:00
if ( ! [ fileTypes containsObject : extension ] )
{
NSAlert * alert = [ NSAlert alertWithError : [ NSError errorWithDomain : @ "Disk image must have file extension of .dsk, .do, .po, or .nib only" code : -1 userInfo : nil ] ] ;
[ alert beginSheetModalForWindow : [ self disksWindow ] completionHandler : nil ] ;
return ;
}
2014-10-19 12:56:30 -07:00
2018-11-04 14:07:30 -08:00
[ self . chooseDiskA setKeyEquivalent : @ "" ] ;
[ self . okButton setKeyEquivalent : @ "\r" ] ;
2014-10-25 13:52:46 -07:00
[ ( drive = = 0 ) ? self . startupLoadDiskA : self . startupLoadDiskB setState : NSOffState ] ;
2014-10-18 12:02:02 -07:00
[ self _insertDisketteInDrive : drive path : path type : extension readOnly : readOnly ] ;
}
} ] ;
}
- ( IBAction ) chooseDriveA : ( id ) sender
{
2015-11-13 23:13:21 -08:00
NSButtonCell * readOnlyChoice = ( NSButtonCell * ) [ [ [ self diskAProtection ] cells ] firstObject ] ;
2014-10-18 12:02:02 -07:00
[ self _chooseDisk : 0 readOnly : ( [ readOnlyChoice state ] = = NSOnState ) ] ;
}
- ( IBAction ) chooseDriveB : ( id ) sender
{
2015-11-13 23:13:21 -08:00
NSButtonCell * readOnlyChoice = ( NSButtonCell * ) [ [ [ self diskBProtection ] cells ] firstObject ] ;
2014-10-18 12:02:02 -07:00
[ self _chooseDisk : 1 readOnly : ( [ readOnlyChoice state ] = = NSOnState ) ] ;
}
2014-10-25 13:52:46 -07:00
- ( IBAction ) startupDiskAChoiceChanged : ( id ) sender
2014-10-19 12:56:30 -07:00
{
2014-10-25 13:52:46 -07:00
[ self _savePrefs ] ;
}
- ( IBAction ) startupDiskBChoiceChanged : ( id ) sender
{
[ self _savePrefs ] ;
}
- ( IBAction ) disksOK : ( id ) sender
{
2018-11-04 14:07:30 -08:00
[ self . chooseDiskA setKeyEquivalent : @ "\r" ] ;
[ self . okButton setKeyEquivalent : @ "" ] ;
2014-10-25 13:52:46 -07:00
[ [ self disksWindow ] close ] ;
2014-10-19 12:56:30 -07:00
}
2014-10-18 12:02:02 -07:00
@ end