Client LuaCsForBarotrauma
LuaCsUtility.cs
3 using MoonSharp.Interpreter;
4 using System;
5 using System.Collections;
6 using System.Collections.Generic;
7 using System.Collections.Immutable;
8 using System.Diagnostics;
9 using System.IO;
10 using System.Linq;
11 using System.Net;
12 using System.Reflection;
13 using System.Xml.Linq;
14 
15 namespace Barotrauma
16 {
17  partial class LuaCsFile
18  {
19  public static bool CanReadFromPath(string path)
20  {
21  string getFullPath(string p) => System.IO.Path.GetFullPath(p).CleanUpPath();
22 
23  path = getFullPath(path);
24 
25  bool pathStartsWith(string prefix) => path.StartsWith(prefix, StringComparison.OrdinalIgnoreCase);
26 
27  string localModsDir = getFullPath(ContentPackage.LocalModsDir);
28  string workshopModsDir = getFullPath(ContentPackage.WorkshopModsDir);
29 #if CLIENT
30  string tempDownloadDir = getFullPath(ModReceiver.DownloadFolder);
31 #endif
32  if (pathStartsWith(getFullPath(string.IsNullOrEmpty(GameSettings.CurrentConfig.SavePath) ? SaveUtil.DefaultSaveFolder : GameSettings.CurrentConfig.SavePath)))
33  return true;
34 
35  if (pathStartsWith(localModsDir))
36  return true;
37 
38  if (pathStartsWith(workshopModsDir))
39  return true;
40 
41 #if CLIENT
42  if (pathStartsWith(tempDownloadDir))
43  return true;
44 #endif
45 
46  if (pathStartsWith(getFullPath(".")))
47  return true;
48 
49  return false;
50  }
51 
52  public static bool CanWriteToPath(string path)
53  {
54  string getFullPath(string p) => System.IO.Path.GetFullPath(p).CleanUpPath();
55 
56  path = getFullPath(path);
57 
58  bool pathStartsWith(string prefix) => path.StartsWith(prefix, StringComparison.OrdinalIgnoreCase);
59 
60  foreach (var package in ContentPackageManager.AllPackages)
61  {
62  if (package.UgcId.ValueEquals(LuaCsSetup.LuaForBarotraumaId) && pathStartsWith(getFullPath(package.Path)))
63  {
64  return false;
65  }
66  }
67 
68  if (pathStartsWith(getFullPath(string.IsNullOrEmpty(GameSettings.CurrentConfig.SavePath) ? SaveUtil.DefaultSaveFolder : GameSettings.CurrentConfig.SavePath)))
69  return true;
70 
71  if (pathStartsWith(getFullPath(ContentPackage.LocalModsDir)))
72  return true;
73 
74  if (pathStartsWith(getFullPath(ContentPackage.WorkshopModsDir)))
75  return true;
76 #if CLIENT
77  if (pathStartsWith(getFullPath(ModReceiver.DownloadFolder)))
78  return true;
79 #endif
80 
81  return false;
82  }
83 
84  public static bool IsPathAllowedException(string path, bool write = true, LuaCsMessageOrigin origin = LuaCsMessageOrigin.Unknown)
85  {
86  if (write)
87  {
88  if (CanWriteToPath(path))
89  {
90  return true;
91  }
92  else
93  {
94  throw new Exception("File access to \"" + path + "\" not allowed.");
95  }
96  }
97  else
98  {
99  if (CanReadFromPath(path))
100  {
101  return true;
102  }
103  else
104  {
105  throw new Exception("File access to \"" + path + "\" not allowed.");
106  }
107  }
108  }
109 
110  public static bool IsPathAllowedLuaException(string path, bool write = true) =>
111  IsPathAllowedException(path, write, LuaCsMessageOrigin.LuaMod);
112  public static bool IsPathAllowedCsException(string path, bool write = true) =>
113  IsPathAllowedException(path, write, LuaCsMessageOrigin.CSharpMod);
114 
115  public static string Read(string path)
116  {
117  if (!IsPathAllowedException(path, false))
118  return "";
119 
120  return File.ReadAllText(path);
121  }
122 
123  public static void Write(string path, string text)
124  {
125  if (!IsPathAllowedException(path))
126  return;
127 
128  File.WriteAllText(path, text);
129  }
130 
131  public static void Delete(string path)
132  {
133  if (!IsPathAllowedException(path))
134  return;
135 
136  File.Delete(path);
137  }
138 
139  public static void DeleteDirectory(string path)
140  {
141  if (!IsPathAllowedException(path))
142  return;
143 
144  Directory.Delete(path, true);
145  }
146 
147  public static void Move(string path, string destination)
148  {
149  if (!IsPathAllowedException(path))
150  return;
151 
152  File.Move(path, destination, true);
153  }
154 
155  public static FileStream OpenRead(string path)
156  {
157  if (!IsPathAllowedException(path))
158  return null;
159 
160  return File.Open(path, FileMode.Open, FileAccess.Read);
161  }
162  public static FileStream OpenWrite(string path)
163  {
164  if (!IsPathAllowedException(path))
165  return null;
166 
167  if (File.Exists(path)) return File.Open(path, FileMode.Truncate, FileAccess.Write);
168  else return File.Open(path, FileMode.Create, FileAccess.Write);
169  }
170 
171  public static bool Exists(string path)
172  {
173  if (!IsPathAllowedException(path, false))
174  return false;
175 
176  return File.Exists(path);
177  }
178 
179  public static bool CreateDirectory(string path)
180  {
181  if (!IsPathAllowedException(path))
182  return false;
183 
184  Directory.CreateDirectory(path);
185 
186  return true;
187  }
188 
189  public static bool DirectoryExists(string path)
190  {
191  if (!IsPathAllowedException(path, false))
192  return false;
193 
194  return Directory.Exists(path);
195  }
196 
197  public static string[] GetFiles(string path)
198  {
199  if (!IsPathAllowedException(path, false))
200  return null;
201 
202  return Directory.GetFiles(path);
203  }
204 
205  public static string[] GetDirectories(string path)
206  {
207  if (!IsPathAllowedException(path, false))
208  return new string[] { };
209 
210  return Directory.GetDirectories(path);
211  }
212 
213  public static string[] DirSearch(string sDir)
214  {
215  if (!IsPathAllowedException(sDir, false))
216  return new string[] { };
217 
218  List<string> files = new List<string>();
219 
220  try
221  {
222  foreach (string f in Directory.GetFiles(sDir))
223  {
224  files.Add(f);
225  }
226 
227  foreach (string d in Directory.GetDirectories(sDir))
228  {
229  foreach (string f in Directory.GetFiles(d))
230  {
231  files.Add(f);
232  }
233  DirSearch(d);
234  }
235  }
236  catch (System.Exception excpt)
237  {
238  Console.WriteLine(excpt.Message);
239  }
240 
241  return files.ToArray();
242  }
243  }
244 
245 
247  {
248  private enum ValueType
249  {
250  None,
251  Text,
252  Integer,
253  Decimal,
254  Boolean,
255  Collection,
256  Object,
257  Enum
258  }
259 
260  private static Type[] LoadDocTypes(XElement typesElem)
261  {
262  var result = new List<Type>();
263  var loadedTypes = LuaCsSetup.AssemblyManager
265  .ToImmutableHashSet();
266 
267  foreach (var elem in typesElem.Elements())
268  {
269  var typesFound = loadedTypes.Where(t => t.FullName?.EndsWith(elem.Value) ?? false).ToImmutableList();
270  if (!typesFound.Any())
271  {
272  ModUtils.Logging.PrintError(
273  $"{nameof(LuaCsConfig)}::{nameof(LoadDocTypes)}() | Unable to find a matching type for {elem.Value}");
274  continue;
275  }
276  result.AddRange(typesFound);
277  }
278 
279  return result.ToArray();
280  }
281 
282  private static IEnumerable<XElement> SaveDocTypes(IEnumerable<Type> types)
283  {
284  return types.Select(t => new XElement("Type", t.ToString()));
285  }
286 
287  private static Type GetTypeAttr(Type[] types, XElement elem)
288  {
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");
291  return types[idx];
292  }
293  private static ValueType GetValueType(XElement elem)
294  {
295  Enum.TryParse(typeof(ValueType), elem.Attribute("Value")?.Value, out object result);
296  if (result != null) return (ValueType)result;
297  else return ValueType.None;
298  }
299  private static object ParseValue(Type[] types, XElement elem)
300  {
301  var type = GetValueType(elem);
302 
303  if (elem.IsEmpty) return null;
304  if (type == ValueType.Enum)
305  {
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;
309  else return null;
310  }
311  if (type == ValueType.Collection)
312  {
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;
317 
318  object result = null;
319 
320  if (result == null) {
321  var ctor = tType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(c =>
322  {
323  var param = c.GetParameters();
324  return param.Count() == 1 && param.Any(p => p.ParameterType.IsGenericType && p.ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>));
325  });
326  if (ctor != null)
327  {
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 });
331  }
332  }
333 
334  if (result == null)
335  {
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 =>
338  {
339  if (m.Name != "Add") return false;
340  var param = m.GetParameters();
341  return param.Count() == 1 && param[0].ParameterType == gArg;
342  });
343  if (ctor != null && addMethod != null)
344  {
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 });
348  }
349  }
350 
351  if (result == null)
352  {
353  var ctor = tType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault();
354  var setMethod = tType.GetMethods(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(m =>
355  {
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;
359  });
360  if (ctor != null || setMethod != null)
361  {
362  var elements = elem.Elements().Select(x => ParseValue(types, x));
363  result = ctor.Invoke(new object[] { elements.Count() });
364  int i = 0;
365  foreach (var el in elements)
366  {
367  setMethod.Invoke(result, new object[] { i, el });
368  i++;
369  }
370  }
371  }
372 
373  return result;
374  }
375  else if (type == ValueType.Text) return elem.Value;
376  else if (type == ValueType.Integer)
377  {
378  int.TryParse(elem.Value, out var num);
379  return num;
380  }
381  else if (type == ValueType.Decimal)
382  {
383  float.TryParse(elem.Value, out var num);
384  return num;
385  }
386  else if (type == ValueType.Boolean)
387  {
388  bool.TryParse(elem.Value, out var boolean);
389  return boolean;
390  }
391  else if (type == ValueType.Object)
392  {
393  var tType = GetTypeAttr(types, elem);
394  if (tType == null) return null;
395 
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));
400 
401  object result = null;
402  var ctor = tType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(c => c.GetParameters().Count() == 0);
403  if (ctor == null)
404  {
405  if (!tType.IsValueType) return null;
406  result = Activator.CreateInstance(tType);
407  }
408  else result = ctor.Invoke(null);
409 
410  foreach(var el in elem.Elements())
411  {
412  var value = ParseValue(types, el);
413 
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);
418  }
419  return result;
420  }
421  else return elem.Value;
422 
423  }
424 
425  private static void AddTypeAttr(List<Type> types, Type type, XElement elem)
426  {
427  if (!types.Contains(type)) types.Add(type);
428  elem.SetAttributeValue("Type", types.IndexOf(type));
429  }
430 
431  private static XElement ParseObject(List<Type> types, string name, object value)
432  {
433  XElement result = new XElement(name);
434 
435  if (value != null)
436  {
437  var tType = value.GetType();
438 
439  if (tType.IsEnum)
440  {
441  result.SetAttributeValue("Value", ValueType.Enum);
442  AddTypeAttr(types, tType, result);
443 
444  result.Value = Enum.GetName(tType, value) ?? "";
445  }
446  else if (value is string str)
447  {
448  result.SetAttributeValue("Value", ValueType.Text);
449  result.Value = str;
450  }
451  else if (value is int integer)
452  {
453  result.SetAttributeValue("Value", ValueType.Integer);
454  result.Value = integer.ToString();
455  }
456  else if (value is float || value is double)
457  {
458  result.SetAttributeValue("Value", ValueType.Decimal);
459  result.Value = value.ToString();
460  }
461  else if (value is bool boolean)
462  {
463  result.SetAttributeValue("Value", ValueType.Boolean);
464  result.Value = boolean.ToString();
465  }
466  else if (tType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
467  {
468  result.SetAttributeValue("Value", ValueType.Collection);
469  AddTypeAttr(types, tType, result);
470 
471  var enumerator = (IEnumerator)tType.GetMethod("GetEnumerator").Invoke(value, null);
472  while (enumerator.MoveNext())
473  {
474  var elVal = ParseObject(types, "Item", enumerator.Current);
475  result.Add(elVal);
476  }
477  }
478  else if (tType.IsClass || tType.IsValueType)
479  {
480  result.SetAttributeValue("Value", ValueType.Object);
481  AddTypeAttr(types, tType, result);
482 
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));
487 
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)));
490  }
491  else
492  {
493  result.SetAttributeValue("Value", ValueType.None);
494  result.Value = value.ToString();
495  }
496  }
497 
498  return result;
499  }
500 
501 
502  public static T Load<T>(FileStream file)
503  {
504  var doc = XDocument.Load(file);
505 
506  var rootElems = doc.Root.Elements().ToArray();
507  var types = rootElems[0];
508  var elem = rootElems[1];
509 
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}'");
513  }
514 
515  public static void Save(FileStream file, object obj)
516  {
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);
520 
521  var doc = new XDocument(root);
522  doc.Save(file);
523  }
524 
525  public static T Load<T>(string path)
526  {
527  using (var file = LuaCsFile.OpenRead(path)) return Load<T>(file);
528  }
529 
530  public static void Save(string path, object obj)
531  {
532  using (var file = LuaCsFile.OpenWrite(path)) Save(file, obj);
533  }
534  }
535 }
IEnumerable< Type > GetAllTypesInLoadedAssemblies()
Allows iteration over all types (including interfaces) in all loaded assemblies managed by the AsmMgr...
static readonly string WorkshopModsDir
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)
Definition: LuaCsUtility.cs:52
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)
Definition: LuaCsUtility.cs:84
static bool CanReadFromPath(string path)
Definition: LuaCsUtility.cs:19
static string[] GetDirectories(string path)
static bool Exists(string path)
static bool IsPathAllowedCsException(string path, bool write=true)
static void DeleteDirectory(string path)