SettingsLoader.cpp
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 #include "SettingsLoader.h" 6 #include <iostream> 7 #include <fstream> 8 #include <sstream> 9 #include <filesystem> 10 #include <Shlobj.h> 11 12 SettingsLoader::SettingsLoader() 13 { 14 } 15 16 SettingsLoader::~SettingsLoader() 17 { 18 } 19 20 std::wstring SettingsLoader::GetPowerToysSettingsRoot() const 21 { 22 // Get %LOCALAPPDATA% 23 PWSTR localAppDataPath = nullptr; 24 HRESULT hr = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &localAppDataPath); 25 26 if (FAILED(hr) || !localAppDataPath) 27 { 28 std::wcerr << L"Error: Failed to get LOCALAPPDATA path\n"; 29 return L""; 30 } 31 32 std::wstring result(localAppDataPath); 33 CoTaskMemFree(localAppDataPath); 34 35 // Append PowerToys directory 36 result += L"\\Microsoft\\PowerToys"; 37 return result; 38 } 39 40 std::wstring SettingsLoader::GetSettingsPath(const std::wstring& moduleName) const 41 { 42 std::wstring root = GetPowerToysSettingsRoot(); 43 if (root.empty()) 44 { 45 return L""; 46 } 47 48 // Construct path: %LOCALAPPDATA%\Microsoft\PowerToys\<ModuleName>\settings.json 49 std::wstring settingsPath = root + L"\\" + moduleName + L"\\settings.json"; 50 return settingsPath; 51 } 52 53 std::wstring SettingsLoader::ReadFileContents(const std::wstring& filePath) const 54 { 55 std::wifstream file(filePath, std::ios::binary); 56 if (!file.is_open()) 57 { 58 std::wcerr << L"Error: Could not open file: " << filePath << L"\n"; 59 return L""; 60 } 61 62 // Read the entire file 63 std::wstringstream buffer; 64 buffer << file.rdbuf(); 65 66 return buffer.str(); 67 } 68 69 std::wstring SettingsLoader::LoadSettings(const std::wstring& moduleName, const std::wstring& moduleDllPath) 70 { 71 const std::wstring powerToysPrefix = L"PowerToys."; 72 73 // Build list of possible module name variations to try 74 std::vector<std::wstring> moduleNameVariants; 75 76 // Try exact name first 77 moduleNameVariants.push_back(moduleName); 78 79 // If doesn't start with "PowerToys.", try adding it 80 if (moduleName.find(powerToysPrefix) != 0) 81 { 82 moduleNameVariants.push_back(powerToysPrefix + moduleName); 83 } 84 // If starts with "PowerToys.", try without it 85 else 86 { 87 moduleNameVariants.push_back(moduleName.substr(powerToysPrefix.length())); 88 } 89 90 // FIRST: Try same directory as the module DLL 91 if (!moduleDllPath.empty()) 92 { 93 std::filesystem::path dllPath(moduleDllPath); 94 std::filesystem::path dllDirectory = dllPath.parent_path(); 95 96 std::wstring localSettingsPath = (dllDirectory / L"settings.json").wstring(); 97 std::wcout << L"Trying settings path (module directory): " << localSettingsPath << L"\n"; 98 99 if (std::filesystem::exists(localSettingsPath)) 100 { 101 std::wstring contents = ReadFileContents(localSettingsPath); 102 if (!contents.empty()) 103 { 104 std::wcout << L"Settings file loaded from module directory (" << contents.size() << L" characters)\n"; 105 return contents; 106 } 107 } 108 } 109 110 // SECOND: Try standard PowerToys settings locations 111 for (const auto& variant : moduleNameVariants) 112 { 113 std::wstring settingsPath = GetSettingsPath(variant); 114 115 std::wcout << L"Trying settings path: " << settingsPath << L"\n"; 116 117 // Check if file exists (case-sensitive path) 118 if (std::filesystem::exists(settingsPath)) 119 { 120 std::wstring contents = ReadFileContents(settingsPath); 121 if (!contents.empty()) 122 { 123 std::wcout << L"Settings file loaded (" << contents.size() << L" characters)\n"; 124 return contents; 125 } 126 } 127 else 128 { 129 // Try case-insensitive search in the parent directory 130 std::wstring root = GetPowerToysSettingsRoot(); 131 if (!root.empty() && std::filesystem::exists(root)) 132 { 133 try 134 { 135 // Search for a directory that matches case-insensitively 136 for (const auto& entry : std::filesystem::directory_iterator(root)) 137 { 138 if (entry.is_directory()) 139 { 140 std::wstring dirName = entry.path().filename().wstring(); 141 142 // Case-insensitive comparison 143 if (_wcsicmp(dirName.c_str(), variant.c_str()) == 0) 144 { 145 std::wstring actualSettingsPath = entry.path().wstring() + L"\\settings.json"; 146 std::wcout << L"Found case-insensitive match: " << actualSettingsPath << L"\n"; 147 148 if (std::filesystem::exists(actualSettingsPath)) 149 { 150 std::wstring contents = ReadFileContents(actualSettingsPath); 151 if (!contents.empty()) 152 { 153 std::wcout << L"Settings file loaded (" << contents.size() << L" characters)\n"; 154 return contents; 155 } 156 } 157 } 158 } 159 } 160 } 161 catch (const std::filesystem::filesystem_error& e) 162 { 163 std::wcerr << L"Error searching directory: " << e.what() << L"\n"; 164 } 165 } 166 } 167 } 168 169 std::wcerr << L"Error: Settings file not found in any expected location:\n"; 170 if (!moduleDllPath.empty()) 171 { 172 std::filesystem::path dllPath(moduleDllPath); 173 std::filesystem::path dllDirectory = dllPath.parent_path(); 174 std::wcerr << L" - " << (dllDirectory / L"settings.json").wstring() << L" (module directory)\n"; 175 } 176 for (const auto& variant : moduleNameVariants) 177 { 178 std::wcerr << L" - " << GetSettingsPath(variant) << L"\n"; 179 } 180 181 return L""; 182 }