diff options
| author | SND\MaddTheSane_cp <SND\MaddTheSane_cp@e17a0e51-4ae3-4d35-97c3-1a29b211df97> | 2014-07-20 05:09:43 +0000 |
|---|---|---|
| committer | SND\MaddTheSane_cp <SND\MaddTheSane_cp@e17a0e51-4ae3-4d35-97c3-1a29b211df97> | 2014-07-20 05:09:43 +0000 |
| commit | d6942932d64a02aa92b1e04e91f6126f33fdb05e (patch) | |
| tree | 7cad698308e39abc2b0e1c71674c610ec3ce74dd /macosx/Source | |
| parent | b8d0d24d56dbc0ee64f4ec9a72ab917604d8109d (diff) | |
OS X: Move source files to their own folder.
git-svn-id: https://pcsxr.svn.codeplex.com/svn/pcsxr@90999 e17a0e51-4ae3-4d35-97c3-1a29b211df97
Diffstat (limited to 'macosx/Source')
48 files changed, 5883 insertions, 0 deletions
diff --git a/macosx/Source/CheatController.h b/macosx/Source/CheatController.h new file mode 100644 index 00000000..c56f2a68 --- /dev/null +++ b/macosx/Source/CheatController.h @@ -0,0 +1,53 @@ +// +// CheatController.h +// Pcsxr +// + +#import <Cocoa/Cocoa.h> +#import "PcsxrHexadecimalFormatter.h" +#include "psxcommon.h" +#include "cheat.h" + +@interface PcsxrCheatTempObject : NSObject <NSCopying> +@property (readwrite) uint32_t cheatAddress; +@property (readwrite) uint16_t cheatValue; + +- (instancetype)initWithAddress:(uint32_t)add value:(uint16_t)val; +- (instancetype)initWithCheatCode:(CheatCode *)theCheat; +@end + +@interface PcsxrCheatTemp : NSObject +@property (readwrite, strong) NSMutableArray *cheatValues; +@property (readwrite, strong) NSString *cheatName; +@property (readwrite, getter = isEnabled) BOOL enabled; + +- (instancetype)initWithCheat:(Cheat *)theCheat; +@end + +@interface CheatController : NSWindowController <NSWindowDelegate, NSTableViewDelegate> + +@property (weak) IBOutlet NSTableView *cheatView; +@property (weak) IBOutlet NSWindow *editCheatWindow; +@property (weak) IBOutlet NSTableView *editCheatView; +@property (weak) IBOutlet PcsxrHexadecimalFormatter *addressFormatter; +@property (weak) IBOutlet PcsxrHexadecimalFormatter *valueFormatter; + +@property (readwrite, strong) NSMutableArray *tempCheatCodes; +@property (readwrite, strong) NSMutableArray *cheats; + +- (void)refresh; + +- (IBAction)saveCheats:(id)sender; +- (IBAction)loadCheats:(id)sender; +- (IBAction)clear:(id)sender; +- (IBAction)editCheat:(id)sender; +- (IBAction)addCheat:(id)sender; +- (IBAction)applyCheats:(id)sender; +- (IBAction)removeCheats:(id)sender; +- (IBAction)changeCheat:(id)sender; + +- (IBAction)closeCheatEdit:(id)sender; +- (IBAction)removeCheatValue:(id)sender; +- (IBAction)addCheatValue:(id)sender; + +@end diff --git a/macosx/Source/CheatController.m b/macosx/Source/CheatController.m new file mode 100644 index 00000000..ebe73137 --- /dev/null +++ b/macosx/Source/CheatController.m @@ -0,0 +1,324 @@ +// +// CheatController.m +// Pcsxr +// + +#import <Cocoa/Cocoa.h> +#import "CheatController.h" +#import "PcsxrCheatHandler.h" +#import "PcsxrHexadecimalFormatter.h" + +#define kTempCheatCodesName @"tempCheatCodes" +#define kCheatsName @"cheats" + +@implementation PcsxrCheatTempObject +@synthesize cheatAddress, cheatValue; + +- (instancetype)init +{ + return [self initWithAddress:0x10000000 value:0]; +} + +- (instancetype)initWithAddress:(uint32_t)add value:(uint16_t)val +{ + if (self = [super init]) { + self.cheatAddress = add; + self.cheatValue = val; + } + return self; +} + +- (instancetype)initWithCheatCode:(CheatCode *)theCheat +{ + return [self initWithAddress:theCheat->Addr value:theCheat->Val]; +} + +- (NSString*)description +{ + return [NSString stringWithFormat:@"%08x %04x", cheatAddress, cheatValue]; +} + +- (BOOL)isEqual:(id)object +{ + if ([object isKindOfClass:[PcsxrCheatTempObject class]]) { + if (cheatAddress != [object cheatAddress]) { + return NO; + } else if (cheatValue != [object cheatValue]) { + return NO; + } else + return YES; + } else + return NO; +} + +- (NSUInteger)hash +{ + return cheatAddress ^ cheatValue; +} + +- (id)copyWithZone:(NSZone *)zone +{ + return [[[self class] allocWithZone:zone] initWithAddress:cheatAddress value:cheatValue]; +} + +@end + +@implementation PcsxrCheatTemp +@synthesize cheatName; +@synthesize cheatValues; +@synthesize enabled; + +- (instancetype)initWithCheat:(Cheat *)theCheat +{ + if (self = [super init]) { + self.cheatName = @(theCheat->Descr); + self.enabled = theCheat->Enabled ? YES : NO; + self.cheatValues = [NSMutableArray arrayWithCapacity:theCheat->n]; + for (int i = 0; i < theCheat->n; i++) { + [cheatValues addObject:[[PcsxrCheatTempObject alloc] initWithCheatCode:&CheatCodes[i+theCheat->First]]]; + } + } + return self; +} + +- (NSUInteger)hash +{ + return [cheatName hash] ^ [cheatValues hash]; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"[%@%@]\n%@", enabled ? @"*" : @"", cheatName, [cheatValues componentsJoinedByString:@"\n"]]; +} + +@end + +@implementation CheatController +@synthesize addressFormatter; +@synthesize cheatView; +@synthesize editCheatView; +@synthesize editCheatWindow; +@synthesize valueFormatter; + +- (NSString *)windowNibName +{ + return @"CheatWindow"; +} + +- (instancetype)init +{ + return self = [self initWithWindowNibName:@"CheatWindow"]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + if (self = [super initWithCoder:aDecoder]) { + self.tempCheatCodes = [NSMutableArray array]; + } + return self; +} + +- (instancetype)initWithWindow:(NSWindow *)window +{ + if (self = [super initWithWindow:window]) { + self.tempCheatCodes = [NSMutableArray array]; + } + return self; +} + +- (void)refreshNSCheatArray +{ + NSMutableArray *tmpArray = [[NSMutableArray alloc] initWithCapacity:NumCheats]; + for (int i = 0; i < NumCheats; i++) { + PcsxrCheatTemp *tmpObj = [[PcsxrCheatTemp alloc] initWithCheat:&Cheats[i]]; + [tmpArray addObject:tmpObj]; + } + self.cheats = tmpArray; + [self setDocumentEdited:NO]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if ([keyPath isEqualToString:kCheatsName]) { + [self setDocumentEdited:YES]; + } +} + +- (void)refresh +{ + [cheatView reloadData]; + [self refreshNSCheatArray]; +} + +- (void)awakeFromNib +{ + [valueFormatter setHexPadding:4]; + [addressFormatter setHexPadding:8]; + [self refreshNSCheatArray]; + [self addObserver:self forKeyPath:kCheatsName options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL]; +} + +- (IBAction)loadCheats:(id)sender +{ + NSOpenPanel *openDlg = [NSOpenPanel openPanel]; + [openDlg setAllowsMultipleSelection:NO]; + [openDlg setAllowedFileTypes:[PcsxrCheatHandler supportedUTIs]]; + + if ([openDlg runModal] == NSFileHandlingPanelOKButton) { + NSURL *file = [openDlg URL]; + LoadCheats([[file path] fileSystemRepresentation]); + [self refresh]; + } +} + +- (IBAction)saveCheats:(id)sender +{ + NSSavePanel *saveDlg = [NSSavePanel savePanel]; + [saveDlg setAllowedFileTypes:[PcsxrCheatHandler supportedUTIs]]; + [saveDlg setCanSelectHiddenExtension:YES]; + [saveDlg setCanCreateDirectories:YES]; + [saveDlg setPrompt:NSLocalizedString(@"Save Cheats", nil)]; + if ([saveDlg runModal] == NSFileHandlingPanelOKButton) { + NSURL *url = [saveDlg URL]; + NSString *saveString = [self.cheats componentsJoinedByString:@"\n"]; + [saveString writeToURL:url atomically:YES encoding:NSUTF8StringEncoding error:NULL]; + } +} + +- (IBAction)clear:(id)sender +{ + self.cheats = [[NSMutableArray alloc] init]; +} + +- (IBAction)closeCheatEdit:(id)sender +{ + [NSApp endSheet:editCheatWindow returnCode:[sender tag] == 1 ? NSCancelButton : NSOKButton]; +} + +- (IBAction)changeCheat:(id)sender +{ + [self setDocumentEdited:YES]; +} + +- (IBAction)removeCheatValue:(id)sender +{ + if ([editCheatView selectedRow] < 0) { + NSBeep(); + return; + } + + NSIndexSet *toRemoveIndex = [editCheatView selectedRowIndexes]; + [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:toRemoveIndex forKey:kTempCheatCodesName]; + [self.tempCheatCodes removeObjectsAtIndexes:toRemoveIndex]; + [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:toRemoveIndex forKey:kTempCheatCodesName]; +} + +- (IBAction)addCheatValue:(id)sender +{ + NSIndexSet *newSet = [NSIndexSet indexSetWithIndex:[self.tempCheatCodes count]]; + [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:newSet forKey:kTempCheatCodesName]; + [self.tempCheatCodes addObject:[[PcsxrCheatTempObject alloc] init]]; + [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:newSet forKey:kTempCheatCodesName]; +} + +- (void)reloadCheats +{ + NSFileManager *manager = [NSFileManager defaultManager]; + NSURL *tmpURL = [[manager URLForDirectory:NSItemReplacementDirectory inDomain:NSUserDomainMask appropriateForURL:[[NSBundle mainBundle] bundleURL] create:YES error:nil] URLByAppendingPathComponent:@"temp.cht" isDirectory:NO]; + NSString *tmpStr = [self.cheats componentsJoinedByString:@"\n"]; + [tmpStr writeToURL:tmpURL atomically:NO encoding:NSUTF8StringEncoding error:NULL]; + LoadCheats([[tmpURL path] fileSystemRepresentation]); + [manager removeItemAtURL:tmpURL error:NULL]; +} + +- (IBAction)editCheat:(id)sender +{ + if ([cheatView selectedRow] < 0) { + NSBeep(); + return; + } + NSMutableArray *tmpArray = [(self.cheats)[[cheatView selectedRow]] cheatValues]; + NSMutableArray *newCheats = [[NSMutableArray alloc] initWithArray:tmpArray copyItems:YES]; + self.tempCheatCodes = newCheats; + [[self window] beginSheet:editCheatWindow completionHandler:^(NSModalResponse returnCode) { + if (returnCode == NSOKButton) { + PcsxrCheatTemp *tmpCheat = (self.cheats)[[cheatView selectedRow]]; + if (![tmpCheat.cheatValues isEqualToArray:self.tempCheatCodes]) { + tmpCheat.cheatValues = self.tempCheatCodes; + [self setDocumentEdited:YES]; + } + } + + [editCheatWindow orderOut:nil]; + }]; +} + +- (IBAction)addCheat:(id)sender +{ + NSIndexSet *newSet = [NSIndexSet indexSetWithIndex:[self.cheats count]]; + [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:newSet forKey:kCheatsName]; + PcsxrCheatTemp *tmpCheat = [[PcsxrCheatTemp alloc] init]; + tmpCheat.cheatName = NSLocalizedString(@"New Cheat", @"New Cheat Name" ); + PcsxrCheatTempObject *tmpObj = [[PcsxrCheatTempObject alloc] initWithAddress:0x10000000 value:0]; + NSMutableArray *tmpArray = [NSMutableArray arrayWithObject:tmpObj]; + tmpCheat.cheatValues = tmpArray; + [self.cheats addObject:tmpCheat]; + [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:newSet forKey:kCheatsName]; + [self setDocumentEdited:YES]; +} + +- (IBAction)applyCheats:(id)sender +{ + [self reloadCheats]; + [self setDocumentEdited:NO]; +} + +- (void)sheetDidDismiss:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + switch (returnCode) { + case NSAlertDefaultReturn: + [self reloadCheats]; + [self close]; + break; + + default: + [self refreshNSCheatArray]; + [self close]; + break; + + case NSAlertOtherReturn: + break; + } +} + +- (BOOL)windowShouldClose:(id)sender +{ + if (![sender isDocumentEdited] || ![[self window] isEqual:sender]) { + return YES; + } else { + NSBeginAlertSheet(NSLocalizedString(@"Unsaved Changes", @"Unsaved changes"), + NSLocalizedString(@"Save", @"Save"), + NSLocalizedString(@"Don't Save",@"Don't Save"), + NSLocalizedString(@"Cancel", @"Cancel"), [self window], self, + NULL, @selector(sheetDidDismiss:returnCode:contextInfo:), NULL, + NSLocalizedString(@"The cheat codes have not been applied. Unapplied cheats will not run nor be saved. Do you wish to save?", nil)); + + return NO; + } +} + +- (IBAction)removeCheats:(id)sender +{ + if ([cheatView selectedRow] < 0) { + NSBeep(); + return; + } + + NSIndexSet *toRemoveIndex = [cheatView selectedRowIndexes]; + [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:toRemoveIndex forKey:kCheatsName]; + [self.cheats removeObjectsAtIndexes:toRemoveIndex]; + [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:toRemoveIndex forKey:kCheatsName]; + [self setDocumentEdited:YES]; +} + +@end diff --git a/macosx/Source/ConfigurationController.h b/macosx/Source/ConfigurationController.h new file mode 100644 index 00000000..4406a7a2 --- /dev/null +++ b/macosx/Source/ConfigurationController.h @@ -0,0 +1,57 @@ +/* ConfigurationController */ + +#import <Cocoa/Cocoa.h> +#import "PluginController.h" +#import "HotkeyController.h" +#import "PluginList.h" + +extern NSString *const memChangeNotifier; +extern NSString *const memCardChangeNumberKey; + +@class PcsxrMemCardController; + +@interface ConfigurationController : NSWindowController <NSWindowDelegate, NSTabViewDelegate> +{ + IBOutlet PluginController *cdromPlugin; + IBOutlet PluginController *graphicsPlugin; + IBOutlet PluginController *padPlugin; + IBOutlet PluginController *soundPlugin; + IBOutlet PluginController *netPlugin; + IBOutlet PluginController *sio1Plugin; + + IBOutlet PcsxrMemCardController *memCardEdit; + + // Hotkeys + IBOutlet HotkeyController *hkController; + IBOutlet NSTabViewItem *hkTab; +} + +@property (weak) IBOutlet NSButtonCell *noXaAudioCell; +@property (weak) IBOutlet NSButtonCell *sioIrqAlwaysCell; +@property (weak) IBOutlet NSButtonCell *bwMdecCell; +@property (weak) IBOutlet NSButtonCell *autoVTypeCell; +@property (weak) IBOutlet NSPopUpButton *vTypePALCell; +@property (weak) IBOutlet NSButtonCell *noCDAudioCell; +@property (weak) IBOutlet NSButtonCell *usesHleCell; +@property (weak) IBOutlet NSButtonCell *usesDynarecCell; +@property (weak) IBOutlet NSButtonCell *consoleOutputCell; +@property (weak) IBOutlet NSButtonCell *spuIrqAlwaysCell; +@property (weak) IBOutlet NSButtonCell *rCountFixCell; +@property (weak) IBOutlet NSButtonCell *vSyncWAFixCell; +@property (weak) IBOutlet NSButtonCell *noFastBootCell; +@property (weak) IBOutlet NSButtonCell *enableNetPlayCell; +@property (weak) IBOutlet NSButtonCell *widescreen; + +- (IBAction)setCheckbox:(id)sender; +- (IBAction)setCheckboxInverse:(id)sender; +- (IBAction)setVideoType:(id)sender; + ++ (void)setMemoryCard:(NSInteger)theCard toPath:(NSString *)theFile; ++ (void)setMemoryCard:(NSInteger)theCard toURL:(NSURL *)theURL; + +- (IBAction)mcdNewClicked:(id)sender; +- (IBAction)mcdChangeClicked:(id)sender; + +- (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem; + +@end diff --git a/macosx/Source/ConfigurationController.m b/macosx/Source/ConfigurationController.m new file mode 100644 index 00000000..aef8f7f4 --- /dev/null +++ b/macosx/Source/ConfigurationController.m @@ -0,0 +1,291 @@ +#import "ConfigurationController.h" +#import "PcsxrController.h" +#import "PluginList.h" +#import "PcsxrPlugin.h" +#import "PcsxrMemCardController.h" +#import "PcsxrMemCardHandler.h" +#include "psxcommon.h" +#include "plugins.h" + +NSString *const memChangeNotifier = @"PcsxrMemoryCardDidChangeNotifier"; +NSString *const memCardChangeNumberKey = @"PcsxrMemoryCardThatChangedKey"; + +@interface ConfigurationController () +@property (strong) NSMutableDictionary *checkBoxDefaults; +- (NSString *)keyForSender:(id)sender; +@end + +@implementation ConfigurationController +@synthesize autoVTypeCell; +@synthesize bwMdecCell; +@synthesize checkBoxDefaults = _checkBoxDefaults; +@synthesize consoleOutputCell; +@synthesize enableNetPlayCell; +@synthesize noCDAudioCell; +@synthesize noFastBootCell; +@synthesize noXaAudioCell; +@synthesize rCountFixCell; +@synthesize sioIrqAlwaysCell; +@synthesize spuIrqAlwaysCell; +@synthesize usesDynarecCell; +@synthesize usesHleCell; +@synthesize vSyncWAFixCell; +@synthesize vTypePALCell; +@synthesize widescreen; + ++ (void)setMemoryCard:(NSInteger)theCard toURL:(NSURL *)theURL; +{ + if (theCard == 1) { + [[NSUserDefaults standardUserDefaults] setURL:theURL forKey:@"Mcd1"]; + strlcpy(Config.Mcd1, [[theURL path] fileSystemRepresentation], MAXPATHLEN ); + } else { + [[NSUserDefaults standardUserDefaults] setURL:theURL forKey:@"Mcd2"]; + strlcpy(Config.Mcd2, [[theURL path] fileSystemRepresentation], MAXPATHLEN ); + } + + [[NSNotificationCenter defaultCenter] postNotificationName:memChangeNotifier object:nil userInfo: + @{memCardChangeNumberKey: @(theCard)}]; +} + ++ (void)setMemoryCard:(NSInteger)theCard toPath:(NSString *)theFile +{ + [self setMemoryCard:theCard toURL:[NSURL fileURLWithPath:theFile isDirectory:NO]]; +} + +- (IBAction)setCheckbox:(id)sender +{ + if ([sender isKindOfClass:[NSMatrix class]]) { + sender = [sender selectedCell]; + } + + NSString *key = [self keyForSender:sender]; + if (key) { + [[NSUserDefaults standardUserDefaults] setBool:[sender intValue] ? YES : NO forKey:key]; + [PcsxrController setConfigFromDefaults]; + } +} + +- (IBAction)setCheckboxInverse:(id)sender +{ + if ([sender isKindOfClass:[NSMatrix class]]) { + sender = [sender selectedCell]; + } + + NSString *key = [self keyForSender:sender]; + if (key) { + [[NSUserDefaults standardUserDefaults] setBool:[sender intValue] ? NO : YES forKey:key]; + [PcsxrController setConfigFromDefaults]; + } +} + +- (IBAction)mcdChangeClicked:(id)sender +{ + NSInteger tag = [sender tag]; + char *mcd; + NSOpenPanel *openDlg = [NSOpenPanel openPanel]; + NSString *path; + + if (tag == 1) { + mcd = Config.Mcd1; + } else { + mcd = Config.Mcd2; + } + + path = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:mcd length:strlen(mcd)]; + + [openDlg setAllowedFileTypes:[PcsxrMemCardHandler supportedUTIs]]; + [openDlg setDirectoryURL:[NSURL fileURLWithPath:[path stringByDeletingLastPathComponent] isDirectory:YES]]; + [openDlg setNameFieldStringValue:[path lastPathComponent]]; + [openDlg beginSheetModalForWindow:[self window] completionHandler:^(NSInteger result) { + if (result == NSFileHandlingPanelOKButton) { + NSURL *mcdURL = [openDlg URLs][0]; + + [ConfigurationController setMemoryCard:tag toURL:mcdURL]; + } + }]; +} + +- (IBAction)mcdNewClicked:(id)sender +{ + NSInteger tag = [sender tag]; + char *mcd; + NSSavePanel *openDlg = [NSSavePanel savePanel]; + NSString *path; + + if (tag == 1) { + mcd = Config.Mcd1; + } else { + mcd = Config.Mcd2; + } + + path = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:mcd length:strlen(mcd)]; + + [openDlg setDirectoryURL:[NSURL fileURLWithPath:[path stringByDeletingLastPathComponent] isDirectory:YES]]; + [openDlg setNameFieldStringValue:NSLocalizedString(@"New Memory Card.mcd", nil)]; + [openDlg setAllowedFileTypes:[PcsxrMemCardHandler supportedUTIs]]; + + [openDlg beginSheetModalForWindow:[self window] completionHandler:^(NSInteger result) { + if (result == NSFileHandlingPanelOKButton) { + NSURL *mcdURL = [openDlg URL]; + const char *fileSysRep; + + if ([mcdURL respondsToSelector:@selector(fileSystemRepresentation)]) { + fileSysRep = [mcdURL fileSystemRepresentation]; + } else { + fileSysRep = [[mcdURL path] fileSystemRepresentation]; + } + + //Workaround/kludge to make sure we create a memory card before posting a notification + strlcpy(mcd, fileSysRep, MAXPATHLEN); + CreateMcd(mcd); + + [ConfigurationController setMemoryCard:tag toURL:mcdURL]; + } + }]; +} + +- (IBAction)setVideoType:(id)sender +{ + NSInteger tag = [[sender selectedItem] tag]; + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + + if (3 == tag) { + [defaults setBool:YES forKey:@"AutoDetectVideoType"]; + } else if (1 == tag || 2 == tag) { + [defaults setBool:NO forKey:@"AutoDetectVideoType"]; + [defaults setBool:tag==2 forKey:@"VideoTypePAL"]; + } else { + return; + } + [PcsxrController setConfigFromDefaults]; + + if ([sender pullsDown]) { + NSArray *items = [sender itemArray]; + for (id object in items) { + [object setState:NSOffState]; + } + + [[sender selectedItem] setState:NSOnState]; + } +} + +- (void)windowWillClose:(NSNotification *)notification +{ + [memCardEdit stopMemoryAnimation]; +} + +- (void)windowDidBecomeMain:(NSNotification *)notification +{ + [memCardEdit beginMemoryAnimation]; +} + +- (void)awakeFromNib +{ + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + + [[self window] center]; + + // setup checkboxes + self.checkBoxDefaults = [[NSMutableDictionary alloc] init]; + + // check that the outlets are active before adding them + if (noXaAudioCell) + _checkBoxDefaults[@"NoXaAudio"] = noXaAudioCell; + if (enableNetPlayCell) + _checkBoxDefaults[@"NetPlay"] = enableNetPlayCell; + if (sioIrqAlwaysCell) + _checkBoxDefaults[@"SioIrqAlways"] = sioIrqAlwaysCell; + if (bwMdecCell) + _checkBoxDefaults[@"BlackAndWhiteMDECVideo"] = bwMdecCell; + if (autoVTypeCell) + _checkBoxDefaults[@"AutoDetectVideoType"] = autoVTypeCell; + if (vTypePALCell) + _checkBoxDefaults[@"VideoTypePAL"] = vTypePALCell; + if (noCDAudioCell) + _checkBoxDefaults[@"NoCDAudio"] = noCDAudioCell; + if (usesHleCell) + _checkBoxDefaults[@"UseHLE"] = usesHleCell; + if (usesDynarecCell) + _checkBoxDefaults[@"NoDynarec"] = usesDynarecCell; + if (consoleOutputCell) + _checkBoxDefaults[@"ConsoleOutput"] = consoleOutputCell; + if (spuIrqAlwaysCell) + _checkBoxDefaults[@"SpuIrqAlways"] = spuIrqAlwaysCell; + if (rCountFixCell) + _checkBoxDefaults[@"RootCounterFix"] = rCountFixCell; + if (vSyncWAFixCell) + _checkBoxDefaults[@"VideoSyncWAFix"] = vSyncWAFixCell; + if (noFastBootCell) + _checkBoxDefaults[@"NoFastBoot"] = noFastBootCell; + if (widescreen) + _checkBoxDefaults[@"Widescreen"] = widescreen; + + // make the visuals match the defaults + + for (NSString* key in _checkBoxDefaults) { + if ([defaults integerForKey:key]) { + [_checkBoxDefaults[key] setNextState]; + } + } + + // special cases + if (![PcsxrController biosAvailable]) { + // no bios means always use HLE + [usesHleCell setState:NSOnState]; + [usesHleCell setEnabled:NO]; + } + + + // setup labels + + NSInteger tag = [defaults integerForKey:@"AutoDetectVideoType"]; + if (tag) + tag = 3; + else { + tag = [defaults integerForKey:@"VideoTypePAL"]+1; + } + [vTypePALCell setAutoenablesItems:NO]; + if ([vTypePALCell pullsDown]) { + [[vTypePALCell itemAtIndex:[vTypePALCell indexOfItemWithTag:tag]] setState:NSOnState]; + } else { + [vTypePALCell selectItemAtIndex:[vTypePALCell indexOfItemWithTag:tag]]; + } + + // setup plugin lists + PluginList *list = [PluginList list]; + + [list refreshPlugins]; + [graphicsPlugin setPluginsTo:[list pluginsForType:PSE_LT_GPU] withType: PSE_LT_GPU]; + [soundPlugin setPluginsTo:[list pluginsForType:PSE_LT_SPU] withType: PSE_LT_SPU]; + [padPlugin setPluginsTo:[list pluginsForType:PSE_LT_PAD] withType: PSE_LT_PAD]; + [cdromPlugin setPluginsTo:[list pluginsForType:PSE_LT_CDR] withType: PSE_LT_CDR]; + [netPlugin setPluginsTo:[list pluginsForType:PSE_LT_NET] withType: PSE_LT_NET]; + [sio1Plugin setPluginsTo:[list pluginsForType:PSE_LT_SIO1] withType:PSE_LT_SIO1]; + + // Setup hotkey view + [hkController initialize]; +} + +- (NSString *)keyForSender:(id)sender +{ + for (NSString *key in [self.checkBoxDefaults keyEnumerator]) { + id object = (self.checkBoxDefaults)[key]; + if ([object isEqual:sender]) + return key; + } + + return nil; +} + +- (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem +{ + NSWindow *window = [self window]; + if(tabViewItem == hkTab) { + [window makeFirstResponder:(NSView*)hkController]; + } + else if([window firstResponder] == (NSView*)hkController) { + [hkController resignFirstResponder]; + } +} + +@end diff --git a/macosx/Source/EmuThread.h b/macosx/Source/EmuThread.h new file mode 100644 index 00000000..affab393 --- /dev/null +++ b/macosx/Source/EmuThread.h @@ -0,0 +1,40 @@ +// +// EmuThread.h +// Pcsxr +// +// Created by Gil Pedersen on Sun Sep 21 2003. +// Copyright (c) 2003 __MyCompanyName__. All rights reserved. +// + +#import <Foundation/Foundation.h> +#include <setjmp.h> + +@interface EmuThread : NSObject { + jmp_buf restartJmp; + BOOL wasPaused; +} + +- (void)EmuThreadRun:(id)anObject; +- (void)EmuThreadRunBios:(id)anObject; +- (void)handleEvents; + ++ (void)run; ++ (void)runBios; ++ (void)stop; ++ (BOOL)pause; ++ (BOOL)pauseSafe; ++ (void)pauseSafeWithBlock:(void (^)(BOOL))theBlock; ++ (void)resume; ++ (void)resetNow; ++ (void)reset; + ++ (BOOL)isPaused; ++ (BOOL)active; ++ (BOOL)isRunBios; + ++ (void)freezeAt:(NSString *)path which:(int)num; ++ (BOOL)defrostAt:(NSString *)path; + +@end + +extern EmuThread *emuThread; diff --git a/macosx/Source/EmuThread.m b/macosx/Source/EmuThread.m new file mode 100644 index 00000000..f4404c8e --- /dev/null +++ b/macosx/Source/EmuThread.m @@ -0,0 +1,396 @@ +// +// EmuThread.m +// Pcsxr +// +// Created by Gil Pedersen on Sun Sep 21 2003. +// Copyright (c) 2003 __MyCompanyName__. All rights reserved. +// + +#import <ExceptionHandling/NSExceptionHandler.h> +#import <Cocoa/Cocoa.h> +#include <pthread.h> +#include <setjmp.h> +#import "EmuThread.h" +#include "psxcommon.h" +#include "plugins.h" +#include "misc.h" + +EmuThread *emuThread = nil; +static NSString *defrostPath = nil; +static int safeEvent; +static BOOL paused; +static BOOL runbios; + +static pthread_cond_t eventCond; +static pthread_mutex_t eventMutex; + +#define EMUEVENT_NONE 0 +#define EMUEVENT_PAUSE (1<<0) +#define EMUEVENT_RESET (1<<1) +#define EMUEVENT_STOP (1<<2) + +@implementation EmuThread + +- (void)setUpThread +{ + NSAssert(![[NSThread currentThread] isEqual:[NSThread mainThread]], @"This function should not be run on the main thread!"); + + [[NSThread currentThread] setName:@"PSX Emu Background thread"]; + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + [center addObserver:self + selector:@selector(emuWindowDidClose:) + name:@"emuWindowDidClose" object:nil]; + + [center addObserver:self + selector:@selector(emuWindowWantPause:) + name:@"emuWindowWantPause" object:nil]; + + [center addObserver:self + selector:@selector(emuWindowWantResume:) + name:@"emuWindowWantResume" object:nil]; + + // we shouldn't change the priority, since we might depend on subthreads + //[NSThread setThreadPriority:1.0-((1.0-[NSThread threadPriority])/4.0)]; +} + +- (void)EmuThreadRun:(id)anObject +{ + [self setUpThread]; + + // Do processing here + if (OpenPlugins() == -1) + goto done; + + setjmp(restartJmp); + + int res = CheckCdrom(); + if (res == -1) { + ClosePlugins(); + SysMessage("%s", _("Could not check CD-ROM!\n")); + goto done; + } + + // Auto-detect: region first, then rcnt reset + EmuReset(); + + LoadCdrom(); + + if (defrostPath) { + LoadState([defrostPath fileSystemRepresentation]); + defrostPath = nil; + } + + psxCpu->Execute(); + +done: + emuThread = nil; + + return; +} + +- (void)EmuThreadRunBios:(id)anObject +{ + [self setUpThread]; + + // Do processing here + if (OpenPlugins() == -1) + goto done; + + EmuReset(); + + psxCpu->Execute(); + +done: + emuThread = nil; + + return; +} + +- (void)dealloc +{ + // remove all registered observers + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)emuWindowDidClose:(NSNotification *)aNotification +{ + [EmuThread stop]; +} + +- (void)emuWindowWantPause:(NSNotification *)aNotification +{ + wasPaused = [EmuThread pause]; +} + +- (void)emuWindowWantResume:(NSNotification *)aNotification +{ + if (!wasPaused) { + [EmuThread resume]; + } + wasPaused = NO; +} + +/* called periodically from the emulation thread */ +- (void)handleEvents +{ + /* only do a trylock here, since we're not interested in blocking, + and we can just handle events next time round */ + if (pthread_mutex_trylock(&eventMutex) == 0) { + while (safeEvent) { + if (safeEvent & EMUEVENT_STOP) { + /* signify that the emulation has stopped */ + emuThread = nil; + paused = NO; + + /* better unlock the mutex before killing ourself */ + pthread_mutex_unlock(&eventMutex); + + ClosePlugins(); + SysClose(); + + //[[NSThread currentThread] autorelease]; + [NSThread exit]; + return; + } + + if (safeEvent & EMUEVENT_RESET) { +#if 0 + /* signify that the emulation has stopped */ + [emuThread autorelease]; + emuThread = nil; + + /* better unlock the mutex before killing ourself */ + pthread_mutex_unlock(&eventMutex); + + ClosePlugins(); + + // start a new emulation thread + [EmuThread run]; + + //[[NSThread currentThread] autorelease]; + [NSThread exit]; + return; +#else + safeEvent &= ~EMUEVENT_RESET; + pthread_mutex_unlock(&eventMutex); + + longjmp(restartJmp, 0); +#endif + } + + if (safeEvent & EMUEVENT_PAUSE) { + paused = 2; + /* wait until we're signalled */ + pthread_cond_wait(&eventCond, &eventMutex); + } + } + pthread_mutex_unlock(&eventMutex); + } +} + ++ (void)run +{ + int err; + + if (emuThread) { + [EmuThread resume]; + return; + } + + if (pthread_mutex_lock(&eventMutex) != 0) { + err = pthread_cond_init(&eventCond, NULL); + if (err) return; + + err = pthread_mutex_init(&eventMutex, NULL); + if (err) return; + + pthread_mutex_lock(&eventMutex); + } + + safeEvent = EMUEVENT_NONE; + paused = NO; + runbios = NO; + + if (SysInit() != 0) { + pthread_mutex_unlock(&eventMutex); + return; + } + + emuThread = [[EmuThread alloc] init]; + + [NSThread detachNewThreadSelector:@selector(EmuThreadRun:) + toTarget:emuThread withObject:nil]; + + pthread_mutex_unlock(&eventMutex); +} + ++ (void)runBios +{ + int err; + + if (emuThread) { + [EmuThread resume]; + return; + } + + if (pthread_mutex_lock(&eventMutex) != 0) { + err = pthread_cond_init(&eventCond, NULL); + if (err) return; + + err = pthread_mutex_init(&eventMutex, NULL); + if (err) return; + + pthread_mutex_lock(&eventMutex); + } + + safeEvent = EMUEVENT_NONE; + paused = NO; + runbios = YES; + + if (SysInit() != 0) { + pthread_mutex_unlock(&eventMutex); + return; + } + + emuThread = [[EmuThread alloc] init]; + + [NSThread detachNewThreadSelector:@selector(EmuThreadRunBios:) + toTarget:emuThread withObject:nil]; + + pthread_mutex_unlock(&eventMutex); +} + ++ (void)stop +{ + pthread_mutex_lock(&eventMutex); + safeEvent = EMUEVENT_STOP; + pthread_mutex_unlock(&eventMutex); + + // wake it if it's sleeping + pthread_cond_broadcast(&eventCond); +} + ++ (BOOL)pause +{ + if (paused || ![EmuThread active]) + return YES; + + pthread_mutex_lock(&eventMutex); + safeEvent |= EMUEVENT_PAUSE; + paused = 1; + pthread_mutex_unlock(&eventMutex); + + pthread_cond_broadcast(&eventCond); + + return NO; +} + ++ (BOOL)pauseSafe +{ + if ((paused == 2) || ![EmuThread active]) + return YES; + + [EmuThread pause]; + while ([EmuThread isPaused] != 2) + [NSThread sleepUntilDate:[[NSDate date] dateByAddingTimeInterval:0.05]]; + + return NO; +} + ++ (void)pauseSafeWithBlock:(void (^)(BOOL))theBlock +{ + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + BOOL wasPaused = [self pauseSafe]; + dispatch_async(dispatch_get_main_queue(), ^{theBlock(wasPaused);}); + }); +} + ++ (void)resume +{ + if (!paused || ![EmuThread active]) + return; + + pthread_mutex_lock(&eventMutex); + + safeEvent &= ~EMUEVENT_PAUSE; + paused = NO; + pthread_mutex_unlock(&eventMutex); + + pthread_cond_broadcast(&eventCond); +} + ++ (void)reset +{ + pthread_mutex_lock(&eventMutex); + safeEvent = EMUEVENT_RESET; + pthread_mutex_unlock(&eventMutex); + + pthread_cond_broadcast(&eventCond); +} + +// must only be called from within the emulation thread!!! ++ (void)resetNow +{ + /* signify that the emulation has stopped */ + emuThread = nil; + + ClosePlugins(); + + // start a new emulation thread + [EmuThread run]; + + //[[NSThread currentThread] autorelease]; + [NSThread exit]; + return; +} + ++ (BOOL)isPaused +{ + return paused; +} + ++ (BOOL)isRunBios +{ + return runbios; +} + ++ (BOOL)active +{ + return emuThread ? YES : NO; +} + ++ (void)freezeAt:(NSString *)path which:(int)num +{ + [self pauseSafeWithBlock:^(BOOL emuWasPaused) { + int tmpNum = num; + char Text[256]; + + GPU_freeze(2, (GPUFreeze_t *)&tmpNum); + int ret = SaveState([path fileSystemRepresentation]); + + if (!emuWasPaused) { + [EmuThread resume]; + } + + if (ret == 0) + snprintf(Text, sizeof(Text), _("*PCSXR*: Saved State %d"), num); + else + snprintf(Text, sizeof(Text), _("*PCSXR*: Error Saving State %d"), num); + GPU_displayText(Text); + }]; +} + ++ (BOOL)defrostAt:(NSString *)path +{ + const char *cPath = [path fileSystemRepresentation]; + if (CheckState(cPath) != 0) + return NO; + + defrostPath = path; + [EmuThread reset]; + + GPU_displayText(_("*PCSXR*: Loaded State")); + return YES; +} + +@end diff --git a/macosx/Source/ExtendedKeys.h b/macosx/Source/ExtendedKeys.h new file mode 100644 index 00000000..b4990b20 --- /dev/null +++ b/macosx/Source/ExtendedKeys.h @@ -0,0 +1,28 @@ + +#ifndef __EXTENDED_KEYS_H__ +#define __EXTENDED_KEYS_H__ + +enum { + PSX_FREEZE_KEY = 0xFFBE/*XK_F1*/, + PSX_NEXT_FREEZE_SLOT_KEY = 0xFFBF/*XK_F2*/, + PSX_DEFROST_KEY = 0xFFC0/*XK_F3*/, + PSX_SHOW_FREEZE_PIC_KEY = 0xFFC1/*XK_F4*/, + PSX_SIO_ALWAYS_ON_KEY = 0xFFC2/*XK_F5*/, + PSX_BW_MDEC_KEY = 0xFFC3/*XK_F6*/, + PSX_XA_AUDIO_ON_KEY = 0xFFC4/*XK_F7*/, + PSX_SNAPSHOT_KEY = 0xFFC5/*XK_F8*/, + PSX_OPEN_SHELL_KEY = 0xFFC6/*XK_F9*/, + PSX_CLOSE_SHELL_KEY = 0xFFC7/*XK_F10*/, + + PSX_STOP_KEY = 0xFF1B/*XK_Escape*/, + + GPU_FULLSCREEN_KEY = 0x0100, + GPU_FPS_DISPLAY_KEY = 0xFFFF,/*XK_Delete*/ + + // Fake HotKeys + GPU_HOTKEYS = 0x020, + GPU_FAST_FORWARD, + GPU_FRAME_LIMIT +}; + +#endif //__EXTENDED_KEYS_H__ diff --git a/macosx/Source/HotkeyController.h b/macosx/Source/HotkeyController.h new file mode 100644 index 00000000..d24a921c --- /dev/null +++ b/macosx/Source/HotkeyController.h @@ -0,0 +1,25 @@ +/** + * HotkeyController + * Nicolas Pépin-Perreault - npepinpe - 2012 + */ + +#import <Cocoa/Cocoa.h> + +@interface HotkeyController : NSView + +@property (weak) IBOutlet NSTextField *FastForward; +@property (weak) IBOutlet NSTextField *SaveState; +@property (weak) IBOutlet NSTextField *LoadState; +@property (weak) IBOutlet NSTextField *NextState; +@property (weak) IBOutlet NSTextField *PrevState; +@property (weak) IBOutlet NSTextField *FrameLimit; + + +@property NSInteger configInput; + +- (void) initialize; +- (BOOL) handleMouseDown:(NSEvent *)mouseEvent; +- (IBAction) hotkeySet:(id)sender; +- (void) hotkeyCancel; + +@end diff --git a/macosx/Source/HotkeyController.m b/macosx/Source/HotkeyController.m new file mode 100644 index 00000000..58ec0fdd --- /dev/null +++ b/macosx/Source/HotkeyController.m @@ -0,0 +1,211 @@ +/** + * HotkeyController.m + * Pcsxr + * + * Created by Nicolas Pepin-Perreault on 12-12-10. + * + * Adapted from the Cocoa port of DeSMuMe + */ + +#import "HotkeyController.h" + +#define INPUT_HOLD_TIME 0.1 + +@interface HotkeyController () +@property (strong) NSButton *lastConfigButton; +@property (strong) NSMutableDictionary *hotkeysList; +@property (strong) NSDictionary *keyNameTable; +@property (strong) NSMutableDictionary *hotkeyOutlets; +@end + +@implementation HotkeyController + +@synthesize FastForward; +@synthesize FrameLimit; +@synthesize LoadState; +@synthesize NextState; +@synthesize PrevState; +@synthesize SaveState; + +- (void)initialize +{ + self.lastConfigButton = nil; + self.configInput = 0; + self.hotkeysList = [[NSMutableDictionary alloc] initWithCapacity:16]; + self.keyNameTable = [[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"KeyNames" ofType:@"plist"]]; + self.hotkeyOutlets = [[NSMutableDictionary alloc] initWithCapacity:8]; + + [self mapOutletToIdentifier:FastForward forIdentifier:@"FastForward"]; + [self mapOutletToIdentifier:SaveState forIdentifier:@"SaveState"]; + [self mapOutletToIdentifier:LoadState forIdentifier:@"LoadState"]; + [self mapOutletToIdentifier:NextState forIdentifier:@"NextState"]; + [self mapOutletToIdentifier:PrevState forIdentifier:@"PrevState"]; + [self mapOutletToIdentifier:FrameLimit forIdentifier:@"FrameLimit"]; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)mapOutletToIdentifier:(id)outlet forIdentifier:(NSString*)identifier1 +{ + (self.hotkeyOutlets)[identifier1] = outlet; + [self setHotkeyDisplay:identifier1]; +} + +- (void)setHotkeyDisplay:(NSString*)keyIdent +{ + NSString *label = [self parseMappingDisplayString:keyIdent]; + NSTextField *displayField = (self.hotkeyOutlets)[keyIdent]; + + if(displayField) { + [displayField setStringValue:label]; + } +} + +- (void)mouseDown:(NSEvent *)theEvent +{ + BOOL isHandled = [self handleMouseDown:theEvent]; + if (!isHandled) + { + [super mouseDown:theEvent]; + } +} + +- (void)mouseDragged:(NSEvent *)theEvent +{ + [self mouseDown:theEvent]; +} + +- (void)rightMouseDown:(NSEvent *)theEvent +{ + BOOL isHandled = [self handleMouseDown:theEvent]; + if (!isHandled) + { + [super rightMouseDown:theEvent]; + } +} + +- (void)rightMouseDragged:(NSEvent *)theEvent +{ + [self rightMouseDown:theEvent]; +} + +- (void)otherMouseDown:(NSEvent *)theEvent +{ + BOOL isHandled = [self handleMouseDown:theEvent]; + if (!isHandled) + { + [super otherMouseDown:theEvent]; + } +} + +- (void)otherMouseDragged:(NSEvent *)theEvent +{ + [self otherMouseDown:theEvent]; +} + +- (BOOL) handleMouseDown:(NSEvent *)mouseEvent +{ + if (self.configInput != 0) + { + [self hotkeyCancel]; + } + + return YES; +} + +- (void)keyDown:(NSEvent *)theEvent +{ + NSString *keyCode = [NSString stringWithFormat:@"%d", [theEvent keyCode]]; + NSString *keyLabel = (NSString *) (self.keyNameTable)[keyCode]; + + if (self.configInput != 0) + { + // Save input + NSString *ident = [self.lastConfigButton identifier]; + [self saveHotkey:ident device:@"NSEventKeyboard" deviceLabel:@"Keyboard" code:keyCode label:keyLabel]; + [self setHotkeyDisplay:ident]; + [self hotkeyCancel]; + } +} + +- (void)saveHotkey:(NSString*)keyIdent device:(NSString*)device deviceLabel:(NSString*)deviceLabel code:(NSString*)keyCode label:(NSString*)keyLabel +{ + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults] ; + NSMutableDictionary *tempUserMappings = [NSMutableDictionary dictionaryWithDictionary:[defaults dictionaryForKey:@"HotkeyBindings"]]; + [tempUserMappings setValue:@{@"device": device, + @"deviceName": deviceLabel, + @"keyCode": keyCode, + @"keyLabel": keyLabel} forKey:keyIdent]; + [defaults setValue:tempUserMappings forKey:@"HotkeyBindings"]; +} + +- (NSString *) parseMappingDisplayString:(NSString *)keyString +{ + NSDictionary *userMappings = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"HotkeyBindings"]; + NSDictionary *binding = (NSDictionary *)[userMappings valueForKey:keyString]; + + NSString *displayString = @""; + if(binding) { + NSString *deviceLabel = (NSString *)[binding valueForKey:@"deviceName"]; + NSString *keyLabel = (NSString *)[binding valueForKey:@"keyLabel"]; + + displayString = [NSString stringWithString:deviceLabel]; + displayString = [displayString stringByAppendingString:@": "]; + displayString = [displayString stringByAppendingString:keyLabel]; + } + + return displayString; +} + +- (IBAction) hotkeySet:(id)sender +{ + NSButton *theButton = (NSButton *)sender; + + if (self.configInput && self.lastConfigButton != theButton) + { + [self.lastConfigButton setState:NSOffState]; + } + + if ([theButton state] == NSOnState) + { + self.lastConfigButton = theButton; + [self.hotkeysList removeAllObjects]; + self.configInput = [theButton tag]; + } + else + { + [self hotkeyCancel]; + } + +} + +- (void) hotkeyCancel +{ + if (self.lastConfigButton != nil) + { + [self.lastConfigButton setState:NSOffState]; + self.lastConfigButton = nil; + } + + self.configInput = 0; +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (BOOL)becomeFirstResponder +{ + return YES; +} + +- (BOOL)resignFirstResponder +{ + return YES; +} + +@end diff --git a/macosx/Source/LaunchArg.h b/macosx/Source/LaunchArg.h new file mode 100644 index 00000000..2584123d --- /dev/null +++ b/macosx/Source/LaunchArg.h @@ -0,0 +1,25 @@ +// +// LaunchArg.h +// Pcsxr +// +// Created by C.W. Betts on 7/8/13. +// +// + +#import <Foundation/Foundation.h> + +typedef enum _LaunchArgOrder { + LaunchArgPreRun = 0, + LaunchArgRun = 200, + LaunchArgPostRun = 400 +}LaunchArgOrder; + +@interface LaunchArg : NSObject +@property (readonly) unsigned launchOrder; +@property (readonly, copy, nonatomic) dispatch_block_t theBlock; +@property (readonly, strong) NSString *argument; + +- (id)initWithLaunchOrder:(unsigned)order block:(dispatch_block_t)block argument:(NSString*)arg; +- (id)initWithLaunchOrder:(unsigned)order argument:(NSString*)arg block:(dispatch_block_t)block; +- (void)addToDictionary:(NSMutableDictionary*)toAdd; +@end diff --git a/macosx/Source/LaunchArg.m b/macosx/Source/LaunchArg.m new file mode 100644 index 00000000..dca47a7b --- /dev/null +++ b/macosx/Source/LaunchArg.m @@ -0,0 +1,51 @@ +// +// LaunchArg.m +// Pcsxr +// +// Created by C.W. Betts on 7/8/13. +// +// + +#import "LaunchArg.h" + +@interface LaunchArg () +@property (readwrite) unsigned launchOrder; +@property (readwrite, copy, nonatomic) dispatch_block_t theBlock; +@property (readwrite, strong) NSString *argument; +@end + +@implementation LaunchArg +@synthesize argument = _argument; +@synthesize launchOrder = _launchOrder; +@synthesize theBlock = _theBlock; +- (void)setTheBlock:(dispatch_block_t)theBlock +{ + _theBlock = [theBlock copy]; +} + +- (id)initWithLaunchOrder:(unsigned)order argument:(NSString*)arg block:(dispatch_block_t)block +{ + return [self initWithLaunchOrder:order block:block argument:arg]; +} + +- (id)initWithLaunchOrder:(unsigned)order block:(dispatch_block_t)block argument:(NSString*)arg +{ + if (self = [super init]) { + self.launchOrder = order; + self.theBlock = block; + self.argument = arg; + } + return self; +} + +- (void)addToDictionary:(NSMutableDictionary*)toAdd +{ + toAdd[self.argument] = self; +} + +- (NSString*)description +{ + return [NSString stringWithFormat:@"Arg: %@, order: %u, block addr: %p", _argument, _launchOrder, _theBlock]; +} + +@end diff --git a/macosx/Source/MemBadgeView.h b/macosx/Source/MemBadgeView.h new file mode 100644 index 00000000..b28353bd --- /dev/null +++ b/macosx/Source/MemBadgeView.h @@ -0,0 +1,14 @@ +// +// MemBadgeView.h +// Pcsxr +// +// Created by C.W. Betts on 7/6/13. +// +// + +#import <Cocoa/Cocoa.h> + +//TODO: also include the memory count in the view as well. +@interface MemBadgeView : NSView + +@end diff --git a/macosx/Source/MemBadgeView.m b/macosx/Source/MemBadgeView.m new file mode 100644 index 00000000..e15a6302 --- /dev/null +++ b/macosx/Source/MemBadgeView.m @@ -0,0 +1,51 @@ +// +// MemBadgeView.m +// Pcsxr +// +// Created by C.W. Betts on 7/6/13. +// +// + +#import "MemBadgeView.h" + +@implementation MemBadgeView + +//TODO: also include the memory count in the view as well. +- (id)initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + // Initialization code here. + } + + return self; +} + +- (void)drawRect:(NSRect)dirtyRect +{ + NSRect drawToRect = dirtyRect; + NSImage *tmpDraw = nil; + if (!NSEqualSizes(self.frame.size, dirtyRect.size)) { + drawToRect = (NSRect) {NSZeroPoint, self.frame.size}; + tmpDraw = [[NSImage alloc] initWithSize:drawToRect.size]; + [tmpDraw lockFocus]; + } + + [[NSColor whiteColor] set]; + [[NSBezierPath bezierPathWithOvalInRect:drawToRect] fill]; + [[NSColor redColor] set]; + NSRect smallerRect = drawToRect; + smallerRect.origin.x += 2; + smallerRect.origin.y += 2; + smallerRect.size.height -= 4; + smallerRect.size.width -= 4; + [[NSBezierPath bezierPathWithOvalInRect:smallerRect] fill]; + + if (tmpDraw) { + [tmpDraw unlockFocus]; + + [tmpDraw drawInRect:dirtyRect fromRect:dirtyRect operation:NSCompositeSourceOver fraction:1.0]; + } +} + +@end diff --git a/macosx/Source/OSXPlugLocalization.h b/macosx/Source/OSXPlugLocalization.h new file mode 100644 index 00000000..134a3daa --- /dev/null +++ b/macosx/Source/OSXPlugLocalization.h @@ -0,0 +1,21 @@ +// +// OSXPlugLocalization.h +// Pcsxr +// +// Created by C.W. Betts on 7/8/13. +// +// + +#ifndef Pcsxr_OSXPlugLocalization_h +#define Pcsxr_OSXPlugLocalization_h + +#define PLUGLOCIMP(klass) \ +char* PLUGLOC(char *toloc) \ +{ \ +NSBundle *mainBundle = [NSBundle bundleForClass:klass]; \ +NSString *origString = @(toloc), *transString = nil; \ +transString = [mainBundle localizedStringForKey:origString value:@"" table:nil]; \ +return (char*)[transString UTF8String]; \ +} + +#endif diff --git a/macosx/Source/PcsxrCheatHandler.h b/macosx/Source/PcsxrCheatHandler.h new file mode 100644 index 00000000..1dc4fc8e --- /dev/null +++ b/macosx/Source/PcsxrCheatHandler.h @@ -0,0 +1,14 @@ +// +// PcsxrCheatHandler.h +// Pcsxr +// +// Created by C.W. Betts on 8/1/13. +// +// + +#import <Foundation/Foundation.h> +#import "PcsxrFileHandle.h" + +@interface PcsxrCheatHandler : NSObject <PcsxrFileHandle> + +@end diff --git a/macosx/Source/PcsxrCheatHandler.m b/macosx/Source/PcsxrCheatHandler.m new file mode 100644 index 00000000..54f6cb5b --- /dev/null +++ b/macosx/Source/PcsxrCheatHandler.m @@ -0,0 +1,36 @@ +// +// PcsxrCheatHandler.m +// Pcsxr +// +// Created by C.W. Betts on 8/1/13. +// +// + +#import "PcsxrCheatHandler.h" +#import "CheatController.h" +#import "PcsxrController.h" +#include "psxcommon.h" +#include "cheat.h" + +@implementation PcsxrCheatHandler + ++ (NSArray *)supportedUTIs +{ + static NSArray *utisupport; + if (utisupport == nil) { + utisupport = @[@"com.codeplex.pcsxr.cheat"]; + } + return utisupport; +} + +- (BOOL)handleFile:(NSString *)theFile +{ + LoadCheats([theFile fileSystemRepresentation]); + + if ([(PcsxrController*)[NSApp delegate] cheatController]) { + [[(PcsxrController*)[NSApp delegate] cheatController] refresh]; + } + return YES; +} + +@end diff --git a/macosx/Source/PcsxrController.h b/macosx/Source/PcsxrController.h new file mode 100644 index 00000000..b271ae55 --- /dev/null +++ b/macosx/Source/PcsxrController.h @@ -0,0 +1,53 @@ +/* PcsxrController */ + +#import <Cocoa/Cocoa.h> +#import "EmuThread.h" +#import "PluginList.h" +#import "RecentItemsMenu.h" + +@class ConfigurationController; +@class CheatController; + +__private_extern void ShowHelpAndExit(FILE* output, int exitCode); +extern BOOL wasFinderLaunch; + +@interface PcsxrController : NSObject <NSApplicationDelegate> +{ + ConfigurationController *preferencesController; + CheatController *cheatController; + PluginList *pluginList; + + struct _PSXflags { + unsigned int sleepInBackground:1; + unsigned int wasPausedBeforeBGSwitch:1; + unsigned int endAtEmuClose:1; + unsigned int wasPausedBeforeDiscEject:1; + unsigned int reserved:28; + } PSXflags; +} +@property (weak) IBOutlet RecentItemsMenu *recentItems; +@property (readonly) CheatController *cheatController; +@property (readonly) BOOL endAtEmuClose; + +- (IBAction)ejectCD:(id)sender; +- (IBAction)pause:(id)sender; +- (IBAction)showCheatsWindow:(id)sender; +- (IBAction)preferences:(id)sender; +- (IBAction)reset:(id)sender; +- (IBAction)runCD:(id)sender; +- (IBAction)runIso:(id)sender; +- (IBAction)runBios:(id)sender; +- (IBAction)freeze:(id)sender; +- (IBAction)defrost:(id)sender; +- (IBAction)fullscreen:(id)sender; +- (IBAction)pauseInBackground:(id)sender; +- (void)runURL:(NSURL*)url; + ++ (void)setConfigFromDefaults; ++ (void)setDefaultFromConfig:(NSString *)defaultKey; ++ (BOOL)biosAvailable; ++ (NSString*)saveStatePath:(int)slot; ++ (void)saveState:(int)num; ++ (void)loadState:(int)num; + +@end diff --git a/macosx/Source/PcsxrController.m b/macosx/Source/PcsxrController.m new file mode 100644 index 00000000..4bdce477 --- /dev/null +++ b/macosx/Source/PcsxrController.m @@ -0,0 +1,974 @@ +#import <Cocoa/Cocoa.h> +#import "PcsxrController.h" +#import "ConfigurationController.h" +#import "CheatController.h" +#import "EmuThread.h" +#import "PcsxrMemCardHandler.h" +#import "PcsxrPluginHandler.h" +#import "PcsxrDiscHandler.h" +#import "PcsxrFreezeStateHandler.h" +#import "PcsxrCheatHandler.h" +#import "LaunchArg.h" +#include <DiskArbitration/DiskArbitration.h> +#include <IOKit/storage/IOCDMedia.h> +#include "psxcommon.h" +#include "plugins.h" +#include "misc.h" +#include "cdrom.h" +#include "ExtendedKeys.h" + +NSDictionary *prefStringKeys = nil; +NSDictionary *prefByteKeys = nil; +NSDictionary *prefURLKeys = nil; +NSMutableArray *biosList = nil; +NSString *saveStatePath = nil; +BOOL wasFinderLaunch = NO; + + +#define HELPSTR "\n" \ +"At least one of these must be passed:\n" \ +"\t--iso path launch with selected ISO\n" \ +"\t--cdrom launch with a CD-ROM\n" \ +"\t--bios launch into the BIOS\n" \ +"\n" \ +"Additional options:\n" \ +"\t--exitAtClose closes PCSX-R at when the emulation has ended\n" \ +"\t--mcd1 path sets the fist memory card to path\n" \ +"\t--mcd2 path sets the second memory card to path\n" \ +"\t--freeze path loads freeze state from path\n" \ +"\n" \ +"Help:\n" \ +"\t--help shows this message\n" \ +"\n" \ + + +void ShowHelpAndExit(FILE* output, int exitCode) +{ + fprintf(output, HELPSTR); + if (!NSApp) { + exit(exitCode); + } else { + [NSApp stop:nil]; + } +} + +@interface PcsxrController () +@property (readwrite) BOOL endAtEmuClose; +@property BOOL sleepInBackground; +@property BOOL wasPausedBeforeBGSwitch; +@property BOOL wasPausedBeforeDiscEject; +@property (strong) NSMutableArray *skipFiles; +@property (strong) NSWindow *preferenceWindow; +@property (strong) NSWindow *cheatWindow; +@property (nonatomic) DASessionRef diskSession; +@end + +@implementation PcsxrController +@synthesize recentItems; +@synthesize skipFiles; +@synthesize cheatController; +@synthesize cheatWindow; +@synthesize preferenceWindow; + +- (BOOL)endAtEmuClose +{ + return PSXflags.endAtEmuClose; +} + +- (void)setEndAtEmuClose:(BOOL)endAtEmuClose +{ + PSXflags.endAtEmuClose = endAtEmuClose; +} + +- (BOOL)sleepInBackground +{ + return PSXflags.sleepInBackground; +} + +- (void)setSleepInBackground:(BOOL)sleepInBackground +{ + PSXflags.sleepInBackground = sleepInBackground; +} + +- (BOOL)wasPausedBeforeBGSwitch +{ + return PSXflags.wasPausedBeforeBGSwitch; +} + +- (void)setWasPausedBeforeBGSwitch:(BOOL)wasPausedBeforeBGSwitch +{ + PSXflags.wasPausedBeforeBGSwitch = wasPausedBeforeBGSwitch; +} + +- (BOOL)wasPausedBeforeDiscEject +{ + return PSXflags.wasPausedBeforeDiscEject; +} + +-(void)setWasPausedBeforeDiscEject:(BOOL)wasPausedBeforeDiscEject +{ + PSXflags.wasPausedBeforeDiscEject = wasPausedBeforeDiscEject; +} + +@synthesize diskSession = _diskSession; +- (void)setDiskSession:(DASessionRef)diskSession +{ + if (diskSession == _diskSession) { + return; + } + if (_diskSession) { + CFRelease(_diskSession); + _diskSession = NULL; + }if (diskSession) { + _diskSession = diskSession; + CFRetain(diskSession); + } +} + +static void PSXDiscAppearedCallback(DADiskRef disk, void *context) +{ + PcsxrController *theSelf = (__bridge PcsxrController*)context; + //sleep(3); //Is this needed? + SetCdOpenCaseTime(time(NULL) + 2); + LidInterrupt(); + + /* and open new cd */ + if ([EmuThread active]) + CDR_open(); + + if (!theSelf.wasPausedBeforeDiscEject) { + [EmuThread resume]; + } + + DASessionUnscheduleFromRunLoop(theSelf.diskSession, CFRunLoopGetMain(), kCFRunLoopCommonModes); + theSelf.diskSession = NULL; +} + +- (IBAction)ejectCD:(id)sender +{ + self.wasPausedBeforeDiscEject = [EmuThread pauseSafe]; + + /* close connection to current cd */ + if ([EmuThread active]) + CDR_close(); + + // switch to another ISO if using internal image reader, otherwise eject the CD + if (UsingIso()) { + NSOpenPanel* openDlg = [NSOpenPanel openPanel]; + [openDlg setAllowedFileTypes:[PcsxrDiscHandler supportedUTIs]]; + + if ([openDlg runModal] == NSFileHandlingPanelOKButton) { + NSArray* files = [openDlg URLs]; + SetIsoFile([[files[0] path] fileSystemRepresentation]); + SetCdOpenCaseTime(time(NULL) + 2); + LidInterrupt(); + } + + if ([EmuThread active]) + CDR_open(); + + if (!self.wasPausedBeforeDiscEject) { + [EmuThread resume]; + } + } else { + NSMutableString *deviceName; + NSTask *ejectTask; + NSRange rdiskRange; + char *driveLetter = CDR_getDriveLetter(); + + if (driveLetter != NULL) { + deviceName = [NSMutableString stringWithString:[[NSFileManager defaultManager] stringWithFileSystemRepresentation:driveLetter length:strlen(driveLetter)]]; + + // delete the 'r' in 'rdisk' + rdiskRange = [deviceName rangeOfString:@"rdisk"]; + if (rdiskRange.length != 0) { + rdiskRange.length = 1; + [deviceName deleteCharactersInRange:rdiskRange]; + } + // execute hdiutil to eject the device + ejectTask = [NSTask launchedTaskWithLaunchPath:@"/usr/bin/hdiutil" arguments:@[@"eject", deviceName]]; + [ejectTask waitUntilExit]; + } + DASessionRef tmpSession = DASessionCreate(kCFAllocatorDefault); + CFDictionaryRef match = CFBridgingRetain(@{(NSString*)kDADiskDescriptionMediaKindKey : @(kIOCDMediaClass), + (NSString*)kDADiskDescriptionMediaWholeKey : @YES}); + DARegisterDiskAppearedCallback(tmpSession, match, PSXDiscAppearedCallback, (__bridge void*)self); + CFRelease(match); + + DASessionScheduleWithRunLoop(tmpSession, CFRunLoopGetMain(), kCFRunLoopCommonModes); + + self.diskSession = tmpSession; + CFRelease(tmpSession); + } +} + +- (void)emuWindowDidClose:(NSNotification*)theNot +{ + if (self.diskSession) { + DASessionUnscheduleFromRunLoop(self.diskSession, CFRunLoopGetMain(), kCFRunLoopCommonModes); + self.diskSession = NULL; + } +} + +- (IBAction)pause:(id)sender +{ + if ([EmuThread isPaused]) { + //[sender setState:NSOffState]; + [EmuThread resume]; + } + else { + //[sender setState:NSOnState]; + [EmuThread pause]; + } +} + +- (IBAction)showCheatsWindow:(id)sender +{ + /* load the nib if it hasn't yet */ + if (cheatWindow == nil) { + if (cheatController == nil) { + cheatController = [[CheatController alloc] initWithWindowNibName:@"CheatWindow"]; + } + cheatWindow = [cheatController window]; + } + + /* show the window */ + [cheatController showWindow:sender]; +} + +- (IBAction)preferences:(id)sender +{ + /* load the nib if it hasn't yet */ + if (preferenceWindow == nil) { + if (preferencesController == nil) { + preferencesController = [[ConfigurationController alloc] initWithWindowNibName:@"Configuration"]; + } + preferenceWindow = [preferencesController window]; + } + + /* show the window */ + [preferencesController showWindow:sender]; +} + +- (IBAction)reset:(id)sender +{ + [EmuThread reset]; +} + +- (IBAction)runCD:(id)sender +{ + SetIsoFile(NULL); + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"NetPlay"]) { + [pluginList enableNetPlug]; + } else { + [pluginList disableNetPlug]; + } + [EmuThread run]; +} + +- (IBAction)runIso:(id)sender +{ + NSOpenPanel* openDlg = [NSOpenPanel openPanel]; + [openDlg setAllowedFileTypes:[PcsxrDiscHandler supportedUTIs]]; + + if ([openDlg runModal] == NSFileHandlingPanelOKButton) { + NSURL *url = [openDlg URLs][0]; + [recentItems addRecentItem:url]; + [self runURL:url]; + } +} + +- (IBAction)runBios:(id)sender +{ + SetIsoFile(NULL); + [pluginList disableNetPlug]; + [EmuThread runBios]; +} + +- (void)runURL:(NSURL*)url +{ + if ([EmuThread active] == YES) { + if (UsingIso()) { + SetIsoFile([[url path] fileSystemRepresentation]); + SetCdOpenCaseTime(time(NULL) + 2); + LidInterrupt(); + } else { + NSBeep(); + } + } else { + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"NetPlay"]) { + [pluginList enableNetPlug]; + } else { + [pluginList disableNetPlug]; + } + SetIsoFile([[url path] fileSystemRepresentation]); + [EmuThread run]; + } +} + +- (IBAction)freeze:(id)sender +{ + NSInteger num = [sender tag]; + [PcsxrController saveState:(int)num]; +} + ++ (void)saveState:(int)num +{ + [EmuThread freezeAt:[PcsxrController saveStatePath:num] which:num]; +} + +- (IBAction)defrost:(id)sender +{ + NSInteger num = [sender tag]; + [PcsxrController loadState:(int)num]; +} + ++ (void)loadState:(int)num +{ + [EmuThread defrostAt:[PcsxrController saveStatePath:num]]; +} + +- (IBAction)fullscreen:(id)sender +{ + GPU_keypressed(GPU_FULLSCREEN_KEY); +} + +- (IBAction)pauseInBackground:(id)sender +{ + self.sleepInBackground = !self.sleepInBackground; + [[NSUserDefaults standardUserDefaults] setBool:self.sleepInBackground forKey:@"PauseInBackground"]; +} + +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem +{ + if ([menuItem action] == @selector(pause:)) { + [menuItem setState:([EmuThread isPaused] ? NSOnState : NSOffState)]; + } + + if ([menuItem action] == @selector(pause:) || [menuItem action] == @selector(fullscreen:)) + return [EmuThread active]; + + if ([menuItem action] == @selector(reset:) || [menuItem action] == @selector(ejectCD:) || + [menuItem action] == @selector(freeze:)) + return [EmuThread active] && ![EmuThread isRunBios]; + + if ([menuItem action] == @selector(runCD:) || [menuItem action] == @selector(runIso:) || + [menuItem action] == @selector(runBios:)) { + if (preferenceWindow != nil) + if ([preferenceWindow isVisible]) + return NO; + + if (cheatWindow != nil) + if ([cheatWindow isVisible]) + return NO; + + if ([menuItem action] == @selector(runBios:) && strcmp(Config.Bios, "HLE") == 0) + return NO; + + return ![EmuThread active]; + } + + if ([menuItem action] == @selector(defrost:)) { + if (![EmuThread active] || [EmuThread isRunBios]) + return NO; + + NSString *path = [saveStatePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%s-%3.3ld.pcsxrstate", CdromId, (long)[menuItem tag]]]; + return (CheckState((char *)[path fileSystemRepresentation]) == 0); + } + + if ([menuItem action] == @selector(preferences:)) + return ![EmuThread active]; + + if ([menuItem action] == @selector(pauseInBackground:)) { + [menuItem setState:(self.sleepInBackground ? NSOnState : NSOffState)]; + return YES; + } + + return YES; +} + +- (void)applicationWillResignActive:(NSNotification *)aNotification +{ + self.wasPausedBeforeBGSwitch = [EmuThread isPaused]; + + if (self.sleepInBackground) { + [EmuThread pause]; + } +} + +- (void)applicationDidBecomeActive:(NSNotification *)aNotification +{ + if (self.sleepInBackground && !self.wasPausedBeforeBGSwitch) { + [EmuThread resume]; + } +} + +- (void)applicationDidFinishLaunching:(NSNotification *)notification +{ + self.skipFiles = nil; +} + +static void ParseErrorStr(NSString *errStr) +{ + NSLog(@"Parse error: %@", errStr); + NSRunCriticalAlertPanel(@"Parsing error", @"%@\n\nPlease check the command line options and try again.\n\nPCSXR will now quit.", nil, nil, nil, errStr); + ShowHelpAndExit(stderr, EXIT_FAILURE); +} + +//DO NOT END THIS MACRO WITH A SIMICOLON! it will break the if-else if process +#define HandleArg(arg, launchable, otherblock) \ +if ([[progArgs objectAtIndex:i] compare:arg options:NSCaseInsensitiveSearch] == NSOrderedSame) { \ +HandleArgBase(arg, launchable, otherblock) + +#define HandleArgElse(arg, launchable, otherblock) \ +else if ([[progArgs objectAtIndex:i] compare:arg options:NSCaseInsensitiveSearch] == NSOrderedSame) { \ +HandleArgBase(arg, launchable, otherblock) + +#define HandleArgBase(arg, launchable, otherblock) \ +if (isLaunchable && launchable) { \ +ParseErrorStr([NSString stringWithFormat:@"The options %@ and %@ are exclusive.", arg, runtimeStr]); \ +} \ +if(launchable) { \ +isLaunchable = YES; \ +runtimeStr = arg; \ +} \ +otherblock();\ +} + +#define kPCSXRArgumentCDROM @"--cdrom" +#define kPCSXRArgumentBIOS @"--bios" +#define kPCSXRArgumentISO @"--iso" +#define kPCSXRArgumentMcd @"--mcd" +#define kPCSXRArgumentMcd1 kPCSXRArgumentMcd @"1" +#define kPCSXRArgumentMcd2 kPCSXRArgumentMcd @"2" +#define kPCSXRArgumentFreeze @"--freeze" +#define kPCSXRArgumentExitAtClose @"--exitAtClose" + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)awakeFromNib +{ + [[NSNotificationCenter defaultCenter] + addObserver:self selector:@selector(emuWindowDidClose:) + name:@"emuWindowDidClose" object:nil]; + + pluginList = [[PluginList alloc] init]; + if (![pluginList configured] /*!Config.Gpu[0] || !Config.Spu[0] || !Config.Pad1[0] || !Config.Cdr[0]*/) { + // configure plugins + [self preferences:nil]; + + NSRunCriticalAlertPanel(NSLocalizedString(@"Missing plugins!", nil), + NSLocalizedString(@"Pcsxr is missing one or more critical plugins. You will need to install these in order to play games.", nil), + nil, nil, nil); + } + + if (![PcsxrController biosAvailable]) { + NSFileManager *manager = [NSFileManager defaultManager]; + NSURL *supportURL = [manager URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL]; + NSURL *biosURL = [[supportURL URLByAppendingPathComponent:@"Pcsxr"] URLByAppendingPathComponent:@"Bios"]; + NSInteger retVal = NSRunInformationalAlertPanel(NSLocalizedString(@"Missing BIOS!", nil), + NSLocalizedString(@"Pcsxr wasn't able to locate any Playstation BIOS ROM files. This means that it will run in BIOS simulation mode which is less stable and compatible than using a real Playstation BIOS.\nIf you have a BIOS available, please copy it to\n%@", nil), + NSLocalizedString(@"Okay", @"OK"), NSLocalizedString(@"Show Folder", @"Show Folder"), nil, [[biosURL path] stringByAbbreviatingWithTildeInPath]); + if (retVal == NSAlertAlternateReturn) { + [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:@[biosURL]]; + } + } + + self.sleepInBackground = [[NSUserDefaults standardUserDefaults] boolForKey:@"PauseInBackground"]; + + NSArray *progArgs = [[NSProcessInfo processInfo] arguments]; + if ([progArgs count] > 1 && !wasFinderLaunch) { + self.skipFiles = [NSMutableArray array]; + + BOOL isLaunchable = NO; + NSString *runtimeStr = nil; + + __block short memcardHandled = 0; + __block BOOL hasParsedAnArgument = NO; + __block NSString *(^FileTestBlock)() = NULL; + __block NSMutableDictionary *argDict = [[NSMutableDictionary alloc] initWithCapacity:[progArgs count]]; + + + NSMutableArray *unknownOptions = [NSMutableArray array]; + + dispatch_block_t cdromBlock = ^{ + hasParsedAnArgument = YES; + LaunchArg *larg = [[LaunchArg alloc] initWithLaunchOrder:LaunchArgRun argument:kPCSXRArgumentCDROM block:^{ + [self runCD:nil]; + }]; + [larg addToDictionary:argDict]; + }; + + dispatch_block_t biosBlock = ^{ + hasParsedAnArgument = YES; + LaunchArg *larg = [[LaunchArg alloc] initWithLaunchOrder:LaunchArgRun argument:kPCSXRArgumentBIOS block:^{ + [self runBios:nil]; + }]; + [larg addToDictionary:argDict]; + }; + + //This block/argument does not need to be sorted + dispatch_block_t emuCloseAtEnd = ^{ + hasParsedAnArgument = YES; + LaunchArg *larg = [[LaunchArg alloc] initWithLaunchOrder:LaunchArgPreRun argument:kPCSXRArgumentExitAtClose block:^{ + self.endAtEmuClose = YES; + }]; + [larg addToDictionary:argDict]; + }; + + dispatch_block_t isoBlock = ^{ + hasParsedAnArgument = YES; + NSString *path = FileTestBlock(); + LaunchArg *larg = [[LaunchArg alloc] initWithLaunchOrder:LaunchArgRun argument:kPCSXRArgumentISO block:^{ + [self runURL:[NSURL fileURLWithPath:path isDirectory:NO]]; + }]; + [larg addToDictionary:argDict]; + }; + + void (^mcdBlock)(int mcdNumber) = ^(int mcdnumber){ + hasParsedAnArgument = YES; + if (memcardHandled & (1 << mcdnumber)) { + NSLog(@"Memory card %i has already been defined. The latest one passed will be used.", mcdnumber); + } else { + memcardHandled |= (1 << mcdnumber); + } + + NSString *path = FileTestBlock(); + NSString *mcdArg = [kPCSXRArgumentMcd stringByAppendingFormat:@"%i", mcdnumber]; + LaunchArg *larg = [[LaunchArg alloc] initWithLaunchOrder:LaunchArgPreRun argument:mcdArg block:^{ + LoadMcd(mcdnumber, (char*)[path fileSystemRepresentation]); + }]; + [larg addToDictionary:argDict]; + }; + + dispatch_block_t freezeBlock = ^{ + hasParsedAnArgument = YES; + NSString *path = FileTestBlock(); + LaunchArg *larg = [[LaunchArg alloc] initWithLaunchOrder:LaunchArgPostRun argument:kPCSXRArgumentFreeze block:^{ + if (![EmuThread isRunBios]) { + //Make sure the emulator is running + sleep(5); + [EmuThread defrostAt:path]; + } + }]; + [larg addToDictionary:argDict]; + }; + + BOOL hasFileTestBlock = NO; + + for (__block int i = 1; i < [progArgs count]; i++) { + if (!hasFileTestBlock) + { + FileTestBlock = ^NSString *(){ + if ([progArgs count] <= ++i) { + ParseErrorStr(@"Not enough arguments."); + } + NSString *path = [progArgs[i] stringByExpandingTildeInPath]; + if (![[NSFileManager defaultManager] fileExistsAtPath:path]) + { + ParseErrorStr([NSString stringWithFormat:@"The file \"%@\" does not exist.", path]); + return nil; + } + [skipFiles addObject:path]; + return path; + }; + hasFileTestBlock = YES; + } + + //DO NOT END these MACROS WITH A SIMICOLON! It will break the if-else if process + HandleArg(kPCSXRArgumentISO, YES, isoBlock) + HandleArgElse(kPCSXRArgumentCDROM, YES, cdromBlock) + HandleArgElse(kPCSXRArgumentBIOS, YES, biosBlock) + HandleArgElse(kPCSXRArgumentExitAtClose, NO, emuCloseAtEnd) + HandleArgElse(kPCSXRArgumentMcd1, NO, ^{mcdBlock(1);}) + HandleArgElse(kPCSXRArgumentMcd2, NO, ^{mcdBlock(2);}) + HandleArgElse(kPCSXRArgumentFreeze, NO, freezeBlock) + else { + [unknownOptions addObject:progArgs[i]]; + } + } +#ifdef DEBUG + if ([unknownOptions count]) { + NSString *unknownString = [unknownOptions componentsJoinedByString:@" "]; + + NSLog(@"The following options weren't recognized by PCSX-R: %@. This may be due to extra arguments passed by the OS or debugger.", unknownString); + } +#endif + unknownOptions = nil; + if (!isLaunchable && hasParsedAnArgument) { + NSMutableArray *mutProgArgs = [NSMutableArray arrayWithArray:progArgs]; + NSString *appRawPath = mutProgArgs[0]; + //Remove the app file path from the array + [mutProgArgs removeObjectAtIndex:0]; + NSString *arg = [mutProgArgs componentsJoinedByString:@" "]; + NSString *recognizedArgs = [[argDict allKeys] componentsJoinedByString:@" "]; + + NSString *tmpStr = [NSString stringWithFormat:@"A launch command wasn't found in the command line and one or more arguments that PCSX-R recognizes were: %@.\nThe following command line arguments were passed with the application launch file at %@: %@.\n\nThe valid launch commands are %@, %@, and %@.", recognizedArgs, appRawPath, arg, kPCSXRArgumentISO, kPCSXRArgumentCDROM, kPCSXRArgumentBIOS]; + ParseErrorStr(tmpStr); + } else if (hasParsedAnArgument){ + NSArray *argArray = [[argDict allValues] sortedArrayWithOptions:NSSortStable usingComparator:^NSComparisonResult(id obj1, id obj2) { + LaunchArg *LA1 = obj1; + LaunchArg *LA2 = obj2; + if (LA1.launchOrder > LA2.launchOrder) { + return NSOrderedDescending; + } else if (LA1.launchOrder < LA2.launchOrder) { + return NSOrderedAscending; + } else { + return NSOrderedSame; + } + }]; + for (LaunchArg *arg in argArray) { + arg.theBlock(); + } + } + } +} + ++ (void)setConfigFromDefaults +{ + const char *str; + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + + /* + enumerator = [prefStringKeys keyEnumerator]; + while ((key = [enumerator nextObject])) { + str = [[defaults stringForKey:key] fileSystemRepresentation]; + char *dst = (char *)[[prefStringKeys objectForKey:key] pointerValue]; + if (str != nil && dst != nil) strncpy(dst, str, 255); + }*/ + + for (NSString *key in prefByteKeys) { + u8 *dst = (u8 *)[prefByteKeys[key] pointerValue]; + if (dst != NULL) *dst = [defaults boolForKey:key]; + } + + // special cases + //str = [[defaults stringForKey:@"PluginPAD"] fileSystemRepresentation]; + //if (str != nil) strncpy(Config.Pad2, str, 255); + + str = [[defaults stringForKey:@"Bios"] fileSystemRepresentation]; + if (str) { + NSString *path = [defaults stringForKey:@"Bios"]; + NSInteger index = [biosList indexOfObject:path]; + + if (-1 == index) { + [biosList insertObject:path atIndex:0]; + } else if (0 < index) { + [biosList exchangeObjectAtIndex:index withObjectAtIndex:0]; + } + } + + { + NSFileManager *manager = [NSFileManager defaultManager]; + NSURL *memoryURL = [[[manager URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL] URLByAppendingPathComponent:@"Pcsxr"] URLByAppendingPathComponent:@"Memory Cards"]; + + str = [[[defaults URLForKey:@"Mcd1"] path] fileSystemRepresentation]; + if (str) { + strlcpy(Config.Mcd1, str, MAXPATHLEN); + } else { + NSURL *url = [memoryURL URLByAppendingPathComponent:@"Mcd001.mcr"]; + [defaults setURL:url forKey:@"Mcd1"]; + str = [[url path] fileSystemRepresentation]; + if (str != nil) strlcpy(Config.Mcd1, str, MAXPATHLEN); + } + + str = [[[defaults URLForKey:@"Mcd2"] path] fileSystemRepresentation]; + if (str) { + strlcpy(Config.Mcd2, str, MAXPATHLEN); + } else { + NSURL *url = [memoryURL URLByAppendingPathComponent:@"Mcd002.mcr"]; + [defaults setURL:url forKey:@"Mcd2"]; + str = [[url path] fileSystemRepresentation]; + if (str != nil) strlcpy(Config.Mcd2, str, MAXPATHLEN); + } + } + + if ([defaults boolForKey:@"UseHLE"] || 0 == [biosList count]) { + strcpy(Config.Bios, "HLE"); + } else { + str = [(NSString *)biosList[0] fileSystemRepresentation]; + if (str != nil) strlcpy(Config.Bios, str, MAXPATHLEN); + else strcpy(Config.Bios, "HLE"); + } + + str = [[defaults stringForKey:@"Net"] fileSystemRepresentation]; + if (str) strlcpy(Config.Net, str, MAXPATHLEN); + else { + strcpy(Config.Net, "Disabled"); + } +} + ++ (void)setDefaultFromConfig:(NSString *)defaultKey +{ + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + + char *str = (char *)[prefStringKeys[defaultKey] pointerValue]; + if (str) { + NSString *tmpNSStr = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:str length:strlen(str)]; + if (!tmpNSStr) { + tmpNSStr = @(str); + } + + [defaults setObject:tmpNSStr forKey:defaultKey]; + return; + } + + str = (char *)[prefURLKeys[defaultKey] pointerValue]; + if (str) { + NSString *tmpNSStr = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:str length:strlen(str)]; + if (!tmpNSStr) { + tmpNSStr = @(str); + } + [defaults setURL:[NSURL fileURLWithPath:tmpNSStr isDirectory:NO] forKey:defaultKey]; + return; + } + + u8 *val = (u8 *)[prefByteKeys[defaultKey] pointerValue]; + if (val) { + [defaults setInteger:*val forKey:defaultKey]; + return; + } +} + ++ (BOOL)biosAvailable +{ + return ([biosList count] > 0); +} + +// called when class is initialized ++ (void)initialize +{ + NSString *path; + const char *str; + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSDictionary *appDefaults = @{@"NoDynarec": @YES, + @"AutoDetectVideoType": @YES, + @"UseHLE": @NO, + @"PauseInBackground": @YES, + @"Widescreen": @NO, + @"NetPlay": @NO}; + + [defaults registerDefaults:appDefaults]; + + prefStringKeys = @{@"PluginGPU": [NSValue valueWithPointer:Config.Gpu], + @"PluginSPU": [NSValue valueWithPointer:Config.Spu], + @"PluginPAD": [NSValue valueWithPointer:Config.Pad1], + @"PluginCDR": [NSValue valueWithPointer:Config.Cdr], + @"PluginNET": [NSValue valueWithPointer:Config.Net], + @"PluginSIO1": [NSValue valueWithPointer:Config.Sio1]}; + + prefURLKeys = @{@"Mcd1": [NSValue valueWithPointer:Config.Mcd1], + @"Mcd2": [NSValue valueWithPointer:Config.Mcd2]}; + + prefByteKeys = @{@"NoXaAudio": [NSValue valueWithPointer:&Config.Xa], + @"SioIrqAlways": [NSValue valueWithPointer:&Config.SioIrq], + @"BlackAndWhiteMDECVideo": [NSValue valueWithPointer:&Config.Mdec], + @"AutoDetectVideoType": [NSValue valueWithPointer:&Config.PsxAuto], + @"VideoTypePAL": [NSValue valueWithPointer:&Config.PsxType], + @"NoCDAudio": [NSValue valueWithPointer:&Config.Cdda], + @"NoDynarec": [NSValue valueWithPointer:&Config.Cpu], + @"ConsoleOutput": [NSValue valueWithPointer:&Config.PsxOut], + @"SpuIrqAlways": [NSValue valueWithPointer:&Config.SpuIrq], + @"RootCounterFix": [NSValue valueWithPointer:&Config.RCntFix], + @"VideoSyncWAFix": [NSValue valueWithPointer:&Config.VSyncWA], + @"Widescreen": [NSValue valueWithPointer:&Config.Widescreen]}; + + // setup application support paths + NSFileManager *manager = [NSFileManager defaultManager]; + NSURL *supportURL = [manager URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL]; + + if(supportURL != nil) { + NSURL *PcsxrAppSupport; + NSURL *MemCardPath; + NSURL *url; + BOOL dir; + + PcsxrAppSupport = [supportURL URLByAppendingPathComponent:@"Pcsxr"]; + + // create them if needed + url = [PcsxrAppSupport URLByAppendingPathComponent:@"Bios"]; + if (![url checkResourceIsReachableAndReturnError:NULL]) + [manager createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:NULL]; + + MemCardPath = [PcsxrAppSupport URLByAppendingPathComponent:@"Memory Cards"]; + url = MemCardPath; + if (![url checkResourceIsReachableAndReturnError:NULL]) + [manager createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:NULL]; + + url = [PcsxrAppSupport URLByAppendingPathComponent:@"Patches"]; + if (![url checkResourceIsReachableAndReturnError:NULL]) + [manager createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:NULL]; + + url = [PcsxrAppSupport URLByAppendingPathComponent:@"PlugIns"]; + if (![url checkResourceIsReachableAndReturnError:NULL]) + [manager createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:NULL]; + + saveStatePath = [[[PcsxrAppSupport URLByAppendingPathComponent:@"Save States"] path] copy]; + if (![manager fileExistsAtPath:saveStatePath isDirectory:&dir]) + [manager createDirectoryAtPath:saveStatePath withIntermediateDirectories:YES attributes:nil error:NULL]; + + url = [MemCardPath URLByAppendingPathComponent:@"Mcd001.mcr"]; + str = [[url path] fileSystemRepresentation]; + if (str != nil) + strlcpy(Config.Mcd1, str, MAXPATHLEN); + + url = [MemCardPath URLByAppendingPathComponent:@"Mcd002.mcr"]; + str = [[url path] fileSystemRepresentation]; + if (str != nil) + strlcpy(Config.Mcd2, str, MAXPATHLEN); + + url = [PcsxrAppSupport URLByAppendingPathComponent:@"Bios"]; + str = [[url path] fileSystemRepresentation]; + if (str != nil) + strlcpy(Config.BiosDir, str, MAXPATHLEN); + + url = [PcsxrAppSupport URLByAppendingPathComponent:@"Patches"]; + str = [[url path] fileSystemRepresentation]; + if (str != nil) { + strlcpy(Config.PatchesDir, str, MAXPATHLEN); + } + } else { + strcpy(Config.BiosDir, "Bios/"); + strcpy(Config.PatchesDir, "Patches/"); + + //NSString constants don't need to be retained/released. In fact, retain/releasing them does nothing. + saveStatePath = @"sstates"; + } + + // set plugin path + path = [[NSBundle mainBundle] builtInPlugInsPath]; + str = [path fileSystemRepresentation]; + if (str != nil) + strlcpy(Config.PluginsDir, str, MAXPATHLEN); + + // locate a bios + biosList = [[NSMutableArray alloc] init]; + + NSString *biosDir = [manager stringWithFileSystemRepresentation:Config.BiosDir length:strlen(Config.BiosDir)]; + NSArray *bioses = [manager contentsOfDirectoryAtPath:biosDir error:NULL]; + if (bioses) { + for (NSString *file in bioses) { + NSDictionary *attrib = [manager attributesOfItemAtPath:[[biosDir stringByAppendingPathComponent:file] stringByResolvingSymlinksInPath] error:NULL]; + + if ([[attrib fileType] isEqualToString:NSFileTypeRegular]) { + unsigned long long size = [attrib fileSize]; + if ((size % (256 * 1024)) == 0 && size > 0) { + [biosList addObject:file]; + } + } + } + } + + [PcsxrController setConfigFromDefaults]; +} + ++ (NSString*)saveStatePath:(int)slot +{ + if(slot >= 0) { + return [saveStatePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%s-%3.3d.pcsxrstate", CdromId, slot]]; + } + + return saveStatePath; +} + +- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename +{ + NSFileManager *fm = [NSFileManager defaultManager]; + NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; + if (skipFiles && [skipFiles count]) { + for (NSString *parsedFile in skipFiles) { + if ([filename isEqualToString:parsedFile]) { + return YES; + } + } + } + + if (![fm fileExistsAtPath:filename]) { + NSLog(@"Nonexistant file %@ was passed to open.", filename ); + return NO; + } + + if ([[filename pathExtension] compare:@"bin" options:(NSCaseInsensitiveSearch | NSWidthInsensitiveSearch)]) { + NSDictionary *attrib = [fm attributesOfItemAtPath:filename error:NULL]; + if ([[attrib fileType] isEqualToString:NSFileTypeRegular] && ([attrib fileSize] % (256 * 1024)) == 0 && [attrib fileSize] > 0 ) { + NSAlert *biosInfo = [NSAlert alertWithMessageText:NSLocalizedString(@"PlayStation BIOS File", @"PSX BIOS File") defaultButton:NSLocalizedString(@"BIOS_Copy", @"copy the BIOS over") alternateButton:NSLocalizedString(@"Cancel", @"Cancel") otherButton:NSLocalizedString(@"BIOS_Move", @"Move the bios over") informativeTextWithFormat:NSLocalizedString(@"The file \"%@\" seems to be a BIOS file. Do you want PCSX-R to copy it to the proper location?", @"Can we copy the BIOS?")]; + biosInfo.alertStyle = NSInformationalAlertStyle; + switch ([biosInfo runModal]) { + case NSAlertFirstButtonReturn: + case NSAlertDefaultReturn: + { + NSError *theErr = nil; + NSURL *biosDirPath = [NSURL fileURLWithPath:[fm stringWithFileSystemRepresentation:Config.BiosDir length:strlen(Config.BiosDir)] isDirectory:YES]; + NSURL *biosPath = [biosDirPath URLByAppendingPathComponent:[filename lastPathComponent]]; + if ([biosPath checkResourceIsReachableAndReturnError:NULL]) { + NSAlert *alreadyThere = [NSAlert alertWithMessageText:NSLocalizedString(@"BIOS Already Exists", @"BIOS file already there.") defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:NSLocalizedString(@"There already exists a BIOS file at \"%1$@\": not copying the file at \"%2$@\".\n\nIf you do want to use the BIOS file at \"%2$@\", delete the BIOS at \"%1$@\".", @"What to do"), [biosPath path], filename]; + alreadyThere.alertStyle = NSCriticalAlertStyle; + [alreadyThere runModal]; + return NO; + } + if (![fm copyItemAtURL:[NSURL fileURLWithPath:filename isDirectory:NO] toURL:biosPath error:&theErr]) { + [[NSAlert alertWithError:theErr] runModal]; + return NO; + } + } + break; + + case NSAlertThirdButtonReturn: + case NSAlertOtherReturn: + { + NSError *theErr = nil; + NSURL *biosDirPath = [NSURL fileURLWithPath:[fm stringWithFileSystemRepresentation:Config.BiosDir length:strlen(Config.BiosDir)] isDirectory:YES]; + NSURL *biosPath = [biosDirPath URLByAppendingPathComponent:[filename lastPathComponent]]; + if ([biosPath checkResourceIsReachableAndReturnError:NULL]) { + NSAlert *alreadyThere = [NSAlert alertWithMessageText:NSLocalizedString(@"BIOS Already Exists", @"BIOS file already there.") defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:NSLocalizedString(@"There already exists a BIOS file at \"%1$@\": not moving the file at \"%2$@\".\n\nIf you do want to use the BIOS file at \"%2$@\", delete the BIOS at \"%1$@\".", @"What to do"), [biosPath path], filename]; + alreadyThere.alertStyle = NSCriticalAlertStyle; + [alreadyThere runModal]; + return NO; + } + if (![fm moveItemAtURL:[NSURL fileURLWithPath:filename isDirectory:NO] toURL:biosPath error:&theErr]) { + [[NSAlert alertWithError:theErr] runModal]; + return NO; + } + } + break; + + default: + break; + } + return YES; + } + } + + NSError *err = nil; + NSString *utiFile = [workspace typeOfFile:filename error:&err]; + if (err) { + NSRunAlertPanel(NSLocalizedString(@"Error opening file", nil), NSLocalizedString(@"Unable to open %@: %@", nil), nil, nil, nil, [filename lastPathComponent], err); + return NO; + } + static NSArray *handlers = nil; + if (handlers == nil) { + handlers = @[[PcsxrPluginHandler class], [PcsxrMemCardHandler class], [PcsxrFreezeStateHandler class], [PcsxrDiscHandler class], [PcsxrCheatHandler class]]; + } + BOOL isHandled = NO; + for (Class fileHandler in handlers) { + NSObject<PcsxrFileHandle> *hand = [[fileHandler alloc] init]; + BOOL canHandle = NO; + for (NSString *uti in [fileHandler supportedUTIs]) { + if ([workspace type:utiFile conformsToType:uti]) { + canHandle = YES; + break; + } + } + if (canHandle) { + isHandled = [hand handleFile:filename]; + break; + } + } + + return isHandled; +} + +@end diff --git a/macosx/Source/PcsxrDiscHandler.h b/macosx/Source/PcsxrDiscHandler.h new file mode 100644 index 00000000..933d7799 --- /dev/null +++ b/macosx/Source/PcsxrDiscHandler.h @@ -0,0 +1,14 @@ +// +// PcsxrDiscHandler.h +// Pcsxr +// +// Created by Charles Betts on 12/11/11. +// Copyright (c) 2011 __MyCompanyName__. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import "PcsxrFileHandle.h" + +@interface PcsxrDiscHandler : NSObject <PcsxrFileHandle> + +@end diff --git a/macosx/Source/PcsxrDiscHandler.m b/macosx/Source/PcsxrDiscHandler.m new file mode 100644 index 00000000..996fdab3 --- /dev/null +++ b/macosx/Source/PcsxrDiscHandler.m @@ -0,0 +1,58 @@ +// +// PcsxrDiscHandler.m +// Pcsxr +// +// Created by Charles Betts on 12/11/11. +// Copyright (c) 2011 __MyCompanyName__. All rights reserved. +// + +#import "PcsxrDiscHandler.h" +#import "EmuThread.h" +#include "psxcommon.h" +#include "plugins.h" +#include "cdrom.h" +#import "RecentItemsMenu.h" +#import "PcsxrController.h" + +@interface PcsxrDiscHandler () +@property (nonatomic, strong) NSURL *discURL; +@property (weak) NSString *discPath; +@end + +@implementation PcsxrDiscHandler +@synthesize discURL = _discURL; +- (NSURL*)discURL +{ + if (!_discURL) { + self.discURL = [NSURL fileURLWithPath:discPath]; + } + return _discURL; +} + +@synthesize discPath; + ++ (NSArray *)supportedUTIs +{ + static NSArray *utisupport = nil; + if (utisupport == nil) { + utisupport = @[@"com.alcohol-soft.mdfdisc", @"com.goldenhawk.cdrwin-cuesheet", + @"com.apple.disk-image-ndif", @"public.iso-image", @"com.sony.psp.firmware", + @"com.codeplex.pcsxr.compressed-bin-image", @"com.coppertino.vox.cue", + @"com.apple.macbinary-​archive"]; + } + return utisupport; +} + +- (BOOL)handleFile:(NSString *)theFile +{ + self.discPath = theFile; + PcsxrController *appDelegate = [NSApp delegate]; + if ([EmuThread active] == YES && !UsingIso()) { + return NO; + } + [appDelegate runURL:[self discURL]]; + [[appDelegate recentItems] addRecentItem:[self discURL]]; + return YES; +} + +@end diff --git a/macosx/Source/PcsxrFileHandle.h b/macosx/Source/PcsxrFileHandle.h new file mode 100644 index 00000000..6c663fae --- /dev/null +++ b/macosx/Source/PcsxrFileHandle.h @@ -0,0 +1,14 @@ +// +// PcsxrFileHandle.h +// Pcsxr +// +// Created by Charles Betts on 12/10/11. +// Copyright (c) 2011 __MyCompanyName__. All rights reserved. +// + +#import <Foundation/Foundation.h> + +@protocol PcsxrFileHandle <NSObject> ++ (NSArray *)supportedUTIs; +- (BOOL)handleFile:(NSString *)theFile; +@end diff --git a/macosx/Source/PcsxrFreezeStateHandler.h b/macosx/Source/PcsxrFreezeStateHandler.h new file mode 100644 index 00000000..ca50f533 --- /dev/null +++ b/macosx/Source/PcsxrFreezeStateHandler.h @@ -0,0 +1,14 @@ +// +// PcsxrFreezeStateHandler.h +// Pcsxr +// +// Created by Charles Betts on 12/11/11. +// Copyright (c) 2011 __MyCompanyName__. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import "PcsxrFileHandle.h" + +@interface PcsxrFreezeStateHandler : NSObject <PcsxrFileHandle> + +@end diff --git a/macosx/Source/PcsxrFreezeStateHandler.m b/macosx/Source/PcsxrFreezeStateHandler.m new file mode 100644 index 00000000..b300fe02 --- /dev/null +++ b/macosx/Source/PcsxrFreezeStateHandler.m @@ -0,0 +1,43 @@ +// +// PcsxrFreezeStateHandler.m +// Pcsxr +// +// Created by Charles Betts on 12/11/11. +// Copyright (c) 2011 __MyCompanyName__. All rights reserved. +// + +#import "PcsxrFreezeStateHandler.h" +#import "EmuThread.h" +#import "PluginList.h" +#include "misc.h" + +@implementation PcsxrFreezeStateHandler + ++ (NSArray *)supportedUTIs +{ + static NSArray *utisupport = nil; + if (utisupport == nil) { + utisupport = @[@"com.codeplex.pcsxr.freeze"]; + } + return utisupport; +} + +- (BOOL)handleFile:(NSString *)theFile +{ + if (CheckState([theFile fileSystemRepresentation]) != 0) { + return NO; + } + if (![EmuThread active]) { + PluginList *pluginList = [PluginList list]; + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"NetPlay"]) { + [pluginList enableNetPlug]; + } else { + [pluginList disableNetPlug]; + } + + [EmuThread run]; + } + return [EmuThread defrostAt:theFile]; +} + +@end diff --git a/macosx/Source/PcsxrHexadecimalFormatter.h b/macosx/Source/PcsxrHexadecimalFormatter.h new file mode 100644 index 00000000..63a3da44 --- /dev/null +++ b/macosx/Source/PcsxrHexadecimalFormatter.h @@ -0,0 +1,14 @@ +// +// PcsxrHexadecimalFormatter.h +// Pcsxr +// +// Created by C.W. Betts on 8/17/13. +// +// + +#import <Foundation/Foundation.h> + +@interface PcsxrHexadecimalFormatter : NSFormatter +@property (nonatomic) char hexPadding; + +@end diff --git a/macosx/Source/PcsxrHexadecimalFormatter.m b/macosx/Source/PcsxrHexadecimalFormatter.m new file mode 100644 index 00000000..550a7cb1 --- /dev/null +++ b/macosx/Source/PcsxrHexadecimalFormatter.m @@ -0,0 +1,88 @@ +// +// PcsxrHexadecimalFormatter.m +// Pcsxr +// +// Created by C.W. Betts on 8/17/13. +// +// + +#import "PcsxrHexadecimalFormatter.h" + +@interface PcsxrHexadecimalFormatter () +@property (strong) NSString *hexFormatString; +@end + +@implementation PcsxrHexadecimalFormatter +@synthesize hexPadding; +@synthesize hexFormatString; + +- (void)setHexPadding:(char)_hexPadding +{ + hexPadding = _hexPadding; + self.hexFormatString = [NSString stringWithFormat:@"0x%%0%ilx", hexPadding]; +} + +- (id)init +{ + if (self = [super init]) { +#ifdef __LP64__ + self.hexPadding = 16; +#else + self.hexPadding = 8; +#endif + } + return self; +} + +- (id)initWithCoder:(NSCoder *)aDecoder +{ + if (self = [super initWithCoder:aDecoder]) { +#ifdef __LP64__ + self.hexPadding = 16; +#else + self.hexPadding = 8; +#endif + } + return self; +} + +- (NSString *)stringForObjectValue:(id)obj +{ + if ([obj isKindOfClass:[NSNumber class]]) { + return [NSString stringWithFormat:self.hexFormatString, (long)[obj integerValue]]; + } else return nil; +} + +- (NSString *)editingStringForObjectValue:(id)obj +{ + if ([obj isKindOfClass:[NSNumber class]]) { + return [NSString stringWithFormat:@"%lx", (long)[obj integerValue]]; + } else return nil; +} + +- (BOOL)getObjectValue:(out id *)obj forString:(NSString *)string errorDescription:(out NSString **)error +{ + NSString *tmpstr = nil; + unsigned int tmpNum; + NSScanner *theScan = [[NSScanner alloc] initWithString:string]; + if ([theScan scanHexInt:&tmpNum]) { + *obj = @(tmpNum); + return YES; + } else { + if ([string hasPrefix:@"0x"]) { + NSRange zeroXRange = [string rangeOfString:@"0x"]; + tmpstr = [string stringByReplacingCharactersInRange:zeroXRange withString:@""]; + }else { + tmpstr = string; + } + long tmpNum = 0; + if (sscanf([tmpstr UTF8String], "%lx", &tmpNum) == 1) { + *obj = @(tmpNum); + return YES; + } else { + return NO; + } + } +} + +@end diff --git a/macosx/Source/PcsxrMemCardArray.h b/macosx/Source/PcsxrMemCardArray.h new file mode 100644 index 00000000..21b45f3c --- /dev/null +++ b/macosx/Source/PcsxrMemCardArray.h @@ -0,0 +1,40 @@ +// +// PcsxrMemCardArray.h +// Pcsxr +// +// Created by C.W. Betts on 7/6/13. +// +// + +#import <Foundation/Foundation.h> +#import "PcsxrMemoryObject.h" + +@interface PcsxrMemCardArray : NSObject + +- (id)initWithMemoryCardNumber:(int)carNum; + +- (void)deleteMemoryBlocksAtIndex:(int)slotnum; +- (void)compactMemory; + +/** + * @fn freeBlocks + * @abstract Blocks that are free from any data + * @result free blocks + */ +- (int)freeBlocks; + +/** + * @fn availableBlocks + * @abstract Blocks that have been deleted + * @result free blocks + */ +- (int)availableBlocks; +- (int)memorySizeAtIndex:(int)idx; +- (BOOL)moveBlockAtIndex:(int)idx toMemoryCard:(PcsxrMemCardArray*)otherCard; +- (int)indexOfFreeBlocksWithSize:(int)asize; + +@property (nonatomic, readonly, unsafe_unretained) NSArray *memoryArray; +@property (nonatomic, readonly, unsafe_unretained) NSURL *memCardURL; +@property (nonatomic, readonly) const char *memCardCPath; + +@end diff --git a/macosx/Source/PcsxrMemCardArray.m b/macosx/Source/PcsxrMemCardArray.m new file mode 100644 index 00000000..57de81ae --- /dev/null +++ b/macosx/Source/PcsxrMemCardArray.m @@ -0,0 +1,345 @@ +// +// PcsxrMemCardArray.m +// Pcsxr +// +// Created by C.W. Betts on 7/6/13. +// +// + +#import "PcsxrMemCardArray.h" +#import "ConfigurationController.h" +#include "sio.h" + +#define MAX_MEMCARD_BLOCKS 15 + +static inline void CopyMemcardData(char *from, char *to, int srci, int dsti, char *str) +{ + // header + memmove(to + (dsti + 1) * 128, from + (srci + 1) * 128, 128); + SaveMcd(str, to, (dsti + 1) * 128, 128); + + // data + memmove(to + (dsti + 1) * 1024 * 8, from + (srci+1) * 1024 * 8, 1024 * 8); + SaveMcd(str, to, (dsti + 1) * 1024 * 8, 1024 * 8); +} + +static inline char* BlankHeader() +{ + struct PSXMemHeader { + unsigned int allocState; + unsigned int fileSize; + unsigned short nextBlock; + char fileName[21]; + unsigned char garbage[96]; + unsigned char checksum; + }; + + static struct PSXMemHeader *toReturn = NULL; + if (!toReturn) { + toReturn = calloc(sizeof(struct PSXMemHeader), 1); + + //FIXME: Which value is right? + toReturn->allocState = 0x000000a0; + //toReturn->allocState = 0xa0000000; + toReturn->nextBlock = 0xFFFF; + unsigned char *bytePtr = (unsigned char*)toReturn; + for (int i = 0; i < sizeof(struct PSXMemHeader) - sizeof(unsigned char); i++) { + toReturn->checksum ^= bytePtr[i]; + } + } + + return (char*)toReturn; +} + +static inline void ClearMemcardData(char *to, int dsti, char *str) +{ + // header + char *header = BlankHeader(); + memcpy(to + (dsti + 1) * 128, header, 128); + SaveMcd(str, to, (dsti + 1) * 128, 128); + + // data + memset(to + (dsti + 1) * 1024 * 8, 0, 1024 * 8); + SaveMcd(str, to, (dsti + 1) * 1024 * 8, 1024 * 8); +} + +@interface PcsxrMemCardArray () +@property (strong) NSArray *rawArray; +@property (readonly) char* memDataPtr; +@property int cardNumber; +@end + +@implementation PcsxrMemCardArray +@synthesize rawArray; +@synthesize cardNumber; + +- (char*)memDataPtr +{ + if (cardNumber == 1) { + return Mcd1Data; + } else { + return Mcd2Data; + } +} + +- (const char *)memCardCPath +{ + if (cardNumber == 1) { + return Config.Mcd1; + } else { + return Config.Mcd2; + } +} + +- (id)initWithMemoryCardNumber:(int)carNum +{ + NSParameterAssert(carNum == 1 || carNum == 2); + if (self = [super init]) { + NSMutableArray *tmpMemArray = [[NSMutableArray alloc] initWithCapacity:MAX_MEMCARD_BLOCKS]; + cardNumber = carNum; + int i = 0, x; + while (i < MAX_MEMCARD_BLOCKS) { + x = 1; + McdBlock memBlock; + GetMcdBlockInfo(carNum, i + 1, &memBlock); + + if ([PcsxrMemoryObject memFlagsFromBlockFlags:memBlock.Flags] == memFlagFree) { + //Free space: ignore + i++; + continue; + } + do { + McdBlock tmpBlock; + GetMcdBlockInfo(carNum, i + x + 1, &tmpBlock); + if ((tmpBlock.Flags & 0x3) == 0x3) { + x++; + break; + } else if ((tmpBlock.Flags & 0x2) == 0x2) { + x++; + } else { + break; + } + } while (i + x - 1 < MAX_MEMCARD_BLOCKS); + @autoreleasepool { + PcsxrMemoryObject *obj = [[PcsxrMemoryObject alloc] initWithMcdBlock:&memBlock startingIndex:i size:x]; + [tmpMemArray addObject:obj]; + } + i += x; + } + self.rawArray = [[NSArray alloc] initWithArray:tmpMemArray]; + } + return self; +} + +- (int)indexOfFreeBlocksWithSize:(int)asize +{ + int foundcount = 0, i = 0; + + McdBlock obj; + // search for empty (formatted) blocks first + while (i < MAX_MEMCARD_BLOCKS && foundcount < asize) { + GetMcdBlockInfo(cardNumber, 1 + i++, &obj); + //&Blocks[target_card][++i]; + if ((obj.Flags & 0xFF) == 0xA0) { // if A0 but not A1 + foundcount++; + } else if (foundcount >= 1) { // need to find n count consecutive blocks + foundcount = 0; + } else { + //i++; + } + //printf("formatstatus=%x\n", Info->Flags); + } + + if (foundcount == asize) + return (i-foundcount); + + // no free formatted slots, try to find a deleted one + foundcount = i = 0; + while (i < MAX_MEMCARD_BLOCKS && foundcount < asize) { + GetMcdBlockInfo(cardNumber, 1 + i++, &obj); + if ((obj.Flags & 0xF0) == 0xA0) { // A2 or A6 f.e. + foundcount++; + } else if (foundcount >= 1) { // need to find n count consecutive blocks + foundcount = 0; + } else { + //i++; + } + //printf("delstatus=%x\n", Info->Flags); + } + + if (foundcount == asize) + return (i-foundcount); + + return -1; +} + +- (BOOL)moveBlockAtIndex:(int)idx toMemoryCard:(PcsxrMemCardArray*)otherCard +{ + if (idx == [rawArray count]) { +#ifdef DEBUG + NSLog(@"Trying to get an object one more than the length of the raw array. Perhaps you were trying to \"move\" the free blocks. We don't want to do this."); +#endif + return NO; + } + PcsxrMemoryObject *tmpObj = rawArray[idx]; + + int memSize = tmpObj.blockSize; + + if ([otherCard availableBlocks] < memSize) { + NSLog(@"Failing because the other card does not have enough space!"); + return NO; + } + + int toCopy = [otherCard indexOfFreeBlocksWithSize:memSize]; + if (toCopy == -1) { + NSLog(@"Not enough consecutive blocks. Compacting the other card."); + [otherCard compactMemory]; + //Since we're accessing the mem card data directly (instead of via PcsxrMemoryObject objects) using the following calls, we don't need to reload the data. + toCopy = [otherCard indexOfFreeBlocksWithSize:memSize]; + NSAssert(toCopy != -1, @"Compacting the card should have made space!"); + } + + int memIdx = tmpObj.startingIndex; + for (int i = 0; i < memSize; i++) { + CopyMemcardData([self memDataPtr], [otherCard memDataPtr], memIdx + i, toCopy + i, (char*)otherCard.memCardCPath); + } + + return YES; +} + +- (int)freeBlocks +{ + int memSize = MAX_MEMCARD_BLOCKS; + for (PcsxrMemoryObject *memObj in rawArray) { + memSize -= memObj.blockSize; + } + return memSize; +} + +- (int)availableBlocks +{ + int memSize = MAX_MEMCARD_BLOCKS; + for (PcsxrMemoryObject *memObj in rawArray) { + if (memObj.flagNameIndex != memFlagDeleted) { + memSize -= memObj.blockSize; + } + } + return memSize; +} + +- (NSArray*)memoryArray +{ + int freeSize = [self freeBlocks]; + + if (freeSize) { + McdBlock theBlock; + //Create a blank "block" that will be used to show the amount of free blocks + theBlock.Flags = 0xA0; + theBlock.IconCount = 0; + PcsxrMemoryObject *freeObj = [[PcsxrMemoryObject alloc] initWithMcdBlock:&theBlock startingIndex:MAX_MEMCARD_BLOCKS - 1 - freeSize size:freeSize]; + return [rawArray arrayByAddingObject:freeObj]; + } else + return rawArray; +} + +- (NSURL*)memCardURL +{ + if (cardNumber == 1) { + return [[NSUserDefaults standardUserDefaults] URLForKey:@"Mcd1"]; + } else { + return [[NSUserDefaults standardUserDefaults] URLForKey:@"Mcd2"]; + } +} + +- (int)memorySizeAtIndex:(int)idx +{ + if (idx == [rawArray count]) { +#ifdef DEBUG + NSLog(@"Trying to get an object one more than the length of the raw array. Perhaps you were trying to \"count\" the free blocks?"); +#endif + return [self freeBlocks]; + } + + return [rawArray[idx] blockSize]; +} + +- (void)compactMemory +{ + int i = 0, x = 1; + while (i < MAX_MEMCARD_BLOCKS && x < MAX_MEMCARD_BLOCKS) { + x = i; + McdBlock baseBlock; + GetMcdBlockInfo(cardNumber, i+1, &baseBlock); + PCSXRMemFlags theFlags = [PcsxrMemoryObject memFlagsFromBlockFlags:baseBlock.Flags]; + + if (theFlags == memFlagDeleted || theFlags == memFlagFree) { + PCSXRMemFlags up1Flags = theFlags; + while ((up1Flags == memFlagDeleted || up1Flags == memFlagFree) && x < MAX_MEMCARD_BLOCKS) { + x++; + McdBlock up1Block; + GetMcdBlockInfo(cardNumber, x+1, &up1Block); + up1Flags = [PcsxrMemoryObject memFlagsFromBlockFlags:up1Block.Flags]; + } + if (x >= MAX_MEMCARD_BLOCKS) { + break; + } + + CopyMemcardData(self.memDataPtr, self.memDataPtr, x, i, (char*)[[self.memCardURL path] fileSystemRepresentation]); + ClearMemcardData(self.memDataPtr, x, (char*)self.memCardCPath); + } + i++; + } + + while (i < MAX_MEMCARD_BLOCKS) { + ClearMemcardData(self.memDataPtr, i, (char*)self.memCardCPath); + i++; + } + + LoadMcd(cardNumber, (char*)self.memCardCPath); +} + +- (void)deleteMemoryBlocksAtIndex:(int)slotnum +{ + int xor = 0, i, j; + char *data, *ptr, *filename; + if (cardNumber == 1) { + filename = Config.Mcd1; + data = Mcd1Data; + } else { + filename = Config.Mcd2; + data = Mcd2Data; + } + + if (slotnum == [rawArray count]) { +#ifdef DEBUG + NSLog(@"Trying to get an object one more than the length of the raw array. Perhaps you were trying to \"delete\" the free blocks?"); +#endif + return; + } + + PcsxrMemoryObject *theObj = rawArray[slotnum]; + + McdBlock flagBlock; + + for(i = theObj.startingIndex + 1; i < (theObj.startingIndex + theObj.blockSize + 1); i++) + { + GetMcdBlockInfo(cardNumber, i, &flagBlock); + ptr = data + i * 128; + + if ((flagBlock.Flags & 0xF0) == 0xA0) { + if ((flagBlock.Flags & 0xF) >= 1 && + (flagBlock.Flags & 0xF) <= 3) { // deleted + *ptr = 0x50 | (flagBlock.Flags & 0xF); + } else return; + } else if ((flagBlock.Flags & 0xF0) == 0x50) { // used + *ptr = 0xA0 | (flagBlock.Flags & 0xF); + } else { continue; } + + for (j = 0; j < 127; j++) xor ^= *ptr++; + *ptr = xor; + + SaveMcd(filename, data, i * 128, 128); + } +} + +@end diff --git a/macosx/Source/PcsxrMemCardController.h b/macosx/Source/PcsxrMemCardController.h new file mode 100644 index 00000000..d4a71088 --- /dev/null +++ b/macosx/Source/PcsxrMemCardController.h @@ -0,0 +1,30 @@ +// +// PcsxrMemCardManager.h +// Pcsxr +// +// Created by Charles Betts on 11/23/11. +// Copyright (c) 2011 __MyCompanyName__. All rights reserved. +// + +#import <Cocoa/Cocoa.h> +@class PcsxrMemCardArray; + +@interface PcsxrMemCardController : NSViewController +{ + IBOutlet NSCollectionView *memCard1view; + IBOutlet NSCollectionView *memCard2view; + IBOutlet NSTextField *memCard1Label; + IBOutlet NSTextField *memCard2Label; +} +@property (readonly, strong) PcsxrMemCardArray *memCard1Array; +@property (readonly, strong) PcsxrMemCardArray *memCard2Array; + +- (IBAction)moveBlock:(id)sender; +- (IBAction)formatCard:(id)sender; +- (IBAction)deleteMemoryObject:(id)sender; +- (void)loadMemoryCardInfoForCard:(int)theCard; + +- (void)beginMemoryAnimation; +- (void)stopMemoryAnimation; + +@end diff --git a/macosx/Source/PcsxrMemCardController.m b/macosx/Source/PcsxrMemCardController.m new file mode 100644 index 00000000..7fb139ea --- /dev/null +++ b/macosx/Source/PcsxrMemCardController.m @@ -0,0 +1,238 @@ +// +// PcsxrMemCardManager.m +// Pcsxr +// +// Created by Charles Betts on 11/23/11. +// Copyright (c) 2011 __MyCompanyName__. All rights reserved. +// + +#import "PcsxrMemCardController.h" +#import "PcsxrMemoryObject.h" +#import "ConfigurationController.h" +#import "PcsxrMemCardHandler.h" +#import "PcsxrMemCardArray.h" +#include "sio.h" + +#define MAX_MEMCARD_BLOCKS 15 + +@interface PcsxrMemCardController () +@property (readwrite, strong) PcsxrMemCardArray *memCard1Array; +@property (readwrite, strong) PcsxrMemCardArray *memCard2Array; +@property (strong) NSTimer *imageAnimateTimer; +@end + +@implementation PcsxrMemCardController +@synthesize memCard1Array; +@synthesize memCard2Array; + +- (void)stopMemoryAnimation +{ + [self.imageAnimateTimer invalidate]; + self.imageAnimateTimer = nil; +} + +- (void)beginMemoryAnimation +{ + if (!_imageAnimateTimer) { + self.imageAnimateTimer = [[NSTimer alloc] initWithFireDate:[NSDate date] interval:0.30 target:self selector:@selector(animateMemCards:) userInfo:nil repeats:YES]; + [[NSRunLoop mainRunLoop] addTimer:self.imageAnimateTimer forMode:NSRunLoopCommonModes]; + } +} + +- (void)setupValues:(int)theCards +{ + NSParameterAssert(theCards < 4 && theCards > 0); + if (theCards == 3) { + LoadMcds(Config.Mcd1, Config.Mcd2); + } else { + LoadMcd(theCards, theCards == 1 ? Config.Mcd1 : Config.Mcd2); + } + NSFileManager *fm = [NSFileManager defaultManager]; + NSUserDefaults *def = [NSUserDefaults standardUserDefaults]; + NSString *fullPath = nil; + NSString *fileName = nil; + + if (theCards & 1) { + fullPath = [[def URLForKey:@"Mcd1"] path]; + fileName = [fm displayNameAtPath:fullPath]; + + [memCard1Label setStringValue:fileName]; + [memCard1Label setToolTip:fullPath]; + + [self loadMemoryCardInfoForCard:1]; + } + + if (theCards & 2) { + fullPath = [[def URLForKey:@"Mcd2"] path]; + fileName = [fm displayNameAtPath:fullPath]; + + [memCard2Label setStringValue:fileName]; + [memCard2Label setToolTip:fullPath]; + + [self loadMemoryCardInfoForCard:2]; + } +} + +- (void)loadMemoryCardInfoForCard:(int)theCard +{ + PcsxrMemCardArray *newArray = [[PcsxrMemCardArray alloc] initWithMemoryCardNumber:theCard]; + + if (theCard == 1) { + [self setMemCard1Array:newArray]; + } else { + [self setMemCard2Array:newArray]; + } +} + +- (void)memoryCardDidChangeNotification:(NSNotification *)aNote +{ + NSDictionary *dict = [aNote userInfo]; + NSNumber *theNum = dict[memCardChangeNumberKey]; + [self setupValues: theNum ? [theNum intValue] : 3]; +} + +- (void)awakeFromNib +{ + [super awakeFromNib]; + [self setupValues:3]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(memoryCardDidChangeNotification:) name:memChangeNotifier object:nil]; + + [self beginMemoryAnimation]; +} + +- (void)animateMemCards:(NSTimer*)theTimer +{ + [[NSNotificationCenter defaultCenter] postNotificationName:memoryAnimateTimerKey object:self]; +} + +- (IBAction)moveBlock:(id)sender +{ + NSInteger memCardSelect = [sender tag]; + NSCollectionView *cardView; + NSIndexSet *selection; + PcsxrMemCardArray *toCard, *fromCard; + int cardnum; + if (memCardSelect == 1) { + cardView = memCard2view; + toCard = memCard1Array; + fromCard = memCard2Array; + cardnum = 1; + } else { + cardView = memCard1view; + toCard = memCard2Array; + fromCard = memCard1Array; + cardnum = 2; + } + selection = [cardView selectionIndexes]; + if (!selection || [selection count] != 1) { + NSBeep(); + return; + } + NSInteger selectedIndex = [selection firstIndex]; + + int cardSize, freeConsBlocks, availBlocks; + + if ([[fromCard memoryArray][selectedIndex] flagNameIndex] == memFlagFree) { + NSBeep(); + return; + } + + cardSize = [fromCard memorySizeAtIndex:(int)selectedIndex]; + freeConsBlocks = [toCard indexOfFreeBlocksWithSize:cardSize]; + availBlocks = [toCard availableBlocks]; + if (freeConsBlocks == -1 && availBlocks >= cardSize) { + PcsxrMemoryObject *tmpmemobj = (fromCard.memoryArray)[selectedIndex]; + NSInteger copyOK = NSRunInformationalAlertPanel(NSLocalizedString(@"Free Size", nil), NSLocalizedString(@"Memory card %i does not have enough free consecutive blocks.\n\nIn order to copy over \"%@ (%@),\" memory card %i must be compressed. Compressing memory cards will make deleted blocks unrecoverable.\n\nDo you want to continue?", nil), NSLocalizedString(@"Yes", nil), NSLocalizedString(@"No", nil), nil, cardnum, tmpmemobj.englishName, tmpmemobj.sjisName, cardnum); + if (copyOK != NSAlertDefaultReturn) { + return; + } + } else if (cardSize > availBlocks) { + NSRunCriticalAlertPanel(NSLocalizedString(@"No Free Space", nil), NSLocalizedString(@"Memory card %d doesn't have %d free consecutive blocks on it. Please remove some blocks on that card to continue", nil), nil, nil, nil, availBlocks, cardnum); + return; + } + + [fromCard moveBlockAtIndex:(int)selectedIndex toMemoryCard:toCard]; + + if (cardnum == 1) { + LoadMcd(1, Config.Mcd1); + } else { + LoadMcd(2, Config.Mcd2); + } + [self loadMemoryCardInfoForCard:cardnum]; +} + +- (IBAction)formatCard:(id)sender +{ + NSInteger formatOkay = NSRunAlertPanel(NSLocalizedString(@"Format Card", nil), NSLocalizedString(@"Formatting a memory card will remove all data on it.\n\nThis cannot be undone.", nil), NSLocalizedString(@"Cancel", nil), NSLocalizedString(@"Format", nil), nil); + if (formatOkay == NSAlertAlternateReturn) { + NSInteger memCardSelect = [sender tag]; + if (memCardSelect == 1) { + CreateMcd(Config.Mcd1); + LoadMcd(1, Config.Mcd1); + } else { + CreateMcd(Config.Mcd2); + LoadMcd(2, Config.Mcd2); + } + [self loadMemoryCardInfoForCard:(int)memCardSelect]; + } +} + +- (void)deleteMemoryBlocksAtIndex:(int)slotnum card:(int)cardNum +{ + PcsxrMemCardArray *cardArray; + if (cardNum == 1) { + cardArray = [self memCard1Array]; + } else { + cardArray = [self memCard2Array]; + } + [cardArray deleteMemoryBlocksAtIndex:slotnum]; +} + +- (IBAction)deleteMemoryObject:(id)sender +{ + PcsxrMemCardArray *curCard; + NSInteger memCardSelect = [sender tag]; + NSIndexSet *selected; + if (memCardSelect == 1) { + curCard = memCard1Array; + selected = [memCard1view selectionIndexes]; + } else { + curCard = memCard2Array; + selected = [memCard2view selectionIndexes]; + } + + if (!selected || [selected count] == 0) { + NSBeep(); + return; + } + + NSInteger selectedIndex = [selected firstIndex]; + + PcsxrMemoryObject *tmpObj = [curCard memoryArray][selectedIndex]; + + if (tmpObj.flagNameIndex == memFlagFree) { + NSBeep(); + return; + } + + NSInteger deleteOkay = NSRunAlertPanel(NSLocalizedString(@"Delete Block", @"The block will be deleted"), NSLocalizedString(@"Deleting a block will remove all saved data on that block.\n\nThis cannot be undone.", @"Delete block cannot be undone"), NSLocalizedString(@"Cancel", @"Cancel"), NSLocalizedString(@"Delete", nil), nil); + if (deleteOkay == NSAlertAlternateReturn) { + [self deleteMemoryBlocksAtIndex:(int)selectedIndex card:(int)memCardSelect]; + + if (memCardSelect == 1) { + LoadMcd(1, Config.Mcd1); + } else { + LoadMcd(2, Config.Mcd2); + } + [self loadMemoryCardInfoForCard:(int)memCardSelect]; + } +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [self.imageAnimateTimer invalidate]; +} + +@end diff --git a/macosx/Source/PcsxrMemCardHandler.h b/macosx/Source/PcsxrMemCardHandler.h new file mode 100644 index 00000000..d87b73a7 --- /dev/null +++ b/macosx/Source/PcsxrMemCardHandler.h @@ -0,0 +1,15 @@ +// +// PcsxrMemCardHandler.h +// Pcsxr +// +// Created by Charles Betts on 12/10/11. +// Copyright (c) 2011 __MyCompanyName__. All rights reserved. +// + +#import <Cocoa/Cocoa.h> +#import "PcsxrFileHandle.h" + +@interface PcsxrMemCardHandler : NSWindowController <PcsxrFileHandle> +@property (weak) IBOutlet NSTextField *cardPath; +- (IBAction)setMemCard:(id)sender; +@end diff --git a/macosx/Source/PcsxrMemCardHandler.m b/macosx/Source/PcsxrMemCardHandler.m new file mode 100644 index 00000000..654ade0e --- /dev/null +++ b/macosx/Source/PcsxrMemCardHandler.m @@ -0,0 +1,81 @@ +// +// PcsxrMemCardHandler.m +// Pcsxr +// +// Created by Charles Betts on 12/10/11. +// Copyright (c) 2011 __MyCompanyName__. All rights reserved. +// + +#import "PcsxrMemCardHandler.h" +#import "ConfigurationController.h" +#import "EmuThread.h" + +@interface PcsxrMemCardHandler () +@property NSInteger memChosen; +@end + +@implementation PcsxrMemCardHandler +@synthesize cardPath; +@synthesize memChosen; + ++ (NSArray *)supportedUTIs +{ + static NSArray *utisupport = nil; + if (utisupport == nil) { + utisupport = @[@"com.codeplex.pcsxr.memcard"]; + } + return utisupport; +} + +- (id)initWithWindow:(NSWindow *)window +{ + self = [super initWithWindow:window]; + if (self) { + memChosen = 0; + } + + return self; +} + +- (id)init +{ + return self = [self initWithWindowNibName:@"PcsxrMemCard"]; +} + +- (void)windowDidLoad +{ + [super windowDidLoad]; + // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. +} + +- (NSString*)windowNibName +{ + return @"PcsxrMemCard"; +} + +- (IBAction)setMemCard:(id)sender +{ + memChosen = [sender tag]; + + [NSApp stopModal]; +} + +- (BOOL)handleFile:(NSString *)theFile +{ + if ([EmuThread active]) { + return NO; + } + + [cardPath setStringValue:[[NSFileManager defaultManager] displayNameAtPath:theFile]]; + + [NSApp runModalForWindow:[self window]]; + + [[self window] orderOut:nil]; + + if (memChosen != 0) { + [ConfigurationController setMemoryCard:memChosen toPath:theFile]; + } + return YES; +} + +@end diff --git a/macosx/Source/PcsxrMemoryObject.h b/macosx/Source/PcsxrMemoryObject.h new file mode 100644 index 00000000..5a7a3b37 --- /dev/null +++ b/macosx/Source/PcsxrMemoryObject.h @@ -0,0 +1,45 @@ +// +// PcsxrMemoryObject.h +// Pcsxr +// +// Created by Charles Betts on 11/23/11. +// Copyright (c) 2011 __MyCompanyName__. All rights reserved. +// + +#import <Cocoa/Cocoa.h> +#include "sio.h" + +extern NSString *const memoryAnimateTimerKey; + +typedef NS_ENUM(char, PCSXRMemFlags) { + memFlagDeleted, + memFlagFree, + memFlagUsed, + memFlagLink, + memFlagEndLink +}; + +@interface PcsxrMemoryObject : NSObject + ++ (NSArray *)imagesFromMcd:(McdBlock *)block; ++ (NSString*)memoryLabelFromFlag:(PCSXRMemFlags)flagNameIndex; ++ (NSImage *)blankImage; ++ (PCSXRMemFlags)memFlagsFromBlockFlags:(unsigned char)blockFlags; + +- (id)initWithMcdBlock:(McdBlock *)infoBlockc startingIndex:(uint8_t)startIdx size:(uint8_t)memSize; + +@property (readonly, strong) NSString *englishName; +@property (readonly, strong) NSString *sjisName; +@property (readonly, strong) NSString *memName; +@property (readonly, strong) NSString *memID; +@property (readonly) PCSXRMemFlags flagNameIndex; +@property (readonly) uint8_t startingIndex; +@property (readonly) uint8_t blockSize; + +@property (readonly, unsafe_unretained, nonatomic) NSImage *memImage; +@property (readonly, nonatomic) unsigned memIconCount; +@property (readonly, unsafe_unretained, nonatomic) NSString *flagName; +@property (readonly, unsafe_unretained, nonatomic) NSAttributedString *attributedFlagName; +@property (readonly, nonatomic) BOOL isBiggerThanOne; + +@end diff --git a/macosx/Source/PcsxrMemoryObject.m b/macosx/Source/PcsxrMemoryObject.m new file mode 100644 index 00000000..7d11b6f8 --- /dev/null +++ b/macosx/Source/PcsxrMemoryObject.m @@ -0,0 +1,323 @@ +// +// PcsxrMemoryObject.m +// Pcsxr +// +// Created by Charles Betts on 11/23/11. +// Copyright (c) 2011 __MyCompanyName__. All rights reserved. +// + +#import <Cocoa/Cocoa.h> +#import "PcsxrMemoryObject.h" + +NSString *const memoryAnimateTimerKey = @"PCSXR Memory Card Image Animate"; + +@interface PcsxrMemoryObject () +@property (readwrite, strong) NSString *englishName; +@property (readwrite, strong) NSString *sjisName; +@property (readwrite, strong) NSString *memName; +@property (readwrite, strong) NSString *memID; +@property (readwrite) uint8_t startingIndex; +@property (readwrite) uint8_t blockSize; + +@property (nonatomic) NSInteger memImageIndex; +@property (strong) NSArray *memImages; +@property (readwrite) PCSXRMemFlags flagNameIndex; +@end + +@implementation PcsxrMemoryObject + ++ (NSArray *)imagesFromMcd:(McdBlock *)block +{ + NSMutableArray *imagesArray = [[NSMutableArray alloc] initWithCapacity:block->IconCount]; + for (int i = 0; i < block->IconCount; i++) { + NSImage *memImage; + { + NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:16 pixelsHigh:16 bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace bytesPerRow:0 bitsPerPixel:0]; + + short *icon = block->Icon; + + int x, y, c, v, r, g, b; + for (v = 0; v < 256; v++) { + x = (v % 16); + y = (v / 16); + c = icon[(i * 256) + v]; + r = (c & 0x001f) << 3; + g = ((c & 0x03e0) >> 5) << 3; + b = ((c & 0x7c00) >> 10) << 3; + [imageRep setColor:[NSColor colorWithCalibratedRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1.0] atX:x y:y]; + } + memImage = [[NSImage alloc] init]; + [memImage addRepresentation:imageRep]; + [memImage setSize:NSMakeSize(32, 32)]; + } + [imagesArray addObject:memImage]; + } + return [NSArray arrayWithArray:imagesArray]; +} + +static NSString *MemLabelDeleted; +static NSString *MemLabelFree; +static NSString *MemLabelUsed; +static NSString *MemLabelLink; +static NSString *MemLabelEndLink; + ++ (void)initialize +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSBundle *mainBundle = [NSBundle mainBundle]; + MemLabelDeleted = [[mainBundle localizedStringForKey:@"MemCard_Deleted" value:@"" table:nil] copy]; + MemLabelFree = [[mainBundle localizedStringForKey:@"MemCard_Free" value:@"" table:nil] copy]; + MemLabelUsed = [[mainBundle localizedStringForKey:@"MemCard_Used" value:@"" table:nil] copy]; + MemLabelLink = [[mainBundle localizedStringForKey:@"MemCard_Link" value:@"" table:nil] copy]; + MemLabelEndLink = [[mainBundle localizedStringForKey:@"MemCard_EndLink" value:@"" table:nil] copy]; + }); +} + ++ (NSString*)memoryLabelFromFlag:(PCSXRMemFlags)flagNameIndex +{ + switch (flagNameIndex) { + default: + case memFlagFree: + return MemLabelFree; + break; + + case memFlagEndLink: + return MemLabelEndLink; + break; + + case memFlagLink: + return MemLabelLink; + break; + + case memFlagUsed: + return MemLabelUsed; + break; + + case memFlagDeleted: + return MemLabelDeleted; + break; + } +} + ++ (NSImage *)blankImage +{ + static NSImage *imageBlank = nil; + if (imageBlank == nil) { + NSRect imageRect = NSMakeRect(0, 0, 32, 32); + imageBlank = [[NSImage alloc] initWithSize:imageRect.size]; + [imageBlank lockFocus]; + [[NSColor blackColor] set]; + [NSBezierPath fillRect:imageRect]; + [imageBlank unlockFocus]; + } + return imageBlank; +} + ++ (PCSXRMemFlags)memFlagsFromBlockFlags:(unsigned char)blockFlags +{ + if ((blockFlags & 0xF0) == 0xA0) { + if ((blockFlags & 0xF) >= 1 && (blockFlags & 0xF) <= 3) + return memFlagDeleted; + else + return memFlagFree; + } else if ((blockFlags & 0xF0) == 0x50) { + if ((blockFlags & 0xF) == 0x1) + return memFlagUsed; + else if ((blockFlags & 0xF) == 0x2) + return memFlagLink; + else if ((blockFlags & 0xF) == 0x3) + return memFlagEndLink; + } else + return memFlagFree; + + //Xcode complains unless we do this... + NSLog(@"Unknown flag %x", blockFlags); + return memFlagFree; +} + +- (id)initWithMcdBlock:(McdBlock *)infoBlock startingIndex:(uint8_t)startIdx size:(uint8_t)memSize +{ + if (self = [super init]) { + self.startingIndex = startIdx; + self.blockSize = memSize; + self.flagNameIndex = [PcsxrMemoryObject memFlagsFromBlockFlags:infoBlock->Flags]; + if (self.flagNameIndex == memFlagFree) { + self.memImages = @[]; + self.memImageIndex = -1; + self.englishName = self.sjisName = @"Free block"; + self.memID = self.memName = @""; + } else { + self.englishName = @(infoBlock->Title); + self.sjisName = [NSString stringWithCString:infoBlock->sTitle encoding:NSShiftJISStringEncoding]; + + if ([englishName isEqualToString:sjisName]) { +#if 0 + if (![englishName isEqualToString:@""]) + NSLog(@"English name and sjis name are the same: %@. Replacing the sjis string with the English string.", englishName); +#endif + self.sjisName = self.englishName; + } + @autoreleasepool { + self.memImages = [PcsxrMemoryObject imagesFromMcd:infoBlock]; + } + + if ([memImages count] == 0) { + self.memImageIndex = -1; + } else if ([memImages count] == 1) { + self.memImageIndex = 0; + } else { + self.memImageIndex = 0; + [[NSNotificationCenter defaultCenter] addObserverForName:memoryAnimateTimerKey object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { + NSInteger index = memImageIndex; + if (++index >= [memImages count]) { + index = 0; + } + self.memImageIndex = index; + }]; + } + self.memName = @(infoBlock->Name); + self.memID = @(infoBlock->ID); + } + } + return self; +} + +#pragma mark - Property Synthesizers +@synthesize englishName; +@synthesize sjisName; +@synthesize memImageIndex; +- (void)setMemImageIndex:(NSInteger)theMemImageIndex +{ + [self willChangeValueForKey:@"memImage"]; + memImageIndex = theMemImageIndex; + [self didChangeValueForKey:@"memImage"]; +} + +@synthesize memName; +@synthesize memID; +@synthesize memImages; +@synthesize flagNameIndex; +@synthesize blockSize; +@synthesize startingIndex; + +#pragma mark Non-synthesized Properties +- (unsigned)memIconCount +{ + return (unsigned)[memImages count]; +} + +- (NSImage*)memImage +{ + if (memImageIndex == -1) { + return [PcsxrMemoryObject blankImage]; + } + return memImages[memImageIndex]; +} + +- (NSString*)flagName +{ + return [PcsxrMemoryObject memoryLabelFromFlag:flagNameIndex]; +} + +static inline void SetupAttrStr(NSMutableAttributedString *mutStr, NSColor *txtclr) +{ + NSRange wholeStrRange = NSMakeRange(0, mutStr.string.length); + [mutStr addAttribute:NSFontAttributeName value:[NSFont userFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]] range:wholeStrRange]; + [mutStr addAttribute:NSForegroundColorAttributeName value:txtclr range:wholeStrRange]; + [mutStr setAlignment:NSCenterTextAlignment range:wholeStrRange]; +} + +- (NSAttributedString*)attributedFlagName +{ + static NSAttributedString *attribMemLabelDeleted; + static NSAttributedString *attribMemLabelFree; + static NSAttributedString *attribMemLabelUsed; + static NSAttributedString *attribMemLabelLink; + static NSAttributedString *attribMemLabelEndLink; + + static dispatch_once_t attrStrSetOnceToken; + dispatch_once(&attrStrSetOnceToken, ^{ + NSMutableAttributedString *tmpStr = [[NSMutableAttributedString alloc] initWithString:MemLabelFree]; + SetupAttrStr(tmpStr, [NSColor greenColor]); + attribMemLabelFree = [tmpStr copy]; + +#ifdef DEBUG + tmpStr = [[NSMutableAttributedString alloc] initWithString:MemLabelEndLink]; + SetupAttrStr(tmpStr, [NSColor blueColor]); + attribMemLabelEndLink = [tmpStr copy]; + + tmpStr = [[NSMutableAttributedString alloc] initWithString:MemLabelLink]; + SetupAttrStr(tmpStr, [NSColor blueColor]); + attribMemLabelLink = [tmpStr copy]; + + tmpStr = [[NSMutableAttributedString alloc] initWithString:MemLabelUsed]; + SetupAttrStr(tmpStr, [NSColor controlTextColor]); + attribMemLabelUsed = [tmpStr copy]; +#else + tmpStr = [[NSMutableAttributedString alloc] initWithString:@"Multi-save"]; + SetupAttrStr(tmpStr, [NSColor blueColor]); + attribMemLabelEndLink = [tmpStr copy]; + + //tmpStr = [[NSMutableAttributedString alloc] initWithString:@"Multi-save"]; + //SetupAttrStr(tmpStr, [NSColor blueColor]); + //attribMemLabelLink = [tmpStr copy]; + //RELEASEOBJ(tmpStr); + attribMemLabelLink = attribMemLabelEndLink; + + //display nothing + attribMemLabelUsed = [[NSAttributedString alloc] initWithString:@""]; +#endif + + tmpStr = [[NSMutableAttributedString alloc] initWithString:MemLabelDeleted]; + SetupAttrStr(tmpStr, [NSColor redColor]); + attribMemLabelDeleted = [tmpStr copy]; + }); + + switch (flagNameIndex) { + default: + case memFlagFree: + return attribMemLabelFree; + break; + + case memFlagEndLink: + return attribMemLabelEndLink; + break; + + case memFlagLink: + return attribMemLabelLink; + break; + + case memFlagUsed: + return attribMemLabelUsed; + break; + + case memFlagDeleted: + return attribMemLabelDeleted; + break; + } +} + +- (BOOL)isBiggerThanOne +{ + if (flagNameIndex == memFlagFree) { + //Always show the size of the free blocks + return YES; + } else { + return blockSize != 1; + } +} + +#pragma mark - + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"%@ (%@): Name: %@ ID: %@, type: %@ start: %i size: %i", englishName, sjisName, memName, memID, self.flagName, startingIndex, blockSize]; +} + +@end diff --git a/macosx/Source/PcsxrPlugin.h b/macosx/Source/PcsxrPlugin.h new file mode 100644 index 00000000..f2a0f854 --- /dev/null +++ b/macosx/Source/PcsxrPlugin.h @@ -0,0 +1,32 @@ +// +// PcsxrPlugin.h +// Pcsxr +// +// Created by Gil Pedersen on Fri Oct 03 2003. +// Copyright (c) 2003 __MyCompanyName__. All rights reserved. +// + +#import <Foundation/Foundation.h> + +@interface PcsxrPlugin : NSObject +@property (readonly, copy) NSString *path; +@property (readonly, strong) NSString *name; +@property (readonly) int type; + ++ (NSString *)prefixForType:(int)type; ++ (NSString *)defaultKeyForType:(int)type; ++ (char **)configEntriesForType:(int)type; ++ (NSArray *)pluginsPaths; + +- (id)initWithPath:(NSString *)aPath; + +- (NSString *)displayVersion; +- (BOOL)hasAboutAs:(int)type; +- (BOOL)hasConfigureAs:(int)type; +- (long)runAs:(int)aType; +- (long)shutdownAs:(int)aType; +- (void)aboutAs:(int)type; +- (void)configureAs:(int)type; +- (BOOL)verifyOK; + +@end diff --git a/macosx/Source/PcsxrPlugin.m b/macosx/Source/PcsxrPlugin.m new file mode 100644 index 00000000..f326c040 --- /dev/null +++ b/macosx/Source/PcsxrPlugin.m @@ -0,0 +1,404 @@ +// +// PcsxrPlugin.m +// Pcsxr +// +// Created by Gil Pedersen on Fri Oct 03 2003. +// Copyright (c) 2003 __MyCompanyName__. All rights reserved. +// + +#import <Cocoa/Cocoa.h> +#import "PcsxrPlugin.h" +#include "psxcommon.h" +#include "plugins.h" + +#define kPCSXRGetLibName "PSEgetLibName" +#define kPCSXRGetLibVersion "PSEgetLibVersion" +#define kPCSXRGetLibType "PSEgetLibType" + +@interface PcsxrPlugin () +@property (readwrite, copy) NSString *path; +@property (readwrite, strong) NSString *name; +@property (strong) NSDate *modDate; +@property (strong) NSString *fullPlugPath; +@property long version; +@property (readwrite) int type; +@property int active; +@property void *pluginRef; +@end + +@implementation PcsxrPlugin +@synthesize pluginRef; +@synthesize active; +@synthesize fullPlugPath; +@synthesize modDate; +@synthesize path; +@synthesize type; +@synthesize version; + ++ (NSString *)prefixForType:(int)aType +{ + switch (aType) { + case PSE_LT_GPU: return @"GPU"; break; + case PSE_LT_CDR: return @"CDR"; break; + case PSE_LT_SPU: return @"SPU"; break; + case PSE_LT_PAD: return @"PAD"; break; + case PSE_LT_NET: return @"NET"; break; + case PSE_LT_SIO1: return @"SIO1"; break; + } + + return @""; +} + ++ (NSString *)defaultKeyForType:(int)aType +{ + //return @"Plugin" [PcsxrPlugin prefixForType:aType]; + switch (aType) { + case PSE_LT_GPU: + case PSE_LT_CDR: + case PSE_LT_SPU: + case PSE_LT_PAD: + case PSE_LT_NET: + case PSE_LT_SIO1: + return [NSString stringWithFormat:@"Plugin%@", [self prefixForType:aType]]; + break; + default: + return @""; + break; + } +} + ++ (char **)configEntriesForType:(int)aType +{ + static char *gpu[2] = {(char *)&Config.Gpu, NULL}; + static char *cdr[2] = {(char *)&Config.Cdr, NULL}; + static char *spu[2] = {(char *)&Config.Spu, NULL}; + static char *pad[3] = {(char *)&Config.Pad1, (char *)&Config.Pad2, NULL}; + static char *net[2] = {(char *)&Config.Net, NULL}; + static char *sio1[2] = {(char *)&Config.Sio1, NULL}; + + switch (aType) { + case PSE_LT_GPU: return (char **)gpu; + case PSE_LT_CDR: return (char **)cdr; + case PSE_LT_SPU: return (char **)spu; + case PSE_LT_PAD: return (char **)pad; + case PSE_LT_NET: return (char **)net; + case PSE_LT_SIO1: return (char **)sio1; + } + + return nil; +} + ++ (NSArray *)pluginsPaths +{ + static NSArray *returnArray = nil; + if (returnArray == nil) + { + NSURL *supportURL = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL]; + NSURL *libraryURL = [[NSFileManager defaultManager] URLForDirectory:NSLibraryDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL]; + NSURL *localSupportURL = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSLocalDomainMask appropriateForURL:nil create:YES error:NULL]; + NSURL *localLibraryURL = [[NSFileManager defaultManager] URLForDirectory:NSLibraryDirectory inDomain:NSLocalDomainMask appropriateForURL:nil create:YES error:NULL]; + + NSMutableArray *mutArray = [NSMutableArray arrayWithCapacity:5]; + + [mutArray addObject:[[NSFileManager defaultManager] stringWithFileSystemRepresentation:Config.PluginsDir length:strlen(Config.PluginsDir)]]; + NSURL *url = [localLibraryURL URLByAppendingPathComponent:@"Playstation Emulator Plugins"]; + if ([url checkResourceIsReachableAndReturnError:NULL]) + [mutArray addObject:[url path]]; + url = [[localSupportURL URLByAppendingPathComponent:@"Pcsxr"] URLByAppendingPathComponent:@"PlugIns"]; + if ([url checkResourceIsReachableAndReturnError:NULL]) + [mutArray addObject:[url path]]; + url = [libraryURL URLByAppendingPathComponent:@"Playstation Emulator Plugins"]; + if ([url checkResourceIsReachableAndReturnError:NULL]) + [mutArray addObject:[url path]]; + url = [[supportURL URLByAppendingPathComponent:@"Pcsxr"] URLByAppendingPathComponent:@"PlugIns"]; + if ([url checkResourceIsReachableAndReturnError:NULL]) + [mutArray addObject:[url path]]; + returnArray = [[NSArray alloc] initWithArray:mutArray]; + } + return returnArray; +} + +- (id)initWithPath:(NSString *)aPath +{ + if (!(self = [super init])) { + return nil; + } + + PSEgetLibType PSE_getLibType = NULL; + PSEgetLibVersion PSE_getLibVersion = NULL; + PSEgetLibName PSE_getLibName = NULL; + + NSFileManager *fm = [NSFileManager defaultManager]; + + pluginRef = NULL; + self.name = nil; + self.path = aPath; + NSString *goodPath = nil; + if ([aPath isAbsolutePath]) { + goodPath = aPath; + } else { + long tempVers = 0; + for (NSString *plugDir in [PcsxrPlugin pluginsPaths]) + { + NSString *fullPath = [plugDir stringByAppendingPathComponent:path]; + if ([fm fileExistsAtPath:fullPath]) { + void *tempHandle = SysLoadLibrary([fullPath fileSystemRepresentation]); + if (tempHandle != NULL) + { + PSEgetLibVersion tempLibVersion = SysLoadSym(tempHandle, kPCSXRGetLibVersion); + if (SysLibError() == NULL) + { + long tempVers2 = tempLibVersion(); + if (tempVers < tempVers2 ){ + goodPath = fullPath; + tempVers = tempVers2; + if (![plugDir isEqualToString:[fm stringWithFileSystemRepresentation:Config.PluginsDir length:strlen(Config.PluginsDir)]]) { + self.path = goodPath; + } + } + } + SysCloseLibrary(tempHandle); + } else { + SysLibError(); + } + } + } + } + + if (goodPath == nil) { + return nil; + } + + pluginRef = SysLoadLibrary([goodPath fileSystemRepresentation]); + if (pluginRef == NULL) { + SysLibError(); + return nil; + } + + // TODO: add support for plugins with multiple functionalities??? + PSE_getLibType = (PSEgetLibType) SysLoadSym(pluginRef, kPCSXRGetLibType); + if (SysLibError() != nil) { + if (([path rangeOfString: @"gpu" options:NSCaseInsensitiveSearch]).length != 0) + type = PSE_LT_GPU; + else if (([path rangeOfString: @"cdr" options:NSCaseInsensitiveSearch]).length != 0) + type = PSE_LT_CDR; + else if (([path rangeOfString: @"spu" options:NSCaseInsensitiveSearch]).length != 0) + type = PSE_LT_SPU; + else if (([path rangeOfString: @"pad" options:NSCaseInsensitiveSearch]).length != 0) + type = PSE_LT_PAD; + else if (([path rangeOfString: @"net" options:NSCaseInsensitiveSearch]).length != 0) + type = PSE_LT_NET; + else if (([path rangeOfString: @"sio1" options:NSCaseInsensitiveSearch]).length != 0) + type = PSE_LT_SIO1; + else { + return nil; + } + } else { + type = (int)PSE_getLibType(); + if (type != PSE_LT_GPU && type != PSE_LT_CDR && type != PSE_LT_SPU && type != PSE_LT_PAD && type != PSE_LT_NET && type != PSE_LT_SIO1) { + return nil; + } + } + + PSE_getLibName = (PSEgetLibName) SysLoadSym(pluginRef, kPCSXRGetLibName); + if (SysLibError() == nil) { + self.name = @(PSE_getLibName()); + } + + PSE_getLibVersion = (PSEgetLibVersion) SysLoadSym(pluginRef, kPCSXRGetLibVersion); + if (SysLibError() == nil) { + version = PSE_getLibVersion(); + } else { + version = -1; + } + + // save the current modification date + NSDictionary *fattrs = [fm attributesOfItemAtPath:[goodPath stringByResolvingSymlinksInPath] error:NULL]; + self.modDate = [fattrs fileModificationDate]; + self.fullPlugPath = goodPath; + + active = 0; + + return self; +} + +- (void)dealloc +{ + int i; + + // shutdown if we had previously been inited + for (i=0; i<32; i++) { + if (active & (1 << i)) { + [self shutdownAs:(1 << i)]; + } + } + + if (pluginRef) { + SysCloseLibrary(pluginRef); + pluginRef = NULL; + } +} + +#define PluginSymbolName(type, theName) [[PcsxrPlugin prefixForType:type] stringByAppendingString:theName] + +- (void)runCommand:(id)arg +{ + @autoreleasepool { + NSString *funcName = arg[0]; + long (*func)(void); + + func = SysLoadSym(pluginRef, [funcName cStringUsingEncoding:NSASCIIStringEncoding]); + if (SysLibError() == NULL) { + func(); + } else { + NSBeep(); + } + + return; + } +} + +- (long)runAs:(int)aType +{ + long (*init)(); + long (*initArg)(long arg); + long res = PSE_ERR_FATAL; + + if ((active & aType) == aType) { + return 0; + } + + init = initArg = SysLoadSym(pluginRef, [PluginSymbolName(aType, @"init") + cStringUsingEncoding:NSASCIIStringEncoding]); + if (SysLibError() == NULL) { + if (aType != PSE_LT_PAD) { + res = init(); + } else { + res = initArg(1|2); + } + } + + if (0 == res) { + active |= aType; + } else { + NSRunCriticalAlertPanel(NSLocalizedString(@"Plugin Initialization Failed!", nil), + NSLocalizedString(@"Pcsxr failed to initialize the selected %@ plugin (error=%i).\nThe plugin might not work with your system.", nil), + nil, nil, nil, [PcsxrPlugin prefixForType:aType], res); + return res; + } + + //Prevent a memory leak. + long (*shutdown)(void); + shutdown = SysLoadSym(pluginRef, [PluginSymbolName(aType, @"shutdown") + cStringUsingEncoding:NSASCIIStringEncoding]); + if (SysLibError() == NULL) { + shutdown(); + } + + return res; +} + +- (long)shutdownAs:(int)aType +{ +#if 0 + long (*shutdown)(void); + + shutdown = SysLoadSym(pluginRef, [PluginSymbolName(aType, @"shutdown") + cStringUsingEncoding:NSASCIIStringEncoding]); + if (SysLibError() == nil) { + active &= ~aType; + return shutdown(); + } + + return PSE_ERR_FATAL; +#else + active &= ~aType; + return PSE_ERR_SUCCESS; +#endif +} + +#define PluginSymbolNameConfigure(type) PluginSymbolName(type, @"configure") +#define PluginSymbolNameAbout(type) PluginSymbolName(type, @"about") + +- (BOOL)hasAboutAs:(int)aType +{ + SysLoadSym(pluginRef, [PluginSymbolNameAbout(aType) + cStringUsingEncoding:NSASCIIStringEncoding]); + + return (SysLibError() == NULL); +} + +- (BOOL)hasConfigureAs:(int)aType +{ + SysLoadSym(pluginRef, [PluginSymbolNameConfigure(aType) + cStringUsingEncoding:NSASCIIStringEncoding]); + + return (SysLibError() == NULL); +} + +- (void)aboutAs:(int)aType +{ + NSArray *arg; + + NSString *aboutSym = PluginSymbolNameAbout(aType); + arg = @[aboutSym, @0]; + + // detach a new thread + [NSThread detachNewThreadSelector:@selector(runCommand:) toTarget:self + withObject:arg]; +} + +- (void)configureAs:(int)aType +{ + NSArray *arg; + + NSString *configSym = PluginSymbolNameConfigure(aType); + arg = @[configSym, @1]; + + // detach a new thread + [NSThread detachNewThreadSelector:@selector(runCommand:) toTarget:self + withObject:arg]; +} + +- (NSString *)displayVersion +{ + if (version == -1) + return @""; + + return [NSString stringWithFormat:@"v%ld.%ld.%ld", version>>16,(version>>8)&0xff,version&0xff]; +} + +- (NSUInteger)hash +{ + return [path hash]; +} + +- (NSString *)description +{ + if (_name == nil) + return [path lastPathComponent]; + + return [NSString stringWithFormat:@"%@ %@ [%@]", self.name, [self displayVersion], [path lastPathComponent]]; +} + +- (NSString*)debugDescription +{ + if (_name == nil) { + return fullPlugPath; + } + return [NSString stringWithFormat:@"%@, %@ [%@]", self.name, [self displayVersion], fullPlugPath]; +} + +// the plugin will check if it's still valid and return the status +- (BOOL)verifyOK +{ + // check that the file is still there with the same modification date + NSFileManager *dfm = [NSFileManager defaultManager]; + if (![dfm fileExistsAtPath:fullPlugPath]) + return NO; + + NSDictionary *fattrs = [dfm attributesOfItemAtPath:[fullPlugPath stringByResolvingSymlinksInPath] error:NULL]; + return [[fattrs fileModificationDate] isEqualToDate:modDate]; +} + +@end diff --git a/macosx/Source/PcsxrPluginHandler.h b/macosx/Source/PcsxrPluginHandler.h new file mode 100644 index 00000000..2855e410 --- /dev/null +++ b/macosx/Source/PcsxrPluginHandler.h @@ -0,0 +1,15 @@ +// +// PcsxrPluginHandler.h +// Pcsxr +// +// Created by Charles Betts on 12/10/11. +// Copyright (c) 2011 __MyCompanyName__. All rights reserved. +// + +#import <Cocoa/Cocoa.h> +#import "PcsxrFileHandle.h" + +@interface PcsxrPluginHandler : NSWindowController <PcsxrFileHandle> +@property (weak) IBOutlet NSTextField *pluginName; +- (IBAction)closeAddPluginSheet:(id)sender; +@end diff --git a/macosx/Source/PcsxrPluginHandler.m b/macosx/Source/PcsxrPluginHandler.m new file mode 100644 index 00000000..6e99932b --- /dev/null +++ b/macosx/Source/PcsxrPluginHandler.m @@ -0,0 +1,97 @@ +// +// PcsxrPluginHandler.m +// Pcsxr +// +// Created by Charles Betts on 12/10/11. +// Copyright (c) 2011 __MyCompanyName__. All rights reserved. +// + +#import "PcsxrPluginHandler.h" +#import "EmuThread.h" + +@interface PcsxrPluginHandler () +@property BOOL moveOK; +@end + +@implementation PcsxrPluginHandler +@synthesize pluginName; +@synthesize moveOK; + ++ (NSArray *)supportedUTIs +{ + static NSArray *utisupport = nil; + if (utisupport == nil) { + utisupport = @[@"com.codeplex.pcsxr.plugin"]; + } + return utisupport; +} + +- (id)initWithWindow:(NSWindow *)window +{ + self = [super initWithWindow:window]; + if (self) { + moveOK = NO; + } + + return self; +} + +- (NSString*)windowNibName +{ + return @"AddPluginSheet"; +} + +- (id)init +{ + return self = [self initWithWindowNibName:@"AddPluginSheet"]; +} + +- (void)windowDidLoad +{ + [super windowDidLoad]; + + // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. +} + +- (IBAction)closeAddPluginSheet:(id)sender +{ + if ([[sender keyEquivalent] isEqualToString:@"\r"]) { + moveOK = YES; + } else { + moveOK = NO; + } + [NSApp stopModal]; +} + +- (BOOL)handleFile:(NSString *)theFile +{ + if ([EmuThread active]) { + return NO; + } + + [pluginName setObjectValue:[theFile lastPathComponent]]; + + [NSApp runModalForWindow:[self window]]; + + [[self window] orderOut:nil]; + if (moveOK == YES) { + NSURL *supportURL = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL]; + NSURL *url = [[supportURL URLByAppendingPathComponent:@"Pcsxr" isDirectory:YES] URLByAppendingPathComponent:@"PlugIns" isDirectory:YES]; + + NSFileWrapper *wrapper = [[NSFileWrapper alloc] initWithPath:theFile]; + NSString *dst = [[url URLByAppendingPathComponent:[wrapper filename]] path]; + if ([wrapper writeToFile:dst atomically:NO updateFilenames:NO]) { + [[NSWorkspace sharedWorkspace] noteFileSystemChanged:[url path]]; + NSRunInformationalAlertPanel(NSLocalizedString(@"Installation Succesfull", nil), + NSLocalizedString(@"The installation of the specified plugin was succesfull. In order to use it, please restart the application.", nil), + nil, nil, nil); + } else { + NSRunAlertPanel(NSLocalizedString(@"Installation Failed!", nil), + NSLocalizedString(@"The installation of the specified plugin failed. Please try again, or make a manual install.", nil), + nil, nil, nil); + } + } + return YES; +} + +@end diff --git a/macosx/Source/Plugin.c b/macosx/Source/Plugin.c new file mode 100644 index 00000000..b9e7ff4a --- /dev/null +++ b/macosx/Source/Plugin.c @@ -0,0 +1,227 @@ +/* Pcsxr - Pc Psx Emulator + * Copyright (C) 1999-2002 Pcsx Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <CoreFoundation/CoreFoundation.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> + +#include "psxcommon.h" +#include "plugins.h" +#include "spu.h" + +void OnFile_Exit(); + +unsigned long gpuDisp; + +long SPU__open(void) { + return SPU_open(); +} + +int StatesC = 0; +extern int UseGui; +int ShowPic=0; + +void gpuShowPic() { +} + +void PADhandleKey(int key) { + //TODO: get these set up properly! + GPU_keypressed(key); +#ifdef ENABLE_SIO1API + SIO1_keypressed(key); +#endif + if (Config.UseNet) + NET_keypressed(key); +} + +long PAD1__open(void) { + return PAD1_open(&gpuDisp); +} + +long PAD2__open(void) { + return PAD2_open(&gpuDisp); +} + +void SignalExit(int sig) { + ClosePlugins(); + OnFile_Exit(); +} + +#define PARSEPATH(dst, src) \ + ptr = src + strlen(src); \ + while (*ptr != '\\' && ptr != src) ptr--; \ + if (ptr != src) { \ + strcpy(dst, ptr+1); \ + } + +int _OpenPlugins() { + long ret; + + GPU_clearDynarec(clearDynarec); + + ret = CDR_open(); + if (ret < 0) { SysMessage("%s", _("Error Opening CDR Plugin")); return -1; } + ret = SPU_open(); + if (ret < 0) { SysMessage("%s", _("Error Opening SPU Plugin")); return -1; } + SPU_registerCallback(SPUirq); + ret = GPU_open(&gpuDisp, "PCSXR", NULL); + if (ret < 0) { SysMessage("%s", _("Error Opening GPU Plugin")); return -1; } + ret = PAD1_open(&gpuDisp); + if (ret < 0) { SysMessage("%s", _("Error Opening PAD1 Plugin")); return -1; } + PAD1_registerVibration(GPU_visualVibration); + PAD1_registerCursor(GPU_cursor); + ret = PAD2_open(&gpuDisp); + if (ret < 0) { SysMessage("%s", _("Error Opening PAD2 Plugin")); return -1; } + PAD2_registerVibration(GPU_visualVibration); + PAD2_registerCursor(GPU_cursor); +#ifdef ENABLE_SIO1API + ret = SIO1_open(&gpuDisp); + if (ret < 0) { SysMessage("%s", _("Error opening SIO1 plugin!")); return -1; } + SIO1_registerCallback(SIO1irq); +#endif + + if (Config.UseNet && !NetOpened) { + netInfo info; + char path[MAXPATHLEN]; + + strcpy(info.EmuName, "PCSXR " PACKAGE_VERSION); + strncpy(info.CdromID, CdromId, 9); + strncpy(info.CdromLabel, CdromLabel, 11); + info.psxMem = psxM; + info.GPU_showScreenPic = GPU_showScreenPic; + info.GPU_displayText = GPU_displayText; + info.GPU_showScreenPic = GPU_showScreenPic; + info.PAD_setSensitive = PAD1_setSensitive; + sprintf(path, "%s%s", Config.BiosDir, Config.Bios); + strcpy(info.BIOSpath, path); + strcpy(info.MCD1path, Config.Mcd1); + strcpy(info.MCD2path, Config.Mcd2); + sprintf(path, "%s%s", Config.PluginsDir, Config.Gpu); + strcpy(info.GPUpath, path); + sprintf(path, "%s%s", Config.PluginsDir, Config.Spu); + strcpy(info.SPUpath, path); + sprintf(path, "%s%s", Config.PluginsDir, Config.Cdr); + strcpy(info.CDRpath, path); + NET_setInfo(&info); + + ret = NET_open(&gpuDisp); + if (ret < 0) { + if (ret == -2) { + // -2 is returned when something in the info + // changed and needs to be synced + char *ptr; + + PARSEPATH(Config.Bios, info.BIOSpath); + PARSEPATH(Config.Gpu, info.GPUpath); + PARSEPATH(Config.Spu, info.SPUpath); + PARSEPATH(Config.Cdr, info.CDRpath); + + strcpy(Config.Mcd1, info.MCD1path); + strcpy(Config.Mcd2, info.MCD2path); + return -2; + } else { + Config.UseNet = FALSE; + } + } else { + if (NET_queryPlayer() == 1) { + if (SendPcsxInfo() == -1) Config.UseNet = FALSE; + } else { + if (RecvPcsxInfo() == -1) Config.UseNet = FALSE; + } + } + NetOpened = TRUE; + } else if (Config.UseNet) { + NET_resume(); + } + + return 0; +} + +int OpenPlugins() { + int ret; + + while ((ret = _OpenPlugins()) == -2) { + ReleasePlugins(); + LoadMcds(Config.Mcd1, Config.Mcd2); + if (LoadPlugins() == -1) return -1; + } + return ret; +} + +void ClosePlugins() { + long ret; + + //signal(SIGINT, SIG_DFL); + //signal(SIGPIPE, SIG_DFL); + ret = CDR_close(); + if (ret < 0) { SysMessage("%s", _("Error Closing CDR Plugin")); return; } + ret = SPU_close(); + if (ret < 0) { SysMessage("%s", _("Error Closing SPU Plugin")); return; } + ret = PAD1_close(); + if (ret < 0) { SysMessage("%s", _("Error Closing PAD1 Plugin")); return; } + ret = PAD2_close(); + if (ret < 0) { SysMessage("%s", _("Error Closing PAD2 Plugin")); return; } + ret = GPU_close(); + if (ret < 0) { SysMessage("%s", _("Error Closing GPU Plugin")); return; } +#ifdef ENABLE_SIO1API + ret = SIO1_close(); + if (ret < 0) { SysMessage("%s", _("Error closing SIO1 plugin!")); return; } +#endif + + if (Config.UseNet) { + NET_pause(); + } +} + +void ResetPlugins() { + long ret; + + CDR_shutdown(); + GPU_shutdown(); + SPU_shutdown(); + PAD1_shutdown(); + PAD2_shutdown(); +#ifdef ENABLE_SIO1API + SIO1_shutdown(); +#endif + if (Config.UseNet) NET_shutdown(); + + ret = CDR_init(); + if (ret < 0) { SysMessage(_("CDRinit error: %d"), ret); return; } + ret = GPU_init(); + if (ret < 0) { SysMessage(_("GPUinit error: %d"), ret); return; } + ret = SPU_init(); + if (ret < 0) { SysMessage(_("SPUinit error: %d"), ret); return; } + ret = PAD1_init(1); + if (ret < 0) { SysMessage(_("PAD1init error: %d"), ret); return; } + ret = PAD2_init(2); + if (ret < 0) { SysMessage(_("PAD2init error: %d"), ret); return; } +#ifdef ENABLE_SIO1API + ret = SIO1_init(); + if (ret < 0) { SysMessage(_("SIO1init error: %d!"), ret); return; } +#endif + + if (Config.UseNet) { + ret = NET_init(); + if (ret < 0) { SysMessage(_("NETinit error: %d"), ret); return; } + } + + NetOpened = FALSE; +} diff --git a/macosx/Source/PluginController.h b/macosx/Source/PluginController.h new file mode 100644 index 00000000..4519d98a --- /dev/null +++ b/macosx/Source/PluginController.h @@ -0,0 +1,18 @@ +/* PluginController */ + +#import <Cocoa/Cocoa.h> +#import "PluginList.h" + +@interface PluginController : NSObject + +@property (weak) IBOutlet NSButton *aboutButton; +@property (weak) IBOutlet NSButton *configureButton; +@property (weak) IBOutlet NSPopUpButton *pluginMenu; + +- (IBAction)doAbout:(id)sender; +- (IBAction)doConfigure:(id)sender; +- (IBAction)selectPlugin:(id)sender; + +- (void)setPluginsTo:(NSArray *)list withType:(int)type; + +@end diff --git a/macosx/Source/PluginController.m b/macosx/Source/PluginController.m new file mode 100644 index 00000000..2d9b26c9 --- /dev/null +++ b/macosx/Source/PluginController.m @@ -0,0 +1,83 @@ +#import "PluginController.h" +#import "PcsxrPlugin.h" +#import "PcsxrController.h" + +@interface PluginController () +@property (strong) NSArray *plugins; +@property (strong) NSString *defaultKey; +@property int pluginType; +@end + +@implementation PluginController +@synthesize aboutButton; +@synthesize configureButton; +@synthesize pluginMenu; + +- (IBAction)doAbout:(id)sender +{ + PcsxrPlugin *plugin = (self.plugins)[[pluginMenu indexOfSelectedItem]]; + [plugin aboutAs:self.pluginType]; +} + +- (IBAction)doConfigure:(id)sender +{ + PcsxrPlugin *plugin = (self.plugins)[[pluginMenu indexOfSelectedItem]]; + [plugin configureAs:self.pluginType]; +} + +- (IBAction)selectPlugin:(id)sender +{ + if (sender == pluginMenu) { + NSInteger index = [pluginMenu indexOfSelectedItem]; + if (index != -1) { + PcsxrPlugin *plugin = (self.plugins)[index]; + + if (![[PluginList list] setActivePlugin:plugin forType:self.pluginType]) { + /* plugin won't initialize */ + } + + // write selection to defaults + [[NSUserDefaults standardUserDefaults] setObject:[plugin path] forKey:self.defaultKey]; + + // set button states + [aboutButton setEnabled:[plugin hasAboutAs:self.pluginType]]; + [configureButton setEnabled:[plugin hasConfigureAs:self.pluginType]]; + } else { + // set button states + [aboutButton setEnabled:NO]; + [configureButton setEnabled:NO]; + } + } +} + +// must be called before anything else +- (void)setPluginsTo:(NSArray *)list withType:(int)type +{ + NSString *sel; + + // remember the list + self.pluginType = type; + self.plugins = list; + self.defaultKey = [PcsxrPlugin defaultKeyForType:self.pluginType]; + + // clear the previous menu items + [pluginMenu removeAllItems]; + + // load the currently selected plugin + sel = [[NSUserDefaults standardUserDefaults] stringForKey:self.defaultKey]; + + // add the menu entries + for (PcsxrPlugin *plug in self.plugins) { + NSString *description = [plug description]; + [pluginMenu addItemWithTitle:description]; + + // make sure the currently selected is set as such + if ([sel isEqualToString:[plug path]]) { + [pluginMenu selectItemWithTitle:description]; + } + } + + [self selectPlugin:pluginMenu]; +} + +@end diff --git a/macosx/Source/PluginList.h b/macosx/Source/PluginList.h new file mode 100644 index 00000000..f6e0de55 --- /dev/null +++ b/macosx/Source/PluginList.h @@ -0,0 +1,28 @@ +// +// PluginList.h +// Pcsxr +// +// Created by Gil Pedersen on Sun Sep 21 2003. +// Copyright (c) 2003 __MyCompanyName__. All rights reserved. +// + +#import <Cocoa/Cocoa.h> +#import "PcsxrPlugin.h" + +//extern NSMutableArray *plugins; + +@interface PluginList : NSObject + ++ (PluginList *)list; + +- (void)refreshPlugins; +- (NSArray *)pluginsForType:(int)typeMask; +- (BOOL)hasPluginAtPath:(NSString *)path; +- (BOOL)configured; +- (PcsxrPlugin *)activePluginForType:(int)type; +- (BOOL)setActivePlugin:(PcsxrPlugin *)plugin forType:(int)type; + +- (void)disableNetPlug; +- (void)enableNetPlug; + +@end diff --git a/macosx/Source/PluginList.m b/macosx/Source/PluginList.m new file mode 100644 index 00000000..6b86bd90 --- /dev/null +++ b/macosx/Source/PluginList.m @@ -0,0 +1,318 @@ +// +// PluginList.m +// Pcsxr +// +// Created by Gil Pedersen on Sun Sep 21 2003. +// Copyright (c) 2003 __MyCompanyName__. All rights reserved. +// + +#import "EmuThread.h" +#import "PluginList.h" +#import "PcsxrPlugin.h" +#include "psxcommon.h" +#include "plugins.h" + +static PluginList __weak *sPluginList = nil; +const static int typeList[] = {PSE_LT_GPU, PSE_LT_SPU, PSE_LT_CDR, PSE_LT_PAD, PSE_LT_NET, PSE_LT_SIO1}; + +@interface PluginList () +@property (strong) NSMutableArray *pluginList; +@property BOOL missingPlugins; +@property (strong) PcsxrPlugin *activeGpuPlugin; +@property (strong) PcsxrPlugin *activeSpuPlugin; +@property (strong) PcsxrPlugin *activeCdrPlugin; +@property (strong) PcsxrPlugin *activePadPlugin; +@property (strong) PcsxrPlugin *activeNetPlugin; +@property (strong) PcsxrPlugin *activeSIO1Plugin; + +@end + +@implementation PluginList +@synthesize missingPlugins; + ++ (PluginList *)list +{ + return sPluginList; +} + +- (id)init +{ + NSUInteger i; + + if (!(self = [super init])) + { + return nil; + } + + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + self.pluginList = [[NSMutableArray alloc] initWithCapacity:20]; + + self.activeGpuPlugin = self.activeSpuPlugin = self.activeCdrPlugin = self.activePadPlugin = self.activeNetPlugin = self.activeSIO1Plugin = nil; + + missingPlugins = NO; + for (i = 0; i < sizeof(typeList) / sizeof(typeList[0]); i++) { + NSString *path = [defaults stringForKey:[PcsxrPlugin defaultKeyForType:typeList[i]]]; + if (nil == path) { + missingPlugins = YES; + continue; + } + if ([path isEqualToString:@"Disabled"]) + continue; + + if (![self hasPluginAtPath:path]) { + @autoreleasepool { + PcsxrPlugin *plugin = [[PcsxrPlugin alloc] initWithPath:path]; + if (plugin) { + [self.pluginList addObject:plugin]; + if (![self setActivePlugin:plugin forType:typeList[i]]) + missingPlugins = YES; + } else { + missingPlugins = YES; + } + } + } + } + + if (missingPlugins) { + [self refreshPlugins]; + } + + sPluginList = self; + + return self; +} + +- (void)refreshPlugins +{ + NSDirectoryEnumerator *dirEnum; + NSString *pname; + NSUInteger i; + + // verify that the ones that are in list still works + for (i=0; i < [self.pluginList count]; i++) { + if (![(self.pluginList)[i] verifyOK]) { + [self.pluginList removeObjectAtIndex:i]; i--; + } + } + + for (NSString *plugDir in [PcsxrPlugin pluginsPaths]) + { + // look for new ones in the plugin directory + dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:plugDir]; + + while ((pname = [dirEnum nextObject])) { + if ([[pname pathExtension] isEqualToString:@"psxplugin"] || + [[pname pathExtension] isEqualToString:@"so"]) { + [dirEnum skipDescendents]; /* don't enumerate this + directory */ + + if (![self hasPluginAtPath:pname]) { + @autoreleasepool { + PcsxrPlugin *plugin = [[PcsxrPlugin alloc] initWithPath:pname]; + if (plugin != nil) { + [self.pluginList addObject:plugin]; + } + } + } + } + } + } + + // check the we have the needed plugins + missingPlugins = NO; + for (i=0; i < 4 /*sizeof(*typeList)*/; i++) { + PcsxrPlugin *plugin = [self activePluginForType:typeList[i]]; + if (nil == plugin) { + NSArray *list = [self pluginsForType:typeList[i]]; + NSUInteger j; + + for (j=0; j < [list count]; j++) { + if ([self setActivePlugin:list[j] forType:typeList[i]]) + break; + } + if (j == [list count]) + missingPlugins = YES; + } + } +} + +- (NSArray *)pluginsForType:(int)typeMask +{ + NSMutableArray *types = [NSMutableArray array]; + + for (PcsxrPlugin *plugin in self.pluginList) { + if ([plugin type] & typeMask) { + [types addObject:plugin]; + } + } + + return types; +} + +- (BOOL)hasPluginAtPath:(NSString *)path +{ + if (nil == path) + return NO; + + for (PcsxrPlugin *plugin in self.pluginList) { + if ([[plugin path] isEqualToString:path]) + return YES; + } + + return NO; +} + +// returns if all the required plugins are available +- (BOOL)configured +{ + return !missingPlugins; +} + +- (BOOL)doInitPlugins +{ + BOOL bad = NO; + + if ([self.activeGpuPlugin runAs:PSE_LT_GPU] != 0) + bad = YES; + if ([self.activeSpuPlugin runAs:PSE_LT_SPU] != 0) + bad = YES; + if ([self.activeCdrPlugin runAs:PSE_LT_CDR] != 0) + bad = YES; + if ([self.activePadPlugin runAs:PSE_LT_PAD] != 0) + bad = YES; + if ([self.activeNetPlugin runAs:PSE_LT_NET] != 0) + bad = YES; + if ([self.activeSIO1Plugin runAs:PSE_LT_SIO1] != 0) + bad = YES; + + return !bad; +} + +- (PcsxrPlugin *)activePluginForType:(int)type +{ + switch (type) { + case PSE_LT_GPU: return self.activeGpuPlugin; break; + case PSE_LT_CDR: return self.activeCdrPlugin; break; + case PSE_LT_SPU: return self.activeSpuPlugin; break; + case PSE_LT_PAD: return self.activePadPlugin; break; + case PSE_LT_NET: return self.activeNetPlugin; break; + case PSE_LT_SIO1: return self.activeSIO1Plugin; break; + } + + return nil; +} + +- (BOOL)setActivePlugin:(PcsxrPlugin *)plugin forType:(int)type +{ + PcsxrPlugin *pluginPtr = nil; + + switch (type) { + case PSE_LT_SIO1: + case PSE_LT_GPU: + case PSE_LT_CDR: + case PSE_LT_SPU: + case PSE_LT_PAD: + case PSE_LT_NET: pluginPtr = [self activePluginForType:type]; break; + default: return NO; break; + } + if (plugin == pluginPtr) { + return YES; + } + + BOOL active = pluginPtr && [EmuThread active]; + BOOL wasPaused = NO; + if (active) { + // TODO: temporary freeze? + wasPaused = [EmuThread pauseSafe]; + ClosePlugins(); + ReleasePlugins(); + } + + // stop the old plugin and start the new one + if (pluginPtr) { + [pluginPtr shutdownAs:type]; + pluginPtr = nil; + } + + if ([plugin runAs:type] != 0) { + plugin = nil; + } + + switch (type) { + case PSE_LT_GPU: + self.activeGpuPlugin = plugin; + break; + case PSE_LT_CDR: + self.activeCdrPlugin = plugin; + break; + case PSE_LT_SPU: + self.activeSpuPlugin = plugin; + break; + case PSE_LT_PAD: + self.activePadPlugin = plugin; + break; + case PSE_LT_NET: + self.activeNetPlugin = plugin; + break; + case PSE_LT_SIO1: + self.activeSIO1Plugin = plugin; + break; + } + + // write path to the correct config entry + const char *str; + if (plugin != nil) { + str = [[plugin path] fileSystemRepresentation]; + if (str == NULL) { + str = "Invalid Plugin"; + } + } else { + str = "Invalid Plugin"; + } + + char **dst = [PcsxrPlugin configEntriesForType:type]; + while (*dst) { + strlcpy(*dst, str, MAXPATHLEN); + dst++; + } + + if (active) { + LoadPlugins(); + OpenPlugins(); + + if (!wasPaused) { + [EmuThread resume]; + } + } + + return plugin != nil; +} + +- (void)disableNetPlug +{ + char **dst = [PcsxrPlugin configEntriesForType:PSE_LT_NET]; + while (*dst) { + strcpy(*dst, "Disabled"); + dst++; + } +} + +- (void)enableNetPlug +{ + PcsxrPlugin *netPlug = [self activePluginForType:PSE_LT_NET]; + + const char *str = NULL; + if (netPlug) { + str = [[netPlug path] fileSystemRepresentation]; + } + if (str) { + char **dst = [PcsxrPlugin configEntriesForType:PSE_LT_NET]; + while (*dst) { + strlcpy(*dst, str, MAXPATHLEN); + dst++; + } + } + +} + +@end diff --git a/macosx/Source/RecentItemsMenu.h b/macosx/Source/RecentItemsMenu.h new file mode 100644 index 00000000..d5bb2e07 --- /dev/null +++ b/macosx/Source/RecentItemsMenu.h @@ -0,0 +1,22 @@ +// +// RecentItemsMenu.h +// Pcsxr +// +// Created by Nicolas Pepin-Perreault on 12-12-16. +// +// + +#import <Cocoa/Cocoa.h> + +@class PcsxrController; +@interface RecentItemsMenu : NSMenu + +@property (weak) IBOutlet PcsxrController *pcsxr; + +- (IBAction)clearRecentDocuments:(id)sender; +- (void)addRecentItem:(NSURL*)documentURL; +- (NSMenuItem*)newMenuItem:(NSURL*)documentURL; +- (void)openRecentItem:(NSMenuItem*)sender; +- (void)addMenuItem:(NSMenuItem*)item; + +@end diff --git a/macosx/Source/RecentItemsMenu.m b/macosx/Source/RecentItemsMenu.m new file mode 100644 index 00000000..926bd270 --- /dev/null +++ b/macosx/Source/RecentItemsMenu.m @@ -0,0 +1,114 @@ +// +// RecentItemsMenu.m +// Pcsxr +// +// Created by Nicolas Pepin-Perreault on 12-12-16. +// +// + +#import "RecentItemsMenu.h" +#import "PcsxrController.h" + +@implementation RecentItemsMenu + +@synthesize pcsxr; + +// Initialization +- (void)awakeFromNib +{ + [self setAutoenablesItems:YES]; + + // Populate the menu + NSArray* recentDocuments = [[NSDocumentController sharedDocumentController] recentDocumentURLs]; + NSInteger index = 0; + for (NSURL* url in recentDocuments) { + NSMenuItem *tempItem = [self newMenuItem:url]; + [self addMenuItem:tempItem atIndex:index]; + index++; + } +} + +- (void)addRecentItem:(NSURL*)documentURL +{ + [[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:documentURL]; + + NSMenuItem* item = [self findMenuItemByURL:documentURL]; + if (item != nil) { + [self removeItem:item]; + [self insertItem:item atIndex:0]; + } else { + NSMenuItem *newitem = [self newMenuItem:documentURL]; + [self addMenuItem:newitem]; + } +} + +- (void)addMenuItem:(NSMenuItem*)item +{ + [self addMenuItem:item atIndex:0]; + + // Prevent menu from overflowing; the -2 accounts for the "Clear..." and the separator items + NSInteger maxNumItems = [[NSDocumentController sharedDocumentController] maximumRecentDocumentCount]; + if (([self numberOfItems] - 2) > maxNumItems) { + [self removeItemAtIndex:maxNumItems]; + } +} + +- (NSMenuItem*)findMenuItemByURL:(NSURL*)url +{ + for(NSMenuItem* item in [self itemArray]) { + if([[item representedObject] isEqual:url]) { + return item; + } + } + + return nil; +} + +- (void)addMenuItem:(NSMenuItem*)item atIndex:(NSInteger)index +{ + [self insertItem:item atIndex:index]; // insert at the top +} + +- (NSMenuItem*)newMenuItem:(NSURL*)documentURL +{ + NSString *lastName = nil; + [documentURL getResourceValue:&lastName forKey:NSURLLocalizedNameKey error:NULL]; + if (!lastName) { + lastName = [documentURL lastPathComponent]; + } + + NSMenuItem *newItem = [[NSMenuItem alloc] initWithTitle:lastName action:@selector(openRecentItem:) keyEquivalent:@""]; + [newItem setRepresentedObject:documentURL]; + [newItem setTarget:self]; + + return newItem; +} + +- (void)openRecentItem:(NSMenuItem*)sender +{ + NSURL* url = [sender representedObject]; + [self addRecentItem:url]; + [pcsxr runURL:url]; +} + +- (IBAction)clearRecentDocuments:(id)sender +{ + [self removeDocumentItems]; + [[NSDocumentController sharedDocumentController] clearRecentDocuments:sender]; +} + +// Document items are menu items with tag 0 +- (void)removeDocumentItems +{ + NSMutableArray *removeItemsArray = [[NSMutableArray alloc] initWithCapacity:10]; + for (NSMenuItem* item in [self itemArray]) { + if([item tag] == 0) { + [removeItemsArray addObject:item]; + } + } + for (NSMenuItem *item in removeItemsArray) { + [self removeItem:item]; + } +} + +@end diff --git a/macosx/Source/config.h b/macosx/Source/config.h new file mode 100644 index 00000000..3be13f38 --- /dev/null +++ b/macosx/Source/config.h @@ -0,0 +1,32 @@ +// +// Copyright (c) 2008, Wei Mingzhi. All rights reserved. +// +// Use, redistribution and modification of this code is unrestricted as long as this +// notice is preserved. +// + +#ifndef CONFIG_H +#define CONFIG_H + +#ifndef MAXPATHLEN +//match PATH_MAX in <sys/param.h> +#define MAXPATHLEN 1024 +#endif + +#ifndef PACKAGE_VERSION +#define PACKAGE_VERSION "1.9" +#endif + +#ifndef PREFIX +#define PREFIX "./" +#endif + +#ifndef inline +#ifdef _DEBUG +#define inline /* */ +#else +#define inline __inline__ +#endif +#endif + +#endif diff --git a/macosx/Source/hotkeys.h b/macosx/Source/hotkeys.h new file mode 100644 index 00000000..592ea0b7 --- /dev/null +++ b/macosx/Source/hotkeys.h @@ -0,0 +1,15 @@ +// +// hotkeys.h +// Pcsxr +// +// Created by Nicolas Pepin-Perreault on 12-12-12. +// +// + +#ifndef Pcsxr_hotkeys_h +#define Pcsxr_hotkeys_h + +void attachHotkeys(); +void detachHotkeys(); + +#endif diff --git a/macosx/Source/hotkeys.m b/macosx/Source/hotkeys.m new file mode 100644 index 00000000..70e3ae15 --- /dev/null +++ b/macosx/Source/hotkeys.m @@ -0,0 +1,141 @@ +// +// hotkeys.m +// Pcsxr +// +// Created by Nicolas Pepin-Perreault on 12-12-12. +// +// + +#import <Cocoa/Cocoa.h> +#import "hotkeys.h" +#import "EmuThread.h" +#include "plugins.h" +#include "ExtendedKeys.h" +#import "PcsxrController.h" + +#define HK_MAX_STATE 10 +static id monitor; +static id gpuMonitor; +static int currentState = 0; +static NSMutableDictionary *hotkeys = nil; + +typedef NS_ENUM(int, PCSXR_HotKey) { + HK_FAST_FORWARD, + HK_SAVE_STATE, + HK_LOAD_STATE, + HK_NEXT_STATE, + HK_PREV_STATE, + HK_FRAME_LIMIT +}; + +void nextState() { + currentState++; + if(currentState == HK_MAX_STATE) { + currentState = 0; + } +} + +void prevState() { + currentState--; + if(currentState < 0) { + currentState = HK_MAX_STATE-1; + } +} + +BOOL handleHotkey(NSString* keyCode) { + if([EmuThread active]) { // Don't catch hotkeys if there is no emulation + NSNumber *ident = hotkeys[keyCode]; + + if(ident != nil) { + switch([ident intValue]) { + case HK_FAST_FORWARD: + // We ignore FastForward requests if the emulation is paused + if(![EmuThread isPaused]) { + GPU_keypressed(GPU_FAST_FORWARD); + } + break; + + case HK_FRAME_LIMIT: + // Ignore FrameLimit requests if paused + if(![EmuThread isPaused]) { + GPU_keypressed(GPU_FRAME_LIMIT); + } + break; + + case HK_SAVE_STATE: + [PcsxrController saveState:currentState]; + break; + + case HK_LOAD_STATE: + [PcsxrController loadState:currentState]; + break; + + case HK_NEXT_STATE: + nextState(); + GPU_displayText((char*)[[NSString stringWithFormat:@"State Slot: %d", currentState] UTF8String]); + break; + + case HK_PREV_STATE: + prevState(); + GPU_displayText((char*)[[NSString stringWithFormat:@"State Slot: %d", currentState] UTF8String]); + break; + + default: + NSLog(@"Invalid hotkey identifier %i.", [ident intValue]); + break; + } + + return YES; + } + } + + return NO; +} + +void setupHotkey(PCSXR_HotKey hk, NSString *label, NSDictionary *binding) { + if(binding != nil) + hotkeys[binding[@"keyCode"]] = @(hk); +} + +void setupHotkeys() { + NSDictionary *bindings = [[NSUserDefaults standardUserDefaults] objectForKey:@"HotkeyBindings"]; + hotkeys = [[NSMutableDictionary alloc] initWithCapacity:[bindings count]]; + + setupHotkey(HK_FAST_FORWARD, @"FastForward", bindings[@"FastForward"]); + setupHotkey(HK_SAVE_STATE, @"SaveState", bindings[@"SaveState"]); + setupHotkey(HK_LOAD_STATE, @"LoadState", bindings[@"LoadState"]); + setupHotkey(HK_NEXT_STATE, @"NextState", bindings[@"NextState"]); + setupHotkey(HK_PREV_STATE, @"PrevState", bindings[@"PrevState"]); + setupHotkey(HK_FRAME_LIMIT, @"FrameLimit", bindings[@"FrameLimit"]); + + currentState = 0; +} + +void attachHotkeys() { + // Configurable hotkeys + NSEvent* (^handler)(NSEvent*) = ^(NSEvent *event) { + if(handleHotkey([NSString stringWithFormat:@"%d", [event keyCode]])) { + return (NSEvent*)nil; // handled + } + + // Not handled + return event; + }; + setupHotkeys(); + monitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyUpMask handler:handler]; + + // GPU key presses + NSEvent* (^gpuKeypress)(NSEvent*) = ^(NSEvent *event) { + GPU_keypressed([event keyCode]); + return (NSEvent*)nil; + }; + gpuMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:(NSKeyUpMask | NSControlKeyMask) handler:gpuKeypress]; +} + +void detachHotkeys() { + hotkeys = nil; + [NSEvent removeMonitor:monitor]; + [NSEvent removeMonitor:gpuMonitor]; + monitor = nil; + gpuMonitor = nil; +} diff --git a/macosx/Source/main.m b/macosx/Source/main.m new file mode 100644 index 00000000..33f74908 --- /dev/null +++ b/macosx/Source/main.m @@ -0,0 +1,311 @@ +// +// main.m +// +// Created by Gil Pedersen on Fri Jun 06 2003. +// Copyright (c) 2003 SoftWorkz. All rights reserved. +// + +#import <Cocoa/Cocoa.h> +#import "EmuThread.h" +#import "PcsxrController.h" +#import "ConfigurationController.h" +#include <dlfcn.h> +#include <unistd.h> +#include "psxcommon.h" +#include "sio.h" +#include <IOKit/pwr_mgt/IOPMLib.h> +#import "hotkeys.h" + +#ifndef NSFoundationVersionNumber10_8_4 +#define NSFoundationVersionNumber10_8_4 945.18 +#endif + +static inline void RunOnMainThreadSync(dispatch_block_t block) +{ + if ([NSThread isMainThread]) { + block(); + } else { + dispatch_sync(dispatch_get_main_queue(), block); + } +} + +static BOOL sysInited = NO; +//#define EMU_LOG +static IOPMAssertionID powerAssertion = kIOPMNullAssertionID; + +void PADhandleKey(int key); + +static inline BOOL IsRootCwd() +{ + char buf[MAXPATHLEN]; + char *cwd = getcwd(buf, sizeof(buf)); + return (cwd && (strcmp(cwd, "/") == 0)); +} + +static inline BOOL IsTenPointNineOrLater() +{ + int curFoundNum = floor(NSFoundationVersionNumber), tenPointEightFoundNum = floor(NSFoundationVersionNumber10_8_4); + return curFoundNum > tenPointEightFoundNum; +} + +static BOOL IsFinderLaunch(const int argc, const char **argv) +{ + BOOL isNewerOS = IsTenPointNineOrLater(); + /* -psn_XXX is passed if we are launched from Finder in 10.8 and earlier */ + if ( (!isNewerOS) && (argc >= 2) && (strncmp(argv[1], "-psn", 4) == 0) ) { + return YES; + } else if ((isNewerOS) && (argc == 1) && IsRootCwd()) { + /* we might still be launched from the Finder; on 10.9+, you might not + get the -psn command line anymore. Check version, if there's no + command line, and if our current working directory is "/". */ + return YES; + } + return NO; /* not a Finder launch. */ +} + +int main(int argc, const char *argv[]) +{ + if (argc >= 2 && IsFinderLaunch(argc, argv)) { + wasFinderLaunch = YES; + char parentdir[MAXPATHLEN]; + char *c; + + strlcpy(parentdir, argv[0], sizeof(parentdir)); + c = (char*)parentdir; + + while (*c != '\0') /* go to end */ + c++; + + while (*c != '/') /* back up to parent */ + c--; + + *c++ = '\0'; /* cut off last part (binary name) */ + + assert(chdir(parentdir) == 0); /* chdir to the binary app's parent */ + assert(chdir("../../../") == 0); /* chdir to the .app's parent */ + } else { + for (int i = 1; i < argc; i++) { + //All the other option will be handled in the app delegate's awakeFromNib + if (!strcasecmp("--help", argv[i])) { + fprintf(stdout, "%s\n", argv[0]); + ShowHelpAndExit(stdout, EXIT_SUCCESS); + } + } + } + + strcpy(Config.BiosDir, "Bios/"); + strcpy(Config.PatchesDir, "Patches/"); + + // Setup the X11 window + if (getenv("DISPLAY") == NULL) + setenv("DISPLAY", ":0.0", 0); // Default to first local display + + return NSApplicationMain(argc, argv); +} + +int SysInit() +{ + if (!sysInited) { +#ifdef EMU_LOG +#ifndef LOG_STDOUT + NSFileManager *manager = [NSFileManager defaultManager]; + NSURL *supportURL = [manager URLForDirectory:NSLibraryDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL]; + NSURL *logFolderURL = [supportURL URLByAppendingPathComponent:@"Logs/PCSXR"]; + if (![logFolderURL checkResourceIsReachableAndReturnError:NULL]) + [manager createDirectoryAtPath:[logFolderURL path] withIntermediateDirectories:YES attributes:nil error:NULL]; + //We use the log extension so that OS X's console app can open it by default. + NSURL *logFileURL = [logFolderURL URLByAppendingPathComponent:@"PCSX-R emuLog.log"]; + + emuLog = fopen([[logFileURL path] fileSystemRepresentation], "wb"); +#else + emuLog = stdout; +#endif + setvbuf(emuLog, NULL, _IONBF, 0); +#endif + + if (EmuInit() != 0) + return -1; + + sysInited = YES; + } + + if (LoadPlugins() == -1) { + return -1; + } + + LoadMcds(Config.Mcd1, Config.Mcd2); + + IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep, kIOPMAssertionLevelOn, CFSTR("PSX Emu Running"), &powerAssertion); + if (success != kIOReturnSuccess) { + NSLog(@"Unable to stop sleep, error code %d", success); + } + + attachHotkeys(); + + return 0; +} + +void SysReset() +{ + [EmuThread resetNow]; + //EmuReset(); +} + +static void AddStringToLogList(NSString *themsg) +{ + static NSMutableString *theStr; + static dispatch_once_t onceToken; + NSRange newlineRange, fullLineRange; + dispatch_once(&onceToken, ^{ + theStr = [[NSMutableString alloc] init]; + }); + [theStr appendString:themsg]; + while ((newlineRange = [theStr rangeOfString:@"\n"]).location != NSNotFound) { + newlineRange = [theStr rangeOfComposedCharacterSequencesForRange:newlineRange]; + NSString *tmpStr = [theStr substringToIndex:newlineRange.location]; + if (tmpStr && ![tmpStr isEqualToString:@""]) { + NSLog(@"%@", tmpStr); + } + fullLineRange.location = 0; + fullLineRange.length = newlineRange.location + newlineRange.length; + fullLineRange = [theStr rangeOfComposedCharacterSequencesForRange:fullLineRange]; + [theStr deleteCharactersInRange:fullLineRange]; + } +} + +void SysPrintf(const char *fmt, ...) +{ + va_list list; + NSString *msg; + + va_start(list, fmt); + msg = [[NSString alloc] initWithFormat:@(fmt) arguments:list]; + va_end(list); + + RunOnMainThreadSync(^{ + if (Config.PsxOut) + AddStringToLogList(msg); +#ifdef EMU_LOG +#ifndef LOG_STDOUT + if (emuLog) + fprintf(emuLog, "%s", [msg UTF8String]); +#endif +#endif + }); +} + +void SysMessage(const char *fmt, ...) +{ + va_list list; + va_start(list, fmt); + NSString *msg = [[NSString alloc] initWithFormat:@(fmt) arguments:list]; + va_end(list); + + NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: msg}; + + RunOnMainThreadSync(^{ + [NSApp presentError:[NSError errorWithDomain:@"Unknown Domain" code:-1 userInfo:userInfo]]; + }); +} + +void *SysLoadLibrary(const char *lib) +{ + NSBundle *bundle = [[NSBundle alloc] initWithPath:[[NSFileManager defaultManager] stringWithFileSystemRepresentation:lib length:strlen(lib)]]; + if (bundle != nil) { + return dlopen([[bundle executablePath] fileSystemRepresentation], RTLD_LAZY /*RTLD_NOW*/); + } + return dlopen(lib, RTLD_LAZY); +} + +void *SysLoadSym(void *lib, const char *sym) +{ + return dlsym(lib, sym); +} + +const char *SysLibError() +{ +#ifdef DEBUG + const char *theErr = dlerror(); + if (theErr) { + NSLog(@"dlerror(): %s.", theErr); + } + return theErr; +#else + return dlerror(); +#endif +} + +void SysCloseLibrary(void *lib) { + // We do not close libraries due to how Objective C code misbehaves if unloaded, + // particularly constant NSStrings. + //dlclose(lib); +} + +// Called periodically from the emu thread +void SysUpdate() +{ +#if 0 + PADhandleKey(PAD1_keypressed() & 0xffffffff); + PADhandleKey(PAD2_keypressed() & 0xffffffff); +#else + PAD1_keypressed(); + PAD2_keypressed(); +#endif + [emuThread handleEvents]; +} + +// Returns to the Gui +void SysRunGui() +{ + if (powerAssertion != kIOPMNullAssertionID) { + IOPMAssertionRelease(powerAssertion); + powerAssertion = kIOPMNullAssertionID; + } +} + +// Close mem and plugins +void SysClose() +{ + EmuShutdown(); + ReleasePlugins(); + + if (powerAssertion != kIOPMNullAssertionID) { + IOPMAssertionRelease(powerAssertion); + powerAssertion = kIOPMNullAssertionID; + } + + if (emuLog != NULL) { + fclose(emuLog); + emuLog = NULL; + } + + sysInited = NO; + detachHotkeys(); + + if (((PcsxrController *)[NSApp delegate]).endAtEmuClose) { + [NSApp stop:nil]; + } + + //Tell the memory card manager that the memory cards changed. + //The number three tells the mem card manager to update both cards 1 and 2. + [[NSNotificationCenter defaultCenter] postNotificationName:memChangeNotifier object:nil userInfo:@{memCardChangeNumberKey: @3}]; + + //Clear the log list + RunOnMainThreadSync(^{ + if (Config.PsxOut) + AddStringToLogList(@"\n"); + }); +} + +void OnFile_Exit() +{ + SysClose(); + [NSApp stop:nil]; +} + +char* Pcsxr_locale_text(char* toloc) +{ + NSString *origString = @(toloc), *transString = nil; + transString = [[NSBundle mainBundle] localizedStringForKey:origString value:@"" table:nil]; + return (char*)[transString UTF8String]; +} |
