Add hacky damage displayer (Will get refactored)

* Change Shared namespace to SteamShared
* Add damage displayer which also calculates, gets local weather and fuel prices
* API Key for tankerkoenig has to be supplied because I didn't push it
This commit is contained in:
MathiasL 2022-05-20 22:20:45 +02:00
parent d1652aa588
commit 50ac34fb28
61 changed files with 2982 additions and 38 deletions

View file

@ -0,0 +1,65 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.32407.343
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DamagePrinter", "DamagePrinter\DamagePrinter.csproj", "{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SteamShared", "..\Shared\SteamHelpers\SteamHelpers\SteamShared.csproj", "{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoreSoft.MathExpressions", "..\Shared\LoreSoft.MathExpressions\LoreSoft.MathExpressions.csproj", "{B6813448-FCE2-429C-81B5-9722FEA839B8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Debug|x64.ActiveCfg = Debug|x64
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Debug|x64.Build.0 = Debug|x64
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Debug|x86.ActiveCfg = Debug|Any CPU
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Debug|x86.Build.0 = Debug|Any CPU
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Release|Any CPU.Build.0 = Release|Any CPU
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Release|x64.ActiveCfg = Release|x64
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Release|x64.Build.0 = Release|x64
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Release|x86.ActiveCfg = Release|Any CPU
{AF6F5837-94D3-4D44-9D1C-6B5CD600C698}.Release|x86.Build.0 = Release|Any CPU
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Debug|x64.ActiveCfg = Debug|x64
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Debug|x64.Build.0 = Debug|x64
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Debug|x86.ActiveCfg = Debug|Any CPU
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Debug|x86.Build.0 = Debug|Any CPU
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Release|Any CPU.Build.0 = Release|Any CPU
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Release|x64.ActiveCfg = Release|x64
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Release|x64.Build.0 = Release|x64
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Release|x86.ActiveCfg = Release|Any CPU
{4DB55154-E33B-49D8-BE05-DC11CA5B32A4}.Release|x86.Build.0 = Release|Any CPU
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Debug|x64.ActiveCfg = Debug|x64
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Debug|x64.Build.0 = Debug|x64
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Debug|x86.ActiveCfg = Debug|x86
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Debug|x86.Build.0 = Debug|x86
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Release|Any CPU.Build.0 = Release|Any CPU
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Release|x64.ActiveCfg = Release|x64
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Release|x64.Build.0 = Release|x64
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Release|x86.ActiveCfg = Release|x86
{B6813448-FCE2-429C-81B5-9722FEA839B8}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B75CC20D-F989-4D9F-86F7-9610737FCBE9}
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\LoreSoft.MathExpressions\LoreSoft.MathExpressions.csproj" />
<ProjectReference Include="..\..\Shared\SteamHelpers\SteamHelpers\SteamShared.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,363 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
using SteamShared;
using LoreSoft.MathExpressions;
using System.Net.Http;
using System.Text.Json.Nodes;
using Newtonsoft.Json;
static class Program
{
static readonly uint WM_COPYDATA = 0x004A;
static SteamHelper steamHelper = new SteamHelper();
static MathEvaluator mathEval = new MathEvaluator();
static HttpClient httpClient = new HttpClient();
[DllImport("user32.dll")]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern int SendMessage(IntPtr windowHandle, uint message, IntPtr wParam, IntPtr lParam);
[STAThread]
static void Main(string[] args)
{
string? gamePath = steamHelper.GetGamePathFromExactName("Counter-Strike: Global Offensive");
if (gamePath == null)
return;
string consoleLogPath = Path.Combine(gamePath, "csgo", "console.log");
bool consoleLogExists = false;
long oldFileSize = 0;
try
{
oldFileSize = new FileInfo(consoleLogPath).Length;
} catch { }
long nextLineOffset = oldFileSize; // bytes from the start of the file, by default not to read anything
int minDamageToCount = 20;
bool initialScan = true;
var prevTaggedPlayers = new List<Tuple<string, int, int>>();
DateTime lastFuelRequestTime = DateTime.MinValue;
if (FindWindow("Valve001", null) == IntPtr.Zero)
File.Delete(consoleLogPath);
// here we start fresh at line 0 cause it got deleted
while (true)
{
if (File.Exists(consoleLogPath))
{
bool update = false;
if (!consoleLogExists && initialScan) {
consoleLogExists = true;
initialScan = false;
}
long curFileSize = new FileInfo(consoleLogPath).Length;
if (curFileSize != oldFileSize)
{
update = true;
oldFileSize = curFileSize;
}
if (update)
{
int damageTakenTotal = 0;
List<string> lines = new List<string>();
using (var fs = File.Open(consoleLogPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
fs.Position = nextLineOffset;
bool endOfFile = false;
while (!endOfFile)
{
string line = "";
bool endOfLine = false;
while (!endOfLine)
{
int nextByte = fs.ReadByte();
if (nextByte == -1 && line != "")
{
lines.Add(line);
endOfFile = true;
break;
}
char nextChar = (char)nextByte;
if (nextChar == '\n' && line != "")
{
lines.Add(line);
endOfLine = true;
}
else
if(nextChar != '\r')
line += nextChar;
}
}
nextLineOffset = fs.Position;
}
var taggedPlayers = new List<Tuple<string, int, int>>();
string calcKeyWord = "!calc";
string fuelPriceKeyWord = "!fuel";
string weatherKeyWord = "!weather";
foreach (string line in lines)
{
/* -------------------------
Damage Given to "BOT Eugene" - 65 in 2 hits
-------------------------
Damage Taken from "BOT Eugene" - 117 in 4 hits*/
if (line.StartsWith("Damage Taken"))
{
Regex regexTaken = new Regex("Damage Taken from \"(.+?)\" - (\\d+) in \\d+ hits?");
Match takenMatch = regexTaken.Match(line);
if (takenMatch.Success)
{
damageTakenTotal += int.Parse(takenMatch.Groups[2].Value);
}
}
else if (line.StartsWith("Damage Given"))
{
Regex regexTaken = new Regex("Damage Given to \"(.+?)\" - (\\d+) in (\\d+) hits?");
Match givenMatch = regexTaken.Match(line);
if (givenMatch.Success)
{
string name = givenMatch.Groups[1].Value;
int damage = int.Parse(givenMatch.Groups[2].Value);
int hits = int.Parse(givenMatch.Groups[3].Value);
if(damage < 100 && damage > minDamageToCount && taggedPlayers.FirstOrDefault(player => player.Item1 == name) == null)
// not in list yet so add
taggedPlayers.Add(new Tuple<string, int, int>(name, damage, hits));
}
}
else if (line.ToLower().Contains(calcKeyWord + ' '))
{
// Calculate
string expression = line.Substring(line.IndexOf(calcKeyWord + ' ') + calcKeyWord.Length);
if (String.IsNullOrWhiteSpace(expression))
continue;
try
{
double res = mathEval.Evaluate(expression);
Thread.Sleep(700); // so the chat message is shown if we requested it ourselves
ExecuteCommands(true, $"say \"Answer: {res}\"");
}
catch { }
}
else if (line.ToLower().Contains(fuelPriceKeyWord))
{
if (DateTime.Now - lastFuelRequestTime < TimeSpan.FromMinutes(1))
continue;
string apiKey = "";
string nordOelID = "69ad1928-e972-421b-a33c-4319da73deaa";
string shellID = "a507dd35-4a7f-46d1-86b6-accc4769a47b";
string request = $"https://creativecommons.tankerkoenig.de/json/prices.php?ids={nordOelID},{shellID}&apikey={apiKey}";
HttpResponseMessage response;
try
{
response = httpClient.GetAsync(request).Result;
lastFuelRequestTime = DateTime.Now;
}
catch { continue; }
var responseString = response.Content.ReadAsStringAsync().Result;
dynamic? jsonResponse = JsonConvert.DeserializeObject(responseString);
if (jsonResponse == null || (bool)jsonResponse!.ok == false)
continue;
var cmds = new List<string>();
if((string)jsonResponse!.prices[nordOelID].status == "open")
{
cmds.Add($"say \"NORDOEL: Super: {(string)jsonResponse!.prices[nordOelID].e5} Euro, Diesel: {(string)jsonResponse!.prices[nordOelID].diesel} Euro\"");
}
else
{
cmds.Add("say \"NORDOEL: Geschlossen\"");
}
if ((string)jsonResponse.prices[shellID].status == "open")
{
cmds.Add($"say \"Shell: Super: {(string)jsonResponse!.prices[shellID].e5} Euro, Diesel: {(string)jsonResponse!.prices[shellID].diesel} Euro\"");
}
else
{
cmds.Add("say \"Shell: Geschlossen\"");
}
ExecuteCommands(true, cmds.ToArray());
}
else if (line.ToLower().Contains(weatherKeyWord))
{
string request = $"https://api.open-meteo.com/v1/forecast?latitude=54.2335&longitude=10.3397&hourly=temperature_2m,cloudcover&daily=precipitation_hours&timezone=Europe%2FBerlin";
HttpResponseMessage response;
try
{
response = httpClient.GetAsync(request).Result;
}
catch { continue; }
var responseString = response.Content.ReadAsStringAsync().Result;
dynamic? jsonResponse = JsonConvert.DeserializeObject(responseString);
if (jsonResponse == null)
continue;
string cmd = $"say \"{((double)jsonResponse!.hourly.temperature_2m[0]).ToString(System.Globalization.CultureInfo.InvariantCulture)} C, Wolkendecke {(double)jsonResponse!.hourly.cloudcover[0]} %, Stunden Regen: {Math.Round((double)jsonResponse!.daily.precipitation_hours[0] * 100 / 24)} %\"";
ExecuteCommands(true, cmd);
}
}
bool didPlayerDie = damageTakenTotal >= 100;
if (!didPlayerDie)
{
continue;
}
if (areListsEqual(taggedPlayers, prevTaggedPlayers))
{
// Last is the same as previous, likely dealt damage, died and now the round ended and it was printed again
Console.WriteLine("Double console output.");
continue;
}
prevTaggedPlayers = taggedPlayers;
if (taggedPlayers.Count < 1)
continue;
string[] commands = new string[taggedPlayers.Count];
// We didn't have a round end, but got killed
for (int i = 0; i < commands.Length; i++)
{
commands[i] += "say_team \"";
if (taggedPlayers[i].Item2 > 90)
{
// One-shot
commands[i] += $"{taggedPlayers[i].Item1} is one-shot {taggedPlayers[i].Item2}";
}
else if (taggedPlayers[i].Item2 >= 70)
{
// Lit
commands[i] += $"{taggedPlayers[i].Item1} is lit for {taggedPlayers[i].Item2}";
}
else
{
// Tagged
commands[i] += $"{taggedPlayers[i].Item1} is tagged for {taggedPlayers[i].Item2}";
}
commands[i] += "\"";
}
// Notify players
ExecuteCommands(false, commands);
}
}
Thread.Sleep(50);
}
}
static bool areListsEqual(List<Tuple<string, int, int>> list1, List<Tuple<string, int, int>> list2)
{
if (list1.Count != list2.Count)
return false;
// still same length
for (int i = 0; i < list1.Count; i++)
{
if (list1[i].Item1 != list2[i].Item1)
return false;
if (list1[i].Item2 != list2[i].Item2)
return false;
if (list1[i].Item3 != list2[i].Item3)
return false;
}
return true;
}
static bool ExecuteCommands(bool triggeredByCommand, params string[] cmds)
{
if (cmds == null)
return false;
IntPtr hWnd = FindWindow("Valve001", null!);
if (hWnd == IntPtr.Zero)
return false;
int chatTimeoutMs = 700;
int commandsHandled = 0;
for (int i = 0; i < cmds.Length; i++)
{
if (cmds[i] == null)
continue;
cmds[i] = cmds[i].Trim();
COPYDATASTRUCT data;
data.dwData = 0;
data.cbData = (uint)cmds[i].Length + 1;
data.lpData = cmds[i];
if (triggeredByCommand)
Thread.Sleep(chatTimeoutMs);
// Allocate for data
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(data));
Marshal.StructureToPtr(data, ptr, false);
int ret = SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, ptr);
Console.WriteLine(cmds[i]);
// Free data
Marshal.FreeHGlobal(ptr);
if (ret == 0)
commandsHandled++;
if (cmds[i].StartsWith("say") || cmds[i].StartsWith("say_team"))
Thread.Sleep(chatTimeoutMs);
}
return cmds.Length > 0 && commandsHandled == cmds.Length;
}
}
struct COPYDATASTRUCT
{
public ulong dwData;
public uint cbData;
public string lpData;
}

View file

@ -0,0 +1,225 @@
using System;
using System.Collections.Generic;
using LoreSoft.MathExpressions.Properties;
using LoreSoft.MathExpressions.UnitConversion;
using System.Globalization;
using LoreSoft.MathExpressions.Metadata;
using System.Reflection;
namespace LoreSoft.MathExpressions
{
/// <summary>
/// A class representing unit convertion expressions.
/// </summary>
public class ConvertExpression : ExpressionBase
{
private static Dictionary<string, ConvertionMap> convertionCache;
private static object cacheLock = new object();
private ConvertionMap current;
private string expression;
/// <summary>The format of a convertion expression.</summary>
public const string ExpressionFormat = "[{0}->{1}]";
/// <summary>Initializes a new instance of the <see cref="ConvertExpression"/> class.</summary>
/// <param name="expression">The convertion expression for this instance.</param>
public ConvertExpression(string expression)
{
VerifyCache();
if (!convertionCache.ContainsKey(expression))
throw new ArgumentException(Resources.InvalidConvertionExpression + expression, "expression");
this.expression = expression;
current = convertionCache[expression];
base.Evaluate = new MathEvaluate(Convert);
}
/// <summary>Gets the number of arguments this expression uses.</summary>
/// <value>The argument count.</value>
public override int ArgumentCount
{
get { return 1; }
}
/// <summary>Convert the numbers to the new unit.</summary>
/// <param name="numbers">The numbers used in the convertion.</param>
/// <returns>The result of the convertion execution.</returns>
/// <exception cref="ArgumentNullException">When numbers is null.</exception>
/// <exception cref="ArgumentException">When the length of numbers do not equal <see cref="ArgumentCount"/>.</exception>
public double Convert(double[] numbers)
{
base.Validate(numbers);
double fromValue = numbers[0];
switch (current.UnitType)
{
case UnitType.Length:
return LengthConverter.Convert(
(LengthUnit) current.FromUnit,
(LengthUnit) current.ToUnit,
fromValue);
case UnitType.Mass:
return MassConverter.Convert(
(MassUnit) current.FromUnit,
(MassUnit) current.ToUnit,
fromValue);
case UnitType.Speed:
return SpeedConverter.Convert(
(SpeedUnit) current.FromUnit,
(SpeedUnit) current.ToUnit,
fromValue);
case UnitType.Temperature:
return TemperatureConverter.Convert(
(TemperatureUnit) current.FromUnit,
(TemperatureUnit) current.ToUnit,
fromValue);
case UnitType.Time:
return TimeConverter.Convert(
(TimeUnit) current.FromUnit,
(TimeUnit) current.ToUnit,
fromValue);
case UnitType.Volume:
return VolumeConverter.Convert(
(VolumeUnit) current.FromUnit,
(VolumeUnit) current.ToUnit,
fromValue);
default:
throw new ArgumentOutOfRangeException("numbers");
}
}
///<summary>
/// Determines whether the specified expression name is for unit convertion.
///</summary>
///<param name="expression">The expression to check.</param>
///<returns><c>true</c> if the specified expression is a unit convertion; otherwise, <c>false</c>.</returns>
public static bool IsConvertExpression(string expression)
{
//do basic checks before creating cache
if (string.IsNullOrEmpty(expression))
return false;
if (expression[0] != '[')
return false;
VerifyCache();
return convertionCache.ContainsKey(expression);
}
private static void VerifyCache()
{
if (convertionCache != null)
return;
lock (cacheLock)
{
if (convertionCache != null)
return;
convertionCache = new Dictionary<string, ConvertionMap>(
StringComparer.OrdinalIgnoreCase);
AddToCache<LengthUnit>(UnitType.Length);
AddToCache<MassUnit>(UnitType.Mass);
AddToCache<SpeedUnit>(UnitType.Speed);
AddToCache<TemperatureUnit>(UnitType.Temperature);
AddToCache<TimeUnit>(UnitType.Time);
AddToCache<VolumeUnit>(UnitType.Volume);
}
}
private static void AddToCache<T>(UnitType unitType)
where T : struct, IComparable, IFormattable, IConvertible
{
Type enumType = typeof(T);
int[] a = (int[])Enum.GetValues(enumType);
for (int x = 0; x < a.Length; x++)
{
MemberInfo parentInfo = GetMemberInfo(enumType, Enum.GetName(enumType, x));
string parrentKey = AttributeReader.GetAbbreviation(parentInfo);
for (int i = 0; i < a.Length; i++)
{
if (x == i)
continue;
MemberInfo info = GetMemberInfo(enumType, Enum.GetName(enumType, i));
string key = string.Format(
CultureInfo.InvariantCulture,
ExpressionFormat,
parrentKey,
AttributeReader.GetAbbreviation(info));
convertionCache.Add(
key, new ConvertionMap(unitType, x, i));
}
}
}
private static MemberInfo GetMemberInfo(Type type, string name)
{
MemberInfo[] info = type.GetMember(name);
if (info == null || info.Length == 0)
return null;
return info[0];
}
/// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </summary>
/// <returns>
/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </returns>
/// <filterPriority>2</filterPriority>
public override string ToString()
{
return expression;
}
private class ConvertionMap
{
public ConvertionMap(UnitType unitType, int fromUnit, int toUnit)
{
_unitType = unitType;
_fromUnit = fromUnit;
_toUnit = toUnit;
}
private UnitType _unitType;
public UnitType UnitType
{
get { return _unitType; }
}
private int _fromUnit;
public int FromUnit
{
get { return _fromUnit; }
}
private int _toUnit;
public int ToUnit
{
get { return _toUnit; }
}
public override string ToString()
{
return string.Format(
CultureInfo.CurrentCulture,
"{0}, [{1}->{2}]",
_unitType,
_fromUnit,
_toUnit);
}
}
}
}

View file

@ -0,0 +1,35 @@
using System;
using LoreSoft.MathExpressions.Properties;
namespace LoreSoft.MathExpressions
{
/// <summary>The base class for expressions</summary>
public abstract class ExpressionBase : IExpression
{
/// <summary>Gets the number of arguments this expression uses.</summary>
/// <value>The argument count.</value>
public abstract int ArgumentCount { get; }
private MathEvaluate _evaluateDelegate;
/// <summary>Gets or sets the evaluate delegate.</summary>
/// <value>The evaluate delegate.</value>
public virtual MathEvaluate Evaluate
{
get { return _evaluateDelegate; }
set { _evaluateDelegate = value; }
}
/// <summary>Validates the specified numbers for the expression.</summary>
/// <param name="numbers">The numbers to validate.</param>
/// <exception cref="ArgumentNullException">When numbers is null.</exception>
/// <exception cref="ArgumentException">When the length of numbers do not equal <see cref="ArgumentCount"/>.</exception>
protected void Validate(double[] numbers)
{
if (numbers == null)
throw new ArgumentNullException("numbers");
if (numbers.Length != ArgumentCount)
throw new ArgumentException(Resources.InvalidLengthOfArray, "numbers");
}
}
}

View file

@ -0,0 +1,113 @@
using System;
using System.Reflection;
using LoreSoft.MathExpressions.Properties;
using System.Globalization;
namespace LoreSoft.MathExpressions
{
/// <summary>
/// A class representing the System.Math function expressions
/// </summary>
public class FunctionExpression : ExpressionBase
{
// must be sorted
/// <summary>The supported math functions by this class.</summary>
private static readonly string[] mathFunctions = new string[]
{
"abs", "acos", "asin", "atan", "ceiling", "cos", "cosh", "exp",
"floor", "log", "log10", "sin", "sinh", "sqrt", "tan", "tanh"
};
/// <summary>Initializes a new instance of the <see cref="FunctionExpression"/> class.</summary>
/// <param name="function">The function name for this instance.</param>
public FunctionExpression(string function) : this(function, true)
{
}
/// <summary>Initializes a new instance of the <see cref="FunctionExpression"/> class.</summary>
/// <param name="function">The function.</param>
/// <param name="validate">if set to <c>true</c> to validate the function name.</param>
internal FunctionExpression(string function, bool validate)
{
function = function.ToLowerInvariant();
if (validate && !IsFunction(function))
throw new ArgumentException(
string.Format(CultureInfo.CurrentCulture, Resources.InvalidFunctionName, _function),
"function");
_function = function;
base.Evaluate = new MathEvaluate(Execute);
}
private string _function;
/// <summary>Gets the name function for this instance.</summary>
/// <value>The function name.</value>
public string Function
{
get { return _function; }
}
/// <summary>Executes the function on specified numbers.</summary>
/// <param name="numbers">The numbers used in the function.</param>
/// <returns>The result of the function execution.</returns>
/// <exception cref="ArgumentNullException">When numbers is null.</exception>
/// <exception cref="ArgumentException">When the length of numbers do not equal <see cref="ArgumentCount"/>.</exception>
public double Execute(double[] numbers)
{
base.Validate(numbers);
string function = char.ToUpperInvariant(_function[0]) + _function.Substring(1);
MethodInfo method = typeof (Math).GetMethod(
function,
BindingFlags.Static | BindingFlags.Public,
null,
new Type[] { typeof(double) },
null);
if (method == null)
throw new InvalidOperationException(
string.Format(CultureInfo.CurrentCulture,
Resources.InvalidFunctionName, _function));
object[] parameters = new object[numbers.Length];
Array.Copy(numbers, parameters, numbers.Length);
return (double) method.Invoke(null, parameters);
}
/// <summary>Gets the number of arguments this expression uses.</summary>
/// <value>The argument count.</value>
public override int ArgumentCount
{
get { return 1; }
}
/// <summary>Determines whether the specified function name is a function.</summary>
/// <param name="function">The function name.</param>
/// <returns><c>true</c> if the specified name is a function; otherwise, <c>false</c>.</returns>
public static bool IsFunction(string function)
{
return (Array.BinarySearch(
mathFunctions, function,
StringComparer.OrdinalIgnoreCase) >= 0);
}
/// <summary>Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.</summary>
/// <returns>A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.</returns>
/// <filterPriority>2</filterPriority>
public override string ToString()
{
return _function;
}
/// <summary>
/// Gets the function names.
/// </summary>
/// <returns>An array of function names.</returns>
public static string[] GetFunctionNames()
{
return (string[])mathFunctions.Clone();
}
}
}

View file

@ -0,0 +1,2 @@
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "LoreSoft.MathExpressions.Metadata")]

