winVolume.cpp

Engine/source/platformWin32/winVolume.cpp

More...

Classes:

Namespaces:

namespace
namespace

Public Defines

define
define
S_ISDIR(Flags)    ((Flags) & FILE_ATTRIBUTE_DIRECTORY)
define
S_ISREG(Flags)    !((Flags) & \
   (FILE_ATTRIBUTE_DIRECTORY | \
   FILE_ATTRIBUTE_OFFLINE | \
   FILE_ATTRIBUTE_SYSTEM | \
   FILE_ATTRIBUTE_TEMPORARY))

Detailed Description

Public Defines

NGROUPS_UMAX() 32
S_ISDIR(Flags)    ((Flags) & FILE_ATTRIBUTE_DIRECTORY)
S_ISREG(Flags)    !((Flags) & \
   (FILE_ATTRIBUTE_DIRECTORY | \
   FILE_ATTRIBUTE_OFFLINE | \
   FILE_ATTRIBUTE_SYSTEM | \
   FILE_ATTRIBUTE_TEMPORARY))
  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 <windows.h>
 25
 26#include "core/crc.h"
 27#include "core/frameAllocator.h"
 28#include "core/util/str.h"
 29#include "core/strings/stringFunctions.h"
 30#include "core/strings/unicode.h"
 31
 32#include "platform/platformVolume.h"
 33
 34#include "platformWin32/winVolume.h"
 35
 36#include "console/console.h"
 37
 38
 39#ifndef NGROUPS_UMAX
 40   #define NGROUPS_UMAX 32
 41#endif
 42
 43namespace Torque
 44{
 45namespace Win32
 46{
 47
 48   // If the file is a Directory, Offline, System or Temporary then FALSE
 49#define S_ISREG(Flags) \
 50   !((Flags) & \
 51   (FILE_ATTRIBUTE_DIRECTORY | \
 52   FILE_ATTRIBUTE_OFFLINE | \
 53   FILE_ATTRIBUTE_SYSTEM | \
 54   FILE_ATTRIBUTE_TEMPORARY))
 55
 56#define S_ISDIR(Flags) \
 57   ((Flags) & FILE_ATTRIBUTE_DIRECTORY)
 58
 59//-----------------------------------------------------------------------------
 60
 61   class Win32FileSystemChangeNotifier : public FileSystemChangeNotifier
 62   {
 63   public:
 64      Win32FileSystemChangeNotifier( FileSystem *fs )
 65         :  FileSystemChangeNotifier( fs )
 66      {
 67         VECTOR_SET_ASSOCIATION( mHandleList );
 68         VECTOR_SET_ASSOCIATION( mDirs );
 69      }
 70
 71      // for use in the thread itself
 72      U32      getNumHandles() const { return mHandleList.size(); }
 73      HANDLE   *getHANDLES() { return mHandleList.address(); }
 74
 75   private:
 76      virtual void   internalProcessOnce();
 77
 78      virtual bool   internalAddNotification( const Path &dir );
 79      virtual bool   internalRemoveNotification( const Path &dir );
 80
 81      Vector<Path>   mDirs;
 82      Vector<HANDLE> mHandleList;
 83   };
 84
 85//-----------------------------------------------------------------------------
 86
 87static String _BuildFileName(const String& prefix,const Path& path)
 88{
 89   // Need to join the path (minus the root) with our
 90   // internal path name.
 91   String file = prefix;
 92   file = Path::Join(file, '/', path.getPath());
 93   file = Path::Join(file, '/', path.getFileName());
 94   file = Path::Join(file, '.', path.getExtension());
 95   return file;
 96}
 97
 98/*
 99static bool _IsFile(const String& file)
100{
101   // Get file info
102   WIN32_FIND_DATA info;
103   HANDLE handle = ::FindFirstFile(PathToOS(file).utf16(), &info);
104   ::FindClose(handle);
105   if (handle == INVALID_HANDLE_VALUE)
106      return false;
107
108   return S_ISREG(info.dwFileAttributes);
109}
110*/
111
112static bool _IsDirectory(const String& file)
113{
114   // Get file info
115   WIN32_FIND_DATAW info;
116   HANDLE handle = ::FindFirstFileW(PathToOS(file).utf16(), &info);
117   ::FindClose(handle);
118   if (handle == INVALID_HANDLE_VALUE)
119      return false;
120
121   return S_ISDIR(info.dwFileAttributes);
122}
123
124
125//-----------------------------------------------------------------------------
126
127static void _CopyStatAttributes(const WIN32_FIND_DATAW& info, FileNode::Attributes* attr)
128{
129   // Fill in the return struct.
130   attr->flags = 0;
131   if (S_ISDIR(info.dwFileAttributes))
132      attr->flags |= FileNode::Directory;
133   if (S_ISREG(info.dwFileAttributes))
134      attr->flags |= FileNode::File;
135
136   if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
137      attr->flags |= FileNode::ReadOnly;
138
139   attr->size = info.nFileSizeLow;
140   attr->mtime = Win32FileTimeToTime(
141      info.ftLastWriteTime.dwLowDateTime,
142      info.ftLastWriteTime.dwHighDateTime);
143
144   attr->atime = Win32FileTimeToTime(
145      info.ftLastAccessTime.dwLowDateTime,
146      info.ftLastAccessTime.dwHighDateTime);
147}
148
149
150//-----------------------------------------------------------------------------
151
152bool Win32FileSystemChangeNotifier::internalAddNotification( const Path &dir )
153{
154   for ( U32 i = 0; i < mDirs.size(); ++i )
155   {
156      if ( mDirs[i] == dir )
157         return false;
158   }
159
160   Path     fullFSPath = mFS->mapTo( dir );
161   String   osPath = PathToOS( fullFSPath );
162
163//   Con::printf( "[Win32FileSystemChangeNotifier::internalAddNotification] : [%s]", osPath.c_str() );
164
165   HANDLE   changeHandle = ::FindFirstChangeNotificationW( 
166                                 osPath.utf16(),      // directory to watch 
167                                 FALSE,                           // do not watch subtree 
168                                 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES);  // watch file write changes
169
170   if (changeHandle == INVALID_HANDLE_VALUE || changeHandle == NULL) 
171   {
172      Con::errorf("[Win32FileSystemChangeNotifier::internalAddNotification] : failed on [%s] [%d]", osPath.c_str(), GetLastError());
173      
174      return false; 
175   }
176
177   mDirs.push_back( dir );
178   mHandleList.push_back( changeHandle );
179
180   return true;
181}
182
183bool Win32FileSystemChangeNotifier::internalRemoveNotification( const Path &dir )
184{
185   for ( U32 i = 0; i < mDirs.size(); ++i )
186   {
187      if ( mDirs[i] != dir )
188         continue;
189
190      ::FindCloseChangeNotification( mHandleList[i] );
191      mDirs.erase( i );
192      mHandleList.erase( i );
193
194      return true;
195   }
196
197   return false;
198}
199
200void  Win32FileSystemChangeNotifier::internalProcessOnce()
201{
202   // WaitForMultipleObjects has a limit of MAXIMUM_WAIT_OBJECTS (64 at 
203   // the moment), so we have to loop till we've handled the entire set.
204
205   for ( U32 i=0; i < mHandleList.size(); i += MAXIMUM_WAIT_OBJECTS )
206   {
207      U32 numHandles = getMin( (U32)MAXIMUM_WAIT_OBJECTS, (U32)mHandleList.size() - i );
208
209      DWORD dwWaitStatus = WaitForMultipleObjects( numHandles, mHandleList.address()+i, FALSE, 0);
210      if ( dwWaitStatus == WAIT_FAILED || dwWaitStatus == WAIT_TIMEOUT )
211         continue;
212
213      if ( dwWaitStatus >= WAIT_OBJECT_0 && dwWaitStatus <= (WAIT_OBJECT_0 + numHandles - 1))
214      {
215         U32 index = i + dwWaitStatus;
216
217         // reset our notification
218         // NOTE: we do this before letting the volume system check mod times so we don't miss any.
219         //    It is going to loop over the files and check their mod time vs. the saved time.
220         //    This may result in extra calls to internalNotifyDirChanged(), but it will simpley check mod times again.
221         ::FindNextChangeNotification( mHandleList[index] );
222
223         internalNotifyDirChanged( mDirs[index] );
224      }
225   }
226}
227
228//-----------------------------------------------------------------------------
229
230Win32FileSystem::Win32FileSystem(String volume)
231{
232   mVolume = volume;
233   mChangeNotifier = new Win32FileSystemChangeNotifier( this );
234}
235
236Win32FileSystem::~Win32FileSystem()
237{
238}
239
240void Win32FileSystem::verifyCompatibility(const Path& _path, WIN32_FIND_DATAW _info)
241{
242   if (_path.getFullFileName().isNotEmpty() && _path.getFullFileName().compare(String(_info.cFileName)) != 0)
243   {
244      Con::warnf("Linux Compatibility Warning: %s != %s", String(_info.cFileName).c_str(), _path.getFullFileName().c_str());
245   }
246}
247
248FileNodeRef Win32FileSystem::resolve(const Path& path)
249{
250   String file = _BuildFileName(mVolume,path);
251
252   WIN32_FIND_DATAW info;
253   HANDLE handle = ::FindFirstFileW(PathToOS(file).utf16(), &info);
254   ::FindClose(handle);
255   if (handle != INVALID_HANDLE_VALUE)
256   {
257#ifdef TORQUE_DEBUG
258      verifyCompatibility(path, info);
259#endif
260      if (S_ISREG(info.dwFileAttributes))
261         return new Win32File(path,file);
262      if (S_ISDIR(info.dwFileAttributes))
263         return new Win32Directory(path,file);
264   }
265   return 0;
266}
267
268FileNodeRef Win32FileSystem::create(const Path& path, FileNode::Mode mode)
269{
270   // The file will be created on disk when it's opened.
271   if (mode & FileNode::File)
272      return new Win32File(path,_BuildFileName(mVolume,path));
273
274   // Create with default permissions.
275   if (mode & FileNode::Directory)
276   {
277      String file = PathToOS(_BuildFileName(mVolume,path));
278
279      if (::CreateDirectoryW(file.utf16(), 0))
280         return new Win32Directory(path, file);
281   }
282   return 0;
283}
284
285bool Win32FileSystem::remove(const Path& path)
286{
287   // Should probably check for outstanding files or directory objects.
288   String file = PathToOS(_BuildFileName(mVolume,path));
289
290   WIN32_FIND_DATAW info;
291   HANDLE handle = ::FindFirstFileW(file.utf16(), &info);
292   ::FindClose(handle);
293   if (handle == INVALID_HANDLE_VALUE)
294      return false;
295
296   if (S_ISDIR(info.dwFileAttributes))
297      return ::RemoveDirectoryW(file.utf16());
298
299   return ::DeleteFileW(file.utf16());
300}
301
302bool Win32FileSystem::rename(const Path& from,const Path& to)
303{
304   String fa = PathToOS(_BuildFileName(mVolume,from));
305   String fb = PathToOS(_BuildFileName(mVolume,to));
306
307   return MoveFile(fa.utf16(),fb.utf16());
308}
309
310Path Win32FileSystem::mapTo(const Path& path)
311{
312   return _BuildFileName(mVolume,path);
313}
314
315Path Win32FileSystem::mapFrom(const Path& path)
316{
317   const String::SizeType  volumePathLen = mVolume.length();
318   
319   String   pathStr = path.getFullPath();
320
321   if ( mVolume.compare( pathStr, volumePathLen, String::NoCase ))
322      return Path();
323
324   return pathStr.substr( volumePathLen, pathStr.length() - volumePathLen );
325}
326
327//-----------------------------------------------------------------------------
328
329Win32File::Win32File(const Path& path,String name)
330{
331   mPath = path;
332   mName = name;
333   mStatus = Closed;
334   mHandle = 0;
335}
336
337Win32File::~Win32File()
338{
339   if (mHandle)
340      close();
341}
342
343Path Win32File::getName() const
344{
345   return mPath;
346}
347
348FileNode::NodeStatus Win32File::getStatus() const
349{
350   return mStatus;
351}
352
353bool Win32File::getAttributes(Attributes* attr)
354{
355   WIN32_FIND_DATAW info;
356   HANDLE handle = ::FindFirstFileW(PathToOS(mName).utf16(), &info);
357   ::FindClose(handle);
358   if (handle == INVALID_HANDLE_VALUE)
359      return false;
360
361   _CopyStatAttributes(info,attr);
362   attr->name = mPath;
363   return true;
364}
365
366U64 Win32File::getSize()
367{
368   U64 size;
369
370   if (mStatus == Open)
371   {
372      // Special case if file is open (handles unflushed buffers)
373      if ( !GetFileSizeEx(mHandle, (PLARGE_INTEGER)&size) )
374         size = 0;
375      return size;
376   }
377   else
378   {
379      // Fallback to generic function
380      size = File::getSize();
381   }
382
383   return size;
384}
385
386U32 Win32File::calculateChecksum()
387{
388   if (!open( Read ))
389      return 0;
390
391   U64 fileSize = getSize();
392   U32 bufSize = 1024 * 1024 * 4; // 4MB
393   FrameTemp<U8> buf( bufSize );
394   U32 crc = CRC::INITIAL_CRC_VALUE;
395
396   while ( fileSize > 0 )
397   {      
398      U32 bytesRead = getMin( fileSize, bufSize );
399      if ( read( buf, bytesRead ) != bytesRead )
400      {
401         close();
402         return 0;
403      }
404
405      fileSize -= bytesRead;
406      crc = CRC::calculateCRC(buf, bytesRead, crc);
407   }   
408
409   close();
410
411   return crc;
412}
413
414bool Win32File::open(AccessMode mode)
415{
416   close();
417
418   if (mName.isEmpty())
419      return mStatus;
420
421   struct Mode
422   {
423      DWORD mode,share,open;
424   } Modes[] =
425   {
426      { GENERIC_READ,FILE_SHARE_READ,OPEN_EXISTING }, // Read
427      { GENERIC_WRITE,0,CREATE_ALWAYS },              // Write
428      { GENERIC_WRITE | GENERIC_READ,0,OPEN_ALWAYS }, // ReadWrite
429      { GENERIC_WRITE,0,OPEN_ALWAYS }                 // WriteAppend
430   };
431
432   Mode& m = (mode == Read)? Modes[0]: (mode == Write)? Modes[1]:
433         (mode == ReadWrite)? Modes[2]: Modes[3];
434
435   mHandle = (void*)::CreateFileW(PathToOS(mName).utf16(),
436               m.mode, m.share,
437               NULL, m.open,
438               FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
439               NULL);
440
441   if ( mHandle == INVALID_HANDLE_VALUE || mHandle == NULL )
442   {
443      _updateStatus();
444      return false;
445   }
446
447   mStatus = Open;
448   return true;
449}
450
451bool Win32File::close()
452{
453   if (mHandle)
454   {
455      ::CloseHandle((HANDLE)mHandle);
456      mHandle = 0;
457   }
458
459   mStatus = Closed;
460   return true;
461}
462
463U32 Win32File::getPosition()
464{
465   if (mStatus == Open || mStatus == EndOfFile)
466      return ::SetFilePointer((HANDLE)mHandle,0,0,FILE_CURRENT);
467   return 0;
468}
469
470U32 Win32File::setPosition(U32 delta, SeekMode mode)
471{
472   if (mStatus != Open && mStatus != EndOfFile)
473      return 0;
474
475   DWORD fmode;
476   switch (mode)
477   {
478      case Begin:    fmode = FILE_BEGIN; break;
479      case Current:  fmode = FILE_CURRENT; break;
480      case End:      fmode = FILE_END; break;
481      default:       fmode = 0; break;
482   }
483
484   DWORD pos = ::SetFilePointer((HANDLE)mHandle,delta,0,fmode);
485   if (pos == INVALID_SET_FILE_POINTER)
486   {
487      mStatus = UnknownError;
488      return 0;
489   }
490
491   mStatus = Open;
492
493   return pos;
494}
495
496U32 Win32File::read(void* dst, U32 size)
497{
498   if (mStatus != Open && mStatus != EndOfFile)
499      return 0;
500
501   DWORD bytesRead;
502   if (!::ReadFile((HANDLE)mHandle,dst,size,&bytesRead,0))
503      _updateStatus();
504   else if (bytesRead != size)
505      mStatus = EndOfFile;
506
507   return bytesRead;
508}
509
510U32 Win32File::write(const void* src, U32 size)
511{
512   if ((mStatus != Open && mStatus != EndOfFile) || !size)
513      return 0;
514
515   DWORD bytesWritten;
516   if (!::WriteFile((HANDLE)mHandle,src,size,&bytesWritten,0))
517      _updateStatus();
518   return bytesWritten;
519}
520
521void Win32File::_updateStatus()
522{
523   switch (::GetLastError())
524   {
525      case ERROR_INVALID_ACCESS:       mStatus = AccessDenied;     break;
526      case ERROR_TOO_MANY_OPEN_FILES:  mStatus = UnknownError;     break;
527      case ERROR_PATH_NOT_FOUND:       mStatus = NoSuchFile;       break;
528      case ERROR_FILE_NOT_FOUND:       mStatus = NoSuchFile;       break;
529      case ERROR_SHARING_VIOLATION:    mStatus = SharingViolation; break;
530      case ERROR_HANDLE_DISK_FULL:     mStatus = FileSystemFull;   break;
531      case ERROR_ACCESS_DENIED:        mStatus = AccessDenied;     break;
532      default:                         mStatus = UnknownError;     break;
533   }
534}
535
536//-----------------------------------------------------------------------------
537
538Win32Directory::Win32Directory(const Path& path,String name)
539{
540   mPath = path;
541   mName = name;
542   mStatus = Closed;
543   mHandle = 0;
544}
545
546Win32Directory::~Win32Directory()
547{
548   if (mHandle)
549      close();
550}
551
552Path Win32Directory::getName() const
553{
554   return mPath;
555}
556
557bool Win32Directory::open()
558{
559   if (!_IsDirectory(mName))
560   {
561      mStatus = NoSuchFile;
562      return false;
563   }
564   mStatus = Open;
565   return true;
566}
567
568bool Win32Directory::close()
569{
570   if (mHandle)
571   {
572      ::FindClose((HANDLE)mHandle);
573      mHandle = 0;
574      return true;
575   }
576   return false;
577}
578
579bool Win32Directory::read(Attributes* entry)
580{
581   if (mStatus != Open)
582      return false;
583
584   WIN32_FIND_DATA info;
585   if (!mHandle)
586   {
587      mHandle = ::FindFirstFileW((PathToOS(mName) + "\\*").utf16(), &info);
588
589      if (mHandle == NULL)
590      {
591         _updateStatus();
592         return false;
593      }
594   }
595   else
596      if (!::FindNextFileW((HANDLE)mHandle, &info))
597      {
598         _updateStatus();
599         return false;
600      }
601
602   // Skip "." and ".." entries
603   if (info.cFileName[0] == '.' && (info.cFileName[1] == '\0' ||
604      (info.cFileName[1] == '.' && info.cFileName[2] == '\0')))
605      return read(entry);
606
607   _CopyStatAttributes(info,entry);
608   entry->name = info.cFileName;
609   return true;
610}
611
612
613U32 Win32Directory::calculateChecksum()
614{
615   // Return checksum of current entry
616   return 0;
617}
618
619bool Win32Directory::getAttributes(Attributes* attr)
620{
621   WIN32_FIND_DATA info;
622   HANDLE handle = ::FindFirstFileW(PathToOS(mName).utf16(), &info);
623   ::FindClose(handle);
624   if (handle == INVALID_HANDLE_VALUE)
625   {
626      _updateStatus();
627      return false;
628   }
629
630   _CopyStatAttributes(info,attr);
631   attr->name = mPath;
632   return true;
633}
634
635FileNode::NodeStatus Win32Directory::getStatus() const
636{
637   return mStatus;
638}
639
640void Win32Directory::_updateStatus()
641{
642   switch (::GetLastError())
643   {
644      case ERROR_NO_MORE_FILES:     mStatus = EndOfFile;        break;
645      case ERROR_INVALID_ACCESS:    mStatus = AccessDenied;     break;
646      case ERROR_PATH_NOT_FOUND:    mStatus = NoSuchFile;       break;
647      case ERROR_SHARING_VIOLATION: mStatus = SharingViolation; break;
648      case ERROR_ACCESS_DENIED:     mStatus = AccessDenied;     break;
649      default:                      mStatus = UnknownError;     break;
650   }
651}
652
653} // Namespace Win32
654
655bool FS::VerifyWriteAccess(const Path &path)
656{
657   // due to UAC's habit of creating "virtual stores" when permission isn't actually available
658   // actually create, write, read, verify, and delete a file to the folder being tested
659
660   String temp = path.getFullPath();
661   temp += "\\torque_writetest.tmp";
662
663   // first, (try and) delete the file if it exists   
664   ::DeleteFileW(temp.utf16());
665
666   // now, create the file
667
668   HANDLE hFile = ::CreateFileW(PathToOS(temp).utf16(),
669               GENERIC_WRITE, 0,
670               NULL, CREATE_ALWAYS,
671               FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
672               NULL);
673
674   if ( hFile == INVALID_HANDLE_VALUE || hFile == NULL )
675      return false;
676
677   U32 t = Platform::getTime();
678
679   DWORD bytesWritten;
680   if (!::WriteFile(hFile,&t,sizeof(t),&bytesWritten,0))
681   {
682      ::CloseHandle(hFile);
683      ::DeleteFileW(temp.utf16());
684      return false;
685   }
686
687   // close the file
688   ::CloseHandle(hFile);
689
690   // open for read
691
692   hFile = ::CreateFileW(PathToOS(temp).utf16(),
693               GENERIC_READ, FILE_SHARE_READ,
694               NULL, OPEN_EXISTING,
695               FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
696               NULL);
697
698   if ( hFile == INVALID_HANDLE_VALUE || hFile == NULL )
699      return false;
700
701   U32 t2 = 0;
702
703   DWORD bytesRead;
704   if (!::ReadFile(hFile,&t2,sizeof(t2),&bytesRead,0))
705   {
706      ::CloseHandle(hFile);
707      ::DeleteFileW(temp.utf16());
708      return false;
709   }
710
711   ::CloseHandle(hFile);
712   ::DeleteFileW(temp.utf16());
713
714   return t == t2;
715}
716
717
718} // Namespace Torque
719
720//-----------------------------------------------------------------------------
721
722Torque::FS::FileSystemRef  Platform::FS::createNativeFS( const String &volume )
723{
724   return new Win32::Win32FileSystem( volume );
725}
726
727String   Platform::FS::getAssetDir()
728{
729   char cen_buf[2048];
730#ifdef TORQUE_UNICODE
731   if (!Platform::getWebDeployment())
732   {
733      TCHAR buf[ 2048 ];
734      ::GetModuleFileNameW( NULL, buf, sizeof( buf ) );
735      convertUTF16toUTF8( buf, cen_buf );
736   }
737   else
738   {
739      TCHAR buf[ 2048 ];
740      GetCurrentDirectoryW( sizeof( buf ) / sizeof( buf[ 0 ] ), buf );
741      convertUTF16toUTF8( buf, cen_buf );
742      return Path::CleanSeparators(cen_buf);
743   }
744#else
745   ::GetModuleFileNameA( NULL, cen_buf, 2047);
746#endif
747
748   char *delimiter = dStrrchr( cen_buf, '\\' );
749
750   if( delimiter != NULL )
751      *delimiter = '\0';
752
753   return Path::CleanSeparators(cen_buf);
754}
755
756/// Function invoked by the kernel layer to install OS specific
757/// file systems.
758bool Platform::FS::InstallFileSystems()
759{
760   WCHAR buffer[1024];
761
762   // [8/24/2009 tomb] This stops Windows from complaining about drives that have no disks in
763   SetErrorMode(SEM_FAILCRITICALERRORS);
764
765   // Load all the Win32 logical drives.
766   DWORD mask = ::GetLogicalDrives();
767   char drive[] = "A";
768   char volume[] = "A:/";
769   while (mask)
770   {
771      if (mask & 1)
772      {
773         volume[0] = drive[0];
774         Platform::FS::Mount(drive, Platform::FS::createNativeFS(volume));
775      }
776      mask >>= 1;
777      drive[0]++;
778   }
779
780   // Set the current working dir.  Windows normally returns
781   // upper case driver letters, but the cygwin bash shell
782   // seems to make it return lower case drives. Force upper
783   // to be consistent with the mounts.
784   ::GetCurrentDirectory(sizeof(buffer), buffer);
785
786   if (buffer[1] == ':')
787      buffer[0] = dToupper(buffer[0]);
788
789   String   wd = buffer;
790   
791   wd += '/';
792
793   Platform::FS::SetCwd(wd);
794
795   return true;
796}
797
798
799