mirror of
https://github.com/k1tbyte/Wand-Enhancer.git
synced 2026-08-28 23:01:13 +00:00
added runtime patcher, added MemoryUtils, fixes
This commit is contained in:
@@ -2,8 +2,7 @@
|
|||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="clr-namespace:WeModPatcher"
|
xmlns:local="clr-namespace:WeModPatcher"
|
||||||
xmlns:converters="clr-namespace:WeModPatcher.Converters"
|
xmlns:converters="clr-namespace:WeModPatcher.Converters">
|
||||||
StartupUri="/View/MainWindow/MainWindow.xaml">
|
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using WeModPatcher.Core;
|
||||||
|
using WeModPatcher.View.MainWindow;
|
||||||
using MessageBox = System.Windows.Forms.MessageBox;
|
using MessageBox = System.Windows.Forms.MessageBox;
|
||||||
|
|
||||||
namespace WeModPatcher
|
namespace WeModPatcher
|
||||||
@@ -12,21 +14,7 @@ namespace WeModPatcher
|
|||||||
{
|
{
|
||||||
protected override void OnStartup(StartupEventArgs e)
|
protected override void OnStartup(StartupEventArgs e)
|
||||||
{
|
{
|
||||||
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
|
this.MainWindow.Show();
|
||||||
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
|
|
||||||
base.OnStartup(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
|
|
||||||
{
|
|
||||||
MessageBox.Show(e.ToString());
|
|
||||||
Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
|
|
||||||
{
|
|
||||||
MessageBox.Show(e.ToString());
|
|
||||||
Shutdown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public new static void Shutdown()
|
public new static void Shutdown()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using WeModPatcher.Models;
|
||||||
|
|
||||||
namespace WeModPatcher
|
namespace WeModPatcher
|
||||||
{
|
{
|
||||||
@@ -10,6 +11,25 @@ namespace WeModPatcher
|
|||||||
public static readonly string RepositoryUrl = $"https://github.com/{Owner}/{RepoName}";
|
public static readonly string RepositoryUrl = $"https://github.com/{Owner}/{RepoName}";
|
||||||
public static readonly Version Version;
|
public static readonly Version Version;
|
||||||
|
|
||||||
|
// cmp dword ptr [rdx], 0
|
||||||
|
// jnz loc_XXXXXXXX
|
||||||
|
// mov rsi, rdx
|
||||||
|
public static Signature ExePatchSignature = new Signature(
|
||||||
|
"83 3A 00 0F ?? ?? 01 00 00 48 89 D6 48 B8",
|
||||||
|
4,
|
||||||
|
new byte[]{ 0x84, 0x17 },
|
||||||
|
new byte[]{ 0x85, 0x22 }
|
||||||
|
);
|
||||||
|
|
||||||
|
/*// ...
|
||||||
|
// test eax, eax (0x85 for r/m16/32/64)
|
||||||
|
// jnz short loc_1403A4DD2 (Integrity check failed)
|
||||||
|
// call near ptr funk_1445527E0
|
||||||
|
// ...
|
||||||
|
private const string PatchSignature = "E8 ?? ?? ?? ?? ?? C0 75 ?? F6 C3 01 74 ?? 48 89 F9 E8 ?? ?? ?? ??";
|
||||||
|
private static readonly byte[] PatchBytes = { 0x31 };
|
||||||
|
private const int PatchOffset = 0x5;*/
|
||||||
|
|
||||||
static Constants()
|
static Constants()
|
||||||
{
|
{
|
||||||
Version = Assembly.GetExecutingAssembly().GetName().Version;
|
Version = Assembly.GetExecutingAssembly().GetName().Version;
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using WeModPatcher.Models;
|
||||||
|
using WeModPatcher.Utils;
|
||||||
|
using WeModPatcher.Utils.Win32;
|
||||||
|
using WeModPatcher.View.MainWindow;
|
||||||
|
|
||||||
|
namespace WeModPatcher.Core
|
||||||
|
{
|
||||||
|
|
||||||
|
public class RuntimePatcher
|
||||||
|
{
|
||||||
|
private readonly string _exePath;
|
||||||
|
|
||||||
|
public RuntimePatcher(string exePath)
|
||||||
|
{
|
||||||
|
_exePath = exePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void StartProcess()
|
||||||
|
{
|
||||||
|
if(string.IsNullOrEmpty(_exePath))
|
||||||
|
{
|
||||||
|
throw new Exception("Path is not specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
KillWeMod();
|
||||||
|
var startupInfo = new Imports.StartupInfo { cb = Marshal.SizeOf(typeof(Imports.StartupInfo)) };
|
||||||
|
if(!Imports.CreateProcessA(_exePath,
|
||||||
|
null,
|
||||||
|
IntPtr.Zero,
|
||||||
|
IntPtr.Zero,
|
||||||
|
false, Imports.DEBUG_PROCESS, IntPtr.Zero,
|
||||||
|
null, ref startupInfo, out var processInfo))
|
||||||
|
{
|
||||||
|
throw new Exception("Failed to create process, error code: " + Marshal.GetLastWin32Error());
|
||||||
|
}
|
||||||
|
|
||||||
|
var debugEvent = new Imports.DEBUG_EVENT();
|
||||||
|
var processIds = new List<uint>();
|
||||||
|
while (Imports.WaitForDebugEvent(ref debugEvent, 1000))
|
||||||
|
{
|
||||||
|
var code = debugEvent.dwDebugEventCode;
|
||||||
|
if (code == Imports.CREATE_PROCESS_DEBUG_EVENT)
|
||||||
|
{
|
||||||
|
processIds.Add(debugEvent.dwProcessId);
|
||||||
|
}
|
||||||
|
else if (code == Imports.EXCEPTION_DEBUG_EVENT)
|
||||||
|
{
|
||||||
|
var exceptionInfo = Imports.MapUnmanagedStructure<Imports.EXCEPTION_DEBUG_INFO>(debugEvent.Union);
|
||||||
|
|
||||||
|
if (exceptionInfo.ExceptionRecord.ExceptionCode == Imports.EXCEPTION_BREAKPOINT)
|
||||||
|
{
|
||||||
|
var process = Process.GetProcessById((int)debugEvent.dwProcessId);
|
||||||
|
var address = MemoryUtils.ScanVirtualMemory(
|
||||||
|
process.Handle,
|
||||||
|
process.Modules[0].BaseAddress,
|
||||||
|
process.Modules[0].ModuleMemorySize,
|
||||||
|
Constants.ExePatchSignature.Sequence, Constants.ExePatchSignature.Mask
|
||||||
|
);
|
||||||
|
|
||||||
|
if (address != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
MemoryUtils.SafeWriteVirtualMemory(
|
||||||
|
process.Handle,
|
||||||
|
address + Constants.ExePatchSignature.Offset,
|
||||||
|
Constants.ExePatchSignature.PatchBytes
|
||||||
|
);
|
||||||
|
|
||||||
|
/*byte[] patchedBytes = new byte[32];
|
||||||
|
if (Imports.ReadProcessMemory(process.Handle, address, patchedBytes, patchedBytes.Length, out int bytesRead))
|
||||||
|
{
|
||||||
|
Console.WriteLine("Bytes after patching: ");
|
||||||
|
for (int i = 0; i < bytesRead; i++)
|
||||||
|
{
|
||||||
|
Console.Write($"{patchedBytes[i]:X2} ");
|
||||||
|
}
|
||||||
|
Console.WriteLine();
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Imports.ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, Imports.DBG_CONTINUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var processId in processIds)
|
||||||
|
{
|
||||||
|
Imports.DebugActiveProcessStop(processId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Imports.CloseHandle(processInfo.hProcess);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Patch(PatchConfig config, Action<string, ELogType> logger)
|
||||||
|
{
|
||||||
|
if (config.Path == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Path is not specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
var parent = Directory.GetParent(config.Path)?.FullName ?? config.Path;
|
||||||
|
var latestPath = Extensions.FindLatestWeMod(parent) ?? config.Path;
|
||||||
|
|
||||||
|
if (!Extensions.CheckWeModPath(latestPath))
|
||||||
|
{
|
||||||
|
throw new Exception("Invalid WeMod path");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!File.Exists(Path.Combine(latestPath, "resources", "app.asar.backup")))
|
||||||
|
{
|
||||||
|
config.PatchMethod = EPatchProcessMethod.None;
|
||||||
|
new StaticPatcher(latestPath, logger, config).Patch();
|
||||||
|
}
|
||||||
|
|
||||||
|
new RuntimePatcher(Path.Combine(latestPath, "WeMod.exe"))
|
||||||
|
.StartProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void KillWeMod()
|
||||||
|
{
|
||||||
|
Process[] processes = Process.GetProcessesByName("WeMod");
|
||||||
|
for (int i = 0; processes.Length > i || i < 5; i++)
|
||||||
|
{
|
||||||
|
foreach (var process in processes)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
process.Kill();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processes = Process.GetProcessesByName("WeMod");
|
||||||
|
Thread.Sleep(250);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (processes.Length > 0)
|
||||||
|
{
|
||||||
|
throw new Exception("Failed to kill WeMod");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,15 +2,19 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Windows.Forms;
|
||||||
using AsarSharp;
|
using AsarSharp;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using WeModPatcher.Models;
|
using WeModPatcher.Models;
|
||||||
|
using WeModPatcher.Utils;
|
||||||
using WeModPatcher.View.MainWindow;
|
using WeModPatcher.View.MainWindow;
|
||||||
|
using Application = System.Windows.Application;
|
||||||
|
|
||||||
namespace WeModPatcher.Utils
|
namespace WeModPatcher.Core
|
||||||
{
|
{
|
||||||
public class Patcher
|
public class StaticPatcher
|
||||||
{
|
{
|
||||||
private class PatchEntry
|
private class PatchEntry
|
||||||
{
|
{
|
||||||
@@ -42,24 +46,16 @@ namespace WeModPatcher.Utils
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ...
|
|
||||||
// test eax, eax (0x85 for r/m16/32/64)
|
|
||||||
// jnz short loc_1403A4DD2 (Integrity check failed)
|
|
||||||
// call near ptr funk_1445527E0
|
|
||||||
// ...
|
|
||||||
private const string PatchSignature = "E8 ?? ?? ?? ?? ?? C0 75 ?? F6 C3 01 74 ?? 48 89 F9 E8 ?? ?? ?? ??";
|
|
||||||
private static readonly byte[] PatchBytes = { 0x31 };
|
|
||||||
private const int PatchOffset = 0x5;
|
|
||||||
|
|
||||||
private readonly string _weModRootFolder;
|
private readonly string _weModRootFolder;
|
||||||
private readonly Action<string, ELogType> _logger;
|
private readonly Action<string, ELogType> _logger;
|
||||||
private readonly HashSet<EPatchType> _config;
|
private readonly PatchConfig _config;
|
||||||
private readonly string _asarPath;
|
private readonly string _asarPath;
|
||||||
private readonly string _backupPath;
|
private readonly string _backupPath;
|
||||||
private readonly string _unpackedPath;
|
private readonly string _unpackedPath;
|
||||||
private int _sumOfPatches = 0;
|
private int _sumOfPatches = 0;
|
||||||
|
private readonly string _exePath;
|
||||||
|
|
||||||
public Patcher(string weModRootFolder, Action<string, ELogType> logger, HashSet<EPatchType> config)
|
public StaticPatcher(string weModRootFolder, Action<string, ELogType> logger, PatchConfig config)
|
||||||
{
|
{
|
||||||
_weModRootFolder = weModRootFolder;
|
_weModRootFolder = weModRootFolder;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -68,6 +64,7 @@ namespace WeModPatcher.Utils
|
|||||||
_asarPath = Path.Combine(weModRootFolder, "resources", "app.asar");
|
_asarPath = Path.Combine(weModRootFolder, "resources", "app.asar");
|
||||||
_unpackedPath = Path.Combine(weModRootFolder, "resources", "app.asar.unpacked");
|
_unpackedPath = Path.Combine(weModRootFolder, "resources", "app.asar.unpacked");
|
||||||
_backupPath = Path.Combine(weModRootFolder, "resources", "app.asar.backup");
|
_backupPath = Path.Combine(weModRootFolder, "resources", "app.asar.backup");
|
||||||
|
_exePath = Path.Combine(_weModRootFolder, "WeMod.exe");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetFetchFieldName(string targetFunction)
|
private static string GetFetchFieldName(string targetFunction)
|
||||||
@@ -126,7 +123,7 @@ namespace WeModPatcher.Utils
|
|||||||
throw new Exception("[PATCHER] No app bundle found");
|
throw new Exception("[PATCHER] No app bundle found");
|
||||||
}
|
}
|
||||||
|
|
||||||
var requestedPatches = _config.ToList();
|
var requestedPatches = _config.PatchTypes.ToList();
|
||||||
requestedPatches.ForEach(patch => _sumOfPatches += (int)patch);
|
requestedPatches.ForEach(patch => _sumOfPatches += (int)patch);
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
{
|
{
|
||||||
@@ -143,11 +140,10 @@ namespace WeModPatcher.Utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task PatchPE()
|
private void PatchPe()
|
||||||
{
|
{
|
||||||
_logger("[PATCHER] Patching PE...", ELogType.Info);
|
_logger("[PATCHER] Patching PE...", ELogType.Info);
|
||||||
var pePath = Path.Combine(_weModRootFolder, "WeMod.exe");
|
var patchResult = MemoryUtils.PatchFile(_exePath,Constants.ExePatchSignature, Constants.ExePatchSignature.PatchBytes);
|
||||||
var patchResult = await PatternScanner.PatchBySignature(pePath, PatchSignature, PatchBytes, PatchOffset);
|
|
||||||
if(patchResult == -1)
|
if(patchResult == -1)
|
||||||
{
|
{
|
||||||
_logger("[PATCHER] Failed to patch PE", ELogType.Error);
|
_logger("[PATCHER] Failed to patch PE", ELogType.Error);
|
||||||
@@ -156,8 +152,41 @@ namespace WeModPatcher.Utils
|
|||||||
_logger(patchResult == 0 ? "[PATCHER] PE already patched!" : "[PATCHER] PE patched successfully!", ELogType.Success);
|
_logger(patchResult == 0 ? "[PATCHER] PE already patched!" : "[PATCHER] PE patched successfully!", ELogType.Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Patch()
|
private void CreateShortcut()
|
||||||
{
|
{
|
||||||
|
// invoke file dialog save file
|
||||||
|
|
||||||
|
var fileDialog = new SaveFileDialog()
|
||||||
|
{
|
||||||
|
CheckPathExists = true,
|
||||||
|
AddExtension = true,
|
||||||
|
SupportMultiDottedExtensions = false,
|
||||||
|
FileName = "WeMod",
|
||||||
|
};
|
||||||
|
|
||||||
|
if(fileDialog.ShowDialog() != DialogResult.OK)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_config.Path = _weModRootFolder;
|
||||||
|
|
||||||
|
var json = JsonConvert.SerializeObject(_config, Formatting.None);
|
||||||
|
Utils.Win32.Shortcut.CreateShortcut(
|
||||||
|
fileName: fileDialog.FileName + ".lnk",
|
||||||
|
targetPath: Assembly.GetExecutingAssembly().Location,
|
||||||
|
arguments: Extensions.Base64Encode(json),
|
||||||
|
workingDirectory: Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
|
||||||
|
description: null,
|
||||||
|
iconPath: _exePath
|
||||||
|
);
|
||||||
|
|
||||||
|
_logger("[PATCHER] The shortcut has been created, now you should only run WeMod through this shortcut", ELogType.Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Patch()
|
||||||
|
{
|
||||||
|
RuntimePatcher.KillWeMod();
|
||||||
if (!File.Exists(_backupPath))
|
if (!File.Exists(_backupPath))
|
||||||
{
|
{
|
||||||
_logger("[PATCHER] Creating backup...", ELogType.Info);
|
_logger("[PATCHER] Creating backup...", ELogType.Info);
|
||||||
@@ -170,8 +199,7 @@ namespace WeModPatcher.Utils
|
|||||||
|
|
||||||
if(!File.Exists(_asarPath))
|
if(!File.Exists(_asarPath))
|
||||||
{
|
{
|
||||||
_logger("[PATCHER] app.asar not found!", ELogType.Error);
|
throw new Exception("app.asar not found");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -181,8 +209,7 @@ namespace WeModPatcher.Utils
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger($"[PATCHER] Failed to unpack app.asar: {e.Message}", ELogType.Error);
|
throw new Exception($"[PATCHER] Failed to unpack app.asar: {e.Message}");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PatchAsar();
|
PatchAsar();
|
||||||
@@ -196,11 +223,17 @@ namespace WeModPatcher.Utils
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger($"[PATCHER] Failed to pack app.asar: {e.Message}", ELogType.Error);
|
throw new Exception($"[PATCHER] Failed to pack app.asar: {e.Message}");
|
||||||
return;
|
}
|
||||||
|
|
||||||
|
if (_config.PatchMethod == EPatchProcessMethod.Static)
|
||||||
|
{
|
||||||
|
PatchPe();
|
||||||
|
}
|
||||||
|
else if(_config.PatchMethod == EPatchProcessMethod.Runtime)
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(CreateShortcut);
|
||||||
}
|
}
|
||||||
|
|
||||||
// await PatchPE();
|
|
||||||
|
|
||||||
_logger("[PATCHER] Done!", ELogType.Success);
|
_logger("[PATCHER] Done!", ELogType.Success);
|
||||||
}
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<Window x:Class="WeModPatcher.MainWindow"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:local="clr-namespace:WeModPatcher"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
Title="MainWindow" Height="350" Width="525">
|
|
||||||
<Grid>
|
|
||||||
|
|
||||||
</Grid>
|
|
||||||
</Window>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace WeModPatcher
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Interaction logic for MainWindow.xaml
|
|
||||||
/// </summary>
|
|
||||||
public partial class MainWindow
|
|
||||||
{
|
|
||||||
public MainWindow()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace WeModPatcher.Models
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace WeModPatcher.Models
|
||||||
{
|
{
|
||||||
|
|
||||||
public enum EPatchType
|
public enum EPatchType
|
||||||
@@ -7,4 +9,18 @@
|
|||||||
DisableUpdates = 2,
|
DisableUpdates = 2,
|
||||||
DisableTelemetry = 4
|
DisableTelemetry = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum EPatchProcessMethod
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Runtime = 1,
|
||||||
|
Static = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PatchConfig
|
||||||
|
{
|
||||||
|
public HashSet<EPatchType> PatchTypes { get; set; }
|
||||||
|
public EPatchProcessMethod PatchMethod { get; set; }
|
||||||
|
public string Path { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using WeModPatcher.Utils;
|
||||||
|
|
||||||
|
namespace WeModPatcher.Models
|
||||||
|
{
|
||||||
|
public sealed class Signature
|
||||||
|
{
|
||||||
|
public readonly byte[] OriginalBytes;
|
||||||
|
public readonly byte[] PatchBytes;
|
||||||
|
public readonly byte[] Sequence;
|
||||||
|
public readonly byte[] Mask;
|
||||||
|
public readonly int Offset;
|
||||||
|
|
||||||
|
public int Length => Sequence.Length;
|
||||||
|
|
||||||
|
public static implicit operator byte[](Signature signature) => signature.Sequence;
|
||||||
|
|
||||||
|
public Signature(string signature, int offset, byte[] patchBytes, byte[] originalBytes)
|
||||||
|
{
|
||||||
|
MemoryUtils.ParseSignature(signature, out Sequence, out Mask);
|
||||||
|
PatchBytes = patchBytes;
|
||||||
|
OriginalBytes = originalBytes;
|
||||||
|
Offset = offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using WeModPatcher.Core;
|
||||||
|
using WeModPatcher.Models;
|
||||||
|
using WeModPatcher.Utils;
|
||||||
|
using WeModPatcher.View.MainWindow;
|
||||||
|
|
||||||
|
namespace WeModPatcher
|
||||||
|
{
|
||||||
|
public static class Program
|
||||||
|
{
|
||||||
|
[STAThread]
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
|
||||||
|
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
|
||||||
|
|
||||||
|
List<LogEntry> logEntries = new List<LogEntry>();
|
||||||
|
if (args.Length > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var patchConfig = JsonConvert.DeserializeObject<PatchConfig>(Extensions.Base64Decode(args[0]));
|
||||||
|
RuntimePatcher.Patch(patchConfig, (message, type) =>
|
||||||
|
{
|
||||||
|
logEntries.Add(new LogEntry
|
||||||
|
{
|
||||||
|
Message = message,
|
||||||
|
LogType = type
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Environment.Exit(0);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logEntries.Add(new LogEntry
|
||||||
|
{
|
||||||
|
Message = "Runtime patching failed: " + e.Message,
|
||||||
|
LogType = ELogType.Error
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var application = new App();
|
||||||
|
application.InitializeComponent();
|
||||||
|
application.MainWindow = new MainWindow();
|
||||||
|
foreach (var logEntry in logEntries)
|
||||||
|
{
|
||||||
|
MainWindow.Instance.ViewModel.LogList.Add(logEntry);
|
||||||
|
}
|
||||||
|
application.Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
|
||||||
|
{
|
||||||
|
MessageBox.Show(e.Exception.ToString());
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||||
|
{
|
||||||
|
MessageBox.Show(e.ExceptionObject.ToString());
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -3,7 +3,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
|
||||||
namespace WeModPatcher.ReactiveCore
|
namespace WeModPatcher.ReactiveUICore
|
||||||
{
|
{
|
||||||
public sealed class AsyncRelayCommand : ICommand
|
public sealed class AsyncRelayCommand : ICommand
|
||||||
{
|
{
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace WeModPatcher.ReactiveCore
|
namespace WeModPatcher.ReactiveUICore
|
||||||
{
|
{
|
||||||
public class ObservableObject : INotifyPropertyChanged
|
public class ObservableObject : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
|
||||||
namespace WeModPatcher.ReactiveCore
|
namespace WeModPatcher.ReactiveUICore
|
||||||
{
|
{
|
||||||
public sealed class RelayCommand : ICommand
|
public sealed class RelayCommand : ICommand
|
||||||
{
|
{
|
||||||
@@ -15,4 +15,20 @@
|
|||||||
<Geometry x:Key="GitHub">
|
<Geometry x:Key="GitHub">
|
||||||
M12 2A10 10 0 0122 12c0 4.42-2.86 8.16-6.83 9.5-.51.09-.67-.23-.67-.5 0-.32 0-1.4 0-2.74 0-.93-.33-1.54-.69-1.85 2.23-.25 4.57-1.09 4.57-4.91 0-1.11-.38-2-1.03-2.71.1-.25.45-1.29-.1-2.64 0 0-.84-.27-2.75 1.02-.79-.22-1.65-.33-2.5-.33s-1.71.11-2.5.33C7.59 5.88 6.75 6.15 6.75 6.15c-.55 1.35-.2 2.39-.1 2.64-.65.71-1.03 1.6-1.03 2.71 0 3.81 2.33 4.67 4.55 4.92-.28.25-.54.69-.63 1.34-.57.24-2.04.69-2.91-.83 0 0-.53-.96-1.53-1.03 0 0-.98-.02-.07.6 0 0 .65.31 1.11 1.47 0 0 .59 1.94 3.36 1.34 0 .83 0 1.46 0 1.69 0 .27-.16.58-.66.5C4.87 20.17 2 16.42 2 12A10 10 0 0112 2Z
|
M12 2A10 10 0 0122 12c0 4.42-2.86 8.16-6.83 9.5-.51.09-.67-.23-.67-.5 0-.32 0-1.4 0-2.74 0-.93-.33-1.54-.69-1.85 2.23-.25 4.57-1.09 4.57-4.91 0-1.11-.38-2-1.03-2.71.1-.25.45-1.29-.1-2.64 0 0-.84-.27-2.75 1.02-.79-.22-1.65-.33-2.5-.33s-1.71.11-2.5.33C7.59 5.88 6.75 6.15 6.75 6.15c-.55 1.35-.2 2.39-.1 2.64-.65.71-1.03 1.6-1.03 2.71 0 3.81 2.33 4.67 4.55 4.92-.28.25-.54.69-.63 1.34-.57.24-2.04.69-2.91-.83 0 0-.53-.96-1.53-1.03 0 0-.98-.02-.07.6 0 0 .65.31 1.11 1.47 0 0 .59 1.94 3.36 1.34 0 .83 0 1.46 0 1.69 0 .27-.16.58-.66.5C4.87 20.17 2 16.42 2 12A10 10 0 0112 2Z
|
||||||
</Geometry>
|
</Geometry>
|
||||||
|
|
||||||
|
<Geometry x:Key="CheckDecagram">
|
||||||
|
M10 17l8-8-1.41-1.42L10 14.17 7.41 11.59 6 13l4 4Zm13-5-2.44 2.78.34 3.68-3.61.82-1.89 3.18L12 21 8.6 22.47 6.71 19.29 3.1 18.47l.34-3.69L1 12 3.44 9.21 3.1 5.53l3.61-.81L8.6 1.54 12 3l3.4-1.46 1.89 3.18 3.61.82-.34 3.68L23 12
|
||||||
|
</Geometry>
|
||||||
|
|
||||||
|
<Geometry x:Key="AlertDecagram">
|
||||||
|
M13 13V7H11v6h2Zm0 4V15H11v2h2m10-5-2.44 2.78.34 3.68-3.61.82-1.89 3.18L12 21 8.6 22.47 6.71 19.29 3.1 18.47l.34-3.69L1 12 3.44 9.21 3.1 5.53l3.61-.81L8.6 1.54 12 3l3.4-1.46 1.89 3.18 3.61.82-.34 3.68L23 12
|
||||||
|
</Geometry>
|
||||||
|
|
||||||
|
<Geometry x:Key="ArrowLeft">
|
||||||
|
M5.05 11.94l5-5v3.99H19l-.03 2.01H10.05v4Z
|
||||||
|
</Geometry>
|
||||||
|
|
||||||
|
<!--<Geometry x:Key="">
|
||||||
|
|
||||||
|
</Geometry>-->
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
@@ -210,9 +210,9 @@
|
|||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Border BorderBrush="{DynamicResource Border}"
|
<Border BorderBrush="{DynamicResource Border}"
|
||||||
BorderThickness="0 0 1 0">
|
BorderThickness="0 0 1 0" IsHitTestVisible="False">
|
||||||
<TextBlock Text="{TemplateBinding Uid}"
|
<TextBlock Text="{TemplateBinding Uid}"
|
||||||
VerticalAlignment="Center" Cursor="Hand"
|
VerticalAlignment="Center"
|
||||||
FontSize="12"
|
FontSize="12"
|
||||||
Foreground="{DynamicResource MutedForeground}"
|
Foreground="{DynamicResource MutedForeground}"
|
||||||
Padding="10 0" />
|
Padding="10 0" />
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace WeModPatcher.Utils
|
||||||
|
{
|
||||||
|
public static class Extensions
|
||||||
|
{
|
||||||
|
public static bool CheckWeModPath(string root)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return File.Exists(Path.Combine(root, "WeMod.exe")) &&
|
||||||
|
File.Exists(Path.Combine(root, "resources", "app.asar"));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FindWeModDirectory()
|
||||||
|
{
|
||||||
|
string localAppDataPath = Environment.GetEnvironmentVariable("LOCALAPPDATA");
|
||||||
|
|
||||||
|
string defaultDir = Path.Combine(localAppDataPath ?? "", "WeMod");
|
||||||
|
|
||||||
|
if (!Directory.Exists(defaultDir))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FindLatestWeMod(defaultDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Base64Decode(string base64EncodedData)
|
||||||
|
{
|
||||||
|
var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData);
|
||||||
|
return System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Base64Encode(string plainText)
|
||||||
|
{
|
||||||
|
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
|
||||||
|
return System.Convert.ToBase64String(plainTextBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FindLatestWeMod(string root)
|
||||||
|
{
|
||||||
|
var appFolders = Directory.EnumerateDirectories(root)
|
||||||
|
.Select(folderPath => new DirectoryInfo(folderPath))
|
||||||
|
.Where(dirInfo => Regex.IsMatch(dirInfo.Name, @"^app-\w+"))
|
||||||
|
.Select(dirInfo => new
|
||||||
|
{
|
||||||
|
Name = dirInfo.Name,
|
||||||
|
Path = dirInfo.FullName,
|
||||||
|
LastModified = dirInfo.LastWriteTime
|
||||||
|
})
|
||||||
|
.OrderByDescending(item => item.LastModified)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return (
|
||||||
|
from folder
|
||||||
|
in appFolders
|
||||||
|
where CheckWeModPath(folder.Path)
|
||||||
|
select folder.Path
|
||||||
|
).FirstOrDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using WeModPatcher.Models;
|
||||||
|
using WeModPatcher.Utils.Win32;
|
||||||
|
|
||||||
|
namespace WeModPatcher.Utils
|
||||||
|
{
|
||||||
|
public class MemoryUtils
|
||||||
|
{
|
||||||
|
public static int ScanMemoryBlock(byte[] buffer, int bufferLength, byte[] pattern, byte[] mask)
|
||||||
|
{
|
||||||
|
var patternLength = pattern.Length;
|
||||||
|
if (bufferLength < patternLength)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a length of length outside the first cycle for optimization
|
||||||
|
var searchEnd = bufferLength - patternLength;
|
||||||
|
|
||||||
|
// first pass - use the first non-empty byte of the mask for a quick check
|
||||||
|
var firstValidIndex = -1;
|
||||||
|
for (var i = 0; i < patternLength; i++)
|
||||||
|
{
|
||||||
|
if (mask[i] == 1)
|
||||||
|
{
|
||||||
|
firstValidIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstValidIndex == -1)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var firstByte = pattern[firstValidIndex];
|
||||||
|
|
||||||
|
for (var i = 0; i <= searchEnd; i++)
|
||||||
|
{
|
||||||
|
// quick check by the first byte before full comparison
|
||||||
|
if (buffer[i + firstValidIndex] != firstByte)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var found = true;
|
||||||
|
|
||||||
|
// check only those positions where mask = 1
|
||||||
|
for (var j = 0; j < patternLength; j++)
|
||||||
|
{
|
||||||
|
if (mask[j] == 0 || buffer[i + j] == pattern[j])
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
found = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ParseSignature(string signatureStr, out byte[] pattern, out byte[] mask)
|
||||||
|
{
|
||||||
|
var parts = signatureStr.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var length = parts.Length;
|
||||||
|
|
||||||
|
pattern = new byte[length];
|
||||||
|
mask = new byte[length];
|
||||||
|
|
||||||
|
for (var i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
if (parts[i] == "??" || parts[i] == "?")
|
||||||
|
{
|
||||||
|
pattern[i] = 0;
|
||||||
|
// wildcard byte
|
||||||
|
mask[i] = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern[i] = Convert.ToByte(parts[i], 16);
|
||||||
|
mask[i] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool SafeWriteVirtualMemory(IntPtr hProcess, IntPtr address, byte[] bytes)
|
||||||
|
{
|
||||||
|
if (!Imports.VirtualProtectEx(hProcess, address, (IntPtr)1, 0x40, out uint oldProtect))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = Imports.WriteProcessMemory(hProcess, address, bytes, bytes.Length, out _);
|
||||||
|
|
||||||
|
// Restore the previous access rights
|
||||||
|
Imports.VirtualProtectEx(hProcess, address, (IntPtr)1, oldProtect, out _);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IntPtr ScanVirtualMemory(IntPtr hProcess, IntPtr startAddress, int searchSize, byte[] signature, byte[] mask)
|
||||||
|
{
|
||||||
|
const int BUFFER_SIZE = 4096;
|
||||||
|
byte[] buffer = new byte[BUFFER_SIZE];
|
||||||
|
|
||||||
|
// We can't copy all the crap of the process into a byte array at once. Don't try this
|
||||||
|
for (long currentAddress = startAddress.ToInt64();
|
||||||
|
currentAddress < startAddress.ToInt64() + searchSize;
|
||||||
|
currentAddress += BUFFER_SIZE - signature.Length)
|
||||||
|
{
|
||||||
|
if (!Imports.ReadProcessMemory(hProcess, new IntPtr(currentAddress), buffer, BUFFER_SIZE, out int bytesRead) || bytesRead == 0)
|
||||||
|
{
|
||||||
|
// Read error or end of memory, throw mb?
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var i = ScanMemoryBlock(buffer, bytesRead, signature, mask);
|
||||||
|
if (i != -1)
|
||||||
|
{
|
||||||
|
return new IntPtr(currentAddress + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int PatchFile(string filePath, Signature signature, byte[] patchBytes)
|
||||||
|
{
|
||||||
|
const int bufferSize = 8192;
|
||||||
|
var buffer = new byte[bufferSize + signature.Length - 1];
|
||||||
|
|
||||||
|
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
|
||||||
|
{
|
||||||
|
int filePosition = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int bytesRead = fileStream.Read(buffer, 0, bufferSize);
|
||||||
|
if (bytesRead == 0) break;
|
||||||
|
|
||||||
|
int matchIndex = ScanMemoryBlock(buffer, bytesRead, signature, signature.Mask);
|
||||||
|
if (matchIndex != -1)
|
||||||
|
{
|
||||||
|
int functionStartPosition = filePosition + matchIndex;
|
||||||
|
|
||||||
|
var checkBuffer = new byte[patchBytes.Length];
|
||||||
|
fileStream.Seek(functionStartPosition + signature.Offset, SeekOrigin.Begin);
|
||||||
|
fileStream.Read(checkBuffer, 0, patchBytes.Length);
|
||||||
|
|
||||||
|
if (checkBuffer.SequenceEqual(patchBytes))
|
||||||
|
{
|
||||||
|
return 0; // Memory already patched
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to patch position
|
||||||
|
fileStream.Seek(functionStartPosition + signature.Offset, SeekOrigin.Begin);
|
||||||
|
fileStream.Write(patchBytes, 0, patchBytes.Length);
|
||||||
|
|
||||||
|
return functionStartPosition; // Return the address of the function start by signature
|
||||||
|
}
|
||||||
|
|
||||||
|
filePosition += bytesRead;
|
||||||
|
Array.Copy(buffer, bufferSize, buffer, 0, signature.Length - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace WeModPatcher.Utils
|
|
||||||
{
|
|
||||||
public class PatternScanner
|
|
||||||
{
|
|
||||||
public static int FindPatternInBuffer(byte[] buffer, int bytesRead, byte[] signature, string mask)
|
|
||||||
{
|
|
||||||
int bufferLength = bytesRead + signature.Length - 1;
|
|
||||||
|
|
||||||
for (int i = 0; i <= bytesRead - signature.Length; i++)
|
|
||||||
{
|
|
||||||
if (IsMatch(buffer, signature, mask, i))
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsMatch(byte[] buffer, byte[] signature, string mask, int offset)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < signature.Length; i++)
|
|
||||||
{
|
|
||||||
if (mask[i] == 'x' && buffer[offset + i] != signature[i])
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static (byte[] signature, string mask) ParseSignature(string signature)
|
|
||||||
{
|
|
||||||
var signatureBytes = new List<byte>();
|
|
||||||
var mask = new StringBuilder();
|
|
||||||
|
|
||||||
var tokens = signature.Split(' ');
|
|
||||||
foreach (var token in tokens)
|
|
||||||
{
|
|
||||||
if (token == "??" || token == "?")
|
|
||||||
{
|
|
||||||
signatureBytes.Add(0);
|
|
||||||
mask.Append('?');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
signatureBytes.Add(Convert.ToByte(token, 16));
|
|
||||||
mask.Append('x');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (signatureBytes.ToArray(), mask.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<int> PatchBySignature(string filePath, string functionSignature, byte[] patchBytes, int patchOffset)
|
|
||||||
{
|
|
||||||
var (signature, mask) = ParseSignature(functionSignature);
|
|
||||||
const int bufferSize = 8192;
|
|
||||||
var buffer = new byte[bufferSize + signature.Length - 1];
|
|
||||||
|
|
||||||
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
|
|
||||||
{
|
|
||||||
int filePosition = 0;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
int bytesRead = await fileStream.ReadAsync(buffer, 0, bufferSize);
|
|
||||||
if (bytesRead == 0) break;
|
|
||||||
|
|
||||||
int matchIndex = FindPatternInBuffer(buffer, bytesRead, signature, mask);
|
|
||||||
if (matchIndex != -1)
|
|
||||||
{
|
|
||||||
int functionStartPosition = filePosition + matchIndex;
|
|
||||||
|
|
||||||
var checkBuffer = new byte[patchBytes.Length];
|
|
||||||
fileStream.Seek(functionStartPosition + patchOffset, SeekOrigin.Begin);
|
|
||||||
await fileStream.ReadAsync(checkBuffer, 0, patchBytes.Length);
|
|
||||||
|
|
||||||
if (checkBuffer.SequenceEqual(patchBytes))
|
|
||||||
{
|
|
||||||
return 0; // Memory already patched
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go to patch position
|
|
||||||
fileStream.Seek(functionStartPosition + patchOffset, SeekOrigin.Begin);
|
|
||||||
await fileStream.WriteAsync(patchBytes, 0, patchBytes.Length);
|
|
||||||
|
|
||||||
return functionStartPosition; // Return the address of the function start by signature
|
|
||||||
}
|
|
||||||
|
|
||||||
filePosition += bytesRead;
|
|
||||||
Array.Copy(buffer, bufferSize, buffer, 0, signature.Length - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace WeModPatcher.Utils.Win32
|
||||||
|
{
|
||||||
|
public static class Imports
|
||||||
|
{
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern bool ReadProcessMemory(
|
||||||
|
IntPtr hProcess,
|
||||||
|
IntPtr lpBaseAddress,
|
||||||
|
[Out] byte[] lpBuffer,
|
||||||
|
int dwSize,
|
||||||
|
out int lpNumberOfBytesRead);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern bool WriteProcessMemory(
|
||||||
|
IntPtr hProcess,
|
||||||
|
IntPtr lpBaseAddress,
|
||||||
|
byte[] lpBuffer,
|
||||||
|
int nSize,
|
||||||
|
out IntPtr lpNumberOfBytesWritten);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern bool VirtualProtectEx(
|
||||||
|
IntPtr hProcess,
|
||||||
|
IntPtr lpAddress,
|
||||||
|
IntPtr dwSize,
|
||||||
|
uint flNewProtect,
|
||||||
|
out uint lpflOldProtect);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern bool WaitForDebugEvent(ref DEBUG_EVENT lpDebugEvent, uint dwMilliseconds);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern bool CloseHandle(IntPtr hObject);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern uint ResumeThread(IntPtr hThread);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern bool DebugActiveProcessStop(uint dwProcessId);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern bool DebugActiveProcess(int dwProcessId);
|
||||||
|
|
||||||
|
[DllImport("psapi.dll", SetLastError = true)]
|
||||||
|
public static extern bool EnumProcessModules(
|
||||||
|
IntPtr hProcess,
|
||||||
|
IntPtr lphModule,
|
||||||
|
uint cb,
|
||||||
|
out uint lpcbNeeded);
|
||||||
|
|
||||||
|
[DllImport("psapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||||
|
public static extern int GetModuleFileNameEx(
|
||||||
|
IntPtr hProcess,
|
||||||
|
IntPtr hModule,
|
||||||
|
StringBuilder lpFilename,
|
||||||
|
int nSize);
|
||||||
|
|
||||||
|
[DllImport("psapi.dll", SetLastError = true)]
|
||||||
|
public static extern bool GetModuleInformation(IntPtr hProcess, IntPtr hModule, out MODULEINFO lpmodinfo, uint cb);
|
||||||
|
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
|
||||||
|
public static extern bool CreateProcessA
|
||||||
|
(
|
||||||
|
String lpApplicationName,
|
||||||
|
String lpCommandLine,
|
||||||
|
IntPtr lpProcessAttributes,
|
||||||
|
IntPtr lpThreadAttributes,
|
||||||
|
Boolean bInheritHandles,
|
||||||
|
uint dwCreationFlags,
|
||||||
|
IntPtr lpEnvironment,
|
||||||
|
String lpCurrentDirectory,
|
||||||
|
[In] ref StartupInfo lpStartupInfo,
|
||||||
|
out ProcessInformation lpProcessInformation
|
||||||
|
);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern bool ContinueDebugEvent(uint dwProcessId, uint dwThreadId, uint dwContinueStatus);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct StartupInfo
|
||||||
|
{
|
||||||
|
public Int32 cb ;
|
||||||
|
public IntPtr lpReserved ;
|
||||||
|
public IntPtr lpDesktop ;
|
||||||
|
public IntPtr lpTitle ;
|
||||||
|
public Int32 dwX ;
|
||||||
|
public Int32 dwY ;
|
||||||
|
public Int32 dwXSize ;
|
||||||
|
public Int32 dwYSize ;
|
||||||
|
public Int32 dwXCountChars ;
|
||||||
|
public Int32 dwYCountChars ;
|
||||||
|
public Int32 dwFillAttribute ;
|
||||||
|
public Int32 dwFlags ;
|
||||||
|
public Int16 wShowWindow ;
|
||||||
|
public Int16 cbReserved2 ;
|
||||||
|
public IntPtr lpReserved2 ;
|
||||||
|
public IntPtr hStdInput ;
|
||||||
|
public IntPtr hStdOutput ;
|
||||||
|
public IntPtr hStdError ;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct ProcessInformation
|
||||||
|
{
|
||||||
|
public IntPtr hProcess;
|
||||||
|
public IntPtr hThread;
|
||||||
|
public Int32 dwProcessId;
|
||||||
|
public Int32 dwThreadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Debug event structures
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
public struct DEBUG_EVENT
|
||||||
|
{
|
||||||
|
[FieldOffset(0)]
|
||||||
|
public uint dwDebugEventCode;
|
||||||
|
[FieldOffset(4)]
|
||||||
|
public uint dwProcessId;
|
||||||
|
[FieldOffset(8)]
|
||||||
|
public uint dwThreadId;
|
||||||
|
[FieldOffset(16)]
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 160)]
|
||||||
|
public byte[] Union;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||||
|
public struct EXCEPTION_DEBUG_INFO
|
||||||
|
{
|
||||||
|
public EXCEPTION_RECORD ExceptionRecord;
|
||||||
|
public uint dwFirstChance;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||||
|
public struct EXCEPTION_RECORD
|
||||||
|
{
|
||||||
|
public uint ExceptionCode;
|
||||||
|
public uint ExceptionFlags;
|
||||||
|
public IntPtr pExceptionRecord;
|
||||||
|
public IntPtr ExceptionAddress;
|
||||||
|
public uint NumberParameters;
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 15)]
|
||||||
|
public IntPtr[] ExceptionInformation;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||||
|
public struct CREATE_THREAD_DEBUG_INFO
|
||||||
|
{
|
||||||
|
public IntPtr hThread;
|
||||||
|
public IntPtr lpThreadLocalBase;
|
||||||
|
public IntPtr lpStartAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||||
|
public struct CREATE_PROCESS_DEBUG_INFO
|
||||||
|
{
|
||||||
|
public IntPtr hFile;
|
||||||
|
public IntPtr hProcess;
|
||||||
|
public IntPtr hThread;
|
||||||
|
public IntPtr lpBaseOfImage;
|
||||||
|
public uint dwDebugInfoFileOffset;
|
||||||
|
public uint nDebugInfoSize;
|
||||||
|
public IntPtr lpThreadLocalBase;
|
||||||
|
public IntPtr lpStartAddress;
|
||||||
|
public IntPtr lpImageName;
|
||||||
|
public ushort fUnicode;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||||
|
public struct MODULEINFO
|
||||||
|
{
|
||||||
|
public IntPtr lpBaseOfDll;
|
||||||
|
public uint SizeOfImage;
|
||||||
|
public IntPtr EntryPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||||
|
public struct EXIT_THREAD_DEBUG_INFO
|
||||||
|
{
|
||||||
|
public uint dwExitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct EXIT_PROCESS_DEBUG_INFO
|
||||||
|
{
|
||||||
|
public uint dwExitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct LOAD_DLL_DEBUG_INFO
|
||||||
|
{
|
||||||
|
public IntPtr hFile;
|
||||||
|
public IntPtr lpBaseOfDll;
|
||||||
|
public uint dwDebugInfoFileOffset;
|
||||||
|
public uint nDebugInfoSize;
|
||||||
|
public IntPtr lpImageName;
|
||||||
|
public ushort fUnicode;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct UNLOAD_DLL_DEBUG_INFO
|
||||||
|
{
|
||||||
|
public IntPtr lpBaseOfDll;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct OUTPUT_DEBUG_STRING_INFO
|
||||||
|
{
|
||||||
|
public IntPtr lpDebugStringData;
|
||||||
|
public ushort fUnicode;
|
||||||
|
public ushort nDebugStringLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct RIP_INFO
|
||||||
|
{
|
||||||
|
public uint dwError;
|
||||||
|
public uint dwType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static T MapUnmanagedStructure<T>(byte[] debugInfo)
|
||||||
|
{
|
||||||
|
GCHandle handle = GCHandle.Alloc(debugInfo, GCHandleType.Pinned);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
handle.Free();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// Determining constants for debugging
|
||||||
|
public const uint INFINITE = 0xFFFFFFFF;
|
||||||
|
public const uint DEBUG_PROCESS = 0x00000001;
|
||||||
|
public const uint DBG_CONTINUE = 0x00010002;
|
||||||
|
public const uint CREATE_PROCESS_DEBUG_EVENT = 3;
|
||||||
|
public const uint EXCEPTION_DEBUG_EVENT = 1;
|
||||||
|
public const uint LOAD_DLL_DEBUG_EVENT = 6;
|
||||||
|
public const uint OUTPUT_DEBUG_STRING_EVENT = 8;
|
||||||
|
public const uint EXCEPTION_BREAKPOINT = 0x80000003;
|
||||||
|
|
||||||
|
// Constants for VirtualProtectex
|
||||||
|
public const uint PAGE_EXECUTE_READWRITE = 0x40;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace WeModPatcher.Utils.Win32
|
||||||
|
{
|
||||||
|
public class Shortcut
|
||||||
|
{
|
||||||
|
public class ShortcutParams
|
||||||
|
{
|
||||||
|
public string FileName { get; set; }
|
||||||
|
public string TargetPath { get; set; }
|
||||||
|
public string Arguments { get; set; }
|
||||||
|
public string WorkingDirectory { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public string Hotkey { get; set; }
|
||||||
|
public string IconPath { get; set; }
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly Type m_type = Type.GetTypeFromProgID("WScript.Shell");
|
||||||
|
private static readonly object m_shell = Activator.CreateInstance(m_type);
|
||||||
|
|
||||||
|
[ComImport, TypeLibType(0x1040), Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B")]
|
||||||
|
private interface IWshShortcut
|
||||||
|
{
|
||||||
|
[DispId(0)]
|
||||||
|
string FullName { [return: MarshalAs(UnmanagedType.BStr)][DispId(0)] get; }
|
||||||
|
[DispId(0x3e8)]
|
||||||
|
string Arguments { [return: MarshalAs(UnmanagedType.BStr)][DispId(0x3e8)] get; [param: In, MarshalAs(UnmanagedType.BStr)][DispId(0x3e8)] set; }
|
||||||
|
[DispId(0x3e9)]
|
||||||
|
string Description { [return: MarshalAs(UnmanagedType.BStr)][DispId(0x3e9)] get; [param: In, MarshalAs(UnmanagedType.BStr)][DispId(0x3e9)] set; }
|
||||||
|
[DispId(0x3ea)]
|
||||||
|
string Hotkey { [return: MarshalAs(UnmanagedType.BStr)][DispId(0x3ea)] get; [param: In, MarshalAs(UnmanagedType.BStr)][DispId(0x3ea)] set; }
|
||||||
|
[DispId(0x3eb)]
|
||||||
|
string IconLocation { [return: MarshalAs(UnmanagedType.BStr)][DispId(0x3eb)] get; [param: In, MarshalAs(UnmanagedType.BStr)][DispId(0x3eb)] set; }
|
||||||
|
[DispId(0x3ec)]
|
||||||
|
string RelativePath { [param: In, MarshalAs(UnmanagedType.BStr)][DispId(0x3ec)] set; }
|
||||||
|
[DispId(0x3ed)]
|
||||||
|
string TargetPath { [return: MarshalAs(UnmanagedType.BStr)][DispId(0x3ed)] get; [param: In, MarshalAs(UnmanagedType.BStr)][DispId(0x3ed)] set; }
|
||||||
|
[DispId(0x3ee)]
|
||||||
|
int WindowStyle { [DispId(0x3ee)] get; [param: In][DispId(0x3ee)] set; }
|
||||||
|
[DispId(0x3ef)]
|
||||||
|
string WorkingDirectory { [return: MarshalAs(UnmanagedType.BStr)][DispId(0x3ef)] get; [param: In, MarshalAs(UnmanagedType.BStr)][DispId(0x3ef)] set; }
|
||||||
|
[TypeLibFunc((short)0x40), DispId(0x7d0)]
|
||||||
|
void Load([In, MarshalAs(UnmanagedType.BStr)] string PathLink);
|
||||||
|
[DispId(0x7d1)]
|
||||||
|
void Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CreateShortcut(string fileName, string targetPath, string arguments, string workingDirectory, string description, string iconPath)
|
||||||
|
{
|
||||||
|
IWshShortcut shortcut = (IWshShortcut)m_type.InvokeMember("CreateShortcut", System.Reflection.BindingFlags.InvokeMethod, null, m_shell, new object[] { fileName });
|
||||||
|
shortcut.Description = description;
|
||||||
|
shortcut.TargetPath = targetPath;
|
||||||
|
shortcut.WorkingDirectory = workingDirectory;
|
||||||
|
shortcut.Arguments = arguments;
|
||||||
|
if (!string.IsNullOrEmpty(iconPath))
|
||||||
|
shortcut.IconLocation = iconPath;
|
||||||
|
shortcut.Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<UserControl x:Class="WeModPatcher.View.Controls.InfoItem"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:WeModPatcher.View.Controls"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignHeight="300" d:DesignWidth="300">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Viewbox Width="20" Height="20" VerticalAlignment="Top">
|
||||||
|
<Path Fill="{Binding IconColor}" Data="{Binding IconData}"/>
|
||||||
|
</Viewbox>
|
||||||
|
<TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="5 0 5 0" TextWrapping="Wrap"
|
||||||
|
FontSize="12" Text="{Binding Text}"/>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
|
namespace WeModPatcher.View.Controls
|
||||||
|
{
|
||||||
|
public partial class InfoItem : UserControl
|
||||||
|
{
|
||||||
|
public static readonly DependencyProperty IconDataProperty =
|
||||||
|
DependencyProperty.Register(nameof(IconData), typeof(Geometry), typeof(InfoItem));
|
||||||
|
|
||||||
|
public static readonly DependencyProperty IconColorProperty =
|
||||||
|
DependencyProperty.Register(nameof(IconColor), typeof(Brush), typeof(InfoItem));
|
||||||
|
|
||||||
|
public static readonly DependencyProperty TextProperty =
|
||||||
|
DependencyProperty.Register(nameof(Text), typeof(string), typeof(InfoItem));
|
||||||
|
|
||||||
|
public Geometry IconData
|
||||||
|
{
|
||||||
|
get => (Geometry)GetValue(IconDataProperty);
|
||||||
|
set => SetValue(IconDataProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Brush IconColor
|
||||||
|
{
|
||||||
|
get => (Brush)GetValue(IconColorProperty);
|
||||||
|
set => SetValue(IconColorProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Text
|
||||||
|
{
|
||||||
|
get => (string)GetValue(TextProperty);
|
||||||
|
set => SetValue(TextProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InfoItem()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
this.DataContext = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,9 +37,11 @@
|
|||||||
</Button.Style>
|
</Button.Style>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<TextBlock x:Name="Title" Text="This is title" Foreground="{DynamicResource Foreground}"
|
<StackPanel Grid.Row="0" x:Name="TitleContainer" Orientation="Horizontal">
|
||||||
HorizontalAlignment="Left" FontWeight="Bold" FontSize="16"
|
<TextBlock x:Name="Title" Text="This is title" Foreground="{DynamicResource Foreground}"
|
||||||
VerticalAlignment="Bottom"/>
|
HorizontalAlignment="Left" FontWeight="Bold" FontSize="16"
|
||||||
|
VerticalAlignment="Bottom"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<ContentPresenter x:Name="Presenter" Margin="0 20 0 0"
|
<ContentPresenter x:Name="Presenter" Margin="0 20 0 0"
|
||||||
Content="{Binding PopupContent}" Grid.Row="2"/>
|
Content="{Binding PopupContent}" Grid.Row="2"/>
|
||||||
|
|||||||
@@ -65,6 +65,10 @@ namespace WeModPatcher.View.Controls
|
|||||||
|
|
||||||
Visibility = Visibility.Collapsed;
|
Visibility = Visibility.Collapsed;
|
||||||
Closed?.Invoke();
|
Closed?.Invoke();
|
||||||
|
if (PopupContent is IDisposable disposable)
|
||||||
|
{
|
||||||
|
disposable.Dispose();
|
||||||
|
}
|
||||||
PopupContent = null;
|
PopupContent = null;
|
||||||
Closed = null;
|
Closed = null;
|
||||||
OpenedSemaphore.Release();
|
OpenedSemaphore.Release();
|
||||||
|
|||||||
@@ -88,14 +88,14 @@
|
|||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<Grid Margin="10" Cursor="Hand" Background="Transparent">
|
<Grid Margin="10" Cursor="Hand" Background="Transparent">
|
||||||
<Grid.InputBindings>
|
|
||||||
<MouseBinding Gesture="LeftClick" Command="{Binding SetFolderPathCommand}" />
|
|
||||||
</Grid.InputBindings>
|
|
||||||
<TextBox Style="{StaticResource TitledTextBox}"
|
<TextBox Style="{StaticResource TitledTextBox}"
|
||||||
Uid="Folder path" IsReadOnly="True"
|
Uid="Folder path" IsReadOnly="True"
|
||||||
Text="{Binding WeModPath}"
|
Text="{Binding WeModPath}"
|
||||||
VerticalAlignment="Center" Tag="Folder not found">
|
VerticalAlignment="Center" Tag="Folder not found">
|
||||||
</TextBox>
|
</TextBox>
|
||||||
|
<Grid.InputBindings>
|
||||||
|
<MouseBinding Gesture="LeftClick" Command="{Binding SetFolderPathCommand}" />
|
||||||
|
</Grid.InputBindings>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,16 +11,19 @@ namespace WeModPatcher.View.MainWindow
|
|||||||
public partial class MainWindow
|
public partial class MainWindow
|
||||||
{
|
{
|
||||||
public static MainWindow Instance;
|
public static MainWindow Instance;
|
||||||
|
public readonly MainWindowVm ViewModel;
|
||||||
|
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
this.DataContext = new MainWindowVm(this);
|
this.ViewModel = new MainWindowVm(this);
|
||||||
|
this.DataContext = ViewModel;
|
||||||
VersionLabel.Text = Constants.Version.ToString();
|
VersionLabel.Text = Constants.Version.ToString();
|
||||||
Instance = this;
|
Instance = this;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenPopup(object content, string title = null)
|
public void OpenPopup(FrameworkElement content, string title = null)
|
||||||
{
|
{
|
||||||
this.PopupHost.PopupContent = content;
|
this.PopupHost.PopupContent = content;
|
||||||
PopupHost.Title.Text = title;
|
PopupHost.Title.Text = title;
|
||||||
|
|||||||
@@ -2,12 +2,15 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
using AsarSharp;
|
using AsarSharp;
|
||||||
using WeModPatcher.ReactiveCore;
|
using WeModPatcher.Core;
|
||||||
|
using WeModPatcher.Models;
|
||||||
|
using WeModPatcher.ReactiveUICore;
|
||||||
using WeModPatcher.Utils;
|
using WeModPatcher.Utils;
|
||||||
using WeModPatcher.View.Popups;
|
using WeModPatcher.View.Popups;
|
||||||
using Application = System.Windows.Application;
|
using Application = System.Windows.Application;
|
||||||
@@ -18,7 +21,7 @@ namespace WeModPatcher.View.MainWindow
|
|||||||
public class MainWindowVm : ObservableObject
|
public class MainWindowVm : ObservableObject
|
||||||
{
|
{
|
||||||
private readonly MainWindow _view;
|
private readonly MainWindow _view;
|
||||||
public ObservableCollection<LogEntry> LogList { get; } = new ObservableCollection<LogEntry>();
|
public ObservableCollection<LogEntry> LogList { get; set; } = new ObservableCollection<LogEntry>();
|
||||||
private static Updater _updater = new Updater();
|
private static Updater _updater = new Updater();
|
||||||
|
|
||||||
private string _weModPath;
|
private string _weModPath;
|
||||||
@@ -71,50 +74,6 @@ namespace WeModPatcher.View.MainWindow
|
|||||||
public RelayCommand RestoreBackupCommand { get; }
|
public RelayCommand RestoreBackupCommand { get; }
|
||||||
public AsyncRelayCommand UpdateCommand { get; }
|
public AsyncRelayCommand UpdateCommand { get; }
|
||||||
|
|
||||||
private static bool CheckWeModPath(string root)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return File.Exists(Path.Combine(root, "WeMod.exe")) &&
|
|
||||||
File.Exists(Path.Combine(root, "resources", "app.asar"));
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string FindWeModDirectory()
|
|
||||||
{
|
|
||||||
string localAppDataPath = Environment.GetEnvironmentVariable("LOCALAPPDATA");
|
|
||||||
|
|
||||||
string defaultDir = Path.Combine(localAppDataPath ?? "", "WeMod");
|
|
||||||
|
|
||||||
if (!Directory.Exists(defaultDir))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var appFolders = Directory.EnumerateDirectories(defaultDir)
|
|
||||||
.Select(folderPath => new DirectoryInfo(folderPath))
|
|
||||||
.Where(dirInfo => Regex.IsMatch(dirInfo.Name, @"^app-\w+"))
|
|
||||||
.Select(dirInfo => new
|
|
||||||
{
|
|
||||||
Name = dirInfo.Name,
|
|
||||||
Path = dirInfo.FullName,
|
|
||||||
LastModified = dirInfo.LastWriteTime
|
|
||||||
})
|
|
||||||
.OrderByDescending(item => item.LastModified)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
return (
|
|
||||||
from folder
|
|
||||||
in appFolders
|
|
||||||
where CheckWeModPath(folder.Path)
|
|
||||||
select folder.Path
|
|
||||||
).FirstOrDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnFolderPathSelection(object obj)
|
private void OnFolderPathSelection(object obj)
|
||||||
{
|
{
|
||||||
using (var dialog = new FolderBrowserDialog())
|
using (var dialog = new FolderBrowserDialog())
|
||||||
@@ -127,7 +86,7 @@ namespace WeModPatcher.View.MainWindow
|
|||||||
string selectedPath = dialog.SelectedPath;
|
string selectedPath = dialog.SelectedPath;
|
||||||
string fileName = Path.GetFileName(selectedPath);
|
string fileName = Path.GetFileName(selectedPath);
|
||||||
|
|
||||||
if (CheckWeModPath(selectedPath))
|
if (Extensions.CheckWeModPath(selectedPath))
|
||||||
{
|
{
|
||||||
WeModPath = selectedPath;
|
WeModPath = selectedPath;
|
||||||
return;
|
return;
|
||||||
@@ -143,6 +102,7 @@ namespace WeModPatcher.View.MainWindow
|
|||||||
|
|
||||||
private void OnBackupRestoring(object param)
|
private void OnBackupRestoring(object param)
|
||||||
{
|
{
|
||||||
|
|
||||||
var backupPath = Path.Combine(WeModPath, "resources", "app.asar.backup");
|
var backupPath = Path.Combine(WeModPath, "resources", "app.asar.backup");
|
||||||
if (!File.Exists(backupPath))
|
if (!File.Exists(backupPath))
|
||||||
{
|
{
|
||||||
@@ -155,6 +115,22 @@ namespace WeModPatcher.View.MainWindow
|
|||||||
using (File.Open(backupPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
|
using (File.Open(backupPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This shit doesn't look at the hash and verify() always returns true
|
||||||
|
//using X509Certificate2 cert = new X509Certificate2(X509Certificate.CreateFromSignedFile(filePath));
|
||||||
|
|
||||||
|
var restoreExeResult = MemoryUtils.PatchFile( Path.Combine( WeModPath, "WeMod.exe"),
|
||||||
|
Constants.ExePatchSignature, Constants.ExePatchSignature.OriginalBytes);
|
||||||
|
if (restoreExeResult == -1)
|
||||||
|
{
|
||||||
|
Log("Failed to restore the backup. Please close the WeMod and try again.", ELogType.Error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log(restoreExeResult == 0 ?
|
||||||
|
"Signature exe is original, does not require restoration"
|
||||||
|
: "WeMod.exe restored successfully", ELogType.Success);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -177,12 +153,24 @@ namespace WeModPatcher.View.MainWindow
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow.Instance.OpenPopup(new PatchVectorsPopup(async config =>
|
MainWindow.Instance.OpenPopup(new PatchVectorsPopup( async config =>
|
||||||
{
|
{
|
||||||
MainWindow.Instance.ClosePopup();
|
MainWindow.Instance.ClosePopup();
|
||||||
IsPatchEnabled = false;
|
IsPatchEnabled = false;
|
||||||
await Task.Run(() => new Patcher(WeModPath, Log, config).Patch());
|
await Task.Run(() =>
|
||||||
IsPatchEnabled = true;
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
new StaticPatcher(WeModPath, Log, config).Patch();
|
||||||
|
AlreadyPatched = true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log($"Failed to patch: {e.Message}", ELogType.Error);
|
||||||
|
IsPatchEnabled = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}), "What are we gonna patch?");
|
}), "What are we gonna patch?");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +217,7 @@ namespace WeModPatcher.View.MainWindow
|
|||||||
RestoreBackupCommand = new RelayCommand(OnBackupRestoring);
|
RestoreBackupCommand = new RelayCommand(OnBackupRestoring);
|
||||||
UpdateCommand = new AsyncRelayCommand(OnUpdate);
|
UpdateCommand = new AsyncRelayCommand(OnUpdate);
|
||||||
|
|
||||||
WeModPath = FindWeModDirectory();
|
WeModPath = Extensions.FindWeModDirectory();
|
||||||
if (WeModPath == null)
|
if (WeModPath == null)
|
||||||
{
|
{
|
||||||
Log("WeMod directory not found.", ELogType.Error);
|
Log("WeMod directory not found.", ELogType.Error);
|
||||||
|
|||||||
@@ -4,28 +4,123 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:local="clr-namespace:WeModPatcher.View.Popups"
|
xmlns:local="clr-namespace:WeModPatcher.View.Popups"
|
||||||
|
xmlns:controls="clr-namespace:WeModPatcher.View.Controls"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="300" d:DesignWidth="300"
|
d:DesignHeight="Auto" d:DesignWidth="Auto"
|
||||||
|
Background="{DynamicResource Background}"
|
||||||
Foreground="{DynamicResource MutedForeground}"
|
Foreground="{DynamicResource MutedForeground}"
|
||||||
FontWeight="Medium"
|
FontWeight="Medium"
|
||||||
FontSize="13">
|
FontSize="13">
|
||||||
<Grid Margin="0 0 5 0">
|
<UserControl.Resources>
|
||||||
<Grid.RowDefinitions>
|
<Button x:Key="BackButton" Click="BackClicked" VerticalAlignment="Bottom" Padding="3"
|
||||||
<RowDefinition Height="27"/>
|
Margin="0 0 15 0"
|
||||||
<RowDefinition Height="27"/>
|
Width="35" Height="23" Style="{StaticResource IconButton}"
|
||||||
<RowDefinition Height="27"/>
|
Tag="{StaticResource ArrowLeft}"/>
|
||||||
<RowDefinition Height="Auto"/>
|
</UserControl.Resources>
|
||||||
</Grid.RowDefinitions>
|
<Grid>
|
||||||
<TextBlock Grid.Row="0" VerticalAlignment="Center" Text="Activate WeMod Pro"/>
|
<Grid x:Name="PatchVectors" Visibility="Visible" Margin="0 0 5 0">
|
||||||
<CheckBox Grid.Row="0" x:Name="ActivateProBox" HorizontalAlignment="Right" VerticalAlignment="Center" IsChecked="True"/>
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="27"/>
|
||||||
|
<RowDefinition Height="27"/>
|
||||||
|
<RowDefinition Height="27"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0" VerticalAlignment="Center" Text="Activate WeMod Pro"/>
|
||||||
|
<CheckBox Grid.Row="0" x:Name="ActivateProBox" HorizontalAlignment="Right" VerticalAlignment="Center" IsChecked="True"/>
|
||||||
|
|
||||||
<TextBlock Grid.Row="1" VerticalAlignment="Center" Text="Disable telemetry"/>
|
<TextBlock Grid.Row="1" VerticalAlignment="Center" Text="Disable telemetry"/>
|
||||||
<CheckBox Grid.Row="1" x:Name="DisableTelemetryBox" HorizontalAlignment="Right" VerticalAlignment="Center"/>
|
<CheckBox Grid.Row="1" x:Name="DisableTelemetryBox" HorizontalAlignment="Right" VerticalAlignment="Center"/>
|
||||||
|
|
||||||
<TextBlock Grid.Row="2" VerticalAlignment="Center" Text="Disable updates"/>
|
<TextBlock Grid.Row="2" VerticalAlignment="Center" Text="Disable updates"/>
|
||||||
<CheckBox Grid.Row="2" x:Name="DisableUpdateBox" HorizontalAlignment="Right" VerticalAlignment="Center"/>
|
<CheckBox Grid.Row="2" x:Name="DisableUpdateBox" HorizontalAlignment="Right" VerticalAlignment="Center"/>
|
||||||
|
|
||||||
<Button Grid.Row="3" Padding="0 5 0 5" Margin="0 15 0 0" Content="Continue"
|
<Button Grid.Row="3" Padding="0 5 0 5" Margin="0 15 0 0" Content="Continue"
|
||||||
Click="ButtonBase_OnClick"/>
|
Click="NextClicked"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid x:Name="PatchMethod" Visibility="Collapsed" Width="650">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="250"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"></RowDefinition>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Border Background="{DynamicResource Muted}" HorizontalAlignment="Right" Width="2"
|
||||||
|
CornerRadius="10"/>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock FontSize="16" Text="Static" Foreground="{DynamicResource Foreground}" HorizontalAlignment="Center" Margin="0 0 0 10"/>
|
||||||
|
<controls:InfoItem
|
||||||
|
IconColor="SpringGreen"
|
||||||
|
IconData="{StaticResource CheckDecagram}"
|
||||||
|
Text="Starting WeMod without this program" />
|
||||||
|
|
||||||
|
<controls:InfoItem
|
||||||
|
Margin="0 15 0 0"
|
||||||
|
IconColor="PaleVioletRed"
|
||||||
|
IconData="{StaticResource AlertDecagram}"
|
||||||
|
Text="Violation of WeMod digital signature (possibly marked by antiviruses, anti-cheats)" />
|
||||||
|
|
||||||
|
<controls:InfoItem
|
||||||
|
Margin="0 10 0 0"
|
||||||
|
IconColor="PaleVioletRed"
|
||||||
|
IconData="{StaticResource AlertDecagram}"
|
||||||
|
Text="Auto-patching after WeMod updates is not available" />
|
||||||
|
|
||||||
|
<controls:InfoItem
|
||||||
|
Margin="0 10 0 0"
|
||||||
|
IconColor="PaleVioletRed"
|
||||||
|
IconData="{StaticResource AlertDecagram}"
|
||||||
|
Text="Hotkeys will be broken" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
<Grid Grid.Row="0" Grid.Column="1">
|
||||||
|
<StackPanel Margin="10 0 0 0">
|
||||||
|
<TextBlock FontSize="16" Text="Runtime" Foreground="{DynamicResource Foreground}"
|
||||||
|
HorizontalAlignment="Center" Margin="-10 0 0 10"/>
|
||||||
|
|
||||||
|
<controls:InfoItem
|
||||||
|
IconColor="SpringGreen"
|
||||||
|
IconData="{StaticResource CheckDecagram}"
|
||||||
|
Text="Hotkeys still work" />
|
||||||
|
|
||||||
|
<controls:InfoItem Margin="0 10 0 0"
|
||||||
|
IconColor="SpringGreen"
|
||||||
|
IconData="{StaticResource CheckDecagram}"
|
||||||
|
Text="Does not break the digital signature (does not make changes to the original .exe)" />
|
||||||
|
|
||||||
|
<controls:InfoItem Margin="0 10 0 0"
|
||||||
|
IconColor="SpringGreen"
|
||||||
|
IconData="{StaticResource CheckDecagram}"
|
||||||
|
Text="Automatically applies patches to new versions (referring to your current selection)" />
|
||||||
|
|
||||||
|
<controls:InfoItem
|
||||||
|
Margin="0 10 0 0"
|
||||||
|
IconColor="Yellow"
|
||||||
|
IconData="{StaticResource AlertDecagram}"
|
||||||
|
Text="The WeMod startup process is controlled by the patcher. (Don't worry, you will no longer see this window. You will run WeMod as usual but using the shortcut that will be created after choosing this method). So you will want to keep this program. Make sure it's in a safe directory (not Temp, Downloads, etc)." />
|
||||||
|
|
||||||
|
<controls:InfoItem Margin="0 10 0 0"
|
||||||
|
IconColor="PaleVioletRed"
|
||||||
|
IconData="{StaticResource AlertDecagram}"
|
||||||
|
Text="Running WeMod directly through official WeMod.exe is not possible until you restore patch backup" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Button Grid.Column="0" Grid.Row="1" VerticalAlignment="Bottom" HorizontalAlignment="Center"
|
||||||
|
Padding="10 5" Margin="0 15 0 0"
|
||||||
|
Click="OnStaticSelected"
|
||||||
|
Content="Use static"/>
|
||||||
|
|
||||||
|
<Button Grid.Column="1" Grid.Row="1" VerticalAlignment="Bottom" HorizontalAlignment="Center"
|
||||||
|
Padding="10 5" Margin="0 15 0 0"
|
||||||
|
Click="OnRuntimeSelected"
|
||||||
|
Content="Use runtime"/>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -3,22 +3,42 @@ using System.Collections.Generic;
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using WeModPatcher.Models;
|
using WeModPatcher.Models;
|
||||||
|
using WeModPatcher.View.Controls;
|
||||||
|
|
||||||
namespace WeModPatcher.View.Popups
|
namespace WeModPatcher.View.Popups
|
||||||
{
|
{
|
||||||
public partial class PatchVectorsPopup : UserControl
|
public partial class PatchVectorsPopup : UserControl, IDisposable
|
||||||
{
|
{
|
||||||
private readonly Action<HashSet<EPatchType>> _onApply;
|
private readonly Action<PatchConfig> _onApply;
|
||||||
|
private readonly StackPanel _popupTitleContainer;
|
||||||
|
private string _originalTitle;
|
||||||
|
private readonly TextBlock _titleTextBlock;
|
||||||
|
|
||||||
public PatchVectorsPopup(Action<HashSet<EPatchType>> onApply)
|
public PatchVectorsPopup(Action<PatchConfig> onApply)
|
||||||
{
|
{
|
||||||
_onApply = onApply;
|
_onApply = onApply;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
_popupTitleContainer = MainWindow.MainWindow.Instance.PopupHost.TitleContainer;
|
||||||
|
_titleTextBlock = _popupTitleContainer.Children[0] as TextBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
|
private void BackClicked(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if(ActivateProBox.IsChecked != true && DisableUpdateBox.IsChecked != true && DisableTelemetryBox.IsChecked != true)
|
Dispose();
|
||||||
|
PatchMethod.Visibility = Visibility.Collapsed;
|
||||||
|
PatchVectors.Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRuntimeSelected(object sender, RoutedEventArgs e)
|
||||||
|
=> RaiseCallback(EPatchProcessMethod.Runtime);
|
||||||
|
|
||||||
|
private void OnStaticSelected(object sender, RoutedEventArgs e)
|
||||||
|
=> RaiseCallback(EPatchProcessMethod.Static);
|
||||||
|
|
||||||
|
private void RaiseCallback(EPatchProcessMethod method)
|
||||||
|
{
|
||||||
|
if (ActivateProBox.IsChecked != true && DisableUpdateBox.IsChecked != true &&
|
||||||
|
DisableTelemetryBox.IsChecked != true)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -28,13 +48,35 @@ namespace WeModPatcher.View.Popups
|
|||||||
{
|
{
|
||||||
result.Add(EPatchType.ActivatePro);
|
result.Add(EPatchType.ActivatePro);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DisableUpdateBox.IsChecked == true)
|
if (DisableUpdateBox.IsChecked == true)
|
||||||
{
|
{
|
||||||
result.Add(EPatchType.DisableUpdates);
|
result.Add(EPatchType.DisableUpdates);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onApply(result);
|
_onApply(new PatchConfig
|
||||||
|
{
|
||||||
|
PatchTypes = result,
|
||||||
|
PatchMethod = method
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NextClicked(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_popupTitleContainer.Children.Insert(0, FindResource("BackButton") as Button);
|
||||||
|
_originalTitle = _titleTextBlock.Text;
|
||||||
|
_titleTextBlock.Text = "Patch method";
|
||||||
|
PatchMethod.Visibility = Visibility.Visible;
|
||||||
|
PatchVectors.Visibility = Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (PatchVectors.Visibility == Visibility.Collapsed)
|
||||||
|
{
|
||||||
|
_popupTitleContainer.Children.RemoveAt(0);
|
||||||
|
_titleTextBlock.Text = _originalTitle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"
|
<Import Project="..\packages\ILRepack.2.0.41\build\ILRepack.props" Condition="Exists('..\packages\ILRepack.2.0.41\build\ILRepack.props')" />
|
||||||
Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')"/>
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
@@ -14,9 +14,10 @@
|
|||||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<ApplicationIcon>..\assets\appicon.ico</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<Optimize>false</Optimize>
|
<Optimize>false</Optimize>
|
||||||
@@ -24,32 +25,37 @@
|
|||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<Prefer32bit>false</Prefer32bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>none</DebugType>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<OutputPath>bin\Release\</OutputPath>
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<Prefer32bit>false</Prefer32bit>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<StartupObject>WeModPatcher.Program</StartupObject>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System"/>
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core"/>
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Data"/>
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Windows.Forms" />
|
<Reference Include="System.Windows.Forms" />
|
||||||
<Reference Include="System.Xml"/>
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
<Reference Include="System.Xaml">
|
<Reference Include="System.Xaml">
|
||||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="WindowsBase"/>
|
<Reference Include="WindowsBase" />
|
||||||
<Reference Include="PresentationCore"/>
|
<Reference Include="PresentationCore" />
|
||||||
<Reference Include="PresentationFramework"/>
|
<Reference Include="PresentationFramework" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ApplicationDefinition Include="App.xaml">
|
<ApplicationDefinition Include="App.xaml">
|
||||||
@@ -59,13 +65,22 @@
|
|||||||
<Compile Include="Constants.cs" />
|
<Compile Include="Constants.cs" />
|
||||||
<Compile Include="Converters\BaseBooleanConverter.cs" />
|
<Compile Include="Converters\BaseBooleanConverter.cs" />
|
||||||
<Compile Include="Converters\ToVisibilityConverter.cs" />
|
<Compile Include="Converters\ToVisibilityConverter.cs" />
|
||||||
|
<Compile Include="Core\RuntimePatcher.cs" />
|
||||||
|
<Compile Include="Core\StaticPatcher.cs" />
|
||||||
<Compile Include="Models\PatchConfig.cs" />
|
<Compile Include="Models\PatchConfig.cs" />
|
||||||
<Compile Include="ReactiveCore\AsyncRelayCommand.cs" />
|
<Compile Include="Models\Signature.cs" />
|
||||||
<Compile Include="ReactiveCore\ObservableObject.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="ReactiveCore\RelayCommand.cs" />
|
<Compile Include="ReactiveUICore\AsyncRelayCommand.cs" />
|
||||||
<Compile Include="Utils\Patcher.cs" />
|
<Compile Include="ReactiveUICore\ObservableObject.cs" />
|
||||||
<Compile Include="Utils\PatternScanner.cs" />
|
<Compile Include="ReactiveUICore\RelayCommand.cs" />
|
||||||
|
<Compile Include="Utils\Extensions.cs" />
|
||||||
|
<Compile Include="Utils\MemoryUtils.cs" />
|
||||||
<Compile Include="Utils\Updater.cs" />
|
<Compile Include="Utils\Updater.cs" />
|
||||||
|
<Compile Include="Utils\Win32\Imports.cs" />
|
||||||
|
<Compile Include="Utils\Win32\Shortcut.cs" />
|
||||||
|
<Compile Include="View\Controls\InfoItem.xaml.cs">
|
||||||
|
<DependentUpon>InfoItem.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="View\Controls\PopupHost.xaml.cs" />
|
<Compile Include="View\Controls\PopupHost.xaml.cs" />
|
||||||
<Compile Include="View\MainWindow\Logs.cs" />
|
<Compile Include="View\MainWindow\Logs.cs" />
|
||||||
<Compile Include="View\MainWindow\MainWindow.xaml.cs" />
|
<Compile Include="View\MainWindow\MainWindow.xaml.cs" />
|
||||||
@@ -73,21 +88,14 @@
|
|||||||
<Compile Include="View\Popups\PatchVectorsPopup.xaml.cs">
|
<Compile Include="View\Popups\PatchVectorsPopup.xaml.cs">
|
||||||
<DependentUpon>PatchVectorsPopup.xaml</DependentUpon>
|
<DependentUpon>PatchVectorsPopup.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Page Include="MainWindow.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
</Page>
|
|
||||||
<Compile Include="App.xaml.cs">
|
<Compile Include="App.xaml.cs">
|
||||||
<DependentUpon>App.xaml</DependentUpon>
|
<DependentUpon>App.xaml</DependentUpon>
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="MainWindow.xaml.cs">
|
|
||||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Page Include="Style\ColorScheme.xaml" />
|
<Page Include="Style\ColorScheme.xaml" />
|
||||||
<Page Include="Style\Icons.xaml" />
|
<Page Include="Style\Icons.xaml" />
|
||||||
<Page Include="Style\Styles.xaml" />
|
<Page Include="Style\Styles.xaml" />
|
||||||
|
<Page Include="View\Controls\InfoItem.xaml" />
|
||||||
<Page Include="View\Controls\PopupHost.xaml" />
|
<Page Include="View\Controls\PopupHost.xaml" />
|
||||||
<Page Include="View\MainWindow\MainWindow.xaml" />
|
<Page Include="View\MainWindow\MainWindow.xaml" />
|
||||||
<Page Include="View\Popups\PatchVectorsPopup.xaml" />
|
<Page Include="View\Popups\PatchVectorsPopup.xaml" />
|
||||||
@@ -107,6 +115,9 @@
|
|||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<None Include="..\assets\appicon.ico">
|
||||||
|
<Link>appicon.ico</Link>
|
||||||
|
</None>
|
||||||
<None Include="App.config" />
|
<None Include="App.config" />
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -119,5 +130,28 @@
|
|||||||
<Name>AsarSharp</Name>
|
<Name>AsarSharp</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets"/>
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\packages\ILRepack.2.0.41\build\ILRepack.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\ILRepack.2.0.41\build\ILRepack.props'))" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<Target Name="ILRepack" AfterTargets="Build" Condition="'$(Configuration)' == 'Release'">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ILRepackExe>..\packages\ILRepack.2.0.41\tools\ILRepack.exe</ILRepackExe>
|
||||||
|
<MainAssembly>$(OutputPath)$(AssemblyName).exe</MainAssembly>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<AssemblyList Include="$(OutputPath)*.dll" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<DllList>@(AssemblyList->'%(FullPath)', ' ')</DllList>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Exec Command=""$(ILRepackExe)" /allowMultiple /copyattrs /out:"$(OutputPath)$(AssemblyName).exe" "$(MainAssembly)" $(DllList)" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="ILRepack" version="2.0.41" targetFramework="net48" developmentDependency="true" />
|
||||||
|
<package id="Microsoft.Build.Framework" version="15.9.20" targetFramework="net48" />
|
||||||
|
<package id="Microsoft.Build.Utilities.Core" version="15.9.20" targetFramework="net48" />
|
||||||
|
<package id="Microsoft.VisualStudio.Setup.Configuration.Interop" version="1.16.30" targetFramework="net48" developmentDependency="true" />
|
||||||
|
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" />
|
||||||
|
<package id="System.Collections.Immutable" version="1.5.0" targetFramework="net48" />
|
||||||
|
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net48" />
|
||||||
|
</packages>
|
||||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 103 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 88 KiB |
Reference in New Issue
Block a user