View file

@ -0,0 +1,15 @@
namespace LoreSoft.MathExpressions
{
/// <summary>
/// The interface used when running expressions
/// </summary>
public interface IExpression
{
/// <summary>Gets the number of arguments this expression uses.</summary>
/// <value>The argument count.</value>
int ArgumentCount { get; }
/// <summary>Gets or sets the evaluate delegate.</summary>
/// <value>The evaluate delegate.</value>
MathEvaluate Evaluate { get; set; }
}
}

View file

@ -0,0 +1,185 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{B6813448-FCE2-429C-81B5-9722FEA839B8}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>LoreSoft.MathExpressions</RootNamespace>
<AssemblyName>LoreSoft.MathExpressions</AssemblyName>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>MathExpressions.snk</AssemblyOriginatorKeyFile>
<FileUpgradeFlags>
</FileUpgradeFlags>
<OldToolsVersion>3.5</OldToolsVersion>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\Build\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>..\..\Build\Debug\LoreSoft.MathExpressions.XML</DocumentationFile>
<RunCodeAnalysis>true</RunCodeAnalysis>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\Build\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>..\..\Build\Release\LoreSoft.MathExpressions.XML</DocumentationFile>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\..\Build\x86\Debug\</OutputPath>
<DefineConstants>CODE_ANALYSIS;DEBUG;TRACE</DefineConstants>
<DocumentationFile>..\..\Build\Debug\LoreSoft.MathExpressions.XML</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<RunCodeAnalysis>true</RunCodeAnalysis>
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>..\..\Build\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DocumentationFile>..\..\Build\Release\LoreSoft.MathExpressions.XML</DocumentationFile>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\..\Build\x64\Debug\</OutputPath>
<DefineConstants>CODE_ANALYSIS;DEBUG;TRACE</DefineConstants>
<DocumentationFile>..\..\Build\Debug\LoreSoft.MathExpressions.XML</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<RunCodeAnalysis>true</RunCodeAnalysis>
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
<OutputPath>..\..\Build\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DocumentationFile>..\..\Build\Release\LoreSoft.MathExpressions.XML</DocumentationFile>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ConvertExpression.cs" />
<Compile Include="ExpressionBase.cs" />
<Compile Include="FunctionExpression.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="IExpression.cs" />
<Compile Include="MathEvaluate.cs" />
<Compile Include="MathEvaluator.cs" />
<Compile Include="MathOperators.cs" />
<Compile Include="Metadata\AttributeReader.cs" />
<Compile Include="NumberExpression.cs" />
<Compile Include="OperatorExpression.cs" />
<Compile Include="ParseException.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Metadata\AbbreviationAttribute.cs" />
<Compile Include="UnitConversion\LengthConverter.cs" />
<Compile Include="UnitConversion\MassConverter.cs" />
<Compile Include="UnitConversion\SpeedConverter.cs" />
<Compile Include="UnitConversion\TemperatureConverter.cs" />
<Compile Include="UnitConversion\TimeConverter.cs" />
<Compile Include="UnitConversion\UnitType.cs" />
<Compile Include="UnitConversion\VolumeConverter.cs" />
<Compile Include="VariableDictionary.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.resx">
<SubType>Designer</SubType>
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="MathExpressions.snk" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
<Visible>False</Visible>
<ProductName>Windows Installer 3.1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View file

