// Filename: find_root_dir.cxx // Created by: drose (29Jun09) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) Carnegie Mellon University. All rights reserved. // // All use of this software is subject to the terms of the revised BSD // license. You should have received a copy of this license along // with this source code in a file named "LICENSE." // //////////////////////////////////////////////////////////////////// #include "find_root_dir.h" #include "mkdir_complete.h" #include "get_tinyxml.h" #include "wstring_encode.h" #ifdef _WIN32 #include #include #else #include #include #include #include #include #endif #ifdef _WIN32 // From KnownFolders.h (part of Vista SDK): #define DEFINE_KNOWN_FOLDER(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } DEFINE_KNOWN_FOLDER(FOLDERID_LocalAppData, 0xF1B32785, 0x6FBA, 0x4FCF, 0x9D, 0x55, 0x7B, 0x8E, 0x7F, 0x15, 0x70, 0x91); DEFINE_KNOWN_FOLDER(FOLDERID_LocalAppDataLow, 0xA520A1A4, 0x1780, 0x4FF6, 0xBD, 0x18, 0x16, 0x73, 0x43, 0xC5, 0xAF, 0x16); DEFINE_KNOWN_FOLDER(FOLDERID_InternetCache, 0x352481E8, 0x33BE, 0x4251, 0xBA, 0x85, 0x60, 0x07, 0xCA, 0xED, 0xCF, 0x9D); #endif // _WIN32 #ifdef _WIN32 //////////////////////////////////////////////////////////////////// // Function: get_csidl_dir_w // Description: A wrapper around SHGetSpecialFolderPath(), to return // the Panda3D directory under the indicated CSIDL // folder. //////////////////////////////////////////////////////////////////// static wstring get_csidl_dir_w(int csidl) { static const int buffer_size = MAX_PATH; wchar_t buffer[buffer_size]; if (SHGetSpecialFolderPathW(NULL, buffer, csidl, true)) { wstring root = buffer; root += wstring(L"/Panda3D"); if (mkdir_complete_w(root, cerr)) { return root; } } // Something went wrong. return wstring(); } #endif // _WIN32 #ifdef _WIN32 //////////////////////////////////////////////////////////////////// // Function: find_root_dir_default_w // Description: Wide-character implementation of // find_root_dir_default(), only needed for Windows. //////////////////////////////////////////////////////////////////// static wstring find_root_dir_default_w() { // First, use IEIsProtectedModeProcess() to determine if we are // running in IE's "protected mode" under Vista. wstring root; bool is_protected = false; HMODULE ieframe = LoadLibrary("ieframe.dll"); if (ieframe != NULL) { typedef HRESULT STDAPICALLTYPE IEIsProtectedModeProcess(BOOL *pbResult); IEIsProtectedModeProcess *func = (IEIsProtectedModeProcess *)GetProcAddress(ieframe, "IEIsProtectedModeProcess"); if (func != NULL) { BOOL result = false; HRESULT hr = (*func)(&result); if (hr == S_OK) { is_protected = (result != 0); } // Any other return value means some error, especially // E_NOTIMPL, which means we're not running under Vista. In // this case we can assume we're not running in protected mode. } if (is_protected) { // If we *are* running in protected mode, we need to use // FOLDERID_LocalAppDataLow. // We should be able to use IEGetWriteableFolderPath() to query // this folder, but for some reason, that function returns // E_ACCESSDENIED on FOLDERID_LocalAppDataLow, even though this is // certainly a folder we have write access to. // Well, SHGetKnownFolderPath() does work. This function only // exists on Vista and above, though, so we still have to pull it // out of the DLL instead of hard-linking it. HMODULE shell32 = LoadLibrary("shell32.dll"); if (shell32 != NULL) { typedef HRESULT STDAPICALLTYPE SHGetKnownFolderPath(REFGUID rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath); SHGetKnownFolderPath *func = (SHGetKnownFolderPath *)GetProcAddress(shell32, "SHGetKnownFolderPath"); if (func != NULL) { LPWSTR cache_path = NULL; HRESULT hr = (*func)(FOLDERID_LocalAppDataLow, 0, NULL, &cache_path); if (SUCCEEDED(hr)) { root = cache_path; CoTaskMemFree(cache_path); root += wstring(L"/Panda3D"); if (mkdir_complete_w(root, cerr)) { FreeLibrary(shell32); FreeLibrary(ieframe); return root; } } } FreeLibrary(shell32); } // Couldn't get FOLDERID_LocalAppDataLow for some reason. We're // in fallback mode now. Use IEGetWriteableFolderPath to get // the standard cache folder. typedef HRESULT STDAPICALLTYPE IEGetWriteableFolderPath(REFGUID clsidFolderID, LPWSTR* lppwstrPath); IEGetWriteableFolderPath *func = (IEGetWriteableFolderPath *)GetProcAddress(ieframe, "IEGetWriteableFolderPath"); if (func != NULL) { LPWSTR cache_path = NULL; // Since we're here, we'll start by asking for // LocalAppDataLow, even though I know it doesn't work. HRESULT hr = (*func)(FOLDERID_LocalAppDataLow, &cache_path); if (FAILED(hr)) { // This one should work. hr = (*func)(FOLDERID_InternetCache, &cache_path); } if (SUCCEEDED(hr)) { root = cache_path; CoTaskMemFree(cache_path); root += wstring(L"/Panda3D"); if (mkdir_complete_w(root, cerr)) { FreeLibrary(ieframe); return root; } } } } FreeLibrary(ieframe); } // All right, here we are in the normal, unprotected mode. This is // also the normal XP codepath. // e.g., c:/Documents and Settings//Local Settings/Application Data/Panda3D root = get_csidl_dir_w(CSIDL_LOCAL_APPDATA); if (!root.empty()) { return root; } // For some crazy reason, we can't get CSIDL_LOCAL_APPDATA. Fall // back to the cache folder. // e.g. c:/Documents and Settings//Local Settings/Temporary Internet Files/Panda3D root = get_csidl_dir_w(CSIDL_INTERNET_CACHE); if (!root.empty()) { return root; } // If we couldn't get any of those folders, huh. Punt and try for // the old standby GetTempPath, for lack of anything better. static const int buffer_size = MAX_PATH; wchar_t buffer[buffer_size]; if (GetTempPathW(buffer_size, buffer) != 0) { root = buffer; root += wstring(L"Panda3D"); if (mkdir_complete_w(root, cerr)) { return root; } } return wstring(); } #endif // _WIN32 //////////////////////////////////////////////////////////////////// // Function: find_root_dir_default // Description: Returns the path to the system-default for the root // directory. This is where we look first. //////////////////////////////////////////////////////////////////// static string find_root_dir_default() { #ifdef _WIN32 wstring root_w = find_root_dir_default_w(); if (!root_w.empty()) { string root; if (wstring_to_string(root, root_w)) { return root; } } #elif defined(__APPLE__) // e.g., /Users//Library/Caches/Panda3D string root = find_osx_root_dir(); if (!root.empty()) { return root; } #else // The Linux/*BSD case // e.g., /home//.panda3d string root; const passwd *pwdata = getpwuid(getuid()); if (pwdata == NULL) { char *home = getenv("HOME"); if (home == NULL) { // Beh. Let's hope it never gets to this point. return "."; } else { root = home; } } else { root = pwdata->pw_dir; } root += "/.panda3d"; if (mkdir(root.c_str(), 0700) == 0 || errno == EEXIST) { return root; } #endif // Couldn't find a directory. Punt. return "."; } //////////////////////////////////////////////////////////////////// // Function: find_root_dir_actual // Description: Returns the path to the installable Panda3D directory // on the user's machine. //////////////////////////////////////////////////////////////////// static string find_root_dir_actual() { string root = find_root_dir_default(); // Now look for a config.xml file in that directory, which might // redirect us elsewhere. string config_filename = root + "/config.xml"; TiXmlDocument doc(config_filename); if (!doc.LoadFile()) { // No config.xml found, or not valid xml. return root; } TiXmlElement *xconfig = doc.FirstChildElement("config"); if (xconfig == NULL) { // No element within config.xml. return root; } const char *new_root = xconfig->Attribute("root_dir"); if (new_root == NULL || *new_root == '\0') { // No root_dir specified. return root; } if (!mkdir_complete(new_root, cerr)) { // The specified root_dir wasn't valid. return root; } // We've been redirected to another location. Respect that. return new_root; } //////////////////////////////////////////////////////////////////// // Function: find_root_dir // Description: This is the public interface to the above functions. //////////////////////////////////////////////////////////////////// string find_root_dir() { string root = find_root_dir_actual(); #ifdef _WIN32 // Now map that (possibly utf-8) filename into its 8.3 equivalent, // so we can safely pass it around to Python and other tools that // might not understand Unicode filenames. Silly Windows, creating // an entirely new and incompatible kind of filename. wstring root_w; string_to_wstring(root_w, root); DWORD length = GetShortPathNameW(root_w.c_str(), NULL, 0); wchar_t *short_name = new wchar_t[length]; GetShortPathNameW(root_w.c_str(), short_name, length); wstring_to_string(root, short_name); delete[] short_name; #endif // _WIN32 return root; }