diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..dac79c6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,239 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+*.db
+*.VC.db
+*.VC.VC.opendb
+
+# Executables
+*.dll
+[Dd]ependencies/*
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+build/
+bld/
+[Bb]in/
+[Oo]bj/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# DNX
+project.lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+
+# Windows Azure Build Output
+csx/
+*.build.csdef
+
+# Windows Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directory
+AppPackages/
+BundleArtifacts/
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+node_modules/
+orleans.codegen.cs
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+
+# FAKE - F# Make
+.fake/
\ No newline at end of file
diff --git a/App.config b/App.config
index 41dc385..4207fd0 100644
--- a/App.config
+++ b/App.config
@@ -1,6 +1,6 @@
-
+
diff --git a/CharacterProfile.cs b/CharacterProfile.cs
index c369f65..009d886 100644
--- a/CharacterProfile.cs
+++ b/CharacterProfile.cs
@@ -19,6 +19,14 @@ limitations under the License.
using System.Windows.Media;
using HighVoltz.HBRelog.Tasks;
using HighVoltz.HBRelog.Settings;
+using System.Xml.Linq;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Linq;
+using System.Xml.Serialization;
+using System.Globalization;
+using System.Diagnostics;
namespace HighVoltz.HBRelog
{
@@ -142,7 +150,11 @@ public void Stop()
private string _lastLog;
public void Log(string format, params object[] args)
{
- var msg = string.Format(format, args);
+ Log(string.Format(format, args));
+ }
+
+ public void Log(string msg)
+ {
if (msg == _lastLog)
return;
_lastLog = msg;
@@ -154,9 +166,14 @@ public void Log(string format, params object[] args)
}
public void Err(string format, params object[] args)
+ {
+ Err(string.Format(format, args));
+ }
+
+ public void Err(string msg)
{
HBRelog.Log.Write(HbRelogManager.Settings.UseDarkStyle ? Colors.LightBlue : Colors.DarkSlateBlue,
- Settings.ProfileName + ": ", Colors.Red, format, args);
+ Settings.ProfileName + ": ", Colors.Red, msg);
}
public CharacterProfile ShadowCopy()
@@ -167,17 +184,88 @@ public CharacterProfile ShadowCopy()
{
cp.Tasks.Add(bmTask.ShadowCopy());
}
- cp.Settings = Settings.ShadowCopy();
+ cp.Settings = (ProfileSettings)Settings.ShadowCopy();
return cp;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
- if (PropertyChanged != null)
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
+ }
+
+ public void LoadFromXml(XElement element)
+ {
+ Settings.LoadFromXml(element.Element("Settings"));
+
+ // Tasks
+ XElement tasksElement = element.Element("Tasks");
+ foreach (XElement taskElement in tasksElement.Elements())
+ {
+ Type taskType = Type.GetType("HighVoltz.HBRelog.Tasks." + taskElement.Name);
+ if (taskType != null)
+ {
+ var task = (BMTask)Activator.CreateInstance(taskType);
+ task.SetProfile(this);
+ // Dictionary of property Names and the corresponding PropertyInfo
+ Dictionary propertyDict =
+ task.GetType()
+ .GetProperties()
+ .Where(pi => pi.GetCustomAttributesData().All(cad => cad.Constructor.DeclaringType != typeof(XmlIgnoreAttribute)))
+ .ToDictionary(k => k.Name);
+
+ foreach (XAttribute attr in taskElement.Attributes())
+ {
+ string propKey = attr.Name.ToString();
+ if (propertyDict.ContainsKey(propKey))
+ {
+ // if property is an enum then use Enum.Parse.. otherwise use Convert.ChangeValue
+ object val = typeof(Enum).IsAssignableFrom(propertyDict[propKey].PropertyType)
+ ? Enum.Parse(propertyDict[propKey].PropertyType, attr.Value)
+ : Convert.ChangeType(attr.Value, propertyDict[propKey].PropertyType, CultureInfo.InvariantCulture);
+ propertyDict[propKey].SetValue(task, val, null);
+ }
+ else
+ {
+ Err("{0} does not have a property called {1}", taskElement.Name, attr.Name);
+ }
+ }
+ Tasks.Add(task);
+ }
+ else
+ {
+ Err("{0} is not a known task type", taskElement.Name);
+ }
+ }
+ }
+
+ public XElement ConvertToXml()
+ {
+ var xml = new XElement("CharacterProfile");
+ var settingsElement = Settings.ConvertToXml();
+ xml.Add(settingsElement);
+
+ // Tasks
+ var tasksElement = new XElement("Tasks");
+ foreach (BMTask task in Tasks)
{
- PropertyChanged(this, new PropertyChangedEventArgs(name));
+ var taskElement = new XElement(task.GetType().Name);
+ // get a list of propertyes that don't have [XmlIgnore] custom attribute attached.
+ List propertyList =
+ task.GetType()
+ .GetProperties()
+ .Where(pi => pi.GetCustomAttributesData().All(cad => cad.Constructor.DeclaringType != typeof(XmlIgnoreAttribute)))
+ .ToList();
+ foreach (PropertyInfo property in propertyList)
+ {
+ var value = property.GetValue(task, null);
+ Debug.Assert(value != null, string.Format("value for {0} != null", property.Name));
+ taskElement.Add(new XAttribute(property.Name, value));
+ }
+ tasksElement.Add(taskElement);
}
+ xml.Add(tasksElement);
+ return xml;
}
}
}
diff --git a/Controls/OptionsUserControl.xaml b/Controls/OptionsUserControl.xaml
index b87e767..414dea8 100644
--- a/Controls/OptionsUserControl.xaml
+++ b/Controls/OptionsUserControl.xaml
@@ -31,6 +31,7 @@
+
-
-
-
-
+
+
+
+
+
-
-
+
-
-
diff --git a/Controls/OptionsUserControl.xaml.cs b/Controls/OptionsUserControl.xaml.cs
index a132a52..3be4758 100644
--- a/Controls/OptionsUserControl.xaml.cs
+++ b/Controls/OptionsUserControl.xaml.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
@@ -28,7 +29,8 @@ public OptionsUserControl()
private void DarkStyleCheckCheckedChanged(object sender, RoutedEventArgs e)
{
- MainWindow.Instance.LoadStyle();
+ if (!DesignerProperties.GetIsInDesignMode(this))
+ MainWindow.Instance.LoadStyle();
}
private void ImportSettingsButton_Click(object sender, RoutedEventArgs e)
@@ -41,23 +43,13 @@ private void ImportSettingsButton_Click(object sender, RoutedEventArgs e)
};
if (ofd.ShowDialog() == true)
{
- HbRelogManager.Settings = GlobalSettings.Import(ofd.FileName);
+ GlobalSettings.Instance.Import(ofd.FileName);
// re-assign the data context for main window.
MainWindow.Instance.DataContext = HbRelogManager.Settings.CharacterProfiles;
- var options = (Controls.OptionsUserControl)MainWindow.Instance.FindName("HbrelogOptions");
+ var options = (OptionsUserControl)MainWindow.Instance.FindName("HbrelogOptions");
options.DataContext = HbRelogManager.Settings;
- var accountConfig = (Controls.AccountConfigUserControl) MainWindow.Instance.FindName("AccountConfig");
- var taskList = (ListBox)accountConfig.FindName("ProfileTaskList");
-
- //HbRelogManager.Settings.CharacterProfiles[0].Tasks.
- //ItemsSource="{Binding CharacterProfiles/Tasks, Source={x:Static HighVoltz:HbRelogManager.Settings}}
- //taskList.ItemsSource = HbRelogManager.Settings.CharacterProfiles;
- //taskList.ItemsSource = HbRelogManager.Settings.CharacterProfiles;
-
- // var a = (CheckBox) MainWindow.Instance.FindName("ProfileEnabledCheckBox");
- // a.DataContext = HbRelogManager.Settings.CharacterProfiles[0].Settings
HbRelogManager.Settings.QueueSave();
}
}
@@ -73,9 +65,8 @@ private void ExportSettingsButton_Click(object sender, RoutedEventArgs e)
if (sfd.ShowDialog() == true)
{
var settings = HbRelogManager.Settings.Export(sfd.FileName);
- settings.Save();
+ settings.Save(sfd.FileName);
}
}
-
}
}
diff --git a/Controls/ProfileEditUserControl.xaml b/Controls/ProfileEditUserControl.xaml
index 7e0801b..7717016 100644
--- a/Controls/ProfileEditUserControl.xaml
+++ b/Controls/ProfileEditUserControl.xaml
@@ -90,7 +90,7 @@
DefaultExt=".exe" Filter=".exe|*.exe"
ToolTip="The path to Wow.exe. click the '...' button on the right to browse to it. This can also be a batch file that launches WoW.exe"/>
-
+
diff --git a/Controls/ProfileEditUserControl.xaml.cs b/Controls/ProfileEditUserControl.xaml.cs
index 5932747..aa86513 100644
--- a/Controls/ProfileEditUserControl.xaml.cs
+++ b/Controls/ProfileEditUserControl.xaml.cs
@@ -135,9 +135,9 @@ private void WowWindowGrabTextClick(object sender, RoutedEventArgs e)
if (MainWindow.Instance.AccountGrid.SelectedItem != null)
{
CharacterProfile profile = (CharacterProfile)MainWindow.Instance.AccountGrid.SelectedItem;
- if (profile.TaskManager.WowManager.Memory != null)
+ if (profile.TaskManager.WowManager.GameWindow != IntPtr.Zero)
{
- var Rect = Utility.GetWindowRect(profile.TaskManager.WowManager.GameProcess.MainWindowHandle);
+ var Rect = Utility.GetWindowRect(profile.TaskManager.WowManager.GameWindow);
profile.Settings.WowSettings.WowWindowX = Rect.Left;
profile.Settings.WowSettings.WowWindowY = Rect.Top;
profile.Settings.WowSettings.WowWindowWidth = Rect.Right - Rect.Left;
@@ -226,15 +226,15 @@ private void WowWindowRatioButtonClick(object sender, RoutedEventArgs e)
if (MainWindow.Instance.AccountGrid.SelectedItem != null)
{
CharacterProfile profile = (CharacterProfile)MainWindow.Instance.AccountGrid.SelectedItem;
- if (profile.TaskManager.WowManager.Memory != null)
+ if (profile.TaskManager.WowManager.GameWindow != IntPtr.Zero)
{
- var screen = Screen.FromHandle(profile.TaskManager.WowManager.GameProcess.MainWindowHandle);
+ var screen = Screen.FromHandle(profile.TaskManager.WowManager.GameWindow);
try
{
int denominator = int.Parse(WowWindowRatioText.Text);
profile.Settings.WowSettings.WowWindowWidth = screen.Bounds.Width / denominator;
profile.Settings.WowSettings.WowWindowHeight = screen.Bounds.Height / denominator;
- Utility.ResizeAndMoveWindow(profile.TaskManager.WowManager.GameProcess.MainWindowHandle, profile.Settings.WowSettings.WowWindowX,
+ Utility.ResizeAndMoveWindow(profile.TaskManager.WowManager.GameWindow, profile.Settings.WowSettings.WowWindowX,
profile.Settings.WowSettings.WowWindowY, profile.Settings.WowSettings.WowWindowWidth, profile.Settings.WowSettings.WowWindowHeight);
}
catch { }
diff --git a/Controls/TaskEditor.xaml.cs b/Controls/TaskEditor.xaml.cs
index ff01a51..4aa9b7d 100644
--- a/Controls/TaskEditor.xaml.cs
+++ b/Controls/TaskEditor.xaml.cs
@@ -16,6 +16,7 @@
using System.Xml.Serialization;
using HighVoltz.HBRelog.Converters;
using HighVoltz.HBRelog.Tasks;
+using System.Globalization;
namespace HighVoltz.HBRelog.Controls
{
@@ -43,7 +44,7 @@ void SourceChanged(object source)
BMTask task = (BMTask)source;
List propertyList = task.GetType().GetProperties().
- Where(pi => pi.GetCustomAttributesData().All(cad => cad.Constructor.DeclaringType != typeof(XmlIgnoreAttribute))).ToList();
+ Where(pi => pi.GetCustomAttributesData().All(cad => cad.AttributeType != typeof(XmlIgnoreAttribute))).ToList();
PropertyGrid.Children.Clear();
PropertyGrid.RowDefinitions.Clear();
for (int index = 0; index < propertyList.Count; index++)
@@ -128,7 +129,7 @@ void TaskPropertyChanged(object sender, EventArgs e)
string str = ((TextBox)sender).Text;
try
{
- object val = Convert.ChangeType(str, pi.PropertyType);
+ object val = Convert.ChangeType(str, pi.PropertyType, CultureInfo.InvariantCulture);
pi.SetValue(task, val, null);
} // in case the type conversion fails fall back to default value.
catch (FormatException)
diff --git a/FiniteStateMachine/Engine.cs b/FiniteStateMachine/Engine.cs
index 5b2b903..6ad7e75 100644
--- a/FiniteStateMachine/Engine.cs
+++ b/FiniteStateMachine/Engine.cs
@@ -23,8 +23,7 @@ protected set
_states = value;
// Remember: We implemented the IComparer, and IComparable
// interfaces on the State class!
- if (_states != null)
- _states.Sort();
+ _states?.Sort((s1, s2) => s2.Priority.CompareTo(s1.Priority));
}
}
diff --git a/FiniteStateMachine/State.cs b/FiniteStateMachine/State.cs
index 53b4854..07cf759 100644
--- a/FiniteStateMachine/State.cs
+++ b/FiniteStateMachine/State.cs
@@ -5,25 +5,12 @@
namespace HighVoltz.HBRelog.FiniteStateMachine
{
- public abstract class State : IComparable, IComparer
+ public abstract class State
{
public abstract int Priority { get; }
public abstract bool NeedToRun { get; }
public abstract void Run();
-
- public int CompareTo(State other)
- {
- // We want the highest first.
- // int, by default, chooses the lowest to be sorted
- // at the bottom of the list. We want the opposite.
- return -Priority.CompareTo(other.Priority);
- }
-
- public int Compare(State x, State y)
- {
- return -x.Priority.CompareTo(y.Priority);
- }
}
}
diff --git a/HBPlugin/HBPlugin.csproj b/HBPlugin/HBPlugin.csproj
index 423a5cb..d07a787 100644
--- a/HBPlugin/HBPlugin.csproj
+++ b/HBPlugin/HBPlugin.csproj
@@ -8,7 +8,7 @@
Properties
HighVoltz.HBRelogHelper
HBRelogHelper
- v4.5.1
+ v4.6.1
512
@@ -20,26 +20,6 @@
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
- false
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
- AnyCPU
- false
-
true
bin\x86\Debug\
@@ -75,8 +55,9 @@
false
+
- ..\..\..\Honorbuddy\Honorbuddy.exe
+ ..\Dependencies\Honorbuddy.exe
@@ -84,6 +65,7 @@
+
@@ -94,8 +76,10 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Program.cs b/Program.cs
index c1b56dc..f0005b2 100644
--- a/Program.cs
+++ b/Program.cs
@@ -24,72 +24,189 @@ limitations under the License.
using System.Linq;
using System.Reflection;
using System.IO;
+using Microsoft.Win32;
+using HighVoltz.HBRelog.Settings;
+using System.CodeDom.Compiler;
+using System.Security.Principal;
+using System.Security.AccessControl;
namespace HighVoltz.HBRelog
{
public class Program
{
- static Dictionary CmdLineArgs = new Dictionary();
+ private static Dictionary s_cmdLineArgs = new Dictionary();
+ internal static string UserSettingsPath { get; private set; }
+
[STAThread]
public static void Main(params string[] args)
{
- bool newInstance;
- using (Mutex m = new Mutex(true, "HBRelog", out newInstance))
+ var baseDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
+
+ // Run under 'System' user
+ using (var identity = WindowsIdentity.GetCurrent())
{
- if (newInstance)
+ if (!identity.IsSystem)
{
- AppDomain.CurrentDomain.ProcessExit += CurrentDomainProcessExit;
- AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
-
- CmdLineArgs = ProcessCmdLineArgs(args);
- if (CmdLineArgs.ContainsKey("AUTOSTART"))
- HbRelogManager.Settings.AutoStart = true;
- if (CmdLineArgs.ContainsKey("WOWDELAY"))
- HbRelogManager.Settings.WowDelay = GetCmdLineArgVal(CmdLineArgs["WOWDELAY"]);
- if (CmdLineArgs.ContainsKey("HBDELAY"))
- HbRelogManager.Settings.HBDelay = GetCmdLineArgVal(CmdLineArgs["HBDELAY"]);
-
- var app = new Application();
- Window window = new MainWindow();
- window.Show();
- app.Run(window);
+ // We need to pass the HBRlog path for logged-in user before we relaunch to System user.
+ var userSettingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
+ "HighVoltz\\HBRelog\\Settings.xml");
+ args = new string[] { userSettingsPath }.Concat(args).ToArray();
+ var hbrelogArgs = string.Join(" ", args.Select(a => $"\"{a}\""));
+ ProcessStartInfo psi = new ProcessStartInfo();
+ psi.Arguments = $"-accepteula -nobanner -i -s \"{Assembly.GetEntryAssembly().Location}\" {hbrelogArgs}";
+ psi.UseShellExecute = false;
+ psi.CreateNoWindow = true;
+ psi.FileName = Path.Combine(baseDirectory, "Tools", "PsExec.exe");
+ Utility.UnblockFileIfZoneRestricted(psi.FileName);
+ Process.Start(psi);
+ return;
}
- else
+ }
+
+ // Use a loader to hide assembly/process name
+ if (AppDomain.CurrentDomain.IsDefaultAppDomain())
+ {
+ var loaderName = GetLoaderName();
+ var cachePath = Path.Combine(baseDirectory, "Cache");
+ if (!Directory.Exists(cachePath))
+ Directory.CreateDirectory(cachePath);
+
+ var loaderPath = Path.Combine(cachePath, loaderName + ".exe");
+ var launcherPath = Path.Combine(baseDirectory, "Tools", "Launcher.exe");
+ if (!File.Exists(loaderPath)
+ || FileVersionInfo.GetVersionInfo(launcherPath).FileVersion != FileVersionInfo.GetVersionInfo(loaderPath).FileVersion)
{
- Process currentProc = Process.GetCurrentProcess();
- Process mutexOwner = Process.GetProcessesByName(currentProc.ProcessName).FirstOrDefault(p => p.Id != currentProc.Id);
- if (mutexOwner != null)
- {
- NativeMethods.SetForegroundWindow(mutexOwner.MainWindowHandle);
- }
+ Utility.UnblockFileIfZoneRestricted(launcherPath);
+ File.Copy(launcherPath, loaderPath, true);
+ // ToDO set permissions
+ EnsureLoaderPermissions(loaderPath);
}
+
+ // Grab and remove the user settings path from args array.
+ var userSettingsPath = args[0];
+ ProcessStartInfo psi = new ProcessStartInfo(loaderPath, string.Join(" ", args.Skip(1).Select(a => $"\"{a}\"")));
+ psi.UseShellExecute = false;
+ psi.CreateNoWindow = true;
+ psi.RedirectStandardInput = true;
+ psi.RedirectStandardOutput = false;
+ psi.WorkingDirectory = baseDirectory;
+ using (var proc = Process.Start(psi))
+ {
+ proc.StandardInput.WriteLine("HBRelog");
+ proc.StandardInput.WriteLine(Assembly.GetExecutingAssembly().Location);
+ proc.StandardInput.WriteLine(userSettingsPath);
+ }
+ return;
}
+
+ UserSettingsPath = (string)AppDomain.CurrentDomain.GetData("UserSettingsPath");
+ AppDomain.CurrentDomain.SetData("UserSettingsPath", null);
+
+ var mutexName = Fnv1($"{GlobalSettings.GetSettingsPath()}|HBRelog|{MachineGuid}").ToString();
+ using (Mutex m = new Mutex(true, mutexName, out bool newInstance))
+ {
+ if (!newInstance)
+ {
+ // ToDO find the mutex owner and bring the process to foreground
+ return;
+ }
+
+
+ AppDomain.CurrentDomain.ProcessExit += CurrentDomainProcessExit;
+ AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
+ AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
+
+ s_cmdLineArgs = ProcessCmdLineArgs(args);
+ if (s_cmdLineArgs.ContainsKey("AUTOSTART"))
+ HbRelogManager.Settings.AutoStart = true;
+ if (s_cmdLineArgs.ContainsKey("WOWDELAY"))
+ HbRelogManager.Settings.WowDelay = GetCmdLineArgVal(s_cmdLineArgs["WOWDELAY"]);
+ if (s_cmdLineArgs.ContainsKey("HBDELAY"))
+ HbRelogManager.Settings.HBDelay = GetCmdLineArgVal(s_cmdLineArgs["HBDELAY"]);
+
+ var app = new Application();
+ Window window = new MainWindow();
+ window.Show();
+ window.Activate();
+ app.Run(window);
+ }
+ }
+
+ private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
+ {
+ AssemblyName askedAssembly = new AssemblyName(args.Name);
+ string[] fields = args.Name.Split(',');
+ string name = fields[0];
+ string culture = fields[2];
+ // failing to ignore queries for satellite resource assemblies
+ // or using [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)]
+ // in AssemblyInfo.cs will crash the program on non en-US based system cultures.
+ if (name.EndsWith(".resources") && !culture.EndsWith("neutral"))
+ return null;
+
+ var path = Path.Combine(Utility.AssemblyDirectory, "Tools", name + ".exe");
+ if (File.Exists(path))
+ return Assembly.LoadFrom(path);
+
+ path = Path.Combine(Utility.AssemblyDirectory, "Tools", name + ".dll");
+ if (File.Exists(path))
+ return Assembly.LoadFrom(path);
+
+ path = Path.Combine(Utility.AssemblyDirectory, name + ".dll");
+ if (File.Exists(path))
+ return Assembly.LoadFrom(path);
+
+ return null;
}
- static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
+ private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
HbRelogManager.Shutdown();
}
- static void CurrentDomainProcessExit(object sender, EventArgs e)
+ private static void CurrentDomainProcessExit(object sender, EventArgs e)
{
HbRelogManager.Shutdown();
}
- static Dictionary ProcessCmdLineArgs(IEnumerable args)
+ internal static bool GetCommandLineArgument(string name, out T value)
+ {
+ value = default(T);
+ string stringVal;
+ if (!s_cmdLineArgs.TryGetValue(name.ToLowerInvariant(), out stringVal))
+ return false;
+
+ try
+ {
+ value = (T)Convert.ChangeType(stringVal, typeof(T));
+ }
+ catch (Exception ex)
+ {
+ Log.Err("Unable to convert {0} to type: {1}\n{2}", stringVal, typeof(T), ex);
+ return false;
+ }
+
+ return true;
+ }
+
+ internal static bool HasCommandLineSwitch(string name)
+ {
+ return s_cmdLineArgs.ContainsKey(name.ToLowerInvariant());
+ }
+
+ private static Dictionary ProcessCmdLineArgs(IEnumerable args)
{
var cmdLineArgs = new Dictionary();
foreach (string s in args)
{
- string[] tokens = s.Split('=');
- // make the / character optional
- string argName = (tokens[0][0] == '/' ? tokens[0].Substring(1) : tokens[0]).ToUpperInvariant();
+ string[] tokens = s.Split('=',':');
+ string argName = tokens[0].Replace("/", "").Replace("-", "").ToLowerInvariant();
cmdLineArgs.Add(argName, tokens.Length > 1 ? tokens[1] : "");
}
return cmdLineArgs;
}
- static T GetCmdLineArgVal(string arg)
+ private static T GetCmdLineArgVal(string arg)
{
try
{
@@ -101,5 +218,105 @@ static T GetCmdLineArgVal(string arg)
return default(T);
}
}
+
+ public static void EnsureLoaderPermissions(string path)
+ {
+ var fs = new FileSecurity();
+ fs.SetAccessRuleProtection(true, false);
+ fs.AddAccessRule(
+ new FileSystemAccessRule(
+ WindowsIdentity.GetCurrent().Name,
+ FileSystemRights.FullControl,
+ AccessControlType.Allow));
+
+ fs.AddAccessRule(
+ new FileSystemAccessRule(
+ "BUILTIN\\Administrators",
+ FileSystemRights.Delete,
+ AccessControlType.Allow));
+
+ File.SetAccessControl(path, fs);
+ }
+
+ private static bool CreateLoader(string loaderPath)
+ {
+ var baseDir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
+ var hbRelogPath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "..", "HBRelog.exe");
+ var src = @"
+using System;
+using System.IO;
+using System.Windows;
+using System.Reflection;
+namespace __Loader__
+{
+ public class Program
+ {
+ [STAThread]
+ public static int Main(params string[] args)
+ {
+ var hbRelogInstallPath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "".."");
+ var hbRelogPath = Path.Combine(hbRelogInstallPath, ""HBRelog.exe"");
+ var setup = new AppDomainSetup
+ {
+ ApplicationBase = hbRelogInstallPath,
+ };
+
+ return AppDomain.CreateDomain(""Domain"", null, setup).ExecuteAssembly(hbRelogPath, args);
+ }
+ }
+}
+";
+
+ using (CodeDomProvider cc = CodeDomProvider.CreateProvider("CSharp"))
+ {
+ CompilerParameters cp = new CompilerParameters();
+ cp.GenerateInMemory = false;
+ cp.GenerateExecutable = true;
+ cp.OutputAssembly = loaderPath;
+ cp.IncludeDebugInformation = false;
+ cp.CompilerOptions = "/optimize /platform:x86 /target:winexe";
+
+ foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
+ {
+ cp.ReferencedAssemblies.Add(asm.Location);
+ }
+
+ var results = cc.CompileAssemblyFromSource(cp, src);
+ if (results.Errors.HasErrors)
+ {
+ foreach (var err in results.Errors.OfType())
+ Log.Err($"{err}");
+ return false;
+ }
+ return true;
+ }
+ }
+
+
+ static string GetLoaderName()
+ {
+ return Fnv1($"{MachineGuid}|HBRelog|{GlobalSettings.GetSettingsPath()}").ToString();
+ }
+
+ //Check the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\MachineGuid registry value.
+ private static string MachineGuid
+ => (string)(Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Cryptography", false)?.GetValue("MachineGuid"));
+
+ private static int Fnv1(string s)
+ {
+ unchecked
+ {
+ uint hash = 0x811C9DC5;
+ foreach (char c in s)
+ {
+ hash *= 16777619;
+ hash ^= (byte)c;
+ hash *= 16777619;
+ hash ^= (byte)(c >> 8);
+ }
+
+ return (int)hash;
+ }
+ }
}
}
diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs
index 1065be0..5a28dbc 100644
--- a/Properties/AssemblyInfo.cs
+++ b/Properties/AssemblyInfo.cs
@@ -1,8 +1,4 @@
-// This file is auto genorated from using SubRCRev.exe and template AssemblyInfo.template
-// Build Date: 2015/01/05 17:55:38
-// SVN url: https://hbrelog.googlecode.com/svn/trunk/Properties
-
-using System.Reflection;
+using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -16,7 +12,7 @@
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("HBRelog")]
-[assembly: AssemblyCopyright("Copyright © 2015 by HighVoltz")]
+[assembly: AssemblyCopyright("Copyright © 2017 by HighVoltz")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -55,5 +51,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.1.3")]
-[assembly: AssemblyFileVersion("1.1.3")]
+[assembly: AssemblyVersion("1.1.27")]
+[assembly: AssemblyFileVersion("1.1.27")]
diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs
index eceffbe..d202297 100644
--- a/Properties/Resources.Designer.cs
+++ b/Properties/Resources.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
//
// This code was generated by a tool.
-// Runtime Version:4.0.30319.34014
+// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs
index 0e65af4..1c4baf0 100644
--- a/Properties/Settings.Designer.cs
+++ b/Properties/Settings.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
//
// This code was generated by a tool.
-// Runtime Version:4.0.30319.34014
+// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -12,7 +12,7 @@ namespace HighVoltz.HBRelog.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.1.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
diff --git a/README.md b/README.md
index a374904..f62f3a2 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,8 @@ Releases can be downloaded from [here](https://github.com/highvoltz/HBRelog/rele
### Requirements
* Windows Vista SP2 or a newer windows.
-* .Net 4.5.1 - [link](http://www.microsoft.com/en-us/download/details.aspx?id=40779)
-* Visual C++ Redist. for Visual Studio 2013 - [link](http://www.microsoft.com/en-us/download/details.aspx?id=40784)
+* .Net 4.6.1 - [link](https://www.microsoft.com/en-us/download/details.aspx?id=49982)
+* Visual C++ Redist. for Visual Studio 2015 - [link](https://www.microsoft.com/en-us/download/details.aspx?id=48145)
* World of Warcraft - http://www.battle.net
* Honorbuddy - http://www.honorbuddy.com
diff --git a/Remoting/IRemotingApi.cs b/Remoting/IRemotingApi.cs
index 14042c7..7e2a053 100644
--- a/Remoting/IRemotingApi.cs
+++ b/Remoting/IRemotingApi.cs
@@ -1,39 +1,58 @@
using System;
+using System.Runtime.Serialization;
using System.ServiceModel;
namespace HighVoltz.HBRelog.Remoting
{
[ServiceContract]
- interface IRemotingApi
+ internal interface IRemotingApi
{
[OperationContract]
- bool Init(int hbProcID);
+ bool Init(int hbProcId);
+
[OperationContract(IsOneWay = true)]
void Heartbeat(int hbProcID);
- [OperationContract (IsOneWay=true)]
+
+ [OperationContract(IsOneWay = true)]
void RestartHB(int hbProcID);
+
[OperationContract(IsOneWay = true)]
void RestartWow(int hbProcID);
+
[OperationContract]
string[] GetProfileNames();
+
[OperationContract]
string GetCurrentProfileName(int hbProcID);
+
[OperationContract(IsOneWay = true)]
void StartProfile(string profileName);
+
[OperationContract(IsOneWay = true)]
void StopProfile(string profileName);
+
[OperationContract(IsOneWay = true)]
void PauseProfile(string profileName);
+
[OperationContract(IsOneWay = true)]
void IdleProfile(string profileName, TimeSpan time);
+
[OperationContract(IsOneWay = true)]
void Logon(int hbProcID, string character, string server, string customClass, string botBase, string profilePath);
+
[OperationContract]
int GetProfileStatus(string profileName);
+
[OperationContract(IsOneWay = true)]
void SetProfileStatusText(int hbProcID, string status);
+
+ [OperationContract(IsOneWay = true)]
+ void ProfileLog(int hbProcID, string msg);
+
+
[OperationContract(IsOneWay = true)]
void SetBotInfoToolTip(int hbProcID, string tooltip);
+
[OperationContract(IsOneWay = true)]
void SkipCurrentTask(string profileName);
}
diff --git a/Remoting/RemotingApi.cs b/Remoting/RemotingApi.cs
index 2016637..0b97f0b 100644
--- a/Remoting/RemotingApi.cs
+++ b/Remoting/RemotingApi.cs
@@ -3,6 +3,7 @@
using System.ServiceModel;
using System.Threading;
using HighVoltz.HBRelog.Tasks;
+using HighVoltz.HBRelog.Settings;
namespace HighVoltz.HBRelog.Remoting
{
@@ -30,10 +31,11 @@ public bool Init(int hbProcID)
{
profile.TaskManager.HonorbuddyManager.SetStartupSequenceToComplete();
profile.Log("Opened communication with HBRelogHelper");
- return true;
+ return true;
}
+
Log.Write("Received communication from an unknown process Id: {0}", hbProcID);
- return false;
+ return false;
}
public void Heartbeat(int hbProcID)
@@ -129,8 +131,8 @@ public void Logon(int hbProcID, string character, string server, string customCl
CharacterProfile profile = GetProfileByHbProcID(hbProcID);
if (profile != null)
{
- var wowSettings = profile.Settings.WowSettings.ShadowCopy();
- var hbSettings = profile.Settings.HonorbuddySettings.ShadowCopy();
+ var wowSettings = (WowSettings)profile.Settings.WowSettings.ShadowCopy();
+ var hbSettings = (HonorbuddySettings)profile.Settings.HonorbuddySettings.ShadowCopy();
if (!string.IsNullOrEmpty(character))
wowSettings.CharacterName = character;
@@ -179,7 +181,14 @@ public void SetProfileStatusText(int hbProcID, string status)
profile.Status = status;
}
- public void SetBotInfoToolTip(int hbProcID, string tooltip)
+ public void ProfileLog(int hbProcID, string msg)
+ {
+ CharacterProfile profile = GetProfileByHbProcID(hbProcID);
+ if (profile != null)
+ profile.Log(msg);
+ }
+
+ public void SetBotInfoToolTip(int hbProcID, string tooltip)
{
CharacterProfile profile = GetProfileByHbProcID(hbProcID);
if (profile != null)
@@ -193,7 +202,13 @@ public void SkipCurrentTask(string profileName)
CharacterProfile profile = GetProfileByName(profileName);
if (profile != null)
{
- BMTask currentTask = profile.TaskManager.CurrentTask;
+ if (!profile.IsRunning || profile.IsPaused)
+ {
+ profile.Log("Could not skip current task because profile is not running or is paused.");
+ return;
+ }
+
+ BMTask currentTask = profile.TaskManager.CurrentTask;
if (currentTask == null)
{
profile.Log("Could not skip current task because there is no task running.");
@@ -209,5 +224,6 @@ public void SkipCurrentTask(string profileName)
Log.Write("Could not find a profile with the name: {0}", profileName);
}
}
- }
+
+ }
}
diff --git a/Settings/GlobalSettings.cs b/Settings/GlobalSettings.cs
index 2e79521..d29f0b8 100644
--- a/Settings/GlobalSettings.cs
+++ b/Settings/GlobalSettings.cs
@@ -27,12 +27,21 @@ limitations under the License.
using System.Xml.Linq;
using System.Xml.Serialization;
using HighVoltz.HBRelog.Tasks;
+using System.Globalization;
namespace HighVoltz.HBRelog.Settings
{
- public class GlobalSettings : INotifyPropertyChanged
+ public class GlobalSettings : SettingsBase
{
- private bool _autoAcceptTosEula;
+ public static GlobalSettings Instance { get; private set; }
+
+ static GlobalSettings()
+ {
+ Instance = new GlobalSettings();
+ Instance.Load(GetSettingsPath());
+ }
+
+ private bool _autoAcceptTosEula;
private Timer _autoSaveTimer;
private bool _autoStart;
private bool _autoUpdateHB;
@@ -43,8 +52,9 @@ public class GlobalSettings : INotifyPropertyChanged
private int _hBDelay;
private DateTime _lastSaveTimeStamp;
private int _loginDelay;
- private bool _minimizeHbOnStart;
- private bool _useDarkStyle;
+ private bool _useLocalSettings;
+ private bool _encryptSettings;
+ private bool _useDarkStyle;
private bool _setGameWindowTitle;
private int _wowDelay;
@@ -54,11 +64,20 @@ private GlobalSettings()
// set some default settings
HBDelay = 3;
AutoUpdateHB = CheckHbResponsiveness = UseDarkStyle = true;
- SettingsPath = DefaultSettingsPath;
}
- public static readonly string DefaultSettingsPath =
- Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "HighVoltz\\HBRelog\\Setting.xml");
+ private static string LocalSettingsPath => Path.Combine(Utility.AssemblyDirectory, "Settings.xml");
+
+ internal static string GetSettingsPath()
+ {
+ if (File.Exists(LocalSettingsPath))
+ return LocalSettingsPath;
+
+ if (File.Exists(Program.UserSettingsPath))
+ return Program.UserSettingsPath;
+
+ return LocalSettingsPath;
+ }
public string SettingsPath { get; private set; }
@@ -68,155 +87,120 @@ private static string GetTempSettingsPath(string settingsPath)
return settingsPath + ".tmp";
}
- public ObservableCollection CharacterProfiles { get; set; }
+ public ObservableCollection CharacterProfiles { get; private set; }
// Automatically start all enabled profiles on start
- public bool AutoStart
- {
- get { return _autoStart; }
- set
- {
- _autoStart = value;
- NotifyPropertyChanged("AutoStart");
- }
- }
+ public bool AutoStart
+ {
+ get { return _autoStart; }
+ set { NotifyPropertyChanged(ref _autoStart, ref value, nameof(AutoStart)); }
+ }
// delay in seconds between starting multiple Wow instance
- public int WowDelay
- {
- get { return _wowDelay; }
- set
- {
- _wowDelay = value;
- NotifyPropertyChanged("WowDelay");
- }
- }
+ public int WowDelay
+ {
+ get { return _wowDelay; }
+ set { NotifyPropertyChanged(ref _wowDelay, ref value, nameof(WowDelay)); }
+ }
- public string GameWindowTitle
- {
- get { return _gameWindowTitle; }
- set
- {
- _gameWindowTitle = value;
- NotifyPropertyChanged("GameWindowTitle");
- }
- }
+ public string GameWindowTitle
+ {
+ get { return _gameWindowTitle; }
+ set { NotifyPropertyChanged(ref _gameWindowTitle, ref value, nameof(GameWindowTitle)); }
+ }
// delay in seconds between starting multiple Honorbuddy instance
public int HBDelay
{
get { return _hBDelay; }
- set
- {
- _hBDelay = value;
- NotifyPropertyChanged("HBDelay");
- }
+ set { NotifyPropertyChanged(ref _hBDelay, ref value, nameof(HBDelay)); }
}
// delay in seconds between executing login actions.
- public int LoginDelay
- {
- get { return _loginDelay; }
- set
- {
- _loginDelay = value;
- NotifyPropertyChanged("LoginDelay");
- }
- }
+ public int LoginDelay
+ {
+ get { return _loginDelay; }
+ set { NotifyPropertyChanged(ref _loginDelay, ref value, nameof(LoginDelay)); }
+ }
- public bool UseDarkStyle
- {
- get { return _useDarkStyle; }
- set
- {
- _useDarkStyle = value;
- NotifyPropertyChanged("UseDarkStyle");
- }
- }
+ public bool UseDarkStyle
+ {
+ get { return _useDarkStyle; }
+ set { NotifyPropertyChanged(ref _useDarkStyle, ref value, nameof(UseDarkStyle)); }
+ }
- public bool SetGameWindowTitle
- {
- get { return _setGameWindowTitle; }
- set
- {
- _setGameWindowTitle = value;
- NotifyPropertyChanged("SetGameWindowTitle");
- }
- }
+ public bool SetGameWindowTitle
+ {
+ get { return _setGameWindowTitle; }
+ set { NotifyPropertyChanged(ref _setGameWindowTitle, ref value, nameof(SetGameWindowTitle)); }
+ }
- public bool CheckRealmStatus
- {
- get { return _checkRealmStatus; }
- set
- {
- _checkRealmStatus = value;
- NotifyPropertyChanged("CheckRealmStatus");
- }
- }
+ public bool CheckRealmStatus
+ {
+ get { return _checkRealmStatus; }
+ set { NotifyPropertyChanged(ref _checkRealmStatus, ref value, nameof(CheckRealmStatus)); }
+ }
- public bool CheckHbResponsiveness
- {
- get { return _checkHbResponsiveness; }
- set
- {
- _checkHbResponsiveness = value;
- NotifyPropertyChanged("CheckHbResponsiveness");
- }
- }
+ public bool CheckHbResponsiveness
+ {
+ get { return _checkHbResponsiveness; }
+ set { NotifyPropertyChanged(ref _checkHbResponsiveness, ref value, nameof(CheckHbResponsiveness)); }
+ }
- public bool CheckWowResponsiveness
- {
- get { return _checkWowResponsiveness; }
- set
- {
- _checkWowResponsiveness = value;
- NotifyPropertyChanged("CheckWowResponsiveness");
- }
- }
+ public bool CheckWowResponsiveness
+ {
+ get { return _checkWowResponsiveness; }
+ set { NotifyPropertyChanged(ref _checkWowResponsiveness, ref value, nameof(CheckWowResponsiveness)); }
+ }
- public bool AutoUpdateHB
- {
- get { return _autoUpdateHB; }
- set
- {
- _autoUpdateHB = value;
- NotifyPropertyChanged("AutoUpdateHB");
- }
- }
+ public bool AutoUpdateHB
+ {
+ get { return _autoUpdateHB; }
+ set { NotifyPropertyChanged(ref _autoUpdateHB, ref value, nameof(AutoUpdateHB)); }
+ }
- public bool AutoAcceptTosEula
- {
- get { return _autoAcceptTosEula; }
- set
- {
- _autoAcceptTosEula = value;
- NotifyPropertyChanged("AutoAcceptTosEula");
- }
- }
+ public bool AutoAcceptTosEula
+ {
+ get { return _autoAcceptTosEula; }
+ set { NotifyPropertyChanged(ref _autoAcceptTosEula, ref value, nameof(AutoAcceptTosEula)); }
+ }
///
/// Minimizes HB to system tray on start
///
- public bool MinimizeHbOnStart
+ public bool UseLocalSettings
{
- get { return _minimizeHbOnStart; }
- set
- {
- _minimizeHbOnStart = value;
- NotifyPropertyChanged("MinimizeHbOnStart");
- }
+ get { return _useLocalSettings; }
+ set { NotifyPropertyChanged(ref _useLocalSettings, ref value, nameof(UseLocalSettings)); }
}
- public string WowVersion { get; set; }
+
+ public bool EncryptSettings
+ {
+ get { return _encryptSettings; }
+ set
+ {
+ if (value == _encryptSettings)
+ return;
+
+ if (value)
+ ConvertToEncryption();
+ else
+ ConvertToPlaintext();
+
+ NotifyPropertyChanged(ref _encryptSettings, ref value, nameof(EncryptSettings));
+ }
+ }
+
+ public string WowVersion { get; set; }
// offsets
public uint GameStateOffset { get; set; }
public uint FocusedWidgetOffset { get; set; }
public uint LuaStateOffset { get; set; }
- public uint GlueStateOffset { get; set; }
public uint LoadingScreenEnableCountOffset { get; set; }
public TimeSpan SaveCompleteTimeSpan
@@ -237,109 +221,35 @@ public TimeSpan SaveCompleteTimeSpan
}
}
- public event PropertyChangedEventHandler PropertyChanged;
// serializers giving me issues with colections.. so saving stuff manually.
- public void Save()
+ public void Save(string path = null)
{
try
{
- var root = new XElement("BotManager");
- root.Add(new XElement("AutoStart", AutoStart));
- root.Add(new XElement("WowDelay", WowDelay));
- root.Add(new XElement("HBDelay", HBDelay));
- root.Add(new XElement("LoginDelay", LoginDelay));
- root.Add(new XElement("UseDarkStyle", UseDarkStyle));
- root.Add(new XElement("CheckRealmStatus", CheckRealmStatus));
- root.Add(new XElement("CheckHbResponsiveness", CheckHbResponsiveness));
- root.Add(new XElement("CheckWowResponsiveness", CheckWowResponsiveness));
- root.Add(new XElement("MinimizeHbOnStart", MinimizeHbOnStart));
- root.Add(new XElement("AutoUpdateHB", AutoUpdateHB));
- root.Add(new XElement("AutoAcceptTosEula", AutoAcceptTosEula));
- root.Add(new XElement("SetGameWindowTitle", SetGameWindowTitle));
- root.Add(new XElement("GameWindowTitle", GameWindowTitle));
-
- root.Add(new XElement("WowVersion", WowVersion));
-
- root.Add(new XElement("GameStateOffset", GameStateOffset));
- root.Add(new XElement("FocusedWidgetOffset", FocusedWidgetOffset));
- root.Add(new XElement("LuaStateOffset", LuaStateOffset));
- root.Add(new XElement("GlueStateOffset", GlueStateOffset));
- root.Add(new XElement("LoadingScreenEnableCountOffset", LoadingScreenEnableCountOffset));
-
- var characterProfilesElement = new XElement("CharacterProfiles");
- foreach (CharacterProfile profile in CharacterProfiles)
- {
- var profileElement = new XElement("CharacterProfile");
- var settingsElement = new XElement("Settings");
- settingsElement.Add(new XElement("ProfileName", profile.Settings.ProfileName));
- settingsElement.Add(new XElement("IsEnabled", profile.Settings.IsEnabled));
- var wowSettingsElement = new XElement("WowSettings");
- // Wow Settings
- wowSettingsElement.Add(new XElement("LoginData", profile.Settings.WowSettings.LoginData));
- wowSettingsElement.Add(new XElement("PasswordData", profile.Settings.WowSettings.PasswordData));
- wowSettingsElement.Add(new XElement("AcountName", profile.Settings.WowSettings.AcountName));
- wowSettingsElement.Add(new XElement("CharacterName", profile.Settings.WowSettings.CharacterName));
- wowSettingsElement.Add(new XElement("ServerName", profile.Settings.WowSettings.ServerName));
- wowSettingsElement.Add(new XElement("AuthenticatorSerialData", profile.Settings.WowSettings.AuthenticatorSerialData));
- wowSettingsElement.Add(new XElement("AuthenticatorRestoreCodeData", profile.Settings.WowSettings.AuthenticatorRestoreCodeData));
- wowSettingsElement.Add(new XElement("Region", profile.Settings.WowSettings.Region));
- wowSettingsElement.Add(new XElement("WowPath", profile.Settings.WowSettings.WowPath));
- wowSettingsElement.Add(new XElement("WowArgs", profile.Settings.WowSettings.WowArgs));
- wowSettingsElement.Add(new XElement("WowWindowWidth", profile.Settings.WowSettings.WowWindowWidth));
- wowSettingsElement.Add(new XElement("WowWindowHeight", profile.Settings.WowSettings.WowWindowHeight));
- wowSettingsElement.Add(new XElement("WowWindowX", profile.Settings.WowSettings.WowWindowX));
- wowSettingsElement.Add(new XElement("WowWindowY", profile.Settings.WowSettings.WowWindowY));
- settingsElement.Add(wowSettingsElement);
- var hbSettingsElement = new XElement("HonorbuddySettings");
- // Honorbuddy Settings
- hbSettingsElement.Add(new XElement("HonorbuddyKeyData", profile.Settings.HonorbuddySettings.HonorbuddyKeyData));
- hbSettingsElement.Add(new XElement("CustomClass", profile.Settings.HonorbuddySettings.CustomClass));
- hbSettingsElement.Add(new XElement("BotBase", profile.Settings.HonorbuddySettings.BotBase));
- hbSettingsElement.Add(new XElement("HonorbuddyProfile", profile.Settings.HonorbuddySettings.HonorbuddyProfile));
- hbSettingsElement.Add(new XElement("HonorbuddyPath", profile.Settings.HonorbuddySettings.HonorbuddyPath));
- hbSettingsElement.Add(new XElement("UseHBBeta", profile.Settings.HonorbuddySettings.UseHBBeta));
-
- settingsElement.Add(hbSettingsElement);
- profileElement.Add(settingsElement);
- var tasksElement = new XElement("Tasks");
-
- foreach (BMTask task in profile.Tasks)
- {
- var taskElement = new XElement(task.GetType().Name);
- // get a list of propertyes that don't have [XmlIgnore] custom attribute attached.
- List propertyList =
- task.GetType()
- .GetProperties()
- .Where(pi => pi.GetCustomAttributesData().All(cad => cad.Constructor.DeclaringType != typeof(XmlIgnoreAttribute)))
- .ToList();
- foreach (PropertyInfo property in propertyList)
- {
- var value = property.GetValue(task, null);
- Debug.Assert(value != null, string.Format("value for {0} != null", property.Name));
- taskElement.Add(new XAttribute(property.Name, value));
- }
- tasksElement.Add(taskElement);
- }
- profileElement.Add(tasksElement);
- characterProfilesElement.Add(profileElement);
- }
- root.Add(characterProfilesElement);
-
- var tempPath = GetTempSettingsPath(SettingsPath);
- var directory = Path.GetDirectoryName(tempPath);
+ SettingsPath = path ?? (UseLocalSettings ? LocalSettingsPath : Program.UserSettingsPath);
+ var xml = ConvertToXml();
+
+ var tempPath = GetTempSettingsPath(SettingsPath);
+ var directory = Path.GetDirectoryName(tempPath);
if (directory != null && !Directory.Exists(directory))
Directory.CreateDirectory(directory);
var xmlSettings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent = true, };
- using (var tempFile = ObtainLock(tempPath, FileAccess.Write, FileShare.Delete))
+ using (var tempFile = ObtainLock(tempPath, FileAccess.Write, FileShare.Delete))
{
using (XmlWriter xmlOutFile = XmlWriter.Create(tempFile, xmlSettings))
- root.Save(xmlOutFile);
+ xml.Save(xmlOutFile);
if (File.Exists(SettingsPath))
File.Delete(SettingsPath);
- File.Move(tempPath, SettingsPath);
+ File.Move(tempPath, SettingsPath);
+
+ // Maintain only one copy of settings.
+ if (UseLocalSettings && File.Exists(Program.UserSettingsPath))
+ File.Delete(Program.UserSettingsPath);
+ else if (!UseLocalSettings && File.Exists(LocalSettingsPath))
+ File.Delete(LocalSettingsPath);
}
}
catch (Exception ex)
@@ -348,6 +258,39 @@ public void Save()
}
}
+ public override XElement ConvertToXml()
+ {
+ var xml = new XElement("BotManager");
+ xml.Add(new XElement("AutoStart", AutoStart));
+ xml.Add(new XElement("WowDelay", WowDelay));
+ xml.Add(new XElement("HBDelay", HBDelay));
+ xml.Add(new XElement("LoginDelay", LoginDelay));
+ xml.Add(new XElement("UseDarkStyle", UseDarkStyle));
+ xml.Add(new XElement("CheckRealmStatus", CheckRealmStatus));
+ xml.Add(new XElement("CheckHbResponsiveness", CheckHbResponsiveness));
+ xml.Add(new XElement("CheckWowResponsiveness", CheckWowResponsiveness));
+ xml.Add(new XElement("UseLocalSettings", UseLocalSettings));
+ xml.Add(new XElement("AutoUpdateHB", AutoUpdateHB));
+ xml.Add(new XElement("AutoAcceptTosEula", AutoAcceptTosEula));
+ xml.Add(new XElement("SetGameWindowTitle", SetGameWindowTitle));
+ xml.Add(new XElement("GameWindowTitle", GameWindowTitle));
+ xml.Add(new XElement("EncryptSettings", EncryptSettings));
+ xml.Add(new XElement("WowVersion", WowVersion));
+ xml.Add(new XElement("GameStateOffset", GameStateOffset));
+ xml.Add(new XElement("FocusedWidgetOffset", FocusedWidgetOffset));
+ xml.Add(new XElement("LuaStateOffset", LuaStateOffset));
+ xml.Add(new XElement("LoadingScreenEnableCountOffset", LoadingScreenEnableCountOffset));
+
+ var characterProfilesElement = new XElement("CharacterProfiles");
+ foreach (CharacterProfile profile in CharacterProfiles)
+ {
+ var profileElement = profile.ConvertToXml();
+ characterProfilesElement.Add(profileElement);
+ }
+ xml.Add(characterProfilesElement);
+ return xml;
+ }
+
private static FileStream ObtainLock(string path, FileAccess access, FileShare share = FileShare.None, int maxWaitTimeMs = 500)
{
var sw = Stopwatch.StartNew();
@@ -370,177 +313,103 @@ private static FileStream ObtainLock(string path, FileAccess access, FileShare s
/// Attempts to load settings from file
///
/// A GlocalSettings
- public static GlobalSettings Load(string path = null)
+ ///
+ private void Load(string path)
{
- path = path ?? DefaultSettingsPath;
- var settings = new GlobalSettings();
try
{
- var hasSettings = File.Exists(path);
-
- var tempPath = GetTempSettingsPath(path);
-
- var recoverFromCrash = !hasSettings && File.Exists(tempPath);
+ IsLoaded = false;
+ var hasSettings = File.Exists(path);
+ var tempPath = GetTempSettingsPath(path);
+ var recoverFromCrash = !hasSettings && File.Exists(tempPath);
if (hasSettings || recoverFromCrash)
{
if (recoverFromCrash)
{
- File.Move(tempPath, path);
- }
-
- XElement root = XElement.Load(path);
- settings.WowVersion = root.Element("WowVersion").Value;
- settings.AutoStart = GetElementValue(root.Element("AutoStart"));
- settings.WowDelay = GetElementValue(root.Element("WowDelay"));
- settings.HBDelay = GetElementValue(root.Element("HBDelay"), 10);
- settings.LoginDelay = GetElementValue(root.Element("LoginDelay"), 3);
- settings.UseDarkStyle = GetElementValue(root.Element("UseDarkStyle"), true);
- settings.CheckRealmStatus = GetElementValue(root.Element("CheckRealmStatus"), false);
- settings.CheckHbResponsiveness = GetElementValue(root.Element("CheckHbResponsiveness"), true);
- settings.CheckWowResponsiveness = GetElementValue(root.Element("CheckWowResponsiveness"), true);
- settings.AutoUpdateHB = GetElementValue(root.Element("AutoUpdateHB"), true);
- settings.MinimizeHbOnStart = GetElementValue(root.Element("MinimizeHbOnStart"), false);
- settings.AutoAcceptTosEula = GetElementValue(root.Element("AutoAcceptTosEula"), false);
- settings.SetGameWindowTitle = GetElementValue(root.Element("SetGameWindowTitle"), true);
- settings.GameWindowTitle = GetElementValue(root.Element("GameWindowTitle"), "{name} - {pid}");
-
- settings.GameStateOffset = GetElementValue(root.Element("GameStateOffset"), 0u);
- // settings.FrameScriptExecuteOffset = GetElementValue(root.Element("FrameScriptExecuteOffset"), 0u);
- settings.FocusedWidgetOffset = GetElementValue(root.Element("FocusedWidgetOffset"), 0u);
- settings.LuaStateOffset = GetElementValue(root.Element("LuaStateOffset"), 0u);
- //settings.LastHardwareEventOffset = GetElementValue(root.Element("LastHardwareEventOffset"), 0u);
- settings.GlueStateOffset = GetElementValue(root.Element("GlueStateOffset"), 0u);
- settings.LoadingScreenEnableCountOffset = GetElementValue(
- root.Element("LoadingScreenEnableCountOffset"),
- 0u);
-
- XElement characterProfilesElement = root.Element("CharacterProfiles");
- foreach (XElement profileElement in characterProfilesElement.Elements("CharacterProfile"))
- {
- var profile = new CharacterProfile();
- XElement settingsElement = profileElement.Element("Settings");
- profile.Settings.ProfileName = GetElementValue(settingsElement.Element("ProfileName"));
- profile.Settings.IsEnabled = GetElementValue(settingsElement.Element("IsEnabled"));
- XElement wowSettingsElement = settingsElement.Element("WowSettings");
-
- // Wow Settings
- if (wowSettingsElement != null)
- {
- profile.Settings.WowSettings.LoginData = GetElementValue(wowSettingsElement.Element("LoginData"));
- profile.Settings.WowSettings.PasswordData = GetElementValue(wowSettingsElement.Element("PasswordData"));
- profile.Settings.WowSettings.AcountName = GetElementValue(wowSettingsElement.Element("AcountName"));
- profile.Settings.WowSettings.CharacterName = GetElementValue(wowSettingsElement.Element("CharacterName"));
- profile.Settings.WowSettings.ServerName = GetElementValue(wowSettingsElement.Element("ServerName"));
- profile.Settings.WowSettings.AuthenticatorSerialData = GetElementValue(wowSettingsElement.Element("AuthenticatorSerialData"));
- profile.Settings.WowSettings.AuthenticatorRestoreCodeData = GetElementValue(wowSettingsElement.Element("AuthenticatorRestoreCodeData"));
- profile.Settings.WowSettings.Region = GetElementValue(wowSettingsElement.Element("Region"));
- profile.Settings.WowSettings.WowPath = GetElementValue(wowSettingsElement.Element("WowPath"));
- profile.Settings.WowSettings.WowArgs = GetElementValue(wowSettingsElement.Element("WowArgs"));
- profile.Settings.WowSettings.WowWindowWidth = GetElementValue(wowSettingsElement.Element("WowWindowWidth"));
- profile.Settings.WowSettings.WowWindowHeight = GetElementValue(wowSettingsElement.Element("WowWindowHeight"));
- profile.Settings.WowSettings.WowWindowX = GetElementValue(wowSettingsElement.Element("WowWindowX"));
- profile.Settings.WowSettings.WowWindowY = GetElementValue(wowSettingsElement.Element("WowWindowY"));
- }
- XElement hbSettingsElement = settingsElement.Element("HonorbuddySettings");
- // Honorbuddy Settings
- if (hbSettingsElement != null)
- {
- profile.Settings.HonorbuddySettings.HonorbuddyKeyData = GetElementValue(hbSettingsElement.Element("HonorbuddyKeyData"));
- profile.Settings.HonorbuddySettings.CustomClass = GetElementValue(hbSettingsElement.Element("CustomClass"));
- profile.Settings.HonorbuddySettings.BotBase = GetElementValue(hbSettingsElement.Element("BotBase"));
- profile.Settings.HonorbuddySettings.HonorbuddyProfile = GetElementValue(hbSettingsElement.Element("HonorbuddyProfile"));
- profile.Settings.HonorbuddySettings.HonorbuddyPath = GetElementValue(hbSettingsElement.Element("HonorbuddyPath"));
- profile.Settings.HonorbuddySettings.UseHBBeta = GetElementValue(hbSettingsElement.Element("UseHBBeta"));
- }
- XElement tasksElement = profileElement.Element("Tasks");
- // Load the Task list.
- foreach (XElement taskElement in tasksElement.Elements())
- {
- Type taskType = Type.GetType("HighVoltz.HBRelog.Tasks." + taskElement.Name);
- if (taskType != null)
- {
- var task = (BMTask)Activator.CreateInstance(taskType);
- task.SetProfile(profile);
- // Dictionary of property Names and the corresponding PropertyInfo
- Dictionary propertyDict =
- task.GetType()
- .GetProperties()
- .Where(pi => pi.GetCustomAttributesData().All(cad => cad.Constructor.DeclaringType != typeof(XmlIgnoreAttribute)))
- .ToDictionary(k => k.Name);
-
- foreach (XAttribute attr in taskElement.Attributes())
- {
- string propKey = attr.Name.ToString();
- if (propertyDict.ContainsKey(propKey))
- {
- // if property is an enum then use Enum.Parse.. otherwise use Convert.ChangeValue
- object val = typeof(Enum).IsAssignableFrom(propertyDict[propKey].PropertyType)
- ? Enum.Parse(propertyDict[propKey].PropertyType, attr.Value)
- : Convert.ChangeType(attr.Value, propertyDict[propKey].PropertyType);
- propertyDict[propKey].SetValue(task, val, null);
- }
- else
- {
- Log.Err("{0} does not have a property called {1}", taskElement.Name, attr.Name);
- }
- }
- profile.Tasks.Add(task);
- }
- else
- {
- Log.Err("{0} is not a known task type", taskElement.Name);
- }
- }
- settings.CharacterProfiles.Add(profile);
+ File.Move(tempPath, path);
}
+ XElement root = XElement.Load(path);
+ LoadFromXml(root);
}
+
+ SettingsPath = UseLocalSettings ? LocalSettingsPath : Program.UserSettingsPath;
}
catch (Exception ex)
{
Log.Err(ex.ToString());
}
- return settings;
+ finally
+ {
+ IsLoaded = true;
+ }
+ }
+
+ public override void LoadFromXml(XElement element)
+ {
+ WowVersion = element.Element("WowVersion").Value;
+ AutoStart = GetElementValue(element.Element("AutoStart"));
+ WowDelay = GetElementValue(element.Element("WowDelay"));
+ HBDelay = GetElementValue(element.Element("HBDelay"), 10);
+ LoginDelay = GetElementValue(element.Element("LoginDelay"), 3);
+ UseDarkStyle = GetElementValue(element.Element("UseDarkStyle"), true);
+ CheckRealmStatus = GetElementValue(element.Element("CheckRealmStatus"), false);
+ CheckHbResponsiveness = GetElementValue(element.Element("CheckHbResponsiveness"), true);
+ CheckWowResponsiveness = GetElementValue(element.Element("CheckWowResponsiveness"), true);
+ AutoUpdateHB = GetElementValue(element.Element("AutoUpdateHB"), true);
+ UseLocalSettings = GetElementValue(element.Element("UseLocalSettings"), false);
+ AutoAcceptTosEula = GetElementValue(element.Element("AutoAcceptTosEula"), false);
+ SetGameWindowTitle = GetElementValue(element.Element("SetGameWindowTitle"), true);
+ GameWindowTitle = GetElementValue(element.Element("GameWindowTitle"), "{name} - {pid}");
+ EncryptSettings = GetElementValue(element.Element("EncryptSettings"), true);
+ GameStateOffset = GetElementValue(element.Element("GameStateOffset"), 0u);
+ // settings.FrameScriptExecuteOffset = GetElementValue(root.Element("FrameScriptExecuteOffset"), 0u);
+ FocusedWidgetOffset = GetElementValue(element.Element("FocusedWidgetOffset"), 0u);
+ LuaStateOffset = GetElementValue(element.Element("LuaStateOffset"), 0u);
+ LoadingScreenEnableCountOffset = GetElementValue(element.Element("LoadingScreenEnableCountOffset"), 0u);
+ //settings.LastHardwareEventOffset = GetElementValue(root.Element("LastHardwareEventOffset"), 0u);
+ CharacterProfiles.Clear();
+ XElement characterProfilesElement = element.Element("CharacterProfiles");
+ foreach (XElement profileElement in characterProfilesElement.Elements("CharacterProfile"))
+ {
+ var profile = new CharacterProfile();
+ profile.LoadFromXml(profileElement);
+ CharacterProfiles.Add(profile);
+ }
}
static readonly byte[] Key = { 230, 123, 245, 78, 43, 229, 126, 109, 126, 10, 134, 61, 167, 2, 138, 142 };
static readonly byte[] Iv = { 113, 110, 177, 211, 193, 101, 36, 36, 52, 12, 51, 73, 61, 42, 239, 236 };
- public static GlobalSettings Import(string path)
+ public void Import(string path)
{
- var settings = Load(path);
- foreach (var characterProfile in settings.CharacterProfiles)
+ Load(path);
+ foreach (var characterProfile in CharacterProfiles)
{
- if (!string.IsNullOrEmpty(characterProfile.Settings.WowSettings.LoginData))
- {
- characterProfile.Settings.WowSettings.LoginData =
- Utility.EncrptDpapi(Utility.DecryptAes(characterProfile.Settings.WowSettings.LoginData, Key, Iv));
- }
-
- if (!string.IsNullOrEmpty(characterProfile.Settings.WowSettings.PasswordData))
- {
- characterProfile.Settings.WowSettings.PasswordData =
- Utility.EncrptDpapi(Utility.DecryptAes(characterProfile.Settings.WowSettings.PasswordData, Key, Iv));
- }
-
- if (!string.IsNullOrEmpty(characterProfile.Settings.WowSettings.AuthenticatorSerialData))
- {
- characterProfile.Settings.WowSettings.AuthenticatorSerialData =
- Utility.EncrptDpapi(Utility.DecryptAes(characterProfile.Settings.WowSettings.AuthenticatorSerialData, Key, Iv));
- }
-
- if (!string.IsNullOrEmpty(characterProfile.Settings.WowSettings.AuthenticatorRestoreCodeData))
- {
- characterProfile.Settings.WowSettings.AuthenticatorRestoreCodeData =
- Utility.EncrptDpapi(Utility.DecryptAes(characterProfile.Settings.WowSettings.AuthenticatorRestoreCodeData, Key, Iv));
- }
-
- if (!string.IsNullOrEmpty(characterProfile.Settings.HonorbuddySettings.HonorbuddyKeyData))
- {
- characterProfile.Settings.HonorbuddySettings.HonorbuddyKeyData =
- Utility.EncrptDpapi(Utility.DecryptAes(characterProfile.Settings.HonorbuddySettings.HonorbuddyKeyData, Key, Iv));
- }
+ var plainText = Utility.DecryptAes(characterProfile.Settings.WowSettings.LoginData, Key, Iv);
+ characterProfile.Settings.WowSettings.LoginData = EncryptSettings
+ ? Utility.EncrptDpapi(plainText)
+ : plainText;
+
+ plainText = Utility.DecryptAes(characterProfile.Settings.WowSettings.PasswordData, Key, Iv);
+ characterProfile.Settings.WowSettings.PasswordData = EncryptSettings
+ ? Utility.EncrptDpapi(plainText)
+ : plainText;
+
+ plainText = Utility.DecryptAes(characterProfile.Settings.WowSettings.AuthenticatorSerialData, Key, Iv);
+ characterProfile.Settings.WowSettings.AuthenticatorSerialData = EncryptSettings
+ ? Utility.EncrptDpapi(plainText)
+ : plainText;
+
+ plainText = Utility.DecryptAes(characterProfile.Settings.WowSettings.AuthenticatorRestoreCodeData, Key, Iv);
+ characterProfile.Settings.WowSettings.AuthenticatorRestoreCodeData = EncryptSettings
+ ? Utility.EncrptDpapi(plainText)
+ : plainText;
+
+ plainText = Utility.DecryptAes(characterProfile.Settings.HonorbuddySettings.HonorbuddyKeyData, Key, Iv);
+ characterProfile.Settings.HonorbuddySettings.HonorbuddyKeyData = EncryptSettings
+ ? Utility.EncrptDpapi(plainText)
+ : plainText;
}
- return settings;
}
public GlobalSettings Export(string path)
@@ -551,34 +420,30 @@ public GlobalSettings Export(string path)
{
var newProfile = characterProfile.ShadowCopy();
- if (!string.IsNullOrEmpty(newProfile.Settings.WowSettings.LoginData))
- {
- newProfile.Settings.WowSettings.LoginData = Utility.EncryptAes(Utility.DecrptDpapi(characterProfile.Settings.WowSettings.LoginData), Key, Iv);
- }
+ var data = EncryptSettings
+ ? Utility.DecrptDpapi(characterProfile.Settings.WowSettings.LoginData)
+ : characterProfile.Settings.WowSettings.LoginData;
+ newProfile.Settings.WowSettings.LoginData = Utility.EncryptAes(data, Key, Iv);
- if (!string.IsNullOrEmpty(newProfile.Settings.WowSettings.PasswordData))
- {
- newProfile.Settings.WowSettings.PasswordData = Utility.EncryptAes(
- Utility.DecrptDpapi(characterProfile.Settings.WowSettings.PasswordData), Key, Iv);
- }
+ data = EncryptSettings
+ ? Utility.DecrptDpapi(characterProfile.Settings.WowSettings.PasswordData)
+ : characterProfile.Settings.WowSettings.PasswordData;
+ newProfile.Settings.WowSettings.PasswordData = Utility.EncryptAes(data, Key, Iv);
- if (!string.IsNullOrEmpty(newProfile.Settings.WowSettings.AuthenticatorSerialData))
- {
- newProfile.Settings.WowSettings.AuthenticatorSerialData = Utility.EncryptAes(
- Utility.DecrptDpapi(characterProfile.Settings.WowSettings.AuthenticatorSerialData), Key, Iv);
- }
+ data = EncryptSettings
+ ? Utility.DecrptDpapi(characterProfile.Settings.WowSettings.AuthenticatorSerialData)
+ : characterProfile.Settings.WowSettings.AuthenticatorSerialData;
+ newProfile.Settings.WowSettings.AuthenticatorSerialData = Utility.EncryptAes(data, Key, Iv);
- if (!string.IsNullOrEmpty(newProfile.Settings.WowSettings.AuthenticatorRestoreCodeData))
- {
- newProfile.Settings.WowSettings.AuthenticatorRestoreCodeData = Utility.EncryptAes(
- Utility.DecrptDpapi(characterProfile.Settings.WowSettings.AuthenticatorRestoreCodeData), Key, Iv);
- }
+ data = EncryptSettings
+ ? Utility.DecrptDpapi(characterProfile.Settings.WowSettings.AuthenticatorRestoreCodeData)
+ : characterProfile.Settings.WowSettings.AuthenticatorRestoreCodeData;
+ newProfile.Settings.WowSettings.AuthenticatorRestoreCodeData = Utility.EncryptAes(data, Key, Iv);
- if (!string.IsNullOrEmpty(newProfile.Settings.HonorbuddySettings.HonorbuddyKeyData))
- {
- newProfile.Settings.HonorbuddySettings.HonorbuddyKeyData =
- Utility.EncryptAes(Utility.DecrptDpapi(characterProfile.Settings.HonorbuddySettings.HonorbuddyKeyData), Key, Iv);
- }
+ data = EncryptSettings
+ ? Utility.DecrptDpapi(characterProfile.Settings.HonorbuddySettings.HonorbuddyKeyData)
+ : characterProfile.Settings.HonorbuddySettings.HonorbuddyKeyData;
+ newProfile.Settings.HonorbuddySettings.HonorbuddyKeyData = Utility.EncryptAes(data, Key, Iv);
settings.CharacterProfiles.Add(newProfile);
}
@@ -586,61 +451,64 @@ public GlobalSettings Export(string path)
return settings;
}
- public GlobalSettings ShadowCopy()
+ public override SettingsBase ShadowCopy()
{
var settings = (GlobalSettings)MemberwiseClone();
settings.CharacterProfiles = new ObservableCollection();
foreach (var characterProfile in CharacterProfiles)
- {
settings.CharacterProfiles.Add(characterProfile.ShadowCopy());
- }
return settings;
}
- private static T GetElementValue(XElement element, T defaultValue = default(T))
- {
- if (element != null)
- {
- if (defaultValue is Enum)
- {
- return (T)Enum.Parse(typeof(T), element.Value);
- }
- return (T)Convert.ChangeType(element.Value, typeof(T));
- }
- return defaultValue;
- }
-
public void QueueSave()
{
if (DateTime.Now - _lastSaveTimeStamp >= TimeSpan.FromSeconds(5) && _autoSaveTimer == null)
- Save();
- else
{
- if (_autoSaveTimer != null)
- _autoSaveTimer.Dispose();
-
- _autoSaveTimer = new Timer(
- state =>
- {
- Save();
- _autoSaveTimer.Dispose();
- _autoSaveTimer = null;
- },
- null,
- 5000,
- -1);
+ Save();
+ return;
}
- _lastSaveTimeStamp = DateTime.Now;
+ _autoSaveTimer?.Dispose();
+ _autoSaveTimer = new Timer(
+ state =>
+ {
+ Save();
+ _autoSaveTimer.Dispose();
+ _autoSaveTimer = null;
+ },
+ null,
+ 5000,
+ -1);
+ _lastSaveTimeStamp = DateTime.Now;
}
- private void NotifyPropertyChanged(string name)
- {
- if (PropertyChanged != null)
- {
- PropertyChanged(this, new PropertyChangedEventArgs(name));
- }
- if (HbRelogManager.Settings != null)
- HbRelogManager.Settings.QueueSave();
- }
+
+ private void ConvertToEncryption()
+ {
+ foreach (var characterProfile in CharacterProfiles)
+ {
+ var settings = characterProfile.Settings;
+ settings.WowSettings.LoginData = Utility.EncrptDpapi(settings.WowSettings.LoginData);
+ settings.WowSettings.PasswordData = Utility.EncrptDpapi(settings.WowSettings.PasswordData);
+ settings.WowSettings.AuthenticatorSerialData = Utility.EncrptDpapi(settings.WowSettings.AuthenticatorSerialData);
+ settings.WowSettings.AuthenticatorRestoreCodeData = Utility.EncrptDpapi(settings.WowSettings.AuthenticatorRestoreCodeData);
+ settings.HonorbuddySettings.HonorbuddyKeyData = Utility.EncrptDpapi(settings.HonorbuddySettings.HonorbuddyKeyData);
+ }
+ }
+
+ private void ConvertToPlaintext()
+ {
+ foreach (var characterProfile in CharacterProfiles)
+ {
+ var settings = characterProfile.Settings;
+ settings.WowSettings.LoginData = Utility.DecrptDpapi(settings.WowSettings.LoginData);
+ settings.WowSettings.PasswordData = Utility.DecrptDpapi(settings.WowSettings.PasswordData);
+ settings.WowSettings.AuthenticatorSerialData = Utility.DecrptDpapi(settings.WowSettings.AuthenticatorSerialData);
+ settings.WowSettings.AuthenticatorRestoreCodeData = Utility.DecrptDpapi(settings.WowSettings.AuthenticatorRestoreCodeData);
+
+ // In the past, HonorbuddyKeyData was not encrypted when it represented an empty string
+ if (settings.HonorbuddySettings.HonorbuddyKeyData != "")
+ settings.HonorbuddySettings.HonorbuddyKeyData = Utility.DecrptDpapi(settings.HonorbuddySettings.HonorbuddyKeyData);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Settings/HonorbuddySettings.cs b/Settings/HonorbuddySettings.cs
index 9d127cb..62b829b 100644
--- a/Settings/HonorbuddySettings.cs
+++ b/Settings/HonorbuddySettings.cs
@@ -16,21 +16,17 @@ limitations under the License.
using System;
using System.ComponentModel;
-using System.Security.Cryptography;
-using System.Text;
using System.Windows;
+using System.Xml.Linq;
namespace HighVoltz.HBRelog.Settings
{
- public class HonorbuddySettings : INotifyPropertyChanged
+ public class HonorbuddySettings : SettingsBase
{
public HonorbuddySettings()
{
- HonorbuddyPath = string.Empty;
CustomClass = "Singular";
- BotBase = string.Empty;
- HonorbuddyProfile = string.Empty;
- HonorbuddyArgs = string.Empty;
+ HonorbuddyPath = HonorbuddyProfile = HonorbuddyKey = HonorbuddyArgs = "";
}
private string _honorbuddyPath;
@@ -40,8 +36,9 @@ public HonorbuddySettings()
public string HonorbuddyPath
{
get { return _honorbuddyPath; }
- set { _honorbuddyPath = value; NotifyPropertyChanged("HonorbuddyPath"); }
+ set { NotifyPropertyChanged(ref _honorbuddyPath, ref value, nameof(HonorbuddyPath)); }
}
+
string _botBase;
///
/// Name of the bot to use
@@ -49,32 +46,44 @@ public string HonorbuddyPath
public string BotBase
{
get { return _botBase; }
- set { _botBase = value; NotifyPropertyChanged("BotBase"); }
+ set { NotifyPropertyChanged(ref _botBase, ref value, nameof(BotBase)); }
}
- public string HonorbuddyKeyData { get; set; }
- ///
- /// The Honorbuddy Key to use. It can be left empty
- ///
- public string HonorbuddyKey
+ private string _honorbuddyKeyData;
+
+ public string HonorbuddyKeyData
+ {
+ get { return _honorbuddyKeyData; }
+ set { _honorbuddyKeyData = value; }
+ }
+
+ ///
+ /// The Honorbuddy Key to use. It can be left empty
+ ///
+ public string HonorbuddyKey
{
get
{
try
{
- return !string.IsNullOrEmpty(HonorbuddyKeyData) ? Utility.DecrptDpapi(HonorbuddyKeyData) : "";
+ if (string.IsNullOrEmpty(HonorbuddyKeyData))
+ return "";
+
+ return GlobalSettings.Instance.EncryptSettings
+ ? Utility.DecrptDpapi(HonorbuddyKeyData)
+ : HonorbuddyKeyData;
}
catch
{
// this error can occur if the Windows password was changed or profile was copied to another computer
- MessageBox.Show(string.Format("Error decrypting Honrobuddy key. Try setting Honrobuddy key again."));
+ MessageBox.Show("Error decrypting Honrobuddy key. Try setting Honrobuddy key again.");
return "";
}
}
set
{
- HonorbuddyKeyData = Utility.EncrptDpapi(value);
- NotifyPropertyChanged("HonorbuddyKey");
+ string val = GlobalSettings.Instance.EncryptSettings ? Utility.EncrptDpapi(value) : value;
+ NotifyPropertyChanged(ref _honorbuddyKeyData, ref val, nameof(HonorbuddyKey));
}
}
private string _honorbuddyProfile;
@@ -85,8 +94,9 @@ public string HonorbuddyKey
public string HonorbuddyProfile
{
get { return _honorbuddyProfile; }
- set { _honorbuddyProfile = value; NotifyPropertyChanged("HonorbuddyProfile"); }
+ set { NotifyPropertyChanged(ref _honorbuddyProfile, ref value, nameof(HonorbuddyProfile)); }
}
+
private string _customClass;
///
/// The Honorbuddy CustomClass to use. It can be left empty
@@ -94,7 +104,7 @@ public string HonorbuddyProfile
public string CustomClass
{
get { return _customClass; }
- set { _customClass = value; NotifyPropertyChanged("CustomClass"); }
+ set { NotifyPropertyChanged(ref _customClass, ref value, nameof(CustomClass)); }
}
private string _honorbuddyArgs;
@@ -104,33 +114,53 @@ public string CustomClass
public string HonorbuddyArgs
{
get { return _honorbuddyArgs; }
- set { _honorbuddyArgs = value; NotifyPropertyChanged("HonorbuddyArgs"); }
+ set { NotifyPropertyChanged(ref _honorbuddyArgs, ref value, nameof(HonorbuddyArgs)); }
}
private bool _useHBBeta;
- ///
+
+ ///
/// The Honorbuddy CustomClass to use. It can be left empty
///
public bool UseHBBeta
{
get { return _useHBBeta; }
- set { _useHBBeta = value; NotifyPropertyChanged("UseHBBeta"); }
- }
- public HonorbuddySettings ShadowCopy()
- {
- return (HonorbuddySettings)MemberwiseClone();
+ set { NotifyPropertyChanged(ref _useHBBeta, ref value, nameof(UseHBBeta)); }
}
+ public override SettingsBase ShadowCopy() => (HonorbuddySettings) MemberwiseClone();
- public event PropertyChangedEventHandler PropertyChanged;
- private void NotifyPropertyChanged(string name)
+ public override void LoadFromXml(XElement element)
{
- if (PropertyChanged != null)
+ try
+ {
+ IsLoaded = false;
+ HonorbuddyKeyData = GetElementValue(element.Element("HonorbuddyKeyData"));
+ CustomClass = GetElementValue(element.Element("CustomClass"));
+ HonorbuddyArgs = GetElementValue(element.Element("HonorbuddyArgs"));
+ BotBase = GetElementValue(element.Element("BotBase"));
+ HonorbuddyProfile = GetElementValue(element.Element("HonorbuddyProfile"));
+ HonorbuddyPath = GetElementValue(element.Element("HonorbuddyPath"));
+ UseHBBeta = GetElementValue(element.Element("UseHBBeta"));
+ }
+ finally
{
- PropertyChanged(this, new PropertyChangedEventArgs(name));
+ IsLoaded = true;
}
- if (HbRelogManager.Settings != null)
- HbRelogManager.Settings.QueueSave();
+ }
+
+ public override XElement ConvertToXml()
+ {
+ var xml = new XElement("HonorbuddySettings");
+ // Honorbuddy Settings
+ xml.Add(new XElement("HonorbuddyKeyData", HonorbuddyKeyData));
+ xml.Add(new XElement("CustomClass", CustomClass));
+ xml.Add(new XElement("HonorbuddyArgs", HonorbuddyArgs));
+ xml.Add(new XElement("BotBase", BotBase));
+ xml.Add(new XElement("HonorbuddyProfile", HonorbuddyProfile));
+ xml.Add(new XElement("HonorbuddyPath", HonorbuddyPath));
+ xml.Add(new XElement("UseHBBeta", UseHBBeta));
+ return xml;
}
}
}
diff --git a/Settings/ProfileSettings.cs b/Settings/ProfileSettings.cs
index 80f6af3..76d169d 100644
--- a/Settings/ProfileSettings.cs
+++ b/Settings/ProfileSettings.cs
@@ -14,11 +14,18 @@ You may obtain a copy of the License at
limitations under the License.
*/
+using HighVoltz.HBRelog.Tasks;
+using System;
+using System.Collections.Generic;
using System.ComponentModel;
+using System.Globalization;
+using System.Linq;
+using System.Reflection;
+using System.Xml.Linq;
namespace HighVoltz.HBRelog.Settings
{
- public class ProfileSettings : INotifyPropertyChanged
+ public class ProfileSettings : SettingsBase
{
public HonorbuddySettings HonorbuddySettings { get; set; }
public WowSettings WowSettings { get; set; }
@@ -36,8 +43,8 @@ public ProfileSettings()
///
public string ProfileName
{
- get { return _profileName; }
- set { _profileName = value; NotifyPropertyChanged("ProfileName"); }
+ get => _profileName;
+ set => NotifyPropertyChanged(ref _profileName, ref value, nameof(ProfileName));
}
private bool _isEnabled;
@@ -46,32 +53,57 @@ public string ProfileName
///
public bool IsEnabled
{
- get { return _isEnabled; }
- set
- {
- _isEnabled = value;
- NotifyPropertyChanged("IsEnabled");
- }
+ get => _isEnabled;
+ set => NotifyPropertyChanged(ref _isEnabled, ref value, nameof(IsEnabled));
}
- public ProfileSettings ShadowCopy()
+ public override SettingsBase ShadowCopy()
{
var settings = (ProfileSettings)MemberwiseClone();
- settings.WowSettings = WowSettings.ShadowCopy();
- settings.HonorbuddySettings = HonorbuddySettings.ShadowCopy();
+ settings.WowSettings = (WowSettings)WowSettings.ShadowCopy();
+ settings.HonorbuddySettings = (HonorbuddySettings)HonorbuddySettings.ShadowCopy();
return settings;
}
- public event PropertyChangedEventHandler PropertyChanged;
- private void NotifyPropertyChanged(string name)
+ public override void LoadFromXml(XElement element)
{
- if (PropertyChanged != null)
+ try
+ {
+ IsLoaded = false;
+ ProfileName = GetElementValue(element.Element("ProfileName"));
+ IsEnabled = GetElementValue(element.Element("IsEnabled"));
+
+ // Wow Settings
+ XElement wowSettingsElement = element.Element("WowSettings");
+ if (wowSettingsElement != null)
+ WowSettings.LoadFromXml(wowSettingsElement);
+
+ // Honorbuddy Settings
+ XElement hbSettingsElement = element.Element("HonorbuddySettings");
+ if (hbSettingsElement != null)
+ HonorbuddySettings.LoadFromXml(hbSettingsElement);
+ }
+ finally
{
- PropertyChanged(this, new PropertyChangedEventArgs(name));
+ IsLoaded = true;
}
- if (HbRelogManager.Settings != null)
- HbRelogManager.Settings.QueueSave();
}
+ public override XElement ConvertToXml()
+ {
+ var xml = new XElement("Settings");
+ xml.Add(new XElement("ProfileName", ProfileName));
+ xml.Add(new XElement("IsEnabled", IsEnabled));
+
+ // Wow Settings
+ var wowSettingsElement = WowSettings.ConvertToXml();
+ xml.Add(wowSettingsElement);
+
+ // Honorbuddy Settings
+ var hbSettingsElement = HonorbuddySettings.ConvertToXml();
+ xml.Add(hbSettingsElement);
+
+ return xml;
+ }
}
}
diff --git a/Settings/SettingsBase.cs b/Settings/SettingsBase.cs
new file mode 100644
index 0000000..1fecbca
--- /dev/null
+++ b/Settings/SettingsBase.cs
@@ -0,0 +1,77 @@
+/*
+Copyright 2012 HighVoltz
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Threading;
+using System.Xml;
+using System.Xml.Linq;
+using System.Xml.Serialization;
+using HighVoltz.HBRelog.Tasks;
+using System.Globalization;
+
+namespace HighVoltz.HBRelog.Settings
+{
+ public abstract class SettingsBase : INotifyPropertyChanged
+ {
+ /// Must be set in the Load overide. It's used for disabling change notification while loading
+ protected bool IsLoaded { get; set; }
+
+ public abstract void LoadFromXml(XElement element);
+ public abstract XElement ConvertToXml();
+
+ public abstract SettingsBase ShadowCopy();
+
+ #region INotifyPropertyChanged
+
+ public event PropertyChangedEventHandler PropertyChanged;
+ protected bool NotifyPropertyChanged(ref T oldValue, ref T newValue, string propertyName)
+ {
+ if (Equals(oldValue, newValue))
+ return false;
+ oldValue = newValue;
+ if (IsLoaded)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ if (HbRelogManager.Settings != null)
+ HbRelogManager.Settings.QueueSave();
+ }
+ return true;
+ }
+
+ #endregion
+
+ protected T GetElementValue(XElement element, T defaultValue = default(T))
+ {
+ if (element != null)
+ {
+ if (defaultValue is Enum)
+ {
+ return (T)Enum.Parse(typeof(T), element.Value);
+ }
+ return (T)Convert.ChangeType(element.Value, typeof(T));
+ }
+ return defaultValue;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Settings/WowSettings.cs b/Settings/WowSettings.cs
index 89b09d6..1f0048d 100644
--- a/Settings/WowSettings.cs
+++ b/Settings/WowSettings.cs
@@ -19,10 +19,11 @@ limitations under the License.
using System.Security.Cryptography;
using System.Text;
using System.Windows;
+using System.Xml.Linq;
namespace HighVoltz.HBRelog.Settings
{
- public class WowSettings : INotifyPropertyChanged
+ public class WowSettings : SettingsBase
{
public WowSettings()
{
@@ -33,7 +34,10 @@ public WowSettings()
AcountName = "WoW1";
Region = WowRegion.Auto;
}
- public string LoginData { get; set; }
+
+ private string _loginData;
+ public string LoginData { get { return _loginData; } set { _loginData = value; } }
+
///
/// The Battlenet email address
///
@@ -43,198 +47,272 @@ public string Login
{
try
{
- return Utility.DecrptDpapi(LoginData);
+ return GlobalSettings.Instance.EncryptSettings
+ ? Utility.DecrptDpapi(LoginData)
+ : LoginData;
}
catch
{
// this error can occur if the Windows password was changed or profile was copied to another computer
- MessageBox.Show(string.Format("Error decrypting login for {0}. Try setting login again.",
- CharacterName));
+ MessageBox.Show($"Error decrypting login for {CharacterName}. Try setting login again.");
return "";
}
}
set
{
- LoginData = Utility.EncrptDpapi(value);
- NotifyPropertyChanged("Login");
+ string val = GlobalSettings.Instance.EncryptSettings ? Utility.EncrptDpapi(value) : value;
+ NotifyPropertyChanged(ref _loginData, ref val, nameof(Login));
}
}
- public string PasswordData { get; set; }
+ private string _passwordData;
+ public string PasswordData { get { return _passwordData; } set { _passwordData = value; } }
+
public string Password
{
get
{
try
{
- var pass = Utility.DecrptDpapi(PasswordData);
- return Utility.DecrptDpapi(PasswordData).Substring(0, Math.Min(16, pass.Length));
+ if (!GlobalSettings.Instance.EncryptSettings)
+ return PasswordData;
+
+ var pass = Utility.DecrptDpapi(PasswordData);
+ return Utility.DecrptDpapi(PasswordData).Substring(0, Math.Min(16, pass.Length));
}
catch
{
- MessageBox.Show(string.Format("Error decrypting password for {0}. Try setting password again.", CharacterName));
+ MessageBox.Show($"Error decrypting password for {CharacterName}. Try setting password again.");
return "";
}
}
set
{
- PasswordData = Utility.EncrptDpapi(value);
- NotifyPropertyChanged("Password");
+ string val = GlobalSettings.Instance.EncryptSettings ? Utility.EncrptDpapi(value) : value;
+ NotifyPropertyChanged(ref _passwordData, ref val, nameof(Password));
}
}
private string _acountName;
+
///
/// The name of the wow account. Only used if the battlenet acount has 2+ acounts attached.
///
public string AcountName
{
get { return _acountName; }
- set { _acountName = value; NotifyPropertyChanged("AcountName"); }
+ set { NotifyPropertyChanged(ref _acountName, ref value, nameof(AcountName)); }
}
- public string AuthenticatorRestoreCodeData { get; set; }
+ private string _authenticatorRestoreCodeData;
+
+ public string AuthenticatorRestoreCodeData
+ {
+ get { return _authenticatorRestoreCodeData; }
+ set { _authenticatorRestoreCodeData = value; }
+ }
public string AuthenticatorRestoreCode
{
- get
- {
- if (string.IsNullOrEmpty(AuthenticatorRestoreCodeData))
- return "";
- try
- {
- return Utility.DecrptDpapi(AuthenticatorRestoreCodeData);
- }
- catch
- {
- MessageBox.Show(string.Format("Error decrypting Authenticator Restore code for {0}. Try setting the restore code again.", CharacterName));
- return "";
- }
- }
- set
- {
- AuthenticatorRestoreCodeData = Utility.EncrptDpapi(value);
- NotifyPropertyChanged("AuthenticatorRestoreCode");
- }
- }
-
- public string AuthenticatorSerialData { get; set; }
-
- public string AuthenticatorSerial
- {
- get
- {
- if (string.IsNullOrEmpty(AuthenticatorSerialData))
- return "";
- try
- {
- return Utility.DecrptDpapi(AuthenticatorSerialData);
- }
- catch
- {
- MessageBox.Show(string.Format("Error decrypting Authenticator Serial for {0}. Try setting the serial again.", CharacterName));
- return "";
- }
- }
- set
- {
- AuthenticatorSerialData = Utility.EncrptDpapi(value);
- NotifyPropertyChanged("AuthenticatorSerial");
- }
- }
+ get
+ {
+ if (string.IsNullOrEmpty(AuthenticatorRestoreCodeData))
+ return "";
+ try
+ {
+ return GlobalSettings.Instance.EncryptSettings
+ ? Utility.DecrptDpapi(AuthenticatorRestoreCodeData)
+ : AuthenticatorRestoreCodeData;
+ }
+ catch
+ {
+ MessageBox.Show(
+ $"Error decrypting Authenticator Restore code for {CharacterName}. Try setting the restore code again.");
+ return "";
+ }
+ }
+ set
+ {
+ string val = GlobalSettings.Instance.EncryptSettings ? Utility.EncrptDpapi(value) : value;
+ NotifyPropertyChanged(ref _authenticatorRestoreCodeData, ref val,
+ nameof(AuthenticatorRestoreCode));
+ }
+ }
+
+ private string _authenticatorSerialData;
+
+ public string AuthenticatorSerialData
+ {
+ get { return _authenticatorSerialData; }
+ set { _authenticatorSerialData = value; }
+ }
+
+ public string AuthenticatorSerial
+ {
+ get
+ {
+ if (string.IsNullOrEmpty(AuthenticatorSerialData))
+ return "";
+ try
+ {
+ return GlobalSettings.Instance.EncryptSettings
+ ? Utility.DecrptDpapi(AuthenticatorSerialData)
+ : AuthenticatorSerialData;
+ }
+ catch
+ {
+ MessageBox.Show(
+ $"Error decrypting Authenticator Serial for {CharacterName}. Try setting the serial again.");
+ return "";
+ }
+ }
+ set
+ {
+ string val = GlobalSettings.Instance.EncryptSettings ? Utility.EncrptDpapi(value) : value;
+ NotifyPropertyChanged(ref _authenticatorSerialData, ref val, nameof(AuthenticatorSerial));
+ }
+ }
private string _characterName;
+
///
/// The in-game character name
///
public string CharacterName
{
get { return _characterName; }
- set { _characterName = value; NotifyPropertyChanged("CharacterName"); }
+ set { NotifyPropertyChanged(ref _characterName, ref value, nameof(CharacterName)); }
}
private string _serverName;
+
///
/// Name of the WoW server
///
public string ServerName
{
get { return _serverName; }
- set { _serverName = value; NotifyPropertyChanged("ServerName"); }
+ set { NotifyPropertyChanged(ref _serverName, ref value, nameof(ServerName)); }
}
+
private string _wowPath;
+
///
/// Path to your WoW.Exe
///
public string WowPath
{
get { return _wowPath; }
- set { _wowPath = value; NotifyPropertyChanged("WowPath"); }
+ set { NotifyPropertyChanged(ref _wowPath, ref value, nameof(WowPath)); }
}
private string _wowArgs;
+
///
/// Command-line arguments to pass to WoW or launcher (Advanced)
///
public string WowArgs
{
get { return _wowArgs; }
- set { _wowArgs = value; NotifyPropertyChanged("WowArgs"); }
+ set { NotifyPropertyChanged(ref _wowArgs, ref value, nameof(WowArgs)); }
}
private int _wowWindowWidth;
+
public int WowWindowWidth
{
get { return _wowWindowWidth; }
- set { _wowWindowWidth = value; NotifyPropertyChanged("WowWindowWidth"); }
+ set { NotifyPropertyChanged(ref _wowWindowWidth, ref value, nameof(WowWindowWidth)); }
}
private int _wowWindowHeight;
+
public int WowWindowHeight
{
get { return _wowWindowHeight; }
- set { _wowWindowHeight = value; NotifyPropertyChanged("WowWindowHeight"); }
+ set { NotifyPropertyChanged(ref _wowWindowHeight, ref value, nameof(WowWindowHeight)); }
}
private int _wowWindowX;
+
public int WowWindowX
{
get { return _wowWindowX; }
- set { _wowWindowX = value; NotifyPropertyChanged("WowWindowX"); }
+ set { NotifyPropertyChanged(ref _wowWindowX, ref value, nameof(WowWindowX)); }
}
private int _wowWindowY;
+
public int WowWindowY
{
get { return _wowWindowY; }
- set { _wowWindowY = value; NotifyPropertyChanged("WowWindowY"); }
+ set { NotifyPropertyChanged(ref _wowWindowY, ref value, nameof(WowWindowY)); }
}
private WowRegion _region;
+
public WowRegion Region
{
get { return _region; }
- set { _region = value; NotifyPropertyChanged("Region"); }
+ set { NotifyPropertyChanged(ref _region, ref value, nameof(Region)); }
}
- public WowSettings ShadowCopy()
+ public override SettingsBase ShadowCopy()
{
- return (WowSettings)MemberwiseClone();
+ return (SettingsBase)MemberwiseClone();
}
- public event PropertyChangedEventHandler PropertyChanged;
- private void NotifyPropertyChanged(string name)
+ public override void LoadFromXml(XElement element)
{
- if (PropertyChanged != null)
+ try
{
- PropertyChanged(this, new PropertyChangedEventArgs(name));
+ IsLoaded = false;
+ LoginData = GetElementValue(element.Element("LoginData"));
+ PasswordData = GetElementValue(element.Element("PasswordData"));
+ AcountName = GetElementValue(element.Element("AcountName"));
+ CharacterName = GetElementValue(element.Element("CharacterName"));
+ ServerName = GetElementValue(element.Element("ServerName"));
+ AuthenticatorSerialData = GetElementValue(element.Element("AuthenticatorSerialData"));
+ AuthenticatorRestoreCodeData = GetElementValue(element.Element("AuthenticatorRestoreCodeData"));
+ Region = GetElementValue(element.Element("Region"));
+ WowPath = GetElementValue(element.Element("WowPath"));
+ WowArgs = GetElementValue(element.Element("WowArgs"));
+ WowWindowWidth = GetElementValue(element.Element("WowWindowWidth"));
+ WowWindowHeight = GetElementValue(element.Element("WowWindowHeight"));
+ WowWindowX = GetElementValue(element.Element("WowWindowX"));
+ WowWindowY = GetElementValue(element.Element("WowWindowY"));
}
- if (HbRelogManager.Settings != null)
- HbRelogManager.Settings.QueueSave();
+ finally
+ {
+ IsLoaded = true;
+ }
+ }
+
+ public override XElement ConvertToXml()
+ {
+ var xml = new XElement("WowSettings");
+ // Wow Settings
+ xml.Add(new XElement("LoginData", LoginData));
+ xml.Add(new XElement("PasswordData", PasswordData));
+ xml.Add(new XElement("AcountName", AcountName));
+ xml.Add(new XElement("CharacterName", CharacterName));
+ xml.Add(new XElement("ServerName", ServerName));
+ xml.Add(new XElement("AuthenticatorSerialData", AuthenticatorSerialData));
+ xml.Add(new XElement("AuthenticatorRestoreCodeData", AuthenticatorRestoreCodeData));
+ xml.Add(new XElement("Region", Region));
+ xml.Add(new XElement("WowPath", WowPath));
+ xml.Add(new XElement("WowArgs", WowArgs));
+ xml.Add(new XElement("WowWindowWidth", WowWindowWidth));
+ xml.Add(new XElement("WowWindowHeight", WowWindowHeight));
+ xml.Add(new XElement("WowWindowX", WowWindowX));
+ xml.Add(new XElement("WowWindowY", WowWindowY));
+ return xml;
}
+
#region Embeded type - WowRegion
+
public enum WowRegion
{
Auto,
@@ -244,6 +322,7 @@ public enum WowRegion
China,
Taiwan
}
+
#endregion
}
diff --git a/Tasks/BMTask.cs b/Tasks/BMTask.cs
index 83d692a..28c5121 100644
--- a/Tasks/BMTask.cs
+++ b/Tasks/BMTask.cs
@@ -83,8 +83,6 @@ protected BMTask PrevTask
protected Process BotProcess { get { return Profile.TaskManager.HonorbuddyManager.BotProcess; } }
- protected Process GameProcess { get { return Profile.TaskManager.WowManager.GameProcess; } }
-
public void SetProfile(CharacterProfile profile)
{
Profile = profile;
diff --git a/Tasks/ChangeProfileTask.cs b/Tasks/ChangeProfileTask.cs
index 0b71557..2def7e1 100644
--- a/Tasks/ChangeProfileTask.cs
+++ b/Tasks/ChangeProfileTask.cs
@@ -18,6 +18,7 @@ limitations under the License.
using System.IO;
using System.Xml.Serialization;
using HighVoltz.HBRelog.Controls;
+using HighVoltz.HBRelog.Settings;
namespace HighVoltz.HBRelog.Tasks
{
@@ -67,7 +68,7 @@ public override void Pulse()
Profile.Settings.ProfileName, ProfilePath, !string.IsNullOrEmpty(Bot) ? "switching to bot " + Bot : "using current bot");
Profile.Status = "Changing to Honorbuddy profile: " + Path.GetFileNameWithoutExtension(ProfilePath);
Profile.TaskManager.HonorbuddyManager.Stop();
- var hbSettings = Profile.Settings.HonorbuddySettings.ShadowCopy();
+ var hbSettings = (HonorbuddySettings)Profile.Settings.HonorbuddySettings.ShadowCopy();
hbSettings.HonorbuddyProfile = ProfilePath;
if (!string.IsNullOrEmpty(Bot))
hbSettings.BotBase = Bot;
diff --git a/Tasks/IdleTask.cs b/Tasks/IdleTask.cs
index a5e7f6b..8c1dae9 100644
--- a/Tasks/IdleTask.cs
+++ b/Tasks/IdleTask.cs
@@ -20,40 +20,32 @@ namespace HighVoltz.HBRelog.Tasks
{
public class IdleTask : BMTask
{
- #region Overrides
+ #region Overrides
- [XmlIgnore]
- public override string Name
- {
- get { return "Idle"; }
- }
+ [XmlIgnore]
+ public override string Name { get { return "Idle"; } }
- [XmlIgnore]
- public override string Help
- {
- get { return "Logs out of Wow for a duration then logs back in"; }
- }
+ [XmlIgnore]
+ public override string Help { get { return "Logs out of Wow for a duration then logs back in"; } }
- private string _toolTip;
+ private string _toolTip;
+
+ [XmlIgnore]
+ public override string ToolTip
+ {
+ get { return _toolTip ?? (ToolTip = string.Format("Idle: {0} minutes", Minutes)); }
+ set
+ {
+ if (value != _toolTip)
+ {
+ _toolTip = value;
+ OnPropertyChanged("ToolTip");
+ }
+ }
+ }
- [XmlIgnore]
- public override string ToolTip
- {
- get
- {
- return _toolTip ?? (ToolTip = string.Format("Idle: {0} minutes", Minutes));
- }
- set
- {
- if (value != _toolTip)
- {
- _toolTip = value;
- OnPropertyChanged("ToolTip");
- }
- }
- }
- private TimeSpan _waitTime = new TimeSpan(0);
+ private TimeSpan _waitTime = new TimeSpan(0);
private DateTime _timeStamp;
public override void Pulse()
@@ -66,16 +58,12 @@ public override void Pulse()
Profile.TaskManager.WowManager.Stop();
Profile.TaskManager.HonorbuddyManager.Stop();
}
- Profile.Status = string.Format(
- "Idling for {0} minutes",
- (int) ((_waitTime - (DateTime.Now - _timeStamp)).TotalMinutes));
- ToolTip = Profile.Status;
+ TimeSpan left = _waitTime - (DateTime.Now - _timeStamp);
+ Profile.Status = $"Idling for {(int)left.TotalMinutes} minutes {left.Seconds} seconds";
+ ToolTip = Profile.Status;
if (DateTime.Now - _timeStamp >= _waitTime)
{
- IsDone = true;
- // if next task is not a LogonTask then we log back in game on same character.
- if (!(NextTask is LogonTask))
- Profile.Start();
+ Stop();
Profile.Log("Idle complete");
}
}
@@ -87,33 +75,47 @@ public override void Reset()
ToolTip = string.Format("Idle: {0} minutes", Minutes);
}
- #endregion
-
- private int _minutes;
- public int Minutes
- {
- get { return _minutes; }
- set
- {
- if (value == _minutes) return;
- _minutes = value;
- // invalidate the tooltip.
- _toolTip = null;
- }
- }
-
- private int _randomMinutes;
- public int RandomMinutes
- {
- get { return _randomMinutes; }
- set
- {
- if (value == _randomMinutes) return;
- _randomMinutes = value;
- // invalidate the tooltip.
- _toolTip = null;
- }
- }
+ public override void Stop()
+ {
+ // if next task is not a LogonTask then we log back in game on same character.
+ if (!(NextTask is LogonTask))
+ {
+ Profile.TaskManager.WowManager.SetSettings(Profile.Settings.WowSettings);
+ Profile.TaskManager.WowManager.Start();
+ }
+ base.Stop();
+ }
+
+ #endregion
+
+ private double _minutes;
+
+ public double Minutes
+ {
+ get { return _minutes; }
+ set
+ {
+ if (value == _minutes)
+ return;
+ _minutes = value;
+ // invalidate the tooltip.
+ _toolTip = null;
+ }
+ }
+
+ private int _randomMinutes;
+ public int RandomMinutes
+ {
+ get { return _randomMinutes; }
+ set
+ {
+ if (value == _randomMinutes)
+ return;
+ _randomMinutes = value;
+ // invalidate the tooltip.
+ _toolTip = null;
+ }
+ }
}
-}
+}
\ No newline at end of file
diff --git a/Tasks/LogonTask.cs b/Tasks/LogonTask.cs
index 7691bfd..7efc482 100644
--- a/Tasks/LogonTask.cs
+++ b/Tasks/LogonTask.cs
@@ -16,6 +16,7 @@ limitations under the License.
using System.Xml.Serialization;
using HighVoltz.HBRelog.Controls;
+using HighVoltz.HBRelog.Settings;
namespace HighVoltz.HBRelog.Tasks
{
@@ -69,8 +70,8 @@ public override void Pulse()
{
if (!_runOnce)
{
- var wowSettings = Profile.Settings.WowSettings.ShadowCopy();
- var hbSettings = Profile.Settings.HonorbuddySettings.ShadowCopy();
+ var wowSettings = (WowSettings)Profile.Settings.WowSettings.ShadowCopy();
+ var hbSettings = (HonorbuddySettings)Profile.Settings.HonorbuddySettings.ShadowCopy();
if (!string.IsNullOrEmpty(CharacterName))
wowSettings.CharacterName = CharacterName;
diff --git a/Tasks/WaitTask.cs b/Tasks/WaitTask.cs
index c5c0afa..3e7d5c4 100644
--- a/Tasks/WaitTask.cs
+++ b/Tasks/WaitTask.cs
@@ -65,15 +65,10 @@ public override void Pulse()
_timeStamp = DateTime.Now;
}
- BMTask nextTask = NextTask;
- if (nextTask != null)
- ToolTip = string.Format(
- "Running {0} task in {1} minutes",
- nextTask,
- (int) ((_waitTime - (DateTime.Now - _timeStamp)).TotalMinutes));
+ TimeSpan left = _waitTime - (DateTime.Now - _timeStamp);
+ ToolTip = $"Waiting {(int)left.TotalMinutes} minutes {left.Seconds} seconds";
-
- if (DateTime.Now - _timeStamp >= _waitTime)
+ if (DateTime.Now - _timeStamp >= _waitTime)
{
IsDone = true;
Profile.Log("Wait complete");
@@ -87,22 +82,22 @@ public override void Reset()
_waitTime = new TimeSpan(0);
}
- #endregion
-
- private int _minutes;
- public int Minutes
- {
- get { return _minutes; }
- set
- {
- if (value == _minutes) return;
- _minutes = value;
- // invalidate the tooltip.
- _toolTip = null;
- }
- }
-
- private int _randomMinutes;
+ #endregion
+
+ private double _minutes;
+ public double Minutes
+ {
+ get { return _minutes; }
+ set
+ {
+ if (value == _minutes) return;
+ _minutes = value;
+ // invalidate the tooltip.
+ _toolTip = null;
+ }
+ }
+
+ private int _randomMinutes;
public int RandomMinutes
{
get { return _randomMinutes; }
diff --git a/Tools/PsExec.exe b/Tools/PsExec.exe
new file mode 100644
index 0000000..446a8df
Binary files /dev/null and b/Tools/PsExec.exe differ
diff --git a/Utility.cs b/Utility.cs
index c6872c3..b44aaeb 100644
--- a/Utility.cs
+++ b/Utility.cs
@@ -15,15 +15,19 @@ limitations under the License.
*/
using System;
+using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
+using System.Security.AccessControl;
using System.Security.Cryptography;
+using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using System.Windows.Forms;
namespace HighVoltz.HBRelog
{
@@ -44,17 +48,6 @@ public static bool HasInternetConnection
}
}
- public static string EncodeToUTF8(this string text)
- {
- var buffer = new StringBuilder(Encoding.UTF8.GetByteCount(text)*2);
- var utf8Encoded = Encoding.UTF8.GetBytes(text);
- foreach (var b in utf8Encoded)
- {
- buffer.Append(string.Format("\\{0:D3}", b));
- }
- return buffer.ToString();
- }
-
public static void UnblockFileIfZoneRestricted(string file)
{
if (!File.Exists(file))
@@ -70,7 +63,9 @@ public static void UnblockFileIfZoneRestricted(string file)
public static void ResizeAndMoveWindow(IntPtr hWnd, int x, int y, int width, int height)
{
NativeMethods.SetWindowPos(hWnd, new IntPtr(0), x, y, width, height,
- NativeMethods.SetWindowPosFlags.SWP_NOZORDER | NativeMethods.SetWindowPosFlags.SWP_NOACTIVATE);
+ NativeMethods.SetWindowPosFlags.SWP_NOZORDER |
+ NativeMethods.SetWindowPosFlags.SWP_NOACTIVATE |
+ NativeMethods.SetWindowPosFlags.SWP_ASYNCWINDOWPOS);
}
public static NativeMethods.Rect GetWindowRect(IntPtr hWnd)
@@ -97,9 +92,48 @@ public static NativeMethods.WindowInfo GetWindowInfo(IntPtr hWnd)
public static Process GetChildProcessByName(int parentPid, string processName)
{
var processes = Process.GetProcessesByName(processName);
- return processes.FirstOrDefault(process => IsChildProcessOf(parentPid, process));
+ var result = processes.FirstOrDefault(process => IsChildProcessOf(parentPid, process));
+
+ // Do proper cleanup
+ foreach (var proc in processes.Where(p => p != result))
+ proc.Dispose();
+
+ if (result != null)
+ return result;
+
+ processes = Process.GetProcesses();
+
+ // search for a process whose exe was renamed.
+ result = (from proc in processes
+ let procPath = GetProcessPath(proc)
+ where !string.IsNullOrEmpty(procPath) && File.Exists(procPath)
+ let exeOriginalNameWithExtention = FileVersionInfo.GetVersionInfo(procPath).OriginalFilename
+ where !string.IsNullOrEmpty(exeOriginalNameWithExtention)
+ let exeOriginalName = Path.GetFileNameWithoutExtension(exeOriginalNameWithExtention)
+ where exeOriginalName != null && exeOriginalName.Equals(processName, StringComparison.OrdinalIgnoreCase) && IsChildProcessOf(parentPid, proc)
+ select proc).FirstOrDefault();
+
+ // Do proper cleanup
+ foreach (var proc in processes.Where(p => p != result))
+ proc.Dispose();
+
+ return result;
}
+ private static string GetProcessPath(Process proc)
+ {
+ // Wrapped in a try/catch since some processes, such as those that are started in suspend state,
+ // will throw exceptions when MainModule is accessed.
+ try
+ {
+ return proc.MainModule.FileName;
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ }
+
public static bool IsChildProcessOf(int parentPid, Process child)
{
var childPid = child.Id;
@@ -211,6 +245,33 @@ public static bool SendBackgroundKey(IntPtr hWnd, char key, bool useVmChar = tru
SendMessage(hWnd, NativeMethods.Message.KEY_UP, key, lParam);
}
+ public static bool KeyDown(IntPtr hWnd, Keys key)
+ {
+ var scanCode = NativeMethods.MapVirtualKey((uint)key, 0);
+ var lParam = (UIntPtr)(0x00000001 | (scanCode << 16));
+ return SendMessage(hWnd, NativeMethods.Message.KEY_DOWN, (char)key, lParam);
+ }
+
+ public static bool KeyUp(IntPtr hWnd, Keys key)
+ {
+ var scanCode = NativeMethods.MapVirtualKey((uint)key, 0);
+ var lParam = (UIntPtr)(0x00000001 | (scanCode << 16));
+ return SendMessage(hWnd, NativeMethods.Message.KEY_UP, (char)key, lParam);
+ }
+
+
+ public static bool PressKey(IntPtr hWnd, Keys key, TimeSpan downTime)
+ {
+ if (!KeyDown(hWnd, key))
+ return false;
+
+ Thread.Sleep(downTime);
+ if (!KeyUp(hWnd, key))
+ return false;
+
+ return true;
+ }
+
public static void SendBackgroundString(IntPtr hWnd, string str, bool downUp = true)
{
foreach (var chr in str)
@@ -247,7 +308,9 @@ private static bool SendMessage(IntPtr hWnd, NativeMethods.Message msg, char key
{
for (var cnt = 0; cnt < 4; cnt++)
{
- if (NativeMethods.SendMessage(hWnd, (uint) msg, (IntPtr) key, lParam) != IntPtr.Zero)
+ UIntPtr result;
+ if (NativeMethods.SendMessageTimeout(hWnd, (uint)msg, (IntPtr)key, lParam, NativeMethods.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, 5000, out result) == IntPtr.Zero
+ || result != UIntPtr.Zero)
continue;
return true;
}
@@ -261,43 +324,92 @@ private static void PostMessage(IntPtr hWnd, NativeMethods.Message msg, char key
private const int SizeOfInput = 28;
- public static void LeftClickAtPos(
- IntPtr hWnd, int x, int y, bool doubleClick = false, bool restore = true, Func restoreCondition = null)
+ public static bool BringWindowIntoFocus(IntPtr hWnd, CharacterProfile profile = null)
+ {
+ if (NativeMethods.GetForegroundWindow() == hWnd)
+ return true;
+
+ NativeMethods.SetForegroundWindow(hWnd);
+ var sw = Stopwatch.StartNew();
+ do
+ {
+ if (sw.Elapsed > TimeSpan.FromSeconds(5))
+ {
+ Action log;
+ if (profile != null)
+ log = profile.Log;
+ else
+ log = Log.Write;
+
+ var winName = NativeMethods.GetWindowText(hWnd);
+ log($"Unabled to bring {(!string.IsNullOrEmpty(winName) ? $"{winName} " : "")}window into focus");
+ return false;
+ }
+ Thread.Sleep(10);
+ } while (NativeMethods.GetForegroundWindow() != hWnd);
+ return true;
+ }
+
+ public static bool RightClickAtPos(IntPtr hwnd, int x, int y)
+ {
+ return ClickAtPos(hwnd, x, y, false);
+ }
+
+ public static bool LeftClickAtPos(
+ IntPtr hWnd, int x, int y, bool doubleClick = false, bool restore = true,
+ Func restoreCondition = null, CharacterProfile profile = null)
+ {
+ return ClickAtPos(hWnd, x, y, true, doubleClick, restore, restoreCondition, profile);
+ }
+
+ private static bool ClickAtPos(
+ IntPtr hWnd, int x, int y, bool left, bool doubleClick = false, bool restore = true,
+ Func restoreCondition = null, CharacterProfile profile = null)
{
var wndBounds = GetWindowRect(hWnd);
double fScreenWidth = NativeMethods.GetSystemMetrics(NativeMethods.SystemMetric.SM_CXSCREEN) - 1;
double fScreenHeight = NativeMethods.GetSystemMetrics(NativeMethods.SystemMetric.SM_CYSCREEN) - 1;
- var fx = (wndBounds.Left + x)*(65535.0f/fScreenWidth);
- var fy = (wndBounds.Top + y)*(65535.0f/fScreenHeight);
+ var fx = (wndBounds.Left + x) * (65535.0f / fScreenWidth);
+ var fy = (wndBounds.Top + y) * (65535.0f / fScreenHeight);
- var structInput = new NativeMethods.Input {type = NativeMethods.SendInputEventType.InputMouse};
- structInput.mkhi.mi.dwFlags = NativeMethods.MouseEventFlags.ABSOLUTE | NativeMethods.MouseEventFlags.MOVE |
- NativeMethods.MouseEventFlags.LEFTDOWN |
- NativeMethods.MouseEventFlags.LEFTUP;
- structInput.mkhi.mi.dx = (int) fx;
- structInput.mkhi.mi.dy = (int) fy;
+ var structInput = new NativeMethods.Input { type = NativeMethods.SendInputEventType.InputMouse };
+ structInput.mkhi.mi.dwFlags = NativeMethods.MouseEventFlags.ABSOLUTE |
+ NativeMethods.MouseEventFlags.MOVE;
+
+ structInput.mkhi.mi.dx = (int)fx;
+ structInput.mkhi.mi.dy = (int)fy;
var forefroundWindow = NativeMethods.GetForegroundWindow();
if (restore)
SaveForegroundWindowAndMouse();
+
+ if (!BringWindowIntoFocus(hWnd, profile))
+ return false;
+
try
{
NativeMethods.BlockInput(true);
+ // check one last time if window is still in foreground after we block input.
+ if (NativeMethods.GetForegroundWindow() != hWnd)
+ return false;
- for (var num = 0; forefroundWindow != hWnd && num < 1000; num++)
- {
- NativeMethods.SetForegroundWindow(hWnd);
- Thread.Sleep(10);
- forefroundWindow = NativeMethods.GetForegroundWindow();
- }
+ NativeMethods.SendInput(1, ref structInput, SizeOfInput);
+ Thread.Sleep(80);
+
+ if (left)
+ structInput.mkhi.mi.dwFlags = NativeMethods.MouseEventFlags.LEFTDOWN |
+ NativeMethods.MouseEventFlags.LEFTUP;
+ else
+ structInput.mkhi.mi.dwFlags = NativeMethods.MouseEventFlags.RIGHTDOWN |
+ NativeMethods.MouseEventFlags.RIGHTUP;
NativeMethods.SendInput(1, ref structInput, SizeOfInput);
- Thread.Sleep(100);
+ SleepForMouseInputReaction();
if (doubleClick)
{
NativeMethods.SendInput(1, ref structInput, SizeOfInput);
- Thread.Sleep(100);
+ SleepForMouseInputReaction();
}
}
finally
@@ -323,6 +435,12 @@ public static void LeftClickAtPos(
}
NativeMethods.BlockInput(false);
}
+ return true;
+ }
+
+ private static void SleepForMouseInputReaction()
+ {
+ Thread.Sleep(Rand.Next(100, 150));
}
///
@@ -400,5 +518,27 @@ private static async Task WaitForProcessToExitAsync(Process process, TimeS
}
return false;
}
+
+ public static bool TryGetProcessById(int procId, out Process process)
+ {
+ try
+ {
+ process= Process.GetProcessById(procId);
+ return true;
+ }
+ catch (Exception)
+ {
+ process = null;
+ return false;
+ }
+ }
+
+ private static Random random = new Random();
+ public static string RandomString(int length)
+ {
+ const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ return new string(Enumerable.Repeat(chars, length)
+ .Select(s => s[random.Next(s.Length)]).ToArray());
+ }
}
}
\ No newline at end of file
diff --git a/WoW/FrameXml/EditBox.cs b/WoW/FrameXml/EditBox.cs
index 8aec14a..00c851d 100644
--- a/WoW/FrameXml/EditBox.cs
+++ b/WoW/FrameXml/EditBox.cs
@@ -9,13 +9,13 @@ public class EditBox : Frame, IFontInstance
{
public EditBox(WowManager wowManager, IntPtr address) : base(wowManager, address) { }
- ///
- /// Gets the cursor position. Works correctly with utf8 text.
- ///
- ///
- /// The cursor position.
- ///
- public int CursorPosition
+ ///
+ /// Gets the cursor position. Works correctly with utf8 text.
+ ///
+ ///
+ /// The cursor position.
+ ///
+ public int CursorPosition
{
get
{
@@ -27,48 +27,36 @@ public int CursorPosition
}
}
- public int Flags
- {
- get { return WowManager.Memory.Read(Address + Offsets.EditBox.FlagsOffset); }
- }
+ public EditBoxFlags EditBoxFlags => (EditBoxFlags)WowManager.Memory.Read(Address + Offsets.EditBox.FlagsOffset);
- public bool HasFocus
- {
- get { return Address == WowManager.FocusedWidgetPtr; }
- }
+ public bool HasFocus => Address == WowManager.FocusedWidgetPtr;
- ///
+ ///
/// Gets the max bytes. NOTE: this can return 0.
///
///
/// The max bytes.
///
- public int MaxBytes
- {
- get { return WowManager.Memory.Read(Address + Offsets.EditBox.MaxBytesOffset) + 1; }
- }
+ public int MaxBytes => WowManager.Memory.Read(Address + Offsets.EditBox.MaxBytesOffset) + 1;
- ///
+ ///
/// Gets the max letters.
///
///
/// The max letters.
///
- public int MaxLetters
- {
- get { return WowManager.Memory.Read(Address + Offsets.EditBox.MaxLettersOffset); }
- }
+ public int MaxLetters => WowManager.Memory.Read(Address + Offsets.EditBox.MaxLettersOffset);
- public int NumLetters
+ public int NumLetters
{
get
{
var text = Text;
if (!IsNumeric)
{
- return text != null ? text.Length : 0;
+ return text?.Length ?? 0;
}
- return text != null ? text.Count(char.IsDigit) : 0;
+ return text?.Count(char.IsDigit) ?? 0;
}
}
@@ -97,39 +85,32 @@ public float Number
}
}
- public bool IsAutoFocus
- {
- get { return (Flags & Offsets.EditBox.IsAutoFocus) != 0; }
- }
+ public bool IsAutoFocus => (EditBoxFlags & EditBoxFlags.IsAutoFocus) != 0;
- public bool IsEnabled
- {
- get { return (WowManager.Memory.Read(Address + Offsets.EditBox.IsEnabledFlagOffset) & Offsets.EditBox.IsEnabledBit) == 0; }
- }
+ public bool IsEnabled => (WowManager.Memory.Read(Address + Offsets.EditBox.IsEnabledFlagOffset) & Offsets.EditBox.IsEnabledBit) == 0;
- public bool IsNumeric
- {
- get { return (Flags & Offsets.EditBox.IsNumericBit) != 0; }
- }
+ public bool IsNumeric => (EditBoxFlags & EditBoxFlags.IsNumeric) != 0;
- public bool IsPassword
- {
- get { return (Flags & Offsets.EditBox.IsPasswordBit) != 0; }
- }
+ public bool IsPassword => (EditBoxFlags & EditBoxFlags.IsPassword) != 0;
- public bool IsMultiline
- {
- get { return (Flags & Offsets.EditBox.IsMultilineBit) != 0; }
- }
+ public bool IsMultiline => (EditBoxFlags & EditBoxFlags.IsMultiline) != 0;
- public bool IsCountInvisibleLetters
- {
- get { return (Flags & Offsets.EditBox.IsCountInvisibleLettersBit) != 0; }
- }
+ public bool IsCountInvisibleLetters => (EditBoxFlags & EditBoxFlags.IsCountInvisibleLetters) != 0;
- public Font FontObject { get { throw new NotImplementedException(); } }
+ public Font FontObject { get { throw new NotImplementedException(); } }
public FontInfo FontInfo { get { throw new NotImplementedException(); } }
}
+
+ [Flags]
+ public enum EditBoxFlags
+ {
+ IsAutoFocus = 1,
+ IsMultiline = 1 << 1,
+ IsNumeric = 1 << 2,
+ IsPassword = 1 << 3,
+ IsCountInvisibleLetters = 1 << 5,
+ }
+
}
diff --git a/WoW/FrameXml/Offsets.cs b/WoW/FrameXml/Offsets.cs
index c283445..f3fb7a9 100644
--- a/WoW/FrameXml/Offsets.cs
+++ b/WoW/FrameXml/Offsets.cs
@@ -1,99 +1,118 @@
namespace HighVoltz.HBRelog.WoW.FrameXml
{
- static class Offsets
+ internal static class Offsets
{
public static class UIObject
{
- public const int GetTypeVtmOffset = 0x20;
- public const int GetFontTypeVtmOffset = 0x1C;
- public const int NamePtrOffset = 0x1C;
- public const int FontNamePtrOffset = 0x5C;
+ public const int GetTypeNameVfuncOffset = 0x1C;
+ public const int NamePtrOffset = 20;
+ // public const int FontNamePtrOffset = 0x5C;
}
public static class ParentedObject
{
- public const int ParentOffset = 0x98;
+ public const int ParentOffset = 0x84;
}
public static class Region
{
- public const int UIScaleOffset = 0x80;
- public const int BottomOffset = 0x68;
- public const int LeftOffset = 0x6C;
- public const int TopOffset = 0x70;
- public const int RightOffset = 0x74;
+ // This is actually 'EffectiveScale'
+ public const int UIScaleOffset = 0x78;
+ public const int BottomOffset = 0x4C;
+ public const int LeftOffset = 0x50;
+ public const int TopOffset = 0x54;
+ public const int RightOffset = 0x58;
}
public static class VisibleRegion
{
- public const int FlagsOffset = 0x64;
- public const int IsVisibleRShiftAmount = 26;
- public const int IsShownRShiftAmount = 25;
+ public const int FlagsOffset = 0x80;
+ public const int IsVisibleRShiftAmount = 19;
+ public const int IsShownRShiftAmount = 18;
}
public static class Frame // Inherits ScriptObject and VisibleFrame
{
- public const int ChildrenOffset = 0x1BC;
- public const int RegionsSizeOffset = 0x160;
- public const int RegionsOffset = 0x168;
- public const int IdOffset = 0xC4;
- public const int LevelOffset = 0xD8;
- public const int StrataOffset = 0x1E3;
+ public const int ChildrenOffset = 0x134;
+ public const int RegionsSizeOffset = 0x114;
+ public const int RegionsOffset = 0x11C;
+ public const int IdOffset = 0x94;
+ // In GetFrameLevel function
+ public const int LevelOffset = 0x144;
+ // In Frame::GetFrameStrata function
+ public const int StrataOffset = 0x143;
}
public static class Button // inherits Frame
{
- public const int FlagsOffset = 0x1F0;
- public const int FontStringOffset = 0x1F8;
- public const int HighlightTextureOffset = 0x220;
+ // first offset in Button::GetButtonState function
+ public const int FlagsOffset = 0x158;
+ // first offset in Button::GetFontString function
+ public const int FontStringOffset = 0x160;
+ // first offset in Button::GetHighlightTexture function
+ public const int HighlightTextureOffset = 0x188;
}
public static class FontString
{
- public const int TextOffset = 0xF8;
+ // Offset in FontString:GetText function
+ public const int TextOffset = 0xE8;
}
public static class EditBox // Inherits FontInstance and Frame
{
- public const int FlagsOffset = 0x1F8;
- public const int AsciiCursorPositionOffset = 0x24C;
- public const int TextOffset = 0x20C;
- public const int MaxBytesOffset = 0x21C;
- public const int MaxLettersOffset = 0x220;
- public const int IsEnabledFlagOffset = 0xC8;
- public const int IsEnabledBit = 0x400;
- public const int IsAutoFocus = 0x1;
- public const int IsMultilineBit = 0x2;
- public const int IsNumericBit = 0x4;
- public const int IsPasswordBit = 0x8;
- public const int IsCountInvisibleLettersBit = 0x20;
+ // In EditBox:IsMultiLine function
+ public const int FlagsOffset = 0x158;
+ // In EditBox:GetCursorPosition function
+ public const int AsciiCursorPositionOffset = 0x1B8;
+ // Last offset in EditBox:GetText function
+ public const int TextOffset = 0x16C;
+ // In EditBox:GetMaxBytes function
+ public const int MaxBytesOffset = 0x17C;
+ // In EditBox:GetMaxLetters function
+ public const int MaxLettersOffset = 0x180;
+ public const int IsEnabledFlagOffset = 0x98;
+ public const int IsEnabledBit = 8;
+
}
public static class ScrollFrame // Inherits from Frame
{
- public const int ScrollChildOffset = 0x1E8;
- public const int HorizontalScrollRangeOffset = 0x1EC;
- public const int VerticalScrollRangeOffset = 0x1F0;
- public const int HorizontalScrollOffset = 0x1F4;
- public const int VerticalScrollOffset = 0x1F8;
+ // In ScrollFrame:GetScrollChild function
+ public const int ScrollChildOffset = 0x150;
+ // In ScrollFrame:GetHorizontalScrollRange function
+ public const int HorizontalScrollRangeOffset = 0x154;
+ // In ScrollFrame:GetVerticalScrollRange function
+ public const int VerticalScrollRangeOffset = 0x158;
+ // In ScrollFrame:GetHorizontalScroll function
+ public const int HorizontalScrollOffset = 0x15C;
+ // In ScrollFrame:GetVerticalScroll function
+ public const int VerticalScrollOffset = 0x160;
}
public static class Slider // Inherits from Frame
{
- public const int IsEnabledFlagOffset = 0xC8;
- public const int MinValueOffset = 0x1E4;
- public const int MaxValueOffset = 0x1E8;
- public const int ValueOffset = 0x1EC;
- public const int ValueStepOffset = 0x1F0;
- public const int ThumbTextureOffset = 0x1F8;
- public const int OrientationOffset = 0x1F4;
- public const int IsEnabledBit = 0x400;
+ public const int Flags = 0x9C;
+ // Lowest offset in Slider:GetMinMaxValues()
+ public const int MinValueOffset = 0x14C;
+ // Highest offset in Slider:GetMinMaxValues()
+ public const int MaxValueOffset = 0x150;
+ // In Slider:GetValue function
+ public const int ValueOffset = 0x154;
+ // In Slider:GetValueStep function
+ public const int ValueStepOffset = 0x158;
+ // In Slider:SetThumbTexture function
+ public const int ThumbTextureOffset = 0x160;
+ // In Slider:GetOrientation function
+ public const int OrientationOffset = 0x15C;
}
public static class Texture // Inherits From Layered Region
{
- public const int TexturePathObjectOffset = 0xC0;
- public const int TexturePathOffset = 0x18;
+ // In Texture:GetTexture go to the 3rd function call (it has one argument). Offset is the first offset used in this function
+ public const int TexturePathObjectOffset = 0xAC;
+ // In Texture:GetTexture go to the 3rd function call (it has one argument), and then go inside the first and only function call. Offset is the only offset in this function.
+ public const int TexturePathOffset = 0x188;
}
}
diff --git a/WoW/FrameXml/Slider.cs b/WoW/FrameXml/Slider.cs
index 131018a..3532298 100644
--- a/WoW/FrameXml/Slider.cs
+++ b/WoW/FrameXml/Slider.cs
@@ -7,12 +7,13 @@ public class Slider : Frame
{
public Slider(WowManager wowManager, IntPtr address) : base(wowManager, address) { }
- public bool IsEnabled
- {
- get { return (WowManager.Memory.Read(Address + Offsets.Slider.IsEnabledFlagOffset) & Offsets.Slider.IsEnabledBit) == 0; }
- }
+ public bool IsEnabled => (SliderFlags & SliderFlags.Disabled) == 0;
+
+
+ public SliderFlags SliderFlags => (SliderFlags)WowManager.Memory.Read(Address + Offsets.Slider.Flags);
- public Orientation Orientation
+
+ public Orientation Orientation
{
get
{
@@ -21,28 +22,16 @@ public Orientation Orientation
}
}
- public float MinValue
- {
- get { return WowManager.Memory.Read(Address + Offsets.Slider.MinValueOffset); }
- }
+ public float MinValue => WowManager.Memory.Read(Address + Offsets.Slider.MinValueOffset);
- public float MaxValue
- {
- get { return WowManager.Memory.Read(Address + Offsets.Slider.MaxValueOffset) + MinValue; }
- }
+ public float MaxValue => WowManager.Memory.Read(Address + Offsets.Slider.MaxValueOffset) + MinValue;
- public float Value
- {
- get { return WowManager.Memory.Read(Address + Offsets.Slider.ValueOffset) + MinValue; }
- }
+ public float Value => WowManager.Memory.Read(Address + Offsets.Slider.ValueOffset) + MinValue;
- public float ValueStep
- {
- get { return WowManager.Memory.Read(Address + Offsets.Slider.ValueStepOffset) + MinValue; }
- }
+ public float ValueStep => WowManager.Memory.Read(Address + Offsets.Slider.ValueStepOffset) + MinValue;
- public Texture ThumbTexture
+ public Texture ThumbTexture
{
get
{
@@ -51,4 +40,10 @@ public Texture ThumbTexture
}
}
}
+
+ [Flags]
+ public enum SliderFlags
+ {
+ Disabled = 1 << 3,
+ }
}
diff --git a/WoW/FrameXml/UIObject.cs b/WoW/FrameXml/UIObject.cs
index 68e5de5..ae8c2bc 100644
--- a/WoW/FrameXml/UIObject.cs
+++ b/WoW/FrameXml/UIObject.cs
@@ -28,7 +28,7 @@ public string Name
{
if (!_triedGetName)
{
- var idx = Type != UIObjectType.Font ? Offsets.UIObject.NamePtrOffset : Offsets.UIObject.FontNamePtrOffset;
+ var idx = Offsets.UIObject.NamePtrOffset;
var ptr = WowManager.Memory.Read(Address + idx);
_name = ptr != IntPtr.Zero ? WowManager.Memory.ReadString(ptr, Encoding.UTF8, 128) : "";
_triedGetName = true;
@@ -55,13 +55,10 @@ public static bool IsUIObject(LuaTable table, out IntPtr lightUserDataPtr)
private static void SetObjectType(ExternalProcessReader memory, IntPtr ptr)
{
- var vtmPtr = memory.Read(ptr + Offsets.UIObject.GetTypeVtmOffset);
- if (!IsValidTypePtr(memory, vtmPtr))
- vtmPtr = memory.Read(ptr + Offsets.UIObject.GetFontTypeVtmOffset);
-
+ var vtmPtr = memory.Read(ptr + Offsets.UIObject.GetTypeNameVfuncOffset);
if (IsValidTypePtr(memory, vtmPtr))
{
- var strPtr = memory.Read(false, vtmPtr + 1, IntPtr.Zero);
+ var strPtr = memory.Read(false, vtmPtr + 1);
var str = memory.ReadString(strPtr, Encoding.UTF8, 128);
UIObjectTypeCache[ptr] = GetUIObjectTypeFromString(str);
}
@@ -152,7 +149,7 @@ private static bool IsValidTypePtr(ExternalProcessReader memory, IntPtr ptr)
try
{
var bytes = memory.ReadBytes(ptr, 6);
- return bytes[0] == 0xA1 /* mov */&& bytes[5] == 0xC3 /* retn */;
+ return bytes[0] == 0xB8 /* mov */&& bytes[5] == 0xC3 /* retn */;
}
catch
{
@@ -185,8 +182,8 @@ public static T GetUIObjectByName(WowManager wowManager, string name) where T
{
if (wowManager == null) throw new ArgumentException("wowManager is null", "wowManager");
if (wowManager.Globals == null) throw new ArgumentException("wowManager.Globals is null", "wowManager.Globals");
- var value = wowManager.Globals.GetValue(name);
- if (value == null || value.Type != LuaType.Table)
+ var value = wowManager.GetLuaObject(name);
+ if (value == null || value.Type != LuaType.Table)
{
return null;
}
@@ -215,6 +212,7 @@ public static UIObject GetUIObjectFromPointer(WowManager wowManager, IntPtr addr
switch (type)
{
case UIObjectType.Button:
+ case UIObjectType.CheckButton:
return new Button(wowManager, address) { Type = type };
case UIObjectType.EditBox:
return new EditBox(wowManager, address) { Type = type };
@@ -226,7 +224,9 @@ public static UIObject GetUIObjectFromPointer(WowManager wowManager, IntPtr addr
return new Frame(wowManager, address) { Type = type };
case UIObjectType.ScrollFrame:
return new ScrollFrame(wowManager, address) { Type = type };
- case UIObjectType.Slider:
+ case UIObjectType.SimpleHTML:
+ return new Frame(wowManager, address) { Type = type };
+ case UIObjectType.Slider:
return new Slider(wowManager, address) { Type = type };
case UIObjectType.Texture:
return new Texture(wowManager, address) { Type = type };
diff --git a/WoW/States/CharacterCreationState.cs b/WoW/States/CharacterCreationState.cs
index c69503e..8e46dd0 100644
--- a/WoW/States/CharacterCreationState.cs
+++ b/WoW/States/CharacterCreationState.cs
@@ -19,7 +19,7 @@ public CharacterCreationState(WowManager wowManager)
public override int Priority
{
- get { return 400; }
+ get { return 300; }
}
public override bool NeedToRun
@@ -28,8 +28,8 @@ public override bool NeedToRun
{
return (_wowManager.GameProcess != null && !_wowManager.GameProcess.HasExitedSafe())
&& !_wowManager.StartupSequenceIsComplete && !_wowManager.InGame
- && !_wowManager.IsConnectiongOrLoading
- && _wowManager.GlueStatus == WowManager.GlueState.CharacterCreation;
+ && !_wowManager.IsConnectingOrLoading
+ && _wowManager.GlueScreen == GlueScreen.CharCreate;
}
}
@@ -38,7 +38,7 @@ public override void Run()
var characterCreateFrame = UIObject.GetUIObjectByName(_wowManager, "CharacterCreateFrame");
if (characterCreateFrame != null && characterCreateFrame.IsVisible)
{
- Utility.SendBackgroundKey(_wowManager.GameProcess.MainWindowHandle, (char) Keys.Escape, false);
+ Utility.SendBackgroundKey(_wowManager.GameWindow, (char) Keys.Escape, false);
_wowManager.Profile.Log("Pressing 'esc' key to exit character creation screen");
}
}
diff --git a/WoW/States/CharacterSelectState.cs b/WoW/States/CharacterSelectState.cs
index 1d20d02..8e2c9cd 100644
--- a/WoW/States/CharacterSelectState.cs
+++ b/WoW/States/CharacterSelectState.cs
@@ -24,7 +24,7 @@ public CharacterSelectState(WowManager wowManager)
public override int Priority
{
- get { return 500; }
+ get { return 400; }
}
public override bool NeedToRun
@@ -34,8 +34,8 @@ public override bool NeedToRun
return (_wowManager.GameProcess != null && !_wowManager.GameProcess.HasExitedSafe())
&& !_wowManager.StartupSequenceIsComplete
&& !_wowManager.InGame
- && !_wowManager.IsConnectiongOrLoading
- && _wowManager.GlueStatus == WowManager.GlueState.CharacterSelection;
+ && !_wowManager.IsConnectingOrLoading
+ && _wowManager.GlueScreen == GlueScreen.CharSelect;
}
}
@@ -50,14 +50,18 @@ public override void Run()
_wowManager.Profile.Pause();
return;
}
-
+
// trial account will have a promotion frame that requires clicking a 'Play Trial' button to enter game.
if (ClickPlayTrial())
{
return;
}
- if (ShouldChangeRealm)
+ bool? shouldChangeRealm = ShouldChangeRealm;
+ if (!shouldChangeRealm.HasValue)
+ return;
+
+ if (shouldChangeRealm.Value)
{
ChangeRealm();
return;
@@ -87,7 +91,15 @@ orderby grandParent.Id
if (wantedCharIndex == 0)
{
- _wowManager.Profile.Status = string.Format("Character name: {0} not found. Double check spelling", charName);
+ var inactivecharName = $"{charName} |cffff2020(Inactive)|r";
+ if (characterNames.Any(n => string.Equals(inactivecharName, n, StringComparison.InvariantCultureIgnoreCase)))
+ {
+ _wowManager.Profile.Status = "WoW subscription is inactive";
+ _wowManager.Profile.Log("WoW subscription is inactive");
+ _wowManager.Profile.Pause();
+ return false;
+ }
+ _wowManager.Profile.Status = $"Character name: {charName} not found. Double check spelling";
_wowManager.Profile.Log("Character name not found. Double check spelling");
return false;
}
@@ -100,28 +112,25 @@ orderby grandParent.Id
var index = wantedCharIndex > currentIndex
? new string((char) Keys.Down, wantedCharIndex - currentIndex)
: new string((char) Keys.Up, currentIndex - wantedCharIndex);
- Utility.SendBackgroundString(_wowManager.GameProcess.MainWindowHandle, index, false);
+ Utility.SendBackgroundString(_wowManager.GameWindow, index, false);
Utility.SleepUntil(() => SelectedCharacterIndex == wantedCharIndex, TimeSpan.FromSeconds(2));
return false;
}
- Utility.SendBackgroundKey(_wowManager.GameProcess.MainWindowHandle, (char)Keys.Enter, false);
+ Utility.SendBackgroundKey(_wowManager.GameWindow, (char)Keys.Enter, false);
return true;
}
// 1-based.
- int SelectedCharacterIndex
- {
- get { return (int)_wowManager.Globals.GetValue("CharacterSelect").Table.GetValue("selectedIndex").Number; }
- }
+ int SelectedCharacterIndex => (int)_wowManager.GetLuaObject("CharacterSelect.selectedIndex").Number;
- bool ShouldChangeRealm
+ bool? ShouldChangeRealm
{
get
{
var realmName = CurrentRealmName;
if (string.IsNullOrEmpty(realmName))
- return false;
+ return null;
return !realmName.ToLowerInvariant().Contains(_wowManager.Settings.ServerName.ToLowerInvariant());
}
}
@@ -140,9 +149,19 @@ void ChangeRealm()
{
if (_realmChangeSw.IsRunning && _realmChangeSw.Elapsed < TimeSpan.FromSeconds(5))
return;
+
+ if (UnableToSwitchRealm)
+ {
+ _wowManager.Profile.Log("Unable to switch realms. Trying a restart of WoW.");
+ _wowManager.CloseGameProcess();
+ return;
+ }
+ // Inserts a delay before pressing button because pressing too fast causes the 'You have been disconnected' error.
+ // See https://github.com/BosslandGmbH/HBRelog/issues/49
+ Thread.Sleep(4000);
var changeRealmButton = UIObject.GetUIObjectByName