mirror of
https://github.com/JDM170/SteamAchievementManager
synced 2025-12-10 05:37:18 +07:00
337 lines
9.3 KiB
C#
337 lines
9.3 KiB
C#
/* Copyright (c) 2019 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 = "<root>";
|
|
public KeyValueType Type = KeyValueType.None;
|
|
public object Value;
|
|
public bool Valid;
|
|
|
|
public List<KeyValue> Children = null;
|
|
|
|
public KeyValue this[string key]
|
|
{
|
|
get
|
|
{
|
|
if (this.Children == null)
|
|
{
|
|
return _Invalid;
|
|
}
|
|
|
|
var child = this.Children.SingleOrDefault(
|
|
c => string.Compare(c.Name, key, StringComparison.InvariantCultureIgnoreCase) == 0);
|
|
|
|
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 "<invalid>";
|
|
}
|
|
|
|
if (this.Type == KeyValueType.None)
|
|
{
|
|
return this.Name;
|
|
}
|
|
|
|
return string.Format(
|
|
System.Globalization.CultureInfo.CurrentCulture,
|
|
"{0} = {1}",
|
|
this.Name,
|
|
this.Value);
|
|
}
|
|
|
|
public static KeyValue LoadAsBinary(string path)
|
|
{
|
|
if (File.Exists(path) == false)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
using (var input = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
|
{
|
|
var kv = new KeyValue();
|
|
if (kv.ReadAsBinary(input) == false)
|
|
{
|
|
return null;
|
|
}
|
|
return kv;
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public bool ReadAsBinary(Stream input)
|
|
{
|
|
this.Children = new List<KeyValue>();
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|