2020-08-29 15:42:06 -04:00
|
|
|
|
//
|
|
|
|
|
// LaunchWindowController.m
|
2020-08-29 23:24:49 -04:00
|
|
|
|
// Ample
|
2020-08-29 15:42:06 -04:00
|
|
|
|
//
|
|
|
|
|
// Created by Kelvin Sherlock on 8/29/2020.
|
|
|
|
|
// Copyright © 2020 Kelvin Sherlock. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
2020-09-01 23:52:11 -04:00
|
|
|
|
#import "Ample.h"
|
2020-08-29 15:42:06 -04:00
|
|
|
|
#import "LaunchWindowController.h"
|
|
|
|
|
#import "MediaViewController.h"
|
2021-05-30 18:19:51 -04:00
|
|
|
|
#import "SlotViewController.h"
|
2021-06-08 22:40:20 -04:00
|
|
|
|
#import "NewMachineViewController.h"
|
2020-08-29 18:45:25 -04:00
|
|
|
|
#import "LogWindowController.h"
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
2021-05-27 19:42:28 -04:00
|
|
|
|
#import "AutocompleteControl.h"
|
|
|
|
|
#import "SoftwareList.h"
|
2021-06-07 00:34:26 -04:00
|
|
|
|
#import "BookmarkManager.h"
|
2022-02-23 20:56:21 -05:00
|
|
|
|
#import "Bookmark.h"
|
2021-01-29 20:07:44 -05:00
|
|
|
|
|
|
|
|
|
#include <sys/stat.h>
|
2021-05-27 19:42:28 -04:00
|
|
|
|
#include <wctype.h>
|
2021-01-29 20:07:44 -05:00
|
|
|
|
|
2020-08-29 15:42:06 -04:00
|
|
|
|
static NSString *kMyContext = @"kMyContext";
|
2020-08-29 16:48:14 -04:00
|
|
|
|
static NSString *kContextMachine = @"kContextMachine";
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
|
|
|
|
|
2021-06-07 00:34:26 -04:00
|
|
|
|
@interface LaunchWindowController () {
|
|
|
|
|
BOOL _loadingBookmark;
|
2021-06-09 22:47:00 -04:00
|
|
|
|
NSString *_machine;
|
|
|
|
|
NSDictionary *_machineDescription;
|
2022-03-18 15:23:56 -04:00
|
|
|
|
BookmarkManager *_manager;
|
2021-06-07 00:34:26 -04:00
|
|
|
|
}
|
2020-08-29 15:42:06 -04:00
|
|
|
|
@property (strong) IBOutlet MediaViewController *mediaController;
|
2021-05-30 18:19:51 -04:00
|
|
|
|
@property (strong) IBOutlet SlotViewController *slotController;
|
2021-06-08 22:40:20 -04:00
|
|
|
|
@property (strong) IBOutlet NewMachineViewController *machineViewController;
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
2020-08-29 16:48:14 -04:00
|
|
|
|
@property (weak) IBOutlet NSView *machineView;
|
2020-08-29 15:42:06 -04:00
|
|
|
|
@property (weak) IBOutlet NSView *slotView;
|
|
|
|
|
@property (weak) IBOutlet NSView *mediaView;
|
|
|
|
|
|
|
|
|
|
/* kvo */
|
|
|
|
|
@property NSString *commandLine;
|
|
|
|
|
@property NSArray *args;
|
|
|
|
|
|
2021-06-09 22:47:00 -04:00
|
|
|
|
@property NSString *machine;
|
2021-06-12 14:02:11 -04:00
|
|
|
|
//@property NSString *machineName;
|
|
|
|
|
|
2020-08-29 15:42:06 -04:00
|
|
|
|
@property BOOL mameDebug;
|
|
|
|
|
@property BOOL mameSquarePixels;
|
2020-09-13 12:04:54 -04:00
|
|
|
|
@property BOOL mameMouse;
|
2021-03-12 20:09:59 -05:00
|
|
|
|
@property BOOL mameSamples;
|
2021-06-07 00:34:26 -04:00
|
|
|
|
@property BOOL mameBGFX;
|
2021-07-14 22:38:15 -04:00
|
|
|
|
@property BOOL mameRewind;
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
2020-09-05 11:00:29 -04:00
|
|
|
|
@property BOOL mameAVI;
|
|
|
|
|
@property BOOL mameWAV;
|
|
|
|
|
@property BOOL mameVGM;
|
2021-06-07 00:34:26 -04:00
|
|
|
|
@property BOOL mameBitBanger;
|
|
|
|
|
@property BOOL mameShareDirectory;
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
2020-09-05 11:00:29 -04:00
|
|
|
|
@property NSString *mameAVIPath;
|
|
|
|
|
@property NSString *mameWAVPath;
|
|
|
|
|
@property NSString *mameVGMPath;
|
2021-06-07 00:34:26 -04:00
|
|
|
|
@property NSString *mameShareDirectoryPath;
|
|
|
|
|
@property NSString *mameBitBangerPath;
|
2020-09-05 11:00:29 -04:00
|
|
|
|
|
|
|
|
|
@property NSInteger mameSpeed;
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
2020-09-06 15:26:20 -04:00
|
|
|
|
@property NSInteger mameBackend;
|
|
|
|
|
@property NSInteger mameEffects;
|
|
|
|
|
|
|
|
|
|
|
2020-09-07 22:00:10 -04:00
|
|
|
|
@property NSInteger mameWindowMode;
|
|
|
|
|
|
2021-05-27 19:42:28 -04:00
|
|
|
|
@property (weak) IBOutlet AutocompleteControl *softwareListControl;
|
|
|
|
|
@property SoftwareSet *softwareSet;
|
|
|
|
|
@property Software *software;
|
2021-06-07 00:34:26 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@property (strong) IBOutlet NSWindow *addBookmarkWindow;
|
|
|
|
|
@property (strong) NSString *bookmarkName;
|
2022-02-23 20:56:21 -05:00
|
|
|
|
@property BOOL bookmarkDefault;
|
2021-06-07 00:34:26 -04:00
|
|
|
|
@property (weak) IBOutlet NSTextField *bookmarkTextField;
|
2022-02-23 20:56:21 -05:00
|
|
|
|
@property (weak) IBOutlet NSTextField *bookmarkErrorField;
|
2021-06-18 21:57:46 -04:00
|
|
|
|
|
|
|
|
|
@property BOOL optionKey;
|
|
|
|
|
|
2022-02-23 20:56:21 -05:00
|
|
|
|
|
2020-08-29 15:42:06 -04:00
|
|
|
|
@end
|
|
|
|
|
|
2021-05-27 19:42:28 -04:00
|
|
|
|
@interface LaunchWindowController (SoftwareList)
|
|
|
|
|
|
|
|
|
|
-(void)updateSoftwareList;
|
|
|
|
|
|
|
|
|
|
@end
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
2021-06-07 00:34:26 -04:00
|
|
|
|
|
|
|
|
|
@interface LaunchWindowController (Bookmark)
|
|
|
|
|
|
|
|
|
|
-(IBAction)addBookmark:(id)sender;
|
|
|
|
|
|
2021-06-12 14:02:11 -04:00
|
|
|
|
-(IBAction)defaultLoad:(id)sender;
|
|
|
|
|
|
2022-02-23 20:56:21 -05:00
|
|
|
|
-(void)bookmarkNotification: (NSNotification *)notification;
|
|
|
|
|
|
2021-06-07 00:34:26 -04:00
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
#define SIZEOF(x) (sizeof(x) / sizeof(x[0]))
|
|
|
|
|
static NSString *BackendStrings[] = {
|
|
|
|
|
@"",
|
|
|
|
|
@"metal",
|
|
|
|
|
@"opengl",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static NSString *EffectsStrings[] = {
|
|
|
|
|
@"-",
|
|
|
|
|
@"unfiltered",
|
|
|
|
|
@"hlsl",
|
|
|
|
|
@"crt-geom",
|
|
|
|
|
@"crt-geom-deluxe",
|
|
|
|
|
@"lcd-grid",
|
2023-03-01 10:56:45 -05:00
|
|
|
|
@"Fighters",
|
2021-06-07 00:34:26 -04:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int BackEndIndex(NSString *str) {
|
|
|
|
|
if (!str) return -1;
|
|
|
|
|
for (int i = 1; i < SIZEOF(BackendStrings); ++i) {
|
|
|
|
|
if ([str isEqualToString: BackendStrings[i]]) return i;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int EffectsIndex(NSString *str) {
|
|
|
|
|
if (!str) return -1;
|
|
|
|
|
for (int i = 1; i < SIZEOF(EffectsStrings); ++i) {
|
|
|
|
|
if ([str isEqualToString: EffectsStrings[i]]) return i;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-08-29 15:42:06 -04:00
|
|
|
|
@implementation LaunchWindowController
|
|
|
|
|
|
|
|
|
|
-(NSString *)windowNibName {
|
|
|
|
|
return @"LaunchWindow";
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-07 00:34:26 -04:00
|
|
|
|
-(void)reset {
|
2021-06-12 22:59:19 -04:00
|
|
|
|
// handled elsewhere.
|
|
|
|
|
//[self setMachine: nil];
|
2021-06-07 00:34:26 -04:00
|
|
|
|
|
2020-09-05 11:00:29 -04:00
|
|
|
|
[self setMameSpeed: 1];
|
2020-09-06 15:26:20 -04:00
|
|
|
|
[self setMameBGFX: YES];
|
2020-09-13 12:04:54 -04:00
|
|
|
|
[self setMameMouse: NO];
|
2021-06-12 14:01:42 -04:00
|
|
|
|
[self setMameSamples: NO];
|
2021-06-07 00:34:26 -04:00
|
|
|
|
[self setMameSquarePixels: NO];
|
|
|
|
|
[self setMameDebug: NO];
|
2021-07-14 22:38:15 -04:00
|
|
|
|
[self setMameRewind: NO];
|
2021-06-16 23:46:26 -04:00
|
|
|
|
[self setMameWindowMode: 1]; // default = 1x window.
|
2021-06-07 00:34:26 -04:00
|
|
|
|
|
|
|
|
|
[self setMameBackend: 0];
|
|
|
|
|
[self setMameEffects: 0];
|
|
|
|
|
|
|
|
|
|
[self setMameBitBangerPath: nil];
|
|
|
|
|
[self setMameShareDirectoryPath: nil];
|
|
|
|
|
[self setMameAVIPath: nil];
|
|
|
|
|
[self setMameWAVPath: nil];
|
|
|
|
|
[self setMameVGMPath: nil];
|
|
|
|
|
|
|
|
|
|
[self setMameAVI: NO];
|
|
|
|
|
[self setMameWAV: NO];
|
|
|
|
|
[self setMameVGM: NO];
|
|
|
|
|
[self setMameBitBanger: NO];
|
|
|
|
|
[self setMameShareDirectory: NO];
|
|
|
|
|
|
2021-06-12 22:59:19 -04:00
|
|
|
|
#if 0
|
|
|
|
|
[self setSoftware: nil];
|
|
|
|
|
//_softwareSet = nil;
|
|
|
|
|
[_softwareListControl setObjectValue: nil];
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(void)resetSoftware {
|
2021-06-07 00:34:26 -04:00
|
|
|
|
[self setSoftware: nil];
|
2021-06-12 22:59:19 -04:00
|
|
|
|
//_softwareSet = nil;
|
2021-06-12 16:03:55 -04:00
|
|
|
|
[_softwareListControl setObjectValue: nil];
|
2021-06-07 00:34:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(void)windowWillLoad {
|
|
|
|
|
|
2022-03-18 15:23:56 -04:00
|
|
|
|
_manager = [BookmarkManager sharedManager];
|
|
|
|
|
|
2021-06-12 14:02:11 -04:00
|
|
|
|
// if this calls [self window], it will recurse. that is bad.
|
|
|
|
|
//[self defaultLoad: nil];
|
2021-06-07 00:34:26 -04:00
|
|
|
|
[self reset];
|
2020-09-05 11:00:29 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-12 14:02:11 -04:00
|
|
|
|
|
|
|
|
|
|
2021-06-09 00:11:25 -04:00
|
|
|
|
static void AddSubview(NSView *parent, NSView *child) {
|
|
|
|
|
|
|
|
|
|
[child setFrame: [parent bounds]];
|
|
|
|
|
[parent addSubview: child];
|
|
|
|
|
}
|
2021-06-12 14:02:11 -04:00
|
|
|
|
|
2020-08-29 15:42:06 -04:00
|
|
|
|
- (void)windowDidLoad {
|
|
|
|
|
[super windowDidLoad];
|
|
|
|
|
|
2021-06-12 14:02:11 -04:00
|
|
|
|
|
|
|
|
|
|
2020-08-29 15:42:06 -04:00
|
|
|
|
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
|
|
|
|
|
|
2021-06-09 00:11:25 -04:00
|
|
|
|
AddSubview(_slotView, [_slotController view]);
|
|
|
|
|
AddSubview(_mediaView, [_mediaController view]);
|
|
|
|
|
AddSubview(_machineView, [_machineViewController view]);
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
2022-02-23 20:56:21 -05:00
|
|
|
|
|
|
|
|
|
[_softwareListControl setMinWidth: 250];
|
|
|
|
|
[_softwareListControl setHidden: YES];
|
2021-06-12 14:02:11 -04:00
|
|
|
|
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
2020-09-05 11:00:29 -04:00
|
|
|
|
NSArray *keys = @[
|
2021-06-07 00:34:26 -04:00
|
|
|
|
//@"mameMachine", // - handled
|
|
|
|
|
@"mameSquarePixels", @"mameWindowMode",
|
2021-03-12 20:09:59 -05:00
|
|
|
|
@"mameMouse", @"mameSamples",
|
2021-07-14 22:38:15 -04:00
|
|
|
|
@"mameDebug", @"mameRewind",
|
2020-09-13 12:04:54 -04:00
|
|
|
|
@"mameSpeed",
|
2020-09-05 11:00:29 -04:00
|
|
|
|
@"mameAVI", @"mameAVIPath",
|
|
|
|
|
@"mameWAV", @"mameWAVPath",
|
|
|
|
|
@"mameVGM", @"mameVGMPath",
|
2021-06-07 00:34:26 -04:00
|
|
|
|
@"mameShareDirectory", @"mameShareDirectoryPath",
|
|
|
|
|
@"mameBitBanger", @"mameBitBangerPath",
|
2020-09-06 15:26:20 -04:00
|
|
|
|
@"mameBGFX", @"mameBackend", @"mameEffects",
|
2021-05-27 19:42:28 -04:00
|
|
|
|
@"software",
|
2020-09-05 11:00:29 -04:00
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
for (NSString *key in keys) {
|
|
|
|
|
[self addObserver: self forKeyPath: key options:0 context: (__bridge void * _Nullable)(kMyContext)];
|
|
|
|
|
}
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
2020-09-05 11:00:29 -04:00
|
|
|
|
|
2020-08-29 16:48:14 -04:00
|
|
|
|
[_slotController addObserver: self forKeyPath: @"args" options: 0 context: (__bridge void * _Nullable)(kMyContext)];
|
|
|
|
|
[_mediaController addObserver: self forKeyPath: @"args" options: 0 context: (__bridge void * _Nullable)(kMyContext)];
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
|
|
|
|
[_mediaController bind: @"media" toObject: _slotController withKeyPath: @"media" options: 0];
|
|
|
|
|
|
2020-08-29 16:48:14 -04:00
|
|
|
|
[_machineViewController addObserver: self forKeyPath: @"machine" options: 0 context: (__bridge void * _Nullable)kContextMachine];
|
2020-09-05 11:00:29 -04:00
|
|
|
|
|
2021-05-27 19:42:28 -04:00
|
|
|
|
|
2022-02-23 20:56:21 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// can't be done until above views are set up.
|
|
|
|
|
[self defaultLoad: nil];
|
|
|
|
|
|
2021-05-27 19:42:28 -04:00
|
|
|
|
|
2020-09-05 11:00:29 -04:00
|
|
|
|
[self buildCommandLine];
|
2022-02-23 20:56:21 -05:00
|
|
|
|
|
|
|
|
|
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
|
|
|
|
|
|
|
|
|
[nc addObserver: self selector: @selector(bookmarkNotification:) name: kNotificationBookmarkMagicRoute object: nil];
|
2020-08-29 15:42:06 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
|
|
|
|
|
|
|
|
|
|
if (context == (__bridge void *)kMyContext) {
|
2021-06-07 00:34:26 -04:00
|
|
|
|
if (_loadingBookmark) return;
|
2020-08-29 15:42:06 -04:00
|
|
|
|
[self buildCommandLine];
|
2020-08-29 16:48:14 -04:00
|
|
|
|
} else if (context == (__bridge void *)kContextMachine) {
|
2021-06-07 00:34:26 -04:00
|
|
|
|
if (_loadingBookmark) return;
|
|
|
|
|
|
2020-08-29 16:48:14 -04:00
|
|
|
|
NSString *machine = [_machineViewController machine];
|
2021-06-09 22:47:00 -04:00
|
|
|
|
[self setMachine: machine];
|
2020-09-12 18:00:34 -04:00
|
|
|
|
[_slotController setMachine: machine];
|
2020-08-29 16:48:14 -04:00
|
|
|
|
[self buildCommandLine];
|
2020-08-29 15:42:06 -04:00
|
|
|
|
} else {
|
|
|
|
|
[super observeValueForKeyPath: keyPath ofObject: object change: change context: context];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-31 18:23:48 -04:00
|
|
|
|
|
|
|
|
|
|
2021-06-09 22:47:00 -04:00
|
|
|
|
-(NSString *)machine {
|
|
|
|
|
return _machine;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(void)setMachine:(NSString *)machine {
|
|
|
|
|
if (_machine == machine) return;
|
|
|
|
|
_machine = machine;
|
|
|
|
|
_machineDescription = MameMachine(machine);
|
|
|
|
|
|
2021-06-12 14:02:11 -04:00
|
|
|
|
#if 0
|
|
|
|
|
[self setMachineName: [_machineDescription objectForKey: @"description"]];
|
|
|
|
|
#else
|
2021-06-09 22:47:00 -04:00
|
|
|
|
NSString *title = _machineDescription
|
|
|
|
|
? [NSString stringWithFormat: @"Ample – %@", [_machineDescription objectForKey: @"description"]]
|
|
|
|
|
: @"Ample";
|
|
|
|
|
|
|
|
|
|
[[self window] setTitle: title];
|
2021-06-12 14:02:11 -04:00
|
|
|
|
#endif
|
2021-06-12 22:59:19 -04:00
|
|
|
|
|
|
|
|
|
// enable/disable the right-click menu
|
|
|
|
|
NSWindow *window = [self window];
|
|
|
|
|
NSView *view = [window contentView];
|
|
|
|
|
if (_machine) [view setMenu: [window menu]];
|
|
|
|
|
else [view setMenu: nil];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// software list.
|
|
|
|
|
[self updateSoftwareList];
|
2021-06-09 22:47:00 -04:00
|
|
|
|
}
|
2020-08-31 18:23:48 -04:00
|
|
|
|
|
2021-01-29 20:07:44 -05:00
|
|
|
|
static NSString * JoinArguments(NSArray *argv, NSString *argv0) {
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
|
|
|
|
static NSCharacterSet *safe = nil;
|
|
|
|
|
static NSCharacterSet *unsafe = nil;
|
|
|
|
|
|
|
|
|
|
if (!safe) {
|
|
|
|
|
NSString *str =
|
|
|
|
|
@"%+-./:=_"
|
|
|
|
|
@"0123456789"
|
|
|
|
|
@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
|
|
|
;
|
|
|
|
|
safe = [NSCharacterSet characterSetWithCharactersInString: str];
|
|
|
|
|
unsafe = [safe invertedSet];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSMutableString *rv = [NSMutableString new];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//unsigned ix = 0;
|
2020-08-31 18:23:48 -04:00
|
|
|
|
//[rv appendString: @"mame"];
|
2021-01-29 20:07:44 -05:00
|
|
|
|
if (argv0) {
|
|
|
|
|
[rv appendString: argv0];
|
|
|
|
|
} else {
|
|
|
|
|
NSString *path = MamePath();
|
|
|
|
|
path = path ? [path lastPathComponent] : @"mame";
|
|
|
|
|
[rv appendString: path];
|
|
|
|
|
}
|
2020-08-29 15:42:06 -04:00
|
|
|
|
for (NSString *s in argv) {
|
|
|
|
|
[rv appendString: @" "];
|
|
|
|
|
NSUInteger l = [s length];
|
|
|
|
|
|
|
|
|
|
if (!l) {
|
|
|
|
|
[rv appendString: @"''"];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!CFStringFindCharacterFromSet((CFStringRef)s, (CFCharacterSetRef)unsafe, CFRangeMake(0, l), 0, NULL)) {
|
|
|
|
|
[rv appendString: s];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unichar *buffer = malloc(sizeof(unichar) * l);
|
|
|
|
|
[s getCharacters: buffer range: NSMakeRange(0, l)];
|
|
|
|
|
|
|
|
|
|
[rv appendString: @"'"];
|
|
|
|
|
for (NSUInteger i = 0; i < l; ++i) {
|
|
|
|
|
unichar c = buffer[i];
|
|
|
|
|
switch (c) {
|
|
|
|
|
case '\'':
|
|
|
|
|
[rv appendString: @"\\'"];
|
|
|
|
|
break;
|
|
|
|
|
case '\\':
|
|
|
|
|
[rv appendString: @"\\\\"];
|
|
|
|
|
break;
|
|
|
|
|
case 0x7f:
|
|
|
|
|
[rv appendString: @"\\177"];
|
|
|
|
|
break;
|
|
|
|
|
default: {
|
|
|
|
|
NSString *cc;
|
|
|
|
|
if (c < 0x20) {
|
|
|
|
|
cc = [NSString stringWithFormat: @"\\%o", c];
|
|
|
|
|
} else {
|
|
|
|
|
cc = [NSString stringWithCharacters: &c length: 1];
|
|
|
|
|
}
|
|
|
|
|
[rv appendString: cc];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
[rv appendString: @"'"];
|
|
|
|
|
free(buffer);
|
|
|
|
|
}
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-29 20:07:44 -05:00
|
|
|
|
static NSString *ShellQuote(NSString *s) {
|
|
|
|
|
|
|
|
|
|
static NSCharacterSet *safe = nil;
|
|
|
|
|
static NSCharacterSet *unsafe = nil;
|
|
|
|
|
|
|
|
|
|
if (!safe) {
|
|
|
|
|
NSString *str =
|
|
|
|
|
@"%+-./:=_"
|
|
|
|
|
@"0123456789"
|
|
|
|
|
@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
|
|
|
;
|
|
|
|
|
safe = [NSCharacterSet characterSetWithCharactersInString: str];
|
|
|
|
|
unsafe = [safe invertedSet];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSUInteger l = [s length];
|
|
|
|
|
|
|
|
|
|
if (!l) {
|
|
|
|
|
return @"''";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!CFStringFindCharacterFromSet((CFStringRef)s, (CFCharacterSetRef)unsafe, CFRangeMake(0, l), 0, NULL)) {
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSMutableString *rv = [NSMutableString new];
|
|
|
|
|
|
|
|
|
|
unichar *buffer = malloc(sizeof(unichar) * l);
|
|
|
|
|
[s getCharacters: buffer range: NSMakeRange(0, l)];
|
|
|
|
|
|
|
|
|
|
[rv appendString: @"'"];
|
|
|
|
|
for (NSUInteger i = 0; i < l; ++i) {
|
|
|
|
|
unichar c = buffer[i];
|
|
|
|
|
switch (c) {
|
|
|
|
|
case '\'':
|
|
|
|
|
[rv appendString: @"\\'"];
|
|
|
|
|
break;
|
|
|
|
|
case '\\':
|
|
|
|
|
[rv appendString: @"\\\\"];
|
|
|
|
|
break;
|
|
|
|
|
case 0x7f:
|
|
|
|
|
[rv appendString: @"\\177"];
|
|
|
|
|
break;
|
|
|
|
|
default: {
|
|
|
|
|
NSString *cc;
|
|
|
|
|
if (c < 0x20) {
|
|
|
|
|
cc = [NSString stringWithFormat: @"\\%o", c];
|
|
|
|
|
} else {
|
|
|
|
|
cc = [NSString stringWithCharacters: &c length: 1];
|
|
|
|
|
}
|
|
|
|
|
[rv appendString: cc];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
[rv appendString: @"'"];
|
|
|
|
|
free(buffer);
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-08-29 15:42:06 -04:00
|
|
|
|
-(void)buildCommandLine {
|
|
|
|
|
|
2020-09-05 11:00:29 -04:00
|
|
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
|
|
|
|
2021-06-09 22:47:00 -04:00
|
|
|
|
if (!_machine) {
|
2020-08-29 15:42:06 -04:00
|
|
|
|
[self setCommandLine: @""];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSMutableArray *argv = [NSMutableArray new];
|
|
|
|
|
|
|
|
|
|
//[argv addObject: @"mame"];
|
2021-06-09 22:47:00 -04:00
|
|
|
|
[argv addObject: _machine];
|
2021-05-27 19:42:28 -04:00
|
|
|
|
|
2020-09-13 12:04:54 -04:00
|
|
|
|
// -confirm_quit?
|
|
|
|
|
[argv addObject: @"-skip_gameinfo"];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (_mameMouse)
|
|
|
|
|
[argv addObject: @"-mouse"]; // capture the mouse cursor when over the window.
|
2021-03-12 20:09:59 -05:00
|
|
|
|
|
|
|
|
|
if (!_mameSamples)
|
|
|
|
|
[argv addObject: @"-nosamples"];
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
|
|
|
|
if (_mameDebug) [argv addObject: @"-debug"];
|
2021-07-14 22:38:15 -04:00
|
|
|
|
if (_mameRewind) [argv addObject: @"-rewind"];
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
2020-09-06 15:26:20 -04:00
|
|
|
|
|
2020-09-07 22:00:10 -04:00
|
|
|
|
/*
|
|
|
|
|
* -window -nomax uses a 4:3 aspect ratio - ie, height = width * 3 / 4 (since height is always the limiting factor)
|
|
|
|
|
* for square pixels, should pass the true size and true aspect ratio.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
NSSize screen = [_slotController resolution];
|
|
|
|
|
switch(_mameWindowMode) {
|
|
|
|
|
case 0: // full screen;
|
2020-09-07 22:20:26 -04:00
|
|
|
|
// no uneven stretch doesn't do anything in full-screen mode.
|
2020-09-07 22:00:10 -04:00
|
|
|
|
break;
|
|
|
|
|
case 1: // 1x
|
2020-09-07 22:10:37 -04:00
|
|
|
|
// make the command-line a bit shorter and more pleasant.
|
2020-09-07 22:00:10 -04:00
|
|
|
|
if (!_mameSquarePixels) {
|
|
|
|
|
[argv addObject: @"-window"];
|
|
|
|
|
[argv addObject: @"-nomax"];
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-09-07 22:10:37 -04:00
|
|
|
|
|
2020-09-07 22:00:10 -04:00
|
|
|
|
// drop through.
|
2020-09-07 22:20:26 -04:00
|
|
|
|
case 2: // 2x
|
2020-09-14 19:09:10 -04:00
|
|
|
|
case 3: // 3x
|
2020-09-07 22:20:26 -04:00
|
|
|
|
|
|
|
|
|
if (_mameSquarePixels) {
|
|
|
|
|
// NSString *aspect = [NSString stringWithFormat: @"%u:%u", (unsigned)screen.width, (unsigned)screen.height];
|
|
|
|
|
// [argv addObject: @"-aspect"];
|
|
|
|
|
// [argv addObject: aspect];
|
|
|
|
|
[argv addObject: @"-nounevenstretch"];
|
|
|
|
|
} else {
|
|
|
|
|
screen.height = round(screen.width * 3 / 4);
|
|
|
|
|
}
|
2020-09-07 22:00:10 -04:00
|
|
|
|
|
|
|
|
|
[argv addObject: @"-window"];
|
|
|
|
|
NSString *res = [NSString stringWithFormat: @"%ux%u",
|
|
|
|
|
(unsigned)(_mameWindowMode * screen.width),
|
|
|
|
|
(unsigned)(_mameWindowMode * screen.height)
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
[argv addObject: @"-resolution"];
|
|
|
|
|
[argv addObject: res];
|
|
|
|
|
break;
|
2020-09-06 15:26:20 -04:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-07 22:00:10 -04:00
|
|
|
|
|
|
|
|
|
|
2020-09-06 15:26:20 -04:00
|
|
|
|
if (_mameBGFX) {
|
2023-03-01 10:56:45 -05:00
|
|
|
|
[argv addObject: @"-video"];
|
|
|
|
|
[argv addObject: @"bgfx"];
|
|
|
|
|
|
2020-09-06 15:26:20 -04:00
|
|
|
|
if (_mameBackend) {
|
|
|
|
|
[argv addObject: @"-bgfx_backend"];
|
2021-06-07 00:34:26 -04:00
|
|
|
|
[argv addObject: BackendStrings[_mameBackend]];
|
2020-08-29 15:42:06 -04:00
|
|
|
|
}
|
2020-09-06 15:26:20 -04:00
|
|
|
|
if (_mameEffects) {
|
|
|
|
|
[argv addObject: @"-bgfx_screen_chains"];
|
2021-06-07 00:34:26 -04:00
|
|
|
|
[argv addObject: EffectsStrings[_mameEffects]];
|
2020-09-06 15:26:20 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
[argv addObject: @"-video"];
|
|
|
|
|
[argv addObject: @"soft"];
|
2020-08-29 15:42:06 -04:00
|
|
|
|
}
|
2020-09-06 15:26:20 -04:00
|
|
|
|
|
|
|
|
|
|
2020-08-29 15:42:06 -04:00
|
|
|
|
// -speed n
|
|
|
|
|
// -scale n
|
|
|
|
|
|
|
|
|
|
NSArray *tmp;
|
|
|
|
|
tmp = [_slotController args];
|
|
|
|
|
if ([tmp count]) {
|
|
|
|
|
[argv addObjectsFromArray: tmp];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmp = [_mediaController args];
|
|
|
|
|
if ([tmp count]) {
|
|
|
|
|
[argv addObjectsFromArray: tmp];
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-26 15:52:13 -05:00
|
|
|
|
// software *AFTER* slots so, eg, apple2ee has access to the superdrive.
|
|
|
|
|
if (_software) {
|
|
|
|
|
NSString *name = [_softwareSet nameForSoftware: _software];
|
|
|
|
|
if (name) [argv addObject: name];
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-05 11:00:29 -04:00
|
|
|
|
if (_mameSpeed < 0) {
|
|
|
|
|
[argv addObject: @"-nothrottle"];
|
|
|
|
|
} else if (_mameSpeed > 1) {
|
|
|
|
|
[argv addObject: @"-speed"];
|
|
|
|
|
[argv addObject: [NSString stringWithFormat: @"%d", (int)_mameSpeed]];
|
|
|
|
|
}
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
2020-09-05 11:00:29 -04:00
|
|
|
|
// audio video.
|
2021-07-10 23:34:00 -04:00
|
|
|
|
|
2020-09-05 11:00:29 -04:00
|
|
|
|
if (_mameAVI && [_mameAVIPath length]) {
|
|
|
|
|
[argv addObject: @"-aviwrite"];
|
|
|
|
|
[argv addObject: _mameAVIPath];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_mameWAV && [_mameWAVPath length]) {
|
|
|
|
|
[argv addObject: @"-wavwrite"];
|
|
|
|
|
[argv addObject: _mameWAVPath];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// vgm only valid for custom mame.
|
|
|
|
|
if (![defaults boolForKey: kUseCustomMame]) {
|
|
|
|
|
if (_mameVGM && [_mameVGMPath length]) {
|
|
|
|
|
[argv addObject: @"-vgmwrite"];
|
|
|
|
|
[argv addObject: _mameVGMPath];
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
2021-06-07 00:34:26 -04:00
|
|
|
|
if (_mameShareDirectory && [_mameShareDirectoryPath length]) {
|
2021-05-16 11:06:12 -04:00
|
|
|
|
[argv addObject: @"-share_directory"];
|
2021-06-07 00:34:26 -04:00
|
|
|
|
[argv addObject: _mameShareDirectoryPath];
|
2021-05-16 11:06:12 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-07 00:34:26 -04:00
|
|
|
|
if (_mameBitBanger && [_mameBitBangerPath length]) {
|
2021-05-30 18:46:46 -04:00
|
|
|
|
[argv addObject: @"-bitbanger"];
|
2021-06-07 00:34:26 -04:00
|
|
|
|
[argv addObject: _mameBitBangerPath];
|
2021-05-30 18:46:46 -04:00
|
|
|
|
}
|
2021-05-16 11:06:12 -04:00
|
|
|
|
|
2021-01-29 20:07:44 -05:00
|
|
|
|
[self setCommandLine: JoinArguments(argv, nil)];
|
2020-08-29 15:42:06 -04:00
|
|
|
|
[self setArgs: argv];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-01-29 20:07:44 -05:00
|
|
|
|
-(BOOL)validateMenuItem:(NSMenuItem *)menuItem {
|
|
|
|
|
SEL cmd = [menuItem action];
|
|
|
|
|
if (cmd == @selector(exportShellScript:)) {
|
|
|
|
|
return [_args count] ? YES : NO;
|
|
|
|
|
}
|
2021-06-07 00:34:26 -04:00
|
|
|
|
if (cmd == @selector(addBookmark:)) {
|
2021-06-09 22:47:00 -04:00
|
|
|
|
return _machine ? YES : NO;
|
2021-06-07 00:34:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
|
//return [super validateMenuItem: menuItem]; // not implemented?
|
2021-01-29 20:07:44 -05:00
|
|
|
|
}
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
2021-11-16 22:44:38 -05:00
|
|
|
|
-(void)defocus {
|
|
|
|
|
[[self window] makeFirstResponder: nil]; // in case text is being edited...
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-29 20:07:44 -05:00
|
|
|
|
# pragma mark - IBActions
|
2020-08-31 18:23:48 -04:00
|
|
|
|
|
2020-08-29 15:42:06 -04:00
|
|
|
|
- (IBAction)launchAction:(id)sender {
|
|
|
|
|
|
2021-11-16 22:44:38 -05:00
|
|
|
|
[self defocus];
|
2020-08-29 15:42:06 -04:00
|
|
|
|
if (![_args count]) return;
|
2020-09-01 23:52:11 -04:00
|
|
|
|
|
2020-09-30 21:49:06 -04:00
|
|
|
|
[LogWindowController controllerForArgs: _args];
|
|
|
|
|
|
2021-01-29 20:07:44 -05:00
|
|
|
|
}
|
2020-08-31 18:23:48 -04:00
|
|
|
|
|
2021-06-18 21:57:46 -04:00
|
|
|
|
|
|
|
|
|
- (IBAction)listMedia:(id)sender {
|
|
|
|
|
|
2021-11-16 22:44:38 -05:00
|
|
|
|
[self defocus];
|
2021-06-18 21:57:46 -04:00
|
|
|
|
if (!_machine) return;
|
|
|
|
|
|
|
|
|
|
NSMutableArray *argv = [NSMutableArray new];
|
|
|
|
|
|
|
|
|
|
[argv addObject: _machine];
|
|
|
|
|
[argv addObject: @"-listmedia"];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NSArray *tmp;
|
|
|
|
|
tmp = [_slotController args];
|
|
|
|
|
if ([tmp count]) {
|
|
|
|
|
[argv addObjectsFromArray: tmp];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
tmp = [_mediaController args];
|
|
|
|
|
if ([tmp count]) {
|
|
|
|
|
[argv addObjectsFromArray: tmp];
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
[LogWindowController controllerForArgs: argv close: NO];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (IBAction)listSlots:(id)sender {
|
|
|
|
|
|
2021-11-16 22:44:38 -05:00
|
|
|
|
[self defocus];
|
2021-06-18 21:57:46 -04:00
|
|
|
|
if (!_machine) return;
|
|
|
|
|
|
|
|
|
|
NSMutableArray *argv = [NSMutableArray new];
|
|
|
|
|
|
|
|
|
|
[argv addObject: _machine];
|
|
|
|
|
[argv addObject: @"-listslots"];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NSArray *tmp;
|
|
|
|
|
tmp = [_slotController args];
|
|
|
|
|
if ([tmp count]) {
|
|
|
|
|
[argv addObjectsFromArray: tmp];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
tmp = [_mediaController args];
|
|
|
|
|
if ([tmp count]) {
|
|
|
|
|
[argv addObjectsFromArray: tmp];
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
[LogWindowController controllerForArgs: argv close: NO];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-01-29 20:07:44 -05:00
|
|
|
|
-(IBAction)exportShellScript: (id)sender {
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
2021-01-29 20:07:44 -05:00
|
|
|
|
NSSavePanel *p = [NSSavePanel savePanel];
|
2021-03-08 21:15:15 -05:00
|
|
|
|
|
2021-06-09 22:47:00 -04:00
|
|
|
|
NSString *defaultName = [_machine stringByAppendingString: @".sh"];
|
2021-03-08 21:15:15 -05:00
|
|
|
|
|
2021-01-29 20:07:44 -05:00
|
|
|
|
[p setTitle: @"Export Shell Script"];
|
|
|
|
|
[p setExtensionHidden: NO];
|
2021-03-08 21:15:15 -05:00
|
|
|
|
[p setNameFieldStringValue: defaultName];
|
2020-09-01 23:52:58 -04:00
|
|
|
|
|
2021-01-29 20:07:44 -05:00
|
|
|
|
//[p setDelegate: self];
|
|
|
|
|
|
|
|
|
|
NSWindow *w = [self window];
|
|
|
|
|
|
|
|
|
|
NSMutableString *data = [NSMutableString new];
|
|
|
|
|
|
|
|
|
|
[data appendString: @"#!/bin/sh\n\n"];
|
|
|
|
|
[data appendFormat: @"MAME=%@\n", ShellQuote(MamePath())];
|
|
|
|
|
[data appendFormat: @"cd %@\n", ShellQuote(MameWorkingDirectoryPath())];
|
|
|
|
|
[data appendString: JoinArguments(_args, @"$MAME")];
|
|
|
|
|
[data appendString: @"\n\n"];
|
|
|
|
|
|
|
|
|
|
[p beginSheetModalForWindow: w completionHandler: ^(NSModalResponse r) {
|
|
|
|
|
|
|
|
|
|
if (r != NSModalResponseOK) return;
|
|
|
|
|
|
|
|
|
|
NSURL *url = [p URL];
|
|
|
|
|
NSError *error = nil;
|
|
|
|
|
[data writeToURL: url atomically: YES encoding: NSUTF8StringEncoding error: &error];
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
2021-01-29 20:07:44 -05:00
|
|
|
|
[p orderOut: nil];
|
|
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
[self presentError: error];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// chmod 755...
|
|
|
|
|
int ok = chmod([url fileSystemRepresentation], 0755);
|
|
|
|
|
if (ok < 0) {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
}];
|
|
|
|
|
}
|
2020-08-29 15:42:06 -04:00
|
|
|
|
|
2021-06-07 00:34:26 -04:00
|
|
|
|
|
2021-06-12 22:59:19 -04:00
|
|
|
|
-(IBAction)resetMachine:(id)sender {
|
2021-06-07 00:34:26 -04:00
|
|
|
|
[self reset];
|
2021-06-12 22:59:19 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(IBAction)resetAll:(id)sender {
|
|
|
|
|
|
2022-03-18 15:23:56 -04:00
|
|
|
|
[_manager setCurrentBookmark: nil];
|
2021-06-12 22:59:19 -04:00
|
|
|
|
[self reset];
|
|
|
|
|
[self resetSoftware];
|
2021-06-07 00:34:26 -04:00
|
|
|
|
[_slotController resetSlots: sender];
|
2021-06-12 22:59:19 -04:00
|
|
|
|
[_mediaController resetMedia: sender];
|
2021-06-07 00:34:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-12 22:59:19 -04:00
|
|
|
|
-(IBAction)resetMedia:(id)sender {
|
|
|
|
|
[_mediaController resetMedia: sender];
|
|
|
|
|
[_softwareListControl setObjectValue: nil];
|
|
|
|
|
[self setSoftware: nil];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-08-29 15:42:06 -04:00
|
|
|
|
@end
|
2021-01-29 20:07:44 -05:00
|
|
|
|
|
2021-05-27 19:42:28 -04:00
|
|
|
|
|
|
|
|
|
@implementation LaunchWindowController (SoftwareList)
|
|
|
|
|
|
|
|
|
|
-(void)updateSoftwareList {
|
|
|
|
|
|
2021-06-09 22:47:00 -04:00
|
|
|
|
_softwareSet = [SoftwareSet softwareSetForMachine: _machine];
|
2021-05-27 19:42:28 -04:00
|
|
|
|
|
|
|
|
|
[_softwareListControl setAutocompleteDelegate: _softwareSet];
|
|
|
|
|
|
|
|
|
|
if (_softwareSet) {
|
|
|
|
|
[_softwareListControl invalidate];
|
|
|
|
|
[_softwareListControl setHidden: NO];
|
|
|
|
|
} else {
|
|
|
|
|
_software = nil;
|
|
|
|
|
[_softwareListControl setHidden: YES];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- (IBAction)softwareChanged:(id)sender {
|
|
|
|
|
|
|
|
|
|
id o = [(NSControl *)sender objectValue];
|
2021-05-27 20:36:24 -04:00
|
|
|
|
//NSLog(@"%@", o);
|
2021-05-27 19:42:28 -04:00
|
|
|
|
[self setSoftware: o];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
2021-06-07 00:34:26 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@implementation LaunchWindowController (Bookmark)
|
|
|
|
|
|
2021-06-12 14:02:11 -04:00
|
|
|
|
-(IBAction)defaultSave:(id)sender {
|
|
|
|
|
|
2022-03-18 15:23:56 -04:00
|
|
|
|
#if 0
|
2021-06-12 14:02:11 -04:00
|
|
|
|
NSDictionary *d = [self makeBookmark];
|
|
|
|
|
|
2022-03-18 15:23:56 -04:00
|
|
|
|
[_manager saveDefault: d];
|
2022-02-23 20:56:21 -05:00
|
|
|
|
#endif
|
2021-06-12 14:02:11 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(IBAction)defaultLoad:(id)sender {
|
|
|
|
|
|
2022-03-18 15:23:56 -04:00
|
|
|
|
Bookmark *b = [_manager defaultBookmark];
|
2021-06-12 14:02:11 -04:00
|
|
|
|
|
2022-03-18 15:23:56 -04:00
|
|
|
|
if (!b) {
|
2021-06-12 22:59:19 -04:00
|
|
|
|
[self resetAll: sender];
|
|
|
|
|
[self setMachine: nil];
|
2021-06-13 17:17:47 -04:00
|
|
|
|
[_machineViewController reset];
|
2021-06-12 16:03:55 -04:00
|
|
|
|
[_slotController setMachine: nil];
|
2021-06-12 14:02:11 -04:00
|
|
|
|
return;
|
|
|
|
|
}
|
2022-03-18 15:23:56 -04:00
|
|
|
|
[self loadBookmark: b];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(IBAction)updateBookmark: (id)sender {
|
|
|
|
|
|
|
|
|
|
Bookmark *b = [sender representedObject];
|
|
|
|
|
if (!b) return;
|
|
|
|
|
|
|
|
|
|
NSDictionary *d = [self makeBookmark];
|
|
|
|
|
[b setDictionary: d];
|
2021-06-12 14:02:11 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-07 00:34:26 -04:00
|
|
|
|
-(IBAction)addBookmark:(id)sender {
|
|
|
|
|
|
2021-06-09 22:47:00 -04:00
|
|
|
|
if (!_machine) return;
|
2022-02-23 20:56:21 -05:00
|
|
|
|
|
2021-06-07 00:34:26 -04:00
|
|
|
|
NSString *name = nil;
|
2021-06-09 22:47:00 -04:00
|
|
|
|
if (_machineDescription) name = [_machineDescription objectForKey:@"description"];
|
|
|
|
|
if (!name) name = _machine;
|
2021-06-07 00:34:26 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (_software) {
|
|
|
|
|
name = [name stringByAppendingFormat: @" - %@", [_software title]];
|
|
|
|
|
}
|
2022-02-23 20:56:21 -05:00
|
|
|
|
|
2022-03-18 15:23:56 -04:00
|
|
|
|
name = [_manager uniqueBookmarkName: name];
|
2022-02-23 20:56:21 -05:00
|
|
|
|
|
2021-06-07 00:34:26 -04:00
|
|
|
|
[self setBookmarkName: name];
|
2022-02-23 20:56:21 -05:00
|
|
|
|
[self setBookmarkDefault: NO];
|
2021-06-07 00:34:26 -04:00
|
|
|
|
[_bookmarkTextField selectText: nil];
|
2022-02-23 20:56:21 -05:00
|
|
|
|
[_bookmarkErrorField setStringValue: @""];
|
|
|
|
|
|
2021-06-07 00:34:26 -04:00
|
|
|
|
[[self window] beginSheet: _addBookmarkWindow completionHandler: nil];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(IBAction)bookmarkCancel:(id)sender {
|
|
|
|
|
[[self window] endSheet: _addBookmarkWindow];
|
|
|
|
|
[_addBookmarkWindow orderOut: nil];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(IBAction)bookmarkSave:(id)sender {
|
2022-02-23 20:56:21 -05:00
|
|
|
|
|
2021-06-07 00:34:26 -04:00
|
|
|
|
|
2022-02-23 20:56:21 -05:00
|
|
|
|
#if 0
|
2022-03-18 15:23:56 -04:00
|
|
|
|
if (![_manager validateName: _bookmarkName]) {
|
2021-06-07 00:34:26 -04:00
|
|
|
|
[_bookmarkTextField selectText: nil];
|
|
|
|
|
NSBeep();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-02-23 20:56:21 -05:00
|
|
|
|
#endif
|
2021-06-07 00:34:26 -04:00
|
|
|
|
|
|
|
|
|
NSDictionary *d = [self makeBookmark];
|
2022-02-23 20:56:21 -05:00
|
|
|
|
NSError *e;
|
2021-06-07 00:34:26 -04:00
|
|
|
|
|
2022-03-18 15:23:56 -04:00
|
|
|
|
if (( e = [_manager saveBookmark: d name: _bookmarkName automatic: _bookmarkDefault])) {
|
2022-02-23 20:56:21 -05:00
|
|
|
|
// probably a duplicate name...
|
|
|
|
|
[_bookmarkTextField selectText: nil];
|
|
|
|
|
[_bookmarkErrorField setStringValue: [e localizedDescription]];
|
|
|
|
|
NSBeep();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-06-07 00:34:26 -04:00
|
|
|
|
|
|
|
|
|
[[self window] endSheet: _addBookmarkWindow];
|
|
|
|
|
[_addBookmarkWindow orderOut: nil];
|
|
|
|
|
[self setBookmarkName: nil];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2022-02-23 20:56:21 -05:00
|
|
|
|
-(void)bookmarkNotification: (NSNotification *)notification {
|
|
|
|
|
|
|
|
|
|
Bookmark *b = [notification object];
|
2022-03-18 15:23:56 -04:00
|
|
|
|
[self loadBookmark: b];
|
2022-02-23 20:56:21 -05:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-07 00:34:26 -04:00
|
|
|
|
-(IBAction)bookmarkMenu:(id)sender {
|
2022-03-18 15:23:56 -04:00
|
|
|
|
|
2022-02-23 20:56:21 -05:00
|
|
|
|
// represented object is a Bookmark.
|
2022-03-18 15:23:56 -04:00
|
|
|
|
Bookmark *b = [sender representedObject];
|
|
|
|
|
[self loadBookmark: b];
|
2021-06-12 14:02:11 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2022-03-18 15:23:56 -04:00
|
|
|
|
-(void)loadBookmark: (Bookmark *)b {
|
2021-06-12 14:02:11 -04:00
|
|
|
|
Class StringClass = [NSString class];
|
|
|
|
|
Class NumberClass = [NSNumber class];
|
2022-03-18 15:23:56 -04:00
|
|
|
|
|
|
|
|
|
NSDictionary *d = [b dictionary];
|
|
|
|
|
|
|
|
|
|
[_manager setCurrentBookmark: b];
|
2021-06-12 14:02:11 -04:00
|
|
|
|
|
2021-06-07 00:34:26 -04:00
|
|
|
|
NSString *machine = [d objectForKey: @"machine"];
|
|
|
|
|
if (!machine) return;
|
|
|
|
|
|
|
|
|
|
_loadingBookmark = YES;
|
|
|
|
|
[_machineViewController willLoadBookmark: d];
|
|
|
|
|
[_slotController willLoadBookmark: d];
|
|
|
|
|
[_mediaController willLoadBookmark: d];
|
|
|
|
|
|
|
|
|
|
[self reset];
|
2021-07-02 17:16:05 -04:00
|
|
|
|
[self resetSoftware];
|
2021-06-07 00:34:26 -04:00
|
|
|
|
|
2021-06-09 22:47:00 -04:00
|
|
|
|
[self setMachine: machine];
|
2021-06-07 00:34:26 -04:00
|
|
|
|
[self updateSoftwareList];
|
|
|
|
|
[_softwareListControl setObjectValue: nil]; // will reload the completion list.
|
|
|
|
|
|
|
|
|
|
NSString *str;
|
|
|
|
|
|
|
|
|
|
str = [d objectForKey: @"software"];
|
|
|
|
|
if ([str isKindOfClass: StringClass]) {
|
|
|
|
|
Software *s = [_softwareSet softwareForName: str];
|
|
|
|
|
if (s) {
|
|
|
|
|
[_softwareListControl setObjectValue: s];
|
|
|
|
|
[self setSoftware: s];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Boolean values.
|
|
|
|
|
NSNumber *n;
|
2021-06-12 14:02:11 -04:00
|
|
|
|
#undef _
|
2021-06-07 00:34:26 -04:00
|
|
|
|
#define _(a,b) n = [d objectForKey: a]; if ([n isKindOfClass: NumberClass]) [self b : [n boolValue]]
|
|
|
|
|
|
|
|
|
|
_(@"debug", setMameDebug);
|
2021-07-14 22:38:15 -04:00
|
|
|
|
_(@"rewind", setMameRewind);
|
2021-06-07 00:34:26 -04:00
|
|
|
|
_(@"squarePixels", setMameSquarePixels);
|
|
|
|
|
_(@"mouse", setMameMouse);
|
|
|
|
|
_(@"samples", setMameSamples);
|
|
|
|
|
_(@"bgfx", setMameBGFX);
|
|
|
|
|
|
|
|
|
|
// numeric values
|
|
|
|
|
// check if in range?
|
|
|
|
|
#undef _
|
|
|
|
|
#define _(a,b) n = [d objectForKey: a]; if ([n isKindOfClass: NumberClass]) [self b : [n intValue]]
|
|
|
|
|
|
|
|
|
|
_(@"windowMode", setMameWindowMode);
|
|
|
|
|
_(@"speed", setMameSpeed);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// string values
|
|
|
|
|
#undef _
|
|
|
|
|
#define _(a,b) str = [d objectForKey: a]; if ([str isKindOfClass: StringClass]) [self b : str]
|
|
|
|
|
|
|
|
|
|
_(@"shareDirectory", setMameShareDirectoryPath);
|
|
|
|
|
_(@"bitBanger", setMameBitBangerPath);
|
|
|
|
|
if ([_mameShareDirectoryPath length]) [self setMameShareDirectory: YES];
|
|
|
|
|
if ([_mameBitBangerPath length]) [self setMameBitBanger: YES];
|
|
|
|
|
|
|
|
|
|
_(@"AVIPath", setMameAVIPath);
|
|
|
|
|
_(@"WAVPath", setMameWAVPath);
|
|
|
|
|
_(@"VGMPath", setMameVGMPath);
|
|
|
|
|
if ([_mameAVIPath length]) [self setMameAVI: YES];
|
|
|
|
|
if ([_mameVGMPath length]) [self setMameVGM: YES];
|
|
|
|
|
if ([_mameWAVPath length]) [self setMameWAV: YES];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
str = [d objectForKey: @"backend"];
|
|
|
|
|
if ([str isKindOfClass: [NSString class]]) {
|
|
|
|
|
int ix = BackEndIndex(str);
|
|
|
|
|
if (ix >= 0) [self setMameBackend: ix];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
str = [d objectForKey: @"effects"];
|
|
|
|
|
if ([str isKindOfClass: [NSString class]]) {
|
|
|
|
|
int ix = EffectsIndex(str);
|
|
|
|
|
if (ix >= 0) [self setMameEffects: ix];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[_machineViewController loadBookmark: d];
|
|
|
|
|
[_slotController loadBookmark: d];
|
|
|
|
|
[_mediaController loadBookmark: d];
|
|
|
|
|
|
|
|
|
|
[_machineViewController didLoadBookmark: d];
|
|
|
|
|
[_slotController didLoadBookmark: d];
|
|
|
|
|
[_mediaController didLoadBookmark: d];
|
|
|
|
|
|
|
|
|
|
_loadingBookmark = NO;
|
|
|
|
|
|
|
|
|
|
[self buildCommandLine];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(NSDictionary *)makeBookmark {
|
|
|
|
|
|
2021-11-16 22:44:38 -05:00
|
|
|
|
[self defocus];
|
2021-06-07 00:34:26 -04:00
|
|
|
|
|
|
|
|
|
NSMutableDictionary *dict = [NSMutableDictionary new];
|
|
|
|
|
|
2021-06-09 22:47:00 -04:00
|
|
|
|
[dict setObject: _machine forKey: @"machine"];
|
2021-06-07 00:34:26 -04:00
|
|
|
|
[dict setObject: @232 forKey: @"version"];
|
|
|
|
|
[_machineViewController saveBookmark: dict];
|
|
|
|
|
[_slotController saveBookmark: dict];
|
|
|
|
|
[_mediaController saveBookmark: dict];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Boolean values
|
|
|
|
|
#undef _
|
|
|
|
|
#define _(v,k) [dict setObject: v ? (NSObject *)kCFBooleanTrue : (NSObject *)kCFBooleanFalse forKey: k]
|
|
|
|
|
|
|
|
|
|
_(_mameDebug, @"debug");
|
2021-07-14 22:38:15 -04:00
|
|
|
|
_(_mameRewind, @"rewind");
|
2021-06-07 00:34:26 -04:00
|
|
|
|
_(_mameSquarePixels, @"squarePixels");
|
|
|
|
|
_(_mameMouse, @"mouse");
|
|
|
|
|
_(_mameSamples, @"samples");
|
|
|
|
|
_(_mameBGFX, @"bgfx");
|
|
|
|
|
|
|
|
|
|
// numeric values
|
|
|
|
|
#undef _
|
|
|
|
|
#define _(v,k) [dict setObject: @(v) forKey: k]
|
|
|
|
|
_(_mameWindowMode, @"windowMode");
|
|
|
|
|
_(_mameSpeed, @"speed");
|
|
|
|
|
|
|
|
|
|
// String values
|
|
|
|
|
#undef _
|
|
|
|
|
#define _(v,k) [dict setObject: v forKey: k]
|
|
|
|
|
|
|
|
|
|
if (_mameAVI && [_mameAVIPath length]) _(_mameAVIPath, @"AVIPath");
|
|
|
|
|
if (_mameWAV && [_mameWAVPath length]) _(_mameWAVPath, @"WAVPath");
|
|
|
|
|
if (_mameVGM && [_mameVGMPath length]) _(_mameVGMPath, @"VGMPath");
|
|
|
|
|
|
|
|
|
|
if (_mameShareDirectory && [_mameShareDirectoryPath length]) _(_mameShareDirectoryPath, @"shareDirectory");
|
|
|
|
|
if (_mameBitBanger && [_mameBitBangerPath length]) _(_mameBitBangerPath, @"bitBanger");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (_software) _([_software fullName], @"software");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (_mameBackend) _(BackendStrings[_mameBackend], @"backend");
|
|
|
|
|
if (_mameEffects) _(EffectsStrings[_mameEffects], @"effects");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return dict;
|
|
|
|
|
|
|
|
|
|
#undef _
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-06-18 21:57:46 -04:00
|
|
|
|
#pragma mark - NSMenuDelegate
|
|
|
|
|
|
2021-06-19 22:05:16 -04:00
|
|
|
|
#if 1
|
2021-06-18 21:57:46 -04:00
|
|
|
|
-(void)menuNeedsUpdate:(NSMenu *)menu {
|
|
|
|
|
NSEventModifierFlags modifiers = [NSEvent modifierFlags];
|
|
|
|
|
|
|
|
|
|
[self setOptionKey: modifiers & NSEventModifierFlagOption ? YES : NO];
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-19 22:05:16 -04:00
|
|
|
|
#else
|
|
|
|
|
/* doesn't trigger when menu is the first responder. */
|
|
|
|
|
-(void)flagsChanged:(NSEvent *)event {
|
|
|
|
|
NSEventModifierFlags modifiers = [event modifierFlags];
|
|
|
|
|
|
|
|
|
|
[self setOptionKey: modifiers & NSEventModifierFlagOption ? YES : NO];
|
|
|
|
|
|
|
|
|
|
[super flagsChanged: event];
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-06-07 00:34:26 -04:00
|
|
|
|
@end
|