316 lines
12 KiB
C#
316 lines
12 KiB
C#
using System;
|
|
using System.ComponentModel;
|
|
using System.Configuration;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Windows.Forms;
|
|
|
|
namespace D2Multi
|
|
{
|
|
public partial class Form1 : Form
|
|
{
|
|
readonly string _xmlPath;
|
|
readonly string _d2rPath;
|
|
BindingList<D2Account> _accounts;
|
|
BindingSource _bindingSource;
|
|
DataGridViewTextBoxColumn _colPassword;
|
|
|
|
public Form1()
|
|
{
|
|
InitializeComponent();
|
|
var cfgXml = ConfigurationManager.AppSettings["AccountsXmlPath"];
|
|
_xmlPath = string.IsNullOrWhiteSpace(cfgXml)
|
|
? D2AccountsXmlStore.DefaultPath
|
|
: cfgXml.Trim();
|
|
_d2rPath = (ConfigurationManager.AppSettings["D2RExePath"] ?? string.Empty).Trim();
|
|
}
|
|
|
|
void Form1_Load(object sender, EventArgs e)
|
|
{
|
|
InitBattleNetServerCombo();
|
|
|
|
var loaded = D2AccountsXmlStore.Load(_xmlPath).OrderBy(a => a.Name).ToList();
|
|
_accounts = new BindingList<D2Account>(loaded);
|
|
_bindingSource = new BindingSource { DataSource = _accounts };
|
|
dgvAccounts.AutoGenerateColumns = false;
|
|
dgvAccounts.Columns.Clear();
|
|
dgvAccounts.Columns.Add(new DataGridViewTextBoxColumn
|
|
{
|
|
DataPropertyName = nameof(D2Account.Name),
|
|
HeaderText = "Name",
|
|
Width = 160
|
|
});
|
|
dgvAccounts.Columns.Add(new DataGridViewTextBoxColumn
|
|
{
|
|
DataPropertyName = nameof(D2Account.Email),
|
|
HeaderText = "Email",
|
|
Width = 220
|
|
});
|
|
// Unbound: two-way bind to Password is unreliable here; sync explicitly from the model and CellValidating.
|
|
_colPassword = new DataGridViewTextBoxColumn
|
|
{
|
|
HeaderText = "Password",
|
|
Width = 160,
|
|
ReadOnly = false
|
|
};
|
|
dgvAccounts.Columns.Add(_colPassword);
|
|
dgvAccounts.DataSource = _bindingSource;
|
|
dgvAccounts.DataBindingComplete += DgvAccounts_DataBindingComplete;
|
|
dgvAccounts.CellValidating += DgvAccounts_CellValidating;
|
|
dgvAccounts.EditingControlShowing += DgvAccounts_EditingControlShowing;
|
|
dgvAccounts.CellPainting += DgvAccounts_CellPainting;
|
|
}
|
|
|
|
void InitBattleNetServerCombo()
|
|
{
|
|
cboServer.DropDownStyle = ComboBoxStyle.DropDownList;
|
|
cboServer.DataSource = new[]
|
|
{
|
|
new { Label = "America", Host = "us.actual.battle.net" },
|
|
new { Label = "Asia", Host = "kr.actual.battle.net" },
|
|
new { Label = "Europe", Host = "eu.actual.battle.net" },
|
|
};
|
|
cboServer.DisplayMember = "Label";
|
|
cboServer.ValueMember = "Host";
|
|
}
|
|
|
|
void DgvAccounts_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
|
|
{
|
|
PushPasswordFromAccountsToGrid();
|
|
}
|
|
|
|
void DgvAccounts_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
|
|
{
|
|
if (e.RowIndex < 0 || dgvAccounts.Columns[e.ColumnIndex] != _colPassword)
|
|
return;
|
|
var row = dgvAccounts.Rows[e.RowIndex];
|
|
if (row.IsNewRow || !(row.DataBoundItem is D2Account acc))
|
|
return;
|
|
acc.Password = e.FormattedValue == null ? string.Empty : Convert.ToString(e.FormattedValue);
|
|
}
|
|
|
|
void PushPasswordFromAccountsToGrid()
|
|
{
|
|
foreach (DataGridViewRow row in dgvAccounts.Rows)
|
|
{
|
|
if (row.IsNewRow || !(row.DataBoundItem is D2Account acc))
|
|
continue;
|
|
if (dgvAccounts.IsCurrentCellInEditMode && dgvAccounts.CurrentCell != null
|
|
&& dgvAccounts.CurrentCell.RowIndex == row.Index
|
|
&& dgvAccounts.CurrentCell.ColumnIndex == _colPassword.Index)
|
|
continue;
|
|
row.Cells[_colPassword.Index].Value = acc.Password ?? string.Empty;
|
|
}
|
|
}
|
|
|
|
void DgvAccounts_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
|
|
{
|
|
if (e.ColumnIndex < 0 || e.RowIndex < 0)
|
|
return;
|
|
if (dgvAccounts.Columns[e.ColumnIndex] != _colPassword)
|
|
return;
|
|
if (e.RowIndex >= dgvAccounts.Rows.Count || dgvAccounts.Rows[e.RowIndex].IsNewRow)
|
|
return;
|
|
|
|
if (dgvAccounts.IsCurrentCellInEditMode && dgvAccounts.CurrentCell != null
|
|
&& dgvAccounts.CurrentCell.RowIndex == e.RowIndex
|
|
&& dgvAccounts.CurrentCell.ColumnIndex == e.ColumnIndex)
|
|
return;
|
|
|
|
var val = e.Value as string;
|
|
e.Paint(e.ClipBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.ContentForeground);
|
|
|
|
if (!string.IsNullOrEmpty(val))
|
|
{
|
|
var mask = new string('\u2022', Math.Min(val.Length, 32));
|
|
var fore = e.State.HasFlag(DataGridViewElementStates.Selected)
|
|
? e.CellStyle.SelectionForeColor
|
|
: e.CellStyle.ForeColor;
|
|
TextRenderer.DrawText(e.Graphics, mask, e.CellStyle.Font, e.CellBounds, fore,
|
|
TextFormatFlags.Left | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine);
|
|
}
|
|
|
|
e.Handled = true;
|
|
}
|
|
|
|
void DgvAccounts_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
|
|
{
|
|
if (dgvAccounts.CurrentCell?.OwningColumn == _colPassword && e.Control is TextBox tb)
|
|
{
|
|
tb.UseSystemPasswordChar = true;
|
|
}
|
|
}
|
|
|
|
void BtnAdd_Click(object sender, EventArgs e)
|
|
{
|
|
var a = new D2Account { Name = "New account" };
|
|
_accounts.Add(a);
|
|
_bindingSource.Position = _accounts.Count - 1;
|
|
}
|
|
|
|
void BtnDelete_Click(object sender, EventArgs e)
|
|
{
|
|
if (_bindingSource.Current is D2Account cur)
|
|
{
|
|
_accounts.Remove(cur);
|
|
}
|
|
}
|
|
|
|
void BtnSave_Click(object sender, EventArgs e)
|
|
{
|
|
dgvAccounts.EndEdit();
|
|
_bindingSource.EndEdit();
|
|
try
|
|
{
|
|
D2AccountsXmlStore.Save(_xmlPath, _accounts.ToList());
|
|
MessageBox.Show(this, "Accounts saved.", Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show(this, ex.Message, "Save failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
|
|
void btnLaunch_Click(object sender, EventArgs e)
|
|
{
|
|
dgvAccounts.EndEdit();
|
|
_bindingSource.EndEdit();
|
|
|
|
if (!(_bindingSource.Current is D2Account cur))
|
|
{
|
|
MessageBox.Show(this, "Select an account.", Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
return;
|
|
}
|
|
|
|
var email = (cur.Email ?? string.Empty).Trim();
|
|
var password = cur.Password ?? string.Empty;
|
|
if (email.Length == 0 || password.Length == 0)
|
|
{
|
|
MessageBox.Show(this, "Email and password are required.", Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
return;
|
|
}
|
|
|
|
var host = Convert.ToString(cboServer.SelectedValue);
|
|
if (string.IsNullOrWhiteSpace(host))
|
|
{
|
|
MessageBox.Show(this, "Select a region.", Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
return;
|
|
}
|
|
|
|
if (!File.Exists(_d2rPath))
|
|
{
|
|
MessageBox.Show(this, "D2R.exe was not found at:\n" + _d2rPath, "Launch failed",
|
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
return;
|
|
}
|
|
|
|
var d2rDir = Path.GetDirectoryName(_d2rPath);
|
|
if (string.IsNullOrEmpty(d2rDir))
|
|
d2rDir = Environment.CurrentDirectory;
|
|
|
|
var handle64Cfg = (ConfigurationManager.AppSettings["Handle64ExePath"] ?? string.Empty).Trim();
|
|
var handle64 = D2RInstanceCheckKiller.ResolveHandle64Path(d2rDir, handle64Cfg);
|
|
var killScript = Path.Combine(d2rDir, "KillInstanceChecks.ps1");
|
|
|
|
if (handle64 != null)
|
|
{
|
|
if (D2RInstanceCheckKiller.TryCloseInstanceCheckHandles(handle64, d2rDir, out var handleLog))
|
|
{
|
|
Thread.Sleep(4000);
|
|
}
|
|
else if (File.Exists(killScript))
|
|
{
|
|
RunKillInstanceChecksScript(d2rDir, killScript);
|
|
}
|
|
else
|
|
{
|
|
MessageBox.Show(this,
|
|
"Could not clear D2R instance check (Handle64 failed). Run D2Multi as Administrator, "
|
|
+ "or place KillInstanceChecks.ps1 next to D2R.exe.\n\n" + handleLog,
|
|
Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
}
|
|
}
|
|
else if (File.Exists(killScript))
|
|
{
|
|
RunKillInstanceChecksScript(d2rDir, killScript);
|
|
}
|
|
|
|
var extra = (txExeAddictional.Text ?? string.Empty).Trim();
|
|
var args = "-username " + QuoteProcessArg(email)
|
|
+ " -password " + QuoteProcessArg(password)
|
|
+ " -address " + QuoteProcessArg(host);
|
|
if (extra.Length > 0)
|
|
args += " " + extra;
|
|
|
|
var psi = new ProcessStartInfo
|
|
{
|
|
FileName = _d2rPath,
|
|
Arguments = args,
|
|
WorkingDirectory = d2rDir,
|
|
UseShellExecute = false,
|
|
};
|
|
try
|
|
{
|
|
Process.Start(psi);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show(this, ex.Message, "Launch failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
|
|
void RunKillInstanceChecksScript(string d2rDir, string killScript)
|
|
{
|
|
var killPsi = new ProcessStartInfo
|
|
{
|
|
FileName = "powershell.exe",
|
|
Arguments = "-NoProfile -ExecutionPolicy Bypass -File \"" + killScript + "\"",
|
|
WorkingDirectory = d2rDir,
|
|
UseShellExecute = true,
|
|
};
|
|
try
|
|
{
|
|
using (var p = Process.Start(killPsi))
|
|
{
|
|
if (p != null)
|
|
p.WaitForExit(15000);
|
|
}
|
|
Thread.Sleep(4000);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show(this, "Could not run KillInstanceChecks.ps1:\n" + ex.Message, Text,
|
|
MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
}
|
|
}
|
|
|
|
static string QuoteProcessArg(string value)
|
|
{
|
|
if (value == null)
|
|
value = string.Empty;
|
|
if (value.Length == 0)
|
|
return "\"\"";
|
|
if (value.IndexOfAny(new[] { ' ', '\t', '"' }) < 0)
|
|
return value;
|
|
return "\"" + value.Replace("\"", "\\\"") + "\"";
|
|
}
|
|
|
|
void btnViewCharacters_Click(object sender, EventArgs e)
|
|
{
|
|
if (!(_bindingSource.Current is D2Account cur))
|
|
{
|
|
MessageBox.Show(this, "Select an account.", Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
return;
|
|
}
|
|
|
|
using (var dlg = new CharacterMissionsForm(cur, cur.Name))
|
|
{
|
|
dlg.ShowDialog(this);
|
|
}
|
|
}
|
|
}
|
|
}
|