@ -0,0 +1,7 @@
namespace LoreSoft.MathExpressions
{
/// <summary>Delegate used by an expression to do the math evaluation.</summary>
/// <param name="numbers">The numbers to evaluate.</param>
/// <returns>The result of the evaluated numbers.</returns>
public delegate double MathEvaluate(double[] numbers);
}

View file

@ -0,0 +1,458 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Text;
using LoreSoft.MathExpressions.Properties;
using System.Globalization;
namespace LoreSoft.MathExpressions
{
/// <summary>
/// Evaluate math expressions
/// </summary>
/// <example>Using the MathEvaluator to calculate a math expression.
/// <code>
/// MathEvaluator eval = new MathEvaluator();
/// //basic math
/// double result = eval.Evaluate("(2 + 1) * (1 + 2)");
/// //calling a function
/// result = eval.Evaluate("sqrt(4)");
/// //evaluate trigonometric
/// result = eval.Evaluate("cos(pi * 45 / 180.0)");
/// //convert inches to feet
/// result = eval.Evaluate("12 [in->ft]");
/// //use variable
/// result = eval.Evaluate("answer * 10");
/// </code>
/// </example>
public class MathEvaluator : IDisposable
{
/// <summary>The name of the answer variable.</summary>
/// <seealso cref="Variables"/>
public const string AnswerVariable = "answer";
//instance scope to optimize reuse
private Stack<string> _symbolStack;
private Queue<IExpression> _expressionQueue;
private Dictionary<string, IExpression> _expressionCache;
private StringBuilder _buffer;
private Stack<double> _calculationStack;
private Stack<double> _parameters;
private List<string> _innerFunctions;
private StringReader _expressionReader;
/// <summary>
/// Initializes a new instance of the <see cref="MathEvaluator"/> class.
/// </summary>
public MathEvaluator()
{
_variables = new VariableDictionary(this);
_innerFunctions = new List<string>(FunctionExpression.GetFunctionNames());
_functions = new ReadOnlyCollection<string>(_innerFunctions);
_expressionCache = new Dictionary<string, IExpression>(StringComparer.OrdinalIgnoreCase);
_symbolStack = new Stack<string>();
_expressionQueue = new Queue<IExpression>();
_buffer = new StringBuilder();
_calculationStack = new Stack<double>();
_parameters = new Stack<double>(2);
}
private VariableDictionary _variables;
/// <summary>
/// Gets the variables collections.
/// </summary>
/// <value>The variables for <see cref="MathEvaluator"/>.</value>
public VariableDictionary Variables
{
get { return _variables; }
}
private ReadOnlyCollection<string> _functions;
/// <summary>Gets the functions available to <see cref="MathEvaluator"/>.</summary>
/// <value>The functions for <see cref="MathEvaluator"/>.</value>
/// <seealso cref="RegisterFunction"/>
public ReadOnlyCollection<string> Functions
{
get { return _functions; }
}
/// <summary>Gets the answer from the last evaluation.</summary>
/// <value>The answer variable value.</value>
/// <seealso cref="Variables"/>
public double Answer
{
get { return _variables[AnswerVariable]; }
}
/// <summary>Evaluates the specified expression.</summary>
/// <param name="expression">The expression to evaluate.</param>
/// <returns>The result of the evaluated expression.</returns>
/// <exception cref="ArgumentNullException">When expression is null or empty.</exception>
/// <exception cref="ParseException">When there is an error parsing the expression.</exception>
public double Evaluate(string expression)
{
if (string.IsNullOrEmpty(expression))
throw new ArgumentNullException("expression");
_expressionReader = new StringReader(expression);
_symbolStack.Clear();
_expressionQueue.Clear();
ParseExpressionToQueue();
double result = CalculateFromQueue();
_variables[AnswerVariable] = result;
return result;
}
/// <summary>Registers a function for the <see cref="MathEvaluator"/>.</summary>
/// <param name="functionName">Name of the function.</param>
/// <param name="expression">An instance of <see cref="IExpression"/> for the function.</param>
/// <exception cref="ArgumentNullException">When functionName or expression are null.</exception>
/// <exception cref="ArgumentException">When IExpression.Evaluate property is null or the functionName is already registered.</exception>
/// <seealso cref="Functions"/>
/// <seealso cref="IExpression"/>
public void RegisterFunction(string functionName, IExpression expression)
{
if (string.IsNullOrEmpty(functionName))
throw new ArgumentNullException("functionName");
if (expression == null)
throw new ArgumentNullException("expression");
if (expression.Evaluate == null)
throw new ArgumentException(Resources.EvaluatePropertyCanNotBeNull, "expression");
if (_innerFunctions.BinarySearch(functionName) >= 0)
throw new ArgumentException(
string.Format(CultureInfo.CurrentCulture,
Resources.FunctionNameRegistered, functionName), "functionName");
_innerFunctions.Add(functionName);
_innerFunctions.Sort();
_expressionCache.Add(functionName, expression);
}
/// <summary>Determines whether the specified name is a function.</summary>
/// <param name="name">The name of the function.</param>
/// <returns><c>true</c> if the specified name is function; otherwise, <c>false</c>.</returns>
internal bool IsFunction(string name)
{
return (_innerFunctions.BinarySearch(name, StringComparer.OrdinalIgnoreCase) >= 0);
}
private void ParseExpressionToQueue()
{
char l = '\0';
char c = '\0';
do
{
// last non white space char
if (!char.IsWhiteSpace(c))
l = c;
c = (char)_expressionReader.Read();
if (char.IsWhiteSpace(c))
continue;
if (TryNumber(c, l))
continue;
if (TryString(c))
continue;
if (TryStartGroup(c))
continue;
if (TryOperator(c))
continue;
if (TryEndGroup(c))
continue;
if (TryConvert(c))
continue;
throw new ParseException(Resources.InvalidCharacterEncountered + c);
} while (_expressionReader.Peek() != -1);
ProcessSymbolStack();
}
private bool TryConvert(char c)
{
if (c != '[')
return false;
_buffer.Length = 0;
_buffer.Append(c);
char p = (char)_expressionReader.Peek();
while (char.IsLetter(p) || char.IsWhiteSpace(p) || p == '-' || p == '>' || p == ']')
{
if (!char.IsWhiteSpace(p))
_buffer.Append((char)_expressionReader.Read());
else
_expressionReader.Read();
if (p == ']')
break;
p = (char)_expressionReader.Peek();
}
if (ConvertExpression.IsConvertExpression(_buffer.ToString()))
{
IExpression e = GetExpressionFromSymbol(_buffer.ToString());
_expressionQueue.Enqueue(e);
return true;
}
throw new ParseException(Resources.InvalidConvertionExpression + _buffer);
}
private bool TryString(char c)
{
if (!char.IsLetter(c))
return false;
_buffer.Length = 0;
_buffer.Append(c);
char p = (char)_expressionReader.Peek();
while (char.IsLetter(p))
{
_buffer.Append((char)_expressionReader.Read());
p = (char)_expressionReader.Peek();
}
if (_variables.ContainsKey(_buffer.ToString()))
{
double value = _variables[_buffer.ToString()];
NumberExpression expression = new NumberExpression(value);
_expressionQueue.Enqueue(expression);
return true;
}
if (IsFunction(_buffer.ToString()))
{
_symbolStack.Push(_buffer.ToString());
return true;
}
throw new ParseException(Resources.InvalidVariableEncountered + _buffer);
}
private bool TryStartGroup(char c)
{
if (c != '(')
return false;
_symbolStack.Push(c.ToString());
return true;
}
private bool TryEndGroup(char c)
{
if (c != ')')
return false;
bool hasStart = false;
while (_symbolStack.Count > 0)
{
string p = _symbolStack.Pop();
if (p == "(")
{
hasStart = true;
if (_symbolStack.Count == 0)
break;
string n = _symbolStack.Peek();
if (FunctionExpression.IsFunction(n))
{
p = _symbolStack.Pop();
IExpression f = GetExpressionFromSymbol(p);
_expressionQueue.Enqueue(f);
}
break;
}
IExpression e = GetExpressionFromSymbol(p);
_expressionQueue.Enqueue(e);
}
if (!hasStart)
throw new ParseException(Resources.UnbalancedParentheses);
return true;
}
private bool TryOperator(char c)
{
if (!OperatorExpression.IsSymbol(c))
return false;
bool repeat;
string s = c.ToString();
do
{
string p = _symbolStack.Count == 0 ? string.Empty : _symbolStack.Peek();
repeat = false;
if (_symbolStack.Count == 0)
_symbolStack.Push(s);
else if (p == "(")
_symbolStack.Push(s);
else if (Precedence(s) > Precedence(p))
_symbolStack.Push(s);
else
{
IExpression e = GetExpressionFromSymbol(_symbolStack.Pop());
_expressionQueue.Enqueue(e);
repeat = true;
}
} while (repeat);
return true;
}
private bool TryNumber(char c, char l)
{
bool isNumber = NumberExpression.IsNumber(c);
// only negative when last char is group start or symbol
bool isNegative = NumberExpression.IsNegativeSign(c) &&
(l == '\0' || l == '(' || OperatorExpression.IsSymbol(l));
if (!isNumber && !isNegative)
return false;
_buffer.Length = 0;
_buffer.Append(c);
char p = (char)_expressionReader.Peek();
while (NumberExpression.IsNumber(p))
{
_buffer.Append((char)_expressionReader.Read());
p = (char)_expressionReader.Peek();
}
double value;
if (!(double.TryParse(_buffer.ToString(), out value)))
throw new ParseException(Resources.InvalidNumberFormat + _buffer);
NumberExpression expression = new NumberExpression(value);
_expressionQueue.Enqueue(expression);
return true;
}
private void ProcessSymbolStack()
{
while (_symbolStack.Count > 0)
{
string p = _symbolStack.Pop();
if (p.Length == 1 && p == "(")
throw new ParseException(Resources.UnbalancedParentheses);
IExpression e = GetExpressionFromSymbol(p);
_expressionQueue.Enqueue(e);
}
}
private IExpression GetExpressionFromSymbol(string p)
{
IExpression e;
if (_expressionCache.ContainsKey(p))
e = _expressionCache[p];
else if (OperatorExpression.IsSymbol(p))
{
e = new OperatorExpression(p);
_expressionCache.Add(p, e);
}
else if (FunctionExpression.IsFunction(p))
{
e = new FunctionExpression(p, false);
_expressionCache.Add(p, e);
}
else if (ConvertExpression.IsConvertExpression(p))
{
e = new ConvertExpression(p);
_expressionCache.Add(p, e);
}
else
throw new ParseException(Resources.InvalidSymbolOnStack + p);
return e;
}
private static int Precedence(string c)
{
if (c.Length == 1 && (c[0] == '*' || c[0] == '/' || c[0] == '%'))
return 2;
return 1;
}
private double CalculateFromQueue()
{
double result;
_calculationStack.Clear();
foreach (IExpression expression in _expressionQueue)
{
if (_calculationStack.Count < expression.ArgumentCount)
throw new ParseException(Resources.NotEnoughNumbers + expression);
_parameters.Clear();
for (int i = 0; i < expression.ArgumentCount; i++)
_parameters.Push(_calculationStack.Pop());
_calculationStack.Push(expression.Evaluate.Invoke(_parameters.ToArray()));
}
result = _calculationStack.Pop();
return result;
}
#region IDisposable Members
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and managed resources
/// </summary>
/// <param name="disposing">
/// <c>true</c> to release both managed and unmanaged resources;
/// <c>false</c> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_expressionReader != null)
{
_expressionReader.Dispose();
_expressionReader = null;
}
}
}
#endregion
}
}

