Files
UnrealEngineUWP/Engine/Source/Programs/UnrealGameSync/UnrealGameSyncShared/ConfigFile.cs
Ben Marsh 0a11ef3048 Back out UGS upgrade to NET 6
#fyi Joe.Kirchoff
#preflight none

[CL 20593478 by Ben Marsh in ue5-main branch]
2022-06-10 07:36:46 -04:00

677 lines
15 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using EpicGames.Core;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace UnrealGameSync
{
[JsonConverter(typeof(ConfigObjectJsonConverter))]
public class ConfigObject
{
const string ConfigSeparatorCharacters = "(),= \t\"";
public List<KeyValuePair<string, string>> Pairs;
public ConfigObject()
{
Pairs = new List<KeyValuePair<string,string>>();
}
public ConfigObject(ConfigObject Other)
{
Pairs = new List<KeyValuePair<string,string>>(Other.Pairs);
}
public ConfigObject(string Text)
{
Pairs = new List<KeyValuePair<string,string>>();
ParseConfigString(Text);
}
public ConfigObject(ConfigObject BaseObject, string Text)
{
Pairs = new List<KeyValuePair<string,string>>(BaseObject.Pairs);
ParseConfigString(Text);
}
void ParseConfigString(string Text)
{
int Idx = 0;
if(ParseConfigToken(Text, ref Idx) == "(")
{
while(Idx < Text.Length)
{
// Read the next key/value pair
string? Key = ParseConfigToken(Text, ref Idx);
if (Key == null)
{
return;
}
if(ParseConfigToken(Text, ref Idx) == "=")
{
string? Value = ParseConfigValueToken(Text, ref Idx);
if (Value == null)
{
return;
}
SetValue(Key, Value);
}
// Check for the end of the list, or a comma before the next pair
for(;;)
{
string? Token = ParseConfigValueToken(Text, ref Idx);
if(Token == ",")
{
break;
}
if(Token == ")" || Token == null)
{
return;
}
}
}
}
}
static string? ParseConfigToken(string Text, ref int Idx)
{
// Skip whitespace
while(Idx < Text.Length && Char.IsWhiteSpace(Text[Idx]))
{
Idx++;
}
if(Idx == Text.Length)
{
return null;
}
// Read the token
if (Text[Idx] == '\"')
{
StringBuilder Token = new StringBuilder();
while (++Idx < Text.Length)
{
if (Text[Idx] == '\"')
{
Idx++;
break;
}
if (Text[Idx] == '\\' && Idx + 1 < Text.Length)
{
Idx++;
}
Token.Append(Text[Idx]);
}
return Token.ToString();
}
else if (ConfigSeparatorCharacters.IndexOf(Text[Idx]) != -1)
{
return Text[Idx++].ToString();
}
else
{
int StartIdx = Idx;
while (Idx < Text.Length && ConfigSeparatorCharacters.IndexOf(Text[Idx]) == -1)
{
Idx++;
}
return Text.Substring(StartIdx, Idx - StartIdx);
}
}
static string? ParseConfigValueToken(string Text, ref int Idx)
{
string? Token = ParseConfigToken(Text, ref Idx);
if (Token == "(")
{
int StartIdx = Idx - 1;
for (; ; )
{
string? NextToken = ParseConfigValueToken(Text, ref Idx);
if (NextToken == null || NextToken == ")")
{
break;
}
}
Token = Text.Substring(StartIdx, Idx - StartIdx);
}
return Token;
}
[return: NotNullIfNotNull("DefaultValue")]
public string? GetValue(string Key, string? DefaultValue = null)
{
for(int Idx = 0; Idx < Pairs.Count; Idx++)
{
if(Pairs[Idx].Key.Equals(Key, StringComparison.InvariantCultureIgnoreCase))
{
return Pairs[Idx].Value;
}
}
return DefaultValue;
}
public Guid GetValue(string Key, Guid DefaultValue)
{
string? StringValue = GetValue(Key);
if(StringValue != null)
{
Guid Value;
if(Guid.TryParse(StringValue, out Value))
{
return Value;
}
}
return DefaultValue;
}
public int GetValue(string Key, int DefaultValue)
{
string? StringValue = GetValue(Key);
if(StringValue != null)
{
int Value;
if(int.TryParse(StringValue, out Value))
{
return Value;
}
}
return DefaultValue;
}
public bool GetValue(string Key, bool DefaultValue)
{
string? StringValue = GetValue(Key);
if(StringValue != null)
{
bool Value;
if(bool.TryParse(StringValue, out Value))
{
return Value;
}
}
return DefaultValue;
}
public void SetValue(string Key, string? Value)
{
if (Value == null)
{
Pairs.RemoveAll(x => x.Key.Equals(Key, StringComparison.OrdinalIgnoreCase));
}
else
{
for (int Idx = 0; Idx < Pairs.Count; Idx++)
{
if (Pairs[Idx].Key.Equals(Key, StringComparison.OrdinalIgnoreCase))
{
Pairs[Idx] = new KeyValuePair<string, string>(Key, Value);
return;
}
}
Pairs.Add(new KeyValuePair<string, string>(Key, Value));
}
}
public void SetValue(string Key, Guid Value)
{
SetValue(Key, Value.ToString());
}
public void SetValue(string Key, int Value)
{
SetValue(Key, Value.ToString());
}
public void SetValue(string Key, bool Value)
{
SetValue(Key, Value.ToString());
}
public string? this[string Key]
{
get { return GetValue(Key); }
set { SetValue(Key, value); }
}
public void SetDefaults(ConfigObject Other)
{
foreach(KeyValuePair<string, string> Pair in Other.Pairs)
{
if(GetValue(Pair.Key) == null)
{
SetValue(Pair.Key, Pair.Value);
}
}
}
public void AddOverrides(ConfigObject Object, ConfigObject? DefaultObject)
{
foreach(KeyValuePair<string, string> Pair in Object.Pairs)
{
if(DefaultObject == null || DefaultObject.GetValue(Pair.Key) != Pair.Value)
{
SetValue(Pair.Key, Pair.Value);
}
}
}
public string ToString(ConfigObject? BaseObject)
{
StringBuilder Result = new StringBuilder();
Result.Append("(");
foreach(KeyValuePair<string, string> Pair in Pairs)
{
if(BaseObject == null || BaseObject.GetValue(Pair.Key) != Pair.Value)
{
if(Result.Length > 1)
{
Result.Append(", ");
}
Result.Append(Pair.Key);
Result.Append("=");
if(Pair.Value == null)
{
Result.Append("\"\"");
}
else
{
Result.AppendFormat("\"{0}\"", Pair.Value.Replace("\\", "\\\\").Replace("\"", "\\\""));
}
}
}
Result.Append(")");
return Result.ToString();
}
public override string ToString()
{
return ToString(null);
}
}
public class ConfigObjectJsonConverter : JsonConverter<ConfigObject>
{
public override ConfigObject Read(ref Utf8JsonReader Reader, Type TypeToConvert, JsonSerializerOptions Options)
{
ConfigObject Object = new ConfigObject();
if (Reader.TokenType == JsonTokenType.StartObject)
{
while (Reader.Read() && Reader.TokenType == JsonTokenType.PropertyName)
{
string Name = Reader.GetString();
Reader.Read();
string Value = Reader.GetString();
Object.Pairs.Add(KeyValuePair.Create(Name, Value));
}
}
else
{
Reader.Skip();
}
return Object;
}
public override void Write(Utf8JsonWriter Writer, ConfigObject Value, JsonSerializerOptions Options)
{
Writer.WriteStartObject();
foreach (KeyValuePair<string, string> Pair in Value.Pairs)
{
Writer.WriteString(Pair.Key, Pair.Value);
}
Writer.WriteEndObject();
}
}
[DebuggerDisplay("{Name}")]
public class ConfigSection
{
public string Name;
public Dictionary<string, string> Pairs = new Dictionary<string,string>();
public ConfigSection(string InName)
{
Name = InName;
}
public void Clear()
{
Pairs.Clear();
}
public void SetValue(string Key, int Value)
{
Pairs[Key] = Value.ToString();
}
public void SetValue(string Key, bool Value)
{
Pairs[Key] = Value? "1" : "0";
}
public void SetValue(string Key, string Value)
{
if(Value == null)
{
RemoveValue(Key);
}
else
{
Pairs[Key] = Value;
}
}
public void SetValues(string Key, string[] Values)
{
if(Values == null || Values.Length == 0)
{
RemoveValue(Key);
}
else
{
Pairs[Key] = String.Join("\n", Values);
}
}
public void SetValues(string Key, Guid[] Values)
{
if(Values == null)
{
RemoveValue(Key);
}
else
{
Pairs[Key] = String.Join("\n", Values.Select(x => x.ToString()));
}
}
public void AppendValue(string Key, string Value)
{
string? CurrentValue;
if(Pairs.TryGetValue(Key, out CurrentValue))
{
Pairs[Key] = CurrentValue + "\n" + Value;
}
else
{
Pairs[Key] = Value;
}
}
public void RemoveValue(string Key)
{
Pairs.Remove(Key);
}
public int GetValue(string Key, int DefaultValue)
{
string? ValueString = GetValue(Key);
if(ValueString != null)
{
int Value;
if(int.TryParse(ValueString, out Value))
{
return Value;
}
}
return DefaultValue;
}
public bool GetValue(string Key, bool DefaultValue)
{
return GetValue(Key, DefaultValue? 1 : 0) != 0;
}
[return: NotNullIfNotNull("DefaultValue")]
public string? GetValue(string Key, string? DefaultValue = null)
{
string? Value;
if(!Pairs.TryGetValue(Key, out Value))
{
Value = DefaultValue;
}
return Value;
}
[return: NotNullIfNotNull("DefaultValue")]
public string[]? GetValues(string Key, string[]? DefaultValue = null)
{
string? Value = GetValue(Key, null);
if(Value == null)
{
return DefaultValue;
}
else
{
return Value.Split('\n');
}
}
[return: NotNullIfNotNull("DefaultValue")]
public Guid[]? GetValues(string Key, Guid[]? DefaultValue = null)
{
string[]? StringValues = GetValues(Key, (string[]?)null);
if(StringValues == null)
{
return DefaultValue;
}
List<Guid> GuidValues = new List<Guid>();
foreach(string StringValue in StringValues)
{
Guid GuidValue;
if(Guid.TryParse(StringValue, out GuidValue))
{
GuidValues.Add(GuidValue);
}
}
return GuidValues.ToArray();
}
}
public class ConfigFile
{
List<ConfigSection> Sections = new List<ConfigSection>();
public ConfigFile()
{
}
FileReference GetTempFileName(FileReference FileName)
{
return FileName + ".tmp";
}
public void Load(FileReference FileName)
{
Parse(FileReference.ReadAllLines(FileName));
}
public bool TryLoad(FileReference FileName, ILogger Logger)
{
FileInfo FileInfo = FileName.ToFileInfo();
if (FileInfo.Exists)
{
Logger.LogDebug("Loading config file from {File} ({Size} bytes)", FileInfo.FullName, FileInfo.Length);
Load(FileName);
return true;
}
FileReference TempFileName = GetTempFileName(FileName);
FileInfo TempFileInfo = TempFileName.ToFileInfo();
if (TempFileInfo.Exists)
{
Logger.LogDebug("Loading temporary config file from {File} ({Size} bytes)", TempFileInfo.FullName, TempFileInfo.Length);
Load(TempFileName);
return true;
}
Logger.LogDebug("No existing config file at {File}", FileName);
return false;
}
public void Parse(string[] Lines)
{
ConfigSection? CurrentSection = null;
foreach(string Line in Lines)
{
string TrimLine = Line.Trim();
if(!TrimLine.StartsWith(";"))
{
if(TrimLine.StartsWith("[") && TrimLine.EndsWith("]"))
{
string SectionName = TrimLine.Substring(1, TrimLine.Length - 2).Trim();
CurrentSection = FindOrAddSection(SectionName);
}
else if(CurrentSection != null)
{
int EqualsIdx = TrimLine.IndexOf('=');
if(EqualsIdx != -1)
{
string Value = Line.Substring(EqualsIdx + 1).TrimStart();
if(TrimLine.StartsWith("+"))
{
CurrentSection.AppendValue(TrimLine.Substring(1, EqualsIdx - 1).Trim(), Value);
}
else
{
CurrentSection.SetValue(TrimLine.Substring(0, EqualsIdx).TrimEnd(), Value);
}
}
}
}
}
}
public void Save(FileReference FileName)
{
FileReference TempFileName = GetTempFileName(FileName);
FileReference.Delete(TempFileName);
using (StreamWriter Writer = new StreamWriter(TempFileName.FullName))
{
for(int Idx = 0; Idx < Sections.Count; Idx++)
{
Writer.WriteLine("[{0}]", Sections[Idx].Name);
foreach(KeyValuePair<string, string> Pair in Sections[Idx].Pairs)
{
if(Pair.Value.Contains('\n'))
{
foreach(string Line in Pair.Value.Split('\n'))
{
Writer.WriteLine("+{0}={1}", Pair.Key, Line);
}
}
else
{
Writer.WriteLine("{0}={1}", Pair.Key, Pair.Value);
}
}
if(Idx < Sections.Count - 1)
{
Writer.WriteLine();
}
}
}
FileReference.Delete(FileName);
FileReference.Move(TempFileName, FileName);
}
public ConfigSection FindSection(string Name)
{
return Sections.FirstOrDefault(x => String.Compare(x.Name, Name, true) == 0);
}
public ConfigSection FindOrAddSection(string Name)
{
ConfigSection Section = FindSection(Name);
if(Section == null)
{
Section = new ConfigSection(Name);
Sections.Add(Section);
}
return Section;
}
public void SetValue(string Key, int Value)
{
int DotIdx = Key.IndexOf('.');
ConfigSection Section = FindOrAddSection(Key.Substring(0, DotIdx));
Section.SetValue(Key.Substring(DotIdx + 1), Value);
}
public void SetValue(string Key, bool Value)
{
int DotIdx = Key.IndexOf('.');
ConfigSection Section = FindOrAddSection(Key.Substring(0, DotIdx));
Section.SetValue(Key.Substring(DotIdx + 1), Value);
}
public void SetValue(string Key, string Value)
{
int DotIdx = Key.IndexOf('.');
ConfigSection Section = FindOrAddSection(Key.Substring(0, DotIdx));
Section.SetValue(Key.Substring(DotIdx + 1), Value);
}
public void SetValues(string Key, string[] Values)
{
int DotIdx = Key.IndexOf('.');
ConfigSection Section = FindOrAddSection(Key.Substring(0, DotIdx));
Section.SetValues(Key.Substring(DotIdx + 1), Values);
}
public bool GetValue(string Key, bool DefaultValue)
{
int DotIdx = Key.IndexOf('.');
ConfigSection Section = FindSection(Key.Substring(0, DotIdx));
return (Section == null)? DefaultValue : Section.GetValue(Key.Substring(DotIdx + 1), DefaultValue);
}
public int GetValue(string Key, int DefaultValue)
{
int DotIdx = Key.IndexOf('.');
ConfigSection Section = FindSection(Key.Substring(0, DotIdx));
return (Section == null)? DefaultValue : Section.GetValue(Key.Substring(DotIdx + 1), DefaultValue);
}
[return: NotNullIfNotNull("DefaultValue")]
public string? GetValue(string Key, string? DefaultValue)
{
int DotIdx = Key.IndexOf('.');
ConfigSection Section = FindSection(Key.Substring(0, DotIdx));
return (Section == null)? DefaultValue : Section.GetValue(Key.Substring(DotIdx + 1), DefaultValue);
}
[return: NotNullIfNotNull("DefaultValue")]
public string[]? GetValues(string Key, string[]? DefaultValue)
{
int DotIdx = Key.IndexOf('.');
ConfigSection Section = FindSection(Key.Substring(0, DotIdx));
return (Section == null)? DefaultValue : Section.GetValues(Key.Substring(DotIdx + 1), DefaultValue);
}
[return: NotNullIfNotNull("DefaultValue")]
public Guid[]? GetGuidValues(string Key, Guid[]? DefaultValue)
{
int DotIdx = Key.IndexOf('.');
ConfigSection Section = FindSection(Key.Substring(0, DotIdx));
return (Section == null)? DefaultValue : Section.GetValues(Key.Substring(DotIdx + 1), DefaultValue);
}
}
}