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