1 //-----------------------------------------------------------------------------
2 // Copyright (c) 2012 GarageGames, LLC
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to
6 // deal in the Software without restriction, including without limitation the
7 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 // sell copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 //-----------------------------------------------------------------------------
23 #import <Cocoa/Cocoa.h>
34 #import "core/fileio.h"
35 #import "core/util/tVector.h"
36 #import "core/stringTable.h"
37 #import "core/strings/stringFunctions.h"
38 #import "console/console.h"
39 #import "platform/profiler.h"
40 #import "cinterface/cinterface.h"
41 #import "core/volume.h"
43 //TODO: file io still needs some work...
45 #define MAX_MAC_PATH_LONG 2048
47 bool dFileDelete(const char * name)
52 if (dStrlen(name) > MAX_MAC_PATH_LONG)
53 Con::warnf("dFileDelete: Filename length is pretty long...");
55 return(remove(name) == 0); // remove returns 0 on success
59 //-----------------------------------------------------------------------------
60 bool dFileTouch(const char *path)
65 // set file at path's modification and access times to now.
66 return( utimes( path, NULL) == 0); // utimes returns 0 on success.
68 //-----------------------------------------------------------------------------
69 bool dPathCopy(const char* source, const char* dest, bool nooverwrite)
71 if(source == NULL || dest == NULL)
75 NSFileManager *manager = [NSFileManager defaultManager];
77 NSString *nsource = [manager stringWithFileSystemRepresentation:source length:dStrlen(source)];
78 NSString *ndest = [manager stringWithFileSystemRepresentation:dest length:dStrlen(dest)];
79 NSString *ndestFolder = [ndest stringByDeletingLastPathComponent];
81 if(! [manager fileExistsAtPath:nsource])
83 Con::errorf("dPathCopy: no file exists at %s",source);
87 if( [manager fileExistsAtPath:ndest] )
91 Con::errorf("dPathCopy: file already exists at %s",dest);
94 Con::warnf("Deleting files at path: %s", dest);
95 if(![manager removeItemAtPath:ndest error:nil] || [manager fileExistsAtPath:ndest])
97 Con::errorf("Copy failed! Could not delete files at path: %s", dest);
102 if([manager fileExistsAtPath:ndestFolder] == NO)
104 ndestFolder = [ndestFolder stringByAppendingString:@"/"]; // createpath requires a trailing slash
105 Platform::createPath([ndestFolder UTF8String]);
108 bool ret = [manager copyItemAtPath:nsource toPath:ndest error:nil];
109 // n.b.: The "success" semantics don't guarantee a copy actually took place, so we'll verify
110 // because this is surprising behavior for a method called copy.
111 if( ![manager fileExistsAtPath:ndest] )
113 Con::warnf("The filemanager returned success, but the file was not copied. Something strange is happening");
121 //-----------------------------------------------------------------------------
123 bool dFileRename(const char *source, const char *dest)
125 if(source == NULL || dest == NULL)
129 NSFileManager *manager = [NSFileManager defaultManager];
131 NSString *nsource = [manager stringWithFileSystemRepresentation:source length:dStrlen(source)];
132 NSString *ndest = [manager stringWithFileSystemRepresentation:dest length:dStrlen(dest)];
134 if(! [manager fileExistsAtPath:nsource])
136 Con::errorf("dFileRename: no file exists at %s",source);
140 if( [manager fileExistsAtPath:ndest] )
142 Con::warnf("dFileRename: Deleting files at path: %s", dest);
145 bool ret = [manager moveItemAtPath:nsource toPath:ndest error:nil];
146 // n.b.: The "success" semantics don't guarantee a move actually took place, so we'll verify
147 // because this is surprising behavior for a method called rename.
149 if( ![manager fileExistsAtPath:ndest] )
151 Con::warnf("The filemanager returned success, but the file was not moved. Something strange is happening");
159 //-----------------------------------------------------------------------------
160 // Constructors & Destructor
161 //-----------------------------------------------------------------------------
163 //-----------------------------------------------------------------------------
164 // After construction, the currentStatus will be Closed and the capabilities
166 //-----------------------------------------------------------------------------
168 : currentStatus(Closed), capability(0)
173 //-----------------------------------------------------------------------------
174 // insert a copy constructor here... (currently disabled)
175 //-----------------------------------------------------------------------------
177 //-----------------------------------------------------------------------------
179 //-----------------------------------------------------------------------------
187 //-----------------------------------------------------------------------------
188 // Open a file in the mode specified by openMode (Read, Write, or ReadWrite).
189 // Truncate the file if the mode is either Write or ReadWrite and truncate is
192 // Sets capability appropriate to the openMode.
193 // Returns the currentStatus of the file.
194 //-----------------------------------------------------------------------------
195 File::FileStatus File::open(const char *filename, const AccessMode openMode)
197 if (dStrlen(filename) > MAX_MAC_PATH_LONG)
198 Con::warnf("File::open: Filename length is pretty long...");
200 // Close the file if it was already open...
201 if (currentStatus != Closed)
204 // create the appropriate type of file...
208 handle = (void *)fopen(filename, "rb"); // read only
211 handle = (void *)fopen(filename, "wb"); // write only
214 handle = (void *)fopen(filename, "ab+"); // write(append) and read
217 handle = (void *)fopen(filename, "ab"); // write(append) only
220 AssertFatal(false, "File::open: bad access mode");
223 // handle not created successfully
227 // successfully created file, so set the file capabilities...
231 capability = FileRead;
235 capability = FileWrite;
238 capability = FileRead | FileWrite;
241 AssertFatal(false, "File::open: bad access mode");
244 // must set the file status before setting the position.
247 if (openMode == ReadWrite)
251 return currentStatus;
254 //-----------------------------------------------------------------------------
255 // Get the current position of the file pointer.
256 //-----------------------------------------------------------------------------
257 U32 File::getPosition() const
259 AssertFatal(currentStatus != Closed , "File::getPosition: file closed");
260 AssertFatal(handle != NULL, "File::getPosition: invalid file handle");
262 return ftell((FILE*)handle);
265 //-----------------------------------------------------------------------------
266 // Set the position of the file pointer.
267 // Absolute and relative positioning is supported via the absolutePos
270 // If positioning absolutely, position MUST be positive - an IOError results if
271 // position is negative.
272 // Position can be negative if positioning relatively, however positioning
273 // before the start of the file is an IOError.
275 // Returns the currentStatus of the file.
276 //-----------------------------------------------------------------------------
277 File::FileStatus File::setPosition(S32 position, bool absolutePos)
279 AssertFatal(Closed != currentStatus, "File::setPosition: file closed");
280 AssertFatal(handle != NULL, "File::setPosition: invalid file handle");
282 if (currentStatus != Ok && currentStatus != EOS )
283 return currentStatus;
289 AssertFatal(0 <= position, "File::setPosition: negative absolute position");
290 // position beyond EOS is OK
291 fseek((FILE*)handle, position, SEEK_SET);
292 finalPos = ftell((FILE*)handle);
297 AssertFatal((getPosition() + position) >= 0, "File::setPosition: negative relative position");
298 // position beyond EOS is OK
299 fseek((FILE*)handle, position, SEEK_CUR);
300 finalPos = ftell((FILE*)handle);
303 // ftell returns -1 on error. set error status
304 if (0xffffffff == finalPos)
307 // success, at end of file
308 else if (finalPos >= getSize())
309 return currentStatus = EOS;
313 return currentStatus = Ok;
316 //-----------------------------------------------------------------------------
317 // Get the size of the file in bytes.
318 // It is an error to query the file size for a Closed file, or for one with an
320 //-----------------------------------------------------------------------------
321 U32 File::getSize() const
323 AssertWarn(Closed != currentStatus, "File::getSize: file closed");
324 AssertFatal(handle != NULL, "File::getSize: invalid file handle");
326 if (Ok == currentStatus || EOS == currentStatus)
328 struct stat statData;
330 if(fstat(fileno((FILE*)handle), &statData) != 0)
333 // return the size in bytes
334 return statData.st_size;
340 //-----------------------------------------------------------------------------
342 // It is an error to flush a read-only file.
343 // Returns the currentStatus of the file.
344 //-----------------------------------------------------------------------------
345 File::FileStatus File::flush()
347 AssertFatal(Closed != currentStatus, "File::flush: file closed");
348 AssertFatal(handle != NULL, "File::flush: invalid file handle");
349 AssertFatal(true == hasCapability(FileWrite), "File::flush: cannot flush a read-only file");
351 if (fflush((FILE*)handle) != 0)
354 return currentStatus = Ok;
357 //-----------------------------------------------------------------------------
360 // Returns the currentStatus
361 //-----------------------------------------------------------------------------
362 File::FileStatus File::close()
364 // check if it's already closed...
365 if (Closed == currentStatus)
366 return currentStatus;
368 // it's not, so close it...
371 if (fclose((FILE*)handle) != 0)
375 return currentStatus = Closed;
378 //-----------------------------------------------------------------------------
380 //-----------------------------------------------------------------------------
381 File::FileStatus File::getStatus() const
383 return currentStatus;
386 //-----------------------------------------------------------------------------
387 // Sets and returns the currentStatus when an error has been encountered.
388 //-----------------------------------------------------------------------------
389 File::FileStatus File::setStatus()
393 case EACCES: // permission denied
394 currentStatus = IOError;
396 case EBADF: // Bad File Pointer
397 case EINVAL: // Invalid argument
398 case ENOENT: // file not found
401 currentStatus = UnknownError;
404 return currentStatus;
407 //-----------------------------------------------------------------------------
408 // Sets and returns the currentStatus to status.
409 //-----------------------------------------------------------------------------
410 File::FileStatus File::setStatus(File::FileStatus status)
412 return currentStatus = status;
415 //-----------------------------------------------------------------------------
417 // The number of bytes to read is passed in size, the data is returned in src.
418 // The number of bytes read is available in bytesRead if a non-Null pointer is
420 //-----------------------------------------------------------------------------
421 File::FileStatus File::read(U32 size, char *dst, U32 *bytesRead)
423 AssertFatal(Closed != currentStatus, "File::read: file closed");
424 AssertFatal(handle != NULL, "File::read: invalid file handle");
425 AssertFatal(NULL != dst, "File::read: NULL destination pointer");
426 AssertFatal(true == hasCapability(FileRead), "File::read: file lacks capability");
427 AssertWarn(0 != size, "File::read: size of zero");
429 if (Ok != currentStatus || 0 == size)
430 return currentStatus;
433 U32 nBytes = fread(dst, 1, size, (FILE*)handle);
435 // did we hit the end of the stream?
439 // if bytesRead is a valid pointer, send number of bytes read there.
443 // successfully read size bytes
444 return currentStatus;
447 //-----------------------------------------------------------------------------
449 // The number of bytes to write is passed in size, the data is passed in src.
450 // The number of bytes written is available in bytesWritten if a non-Null
451 // pointer is provided.
452 //-----------------------------------------------------------------------------
453 File::FileStatus File::write(U32 size, const char *src, U32 *bytesWritten)
455 AssertFatal(Closed != currentStatus, "File::write: file closed");
456 AssertFatal(handle != NULL, "File::write: invalid file handle");
457 AssertFatal(NULL != src, "File::write: NULL source pointer");
458 AssertFatal(true == hasCapability(FileWrite), "File::write: file lacks capability");
459 AssertWarn(0 != size, "File::write: size of zero");
461 if ((Ok != currentStatus && EOS != currentStatus) || 0 == size)
462 return currentStatus;
464 // write bytes to the stream
465 U32 nBytes = fwrite(src, 1, size,(FILE*)handle);
467 // if we couldn't write everything, we've got a problem. set error status.
471 // if bytesWritten is a valid pointer, put number of bytes read there.
473 *bytesWritten = nBytes;
475 // return current File status, whether good or ill.
476 return currentStatus;
480 //-----------------------------------------------------------------------------
482 //-----------------------------------------------------------------------------
483 bool File::hasCapability(Capability cap) const
485 return (0 != (U32(cap) & capability));
488 //-----------------------------------------------------------------------------
489 S32 Platform::compareFileTimes(const FileTime &a, const FileTime &b)
499 //-----------------------------------------------------------------------------
500 // either time param COULD be null.
501 //-----------------------------------------------------------------------------
502 bool Platform::getFileTimes(const char *path, FileTime *createTime, FileTime *modifyTime)
504 // MacOSX is NOT guaranteed to be running off a HFS volume,
505 // and UNIX does not keep a record of a file's creation time anywhere.
506 // So instead of creation time we return changed time,
507 // just like the Linux platform impl does.
512 struct stat statData;
514 if (stat(path, &statData) == -1)
518 *createTime = statData.st_ctime;
521 *modifyTime = statData.st_mtime;
527 //-----------------------------------------------------------------------------
528 bool Platform::createPath(const char *file)
530 // if the path exists, we're done.
531 struct stat statData;
532 if( stat(file, &statData) == 0 )
534 return true; // exists, rejoice.
537 Con::warnf( "creating path %s", file );
539 // get the parent path.
540 // we're not using basename because it's not thread safe.
541 U32 len = dStrlen(file);
543 bool isDirPath = false;
545 dStrncpy(parent,file,len);
547 if(parent[len - 1] == '/')
549 parent[len - 1] = '\0'; // cut off the trailing slash, if there is one
550 isDirPath = true; // we got a trailing slash, so file is a directory.
553 // recusively create the parent path.
554 // only recurse if newpath has a slash that isn't a leading slash.
555 char *slash = dStrrchr(parent,'/');
556 if( slash && slash != parent)
558 // snip the path just after the last slash.
560 // recusively create the parent path. fail if parent path creation failed.
561 if(!Platform::createPath(parent))
565 // create *file if it is a directory path.
568 // try to create the directory
569 if( mkdir(file, 0777) != 0) // app may reside in global apps dir, and so must be writable to all.
577 //-----------------------------------------------------------------------------
578 bool Platform::cdFileExists(const char *filePath, const char *volumeName, S32 serialNum)
583 #pragma mark ---- Directories ----
584 //-----------------------------------------------------------------------------
585 StringTableEntry Platform::getCurrentDirectory()
587 // get the current directory, the one that would be opened if we did a fopen(".")
588 char* cwd = getcwd(NULL, 0);
589 StringTableEntry ret = StringTable->insert(cwd);
594 //-----------------------------------------------------------------------------
595 bool Platform::setCurrentDirectory(StringTableEntry newDir)
597 return (chdir(newDir) == 0);
600 //-----------------------------------------------------------------------------
602 // helper func for getWorkingDirectory
603 bool isMainDotCsPresent(NSString* dir)
605 return [[NSFileManager defaultManager] fileExistsAtPath:[dir stringByAppendingPathComponent:@"main.cs"]] == YES;
608 //-----------------------------------------------------------------------------
609 /// Finds and sets the current working directory.
610 /// Torque tries to automatically detect whether you have placed the game files
611 /// inside or outside the application's bundle. It checks for the presence of
612 /// the file 'main.cs'. If it finds it, Torque will assume that the other game
613 /// files are there too. If Torque does not see 'main.cs' inside its bundle, it
614 /// will assume the files are outside the bundle.
615 /// Since you probably don't want to copy the game files into the app every time
616 /// you build, you will want to leave them outside the bundle for development.
618 /// Placing all content inside the application bundle gives a much better user
619 /// experience when you distribute your app.
620 StringTableEntry Platform::getExecutablePath()
622 static const char* cwd = NULL;
624 // this isn't actually being used due to some static constructors at bundle load time
625 // calling this method (before there is a chance to set it)
626 // for instance, FMOD sound provider (this should be fixed in FMOD as it is with windows)
627 if (!cwd && torque_getexecutablepath())
629 // we're in a plugin using the cinterface
630 cwd = torque_getexecutablepath();
635 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
637 //first check the cwd for main.cs
638 static char buf[4096];
639 NSString* currentDir = [[NSString alloc ] initWithUTF8String:getcwd(buf,(4096 * sizeof(char))) ];
641 if (isMainDotCsPresent(currentDir))
645 [currentDir release];
649 NSString* string = [[NSBundle mainBundle] pathForResource:@"main" ofType:@"cs"];
651 string = [[NSBundle mainBundle] bundlePath];
653 string = [string stringByDeletingLastPathComponent];
654 AssertISV(isMainDotCsPresent(string), "Platform::getExecutablePath - Failed to find main.cs!");
655 cwd = dStrdup([string UTF8String]);
658 [currentDir release];
664 //-----------------------------------------------------------------------------
665 StringTableEntry Platform::getExecutableName()
667 static const char* name = NULL;
669 name = [[[[NSBundle mainBundle] bundlePath] lastPathComponent] UTF8String];
674 //-----------------------------------------------------------------------------
675 bool Platform::isFile(const char *path)
680 // make sure we can stat the file
681 struct stat statData;
682 if( stat(path, &statData) < 0 )
684 // Since file does not exist on disk see if it exists in a zip file loaded
685 return Torque::FS::IsFile(path);
688 // now see if it's a regular file
689 if( (statData.st_mode & S_IFMT) == S_IFREG)
696 //-----------------------------------------------------------------------------
697 bool Platform::isDirectory(const char *path)
702 // make sure we can stat the file
703 struct stat statData;
704 if( stat(path, &statData) < 0 )
707 // now see if it's a directory
708 if( (statData.st_mode & S_IFMT) == S_IFDIR)
715 S32 Platform::getFileSize(const char* pFilePath)
717 if (!pFilePath || !*pFilePath)
720 struct stat statData;
721 if( stat(pFilePath, &statData) < 0 )
724 // and return it's size in bytes
725 return (S32)statData.st_size;
729 //-----------------------------------------------------------------------------
730 bool Platform::isSubDirectory(const char *pathParent, const char *pathSub)
732 char fullpath[MAX_MAC_PATH_LONG];
733 dStrcpyl(fullpath, MAX_MAC_PATH_LONG, pathParent, "/", pathSub, NULL);
734 return isDirectory((const char *)fullpath);
737 //-----------------------------------------------------------------------------
738 // utility for platform::hasSubDirectory() and platform::dumpDirectories()
739 // ensures that the entry is a directory, and isnt on the ignore lists.
740 inline bool isGoodDirectory(dirent* entry)
742 return (entry->d_type == DT_DIR // is a dir
743 && dStrcmp(entry->d_name,".") != 0 // not here
744 && dStrcmp(entry->d_name,"..") != 0 // not parent
745 && !Platform::isExcludedDirectory(entry->d_name)); // not excluded
748 //-----------------------------------------------------------------------------
749 bool Platform::hasSubDirectory(const char *path)
756 return false; // we got a bad path, so no, it has no subdirectory.
758 while( (entry = readdir(dir)) )
760 if(isGoodDirectory(entry) )
763 return true; // we have a subdirectory, that isnt on the exclude list.
768 return false; // either this dir had no subdirectories, or they were all on the exclude list.
771 bool Platform::fileDelete(const char * name)
773 return dFileDelete(name);
776 static bool recurseDumpDirectories(const char *basePath, const char *subPath, Vector<StringTableEntry> &directoryVector, S32 currentDepth, S32 recurseDepth, bool noBasePath)
782 dsize_t trLen = basePath ? dStrlen(basePath) : 0;
783 dsize_t subtrLen = subPath ? dStrlen(subPath) : 0;
784 char trail = trLen > 0 ? basePath[trLen - 1] : '\0';
785 char subTrail = subtrLen > 0 ? subPath[subtrLen - 1] : '\0';
789 if (subPath && (dStrncmp(subPath, "", 1) != 0))
792 dSprintf(Path, 1024, "%s%s", basePath, subPath);
794 dSprintf(Path, 1024, "%s%s/", basePath, subPath);
797 dSprintf(Path, 1024, "%s", basePath);
801 if (subPath && (dStrncmp(subPath, "", 1) != 0))
804 dSprintf(Path, 1024, "%s%s", basePath, subPath);
806 dSprintf(Path, 1024, "%s%s/", basePath, subPath);
809 dSprintf(Path, 1024, "%s/", basePath);
816 //////////////////////////////////////////////////////////////////////////
817 // add path to our return list ( provided it is valid )
818 //////////////////////////////////////////////////////////////////////////
819 if (!Platform::isExcludedDirectory(subPath))
823 // We have a path and it's not an empty string or an excluded directory
824 if ( (subPath && (dStrncmp (subPath, "", 1) != 0)) )
825 directoryVector.push_back(StringTable->insert(subPath));
829 if ( (subPath && (dStrncmp(subPath, "", 1) != 0)) )
832 dMemset(szPath, 0, 1024);
835 if ((basePath[dStrlen(basePath) - 1]) != '/')
836 dSprintf(szPath, 1024, "%s%s", basePath, &subPath[1]);
838 dSprintf(szPath, 1024, "%s%s", basePath, subPath);
842 if ((basePath[dStrlen(basePath) - 1]) != '/')
843 dSprintf(szPath, 1024, "%s%s", basePath, subPath);
845 dSprintf(szPath, 1024, "%s/%s", basePath, subPath);
848 directoryVector.push_back(StringTable->insert(szPath));
851 directoryVector.push_back(StringTable->insert(basePath));
854 //////////////////////////////////////////////////////////////////////////
855 // Iterate through and grab valid directories
856 //////////////////////////////////////////////////////////////////////////
858 while (d = readdir(dip))
862 if (d->d_type == DT_UNKNOWN)
865 if ((Path[dStrlen(Path) - 1] == '/'))
866 dSprintf(child, 1024, "%s%s", Path, d->d_name);
868 dSprintf(child, 1024, "%s/%s", Path, d->d_name);
869 isDir = Platform::isDirectory (child);
871 else if (d->d_type & DT_DIR)
876 if (dStrcmp(d->d_name, ".") == 0 ||
877 dStrcmp(d->d_name, "..") == 0)
879 if (Platform::isExcludedDirectory(d->d_name))
881 if ( (subPath && (dStrncmp(subPath, "", 1) != 0)) )
884 if ((subPath[dStrlen(subPath) - 1] == '/'))
885 dSprintf(child, 1024, "%s%s", subPath, d->d_name);
887 dSprintf(child, 1024, "%s/%s", subPath, d->d_name);
888 if (currentDepth < recurseDepth || recurseDepth == -1 )
889 recurseDumpDirectories(basePath, child, directoryVector,
890 currentDepth + 1, recurseDepth,
896 if ( (basePath[dStrlen(basePath) - 1]) == '/')
897 dStrcpy (child, d->d_name);
899 dSprintf(child, 1024, "/%s", d->d_name);
900 if (currentDepth < recurseDepth || recurseDepth == -1)
901 recurseDumpDirectories(basePath, child, directoryVector,
902 currentDepth + 1, recurseDepth,
911 //-----------------------------------------------------------------------------
912 bool Platform::dumpDirectories(const char *path, Vector<StringTableEntry> &directoryVector, S32 depth, bool noBasePath)
914 bool retVal = recurseDumpDirectories(path, "", directoryVector, 0, depth, noBasePath);
915 clearExcludedDirectories();
919 //-----------------------------------------------------------------------------
920 static bool recurseDumpPath(const char* curPath, Vector<Platform::FileInfo>& fileVector, U32 depth)
926 dir = opendir(curPath);
930 // look inside the current directory
931 while( (entry = readdir(dir)) )
933 // construct the full file path. we need this to get the file size and to recurse
934 U32 len = dStrlen(curPath) + entry->d_namlen + 2;
936 dSprintf( pathbuf, len, "%s/%s", curPath, entry->d_name);
939 // ok, deal with directories and files seperately.
940 if( entry->d_type == DT_DIR )
945 // filter out dirs we dont want.
946 if( !isGoodDirectory(entry) )
949 // recurse into the dir
950 recurseDumpPath( pathbuf, fileVector, depth-1);
954 //add the file entry to the list
955 // unlike recurseDumpDirectories(), we need to return more complex info here.
956 U32 fileSize = Platform::getFileSize(pathbuf);
957 fileVector.increment();
958 Platform::FileInfo& rInfo = fileVector.last();
959 rInfo.pFullPath = StringTable->insert(curPath);
960 rInfo.pFileName = StringTable->insert(entry->d_name);
961 rInfo.fileSize = fileSize;
970 //-----------------------------------------------------------------------------
971 bool Platform::dumpPath(const char *path, Vector<Platform::FileInfo>& fileVector, S32 depth)
973 PROFILE_START(dumpPath);
974 int len = dStrlen(path);
977 dStrncpy(newpath,path,len);
978 newpath[len] = '\0'; // null terminate
979 if(newpath[len - 1] == '/')
980 newpath[len - 1] = '\0'; // cut off the trailing slash, if there is one
982 bool ret = recurseDumpPath( newpath, fileVector, depth);
988 // TODO: implement stringToFileTime()
989 bool Platform::stringToFileTime(const char * string, FileTime * time) { return false;}
990 // TODO: implement fileTimeToString()
991 bool Platform::fileTimeToString(FileTime * time, char * string, U32 strLen) { return false;}
993 //-----------------------------------------------------------------------------
994 #if defined(TORQUE_DEBUG)
995 ConsoleFunction(testHasSubdir,void,2,2,"tests platform::hasSubDirectory") {
996 Con::printf("testing %s",(const char*)argv[1]);
997 Platform::addExcludedDirectory(".svn");
998 if(Platform::hasSubDirectory(argv[1]))
999 Con::printf(" has subdir");
1001 Con::printf(" does not have subdir");
1004 ConsoleFunction(testDumpDirectories,void,4,4,"testDumpDirectories('path', int depth, bool noBasePath)") {
1005 Vector<StringTableEntry> paths;
1006 const S32 depth = dAtoi(argv[2]);
1007 const bool noBasePath = dAtob(argv[3]);
1009 Platform::addExcludedDirectory(".svn");
1011 Platform::dumpDirectories(argv[1], paths, depth, noBasePath);
1013 Con::printf("Dumping directories starting from %s with depth %i", (const char*)argv[1],depth);
1015 for(Vector<StringTableEntry>::iterator itr = paths.begin(); itr != paths.end(); itr++) {
1021 ConsoleFunction(testDumpPaths, void, 3, 3, "testDumpPaths('path', int depth)")
1023 Vector<Platform::FileInfo> files;
1024 S32 depth = dAtoi(argv[2]);
1026 Platform::addExcludedDirectory(".svn");
1028 Platform::dumpPath(argv[1], files, depth);
1030 for(Vector<Platform::FileInfo>::iterator itr = files.begin(); itr != files.end(); itr++) {
1031 Con::printf("%s/%s",itr->pFullPath, itr->pFileName);
1035 //-----------------------------------------------------------------------------
1036 ConsoleFunction(testFileTouch, bool , 2,2, "testFileTouch('path')")
1038 return dFileTouch(argv[1]);
1041 ConsoleFunction(testGetFileTimes, bool, 2,2, "testGetFileTimes('path')")
1043 FileTime create, modify;
1044 bool ok = Platform::getFileTimes(argv[1], &create, &modify);
1045 Con::printf("%s Platform::getFileTimes %i, %i", ok ? "+OK" : "-FAIL", create, modify);