// standard includes #include // local includes #include "driver_settings.h" #include "nvprefs_interface.h" #include "undo_file.h" namespace { const auto sunshine_program_data_folder = "Sunshine"; const auto nvprefs_undo_file_name = "nvprefs_undo.json"; } // namespace namespace nvprefs { struct nvprefs_interface::impl { bool loaded = false; driver_settings_t driver_settings; std::filesystem::path undo_folder_path; std::filesystem::path undo_file_path; std::optional undo_data; std::optional undo_file; }; nvprefs_interface::nvprefs_interface(): pimpl(new impl()) { } nvprefs_interface::~nvprefs_interface() { if (owning_undo_file() && load()) { restore_global_profile(); } unload(); } bool nvprefs_interface::load() { if (!pimpl->loaded) { // Check %ProgramData% variable, need it for storing undo file wchar_t program_data_env[MAX_PATH]; auto get_env_result = GetEnvironmentVariableW(L"ProgramData", program_data_env, MAX_PATH); if (get_env_result == 0 || get_env_result >= MAX_PATH || !std::filesystem::is_directory(program_data_env)) { error_message("Missing or malformed %ProgramData% environment variable"); return false; } // Prepare undo file path variables pimpl->undo_folder_path = std::filesystem::path(program_data_env) / sunshine_program_data_folder; pimpl->undo_file_path = pimpl->undo_folder_path / nvprefs_undo_file_name; // Dynamically load nvapi library and load driver settings pimpl->loaded = pimpl->driver_settings.init(); } return pimpl->loaded; } void nvprefs_interface::unload() { if (pimpl->loaded) { // Unload dynamically loaded nvapi library pimpl->driver_settings.destroy(); pimpl->loaded = false; } } bool nvprefs_interface::restore_from_and_delete_undo_file_if_exists() { if (!pimpl->loaded) return false; // Check for undo file from previous improper termination bool access_denied = false; if (auto undo_file = undo_file_t::open_existing_file(pimpl->undo_file_path, access_denied)) { // Try to restore from the undo file info_message("Opened undo file from previous improper termination"); if (auto undo_data = undo_file->read_undo_data()) { if (pimpl->driver_settings.restore_global_profile_to_undo(*undo_data) && pimpl->driver_settings.save_settings()) { info_message("Restored global profile settings from undo file - deleting the file"); } else { error_message("Failed to restore global profile settings from undo file, deleting the file anyway"); } } else { error_message("Coulnd't read undo file, deleting the file anyway"); } if (!undo_file->delete_file()) { error_message("Couldn't delete undo file"); return false; } } else if (access_denied) { error_message("Couldn't open undo file from previous improper termination, or confirm that there's no such file"); return false; } return true; } bool nvprefs_interface::modify_application_profile() { if (!pimpl->loaded) return false; // Modify and save sunshine.exe application profile settings, if needed bool modified = false; if (!pimpl->driver_settings.check_and_modify_application_profile(modified)) { error_message("Failed to modify application profile settings"); return false; } else if (modified) { if (pimpl->driver_settings.save_settings()) { info_message("Modified application profile settings"); } else { error_message("Couldn't save application profile settings"); return false; } } else { info_message("No need to modify application profile settings"); } return true; } bool nvprefs_interface::modify_global_profile() { if (!pimpl->loaded) return false; // Modify but not save global profile settings, if needed std::optional undo_data; if (!pimpl->driver_settings.check_and_modify_global_profile(undo_data)) { error_message("Couldn't modify global profile settings"); return false; } else if (!undo_data) { info_message("No need to modify global profile settings"); return true; } auto make_undo_and_commit = [&]() -> bool { // Create and lock undo file if it hasn't been done yet if (!pimpl->undo_file) { // Prepare Sunshine folder in ProgramData if it doesn't exist if (!CreateDirectoryW(pimpl->undo_folder_path.c_str(), nullptr) && GetLastError() != ERROR_ALREADY_EXISTS) { error_message("Couldn't create undo folder"); return false; } // Create undo file to handle improper termination of nvprefs.exe pimpl->undo_file = undo_file_t::create_new_file(pimpl->undo_file_path); if (!pimpl->undo_file) { error_message("Couldn't create undo file"); return false; } } assert(undo_data); if (pimpl->undo_data) { // Merge undo data if settings has been modified externally since our last modification pimpl->undo_data->merge(*undo_data); } else { pimpl->undo_data = undo_data; } // Write undo data to undo file if (!pimpl->undo_file->write_undo_data(*pimpl->undo_data)) { error_message("Couldn't write to undo file - deleting the file"); if (!pimpl->undo_file->delete_file()) { error_message("Couldn't delete undo file"); } return false; } // Save global profile settings if (!pimpl->driver_settings.save_settings()) { error_message("Couldn't save global profile settings"); return false; } return true; }; if (!make_undo_and_commit()) { // Revert settings modifications pimpl->driver_settings.load_settings(); return false; } return true; } bool nvprefs_interface::owning_undo_file() { return pimpl->undo_file.has_value(); } bool nvprefs_interface::restore_global_profile() { if (!pimpl->loaded || !pimpl->undo_data || !pimpl->undo_file) return false; // Restore global profile settings with undo data if (pimpl->driver_settings.restore_global_profile_to_undo(*pimpl->undo_data) && pimpl->driver_settings.save_settings()) { // Global profile settings sucessfully restored, can delete undo file if (!pimpl->undo_file->delete_file()) { error_message("Couldn't delete undo file"); return false; } pimpl->undo_data = std::nullopt; pimpl->undo_file = std::nullopt; } else { error_message("Couldn't restore global profile settings"); return false; } return true; } } // namespace nvprefs