pcsxr/macosx/PcsxrController.m

568 lines
18 KiB
Objective-C

#import <Cocoa/Cocoa.h>
#import "PcsxrController.h"
#import "ConfigurationController.h"
#import "EmuThread.h"
#import "PcsxrMemCardHandler.h"
#import "PcsxrPluginHandler.h"
#import "PcsxrDiscHandler.h"
#import "PcsxrFreezeStateHandler.h"
#include "psxcommon.h"
#include "plugins.h"
#include "misc.h"
#include "ExtendedKeys.h"
NSDictionary *prefStringKeys;
NSDictionary *prefByteKeys;
NSMutableArray *biosList;
NSString *saveStatePath;
static NSString *GetBin(NSString *toHandle)
{
NSString *extension = [toHandle pathExtension];
BOOL getBin = NO;
if ([extension caseInsensitiveCompare:@"cue"] == NSOrderedSame)
getBin = YES;
if ([extension caseInsensitiveCompare:@"toc"] == NSOrderedSame)
getBin = YES;
if (getBin == YES)
{
NSString *returnPath = nil;
NSString *rawPath = [toHandle stringByDeletingPathExtension];
returnPath = [rawPath stringByAppendingPathExtension:@"bin"];
if(![[NSFileManager defaultManager] fileExistsAtPath:returnPath])
{
//TODO: handle case-sensitive filesystems better
returnPath = [rawPath stringByAppendingPathExtension:@"BIN"];
}
return returnPath;
} else {
return toHandle;
}
}
static NSString *HandleBinCue(NSString *toHandle)
{
NSURL *tempURL = [[NSURL alloc] initFileURLWithPath:toHandle];
BOOL gotBin = NO;
NSString *extension = [tempURL pathExtension];
NSString *newName = toHandle;
NSURL *temp1 = [tempURL URLByDeletingLastPathComponent];
NSURL *temp2 = nil;
if ([extension caseInsensitiveCompare:@"cue"] == NSOrderedSame) {
//Get the bin file name from the cue.
NSString *cueFile = [NSString stringWithContentsOfURL:tempURL encoding:NSUTF8StringEncoding error:nil];
if (!cueFile) {
cueFile = [NSString stringWithContentsOfURL:tempURL encoding:NSASCIIStringEncoding error:nil];
if (!cueFile) {
goto badCue;
}
}
NSRange firstQuote, lastQuote, filePath;
firstQuote = [cueFile rangeOfString:@"\""];
if (firstQuote.location == NSNotFound) {
goto badCue;
}
lastQuote = [cueFile rangeOfString:@".bin\"" options:NSCaseInsensitiveSearch];
if (lastQuote.location == NSNotFound) {
goto badCue;
}
filePath.location = firstQuote.location + 1; //Don't include the quote symbol
filePath.length = (lastQuote.location + 4) - (firstQuote.location + 1 ); //Include the .bin but not the first quote symbol
temp2 = [temp1 URLByAppendingPathComponent:[cueFile substringWithRange:filePath]];
if (![[NSFileManager defaultManager] fileExistsAtPath:[temp2 path]])
goto badCue;
gotBin = YES;
badCue:
;
}
if (gotBin == YES)
{
newName = [temp2 path];
} else {
newName = GetBin(toHandle);
}
[tempURL release];
return newName;
}
@implementation PcsxrController
- (IBAction)ejectCD:(id)sender
{
NSMutableString *deviceName;
NSTask *ejectTask;
NSRange rdiskRange;
BOOL wasPaused = [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 retain];
[openDlg setCanChooseFiles:YES];
[openDlg setCanChooseDirectories:NO];
[openDlg setAllowedFileTypes:[PcsxrDiscHandler supportedUTIs]];
if ([openDlg runModal] == NSFileHandlingPanelOKButton) {
NSArray* files = [openDlg URLs];
SetCdOpenCaseTime(time(NULL) + 2);
SetIsoFile((const char *)[HandleBinCue([[files objectAtIndex:0] path]) fileSystemRepresentation]);
}
[openDlg release];
} else {
char *driveLetter = CDR_getDriveLetter();
if (driveLetter != nil) {
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:[NSArray arrayWithObjects:@"eject", deviceName, nil]];
[ejectTask waitUntilExit];
}
}
/* and open new cd */
if ([EmuThread active])
CDR_open();
if (!wasPaused) {
[EmuThread resume];
}
}
- (IBAction)pause:(id)sender
{
if ([EmuThread isPaused]) {
//[sender setState:NSOffState];
[EmuThread resume];
}
else {
//[sender setState:NSOnState];
[EmuThread pause];
}
}
- (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 */
[preferenceWindow makeKeyAndOrderFront:self];
[preferencesController showWindow:self];
}
- (IBAction)reset:(id)sender
{
[EmuThread reset];
}
- (IBAction)runCD:(id)sender
{
SetIsoFile(NULL);
[EmuThread run];
}
- (IBAction)runIso:(id)sender
{
NSOpenPanel* openDlg = [NSOpenPanel openPanel];
[openDlg retain];
[openDlg setCanChooseFiles:YES];
[openDlg setCanChooseDirectories:NO];
[openDlg setAllowedFileTypes:[PcsxrDiscHandler supportedUTIs]];
if ([openDlg runModal] == NSFileHandlingPanelOKButton) {
NSArray* urls = [openDlg URLs];
SetIsoFile((const char *)[HandleBinCue([[urls objectAtIndex:0] path]) fileSystemRepresentation]);
[EmuThread run];
}
[openDlg release];
}
- (IBAction)runBios:(id)sender
{
SetIsoFile(NULL);
[EmuThread runBios];
}
- (IBAction)freeze:(id)sender
{
NSInteger num = [sender tag];
NSString *path = [saveStatePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%s-%3.3ld.pcsxrstate", CdromId, (long)num]];
[EmuThread freezeAt:path which:num-1];
}
- (IBAction)defrost:(id)sender
{
NSString *path = [saveStatePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%s-%3.3ld.pcsxrstate", CdromId, (long)[sender tag]]];
[EmuThread defrostAt:path];
}
- (IBAction)fullscreen:(id)sender
{
GPU_keypressed(GPU_FULLSCREEN_KEY);
}
- (IBAction)pauseInBackground:(id)sender
{
sleepInBackground = !sleepInBackground;
[[NSUserDefaults standardUserDefaults] setBool: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 (preferencesController != nil) {
if ([preferencesController isMemoryCardWindowVisible] == YES)
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:(sleepInBackground ? NSOnState : NSOffState)];
return YES;
}
return YES;
}
- (void)applicationWillResignActive:(NSNotification *)aNotification
{
wasPausedBeforeBGSwitch = [EmuThread isPaused];
if (sleepInBackground) {
[EmuThread pause];
}
}
- (void)applicationDidBecomeActive:(NSNotification *)aNotification
{
if (sleepInBackground && !wasPausedBeforeBGSwitch) {
[EmuThread resume];
}
}
- (void)awakeFromNib
{
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]) {
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~/Library/Application Support/Pcsxr/Bios/", nil),
nil, nil, nil);
}
sleepInBackground = [[NSUserDefaults standardUserDefaults] boolForKey:@"PauseInBackground"];
}
- (void)dealloc
{
[pluginList release];
[super dealloc];
}
+ (void)setConfigFromDefaults
{
NSEnumerator *enumerator;
const char *str;
NSString *key;
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);
}*/
enumerator = [prefByteKeys keyEnumerator];
while ((key = [enumerator nextObject])) {
u8 *dst = (u8 *)[[prefByteKeys objectForKey:key] pointerValue];
if (dst != nil) *dst = [defaults integerForKey: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"];
int index = [biosList indexOfObject:path];
if (-1 == index) {
[biosList insertObject:path atIndex:0];
} else if (0 < index) {
[biosList exchangeObjectAtIndex:index withObjectAtIndex:0];
}
}
str = [[defaults stringForKey:@"Mcd1"] fileSystemRepresentation];
if (str) strncpy(Config.Mcd1, str, MAXPATHLEN);
str = [[defaults stringForKey:@"Mcd2"] fileSystemRepresentation];
if (str) strncpy(Config.Mcd2, str, MAXPATHLEN);
if ([defaults boolForKey:@"UseHLE"] || 0 == [biosList count]) {
strcpy(Config.Bios, "HLE");
} else {
str = [(NSString *)[biosList objectAtIndex:0] fileSystemRepresentation];
if (str != nil) strncpy(Config.Bios, str, MAXPATHLEN);
else strcpy(Config.Bios, "HLE");
}
str = [[defaults stringForKey:@"Net"] fileSystemRepresentation];
if (str) strncpy(Config.Net, str, MAXPATHLEN);
else {
strcpy(Config.Net, "Disabled");
}
}
+ (void)setDefaultFromConfig:(NSString *)defaultKey
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
char *str = (char *)[[prefStringKeys objectForKey:defaultKey] pointerValue];
if (str) {
[defaults setObject:[NSString stringWithCString:str encoding:NSUTF8StringEncoding] forKey:defaultKey];
return;
}
u8 *val = (u8 *)[[prefByteKeys objectForKey: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 = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], @"NoDynarec",
[NSNumber numberWithBool:YES], @"AutoDetectVideoType",
[NSNumber numberWithBool:NO], @"UseHLE",
[NSNumber numberWithBool:YES], @"PauseInBackground",
[NSNumber numberWithBool:NO], @"Widescreen",
nil];
[defaults registerDefaults:appDefaults];
prefStringKeys = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSValue valueWithPointer:Config.Gpu], @"PluginGPU",
[NSValue valueWithPointer:Config.Spu], @"PluginSPU",
[NSValue valueWithPointer:Config.Pad1], @"PluginPAD",
[NSValue valueWithPointer:Config.Cdr], @"PluginCDR",
[NSValue valueWithPointer:Config.Net], @"PluginNET",
[NSValue valueWithPointer:Config.Mcd1], @"Mcd1",
[NSValue valueWithPointer:Config.Mcd2], @"Mcd2",
nil];
prefByteKeys = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSValue valueWithPointer:&Config.Xa], @"NoXaAudio",
[NSValue valueWithPointer:&Config.UseNet], @"NetPlay",
[NSValue valueWithPointer:&Config.Sio], @"SioIrqAlways",
[NSValue valueWithPointer:&Config.Mdec], @"BlackAndWhiteMDECVideo",
[NSValue valueWithPointer:&Config.PsxAuto], @"AutoDetectVideoType",
[NSValue valueWithPointer:&Config.PsxType], @"VideoTypePAL",
[NSValue valueWithPointer:&Config.Cdda], @"NoCDAudio",
[NSValue valueWithPointer:&Config.Cpu], @"NoDynarec",
[NSValue valueWithPointer:&Config.PsxOut], @"ConsoleOutput",
[NSValue valueWithPointer:&Config.SpuIrq], @"SpuIrqAlways",
[NSValue valueWithPointer:&Config.RCntFix], @"RootCounterFix",
[NSValue valueWithPointer:&Config.VSyncWA], @"VideoSyncWAFix",
[NSValue valueWithPointer:&Config.Widescreen], @"Widescreen",
nil];
// 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 createDirectoryAtPath:[url path] withIntermediateDirectories:YES attributes:nil error:NULL];
MemCardPath = [PcsxrAppSupport URLByAppendingPathComponent:@"Memory Cards"];
url = MemCardPath;
if (![url checkResourceIsReachableAndReturnError:NULL])
[manager createDirectoryAtPath:[url path] withIntermediateDirectories:YES attributes:nil error:NULL];
url = [PcsxrAppSupport URLByAppendingPathComponent:@"Patches"];
if (![url checkResourceIsReachableAndReturnError:NULL])
[manager createDirectoryAtPath:[url path] withIntermediateDirectories:YES attributes:nil error:NULL];
url = [PcsxrAppSupport URLByAppendingPathComponent:@"PlugIns"];
if (![url checkResourceIsReachableAndReturnError:NULL])
[manager createDirectoryAtPath:[url path] 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) strncpy(Config.Mcd1, str, MAXPATHLEN);
url = [MemCardPath URLByAppendingPathComponent:@"Mcd002.mcr"];
str = [[url path] fileSystemRepresentation];
if (str != nil) strncpy(Config.Mcd2, str, MAXPATHLEN);
url = [PcsxrAppSupport URLByAppendingPathComponent:@"Bios"];
str = [[url path] fileSystemRepresentation];
if (str != nil) strncpy(Config.BiosDir, str, MAXPATHLEN);
url = [PcsxrAppSupport URLByAppendingPathComponent:@"Patches"];
str = [[url path] fileSystemRepresentation];
if (str != nil) strncpy(Config.PatchesDir, str, MAXPATHLEN);
} else {
strcpy(Config.BiosDir, "Bios/");
strcpy(Config.PatchesDir, "Patches/");
saveStatePath = @"sstates";
[saveStatePath retain];
}
// set plugin path
path = [[NSBundle mainBundle] builtInPlugInsPath];
str = [path fileSystemRepresentation];
if (str != nil) strncpy(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) {
NSUInteger i;
for (i = 0; i < [bioses count]; i++) {
NSString *file = [bioses objectAtIndex:i];
NSDictionary *attrib = [manager attributesOfItemAtPath:[[biosDir stringByAppendingPathComponent:file] stringByResolvingSymlinksInPath] error:NULL];
if ([[attrib fileType] isEqualToString:NSFileTypeRegular]) {
unsigned long long size = [attrib fileSize];
if (([attrib fileSize] % (256 * 1024)) == 0 && size > 0) {
[biosList addObject:file];
}
}
}
}
[PcsxrController setConfigFromDefaults];
}
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
NSError *err = nil;
NSString *utiFile = [[NSWorkspace sharedWorkspace] typeOfFile:filename error:&err];
if (err) {
NSRunAlertPanel(NSLocalizedString(@"Error opening file", nil), [NSString stringWithFormat:NSLocalizedString(@"Unable to open %@: %@", nil), [filename lastPathComponent], [err localizedFailureReason]], nil, nil, nil);
return NO;
}
static NSArray *handlers = nil;
if (handlers == nil) {
handlers = [[NSArray alloc] initWithObjects:[PcsxrPluginHandler class], [PcsxrMemCardHandler class], [PcsxrFreezeStateHandler class], [PcsxrDiscHandler class], nil];
}
BOOL isHandled = NO;
for (Class fileHandler in handlers) {
NSObject<PcsxrFileHandle> *hand = [[fileHandler alloc] init];
BOOL canHandle = NO;
for (NSString *uti in [fileHandler supportedUTIs]) {
if ([[NSWorkspace sharedWorkspace] type:utiFile conformsToType:uti]) {
canHandle = YES;
}
}
if (canHandle) {
isHandled = [hand handleFile:HandleBinCue(filename)];
[hand release];
break;
}
[hand release];
}
return isHandled;
}
@end