openmw/components/files/windowspath.cpp
AnyOldName3 166852254f Use non-deprecated known folder API
SHGetFolderPathW was deprecated in Windows Vista nearly two decades ago. ShGetKnownFolderPath is the replacement.

Also log if there was an error. Someone seemed to be getting an error on Discord, despite other apps being able to get the path just fine with these functions.

Also don't pass the flags to create the folders if they don't exist. We probably don't have the right permissions and if they don't exist, then there are bigger problems. Maybe this will fix the issue the user was having.

Also add a comment about global config on Windows being fundamentally wrong.
2025-03-25 01:32:44 +00:00

126 lines
3.5 KiB
C++

#include "windowspath.hpp"
#if defined(_WIN32) || defined(__WINDOWS__)
#include <array>
#include <cstring>
#define FAR
#define NEAR
#include <shlobj.h>
#include <shlwapi.h>
#include <winreg.h>
#undef NEAR
#undef FAR
#include <components/debug/debuglog.hpp>
/**
* \namespace Files
*/
namespace Files
{
WindowsPath::WindowsPath(const std::string& application_name)
: mName(application_name)
{
std::error_code ec;
current_path(getLocalPath(), ec);
if (ec.value() != 0)
Log(Debug::Warning) << "Error " << ec.value() << " when changing current directory";
}
std::filesystem::path WindowsPath::getUserConfigPath() const
{
std::filesystem::path userPath = std::filesystem::current_path();
PWSTR cString;
HRESULT result = SHGetKnownFolderPath(FOLDERID_Documents, 0, nullptr, &cString);
if (SUCCEEDED(result))
userPath = std::filesystem::path(cString);
else
Log(Debug::Error) << "Error " << result << " when getting Documents path";
CoTaskMemFree(cString);
return userPath / "My Games" / mName;
}
std::filesystem::path WindowsPath::getUserDataPath() const
{
// Have some chaos, windows people!
return getUserConfigPath();
}
std::filesystem::path WindowsPath::getGlobalConfigPath() const
{
// The concept of a global config path is absurd on Windows.
// Always use local config instead.
// The virtual base class requires that we provide this, though.
std::filesystem::path globalPath = std::filesystem::current_path();
PWSTR cString;
HRESULT result = SHGetKnownFolderPath(FOLDERID_ProgramFiles, 0, nullptr, &cString);
if (SUCCEEDED(result))
globalPath = std::filesystem::path(cString);
else
Log(Debug::Error) << "Error " << result << " when getting Program Files path";
CoTaskMemFree(cString);
return globalPath / mName;
}
std::filesystem::path WindowsPath::getLocalPath() const
{
std::filesystem::path localPath = std::filesystem::current_path() / "";
WCHAR path[MAX_PATH + 1] = {};
if (GetModuleFileNameW(nullptr, path, MAX_PATH + 1) > 0)
{
localPath = std::filesystem::path(path).parent_path() / "";
}
// lookup exe path
return localPath;
}
std::filesystem::path WindowsPath::getGlobalDataPath() const
{
return getGlobalConfigPath();
}
std::filesystem::path WindowsPath::getCachePath() const
{
return getUserConfigPath() / "cache";
}
std::filesystem::path WindowsPath::getInstallPath() const
{
std::filesystem::path installPath{};
if (HKEY hKey; RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Bethesda Softworks\\Morrowind", 0,
KEY_READ | KEY_WOW64_32KEY, &hKey)
== ERROR_SUCCESS)
{
// Key existed, let's try to read the install dir
std::array<wchar_t, 512> buf{};
DWORD len = static_cast<DWORD>(buf.size() * sizeof(wchar_t));
if (RegQueryValueExW(hKey, L"Installed Path", nullptr, nullptr, reinterpret_cast<LPBYTE>(buf.data()), &len)
== ERROR_SUCCESS)
{
installPath = std::filesystem::path(buf.data());
}
RegCloseKey(hKey);
}
return installPath;
}
} /* namespace Files */
#endif /* defined(_WIN32) || defined(__WINDOWS__) */