ProjectArcade
498 строк · 16.9 Кб
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using System.IO;
6using System.Dynamic;
7
8namespace EmulatorLauncher.Common.FileFormats
9{
10public class BmlFile : BmlContainer
11{
12public override string ToString()
13{
14var sb = new StringBuilder();
15SerializeTo(sb);
16var final = sb.ToString();
17return final;
18}
19
20public void Save()
21{
22if (!string.IsNullOrEmpty(_path))
23Save(_path);
24}
25
26public void Save(string bmlFile)
27{
28File.WriteAllText(bmlFile, ToString());
29}
30
31private string _path;
32
33public static BmlFile Parse(string bml)
34{
35var root = new BmlFile() { Name = "root", Indent = -1 };
36if (string.IsNullOrEmpty(bml))
37return root;
38
39BmlContainer current = root;
40
41Stack<BmlContainer> stack = new Stack<BmlContainer>();
42stack.Push(root);
43
44var lines = bml.Replace("\r\n", "\n").Replace("\r", "").Split(new char[] { '\n' }, StringSplitOptions.None);
45for (int i = 0; i < lines.Length; i++)
46{
47string line = lines[i];
48
49if (string.IsNullOrEmpty(line))
50continue;
51
52int indent = GetIndent(line);
53string tmp = line.Trim();
54
55if (!string.IsNullOrEmpty(tmp))
56{
57while (stack.Count > 0 && current.Indent >= indent)
58current = stack.Pop();
59
60if (!stack.Contains(current))
61stack.Push(current);
62}
63
64int idx = tmp.IndexOf(":");
65if (idx >= 0 || idx == -1)
66{
67if (idx >= 0)
68{
69string name = tmp.Substring(0, idx).Trim();
70string value = tmp.Substring(idx + 1).Trim();
71var item = new BmlElement() { Name = name, Value = value };
72current.Elements.Add(item);
73}
74
75if (idx == -1)
76{
77string name = tmp.Trim();
78var folder = new BmlContainer() { Name = name, Indent = indent };
79current.Elements.Add(folder);
80stack.Push(folder);
81current = folder;
82}
83/*else
84{
85if (value == "|" || value == ">")
86{
87StringBuilder sbValue = new StringBuilder();
88
89i++;
90while (i < lines.Length)
91{
92var childLine = lines[i].Replace("\r", "");
93
94if (childLine.Trim().Length == 0)
95{
96if (value == "|")
97sbValue.AppendLine();
98else
99sbValue.Append(" ");
100
101i++;
102continue;
103}
104
105int childIndent = GetIndent(childLine);
106if (childIndent <= indent)
107break;
108
109if (sbValue.Length > 0)
110{
111if (value == "|")
112sbValue.AppendLine();
113else
114sbValue.Append(" ");
115}
116
117sbValue.Append(childLine.Substring((indent + 1) * 2));
118i++;
119}
120
121if (sbValue.Length > 0)
122sbValue.AppendLine();
123
124i--;
125value = sbValue.ToString();
126}*/
127}
128else if (!string.IsNullOrEmpty(tmp))
129{
130var item = new BmlElement() { Name = "", Value = tmp };
131current.Elements.Add(item);
132}
133}
134
135return root;
136}
137
138private static int GetIndent(string line)
139{
140int indent = 0;
141foreach (var chr in line)
142if (chr == 32)
143indent++;
144else
145break;
146
147indent /= 2;
148return indent;
149}
150
151public static BmlFile Load(string bmlFile)
152{
153var root = new BmlFile() { Name = "root", Indent = -1 };
154root._path = bmlFile;
155if (!File.Exists(bmlFile))
156return root;
157
158string bml = File.ReadAllText(bmlFile);
159
160BmlFile file = Parse(bml);
161file._path = bmlFile;
162return file;
163}
164}
165
166public interface IBmlElement
167{
168string Name { get; set; }
169}
170
171public class BmlElement : IBmlElement
172{
173public string Name { get; set; }
174public string Value { get; set; }
175
176public override string ToString()
177{
178return Name + ": " + Value.ToString();
179}
180}
181
182public class BmlContainer : DynamicObject, IBmlElement, IEnumerable<IBmlElement>
183{
184#region DynamicObject
185public override IEnumerable<string> GetDynamicMemberNames()
186{
187return Elements.Where(d => !string.IsNullOrEmpty(d.Name)).Select(d => d.Name);
188}
189
190public override bool TryGetMember(GetMemberBinder binder, out object result)
191{
192var element = Elements.FirstOrDefault(e => binder.Name.Equals(e.Name, StringComparison.InvariantCultureIgnoreCase));
193if (element == null)
194element = Elements.FirstOrDefault(e => binder.Name.Replace("_", " ").Equals(e.Name, StringComparison.InvariantCultureIgnoreCase));
195
196if (element != null)
197{
198BmlElement elt = element as BmlElement;
199if (elt != null)
200result = elt.Value;
201else
202result = element;
203
204return true;
205}
206
207result = null;
208return true;
209}
210
211class BmlSetMemberBinder : SetMemberBinder
212{
213public BmlSetMemberBinder(string name, bool ignoreCase) : base(name, ignoreCase) { }
214public override DynamicMetaObject FallbackSetMember(DynamicMetaObject target, DynamicMetaObject value, DynamicMetaObject errorSuggestion) { return null; }
215}
216
217public override bool TrySetMember(SetMemberBinder binder, object value)
218{
219if (value == null)
220{
221this.Remove(binder.Name.Replace("_", " "));
222this.Remove(binder.Name);
223}
224else if (value is string || value.GetType().IsValueType)
225{
226string newValue = value.ToString();
227
228if (value is bool)
229newValue = ((bool)value).ToString().ToLowerInvariant();
230else if (value is decimal || value is float || value is double)
231newValue = ((double)value).ToString(System.Globalization.CultureInfo.InvariantCulture);
232
233var element = Elements.FirstOrDefault(e => binder.Name.Equals(e.Name, StringComparison.InvariantCultureIgnoreCase));
234if (element == null)
235{
236element = Elements.FirstOrDefault(e => binder.Name.Replace("_", " ").Equals(e.Name, StringComparison.InvariantCultureIgnoreCase));
237if (element != null)
238{
239this[binder.Name.Replace("_", " ")] = newValue;
240}
241}
242
243this[binder.Name] = newValue;
244}
245else
246{
247BmlContainer container = GetOrCreateContainer(binder.Name);
248
249foreach (var item in value.GetType()
250.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
251.Select(pi => new { Name = pi.Name, Value = pi.GetValue(value, null) }))
252{
253if (item.Value != null)
254container.TrySetMember(new BmlSetMemberBinder(item.Name, binder.IgnoreCase), item.Value);
255}
256
257}
258
259return true;
260}
261#endregion
262
263public BmlContainer()
264{
265Elements = new List<IBmlElement>();
266}
267
268public string Name { get; set; }
269
270private void AddElement(IBmlElement element)
271{
272BmlElement last = (Elements.Count > 0 ? Elements[Elements.Count - 1] : null) as BmlElement;
273if (last != null && string.IsNullOrEmpty(last.Name) && last.Value == "...")
274{
275Elements.Insert(Elements.Count - 1, element);
276return;
277}
278
279Elements.Add(element);
280}
281
282public BmlContainer GetContainer(string key)
283{
284return Elements.OfType<BmlContainer>().FirstOrDefault(e => key.Equals(e.Name, StringComparison.InvariantCultureIgnoreCase));
285}
286
287public BmlContainer GetOrCreateContainer(string key)
288{
289var element = GetContainer(key);
290if (element == null)
291{
292element = new BmlContainer() { Name = key };
293
294// Convert Element to Container
295var item = Elements.FirstOrDefault(e => !(e is BmlContainer) && key.Equals(e.Name, StringComparison.InvariantCultureIgnoreCase));
296if (item != null)
297{
298int pos = Elements.IndexOf(item);
299Elements.Remove(item);
300Elements.Insert(pos, element);
301}
302else
303AddElement(element);
304}
305
306return element;
307}
308
309public string this[string key]
310{
311get
312{
313var element = Elements.OfType<BmlElement>().FirstOrDefault(e => key.Equals(e.Name, StringComparison.InvariantCultureIgnoreCase));
314if (element != null)
315return element.Value;
316
317return null;
318}
319set
320{
321var element = Elements.OfType<BmlElement>().FirstOrDefault(e => key.Equals(e.Name, StringComparison.InvariantCultureIgnoreCase));
322if (element == null)
323{
324element = new BmlElement() { Name = key };
325
326// Convert Container to Element
327var container = Elements.FirstOrDefault(e => e is BmlContainer && key.Equals(e.Name, StringComparison.InvariantCultureIgnoreCase));
328if (container != null)
329{
330int pos = Elements.IndexOf(container);
331Elements.Remove(container);
332Elements.Insert(pos, element);
333}
334else
335AddElement(element);
336}
337
338element.Value = value;
339}
340}
341
342public void Remove(string key)
343{
344var element = Elements.FirstOrDefault(e => key.Equals(e.Name, StringComparison.InvariantCultureIgnoreCase));
345if (element != null)
346Elements.Remove(element);
347}
348
349public List<IBmlElement> Elements { get; private set; }
350
351public override string ToString()
352{
353return "[Folder] " + (Name ?? "");
354}
355
356public IEnumerator<IBmlElement> GetEnumerator()
357{
358return Elements.GetEnumerator();
359}
360
361System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
362{
363return Elements.GetEnumerator();
364}
365
366protected void SerializeTo(StringBuilder sb, int indent = 0)
367{
368foreach (var item in Elements)
369{
370BmlContainer container = item as BmlContainer;
371if (container != null)
372{
373if (container.Elements.Count > 0)
374{
375sb.Append(new string(' ', indent * 2));
376sb.Append(item.Name);
377sb.AppendLine();
378
379container.SerializeTo(sb, indent + 1);
380}
381
382continue;
383}
384
385BmlElement element = item as BmlElement;
386if (element == null)
387continue;
388
389if (element.Value == null)
390continue;
391
392sb.Append(new string(' ', indent * 2));
393
394if (!string.IsNullOrEmpty(element.Name))
395{
396sb.Append(element.Name);
397sb.Append(": ");
398
399if (element.Value.Contains("\r\n"))
400{
401sb.AppendLine("|");
402
403var offset = new string(' ', (indent+1) * 2);
404var lines = element.Value.Split(new string[] { "\r\n" }, StringSplitOptions.None);
405for (int i = 0; i < lines.Length - 1; i++)
406{
407sb.Append(offset);
408sb.AppendLine(lines[i]);
409}
410}
411else
412sb.AppendLine(element.Value);
413}
414else
415sb.AppendLine(element.Value);
416}
417}
418
419internal int Indent;
420}
421
422class SimpleBml<T> : IEnumerable<T> where T : new()
423{
424private List<T> _values;
425
426private static List<T> FillElements(object obj, BmlContainer bmlElements)
427{
428List<T> ret = null;
429
430foreach (var bmlEntry in bmlElements.Elements)
431{
432BmlContainer container = bmlEntry as BmlContainer;
433if (container != null)
434{
435if (typeof(T).Equals(obj))
436{
437T current = Activator.CreateInstance<T>();
438
439var bmlNameProperty = typeof(T).GetProperties().FirstOrDefault(prop => Attribute.IsDefined(prop, typeof(BmlNameAttribute)));
440if (bmlNameProperty != null)
441bmlNameProperty.SetValue(current, container.Name, null);
442
443FillElements(current, container);
444
445if (ret == null)
446ret = new List<T>();
447
448ret.Add(current);
449}
450else
451{
452var propertyType = (obj is Type) ? (Type)obj : obj.GetType();
453var objectProperty = propertyType.GetProperty(bmlEntry.Name);
454if (objectProperty != null && !objectProperty.PropertyType.IsValueType && objectProperty.PropertyType != typeof(string))
455{
456object child = Activator.CreateInstance(objectProperty.PropertyType);
457
458var bmlNameProperty = objectProperty.PropertyType.GetProperties().FirstOrDefault(prop => Attribute.IsDefined(prop, typeof(BmlNameAttribute)));
459if (bmlNameProperty != null)
460bmlNameProperty.SetValue(obj, container.Name, null);
461
462FillElements(child, container);
463objectProperty.SetValue(obj, child, null);
464}
465}
466continue;
467}
468
469var type = (obj is Type) ? (Type)obj : obj.GetType();
470
471var property = type.GetProperty(bmlEntry.Name);
472if (property != null && bmlEntry is BmlElement)
473property.SetValue(obj, ((BmlElement)bmlEntry).Value, null);
474}
475
476return ret;
477}
478
479public static SimpleBml<T> Parse(string bml)
480{
481var ret = new SimpleBml<T>();
482ret._values = FillElements(typeof(T), BmlFile.Parse(bml));
483return ret;
484}
485
486public IEnumerator<T> GetEnumerator()
487{
488return _values.GetEnumerator();
489}
490
491System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
492{
493return _values.GetEnumerator();
494}
495}
496
497class BmlNameAttribute : Attribute { }
498}
499