Binary file not shown.

View file

@ -0,0 +1,19 @@
namespace LoreSoft.MathExpressions
{
/// <summary>Math Operators</summary>
public enum MathOperators
{
/// <summary>Add Operator</summary>
Add,
/// <summary>Subtract Operator</summary>
Subtract,
/// <summary>Multiple Operator</summary>
Multiple,
/// <summary>Divide Operator</summary>
Divide,
/// <summary>Modulo Operator</summary>
Modulo,
/// <summary>Power Operator</summary>
Power
}
}

View file

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace LoreSoft.MathExpressions.Metadata
{
/// <summary>
/// Specifies an abbreviation for a instance.
/// </summary>
[AttributeUsageAttribute(AttributeTargets.All)]
public sealed class AbbreviationAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="AbbreviationAttribute"/> class.
/// </summary>
/// <param name="text">The abbreviation text.</param>
public AbbreviationAttribute(string text)
{
_text = text;
}
private string _text;
/// <summary>
/// Gets the abbreviation text.
/// </summary>
/// <value>The abbreviation text.</value>
public string Text
{
get { return _text; }
}
/// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </summary>
/// <returns>
/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </returns>
/// <filterPriority>2</filterPriority>
public override string ToString()
{
return _text;
}
}
}

View file

@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.ComponentModel;
namespace LoreSoft.MathExpressions.Metadata
{
/// <summary>
/// A class to read attributes from type members.
/// </summary>
public static class AttributeReader
{
/// <summary>
/// Gets the description from the <see cref="DescriptionAttribute"/> on an enum.
/// </summary>
/// <typeparam name="T">An enum type.</typeparam>
/// <param name="instance">The value to get the description from.</param>
/// <returns>The <see cref="DescriptionAttribute.Description"/> or the name of the instance.</returns>
/// <seealso cref="DescriptionAttribute"/>
public static string GetDescription<T>(T instance)
{
if (instance == null)
throw new ArgumentNullException("instance");
string result = instance.ToString();
Type type = instance.GetType();
MemberInfo[] members = type.GetMember(result);
if (members == null || members.Length == 0)
return result;
return GetDescription(members[0]);
}
/// <summary>
/// Gets the description from the <see cref="DescriptionAttribute"/> on a MemberInfo.
/// </summary>
/// <param name="info">The member info to look for the description.</param>
/// <returns>The <see cref="DescriptionAttribute.Description"/> or the name of the member.</returns>
/// <seealso cref="DescriptionAttribute"/>
public static string GetDescription(MemberInfo info)
{
if (info == null)
throw new ArgumentNullException("info");
string result = info.Name;
object[] attributes = info.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes == null || attributes.Length == 0)
return result;
DescriptionAttribute description = attributes[0] as DescriptionAttribute;
if (description == null || string.IsNullOrEmpty(description.Description))
return result;
return description.Description;
}
/// <summary>
/// Gets the abbreviation from the <see cref="AbbreviationAttribute"/> on an enum.
/// </summary>
/// <typeparam name="T">An enum type.</typeparam>
/// <param name="instance">The enum to get the abbreviation from.</param>
/// <returns>The <see cref="AbbreviationAttribute.Text"/> or the name of the memeber.</returns>
/// <seealso cref="AbbreviationAttribute"/>
public static string GetAbbreviation<T>(T instance)
{
if (instance == null)
throw new ArgumentNullException("instance");
string result = instance.ToString();
Type type = instance.GetType();
MemberInfo[] members = type.GetMember(result);
if (members == null || members.Length == 0)
return result;
return GetAbbreviation(members[0]);
}
/// <summary>
/// Gets the abbreviation from the <see cref="AbbreviationAttribute"/> on a instance.
/// </summary>
/// <param name="info">The instance info look for the abbreviation.</param>
/// <returns>The <see cref="AbbreviationAttribute.Text"/> or the name of the instance.</returns>
/// <seealso cref="AbbreviationAttribute"/>
public static string GetAbbreviation(MemberInfo info)
{
if (info == null)
throw new ArgumentNullException("info");
string result = info.Name;
object[] attributes = info.GetCustomAttributes(typeof(AbbreviationAttribute), false);
if (attributes == null || attributes.Length == 0)
return result;
AbbreviationAttribute abbreviation = attributes[0] as AbbreviationAttribute;
if (abbreviation == null || string.IsNullOrEmpty(abbreviation.Text))
return result;
return abbreviation.Text;
}
}
}

