1 /*****************************************************************************\
2 Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3 This file is licensed under the Snes9x License.
4 For further information, consult the LICENSE file in the root directory.
5 \*****************************************************************************/
6
7 /***********************************************************************************
8 SNES9X for Mac OS (c) Copyright John Stiles
9
10 Snes9x for Mac OS X
11
12 (c) Copyright 2001 - 2011 zones
13 (c) Copyright 2002 - 2005 107
14 (c) Copyright 2002 PB1400c
15 (c) Copyright 2004 Alexander and Sander
16 (c) Copyright 2004 - 2005 Steven Seeger
17 (c) Copyright 2005 Ryan Vogt
18 ***********************************************************************************/
19
20
21 #include "snes9x.h"
22 #include "memmap.h"
23 #include "movie.h"
24 #include "display.h"
25
26 #include <libgen.h>
27
28 #include "mac-prefix.h"
29 #include "mac-dialog.h"
30 #include "mac-os.h"
31 #include "mac-stringtools.h"
32 #include "mac-file.h"
33
34 static void AddFolderIcon (FSRef *, const char *);
35 static OSStatus FindSNESFolder (FSRef *, char *, const char *);
36 static OSStatus FindApplicationSupportFolder (FSRef *, char *, const char *);
37 static OSStatus FindCustomFolder (FSRef *, char *, const char *);
38
39
CheckSaveFolder(FSRef * cartRef)40 void CheckSaveFolder (FSRef *cartRef)
41 {
42 OSStatus err;
43 Boolean r;
44 FSCatalogInfo finfo;
45 FSVolumeInfo vinfo;
46 FSRef ref;
47 CFURLRef burl, purl;
48 char s[PATH_MAX + 1];
49
50 switch (saveInROMFolder)
51 {
52 case 0: // Snes9x folder
53 burl = CFBundleCopyBundleURL(CFBundleGetMainBundle());
54 purl = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, burl);
55 r = CFURLGetFSRef(purl, &ref);
56 CFRelease(purl);
57 CFRelease(burl);
58 break;
59
60 case 1: // ROM folder
61 err = FSGetCatalogInfo(cartRef, kFSCatInfoNone, NULL, NULL, NULL, &ref);
62 break;
63
64 case 2: // Application Support folder
65 return;
66
67 case 4: // Custom folder
68 if (saveFolderPath == NULL)
69 {
70 saveInROMFolder = 2;
71 return;
72 }
73
74 r = CFStringGetCString(saveFolderPath, s, PATH_MAX, kCFStringEncodingUTF8);
75 err = FSPathMakeRef((unsigned char *) s, &ref, NULL);
76 if (err)
77 {
78 AppearanceAlert(kAlertCautionAlert, kS9xMacAlertFolderNotFound, kS9xMacAlertFolderNotFoundHint);
79 saveInROMFolder = 2;
80 return;
81 }
82
83 break;
84 }
85
86 err = FSGetCatalogInfo(&ref, kFSCatInfoUserPrivs | kFSCatInfoVolume, &finfo, NULL, NULL, NULL);
87 if (err == noErr)
88 {
89 if (finfo.userPrivileges & kioACUserNoMakeChangesMask)
90 {
91 AppearanceAlert(kAlertCautionAlert, kS9xMacAlertFolderFailToCreate, kS9xMacAlertFolderFailToCreateHint);
92 saveInROMFolder = 2;
93 return;
94 }
95
96 err = FSGetVolumeInfo(finfo.volume, 0, NULL, kFSVolInfoFlags, &vinfo, NULL, NULL);
97 if (err == noErr)
98 {
99 if ((vinfo.flags & kFSVolFlagHardwareLockedMask) || (vinfo.flags & kFSVolFlagSoftwareLockedMask))
100 {
101 AppearanceAlert(kAlertCautionAlert, kS9xMacAlertFolderFailToCreate, kS9xMacAlertFolderFailToCreateHint);
102 saveInROMFolder = 2;
103 return;
104 }
105 }
106 }
107
108 if (err)
109 saveInROMFolder = 2;
110 }
111
FindSNESFolder(FSRef * folderRef,char * folderPath,const char * folderName)112 static OSStatus FindSNESFolder (FSRef *folderRef, char *folderPath, const char *folderName)
113 {
114 OSStatus err;
115 CFURLRef burl, purl;
116 CFStringRef fstr;
117 FSRef pref;
118 UniChar buffer[PATH_MAX + 1];
119 Boolean r;
120
121 fstr = CFStringCreateWithCString(kCFAllocatorDefault, folderName, CFStringGetSystemEncoding());
122 CFStringGetCharacters(fstr, CFRangeMake(0, CFStringGetLength(fstr)), buffer);
123
124 burl = CFBundleCopyBundleURL(CFBundleGetMainBundle());
125 purl = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, burl);
126 r = CFURLGetFSRef(purl, &pref);
127
128 err = FSMakeFSRefUnicode(&pref, CFStringGetLength(fstr), buffer, kTextEncodingUnicodeDefault, folderRef);
129 if (err == dirNFErr || err == fnfErr)
130 {
131 err = FSCreateDirectoryUnicode(&pref, CFStringGetLength(fstr), buffer, kFSCatInfoNone, NULL, folderRef, NULL, NULL);
132 if (err == noErr)
133 AddFolderIcon(folderRef, folderName);
134 }
135
136 if (err == noErr)
137 err = FSRefMakePath(folderRef, (unsigned char *) folderPath, PATH_MAX);
138
139 CFRelease(purl);
140 CFRelease(burl);
141 CFRelease(fstr);
142
143 return (err);
144 }
145
FindApplicationSupportFolder(FSRef * folderRef,char * folderPath,const char * folderName)146 static OSStatus FindApplicationSupportFolder (FSRef *folderRef, char *folderPath, const char *folderName)
147 {
148 OSStatus err;
149 FSRef p2ref, p1ref;
150 CFStringRef fstr;
151 UniChar buffer[PATH_MAX + 1];
152 UniChar s9xfolder[6] = { 'S', 'n', 'e', 's', '9', 'x' },
153 oldfolder[6] = { 'S', 'N', 'E', 'S', '9', 'X' };
154
155 err = FSFindFolder(kUserDomain, kApplicationSupportFolderType, kCreateFolder, &p2ref);
156 if (err)
157 return (err);
158
159 err = FSMakeFSRefUnicode(&p2ref, 6, s9xfolder, kTextEncodingUnicodeDefault, &p1ref);
160 if (err == dirNFErr || err == fnfErr)
161 {
162 err = FSMakeFSRefUnicode(&p2ref, 6, oldfolder, kTextEncodingUnicodeDefault, &p1ref);
163 if (err == dirNFErr || err == fnfErr)
164 err = FSCreateDirectoryUnicode(&p2ref, 6, s9xfolder, kFSCatInfoNone, NULL, &p1ref, NULL, NULL);
165 }
166
167 if (err)
168 return (err);
169
170 fstr = CFStringCreateWithCString(kCFAllocatorDefault, folderName, CFStringGetSystemEncoding());
171 CFStringGetCharacters(fstr, CFRangeMake(0, CFStringGetLength(fstr)), buffer);
172
173 err = FSMakeFSRefUnicode(&p1ref, CFStringGetLength(fstr), buffer, kTextEncodingUnicodeDefault, folderRef);
174 if (err == dirNFErr || err == fnfErr)
175 {
176 err = FSCreateDirectoryUnicode(&p1ref, CFStringGetLength(fstr), buffer, kFSCatInfoNone, NULL, folderRef, NULL, NULL);
177 if (err == noErr)
178 AddFolderIcon(folderRef, folderName);
179 }
180
181 if (err == noErr)
182 err = FSRefMakePath(folderRef, (unsigned char *) folderPath, PATH_MAX);
183
184 CFRelease(fstr);
185
186 return (err);
187 }
188
FindCustomFolder(FSRef * folderRef,char * folderPath,const char * folderName)189 static OSStatus FindCustomFolder (FSRef *folderRef, char *folderPath, const char *folderName)
190 {
191 OSStatus err;
192 CFStringRef fstr;
193 FSRef pref;
194 UniChar buffer[PATH_MAX + 1];
195 char s[PATH_MAX + 1];
196
197 if (saveFolderPath == NULL)
198 return (-1);
199
200 err = CFStringGetCString(saveFolderPath, s, PATH_MAX, kCFStringEncodingUTF8) ? noErr : -1;
201 if (err == noErr)
202 err = FSPathMakeRef((unsigned char *) s, &pref, NULL);
203
204 if (err)
205 return (err);
206
207 fstr = CFStringCreateWithCString(kCFAllocatorDefault, folderName, CFStringGetSystemEncoding());
208 CFStringGetCharacters(fstr, CFRangeMake(0, CFStringGetLength(fstr)), buffer);
209
210 err = FSMakeFSRefUnicode(&pref, CFStringGetLength(fstr), buffer, kTextEncodingUnicodeDefault, folderRef);
211 if (err == dirNFErr || err == fnfErr)
212 {
213 err = FSCreateDirectoryUnicode(&pref, CFStringGetLength(fstr), buffer, kFSCatInfoNone, NULL, folderRef, NULL, NULL);
214 if (err == noErr)
215 AddFolderIcon(folderRef, folderName);
216 }
217
218 if (err == noErr)
219 err = FSRefMakePath(folderRef, (unsigned char *) folderPath, PATH_MAX);
220
221 CFRelease(fstr);
222
223 return (err);
224 }
225
ChangeTypeAndCreator(const char * path,OSType type,OSType creator)226 void ChangeTypeAndCreator (const char *path, OSType type, OSType creator)
227 {
228 OSStatus err;
229 FSRef ref;
230
231 err = FSPathMakeRef((unsigned char *) path, &ref, NULL);
232 if (err == noErr)
233 {
234 FSCatalogInfo catinfo;
235
236 err = FSGetCatalogInfo(&ref, kFSCatInfoFinderInfo, &catinfo, NULL, NULL, NULL);
237 if (err == noErr)
238 {
239 ((FileInfo *) &catinfo.finderInfo)->fileCreator = creator;
240 ((FileInfo *) &catinfo.finderInfo)->fileType = type;
241
242 err = FSSetCatalogInfo(&ref, kFSCatInfoFinderInfo, &catinfo);
243 }
244 }
245 }
246
AddFolderIcon(FSRef * fref,const char * folderName)247 static void AddFolderIcon (FSRef *fref, const char *folderName)
248 {
249 OSStatus err;
250 FSCatalogInfo fcat, icat;
251 FSRef bref, iref;
252 CFStringRef str;
253 CFURLRef url;
254 IconFamilyHandle family;
255 IconRef icon;
256 HFSUniStr255 fork;
257 Boolean r;
258 SInt16 resf;
259 char name[64];
260 UniChar iconName[5] = { 'I', 'c', 'o', 'n', '\r' };
261
262 strcpy(name, "folder_");
263 strcat(name, folderName);
264
265 str = CFStringCreateWithCString(kCFAllocatorDefault, name, CFStringGetSystemEncoding());
266 if (str)
267 {
268 url = CFBundleCopyResourceURL(CFBundleGetMainBundle(), str, CFSTR("icns"), NULL);
269 if (url)
270 {
271 r = CFURLGetFSRef(url, &bref);
272 if (r)
273 {
274 err = RegisterIconRefFromFSRef('~9X~', 'TEMP', &bref, &icon);
275 if (err == noErr)
276 {
277 err = FSGetResourceForkName(&fork);
278 if (err == noErr)
279 {
280 err = FSCreateResourceFile(fref, 5, iconName, kFSCatInfoNone, NULL, fork.length, fork.unicode, &iref, NULL);
281 if (err == noErr)
282 {
283 err = FSOpenResourceFile(&iref, fork.length, fork.unicode, fsWrPerm, &resf);
284 if (err == noErr)
285 {
286 err = IconRefToIconFamily(icon, kSelectorAllAvailableData, &family);
287 if (err == noErr)
288 {
289 AddResource((Handle) family, 'icns', -16455, "\p");
290 WriteResource((Handle) family);
291 ReleaseResource((Handle) family);
292
293 err = FSGetCatalogInfo(&iref, kFSCatInfoFinderInfo, &icat, NULL, NULL, NULL);
294 ((FileInfo *) &icat.finderInfo)->finderFlags |= kIsInvisible;
295 ((FileInfo *) &icat.finderInfo)->fileCreator = 'MACS';
296 ((FileInfo *) &icat.finderInfo)->fileType = 'icon';
297 err = FSSetCatalogInfo(&iref, kFSCatInfoFinderInfo, &icat);
298
299 err = FSGetCatalogInfo(fref, kFSCatInfoFinderInfo, &fcat, NULL, NULL, NULL);
300 ((FolderInfo *) &fcat.finderInfo)->finderFlags |= kHasCustomIcon;
301 ((FolderInfo *) &fcat.finderInfo)->finderFlags &= ~kHasBeenInited;
302 err = FSSetCatalogInfo(fref, kFSCatInfoFinderInfo, &fcat);
303 }
304
305 CloseResFile(resf);
306 }
307 }
308 }
309
310 err = UnregisterIconRef('~9X~', 'TEMP');
311 }
312 }
313
314 CFRelease(url);
315 }
316
317 CFRelease(str);
318 }
319 }
320
S9xGetFilename(const char * inExt,enum s9x_getdirtype dirtype)321 const char * S9xGetFilename (const char *inExt, enum s9x_getdirtype dirtype)
322 {
323 static int index = 0;
324 static char filePath[4][PATH_MAX + 1];
325
326 OSStatus err;
327 FSRef ref;
328 uint32 type;
329 char folderName[16];
330 char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1];
331 const char *p;
332
333 index++;
334 if (index > 3)
335 index = 0;
336
337 folderName[0] = filePath[index][0] = 0;
338
339 if (strlen(inExt) < 4)
340 return (filePath[index]);
341
342 p = inExt + strlen(inExt) - 4;
343 type = ((uint32) p[0] << 24) + ((uint32) p[1] << 16) + ((uint32) p[2] << 8) + (uint32) p[3];
344
345 switch (type)
346 {
347 case '.srm':
348 case '.rtc':
349 strcpy(folderName, "SRAMs");
350 break;
351
352 case '.frz':
353 strcpy(folderName, "Freezes");
354 break;
355
356 case '.spc':
357 strcpy(folderName, "SPCs");
358 break;
359
360 case '.cht':
361 strcpy(folderName, "Cheats");
362 break;
363
364 case '.ups':
365 case '.ips':
366 strcpy(folderName, "Patches");
367 break;
368
369 case '.png':
370 strcpy(folderName, "Screenshots");
371 break;
372
373 case '.dat':
374 case '.out':
375 case '.log':
376 strcpy(folderName, "Logs");
377 break;
378
379 case '.bio': // dummy
380 strcpy(folderName, "BIOSes");
381 break;
382 }
383
384 if (folderName[0] && (saveInROMFolder != 1))
385 {
386 char s[PATH_MAX + 1];
387
388 s[0] = 0;
389 err = -1;
390
391 if (saveInROMFolder == 0)
392 {
393 err = FindSNESFolder(&ref, s, folderName);
394 if (err)
395 saveInROMFolder = 2;
396 }
397
398 if (saveInROMFolder == 4)
399 {
400 err = FindCustomFolder(&ref, s, folderName);
401 if (err)
402 saveInROMFolder = 2;
403
404 }
405
406 if (saveInROMFolder == 2)
407 err = FindApplicationSupportFolder(&ref, s, folderName);
408
409 if (err == noErr)
410 {
411 _splitpath(Memory.ROMFilename, drive, dir, fname, ext);
412 snprintf(filePath[index], PATH_MAX + 1, "%s%s%s%s", s, MAC_PATH_SEPARATOR, fname, inExt);
413 }
414 else
415 {
416 _splitpath(Memory.ROMFilename, drive, dir, fname, ext);
417 _makepath(filePath[index], drive, dir, fname, inExt);
418 }
419 }
420 else
421 {
422 _splitpath(Memory.ROMFilename, drive, dir, fname, ext);
423 _makepath(filePath[index], drive, dir, fname, inExt);
424 }
425
426 return (filePath[index]);
427 }
428
S9xGetSPCFilename(void)429 const char * S9xGetSPCFilename (void)
430 {
431 char spcExt[16];
432
433 sprintf(spcExt, ".%03d.spc", (int) spcFileCount);
434
435 spcFileCount++;
436 if (spcFileCount == 1000)
437 spcFileCount = 0;
438
439 return (S9xGetFilename(spcExt, SPC_DIR));
440 }
441
S9xGetPNGFilename(void)442 const char * S9xGetPNGFilename (void)
443 {
444 char pngExt[16];
445
446 sprintf(pngExt, ".%03d.png", (int) pngFileCount);
447
448 pngFileCount++;
449 if (pngFileCount == 1000)
450 pngFileCount = 0;
451
452 return (S9xGetFilename(pngExt, SCREENSHOT_DIR));
453 }
454
S9xGetFreezeFilename(int which)455 const char * S9xGetFreezeFilename (int which)
456 {
457 char frzExt[16];
458
459 sprintf(frzExt, ".%03d.frz", which);
460
461 return (S9xGetFilename(frzExt, SNAPSHOT_DIR));
462 }
463
S9xGetFilenameInc(const char * inExt,enum s9x_getdirtype dirtype)464 const char * S9xGetFilenameInc (const char *inExt, enum s9x_getdirtype dirtype)
465 {
466 uint32 type;
467 const char *p;
468
469 if (strlen(inExt) < 4)
470 return (NULL);
471
472 p = inExt + strlen(inExt) - 4;
473 type = ((uint32) p[0] << 24) + ((uint32) p[1] << 16) + ((uint32) p[2] << 8) + (uint32) p[3];
474
475 switch (type)
476 {
477 case '.spc':
478 return (S9xGetSPCFilename());
479
480 case '.png':
481 return (S9xGetPNGFilename());
482 }
483
484 return (NULL);
485 }
486
S9xChooseFilename(bool8 read_only)487 const char * S9xChooseFilename (bool8 read_only)
488 {
489 return (NULL);
490 }
491
S9xChooseMovieFilename(bool8 read_only)492 const char * S9xChooseMovieFilename (bool8 read_only)
493 {
494 return (NULL);
495 }
496
S9xOpenSnapshotFile(const char * fname,bool8 read_only,STREAM * file)497 bool8 S9xOpenSnapshotFile (const char *fname, bool8 read_only, STREAM *file)
498 {
499 if (read_only)
500 {
501 if (0 != (*file = OPEN_STREAM(fname, "rb")))
502 return (true);
503 }
504 else
505 {
506 if (0 != (*file = OPEN_STREAM(fname, "wb")))
507 return (true);
508 }
509
510 return (false);
511 }
512
S9xCloseSnapshotFile(STREAM file)513 void S9xCloseSnapshotFile (STREAM file)
514 {
515 CLOSE_STREAM(file);
516 }
517
S9xBasename(const char * in)518 const char * S9xBasename (const char *in)
519 {
520 static char s[PATH_MAX + 1];
521
522 strncpy(s, in, PATH_MAX + 1);
523 s[PATH_MAX] = 0;
524
525 size_t l = strlen(s);
526
527 for (unsigned int i = 0; i < l; i++)
528 {
529 if (s[i] < 32 || s[i] >= 127)
530 s[i] = '_';
531 }
532
533 return (basename(s));
534 }
535
S9xGetDirectory(enum s9x_getdirtype dirtype)536 const char * S9xGetDirectory (enum s9x_getdirtype dirtype)
537 {
538 static int index = 0;
539 static char path[4][PATH_MAX + 1];
540
541 char inExt[16];
542 char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1];
543
544 index++;
545 if (index > 3)
546 index = 0;
547
548 switch (dirtype)
549 {
550 case SNAPSHOT_DIR: strcpy(inExt, ".frz"); break;
551 case SRAM_DIR: strcpy(inExt, ".srm"); break;
552 case SCREENSHOT_DIR: strcpy(inExt, ".png"); break;
553 case SPC_DIR: strcpy(inExt, ".spc"); break;
554 case CHEAT_DIR: strcpy(inExt, ".cht"); break;
555 case BIOS_DIR: strcpy(inExt, ".bio"); break;
556 case LOG_DIR: strcpy(inExt, ".log"); break;
557 default: strcpy(inExt, ".xxx"); break;
558 }
559
560 _splitpath(S9xGetFilename(inExt, dirtype), drive, dir, fname, ext);
561 _makepath(path[index], drive, dir, "", "");
562
563 int l = strlen(path[index]);
564 if (l > 1)
565 path[index][l - 1] = 0;
566
567 return (path[index]);
568 }
569
_splitpath(const char * path,char * drive,char * dir,char * fname,char * ext)570 void _splitpath (const char *path, char *drive, char *dir, char *fname, char *ext)
571 {
572 drive[0] = 0;
573 fname[0] = 0;
574 ext[0] = 0;
575 dir[0] = 0;
576
577 int x;
578
579 x = strlen(path) - 1;
580 if (x < 0)
581 return;
582
583 while (x && (path[x] != MAC_PATH_SEP_CHAR))
584 x--;
585
586 if (x)
587 {
588 strcpy(dir, path);
589 dir[x + 1] = 0;
590
591 strcpy(fname, path + x + 1);
592 }
593 else
594 strcpy(fname, path);
595
596 x = strlen(fname);
597 while (x && (fname[x] != '.'))
598 x--;
599
600 if (x)
601 {
602 strcpy(ext, fname + x);
603 fname[x] = 0;
604 }
605 }
606
_makepath(char * path,const char * drive,const char * dir,const char * fname,const char * ext)607 void _makepath (char *path, const char *drive, const char *dir, const char *fname, const char *ext)
608 {
609 static const char emp[] = "", dot[] = ".";
610
611 const char *d, *f, *e, *p;
612
613 d = dir ? dir : emp;
614 f = fname ? fname : emp;
615 e = ext ? ext : emp;
616 p = (e[0] && e[0] != '.') ? dot : emp;
617
618 snprintf(path, PATH_MAX + 1, "%s%s%s%s", d, f, p, e);
619 }
620