| //========= Copyright Valve Corporation ============// | |
| #include "compat.h" | |
| #include "strtools.h" | |
| #include "pathtools.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> | |
| #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; | |
| } | |
| /** 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 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(); | |
| 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 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) | |
| NSBundle *bundle = [ NSBundle bundleWithPath: [ NSString stringWithUTF8String:sPath.c_str() ] ]; | |
| bool bisAppBundle = ( nullptr != bundle ); | |
| [ bundle release ]; | |
| 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_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 | |
| // ---------------------------------------------------------------------------------------------------------------------------- | |
| // 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 ( !strnicmp( sRelativePath.c_str(), "http://", 7 ) | |
| || !strnicmp( sRelativePath.c_str(), "https://", 8 ) | |
| || !strnicmp( sRelativePath.c_str(), "file://", 7 ) ) | |
| { | |
| return sRelativePath; | |
| } | |
| else | |
| { | |
| std::string sAbsolute = Path_MakeAbsolute( sRelativePath, sBasePath ); | |
| if ( sAbsolute.empty() ) | |
| return sAbsolute; | |
| return std::string( FILE_URL_PREFIX ) + sAbsolute; | |
| } | |
| } | |
| // ----------------------------------------------------------------------------------------------------- | |
| // 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 ) ) ) | |
| { | |
| return sFileUrl.c_str() + strlen( FILE_URL_PREFIX ); | |
| } | |
| 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 | |
| } | |