TomlTypeConverter.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Text.RegularExpressions;
  5. using UnityEngine;
  6. namespace BepInEx.Configuration
  7. {
  8. /// <summary>
  9. /// Serializer/deserializer used by the config system.
  10. /// </summary>
  11. public static class TomlTypeConverter
  12. {
  13. private static Dictionary<Type, TypeConverter> TypeConverters { get; } = new Dictionary<Type, TypeConverter>
  14. {
  15. [typeof(string)] = new TypeConverter
  16. {
  17. ConvertToString = (obj, type) => Regex.Escape((string)obj),
  18. ConvertToObject = (str, type) =>
  19. {
  20. // Check if the string is a file path with unescaped \ path separators (e.g. D:\test and not D:\\test)
  21. if (Regex.IsMatch(str, @"^""?\w:\\(?!\\)(?!.+\\\\)"))
  22. return str;
  23. return Regex.Unescape(str);
  24. },
  25. },
  26. [typeof(bool)] = new TypeConverter
  27. {
  28. ConvertToString = (obj, type) => obj.ToString().ToLowerInvariant(),
  29. ConvertToObject = (str, type) => bool.Parse(str),
  30. },
  31. [typeof(byte)] = new TypeConverter
  32. {
  33. ConvertToString = (obj, type) => obj.ToString(),
  34. ConvertToObject = (str, type) => byte.Parse(str),
  35. },
  36. //integral types
  37. [typeof(sbyte)] = new TypeConverter
  38. {
  39. ConvertToString = (obj, type) => obj.ToString(),
  40. ConvertToObject = (str, type) => sbyte.Parse(str),
  41. },
  42. [typeof(byte)] = new TypeConverter
  43. {
  44. ConvertToString = (obj, type) => obj.ToString(),
  45. ConvertToObject = (str, type) => byte.Parse(str),
  46. },
  47. [typeof(short)] = new TypeConverter
  48. {
  49. ConvertToString = (obj, type) => obj.ToString(),
  50. ConvertToObject = (str, type) => short.Parse(str),
  51. },
  52. [typeof(ushort)] = new TypeConverter
  53. {
  54. ConvertToString = (obj, type) => obj.ToString(),
  55. ConvertToObject = (str, type) => ushort.Parse(str),
  56. },
  57. [typeof(int)] = new TypeConverter
  58. {
  59. ConvertToString = (obj, type) => obj.ToString(),
  60. ConvertToObject = (str, type) => int.Parse(str),
  61. },
  62. [typeof(uint)] = new TypeConverter
  63. {
  64. ConvertToString = (obj, type) => obj.ToString(),
  65. ConvertToObject = (str, type) => uint.Parse(str),
  66. },
  67. [typeof(long)] = new TypeConverter
  68. {
  69. ConvertToString = (obj, type) => obj.ToString(),
  70. ConvertToObject = (str, type) => long.Parse(str),
  71. },
  72. [typeof(ulong)] = new TypeConverter
  73. {
  74. ConvertToString = (obj, type) => obj.ToString(),
  75. ConvertToObject = (str, type) => ulong.Parse(str),
  76. },
  77. //floating point types
  78. [typeof(float)] = new TypeConverter
  79. {
  80. ConvertToString = (obj, type) => ((float)obj).ToString(NumberFormatInfo.InvariantInfo),
  81. ConvertToObject = (str, type) => float.Parse(str, NumberFormatInfo.InvariantInfo),
  82. },
  83. [typeof(double)] = new TypeConverter
  84. {
  85. ConvertToString = (obj, type) => ((double)obj).ToString(NumberFormatInfo.InvariantInfo),
  86. ConvertToObject = (str, type) => double.Parse(str, NumberFormatInfo.InvariantInfo),
  87. },
  88. [typeof(decimal)] = new TypeConverter
  89. {
  90. ConvertToString = (obj, type) => ((decimal)obj).ToString(NumberFormatInfo.InvariantInfo),
  91. ConvertToObject = (str, type) => decimal.Parse(str, NumberFormatInfo.InvariantInfo),
  92. },
  93. //enums are special
  94. [typeof(Enum)] = new TypeConverter
  95. {
  96. ConvertToString = (obj, type) => obj.ToString(),
  97. ConvertToObject = (str, type) => Enum.Parse(type, str, true),
  98. },
  99. //unity types
  100. [typeof(Color)] = new TypeConverter
  101. {
  102. ConvertToString = (obj, type) => ColorUtility.ToHtmlStringRGBA((Color)obj),
  103. ConvertToObject = (str, type) =>
  104. {
  105. if (string.IsNullOrEmpty(str)) return Color.clear;
  106. Color c;
  107. if (!ColorUtility.TryParseHtmlString("#" + str.Trim('#', ' '), out c))
  108. throw new FormatException("Invalid color string, expected hex #RRGGBBAA");
  109. return c;
  110. },
  111. },
  112. };
  113. /// <summary>
  114. /// Convert object of a given type to a string using available converters.
  115. /// </summary>
  116. public static string ConvertToString(object value, Type valueType)
  117. {
  118. var conv = GetConverter(valueType);
  119. if (conv == null)
  120. throw new InvalidOperationException($"Cannot convert from type {valueType}");
  121. return conv.ConvertToString(value, valueType);
  122. }
  123. /// <summary>
  124. /// Convert string to an object of a given type using available converters.
  125. /// </summary>
  126. public static T ConvertToValue<T>(string value)
  127. {
  128. return (T)ConvertToValue(value, typeof(T));
  129. }
  130. /// <summary>
  131. /// Convert string to an object of a given type using available converters.
  132. /// </summary>
  133. public static object ConvertToValue(string value, Type valueType)
  134. {
  135. var conv = GetConverter(valueType);
  136. if (conv == null)
  137. throw new InvalidOperationException($"Cannot convert to type {valueType.Name}");
  138. return conv.ConvertToObject(value, valueType);
  139. }
  140. /// <summary>
  141. /// Get a converter for a given type if there is any.
  142. /// </summary>
  143. public static TypeConverter GetConverter(Type valueType)
  144. {
  145. if (valueType == null) throw new ArgumentNullException(nameof(valueType));
  146. if (valueType.IsEnum)
  147. return TypeConverters[typeof(Enum)];
  148. TypeConverters.TryGetValue(valueType, out var result);
  149. return result;
  150. }
  151. /// <summary>
  152. /// Add a new type converter for a given type.
  153. /// </summary>
  154. public static void AddConverter(Type type, TypeConverter converter)
  155. {
  156. if (type == null) throw new ArgumentNullException(nameof(type));
  157. if (converter == null) throw new ArgumentNullException(nameof(converter));
  158. if (CanConvert(type)) throw new ArgumentException("The specified type already has a converter assigned to it", nameof(type));
  159. TypeConverters.Add(type, converter);
  160. }
  161. /// <summary>
  162. /// Check if a given type can be converted to and from string.
  163. /// </summary>
  164. public static bool CanConvert(Type type)
  165. {
  166. return GetConverter(type) != null;
  167. }
  168. /// <summary>
  169. /// Give a list of types with registered converters.
  170. /// </summary>
  171. public static IEnumerable<Type> GetSupportedTypes()
  172. {
  173. return TypeConverters.Keys;
  174. }
  175. }
  176. }