Przeglądaj źródła

Merge pull request #713 from Ragora/feature-vfs-security

Feature: VFS Security
Brian Roberts 3 lat temu
rodzic
commit
b753c9d91c

+ 43 - 18
Engine/source/T3D/assets/assetImporter.cpp

@@ -1426,7 +1426,7 @@ void AssetImportConfig::loadSISFile(Torque::Path filePath)
    String settingsFile = settingsFilePath + "/" + fileExtension + ".sis";
 
    FileObject* fileObj = new FileObject();
-   if (Platform::isFile(settingsFile))
+   if (Torque::FS::IsFile(settingsFile))
    {
       if (!fileObj->readMemory(settingsFile.c_str()))
       {
@@ -2170,7 +2170,7 @@ void AssetImporter::processShapeMaterialInfo(AssetImportObject* assetItem, S32 m
 
       if (fullFilePath.isNotEmpty())
       {
-         if (!Platform::isFile(fullFilePath.c_str()))
+         if (!Torque::FS::IsFile(fullFilePath.c_str()))
          {
             //could be a stale path reference, such as if it was downloaded elsewhere. Trim to just the filename and see
             //if we can find it there
@@ -2182,7 +2182,7 @@ void AssetImporter::processShapeMaterialInfo(AssetImportObject* assetItem, S32 m
             if(filePath.getPath().isEmpty())
                fullFilePath = shapePathBase + "/" + fullFilePath;
  
-            if (Platform::isFile(fullFilePath.c_str()))
+            if (Torque::FS::IsFile(fullFilePath.c_str()))
             {
                filePath = Torque::Path(fullFilePath);
             }
@@ -2316,7 +2316,7 @@ void AssetImporter::validateAsset(AssetImportObject* assetItem)
       }
    }
 
-   if (!assetItem->filePath.isEmpty() && !assetItem->generatedAsset && !Platform::isFile(assetItem->filePath.getFullPath().c_str()))
+   if (!assetItem->filePath.isEmpty() && !assetItem->generatedAsset && !Torque::FS::IsFile(assetItem->filePath.getFullPath().c_str()))
    {
       assetItem->status = "Error";
       assetItem->statusType = "MissingFile";
@@ -2806,15 +2806,20 @@ Torque::Path AssetImporter::importImageAsset(AssetImportObject* assetItem)
    char qualifiedFromFile[2048];
    char qualifiedToFile[2048];
 
+#ifndef TORQUE_SECURE_VFS
    Platform::makeFullPathName(originalPath.c_str(), qualifiedFromFile, sizeof(qualifiedFromFile));
    Platform::makeFullPathName(assetPath.c_str(), qualifiedToFile, sizeof(qualifiedToFile));
+#else
+   dStrcpy(qualifiedFromFile, originalPath.c_str(), sizeof(qualifiedFromFile));
+   dStrcpy(qualifiedToFile, assetPath.c_str(), sizeof(qualifiedToFile));
+#endif
    
    newAsset->setAssetName(assetName);
    newAsset->setImageFileName(imageFileName.c_str());
 
    //If it's not a re-import, check that the file isn't being in-place imported. If it isn't, store off the original
    //file path for reimporting support later
-   if (!isReimport && String::compare(qualifiedFromFile, qualifiedToFile) && Platform::isFile(qualifiedFromFile))
+   if (!isReimport && String::compare(qualifiedFromFile, qualifiedToFile) && Torque::FS::IsFile(qualifiedFromFile))
    {
       newAsset->setDataField(StringTable->insert("originalFilePath"), nullptr, qualifiedFromFile);
    }
@@ -2843,7 +2848,7 @@ Torque::Path AssetImporter::importImageAsset(AssetImportObject* assetItem)
    {
       bool isInPlace = !String::compare(qualifiedFromFile, qualifiedToFile);
 
-      if (!isInPlace && !dPathCopy(qualifiedFromFile, qualifiedToFile, !isReimport))
+      if (!isInPlace && !Torque::FS::CopyFile(qualifiedFromFile, qualifiedToFile, !isReimport))
       {
          dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Unable to copy file %s", assetItem->filePath.getFullPath().c_str());
          activityLog.push_back(importLogBuffer);
@@ -2869,11 +2874,15 @@ Torque::Path AssetImporter::importMaterialAsset(AssetImportObject* assetItem)
 
    char qualifiedFromFile[2048];
 
+#ifndef TORQUE_SECURE_VFS
    Platform::makeFullPathName(originalPath.c_str(), qualifiedFromFile, sizeof(qualifiedFromFile));
+#else
+   dStrcpy(qualifiedFromFile, originalPath.c_str(), sizeof(qualifiedFromFile));
+#endif
 
    newAsset->setAssetName(assetName);
 
-   if (!isReimport && Platform::isFile(qualifiedFromFile))
+   if (!isReimport && Torque::FS::IsFile(qualifiedFromFile))
    {
    newAsset->setDataField(StringTable->insert("originalFilePath"), nullptr, qualifiedFromFile);
    }
@@ -3035,11 +3044,17 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
    char qualifiedFromCSFile[2048];
    char qualifiedToCSFile[2048];
 
+#ifndef TORQUE_SECURE_VFS
    Platform::makeFullPathName(originalPath.c_str(), qualifiedFromFile, sizeof(qualifiedFromFile));
    Platform::makeFullPathName(assetPath.c_str(), qualifiedToFile, sizeof(qualifiedToFile));
-
    Platform::makeFullPathName(originalConstructorPath.c_str(), qualifiedFromCSFile, sizeof(qualifiedFromCSFile));
    Platform::makeFullPathName(constructorPath.c_str(), qualifiedToCSFile, sizeof(qualifiedToCSFile));
+#else
+   dStrcpy(qualifiedFromFile, originalPath.c_str(), sizeof(qualifiedFromFile));
+   dStrcpy(qualifiedToFile, assetPath.c_str(), sizeof(qualifiedToFile));
+   dStrcpy(qualifiedFromCSFile, originalConstructorPath.c_str(), sizeof(qualifiedFromCSFile));
+   dStrcpy(qualifiedToCSFile, constructorPath.c_str(), sizeof(qualifiedToCSFile));
+#endif
 
    newAsset->setAssetName(assetName);
    newAsset->setShapeFile(shapeFileName.c_str());
@@ -3057,7 +3072,7 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
 
    //If it's not a re-import, check that the file isn't being in-place imported. If it isn't, store off the original
    //file path for reimporting support later
-   if (!isReimport && String::compare(qualifiedFromFile, qualifiedToFile) && Platform::isFile(qualifiedFromFile))
+   if (!isReimport && String::compare(qualifiedFromFile, qualifiedToFile) && Torque::FS::IsFile(qualifiedFromFile))
    {
       newAsset->setDataField(StringTable->insert("originalFilePath"), nullptr, qualifiedFromFile);
    }
@@ -3121,7 +3136,7 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
    {
       bool isInPlace = !String::compare(qualifiedFromFile, qualifiedToFile);
 
-      if (!isInPlace && !dPathCopy(qualifiedFromFile, qualifiedToFile, !isReimport))
+      if (!isInPlace && !Torque::FS::CopyFile(qualifiedFromFile, qualifiedToFile, !isReimport))
       {
          dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Unable to copy file %s", qualifiedFromFile);
          activityLog.push_back(importLogBuffer);
@@ -3130,9 +3145,9 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
 
       if (!isInPlace)
       {
-         if (Platform::isFile(qualifiedFromCSFile))
+         if (Torque::FS::IsFile(qualifiedFromCSFile))
          {
-            if (!dPathCopy(qualifiedFromCSFile, qualifiedToCSFile, !isReimport))
+            if (!Torque::FS::CopyFile(qualifiedFromCSFile, qualifiedToCSFile, !isReimport))
             {
                dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Unable to copy file %s", qualifiedFromCSFile);
                activityLog.push_back(importLogBuffer);
@@ -3149,7 +3164,7 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
       else
       {
          //We're doing an in-place import, so double check we've already got a constructor file in the expected spot
-         if (Platform::isFile(qualifiedFromCSFile))
+         if (Torque::FS::IsFile(qualifiedFromCSFile))
          {
             //Yup, found it, we're good to go
             makeNewConstructor = false;
@@ -3162,7 +3177,7 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
             Torque::Path constrFilePath = qualifiedFromCSFile;
             constrFilePath.setExtension("cs");
 
-            if (Platform::isFile(constrFilePath.getFullPath().c_str()))
+            if (Torque::FS::IsFile(constrFilePath.getFullPath().c_str()))
             {
                //Yup, found it, we're good to go
                makeNewConstructor = false;
@@ -3324,15 +3339,20 @@ Torque::Path AssetImporter::importSoundAsset(AssetImportObject* assetItem)
    char qualifiedFromFile[2048];
    char qualifiedToFile[2048];
 
+#ifndef TORQUE_SECURE_VFS
    Platform::makeFullPathName(originalPath.c_str(), qualifiedFromFile, sizeof(qualifiedFromFile));
    Platform::makeFullPathName(assetPath.c_str(), qualifiedToFile, sizeof(qualifiedToFile));
+#else
+   dStrcpy(qualifiedFromFile, originalPath.c_str(), sizeof(qualifiedFromFile));
+   dStrcpy(qualifiedToFile, assetPath.c_str(), sizeof(qualifiedToFile));
+#endif
 
    newAsset->setAssetName(assetName);
    newAsset->setSoundFile(imageFileName.c_str());
 
    //If it's not a re-import, check that the file isn't being in-place imported. If it isn't, store off the original
    //file path for reimporting support later
-   if (!isReimport && String::compare(qualifiedFromFile, qualifiedToFile) && Platform::isFile(qualifiedFromFile))
+   if (!isReimport && String::compare(qualifiedFromFile, qualifiedToFile) && Torque::FS::IsFile(qualifiedFromFile))
    {
       newAsset->setDataField(StringTable->insert("originalFilePath"), nullptr, qualifiedFromFile);
    }
@@ -3351,7 +3371,7 @@ Torque::Path AssetImporter::importSoundAsset(AssetImportObject* assetItem)
    {
       bool isInPlace = !String::compare(qualifiedFromFile, qualifiedToFile);
 
-      if (!isInPlace && !dPathCopy(qualifiedFromFile, qualifiedToFile, !isReimport))
+      if (!isInPlace && !Torque::FS::CopyFile(qualifiedFromFile, qualifiedToFile, !isReimport))
       {
          dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Unable to copy file %s", assetItem->filePath.getFullPath().c_str());
          activityLog.push_back(importLogBuffer);
@@ -3380,15 +3400,20 @@ Torque::Path AssetImporter::importShapeAnimationAsset(AssetImportObject* assetIt
    char qualifiedFromFile[2048];
    char qualifiedToFile[2048];
 
+#ifndef TORQUE_SECURE_VFS
    Platform::makeFullPathName(originalPath.c_str(), qualifiedFromFile, sizeof(qualifiedFromFile));
    Platform::makeFullPathName(assetPath.c_str(), qualifiedToFile, sizeof(qualifiedToFile));
+#else
+   dStrcpy(qualifiedFromFile, originalPath.c_str(), sizeof(qualifiedFromFile));
+   dStrcpy(qualifiedToFile, assetPath.c_str(), sizeof(qualifiedToFile));
+#endif
 
    newAsset->setAssetName(assetName);
    newAsset->setAnimationFile(imageFileName.c_str());
 
    //If it's not a re-import, check that the file isn't being in-place imported. If it isn't, store off the original
    //file path for reimporting support later
-   if (!isReimport && String::compare(qualifiedFromFile, qualifiedToFile) && Platform::isFile(qualifiedFromFile))
+   if (!isReimport && String::compare(qualifiedFromFile, qualifiedToFile) && Torque::FS::IsFile(qualifiedFromFile))
    {
       newAsset->setDataField(StringTable->insert("originalFilePath"), nullptr, qualifiedFromFile);
    }
@@ -3407,7 +3432,7 @@ Torque::Path AssetImporter::importShapeAnimationAsset(AssetImportObject* assetIt
    {
       bool isInPlace = !String::compare(qualifiedFromFile, qualifiedToFile);
 
-      if (!isInPlace && !dPathCopy(qualifiedFromFile, qualifiedToFile, !isReimport))
+      if (!isInPlace && !Torque::FS::CopyFile(qualifiedFromFile, qualifiedToFile, !isReimport))
       {
          dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Unable to copy file %s", assetItem->filePath.getFullPath().c_str());
          activityLog.push_back(importLogBuffer);

+ 4 - 4
Engine/source/T3D/lighting/reflectionProbe.cpp

@@ -330,15 +330,15 @@ void ReflectionProbe::handleDeleteAction()
    if (mReflectionModeType != StaticCubemap)
    {
       String prefilPath = getPrefilterMapPath();
-      if (Platform::isFile(prefilPath))
+      if (Torque::FS::IsFile(prefilPath))
       {
-         Platform::fileDelete(prefilPath);
+         Torque::FS::Remove(prefilPath);
       }
 
       String irrPath = getIrradianceMapPath();
-      if (Platform::isFile(irrPath))
+      if (Torque::FS::IsFile(irrPath))
       {
-         Platform::fileDelete(irrPath);
+         Torque::FS::Remove(irrPath);
       }
    }
 

+ 1 - 1
Engine/source/console/consoleFunctions.cpp

@@ -2157,7 +2157,7 @@ DefineEngineFunction( gotoWebPage, void, ( const char* address ),,
    // the bogus url, and hope for the best.
    
    String addr;
-   if( Platform::isFile( address ) || Platform::isDirectory( address ) )
+   if( Torque::FS::IsFile( address ) || Torque::FS::IsDirectory( address ) )
    {
 #ifdef TORQUE2D_TOOLS_FIXME
       addr = String::ToString( "file://%s", address );

+ 74 - 80
Engine/source/console/fileSystemFunctions.cpp

@@ -368,15 +368,11 @@ DefineEngineFunction(getFileCRC, S32, ( const char* fileName ),,
    
    "@ingroup FileSystem")
 {
-   String cleanfilename(Torque::Path::CleanSeparators(fileName));
-   Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), cleanfilename.c_str());
-
-   Torque::Path givenPath(Torque::Path::CompressPath(sgScriptFilenameBuffer));
-   Torque::FS::FileNodeRef fileRef = Torque::FS::GetFileNode( givenPath );
+   Torque::FS::FileNodeRef fileRef = Torque::FS::GetFileNode( fileName );
 
    if ( fileRef == NULL )
    {
-      Con::errorf("getFileCRC() - could not access file: [%s]", givenPath.getFullPath().c_str() );
+      Con::errorf("getFileCRC() - could not access file: [%s]", fileName );
       return -1;
    }
 
@@ -391,10 +387,7 @@ DefineEngineFunction(isFile, bool, ( const char* fileName ),,
    
    "@ingroup FileSystem")
 {
-   String cleanfilename(Torque::Path::CleanSeparators(fileName));
-   Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), cleanfilename.c_str());
-
-   Torque::Path givenPath(Torque::Path::CompressPath(sgScriptFilenameBuffer));
+   Torque::Path givenPath(fileName);
 
    if (givenPath.getFileName().isEmpty() && givenPath.getExtension().isNotEmpty())
    {
@@ -415,11 +408,7 @@ DefineEngineFunction(isScriptFile, bool, (const char* fileName), ,
 
    "@ingroup FileSystem")
 {
-   String cleanfilename(Torque::Path::CleanSeparators(fileName));
-   Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), cleanfilename.c_str());
-
-   Torque::Path givenPath(Torque::Path::CompressPath(sgScriptFilenameBuffer));
-   return Torque::FS::IsScriptFile(givenPath.getFullPath());
+   return Torque::FS::IsScriptFile(fileName);
 }
 
 DefineEngineFunction( IsDirectory, bool, ( const char* directory ),,
@@ -432,11 +421,7 @@ DefineEngineFunction( IsDirectory, bool, ( const char* directory ),,
 
    "@ingroup FileSystem")
 {
-   String dir(Torque::Path::CleanSeparators(directory));
-   Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), dir.c_str());
-
-   Torque::Path givenPath(Torque::Path::CompressPath(sgScriptFilenameBuffer));
-   return Torque::FS::IsDirectory( givenPath );
+   return Torque::FS::IsDirectory( directory );
 }
 
 DefineEngineFunction(isWriteableFileName, bool, ( const char* fileName ),,
@@ -447,14 +432,7 @@ DefineEngineFunction(isWriteableFileName, bool, ( const char* fileName ),,
 
    "@ingroup FileSystem")
 {
-   String filename(Torque::Path::CleanSeparators(fileName));
-   Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), filename.c_str());
-
-   Torque::Path givenPath(Torque::Path::CompressPath(sgScriptFilenameBuffer));
-   Torque::FS::FileSystemRef fs = Torque::FS::GetFileSystem(givenPath);
-   Torque::Path path = fs->mapTo(givenPath);
-
-   return !Torque::FS::IsReadOnly(path);
+   return !Torque::FS::IsReadOnly(fileName);
 }
 
 DefineEngineFunction(startFileChangeNotifications, void, (),,
@@ -487,7 +465,12 @@ DefineEngineFunction(getDirectoryList, String, ( const char* path, S32 depth ),
 {
    // Grab the full path.
    char fullpath[1024];
+
+#ifdef TORQUE_SECURE_VFS
+   dStrcpy(fullpath, path, sizeof(fullpath));
+#else
    Platform::makeFullPathName(String::compare(path, "/") == 0 ? "" : path, fullpath, sizeof(fullpath));
+#endif
 
    //dSprintf(fullpath, 511, "%s/%s", Platform::getWorkingDirectory(), path);
 
@@ -501,7 +484,7 @@ DefineEngineFunction(getDirectoryList, String, ( const char* path, S32 depth ),
 
    // Dump the directories.
    Vector<StringTableEntry> directories;
-   Platform::dumpDirectories(fullpath, directories, depth, true);
+   Torque::FS::DumpDirectories(fullpath, directories, depth, true);
 
    if( directories.empty() )
       return "";
@@ -539,8 +522,12 @@ DefineEngineFunction(fileSize, S32, ( const char* fileName ),,
 
    "@ingroup FileSystem")
 {
-   Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), fileName);
-   return Platform::getFileSize( sgScriptFilenameBuffer );
+   StrongRefPtr<Torque::FS::FileNode> node = Torque::FS::GetFileNode(fileName);
+   if (node.isValid())
+   {
+      return node->getSize();
+   }
+   return -1;
 }
 
 DefineEngineFunction( fileModifiedTime, String, ( const char* fileName ),,
@@ -550,20 +537,20 @@ DefineEngineFunction( fileModifiedTime, String, ( const char* fileName ),,
    "@return Formatted string (OS specific) containing modified time, \"9/3/2010 12:33:47 PM\" for example\n"
    "@ingroup FileSystem")
 {
-   Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), fileName);
+   Torque::FS::FileNodeRef node = Torque::FS::GetFileNode(fileName);
 
-   FileTime ft = {0};
-   Platform::getFileTimes( sgScriptFilenameBuffer, NULL, &ft );
+   if (node)
+   {
+      Platform::LocalTime lt = node->getModifiedTime().toLocalTime();
 
-   Platform::LocalTime lt = {0};
-   Platform::fileToLocalTime( ft, &lt );   
-   
-   String fileStr = Platform::localTimeToString( lt );
-   
-   char *buffer = Con::getReturnBuffer( fileStr.size() );
-   dStrcpy( buffer, fileStr, fileStr.size() );
-   
-   return buffer;
+      String fileStr = Platform::localTimeToString(lt);
+
+      char *buffer = Con::getReturnBuffer(fileStr.size());
+      dStrcpy(buffer, fileStr, fileStr.size());
+
+      return buffer;
+   }
+   return "";
 }
 
 DefineEngineFunction( fileCreatedTime, String, ( const char* fileName ),,
@@ -573,20 +560,20 @@ DefineEngineFunction( fileCreatedTime, String, ( const char* fileName ),,
    "@return Formatted string (OS specific) containing created time, \"9/3/2010 12:33:47 PM\" for example\n"
    "@ingroup FileSystem")
 {
-   Con::expandScriptFilename( sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), fileName );
-
-   FileTime ft = {0};
-   Platform::getFileTimes( sgScriptFilenameBuffer, &ft, NULL );
+   Torque::FS::FileNodeRef node = Torque::FS::GetFileNode(fileName);
 
-   Platform::LocalTime lt = {0};
-   Platform::fileToLocalTime( ft, &lt );   
+   if (node)
+   {
+      Platform::LocalTime lt = node->getCreatedTime().toLocalTime();
 
-   String fileStr = Platform::localTimeToString( lt );
+      String fileStr = Platform::localTimeToString(lt);
 
-   char *buffer = Con::getReturnBuffer( fileStr.size() );
-   dStrcpy( buffer, fileStr, fileStr.size() );
+      char *buffer = Con::getReturnBuffer(fileStr.size());
+      dStrcpy(buffer, fileStr, fileStr.size());
 
-   return buffer;
+      return buffer;
+   }
+   return "";
 }
 
 DefineEngineFunction(compareFileTimes, S32, (const char* fileA, const char* fileB), ("", ""),
@@ -597,17 +584,33 @@ DefineEngineFunction(compareFileTimes, S32, (const char* fileA, const char* file
    "@return S32. If value is 1, then fileA is newer. If value is -1, then fileB is newer. If value is 0, they are equal.\n"
    "@ingroup FileSystem")
 {
-   Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), fileA);
+   Torque::FS::FileNodeRef nodeA = Torque::FS::GetFileNode(fileA);
+   Torque::FS::FileNodeRef nodeB = Torque::FS::GetFileNode(fileB);
 
-   FileTime fileATime = { 0 };
-   Platform::getFileTimes(sgScriptFilenameBuffer, NULL, &fileATime);
+   // Can't do anything if either file doesn't exist
+   if (!nodeA || !nodeB)
+   {
+      return 0;
+   }
 
-   Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), fileB);
+   Torque::FS::FileNode::Attributes fileAAttributes;
+   Torque::FS::FileNode::Attributes fileBAttributes;
 
-   FileTime fileBTime = { 0 };
-   Platform::getFileTimes(sgScriptFilenameBuffer, NULL, &fileBTime);
+   // If retrieval of attributes fails, we can't compare   
+   if (!nodeA->getAttributes(&fileAAttributes) || !nodeB->getAttributes(&fileBAttributes))
+   {
+      return 0;
+   }
 
-   return Platform::compareFileTimes(fileATime, fileBTime);
+   if (fileAAttributes.mtime > fileBAttributes.mtime)
+   {
+      return 1;
+   }
+   else if (fileAAttributes.mtime < fileBAttributes.mtime)
+   {
+      return -1;
+   }
+   return 0;
 }
 
 DefineEngineFunction(fileDelete, bool, ( const char* path ),,
@@ -618,13 +621,7 @@ DefineEngineFunction(fileDelete, bool, ( const char* path ),,
    "@return True if file was successfully deleted\n"
    "@ingroup FileSystem")
 {
-   static char fileName[1024];
-   static char sandboxFileName[1024];
-
-   Con::expandScriptFilename( fileName, sizeof( fileName ), path );
-   Platform::makeFullPathName(fileName, sandboxFileName, sizeof(sandboxFileName));
-
-   return dFileDelete(sandboxFileName);
+   return Torque::FS::Remove(path);
 }
 
 
@@ -839,13 +836,7 @@ DefineEngineFunction( pathCopy, bool, ( const char* fromFile, const char* toFile
    "@note Only present in a Tools build of Torque.\n"
    "@ingroup FileSystem")
 {
-   char qualifiedFromFile[ 2048 ];
-   char qualifiedToFile[ 2048 ];
-   
-   Platform::makeFullPathName( fromFile, qualifiedFromFile, sizeof( qualifiedFromFile ) );
-   Platform::makeFullPathName( toFile, qualifiedToFile, sizeof( qualifiedToFile ) );
-
-   return dPathCopy( qualifiedFromFile, qualifiedToFile, noOverwrite );
+   return Torque::FS::CopyFile(fromFile, toFile, noOverwrite);
 }
 
 //-----------------------------------------------------------------------------
@@ -857,7 +848,11 @@ DefineEngineFunction( getCurrentDirectory, String, (),,
    "@see getWorkingDirectory()"
    "@ingroup FileSystem")
 {
+#ifdef TORQUE_SECURE_VFS
+   return Torque::FS::GetCwd();
+#else
    return Platform::getCurrentDirectory();
+#endif
 }
 
 //-----------------------------------------------------------------------------
@@ -870,8 +865,11 @@ DefineEngineFunction( setCurrentDirectory, bool, ( const char* path ),,
    "@note Only present in a Tools build of Torque.\n"
    "@ingroup FileSystem")
 {
+#ifdef TORQUE_SECURE_VFS
+   return Torque::FS::SetCwd(path);
+#else
    return Platform::setCurrentDirectory( StringTable->insert( path ) );
-
+#endif
 }
 
 //-----------------------------------------------------------------------------
@@ -885,11 +883,7 @@ DefineEngineFunction( createPath, bool, ( const char* path ),,
    "@note Only present in a Tools build of Torque.\n"
    "@ingroup FileSystem" )
 {
-   static char pathName[1024];
-
-   Con::expandScriptFilename( pathName, sizeof( pathName ), path );
-
-   return Platform::createPath( pathName );
+   return Torque::FS::CreatePath(path);
 }
 
 DefineEngineFunction(deleteDirectory, bool, (const char* path), ,

+ 5 - 2
Engine/source/core/memVolume.cpp

@@ -46,7 +46,8 @@ namespace Torque
             mBuffer = dMalloc(mBufferSize);
             dMemset(mBuffer, 0, mBufferSize);
             mModified = Time::getCurrentTime();
-            mLastAccess = mModified;    
+            mLastAccess = mModified;
+            mCreated = mModified;
             mFileSystem = fs;
          }
 
@@ -62,6 +63,7 @@ namespace Torque
             attr->size = mFileSize;
             attr->mtime = mModified;
             attr->atime = mLastAccess;
+            attr->ctime = mCreated;
             return true;
          }
 
@@ -81,6 +83,7 @@ namespace Torque
          U32 mBufferSize;  // This is the size of the memory buffer >= mFileSize
          U32 mFileSize;    // This is the size of the "file" <= mBufferSize
          Time mModified;      // Last modified
+         Time mCreated; // When Created
          Time mLastAccess;      // Last access
          MemFileSystem* mFileSystem;
       };
@@ -508,4 +511,4 @@ namespace Torque
       }
    } // Namespace Mem
 
-} // Namespace Torque
+} // Namespace Torque

+ 1 - 1
Engine/source/core/memVolume.h

@@ -129,4 +129,4 @@ namespace Torque
    } // Namespace
 } // Namespace
 
-#endif
+#endif

+ 25 - 0
Engine/source/core/util/timeClass.cpp

@@ -195,4 +195,29 @@ void Time::get(S32 *pyear, S32 *pmonth, S32 *pday, S32 *phour, S32 *pminute, S32
       *pmicrosecond = time % OneSecond;
 }
 
+Platform::LocalTime Time::toLocalTime()
+{
+   Platform::LocalTime result;
+   result.isdst = false;
+
+   S32 year;
+   S32 month;
+   S32 day;
+   S32 hour;
+   S32 minute;
+   S32 second;
+   S32 microsecond;
+
+   get(&year, &month, &day, &hour, &minute, &second, &microsecond);
+   result.year = year - 1900;
+   result.month = month - 1;
+   result.yearday = day;
+   result.hour = hour;
+   result.min = minute;
+   result.sec = second;
+   result.monthday = day % 32;
+   result.weekday = day % 7;
+   return result;
+}
+
 } // Namespace

+ 5 - 1
Engine/source/core/util/timeClass.h

@@ -26,7 +26,9 @@
 #ifndef _TORQUE_TYPES_H_
 #include "platform/types.h"
 #endif
-
+#ifndef _PLATFORM_H_
+#include "platform/platform.h"
+#endif
 
 #if defined(TORQUE_COMPILER_VISUALC)
    #define TORQUE_CONSTANT_S64(a) (a##I64)
@@ -105,6 +107,8 @@ public:
    S64 getMicroseconds() const;
    S64 getInternalRepresentation() const;
 
+   Platform::LocalTime toLocalTime();
+
 private:
    class Tester
    {

+ 3 - 0
Engine/source/core/util/zip/zipVolume.cpp

@@ -86,6 +86,7 @@ public:
       // use the mod time for both mod and access time, since we only have mod time in the CD
       attr->mtime = ZipArchive::DOSTimeToTime(mZipEntry->mCD.mModTime, mZipEntry->mCD.mModDate);
       attr->atime = ZipArchive::DOSTimeToTime(mZipEntry->mCD.mModTime, mZipEntry->mCD.mModDate);
+      attr->ctime = ZipArchive::DOSTimeToTime(mZipEntry->mCD.mModTime, mZipEntry->mCD.mModDate);
       attr->size = mZipEntry->mCD.mUncompressedSize;
 
       return true; 
@@ -197,6 +198,7 @@ public:
       // use the mod time for both mod and access time, since we only have mod time in the CD
       attr->mtime = ZipArchive::DOSTimeToTime(mZipEntry->mCD.mModTime, mZipEntry->mCD.mModDate);
       attr->atime = ZipArchive::DOSTimeToTime(mZipEntry->mCD.mModTime, mZipEntry->mCD.mModDate);
+      attr->ctime = ZipArchive::DOSTimeToTime(mZipEntry->mCD.mModTime, mZipEntry->mCD.mModDate);
       attr->size = mZipEntry->mCD.mUncompressedSize;
 
       return true; 
@@ -291,6 +293,7 @@ public:
       ZipArchive::ZipEntry* zipEntry = mArchive->getRoot();
       attr->mtime = ZipArchive::DOSTimeToTime(zipEntry->mCD.mModTime, zipEntry->mCD.mModDate);
       attr->atime = ZipArchive::DOSTimeToTime(zipEntry->mCD.mModTime, zipEntry->mCD.mModDate);
+      attr->ctime = ZipArchive::DOSTimeToTime(zipEntry->mCD.mModTime, zipEntry->mCD.mModDate);
       attr->size = zipEntry->mCD.mUncompressedSize;
 
       return true; 

+ 1 - 1
Engine/source/core/util/zip/zipVolume.h

@@ -75,4 +75,4 @@ private:
 
 }
 
-#endif
+#endif

+ 170 - 9
Engine/source/core/volume.cpp

@@ -255,6 +255,64 @@ File::~File() {}
 Directory::Directory() {}
 Directory::~Directory() {}
 
+bool Directory::dump(Vector<Path>& out)
+{
+   const Path sourcePath = getName();
+
+   FileNode::Attributes currentAttributes;
+   while (read(&currentAttributes))
+   {
+      Path currentPath = sourcePath;
+      currentPath.appendPath(currentPath.getFileName());
+      currentPath.setFileName(currentAttributes.name);
+
+      out.push_back(currentPath);
+   }
+
+   return true;
+}
+
+bool Directory::dumpFiles(Vector<Path>& out)
+{
+   const Path sourcePath = getName();
+
+   FileNode::Attributes currentAttributes;
+   while (read(&currentAttributes))
+   {
+      Path currentPath = sourcePath;
+      currentPath.appendPath(currentPath.getFileName());
+      currentPath.setFileName(currentAttributes.name);
+
+      if (IsFile(currentPath))
+      {
+         out.push_back(currentPath);
+      }
+   }
+
+   return true;
+}
+
+bool Directory::dumpDirectories(Vector<Path>& out)
+{
+   const Path sourcePath = getName();
+
+   FileNode::Attributes currentAttributes;
+   while (read(&currentAttributes))
+   {
+      Path currentPath = sourcePath;
+      currentPath.appendPath(currentPath.getFileName());
+      currentPath.setFileName(currentAttributes.name);
+
+      const bool result = IsDirectory(currentPath);
+      if (result)
+      {
+         out.push_back(currentPath);
+      }
+   }
+
+   return true;
+}
+
 
 FileNode::FileNode()
 :  mChecksum(0)
@@ -273,6 +331,18 @@ Time   FileNode::getModifiedTime()
    return attrs.mtime;
 }
 
+Time  FileNode::getCreatedTime()
+{
+   Attributes attrs;
+
+   bool success = getAttributes(&attrs);
+
+   if (!success)
+      return Time();
+
+   return attrs.ctime;
+}
+
 U64    FileNode::getSize()
 {
    Attributes attrs;
@@ -500,6 +570,90 @@ Path MountSystem::_normalize(const Path& path)
    return po;
 }
 
+bool MountSystem::copyFile(const Path& source, const Path& destination, bool noOverwrite)
+{
+   // Exit out early if we're not overriding
+   if (isFile(destination) && noOverwrite)
+   {
+      return true;
+   }
+
+   FileRef sourceFile = openFile(source, FS::File::AccessMode::Read);
+   const U64 sourceFileSize = sourceFile->getSize();
+
+   void* writeBuffer = dMalloc(sourceFileSize);
+   sourceFile->read(writeBuffer, sourceFileSize);
+
+   FileRef destinationFile = openFile(destination, FS::File::AccessMode::Write);
+   const bool success = destinationFile->write(writeBuffer, sourceFileSize) == sourceFileSize;
+
+   dFree(writeBuffer);
+
+   return success;
+}
+
+bool MountSystem::_dumpDirectories(DirectoryRef directory, Vector<StringTableEntry>& directories, S32 depth, bool noBasePath, S32 currentDepth, const Path& basePath)
+{
+   Vector<Torque::Path> directoryPaths;
+   if (!directory->dumpDirectories(directoryPaths))
+   {
+      return false;
+   }
+
+   // Queries against / will return a directory count of 1, but the code relies on actual directory entries (Eg. /data) so we handle that special case
+   const U32 basePathDirectoryCount = String::compare(basePath.getFullPathWithoutRoot(), "/") == 0 ? basePath.getDirectoryCount() - 1 : basePath.getDirectoryCount();
+
+   for (U32 iteration = 0; iteration < directoryPaths.size(); ++iteration)
+   {
+      const Path& directoryPath = directoryPaths[iteration];
+
+      // Load the full path to the directory unless we're not supposed to include base paths
+      String directoryPathString = directoryPath.getFullPath().c_str();
+      if (noBasePath)
+      {
+         // Build a path representing the directory tree *after* the base path query but excluding the base
+         // So if we queried for data/ and are currently processing data/ExampleModule/datablocks we want to output
+         // ExampleModule/datablocks
+         Path newDirectoryPath;
+         for (U32 iteration = basePathDirectoryCount; iteration < directoryPath.getDirectoryCount(); ++iteration)
+         {
+            if (iteration > basePathDirectoryCount)
+            {
+               newDirectoryPath.setPath(newDirectoryPath.getPath() + "/");
+            }
+            newDirectoryPath.setPath(newDirectoryPath.getPath() + directoryPath.getDirectory(iteration));
+         }
+
+         newDirectoryPath.setFileName(directoryPath.getFileName());
+         newDirectoryPath.setExtension(directoryPath.getExtension());
+         directoryPathString = newDirectoryPath.getFullPathWithoutRoot();
+      }
+
+      // Output result and enumerate subdirectories if we're not too deep according to the depth parameter
+      directories.push_back(StringTable->insert(directoryPathString, true));
+      if (currentDepth <= depth)
+      {
+         const String subdirectoryPath = directoryPath.getFullPath() + "/";
+
+         DirectoryRef nextDirectory = OpenDirectory(subdirectoryPath);
+         _dumpDirectories(nextDirectory, directories, depth, noBasePath, currentDepth + 1, basePath);
+      }
+   }
+
+   return true;
+}
+
+bool MountSystem::dumpDirectories(const Path& path, Vector<StringTableEntry>& directories, S32 depth, bool noBasePath)
+{
+   if (!isDirectory(path))
+   {
+      return false;
+   }
+
+   DirectoryRef sourceDirectory = openDirectory(path);
+   return _dumpDirectories(sourceDirectory, directories, depth, noBasePath, 1, path);
+}
+
 FileRef MountSystem::createFile(const Path& path)
 {
    Path np = _normalize(path);
@@ -792,22 +946,19 @@ bool MountSystem::isDirectory(const Path& path, FileSystemRef fsRef)
 {
    FileNode::Attributes attr;
 
+   bool result = false;
    if (fsRef.isNull())
    {
-      if (getFileAttributes(path,&attr))
-         return attr.flags & FileNode::Directory;
-      return false;
+      if (getFileAttributes(path, &attr))
+         result = (attr.flags & FileNode::Directory) != 0;
    }
    else
    {
       FileNodeRef fnRef = fsRef->resolve(path);
-      if (fnRef.isNull())
-         return false;
-
-      if (fnRef->getAttributes(&attr))
-         return attr.flags & FileNode::Directory;
-      return false;
+      if (!fnRef.isNull() && fnRef->getAttributes(&attr))
+         result = (attr.flags & FileNode::Directory) != 0;
    }
+   return result;
 }
 
 bool MountSystem::isReadOnly(const Path& path)
@@ -905,6 +1056,16 @@ FileRef CreateFile(const Path &path)
    return sgMountSystem.createFile(path);
 }
 
+bool CopyFile(const Path& source, const Path& destination, bool noOverwrite)
+{
+   return sgMountSystem.copyFile(source, destination, noOverwrite);
+}
+
+bool DumpDirectories(const Path& path, Vector<StringTableEntry>& directories, S32 depth, bool noBasePath)
+{
+   return sgMountSystem.dumpDirectories(path, directories, depth, noBasePath);
+}
+
 DirectoryRef CreateDirectory(const Path &path)
 {
    return sgMountSystem.createDirectory(path);

+ 18 - 1
Engine/source/core/volume.h

@@ -114,6 +114,7 @@ public:
       String   name;    ///< File/Directory name
       Time     mtime;   ///< Last modified time
       Time     atime;   ///< Last access time
+      Time     ctime;   ///< Creation Time
       U64      size;
    };
 
@@ -128,6 +129,7 @@ public:
 
    // Convenience routines - may be overridden for optimal access
    virtual Time   getModifiedTime();   ///< @note This will return Time() on failure
+   virtual Time   getCreatedTime();      ///< @note This will return Time() on failure
    virtual U64    getSize();           ///< @note This will return 0 on failure
    virtual U32    getChecksum();       ///< @note This will return 0 on failure
 
@@ -201,7 +203,11 @@ public:
    // Functions
    virtual bool open() = 0;
    virtual bool close() = 0;
-   virtual bool read(Attributes*) = 0;   
+   virtual bool read(Attributes*) = 0;
+
+   bool dump(Vector<Path>& out);
+   bool dumpFiles(Vector<Path>& out);
+   bool dumpDirectories(Vector<Path>& out);
 };
 
 typedef WeakRefPtr<Directory> DirectoryPtr;
@@ -335,6 +341,9 @@ public:
    virtual ~MountSystem() {}
 
    FileRef createFile(const Path& path);
+   bool copyFile(const Path& source, const Path& destination, bool noOverwrite);
+   bool dumpDirectories(const Path& path, Vector<StringTableEntry>& directories, S32 depth, bool noBasePath);
+
    DirectoryRef createDirectory(const Path& path, FileSystemRef fs = NULL);
    virtual bool createPath(const Path& path);
 
@@ -374,6 +383,8 @@ public:
    void  startFileChangeNotifications();
    void  stopFileChangeNotifications();
 
+private:
+   bool _dumpDirectories(DirectoryRef directory, Vector<StringTableEntry>& directories, S32 depth, bool noBasePath, S32 currentDepth, const Path& basePath);
 protected:
    virtual void _log(const String& msg);
 
@@ -538,6 +549,12 @@ DirectoryRef OpenDirectory(const Path &file);
 ///@ingroup VolumeSystem
 FileRef CreateFile(const Path &file);
 
+/// Copy a file from one location to another.
+bool CopyFile(const Path& source, const Path& destination, bool noOverride);
+
+/// Retrieve list of directories in the specified directory.
+bool DumpDirectories(const Path& path, Vector<StringTableEntry>& directories, S32 depth, bool noBasePath);
+
 /// Create a directory.
 /// The directory object is returned in a closed state.
 ///@ingroup VolumeSystem

+ 6 - 1
Engine/source/persistence/taml/taml.cpp

@@ -212,8 +212,13 @@ ImplementEnumType(_TamlFormatMode,
       AssertFatal(pSimObject != NULL, "Cannot write a NULL object.");
       AssertFatal(pFilename != NULL, "Cannot write to a NULL filename.");
 
-      // Expand the file-name into the file-path buffer.
+      // Expand the file-name into the file-path buffer unless we're a secure VFS
+#ifndef TORQUE_SECURE_VFS
       Con::expandToolScriptFilename(mFilePathBuffer, sizeof(mFilePathBuffer), pFilename);
+#else
+      dMemset(mFilePathBuffer, 0x00, sizeof(mFilePathBuffer));
+      dMemcpy(mFilePathBuffer, pFilename, dStrlen(pFilename));
+#endif
 
       FileStream stream;
 

+ 5 - 0
Engine/source/platform/platformFileIO.cpp

@@ -328,8 +328,13 @@ char * Platform::makeFullPathName(const char *path, char *buffer, U32 size, cons
    // [rene, 05/05/2008] Based on overall file handling in Torque, it does not seem to make
    //    that much sense to me to base things off the current working directory here.
 
+   #ifndef TORQUE_SCECURE_VFS
    if(cwd == NULL)
       cwd = Con::isCurrentScriptToolScript() ? Platform::getMainDotCsDir() : Platform::getCurrentDirectory();
+   #else
+   if (cwd == NULL)
+      cwd = "game:/";
+   #endif
 
    dStrncpy(buffer, cwd, size);
    buffer[size-1] = 0;

+ 43 - 0
Engine/source/platform/platformVolume.cpp

@@ -20,6 +20,7 @@
 // IN THE SOFTWARE.
 //-----------------------------------------------------------------------------
 
+#include "console/console.h"
 #include "platform/platform.h"
 
 #if defined(TORQUE_OS_WIN)
@@ -48,7 +49,49 @@ bool  MountDefaults()
    if ( !mounted )
       return false;
 
+#ifdef TORQUE_SECURE_VFS
+   mounted = Mount("/", createNativeFS(path));
+   if (!mounted)
+   {
+      return false;
+   }
+#endif
+
 #ifndef TORQUE_DISABLE_VIRTUAL_MOUNT_SYSTEM
+   // Always mount the data & home dir so scripts work in either configuration. This is used for eg. preferences storage.
+   Path dataDirectory = Platform::getUserDataDirectory();
+   Path appDataDirectory = Path::Join(dataDirectory.getFullPath().c_str(), '/', TORQUE_APP_NAME);
+   Path homeDirectory = Platform::getUserHomeDirectory();
+   Path appHomeDirectory = Path::Join(homeDirectory.getFullPath().c_str(), '/', TORQUE_APP_NAME);
+
+   // Ensure the root of the data directory exists before trying to mount data VFS
+   if (!Platform::FS::IsDirectory(appDataDirectory) && !Platform::FS::CreateDirectory(appDataDirectory))
+   {
+      // NOTE: We can't Con::errorf here because it doesn't actually output by this point in execution
+   }
+
+   // Ensure the root of the home directory exists before trying to mount home VFS
+   if (!Platform::FS::IsDirectory(appHomeDirectory) && !Platform::FS::CreateDirectory(appHomeDirectory))
+   {
+      // NOTE: We can't Con::errorf here because it doesn't actually output by this point in execution
+   }
+
+   // data:/ points to a directory that is usually buried someplace harder to reach on OS
+   mounted = Mount("data", Platform::FS::createNativeFS(appDataDirectory.getFullPath()));
+   if (!mounted)
+   {
+      // NOTE: We can't Con::errorf here because it doesn't actually output by this point in execution
+      return false;
+   }
+
+   // home:/ refers to your Documents/<APPNAME> folder which is easier to reach than the data:/ mount
+   mounted = Mount("home", Platform::FS::createNativeFS(appHomeDirectory.getFullPath()));
+   if (!mounted)
+   {
+      // NOTE: We can't Con::errorf here because it doesn't actually output by this point in execution
+      return false;
+   }
+
    // Note that the VirtualMountSystem must be enabled in volume.cpp for zip support to work.
    return MountZips("game");
 #else

+ 3 - 0
Engine/source/platformPOSIX/posixVolume.cpp

@@ -135,6 +135,7 @@ static void copyStatAttributes(const struct stat& info, FileNode::Attributes* at
    attr->size = info.st_size;
    attr->mtime = UnixTimeToTime(info.st_mtime);
    attr->atime = UnixTimeToTime(info.st_atime);
+   attr->ctime = UnixTimeToTime(info.st_ctime);
 }
 
 
@@ -595,6 +596,7 @@ String   Platform::FS::getAssetDir()
 /// file systems.
 bool Platform::FS::InstallFileSystems()
 {
+#ifndef TORQUE_SECURE_VFS
    Platform::FS::Mount( "/", Platform::FS::createNativeFS( String() ) );
 
    // Setup the current working dir.
@@ -611,6 +613,7 @@ bool Platform::FS::InstallFileSystems()
    // Mount the home directory
    if (char* home = getenv("HOME"))
       Platform::FS::Mount( "home", Platform::FS::createNativeFS(home) );
+#endif
 
    return true;
 }

+ 6 - 0
Engine/source/platformWin32/winVolume.cpp

@@ -143,6 +143,10 @@ static void _CopyStatAttributes(const WIN32_FIND_DATAW& info, FileNode::Attribut
    attr->atime = Win32FileTimeToTime(
       info.ftLastAccessTime.dwLowDateTime,
       info.ftLastAccessTime.dwHighDateTime);
+
+   attr->ctime = Win32FileTimeToTime(
+      info.ftCreationTime.dwLowDateTime,
+      info.ftCreationTime.dwHighDateTime);
 }
 
 
@@ -758,6 +762,7 @@ String   Platform::FS::getAssetDir()
 /// file systems.
 bool Platform::FS::InstallFileSystems()
 {
+#ifndef TORQUE_SECURE_VFS
    WCHAR buffer[1024];
 
    // [8/24/2009 tomb] This stops Windows from complaining about drives that have no disks in
@@ -792,6 +797,7 @@ bool Platform::FS::InstallFileSystems()
    wd += '/';
 
    Platform::FS::SetCwd(wd);
+#endif
 
    return true;
 }

+ 1 - 4
Engine/source/sfx/sfxProfile.cpp

@@ -284,10 +284,7 @@ bool SFXProfile::_preloadBuffer()
 
 Resource<SFXResource>& SFXProfile::getResource()
 {
-   char buf[1024];
-   FileName fullFilename = String(Platform::makeFullPathName(mFilename, buf, sizeof(buf)));
-
-   if (!mResource && SFXResource::exists(fullFilename))
+   if (!mResource && SFXResource::exists(mFilename))
       mResource = SFXResource::load(mFilename);
    else
       mResource = NULL;

+ 2 - 0
Engine/source/windowManager/sdl/sdlWindowMgr.cpp

@@ -22,6 +22,8 @@
 
 #include "windowManager/sdl/sdlWindowMgr.h"
 #include "platformSDL/sdlInputManager.h"
+#include "platform/platformVolume.h"
+#include "core/util/path.h"
 #include "gfx/gfxDevice.h"
 #include "core/util/journal/process.h"
 #include "core/strings/unicode.h"

+ 1 - 1
Templates/BaseGame/game/core/utility/scripts/helperFunctions.tscript

@@ -175,7 +175,7 @@ function getUserPath()
 
 function getPrefpath()
 {
-   $prefPath = getUserPath() @ "/preferences";
+   $prefPath = "home:/preferences";
 	return $prefPath;
 }
 

+ 4 - 1
Tools/CMake/torque3d.cmake

@@ -169,6 +169,9 @@ if(NOT MSVC AND NOT APPLE) # handle single-configuration generator
     mark_as_advanced(TORQUE_ADDITIONAL_LINKER_FLAGS)
 endif()
 
+option(TORQUE_SECURE_VFS "Secure VFS configuration. Arbitrary script access to file system will be heavily restricted." OFF)
+mark_as_advanced(TORQUE_SECURE_VFS)
+
 option(TORQUE_MULTITHREAD "Multi Threading" ON)
 mark_as_advanced(TORQUE_MULTITHREAD)
 
@@ -999,4 +1002,4 @@ if(TORQUE_SDL)
     else()
         set_target_properties(SDL2 PROPERTIES FOLDER ${TORQUE_LIBS_FOLDER_NAME})
     endif()
-endif()
+endif()

+ 4 - 1
Tools/CMake/torqueConfig.h.in

@@ -50,6 +50,9 @@
 /// Define me if you want path case insensitivity support in ZIP files.
 #cmakedefine TORQUE_ZIP_PATH_CASE_INSENSITIVE
 
+/// Define me if you want to enable secure VFS support.
+#cmakedefine TORQUE_SECURE_VFS
+
 /// Define me if you want to enable multithreading support.
 #cmakedefine TORQUE_MULTITHREAD
 
@@ -225,4 +228,4 @@
 #endif
 
 /// Password to use when opening encrypted zip files. Change this to whatever the password is for your zips.
-#define DEFAULT_ZIP_PASSWORD     "@TORQUE_APP_PASSWORD@"
+#define DEFAULT_ZIP_PASSWORD     "@TORQUE_APP_PASSWORD@"