123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2013 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #import "platformOSX/platformOSX.h"
- #include "platform/platform.h"
- #include "platform/platformFileIO.h"
- #include "console/console.h"
- #include "string/stringTable.h"
- #include "collection/vector.h"
- #include "io/resource/resourceManager.h"
- #include "debug/profiler.h"
- #include <sys/stat.h>
- #include <sys/time.h>
- // Maximum character length for file paths
- #define MAX_MAC_PATH_LONG 2048
- #pragma mark ---- File IO Helper Functions ----
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- // utility for platform::hasSubDirectory() and platform::dumpDirectories()
- // ensures that the entry is a directory, and isnt on the ignore lists.
- inline bool isGoodDirectory(const char* path)
- {
- return (Platform::isDirectory(path) // is a dir
- && dStrcmp(path,".") != 0 // not here
- && dStrcmp(path,"..") != 0 // not parent
- && !Platform::isExcludedDirectory(path)); // not excluded
- }
- //-----------------------------------------------------------------------------
- static bool isMainDotCsPresent(char *dir)
- {
- // Create the full path using the dir and pre-determined script name of "main.cs"
- char maincsbuf[MAX_MAC_PATH_LONG];
- const char *maincsname = "/main.cs";
- U32 len = dStrlen(dir) + dStrlen(maincsname);
-
- // The length of the above file path is too long. Error out
- AssertISV(len < MAX_MAC_PATH_LONG, "Sorry, path is too long, I can't run from this folder.");
-
- // Store the main.cs file path
- dSprintf(maincsbuf,MAX_MAC_PATH_LONG,"%s%s", dir, maincsname);
-
- // Check to see if the main.cs exists and return the result
- return Platform::isFile(maincsbuf);
- }
- //-----------------------------------------------------------------------------
- void recurseDumpDirectories(const char* basePath, const char* subPath, Vector<StringTableEntry> &directoryVector, S32 depth, bool noBasePath)
- {
- // Is path a directory?
- if ( !Platform::isDirectory(basePath) )
- {
- // No, so warn.
- Con::warnf("recurseDumpDirectories: %s is not a directory", basePath);
-
- // Fnish.
- return;
- }
-
- // Fetch the shared file manager.
- NSFileManager* fileManager = [NSFileManager defaultManager];
-
- // Convert the path to a NSString, then to a URL
- NSString* directoryPath = [[NSString stringWithFormat:@"%s/%s", basePath, subPath] stringByStandardizingPath];
- NSURL* directoryToScan = [NSURL fileURLWithPath:directoryPath];
-
- // Fetch the contents of the directory.
- NSArray* directoryContents = [fileManager contentsOfDirectoryAtURL:directoryToScan
- includingPropertiesForKeys:[NSArray arrayWithObjects:NSURLNameKey, NSURLIsDirectoryKey, NSURLIsPackageKey, nil]
- options:NSDirectoryEnumerationSkipsHiddenFiles
- error:nil];
-
- // Enumerate sub-directories.
- for( NSURL* itemURL in directoryContents )
- {
- // Is the item a directory?
- NSNumber* isDirectory;
- [itemURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:NULL];
-
- // Skip if this is NOT a directory.
- if ( [isDirectory boolValue] == false )
- continue;
-
- // Is the sub-directory a package directory?
- NSNumber* isPackageDirectory;
- [itemURL getResourceValue:&isPackageDirectory forKey:NSURLIsPackageKey error:NULL];
-
- // Skip if this is a package directory.
- if ( [isPackageDirectory boolValue] == true )
- continue;
-
- // Fetch the sub-directory name.
- NSString* subDirectoryName;
- [itemURL getResourceValue:&subDirectoryName forKey:NSURLNameKey error:NULL];
-
- // Fetch UTF8 sub-directory name.
- const char* pSubDirectoryName = [subDirectoryName UTF8String];
-
- // Skip if this is an excluded directory.
- if ( Platform::isExcludedDirectory(pSubDirectoryName) )
- continue;
-
- // Format new sub-path.
- const char* pNewSubPath = dStrlen(subPath) > 0 ? [[NSString stringWithFormat:@"%s/%s", subPath, pSubDirectoryName] UTF8String] : pSubDirectoryName;
-
- // Format with base-path accordingly.
- if ( noBasePath )
- {
- // Store the directory excluding the base-path.
- directoryVector.push_back( StringTable->insert( pNewSubPath ) );
- }
- else
- {
- // Store the directory with the bath-path.
- directoryVector.push_back( StringTable->insert( [[itemURL path] UTF8String] ) );
- }
-
- // Skip if no further recursion is required.
- if ( depth == 0 )
- continue;
-
- // Recurse the sub-directory.
- recurseDumpDirectories( basePath, pNewSubPath, directoryVector, depth-1, noBasePath );
- }
- }
- //-----------------------------------------------------------------------------
- static void recurseDumpPath(const char* curPath, Vector<Platform::FileInfo>& fileVector, U32 depth)
- {
- // Fetch the shared file manager.
- NSFileManager* fileManager = [NSFileManager defaultManager];
-
- // Convert the path to a NSString, then to a URL
- NSString* directoryPath = [[NSString stringWithUTF8String:curPath] stringByStandardizingPath];
- NSURL* directoryToScan = [NSURL fileURLWithPath:directoryPath];
- const char* pDirectoryPath = [directoryPath UTF8String];
- // Fetch the contents of the directory.
- NSArray* directoryContents = [fileManager contentsOfDirectoryAtURL:directoryToScan
- includingPropertiesForKeys:[NSArray arrayWithObjects:NSURLNameKey, NSURLIsDirectoryKey, NSURLIsPackageKey, NSURLFileSizeKey, nil]
- options:NSDirectoryEnumerationSkipsHiddenFiles
- error:nil];
-
- // Enumerate files.
- for( NSURL* itemURL in directoryContents )
- {
- // Is the item a directory?
- NSNumber* isDirectory;
- [itemURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:NULL];
-
- // Skip if this is a directory.
- if ( [isDirectory boolValue] == true )
- continue;
-
- // Fetch the file-name.
- NSString* fileName;
- [itemURL getResourceValue:&fileName forKey:NSURLNameKey error:NULL];
- // Fetch the file size.
- NSNumber* fileSize;
- [itemURL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:NULL];
-
- // Allocate a file entry.
- fileVector.increment();
- Platform::FileInfo& info = fileVector.last();
-
- // Populate file entry.
- info.pFullPath = StringTable->insert( pDirectoryPath );
- info.pFileName = StringTable->insert( [fileName UTF8String] );
- info.fileSize = [fileSize intValue];
- }
-
- // Finish if no further recursion is required.
- if ( depth == 0 )
- return;
-
- // Enumerate sub-directories.
- for( NSURL* itemURL in directoryContents )
- {
- // Is the item a directory?
- NSNumber* isDirectory;
- [itemURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:NULL];
-
- // Skip if this is NOT a directory.
- if ( [isDirectory boolValue] == false )
- continue;
-
- // Is the sub-directory a package directory?
- NSNumber* isPackageDirectory;
- [itemURL getResourceValue:&isPackageDirectory forKey:NSURLIsPackageKey error:NULL];
-
- // Skip if this is a package directory.
- if ( [isPackageDirectory boolValue] == true )
- continue;
-
- // Fetch the sub-directory name.
- NSString* subDirectoryName;
- [itemURL getResourceValue:&subDirectoryName forKey:NSURLNameKey error:NULL];
-
- // Skip if this is an excluded directory.
- if ( Platform::isExcludedDirectory([subDirectoryName UTF8String]) )
- continue;
-
- // Recurse the sub-directory.
- recurseDumpPath( [[itemURL path] UTF8String], fileVector, depth-1 );
- }
- }
- //-----------------------------------------------------------------------------
- #pragma mark ---- File Class Methods ----
- //-----------------------------------------------------------------------------
- File::File() : currentStatus(Closed), capability(0)
- {
- handle = NULL;
- }
- //-----------------------------------------------------------------------------
- File::~File()
- {
- close();
- handle = NULL;
- }
- //-----------------------------------------------------------------------------
- File::Status File::open(const char *filename, const AccessMode openMode)
- {
- if (dStrlen(filename) > MAX_MAC_PATH_LONG)
- Con::warnf("File::open: Filename length is pretty long...");
-
- // Close the file if it was already open...
- if (currentStatus != Closed)
- close();
-
- // create the appropriate type of file...
- switch (openMode)
- {
- case Read:
- handle = (void *)fopen(filename, "rb"); // read only
- break;
- case Write:
- handle = (void *)fopen(filename, "wb"); // write only
- break;
- case ReadWrite:
- handle = (void *)fopen(filename, "ab+"); // write(append) and read
- break;
- case WriteAppend:
- handle = (void *)fopen(filename, "ab"); // write(append) only
- break;
- default:
- AssertFatal(false, "File::open: bad access mode");
- }
-
- // handle not created successfully
- if (handle == NULL)
- return setStatus();
-
- // successfully created file, so set the file capabilities...
- switch (openMode)
- {
- case Read:
- capability = FileRead;
- break;
- case Write:
- case WriteAppend:
- capability = FileWrite;
- break;
- case ReadWrite:
- capability = FileRead | FileWrite;
- break;
- default:
- AssertFatal(false, "File::open: bad access mode");
- }
-
- // must set the file status before setting the position.
- currentStatus = Ok;
-
- if (openMode == ReadWrite)
- setPosition(0);
-
- // success!
- return currentStatus;
- }
- //-----------------------------------------------------------------------------
- U32 File::getPosition() const
- {
- // Before proceeding, make sure the file is open and the handle is valid
- AssertFatal(currentStatus != Closed , "File::getPosition: file closed");
- AssertFatal(handle != NULL, "File::getPosition: invalid file handle");
-
- // Return the position (aka, offset)
- return (U32)ftell((FILE*)handle);
- }
- //-----------------------------------------------------------------------------
- File::Status File::setPosition( S32 position, bool absolutePos )
- {
- // Before proceeding, make sure the file is open and the handle is valid
- AssertFatal(Closed != currentStatus, "File::setPosition: file closed");
- AssertFatal(handle != NULL, "File::setPosition: invalid file handle");
-
- // Check for valid status and not end of the file stream
- if (currentStatus != Ok && currentStatus != EOS )
- return currentStatus;
-
- // This will hold the final position of the current stream
- U32 finalPos;
-
- // Is the position absolute?
- if( absolutePos )
- {
- // Yes, so sanity!
- AssertFatal(0 <= position, "File::setPosition: negative absolute position");
-
- // Position beyond EOS is OK
- fseek((FILE*)handle, position, SEEK_SET);
- finalPos = (U32)ftell((FILE*)handle);
- }
- else
- {
- // No, so sanity!
- AssertFatal((getPosition() + position) >= 0, "File::setPosition: negative relative position");
-
- // Position beyond EOS is OK
- fseek((FILE*)handle, position, SEEK_CUR);
- finalPos = (U32)ftell((FILE*)handle);
- }
-
- // ftell returns -1 on error. set error status
- if (0xffffffff == finalPos)
- {
- return setStatus(IOError);
- }
- else if ( finalPos >= getSize() )
- {
- // Success, at end of file
- return currentStatus = EOS;
- }
- else
- {
- // Success!
- return currentStatus = Ok;
- }
- }
- //-----------------------------------------------------------------------------
- U32 File::getSize() const
- {
- // Before proceeding, make sure the file is open and the handle is valid
- AssertWarn(Closed != currentStatus, "File::getSize: file closed");
- AssertFatal(handle != NULL, "File::getSize: invalid file handle");
-
- if ( Ok == currentStatus || EOS == currentStatus )
- {
- struct stat statData;
-
- if(fstat(fileno((FILE*)handle), &statData) != 0)
- return 0;
-
- // return the size in bytes
- return (U32)statData.st_size;
- }
-
- return 0;
- }
- //-----------------------------------------------------------------------------
- File::Status File::flush()
- {
- // Before proceeding, make sure the file is open, the handle is valid, and the file can
- // be written to.
- AssertFatal(Closed != currentStatus, "File::flush: file closed");
- AssertFatal(handle != NULL, "File::flush: invalid file handle");
- AssertFatal(true == hasCapability(FileWrite), "File::flush: cannot flush a read-only file");
-
- if (fflush((FILE*)handle) != 0)
- return setStatus();
- else
- return currentStatus = Ok;
- }
- //-----------------------------------------------------------------------------
- File::Status File::close()
- {
- // check if it's already closed...
- if (Closed == currentStatus)
- return currentStatus;
-
- // it's not, so close it...
- if (handle != NULL)
- {
- if (fclose((FILE*)handle) != 0)
- return setStatus();
- }
- handle = NULL;
- return currentStatus = Closed;
- }
- //-----------------------------------------------------------------------------
- File::Status File::getStatus() const
- {
- return currentStatus;
- }
- //-----------------------------------------------------------------------------
- File::Status File::setStatus()
- {
- strerror(errno);
- switch (errno)
- {
- case EACCES: // permission denied
- currentStatus = IOError;
- break;
- case EBADF: // Bad File Pointer
- case EINVAL: // Invalid argument
- case ENOENT: // file not found
- case ENAMETOOLONG:
- default:
- currentStatus = UnknownError;
- }
-
- return currentStatus;
- }
- //-----------------------------------------------------------------------------
- File::Status File::setStatus(File::Status status)
- {
- return currentStatus = status;
- }
- //-----------------------------------------------------------------------------
- File::Status File::read(U32 size, char *dst, U32 *bytesRead)
- {
- // Before proceeding, make sure the file is not closed, the handle is valid,
- // the destination is not null, the file can be read, and the read size is
- // greater than 0.
- AssertFatal(Closed != currentStatus, "File::read: file closed");
- AssertFatal(handle != NULL, "File::read: invalid file handle");
- AssertFatal(NULL != dst, "File::read: NULL destination pointer");
- AssertFatal(true == hasCapability(FileRead), "File::read: file lacks capability");
- AssertWarn(0 != size, "File::read: size of zero");
-
- if (Ok != currentStatus || 0 == size)
- return currentStatus;
-
- // read from stream
- U32 nBytes = (U32)fread(dst, 1, size, (FILE*)handle);
-
- // did we hit the end of the stream?
- if( nBytes != size)
- currentStatus = EOS;
-
- // if bytesRead is a valid pointer, send number of bytes read there.
- if(bytesRead)
- *bytesRead = nBytes;
-
- // successfully read size bytes
- return currentStatus;
- }
- //-----------------------------------------------------------------------------
- File::Status File::write(U32 size, const char *src, U32 *bytesWritten)
- {
- // Before proceeding, make sure the file is not closed, the handle is valid,
- // the source is not null, the file can be written to, and the write size is
- // greater than 0.
- AssertFatal(Closed != currentStatus, "File::write: file closed");
- AssertFatal(handle != NULL, "File::write: invalid file handle");
- AssertFatal(NULL != src, "File::write: NULL source pointer");
- AssertFatal(true == hasCapability(FileWrite), "File::write: file lacks capability");
- AssertWarn(0 != size, "File::write: size of zero");
-
- if ((Ok != currentStatus && EOS != currentStatus) || 0 == size)
- return currentStatus;
-
- // write bytes to the stream
- U32 nBytes = (U32)fwrite(src, 1, size,(FILE*)handle);
-
- // if we couldn't write everything, we've got a problem. set error status.
- if(nBytes != size)
- setStatus();
-
- // if bytesWritten is a valid pointer, put number of bytes read there.
- if(bytesWritten)
- *bytesWritten = nBytes;
-
- // return current File status, whether good or ill.
- return currentStatus;
- }
- //-----------------------------------------------------------------------------
- bool File::hasCapability(Capability cap) const
- {
- return (0 != (U32(cap) & capability));
- }
- #pragma mark ---- Platform Namespace Methods ----
- //-----------------------------------------------------------------------------
- bool Platform::fileDelete(const char * name)
- {
- // Invalid pointer, return false
- if (!name)
- return false;
-
- // Name is longer than our limit, throw a warning
- if (dStrlen(name) > MAX_MAC_PATH_LONG)
- Con::warnf("Platform::fileDelete() - Filename length is pretty long...");
-
- // Atandard auto-release pool
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- // Get the default NSFileManager
- NSFileManager* fileManager = [NSFileManager defaultManager];
-
- // Convert the name char* to a NSString
- NSString* filePath = [[NSString stringWithUTF8String:name] stringByStandardizingPath];
-
- // Delete the file
- // Returns YES if the item was removed successfully or if path was nil.
- // Returns NO if an error occurred. If the delegate aborts the operation for a file, this method returns YES.
- // However, if the delegate aborts the operation for a directory, this method returns NO.
- NSError* errorResponse = nil;
-
- BOOL result = [fileManager removeItemAtPath:filePath error:&errorResponse];
-
- // Could not delete the file, so print the error
- if (!result)
- {
- Con::errorf("Platform::fileDelete: %s", [[errorResponse localizedDescription] UTF8String]);
- }
-
- // Drain the memory and release the pool
- [pool drain];
-
- // Return results of the deletion
- return result;
- }
- //-----------------------------------------------------------------------------
- bool Platform::fileTouch(const char *path)
- {
- if (!path || !*path)
- return false;
-
- // set file at path's modification and access times to now.
- return( utimes( path, NULL) == 0); // utimes returns 0 on success.
- }
- //-----------------------------------------------------------------------------
- S32 Platform::compareFileTimes(const FileTime &a, const FileTime &b)
- {
- if(a > b)
- return 1;
- if(a < b)
- return -1;
- return 0;
- }
- //-----------------------------------------------------------------------------
- // either time param COULD be null.
- //-----------------------------------------------------------------------------
- bool Platform::getFileTimes(const char *path, FileTime *createTime, FileTime *modifyTime)
- {
- if (!path || !*path)
- return false;
- struct stat statData;
- if (stat(path, &statData) == -1)
- return false;
- if (createTime)
- *createTime = statData.st_ctime;
- if (modifyTime)
- *modifyTime = statData.st_mtime;
- return true;
- // Make sure the path pointer is valid
- /*if (!path || !*path)
- return false;
- struct stat statData;
- // Standard auto-release pool
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- // Convert the path to a NSString
- NSString* convertedPath = [[NSString stringWithUTF8String:path] stringByStandardizingPath];
-
- // Error catcher
- NSError* errorResponse = nil;
-
- // Get the file attributes
- NSDictionary* attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:convertedPath error:&errorResponse];
-
- // If we failed to get the file attributes, something is wrong with the file or logic
- if (attributes == nil)
- {
- // Report the error and assign 0 file times
- Con::errorf("File::getSize: %s", [[errorResponse localizedDescription] UTF8String]);
- *createTime = 0;
- *modifyTime = 0;
- return false;
- }
- else
- {
- // Assign the creation and modified file times
- *createTime = [[attributes objectForKey:NSFileCreationDate] unsignedLongLongValue];
- *modifyTime = [[attributes objectForKey:NSFileModificationDate] unsignedLongLongValue];
- }
-
- // Auto-release memory
- [pool drain];
-
- return true;*/
- }
- //-----------------------------------------------------------------------------
- bool Platform::createPath(const char *file)
- {
- // Make sure the file pointer is valid
- if (!file || !*file)
- return false;
- // Ensure that the path has no file on the end.
- char pathBuffer[1024];
- dSprintf(pathBuffer, sizeof(pathBuffer), "%s", file );
- const S32 pathLength = dStrlen(pathBuffer);
- if ( pathLength == sizeof(pathBuffer)-1 )
- {
- Con::warnf("Could not create path as it was too long: '%s", file);
- return false;
- }
- char* pFinalSlash = dStrrchr(pathBuffer, '/');
- if ( pFinalSlash != pathBuffer+pathLength-1 )
- {
- pFinalSlash[1] = 0;
- }
- file = pathBuffer;
-
- // Standard auto-release pool
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- // Conver the file path to a NSString
- NSString* convertedPath = [[NSString stringWithUTF8String:file] stringByStandardizingPath];
- // Get the shared file manager
- NSFileManager* fileManager = [NSFileManager defaultManager];
-
- // Error catcher
- NSError* createError = nil;
-
- // Try to create the path
- BOOL result = [fileManager createDirectoryAtPath:convertedPath withIntermediateDirectories:YES attributes:nil error:&createError];
-
- // If the create path failed, report the error and return
- if (!result)
- {
- Con::errorf("Platform::createPath error: %s", [[createError localizedDescription] UTF8String]);
- [pool drain];
- return false;
- }
-
- // Auto-release memory
- [pool drain];
-
- // Success
- return true;
- }
- //-----------------------------------------------------------------------------
- StringTableEntry Platform::getCurrentDirectory()
- {
- // Use the shared file manager to get the current path
- return [[[NSFileManager defaultManager] currentDirectoryPath] UTF8String];
- }
- //-----------------------------------------------------------------------------
- bool Platform::setCurrentDirectory(StringTableEntry newDir)
- {
- // Standard auto-release pool
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- // Get the shared file manager
- NSFileManager* fileManager = [NSFileManager defaultManager];
-
- // Convert the newDir to a NSString
- NSString* convertedString = [[NSString stringWithUTF8String:newDir] stringByStandardizingPath];
-
- // Attempt to set the current directory to what was passed
- BOOL result = [fileManager changeCurrentDirectoryPath:convertedString];
-
- // Auto-release memory
- [pool drain];
-
- // Return the result
- return result;
- }
- //-----------------------------------------------------------------------------
- void Platform::openFolder(const char* path )
- {
- // Standard auto-release pool
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- // Convert the path to a NSString
- NSString* convertedPath = [[NSString stringWithUTF8String:path] stringByStandardizingPath];
-
- // Try to open the folder
- BOOL result = [[NSWorkspace sharedWorkspace] openFile:convertedPath];
-
- // Auto-release the memory
- [pool drain];
-
- // If the operation failed, print an error
- if (!result)
- Con::errorf("Platform::openFolder: Failed to open folder %s", path);
- }
- //-----------------------------------------------------------------------------
- /// Finds and sets the current working directory.
- /// Torque tries to automatically detect whether you have placed the game files
- /// inside or outside the application's bundle. It checks for the presence of
- /// the file 'main.cs'. If it finds it, Torque will assume that the other game
- /// files are there too. If Torque does not see 'main.cs' inside its bundle, it
- /// will assume the files are outside the bundle.
- /// Since you probably don't want to copy the game files into the app every time
- /// you build, you will want to leave them outside the bundle for development.
- ///
- /// Placing all content inside the application bundle gives a much better user
- /// experience when you distribute your app.
- StringTableEntry Platform::getExecutablePath()
- {
- // Get the shared OSX platform state
- osxPlatState * platState = [osxPlatState sharedPlatState];
-
- // If we already have a main.cs file, return that
- if (platState.mainCSDirectory)
- return [[platState mainCSDirectory] UTF8String];
-
- // Get the main application bundle
- char cwd_buf[MAX_MAC_PATH_LONG];
- CFBundleRef mainBundle = CFBundleGetMainBundle();
- CFURLRef bundleUrl = CFBundleCopyBundleURL(mainBundle);
-
- // Used to determine where the executable exists.
- bool inside = true;
- bool outside = false;
- bool done = false;
-
- while(!done)
- {
- // first look for game content inside the application bundle.
- // then we look outside the bundle
- // then we assume it's a tool, and the "bundle" = the binary file.
- CFURLRef workingUrl;
- if (inside)
- workingUrl = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault,bundleUrl,CFSTR("Contents/Resources"),true);
- else if(outside)
- workingUrl = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, bundleUrl);
- else
- {
- workingUrl = bundleUrl;
- CFRetain(workingUrl); // so that we can release bundleUrl twice.
- }
-
- CFStringRef workingString = CFURLCopyFileSystemPath(workingUrl, kCFURLPOSIXPathStyle);
- CFMutableStringRef normalizedString = CFStringCreateMutableCopy(NULL, 0, workingString);
- CFStringNormalize(normalizedString,kCFStringNormalizationFormC);
- CFStringGetCString(normalizedString, cwd_buf, sizeof(cwd_buf)-1, kCFStringEncodingUTF8);
-
- // if we dont see main.cs inside the bundle, try again looking outside
- // we're done if we find it, or if we find it neither inside or outside.
- if (isMainDotCsPresent(cwd_buf) || (!inside && !outside))
- done = true;
-
- if (inside)
- inside = false, outside = true;
- else if (outside)
- outside = false;
-
- CFRelease(workingUrl);
- CFRelease(workingString);
- CFRelease(normalizedString);
- }
-
- CFRelease(bundleUrl);
-
- char* ret = NULL;
-
- if (StringTable)
- platState.mainCSDirectory = [NSString stringWithUTF8String: StringTable->insert(cwd_buf)];
- else
- ret = dStrdup(cwd_buf);
-
- return ret ? ret : [platState.mainCSDirectory UTF8String];
- }
- //-----------------------------------------------------------------------------
- StringTableEntry Platform::getExecutableName()
- {
- char path_buf[MAX_MAC_PATH_LONG];
-
- // get a cfurl to the executable name
- CFBundleRef mainBundle = CFBundleGetMainBundle();
- CFURLRef bundleUrl = CFBundleCopyBundleURL(mainBundle);
-
- // get a cfstring of just the app name
- CFStringRef workingString = CFURLCopyLastPathComponent(bundleUrl);
- CFMutableStringRef normalizedString = CFStringCreateMutableCopy(NULL, 0, workingString);
- CFStringNormalize(normalizedString,kCFStringNormalizationFormC);
- CFStringGetCString(normalizedString, path_buf, sizeof(path_buf)-1, kCFStringEncodingUTF8);
-
- CFRelease(bundleUrl);
- CFRelease(workingString);
- CFRelease(normalizedString);
-
- return StringTable->insert(path_buf);
- }
- //-----------------------------------------------------------------------------
- bool Platform::isFile(const char *path)
- {
- // Make sure a valid pointer was passed
- if (!path || !*path)
- return false;
-
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- // Convert the path to a temp NSString
- NSString* filePath = [[NSString stringWithUTF8String:path] stringByStandardizingPath];
-
- // This is required to explicitly tell the file manager to determine if the file
- // in question is a directory
- BOOL isDirectory;
-
- // Scan the path location
- bool exists = [[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&isDirectory];
-
- // Clean up the temp string
- [pool drain];
-
- // Return the results of the scan
- return !isDirectory && exists;
-
- }
- //-----------------------------------------------------------------------------
- bool Platform::isDirectory(const char *path)
- {
- // Make sure a valid pointer was passed
- if (!path || !*path)
- return false;
-
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- // Convert the path to a temp NSString
- NSString* folderPath = [[NSString stringWithUTF8String:path] stringByStandardizingPath];
-
- // This is required to explicitly tell the file manager to determine if the file
- // in question is a directory
- BOOL isDirectory;
-
- // Scan the path location
- bool exists = [[NSFileManager defaultManager] fileExistsAtPath:folderPath isDirectory:&isDirectory];
-
- // Clean up the temp string
- [pool drain];
-
- // Return the results of the scan
- return isDirectory && exists;
- }
- //-----------------------------------------------------------------------------
- S32 Platform::getFileSize(const char* pFilePath)
- {
- // Make sure a valid pointer was passed
- if (!pFilePath || !*pFilePath)
- return 0;
-
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- // This will catch an error if the file manager fails to gather attributes
- NSError* fileError = nil;
-
- // Convert pFilePath to a NSString
- NSString* path = [[NSString stringWithUTF8String:pFilePath] stringByStandardizingPath];
-
- // Get all the attributes of the file
- NSDictionary* attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&fileError];
-
- // File manager could not gather attributes
- if (attributes == nil)
- {
- // Get the error message as a char*
- const char* errorMessage = [[fileError localizedDescription] UTF8String];
-
- // Print the error to the console
- Con::errorf("Platform::getFileSize: %s", errorMessage);
-
- [pool drain];
-
- // Return a 0 filesize
- return 0;
- }
-
- // Get the file size
- U32 fileLength = (U32)[[attributes objectForKey:NSFileSize] integerValue];
-
- // Clean up temp string
- [pool drain];
-
- // Return the result
- return fileLength;
- }
- //-----------------------------------------------------------------------------
- bool Platform::isSubDirectory(const char *pathParent, const char *pathSub)
- {
- // Concatenate the parent and sub directories
- char fullpath[MAX_MAC_PATH_LONG];
- dStrcpyl(fullpath, MAX_MAC_PATH_LONG, pathParent, "/", pathSub, NULL);
-
- // Return the isDirectory check
- return isDirectory((const char *)fullpath);
- }
- //-----------------------------------------------------------------------------
- // Check to see if the path has sub-directories
- // First pass at the OSX Cocoa implementation started with [NSFileManager subpathsAtPath]
- // That implementation was very simple, but the performance cost would be unacceptable
- // with large file structures. Since Torque 2D has many sub-directories, it makes more
- // sense to manually walk through directory contents using [NSFileManager enumeratorAtPath]
- // This approach is used in other functions where directories are dumped recursively
- bool Platform::hasSubDirectory(const char *path)
- {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- // Convert the path to a NSString
- NSString* filePath = [[NSString stringWithUTF8String:path] stringByStandardizingPath];
-
- // Check to make sure we are scannning a directory
- if (Platform::isDirectory(path))
- {
- // This will hold a list of subpaths
- NSDirectoryEnumerator* dirEnum = nil;
-
- // Get the default NSFileManager
- NSFileManager *fileManager = [NSFileManager defaultManager];
-
- // Initially, assume there is not a sub-directory
- BOOL foundDirectory = NO;
-
- // Enumerate the contes of the file path.
- dirEnum = [fileManager enumeratorAtPath:filePath];
-
- // This will walk the contents of the enumerated object
- NSString* entry;
-
- // Start walking through the enumerator
- while (entry = [dirEnum nextObject])
- {
- // Is the current entry a directory?
- if (Platform::isDirectory([entry UTF8String]))
- {
- // Success. Store the result and exit the loop
- foundDirectory = YES;
- break;
- }
- }
-
- // Clean up the temp string
- [pool drain];
-
- // Return the result of the scan
- return foundDirectory;
- }
- else
- {
- // Path was not a directory or did not exist
- Con::errorf("Platform::hasSubDirectory: path is not a directory");
-
- // Clean up the temp string
- [pool drain];
-
- // Return false
- return false;
- }
-
- // Clean up temp string
- [pool drain];
-
- // Something went wrong, so return false
- return false;
- }
- //-----------------------------------------------------------------------------
- bool Platform::dumpDirectories(const char *path, Vector<StringTableEntry> &directoryVector, S32 depth, bool noBasePath)
- {
- // Start profiling
- PROFILE_START(dumpDirectories);
-
- // Initialize the excluded directories so they are ignored
- ResourceManager->initExcludedDirectories();
-
- // Get the length of the path and pre-allocate the new path
- int len = dStrlen(path);
- char newpath[len+1];
-
- // Copy in the original path to the new path variable
- dSprintf(newpath, len, "%s", path );
-
- // cut off the trailing slash, if there is one
- if (newpath[len - 1] == '/')
- newpath[len - 1] = '\0';
-
- // Insert base path to follow what Windows does.
- if (!noBasePath )
- directoryVector.push_back(StringTable->insert(newpath));
-
- // Standard auto-release pool
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- // Recursively dump the directories
- recurseDumpDirectories(newpath, "", directoryVector, depth, noBasePath);
-
- // Drain the pool.
- [pool drain];
-
- // End profiling
- PROFILE_END();
-
- // Return the result of recursively dumping the directories
- return true;
- }
- //-----------------------------------------------------------------------------
- bool Platform::dumpPath(const char *path, Vector<Platform::FileInfo>& fileVector, S32 depth)
- {
- // Start profiling
- PROFILE_START(dumpPath);
-
- // Initialize the excluded directories so they are ignored
- ResourceManager->initExcludedDirectories();
-
- // Get the length of the path and pre-allocate the new path variable
- int len = dStrlen(path);
- char newpath[len+1];
-
- // Copy the source path into the new path
- dSprintf(newpath, len, "%s", path);
-
- // Cut off the trailing slash, if there is one
- if (newpath[len - 1] == '/')
- newpath[len - 1] = '\0';
-
- // Is the initial directory excluded?
- if ( Platform::isExcludedDirectory(path) )
- {
- // Yes, so stop profiling
- PROFILE_END();
-
- return false;
- }
-
- // Standard auto-release pool
- NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
-
- // Recursively dump the contents of the path
- recurseDumpPath(newpath, fileVector, depth);
-
- // Drain the pool.
- [pool drain];
-
- // Stop profiling
- PROFILE_END();
-
- return true;
- }
- //-----------------------------------------------------------------------------
- bool Platform::fileRename(const char *source, const char *dest)
- {
- // Make sure the pointers are valid
- if (source == NULL || dest == NULL)
- return false;
-
- // Get the shared file manager
- NSFileManager *manager = [NSFileManager defaultManager];
-
- // Conver the source and dest pointers to NSString
- NSString *nsource = [manager stringWithFileSystemRepresentation:source length:dStrlen(source)];
- NSString *ndest = [manager stringWithFileSystemRepresentation:dest length:dStrlen(dest)];
-
- // If the source file doesn't exist, error out
- if (![manager fileExistsAtPath:nsource])
- {
- Con::errorf("Platform::fileRename: no file exists at %s",source);
- return false;
- }
-
- // If the destination file already exists, throw a warning. We are about to overwrite it
- if ([manager fileExistsAtPath:ndest] )
- {
- Con::warnf("Platform::fileRename: Deleting files at path: %s", dest);
- }
-
- // Error catcher
- NSError* errorResponse = nil;
-
- // Rename the file
- bool result = [manager moveItemAtPath:nsource toPath:ndest error:&errorResponse];
-
- // Rename operation failed
- if (!result)
- Con::errorf("Platform::fileRename: %S", [[errorResponse localizedDescription] UTF8String] );
-
- // Return the result
- return result;
- }
- bool Platform::pathCopy(const char* source, const char* dest, bool nooverwrite)
- {
- // Standard auto-release pool
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- // Get the shared file manager
- NSFileManager *manager = [NSFileManager defaultManager];
-
- // Convert the char pointers to NSString
- NSString *nsource = [[NSString stringWithUTF8String:source] stringByStandardizingPath];
- NSString *ndest = [[NSString stringWithUTF8String:dest] stringByStandardizingPath];
- NSString *ndestFolder = [ndest stringByDeletingLastPathComponent];
-
- // If the source file does not exist, error out
- if (![manager fileExistsAtPath:nsource])
- {
- Con::errorf("Platform::pathCopy: no file exists at %s",source);
- return false;
- }
-
- // If the destination file already exists
- if ([manager fileExistsAtPath:ndest])
- {
- // If we can't overwrite, error out
- if(nooverwrite)
- {
- Con::errorf("Platform::pathCopy: file already exists at %s",dest);
- return false;
- }
-
- // Warn that we are about to overwrite the file
- Con::warnf("Deleting files at path: %s", dest);
-
- // Error catcher
- NSError* errorResponse = nil;
-
- // Try to delete the item
- bool deleted = [manager removeItemAtPath:ndest error:&errorResponse];
-
- // Failed to delete, so print the error and return
- if(!deleted)
- {
- Con::errorf("Platform::pathCopy failed at %s. %s", dest, [[errorResponse localizedDescription] UTF8String]);
- return false;
- }
- }
-
- if ([manager fileExistsAtPath:ndestFolder] == NO)
- {
- ndestFolder = [ndestFolder stringByAppendingString:@"/"]; // createpath requires a trailing slash
- Platform::createPath([ndestFolder UTF8String]);
- }
-
- NSError* errorResponse = nil;
-
- bool ret = [manager copyItemAtPath:nsource toPath:ndest error:&errorResponse];
-
- if (errorResponse != nil)
- {
- Con::errorf("Platform::pathCopy: %s", [[errorResponse localizedDescription] UTF8String]);
- }
-
- [pool drain];
-
- return ret;
- }
- StringTableEntry Platform::getUserHomeDirectory()
- {
- return StringTable->insert([[@"~/Documents" stringByStandardizingPath] UTF8String]);
- }
- const char* Platform::getUserDataDirectory()
- {
- // orb: Getting the path of the Application Support directory is only the first step.
- NSArray* paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
-
- if ([paths count] == 0)
- {
- // orb: This is a catastrophic failure - the system doesn't know where ~/Library/Application Support is!
- return NULL;
- }
-
- NSString* fullPath = [paths objectAtIndex:0];
-
- BOOL exists;
- BOOL isDir;
-
- NSFileManager* fileManager = [NSFileManager defaultManager];
-
- exists = [fileManager fileExistsAtPath:fullPath isDirectory:&isDir];
-
- if (!exists || !isDir)
- {
- // orb: This is probably an extremely rare occurence, but I have seen a few disk checks
- // converting directories into files before.
- if (exists)
- {
- NSError* errorResponse = nil;
-
- BOOL success = [fileManager removeItemAtPath:fullPath error:&errorResponse];
-
- if (!success)
- {
- Con::errorf("Platform::getUserDataDirectory: %s", [[errorResponse localizedDescription] UTF8String]);
- }
- }
-
- NSError* errorResponse = nil;
-
- BOOL success = [fileManager createDirectoryAtPath:fullPath withIntermediateDirectories:YES attributes:nil error:&errorResponse];
-
- if (!success)
- {
- Con::errorf("Platform::getUserDataDirectory: %s", [[errorResponse localizedDescription] UTF8String]);
- return NULL;
- }
- }
-
- // The directory exists and can be returned.
- return StringTable->insert([fullPath UTF8String]);
- }
- StringTableEntry Platform::osGetTemporaryDirectory()
- {
- NSString *tdir = NSTemporaryDirectory();
-
- const char *path = [tdir UTF8String];
-
- return StringTable->insert(path);
- }
|