/* =========================================================================== Wolfenstein: Enemy Territory GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Wolfenstein: Enemy Territory GPL Source Code (“Wolf ET Source Code”). Wolf ET Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Wolf ET Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Wolf ET Source Code. If not, see . In addition, the Wolf: ET Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Wolf ET Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #define text_type 'TEXT' #define text_creator 'ttxt' #undef fopen #include unsigned int _ftype; unsigned int _fcreator; #include #include #define USE_POSIX_PATH 1 #if USE_POSIX_PATH const char *kPathSep = "/"; #else const char *kPathSep = ":"; #endif #include Q3DEF_BEGIN #if MAC_MOHAA && GAME_DLL #include "../fgame/g_local.h" #else #include "../client/client.h" #endif #include "mac_local.h" Q3DEF_END #if __cplusplus extern "C" { #endif void GetHFSPathFromPosix( const char *inPath, char *outPath ) { CFStringRef theFileName = NULL; CFStringRef newPathString = NULL; CFURLRef theFileURL = NULL; theFileName = CFStringCreateWithCString( CFAllocatorGetDefault(), inPath, kCFStringEncodingMacRoman ); if ( !theFileName ) { goto bail; } theFileURL = CFURLCreateWithFileSystemPath( CFAllocatorGetDefault(), theFileName, kCFURLPOSIXPathStyle, false ); if ( !theFileURL ) { goto bail; } newPathString = CFURLCopyFileSystemPath( theFileURL, kCFURLHFSPathStyle ); if ( !newPathString ) { goto bail; } CFStringGetCString( newPathString, outPath, MAX_OSPATH, kCFStringEncodingMacRoman ); CFRelease( theFileURL ); CFRelease( theFileName ); CFRelease( newPathString ); return; bail: if ( theFileURL ) { CFRelease( theFileURL ); } if ( theFileName ) { CFRelease( theFileName ); } if ( newPathString ) { CFRelease( newPathString ); } *outPath = 0; } void GetPosixPathFromHFS( const char *inPath, char *outPath ) { CFStringRef theFileName = NULL; CFStringRef newPathString = NULL; CFURLRef theFileURL = NULL; theFileName = CFStringCreateWithCString( CFAllocatorGetDefault(), inPath, kCFStringEncodingMacRoman ); if ( !theFileName ) { goto bail; } theFileURL = CFURLCreateWithFileSystemPath( CFAllocatorGetDefault(), theFileName, kCFURLHFSPathStyle, false ); if ( !theFileURL ) { goto bail; } newPathString = CFURLCopyFileSystemPath( theFileURL, kCFURLPOSIXPathStyle ); if ( !newPathString ) { goto bail; } CFStringGetCString( newPathString, outPath, MAX_OSPATH, kCFStringEncodingMacRoman ); CFRelease( theFileURL ); CFRelease( theFileName ); CFRelease( newPathString ); return; bail: if ( theFileURL ) { CFRelease( theFileURL ); } if ( theFileName ) { CFRelease( theFileName ); } if ( newPathString ) { CFRelease( newPathString ); } *outPath = 0; } void FILE_SetCreator( const char *inPath, OSType inCreator, OSType inType ) { CFStringRef theFileName = NULL; FSRef fileRef; FSSpec fileSpec; CFURLRef theFileURL = NULL; Boolean success; OSErr err; theFileName = CFStringCreateWithCString( CFAllocatorGetDefault(), inPath, kCFStringEncodingMacRoman ); if ( !theFileName ) { goto bail; } #if USE_POSIX_PATH theFileURL = CFURLCreateWithFileSystemPath( CFAllocatorGetDefault(), theFileName, kCFURLPOSIXPathStyle, false ); #else theFileURL = CFURLCreateWithFileSystemPath( CFAllocatorGetDefault(), theFileName, kCFURLHFSPathStyle, false ); #endif if ( !theFileURL ) { goto bail; } success = CFURLGetFSRef( theFileURL, &fileRef ); if ( !success ) { goto bail; } err = FSGetCatalogInfo( &fileRef, kFSCatInfoNone, NULL, NULL, &fileSpec, NULL ); FInfo finderInfo; FSpGetFInfo( &fileSpec, &finderInfo ); finderInfo.fdCreator = inCreator; finderInfo.fdType = inType; FSpSetFInfo( &fileSpec, &finderInfo ); bail: if ( theFileURL ) { CFRelease( theFileURL ); } if ( theFileName ) { CFRelease( theFileName ); } return; } FILE * mac_fopen( const char *filename, const char * open_mode ) { if ( strstr( filename, ".cfg" ) || strstr( filename, ".log" ) || strstr( filename, ".txt" ) ) { _ftype = text_type; _fcreator = text_creator; } else if ( strstr( filename, ".tga" ) ) { _ftype = 'TPIC'; _fcreator = 'prvw'; } else if ( strstr( filename, ".jpg" ) ) { _ftype = 'JPEG'; _fcreator = 'prvw'; } else { _ftype = kGameBinaryType; _fcreator = kGameCreator; } // char tempPath[MAX_OSPATH]; FILE *reply = NULL; int create = strchr( open_mode, 'w' ) || strchr( open_mode, 'a' ); // GetPosixPathFromHFS (filename, tempPath); reply = fopen( filename, open_mode ); if ( reply && create ) { FILE_SetCreator( filename, _fcreator, _ftype ); } return reply; } Boolean AmIBundled( void ) { FSRef processRef; FSCatalogInfo processInfo; int isBundled; ProcessSerialNumber psn = {0, kCurrentProcess}; GetProcessBundleLocation( &psn, &processRef ); FSGetCatalogInfo( &processRef, kFSCatInfoNodeFlags, &processInfo, NULL, NULL, NULL ); isBundled = processInfo.nodeFlags & kFSNodeIsDirectoryMask; return( isBundled ); } OSErr GetFullPathFromSpec( const FSSpec *spec, char *fullpath ) { OSErr err; FSRef theRef; FSSpec tempSpec; Boolean isFolder, wasAliased; // Make a copy of the spec and attempt to resolive it if it's an alias tempSpec = *spec; err = ResolveAliasFile( &tempSpec, true, &isFolder, &wasAliased ); // Conver the FSSpec to an FSRef and attempt to get a full path from it. err = FSpMakeFSRef( &tempSpec, &theRef ); if ( err == noErr ) { FSRefMakePath( &theRef, (UInt8 *) fullpath, MAX_OSPATH ); strcat( fullpath, kPathSep ); } // It's not possible to have an FSRef for files that don't exist yet. In this // case, we create an FSRef for the parent directory, convert that to a full path // and append the filename to that. if ( err == fnfErr ) { FSRefParam paramBlock; CFURLRef theParentURL; CFStringRef theFileName; CFURLRef theFileURL; Boolean successful; paramBlock.ioVRefNum = spec->vRefNum; paramBlock.ioNamePtr = NULL; paramBlock.ioDirID = spec->parID; paramBlock.newRef = &theRef; err = PBMakeFSRefSync( ¶mBlock ); if ( err == noErr ) { theParentURL = CFURLCreateFromFSRef( CFAllocatorGetDefault(), paramBlock.newRef ); theFileName = CFStringCreateWithBytes( CFAllocatorGetDefault(), spec->name + 1, spec->name[0], kCFStringEncodingMacRoman, false ); theFileURL = CFURLCreateWithFileSystemPathRelativeToBase( CFAllocatorGetDefault(), theFileName, #if USE_POSIX_PATH kCFURLPOSIXPathStyle, false, #else kCFURLHFSPathStyle, false, #endif theParentURL ); successful = CFURLGetFileSystemRepresentation( theFileURL, true, (UInt8*)fullpath, MAX_OSPATH ); CFRelease( theFileURL ); CFRelease( theFileName ); CFRelease( theParentURL ); } } return err; } FILE * FSp_fopen( const FSSpec *spec, const char *open_mode ) { char path[MAX_OSPATH]; OSErr err; err = GetFullPathFromSpec( spec, path ); if ( err == noErr ) { return fopen( path, open_mode ); } else {return NULL;} } OSErr GetApplicationFSSpec( FSSpecPtr theFSSpecPtr ) { ProcessSerialNumber serial; ProcessInfoRec info; OSErr err; serial.highLongOfPSN = 0; serial.lowLongOfPSN = kCurrentProcess; info.processInfoLength = sizeof( ProcessInfoRec ); info.processName = NULL; info.processAppSpec = theFSSpecPtr; err = GetProcessInformation( &serial, &info ); return err; } OSErr GetApplicationPackageFSSpecFromBundle( FSSpecPtr theFSSpecPtr ) { FSRef myBundleRef; CFBundleRef refMainBundle = NULL; CFURLRef refMainBundleURL = NULL; Boolean ok; // get app bundle refMainBundle = CFBundleGetMainBundle(); if ( !refMainBundle ) { return paramErr; } // create a URL to the app bundle refMainBundleURL = CFBundleCopyBundleURL( refMainBundle ); if ( !refMainBundleURL ) { return paramErr; } ok = CFURLGetFSRef( refMainBundleURL, &myBundleRef ); CFRelease( refMainBundleURL ); if ( !ok ) { return fnfErr; } return FSGetCatalogInfo( &myBundleRef, kFSCatInfoNone, NULL, NULL, theFSSpecPtr, NULL ); } #if __cplusplus } #endif #pragma mark - void Sys_Mkdir( const char *inPath ) { // LBO 1/30/05. Modified to create any intermediate folders. If built // against MSL, mkdir tends to return EMACOSERR for any kind of // failure instead of, e.g., EEXIST or ENOENT. if ( inPath == NULL ) { return; } char macPath[MAX_OSPATH]; strcpy( macPath, inPath ); // Don't bother with "" or "/", just return success. if ( macPath[0] == 0 || ( macPath[0] == kPathSep[0] && macPath[1] == 0 ) ) { return; } // Don't bother if we've got an app-relative path. Those are pointing // to the wrong place anyway. if ( !strchr( &macPath[1], kPathSep[0] ) ) { return; } int result = mkdir( macPath, 0777 ); // Don't fail if the folder already exists. if ( result != 0 && ( errno == EEXIST ) ) { result = 0; } if ( result != 0 /*&& errno == ENOENT*/ ) { // ENOENT: A component of the path prefix does not exist // Parse path and create each folder in path. int len = strlen( macPath ); // Remove trailing delimiter if it exists, we'll use null terminator instead. if ( macPath[len - 1] == kPathSep[0] ) { macPath[--len] = 0; } // Only try this if there is a delimiter in the path. if ( strchr( &macPath[1], kPathSep[0] ) ) { result = 0; int pos = strchr( &macPath[1], kPathSep[0] ) - &macPath[0] + 1; while ( /*result == 0 &&*/ pos <= len ) { int c = macPath[pos]; if ( ( c == kPathSep[0] || c == 0 ) && ( pos != 0 ) ) { // Temporarily truncate the path and create that folder. macPath[pos] = 0; result = mkdir( macPath, 0777 ); macPath[pos] = c; // Don't fail if the folder already exists. if ( result != 0 && errno == EEXIST ) { result = 0; } } pos++; } } } } char *Sys_Cwd( void ) { static char dir[MAX_OSPATH]; int l; if ( AmIBundled() ) { FSSpec bundleSpec; OSErr err; GetApplicationPackageFSSpecFromBundle( &bundleSpec ); bundleSpec.name[0] = 0; err = GetFullPathFromSpec( &bundleSpec, dir ); } else { FSSpec bundleSpec; OSErr err; GetApplicationFSSpec( &bundleSpec ); bundleSpec.name[0] = 0; err = GetFullPathFromSpec( &bundleSpec, dir ); } // strip off the last delimiter l = strlen( dir ); if ( l > 0 ) { #if USE_POSIX_PATH if ( dir[l - 1] == '/' ) #else if ( dir[l - 1] == ':' ) #endif { dir[l - 1] = 0;} } return dir; } char *Sys_DefaultCDPath( void ) { // LBO - We treat the game directory as un-writable to work better // with multiple users. Therefore, the "CD Path" will point to the // install path for the application. return Sys_Cwd(); } char *Sys_DefaultInstallPath( void ) { // The base path of the installation. If the Q3 engine supports this call, // it means that it differentiates between the install path and a specific // home directory path for the user. In this case, Sys_DefaultBasePath() // is not used. return Sys_Cwd(); } char *Sys_DefaultHomePath( void ) { static char fullpath[MAX_OSPATH]; OSErr err; long dirID; short refNum; FSSpec spec; if ( 1 ) { //(gSystemVersion >= 0x1000) // The "base path" is a writable location. We want to play // nicely with multiple users, so we point the base path to the // Application Support-> folder. err = FindFolder( kUserDomain, kApplicationSupportFolderType, kCreateFolder, &refNum, &dirID ); spec.parID = dirID; spec.vRefNum = refNum; spec.name[0] = 0; err = GetFullPathFromSpec( &spec, fullpath ); strcat( fullpath, kGameFolderName ); Sys_Mkdir( fullpath ); return fullpath; } else { return Sys_Cwd(); } } char *Sys_DefaultBasePath( void ) { // In older versions of the Q3 engine, it doesn't differentiate between the install path // and the user's home directory. Instead, it calls the installation path the "base path". // In this case, we point the install to the home directory and fake up the CD path to point // to the install path, since we never use the CD path anyway. return Sys_DefaultHomePath(); } /* ================================================================================= FILE FINDING ================================================================================= */ #define MAX_FOUND_FILES 0x1000 #if MAC_JK2_MP || MAC_MOHAA || MAC_WOLF void Sys_ListFilteredFiles( const char *basedir, char *subdirs, char *filter, char **list, int *numfiles ) { char search[MAX_OSPATH], newsubdirs[MAX_OSPATH]; char filename[MAX_OSPATH]; int VRefNum; int DrDirId; FSSpec fsspec; int index; if ( *numfiles >= MAX_FOUND_FILES - 1 ) { return; } // LBO 6/1/04 - bugfix. "basedir" should already have a path delimiter on it. if ( strlen( subdirs ) ) { Com_sprintf( search, sizeof( search ), "%s%s:", basedir, subdirs ); } else { Com_sprintf( search, sizeof( search ), "%s", basedir ); } // get the volume and directory numbers // there has to be a better way than this... { CInfoPBRec paramBlock; Q_strncpyz( search, search, sizeof( search ) ); c2pstrcpy( (StringPtr) search, search ); FSMakeFSSpec( 0, 0, (unsigned char *)search, &fsspec ); VRefNum = fsspec.vRefNum; memset( ¶mBlock, 0, sizeof( paramBlock ) ); paramBlock.hFileInfo.ioNamePtr = (unsigned char *)search; PBGetCatInfoSync( ¶mBlock ); DrDirId = paramBlock.hFileInfo.ioDirID; } for ( index = 1 ; ; index++ ) { CInfoPBRec paramBlock; char macFileName[MAX_OSPATH]; OSErr err; memset( ¶mBlock, 0, sizeof( paramBlock ) ); paramBlock.hFileInfo.ioNamePtr = (unsigned char *)macFileName; paramBlock.hFileInfo.ioVRefNum = VRefNum; paramBlock.hFileInfo.ioFDirIndex = index; paramBlock.hFileInfo.ioDirID = DrDirId; err = PBGetCatInfoSync( ¶mBlock ); if ( err != noErr ) { break; } p2cstrcpy( macFileName, (const StringPtr)macFileName ); if ( paramBlock.hFileInfo.ioFlAttrib & 16 ) { if ( strlen( subdirs ) ) { Com_sprintf( newsubdirs, sizeof( newsubdirs ), "%s:%s", subdirs, macFileName ); } else { Com_sprintf( newsubdirs, sizeof( newsubdirs ), "%s", macFileName ); } Sys_ListFilteredFiles( basedir, newsubdirs, filter, list, numfiles ); } if ( *numfiles >= MAX_FOUND_FILES - 1 ) { break; } // LBO 6/1/04 - bugfix. If no subdirectory string, just use "macFileName". if ( strlen( subdirs ) ) { Com_sprintf( filename, sizeof( filename ), "%s:%s", subdirs, macFileName ); } else { Com_sprintf( filename, sizeof( filename ), "%s", macFileName ); } if ( !Com_FilterPath( filter, filename, qfalse ) ) { continue; } list[ *numfiles ] = CopyString( filename ); ( *numfiles )++; } } #endif #pragma mark Sys_ListFiles #if MAC_MOHAA Q3DEF char **Sys_ListFiles( const char *directory, const char *extension, const char *filter, int *numfiles, qboolean wantsubs ) #elif MAC_WOLF || MAC_JK2_MP Q3DEF char **Sys_ListFiles( const char *directory, const char *extension, char *filter, int *numfiles, qboolean wantsubs ) #else Q3DEF char **Sys_ListFiles( const char *directory, const char *extension, int *numfiles, qboolean wantsubs ) #endif { int nfiles; char **listCopy; char pdirectory[MAX_OSPATH]; char *list[MAX_FOUND_FILES]; int directoryFlag; int i; int extensionLength; int VRefNum; int DrDirId; int index; FSSpec fsspec; #if MAC_JK2_MP || MAC_MOHAA || MAC_WOLF if ( filter ) { nfiles = 0; Sys_ListFilteredFiles( directory, "", (char *)filter, list, &nfiles ); list[ nfiles ] = 0; *numfiles = nfiles; if ( !nfiles ) { return NULL; } #if MAC_Q3_OLDSTUFF listCopy = (char **)Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); #else listCopy = (char **)Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ), TAG_FILESYS, qfalse ); #endif for ( i = 0 ; i < nfiles ; i++ ) { listCopy[i] = list[i]; } listCopy[i] = NULL; return listCopy; } #endif // get the volume and directory numbers // there has to be a better way than this... { CInfoPBRec paramBlock; #if USE_POSIX_PATH GetHFSPathFromPosix( directory, pdirectory ); // Q_strncpyz( pdirectory, directory, sizeof(pdirectory) ); #else Q_strncpyz( pdirectory, directory, sizeof( pdirectory ) ); #endif c2pstrcpy( (StringPtr) pdirectory, pdirectory ); FSMakeFSSpec( 0, 0, (unsigned char *)pdirectory, &fsspec ); VRefNum = fsspec.vRefNum; memset( ¶mBlock, 0, sizeof( paramBlock ) ); paramBlock.hFileInfo.ioNamePtr = (unsigned char *)pdirectory; PBGetCatInfoSync( ¶mBlock ); DrDirId = paramBlock.hFileInfo.ioDirID; } if ( !extension ) { extension = ""; } extensionLength = strlen( extension ); if ( wantsubs || ( extension[0] == '/' && extension[1] == 0 ) ) { directoryFlag = 16; } else { directoryFlag = 0; } nfiles = 0; for ( index = 1 ; ; index++ ) { CInfoPBRec paramBlock; char fileName[MAX_OSPATH]; int length; OSErr err; memset( ¶mBlock, 0, sizeof( paramBlock ) ); paramBlock.hFileInfo.ioNamePtr = (unsigned char *)fileName; paramBlock.hFileInfo.ioVRefNum = VRefNum; paramBlock.hFileInfo.ioFDirIndex = index; paramBlock.hFileInfo.ioDirID = DrDirId; err = PBGetCatInfoSync( ¶mBlock ); if ( err != noErr ) { break; } if ( directoryFlag ^ ( paramBlock.hFileInfo.ioFlAttrib & 16 ) ) { continue; } // convert filename to C string length = fileName[0]; p2cstrcpy( fileName, (const StringPtr) fileName ); // check the extension if ( !directoryFlag ) { if ( length < extensionLength ) { continue; } if ( Q_stricmp( fileName + length - extensionLength, extension ) ) { continue; } } // add this file if ( nfiles == MAX_FOUND_FILES - 1 ) { break; } list[ nfiles ] = CopyString( fileName ); nfiles++; } list[ nfiles ] = 0; // return a copy of the list *numfiles = nfiles; if ( !nfiles ) { return NULL; } #if MAC_Q3_OLDSTUFF listCopy = (char **) Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); #else listCopy = (char **) Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ), TAG_LISTFILES, qfalse ); #endif for ( i = 0 ; i < nfiles ; i++ ) { listCopy[i] = list[i]; } listCopy[i] = NULL; return listCopy; } void Sys_FreeFileList( char **list ) { int i; if ( !list ) { return; } for ( i = 0 ; list[i] ; i++ ) { Z_Free( list[i] ); } Z_Free( list ); } void Mac_GetOSPath( short inDomain, OSType inFolderType, char *outPath ) { OSStatus err = noErr; char temp[PATH_MAX]; FSRef foundRef; if ( !outPath ) { return; } err = FSFindFolder( inDomain, inFolderType, true, &foundRef ); if ( err ) { return; } err = FSRefMakePath( &foundRef, (UInt8 *)outPath, PATH_MAX ); } #if MAC_JKJA qboolean Sys_FileOutOfDate( LPCSTR psFinalFileName /* dest */, LPCSTR psDataFileName /* src */ ) { return qfalse; } qboolean Sys_CopyFile( LPCSTR lpExistingFileName, LPCSTR lpNewFileName, qboolean bOverwrite ) { return qfalse; } #endif