added updater

This commit is contained in:
kitbyte
2025-03-23 11:37:00 +02:00
parent 68cbbee275
commit 123307aed7
8 changed files with 228 additions and 30 deletions
+5
View File
@@ -28,5 +28,10 @@ namespace WeModPatcher
MessageBox.Show(e.ToString()); MessageBox.Show(e.ToString());
Shutdown(); Shutdown();
} }
public new static void Shutdown()
{
Current.Dispatcher.Invoke(() => Current.Shutdown());
}
} }
} }
+13 -2
View File
@@ -1,7 +1,18 @@
namespace WeModPatcher using System;
using System.Reflection;
namespace WeModPatcher
{ {
public static class Constants public static class Constants
{ {
public const string RepositoryUrl = "https://github.com/k1tbyte/Wemod-Patcher"; public const string RepoName = "Wemod-Patcher";
public const string Owner = "k1tbyte";
public static readonly string RepositoryUrl = $"https://github.com/{Owner}/{RepoName}";
public static readonly Version Version;
static Constants()
{
Version = Assembly.GetExecutingAssembly().GetName().Version;
}
} }
} }
+1 -1
View File
@@ -200,7 +200,7 @@ namespace WeModPatcher.Utils
return; return;
} }
await PatchPE(); // await PatchPE();
_logger("[PATCHER] Done!", ELogType.Success); _logger("[PATCHER] Done!", ELogType.Success);
} }
+130
View File
@@ -0,0 +1,130 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Net.Http;
using System.Windows;
using Newtonsoft.Json;
namespace WeModPatcher.Utils
{
public class GitHubRelease
{
public class AssetsType
{
public string Name { get; set; }
[JsonProperty("browser_download_url")]
public string Url { get; set; }
}
[JsonProperty("tag_name")]
public string TagName { get; set; }
[JsonProperty("assets")]
public AssetsType[] Assets { get; set; }
}
public class Updater
{
private GitHubRelease _release = null;
private static readonly HttpClient _httpClient = new HttpClient()
{
DefaultRequestHeaders =
{
{ "User-Agent", "GitHub-Updater" }
}
};
private static readonly string ApiUrl = $"https://api.github.com/repos/{Constants.Owner}/{Constants.RepoName}/releases/latest";
public async Task<bool> CheckForUpdates()
{
try
{
var currentVersion = Assembly.GetExecutingAssembly().GetName().Version;
var response = await _httpClient.GetAsync(ApiUrl);
response.EnsureSuccessStatusCode();
_release = JsonConvert.DeserializeObject<GitHubRelease>(await response.Content.ReadAsStringAsync());
if (_release == null)
{
return false;
}
var latestVersion = new Version(_release.TagName);
return latestVersion > currentVersion;
}
catch (Exception e)
{
return false;
}
}
public async Task Update()
{
if (_release == null)
{
throw new Exception("No release found");
}
var asset = _release.Assets.FirstOrDefault(o => o.Name.EndsWith(".exe"));
if(asset == null)
{
throw new Exception("No asset found");
}
// download to temp
var downloadPath = Path.Combine(Path.GetTempPath(), asset.Name);
using(var response = await _httpClient.GetAsync(asset.Url))
using(var fileStream = File.Create(downloadPath))
{
response.EnsureSuccessStatusCode();
await response.Content.CopyToAsync(fileStream);
}
ApplyUpdate(downloadPath);
}
private static void ApplyUpdate(string filePath)
{
try
{
var currentExecutable = Assembly.GetExecutingAssembly().Location;
var psCommand = $"Start-Sleep -Seconds 2; " +
$"Copy-Item -Path '{filePath}' -Destination '{currentExecutable}' -Force; " +
$"Remove-Item -Path '{filePath}' -Force; " +
$"Start-Sleep -Seconds 1; " +
$"Start-Process -FilePath '{currentExecutable}';";
var startInfo = new ProcessStartInfo
{
FileName = "powershell.exe",
Arguments = $"-WindowStyle Hidden -ExecutionPolicy Bypass -Command \"{psCommand}\"",
UseShellExecute = true,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
};
Process.Start(startInfo);
Task.Delay(500).ContinueWith(_ =>
{
App.Shutdown();
});
}
catch (Exception ex)
{
throw new Exception($"Update failed: {ex.Message}");
}
}
}
}
+39 -22
View File
@@ -39,6 +39,18 @@
WeMod Patcher WeMod Patcher
</Bold> </Bold>
</TextBlock> </TextBlock>
<TextBlock x:Name="VersionLabel" VerticalAlignment="Bottom"
Opacity="0.7" FontSize="10" Margin="5 0 0 5"
Foreground="{DynamicResource Foreground}">
v 1.0.0
</TextBlock>
<Button Background="SpringGreen" Foreground="{DynamicResource Muted}"
FontWeight="Medium" Padding="20 0" Margin="10 5 20 5"
ToolTip="Click to update"
Command="{Binding UpdateCommand}"
Visibility="{Binding IsUpdateAvailable, Converter={StaticResource ToVisibilityConverter}}"
Content="A new version is available"/>
</StackPanel> </StackPanel>
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal"> <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
@@ -136,28 +148,33 @@
</Border> </Border>
<Grid Grid.Row="2" HorizontalAlignment="Right" Margin="10 0 10 10"> <StackPanel HorizontalAlignment="Right" Orientation="Horizontal" Grid.Row="2" Margin="10 0 10 10">
<Grid.Style> <Grid>
<Style TargetType="{x:Type Grid}"> <Grid HorizontalAlignment="Right" >
<Style.Triggers> <Grid.Style>
<DataTrigger Binding="{Binding IsPatchEnabled}" Value="False"> <Style TargetType="{x:Type Grid}">
<Setter Property="Cursor" Value="No"/> <Style.Triggers>
</DataTrigger> <DataTrigger Binding="{Binding IsPatchEnabled}" Value="False">
</Style.Triggers> <Setter Property="Cursor" Value="No"/>
</Style> </DataTrigger>
</Grid.Style> </Style.Triggers>
<Button Style="{StaticResource ColoredButton}" </Style>
IsEnabled="{Binding IsPatchEnabled}" </Grid.Style>
FontWeight="Bold" FontSize="16" Width="200" <Button Style="{StaticResource ColoredButton}"
Command="{Binding ApplyPatchCommand}">Patch</Button> IsEnabled="{Binding IsPatchEnabled}"
</Grid> FontWeight="Bold" FontSize="16" Width="200"
<Button Grid.Row="2" Command="{Binding ApplyPatchCommand}">Patch</Button>
Margin="10 0 10 10" HorizontalAlignment="Right" </Grid>
Command="{Binding RestoreBackupCommand }" <Button HorizontalAlignment="Right"
FontWeight="Bold" FontSize="16" Width="200" Command="{Binding RestoreBackupCommand }"
Style="{StaticResource ColoredButton}" FontWeight="Bold" FontSize="16" Width="200"
Visibility="{Binding AlreadyPatched, Converter={StaticResource ToVisibilityConverter}}" Style="{StaticResource ColoredButton}"
Content="Restore"/> Visibility="{Binding AlreadyPatched, Converter={StaticResource ToVisibilityConverter}}"
Content="Restore"/>
</Grid>
</StackPanel>
<DockPanel Grid.Row="2" Margin="10 0 10 10"> <DockPanel Grid.Row="2" Margin="10 0 10 10">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" <StackPanel Orientation="Horizontal" VerticalAlignment="Center"
@@ -16,6 +16,7 @@ namespace WeModPatcher.View.MainWindow
{ {
InitializeComponent(); InitializeComponent();
this.DataContext = new MainWindowVm(this); this.DataContext = new MainWindowVm(this);
VersionLabel.Text = Constants.Version.ToString();
Instance = this; Instance = this;
} }
+33 -5
View File
@@ -19,6 +19,7 @@ namespace WeModPatcher.View.MainWindow
{ {
private readonly MainWindow _view; private readonly MainWindow _view;
public ObservableCollection<LogEntry> LogList { get; } = new ObservableCollection<LogEntry>(); public ObservableCollection<LogEntry> LogList { get; } = new ObservableCollection<LogEntry>();
private static Updater _updater = new Updater();
private string _weModPath; private string _weModPath;
@@ -43,7 +44,7 @@ namespace WeModPatcher.View.MainWindow
} }
} }
private bool _isPatchEnabled = false; private bool _isPatchEnabled;
public bool IsPatchEnabled public bool IsPatchEnabled
{ {
@@ -51,18 +52,26 @@ namespace WeModPatcher.View.MainWindow
set => SetProperty(ref _isPatchEnabled, value); set => SetProperty(ref _isPatchEnabled, value);
} }
private bool _alreadyPatched = false; private bool _alreadyPatched;
public bool AlreadyPatched public bool AlreadyPatched
{ {
get => _alreadyPatched; get => _alreadyPatched;
set => SetProperty(ref _alreadyPatched, value); set => SetProperty(ref _alreadyPatched, value);
} }
private bool _isUpdateAvailable;
public bool IsUpdateAvailable
{
get => _isUpdateAvailable;
set => SetProperty(ref _isUpdateAvailable, value);
}
public RelayCommand SetFolderPathCommand { get; } public RelayCommand SetFolderPathCommand { get; }
public RelayCommand ApplyPatchCommand { get; } public RelayCommand ApplyPatchCommand { get; }
public RelayCommand RestoreBackupCommand { get; } public RelayCommand RestoreBackupCommand { get; }
public AsyncRelayCommand UpdateCommand { get; }
private bool CheckWeModPath(string root) private static bool CheckWeModPath(string root)
{ {
try try
{ {
@@ -79,7 +88,7 @@ namespace WeModPatcher.View.MainWindow
{ {
string localAppDataPath = Environment.GetEnvironmentVariable("LOCALAPPDATA"); string localAppDataPath = Environment.GetEnvironmentVariable("LOCALAPPDATA");
string defaultDir = Path.Combine(localAppDataPath, "WeMod"); string defaultDir = Path.Combine(localAppDataPath ?? "", "WeMod");
if (!Directory.Exists(defaultDir)) if (!Directory.Exists(defaultDir))
{ {
@@ -193,18 +202,37 @@ namespace WeModPatcher.View.MainWindow
}); });
} }
private async Task OnUpdate(object param)
{
await Task.Run(async () =>
{
try
{
await _updater.Update();
}
catch (Exception e)
{
Log($"Failed to update: {e.Message}", ELogType.Error);
return;
}
Log("WeModPatcher updated successfully. Restarting...", ELogType.Success);
});
}
public MainWindowVm(MainWindow view) public MainWindowVm(MainWindow view)
{ {
Task.Run(async () => IsUpdateAvailable = await _updater.CheckForUpdates());
_view = view; _view = view;
SetFolderPathCommand = new RelayCommand(OnFolderPathSelection); SetFolderPathCommand = new RelayCommand(OnFolderPathSelection);
ApplyPatchCommand = new RelayCommand(OnPatching); ApplyPatchCommand = new RelayCommand(OnPatching);
RestoreBackupCommand = new RelayCommand(OnBackupRestoring); RestoreBackupCommand = new RelayCommand(OnBackupRestoring);
UpdateCommand = new AsyncRelayCommand(OnUpdate);
WeModPath = FindWeModDirectory(); WeModPath = FindWeModDirectory();
if (WeModPath == null) if (WeModPath == null)
{ {
Log("WeMod directory not found.", ELogType.Error); Log("WeMod directory not found.", ELogType.Error);
return;
} }
} }
} }
+6
View File
@@ -35,11 +35,15 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<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>
</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.Xaml"> <Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework> <RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference> </Reference>
@@ -61,6 +65,7 @@
<Compile Include="ReactiveCore\RelayCommand.cs" /> <Compile Include="ReactiveCore\RelayCommand.cs" />
<Compile Include="Utils\Patcher.cs" /> <Compile Include="Utils\Patcher.cs" />
<Compile Include="Utils\PatternScanner.cs" /> <Compile Include="Utils\PatternScanner.cs" />
<Compile Include="Utils\Updater.cs" />
<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" />
@@ -103,6 +108,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="App.config" /> <None Include="App.config" />
<None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Style\Inter_18pt-Regular.ttf" /> <Content Include="Style\Inter_18pt-Regular.ttf" />