2007-03-21 06:03:17 +01:00
|
|
|
/*
|
|
|
|
* Mac OS X support routines for PhysicsFS.
|
|
|
|
*
|
|
|
|
* Please see the file LICENSE.txt in the source's root directory.
|
|
|
|
*
|
|
|
|
* This file written by Ryan C. Gordon.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define __PHYSICSFS_INTERNAL__
|
|
|
|
#include "physfs_platforms.h"
|
|
|
|
|
|
|
|
#ifdef PHYSFS_PLATFORM_MACOSX
|
|
|
|
|
2012-06-25 20:40:03 +02:00
|
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
|
|
|
|
|
|
#if !defined(PHYSFS_NO_CDROM_SUPPORT)
|
2012-06-25 21:25:55 +02:00
|
|
|
#include <Carbon/Carbon.h> /* !!! FIXME */
|
2007-03-21 06:03:17 +01:00
|
|
|
#include <IOKit/storage/IOMedia.h>
|
|
|
|
#include <IOKit/storage/IOCDMedia.h>
|
|
|
|
#include <IOKit/storage/IODVDMedia.h>
|
|
|
|
#include <sys/mount.h>
|
2012-06-25 20:40:03 +02:00
|
|
|
#endif
|
|
|
|
|
2011-07-10 08:11:17 +02:00
|
|
|
#include <sys/stat.h>
|
2007-03-21 06:03:17 +01:00
|
|
|
|
|
|
|
/* Seems to get defined in some system header... */
|
|
|
|
#ifdef Free
|
|
|
|
#undef Free
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "physfs_internal.h"
|
|
|
|
|
|
|
|
|
|
|
|
/* Wrap PHYSFS_Allocator in a CFAllocator... */
|
|
|
|
static CFAllocatorRef cfallocator = NULL;
|
|
|
|
|
|
|
|
CFStringRef cfallocDesc(const void *info)
|
|
|
|
{
|
2012-03-20 20:38:12 +01:00
|
|
|
return CFStringCreateWithCString(cfallocator, "PhysicsFS",
|
|
|
|
kCFStringEncodingASCII);
|
2007-03-21 06:03:17 +01:00
|
|
|
} /* cfallocDesc */
|
|
|
|
|
|
|
|
|
|
|
|
static void *cfallocMalloc(CFIndex allocSize, CFOptionFlags hint, void *info)
|
|
|
|
{
|
|
|
|
return allocator.Malloc(allocSize);
|
|
|
|
} /* cfallocMalloc */
|
|
|
|
|
|
|
|
|
|
|
|
static void cfallocFree(void *ptr, void *info)
|
|
|
|
{
|
|
|
|
allocator.Free(ptr);
|
|
|
|
} /* cfallocFree */
|
|
|
|
|
|
|
|
|
|
|
|
static void *cfallocRealloc(void *ptr, CFIndex newsize,
|
|
|
|
CFOptionFlags hint, void *info)
|
|
|
|
{
|
|
|
|
if ((ptr == NULL) || (newsize <= 0))
|
|
|
|
return NULL; /* ADC docs say you should always return NULL here. */
|
|
|
|
return allocator.Realloc(ptr, newsize);
|
|
|
|
} /* cfallocRealloc */
|
|
|
|
|
|
|
|
|
|
|
|
int __PHYSFS_platformInit(void)
|
|
|
|
{
|
|
|
|
/* set up a CFAllocator, so Carbon can use the physfs allocator, too. */
|
|
|
|
CFAllocatorContext ctx;
|
|
|
|
memset(&ctx, '\0', sizeof (ctx));
|
|
|
|
ctx.copyDescription = cfallocDesc;
|
|
|
|
ctx.allocate = cfallocMalloc;
|
|
|
|
ctx.reallocate = cfallocRealloc;
|
|
|
|
ctx.deallocate = cfallocFree;
|
|
|
|
cfallocator = CFAllocatorCreate(kCFAllocatorUseContext, &ctx);
|
2012-03-20 20:38:12 +01:00
|
|
|
BAIL_IF_MACRO(!cfallocator, PHYSFS_ERR_OUT_OF_MEMORY, 0);
|
2010-01-28 08:27:45 +01:00
|
|
|
return 1; /* success. */
|
2007-03-21 06:03:17 +01:00
|
|
|
} /* __PHYSFS_platformInit */
|
|
|
|
|
|
|
|
|
|
|
|
int __PHYSFS_platformDeinit(void)
|
|
|
|
{
|
|
|
|
CFRelease(cfallocator);
|
|
|
|
cfallocator = NULL;
|
2010-01-28 08:27:45 +01:00
|
|
|
return 1; /* always succeed. */
|
2007-03-21 06:03:17 +01:00
|
|
|
} /* __PHYSFS_platformDeinit */
|
|
|
|
|
|
|
|
|
2012-06-25 20:40:03 +02:00
|
|
|
|
2007-03-21 06:03:17 +01:00
|
|
|
/* CD-ROM detection code... */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Code based on sample from Apple Developer Connection:
|
|
|
|
* http://developer.apple.com/samplecode/Sample_Code/Devices_and_Hardware/Disks/VolumeToBSDNode/VolumeToBSDNode.c.htm
|
|
|
|
*/
|
|
|
|
|
2012-06-25 20:40:03 +02:00
|
|
|
#if !defined(PHYSFS_NO_CDROM_SUPPORT)
|
|
|
|
|
2007-03-21 06:03:17 +01:00
|
|
|
static int darwinIsWholeMedia(io_service_t service)
|
|
|
|
{
|
|
|
|
int retval = 0;
|
|
|
|
CFTypeRef wholeMedia;
|
|
|
|
|
|
|
|
if (!IOObjectConformsTo(service, kIOMediaClass))
|
2010-01-28 08:27:45 +01:00
|
|
|
return 0;
|
2007-03-21 06:03:17 +01:00
|
|
|
|
|
|
|
wholeMedia = IORegistryEntryCreateCFProperty(service,
|
|
|
|
CFSTR(kIOMediaWholeKey),
|
|
|
|
cfallocator, 0);
|
|
|
|
if (wholeMedia == NULL)
|
2010-01-28 08:27:45 +01:00
|
|
|
return 0;
|
2007-03-21 06:03:17 +01:00
|
|
|
|
|
|
|
retval = CFBooleanGetValue(wholeMedia);
|
|
|
|
CFRelease(wholeMedia);
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
} /* darwinIsWholeMedia */
|
|
|
|
|
|
|
|
|
|
|
|
static int darwinIsMountedDisc(char *bsdName, mach_port_t masterPort)
|
|
|
|
{
|
|
|
|
int retval = 0;
|
|
|
|
CFMutableDictionaryRef matchingDict;
|
|
|
|
kern_return_t rc;
|
|
|
|
io_iterator_t iter;
|
|
|
|
io_service_t service;
|
|
|
|
|
|
|
|
if ((matchingDict = IOBSDNameMatching(masterPort, 0, bsdName)) == NULL)
|
2010-01-28 08:27:45 +01:00
|
|
|
return 0;
|
2007-03-21 06:03:17 +01:00
|
|
|
|
|
|
|
rc = IOServiceGetMatchingServices(masterPort, matchingDict, &iter);
|
|
|
|
if ((rc != KERN_SUCCESS) || (!iter))
|
2010-01-28 08:27:45 +01:00
|
|
|
return 0;
|
2007-03-21 06:03:17 +01:00
|
|
|
|
|
|
|
service = IOIteratorNext(iter);
|
|
|
|
IOObjectRelease(iter);
|
|
|
|
if (!service)
|
2010-01-28 08:27:45 +01:00
|
|
|
return 0;
|
2007-03-21 06:03:17 +01:00
|
|
|
|
|
|
|
rc = IORegistryEntryCreateIterator(service, kIOServicePlane,
|
|
|
|
kIORegistryIterateRecursively | kIORegistryIterateParents, &iter);
|
|
|
|
|
|
|
|
if (!iter)
|
2010-01-28 08:27:45 +01:00
|
|
|
return 0;
|
2007-03-21 06:03:17 +01:00
|
|
|
|
|
|
|
if (rc != KERN_SUCCESS)
|
|
|
|
{
|
|
|
|
IOObjectRelease(iter);
|
2010-01-28 08:27:45 +01:00
|
|
|
return 0;
|
2007-03-21 06:03:17 +01:00
|
|
|
} /* if */
|
|
|
|
|
|
|
|
IOObjectRetain(service); /* add an extra object reference... */
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (darwinIsWholeMedia(service))
|
|
|
|
{
|
|
|
|
if ( (IOObjectConformsTo(service, kIOCDMediaClass)) ||
|
|
|
|
(IOObjectConformsTo(service, kIODVDMediaClass)) )
|
|
|
|
{
|
|
|
|
retval = 1;
|
|
|
|
} /* if */
|
|
|
|
} /* if */
|
|
|
|
IOObjectRelease(service);
|
|
|
|
} while ((service = IOIteratorNext(iter)) && (!retval));
|
|
|
|
|
|
|
|
IOObjectRelease(iter);
|
|
|
|
IOObjectRelease(service);
|
|
|
|
|
2010-01-28 08:27:45 +01:00
|
|
|
return retval;
|
2007-03-21 06:03:17 +01:00
|
|
|
} /* darwinIsMountedDisc */
|
|
|
|
|
2012-06-25 20:40:03 +02:00
|
|
|
#endif /* !defined(PHYSFS_NO_CDROM_SUPPORT) */
|
|
|
|
|
2007-03-21 06:03:17 +01:00
|
|
|
|
|
|
|
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
|
|
|
|
{
|
2012-06-25 20:40:03 +02:00
|
|
|
#if !defined(PHYSFS_NO_CDROM_SUPPORT)
|
2007-03-21 06:03:17 +01:00
|
|
|
const char *devPrefix = "/dev/";
|
|
|
|
const int prefixLen = strlen(devPrefix);
|
|
|
|
mach_port_t masterPort = 0;
|
|
|
|
struct statfs *mntbufp;
|
|
|
|
int i, mounts;
|
|
|
|
|
|
|
|
if (IOMasterPort(MACH_PORT_NULL, &masterPort) != KERN_SUCCESS)
|
2012-03-20 20:38:12 +01:00
|
|
|
BAIL_MACRO(PHYSFS_ERR_OS_ERROR, ) /*return void*/;
|
2007-03-21 06:03:17 +01:00
|
|
|
|
|
|
|
mounts = getmntinfo(&mntbufp, MNT_WAIT); /* NOT THREAD SAFE! */
|
|
|
|
for (i = 0; i < mounts; i++)
|
|
|
|
{
|
|
|
|
char *dev = mntbufp[i].f_mntfromname;
|
|
|
|
char *mnt = mntbufp[i].f_mntonname;
|
|
|
|
if (strncmp(dev, devPrefix, prefixLen) != 0) /* a virtual device? */
|
|
|
|
continue;
|
|
|
|
|
|
|
|
dev += prefixLen;
|
|
|
|
if (darwinIsMountedDisc(dev, masterPort))
|
|
|
|
cb(data, mnt);
|
|
|
|
} /* for */
|
2012-06-25 20:40:03 +02:00
|
|
|
#endif /* !defined(PHYSFS_NO_CDROM_SUPPORT) */
|
2007-03-21 06:03:17 +01:00
|
|
|
} /* __PHYSFS_platformDetectAvailableCDs */
|
|
|
|
|
|
|
|
|
|
|
|
static char *convertCFString(CFStringRef cfstr)
|
|
|
|
{
|
|
|
|
CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
|
|
|
|
kCFStringEncodingUTF8) + 1;
|
|
|
|
char *retval = (char *) allocator.Malloc(len);
|
2012-03-20 20:38:12 +01:00
|
|
|
BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
|
2007-03-21 06:03:17 +01:00
|
|
|
|
|
|
|
if (CFStringGetCString(cfstr, retval, len, kCFStringEncodingUTF8))
|
|
|
|
{
|
|
|
|
/* shrink overallocated buffer if possible... */
|
|
|
|
CFIndex newlen = strlen(retval) + 1;
|
|
|
|
if (newlen < len)
|
|
|
|
{
|
|
|
|
void *ptr = allocator.Realloc(retval, newlen);
|
|
|
|
if (ptr != NULL)
|
|
|
|
retval = (char *) ptr;
|
|
|
|
} /* if */
|
|
|
|
} /* if */
|
|
|
|
|
|
|
|
else /* probably shouldn't fail, but just in case... */
|
|
|
|
{
|
|
|
|
allocator.Free(retval);
|
2012-03-20 20:38:12 +01:00
|
|
|
BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
|
2007-03-21 06:03:17 +01:00
|
|
|
} /* else */
|
|
|
|
|
2010-01-28 08:27:45 +01:00
|
|
|
return retval;
|
2007-03-21 06:03:17 +01:00
|
|
|
} /* convertCFString */
|
|
|
|
|
|
|
|
|
|
|
|
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
|
|
|
|
{
|
2011-07-10 08:11:17 +02:00
|
|
|
struct stat statbuf;
|
2007-03-21 06:03:17 +01:00
|
|
|
CFRange cfrange;
|
|
|
|
CFURLRef cfurl = NULL;
|
|
|
|
CFStringRef cfstr = NULL;
|
|
|
|
CFMutableStringRef cfmutstr = NULL;
|
|
|
|
char *retval = NULL;
|
2011-07-10 08:11:17 +02:00
|
|
|
char *cstr = NULL;
|
|
|
|
int rc = 0;
|
2007-03-21 06:03:17 +01:00
|
|
|
|
2012-06-25 20:40:03 +02:00
|
|
|
cfurl = CFBundleCopyBundleURL(CFBundleGetMainBundle());
|
2012-03-20 20:38:12 +01:00
|
|
|
BAIL_IF_MACRO(cfurl == NULL, PHYSFS_ERR_OS_ERROR, NULL);
|
2007-03-21 06:03:17 +01:00
|
|
|
cfstr = CFURLCopyFileSystemPath(cfurl, kCFURLPOSIXPathStyle);
|
|
|
|
CFRelease(cfurl);
|
2012-03-20 20:38:12 +01:00
|
|
|
BAIL_IF_MACRO(!cfstr, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
|
2007-03-21 06:03:17 +01:00
|
|
|
cfmutstr = CFStringCreateMutableCopy(cfallocator, 0, cfstr);
|
|
|
|
CFRelease(cfstr);
|
2012-03-20 20:38:12 +01:00
|
|
|
BAIL_IF_MACRO(!cfmutstr, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
|
2007-03-21 06:03:17 +01:00
|
|
|
|
2011-07-10 08:11:17 +02:00
|
|
|
/* we have to decide if we got a binary's path, or the .app dir... */
|
|
|
|
cstr = convertCFString(cfmutstr);
|
|
|
|
if (cstr == NULL)
|
2007-03-21 06:03:17 +01:00
|
|
|
{
|
|
|
|
CFRelease(cfmutstr);
|
2010-01-28 08:27:45 +01:00
|
|
|
return NULL;
|
2007-03-21 06:03:17 +01:00
|
|
|
} /* if */
|
|
|
|
|
2011-07-10 08:11:17 +02:00
|
|
|
rc = stat(cstr, &statbuf);
|
|
|
|
allocator.Free(cstr); /* done with this. */
|
|
|
|
if (rc == -1)
|
|
|
|
{
|
|
|
|
CFRelease(cfmutstr);
|
|
|
|
return NULL; /* maybe default behaviour will work? */
|
|
|
|
} /* if */
|
2007-03-21 06:03:17 +01:00
|
|
|
|
2011-07-10 08:11:17 +02:00
|
|
|
if (S_ISREG(statbuf.st_mode))
|
|
|
|
{
|
|
|
|
/* Find last dirsep so we can chop the filename from the path. */
|
|
|
|
cfrange = CFStringFind(cfmutstr, CFSTR("/"), kCFCompareBackwards);
|
|
|
|
if (cfrange.location == kCFNotFound)
|
|
|
|
{
|
|
|
|
assert(0); /* shouldn't ever hit this... */
|
|
|
|
CFRelease(cfmutstr);
|
|
|
|
return NULL;
|
|
|
|
} /* if */
|
|
|
|
|
2012-03-23 09:14:01 +01:00
|
|
|
/* chop the "exename" from the end of the path string (leave '/')... */
|
|
|
|
cfrange.location++;
|
2011-07-10 08:11:17 +02:00
|
|
|
cfrange.length = CFStringGetLength(cfmutstr) - cfrange.location;
|
|
|
|
CFStringDelete(cfmutstr, cfrange);
|
2007-03-21 06:03:17 +01:00
|
|
|
|
2011-07-10 08:11:17 +02:00
|
|
|
/* If we're an Application Bundle, chop everything but the base. */
|
2012-03-23 09:14:01 +01:00
|
|
|
cfrange = CFStringFind(cfmutstr, CFSTR("/Contents/MacOS/"),
|
2011-07-10 08:11:17 +02:00
|
|
|
kCFCompareCaseInsensitive |
|
|
|
|
kCFCompareBackwards |
|
|
|
|
kCFCompareAnchored);
|
|
|
|
|
|
|
|
if (cfrange.location != kCFNotFound)
|
2012-03-23 09:14:01 +01:00
|
|
|
{
|
|
|
|
cfrange.location++; /* leave the trailing '/' char ... */
|
|
|
|
cfrange.length--;
|
2011-07-10 08:11:17 +02:00
|
|
|
CFStringDelete(cfmutstr, cfrange); /* chop that, too. */
|
2012-03-23 09:14:01 +01:00
|
|
|
} /* if */
|
2011-07-10 08:11:17 +02:00
|
|
|
} /* if */
|
2007-03-21 06:03:17 +01:00
|
|
|
|
|
|
|
retval = convertCFString(cfmutstr);
|
|
|
|
CFRelease(cfmutstr);
|
|
|
|
|
2010-01-28 08:27:45 +01:00
|
|
|
return retval; /* whew. */
|
2007-03-21 06:03:17 +01:00
|
|
|
} /* __PHYSFS_platformCalcBaseDir */
|
|
|
|
|
|
|
|
|
2012-03-22 04:30:50 +01:00
|
|
|
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
|
|
|
|
{
|
|
|
|
/* !!! FIXME: there's a real API to determine this */
|
|
|
|
const char *userdir = __PHYSFS_getUserDir();
|
|
|
|
const char *append = "Library/Application Support/";
|
2012-03-22 04:59:43 +01:00
|
|
|
const size_t len = strlen(userdir) + strlen(append) + strlen(app) + 2;
|
2012-03-22 04:30:50 +01:00
|
|
|
char *retval = allocator.Malloc(len);
|
|
|
|
BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
|
2012-03-22 04:59:43 +01:00
|
|
|
snprintf(retval, len, "%s%s%s/", userdir, append, app);
|
2012-03-22 04:30:50 +01:00
|
|
|
return retval;
|
|
|
|
} /* __PHYSFS_platformCalcPrefDir */
|
|
|
|
|
|
|
|
|
2007-03-21 06:03:17 +01:00
|
|
|
/* Platform allocator uses default CFAllocator at PHYSFS_init() time. */
|
|
|
|
|
|
|
|
static CFAllocatorRef cfallocdef = NULL;
|
|
|
|
|
|
|
|
static int macosxAllocatorInit(void)
|
|
|
|
{
|
|
|
|
int retval = 0;
|
|
|
|
cfallocdef = CFAllocatorGetDefault();
|
|
|
|
retval = (cfallocdef != NULL);
|
|
|
|
if (retval)
|
|
|
|
CFRetain(cfallocdef);
|
2010-01-28 08:27:45 +01:00
|
|
|
return retval;
|
2007-03-21 06:03:17 +01:00
|
|
|
} /* macosxAllocatorInit */
|
|
|
|
|
|
|
|
|
|
|
|
static void macosxAllocatorDeinit(void)
|
|
|
|
{
|
|
|
|
if (cfallocdef != NULL)
|
|
|
|
{
|
|
|
|
CFRelease(cfallocdef);
|
|
|
|
cfallocdef = NULL;
|
|
|
|
} /* if */
|
|
|
|
} /* macosxAllocatorDeinit */
|
|
|
|
|
|
|
|
|
|
|
|
static void *macosxAllocatorMalloc(PHYSFS_uint64 s)
|
|
|
|
{
|
2012-03-20 20:38:12 +01:00
|
|
|
if (!__PHYSFS_ui64FitsAddressSpace(s))
|
|
|
|
BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
|
2010-01-28 08:27:45 +01:00
|
|
|
return CFAllocatorAllocate(cfallocdef, (CFIndex) s, 0);
|
2007-03-21 06:03:17 +01:00
|
|
|
} /* macosxAllocatorMalloc */
|
|
|
|
|
|
|
|
|
|
|
|
static void *macosxAllocatorRealloc(void *ptr, PHYSFS_uint64 s)
|
|
|
|
{
|
2012-03-20 20:38:12 +01:00
|
|
|
if (!__PHYSFS_ui64FitsAddressSpace(s))
|
|
|
|
BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
|
2010-01-28 08:27:45 +01:00
|
|
|
return CFAllocatorReallocate(cfallocdef, ptr, (CFIndex) s, 0);
|
2007-03-21 06:03:17 +01:00
|
|
|
} /* macosxAllocatorRealloc */
|
|
|
|
|
|
|
|
|
|
|
|
static void macosxAllocatorFree(void *ptr)
|
|
|
|
{
|
|
|
|
CFAllocatorDeallocate(cfallocdef, ptr);
|
|
|
|
} /* macosxAllocatorFree */
|
|
|
|
|
|
|
|
|
|
|
|
int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
|
|
|
|
{
|
|
|
|
allocator.Init = macosxAllocatorInit;
|
|
|
|
allocator.Deinit = macosxAllocatorDeinit;
|
|
|
|
allocator.Malloc = macosxAllocatorMalloc;
|
|
|
|
allocator.Realloc = macosxAllocatorRealloc;
|
|
|
|
allocator.Free = macosxAllocatorFree;
|
2010-01-28 08:27:45 +01:00
|
|
|
return 1; /* return non-zero: we're supplying custom allocator. */
|
2007-03-21 06:03:17 +01:00
|
|
|
} /* __PHYSFS_platformSetDefaultAllocator */
|
|
|
|
|
|
|
|
#endif /* PHYSFS_PLATFORM_MACOSX */
|
|
|
|
|
|
|
|
/* end of macosx.c ... */
|
|
|
|
|