Add project files.

This commit is contained in:
Junior 2023-05-12 17:19:21 -03:00
commit 5d3b4542bf
120 changed files with 36258 additions and 0 deletions

View file

@ -0,0 +1,104 @@
using Newtonsoft.Json;
using RHLauncher.RHLauncher;
using RHLauncher.RHLauncher.Helper;
using System.Diagnostics;
using System.IO.Compression;
using System.Text;
namespace RHLauncher
{
public class LauncherUpdater
{
private readonly string LauncherVersionUrl = Configuration.Default.GetLauncherVersion;
private readonly string LauncherUpdateUrl = Configuration.Default.UpdateLauncherVersion;
public async Task CheckForLauncherUpdateAsync()
{
try
{
using HttpClient client = new();
HttpResponseMessage response = await client.GetAsync(LauncherVersionUrl);
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
dynamic result = JsonConvert.DeserializeObject(json);
string version = result.version;
if (!string.IsNullOrEmpty(version))
{
string currentVersion = GetLauncherVersion();
if (!string.IsNullOrEmpty(currentVersion) && !currentVersion.Equals(version, StringComparison.OrdinalIgnoreCase))
{
MsgBoxForm.Show(LocalizedStrings.LauncherUpdateText, LocalizedStrings.Info);
await DownloadLauncherUpdateAsync(json);
}
await Task.Delay(1000);
}
}
catch (Exception ex)
{
string errorMessage = LocalizedStrings.LauncherUpdateCheckFailed + ex.Message;
string errorLog = LocalizedStrings.LauncherUpdateCheckFailed + ex.Message + ex.StackTrace;
Exception newEx = new(errorMessage, ex);
Exception newLogEx = new(errorLog, ex);
ExceptionHandler.HandleException(newEx, newLogEx);
}
}
private async Task DownloadLauncherUpdateAsync(string version)
{
try
{
using HttpClient client = new();
using StringContent content = new(version, Encoding.UTF8, "application/json");
dynamic result = JsonConvert.DeserializeObject(version);
string v = result.version;
using HttpResponseMessage response = await client.PostAsync(LauncherUpdateUrl, content);
response.EnsureSuccessStatusCode();
byte[] updateBytes = await response.Content.ReadAsByteArrayAsync();
string tempFilePath = Path.Combine(Path.GetTempPath(), $"launcher_update.zip");
File.WriteAllBytes(tempFilePath, updateBytes);
string executablePath = AppDomain.CurrentDomain.BaseDirectory;
string backupFilePath = Path.Combine(executablePath, $"Launcher_old.exe");
string launcherExePath = Path.Combine(executablePath, "Launcher.exe");
if (File.Exists(backupFilePath))
{
File.Delete(backupFilePath);
}
// Move existing launcher.exe to temporary location
File.Move(launcherExePath, backupFilePath);
// Extract new update files
ZipFile.ExtractToDirectory(tempFilePath, executablePath, true);
// Delete temporary zip file
File.Delete(tempFilePath);
MsgBoxForm.Show(LocalizedStrings.LauncherUpdateSuccess + v, LocalizedStrings.Info);
Application.Restart();
}
catch (Exception ex)
{
string errorMessage = LocalizedStrings.LauncherUpdateFailed + ex.Message;
string errorLog = LocalizedStrings.LauncherUpdateFailed + ex.Message + ex.StackTrace;
Exception newEx = new(errorMessage, ex);
Exception newLogEx = new(errorLog, ex);
ExceptionHandler.HandleException(newEx, newLogEx);
}
}
public static string GetLauncherVersion()
{
// Get the version information of the application
FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(Application.ExecutablePath);
// Extract the version number
string version = $"{versionInfo.FileMajorPart}.{versionInfo.FileMinorPart}.{versionInfo.FileBuildPart}";
return version;
}
}
}

783
RHLauncher/LocalizedStrings.Designer.cs generated Normal file
View file

