3 using MoonSharp.Interpreter;
5 using System.Collections;
6 using System.Collections.Generic;
7 using System.Collections.Immutable;
8 using System.Diagnostics;
12 using System.Reflection;
13 using System.Xml.Linq;
21 string getFullPath(
string p) => System.IO.Path.GetFullPath(p).CleanUpPath();
23 path = getFullPath(path);
25 bool pathStartsWith(
string prefix) => path.StartsWith(prefix, StringComparison.OrdinalIgnoreCase);
30 string tempDownloadDir = getFullPath(ModReceiver.DownloadFolder);
32 if (pathStartsWith(getFullPath(
string.IsNullOrEmpty(GameSettings.CurrentConfig.SavePath) ? SaveUtil.DefaultSaveFolder : GameSettings.CurrentConfig.SavePath)))
35 if (pathStartsWith(localModsDir))
38 if (pathStartsWith(workshopModsDir))
42 if (pathStartsWith(tempDownloadDir))
46 if (pathStartsWith(getFullPath(
".")))
54 string getFullPath(
string p) => System.IO.Path.GetFullPath(p).CleanUpPath();
56 path = getFullPath(path);
58 bool pathStartsWith(
string prefix) => path.StartsWith(prefix, StringComparison.OrdinalIgnoreCase);
60 foreach (var package
in ContentPackageManager.AllPackages)
62 if (package.UgcId.ValueEquals(
LuaCsSetup.LuaForBarotraumaId) && pathStartsWith(getFullPath(package.Path)))
68 if (pathStartsWith(getFullPath(
string.IsNullOrEmpty(GameSettings.CurrentConfig.SavePath) ? SaveUtil.DefaultSaveFolder : GameSettings.CurrentConfig.SavePath)))
77 if (pathStartsWith(getFullPath(ModReceiver.DownloadFolder)))
84 public static bool IsPathAllowedException(
string path,
bool write =
true, LuaCsMessageOrigin origin = LuaCsMessageOrigin.Unknown)
94 throw new Exception(
"File access to \"" + path +
"\" not allowed.");
105 throw new Exception(
"File access to \"" + path +
"\" not allowed.");
115 public static string Read(
string path)
120 return File.ReadAllText(path);
123 public static void Write(
string path,
string text)
128 File.WriteAllText(path, text);
144 Directory.Delete(path,
true);
147 public static void Move(
string path,
string destination)
152 File.Move(path, destination,
true);
160 return File.Open(path, FileMode.Open, FileAccess.Read);
167 if (File.Exists(path))
return File.Open(path, FileMode.Truncate, FileAccess.Write);
168 else return File.Open(path, FileMode.Create, FileAccess.Write);
176 return File.Exists(path);
184 Directory.CreateDirectory(path);
194 return Directory.Exists(path);
202 return Directory.GetFiles(path);
208 return new string[] { };
210 return Directory.GetDirectories(path);
216 return new string[] { };
218 List<string> files =
new List<string>();
222 foreach (
string f
in Directory.GetFiles(sDir))
227 foreach (
string d
in Directory.GetDirectories(sDir))
229 foreach (
string f
in Directory.GetFiles(d))
236 catch (System.Exception excpt)
238 Console.WriteLine(excpt.Message);
241 return files.ToArray();
248 private enum ValueType
260 private static Type[] LoadDocTypes(XElement typesElem)
262 var result =
new List<Type>();
265 .ToImmutableHashSet();
267 foreach (var elem
in typesElem.Elements())
269 var typesFound = loadedTypes.Where(t => t.FullName?.EndsWith(elem.Value) ??
false).ToImmutableList();
270 if (!typesFound.Any())
272 ModUtils.Logging.PrintError(
273 $
"{nameof(LuaCsConfig)}::{nameof(LoadDocTypes)}() | Unable to find a matching type for {elem.Value}");
276 result.AddRange(typesFound);
279 return result.ToArray();
282 private static IEnumerable<XElement> SaveDocTypes(IEnumerable<Type> types)
284 return types.Select(t =>
new XElement(
"Type", t.ToString()));
287 private static Type GetTypeAttr(Type[] types, XElement elem)
289 var idx = elem.GetAttributeInt(
"Type", -1);
290 if (idx < 0 || idx >= types.Length)
throw new Exception($
"Type index '{idx}' is outside of saved types bounds");
293 private static ValueType GetValueType(XElement elem)
295 Enum.TryParse(typeof(ValueType), elem.Attribute(
"Value")?.Value, out
object result);
296 if (result !=
null)
return (ValueType)result;
297 else return ValueType.None;
299 private static object ParseValue(Type[] types, XElement elem)
301 var type = GetValueType(elem);
303 if (elem.IsEmpty)
return null;
304 if (type == ValueType.Enum)
306 var tType = GetTypeAttr(types, elem);
307 if (tType ==
null || !tType.IsSubclassOf(typeof(Enum)))
return null;
308 if (Enum.TryParse(tType, elem.Value, out
object result))
return result;
311 if (type == ValueType.Collection)
313 var tType = GetTypeAttr(types, elem);
314 var tInt = tType.GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
315 var gArg = tInt.GetGenericArguments()[0];
316 if (tType ==
null || !tType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
return null;
318 object result =
null;
320 if (result ==
null) {
321 var ctor = tType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(c =>
323 var param = c.GetParameters();
324 return param.Count() == 1 && param.Any(p => p.ParameterType.IsGenericType && p.ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>));
328 var elements = elem.Elements().Select(x => ParseValue(types, x));
329 var castElems = typeof(Enumerable).GetMethod(
"Cast").MakeGenericMethod(gArg).Invoke(elements,
new object[] { elements });
330 result = ctor.Invoke(
new object[] { castElems });
336 var ctor = tType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(c => c.GetParameters().Count() == 0);
337 var addMethod = tType.GetMethods(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(m =>
339 if (m.Name !=
"Add")
return false;
340 var param = m.GetParameters();
341 return param.Count() == 1 && param[0].ParameterType == gArg;
343 if (ctor !=
null && addMethod !=
null)
345 var elements = elem.Elements().Select(x => ParseValue(types, x));
346 result = ctor.Invoke(
null);
347 foreach (var el
in elements) addMethod.Invoke(result,
new object[] { el });
353 var ctor = tType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault();
354 var setMethod = tType.GetMethods(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(m =>
356 if (m.Name !=
"Set")
return false;
357 var param = m.GetParameters();
358 return param.Count() == 2 && param[0].ParameterType == typeof(
int) && param[1].ParameterType == gArg;
360 if (ctor !=
null || setMethod !=
null)
362 var elements = elem.Elements().Select(x => ParseValue(types, x));
363 result = ctor.Invoke(
new object[] { elements.Count() });
365 foreach (var el
in elements)
367 setMethod.Invoke(result,
new object[] { i, el });
375 else if (type == ValueType.Text)
return elem.Value;
376 else if (type == ValueType.Integer)
378 int.TryParse(elem.Value, out var num);
381 else if (type == ValueType.Decimal)
383 float.TryParse(elem.Value, out var num);
386 else if (type == ValueType.Boolean)
388 bool.TryParse(elem.Value, out var
boolean);
391 else if (type == ValueType.Object)
393 var tType = GetTypeAttr(types, elem);
394 if (tType ==
null)
return null;
396 IEnumerable<FieldInfo> fields = tType.GetFields(BindingFlags.Instance | BindingFlags.Public)
397 .Concat(tType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic));
398 IEnumerable<PropertyInfo> properties = tType.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.GetSetMethod() !=
null)
399 .Concat(tType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic).Where(p => p.GetSetMethod() !=
null));
401 object result =
null;
402 var ctor = tType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(c => c.GetParameters().Count() == 0);
405 if (!tType.IsValueType)
return null;
406 result = Activator.CreateInstance(tType);
408 else result = ctor.Invoke(
null);
410 foreach(var el
in elem.Elements())
412 var value = ParseValue(types, el);
414 var field = fields.FirstOrDefault(f => f.Name == el.Name.LocalName);
415 if (field !=
null) field.SetValue(result, value);
416 var
property = properties.FirstOrDefault(p => p.Name == el.Name.LocalName);
417 if (property !=
null)
property.SetValue(result, value);
421 else return elem.Value;
425 private static void AddTypeAttr(List<Type> types, Type type, XElement elem)
427 if (!types.Contains(type)) types.Add(type);
428 elem.SetAttributeValue(
"Type", types.IndexOf(type));
431 private static XElement ParseObject(List<Type> types,
string name,
object value)
433 XElement result =
new XElement(name);
437 var tType = value.GetType();
441 result.SetAttributeValue(
"Value", ValueType.Enum);
442 AddTypeAttr(types, tType, result);
444 result.Value = Enum.GetName(tType, value) ??
"";
446 else if (value is
string str)
448 result.SetAttributeValue(
"Value", ValueType.Text);
451 else if (value is
int integer)
453 result.SetAttributeValue(
"Value", ValueType.Integer);
454 result.Value = integer.ToString();
456 else if (value is
float || value is
double)
458 result.SetAttributeValue(
"Value", ValueType.Decimal);
459 result.Value = value.ToString();
461 else if (value is
bool boolean)
463 result.SetAttributeValue(
"Value", ValueType.Boolean);
464 result.Value =
boolean.ToString();
466 else if (tType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
468 result.SetAttributeValue(
"Value", ValueType.Collection);
469 AddTypeAttr(types, tType, result);
471 var enumerator = (IEnumerator)tType.GetMethod(
"GetEnumerator").Invoke(value,
null);
472 while (enumerator.MoveNext())
474 var elVal = ParseObject(types,
"Item", enumerator.Current);
478 else if (tType.IsClass || tType.IsValueType)
480 result.SetAttributeValue(
"Value", ValueType.Object);
481 AddTypeAttr(types, tType, result);
483 IEnumerable<FieldInfo> fields = tType.GetFields(BindingFlags.Instance | BindingFlags.Public)
484 .Concat(tType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic));
485 IEnumerable<PropertyInfo> properties = tType.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.GetSetMethod() !=
null)
486 .Concat(tType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic).Where(p => p.GetSetMethod() !=
null));
488 foreach(var field
in fields) result.Add(ParseObject(types, field.Name, field.GetValue(value)));
489 foreach (var property
in properties) result.Add(ParseObject(types, property.Name, property.GetValue(value)));
493 result.SetAttributeValue(
"Value", ValueType.None);
494 result.Value = value.ToString();
504 var doc = XDocument.Load(file);
506 var rootElems = doc.Root.Elements().ToArray();
507 var types = rootElems[0];
508 var elem = rootElems[1];
510 var dict = ParseValue(LoadDocTypes(types), elem);
511 if (dict.GetType() == typeof(T))
return (T)dict;
512 else throw new Exception($
"Loaded configuration is not of the type '{typeof(T).Name}'");
515 public static void Save(FileStream file,
object obj)
517 var types =
new List<Type>();
518 var elem = ParseObject(types,
"Root", obj);
519 var root =
new XElement(
"Configuration",
new XElement(
"Types", SaveDocTypes(types)), elem);
521 var doc =
new XDocument(root);
530 public static void Save(
string path,
object obj)
IEnumerable< Type > GetAllTypesInLoadedAssemblies()
Allows iteration over all types (including interfaces) in all loaded assemblies managed by the AsmMgr...
static readonly string WorkshopModsDir
const string LocalModsDir
static void Save(string path, object obj)
static void Save(FileStream file, object obj)
static T Load< T >(FileStream file)
static void Move(string path, string destination)
static bool CreateDirectory(string path)
static string Read(string path)
static bool IsPathAllowedLuaException(string path, bool write=true)
static FileStream OpenRead(string path)
static void Delete(string path)
static void Write(string path, string text)
static bool CanWriteToPath(string path)
static string[] GetFiles(string path)
static FileStream OpenWrite(string path)
static string[] DirSearch(string sDir)
static bool DirectoryExists(string path)
static bool IsPathAllowedException(string path, bool write=true, LuaCsMessageOrigin origin=LuaCsMessageOrigin.Unknown)
static bool CanReadFromPath(string path)
static string[] GetDirectories(string path)
static bool Exists(string path)
static bool IsPathAllowedCsException(string path, bool write=true)
static void DeleteDirectory(string path)
static AssemblyManager AssemblyManager