View file

@ -0,0 +1,68 @@
using System.Globalization;
namespace LoreSoft.MathExpressions
{
/// <summary>
/// Class representing a constant number expression.
/// </summary>
public class NumberExpression : ExpressionBase
{
/// <summary>Initializes a new instance of the <see cref="NumberExpression"/> class.</summary>
/// <param name="value">The number value for this expression.</param>
public NumberExpression(double value)
{
_value = value;
base.Evaluate = delegate
{
return Value;
};
}
/// <summary>Gets the number of arguments this expression uses.</summary>
/// <value>The argument count.</value>
public override int ArgumentCount
{
get { return 0; }
}
private double _value;
/// <summary>Gets the number value for this expression.</summary>
/// <value>The number value.</value>
public double Value
{
get { return _value; }
}
/// <summary>Determines whether the specified char is a number.</summary>
/// <param name="c">The char to test.</param>
/// <returns><c>true</c> if the specified char is a number; otherwise, <c>false</c>.</returns>
/// <remarks>This method checks if the char is a digit or a decimal separator.</remarks>
public static bool IsNumber(char c)
{
NumberFormatInfo f = CultureInfo.CurrentUICulture.NumberFormat;
return char.IsDigit(c) || f.NumberDecimalSeparator.IndexOf(c) >= 0;
}
/// <summary>Determines whether the specified char is negative sign.</summary>
/// <param name="c">The char to check.</param>
/// <returns><c>true</c> if the specified char is negative sign; otherwise, <c>false</c>.</returns>
public static bool IsNegativeSign(char c)
{
NumberFormatInfo f = CultureInfo.CurrentUICulture.NumberFormat;
return f.NegativeSign.IndexOf(c) >= 0;
}
/// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </summary>
/// <returns>
/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </returns>
/// <filterPriority>2</filterPriority>
public override string ToString()
{
return _value.ToString(CultureInfo.CurrentCulture);
}
}
}

View file

