D2Multi/Form1.cs
2026-03-22 19:54:19 -07:00

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);
}
}
}
}