FilePathsAndMacho

From X-Plane SDK
Jump to: navigation, search

Mac OS X has two conventions for file paths:

  • HFS file paths, which date back to Mac OS 9. The file path starts with a volume name and uses : as a separator. Example:
Macintosh HD:My Favorite Apps:X-Plane:Resources:Plugins:SuperPlugin.xpl
  • Unix file paths, which use / as a separator and have / as a "root" of the file system. Non-boot drives are in a folder called Volumes. Example:
/Volumes/Macintosh HD/My Favorite Apps/X-Plane/Resources/Plugins/SuperPlugin.xpl

The X-Plane SDK currently requires and uses HFS file paths. This is because of its origin during X-Plane 6 with OS 9. XPLMGetDirectorySeparator will return : on the Mac. (Note that it's hard to tell what "root" we use for the file system for some routines because they use X-System-folder relative paths.

Paths and APIs

While the SDK currently always uses HFS paths, the path style expected by a file system routine depends on the API and the "provider".

  1. FSMakeFSSpec ALWAYS requires a traditional path style.
  2. FSPathMakeRef ALWAYS requires a unix path.
  3. fopen() and stdio's path style depends on the C runtime you use.

On this last point, 99% of programmers are now using X-Code and GCC, linking to the native C runtime, and thus require unix file paths. But if you use CodeWarrior and build a CFM plugin, you will be getting the old Carbon-based MSL, which requires HFS paths.

The plugin system will not change over to Unix paths, even if you build a Mach-O plugin and link against the GCC runtime. The SDK doesn't know what runtime you have linked against (I believe that CW at one time offered multiple options).

Path Conversion

Therefore if you want to use unix file I/O on the Mac, you may have to convert file path conventions. The easiest way to do this is with the CFURL functions:

int ConvertPath(const char * inPath, char * outPath, int outPathMaxLen)
{ 
	CFStringRef inStr = CFStringCreateWithCString(kCFAllocatorDefault, inPath ,kCFStringEncodingMacRoman);
	if (inStr == NULL)
		return -1;
	CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, inStr, kCFURLHFSPathStyle,0);
	CFStringRef outStr = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
	if (!CFStringGetCString(outStr, outPath, outPathMaxLen, kCFURLPOSIXPathStyle))
		return -1;
	CFRelease(outStr);
	CFRelease(url);
	CFRelease(inStr);
	return 0;
}