mirror of https://github.com/snes9xgit/snes9x.git
506 lines
11 KiB
Plaintext
506 lines
11 KiB
Plaintext
/*****************************************************************************\
|
|
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
|
|
This file is licensed under the Snes9x License.
|
|
For further information, consult the LICENSE file in the root directory.
|
|
\*****************************************************************************/
|
|
|
|
/***********************************************************************************
|
|
SNES9X for Mac OS (c) Copyright John Stiles
|
|
|
|
Snes9x for Mac OS X
|
|
|
|
(c) Copyright 2001 - 2011 zones
|
|
(c) Copyright 2002 - 2005 107
|
|
(c) Copyright 2002 PB1400c
|
|
(c) Copyright 2004 Alexander and Sander
|
|
(c) Copyright 2004 - 2005 Steven Seeger
|
|
(c) Copyright 2005 Ryan Vogt
|
|
(c) Copyright 2019 Michael Donald Buckley
|
|
***********************************************************************************/
|
|
|
|
#import <Cocoa/Cocoa.h>
|
|
|
|
#include "snes9x.h"
|
|
#include "memmap.h"
|
|
#include "movie.h"
|
|
#include "display.h"
|
|
|
|
#include <libgen.h>
|
|
|
|
#include "mac-prefix.h"
|
|
#include "mac-dialog.h"
|
|
#include "mac-os.h"
|
|
#include "mac-stringtools.h"
|
|
#include "mac-file.h"
|
|
|
|
static void AddFolderIcon (NSURL *, const char *);
|
|
static NSURL *FindSNESFolder (const char *);
|
|
static NSURL *FindApplicationSupportFolder (NSURL *, const char *);
|
|
static NSURL *FindCustomFolder (NSURL *, const char *);
|
|
|
|
|
|
void CheckSaveFolder (NSURL *cartURL)
|
|
{
|
|
NSString *folderPath = nil;
|
|
|
|
switch (saveInROMFolder)
|
|
{
|
|
case 1: // ROM folder
|
|
folderPath = cartURL.URLByDeletingLastPathComponent.path;
|
|
break;
|
|
|
|
case 2: // Application Support folder
|
|
return;
|
|
|
|
case 4: // Custom folder
|
|
if (saveFolderPath == NULL)
|
|
{
|
|
saveInROMFolder = 2;
|
|
return;
|
|
}
|
|
|
|
BOOL isDirectory = NO;
|
|
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:saveFolderPath isDirectory:&isDirectory];
|
|
|
|
if (exists && isDirectory)
|
|
{
|
|
folderPath = saveFolderPath;
|
|
}
|
|
else
|
|
{
|
|
//AppearanceAlert(kAlertCautionAlert, kS9xMacAlertFolderNotFound, kS9xMacAlertFolderNotFoundHint);
|
|
saveInROMFolder = 2;
|
|
return;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
BOOL writable = [[NSFileManager defaultManager] isWritableFileAtPath:folderPath];
|
|
|
|
if (!writable)
|
|
{
|
|
//AppearanceAlert(kAlertCautionAlert, kS9xMacAlertFolderNotWritable, kS9xMacAlertFolderNotWritableHint);
|
|
saveInROMFolder = 2;
|
|
return;
|
|
}
|
|
}
|
|
|
|
static NSURL *FindSNESFolder (const char *folderName)
|
|
{
|
|
NSURL *purl = nil;
|
|
NSString *fstr = [NSString stringWithUTF8String:folderName];
|
|
|
|
purl = [[[NSBundle mainBundle] bundleURL].URLByDeletingLastPathComponent URLByAppendingPathComponent:fstr];
|
|
|
|
if (![NSFileManager.defaultManager fileExistsAtPath:purl.path])
|
|
{
|
|
NSError *error = nil;
|
|
if ( [NSFileManager.defaultManager createDirectoryAtURL:purl withIntermediateDirectories:YES attributes:nil error:&error] )
|
|
{
|
|
AddFolderIcon(purl, folderName);
|
|
}
|
|
else
|
|
{
|
|
[[NSAlert alertWithError:error] runModal];
|
|
}
|
|
}
|
|
|
|
return purl;
|
|
}
|
|
|
|
static NSURL *FindApplicationSupportFolder (const char *folderName)
|
|
{
|
|
NSURL *purl = nil;
|
|
NSURL *baseURL = nil;
|
|
NSURL *s9xURL = nil;
|
|
NSURL *oldURL = nil;
|
|
NSString *fstr = [NSString stringWithUTF8String:folderName];
|
|
|
|
baseURL = [[NSFileManager.defaultManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask].firstObject URLByAppendingPathComponent:fstr];
|
|
|
|
if (!baseURL)
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
s9xURL = [baseURL URLByAppendingPathComponent:@"Snes9x"];
|
|
oldURL = [baseURL URLByAppendingPathComponent:@"SNES9X"];
|
|
|
|
if ([NSFileManager.defaultManager fileExistsAtPath:s9xURL.path])
|
|
{
|
|
purl = s9xURL;
|
|
}
|
|
else
|
|
{
|
|
if ([NSFileManager.defaultManager fileExistsAtPath:oldURL.path])
|
|
{
|
|
purl = oldURL;
|
|
}
|
|
else
|
|
{
|
|
NSError *error = nil;
|
|
if ([NSFileManager.defaultManager createDirectoryAtURL:s9xURL withIntermediateDirectories:YES attributes:nil error:&error])
|
|
{
|
|
purl = s9xURL;
|
|
AddFolderIcon(purl, folderName);
|
|
}
|
|
}
|
|
}
|
|
|
|
return purl;
|
|
}
|
|
|
|
static NSURL *FindCustomFolder (const char *folderName)
|
|
{
|
|
NSURL *purl = nil;
|
|
|
|
if (saveFolderPath == NULL)
|
|
return nil;
|
|
|
|
purl = [NSURL fileURLWithPath:saveFolderPath];
|
|
|
|
if (![NSFileManager.defaultManager fileExistsAtPath:saveFolderPath])
|
|
{
|
|
NSError *error = nil;
|
|
if (![NSFileManager.defaultManager createDirectoryAtPath:saveFolderPath withIntermediateDirectories:YES attributes:nil error:&error])
|
|
{
|
|
[[NSAlert alertWithError:error] runModal];
|
|
return nil;
|
|
}
|
|
}
|
|
|
|
purl = [purl URLByAppendingPathComponent:[NSString stringWithUTF8String:folderName]];
|
|
|
|
if (![NSFileManager.defaultManager fileExistsAtPath:purl.path])
|
|
{
|
|
NSError *error = nil;
|
|
if ([NSFileManager.defaultManager createDirectoryAtPath:saveFolderPath withIntermediateDirectories:YES attributes:nil error:&error])
|
|
{
|
|
AddFolderIcon(purl, folderName);
|
|
}
|
|
{
|
|
[[NSAlert alertWithError:error] runModal];
|
|
return nil;
|
|
}
|
|
}
|
|
|
|
return purl;
|
|
}
|
|
|
|
void ChangeTypeAndCreator (const char *path, OSType type, OSType creator)
|
|
{
|
|
NSError *error = nil;
|
|
if (![NSFileManager.defaultManager setAttributes:@{NSFileHFSCreatorCode: @(creator), NSFileHFSTypeCode: @(type)} ofItemAtPath:[NSString stringWithUTF8String:path] error:&error])
|
|
{
|
|
[[NSAlert alertWithError:error] runModal];
|
|
}
|
|
}
|
|
|
|
static void AddFolderIcon (NSURL *fref, const char *folderName)
|
|
{
|
|
NSBundle *bundle = [NSBundle bundleWithIdentifier:@"com.snes9x.macos.snes9x-framework"];
|
|
NSString *filename = [@"folder_" stringByAppendingString:[NSString stringWithUTF8String:folderName]];
|
|
NSURL *imageURL = [bundle URLForResource:filename withExtension:@"icns"];
|
|
NSImage *image = [[NSImage alloc] initWithContentsOfURL:imageURL];
|
|
|
|
if ( image != nil )
|
|
{
|
|
[NSWorkspace.sharedWorkspace setIcon:image forFile:fref.path options:0];
|
|
}
|
|
}
|
|
|
|
const char * S9xGetFilename (const char *inExt, enum s9x_getdirtype dirtype)
|
|
{
|
|
static int index = 0;
|
|
static char filePath[4][PATH_MAX + 1];
|
|
|
|
uint32 type;
|
|
char folderName[16];
|
|
char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1];
|
|
const char *p;
|
|
|
|
index++;
|
|
if (index > 3)
|
|
index = 0;
|
|
|
|
folderName[0] = filePath[index][0] = 0;
|
|
|
|
if (strlen(inExt) < 4)
|
|
return (filePath[index]);
|
|
|
|
p = inExt + strlen(inExt) - 4;
|
|
type = ((uint32) p[0] << 24) + ((uint32) p[1] << 16) + ((uint32) p[2] << 8) + (uint32) p[3];
|
|
|
|
switch (type)
|
|
{
|
|
case '.srm':
|
|
case '.rtc':
|
|
strcpy(folderName, "SRAMs");
|
|
break;
|
|
|
|
case '.frz':
|
|
strcpy(folderName, "Freezes");
|
|
break;
|
|
|
|
case '.spc':
|
|
strcpy(folderName, "SPCs");
|
|
break;
|
|
|
|
case '.cht':
|
|
strcpy(folderName, "Cheats");
|
|
break;
|
|
|
|
case '.ups':
|
|
case '.ips':
|
|
strcpy(folderName, "Patches");
|
|
break;
|
|
|
|
case '.png':
|
|
strcpy(folderName, "Screenshots");
|
|
break;
|
|
|
|
case '.dat':
|
|
case '.out':
|
|
case '.log':
|
|
strcpy(folderName, "Logs");
|
|
break;
|
|
|
|
case '.bio': // dummy
|
|
strcpy(folderName, "BIOSes");
|
|
break;
|
|
}
|
|
|
|
if (folderName[0] && (saveInROMFolder != 1))
|
|
{
|
|
NSURL *folderURL = nil;
|
|
if (saveInROMFolder == 0)
|
|
{
|
|
folderURL = FindSNESFolder(folderName);
|
|
if (folderURL == nil)
|
|
saveInROMFolder = 2;
|
|
}
|
|
|
|
if (saveInROMFolder == 4)
|
|
{
|
|
folderURL = FindCustomFolder(folderName);
|
|
if (folderURL == nil)
|
|
saveInROMFolder = 2;
|
|
|
|
}
|
|
|
|
if (saveInROMFolder == 2)
|
|
folderURL = FindApplicationSupportFolder(folderName);
|
|
|
|
if (folderURL != nil)
|
|
{
|
|
_splitpath(Memory.ROMFilename, drive, dir, fname, ext);
|
|
snprintf(filePath[index], PATH_MAX + 1, "%s%s%s%s", folderURL.path.UTF8String, MAC_PATH_SEPARATOR, fname, inExt);
|
|
}
|
|
else
|
|
{
|
|
_splitpath(Memory.ROMFilename, drive, dir, fname, ext);
|
|
_makepath(filePath[index], drive, dir, fname, inExt);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_splitpath(Memory.ROMFilename, drive, dir, fname, ext);
|
|
_makepath(filePath[index], drive, dir, fname, inExt);
|
|
}
|
|
|
|
return (filePath[index]);
|
|
}
|
|
|
|
const char * S9xGetSPCFilename (void)
|
|
{
|
|
char spcExt[16];
|
|
|
|
sprintf(spcExt, ".%03d.spc", (int) spcFileCount);
|
|
|
|
spcFileCount++;
|
|
if (spcFileCount == 1000)
|
|
spcFileCount = 0;
|
|
|
|
return (S9xGetFilename(spcExt, SPC_DIR));
|
|
}
|
|
|
|
const char * S9xGetPNGFilename (void)
|
|
{
|
|
char pngExt[16];
|
|
|
|
sprintf(pngExt, ".%03d.png", (int) pngFileCount);
|
|
|
|
pngFileCount++;
|
|
if (pngFileCount == 1000)
|
|
pngFileCount = 0;
|
|
|
|
return (S9xGetFilename(pngExt, SCREENSHOT_DIR));
|
|
}
|
|
|
|
const char * S9xGetFreezeFilename (int which)
|
|
{
|
|
char frzExt[16];
|
|
|
|
sprintf(frzExt, ".%03d.frz", which);
|
|
|
|
return (S9xGetFilename(frzExt, SNAPSHOT_DIR));
|
|
}
|
|
|
|
const char * S9xGetFilenameInc (const char *inExt, enum s9x_getdirtype dirtype)
|
|
{
|
|
uint32 type;
|
|
const char *p;
|
|
|
|
if (strlen(inExt) < 4)
|
|
return (NULL);
|
|
|
|
p = inExt + strlen(inExt) - 4;
|
|
type = ((uint32) p[0] << 24) + ((uint32) p[1] << 16) + ((uint32) p[2] << 8) + (uint32) p[3];
|
|
|
|
switch (type)
|
|
{
|
|
case '.spc':
|
|
return (S9xGetSPCFilename());
|
|
|
|
case '.png':
|
|
return (S9xGetPNGFilename());
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
const char * S9xChooseFilename (bool8 read_only)
|
|
{
|
|
return (NULL);
|
|
}
|
|
|
|
const char * S9xChooseMovieFilename (bool8 read_only)
|
|
{
|
|
return (NULL);
|
|
}
|
|
|
|
bool8 S9xOpenSnapshotFile (const char *fname, bool8 read_only, STREAM *file)
|
|
{
|
|
if (read_only)
|
|
{
|
|
if (0 != (*file = OPEN_STREAM(fname, "rb")))
|
|
return (true);
|
|
}
|
|
else
|
|
{
|
|
if (0 != (*file = OPEN_STREAM(fname, "wb")))
|
|
return (true);
|
|
}
|
|
|
|
return (false);
|
|
}
|
|
|
|
void S9xCloseSnapshotFile (STREAM file)
|
|
{
|
|
CLOSE_STREAM(file);
|
|
}
|
|
|
|
const char * S9xBasename (const char *in)
|
|
{
|
|
static char s[PATH_MAX + 1];
|
|
|
|
strncpy(s, in, PATH_MAX + 1);
|
|
s[PATH_MAX] = 0;
|
|
|
|
size_t l = strlen(s);
|
|
|
|
for (unsigned int i = 0; i < l; i++)
|
|
{
|
|
if (s[i] < 32 || s[i] >= 127)
|
|
s[i] = '_';
|
|
}
|
|
|
|
return (basename(s));
|
|
}
|
|
|
|
const char * S9xGetDirectory (enum s9x_getdirtype dirtype)
|
|
{
|
|
static int index = 0;
|
|
static char path[4][PATH_MAX + 1];
|
|
|
|
char inExt[16];
|
|
char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1];
|
|
|
|
index++;
|
|
if (index > 3)
|
|
index = 0;
|
|
|
|
switch (dirtype)
|
|
{
|
|
case SNAPSHOT_DIR: strcpy(inExt, ".frz"); break;
|
|
case SRAM_DIR: strcpy(inExt, ".srm"); break;
|
|
case SCREENSHOT_DIR: strcpy(inExt, ".png"); break;
|
|
case SPC_DIR: strcpy(inExt, ".spc"); break;
|
|
case CHEAT_DIR: strcpy(inExt, ".cht"); break;
|
|
case BIOS_DIR: strcpy(inExt, ".bio"); break;
|
|
case LOG_DIR: strcpy(inExt, ".log"); break;
|
|
default: strcpy(inExt, ".xxx"); break;
|
|
}
|
|
|
|
_splitpath(S9xGetFilename(inExt, dirtype), drive, dir, fname, ext);
|
|
_makepath(path[index], drive, dir, "", "");
|
|
|
|
size_t l = strlen(path[index]);
|
|
if (l > 1)
|
|
path[index][l - 1] = 0;
|
|
|
|
return (path[index]);
|
|
}
|
|
|
|
void _splitpath (const char *path, char *drive, char *dir, char *fname, char *ext)
|
|
{
|
|
drive[0] = 0;
|
|
fname[0] = 0;
|
|
ext[0] = 0;
|
|
dir[0] = 0;
|
|
|
|
size_t x;
|
|
|
|
x = strlen(path) - 1;
|
|
if (x < 0)
|
|
return;
|
|
|
|
while (x && (path[x] != MAC_PATH_SEP_CHAR))
|
|
x--;
|
|
|
|
if (x)
|
|
{
|
|
strcpy(dir, path);
|
|
dir[x + 1] = 0;
|
|
|
|
strcpy(fname, path + x + 1);
|
|
}
|
|
else
|
|
strcpy(fname, path);
|
|
|
|
x = strlen(fname);
|
|
while (x && (fname[x] != '.'))
|
|
x--;
|
|
|
|
if (x)
|
|
{
|
|
strcpy(ext, fname + x);
|
|
fname[x] = 0;
|
|
}
|
|
}
|
|
|
|
void _makepath (char *path, const char *drive, const char *dir, const char *fname, const char *ext)
|
|
{
|
|
static const char emp[] = "", dot[] = ".";
|
|
|
|
const char *d, *f, *e, *p;
|
|
|
|
d = dir ? dir : emp;
|
|
f = fname ? fname : emp;
|
|
e = ext ? ext : emp;
|
|
p = (e[0] && e[0] != '.') ? dot : emp;
|
|
|
|
snprintf(path, PATH_MAX + 1, "%s%s%s%s", d, f, p, e);
|
|
}
|