1253 lines
41 KiB
C#
1253 lines
41 KiB
C#
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Text.Json.Serialization;
|
|
using EpicGames.Core;
|
|
using EpicGames.Perforce;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace UnrealGameSync
|
|
{
|
|
public enum BuildConfig
|
|
{
|
|
Debug,
|
|
DebugGame,
|
|
Development,
|
|
}
|
|
|
|
public enum TabLabels
|
|
{
|
|
Stream,
|
|
WorkspaceName,
|
|
WorkspaceRoot,
|
|
ProjectFile,
|
|
}
|
|
|
|
public enum BisectState
|
|
{
|
|
Include,
|
|
Exclude,
|
|
Pass,
|
|
Fail,
|
|
}
|
|
|
|
public enum UserSelectedProjectType
|
|
{
|
|
Client,
|
|
Local
|
|
}
|
|
|
|
public enum FilterType
|
|
{
|
|
None,
|
|
Code,
|
|
Content
|
|
}
|
|
|
|
/// <summary>
|
|
/// Config specified class to add a lockable editor argument to a project
|
|
/// </summary>
|
|
public class LockableEditorArgument
|
|
{
|
|
public string Name { get; set; } = "";
|
|
public bool Enabled { get; set; } = false;
|
|
public bool Locked { get; set; } = false;
|
|
|
|
public LockableEditorArgument( string name, bool enabled, bool locked = false )
|
|
{
|
|
Name = name;
|
|
Enabled = enabled;
|
|
Locked = locked;
|
|
}
|
|
|
|
public LockableEditorArgument(LockableEditorArgument lockableEditorArgument)
|
|
{
|
|
Name = lockableEditorArgument.Name;
|
|
Enabled = lockableEditorArgument.Enabled;
|
|
Locked = lockableEditorArgument.Locked;
|
|
}
|
|
|
|
public static bool TryParseConfigEntry(string text, [NotNullWhen(true)] out LockableEditorArgument? editorArgument)
|
|
{
|
|
ConfigObject definitionObject = new ConfigObject(text);
|
|
|
|
string editorArgumentName = definitionObject.GetValue("Name", "");
|
|
if (editorArgumentName.Length > 0)
|
|
{
|
|
editorArgument = new LockableEditorArgument(
|
|
editorArgumentName,
|
|
definitionObject.GetValue("Enabled", false),
|
|
definitionObject.GetValue("Locked", false)
|
|
);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
editorArgument = null;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Config specified class to determine what is the Latest Change to Sync
|
|
/// Can be configured using badges, good and starred CLs
|
|
/// </summary>
|
|
public class LatestChangeType
|
|
{
|
|
// Display Specifiers
|
|
public string Name { get; set; } = ""; // Name will the saved ID for this LatestChangeType
|
|
public string Description { get; set; } = "";
|
|
public int OrderIndex { get; set; } = Int32.MaxValue;
|
|
|
|
// What Rules to Check for.
|
|
public bool Good { get; set; } = false;
|
|
public bool Starred { get; set; } = false;
|
|
public bool FindNewestGoodContent { get; set; } = false;
|
|
public List<string> RequiredBadges { get; init; } = new List<string>();
|
|
|
|
// Depot path to read for the latest change number
|
|
public string? ReadFrom { get; set; }
|
|
|
|
public static bool TryParseConfigEntry(string text, [NotNullWhen(true)] out LatestChangeType? changeType)
|
|
{
|
|
ConfigObject definitionObject = new ConfigObject(text);
|
|
|
|
string latestChangeTypeName = definitionObject.GetValue("Name", "");
|
|
if (latestChangeTypeName.Length > 0)
|
|
{
|
|
changeType = new LatestChangeType();
|
|
changeType.Name = latestChangeTypeName;
|
|
changeType.Description = definitionObject.GetValue("Description", "No \"Description\" for LatestChangeType given.");
|
|
changeType.OrderIndex = definitionObject.GetValue("OrderIndex", Int32.MaxValue);
|
|
|
|
changeType.Good = definitionObject.GetValue("bGood", false);
|
|
changeType.Starred = definitionObject.GetValue("bStarred", false);
|
|
changeType.FindNewestGoodContent = definitionObject.GetValue("bFindNewestGoodContent", false);
|
|
changeType.RequiredBadges.AddRange(definitionObject.GetValue("RequiredBadges", "").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries));
|
|
|
|
changeType.ReadFrom = definitionObject.GetValue("ReadFrom", null);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
changeType = null;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Default Template for Latest Change
|
|
public static LatestChangeType LatestChange()
|
|
{
|
|
LatestChangeType latest = new LatestChangeType();
|
|
latest.Name = "LatestChange";
|
|
latest.Description = "Latest Change";
|
|
latest.OrderIndex = -3;
|
|
latest.FindNewestGoodContent = true;
|
|
|
|
return latest;
|
|
}
|
|
|
|
// Default Template for Latest Good Change
|
|
public static LatestChangeType LatestGoodChange()
|
|
{
|
|
LatestChangeType latestGood = new LatestChangeType();
|
|
latestGood.Name = "LatestGoodChange";
|
|
latestGood.Description = "Latest Good Change";
|
|
latestGood.OrderIndex = -2;
|
|
latestGood.Good = true;
|
|
latestGood.FindNewestGoodContent = true;
|
|
|
|
return latestGood;
|
|
}
|
|
|
|
// Default Template for Latest Starred Change
|
|
public static LatestChangeType LatestStarredChange()
|
|
{
|
|
LatestChangeType latestStarred = new LatestChangeType();
|
|
latestStarred.Name = "LatestStarredChange";
|
|
latestStarred.Description = "Latest Starred Change";
|
|
latestStarred.OrderIndex = -1;
|
|
latestStarred.Starred = true;
|
|
latestStarred.FindNewestGoodContent = true;
|
|
|
|
return latestStarred;
|
|
}
|
|
}
|
|
|
|
#pragma warning disable CA1027 // Mark enums with FlagsAttribute
|
|
public enum UserSettingsVersion
|
|
{
|
|
Initial = 0,
|
|
DefaultServerSettings = 1,
|
|
XgeShaderCompilation = 2,
|
|
DefaultNumberOfThreads = 3,
|
|
Latest = DefaultNumberOfThreads
|
|
}
|
|
#pragma warning restore CA1027 // Mark enums with FlagsAttribute
|
|
|
|
public class ArchiveSettings
|
|
{
|
|
public bool Enabled { get; set; }
|
|
public string Type { get; set; }
|
|
public List<string> Order { get; init; }
|
|
|
|
public ArchiveSettings(bool enabled, string type, IEnumerable<string> order)
|
|
{
|
|
Enabled = enabled;
|
|
Type = type;
|
|
Order = new List<string>(order);
|
|
}
|
|
|
|
public static bool TryParseConfigEntry(string text, [NotNullWhen(true)] out ArchiveSettings? settings)
|
|
{
|
|
ConfigObject obj = new ConfigObject(text);
|
|
|
|
string? type = obj.GetValue("Type", null);
|
|
if (type == null)
|
|
{
|
|
settings = null;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
string[] order = obj.GetValue("Order", "").Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
|
bool enabled = obj.GetValue("Enabled", 0) != 0;
|
|
|
|
settings = new ArchiveSettings(enabled, type, order);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public string ToConfigEntry()
|
|
{
|
|
ConfigObject obj = new ConfigObject();
|
|
|
|
obj.SetValue("Enabled", Enabled ? 1 : 0);
|
|
obj.SetValue("Type", Type);
|
|
obj.SetValue("Order", String.Join(";", Order));
|
|
|
|
return obj.ToString();
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return ToConfigEntry();
|
|
}
|
|
}
|
|
|
|
public class SnapshotSettings
|
|
{
|
|
public string Runtime { get; set; } = "";
|
|
public string Platform { get; set; } = "";
|
|
}
|
|
|
|
public class UserSelectedProjectSettings
|
|
{
|
|
public string? ServerAndPort { get; set; }
|
|
public string? UserName { get; set; }
|
|
public UserSelectedProjectType Type { get; set; }
|
|
public string? ClientPath { get; set; }
|
|
public string? LocalPath { get; set; }
|
|
public string? ScheduledSyncTypeId { get; set; }
|
|
|
|
public UserSelectedProjectSettings(string? serverAndPort, string? userName, UserSelectedProjectType type, string? clientPath, string? localPath)
|
|
{
|
|
ServerAndPort = serverAndPort;
|
|
UserName = userName;
|
|
Type = type;
|
|
ClientPath = clientPath;
|
|
LocalPath = localPath;
|
|
}
|
|
|
|
public static bool TryParseConfigEntry(string text, [NotNullWhen(true)] out UserSelectedProjectSettings? project)
|
|
{
|
|
ConfigObject obj = new ConfigObject(text);
|
|
|
|
UserSelectedProjectType type;
|
|
if (Enum.TryParse(obj.GetValue("Type", ""), out type))
|
|
{
|
|
string? serverAndPort = obj.GetValue("ServerAndPort", null);
|
|
if (String.IsNullOrWhiteSpace(serverAndPort))
|
|
{
|
|
serverAndPort = null;
|
|
}
|
|
|
|
// Fixup for code that was saving server host name rather than DNS entry
|
|
if (serverAndPort != null && serverAndPort.Equals("p4-nodeb.epicgames.net:1666", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
serverAndPort = "perforce:1666";
|
|
}
|
|
|
|
string? userName = obj.GetValue("UserName", null);
|
|
if (String.IsNullOrWhiteSpace(userName))
|
|
{
|
|
userName = null;
|
|
}
|
|
|
|
string? localPath = obj.GetValue("LocalPath", null);
|
|
if (String.IsNullOrWhiteSpace(localPath))
|
|
{
|
|
localPath = null;
|
|
}
|
|
|
|
string? clientPath = obj.GetValue("ClientPath", null);
|
|
if (String.IsNullOrWhiteSpace(clientPath))
|
|
{
|
|
clientPath = null;
|
|
}
|
|
|
|
string? scheduledSyncTypeId = obj.GetValue("ScheduledSyncTypeID", null);
|
|
if (String.IsNullOrWhiteSpace(scheduledSyncTypeId))
|
|
{
|
|
scheduledSyncTypeId = null;
|
|
}
|
|
|
|
if ((type == UserSelectedProjectType.Client && clientPath != null) || (type == UserSelectedProjectType.Local && localPath != null))
|
|
{
|
|
project = new UserSelectedProjectSettings(serverAndPort, userName, type, clientPath, localPath);
|
|
project.ScheduledSyncTypeId = scheduledSyncTypeId;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
project = null;
|
|
return false;
|
|
}
|
|
|
|
public string ToConfigEntry()
|
|
{
|
|
ConfigObject obj = new ConfigObject();
|
|
|
|
if (ServerAndPort != null)
|
|
{
|
|
obj.SetValue("ServerAndPort", ServerAndPort);
|
|
}
|
|
if (UserName != null)
|
|
{
|
|
obj.SetValue("UserName", UserName);
|
|
}
|
|
|
|
obj.SetValue("Type", Type.ToString());
|
|
|
|
if (ClientPath != null)
|
|
{
|
|
obj.SetValue("ClientPath", ClientPath);
|
|
}
|
|
if (LocalPath != null)
|
|
{
|
|
obj.SetValue("LocalPath", LocalPath);
|
|
}
|
|
if (ScheduledSyncTypeId != null)
|
|
{
|
|
obj.SetValue("ScheduledSyncTypeID", ScheduledSyncTypeId);
|
|
}
|
|
|
|
return obj.ToString();
|
|
}
|
|
|
|
public override string? ToString()
|
|
{
|
|
return LocalPath ?? ClientPath;
|
|
}
|
|
}
|
|
|
|
public class SyncCategory
|
|
{
|
|
public Guid Id { get; set; }
|
|
public bool Enable { get; set; }
|
|
}
|
|
|
|
public class BisectEntry
|
|
{
|
|
public int Change { get; set; }
|
|
public BisectState State { get; set; }
|
|
}
|
|
|
|
public class UserWorkspaceSettings
|
|
{
|
|
[JsonIgnore]
|
|
public DirectoryReference RootDir { get; set; } = null!;
|
|
|
|
[JsonIgnore]
|
|
public long LastModifiedTimeUtc { get; set; }
|
|
|
|
// Connection settings
|
|
public string? ServerAndPort { get; set; }
|
|
public string? UserName { get; set; }
|
|
public string ClientName { get; set; } = String.Empty;
|
|
|
|
// Path to the root of the branch within this client, with a trailing slash if non-empty
|
|
public string BranchPath { get; set; } = String.Empty;
|
|
|
|
// The currently selected project, relative to the root directory
|
|
public string ProjectPath { get; set; } = String.Empty;
|
|
|
|
// The currently selected preset
|
|
public string Preset { get; set; } = String.Empty;
|
|
|
|
// Workspace specific SyncFilters
|
|
public FilterSettings Filter { get; set; } = new FilterSettings();
|
|
|
|
[JsonIgnore]
|
|
public FileReference ConfigFile => GetConfigFile(RootDir);
|
|
|
|
[JsonIgnore]
|
|
public string ClientProjectPath => $"//{ClientName}{BranchPath}{ProjectPath}";
|
|
|
|
[JsonIgnore]
|
|
public FileReference LocalProjectPath => new FileReference(RootDir.FullName + ProjectPath);
|
|
|
|
public void Init(string? serverAndPort, string? userName, string clientName, string branchPath, string projectPath)
|
|
{
|
|
ProjectInfo.ValidateBranchPath(branchPath);
|
|
ProjectInfo.ValidateProjectPath(projectPath);
|
|
|
|
ServerAndPort = serverAndPort;
|
|
UserName = userName;
|
|
ClientName = clientName;
|
|
BranchPath = branchPath;
|
|
ProjectPath = projectPath;
|
|
}
|
|
|
|
public static bool TryLoad(DirectoryReference rootDir, [NotNullWhen(true)] out UserWorkspaceSettings? settings)
|
|
{
|
|
FileReference configFile = GetConfigFile(rootDir);
|
|
if (Utility.TryLoadJson(configFile, out settings))
|
|
{
|
|
settings.RootDir = rootDir;
|
|
settings.LastModifiedTimeUtc = FileReference.GetLastWriteTimeUtc(configFile).Ticks;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
settings = null;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static readonly object _syncRoot = new object();
|
|
|
|
public bool Save(ILogger logger)
|
|
{
|
|
try
|
|
{
|
|
SaveInternal();
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Unable to save {ConfigFile}: {Message}", ConfigFile, ex.Message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private void SaveInternal()
|
|
{
|
|
lock (_syncRoot)
|
|
{
|
|
UserSettings.CreateConfigDir(ConfigFile.Directory);
|
|
Utility.SaveJson(ConfigFile, this);
|
|
LastModifiedTimeUtc = FileReference.GetLastWriteTimeUtc(ConfigFile).Ticks;
|
|
}
|
|
}
|
|
|
|
public static FileReference GetConfigFile(DirectoryReference rootDir)
|
|
{
|
|
return FileReference.Combine(UserSettings.GetConfigDir(rootDir), "settings.json");
|
|
}
|
|
}
|
|
|
|
public class UserProjectSettings
|
|
{
|
|
[JsonIgnore]
|
|
public FileReference ConfigFile { get; private set; } = null!;
|
|
|
|
public List<ConfigObject> BuildSteps { get; init; } = new List<ConfigObject>();
|
|
public FilterType FilterType { get; set; }
|
|
public HashSet<string> FilterBadges { get; init; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
[JsonIgnore]
|
|
public List<string> RequiredBadges { get; init; } = new List<string>();
|
|
|
|
static readonly object _syncRoot = new object();
|
|
|
|
private UserProjectSettings()
|
|
{
|
|
}
|
|
|
|
public UserProjectSettings(FileReference configFile)
|
|
{
|
|
ConfigFile = configFile;
|
|
}
|
|
|
|
public static bool TryLoad(FileReference configFile, [NotNullWhen(true)] out UserProjectSettings? settings)
|
|
{
|
|
if (Utility.TryLoadJson(configFile, out settings))
|
|
{
|
|
settings.ConfigFile = configFile;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
settings = null;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool Save(ILogger logger)
|
|
{
|
|
try
|
|
{
|
|
SaveInternal();
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Unable to save {ConfigFile}: {Message}", ConfigFile, ex.Message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void SaveInternal()
|
|
{
|
|
lock (_syncRoot)
|
|
{
|
|
Utility.SaveJson(ConfigFile, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class UserSettings : GlobalSettingsFile
|
|
{
|
|
/// <summary>
|
|
/// Enum that decribes which robomerge changes to show
|
|
/// </summary>
|
|
public enum RobomergeShowChangesOption
|
|
{
|
|
All, // Show all changes from robomerge
|
|
Badged, // Show only robomerge changes that have an associated badge
|
|
None // Show no robomerge changes
|
|
};
|
|
|
|
readonly FileReference _fileName;
|
|
readonly ConfigFile _configFile;
|
|
|
|
// General settings
|
|
public UserSettingsVersion Version { get; set; } = UserSettingsVersion.Latest;
|
|
public bool GenerateProjectAfterSync { get; set; }
|
|
public bool BuildAfterSync { get; set; }
|
|
public bool RunAfterSync { get; set; }
|
|
public bool OpenSolutionAfterSync { get; set; }
|
|
public bool ShowLogWindow { get; set; }
|
|
public bool ShowUnreviewedChanges { get; set; }
|
|
public bool ShowAutomatedChanges { get; set; }
|
|
public RobomergeShowChangesOption ShowRobomerge { get; set; }
|
|
public bool AnnotateRobmergeChanges { get; set; }
|
|
public bool ShowLocalTimes { get; set; }
|
|
public bool KeepInTray { get; set; }
|
|
public HashSet<Guid> EnabledTools { get; init; } = new HashSet<Guid>();
|
|
public int FilterIndex { get; set; }
|
|
public UserSelectedProjectSettings? LastProject { get; set; }
|
|
public List<UserSelectedProjectSettings> OpenProjects { get; init; }
|
|
public List<UserSelectedProjectSettings> RecentProjects { get; init; }
|
|
public string SyncTypeId { get; set; }
|
|
public BuildConfig CompiledEditorBuildConfig { get; set; } // NB: This assumes not using precompiled editor. See CurrentBuildConfig.
|
|
public TabLabels TabLabels { get; set; }
|
|
public long NextLauncherVersionCheck { get; set; }
|
|
public ApplicationTheme? Theme { get; set; }
|
|
public Dictionary<string, string> ThemeList { get; init; }
|
|
|
|
// Precompiled binaries
|
|
public List<ArchiveSettings> Archives { get; init; } = new List<ArchiveSettings>();
|
|
|
|
public List<SnapshotSettings> Snapshots { get; init; } = new List<SnapshotSettings>();
|
|
public string SnapshotHost { get; set; } = "";
|
|
public string SnapshotNamespace { get; set; } = "";
|
|
|
|
// OIDC Settings
|
|
public Dictionary<string, string> ProviderToRefreshTokens { get; init; } = new Dictionary<string, string>();
|
|
|
|
// Window settings
|
|
public bool WindowVisible { get; set; }
|
|
public string WindowState { get; set; }
|
|
public Rectangle? WindowBounds { get; set; }
|
|
|
|
// Schedule settings
|
|
public bool ScheduleEnabled { get; set; }
|
|
public TimeSpan ScheduleTime { get; set; }
|
|
public bool ScheduleAnyOpenProject { get; set; }
|
|
public List<UserSelectedProjectSettings> ScheduleProjects { get; init; } = new List<UserSelectedProjectSettings>();
|
|
|
|
// Run configuration
|
|
public List<LockableEditorArgument> EditorArguments { get; init; } = new List<LockableEditorArgument>();
|
|
public bool EditorArgumentsPrompt { get; set; }
|
|
|
|
// Notification settings
|
|
public List<string> NotifyProjects { get; init; } = new List<string>();
|
|
public int NotifyUnassignedMinutes { get; set; }
|
|
public int NotifyUnacknowledgedMinutes { get; set; }
|
|
public int NotifyUnresolvedMinutes { get; set; }
|
|
|
|
// Project settings
|
|
readonly Dictionary<DirectoryReference, UserWorkspaceSettings> _workspaceDirToSettings = new Dictionary<DirectoryReference, UserWorkspaceSettings>();
|
|
readonly Dictionary<FileReference, UserProjectSettings> _projectKeyToSettings = new Dictionary<FileReference, UserProjectSettings>();
|
|
|
|
// Perforce settings
|
|
public PerforceSyncOptions SyncOptions => Global.Perforce;
|
|
public ClientSubmitOptions ClientSubmitOptions { get; set; }
|
|
|
|
private List<UserSelectedProjectSettings> ReadProjectList(string settingName, string legacySettingName)
|
|
{
|
|
List<UserSelectedProjectSettings> projects = new List<UserSelectedProjectSettings>();
|
|
|
|
string[]? projectStrings = _configFile.GetValues(settingName, null);
|
|
if (projectStrings != null)
|
|
{
|
|
foreach (string projectString in projectStrings)
|
|
{
|
|
UserSelectedProjectSettings? project;
|
|
if (UserSelectedProjectSettings.TryParseConfigEntry(projectString, out project))
|
|
{
|
|
projects.Add(project);
|
|
}
|
|
}
|
|
}
|
|
else if (legacySettingName != null)
|
|
{
|
|
string[]? legacyProjectStrings = _configFile.GetValues(legacySettingName, null);
|
|
if (legacyProjectStrings != null)
|
|
{
|
|
foreach (string legacyProjectString in legacyProjectStrings)
|
|
{
|
|
if (!String.IsNullOrWhiteSpace(legacyProjectString))
|
|
{
|
|
projects.Add(new UserSelectedProjectSettings(null, null, UserSelectedProjectType.Local, null, legacyProjectString));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return projects;
|
|
}
|
|
|
|
public static UserSettings Create(DirectoryReference settingsDir, ILogger logger)
|
|
{
|
|
return Create(FileReference.Combine(settingsDir, "UnrealGameSyncV2.ini"), FileReference.Combine(settingsDir, "UnrealGameSync.ini"), FileReference.Combine(settingsDir, "Global.json"), logger);
|
|
}
|
|
|
|
public static UserSettings Create(FileReference fileName, FileReference legacyFileName, FileReference coreFileName, ILogger logger)
|
|
{
|
|
ConfigFile configFile = new ConfigFile();
|
|
if (FileReference.Exists(fileName))
|
|
{
|
|
configFile.TryLoad(fileName, logger);
|
|
}
|
|
else
|
|
{
|
|
configFile.TryLoad(legacyFileName, logger);
|
|
}
|
|
|
|
ApplicationTheme.AddDefaultThemesToConfig(configFile);
|
|
|
|
GlobalSettings? coreSettingsData = null;
|
|
if (FileReference.Exists(coreFileName))
|
|
{
|
|
try
|
|
{
|
|
coreSettingsData = Utility.LoadJson<GlobalSettings>(coreFileName);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error while reading {File}.", coreFileName);
|
|
}
|
|
}
|
|
|
|
if (coreSettingsData == null)
|
|
{
|
|
coreSettingsData = new GlobalSettings();
|
|
coreSettingsData.Filter.View.AddRange(configFile.GetValues("General.SyncFilter", Array.Empty<string>()));
|
|
coreSettingsData.Filter.SetCategories(GetCategorySettings(configFile.FindSection("General"), "SyncIncludedCategories", "SyncExcludedCategories"));
|
|
coreSettingsData.Filter.AllProjects = configFile.GetValue("General.SyncAllProjects", false);
|
|
coreSettingsData.Filter.AllProjectsInSln = configFile.GetValue("General.IncludeAllProjectsInSolution", false);
|
|
coreSettingsData.Filter.UprojectSpecificSln = configFile.GetValue("General.UprojectSpecificSolution", false);
|
|
}
|
|
|
|
return new UserSettings(fileName, configFile, coreFileName, coreSettingsData);
|
|
}
|
|
|
|
public UserSettings(FileReference inFileName, ConfigFile inConfigFile, FileReference inCoreFileName, GlobalSettings inCoreSettingsData)
|
|
: base(inCoreFileName, inCoreSettingsData)
|
|
{
|
|
_fileName = inFileName;
|
|
_configFile = inConfigFile;
|
|
|
|
// General settings
|
|
Version = (UserSettingsVersion)_configFile.GetValue("General.Version", (int)UserSettingsVersion.Initial);
|
|
GenerateProjectAfterSync = (_configFile.GetValue("General.GenerateProjectAfterSync", "1") != "0");
|
|
BuildAfterSync = (_configFile.GetValue("General.BuildAfterSync", "1") != "0");
|
|
RunAfterSync = (_configFile.GetValue("General.RunAfterSync", "1") != "0");
|
|
bool syncPrecompiledEditor = (_configFile.GetValue("General.SyncPrecompiledEditor", "0") != "0");
|
|
OpenSolutionAfterSync = (_configFile.GetValue("General.OpenSolutionAfterSync", "0") != "0");
|
|
ShowLogWindow = (_configFile.GetValue("General.ShowLogWindow", false));
|
|
|
|
string? autoResolveConflicts = _configFile.GetValue("General.AutoResolveConflicts", "");
|
|
if (!String.IsNullOrEmpty(autoResolveConflicts))
|
|
{
|
|
Global.AutoResolveConflicts = (autoResolveConflicts != "0");
|
|
}
|
|
|
|
ShowUnreviewedChanges = _configFile.GetValue("General.ShowUnreviewed", true);
|
|
ShowAutomatedChanges = _configFile.GetValue("General.ShowAutomated", false);
|
|
NextLauncherVersionCheck = _configFile.GetValue("General.NextLauncherVersionCheck", 0);
|
|
ShowRobomerge = _configFile.GetEnumValue("General.RobomergeFilter", RobomergeShowChangesOption.All);
|
|
AnnotateRobmergeChanges = _configFile.GetValue("General.AnnotateRobomerge", true);
|
|
ShowLocalTimes = _configFile.GetValue("General.ShowLocalTimes", false);
|
|
KeepInTray = _configFile.GetValue("General.KeepInTray", true);
|
|
|
|
string themeName = _configFile.GetValue("General.Theme", ApplicationTheme.DefaultThemeId);
|
|
Theme = ApplicationTheme.LoadThemeFromConfig(themeName, _configFile);
|
|
ThemeList = ApplicationTheme.GetThemeList(_configFile);
|
|
|
|
EnabledTools.Clear();
|
|
EnabledTools.UnionWith(_configFile.GetGuidValues("General.EnabledTools", Array.Empty<Guid>()));
|
|
if (_configFile.GetValue("General.EnableP4VExtensions", false))
|
|
{
|
|
EnabledTools.Add(new Guid("963850A0-BF63-4E0E-B903-1C5954C7DCF8"));
|
|
}
|
|
if (_configFile.GetValue("General.EnableUshell", false))
|
|
{
|
|
EnabledTools.Add(new Guid("922EED87-E732-464C-92DC-5A8F7ED955E2"));
|
|
}
|
|
|
|
FilterIndex = _configFile.GetValue("General.FilterIndex", 0);
|
|
|
|
string? lastProjectString = _configFile.GetValue("General.LastProject", null);
|
|
if (lastProjectString != null)
|
|
{
|
|
UserSelectedProjectSettings? lastProject;
|
|
if (!UserSelectedProjectSettings.TryParseConfigEntry(lastProjectString, out lastProject))
|
|
{
|
|
lastProject = null;
|
|
}
|
|
LastProject = lastProject;
|
|
}
|
|
else
|
|
{
|
|
string? lastProjectFileName = _configFile.GetValue("General.LastProjectFileName", null);
|
|
if (lastProjectFileName != null)
|
|
{
|
|
LastProject = new UserSelectedProjectSettings(null, null, UserSelectedProjectType.Local, null, lastProjectFileName);
|
|
}
|
|
}
|
|
|
|
OpenProjects = ReadProjectList("General.OpenProjects", "General.OpenProjectFileNames");
|
|
RecentProjects = ReadProjectList("General.RecentProjects", "General.OtherProjectFileNames");
|
|
SyncTypeId = _configFile.GetValue("General.SyncTypeID", "");
|
|
ClientSubmitOptions = _configFile.GetEnumValue("General.ClientSubmitOptions", ClientSubmitOptions.RevertUnchanged);
|
|
string? oldSyncTye = _configFile.GetValue("General.SyncType", null);
|
|
if (oldSyncTye != null)
|
|
{
|
|
if (oldSyncTye == "Any")
|
|
{
|
|
SyncTypeId = LatestChangeType.LatestChange().Name;
|
|
}
|
|
else if (oldSyncTye == "Good")
|
|
{
|
|
SyncTypeId = LatestChangeType.LatestGoodChange().Name;
|
|
}
|
|
else if (oldSyncTye == "Starred")
|
|
{
|
|
SyncTypeId = LatestChangeType.LatestStarredChange().Name;
|
|
}
|
|
}
|
|
|
|
// Build configuration
|
|
CompiledEditorBuildConfig = _configFile.GetEnumValue("General.BuildConfig", BuildConfig.Development);
|
|
|
|
// Tab names
|
|
TabLabels = _configFile.GetEnumValue("General.TabLabels", TabLabels.Stream);
|
|
|
|
// Editor arguments
|
|
string[] arguments = _configFile.GetValues("General.EditorArguments", Array.Empty<string>());
|
|
if (Version < UserSettingsVersion.XgeShaderCompilation)
|
|
{
|
|
arguments = Enumerable.Concat(arguments, new string[] { "0:-noxgeshadercompile" }).ToArray();
|
|
}
|
|
foreach (string argument in arguments)
|
|
{
|
|
if (argument.StartsWith("0:", StringComparison.Ordinal))
|
|
{
|
|
EditorArguments.Add(new LockableEditorArgument(argument.Substring(2), /* Enabled = */ false));
|
|
}
|
|
else if (argument.StartsWith("1:", StringComparison.Ordinal))
|
|
{
|
|
EditorArguments.Add(new LockableEditorArgument(argument.Substring(2), /* Enabled = */ true));
|
|
}
|
|
else
|
|
{
|
|
EditorArguments.Add(new LockableEditorArgument(argument, /* Enabled = */ true));
|
|
}
|
|
}
|
|
EditorArgumentsPrompt = _configFile.GetValue("General.EditorArgumentsPrompt", false);
|
|
|
|
// Precompiled binaries
|
|
string[] archiveValues = _configFile.GetValues("PrecompiledBinaries.Archives", Array.Empty<string>());
|
|
foreach (string archiveValue in archiveValues)
|
|
{
|
|
ArchiveSettings? settings;
|
|
if (ArchiveSettings.TryParseConfigEntry(archiveValue, out settings))
|
|
{
|
|
Archives.Add(settings);
|
|
}
|
|
}
|
|
|
|
if (syncPrecompiledEditor)
|
|
{
|
|
Archives.Add(new ArchiveSettings(true, "Editor", Array.Empty<string>()));
|
|
}
|
|
|
|
// OIDC Settings
|
|
string[] tokens = _configFile.GetValues("OIDCProviders.Tokens", Array.Empty<string>());
|
|
foreach (string tokenValue in tokens)
|
|
{
|
|
ConfigObject o = new ConfigObject(tokenValue);
|
|
string? provider = o.GetValue("Provider");
|
|
string? token = o.GetValue("Token");
|
|
if (provider != null && token != null)
|
|
{
|
|
ProviderToRefreshTokens.TryAdd(provider, token);
|
|
}
|
|
}
|
|
|
|
// Window settings
|
|
WindowVisible = _configFile.GetValue("Window.Visible", true);
|
|
WindowState = _configFile.GetValue("Window.State", "");
|
|
WindowBounds = ParseRectangleValue(_configFile.GetValue("Window.Bounds", ""));
|
|
|
|
// Schedule settings
|
|
ScheduleEnabled = _configFile.GetValue("Schedule.Enabled", false);
|
|
|
|
TimeSpan scheduleTime;
|
|
if (!TimeSpan.TryParse(_configFile.GetValue("Schedule.Time", ""), out scheduleTime))
|
|
{
|
|
scheduleTime = new TimeSpan(6, 0, 0);
|
|
}
|
|
ScheduleTime = scheduleTime;
|
|
|
|
ScheduleAnyOpenProject = _configFile.GetValue("Schedule.AnyOpenProject", true);
|
|
ScheduleProjects = ReadProjectList("Schedule.Projects", "Schedule.ProjectFileNames");
|
|
|
|
// Notification settings
|
|
NotifyProjects = _configFile.GetValues("Notifications.NotifyProjects", Array.Empty<string>()).ToList();
|
|
NotifyUnassignedMinutes = _configFile.GetValue("Notifications.NotifyUnassignedMinutes", -1);
|
|
NotifyUnacknowledgedMinutes = _configFile.GetValue("Notifications.NotifyUnacknowledgedMinutes", -1);
|
|
NotifyUnresolvedMinutes = _configFile.GetValue("Notifications.NotifyUnresolvedMinutes", -1);
|
|
|
|
// Perforce settings
|
|
string? numThreadsStr = _configFile.GetValue("Perforce.NumThreads", null);
|
|
if (numThreadsStr != null)
|
|
{
|
|
int numThreads;
|
|
if (Int32.TryParse(numThreadsStr, out numThreads) && numThreads > 0)
|
|
{
|
|
if (Version >= UserSettingsVersion.DefaultNumberOfThreads || numThreads > 1)
|
|
{
|
|
SyncOptions.NumThreads = numThreads;
|
|
}
|
|
}
|
|
}
|
|
|
|
SyncOptions.MaxCommandsPerBatch = _configFile.GetOptionalIntValue("Perforce.MaxCommandsPerBatch", SyncOptions.MaxCommandsPerBatch);
|
|
SyncOptions.MaxSizePerBatch = _configFile.GetOptionalIntValue("Perforce.MaxSizePerBatch", SyncOptions.MaxSizePerBatch);
|
|
SyncOptions.NumSyncErrorRetries = _configFile.GetOptionalIntValue("Perforce.NumSyncErrorRetries", SyncOptions.NumSyncErrorRetries);
|
|
SyncOptions.SyncErrorRetryDelay = _configFile.GetOptionalIntValue("Perforce.SyncErrorRetryDelay", SyncOptions.SyncErrorRetryDelay);
|
|
}
|
|
|
|
static Dictionary<Guid, bool> GetCategorySettings(ConfigSection? section, string includedKey, string excludedKey)
|
|
{
|
|
Dictionary<Guid, bool> result = new Dictionary<Guid, bool>();
|
|
if (section != null)
|
|
{
|
|
foreach (Guid uniqueId in section.GetValues(includedKey, Array.Empty<Guid>()))
|
|
{
|
|
result[uniqueId] = true;
|
|
}
|
|
foreach (Guid uniqueId in section.GetValues(excludedKey, Array.Empty<Guid>()))
|
|
{
|
|
result[uniqueId] = false;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static Rectangle? ParseRectangleValue(string text)
|
|
{
|
|
ConfigObject obj = new ConfigObject(text);
|
|
|
|
int x = obj.GetValue("X", -1);
|
|
int y = obj.GetValue("Y", -1);
|
|
int w = obj.GetValue("W", -1);
|
|
int h = obj.GetValue("H", -1);
|
|
|
|
if (x == -1 || y == -1 || w == -1 || h == -1)
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
return new Rectangle(x, y, w, h);
|
|
}
|
|
}
|
|
|
|
static string FormatRectangleValue(Rectangle value)
|
|
{
|
|
ConfigObject obj = new ConfigObject();
|
|
|
|
obj.SetValue("X", value.X);
|
|
obj.SetValue("Y", value.Y);
|
|
obj.SetValue("W", value.Width);
|
|
obj.SetValue("H", value.Height);
|
|
|
|
return obj.ToString();
|
|
}
|
|
|
|
public static DirectoryReference GetConfigDir(DirectoryReference workspaceDir)
|
|
{
|
|
DirectoryReference configDir = DirectoryReference.Combine(workspaceDir, ".ugs");
|
|
return configDir;
|
|
}
|
|
|
|
public static void CreateConfigDir(DirectoryReference configDir)
|
|
{
|
|
DirectoryInfo configDirInfo = configDir.ToDirectoryInfo();
|
|
if (!configDirInfo.Exists)
|
|
{
|
|
configDirInfo.Create();
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
{
|
|
configDirInfo.Attributes = FileAttributes.Directory | FileAttributes.Hidden;
|
|
}
|
|
}
|
|
}
|
|
|
|
public ApplicationTheme? LoadTheme(string themeId)
|
|
{
|
|
return ApplicationTheme.LoadThemeFromConfig(themeId, _configFile);
|
|
}
|
|
|
|
protected override void ImportWorkspaceState(DirectoryReference rootDir, string clientName, string branchPath, WorkspaceState currentWorkspace)
|
|
{
|
|
// Read the workspace settings
|
|
ConfigSection? workspaceSection = _configFile.FindSection(clientName + branchPath);
|
|
if (workspaceSection == null)
|
|
{
|
|
string legacyBranchAndClientKey = clientName + branchPath;
|
|
|
|
int slashIdx = legacyBranchAndClientKey.IndexOf('/', StringComparison.Ordinal);
|
|
if (slashIdx != -1)
|
|
{
|
|
legacyBranchAndClientKey = legacyBranchAndClientKey.Substring(0, slashIdx) + "$" + legacyBranchAndClientKey.Substring(slashIdx + 1);
|
|
}
|
|
|
|
string? currentSync = _configFile.GetValue("Clients." + legacyBranchAndClientKey, null);
|
|
if (currentSync != null)
|
|
{
|
|
int atIdx = currentSync.LastIndexOf('@');
|
|
if (atIdx != -1)
|
|
{
|
|
int changeNumber;
|
|
if (Int32.TryParse(currentSync.Substring(atIdx + 1), out changeNumber))
|
|
{
|
|
currentWorkspace.ProjectIdentifier = currentSync.Substring(0, atIdx);
|
|
currentWorkspace.CurrentChangeNumber = changeNumber;
|
|
}
|
|
}
|
|
}
|
|
|
|
string? lastUpdateResultText = _configFile.GetValue("Clients." + legacyBranchAndClientKey + "$LastUpdate", null);
|
|
if (lastUpdateResultText != null)
|
|
{
|
|
int colonIdx = lastUpdateResultText.LastIndexOf(':');
|
|
if (colonIdx != -1)
|
|
{
|
|
int changeNumber;
|
|
if (Int32.TryParse(lastUpdateResultText.Substring(0, colonIdx), out changeNumber))
|
|
{
|
|
WorkspaceUpdateResult result;
|
|
if (Enum.TryParse(lastUpdateResultText.Substring(colonIdx + 1), out result))
|
|
{
|
|
currentWorkspace.LastSyncChangeNumber = changeNumber;
|
|
currentWorkspace.LastSyncResult = result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
currentWorkspace.ProjectIdentifier = workspaceSection.GetValue("CurrentProjectPath", "");
|
|
currentWorkspace.CurrentChangeNumber = workspaceSection.GetValue("CurrentChangeNumber", -1);
|
|
currentWorkspace.CurrentSyncFilterHash = workspaceSection.GetValue("CurrentSyncFilterHash", null);
|
|
foreach (string additionalChangeNumberString in workspaceSection.GetValues("AdditionalChangeNumbers", Array.Empty<string>()))
|
|
{
|
|
int additionalChangeNumber;
|
|
if (Int32.TryParse(additionalChangeNumberString, out additionalChangeNumber))
|
|
{
|
|
currentWorkspace.AdditionalChangeNumbers.Add(additionalChangeNumber);
|
|
}
|
|
}
|
|
|
|
currentWorkspace.LastSyncResult = workspaceSection.GetEnumValue("LastSyncResult", WorkspaceUpdateResult.Canceled);
|
|
currentWorkspace.LastSyncResultMessage = UnescapeText(workspaceSection.GetValue("LastSyncResultMessage"));
|
|
currentWorkspace.LastSyncChangeNumber = workspaceSection.GetValue("LastSyncChangeNumber", -1);
|
|
|
|
DateTime lastSyncTime;
|
|
if (DateTime.TryParse(workspaceSection.GetValue("LastSyncTime", ""), out lastSyncTime))
|
|
{
|
|
currentWorkspace.LastSyncTime = lastSyncTime;
|
|
}
|
|
|
|
currentWorkspace.LastSyncDurationSeconds = workspaceSection.GetValue("LastSyncDuration", 0);
|
|
currentWorkspace.LastBuiltChangeNumber = workspaceSection.GetValue("LastBuiltChangeNumber", 0);
|
|
|
|
currentWorkspace.LastSyncEditorArchive = workspaceSection.GetValue("LastSyncEditorArchive", "0");
|
|
|
|
currentWorkspace.ExpandedArchiveTypes.Clear();
|
|
currentWorkspace.ExpandedArchiveTypes.UnionWith(workspaceSection.GetValues("ExpandedArchiveName", Array.Empty<string>()));
|
|
|
|
string[] bisectEntries = workspaceSection.GetValues("Bisect", Array.Empty<string>());
|
|
foreach (string bisectEntry in bisectEntries)
|
|
{
|
|
ConfigObject bisectEntryObject = new ConfigObject(bisectEntry);
|
|
|
|
int changeNumber = bisectEntryObject.GetValue("Change", -1);
|
|
if (changeNumber != -1)
|
|
{
|
|
BisectState state;
|
|
if (Enum.TryParse(bisectEntryObject.GetValue("State", ""), out state))
|
|
{
|
|
BisectEntry entry = new BisectEntry();
|
|
entry.Change = changeNumber;
|
|
entry.State = state;
|
|
currentWorkspace.BisectChanges.Add(entry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override void ImportWorkspaceSettings(DirectoryReference rootDir, string clientName, string branchPath, UserWorkspaceSettings currentWorkspace)
|
|
{
|
|
ConfigSection? workspaceSection = _configFile.FindSection(clientName + branchPath);
|
|
if (workspaceSection != null)
|
|
{
|
|
currentWorkspace.Filter.View.Clear();
|
|
currentWorkspace.Filter.View.AddRange(workspaceSection.GetValues("SyncFilter", Array.Empty<string>()));
|
|
|
|
currentWorkspace.Filter.SetCategories(GetCategorySettings(workspaceSection, "SyncIncludedCategories", "SyncExcludedCategories"));
|
|
|
|
int syncAllProjects = workspaceSection.GetValue("SyncAllProjects", -1);
|
|
currentWorkspace.Filter.AllProjects = (syncAllProjects == 0) ? (bool?)false : (syncAllProjects == 1) ? (bool?)true : (bool?)null;
|
|
|
|
int includeAllProjectsInSolution = workspaceSection.GetValue("IncludeAllProjectsInSolution", -1);
|
|
currentWorkspace.Filter.AllProjectsInSln = (includeAllProjectsInSolution == 0) ? (bool?)false : (includeAllProjectsInSolution == 1) ? (bool?)true : (bool?)null;
|
|
|
|
int uprojectSpecificSolution = workspaceSection.GetValue("UprojectSpecificSolution", -1);
|
|
currentWorkspace.Filter.UprojectSpecificSln = (uprojectSpecificSolution == 0) ? (bool?)false : (uprojectSpecificSolution == 1) ? (bool?)true : (bool?)null;
|
|
}
|
|
}
|
|
|
|
protected override void ImportProjectSettings(ProjectInfo projectInfo, UserProjectSettings currentProject)
|
|
{
|
|
string clientProjectFileName = projectInfo.ClientFileName;
|
|
|
|
// Read the project settings
|
|
ConfigSection projectSection = _configFile.FindOrAddSection(clientProjectFileName);
|
|
currentProject.BuildSteps.AddRange(projectSection.GetValues("BuildStep", Array.Empty<string>()).Select(x => new ConfigObject(x)));
|
|
currentProject.FilterType = projectSection.GetEnumValue("FilterType", FilterType.None);
|
|
currentProject.FilterBadges.UnionWith(projectSection.GetValues("FilterBadges", Array.Empty<string>()));
|
|
currentProject.RequiredBadges.AddRange(projectSection.GetValues("RequiredBadges", Array.Empty<string>()));
|
|
}
|
|
|
|
public override bool Save(ILogger logger)
|
|
{
|
|
if (!base.Save(logger))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// General settings
|
|
ConfigSection generalSection = _configFile.FindOrAddSection("General");
|
|
generalSection.Clear();
|
|
generalSection.SetValue("Version", (int)Version);
|
|
generalSection.SetValue("GenerateProjectAfterSync", GenerateProjectAfterSync);
|
|
generalSection.SetValue("BuildAfterSync", BuildAfterSync);
|
|
generalSection.SetValue("RunAfterSync", RunAfterSync);
|
|
generalSection.SetValue("OpenSolutionAfterSync", OpenSolutionAfterSync);
|
|
generalSection.SetValue("ShowLogWindow", ShowLogWindow);
|
|
generalSection.SetValue("ShowUnreviewed", ShowUnreviewedChanges);
|
|
generalSection.SetValue("ShowAutomated", ShowAutomatedChanges);
|
|
generalSection.SetValue("RobomergeFilter", ShowRobomerge.ToString());
|
|
generalSection.SetValue("AnnotateRobomerge", AnnotateRobmergeChanges);
|
|
generalSection.SetValue("ShowLocalTimes", ShowLocalTimes);
|
|
if (LastProject != null)
|
|
{
|
|
generalSection.SetValue("LastProject", LastProject.ToConfigEntry());
|
|
}
|
|
generalSection.SetValues("OpenProjects", OpenProjects.Select(x => x.ToConfigEntry()).ToArray());
|
|
generalSection.SetValue("KeepInTray", KeepInTray);
|
|
generalSection.SetValues("EnabledTools", EnabledTools.ToArray());
|
|
generalSection.SetValue("FilterIndex", FilterIndex);
|
|
generalSection.SetValues("RecentProjects", RecentProjects.Select(x => x.ToConfigEntry()).ToArray());
|
|
generalSection.SetValue("SyncTypeID", SyncTypeId);
|
|
generalSection.SetValue("ClientSubmitOptions", ClientSubmitOptions.ToString());
|
|
generalSection.SetValue("NextLauncherVersionCheck", NextLauncherVersionCheck);
|
|
generalSection.SetValue("Theme", Theme?.ThemeId ?? ApplicationTheme.DefaultThemeId);
|
|
|
|
// Build configuration
|
|
generalSection.SetValue("BuildConfig", CompiledEditorBuildConfig.ToString());
|
|
|
|
// Tab labels
|
|
generalSection.SetValue("TabLabels", TabLabels.ToString());
|
|
|
|
// Editor arguments
|
|
List<string> editorArgumentList = new List<string>();
|
|
foreach (LockableEditorArgument editorArgument in EditorArguments)
|
|
{
|
|
editorArgumentList.Add(String.Format("{0}:{1}", editorArgument.Enabled ? 1 : 0, editorArgument.Name));
|
|
}
|
|
generalSection.SetValues("EditorArguments", editorArgumentList.ToArray());
|
|
generalSection.SetValue("EditorArgumentsPrompt", EditorArgumentsPrompt);
|
|
|
|
// Schedule settings
|
|
ConfigSection scheduleSection = _configFile.FindOrAddSection("Schedule");
|
|
scheduleSection.Clear();
|
|
scheduleSection.SetValue("Enabled", ScheduleEnabled);
|
|
scheduleSection.SetValue("Time", ScheduleTime.ToString());
|
|
scheduleSection.SetValue("AnyOpenProject", ScheduleAnyOpenProject);
|
|
scheduleSection.SetValues("Projects", ScheduleProjects.Select(x => x.ToConfigEntry()).ToArray());
|
|
|
|
// Precompiled binaries
|
|
ConfigSection archivesSection = _configFile.FindOrAddSection("PrecompiledBinaries");
|
|
archivesSection.SetValues("Archives", Archives.Select(x => x.ToConfigEntry()).ToArray());
|
|
|
|
// OIDC Settings
|
|
ConfigSection oidcSection = _configFile.FindOrAddSection("OIDCProviders");
|
|
List<ConfigObject> tokenObjects = new List<ConfigObject>();
|
|
foreach (KeyValuePair<string, string> pair in ProviderToRefreshTokens)
|
|
{
|
|
ConfigObject tokenEntryObject = new ConfigObject();
|
|
tokenEntryObject.SetValue("Provider", pair.Key);
|
|
tokenEntryObject.SetValue("Token", pair.Value);
|
|
tokenObjects.Add(tokenEntryObject);
|
|
}
|
|
oidcSection.SetValues("Tokens", tokenObjects.Select(x => x.ToString()).ToArray());
|
|
|
|
// Window settings
|
|
ConfigSection windowSection = _configFile.FindOrAddSection("Window");
|
|
windowSection.Clear();
|
|
windowSection.SetValue("Visible", WindowVisible);
|
|
windowSection.SetValue("State", WindowState);
|
|
if (WindowBounds != null)
|
|
{
|
|
windowSection.SetValue("Bounds", FormatRectangleValue(WindowBounds.Value));
|
|
}
|
|
|
|
// Notification settings
|
|
ConfigSection notificationSection = _configFile.FindOrAddSection("Notifications");
|
|
notificationSection.Clear();
|
|
if (NotifyProjects.Count > 0)
|
|
{
|
|
notificationSection.SetValues("NotifyProjects", NotifyProjects.ToArray());
|
|
}
|
|
if (NotifyUnassignedMinutes != -1)
|
|
{
|
|
notificationSection.SetValue("NotifyUnassignedMinutes", NotifyUnassignedMinutes);
|
|
}
|
|
if (NotifyUnacknowledgedMinutes != -1)
|
|
{
|
|
notificationSection.SetValue("NotifyUnacknowledgedMinutes", NotifyUnacknowledgedMinutes);
|
|
}
|
|
if (NotifyUnresolvedMinutes != -1)
|
|
{
|
|
notificationSection.SetValue("NotifyUnresolvedMinutes", NotifyUnresolvedMinutes);
|
|
}
|
|
|
|
// Perforce settings
|
|
_configFile.RemoveSection("Perforce");
|
|
|
|
// Save the file
|
|
try
|
|
{
|
|
_configFile.Save(_fileName);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Unable to save config file {FileName}: {Message}", _fileName, ex.Message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
[return: NotNullIfNotNull("text")]
|
|
static string? UnescapeText(string? text)
|
|
{
|
|
if (text == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
StringBuilder result = new StringBuilder();
|
|
for (int idx = 0; idx < text.Length; idx++)
|
|
{
|
|
if (text[idx] == '\\' && idx + 1 < text.Length)
|
|
{
|
|
switch (text[++idx])
|
|
{
|
|
case 't':
|
|
result.Append('\t');
|
|
break;
|
|
case 'r':
|
|
result.Append('\r');
|
|
break;
|
|
case 'n':
|
|
result.Append('\n');
|
|
break;
|
|
case '\'':
|
|
result.Append('\'');
|
|
break;
|
|
case '\"':
|
|
result.Append('\"');
|
|
break;
|
|
default:
|
|
result.Append(text[idx]);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.Append(text[idx]);
|
|
}
|
|
}
|
|
return result.ToString();
|
|
}
|
|
|
|
public IEnumerable<FileReference> GetCachedFilePaths()
|
|
{
|
|
List<FileReference> files = new List<FileReference>();
|
|
files.AddRange(_workspaceDirToSettings.Values.Select(x => x.ConfigFile));
|
|
files.AddRange(_projectKeyToSettings.Keys);
|
|
return files;
|
|
}
|
|
}
|
|
}
|