TomlTypeConverter.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Text.RegularExpressions;
  5. namespace BepInEx.Configuration
  6. {
  7. /// <summary>
  8. /// Serializer/deserializer used by the config system.
  9. /// </summary>
  10. public static class TomlTypeConverter
  11. {
  12. // Don't put anything from UnityEngine here or it will break preloader (loads the assembly before it's patched)
  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. };
  100. /// <summary>
  101. /// Convert object of a given type to a string using available converters.
  102. /// </summary>
  103. public static string ConvertToString(object value, Type valueType)
  104. {
  105. var conv = GetConverter(valueType);
  106. if (conv == null)
  107. throw new InvalidOperationException($"Cannot convert from type {valueType}");
  108. return conv.ConvertToString(value, valueType);
  109. }
  110. /// <summary>
  111. /// Convert string to an object of a given type using available converters.
  112. /// </summary>
  113. public static T ConvertToValue<T>(string value)
  114. {
  115. return (T)ConvertToValue(value, typeof(T));
  116. }
  117. /// <summary>
  118. /// Convert string to an object of a given type using available converters.
  119. /// </summary>
  120. public static object ConvertToValue(string value, Type valueType)
  121. {
  122. var conv = GetConverter(valueType);
  123. if (conv == null)
  124. throw new InvalidOperationException($"Cannot convert to type {valueType.Name}");
  125. return conv.ConvertToObject(value, valueType);
  126. }
  127. /// <summary>
  128. /// Get a converter for a given type if there is any.
  129. /// </summary>
  130. public static TypeConverter GetConverter(Type valueType)
  131. {
  132. if (valueType == null) throw new ArgumentNullException(nameof(valueType));
  133. if (valueType.IsEnum)
  134. return TypeConverters[typeof(Enum)];
  135. TypeConverters.TryGetValue(valueType, out var result);
  136. return result;
  137. }
  138. /// <summary>
  139. /// Add a new type converter for a given type.
  140. /// If a different converter is already added, an ArgumentException is thrown.
  141. /// </summary>
  142. public static void AddConverter(Type type, TypeConverter converter)
  143. {
  144. if (type == null) throw new ArgumentNullException(nameof(type));
  145. if (converter == null) throw new ArgumentNullException(nameof(converter));
  146. if (CanConvert(type)) throw new ArgumentException("The specified type already has a converter assigned to it", nameof(type));
  147. TypeConverters.Add(type, converter);
  148. }
  149. /// <summary>
  150. /// Check if a given type can be converted to and from string.
  151. /// </summary>
  152. public static bool CanConvert(Type type)
  153. {
  154. return GetConverter(type) != null;
  155. }
  156. /// <summary>
  157. /// Give a list of types with registered converters.
  158. /// </summary>
  159. public static IEnumerable<Type> GetSupportedTypes()
  160. {
  161. return TypeConverters.Keys;
  162. }
  163. }
  164. }