@ -0,0 +1,203 @@
using System;
using LoreSoft.MathExpressions.Properties;
using System.Diagnostics.CodeAnalysis;
namespace LoreSoft.MathExpressions
{
/// <summary>
/// Class representing a math operator expression.
/// </summary>
public class OperatorExpression : ExpressionBase
{
/// <summary>The supported math operators by this class.</summary>
private static readonly char[] operatorSymbols = new char[] {'+', '-', '*', '/', '%', '^'};
/// <summary>Initializes a new instance of the <see cref="OperatorExpression"/> class.</summary>
/// <param name="operator">The operator to use for this class.</param>
/// <exception cref="ArgumentNullException">When the operator is null or empty.</exception>
/// <exception cref="ArgumentException">When the operator is invalid.</exception>
public OperatorExpression(string @operator)
{
if (string.IsNullOrEmpty(@operator))
throw new ArgumentNullException("operator");
switch (@operator)
{
case "+":
base.Evaluate = new MathEvaluate(Add);
_mathOperator = MathOperators.Add;
break;
case "-":
base.Evaluate = new MathEvaluate(Subtract);
_mathOperator = MathOperators.Subtract;
break;
case "*":
base.Evaluate = new MathEvaluate(Multiple);
_mathOperator = MathOperators.Multiple;
break;
case "/":
base.Evaluate = new MathEvaluate(Divide);
_mathOperator = MathOperators.Divide;
break;
case "%":
base.Evaluate = new MathEvaluate(Modulo);
_mathOperator = MathOperators.Modulo;
break;
case "^":
base.Evaluate = new MathEvaluate(Power);
_mathOperator = MathOperators.Power;
break;
default:
throw new ArgumentException(Resources.InvalidOperator + @operator, "operator");
}
}
private MathOperators _mathOperator;
/// <summary>Gets the math operator.</summary>
/// <value>The math operator.</value>
public MathOperators MathOperator
{
get { return _mathOperator; }
}
/// <summary>Gets the number of arguments this expression uses.</summary>
/// <value>The argument count.</value>
public override int ArgumentCount
{
get { return 2; }
}
/// <summary>Adds the specified numbers.</summary>
/// <param name="numbers">The numbers.</param>
/// <returns>The result of the operation.</returns>
/// <exception cref="ArgumentNullException">When numbers is null.</exception>
/// <exception cref="ArgumentException">When the length of numbers do not equal <see cref="ArgumentCount"/>.</exception>
public double Add(double[] numbers)
{
base.Validate(numbers);
double result = 0;
foreach (double n in numbers)
result += n;
return result;
}
/// <summary>Subtracts the specified numbers.</summary>
/// <param name="numbers">The numbers.</param>
/// <returns>The result of the operation.</returns>
/// <exception cref="ArgumentNullException">When numbers is null.</exception>
/// <exception cref="ArgumentException">When the length of numbers do not equal <see cref="ArgumentCount"/>.</exception>
public double Subtract(double[] numbers)
{
base.Validate(numbers);
double? result = null;
foreach (double n in numbers)
if (result.HasValue)
result -= n;
else
result = n;
return result ?? 0;
}
/// <summary>Multiples the specified numbers.</summary>
/// <param name="numbers">The numbers.</param>
/// <returns>The result of the operation.</returns>
/// <exception cref="ArgumentNullException">When numbers is null.</exception>
/// <exception cref="ArgumentException">When the length of numbers do not equal <see cref="ArgumentCount"/>.</exception>
public double Multiple(double[] numbers)
{
base.Validate(numbers);
double? result = null;
foreach (double n in numbers)
if (result.HasValue)
result *= n;
else
result = n;
return result ?? 0;
}
/// <summary>Divides the specified numbers.</summary>
/// <param name="numbers">The numbers.</param>
/// <returns>The result of the operation.</returns>
/// <exception cref="ArgumentNullException">When numbers is null.</exception>
/// <exception cref="ArgumentException">When the length of numbers do not equal <see cref="ArgumentCount"/>.</exception>
public double Divide(double[] numbers)
{
base.Validate(numbers);
double? result = null;
foreach (double n in numbers)
if (result.HasValue)
result /= n;
else
result = n;
return result ?? 0;
}
/// <summary>Modulo the specified numbers.</summary>
/// <param name="numbers">The numbers.</param>
/// <returns>The result of the operation.</returns>
/// <exception cref="ArgumentNullException">When numbers is null.</exception>
/// <exception cref="ArgumentException">When the length of numbers do not equal <see cref="ArgumentCount"/>.</exception>
public double Modulo(double[] numbers)
{
base.Validate(numbers);
double? result = null;
foreach (double n in numbers)
if (result.HasValue)
result %= n;
else
result = n;
return result ?? 0;
}
/// <summary>Power for the specified numbers.</summary>
/// <param name="numbers">The numbers.</param>
/// <returns>The result of the operation.</returns>
/// <exception cref="ArgumentNullException">When numbers is null.</exception>
/// <exception cref="ArgumentException">When the length of numbers do not equal <see cref="ArgumentCount"/>.</exception>
public double Power(double[] numbers)
{
base.Validate(numbers);
return Math.Pow(numbers[0], numbers[1]);
}
/// <summary>Determines whether the specified string is a math symbol.</summary>
/// <param name="s">The string to check.</param>
/// <returns><c>true</c> if the specified string is a math symbol; otherwise, <c>false</c>.</returns>
[SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")]
public static bool IsSymbol(string s)
{
if (s == null || s.Length != 1)
return false;
char c = s[0];
return IsSymbol(c);
}
/// <summary>Determines whether the specified char is a math symbol.</summary>
/// <param name="c">The char to check.</param>
/// <returns><c>true</c> if the specified char is a math symbol; otherwise, <c>false</c>.</returns>
public static bool IsSymbol(char c)
{
return Array.Exists(operatorSymbols, delegate(char s) { return s == c; });
}
/// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </summary>
/// <returns>
/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </returns>
/// <filterPriority>2</filterPriority>
public override string ToString()
{
return _mathOperator.ToString();
}
}
}

View file

@ -0,0 +1,40 @@
using System;
using System.Runtime.Serialization;
namespace LoreSoft.MathExpressions
{
/// <summary>
/// The exception that is thrown when there is an error parsing a math expression.
/// </summary>
[Serializable]
public class ParseException : Exception
{
/// <summary>Initializes a new instance of the <see cref="ParseException"/> class.</summary>
public ParseException()
: base()
{ }
/// <summary>Initializes a new instance of the <see cref="ParseException"/> class.</summary>
/// <param name="message">The message.</param>
public ParseException(string message)
: base(message)
{ }
/// <summary>Initializes a new instance of the <see cref="ParseException"/> class.</summary>
/// <param name="message">The message.</param>
/// <param name="innerException">The inner exception.</param>
public ParseException(string message, Exception innerException)
: base(message, innerException)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="ParseException"/> class with serialized data.
/// </summary>
/// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
protected ParseException(SerializationInfo info, StreamingContext context)
: base(info, context)
{ }
}
}

View file

@ -0,0 +1,50 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.34209
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[assembly: System.Reflection.AssemblyTitle("LoreSoft.MathExpressions")]
[assembly: System.Reflection.AssemblyProduct("LoreSoft.MathExpressions")]
[assembly: System.Reflection.AssemblyDescription("Math Expression Parser.")]
[assembly: System.Reflection.AssemblyCompany("LoreSoft")]
[assembly: System.Reflection.AssemblyCopyright("Copyright © 2015 Paul Welter")]
[assembly: System.Runtime.InteropServices.Guid("348985f4-3fcf-4ec3-b207-6f09e918b297")]
[assembly: System.Runtime.InteropServices.ComVisible(false)]
[assembly: System.CLSCompliant(true)]
[assembly: System.Reflection.AssemblyConfiguration("Release")]
[assembly: System.Reflection.AssemblyVersion("1.2.0.0")]
[assembly: System.Reflection.AssemblyFileVersion("1.2.0.36")]
[assembly: System.Reflection.AssemblyInformationalVersion("1.2.0.36")]
internal sealed partial class ThisAssembly {
internal const string AssemblyTitle = "LoreSoft.MathExpressions";
internal const string AssemblyProduct = "LoreSoft.MathExpressions";
internal const string AssemblyDescription = "Math Expression Parser.";
internal const string AssemblyCompany = "LoreSoft";
internal const string AssemblyCopyright = "Copyright © 2015 Paul Welter";
internal const string Guid = "348985f4-3fcf-4ec3-b207-6f09e918b297";
internal const string AssemblyConfiguration = "Release";
internal const string AssemblyVersion = "1.2.0.0";
internal const string AssemblyFileVersion = "1.2.0.36";
internal const string AssemblyInformationalVersion = "1.2.0.36";
private ThisAssembly() {
}
}

View file

@ -0,0 +1,198 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace LoreSoft.MathExpressions.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LoreSoft.MathExpressions.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to The IExpression.Evaluate property can not be null..
/// </summary>
internal static string EvaluatePropertyCanNotBeNull {
get {
return ResourceManager.GetString("EvaluatePropertyCanNotBeNull", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The function name &apos;{0}&apos; is already registered..
/// </summary>
internal static string FunctionNameRegistered {
get {
return ResourceManager.GetString("FunctionNameRegistered", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid character: .
/// </summary>
internal static string InvalidCharacterEncountered {
get {
return ResourceManager.GetString("InvalidCharacterEncountered", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid convertion expression: .
/// </summary>
internal static string InvalidConvertionExpression {
get {
return ResourceManager.GetString("InvalidConvertionExpression", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid function name &apos;{0}&apos;..
/// </summary>
internal static string InvalidFunctionName {
get {
return ResourceManager.GetString("InvalidFunctionName", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid length of array..
/// </summary>
internal static string InvalidLengthOfArray {
get {
return ResourceManager.GetString("InvalidLengthOfArray", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid math expression..
/// </summary>
internal static string InvalidMathExpression {
get {
return ResourceManager.GetString("InvalidMathExpression", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid number format: .
/// </summary>
internal static string InvalidNumberFormat {
get {
return ResourceManager.GetString("InvalidNumberFormat", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid operator: .
/// </summary>
internal static string InvalidOperator {
get {
return ResourceManager.GetString("InvalidOperator", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid symbol on stack: .
/// </summary>
internal static string InvalidSymbolOnStack {
get {
return ResourceManager.GetString("InvalidSymbolOnStack", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid function or variable: .
/// </summary>
internal static string InvalidVariableEncountered {
get {
return ResourceManager.GetString("InvalidVariableEncountered", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid number for expression: .
/// </summary>
internal static string NotEnoughNumbers {
get {
return ResourceManager.GetString("NotEnoughNumbers", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unbalanced parentheses..
/// </summary>
internal static string UnbalancedParentheses {
get {
return ResourceManager.GetString("UnbalancedParentheses", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The variable name conflicts with function &apos;{0}&apos;..
/// </summary>
internal static string VariableNameConflict {
get {
return ResourceManager.GetString("VariableNameConflict", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The variable name can only contain letters..
/// </summary>
internal static string VariableNameContainsLetters {
get {
return ResourceManager.GetString("VariableNameContainsLetters", resourceCulture);
}
}
}
}

View file

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="InvalidLengthOfArray" xml:space="preserve">
<value>Invalid length of array.</value>
</data>
<data name="InvalidFunctionName" xml:space="preserve">
<value>Invalid function name '{0}'.</value>
</data>
<data name="EvaluatePropertyCanNotBeNull" xml:space="preserve">
<value>The IExpression.Evaluate property can not be null.</value>
</data>
<data name="FunctionNameRegistered" xml:space="preserve">
<value>The function name '{0}' is already registered.</value>
</data>
<data name="InvalidCharacterEncountered" xml:space="preserve">
<value>Invalid character: </value>
</data>
<data name="InvalidVariableEncountered" xml:space="preserve">
<value>Invalid function or variable: </value>
</data>
<data name="UnbalancedParentheses" xml:space="preserve">
<value>Unbalanced parentheses.</value>
</data>
<data name="InvalidNumberFormat" xml:space="preserve">
<value>Invalid number format: </value>
</data>
<data name="InvalidSymbolOnStack" xml:space="preserve">
<value>Invalid symbol on stack: </value>
</data>
<data name="InvalidOperator" xml:space="preserve">
<value>Invalid operator: </value>
</data>
<data name="VariableNameConflict" xml:space="preserve">
<value>The variable name conflicts with function '{0}'.</value>
</data>
<data name="VariableNameContainsLetters" xml:space="preserve">
<value>The variable name can only contain letters.</value>
</data>
<data name="InvalidMathExpression" xml:space="preserve">
<value>Invalid math expression.</value>
</data>
<data name="InvalidConvertionExpression" xml:space="preserve">
<value>Invalid convertion expression: </value>
</data>
<data name="NotEnoughNumbers" xml:space="preserve">
<value>Invalid number for expression: </value>
</data>
</root>

View file

@ -0,0 +1,75 @@
using LoreSoft.MathExpressions.Metadata;
namespace LoreSoft.MathExpressions.UnitConversion
{
/// <summary>Units for Length</summary>
public enum LengthUnit
{
/// <summary>Millimeter unit (mm)</summary>
[Abbreviation("mm")]
Millimeter = 0,
/// <summary>Centimeter unit (cm)</summary>
[Abbreviation("cm")]
Centimeter = 1,
/// <summary>Meter unit (m)</summary>
[Abbreviation("m")]
Meter = 2,
/// <summary>Kilometer unit (km)</summary>
[Abbreviation("km")]
Kilometer = 3,
/// <summary>Inch unit (in)</summary>
[Abbreviation("in")]
Inch = 4,
/// <summary>Feet unit (ft)</summary>
[Abbreviation("ft")]
Feet = 5,
/// <summary>Yard unit (yd)</summary>
[Abbreviation("yd")]
Yard = 6,
/// <summary>Mile unit (mile)</summary>
[Abbreviation("mile")]
Mile = 7
}
/// <summary>
/// Class representing length convertion.
/// </summary>
public static class LengthConverter
{
// In enum order
private static readonly double[] factors = new double[]
{
0.001d, //millimeter
0.01d, //centimeter
1d, //meter
1000d, //kilometer
0.3048d/12d, //inch
0.3048d, //feet
0.9144d, //yard
0.3048d*5280d, //mile
};
/// <summary>
/// Converts the specified from unit to the specified unit.
/// </summary>
/// <param name="fromUnit">Covert from unit.</param>
/// <param name="toUnit">Covert to unit.</param>
/// <param name="fromValue">Covert from value.</param>
/// <returns>The converted value.</returns>
public static double Convert(
LengthUnit fromUnit,
LengthUnit toUnit,
double fromValue)
{
if (fromUnit == toUnit)
return fromValue;
double fromFactor = factors[(int)fromUnit];
double toFactor = factors[(int) toUnit];
double result = fromFactor * fromValue / toFactor;
return result;
}
}
}

View file

@ -0,0 +1,65 @@
using LoreSoft.MathExpressions.Metadata;
namespace LoreSoft.MathExpressions.UnitConversion
{
/// <summary>Units for Mass</summary>
public enum MassUnit
{
/// <summary>Milligram unit (mg)</summary>
[Abbreviation("mg")]
Milligram = 0,
/// <summary>Gram unit (g)</summary>
[Abbreviation("g")]
Gram = 1,
/// <summary>Kilogram unit (kg)</summary>
[Abbreviation("kg")]
Kilogram = 2,
/// <summary>Ounce unit (oz)</summary>
[Abbreviation("oz")]
Ounce = 3,
/// <summary>Pound unit (lb)</summary>
[Abbreviation("lb")]
Pound = 4,
/// <summary>Ton unit (ton)</summary>
[Abbreviation("ton")]
Ton = 5,
}
/// <summary>
/// Class representing mass convertion.
/// </summary>
public static class MassConverter
{
// In enum order
private static readonly double[] factors = new double[]
{
0.000001d, //milligram
0.001d, //gram
1d, //kilogram
0.45359237d/16d, //ounce
0.45359237d, //pound
0.45359237d*2000d, //ton [short, US]
};
/// <summary>
/// Converts the specified from unit to the specified unit.
/// </summary>
/// <param name="fromUnit">Covert from unit.</param>
/// <param name="toUnit">Covert to unit.</param>
/// <param name="fromValue">Covert from value.</param>
/// <returns>The converted value.</returns>
public static double Convert(
MassUnit fromUnit,
MassUnit toUnit,
double fromValue)
{
if (fromUnit == toUnit)
return fromValue;
double fromFactor = factors[(int)fromUnit];
double toFactor = factors[(int)toUnit];
double result = fromFactor * fromValue / toFactor;
return result;
}
}
}

View file

@ -0,0 +1,71 @@
using LoreSoft.MathExpressions.Metadata;
using System.ComponentModel;
namespace LoreSoft.MathExpressions.UnitConversion
{
/// <summary>Units for Speed</summary>
public enum SpeedUnit
{
/// <summary>Meter/Second unit (m/s)</summary>
[Abbreviation("m/s")]
[Description("Meter/Second")]
MeterPerSecond = 0,
/// <summary>Kilometer/Hour unit (kph)</summary>
[Abbreviation("kph")]
[Description("Kilometer/Hour")]
KilometerPerHour = 1,
/// <summary>Foot/Second unit (ft/s)</summary>
[Abbreviation("ft/s")]
[Description("Foot/Second")]
FootPerSecond = 2,
/// <summary>Mile/Hour unit (mph)</summary>
[Abbreviation("mph")]
[Description("Mile/Hour")]
MilePerHour = 3,
/// <summary>Knot unit (knot)</summary>
[Abbreviation("knot")]
Knot = 4,
/// <summary>Mach unit (mach)</summary>
[Abbreviation("mach")]
Mach = 5,
}
/// <summary>
/// Class representing speed convertion.
/// </summary>
public static class SpeedConverter
{
// In enum order
private static readonly double[] factors = new double[]
{
1d, //meter/second
1000d/3600d, //kilometer/hour
0.3048d, //foot/second
(0.3048d*5280d)/3600d, //mile/hour (mph)
1852d/3600d, //knot
340.29d, //mach
};
/// <summary>
/// Converts the specified from unit to the specified unit.
/// </summary>
/// <param name="fromUnit">Covert from unit.</param>
/// <param name="toUnit">Covert to unit.</param>
/// <param name="fromValue">Covert from value.</param>
/// <returns>The converted value.</returns>
public static double Convert(
SpeedUnit fromUnit,
SpeedUnit toUnit,
double fromValue)
{
if (fromUnit == toUnit)
return fromValue;
double fromFactor = factors[(int)fromUnit];
double toFactor = factors[(int)toUnit];
double result = fromFactor * fromValue / toFactor;
return result;
}
}
}

View file

@ -0,0 +1,67 @@
using LoreSoft.MathExpressions.Metadata;
namespace LoreSoft.MathExpressions.UnitConversion
{
/// <summary>Units for Temperature</summary>
public enum TemperatureUnit
{
/// <summary>Degrees Celsius unit (c)</summary>
[Abbreviation("c")]
Celsius = 0,
/// <summary>Degrees Fahrenheit unit (f)</summary>
[Abbreviation("f")]
Fahrenheit = 1,
/// <summary>Degrees Kelvin unit (k)</summary>
[Abbreviation("k")]
Kelvin = 2
}
/// <summary>
/// Class representing temperature convertion.
/// </summary>
public static class TemperatureConverter
{
/// <summary>
/// Converts the specified from unit to the specified unit.
/// </summary>
/// <param name="fromUnit">Covert from unit.</param>
/// <param name="toUnit">Covert to unit.</param>
/// <param name="fromValue">Covert from value.</param>
/// <returns>The converted value.</returns>
public static double Convert(
TemperatureUnit fromUnit,
TemperatureUnit toUnit,
double fromValue)
{
if (fromUnit == toUnit)
return fromValue;
double result = 0;
if (fromUnit == TemperatureUnit.Celsius)
{
if (toUnit == TemperatureUnit.Kelvin)
result = fromValue + 273.15d;
else if (toUnit == TemperatureUnit.Fahrenheit)
//(9/5 * C) + 32 = F
result = (9.0d/5.0d*fromValue) + 32d;
}
else if (fromUnit == TemperatureUnit.Kelvin)
{
if (toUnit == TemperatureUnit.Celsius)
result = fromValue - 273.15d;
else if (toUnit == TemperatureUnit.Fahrenheit)
result = 5.0d/9.0d*((fromValue - 273.15d) + 32d);
}
else if (fromUnit == TemperatureUnit.Fahrenheit)
{
if (toUnit == TemperatureUnit.Celsius)
//(F - 32) * 5/9 = C
result = 5.0d/9.0d*(fromValue - 32d);
else if (toUnit == TemperatureUnit.Kelvin)
result = (5.0d/9.0d*(fromValue - 32d)) + 273.15;
}
return result;
}
}
}

View file

@ -0,0 +1,94 @@
using System;
using LoreSoft.MathExpressions.Metadata;
namespace LoreSoft.MathExpressions.UnitConversion
{
/// <summary>Units for Time</summary>
public enum TimeUnit
{
/// <summary>Millisecond unit (ms)</summary>
[Abbreviation("ms")]
Millisecond = 0,
/// <summary>Second unit (sec)</summary>
[Abbreviation("sec")]
Second = 1,
/// <summary>Minute unit (min)</summary>
[Abbreviation("min")]
Minute = 2,
/// <summary>Hour unit (hr)</summary>
[Abbreviation("hr")]
Hour = 3,
/// <summary>Day unit (d)</summary>
[Abbreviation("d")]
Day = 4,
/// <summary>Week unit (wk)</summary>
[Abbreviation("wk")]
Week = 5
}
/// <summary>
/// Class representing time convertion.
/// </summary>
public static class TimeConverter
{
/// <summary>
/// Converts the specified from unit to the specified unit.
/// </summary>
/// <param name="fromUnit">Covert from unit.</param>
/// <param name="toUnit">Covert to unit.</param>
/// <param name="fromValue">Covert from value.</param>
/// <returns>The converted value.</returns>
public static double Convert(
TimeUnit fromUnit,
TimeUnit toUnit,
double fromValue)
{
if (fromUnit == toUnit)
return fromValue;
TimeSpan span;
switch (fromUnit)
{
case TimeUnit.Millisecond:
span = TimeSpan.FromMilliseconds(fromValue);
break;
case TimeUnit.Second:
span = TimeSpan.FromSeconds(fromValue);
break;
case TimeUnit.Minute:
span = TimeSpan.FromMinutes(fromValue);
break;
case TimeUnit.Hour:
span = TimeSpan.FromHours(fromValue);
break;
case TimeUnit.Day:
span = TimeSpan.FromDays(fromValue);
break;
case TimeUnit.Week:
span = TimeSpan.FromDays(fromValue * 7d);
break;
default:
throw new ArgumentOutOfRangeException("fromUnit");
}
switch (toUnit)
{
case TimeUnit.Millisecond:
return span.TotalMilliseconds;
case TimeUnit.Second:
return span.TotalSeconds;
case TimeUnit.Minute:
return span.TotalMinutes;
case TimeUnit.Hour:
return span.TotalHours;
case TimeUnit.Day:
return span.TotalDays;
case TimeUnit.Week:
return span.TotalDays / 7d;
default:
throw new ArgumentOutOfRangeException("toUnit");
}
}
}
}

View file

@ -0,0 +1,19 @@
namespace LoreSoft.MathExpressions.UnitConversion
{
///<summary>The unit types available for conversion.</summary>
public enum UnitType
{
///<summary>Length unit types.</summary>
Length,
///<summary>Mass unit types.</summary>
Mass,
///<summary>Speed unit types.</summary>
Speed,
///<summary>Temperature unit types.</summary>
Temperature,
///<summary>Time unit types.</summary>
Time,
///<summary>Volume unit types.</summary>
Volume
}
}

View file

@ -0,0 +1,74 @@
using LoreSoft.MathExpressions.Metadata;
using System.ComponentModel;
namespace LoreSoft.MathExpressions.UnitConversion
{
/// <summary>Units for Liquid Volume</summary>
public enum VolumeUnit
{
/// <summary>Milliliter unit (ml)</summary>
[Abbreviation("ml")]
Milliliter = 0,
/// <summary>Liter unit (l)</summary>
[Abbreviation("l")]
Liter = 1,
/// <summary>Kiloliter unit (kl)</summary>
[Abbreviation("kl")]
Kiloliter = 2,
/// <summary>Fluid ounce unit (oz)</summary>
[Abbreviation("oz")]
[Description("Fluid Ounce")]
FluidOunce = 3,
/// <summary>Cup unit (cup)</summary>
[Abbreviation("cup")]
Cup = 4,
/// <summary>Pint unit (pt)</summary>
[Abbreviation("pt")]
Pint = 5,
/// <summary>Quart unit (qt)</summary>
[Abbreviation("qt")]
Quart = 6,
/// <summary>Gallon unit (gal)</summary>
[Abbreviation("gal")]
Gallon = 7
}
/// <summary>
/// Class representing liquid volume convertion.
/// </summary>
public static class VolumeConverter
{
// In enum order
private static readonly double[] factors = new double[]
{
0.000001d, //milliliter
0.001d, //liter
1d, //kiloliter
0.0037854118d/128d, //ounce [US, liquid]
0.0037854118d/16d, //cup [US]
0.0037854118d/8d, //pint [US, liquid]
0.0037854118d/4d, //quart [US, liquid]
0.0037854118d, //gallon [US, liquid]
};
/// <summary>
/// Converts the specified from unit to the specified unit.
/// </summary>
/// <param name="fromUnit">Covert from unit.</param>
/// <param name="toUnit">Covert to unit.</param>
/// <param name="fromValue">Covert from value.</param>
/// <returns>The converted value.</returns>
public static double Convert(
VolumeUnit fromUnit,
VolumeUnit toUnit,
double fromValue)
{
if (fromUnit == toUnit)
return fromValue;
double fromFactor = factors[(int)fromUnit];
double toFactor = factors[(int)toUnit];
double result = fromFactor * fromValue / toFactor;
return result;
}
}
}

View file

@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using LoreSoft.MathExpressions.Properties;
using System.Globalization;
using System.Runtime.Serialization;
using System.Security.Permissions;
namespace LoreSoft.MathExpressions
{
/// <summary>
/// Class representing a collection of variable names and values.
/// </summary>
/// <remarks>
/// Variable names can only contain letters, numbers and symbols are not allowed.
/// </remarks>
[Serializable]
public class VariableDictionary : Dictionary<string, double>
{
private MathEvaluator _evaluator;
/// <summary>Initializes a new instance of the <see cref="VariableDictionary"/> class.</summary>
/// <param name="evaluator">The evaluator.</param>
internal VariableDictionary(MathEvaluator evaluator)
: base(StringComparer.OrdinalIgnoreCase)
{
_evaluator = evaluator;
base.Add(MathEvaluator.AnswerVariable, 0);
base.Add("pi", Math.PI);
base.Add("e", Math.E);
}
/// <summary>
/// Initializes a new instance of the <see cref="VariableDictionary"/> class.
/// </summary>
/// <param name="info">A <see cref="T:System.Runtime.Serialization.SerializationInfo"/> object containing the information required to serialize the <see cref="T:System.Collections.Generic.Dictionary`2"/>.</param>
/// <param name="context">A <see cref="T:System.Runtime.Serialization.StreamingContext"/> structure containing the source and destination of the serialized stream associated with the <see cref="T:System.Collections.Generic.Dictionary`2"/>.</param>
protected VariableDictionary(SerializationInfo info, StreamingContext context)
: base(info, context)
{ }
/// <summary>Adds the specified variable and value to the dictionary.</summary>
/// <param name="name">The name of the variable to add.</param>
/// <param name="value">The value of the variable.</param>
/// <exception cref="ArgumentNullException">When variable name is null.</exception>
/// <exception cref="ArgumentException">When variable name contains non-letters or the name exists in the <see cref="MathEvaluator.Functions"/> list.</exception>
/// <seealso cref="MathEvaluator"/>
/// <seealso cref="MathEvaluator.Variables"/>
/// <seealso cref="MathEvaluator.Functions"/>
public new void Add(string name, double value)
{
Validate(name);
base.Add(name, value);
}
private void Validate(string name)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentNullException("name");
if (_evaluator.IsFunction(name))
throw new ArgumentException(
string.Format(CultureInfo.CurrentCulture,
Resources.VariableNameConflict, name), "name");
for (int i = 0; i < name.Length; i++)
if (!char.IsLetter(name[i]))
throw new ArgumentException(Resources.VariableNameContainsLetters, "name");
}
/// <summary>
/// Implements the <see cref="T:System.Runtime.Serialization.ISerializable"/> interface and returns the data needed to serialize the <see cref="T:System.Collections.Generic.Dictionary`2"/> instance.
/// </summary>
/// <param name="info">A <see cref="T:System.Runtime.Serialization.SerializationInfo"/> object that contains the information required to serialize the <see cref="T:System.Collections.Generic.Dictionary`2"/> instance.</param>
/// <param name="context">A <see cref="T:System.Runtime.Serialization.StreamingContext"/> structure that contains the source and destination of the serialized stream associated with the <see cref="T:System.Collections.Generic.Dictionary`2"/> instance.</param>
[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
}
}
}

View file

@ -1,5 +1,5 @@
using Shared.Models;
using Shared.ZatVdfParser;
using SteamShared.Models;
using SteamShared.ZatVdfParser;
using System;
using System.Collections.Generic;
using System.Globalization;
@ -9,7 +9,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared
namespace SteamShared
{
public class CsgoHelper
{

View file

@ -5,7 +5,7 @@ using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.IO;
namespace Shared
namespace SteamShared
{
#region DDSImage Class
public class DDSImage : IDisposable

View file

@ -7,7 +7,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
namespace Shared
namespace SteamShared
{
public static class Globals
{

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
internal class BSPHeader
{

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
internal class BSPLump
{

View file

@ -6,7 +6,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
namespace Shared.Models
namespace SteamShared.Models
{
public class CsgoMap
{

View file

@ -6,7 +6,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
namespace Shared.Models
namespace SteamShared.Models
{
public class CsgoWeapon
{

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
public class MapCustomOverwriteMapping
{

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
public class MapPoint
{

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
public class NavApproachSpot
{

View file

@ -5,7 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
public class NavArea
{

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
public class NavAreaBind
{

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
public class NavConnectionData
{

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
public class NavEncounterPath
{

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
public class NavEncounterSpot
{

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
public class NavHeader
{

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
[Flags]
public enum HidingSpotAttribute

View file

@ -5,7 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
public class NavLadder
{

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
public class NavLadderIDSequence
{

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
public class NavMesh
{

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
public class PlayerSpawn
{

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
public class Settings
{

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
public class SteamGame
{

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
public class SteamLibrary
{

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shared.Models
namespace SteamShared.Models
{
public class Vector3
{

View file

@ -4,9 +4,9 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Shared.Models;
using SteamShared.Models;
namespace Shared
namespace SteamShared
{
public enum NavDisplayModes { None, Wireframe, Filled }

View file

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SteamShared
{
public class SourceCFG
{
}
}

View file

@ -4,11 +4,11 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Shared.ZatVdfParser;
using SteamShared.ZatVdfParser;
using Microsoft.Win32;
using Shared.Models;
using SteamShared.Models;
namespace Shared
namespace SteamShared
{
public class SteamHelper
{
@ -75,7 +75,7 @@ namespace Shared
}
/// <summary>
/// Forcefully tries to update the <see cref="SteamPath"/> property with the current Steam path, even if it should be already set.
/// Forcefully tries to update the <see cref="SteamLibraries"/> property with the current Steam libraries, even if they should be already set.
/// </summary>
public void UpdateSteamLibraries()
{

View file

@ -1,10 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>
<RuntimeHostConfigurationOption Include="System.Globalization.UseNls" Value="true" />

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.IO;
namespace Shared.ZatVdfParser
namespace SteamShared.ZatVdfParser
{
/// <summary>
/// This class was copied from <see cref="https://stackoverflow.com/questions/39065573/reading-values-from-an-acf-manifest-file">here</see> as a backup, in case <see cref="VDFFile"/> didn't work well enough.

View file

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Shared.ZatVdfParser
namespace SteamShared.ZatVdfParser
{
public class Element
{

View file

@ -5,7 +5,7 @@ using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace Shared.ZatVdfParser
namespace SteamShared.ZatVdfParser
{
public class VDFFile
{
@ -55,7 +55,7 @@ namespace Shared.ZatVdfParser
string? line = null;
while ((line = reader.ReadLine()) != null)
{
if (line.StartsWith("\0"))
if (line.StartsWith("\0", StringComparison.Ordinal))
return;
line = line.Trim();