/*****************************************************************************\ 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 ***********************************************************************************/ #include "snes9x.h" #include "memmap.h" #include "movie.h" #include "display.h" #include #include "mac-prefix.h" #include "mac-dialog.h" #include "mac-os.h" #include "mac-stringtools.h" #include "mac-file.h" static void AddFolderIcon (FSRef *, const char *); static OSStatus FindSNESFolder (FSRef *, char *, const char *); static OSStatus FindApplicationSupportFolder (FSRef *, char *, const char *); static OSStatus FindCustomFolder (FSRef *, char *, const char *); void CheckSaveFolder (FSRef *cartRef) { OSStatus err; Boolean r; FSCatalogInfo finfo; FSVolumeInfo vinfo; FSRef ref; CFURLRef burl, purl; char s[PATH_MAX + 1]; switch (saveInROMFolder) { case 0: // Snes9x folder burl = CFBundleCopyBundleURL(CFBundleGetMainBundle()); purl = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, burl); r = CFURLGetFSRef(purl, &ref); CFRelease(purl); CFRelease(burl); break; case 1: // ROM folder err = FSGetCatalogInfo(cartRef, kFSCatInfoNone, NULL, NULL, NULL, &ref); break; case 2: // Application Support folder return; case 4: // Custom folder if (saveFolderPath == NULL) { saveInROMFolder = 2; return; } r = CFStringGetCString(saveFolderPath, s, PATH_MAX, kCFStringEncodingUTF8); err = FSPathMakeRef((unsigned char *) s, &ref, NULL); if (err) { AppearanceAlert(kAlertCautionAlert, kS9xMacAlertFolderNotFound, kS9xMacAlertFolderNotFoundHint); saveInROMFolder = 2; return; } break; } err = FSGetCatalogInfo(&ref, kFSCatInfoUserPrivs | kFSCatInfoVolume, &finfo, NULL, NULL, NULL); if (err == noErr) { if (finfo.userPrivileges & kioACUserNoMakeChangesMask) { AppearanceAlert(kAlertCautionAlert, kS9xMacAlertFolderFailToCreate, kS9xMacAlertFolderFailToCreateHint); saveInROMFolder = 2; return; } err = FSGetVolumeInfo(finfo.volume, 0, NULL, kFSVolInfoFlags, &vinfo, NULL, NULL); if (err == noErr) { if ((vinfo.flags & kFSVolFlagHardwareLockedMask) || (vinfo.flags & kFSVolFlagSoftwareLockedMask)) { AppearanceAlert(kAlertCautionAlert, kS9xMacAlertFolderFailToCreate, kS9xMacAlertFolderFailToCreateHint); saveInROMFolder = 2; return; } } } if (err) saveInROMFolder = 2; } static OSStatus FindSNESFolder (FSRef *folderRef, char *folderPath, const char *folderName) { OSStatus err; CFURLRef burl, purl; CFStringRef fstr; FSRef pref; UniChar buffer[PATH_MAX + 1]; Boolean r; fstr = CFStringCreateWithCString(kCFAllocatorDefault, folderName, CFStringGetSystemEncoding()); CFStringGetCharacters(fstr, CFRangeMake(0, CFStringGetLength(fstr)), buffer); burl = CFBundleCopyBundleURL(CFBundleGetMainBundle()); purl = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, burl); r = CFURLGetFSRef(purl, &pref); err = FSMakeFSRefUnicode(&pref, CFStringGetLength(fstr), buffer, kTextEncodingUnicodeDefault, folderRef); if (err == dirNFErr || err == fnfErr) { err = FSCreateDirectoryUnicode(&pref, CFStringGetLength(fstr), buffer, kFSCatInfoNone, NULL, folderRef, NULL, NULL); if (err == noErr) AddFolderIcon(folderRef, folderName); } if (err == noErr) err = FSRefMakePath(folderRef, (unsigned char *) folderPath, PATH_MAX); CFRelease(purl); CFRelease(burl); CFRelease(fstr); return (err); } static OSStatus FindApplicationSupportFolder (FSRef *folderRef, char *folderPath, const char *folderName) { OSStatus err; FSRef p2ref, p1ref; CFStringRef fstr; UniChar buffer[PATH_MAX + 1]; UniChar s9xfolder[6] = { 'S', 'n', 'e', 's', '9', 'x' }, oldfolder[6] = { 'S', 'N', 'E', 'S', '9', 'X' }; err = FSFindFolder(kUserDomain, kApplicationSupportFolderType, kCreateFolder, &p2ref); if (err) return (err); err = FSMakeFSRefUnicode(&p2ref, 6, s9xfolder, kTextEncodingUnicodeDefault, &p1ref); if (err == dirNFErr || err == fnfErr) { err = FSMakeFSRefUnicode(&p2ref, 6, oldfolder, kTextEncodingUnicodeDefault, &p1ref); if (err == dirNFErr || err == fnfErr) err = FSCreateDirectoryUnicode(&p2ref, 6, s9xfolder, kFSCatInfoNone, NULL, &p1ref, NULL, NULL); } if (err) return (err); fstr = CFStringCreateWithCString(kCFAllocatorDefault, folderName, CFStringGetSystemEncoding()); CFStringGetCharacters(fstr, CFRangeMake(0, CFStringGetLength(fstr)), buffer); err = FSMakeFSRefUnicode(&p1ref, CFStringGetLength(fstr), buffer, kTextEncodingUnicodeDefault, folderRef); if (err == dirNFErr || err == fnfErr) { err = FSCreateDirectoryUnicode(&p1ref, CFStringGetLength(fstr), buffer, kFSCatInfoNone, NULL, folderRef, NULL, NULL); if (err == noErr) AddFolderIcon(folderRef, folderName); } if (err == noErr) err = FSRefMakePath(folderRef, (unsigned char *) folderPath, PATH_MAX); CFRelease(fstr); return (err); } static OSStatus FindCustomFolder (FSRef *folderRef, char *folderPath, const char *folderName) { OSStatus err; CFStringRef fstr; FSRef pref; UniChar buffer[PATH_MAX + 1]; char s[PATH_MAX + 1]; if (saveFolderPath == NULL) return (-1); err = CFStringGetCString(saveFolderPath, s, PATH_MAX, kCFStringEncodingUTF8) ? noErr : -1; if (err == noErr) err = FSPathMakeRef((unsigned char *) s, &pref, NULL); if (err) return (err); fstr = CFStringCreateWithCString(kCFAllocatorDefault, folderName, CFStringGetSystemEncoding()); CFStringGetCharacters(fstr, CFRangeMake(0, CFStringGetLength(fstr)), buffer); err = FSMakeFSRefUnicode(&pref, CFStringGetLength(fstr), buffer, kTextEncodingUnicodeDefault, folderRef); if (err == dirNFErr || err == fnfErr) { err = FSCreateDirectoryUnicode(&pref, CFStringGetLength(fstr), buffer, kFSCatInfoNone, NULL, folderRef, NULL, NULL); if (err == noErr) AddFolderIcon(folderRef, folderName); } if (err == noErr) err = FSRefMakePath(folderRef, (unsigned char *) folderPath, PATH_MAX); CFRelease(fstr); return (err); } void ChangeTypeAndCreator (const char *path, OSType type, OSType creator) { OSStatus err; FSRef ref; err = FSPathMakeRef((unsigned char *) path, &ref, NULL); if (err == noErr) { FSCatalogInfo catinfo; err = FSGetCatalogInfo(&ref, kFSCatInfoFinderInfo, &catinfo, NULL, NULL, NULL); if (err == noErr) { ((FileInfo *) &catinfo.finderInfo)->fileCreator = creator; ((FileInfo *) &catinfo.finderInfo)->fileType = type; err = FSSetCatalogInfo(&ref, kFSCatInfoFinderInfo, &catinfo); } } } static void AddFolderIcon (FSRef *fref, const char *folderName) { OSStatus err; FSCatalogInfo fcat, icat; FSRef bref, iref; CFStringRef str; CFURLRef url; IconFamilyHandle family; IconRef icon; HFSUniStr255 fork; Boolean r; SInt16 resf; char name[64]; UniChar iconName[5] = { 'I', 'c', 'o', 'n', '\r' }; strcpy(name, "folder_"); strcat(name, folderName); str = CFStringCreateWithCString(kCFAllocatorDefault, name, CFStringGetSystemEncoding()); if (str) { url = CFBundleCopyResourceURL(CFBundleGetMainBundle(), str, CFSTR("icns"), NULL); if (url) { r = CFURLGetFSRef(url, &bref); if (r) { err = RegisterIconRefFromFSRef('~9X~', 'TEMP', &bref, &icon); if (err == noErr) { err = FSGetResourceForkName(&fork); if (err == noErr) { err = FSCreateResourceFile(fref, 5, iconName, kFSCatInfoNone, NULL, fork.length, fork.unicode, &iref, NULL); if (err == noErr) { err = FSOpenResourceFile(&iref, fork.length, fork.unicode, fsWrPerm, &resf); if (err == noErr) { err = IconRefToIconFamily(icon, kSelectorAllAvailableData, &family); if (err == noErr) { AddResource((Handle) family, 'icns', -16455, "\p"); WriteResource((Handle) family); ReleaseResource((Handle) family); err = FSGetCatalogInfo(&iref, kFSCatInfoFinderInfo, &icat, NULL, NULL, NULL); ((FileInfo *) &icat.finderInfo)->finderFlags |= kIsInvisible; ((FileInfo *) &icat.finderInfo)->fileCreator = 'MACS'; ((FileInfo *) &icat.finderInfo)->fileType = 'icon'; err = FSSetCatalogInfo(&iref, kFSCatInfoFinderInfo, &icat); err = FSGetCatalogInfo(fref, kFSCatInfoFinderInfo, &fcat, NULL, NULL, NULL); ((FolderInfo *) &fcat.finderInfo)->finderFlags |= kHasCustomIcon; ((FolderInfo *) &fcat.finderInfo)->finderFlags &= ~kHasBeenInited; err = FSSetCatalogInfo(fref, kFSCatInfoFinderInfo, &fcat); } CloseResFile(resf); } } } err = UnregisterIconRef('~9X~', 'TEMP'); } } CFRelease(url); } CFRelease(str); } } const char * S9xGetFilename (const char *inExt, enum s9x_getdirtype dirtype) { static int index = 0; static char filePath[4][PATH_MAX + 1]; OSStatus err; FSRef ref; 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)) { char s[PATH_MAX + 1]; s[0] = 0; err = -1; if (saveInROMFolder == 0) { err = FindSNESFolder(&ref, s, folderName); if (err) saveInROMFolder = 2; } if (saveInROMFolder == 4) { err = FindCustomFolder(&ref, s, folderName); if (err) saveInROMFolder = 2; } if (saveInROMFolder == 2) err = FindApplicationSupportFolder(&ref, s, folderName); if (err == noErr) { _splitpath(Memory.ROMFilename, drive, dir, fname, ext); snprintf(filePath[index], PATH_MAX + 1, "%s%s%s%s", s, 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, "", ""); int 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; int 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); }