/* Copyright (c) 2017 Rick (rick 'at' gibbed 'dot' us) * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would * be appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not * be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. */ using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace SAM.Game { internal class KeyValue { private static readonly KeyValue _Invalid = new KeyValue(); public string Name = ""; public KeyValueType Type = KeyValueType.None; public object Value; public bool Valid; public List Children = null; public KeyValue this[string key] { get { if (this.Children == null) { return _Invalid; } var child = this.Children.SingleOrDefault( c => c.Name.ToLowerInvariant() == key.ToLowerInvariant()); if (child == null) { return _Invalid; } return child; } } public string AsString(string defaultValue) { if (this.Valid == false) { return defaultValue; } if (this.Value == null) { return defaultValue; } return this.Value.ToString(); } public int AsInteger(int defaultValue) { if (this.Valid == false) { return defaultValue; } switch (this.Type) { case KeyValueType.String: case KeyValueType.WideString: { int value; if (int.TryParse((string)this.Value, out value) == false) { return defaultValue; } return value; } case KeyValueType.Int32: { return (int)this.Value; } case KeyValueType.Float32: { return (int)((float)this.Value); } case KeyValueType.UInt64: { return (int)((ulong)this.Value & 0xFFFFFFFF); } } return defaultValue; } public float AsFloat(float defaultValue) { if (this.Valid == false) { return defaultValue; } switch (this.Type) { case KeyValueType.String: case KeyValueType.WideString: { float value; if (float.TryParse((string)this.Value, out value) == false) { return defaultValue; } return value; } case KeyValueType.Int32: { return (int)this.Value; } case KeyValueType.Float32: { return (float)this.Value; } case KeyValueType.UInt64: { return (ulong)this.Value & 0xFFFFFFFF; } } return defaultValue; } public bool AsBoolean(bool defaultValue) { if (this.Valid == false) { return defaultValue; } switch (this.Type) { case KeyValueType.String: case KeyValueType.WideString: { int value; if (int.TryParse((string)this.Value, out value) == false) { return defaultValue; } return value != 0; } case KeyValueType.Int32: { return ((int)this.Value) != 0; } case KeyValueType.Float32: { return ((int)((float)this.Value)) != 0; } case KeyValueType.UInt64: { return ((ulong)this.Value) != 0; } } return defaultValue; } public override string ToString() { if (this.Valid == false) { return ""; } if (this.Type == KeyValueType.None) { return this.Name; } return string.Format("{0} = {1}", this.Name, this.Value); } public static KeyValue LoadAsBinary(string path) { if (File.Exists(path) == false) { return null; } try { var input = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); var kv = new KeyValue(); if (kv.ReadAsBinary(input) == false) { return null; } input.Close(); return kv; } catch (Exception) { return null; } } public bool ReadAsBinary(Stream input) { this.Children = new List(); try { while (true) { var type = (KeyValueType)input.ReadValueU8(); if (type == KeyValueType.End) { break; } var current = new KeyValue { Type = type, Name = input.ReadStringUnicode(), }; switch (type) { case KeyValueType.None: { current.ReadAsBinary(input); break; } case KeyValueType.String: { current.Valid = true; current.Value = input.ReadStringUnicode(); break; } case KeyValueType.WideString: { throw new FormatException("wstring is unsupported"); } case KeyValueType.Int32: { current.Valid = true; current.Value = input.ReadValueS32(); break; } case KeyValueType.UInt64: { current.Valid = true; current.Value = input.ReadValueU64(); break; } case KeyValueType.Float32: { current.Valid = true; current.Value = input.ReadValueF32(); break; } case KeyValueType.Color: { current.Valid = true; current.Value = input.ReadValueU32(); break; } case KeyValueType.Pointer: { current.Valid = true; current.Value = input.ReadValueU32(); break; } default: { throw new FormatException(); } } if (input.Position >= input.Length) { throw new FormatException(); } this.Children.Add(current); } this.Valid = true; return input.Position == input.Length; } catch (Exception) { return false; } } } }