winFileio.cpp

Engine/source/platformWin32/winFileio.cpp

More...

Public Functions

bool
dFileDelete(const char * name)
bool
dFileRename(const char * oldName, const char * newName)
bool
dFileTouch(const char * name)
bool
dPathCopy(const char * fromName, const char * toName, bool nooverwrite)
bool
recurseDumpDirectories(const char * basePath, const char * subPath, Vector< StringTableEntry > & directoryVector, S32 currentDepth, S32 recurseDepth, bool noBasePath)
bool
recurseDumpPath(const char * path, const char * pattern, Vector< Platform::FileInfo > & fileVector, S32 recurseDepth)

Detailed Description

Public Functions

dFileDelete(const char * name)

dFileRename(const char * oldName, const char * newName)

dFileTouch(const char * name)

dPathCopy(const char * fromName, const char * toName, bool nooverwrite)

osGetTemporaryDirectory()

recurseDumpDirectories(const char * basePath, const char * subPath, Vector< StringTableEntry > & directoryVector, S32 currentDepth, S32 recurseDepth, bool noBasePath)

recurseDumpPath(const char * path, const char * pattern, Vector< Platform::FileInfo > & fileVector, S32 recurseDepth)

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24#include "core/strings/stringFunctions.h"
  25#include "platformWin32/platformWin32.h"
  26#include "core/fileio.h"
  27#include "core/util/tVector.h"
  28#include "core/stringTable.h"
  29#include "console/console.h"
  30#include "core/strings/unicode.h"
  31#include "util/tempAlloc.h"
  32#include "core/util/safeDelete.h"
  33#include "core/volume.h"
  34
  35// Microsoft VC++ has this POSIX header in the wrong directory
  36#if defined(TORQUE_COMPILER_VISUALC)
  37#include <sys/utime.h>
  38#elif defined (TORQUE_COMPILER_GCC)
  39#include <time.h>
  40#include <sys/utime.h>
  41#else
  42#include <utime.h>
  43#endif
  44
  45StringTableEntry Platform::createPlatformFriendlyFilename( const char *filename )
  46{
  47   return StringTable->insert( filename );
  48}
  49
  50//-----------------------------------------------------------------------------
  51bool dFileDelete(const char * name)
  52{
  53   AssertFatal( name != NULL, "dFileDelete - NULL file name" );
  54
  55   TempAlloc< TCHAR> buf( dStrlen( name ) + 1 );
  56
  57#ifdef UNICODE
  58   convertUTF8toUTF16N( name, buf, buf.size );
  59#else
  60   dStrcpy( buf, name, buf.size );
  61#endif
  62
  63   backslash( buf );
  64   if( Platform::isFile( name ) )
  65      return DeleteFile( buf );
  66   else
  67      return RemoveDirectory( buf );
  68}
  69
  70bool Platform::fileDelete(const char * name)
  71{
  72   if (!name || (dStrlen(name) >= MAX_PATH))
  73      return(false);
  74   //return(::DeleteFile(name));
  75   if (Platform::isFile(name))
  76      return(remove(name) == 0);
  77   else
  78      return ::RemoveDirectoryA(name) != 0;
  79}
  80
  81bool dFileRename(const char *oldName, const char *newName)
  82{
  83   AssertFatal( oldName != NULL && newName != NULL, "dFileRename - NULL file name" );
  84
  85   TempAlloc< TCHAR> oldf( dStrlen( oldName ) + 1 );
  86   TempAlloc< TCHAR> newf( dStrlen( newName ) + 1 );
  87
  88#ifdef UNICODE
  89   convertUTF8toUTF16N( oldName, oldf, oldf.size );
  90   convertUTF8toUTF16N( newName, newf, newf.size );
  91#else
  92   dStrcpy(oldf, oldName, oldf.size);
  93   dStrcpy(newf, newName, newf.size);
  94#endif
  95   backslash(oldf);
  96   backslash(newf);
  97
  98   return MoveFile( oldf, newf );
  99}
 100
 101bool dFileTouch(const char * name)
 102{
 103   AssertFatal( name != NULL, "dFileTouch - NULL file name" );
 104
 105   TempAlloc< TCHAR> buf( dStrlen( name ) + 1 );
 106
 107#ifdef UNICODE
 108   convertUTF8toUTF16N( name, buf, buf.size );
 109#else
 110   dStrcpy( buf, name, buf.size );
 111#endif
 112
 113   backslash( buf );
 114   FILETIME ftime;
 115   GetSystemTimeAsFileTime( &ftime );
 116   HANDLE handle = CreateFile( buf, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
 117      NULL, OPEN_EXISTING, 0, NULL );
 118   if( handle == INVALID_HANDLE_VALUE )
 119      return false;
 120   bool result = SetFileTime( handle, NULL, NULL, &ftime );
 121   CloseHandle( handle );
 122
 123   return result;
 124};
 125
 126bool dPathCopy(const char *fromName, const char *toName, bool nooverwrite)
 127{
 128   AssertFatal( fromName != NULL && toName != NULL, "dPathCopy - NULL file name" );
 129
 130   TempAlloc< TCHAR> from( dStrlen( fromName ) + 1 );
 131   TempAlloc< TCHAR> to( dStrlen( toName ) + 1 );
 132
 133#ifdef UNICODE
 134   convertUTF8toUTF16N( fromName, from, from.size );
 135   convertUTF8toUTF16N( toName, to, to.size );
 136#else
 137   dStrcpy( from, fromName, from.size );
 138   dStrcpy( to, toName, to.size );
 139#endif
 140
 141   backslash( from );
 142   backslash( to );
 143
 144   // Copy File
 145   if (Platform::isFile(fromName))
 146      return CopyFile( from, to, nooverwrite );
 147   // Copy Path
 148   else if (Platform::isDirectory(fromName))
 149   {
 150      // If the destination path exists and we don't want to overwrite, return.
 151      if ((Platform::isDirectory(toName) || Platform::isFile(toName)) && nooverwrite)
 152         return false;
 153
 154      Vector<StringTableEntry> directoryInfo;
 155      Platform::dumpDirectories(fromName, directoryInfo, -1);
 156
 157      Vector<Platform::FileInfo> fileInfo;
 158      Platform::dumpPath(fromName, fileInfo);
 159
 160      Platform::clearExcludedDirectories();
 161
 162      TempAlloc< char> tempBuf( to.size * 3 + MAX_PATH * 3 );
 163
 164      // Create all the directories.
 165      for (S32 i = 0; i < directoryInfo.size(); i++)
 166      {
 167         const char* fromDir = directoryInfo[i];
 168
 169         char* toDir = tempBuf;
 170         Platform::makeFullPathName(fromDir + dStrlen(fromName) + (dStricmp(fromDir, fromName) ? 1 : 0), tempBuf, tempBuf.size, toName);
 171         if(*(toDir + dStrlen(toDir) - 1) != '/')
 172            dStrcat(toDir, "/", tempBuf.size);
 173         forwardslash(toDir);
 174
 175         if (!Platform::createPath(toDir))
 176         {
 177            //TODO: New directory should be deleted here.
 178            return false;
 179         }
 180      }
 181
 182      TempAlloc< char> tempBuf1( from.size * 3 + MAX_PATH * 3 );
 183#ifdef UNICODE
 184      TempAlloc< WCHAR> wtempBuf( tempBuf.size / 3 );
 185      TempAlloc< WCHAR> wtempBuf1( tempBuf1.size / 3 );
 186#endif
 187
 188      for (S32 i = 0; i < fileInfo.size(); i++)
 189      {
 190         char* fromFile = tempBuf1;
 191         dSprintf( tempBuf1, tempBuf1.size, "%s/%s", fileInfo[i].pFullPath, fileInfo[i].pFileName);
 192
 193         char* toFile = tempBuf;
 194         Platform::makeFullPathName(fileInfo[i].pFullPath + dStrlen(fromName) + (dStricmp(fileInfo[i].pFullPath, fromName) ? 1 : 0), tempBuf, tempBuf.size, toName);
 195         dStrcat(toFile, "/", tempBuf.size);
 196         dStrcat(toFile, fileInfo[i].pFileName, tempBuf.size);
 197
 198         backslash(fromFile);
 199         backslash(toFile);
 200         
 201#ifdef UNICODE
 202         convertUTF8toUTF16N( tempBuf, wtempBuf, wtempBuf.size );
 203         convertUTF8toUTF16N( tempBuf1, wtempBuf1, wtempBuf1.size );
 204         WCHAR* f = wtempBuf1;
 205         WCHAR* t = wtempBuf;
 206#else
 207         char *f = (char*)fromFile;
 208         char *t = (char*)toFile;
 209#endif
 210
 211         if (!::CopyFile(f, t, nooverwrite))
 212         {
 213            // New directory should be deleted here.
 214            return false;
 215         }
 216
 217      }
 218
 219      return true;
 220   }
 221
 222   return false;
 223}
 224
 225//-----------------------------------------------------------------------------
 226// Constructors & Destructor
 227//-----------------------------------------------------------------------------
 228
 229//-----------------------------------------------------------------------------
 230// After construction, the currentStatus will be Closed and the capabilities
 231// will be 0.
 232//-----------------------------------------------------------------------------
 233File::File()
 234: currentStatus(Closed), capability(0)
 235{
 236    AssertFatal(sizeof(HANDLE) == sizeof(void *), "File::File: cannot cast void* to HANDLE");
 237
 238    handle = (void *)INVALID_HANDLE_VALUE;
 239}
 240
 241//-----------------------------------------------------------------------------
 242// insert a copy constructor here... (currently disabled)
 243//-----------------------------------------------------------------------------
 244
 245//-----------------------------------------------------------------------------
 246// Destructor
 247//-----------------------------------------------------------------------------
 248File::~File()
 249{
 250    close();
 251    handle = (void *)INVALID_HANDLE_VALUE;
 252}
 253
 254
 255//-----------------------------------------------------------------------------
 256// Open a file in the mode specified by openMode (Read, Write, or ReadWrite).
 257// Truncate the file if the mode is either Write or ReadWrite and truncate is
 258// true.
 259//
 260// Sets capability appropriate to the openMode.
 261// Returns the currentStatus of the file.
 262//-----------------------------------------------------------------------------
 263File::FileStatus File::open(const char *filename, const AccessMode openMode)
 264{
 265   AssertFatal(NULL != filename, "File::open: NULL fname");
 266   AssertWarn(INVALID_HANDLE_VALUE == (HANDLE)handle, "File::open: handle already valid");
 267
 268   TempAlloc< TCHAR> fname( dStrlen( filename ) + 1 );
 269
 270#ifdef UNICODE
 271   convertUTF8toUTF16N( filename, fname, fname.size );
 272#else
 273   dStrcpy(fname, filename, fname.size);
 274#endif
 275   backslash( fname );
 276
 277   // Close the file if it was already open...
 278   if (Closed != currentStatus)
 279      close();
 280
 281   // create the appropriate type of file...
 282   switch (openMode)
 283   {
 284   case Read:
 285      handle = (void *)CreateFile(fname,
 286         GENERIC_READ,
 287         FILE_SHARE_READ,
 288         NULL,
 289         OPEN_EXISTING,
 290         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
 291         NULL);
 292      break;
 293   case Write:
 294      handle = (void *)CreateFile(fname,
 295         GENERIC_WRITE,
 296         0,
 297         NULL,
 298         CREATE_ALWAYS,
 299         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
 300         NULL);
 301      break;
 302   case ReadWrite:
 303      handle = (void *)CreateFile(fname,
 304         GENERIC_WRITE | GENERIC_READ,
 305         0,
 306         NULL,
 307         OPEN_ALWAYS,
 308         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
 309         NULL);
 310      break;
 311   case WriteAppend:
 312      handle = (void *)CreateFile(fname,
 313         GENERIC_WRITE,
 314         0,
 315         NULL,
 316         OPEN_ALWAYS,
 317         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
 318         NULL);
 319      break;
 320
 321   default:
 322      AssertFatal(false, "File::open: bad access mode");    // impossible
 323   }
 324
 325   if (INVALID_HANDLE_VALUE == (HANDLE)handle)                // handle not created successfully
 326   {
 327      return setStatus();
 328   }
 329   else
 330   {
 331      // successfully created file, so set the file capabilities...
 332      switch (openMode)
 333      {
 334      case Read:
 335         capability = U32(FileRead);
 336         break;
 337      case Write:
 338      case WriteAppend:
 339         capability = U32(FileWrite);
 340         break;
 341      case ReadWrite:
 342         capability = U32(FileRead)  |
 343            U32(FileWrite);
 344         break;
 345      default:
 346         AssertFatal(false, "File::open: bad access mode");
 347      }
 348      return currentStatus = Ok;                                // success!
 349   }
 350}
 351
 352//-----------------------------------------------------------------------------
 353// Get the current position of the file pointer.
 354//-----------------------------------------------------------------------------
 355U32 File::getPosition() const
 356{
 357    AssertFatal(Closed != currentStatus, "File::getPosition: file closed");
 358    AssertFatal(INVALID_HANDLE_VALUE != (HANDLE)handle, "File::getPosition: invalid file handle");
 359
 360    return SetFilePointer((HANDLE)handle,
 361                          0,                                    // how far to move
 362                          NULL,                                    // pointer to high word
 363                          FILE_CURRENT);                        // from what point
 364}
 365
 366//-----------------------------------------------------------------------------
 367// Set the position of the file pointer.
 368// Absolute and relative positioning is supported via the absolutePos
 369// parameter.
 370//
 371// If positioning absolutely, position MUST be positive - an IOError results if
 372// position is negative.
 373// Position can be negative if positioning relatively, however positioning
 374// before the start of the file is an IOError.
 375//
 376// Returns the currentStatus of the file.
 377//-----------------------------------------------------------------------------
 378File::FileStatus File::setPosition(S32 position, bool absolutePos)
 379{
 380    AssertFatal(Closed != currentStatus, "File::setPosition: file closed");
 381    AssertFatal(INVALID_HANDLE_VALUE != (HANDLE)handle, "File::setPosition: invalid file handle");
 382
 383    if (Ok != currentStatus && EOS != currentStatus)
 384        return currentStatus;
 385
 386    U32 finalPos;
 387    if (absolutePos)
 388    {
 389        AssertFatal(0 <= position, "File::setPosition: negative absolute position");
 390
 391        // position beyond EOS is OK
 392        finalPos = SetFilePointer((HANDLE)handle,
 393                                  position,
 394                                  NULL,
 395                                  FILE_BEGIN);
 396    }
 397    else
 398    {
 399       AssertFatal((getPosition() >= (U32)abs(position) && 0 > position) || 0 <= position, "File::setPosition: negative relative position");
 400
 401        // position beyond EOS is OK
 402        finalPos = SetFilePointer((HANDLE)handle,
 403                                  position,
 404                                  NULL,
 405                                  FILE_CURRENT);
 406    }
 407
 408    if (0xffffffff == finalPos)
 409        return setStatus();                                        // unsuccessful
 410    else if (finalPos >= getSize())
 411        return currentStatus = EOS;                                // success, at end of file
 412    else
 413        return currentStatus = Ok;                                // success!
 414}
 415
 416//-----------------------------------------------------------------------------
 417// Get the size of the file in bytes.
 418// It is an error to query the file size for a Closed file, or for one with an
 419// error status.
 420//-----------------------------------------------------------------------------
 421U32 File::getSize() const
 422{
 423    AssertWarn(Closed != currentStatus, "File::getSize: file closed");
 424    AssertFatal(INVALID_HANDLE_VALUE != (HANDLE)handle, "File::getSize: invalid file handle");
 425
 426    if (Ok == currentStatus || EOS == currentStatus)
 427    {
 428        DWORD high;
 429        return GetFileSize((HANDLE)handle, &high);                // success!
 430    }
 431    else
 432        return 0;                                                // unsuccessful
 433}
 434
 435//-----------------------------------------------------------------------------
 436// Flush the file.
 437// It is an error to flush a read-only file.
 438// Returns the currentStatus of the file.
 439//-----------------------------------------------------------------------------
 440File::FileStatus File::flush()
 441{
 442    AssertFatal(Closed != currentStatus, "File::flush: file closed");
 443    AssertFatal(INVALID_HANDLE_VALUE != (HANDLE)handle, "File::flush: invalid file handle");
 444    AssertFatal(true == hasCapability(FileWrite), "File::flush: cannot flush a read-only file");
 445
 446    if (0 != FlushFileBuffers((HANDLE)handle))
 447        return setStatus();                                        // unsuccessful
 448    else
 449        return currentStatus = Ok;                                // success!
 450}
 451
 452//-----------------------------------------------------------------------------
 453// Close the File.
 454//
 455// Returns the currentStatus
 456//-----------------------------------------------------------------------------
 457File::FileStatus File::close()
 458{
 459    // check if it's already closed...
 460    if (Closed == currentStatus)
 461        return currentStatus;
 462
 463    // it's not, so close it...
 464    if (INVALID_HANDLE_VALUE != (HANDLE)handle)
 465    {
 466        if (0 == CloseHandle((HANDLE)handle))
 467            return setStatus();                                    // unsuccessful
 468    }
 469    handle = (void *)INVALID_HANDLE_VALUE;
 470    return currentStatus = Closed;
 471}
 472
 473//-----------------------------------------------------------------------------
 474// Self-explanatory.
 475//-----------------------------------------------------------------------------
 476File::FileStatus File::getStatus() const
 477{
 478    return currentStatus;
 479}
 480
 481//-----------------------------------------------------------------------------
 482// Sets and returns the currentStatus when an error has been encountered.
 483//-----------------------------------------------------------------------------
 484File::FileStatus File::setStatus()
 485{
 486    switch (GetLastError())
 487    {
 488    case ERROR_INVALID_HANDLE:
 489    case ERROR_INVALID_ACCESS:
 490    case ERROR_TOO_MANY_OPEN_FILES:
 491    case ERROR_FILE_NOT_FOUND:
 492    case ERROR_SHARING_VIOLATION:
 493    case ERROR_HANDLE_DISK_FULL:
 494          return currentStatus = IOError;
 495
 496    default:
 497          return currentStatus = UnknownError;
 498    }
 499}
 500
 501//-----------------------------------------------------------------------------
 502// Sets and returns the currentStatus to status.
 503//-----------------------------------------------------------------------------
 504File::FileStatus File::setStatus(File::FileStatus status)
 505{
 506    return currentStatus = status;
 507}
 508
 509//-----------------------------------------------------------------------------
 510// Read from a file.
 511// The number of bytes to read is passed in size, the data is returned in src.
 512// The number of bytes read is available in bytesRead if a non-Null pointer is
 513// provided.
 514//-----------------------------------------------------------------------------
 515File::FileStatus File::read(U32 size, char *dst, U32 *bytesRead)
 516{
 517    AssertFatal(Closed != currentStatus, "File::read: file closed");
 518    AssertFatal(INVALID_HANDLE_VALUE != (HANDLE)handle, "File::read: invalid file handle");
 519    AssertFatal(NULL != dst, "File::read: NULL destination pointer");
 520    AssertFatal(true == hasCapability(FileRead), "File::read: file lacks capability");
 521    AssertWarn(0 != size, "File::read: size of zero");
 522
 523    if (Ok != currentStatus || 0 == size)
 524        return currentStatus;
 525    else
 526    {
 527        DWORD lastBytes;
 528        DWORD *bytes = (NULL == bytesRead) ? &lastBytes : (DWORD *)bytesRead;
 529        if (0 != ReadFile((HANDLE)handle, dst, size, bytes, NULL))
 530        {
 531            if(*((U32 *)bytes) != size)
 532                return currentStatus = EOS;                        // end of stream
 533        }
 534        else
 535            return setStatus();                                    // unsuccessful
 536    }
 537    return currentStatus = Ok;                                    // successfully read size bytes
 538}
 539
 540//-----------------------------------------------------------------------------
 541// Write to a file.
 542// The number of bytes to write is passed in size, the data is passed in src.
 543// The number of bytes written is available in bytesWritten if a non-Null
 544// pointer is provided.
 545//-----------------------------------------------------------------------------
 546File::FileStatus File::write(U32 size, const char *src, U32 *bytesWritten)
 547{
 548    AssertFatal(Closed != currentStatus, "File::write: file closed");
 549    AssertFatal(INVALID_HANDLE_VALUE != (HANDLE)handle, "File::write: invalid file handle");
 550    AssertFatal(NULL != src, "File::write: NULL source pointer");
 551    AssertFatal(true == hasCapability(FileWrite), "File::write: file lacks capability");
 552    AssertWarn(0 != size, "File::write: size of zero");
 553
 554    if ((Ok != currentStatus && EOS != currentStatus) || 0 == size)
 555        return currentStatus;
 556    else
 557    {
 558        DWORD lastBytes;
 559        DWORD *bytes = (NULL == bytesWritten) ? &lastBytes : (DWORD *)bytesWritten;
 560        if (0 != WriteFile((HANDLE)handle, src, size, bytes, NULL))
 561            return currentStatus = Ok;                            // success!
 562        else
 563            return setStatus();                                    // unsuccessful
 564    }
 565}
 566
 567//-----------------------------------------------------------------------------
 568// Self-explanatory.
 569//-----------------------------------------------------------------------------
 570bool File::hasCapability(Capability cap) const
 571{
 572    return (0 != (U32(cap) & capability));
 573}
 574
 575S32 Platform::compareFileTimes(const FileTime &a, const FileTime &b)
 576{
 577   if(a.v2 > b.v2)
 578      return 1;
 579   if(a.v2 < b.v2)
 580      return -1;
 581   if(a.v1 > b.v1)
 582      return 1;
 583   if(a.v1 < b.v1)
 584      return -1;
 585   return 0;
 586}
 587
 588static bool recurseDumpPath(const char *path, const char *pattern, Vector<Platform::FileInfo> &fileVector, S32 recurseDepth )
 589{
 590   WIN32_FIND_DATA findData;
 591
 592   TempAlloc< char> fullPath( dStrlen( path ) * 3 + MAX_PATH * 3 + 1 );
 593   Platform::makeFullPathName( path, fullPath, fullPath.size );
 594
 595   U32 lenFullPath = dStrlen( fullPath );
 596   TempAlloc< char> buf( lenFullPath + MAX_PATH * 3 + 2 );
 597   dSprintf( buf, buf.size, "%s/%s", fullPath.ptr, pattern );
 598
 599#ifdef UNICODE
 600   TempAlloc< WCHAR> searchBuf( buf.size );
 601   convertUTF8toUTF16N( buf, searchBuf, searchBuf.size );
 602   WCHAR* search = searchBuf;
 603#else
 604   char *search = buf;
 605#endif
 606
 607   backslash( search );
 608   
 609   HANDLE handle = FindFirstFile(search, &findData);
 610   if (handle == INVALID_HANDLE_VALUE)
 611      return false;
 612
 613   do
 614   {
 615#ifdef UNICODE
 616      convertUTF16toUTF8N( findData.cFileName, buf, buf.size );
 617      char* fnbuf = buf;
 618#else
 619      char *fnbuf = findData.cFileName;
 620#endif
 621
 622      if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
 623      {
 624         // make sure it is a directory
 625         if (findData.dwFileAttributes & (FILE_ATTRIBUTE_OFFLINE|FILE_ATTRIBUTE_SYSTEM) )                             
 626            continue;
 627
 628         // skip . and .. directories
 629         if (String::compare( findData.cFileName, TEXT( "." ) ) == 0 || String::compare( findData.cFileName, TEXT( ".." ) ) == 0)
 630            continue;
 631
 632         // Skip excluded directores
 633         if(Platform::isExcludedDirectory(fnbuf))
 634            continue;
 635
 636         dSprintf( fullPath, fullPath.size, "%s/%s", path, fnbuf);
 637         char* child = fullPath;
 638         if( recurseDepth > 0 )
 639            recurseDumpPath(child, pattern, fileVector, recurseDepth - 1);
 640         else if (recurseDepth == -1)
 641            recurseDumpPath(child, pattern, fileVector, -1);
 642      }      
 643      else
 644      {
 645         // make sure it is the kind of file we're looking for
 646         if (findData.dwFileAttributes & 
 647             (FILE_ATTRIBUTE_DIRECTORY|                                      
 648              FILE_ATTRIBUTE_OFFLINE|
 649              FILE_ATTRIBUTE_SYSTEM|
 650              FILE_ATTRIBUTE_TEMPORARY) )                             
 651            continue;
 652         
 653         // add it to the list
 654         fileVector.increment();
 655         Platform::FileInfo& rInfo = fileVector.last();
 656
 657         forwardslash( fnbuf );
 658
 659         rInfo.pFullPath = StringTable->insert(path);
 660         rInfo.pFileName = StringTable->insert(fnbuf);
 661         rInfo.fileSize  = findData.nFileSizeLow;
 662      }
 663
 664   }while(FindNextFile(handle, &findData));
 665
 666   FindClose(handle);
 667   return true;
 668}
 669
 670
 671//--------------------------------------
 672
 673bool Platform::getFileTimes(const char *filePath, FileTime *createTime, FileTime *modifyTime)
 674{
 675   WIN32_FIND_DATA findData;
 676
 677   TempAlloc< TCHAR> fp( dStrlen( filePath ) + 1 );
 678
 679#ifdef UNICODE
 680   convertUTF8toUTF16N( filePath, fp, fp.size );
 681#else
 682   dStrcpy( fp, filePath, fp.size );
 683#endif
 684
 685   backslash( fp );
 686
 687   HANDLE h = FindFirstFile(fp, &findData);
 688   if(h == INVALID_HANDLE_VALUE)
 689      return false;
 690
 691   if(createTime)
 692   {
 693      createTime->v1 = findData.ftCreationTime.dwLowDateTime;
 694      createTime->v2 = findData.ftCreationTime.dwHighDateTime;
 695   }
 696   if(modifyTime)
 697   {
 698      modifyTime->v1 = findData.ftLastWriteTime.dwLowDateTime;
 699      modifyTime->v2 = findData.ftLastWriteTime.dwHighDateTime;
 700   }
 701   FindClose(h);
 702   return true;
 703}
 704
 705//--------------------------------------
 706bool Platform::createPath(const char *file)
 707{
 708   TempAlloc< TCHAR> pathbuf( dStrlen( file ) + 1 );
 709
 710#ifdef UNICODE
 711   TempAlloc< WCHAR> fileBuf( pathbuf.size );
 712   convertUTF8toUTF16N( file, fileBuf, fileBuf.size );
 713   const WCHAR* fileName = fileBuf;
 714   const WCHAR* dir;
 715#else
 716   const char* fileName = file;
 717   const char* dir;
 718#endif
 719
 720   pathbuf[ 0 ] = 0;
 721   U32 pathLen = 0;
 722
 723   while((dir = dStrchr(fileName, '/')) != NULL)
 724   {
 725      TCHAR* pathptr = pathbuf;
 726      dMemcpy( pathptr + pathLen, fileName, ( dir - fileName ) * sizeof( TCHAR ) );
 727      pathbuf[pathLen + dir-fileName] = 0;
 728 
 729      // ignore return value because we are fine with already existing directory
 730      CreateDirectory(pathbuf, NULL);
 731
 732      pathLen += dir - fileName;
 733      pathbuf[pathLen++] = '\\';
 734      fileName = dir + 1;
 735   }
 736   return true;
 737}
 738
 739// [rene, 04/05/2008] Not used currently so did not bother updating.
 740#if 0
 741// [tom, 7/12/2005] Rather then converting this to unicode, just using the ANSI
 742// versions of the Win32 API as its quicker for testing.
 743bool S32 serialNum)
 744{
 745   if (!filePath || !filePath[0])
 746      return true;
 747
 748   //first find the CD device...
 749   char fileBuf[1024];
 750   char drivesBuf[256];
 751   S32 length = GetLogicalDriveStringsA(256, drivesBuf);
 752   char *drivePtr = drivesBuf;
 753   while (S32(drivePtr - drivesBuf) < length)
 754   {
 755      char driveVolume[256], driveFileSystem[256];
 756      U32 driveSerial, driveFNLength, driveFlags;
 757      if ((dStricmp(drivePtr, "B:\\") != 0) &&
 758          GetVolumeInformationA((const char*)drivePtr, &driveVolume[0], (unsigned long)255,
 759                               (unsigned long*)&driveSerial, (unsigned long*)&driveFNLength,
 760                               (unsigned long*)&driveFlags, &driveFileSystem[0], (unsigned long)255))
 761      {
 762#if defined (TORQUE_DEBUG) || !defined (TORQUE_SHIPPING)
 763         Con::printf("Found Drive: %s, vol: %s, serial: %d", drivePtr, driveVolume, driveSerial);
 764#endif
 765         //see if the volume and serial number match
 766         if (!dStricmp(volumeName, driveVolume) && (!serialNum || (serialNum == driveSerial)))
 767         {
 768            //see if the file exists on this volume
 769            if(dStrlen(drivePtr) == 3 && drivePtr[2] == '\\' && filePath[0] == '\\')
 770               dSprintf(fileBuf, sizeof(fileBuf), "%s%s", drivePtr, filePath + 1);
 771            else
 772               dSprintf(fileBuf, sizeof(fileBuf), "%s%s", drivePtr, filePath);
 773#if defined (TORQUE_DEBUG) || !defined (TORQUE_SHIPPING)
 774            Con::printf("Looking for file: %s on %s", fileBuf, driveVolume);
 775#endif
 776            WIN32_FIND_DATAA findData;
 777            HANDLE h = FindFirstFileA(fileBuf, &findData);
 778            if(h != INVALID_HANDLE_VALUE)
 779            {
 780               FindClose(h);
 781               return true;
 782            }
 783            FindClose(h);
 784         }
 785      }
 786
 787      //check the next drive
 788      drivePtr += dStrlen(drivePtr) + 1;
 789   }
 790
 791   return false;
 792}
 793#endif
 794
 795//--------------------------------------
 796bool Platform::dumpPath(const char *path, Vector<Platform::FileInfo> &fileVector, S32 recurseDepth)
 797{
 798   return recurseDumpPath(path, "*", fileVector, recurseDepth );
 799}
 800
 801
 802//--------------------------------------
 803
 804//StringTableEntry Platform::getWorkingDirectory()
 805//{
 806//   return getCurrentDirectory();
 807//}
 808
 809StringTableEntry Platform::getCurrentDirectory()
 810{
 811   TempAlloc< TCHAR> buf( 2048 );
 812
 813   GetCurrentDirectory( buf.size, buf );
 814   forwardslash( buf );
 815
 816#ifdef UNICODE
 817   char* utf8 = createUTF8string( buf );
 818   StringTableEntry result = StringTable->insert( utf8 );
 819   SAFE_DELETE_ARRAY( utf8 );
 820   return result;
 821#else
 822   return StringTable->insert( buf );
 823#endif
 824}
 825
 826bool Platform::setCurrentDirectory(StringTableEntry newDir)
 827{
 828
 829   if (Platform::getWebDeployment())
 830      return true;
 831
 832   TempAlloc< TCHAR> buf( dStrlen( newDir ) + 2 );
 833
 834#ifdef UNICODE
 835   convertUTF8toUTF16N( newDir, buf, buf.size - 1 );
 836#else
 837   dStrcpy( buf, newDir, buf.size );
 838#endif
 839
 840   backslash( buf );
 841   return SetCurrentDirectory( buf );
 842}
 843
 844#ifdef UNICODE
 845static void getExecutableInfo( StringTableEntry* path, StringTableEntry* exe )
 846{
 847   static StringTableEntry pathEntry = NULL;
 848   static StringTableEntry exeEntry = NULL;
 849
 850   if( !pathEntry )
 851   {
 852      if (!Platform::getWebDeployment())
 853      {
 854         WCHAR cen_buf[ 2048 ];
 855         GetModuleFileNameW( NULL, cen_buf, sizeof( cen_buf ) / sizeof( cen_buf[ 0 ] ) );
 856         forwardslash( cen_buf );
 857
 858         WCHAR* delimiter = dStrrchr( cen_buf, '/' );
 859         if( delimiter )
 860            *delimiter = '\0';
 861
 862         char* pathBuf = createUTF8string( cen_buf );
 863         char* exeBuf = createUTF8string( delimiter + 1 );
 864
 865         pathEntry = StringTable->insert( pathBuf );
 866         exeEntry = StringTable->insert( exeBuf );
 867
 868         SAFE_DELETE_ARRAY( pathBuf );
 869         SAFE_DELETE_ARRAY( exeBuf );
 870      }
 871      else
 872      {
 873         char cdir[4096];
 874         GetCurrentDirectoryA(4096, cdir);
 875         pathEntry = StringTable->insert(cdir);
 876         exeEntry = StringTable->insert("WebGameCtrl.exe");
 877      }
 878   }
 879
 880   if( path )
 881      *path = pathEntry;
 882   if( exe )
 883      *exe = exeEntry;
 884}
 885#endif
 886
 887StringTableEntry Platform::getExecutableName()
 888{
 889#ifdef UNICODE
 890   StringTableEntry exe;
 891   getExecutableInfo( NULL, &exe );
 892   return exe;
 893#else
 894   static StringTableEntry cen = NULL;
 895   if (!cen)
 896   {
 897      char cen_buf[2048];
 898      GetModuleFileNameA( NULL, cen_buf, 2047);
 899      forwardslash(cen_buf);
 900
 901      char *delimiter = dStrrchr( cen_buf, '/' );
 902
 903      if( delimiter != NULL )
 904      {
 905         *delimiter = 0x00;
 906         delimiter++;
 907         cen = StringTable->insert(delimiter);
 908      }
 909      else
 910         cen = StringTable->insert(cen_buf);
 911   }
 912   return cen;
 913#endif
 914}
 915
 916StringTableEntry Platform::getExecutablePath()
 917{
 918#ifdef UNICODE
 919   StringTableEntry path;
 920   getExecutableInfo( &path, NULL );
 921   return path;
 922#else
 923   static StringTableEntry cen = NULL;
 924   if (!cen)
 925   {
 926      char cen_buf[2048];
 927      GetModuleFileNameA( NULL, cen_buf, 2047);
 928      forwardslash(cen_buf);
 929
 930      char *delimiter = dStrrchr( cen_buf, '/' );
 931
 932      if( delimiter != NULL )
 933         *delimiter = 0x00;
 934
 935      cen = StringTable->insert(cen_buf);
 936   }
 937   return cen;
 938#endif
 939}
 940
 941//--------------------------------------
 942bool Platform::isFile(const char *pFilePath)
 943{
 944   if (!pFilePath || !*pFilePath)
 945      return false;
 946
 947   TempAlloc< TCHAR> buf( dStrlen( pFilePath ) + 1 );
 948
 949#ifdef UNICODE
 950   convertUTF8toUTF16N( pFilePath, buf, buf.size );
 951#else
 952   dStrcpy( buf, pFilePath, buf.size );
 953#endif
 954   backslash( buf );
 955
 956   // Get file info
 957   WIN32_FIND_DATA findData;
 958   HANDLE handle = FindFirstFile(buf, &findData);
 959   FindClose(handle);
 960
 961   if(handle == INVALID_HANDLE_VALUE)
 962   {
 963    
 964      // Since file does not exist on disk see if it exists in a zip file loaded
 965      return Torque::FS::IsFile(pFilePath);
 966   }
 967
 968   // if the file is a Directory, Offline, System or Temporary then FALSE
 969   if (findData.dwFileAttributes &
 970       (FILE_ATTRIBUTE_DIRECTORY|
 971        FILE_ATTRIBUTE_OFFLINE|
 972        FILE_ATTRIBUTE_SYSTEM|
 973        FILE_ATTRIBUTE_TEMPORARY) )
 974      return false;
 975
 976   // must be a real file then
 977   return true;
 978}
 979
 980//--------------------------------------
 981S32 Platform::getFileSize(const char *pFilePath)
 982{
 983   if (!pFilePath || !*pFilePath)
 984      return -1;
 985
 986   TempAlloc< TCHAR> buf( dStrlen( pFilePath ) + 1 );
 987
 988#ifdef UNICODE
 989   convertUTF8toUTF16N( pFilePath, buf, buf.size );
 990#else
 991   dStrcpy( buf, pFilePath, buf.size );
 992#endif
 993   backslash( buf );
 994
 995   // Get file info
 996   WIN32_FIND_DATA findData;
 997   HANDLE handle = FindFirstFile(buf, &findData);
 998
 999   if(handle == INVALID_HANDLE_VALUE)
1000      return -1;
1001
1002   FindClose(handle);
1003
1004   // if the file is a Directory, Offline, System or Temporary then FALSE
1005   if (findData.dwFileAttributes &
1006       (FILE_ATTRIBUTE_DIRECTORY|
1007        FILE_ATTRIBUTE_OFFLINE|
1008        FILE_ATTRIBUTE_SYSTEM|
1009        FILE_ATTRIBUTE_TEMPORARY) )
1010      return -1;
1011
1012   // must be a real file then
1013   return ((findData.nFileSizeHigh * (MAXDWORD+1)) + findData.nFileSizeLow);
1014}
1015
1016
1017//--------------------------------------
1018bool Platform::isDirectory(const char *pDirPath)
1019{
1020   if (!pDirPath || !*pDirPath)
1021      return false;
1022
1023   TempAlloc< TCHAR> buf( dStrlen( pDirPath ) + 1 );
1024
1025#ifdef UNICODE
1026   convertUTF8toUTF16N( pDirPath, buf, buf.size );
1027#else
1028   dStrcpy( buf, pDirPath, buf.size );
1029#endif
1030   backslash( buf );
1031
1032   // Get file info
1033   WIN32_FIND_DATA findData;
1034   HANDLE handle = FindFirstFile(buf, &findData);
1035
1036   // [neo, 5/15/2007]
1037   // This check was AFTER FindClose for some reason - this is most probably the
1038   // original intent.
1039   if(handle == INVALID_HANDLE_VALUE)
1040      return false;
1041
1042   FindClose(handle);
1043   
1044   // if the file is a Directory, Offline, System or Temporary then FALSE
1045   if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1046   {
1047      // make sure it's a valid game directory
1048      if (findData.dwFileAttributes & (FILE_ATTRIBUTE_OFFLINE|FILE_ATTRIBUTE_SYSTEM) )
1049         return false;
1050
1051      // must be a directory
1052      return true;
1053   }
1054
1055   return false;
1056}
1057
1058
1059
1060//--------------------------------------
1061bool Platform::isSubDirectory(const char *pParent, const char *pDir)
1062{
1063   if (!pParent || !*pDir)
1064      return false;
1065
1066   const char* fileName = avar("%s/*", pParent);
1067
1068   TempAlloc< TCHAR> file( dStrlen( fileName ) + 1 );
1069   TempAlloc< TCHAR> dir( dStrlen( pDir ) + 1 );
1070
1071#ifdef UNICODE
1072   convertUTF8toUTF16N( fileName, file, file.size );
1073   convertUTF8toUTF16N( pDir, dir, dir.size );
1074#else
1075   dStrcpy( file, fileName, file.size );
1076   dStrcpy( dir, pDir, dir.size );
1077#endif
1078
1079   backslash( file );
1080   backslash( dir );
1081
1082   // this is somewhat of a brute force method but we need to be 100% sure
1083   // that the user cannot enter things like ../dir or /dir etc,...
1084   WIN32_FIND_DATA findData;
1085   HANDLE handle = FindFirstFile(file, &findData);
1086   if (handle == INVALID_HANDLE_VALUE)
1087      return false;
1088   do
1089   {
1090      // if it is a directory...
1091      if (findData.dwFileAttributes &
1092          (FILE_ATTRIBUTE_DIRECTORY|
1093           FILE_ATTRIBUTE_OFFLINE|
1094           FILE_ATTRIBUTE_SYSTEM|
1095           FILE_ATTRIBUTE_TEMPORARY) )
1096      {
1097         //FIXME: this has to be dStrcasecmp but there's no implementation for Unicode
1098
1099         // and the names match
1100         if (String::compare(dir, findData.cFileName ) == 0)
1101         {
1102            // then we have a real sub directory
1103            FindClose(handle);
1104            return true;
1105         }
1106      }
1107   }while(FindNextFile(handle, &findData));
1108
1109   FindClose(handle);
1110   return false;
1111}
1112
1113//------------------------------------------------------------------------------
1114
1115bool Platform::fileTimeToString(FileTime * time, char * string, U32 strLen)
1116{
1117   if(!time || !string)
1118      return(false);
1119
1120   dSprintf(string, strLen, "%d:%d", time->v2, time->v1);
1121   return(true);
1122}
1123
1124bool Platform::stringToFileTime(const char * string, FileTime * time)
1125{
1126   if(!time || !string)
1127      return(false);
1128
1129   char buf[80];
1130   dSprintf(buf, sizeof(buf), (char *)string);
1131
1132   char * sep = (char *)dStrstr((const char *)buf, (const char *)":");
1133   if(!sep)
1134      return(false);
1135
1136   *sep = 0;
1137   sep++;
1138
1139   time->v2 = dAtoi(buf);
1140   time->v1 = dAtoi(sep);
1141
1142   return(true);
1143}
1144
1145// Volume Functions
1146
1147void Platform::getVolumeNamesList( Vector<const char*>& out_rNameVector, bool bOnlyFixedDrives )
1148{
1149   DWORD dwDrives = GetLogicalDrives();
1150   DWORD dwMask = 1;
1151   char driveLetter[12];
1152
1153   out_rNameVector.clear();
1154      
1155   for(S32 i = 0; i < 32; i++ )
1156   {
1157      dMemset(driveLetter,0,12);
1158      if( dwDrives & dwMask )
1159      {
1160         dSprintf(driveLetter, 12, "%c:", (i + 'A'));
1161
1162         if( bOnlyFixedDrives && GetDriveTypeA(driveLetter) == DRIVE_FIXED )
1163            out_rNameVector.push_back( StringTable->insert( driveLetter ) );
1164         else if ( !bOnlyFixedDrives )
1165            out_rNameVector.push_back( StringTable->insert( driveLetter ) );
1166      }
1167      dwMask <<= 1;
1168   }
1169}
1170
1171void Platform::getVolumeInformationList( Vector<VolumeInformation>& out_rVolumeInfoVector, bool bOnlyFixedDrives )
1172{
1173   Vector<const char*> drives;
1174
1175   getVolumeNamesList( drives, bOnlyFixedDrives );
1176
1177   if( ! drives.empty() )
1178   {
1179      Vector<StringTableEntry>::iterator i;
1180      for( i = drives.begin(); i != drives.end(); i++ )
1181      {
1182         VolumeInformation info;
1183         TCHAR lpszVolumeName[ 256 ];
1184         TCHAR lpszFileSystem[ 256 ];
1185         DWORD dwSerial = 0;
1186         DWORD dwMaxComponentLength = 0;
1187         DWORD dwFileSystemFlags = 0;
1188
1189         dMemset( lpszVolumeName, 0, sizeof( lpszVolumeName ) );
1190         dMemset( lpszFileSystem, 0, sizeof( lpszFileSystem ) );
1191         dMemset( &info, 0, sizeof( VolumeInformation ) );
1192
1193         // More volume information
1194         UINT uDriveType = GetDriveTypeA( (*i) );
1195         if( uDriveType == DRIVE_UNKNOWN )
1196            info.Type = DRIVETYPE_UNKNOWN;
1197         else if( uDriveType == DRIVE_REMOVABLE )
1198            info.Type = DRIVETYPE_REMOVABLE;
1199         else if( uDriveType == DRIVE_FIXED )
1200            info.Type = DRIVETYPE_FIXED;
1201         else if( uDriveType == DRIVE_CDROM )
1202            info.Type = DRIVETYPE_CDROM;
1203         else if( uDriveType == DRIVE_RAMDISK )
1204            info.Type = DRIVETYPE_RAMDISK;
1205         else if( uDriveType == DRIVE_REMOTE )
1206            info.Type = DRIVETYPE_REMOTE;
1207
1208         info.RootPath = StringTable->insert( (*i) );
1209
1210         // We don't retrieve drive volume info for removable drives, because it's loud :(
1211         if( info.Type != DRIVETYPE_REMOVABLE )
1212         {
1213#ifdef UNICODE
1214            WCHAR ibuf[ 3 ];
1215            ibuf[ 0 ] = ( *i )[ 0 ];
1216            ibuf[ 1 ] = ':';
1217            ibuf[ 2 ] = '\0';
1218#else
1219            char* ibuf = *i;
1220#endif
1221            // Standard volume information
1222            GetVolumeInformation( ibuf, lpszVolumeName, sizeof( lpszVolumeName ) / sizeof( lpszVolumeName[ 0 ] ),
1223               &dwSerial, &dwMaxComponentLength, &dwFileSystemFlags, lpszFileSystem,
1224               sizeof( lpszFileSystem ) / sizeof( lpszFileSystem[ 0 ] ) );
1225
1226#ifdef UNICODE
1227            char buf[ sizeof( lpszFileSystem ) / sizeof( lpszFileSystem[ 0 ] ) * 3 + 1 ];
1228            convertUTF16toUTF8( lpszFileSystem, buf );
1229            info.FileSystem = StringTable->insert( buf );
1230
1231            convertUTF16toUTF8( lpszVolumeName, buf );
1232            info.Name = StringTable->insert( buf );
1233#else
1234            info.FileSystem = StringTable->insert( lpszFileSystem );
1235            info.Name = StringTable->insert( lpszVolumeName );
1236#endif
1237            info.SerialNumber = dwSerial;
1238            // Won't compile on something prior to XP.
1239            info.ReadOnly = dwFileSystemFlags & FILE_READ_ONLY_VOLUME;
1240         }
1241         out_rVolumeInfoVector.push_back( info );
1242
1243         // I opted not to get free disk space because of the overhead of the calculations required for it
1244
1245      }
1246   }
1247}
1248
1249
1250bool Platform::hasSubDirectory(const char *pPath)
1251{
1252   if( !pPath )
1253      return false;
1254
1255   char searchBuf[1024];
1256
1257   // Compose our search string - Format : ([path]/[subpath]/*)
1258   char trail = pPath[ dStrlen(pPath) - 1 ];
1259   if( trail == '/' )
1260      dStrcpy( searchBuf, pPath, 1024 );
1261   else
1262      dSprintf(searchBuf, 1024, "%s/*", pPath );
1263
1264#ifdef UNICODE
1265   WCHAR buf[ 1024 ];
1266   convertUTF8toUTF16( searchBuf, buf );
1267   WCHAR* search = buf;
1268#else
1269   char* search = searchBuf;
1270#endif
1271
1272   backslash( search );
1273
1274   // See if we get any hits
1275   WIN32_FIND_DATA findData;
1276   HANDLE handle = FindFirstFile(search, &findData);
1277   if (handle == INVALID_HANDLE_VALUE)
1278      return false;
1279
1280   bool result = false;
1281   do
1282   {
1283      if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1284      {
1285         // skip . and .. directories
1286         if (String::compare(findData.cFileName, TEXT( "." ) ) == 0 || String::compare(findData.cFileName, TEXT( ".." ) ) == 0)
1287            continue;
1288
1289#ifdef UNICODE
1290         char fileName[ 1024 ];
1291         convertUTF16toUTF8( findData.cFileName, fileName );
1292#else
1293         char* fileName = findData.cFileName;
1294#endif
1295
1296         if( Platform::isExcludedDirectory( fileName ) )
1297            continue;
1298
1299         result = true;
1300         break;
1301      }      
1302   }
1303   while(FindNextFile(handle, &findData));
1304
1305   FindClose(handle);
1306
1307   Platform::clearExcludedDirectories();
1308
1309   return result;
1310}
1311
1312
1313static bool recurseDumpDirectories(const char *basePath, const char *subPath, Vector<StringTableEntry> &directoryVector, S32 currentDepth, S32 recurseDepth, bool noBasePath)
1314{
1315   TempAlloc< char> search( 1024 );
1316
1317   //-----------------------------------------------------------------------------
1318   // Compose our search string - Format : ([path]/[subpath]/*)
1319   //-----------------------------------------------------------------------------
1320
1321   dsize_t trLen = basePath ? dStrlen(basePath) : 0;
1322   dsize_t subtrLen = subPath ? dStrlen(subPath) : 0;
1323   char trail = trLen > 0 ? basePath[ trLen - 1 ] : '\0';
1324   char subTrail = subtrLen > 0 ? subPath[ subtrLen - 1 ] : '\0';
1325   char subLead = subtrLen > 0 ? subPath[0] : '\0';
1326
1327   if( trail == '/' )
1328   {
1329      // we have a sub path and it's not an empty string
1330      if(  subPath  && ( dStrncmp( subPath, "", 1 ) != 0 ) )
1331      {
1332         if( subTrail == '/' )
1333            dSprintf(search, search.size, "%s%s*", basePath,subPath );
1334         else
1335            dSprintf(search, search.size, "%s%s/*", basePath,subPath );
1336      }
1337      else
1338         dSprintf( search, search.size, "%s*", basePath );
1339   }
1340   else
1341   {
1342      if(  subPath  && ( dStrncmp( subPath, "", 1 ) != 0 ) )
1343         if( subTrail == '/' )
1344            dSprintf(search, search.size, "%s%s*", basePath,subPath );
1345         else
1346            dSprintf(search, search.size, "%s%s/*", basePath,subPath );
1347      else
1348         dSprintf(search, search.size, "%s/*", basePath );
1349   }
1350
1351#ifdef UNICODE
1352   TempAlloc< WCHAR> searchStr( dStrlen( search ) + 1 );
1353   convertUTF8toUTF16N( search, searchStr, searchStr.size );
1354#else
1355   char* searchStr = search;
1356#endif
1357
1358   backslash( searchStr );
1359
1360   //-----------------------------------------------------------------------------
1361   // See if we get any hits
1362   //-----------------------------------------------------------------------------
1363
1364   WIN32_FIND_DATA findData;
1365   HANDLE handle = FindFirstFile(searchStr, &findData);
1366   if (handle == INVALID_HANDLE_VALUE)
1367      return false;
1368
1369   //-----------------------------------------------------------------------------
1370   // add path to our return list ( provided it is valid )
1371   //-----------------------------------------------------------------------------
1372   if( !Platform::isExcludedDirectory( subPath ) )
1373   {
1374
1375      if( noBasePath )
1376      {
1377         // We have a path and it's not an empty string or an excluded directory
1378         if( ( subPath  && ( dStrncmp( subPath, "", 1 ) != 0 ) ) )
1379            directoryVector.push_back( StringTable->insert( subPath ) );
1380      }
1381      else
1382      {
1383         if( ( subPath  && ( dStrncmp( subPath, "", 1 ) != 0 ) ) )
1384         {
1385            char szPath[1024];
1386            dMemset(szPath, 0, 1024);
1387            if (trail == '/')
1388            {
1389               if (subLead == '/')
1390                  dSprintf(szPath, 1024, "%s%s", basePath, &subPath[1]);
1391               else
1392                  dSprintf(szPath, 1024, "%s%s", basePath, subPath);
1393            }
1394            else
1395            {
1396               if (subLead == '/')
1397                  dSprintf(szPath, 1024, "%s%s", basePath, subPath);
1398               else
1399                  dSprintf(szPath, 1024, "%s/%s", basePath, subPath);
1400            }
1401            directoryVector.push_back(StringTable->insert(szPath));
1402         }
1403         else
1404            directoryVector.push_back( StringTable->insert( basePath ) );
1405      }
1406   }
1407
1408   //-----------------------------------------------------------------------------
1409   // Iterate through and grab valid directories
1410   //-----------------------------------------------------------------------------
1411
1412#ifdef UNICODE
1413   TempAlloc< char> fileName( 1024 );
1414#endif
1415
1416   do
1417   {
1418      if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1419      {
1420         // skip . and .. directories
1421         if (String::compare(findData.cFileName, TEXT( "." )) == 0 || String::compare(findData.cFileName, TEXT( ".." )) == 0)
1422            continue;
1423
1424#ifdef UNICODE
1425         convertUTF16toUTF8N( findData.cFileName, fileName, fileName.size );
1426#else
1427         char* fileName = findData.cFileName;
1428#endif
1429
1430         // skip excluded directories
1431         if( Platform::isExcludedDirectory( fileName ) )
1432            continue;
1433
1434         if( ( subPath  && ( dStrncmp( subPath, "", 1 ) != 0 ) ))
1435         {
1436            if( subTrail == '/' )
1437               dSprintf(search, search.size, "%s%s", subPath, fileName.ptr);
1438            else
1439               dSprintf(search, search.size, "%s/%s", subPath, fileName.ptr);
1440            char* child = search;
1441
1442            if( currentDepth < recurseDepth || recurseDepth == -1 )
1443               recurseDumpDirectories(basePath, child, directoryVector, currentDepth+1, recurseDepth, noBasePath );
1444
1445         }
1446         else
1447         {
1448            char* child;
1449            if( trail == '/' )
1450               child = fileName;
1451            else
1452            {
1453               dSprintf(search, search.size, "/%s", fileName.ptr);
1454               child = search;
1455            }
1456
1457            if( currentDepth < recurseDepth || recurseDepth == -1 )
1458               recurseDumpDirectories(basePath, child, directoryVector, currentDepth+1, recurseDepth, noBasePath );
1459         }
1460      }      
1461   }
1462   while(FindNextFile(handle, &findData));
1463
1464   FindClose(handle);
1465   return true;
1466}
1467
1468bool Platform::dumpDirectories( const char *path, Vector<StringTableEntry> &directoryVector, S32 depth, bool noBasePath )
1469{
1470   bool retVal = recurseDumpDirectories( path, "", directoryVector, -1, depth, noBasePath );
1471
1472   clearExcludedDirectories();
1473
1474   return retVal;
1475}
1476
1477//-----------------------------------------------------------------------------
1478
1479StringTableEntry osGetTemporaryDirectory()
1480{
1481   TCHAR buf[ 1024 ];
1482   const U32 bufSize = sizeof( buf ) / sizeof( buf[ 0 ] );
1483   DWORD len = GetTempPath( sizeof( buf ) / sizeof( buf[ 0 ] ), buf );
1484
1485   TempAlloc< TCHAR> temp;
1486   TCHAR* buffer = buf;
1487   if( len > bufSize - 1 )
1488   {
1489      temp = TempAlloc< TCHAR>( len + 1 );
1490      buffer = temp;
1491      GetTempPath( len + 1, buffer );
1492   }
1493
1494   // Remove the trailing slash
1495   buffer[len-1] = 0;
1496
1497#ifdef UNICODE
1498   TempAlloc< char> dirBuffer( len * 3 + 1 );
1499   char* dir = dirBuffer;
1500   convertUTF16toUTF8N( buffer, dir, dirBuffer.size );
1501#else
1502   char* dir = buf;
1503#endif
1504
1505   forwardslash(dir);
1506   return StringTable->insert(dir);
1507}
1508