summaryrefslogtreecommitdiffstats
path: root/gfx/vr/service/openvr/src/pathtools_public.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/vr/service/openvr/src/pathtools_public.cpp')
-rw-r--r--gfx/vr/service/openvr/src/pathtools_public.cpp901
1 files changed, 901 insertions, 0 deletions
diff --git a/gfx/vr/service/openvr/src/pathtools_public.cpp b/gfx/vr/service/openvr/src/pathtools_public.cpp
new file mode 100644
index 0000000000..2f401fa501
--- /dev/null
+++ b/gfx/vr/service/openvr/src/pathtools_public.cpp
@@ -0,0 +1,901 @@
+//========= Copyright Valve Corporation ============//
+#include "strtools_public.h"
+#include "pathtools_public.h"
+
+#if defined( _WIN32)
+#include <windows.h>
+#include <direct.h>
+#include <shobjidl.h>
+#include <knownfolders.h>
+#include <shlobj.h>
+#include <share.h>
+
+#undef GetEnvironmentVariable
+#else
+#include <dlfcn.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <alloca.h>
+#endif
+
+#if defined OSX
+#include <Foundation/Foundation.h>
+#include <AppKit/AppKit.h>
+#include <mach-o/dyld.h>
+#define _S_IFDIR S_IFDIR // really from tier0/platform.h which we dont have yet
+#endif
+
+#include <sys/stat.h>
+
+#include <algorithm>
+
+/** Returns the path (including filename) to the current executable */
+std::string Path_GetExecutablePath()
+{
+#if defined( _WIN32 )
+ wchar_t *pwchPath = new wchar_t[MAX_UNICODE_PATH];
+ char *pchPath = new char[MAX_UNICODE_PATH_IN_UTF8];
+ ::GetModuleFileNameW( NULL, pwchPath, MAX_UNICODE_PATH );
+ WideCharToMultiByte( CP_UTF8, 0, pwchPath, -1, pchPath, MAX_UNICODE_PATH_IN_UTF8, NULL, NULL );
+ delete[] pwchPath;
+
+ std::string sPath = pchPath;
+ delete[] pchPath;
+ return sPath;
+#elif defined( OSX )
+ char rchPath[1024];
+ uint32_t nBuff = sizeof( rchPath );
+ bool bSuccess = _NSGetExecutablePath(rchPath, &nBuff) == 0;
+ rchPath[nBuff-1] = '\0';
+ if( bSuccess )
+ return rchPath;
+ else
+ return "";
+#elif defined LINUX
+ char rchPath[1024];
+ size_t nBuff = sizeof( rchPath );
+ ssize_t nRead = readlink("/proc/self/exe", rchPath, nBuff-1 );
+ if ( nRead != -1 )
+ {
+ rchPath[ nRead ] = 0;
+ return rchPath;
+ }
+ else
+ {
+ return "";
+ }
+#else
+ AssertMsg( false, "Implement Plat_GetExecutablePath" );
+ return "";
+#endif
+
+}
+
+/** Returns the path of the current working directory */
+std::string Path_GetWorkingDirectory()
+{
+ std::string sPath;
+#if defined( _WIN32 )
+ wchar_t buf[MAX_UNICODE_PATH];
+ sPath = UTF16to8( _wgetcwd( buf, MAX_UNICODE_PATH ) );
+#else
+ char buf[ 1024 ];
+ sPath = getcwd( buf, sizeof( buf ) );
+#endif
+ return sPath;
+}
+
+/** Sets the path of the current working directory. Returns true if this was successful. */
+bool Path_SetWorkingDirectory( const std::string & sPath )
+{
+ bool bSuccess;
+#if defined( _WIN32 )
+ std::wstring wsPath = UTF8to16( sPath.c_str() );
+ bSuccess = 0 == _wchdir( wsPath.c_str() );
+#else
+ bSuccess = 0 == chdir( sPath.c_str() );
+#endif
+ return bSuccess;
+}
+
+/** Gets the path to a temporary directory. */
+std::string Path_GetTemporaryDirectory()
+{
+#if defined( _WIN32 )
+ wchar_t buf[MAX_UNICODE_PATH];
+ if ( GetTempPathW( MAX_UNICODE_PATH, buf ) == 0 )
+ return Path_GetWorkingDirectory();
+ return UTF16to8( buf );
+#else
+ const char *pchTmpDir = getenv( "TMPDIR" );
+ if ( pchTmpDir == NULL )
+ {
+ return "";
+ }
+ return pchTmpDir;
+#endif
+}
+
+/** Returns the specified path without its filename */
+std::string Path_StripFilename( const std::string & sPath, char slash )
+{
+ if( slash == 0 )
+ slash = Path_GetSlash();
+
+ std::string::size_type n = sPath.find_last_of( slash );
+ if( n == std::string::npos )
+ return sPath;
+ else
+ return std::string( sPath.begin(), sPath.begin() + n );
+}
+
+/** returns just the filename from the provided full or relative path. */
+std::string Path_StripDirectory( const std::string & sPath, char slash )
+{
+ if( slash == 0 )
+ slash = Path_GetSlash();
+
+ std::string::size_type n = sPath.find_last_of( slash );
+ if( n == std::string::npos )
+ return sPath;
+ else
+ return std::string( sPath.begin() + n + 1, sPath.end() );
+}
+
+/** returns just the filename with no extension of the provided filename.
+* If there is a path the path is left intact. */
+std::string Path_StripExtension( const std::string & sPath )
+{
+ for( std::string::const_reverse_iterator i = sPath.rbegin(); i != sPath.rend(); i++ )
+ {
+ if( *i == '.' )
+ {
+ return std::string( sPath.begin(), i.base() - 1 );
+ }
+
+ // if we find a slash there is no extension
+ if( *i == '\\' || *i == '/' )
+ break;
+ }
+
+ // we didn't find an extension
+ return sPath;
+}
+
+/** returns just extension of the provided filename (if any). */
+std::string Path_GetExtension( const std::string & sPath )
+{
+ for ( std::string::const_reverse_iterator i = sPath.rbegin(); i != sPath.rend(); i++ )
+ {
+ if ( *i == '.' )
+ {
+ return std::string( i.base(), sPath.end() );
+ }
+
+ // if we find a slash there is no extension
+ if ( *i == '\\' || *i == '/' )
+ break;
+ }
+
+ // we didn't find an extension
+ return "";
+}
+
+bool Path_IsAbsolute( const std::string & sPath )
+{
+ if( sPath.empty() )
+ return false;
+
+#if defined( WIN32 )
+ if ( sPath.size() < 3 ) // must be c:\x or \\x at least
+ return false;
+
+ if ( sPath[1] == ':' ) // drive letter plus slash, but must test both slash cases
+ {
+ if ( sPath[2] == '\\' || sPath[2] == '/' )
+ return true;
+ }
+ else if ( sPath[0] == '\\' && sPath[1] == '\\' ) // UNC path
+ return true;
+#else
+ if( sPath[0] == '\\' || sPath[0] == '/' ) // any leading slash
+ return true;
+#endif
+
+ return false;
+}
+
+
+/** Makes an absolute path from a relative path and a base path */
+std::string Path_MakeAbsolute( const std::string & sRelativePath, const std::string & sBasePath )
+{
+ if( Path_IsAbsolute( sRelativePath ) )
+ return Path_Compact( sRelativePath );
+ else
+ {
+ if( !Path_IsAbsolute( sBasePath ) )
+ return "";
+
+ std::string sCompacted = Path_Compact( Path_Join( sBasePath, sRelativePath ) );
+ if( Path_IsAbsolute( sCompacted ) )
+ return sCompacted;
+ else
+ return "";
+ }
+}
+
+
+/** Fixes the directory separators for the current platform */
+std::string Path_FixSlashes( const std::string & sPath, char slash )
+{
+ if( slash == 0 )
+ slash = Path_GetSlash();
+
+ std::string sFixed = sPath;
+ for( std::string::iterator i = sFixed.begin(); i != sFixed.end(); i++ )
+ {
+ if( *i == '/' || *i == '\\' )
+ *i = slash;
+ }
+
+ return sFixed;
+}
+
+
+char Path_GetSlash()
+{
+#if defined(_WIN32)
+ return '\\';
+#else
+ return '/';
+#endif
+}
+
+/** Jams two paths together with the right kind of slash */
+std::string Path_Join( const std::string & first, const std::string & second, char slash )
+{
+ if( slash == 0 )
+ slash = Path_GetSlash();
+
+ // only insert a slash if we don't already have one
+ std::string::size_type nLen = first.length();
+ if( !nLen )
+ return second;
+#if defined(_WIN32)
+ if( first.back() == '\\' || first.back() == '/' )
+ nLen--;
+#else
+ char last_char = first[first.length()-1];
+ if (last_char == '\\' || last_char == '/')
+ nLen--;
+#endif
+
+ return first.substr( 0, nLen ) + std::string( 1, slash ) + second;
+}
+
+
+std::string Path_Join( const std::string & first, const std::string & second, const std::string & third, char slash )
+{
+ return Path_Join( Path_Join( first, second, slash ), third, slash );
+}
+
+std::string Path_Join( const std::string & first, const std::string & second, const std::string & third, const std::string &fourth, char slash )
+{
+ return Path_Join( Path_Join( Path_Join( first, second, slash ), third, slash ), fourth, slash );
+}
+
+std::string Path_Join(
+ const std::string & first,
+ const std::string & second,
+ const std::string & third,
+ const std::string & fourth,
+ const std::string & fifth,
+ char slash )
+{
+ return Path_Join( Path_Join( Path_Join( Path_Join( first, second, slash ), third, slash ), fourth, slash ), fifth, slash );
+}
+
+
+std::string Path_RemoveTrailingSlash( const std::string & sRawPath, char slash )
+{
+ if ( slash == 0 )
+ slash = Path_GetSlash();
+
+ std::string sPath = sRawPath;
+ std::string::size_type nCurrent = sRawPath.length();
+ if ( nCurrent == 0 )
+ return sPath;
+
+ int nLastFound = -1;
+ nCurrent--;
+ while( nCurrent != 0 )
+ {
+ if ( sRawPath[ nCurrent ] == slash )
+ {
+ nLastFound = (int)nCurrent;
+ nCurrent--;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if ( nLastFound >= 0 )
+ {
+ sPath.erase( nLastFound, std::string::npos );
+ }
+
+ return sPath;
+}
+
+
+/** Removes redundant <dir>/.. elements in the path. Returns an empty path if the
+* specified path has a broken number of directories for its number of ..s */
+std::string Path_Compact( const std::string & sRawPath, char slash )
+{
+ if( slash == 0 )
+ slash = Path_GetSlash();
+
+ std::string sPath = Path_FixSlashes( sRawPath, slash );
+ std::string sSlashString( 1, slash );
+
+ // strip out all /./
+ for( std::string::size_type i = 0; (i + 3) < sPath.length(); )
+ {
+ if( sPath[ i ] == slash && sPath[ i+1 ] == '.' && sPath[ i+2 ] == slash )
+ {
+ sPath.replace( i, 3, sSlashString );
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+
+ // get rid of trailing /. but leave the path separator
+ if( sPath.length() > 2 )
+ {
+ std::string::size_type len = sPath.length();
+ if( sPath[ len-1 ] == '.' && sPath[ len-2 ] == slash )
+ {
+ sPath.pop_back();
+ //Not sure why the following line of code was used for a while. It causes problems with strlen.
+ //sPath[len-1] = 0; // for now, at least
+ }
+ }
+
+ // get rid of leading ./
+ if( sPath.length() > 2 )
+ {
+ if( sPath[ 0 ] == '.' && sPath[ 1 ] == slash )
+ {
+ sPath.replace( 0, 2, "" );
+ }
+ }
+
+ // each time we encounter .. back up until we've found the previous directory name
+ // then get rid of both
+ std::string::size_type i = 0;
+ while( i < sPath.length() )
+ {
+ if( i > 0 && sPath.length() - i >= 2
+ && sPath[i] == '.'
+ && sPath[i+1] == '.'
+ && ( i + 2 == sPath.length() || sPath[ i+2 ] == slash )
+ && sPath[ i-1 ] == slash )
+ {
+ // check if we've hit the start of the string and have a bogus path
+ if( i == 1 )
+ return "";
+
+ // find the separator before i-1
+ std::string::size_type iDirStart = i-2;
+ while( iDirStart > 0 && sPath[ iDirStart - 1 ] != slash )
+ --iDirStart;
+
+ // remove everything from iDirStart to i+2
+ sPath.replace( iDirStart, (i - iDirStart) + 3, "" );
+
+ // start over
+ i = 0;
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+ return sPath;
+}
+
+
+/** Returns true if these two paths are the same without respect for internal . or ..
+* sequences, slash type, or case (on case-insensitive platforms). */
+bool Path_IsSamePath( const std::string & sPath1, const std::string & sPath2 )
+{
+ std::string sCompact1 = Path_Compact( sPath1 );
+ std::string sCompact2 = Path_Compact( sPath2 );
+#if defined(WIN32)
+ return !stricmp( sCompact1.c_str(), sCompact2.c_str() );
+#else
+ return !strcmp( sCompact1.c_str(), sCompact2.c_str() );
+#endif
+}
+
+
+/** Returns the path to the current DLL or exe */
+std::string Path_GetThisModulePath()
+{
+ // gets the path of vrclient.dll itself
+#ifdef WIN32
+ HMODULE hmodule = NULL;
+
+ ::GetModuleHandleEx( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast<LPCTSTR>(Path_GetThisModulePath), &hmodule );
+
+ wchar_t *pwchPath = new wchar_t[MAX_UNICODE_PATH];
+ char *pchPath = new char[ MAX_UNICODE_PATH_IN_UTF8 ];
+ ::GetModuleFileNameW( hmodule, pwchPath, MAX_UNICODE_PATH );
+ WideCharToMultiByte( CP_UTF8, 0, pwchPath, -1, pchPath, MAX_UNICODE_PATH_IN_UTF8, NULL, NULL );
+ delete[] pwchPath;
+
+ std::string sPath = pchPath;
+ delete [] pchPath;
+ return sPath;
+
+#elif defined( OSX ) || defined( LINUX )
+ // get the addr of a function in vrclient.so and then ask the dlopen system about it
+ Dl_info info;
+ dladdr( (void *)Path_GetThisModulePath, &info );
+ return info.dli_fname;
+#endif
+
+}
+
+
+/** returns true if the specified path exists and is a directory */
+bool Path_IsDirectory( const std::string & sPath )
+{
+ std::string sFixedPath = Path_FixSlashes( sPath );
+ if( sFixedPath.empty() )
+ return false;
+ char cLast = sFixedPath[ sFixedPath.length() - 1 ];
+ if( cLast == '/' || cLast == '\\' )
+ sFixedPath.erase( sFixedPath.end() - 1, sFixedPath.end() );
+
+ // see if the specified path actually exists.
+
+#if defined(POSIX)
+ struct stat buf;
+ if ( stat( sFixedPath.c_str(), &buf ) == -1 )
+ {
+ return false;
+ }
+
+#if defined( LINUX ) || defined( OSX )
+ return S_ISDIR( buf.st_mode );
+#else
+ return (buf.st_mode & _S_IFDIR) != 0;
+#endif
+
+#else
+ struct _stat buf;
+ std::wstring wsFixedPath = UTF8to16( sFixedPath.c_str() );
+ if ( _wstat( wsFixedPath.c_str(), &buf ) == -1 )
+ {
+ return false;
+ }
+
+ return (buf.st_mode & _S_IFDIR) != 0;
+#endif
+}
+
+/** returns true if the specified path represents an app bundle */
+bool Path_IsAppBundle( const std::string & sPath )
+{
+#if defined(OSX)
+ @autoreleasepool {
+ NSBundle *bundle = [ NSBundle bundleWithPath: [ NSString stringWithUTF8String:sPath.c_str() ] ];
+ bool bisAppBundle = ( nullptr != bundle );
+ return bisAppBundle;
+ }
+#else
+ return false;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the the path exists
+//-----------------------------------------------------------------------------
+bool Path_Exists( const std::string & sPath )
+{
+ std::string sFixedPath = Path_FixSlashes( sPath );
+ if( sFixedPath.empty() )
+ return false;
+
+#if defined( WIN32 )
+ struct _stat buf;
+ std::wstring wsFixedPath = UTF8to16( sFixedPath.c_str() );
+ if ( _wstat( wsFixedPath.c_str(), &buf ) == -1 )
+ {
+ return false;
+ }
+#else
+ struct stat buf;
+ if ( stat ( sFixedPath.c_str(), &buf ) == -1)
+ {
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: helper to find a directory upstream from a given path
+//-----------------------------------------------------------------------------
+std::string Path_FindParentDirectoryRecursively( const std::string &strStartDirectory, const std::string &strDirectoryName )
+{
+ std::string strFoundPath = "";
+ std::string strCurrentPath = Path_FixSlashes( strStartDirectory );
+ if ( strCurrentPath.length() == 0 )
+ return "";
+
+ bool bExists = Path_Exists( strCurrentPath );
+ std::string strCurrentDirectoryName = Path_StripDirectory( strCurrentPath );
+ if ( bExists && stricmp( strCurrentDirectoryName.c_str(), strDirectoryName.c_str() ) == 0 )
+ return strCurrentPath;
+
+ while( bExists && strCurrentPath.length() != 0 )
+ {
+ strCurrentPath = Path_StripFilename( strCurrentPath );
+ strCurrentDirectoryName = Path_StripDirectory( strCurrentPath );
+ bExists = Path_Exists( strCurrentPath );
+ if ( bExists && stricmp( strCurrentDirectoryName.c_str(), strDirectoryName.c_str() ) == 0 )
+ return strCurrentPath;
+ }
+
+ return "";
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: helper to find a subdirectory upstream from a given path
+//-----------------------------------------------------------------------------
+std::string Path_FindParentSubDirectoryRecursively( const std::string &strStartDirectory, const std::string &strDirectoryName )
+{
+ std::string strFoundPath = "";
+ std::string strCurrentPath = Path_FixSlashes( strStartDirectory );
+ if ( strCurrentPath.length() == 0 )
+ return "";
+
+ bool bExists = Path_Exists( strCurrentPath );
+ while( bExists && strCurrentPath.length() != 0 )
+ {
+ strCurrentPath = Path_StripFilename( strCurrentPath );
+ bExists = Path_Exists( strCurrentPath );
+
+ if( Path_Exists( Path_Join( strCurrentPath, strDirectoryName ) ) )
+ {
+ strFoundPath = Path_Join( strCurrentPath, strDirectoryName );
+ break;
+ }
+ }
+ return strFoundPath;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: reading and writing files in the vortex directory
+//-----------------------------------------------------------------------------
+unsigned char * Path_ReadBinaryFile( const std::string &strFilename, int *pSize )
+{
+ FILE *f;
+#if defined( POSIX )
+ f = fopen( strFilename.c_str(), "rb" );
+#else
+ std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
+ // the open operation needs to be sharable, therefore use of _wfsopen instead of _wfopen_s
+ f = _wfsopen( wstrFilename.c_str(), L"rb", _SH_DENYNO );
+#endif
+
+ unsigned char* buf = NULL;
+
+ if ( f != NULL )
+ {
+ fseek(f, 0, SEEK_END);
+ int size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ buf = new unsigned char[size];
+ if (buf && fread(buf, size, 1, f) == 1)
+ {
+ if (pSize)
+ *pSize = size;
+ }
+ else
+ {
+ delete[] buf;
+ buf = 0;
+ }
+
+ fclose(f);
+ }
+
+ return buf;
+}
+
+uint32_t Path_ReadBinaryFile( const std::string &strFilename, unsigned char *pBuffer, uint32_t unSize )
+{
+ FILE *f;
+#if defined( POSIX )
+ f = fopen( strFilename.c_str(), "rb" );
+#else
+ std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
+ errno_t err = _wfopen_s( &f, wstrFilename.c_str(), L"rb" );
+ if ( err != 0 )
+ {
+ f = NULL;
+ }
+#endif
+
+ uint32_t unSizeToReturn = 0;
+
+ if ( f != NULL )
+ {
+ fseek( f, 0, SEEK_END );
+ uint32_t size = (uint32_t)ftell( f );
+ fseek( f, 0, SEEK_SET );
+
+ if ( size > unSize || !pBuffer )
+ {
+ unSizeToReturn = (uint32_t)size;
+ }
+ else
+ {
+ if ( fread( pBuffer, size, 1, f ) == 1 )
+ {
+ unSizeToReturn = (uint32_t)size;
+ }
+ }
+
+ fclose( f );
+ }
+
+ return unSizeToReturn;
+}
+
+bool Path_WriteBinaryFile(const std::string &strFilename, unsigned char *pData, unsigned nSize)
+{
+ FILE *f;
+#if defined( POSIX )
+ f = fopen(strFilename.c_str(), "wb");
+#else
+ std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
+ errno_t err = _wfopen_s( &f, wstrFilename.c_str(), L"wb" );
+ if (err != 0)
+ {
+ f = NULL;
+ }
+#endif
+
+ size_t written = 0;
+ if (f != NULL) {
+ written = fwrite(pData, sizeof(unsigned char), nSize, f);
+ fclose(f);
+ }
+
+ return written == nSize ? true : false;
+}
+
+std::string Path_ReadTextFile( const std::string &strFilename )
+{
+ // doing it this way seems backwards, but I don't
+ // see an easy way to do this with C/C++ style IO
+ // that isn't worse...
+ int size;
+ unsigned char* buf = Path_ReadBinaryFile( strFilename, &size );
+ if (!buf)
+ return "";
+
+ // convert CRLF -> LF
+ size_t outsize = 1;
+ for (int i=1; i < size; i++)
+ {
+ if (buf[i] == '\n' && buf[i-1] == '\r') // CRLF
+ buf[outsize-1] = '\n'; // ->LF
+ else
+ buf[outsize++] = buf[i]; // just copy
+ }
+
+ std::string ret((char *)buf, outsize);
+ delete[] buf;
+ return ret;
+}
+
+
+bool Path_MakeWritable( const std::string &strFilename )
+{
+#if defined ( _WIN32 )
+ std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
+
+ DWORD dwAttrs = GetFileAttributesW( wstrFilename.c_str() );
+ if ( dwAttrs != INVALID_FILE_ATTRIBUTES && ( dwAttrs & FILE_ATTRIBUTE_READONLY ) )
+ {
+ return SetFileAttributesW( wstrFilename.c_str(), dwAttrs & ~FILE_ATTRIBUTE_READONLY );
+ }
+#else
+ struct stat sb;
+
+ if ( stat( strFilename.c_str(), &sb ) == 0 && !( sb.st_mode & S_IWUSR ) )
+ {
+ return ( chmod( strFilename.c_str(), sb.st_mode | S_IWUSR ) == 0 );
+ }
+#endif
+
+ return true;
+}
+
+bool Path_WriteStringToTextFile( const std::string &strFilename, const char *pchData )
+{
+ FILE *f;
+#if defined( POSIX )
+ f = fopen( strFilename.c_str(), "w" );
+#else
+ std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
+ errno_t err = _wfopen_s( &f, wstrFilename.c_str(), L"w" );
+ if ( err != 0 )
+ {
+ f = NULL;
+ }
+#endif
+
+ bool ok = false;
+
+ if ( f != NULL )
+ {
+ ok = fputs( pchData, f) >= 0;
+ fclose(f);
+ }
+
+ return ok;
+}
+
+bool Path_WriteStringToTextFileAtomic( const std::string &strFilename, const char *pchData )
+{
+ std::string strTmpFilename = strFilename + ".tmp";
+
+ if ( !Path_WriteStringToTextFile( strTmpFilename, pchData ) )
+ return false;
+
+ // Platform specific atomic file replacement
+#if defined( _WIN32 )
+ std::wstring wsFilename = UTF8to16( strFilename.c_str() );
+ std::wstring wsTmpFilename = UTF8to16( strTmpFilename.c_str() );
+ if ( !::ReplaceFileW( wsFilename.c_str(), wsTmpFilename.c_str(), nullptr, 0, 0, 0 ) )
+ {
+ // if we couldn't ReplaceFile, try a non-atomic write as a fallback
+ if ( !Path_WriteStringToTextFile( strFilename, pchData ) )
+ return false;
+ }
+#elif defined( POSIX )
+ if ( rename( strTmpFilename.c_str(), strFilename.c_str() ) == -1 )
+ return false;
+#else
+#error Do not know how to write atomic file
+#endif
+
+ return true;
+}
+
+
+#if defined(WIN32)
+#define FILE_URL_PREFIX "file:///"
+#else
+#define FILE_URL_PREFIX "file://"
+#endif
+
+// Mozilla: see mozilla.patch for more details
+// ----------------------------------------------------------------------------------------------------------------------------
+// Purpose: Turns a path to a file on disk into a URL (or just returns the value if it's already a URL)
+// ----------------------------------------------------------------------------------------------------------------------------
+// std::string Path_FilePathToUrl( const std::string & sRelativePath, const std::string & sBasePath )
+// {
+// if ( StringHasPrefix( sRelativePath, "http://" )
+// || StringHasPrefix( sRelativePath, "https://" )
+// || StringHasPrefix( sRelativePath, "vr-input-workshop://" )
+// || StringHasPrefix( sRelativePath, "file://" )
+// )
+// {
+// return sRelativePath;
+// }
+// else
+// {
+// std::string sAbsolute = Path_MakeAbsolute( sRelativePath, sBasePath );
+// if ( sAbsolute.empty() )
+// return sAbsolute;
+// sAbsolute = Path_FixSlashes( sAbsolute, '/' );
+
+// size_t unBufferSize = sAbsolute.length() * 3;
+// char *pchBuffer = (char *)alloca( unBufferSize );
+// V_URLEncodeFullPath( pchBuffer, (int)unBufferSize, sAbsolute.c_str(), (int)sAbsolute.length() );
+
+// return std::string( FILE_URL_PREFIX ) + pchBuffer;
+// }
+// }
+
+// Mozilla: see mozilla.patch for more details
+// -----------------------------------------------------------------------------------------------------
+// Purpose: Strips off file:// off a URL and returns the path. For other kinds of URLs an empty string is returned
+// -----------------------------------------------------------------------------------------------------
+// std::string Path_UrlToFilePath( const std::string & sFileUrl )
+// {
+// if ( !strnicmp( sFileUrl.c_str(), FILE_URL_PREFIX, strlen( FILE_URL_PREFIX ) ) )
+// {
+// char *pchBuffer = (char *)alloca( sFileUrl.length() );
+// V_URLDecodeNoPlusForSpace( pchBuffer, (int)sFileUrl.length(),
+// sFileUrl.c_str() + strlen( FILE_URL_PREFIX ), (int)( sFileUrl.length() - strlen( FILE_URL_PREFIX ) ) );
+
+// return Path_FixSlashes( pchBuffer );
+// }
+// else
+// {
+// return "";
+// }
+// }
+
+
+// -----------------------------------------------------------------------------------------------------
+// Purpose: Returns the root of the directory the system wants us to store user documents in
+// -----------------------------------------------------------------------------------------------------
+std::string GetUserDocumentsPath()
+{
+#if defined( WIN32 )
+ WCHAR rwchPath[MAX_PATH];
+
+ if ( !SUCCEEDED( SHGetFolderPathW( NULL, CSIDL_MYDOCUMENTS | CSIDL_FLAG_CREATE, NULL, 0, rwchPath ) ) )
+ {
+ return "";
+ }
+
+ // Convert the path to UTF-8 and store in the output
+ std::string sUserPath = UTF16to8( rwchPath );
+
+ return sUserPath;
+#elif defined( OSX )
+ @autoreleasepool {
+ NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES );
+ if ( [paths count] == 0 )
+ {
+ return "";
+ }
+
+ return [[paths objectAtIndex:0] UTF8String];
+ }
+#elif defined( LINUX )
+ // @todo: not solved/changed as part of OSX - still not real - just removed old class based steam cut and paste
+ const char *pchHome = getenv( "HOME" );
+ if ( pchHome == NULL )
+ {
+ return "";
+ }
+ return pchHome;
+#endif
+}
+
+
+// -----------------------------------------------------------------------------------------------------
+// Purpose: deletes / unlinks a single file
+// -----------------------------------------------------------------------------------------------------
+bool Path_UnlinkFile( const std::string &strFilename )
+{
+#if defined( WIN32 )
+ std::wstring wsFilename = UTF8to16( strFilename.c_str() );
+ return ( 0 != DeleteFileW( wsFilename.c_str() ) );
+#else
+ return ( 0 == unlink( strFilename.c_str() ) );
+#endif
+}