@ -0,0 +1,783 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace RHLauncher.RHLauncher {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class LocalizedStrings {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal LocalizedStrings() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RHLauncher.RHLauncher.LocalizedStrings", typeof(LocalizedStrings).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Account.
/// </summary>
public static string Account {
get {
return ResourceManager.GetString("Account", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to A account with this email was not found.
/// </summary>
public static string AccountNotFound {
get {
return ResourceManager.GetString("AccountNotFound", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to rustyhearts.exe is already running..
/// </summary>
public static string AlreadyExecute {
get {
return ResourceManager.GetString("AlreadyExecute", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cancelling.
/// </summary>
public static string Cancelling {
get {
return ResourceManager.GetString("Cancelling", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Change Password.
/// </summary>
public static string ChangePassword {
get {
return ResourceManager.GetString("ChangePassword", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Auto Login.
/// </summary>
public static string CheckBoxAutoLogin {
get {
return ResourceManager.GetString("CheckBoxAutoLogin", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Remember Username.
/// </summary>
public static string CheckBoxSaveUser {
get {
return ResourceManager.GetString("CheckBoxSaveUser", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Checking.
/// </summary>
public static string Checking {
get {
return ResourceManager.GetString("Checking", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Insert the verification code.
/// </summary>
public static string CodeDescLabel {
get {
return ResourceManager.GetString("CodeDescLabel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid Verification Code format.
/// </summary>
public static string CodeDescLabelInvalid {
get {
return ResourceManager.GetString("CodeDescLabelInvalid", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Confirmation.
/// </summary>
public static string Confirmation {
get {
return ResourceManager.GetString("Confirmation", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Confirm Uninstall.
/// </summary>
public static string ConfirmUninstall {
get {
return ResourceManager.GetString("ConfirmUninstall", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Are you sure you want to uninstall and delete the install directory?.
/// </summary>
public static string ConfirmUninstallText {
get {
return ResourceManager.GetString("ConfirmUninstallText", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The verification email has been sent to.
/// </summary>
public static string DescLabelS2Email {
get {
return ResourceManager.GetString("DescLabelS2Email", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Downloading.
/// </summary>
public static string Downloading {
get {
return ResourceManager.GetString("Downloading", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Email cannot be empty.
/// </summary>
public static string EmailDescLabelEmpty {
get {
return ResourceManager.GetString("EmailDescLabelEmpty", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid email address.
/// </summary>
public static string EmailDescLabelInvalid {
get {
return ResourceManager.GetString("EmailDescLabelInvalid", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Enter your Email address.
/// </summary>
public static string EnterEmail {
get {
return ResourceManager.GetString("EnterEmail", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error.
/// </summary>
public static string Error {
get {
return ResourceManager.GetString("Error", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to This verification code has expired, please request a new one..
/// </summary>
public static string ExpiredVerificationCode {
get {
return ResourceManager.GetString("ExpiredVerificationCode", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Failed.
/// </summary>
public static string Failed {
get {
return ResourceManager.GetString("Failed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Forgot Password?.
/// </summary>
public static string ForgotPwdLabel {
get {
return ResourceManager.GetString("ForgotPwdLabel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Info.
/// </summary>
public static string Info {
get {
return ResourceManager.GetString("Info", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Install.
/// </summary>
public static string Install {
get {
return ResourceManager.GetString("Install", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid Verification Code.
/// </summary>
public static string InvalidVerificationCode {
get {
return ResourceManager.GetString("InvalidVerificationCode", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to News.
/// </summary>
public static string LabelNews {
get {
return ResourceManager.GetString("LabelNews", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Launch.
/// </summary>
public static string Launch {
get {
return ResourceManager.GetString("Launch", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error checking for launcher update: .
/// </summary>
public static string LauncherUpdateCheckFailed {
get {
return ResourceManager.GetString("LauncherUpdateCheckFailed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error downloading launcher update: .
/// </summary>
public static string LauncherUpdateFailed {
get {
return ResourceManager.GetString("LauncherUpdateFailed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Launcher updated successfully to version .
/// </summary>
public static string LauncherUpdateSuccess {
get {
return ResourceManager.GetString("LauncherUpdateSuccess", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to A new version of the launcher is available. It will be downloaded and installed automatically..
/// </summary>
public static string LauncherUpdateText {
get {
return ResourceManager.GetString("LauncherUpdateText", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Launching.
/// </summary>
public static string Launching {
get {
return ResourceManager.GetString("Launching", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Login Info.
/// </summary>
public static string LoginInfoTitle {
get {
return ResourceManager.GetString("LoginInfoTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Please insert the password..
/// </summary>
public static string LoginInsertPassword {
get {
return ResourceManager.GetString("LoginInsertPassword", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Please insert the username..
/// </summary>
public static string LoginInsertUsername {
get {
return ResourceManager.GetString("LoginInsertUsername", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid username or password..
/// </summary>
public static string LoginInvalidCredentials {
get {
return ResourceManager.GetString("LoginInvalidCredentials", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid username format..
/// </summary>
public static string LoginInvalidUsernameFormat {
get {
return ResourceManager.GetString("LoginInvalidUsernameFormat", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Your account is locked. Please contact customer support..
/// </summary>
public static string LoginLocked {
get {
return ResourceManager.GetString("LoginLocked", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Too many login attempts. Please try again later..
/// </summary>
public static string LoginTooManyAttempts {
get {
return ResourceManager.GetString("LoginTooManyAttempts", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Login Window.
/// </summary>
public static string LoginWindowTitle {
get {
return ResourceManager.GetString("LoginWindowTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Are you sure you want to logout?.
/// </summary>
public static string LogoutText {
get {
return ResourceManager.GetString("LogoutText", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Enter the new password.
/// </summary>
public static string NewPassword {
get {
return ResourceManager.GetString("NewPassword", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 6-16 characters.
/// </summary>
public static string NewPasswordDesc {
get {
return ResourceManager.GetString("NewPasswordDesc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Packing.
/// </summary>
public static string Packing {
get {
return ResourceManager.GetString("Packing", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Password changed successfully! Please log in again..
/// </summary>
public static string PasswordChanged {
get {
return ResourceManager.GetString("PasswordChanged", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Password.
/// </summary>
public static string PasswordLabel {
get {
return ResourceManager.GetString("PasswordLabel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Confirm Password cannot be empty!.
/// </summary>
public static string PwdConfirmDescLabelEmpty {
get {
return ResourceManager.GetString("PwdConfirmDescLabelEmpty", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Passwords do not match!.
/// </summary>
public static string PwdConfirmDescLabelMatch {
get {
return ResourceManager.GetString("PwdConfirmDescLabelMatch", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Password must have at least one uppercase, one lowercase letter, and one number.
/// </summary>
public static string PwdDescLabelCriteria {
get {
return ResourceManager.GetString("PwdDescLabelCriteria", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Password cannot be empty!.
/// </summary>
public static string PwdDescLabelEmpty {
get {
return ResourceManager.GetString("PwdDescLabelEmpty", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Password must be between 6-16 characters!.
/// </summary>
public static string PwdDescLabelSize {
get {
return ResourceManager.GetString("PwdDescLabelSize", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Medium.
/// </summary>
public static string PwdStrengthLabelMedium {
get {
return ResourceManager.GetString("PwdStrengthLabelMedium", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Strong.
/// </summary>
public static string PwdStrengthLabelStrong {
get {
return ResourceManager.GetString("PwdStrengthLabelStrong", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Weak.
/// </summary>
public static string PwdStrengthLabelWeak {
get {
return ResourceManager.GetString("PwdStrengthLabelWeak", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Re-enter the new password.
/// </summary>
public static string RepeatPassword {
get {
return ResourceManager.GetString("RepeatPassword", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Repeat the password.
/// </summary>
public static string RepeatPasswordDesc {
get {
return ResourceManager.GetString("RepeatPasswordDesc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &lt; Return.
/// </summary>
public static string Return {
get {
return ResourceManager.GetString("Return", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Running.
/// </summary>
public static string Running {
get {
return ResourceManager.GetString("Running", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Rusty Hearts.
/// </summary>
public static string RustyHearts {
get {
return ResourceManager.GetString("RustyHearts", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Could not find rustyheartsconfig.exe.
/// </summary>
public static string rustyheartsconfig {
get {
return ResourceManager.GetString("rustyheartsconfig", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Same password. Please use another password..
/// </summary>
public static string SamePassword {
get {
return ResourceManager.GetString("SamePassword", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cannot connect to the game server..
/// </summary>
public static string ServerOffline {
get {
return ResourceManager.GetString("ServerOffline", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Success.
/// </summary>
public static string Success {
get {
return ResourceManager.GetString("Success", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Uninstall.
/// </summary>
public static string Uninstall {
get {
return ResourceManager.GetString("Uninstall", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Uninstalling.
/// </summary>
public static string Uninstalling {
get {
return ResourceManager.GetString("Uninstalling", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Uninstall complete..
/// </summary>
public static string UninstallText {
get {
return ResourceManager.GetString("UninstallText", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unpacking.
/// </summary>
public static string Unpacking {
get {
return ResourceManager.GetString("Unpacking", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unsupported client region..
/// </summary>
public static string UnsupportedService {
get {
return ResourceManager.GetString("UnsupportedService", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Update.
/// </summary>
public static string Update {
get {
return ResourceManager.GetString("Update", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to An error occurred while checking for updates..
/// </summary>
public static string UpdateCheckError {
get {
return ResourceManager.GetString("UpdateCheckError", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to An error occurred while downloading updates..
/// </summary>
public static string UpdateDownloadError {
get {
return ResourceManager.GetString("UpdateDownloadError", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Updating.
/// </summary>
public static string Updating {
get {
return ResourceManager.GetString("Updating", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Name cannot be empty.
/// </summary>
public static string UsernameDescLabelEmpty {
get {
return ResourceManager.GetString("UsernameDescLabelEmpty", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Name must be alphanumeric with at least one letter.
/// </summary>
public static string UsernameDescLabelInvalid {
get {
return ResourceManager.GetString("UsernameDescLabelInvalid", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Username must be between 6-16 characters.
/// </summary>
public static string UsernameDescLabelSize {
get {
return ResourceManager.GetString("UsernameDescLabelSize", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Username/Email.
/// </summary>
public static string UsernameLabel {
get {
return ResourceManager.GetString("UsernameLabel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Verification Code.
/// </summary>
public static string VerificationCode {
get {
return ResourceManager.GetString("VerificationCode", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The verificaton email has been sent to.
/// </summary>
public static string VerificationEmailSent {
get {
return ResourceManager.GetString("VerificationEmailSent", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Version.
/// </summary>
public static string Version {
get {
return ResourceManager.GetString("Version", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Welcome.
/// </summary>
public static string Welcome {
get {
return ResourceManager.GetString("Welcome", resourceCulture);
}
}
}
}

View file

@ -0,0 +1,360 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Account" xml:space="preserve">
<value>Account</value>
</data>
<data name="AccountNotFound" xml:space="preserve">
<value>A account with this email was not found</value>
</data>
<data name="AlreadyExecute" xml:space="preserve">
<value>rustyhearts.exe is already running.</value>
</data>
<data name="Cancelling" xml:space="preserve">
<value>Cancelling</value>
</data>
<data name="ChangePassword" xml:space="preserve">
<value>Change Password</value>
</data>
<data name="CheckBoxAutoLogin" xml:space="preserve">
<value>Auto Login</value>
</data>
<data name="CheckBoxSaveUser" xml:space="preserve">
<value>Remember Username</value>
</data>
<data name="Checking" xml:space="preserve">
<value>Checking</value>
</data>
<data name="CodeDescLabel" xml:space="preserve">
<value>Insert the verification code</value>
</data>
<data name="CodeDescLabelInvalid" xml:space="preserve">
<value>Invalid Verification Code format</value>
</data>
<data name="Confirmation" xml:space="preserve">
<value>Confirmation</value>
</data>
<data name="ConfirmUninstall" xml:space="preserve">
<value>Confirm Uninstall</value>
</data>
<data name="ConfirmUninstallText" xml:space="preserve">
<value>Are you sure you want to uninstall and delete the install directory?</value>
</data>
<data name="DescLabelS2Email" xml:space="preserve">
<value>The verification email has been sent to</value>
</data>
<data name="Downloading" xml:space="preserve">
<value>Downloading</value>
</data>
<data name="EmailDescLabelEmpty" xml:space="preserve">
<value>Email cannot be empty</value>
</data>
<data name="EmailDescLabelInvalid" xml:space="preserve">
<value>Invalid email address</value>
</data>
<data name="EnterEmail" xml:space="preserve">
<value>Enter your Email address</value>
</data>
<data name="Error" xml:space="preserve">
<value>Error</value>
</data>
<data name="ExpiredVerificationCode" xml:space="preserve">
<value>This verification code has expired, please request a new one.</value>
</data>
<data name="Failed" xml:space="preserve">
<value>Failed</value>
</data>
<data name="ForgotPwdLabel" xml:space="preserve">
<value>Forgot Password?</value>
</data>
<data name="Info" xml:space="preserve">
<value>Info</value>
</data>
<data name="Install" xml:space="preserve">
<value>Install</value>
</data>
<data name="InvalidVerificationCode" xml:space="preserve">
<value>Invalid Verification Code</value>
</data>
<data name="LabelNews" xml:space="preserve">
<value>News</value>
</data>
<data name="Launch" xml:space="preserve">
<value>Launch</value>
</data>
<data name="LauncherUpdateCheckFailed" xml:space="preserve">
<value>Error checking for launcher update: </value>
</data>
<data name="LauncherUpdateFailed" xml:space="preserve">
<value>Error downloading launcher update: </value>
</data>
<data name="LauncherUpdateSuccess" xml:space="preserve">
<value>Launcher updated successfully to version </value>
</data>
<data name="LauncherUpdateText" xml:space="preserve">
<value>A new version of the launcher is available. It will be downloaded and installed automatically.</value>
</data>
<data name="Launching" xml:space="preserve">
<value>Launching</value>
</data>
<data name="LoginInfoTitle" xml:space="preserve">
<value>Login Info</value>
</data>
<data name="LoginInsertPassword" xml:space="preserve">
<value>Please insert the password.</value>
</data>
<data name="LoginInsertUsername" xml:space="preserve">
<value>Please insert the username.</value>
</data>
<data name="LoginInvalidCredentials" xml:space="preserve">
<value>Invalid username or password.</value>
</data>
<data name="LoginInvalidUsernameFormat" xml:space="preserve">
<value>Invalid username format.</value>
</data>
<data name="LoginLocked" xml:space="preserve">
<value>Your account is locked. Please contact customer support.</value>
</data>
<data name="LoginTooManyAttempts" xml:space="preserve">
<value>Too many login attempts. Please try again later.</value>
</data>
<data name="LoginWindowTitle" xml:space="preserve">
<value>Login Window</value>
</data>
<data name="LogoutText" xml:space="preserve">
<value>Are you sure you want to logout?</value>
</data>
<data name="NewPassword" xml:space="preserve">
<value>Enter the new password</value>
</data>
<data name="NewPasswordDesc" xml:space="preserve">
<value>6-16 characters</value>
</data>
<data name="Packing" xml:space="preserve">
<value>Packing</value>
</data>
<data name="PasswordChanged" xml:space="preserve">
<value>Password changed successfully! Please log in again.</value>
</data>
<data name="PasswordLabel" xml:space="preserve">
<value>Password</value>
</data>
<data name="PwdConfirmDescLabelEmpty" xml:space="preserve">
<value>Confirm Password cannot be empty!</value>
</data>
<data name="PwdConfirmDescLabelMatch" xml:space="preserve">
<value>Passwords do not match!</value>
</data>
<data name="PwdDescLabelCriteria" xml:space="preserve">
<value>Password must have at least one uppercase, one lowercase letter, and one number</value>
</data>
<data name="PwdDescLabelEmpty" xml:space="preserve">
<value>Password cannot be empty!</value>
</data>
<data name="PwdDescLabelSize" xml:space="preserve">
<value>Password must be between 6-16 characters!</value>
</data>
<data name="PwdStrengthLabelMedium" xml:space="preserve">
<value>Medium</value>
</data>
<data name="PwdStrengthLabelStrong" xml:space="preserve">
<value>Strong</value>
</data>
<data name="PwdStrengthLabelWeak" xml:space="preserve">
<value>Weak</value>
</data>
<data name="RepeatPassword" xml:space="preserve">
<value>Re-enter the new password</value>
</data>
<data name="RepeatPasswordDesc" xml:space="preserve">
<value>Repeat the password</value>
</data>
<data name="Return" xml:space="preserve">
<value>&lt; Return</value>
</data>
<data name="Running" xml:space="preserve">
<value>Running</value>
</data>
<data name="RustyHearts" xml:space="preserve">
<value>Rusty Hearts</value>
</data>
<data name="rustyheartsconfig" xml:space="preserve">
<value>Could not find rustyheartsconfig.exe</value>
</data>
<data name="SamePassword" xml:space="preserve">
<value>Same password. Please use another password.</value>
</data>
<data name="ServerOffline" xml:space="preserve">
<value>Cannot connect to the game server.</value>
</data>
<data name="Success" xml:space="preserve">
<value>Success</value>
</data>
<data name="Uninstall" xml:space="preserve">
<value>Uninstall</value>
</data>
<data name="Uninstalling" xml:space="preserve">
<value>Uninstalling</value>
</data>
<data name="UninstallText" xml:space="preserve">
<value>Uninstall complete.</value>
</data>
<data name="Unpacking" xml:space="preserve">
<value>Unpacking</value>
</data>
<data name="UnsupportedService" xml:space="preserve">
<value>Unsupported client region.</value>
</data>
<data name="Update" xml:space="preserve">
<value>Update</value>
</data>
<data name="UpdateCheckError" xml:space="preserve">
<value>An error occurred while checking for updates.</value>
</data>
<data name="UpdateDownloadError" xml:space="preserve">
<value>An error occurred while downloading updates.</value>
</data>
<data name="Updating" xml:space="preserve">
<value>Updating</value>
</data>
<data name="UsernameDescLabelEmpty" xml:space="preserve">
<value>Name cannot be empty</value>
</data>
<data name="UsernameDescLabelInvalid" xml:space="preserve">
<value>Name must be alphanumeric with at least one letter</value>
</data>
<data name="UsernameDescLabelSize" xml:space="preserve">
<value>Username must be between 6-16 characters</value>
</data>
<data name="UsernameLabel" xml:space="preserve">
<value>Username/Email</value>
</data>
<data name="VerificationCode" xml:space="preserve">
<value>Verification Code</value>
</data>
<data name="VerificationEmailSent" xml:space="preserve">
<value>The verificaton email has been sent to</value>
</data>
<data name="Version" xml:space="preserve">
<value>Version</value>
</data>
<data name="Welcome" xml:space="preserve">
<value>Welcome</value>
</data>
</root>

View file

@ -0,0 +1,25 @@
namespace RHLauncher.RHLauncher
{
public class ProgressReport
{
public string? Label { get; set; }
public int PercentComplete { get; set; }
public string? FileName { get; set; }
public string? ErrorMessage { get; set; }
public long BytesDownloaded { get; set; }
public long TotalBytesToDownload { get; set; }
public double TotalSpeed { get; set; }
public long TimeLeft { get; set; }
public int NumFilesDownloaded { get; set; }
public int TotalNumFiles { get; set; }
public int NumFilesPacked { get; set; }
public int NumFilesUnpacked { get; set; }
public bool Panel { get; set; }
public CancellationToken CancellationToken { get; set; }
public bool ShowFileNameLabel { get; set; }
public bool ShowSpeedLabel { get; set; }
public bool ShowTimeLabel { get; set; }
public bool ShowFileSizeLabel { get; set; }
public bool ShowFileCountLabel { get; set; }
}
}

View file

@ -0,0 +1,86 @@
using System.Diagnostics;
namespace RHLauncher.RHLauncher
{
internal class ProgressReporter
{
public static void ReportInstallProgress(IProgress<ProgressReport> progress, string label, string file, int pos, int count, CancellationToken cancellationToken)
{
ProgressReport report = new()
{
Panel = true,
ShowFileNameLabel = true,
ShowSpeedLabel = false,
ShowTimeLabel = false,
ShowFileSizeLabel = false,
ShowFileCountLabel = true,
Label = label,
FileName = Path.GetFileName(file),
PercentComplete = (int)Math.Round(pos * 100.0 / count),
NumFilesPacked = pos,
TotalNumFiles = count,
CancellationToken = cancellationToken
};
progress.Report(report);
}
public static void ReportDownloadProgress(IProgress<ProgressReport> progress, string fileName, long downloadedSize, long totalBytesToDownload, int downloadedFileCount, int totalFilesToDownload, Stopwatch sw, CancellationToken cancellationToken)
{
int percentComplete = totalBytesToDownload > 0 ? (int)Math.Round(downloadedSize * 100.0 / totalBytesToDownload) : 0;
double totalSpeed = downloadedSize / sw.Elapsed.TotalSeconds;
long timeLeft = totalBytesToDownload > 0 ? (long)((totalBytesToDownload - downloadedSize) / (downloadedSize / sw.Elapsed.TotalSeconds)) : 0;
ProgressReport report = new()
{
Panel = true,
ShowFileNameLabel = true,
ShowSpeedLabel = true,
ShowTimeLabel = true,
ShowFileSizeLabel = true,
ShowFileCountLabel = true,
Label = LocalizedStrings.Downloading,
PercentComplete = percentComplete,
FileName = fileName,
BytesDownloaded = downloadedSize,
TotalBytesToDownload = totalBytesToDownload,
TotalSpeed = totalSpeed,
TimeLeft = timeLeft,
NumFilesDownloaded = downloadedFileCount,
TotalNumFiles = totalFilesToDownload,
CancellationToken = cancellationToken
};
progress.Report(report);
}
public static void ReportErrorProgress(IProgress<ProgressReport> progress, string message, CancellationToken cancellationToken)
{
ProgressReport report = new()
{
Panel = true,
ShowFileNameLabel = false,
ShowSpeedLabel = false,
ShowTimeLabel = false,
ShowFileSizeLabel = false,
ShowFileCountLabel = false,
Label = LocalizedStrings.Error,
ErrorMessage = message,
CancellationToken = cancellationToken
};
progress.Report(report);
}
public static void ReportFinishedProgress(IProgress<ProgressReport> progress, CancellationToken cancellationToken)
{
ProgressReport report = new()
{
Panel = false,
CancellationToken = cancellationToken
};
progress.Report(report);
}
}
}

View file

@ -0,0 +1,40 @@
namespace RHLauncher.RHLauncher
{
public class ProgressThrottler : IProgress<ProgressReport>
{
private readonly IProgress<ProgressReport> _progress;
private readonly int _intervalMs;
private readonly TaskScheduler _scheduler;
private readonly object _lock = new();
private bool _isScheduled = false;
private ProgressReport? _latestReport;
public ProgressThrottler(IProgress<ProgressReport> progress, int intervalMs)
{
_progress = progress;
_intervalMs = intervalMs;
_scheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
public void Report(ProgressReport value)
{
lock (_lock)
{
_latestReport = value;
if (!_isScheduled)
{
_isScheduled = true;
Task.Delay(_intervalMs, _latestReport.CancellationToken).ContinueWith(_ =>
{
lock (_lock)
{
_progress.Report(_latestReport);
_isScheduled = false;
}
}, _latestReport.CancellationToken, TaskContinuationOptions.ExecuteSynchronously, _scheduler);
}
}
}
}
}

View file

@ -0,0 +1,226 @@
using RHLauncher.Helper;
using RHLauncher.PCK;
using RHLauncher.RHLauncher.Helper;
using System.Diagnostics;
using System.Globalization;
using System.Net;
namespace RHLauncher.RHLauncher
{
public class UpdateDownloader
{
private const int BufferSize = 8192;
private const int TimeoutSeconds = 30;
public static async Task<UpdateState> CheckForUpdatesAsync()
{
try
{
string fileListUrl = Configuration.Default.FileListUrl;
using var client = new HttpClient();
using var response = await client.GetAsync(fileListUrl);
using var content = await response.Content.ReadAsStreamAsync();
using var reader = new StreamReader(content);
string filelistText = await reader.ReadToEndAsync();
List<PCKFile> fileList = PCKReader.GetPCKFileList();
Dictionary<string, uint> fileHashes = fileList.ToDictionary(f => f.Name, f => f.Hash);
List<string> filesToUpdate = new();
foreach (string line in filelistText.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
{
string[] parts = line.Split(' ');
if (parts.Length != 3)
{
continue;
}
string fileName = parts[0];
if (fileName.Contains(' '))
{
fileName = $"\"{fileName}\"";
}
if (!ulong.TryParse(parts[2], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong fileHashFromServer))
{
continue;
}
if (!fileHashes.TryGetValue(fileName, out uint fileHash) || fileHash != fileHashFromServer)
{
filesToUpdate.Add(fileName);
}
}
fileHashes.Clear();
return filesToUpdate.Count > 0 ? UpdateState.UpdateAvailable : UpdateState.NoUpdateAvailable;
}
catch (Exception ex)
{
string errorMessage = LocalizedStrings.UpdateCheckError + "\n" + ex.Message;
string errorLog = LocalizedStrings.UpdateCheckError + ex.Message + ex.StackTrace;
Exception newEx = new(errorMessage, ex);
Exception newLogEx = new(errorLog, ex);
ExceptionHandler.HandleException(newEx, newLogEx);
return UpdateState.Error;
}
}
public enum UpdateState
{
UpdateAvailable,
NoUpdateAvailable,
Error
}
public static async Task DownloadUpdatesAsync(string installDirectory, IProgress<ProgressReport> progress, CancellationToken cancellationToken)
{
using HttpClientHandler handler = new() { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate };
using HttpClient client = new(handler, true) { Timeout = TimeSpan.FromSeconds(TimeoutSeconds) };
try
{
string filelistUrl = Configuration.Default.FileListUrl;
string filelistText = await client.GetStringAsync(filelistUrl, cancellationToken).ConfigureAwait(false);
List<PCKFile> fileList = PCKReader.GetPCKFileList();
Dictionary<string, uint> fileHashes = fileList.ToDictionary(f => f.Name, f => f.Hash);
HashSet<string> localFileNames = new(fileHashes.Keys);
List<string> filesToBeDownloaded = new();
long totalBytesToDownload = 0L;
int totalFilesToDownload = 0;
foreach (string line in filelistText.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
{
cancellationToken.ThrowIfCancellationRequested();
string[] parts = line.Split(' ');
if (parts.Length != 3)
{
continue;
}
string fileName = parts[0];
if (string.IsNullOrWhiteSpace(parts[0]))
{
fileName = $"\"{fileName}\"";
}
if (!ulong.TryParse(parts[2], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong fileHashFromServer))
{
continue;
}
if (!fileHashes.TryGetValue(fileName, out uint fileHash) || fileHash != fileHashFromServer)
{
filesToBeDownloaded.Add(fileName);
long fileSize = await GetFileSizeAsync(client, $"{Configuration.Default.DownloadUpdateFileUrl}/{fileName}.mip", cancellationToken).ConfigureAwait(false);
totalBytesToDownload += fileSize;
totalFilesToDownload++;
}
}
string archiveDir = Path.Combine(installDirectory, "archive");
if (!Directory.Exists(archiveDir))
{
Directory.CreateDirectory(archiveDir);
}
Stopwatch sw = Stopwatch.StartNew();
long downloadedBytes = 0L;
int downloadedSize = 0;
int downloadedFileCount = 0;
foreach (string fileName in filesToBeDownloaded)
{
cancellationToken.ThrowIfCancellationRequested();
string finalfileName = fileName + ".mip";
string fileUrl = $"{Configuration.Default.DownloadUpdateFileUrl}/{finalfileName}";
string filePath = Path.Combine(archiveDir, finalfileName);
using HttpResponseMessage response = await client.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
await using Stream contentStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
if (!Directory.Exists(Path.GetDirectoryName(filePath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
}
await using FileStream fileStream = new(filePath, FileMode.Create, FileAccess.Write, FileShare.None, BufferSize, true);
byte[] buffer = new byte[BufferSize];
int bytesRead;
while ((bytesRead = await contentStream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) > 0)
{
cancellationToken.ThrowIfCancellationRequested();
await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false);
downloadedBytes += bytesRead;
downloadedSize += bytesRead;
}
ProgressReporter.ReportDownloadProgress(progress, finalfileName, downloadedSize, totalBytesToDownload, downloadedFileCount, totalFilesToDownload, sw, cancellationToken);
await Task.Yield();
downloadedFileCount++;
}
fileHashes.Clear();
ProgressReporter.ReportFinishedProgress(progress, cancellationToken);
}
catch (OperationCanceledException ex)
{
Logger.WriteLog($"Download update cancelled: {ex.Message}");
}
catch (Exception ex)
{
ProgressReporter.ReportErrorProgress(progress, ex.Message, cancellationToken);
string errorMessage = LocalizedStrings.UpdateDownloadError + "\n" + ex.Message;
string errorLog = LocalizedStrings.UpdateDownloadError + ex.Message + ex.StackTrace;
Exception newEx = new(errorMessage, ex);
Exception newLogEx = new(errorLog, ex);
ExceptionHandler.HandleException(newEx, newLogEx);
}
}
private static async Task<long> GetFileSizeAsync(HttpClient client, string fileUrl, CancellationToken cancellationToken)
{
using HttpRequestMessage request = new(HttpMethod.Get, fileUrl);
using HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
return response.Content.Headers.ContentLength ?? throw new Exception("Content-Length header not found in response headers.");
}
public static string FormatDownloadSpeed(double totalDownloadSpeed)
{
string[] sizes = { "B/s", "KB/s", "MB/s", "GB/s" };
int order = 0;
while (totalDownloadSpeed >= 1024 && order < sizes.Length - 1)
{
order++;
totalDownloadSpeed /= 1024;
}
return $"{totalDownloadSpeed:0.#} {sizes[order]}";
}
public static string FormatFileSize(long bytes)
{
string[] sizes = { "B", "KB", "MB", "GB" };
int order = 0;
while (bytes >= 1024 && order < sizes.Length - 1)
{
order++;
bytes /= 1024;
}
return $"{bytes:0.#} {sizes[order]}";
}
}
}

View file

@ -0,0 +1,86 @@
using RHLauncher.Helper;
using RHLauncher.PCK;
namespace RHLauncher.RHLauncher
{
public class UpdateInstaller
{
public static async Task PackDownloadedFilesAsync(string installDir, IProgress<ProgressReport> progress, CancellationToken cancellationToken)
{
string archiveDir = Path.Combine(installDir, "archive");
string updateRootFolder = Path.Combine(installDir, "Update");
if (!Directory.Exists(updateRootFolder))
{
return;
}
List<string> fileList = Directory.EnumerateFiles(updateRootFolder, "*", SearchOption.AllDirectories).ToList();
List<PCKFile> existingFiles = PCKReader.GetPCKFileList();
int count = fileList.Count;
int pos = 0;
try
{
await PCKWriter.Packing(updateRootFolder, fileList, existingFiles, true,
(string fileName, int curPos, int totalCount) =>
{
cancellationToken.ThrowIfCancellationRequested();
pos++;
ProgressReporter.ReportInstallProgress(progress, LocalizedStrings.Packing, fileName, pos, count, cancellationToken);
},
cancellationToken
).ConfigureAwait(false);
}
catch (OperationCanceledException ex)
{
if (Directory.Exists(archiveDir))
{
Directory.Delete(archiveDir, true);
Directory.CreateDirectory(archiveDir);
}
if (Directory.Exists(updateRootFolder))
{
GC.Collect();
GC.WaitForPendingFinalizers();
Directory.Delete(updateRootFolder, true);
}
Logger.WriteLog($"Update installation canceled: {ex.Message}");
}
catch (Exception ex)
{
Logger.WriteLog($"An error occurred while packing: {ex.Message}");
ProgressReporter.ReportErrorProgress(progress, ex.Message, cancellationToken);
throw;
}
finally
{
fileList.Clear();
existingFiles.Clear();
}
ProgressReporter.ReportFinishedProgress(progress, cancellationToken);
// Pause for a short time to allow other tasks to run
await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
if (Directory.Exists(archiveDir))
{
Directory.Delete(archiveDir, true);
Directory.CreateDirectory(archiveDir);
}
if (Directory.Exists(updateRootFolder))
{
GC.Collect();
GC.WaitForPendingFinalizers();
Directory.Delete(updateRootFolder, true);
}
}
}
}

View file

@ -0,0 +1,74 @@
using RHLauncher.Helper;
namespace RHLauncher.RHLauncher
{
public class UpdateUnpacker
{
public static async Task UnpackDownloadedFilesAsync(string installDir, IProgress<ProgressReport> progress, CancellationToken cancellationToken)
{
if (installDir == null)
throw new ArgumentNullException(nameof(installDir));
if (progress == null)
throw new ArgumentNullException(nameof(progress));
string archiveDir = Path.Combine(installDir, "archive");
string updateRootFolder = Path.Combine(installDir, "Update");
if (!Directory.Exists(archiveDir))
{
return;
}
if (!Directory.Exists(updateRootFolder))
{
Directory.CreateDirectory(updateRootFolder);
}
List<string> files = Directory.EnumerateFiles(archiveDir, "*.mip", SearchOption.AllDirectories)
.OrderBy(f => f, StringComparer.OrdinalIgnoreCase)
.ToList();
int count = files.Count;
int pos = 0;
await Task.Run(async () =>
{
foreach (string file in files)
{
cancellationToken.ThrowIfCancellationRequested();
string relativePath = Path.GetRelativePath(archiveDir, file);
string outputPath = Path.Combine(updateRootFolder, relativePath.Substring(0, relativePath.Length - 4)); // Remove the ".mip" extension
try
{
Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
await MIPDecoder.DecompressFileAsync(file, outputPath, cancellationToken);
pos++;
ProgressReporter.ReportInstallProgress(progress, LocalizedStrings.Unpacking, file, pos, count, cancellationToken);
}
catch (OperationCanceledException ex)
{
Logger.WriteLog($"Update unpack canceled: {ex.Message}");
}
catch (ArgumentException ex)
{
Logger.WriteLog($"An error occurred while unpacking: {file} {ex}");
ProgressReporter.ReportErrorProgress(progress, ex.Message, cancellationToken);
}
catch (Exception ex)
{
Logger.WriteLog($"Error: {ex.Message} {ex.StackTrace} ");
throw;
}
}
ProgressReporter.ReportFinishedProgress(progress, cancellationToken);
}, cancellationToken);
}
}
}