diff --git a/SCTools/SCTool_Redesigned/Pages/updatePatcher.xaml.cs b/SCTools/SCTool_Redesigned/Pages/updatePatcher.xaml.cs index 70963a2..29edf86 100644 --- a/SCTools/SCTool_Redesigned/Pages/updatePatcher.xaml.cs +++ b/SCTools/SCTool_Redesigned/Pages/updatePatcher.xaml.cs @@ -16,8 +16,8 @@ namespace SCTool_Redesigned.Pages /// public partial class updatePatcher : Page { - private static ApplicationUpdater _updater = new ApplicationUpdater(GetUpdateRepository(), App.ExecutableDir, Properties.Resources.UpdateScript, new CustomPackageVerifier()); - private static CancellationTokenSource _cancellationToken = new CancellationTokenSource(); //TODO: Dispose, cancel when exit + private static CustomApplicationUpdater _updater = new(GetUpdateRepository(), App.ExecutableDir, Properties.Resources.UpdateScript, new CustomPackageVerifier()); + private static CancellationTokenSource _cancellationToken = new(); //TODO: Dispose, cancel when exit public updatePatcher() { InitializeComponent(); diff --git a/SCTools/SCTool_Redesigned/Properties/Resources.ko-KR.resx b/SCTools/SCTool_Redesigned/Properties/Resources.ko-KR.resx index fc9b46f..1e78d7c 100644 --- a/SCTools/SCTool_Redesigned/Properties/Resources.ko-KR.resx +++ b/SCTools/SCTool_Redesigned/Properties/Resources.ko-KR.resx @@ -401,7 +401,7 @@ **문서를 불러올 수 없습니다.** - 런처 업데이트 중 + 패쳐 업데이트 중 환영합니다 diff --git a/SCTools/SCTool_Redesigned/Resources/update.bat b/SCTools/SCTool_Redesigned/Resources/update.bat index 526b4fd..877ade0 100644 --- a/SCTools/SCTool_Redesigned/Resources/update.bat +++ b/SCTools/SCTool_Redesigned/Resources/update.bat @@ -5,14 +5,14 @@ set updatepath=%workpath%updates\ set latestpath=%updatepath%latest\ timeout 1 -xcopy "%latestpath%*.*" "%workpath%" /s /k /h /y +::xcopy "%latestpath%*.*" "%workpath%" /s /k /h /y if not errorlevel 0 goto update_error del "%updatepath%latest.json" -del "%updatepath%latest.zip" -del /q "%latestpath%*" -for /d %%p in ("%latestpath%*.*") do rmdir /s /q "%%p" -rmdir /s /q "%latestpath%" +::del "%updatepath%latest.zip" +::del /q "%latestpath%*" +::for /d %%p in ("%latestpath%*.*") do rmdir /s /q "%%p" +::rmdir /s /q "%latestpath%" start "" "%workpath%Shatagon.exe" update_status 0 exit diff --git a/SCTools/SCTool_Redesigned/Update/CustomApplicationUpdater.cs b/SCTools/SCTool_Redesigned/Update/CustomApplicationUpdater.cs new file mode 100644 index 0000000..5663c35 --- /dev/null +++ b/SCTools/SCTool_Redesigned/Update/CustomApplicationUpdater.cs @@ -0,0 +1,232 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO.Compression; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NLog; +using NSW.StarCitizen.Tools.Lib.Helpers; +using NSW.StarCitizen.Tools.Lib.Update; + +namespace SCTool_Redesigned.Update +{ + public class CustomApplicationUpdater : IDisposable + { + private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); + private readonly IUpdateRepository _updateRepository; + private readonly IPackageVerifier _packageVerifier; + private readonly string _executableDir; + private readonly string _updateScriptContent; + private readonly string _updateScriptPath; + private readonly string _updatesStoragePath; + private readonly string _schedInstallArchivePath; + private readonly string _schedInstallExecutablePath; + private readonly string _schedInstallJsonPath; + private readonly string _installUnpackedDir; + private readonly string _installUnpackedExecutablePath; + private readonly string _currentVersion; + + public interface IPackageVerifier + { + bool VerifyPackage(string path); + } + + public event EventHandler MonitorStarted + { + add { _updateRepository.MonitorStarted += value; } + remove { _updateRepository.MonitorStarted -= value; } + } + + public event EventHandler MonitorStopped + { + add { _updateRepository.MonitorStopped += value; } + remove { _updateRepository.MonitorStopped -= value; } + } + + public event EventHandler MonitorNewVersion + { + add { _updateRepository.MonitorNewVersion += value; } + remove { _updateRepository.MonitorNewVersion -= value; } + } + + public bool AllowPreReleases + { + get => _updateRepository.AllowPreReleases; + set => _updateRepository.AllowPreReleases = value; + } + + public CustomApplicationUpdater(IUpdateRepository updateRepository, string executableDir, + string updateScriptContent, IPackageVerifier packageVerifier) + { + if (updateRepository.CurrentVersion == null) + throw new InvalidOperationException("update repository current version is not set"); + _updateRepository = updateRepository; + _executableDir = executableDir; + _updateScriptContent = updateScriptContent; + _packageVerifier = packageVerifier; + _updateScriptPath = Path.Combine(_executableDir, "update.bat"); + _updatesStoragePath = Path.Combine(_executableDir, "updates"); + _schedInstallArchivePath = Path.Combine(_updatesStoragePath, "latest.zip"); + _schedInstallExecutablePath = Path.Combine(_updatesStoragePath, "shatagon.exe"); + _schedInstallJsonPath = Path.Combine(_updatesStoragePath, "latest.json"); + _installUnpackedDir = Path.Combine(_updatesStoragePath, "latest"); + _installUnpackedExecutablePath = Path.Combine(_updatesStoragePath, "latest", "shatagon.exe"); + _currentVersion = _updateRepository.CurrentVersion; + } + + public void Dispose() => _updateRepository.Dispose(); + + public void MonitorStart(int refreshTime) => _updateRepository.MonitorStart(refreshTime); + + public void MonitorStop() => _updateRepository.MonitorStop(); + + public async Task CheckForUpdateVersionAsync(CancellationToken cancellationToken) + { + var latestUpdateInfo = await _updateRepository.GetLatestAsync(cancellationToken); + if (latestUpdateInfo != null && string.Compare(latestUpdateInfo.GetVersion(), + _currentVersion, StringComparison.OrdinalIgnoreCase) != 0) + { + return latestUpdateInfo; + } + return null; + } + + public async Task DownloadVersionAsync(UpdateInfo version, CancellationToken cancellationToken, IDownloadProgress downloadProgress) + { + if (!Directory.Exists(_updatesStoragePath)) + { + Directory.CreateDirectory(_updatesStoragePath); + } + return await _updateRepository.DownloadAsync(version, _updatesStoragePath, cancellationToken, downloadProgress); + } + + public InstallUpdateStatus InstallScheduledUpdate() + { + _logger.Info("Install scheduled update"); + if (ExtractReadyInstallUpdate() && ExtractUpdateScript()) + { + using var updateProcess = new Process(); + updateProcess.StartInfo.UseShellExecute = false; + updateProcess.StartInfo.RedirectStandardInput = false; + updateProcess.StartInfo.RedirectStandardOutput = false; + updateProcess.StartInfo.RedirectStandardError = false; + updateProcess.StartInfo.ErrorDialog = false; + updateProcess.StartInfo.CreateNoWindow = true; + updateProcess.StartInfo.WorkingDirectory = _executableDir; + updateProcess.StartInfo.FileName = _updateScriptPath; + if (!updateProcess.Start()) + { + RemoveUpdateScript(); + _logger.Info($"Failed launch updater script: {_updateScriptPath}"); + return InstallUpdateStatus.LaunchScriptError; + } + return InstallUpdateStatus.Success; + } + CancelScheduleInstallUpdate(); + return InstallUpdateStatus.ExtractFilesError; + } + + public UpdateInfo? GetScheduledUpdateInfo() => File.Exists(_schedInstallArchivePath) ? JsonHelper.ReadFile(_schedInstallJsonPath) : null; + + public bool IsAlreadyInstalledVersion(UpdateInfo updateInfo) => + string.Compare(updateInfo.GetVersion(), _currentVersion, StringComparison.OrdinalIgnoreCase) == 0; + + public void ApplyScheduledUpdateProps(UpdateInfo updateInfo) => _updateRepository.SetCurrentVersion(updateInfo.GetVersion()); + + public bool ScheduleInstallUpdate(UpdateInfo updateInfo, string filePath) + { + _logger.Info($"Shedule install update with version: {updateInfo.GetVersion()}"); + if (File.Exists(filePath)) + { + _updateRepository.SetCurrentVersion(_currentVersion); + try + { + if (!Directory.Exists(_updatesStoragePath)) + { + Directory.CreateDirectory(_updatesStoragePath); + } + if (File.Exists(_schedInstallArchivePath)) + { + File.Delete(_schedInstallArchivePath); + } + File.Move(filePath, _schedInstallArchivePath); + if (JsonHelper.WriteFile(_schedInstallJsonPath, updateInfo)) + { + _updateRepository.SetCurrentVersion(updateInfo.GetVersion()); + return true; + } + _logger.Error($"Failed write schedule json: {_schedInstallJsonPath}"); + return false; + } + catch (Exception e) + { + _logger.Error(e, $"Exception during schedule install update at: {filePath}"); + CancelScheduleInstallUpdate(); + return false; + } + } + _logger.Error($"No schedule update package: {filePath}"); + return false; + } + + public bool CancelScheduleInstallUpdate() + { + _updateRepository.SetCurrentVersion(_currentVersion); + if (File.Exists(_schedInstallJsonPath)) + FileUtils.DeleteFileNoThrow(_schedInstallJsonPath); + return File.Exists(_schedInstallArchivePath) && + FileUtils.DeleteFileNoThrow(_schedInstallArchivePath); + } + + public void RemoveUpdateScript() + { + if (File.Exists(_updateScriptPath)) + { + FileUtils.DeleteFileNoThrow(_updateScriptPath); + } + } + + private bool ExtractUpdateScript() + { + try + { + File.WriteAllText(_updateScriptPath, _updateScriptContent); + } + catch (Exception e) + { + _logger.Error(e, $"Failed extract update script to: {_updateScriptPath}"); + return false; + } + return true; + } + + private bool ExtractReadyInstallUpdate() + { + var installUnpackedDir = new DirectoryInfo(_installUnpackedDir); + var extractTempDir = new DirectoryInfo(Path.Combine(_updatesStoragePath, "temp_" + Path.GetRandomFileName())); + try + { + if (installUnpackedDir.Exists && !FileUtils.DeleteDirectoryNoThrow(installUnpackedDir, true)) + { + _logger.Error($"Already exist extract directory can't be removed: {_installUnpackedDir}"); + return false; + } + + Directory.CreateDirectory(_installUnpackedDir); + File.Move(_schedInstallExecutablePath, _installUnpackedExecutablePath); + } + catch (Exception e) + { + _logger.Error(e, $"Failed extract update package to: {_installUnpackedDir}"); + if (extractTempDir.Exists) + FileUtils.DeleteDirectoryNoThrow(extractTempDir, true); + if (installUnpackedDir.Exists) + FileUtils.DeleteDirectoryNoThrow(installUnpackedDir, true); + return false; + } + return true; + } + } +} diff --git a/SCTools/SCTool_Redesigned/Update/CustomPackageVerifier.cs b/SCTools/SCTool_Redesigned/Update/CustomPackageVerifier.cs index 98898aa..bf12c92 100644 --- a/SCTools/SCTool_Redesigned/Update/CustomPackageVerifier.cs +++ b/SCTools/SCTool_Redesigned/Update/CustomPackageVerifier.cs @@ -4,7 +4,7 @@ namespace SCTool_Redesigned.Update { - internal class CustomPackageVerifier : ApplicationUpdater.IPackageVerifier + internal class CustomPackageVerifier : CustomApplicationUpdater.IPackageVerifier { private string ExecutorName = Assembly.GetExecutingAssembly().GetName().Name;