mirror of
https://github.com/k1tbyte/Wand-Enhancer.git
synced 2026-08-28 17:01:04 +00:00
13759b1db6
- reduce ASAR IO overhead with streamed archive reads, buffered copies, faster relative-path handling and placeholder integrity records - fix in-place app.asar.unpacked packing/extraction self-copy cases that caused locked-file failures - tighten JS patch discovery with candidate bundle filters and search hints - require prebuilt remote-panel dist artifacts and clean up embedded bridge/script packaging - add unified build entrypoints for PowerShell, cmd and bash and move native CMake output under .tmp - add release metadata validation, changelog section extraction, pre-commit hook and GitHub Actions validation/release pipelines - make CHANGELOG the source of truth for release notes and document the tag-driven release flow - add updater release notes UI with latest/full changelog loading and localize the new update strings - modularize bridge renderer scripts, add installed apps and game status sync, and support remote launch/stop commands - centralize bridge protocol, IPC and WebSocket constants and improve LAN IP selection for QR pairing - refactor remote panel controls/state enums, persist accent color, polish library/session UI and refresh assets
304 lines
10 KiB
C#
304 lines
10 KiB
C#
using System;
|
|
using System.Collections.ObjectModel;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
using WandEnhancer.Core;
|
|
using WandEnhancer.Models;
|
|
using WandEnhancer.ReactiveUICore;
|
|
using WandEnhancer.Utils;
|
|
using WandEnhancer.View.Popups;
|
|
using Application = System.Windows.Application;
|
|
|
|
namespace WandEnhancer.View.MainWindow
|
|
{
|
|
public class MainWindowVm : ObservableObject
|
|
{
|
|
private readonly MainWindow _view;
|
|
public ObservableCollection<LogEntry> LogList { get; set; } = new ObservableCollection<LogEntry>();
|
|
private static Updater _updater = new Updater();
|
|
|
|
private WeModConfig _weModConfig;
|
|
|
|
public WeModConfig WeModInfo
|
|
{
|
|
get => _weModConfig;
|
|
set
|
|
{
|
|
SetProperty(ref _weModConfig, value);
|
|
if (value == null) return;
|
|
|
|
Log($"WeMod directory found at '{_weModConfig}' ({_weModConfig.ExecutableName})", ELogType.Success);
|
|
if (File.Exists(Path.Combine(_weModConfig.RootDirectory, "resources", "app.asar.backup")))
|
|
{
|
|
Log("WeMod already patched. If you want to patch again, please restore the backup first.",
|
|
ELogType.Warn);
|
|
IsPatchEnabled = false;
|
|
AlreadyPatched = true;
|
|
return;
|
|
}
|
|
|
|
Log("Ready for patching.", ELogType.Info);
|
|
IsPatchEnabled = true;
|
|
}
|
|
}
|
|
|
|
private bool _isPatchEnabled;
|
|
|
|
public bool IsPatchEnabled
|
|
{
|
|
get => _isPatchEnabled;
|
|
set => SetProperty(ref _isPatchEnabled, value);
|
|
}
|
|
|
|
private bool _alreadyPatched;
|
|
|
|
public bool AlreadyPatched
|
|
{
|
|
get => _alreadyPatched;
|
|
set => SetProperty(ref _alreadyPatched, value);
|
|
}
|
|
|
|
private bool _isUpdateAvailable;
|
|
|
|
public bool IsUpdateAvailable
|
|
{
|
|
get => _isUpdateAvailable;
|
|
set => SetProperty(ref _isUpdateAvailable, value);
|
|
}
|
|
|
|
public RelayCommand SetFolderPathCommand { get; }
|
|
public RelayCommand ApplyPatchCommand { get; }
|
|
public RelayCommand RestoreBackupCommand { get; }
|
|
public RelayCommand UpdateCommand { get; }
|
|
public RelayCommand OpenSettingsCommand { get; }
|
|
public RelayCommand CopyLogsCommand { get; }
|
|
public RelayCommand ExportLogsCommand { get; }
|
|
|
|
private void OnFolderPathSelection(object obj)
|
|
{
|
|
using (var dialog = new FolderBrowserDialog())
|
|
{
|
|
dialog.SelectedPath = Environment.GetEnvironmentVariable("LOCALAPPDATA");
|
|
dialog.Description = "Select the WeMod directory";
|
|
dialog.ShowNewFolderButton = false;
|
|
|
|
if (dialog.ShowDialog() != DialogResult.OK) return;
|
|
string selectedPath = dialog.SelectedPath;
|
|
string fileName = Path.GetFileName(selectedPath);
|
|
|
|
var info = Extensions.CheckWeModPath(selectedPath);
|
|
|
|
if (info != null)
|
|
{
|
|
WeModInfo = info;
|
|
return;
|
|
}
|
|
|
|
LogList.Add(new LogEntry
|
|
{
|
|
LogType = ELogType.Error,
|
|
Message = $"The selected folder '{fileName}' is not a valid WeMod directory."
|
|
});
|
|
}
|
|
}
|
|
|
|
private void OnBackupRestoring(object param)
|
|
{
|
|
var backupPath = Path.Combine(WeModInfo.RootDirectory, "resources", "app.asar.backup");
|
|
if (!File.Exists(backupPath))
|
|
{
|
|
Log("Backup not found. Please dont delete it manually", ELogType.Error);
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Try to lock the file to see if it's in use
|
|
using (File.Open(backupPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
|
|
{
|
|
}
|
|
|
|
var proxyDllPath = Path.Combine(WeModInfo.RootDirectory, "version.dll");
|
|
|
|
if(File.Exists(proxyDllPath))
|
|
{
|
|
File.Delete(proxyDllPath);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
Log("Backup file is locked. Please close the WeMod and try again.", ELogType.Error);
|
|
return;
|
|
}
|
|
|
|
File.Copy(backupPath, Path.Combine(WeModInfo.RootDirectory, "resources", "app.asar"), true);
|
|
File.Delete(backupPath);
|
|
Log("Backup restored successfully.", ELogType.Success);
|
|
AlreadyPatched = false;
|
|
IsPatchEnabled = true;
|
|
}
|
|
|
|
private void OnPatching(object param)
|
|
{
|
|
if (WeModInfo == null)
|
|
{
|
|
Log("Can't be done. Please specify the directory first.", ELogType.Warn);
|
|
return;
|
|
}
|
|
|
|
MainWindow.Instance.OpenPopup(new PatchVectorsPopup(async config =>
|
|
{
|
|
MainWindow.Instance.ClosePopup();
|
|
IsPatchEnabled = false;
|
|
await Task.Run(() =>
|
|
{
|
|
try
|
|
{
|
|
new Enhancer(WeModInfo, Log, config).Patch();
|
|
AlreadyPatched = true;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log($"Failed to patch: {e.Message}", ELogType.Error);
|
|
IsPatchEnabled = true;
|
|
}
|
|
});
|
|
}), Application.Current.FindResource("pv_popup_title") as string);
|
|
}
|
|
|
|
private void Log(string message, ELogType logType)
|
|
{
|
|
Application.Current.Dispatcher.Invoke(() =>
|
|
{
|
|
message = $"[{logType.ToString().ToUpper()}] {message}";
|
|
|
|
var entry = new LogEntry
|
|
{
|
|
LogType = logType,
|
|
Message = message
|
|
};
|
|
LogList.Add(entry);
|
|
_view.LogList.ScrollIntoView(entry);
|
|
});
|
|
}
|
|
|
|
private async void OnUpdate(object param)
|
|
{
|
|
var updateInfo = await _updater.GetUpdateInfoAsync();
|
|
if (updateInfo == null)
|
|
{
|
|
Log("No update details are available right now.", ELogType.Warn);
|
|
return;
|
|
}
|
|
|
|
MainWindow.Instance.OpenPopup(new UpdatePopup(Constants.Version.ToString(), updateInfo.Version,
|
|
updateInfo.LatestNotes, () =>
|
|
{
|
|
MainWindow.Instance.ClosePopup();
|
|
Task.Run(async () =>
|
|
{
|
|
try
|
|
{
|
|
await _updater.Update();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log($"Failed to update: {e.Message}", ELogType.Error);
|
|
return;
|
|
}
|
|
|
|
Log("WandEnhancer updated successfully. Restarting...", ELogType.Success);
|
|
});
|
|
}, () => _updater.GetFullChangelogAsync()), Application.Current.FindResource("up_popup_title") as string);
|
|
}
|
|
|
|
private void OnOpenSettings(object param)
|
|
{
|
|
MainWindow.Instance.OpenPopup(new SettingsPopup(), Application.Current.FindResource("settings_title") as string);
|
|
}
|
|
|
|
private string BuildLogReport()
|
|
{
|
|
var builder = new StringBuilder();
|
|
foreach (var entry in LogList)
|
|
{
|
|
builder.AppendLine(entry.Message);
|
|
}
|
|
return builder.ToString();
|
|
}
|
|
|
|
private void OnCopyLogs(object param)
|
|
{
|
|
if (LogList.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
System.Windows.Clipboard.SetText(BuildLogReport());
|
|
Log("Logs copied to clipboard.", ELogType.Success);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log($"Failed to copy logs: {e.Message}", ELogType.Error);
|
|
}
|
|
}
|
|
|
|
private void OnExportLogs(object param)
|
|
{
|
|
if (LogList.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
using (var dialog = new SaveFileDialog
|
|
{
|
|
Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*",
|
|
FileName = $"wand-enhancer-log-{DateTime.Now:yyyyMMdd-HHmmss}.txt"
|
|
})
|
|
{
|
|
if (dialog.ShowDialog() != DialogResult.OK)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
File.WriteAllText(dialog.FileName, BuildLogReport());
|
|
Log($"Logs exported to '{dialog.FileName}'.", ELogType.Success);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log($"Failed to export logs: {e.Message}", ELogType.Error);
|
|
}
|
|
}
|
|
}
|
|
|
|
public MainWindowVm(MainWindow view)
|
|
{
|
|
Task.Run(async () =>
|
|
{
|
|
var isUpdateAvailable = await _updater.CheckForUpdates();
|
|
Application.Current.Dispatcher.Invoke(() => IsUpdateAvailable = isUpdateAvailable);
|
|
});
|
|
_view = view;
|
|
SetFolderPathCommand = new RelayCommand(OnFolderPathSelection);
|
|
ApplyPatchCommand = new RelayCommand(OnPatching);
|
|
RestoreBackupCommand = new RelayCommand(OnBackupRestoring);
|
|
UpdateCommand = new RelayCommand(OnUpdate);
|
|
OpenSettingsCommand = new RelayCommand(OnOpenSettings);
|
|
CopyLogsCommand = new RelayCommand(OnCopyLogs);
|
|
ExportLogsCommand = new RelayCommand(OnExportLogs);
|
|
|
|
WeModInfo = Extensions.FindWeMod();
|
|
if (WeModInfo == null)
|
|
{
|
|
Log("WeMod directory not found.", ELogType.Error);
|
|
}
|
|
}
|
|
}
|
|
} |