/// <exception cref = "UnauthorizedAccessException">A path needs admin priviledges to write</exception> /// <exception cref = "ArgumentException">An argument is empty</exception> public ProjectManager(string projectRoot) { projectRoot = projectRoot.Trim(); if (string.IsNullOrEmpty(projectRoot)) { throw new ArgumentException(Localization.Get(StringId.E_XCanNotBeEmpty, "'projectRoot'")); } if (!PatchUtils.CheckWriteAccessToFolder(projectRoot)) { throw new UnauthorizedAccessException(Localization.Get(StringId.E_AccessToXIsForbiddenRunInAdminMode, projectRoot)); } this.projectRoot = PatchUtils.GetPathWithTrailingSeparatorChar(projectRoot); versionsPath = this.projectRoot + PatchParameters.PROJECT_VERSIONS_DIRECTORY + Path.DirectorySeparatorChar; outputPath = this.projectRoot + PatchParameters.PROJECT_OUTPUT_DIRECTORY + Path.DirectorySeparatorChar; selfPatcherPath = this.projectRoot + PatchParameters.PROJECT_SELF_PATCHER_DIRECTORY + Path.DirectorySeparatorChar; utilitiesPath = this.projectRoot + PatchParameters.PROJECT_OTHER_DIRECTORY + Path.DirectorySeparatorChar; projectInfoPath = this.projectRoot + PatchParameters.PROJECT_SETTINGS_FILENAME; downloadLinksPath = utilitiesPath + PatchParameters.PROJECT_UPDATE_LINKS_FILENAME; Localization.Get(StringId.Done); // Force the localization system to be initialized with the current culture/language logs = new Queue <string>(); cancel = false; silentMode = false; IsRunning = false; Result = PatchResult.Failed; }
/// <exception cref = "ArgumentException">An argument is empty</exception> public SimplePatchTool(string rootPath, string versionInfoURL) { rootPath = rootPath.Trim(); versionInfoURL = versionInfoURL.Trim(); if (string.IsNullOrEmpty(rootPath)) { throw new ArgumentException(Localization.Get(StringId.E_XCanNotBeEmpty, "'rootPath'")); } if (string.IsNullOrEmpty(versionInfoURL)) { throw new ArgumentException(Localization.Get(StringId.E_XCanNotBeEmpty, "'versionInfoURL'")); } comms = new PatchIntercomms(this, PatchUtils.GetPathWithTrailingSeparatorChar(rootPath)); this.versionInfoURL = versionInfoURL; canIncrementalPatch = true; canRepair = true; incrementalPatches = new List <IncrementalPatch>(); incrementalPatchesInfo = new List <PatchInfo>(); filesInVersion = new HashSet <string>(); UseCustomDownloadHandler(null); UseCustomFreeSpaceCalculator(null); IsRunning = false; Result = PatchResult.Failed; }
/// <exception cref = "FileNotFoundException">Version info does not exist at path</exception> /// <exception cref = "ArgumentException">versionInfoPath is empty</exception> /// <exception cref = "UnauthorizedAccessException">Admin priviledges needed to update the version info</exception> /// <exception cref = "InvalidOperationException">Version info could not be deserialized</exception> public PatchUpdater(string versionInfoPath, LogEvent logger = null) { versionInfoPath = versionInfoPath.Trim(); if (string.IsNullOrEmpty(versionInfoPath)) { throw new ArgumentException(Localization.Get(StringId.E_XCanNotBeEmpty, "'versionInfoPath'")); } if (!File.Exists(versionInfoPath)) { throw new FileNotFoundException(Localization.Get(StringId.E_PatchInfoDoesNotExistAtX, versionInfoPath)); } if (!PatchUtils.CheckWriteAccessToFolder(Path.GetDirectoryName(versionInfoPath))) { throw new UnauthorizedAccessException(Localization.Get(StringId.E_AccessToXIsForbiddenRunInAdminMode, versionInfoPath)); } this.versionInfoPath = versionInfoPath; VersionInfo = PatchUtils.GetVersionInfoFromPath(versionInfoPath); if (VersionInfo == null) { throw new InvalidOperationException(Localization.Get(StringId.E_VersionInfoCouldNotBeDeserializedFromX, versionInfoPath)); } Logger = logger; }
private bool RenameItems(List <PatchRenamedItem> items) { string rootPath = comms.SelfPatching ? comms.DecompressedFilesPath : comms.RootPath; for (int i = 0; i < items.Count; i++) { if (comms.Cancel) { return(false); } string fromAbsolutePath = rootPath + items[i].BeforePath; string toAbsolutePath = rootPath + items[i].AfterPath; if (File.Exists(fromAbsolutePath)) { PatchUtils.MoveFile(fromAbsolutePath, toAbsolutePath); } else if (Directory.Exists(fromAbsolutePath)) { PatchUtils.MoveDirectory(fromAbsolutePath, toAbsolutePath); } } return(true); }
public FileInfo DownloadFileFromURLToPath(string url, string path, long fileSize) { if (comms.Cancel) { return(null); } if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(path)) { return(null); } downloadHandler.DownloadedFilePath = path; downloadHandler.DownloadedFilename = Path.GetFileName(path); downloadHandler.DownloadedFileSize = fileSize; if (comms.LogProgress) { downloadHandler.Progress = new DownloadProgress(downloadHandler); comms.SetProgress(downloadHandler.Progress); } if (IsGoogleDriveURL(url)) { url = PatchUtils.GetGoogleDriveDownloadLinkFromUrl(url); verifyDownloadSize = false; return(DownloadGoogleDriveFileFromURLToPath(url, path)); } else { verifyDownloadSize = true; return(DownloadFileFromURLToPathInternal(url, path)); } }
private bool FetchVersionInfo() { string versionInfoXML = comms.DownloadManager.DownloadTextFromURL(versionInfoURL); if (versionInfoXML == null) { FailReason = PatchFailReason.DownloadError; FailDetails = Localization.Get(StringId.E_VersionInfoCouldNotBeDownloaded); return(false); } if (VersionInfoVerifier != null && !VersionInfoVerifier(ref versionInfoXML)) { FailReason = PatchFailReason.CantVerifyVersionInfo; FailDetails = Localization.Get(StringId.E_VersionInfoCouldNotBeVerified); return(false); } try { comms.VersionInfo = PatchUtils.DeserializeXMLToVersionInfo(versionInfoXML); } catch { FailReason = PatchFailReason.XmlDeserializeError; FailDetails = Localization.Get(StringId.E_VersionInfoInvalid); return(false); } if (comms.Cancel) { return(false); } if (!comms.VersionInfo.Version.IsValid) { FailReason = PatchFailReason.InvalidVersionCode; FailDetails = Localization.Get(StringId.E_VersionInfoInvalid); return(false); } incrementalPatches.Clear(); incrementalPatchesInfo.Clear(); filesInVersion.Clear(); List <VersionItem> versionInfoFiles = comms.VersionInfo.Files; for (int i = 0; i < versionInfoFiles.Count; i++) { filesInVersion.Add(versionInfoFiles[i].Path); } currentVersion = PatchUtils.GetVersion(comms.RootPath, comms.VersionInfo.Name); return(true); }
private bool ApplyDiffToFile(string filePath, string diffPath, string targetPath, int patchItemIndex) { PatchItem item = patchInfo.Files[patchItemIndex]; FileInfo targetFile = new FileInfo(targetPath); if (targetFile.Exists && targetFile.MatchesSignature(item.AfterFileSize, item.AfterMd5Hash)) { comms.Log(Localization.Get(StringId.AlreadyUpToDateXthFile, patchItemIndex + 1, patchInfo.Files.Count, item.Path)); return(true); } if (item.BeforeFileSize == 0L) { comms.Log(Localization.Get(StringId.CreatingXthFile, patchItemIndex + 1, patchInfo.Files.Count, item.Path)); Directory.CreateDirectory(Path.GetDirectoryName(targetPath)); PatchUtils.CopyFile(diffPath, targetPath); File.Delete(diffPath); return(true); } FileInfo localFile = new FileInfo(filePath); if (!localFile.Exists || !localFile.MatchesSignature(item.BeforeFileSize, item.BeforeMd5Hash)) { return(false); } comms.Log(Localization.Get(StringId.UpdatingXthFile, patchItemIndex + 1, patchInfo.Files.Count, item.Path)); FilePatchProgress progress = null; string tempOutputPath = diffPath + "_.tmp"; if (comms.LogProgress) { progress = new FilePatchProgress(comms, Path.GetFileName(filePath)); } OctoUtils.ApplyDelta(filePath, tempOutputPath, diffPath, progress); FileInfo updatedFile = new FileInfo(tempOutputPath); if (!updatedFile.Exists || !updatedFile.MatchesSignature(item.AfterFileSize, item.AfterMd5Hash)) { return(false); } Directory.CreateDirectory(Path.GetDirectoryName(targetPath)); PatchUtils.CopyFile(tempOutputPath, targetPath); File.Delete(tempOutputPath); File.Delete(diffPath); return(true); }
public string DownloadTextFromURL(string url) { if (string.IsNullOrEmpty(url)) { return(null); } if (IsGoogleDriveURL(url)) { url = PatchUtils.GetGoogleDriveDownloadLinkFromUrl(url); } for (int i = 0; i < PatchParameters.FailedDownloadsRetryLimit; i++) { if (comms.Cancel) { return(null); } try { var syncObject = new object(); lock ( syncObject ) { downloadHandler.DownloadString(url, syncObject); Monitor.Wait(syncObject); } if (comms.Cancel) { return(null); } if (downloadStringResult == null) { Thread.Sleep(PatchParameters.FailedDownloadsCooldownMillis); continue; } return(downloadStringResult); } catch (WebException e) { comms.LogToFile(e); Thread.Sleep(PatchParameters.FailedDownloadsCooldownMillis); } catch (UriFormatException e) { comms.LogToFile(e); return(null); } } return(null); }
/// <exception cref = "FormatException">projectName contains invalid character(s)</exception> private PatchResult CreatePatch() { versionInfo = new VersionInfo() { Name = projectName, // throws FormatException if 'projectName' contains invalid character(s) Version = version, BaseDownloadURL = baseDownloadURL, MaintenanceCheckURL = maintenanceCheckURL, CompressionFormat = compressionFormatRepairPatch }; PatchUtils.DeleteDirectory(outputPath); Directory.CreateDirectory(outputPath); if (cancel) { return(PatchResult.Failed); } Log(Localization.Get(StringId.GeneratingListOfFilesInBuild)); AddFilesToVersionRecursively(new DirectoryInfo(rootPath), ""); if (cancel) { return(PatchResult.Failed); } if (generateRepairPatch && CreateRepairPatch() == PatchResult.Failed) { return(PatchResult.Failed); } if (generateInstallerPatch && CreateInstallerPatch() == PatchResult.Failed) { return(PatchResult.Failed); } if (GenerateIncrementalPatch && CreateIncrementalPatch() == PatchResult.Failed) { return(PatchResult.Failed); } PatchUtils.SetVersion(rootPath, projectName, version); versionInfo.IgnoredPaths.AddRange(ignoredPaths); Log(Localization.Get(StringId.WritingVersionInfoToXML)); PatchUtils.SerializeVersionInfoToXML(versionInfo, outputPath + PatchParameters.VERSION_INFO_FILENAME); Log(Localization.Get(StringId.Done)); return(PatchResult.Success); }
private void TraverseIncrementalPatchRecursively(DirectoryInfo directory, string relativePath) { FileInfo[] files = directory.GetFiles(); for (int i = 0; i < files.Length; i++) { if (cancel) { return; } FileInfo fileInfo = files[i]; string fileRelativePath = relativePath + fileInfo.Name; if (!ignoredPathsRegex.PathMatchesPattern(fileRelativePath)) { string targetAbsolutePath = previousVersionRoot + fileRelativePath; string diffFileAbsolutePath = incrementalPatchTempPath + fileRelativePath; if (!File.Exists(targetAbsolutePath)) { Log(Localization.Get(StringId.CopyingXToPatch, fileRelativePath)); PatchUtils.CopyFile(fileInfo.FullName, diffFileAbsolutePath); incrementalPatch.Files.Add(new PatchItem(fileRelativePath, null, fileInfo)); } else { FileInfo prevVersion = new FileInfo(targetAbsolutePath); if (!fileInfo.MatchesSignature(prevVersion)) { Log(Localization.Get(StringId.CalculatingDiffOfX, fileRelativePath)); OctoUtils.CalculateDelta(targetAbsolutePath, fileInfo.FullName, diffFileAbsolutePath, diffQuality); incrementalPatch.Files.Add(new PatchItem(fileRelativePath, prevVersion, fileInfo)); } } } } DirectoryInfo[] subDirectories = directory.GetDirectories(); for (int i = 0; i < subDirectories.Length; i++) { if (cancel) { return; } string directoryRelativePath = relativePath + subDirectories[i].Name + Path.DirectorySeparatorChar; if (!ignoredPathsRegex.PathMatchesPattern(directoryRelativePath)) { Directory.CreateDirectory(incrementalPatchTempPath + directoryRelativePath); TraverseIncrementalPatchRecursively(subDirectories[i], directoryRelativePath); } } }
public bool Run() { if (!IsRunning) { IsRunning = true; cancel = false; PatchUtils.CreateBackgroundThread(new ThreadStart(ThreadCreatePatchFunction)).Start(); return(true); } return(false); }
// Credit: http://stackoverflow.com/questions/1460273/how-to-check-if-a-file-exists-on-an-webserver-by-its-url // Credit: http://stackoverflow.com/questions/3614034/system-net-webexception-http-status-code public bool FileExistsAtUrl(string url, out long fileSize) { if (string.IsNullOrEmpty(url)) { fileSize = 0L; return(false); } if (IsGoogleDriveURL(url)) { url = PatchUtils.GetGoogleDriveDownloadLinkFromUrl(url); } WebRequest request = WebRequest.Create(url); request.Method = "HEAD"; request.Timeout = PatchParameters.FileAvailabilityCheckTimeout; try { using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) { fileSize = response.ContentLength; return(response.StatusCode == HttpStatusCode.OK); } } catch (WebException e) { fileSize = 0L; if (e.Response != null && e.Response is HttpWebResponse) { using (HttpWebResponse exResponse = (HttpWebResponse)e.Response) { if (exResponse.StatusCode == HttpStatusCode.ServiceUnavailable) { // Drive returns 503 error while requesting HEAD for valid download links if (IsGoogleDriveURL(url)) { return(true); } } } } comms.LogToFile(e); return(false); } }
public bool CheckForUpdates(bool checkVersionOnly = true) { if (!IsRunning) { IsRunning = true; Operation = PatchOperation.CheckingForUpdates; PatchMethod = PatchMethod.None; comms.Cancel = false; PatchUtils.CreateBackgroundThread(new ParameterizedThreadStart(ThreadCheckForUpdatesFunction)).Start(checkVersionOnly); return(true); } return(false); }
public bool Run(bool selfPatching) { if (!IsRunning) { IsRunning = true; Operation = selfPatching ? PatchOperation.SelfPatching : PatchOperation.Patching; PatchMethod = PatchMethod.None; comms.Cancel = false; PatchUtils.CreateBackgroundThread(new ThreadStart(ThreadPatchFunction)).Start(); return(true); } return(false); }
public PatchCreator AddIgnoredPath(string ignoredPath) { if (ignoredPath != null) { ignoredPath = ignoredPath.Trim().Replace(PatchUtils.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); } if (!string.IsNullOrEmpty(ignoredPath)) { if (ignoredPaths.Add(ignoredPath)) { ignoredPathsRegex.Add(PatchUtils.WildcardToRegex(ignoredPath)); } } return(this); }
/// <exception cref = "ArgumentException">An argument is empty</exception> public SimplePatchTool(string rootPath, string versionInfoURL) { rootPath = rootPath.Trim(); versionInfoURL = versionInfoURL.Trim(); if (string.IsNullOrEmpty(rootPath)) { throw new ArgumentException(Localization.Get(StringId.E_XCanNotBeEmpty, "'rootPath'")); } if (string.IsNullOrEmpty(versionInfoURL)) { throw new ArgumentException(Localization.Get(StringId.E_XCanNotBeEmpty, "'versionInfoURL'")); } Localization.Get(StringId.Done); // Force the localization system to be initialized with the current culture/language for (int i = 0; i < ROOT_PATH_PLACEHOLDERS.Length; i++) { if (rootPath.IndexOf(ROOT_PATH_PLACEHOLDERS[i]) >= 0) { rootPath.Replace(ROOT_PATH_PLACEHOLDERS[i], Path.GetDirectoryName(PatchUtils.GetCurrentExecutablePath())); } } comms = new PatchIntercomms(this, PatchUtils.GetPathWithTrailingSeparatorChar(rootPath)); this.versionInfoURL = versionInfoURL; canRepairPatch = true; canIncrementalPatch = true; canInstallerPatch = true; checkForMultipleRunningInstances = true; incrementalPatches = new List <IncrementalPatch>(); incrementalPatchesInfo = new List <IncrementalPatchInfo>(); filesInVersion = new HashSet <string>(); UseCustomDownloadHandler(null); UseCustomFreeSpaceCalculator(null); IsRunning = false; Operation = PatchOperation.CheckingForUpdates; PatchMethod = PatchMethod.None; Result = PatchResult.Failed; }
/// <exception cref = "FormatException">projectName contains invalid character(s)</exception> private PatchResult CreatePatch() { patch = new VersionInfo() { Name = projectName, // throws FormatException if 'projectName' contains invalid character(s) Version = version }; PatchUtils.DeleteDirectory(outputPath); Directory.CreateDirectory(outputPath); if (cancel) { return(PatchResult.Failed); } Log(Localization.Get(StringId.GeneratingListOfFilesInBuild)); CreateFileList(); if (cancel) { return(PatchResult.Failed); } if (generateRepairPatch && CreateRepairPatch() == PatchResult.Failed) { return(PatchResult.Failed); } if (GenerateIncrementalPatch && CreateIncrementalPatch() == PatchResult.Failed) { return(PatchResult.Failed); } PatchUtils.SetVersion(rootPath, projectName, version); patch.IgnoredPaths.AddRange(ignoredPaths); Log(Localization.Get(StringId.WritingVersionInfoToXML)); PatchUtils.SerializeVersionInfoToXML(patch, outputPath + PatchParameters.VERSION_INFO_FILENAME); Log(Localization.Get(StringId.Done)); return(PatchResult.Success); }
private PatchResult CreateIncrementalPatch() { if (cancel) { return(PatchResult.Failed); } Directory.CreateDirectory(incrementalPatchOutputPath); Directory.CreateDirectory(incrementalPatchTempPath); incrementalPatch = new IncrementalPatchInfo { FromVersion = previousVersion, ToVersion = version }; Log(Localization.Get(StringId.CreatingIncrementalPatch)); Stopwatch timer = Stopwatch.StartNew(); DirectoryInfo rootDirectory = new DirectoryInfo(rootPath); TraverseIncrementalPatchRecursively(rootDirectory, ""); if (cancel) { return(PatchResult.Failed); } Log(Localization.Get(StringId.CompressingPatchIntoOneFile)); string compressedPatchPath = incrementalPatchOutputPath + incrementalPatch.PatchVersion() + PatchParameters.INCREMENTAL_PATCH_FILE_EXTENSION; ZipUtils.CompressFolder(incrementalPatchTempPath, compressedPatchPath, compressionFormatIncrementalPatch); Log(Localization.Get(StringId.WritingIncrementalPatchInfoToXML)); PatchUtils.SerializeIncrementalPatchInfoToXML(incrementalPatch, incrementalPatchOutputPath + incrementalPatch.PatchVersion() + PatchParameters.INCREMENTAL_PATCH_INFO_EXTENSION); versionInfo.IncrementalPatches.Add(new IncrementalPatch(previousVersion, version, new FileInfo(compressedPatchPath), incrementalPatch.Files.Count, compressionFormatIncrementalPatch)); PatchUtils.DeleteDirectory(incrementalPatchTempPath); Log(Localization.Get(StringId.IncrementalPatchCreatedInXSeconds, timer.ElapsedSeconds())); return(PatchResult.Success); }
/// <exception cref = "ArgumentException">previousVersionRoot is empty</exception> /// <exception cref = "DirectoryNotFoundException">Previous version's path does not exist</exception> /// <exception cref = "FileNotFoundException">Previous version's version code does not exist</exception> /// <exception cref = "FormatException">Previous version's version code is not valid</exception> /// <exception cref = "InvalidOperationException">Previous version's version code is greater than or equal to current version's version code</exception> public PatchCreator CreateIncrementalPatch(bool value, string previousVersionRoot = null, int diffQuality = 3) { if (!value || previousVersionRoot == null) { this.previousVersionRoot = null; } else { previousVersionRoot = previousVersionRoot.Trim(); if (string.IsNullOrEmpty(previousVersionRoot)) { throw new ArgumentException(Localization.Get(StringId.E_XCanNotBeEmpty, "'previousVersionRoot'")); } previousVersionRoot = PatchUtils.GetPathWithTrailingSeparatorChar(previousVersionRoot); if (!Directory.Exists(previousVersionRoot)) { throw new DirectoryNotFoundException(Localization.Get(StringId.E_XDoesNotExist, previousVersionRoot)); } previousVersion = PatchUtils.GetVersion(previousVersionRoot, projectName); if (previousVersion == null) { throw new FileNotFoundException(Localization.Get(StringId.E_XDoesNotExist, projectName + PatchParameters.VERSION_HOLDER_FILENAME_POSTFIX)); } else if (!previousVersion.IsValid) { throw new FormatException(Localization.Get(StringId.E_VersionCodeXIsInvalid, previousVersion)); } if (previousVersion >= version) { throw new InvalidOperationException(Localization.Get(StringId.E_PreviousVersionXIsNotLessThanY, previousVersion, version)); } this.previousVersionRoot = previousVersionRoot; this.diffQuality = diffQuality; } return(this); }
// Returns null if project is valid private string ValidateProject() { System.Text.StringBuilder sb = new System.Text.StringBuilder(300); if (!Directory.Exists(versionsPath)) { sb.AppendLine(Localization.Get(StringId.E_DirectoryXMissing, versionsPath)); } if (!File.Exists(projectInfoPath)) { sb.AppendLine(Localization.Get(StringId.E_FileXMissing, projectInfoPath)); } else { ProjectInfo projectInfo = null; try { projectInfo = PatchUtils.GetProjectInfoFromPath(projectInfoPath); } catch (Exception e) { sb.AppendLine(e.ToString()); } if (projectInfo == null) { sb.AppendLine(Localization.Get(StringId.E_ProjectInfoCouldNotBeDeserializedFromX, projectInfoPath)); } else if (projectInfo.Version != ProjectInfo.LATEST_VERSION) { sb.AppendLine(Localization.Get(StringId.E_ProjectInfoOutdated)); } } if (sb.Length == 0) { return(null); } return(string.Concat(Localization.Get(StringId.E_ProjectInvalid), Environment.NewLine, Environment.NewLine, sb.ToString())); }
/// <exception cref = "DirectoryNotFoundException">Previous patch files' directory does not exist</exception> /// <exception cref = "FileNotFoundException">VersionInfo of the previous patch files does not exist</exception> /// <exception cref = "FormatException">VersionInfo of the previous patch could not be deserialized</exception> public PatchCreator SetPreviousPatchFilesRoot(string previousPatchFilesRoot, bool dontCreatePatchFilesForUnchangedFiles = false) { if (previousPatchFilesRoot != null) { previousPatchFilesRoot = previousPatchFilesRoot.Trim(); } if (string.IsNullOrEmpty(previousPatchFilesRoot)) { this.previousPatchFilesRoot = null; this.previousVersionInfo = null; this.dontCreatePatchFilesForUnchangedFiles = false; } else { previousPatchFilesRoot = PatchUtils.GetPathWithTrailingSeparatorChar(previousPatchFilesRoot); if (!Directory.Exists(previousPatchFilesRoot)) { throw new DirectoryNotFoundException(Localization.Get(StringId.E_XDoesNotExist, previousPatchFilesRoot)); } string previousVersionInfoPath = previousPatchFilesRoot + PatchParameters.VERSION_INFO_FILENAME; if (!File.Exists(previousVersionInfoPath)) { throw new FileNotFoundException(Localization.Get(StringId.E_XDoesNotExist, previousVersionInfoPath)); } VersionInfo previousVersionInfo = PatchUtils.GetVersionInfoFromPath(previousVersionInfoPath); if (previousVersionInfo == null) { throw new FormatException(Localization.Get(StringId.E_VersionInfoCouldNotBeDeserializedFromX, previousVersionInfoPath)); } this.previousPatchFilesRoot = previousPatchFilesRoot; this.previousVersionInfo = previousVersionInfo; this.dontCreatePatchFilesForUnchangedFiles = dontCreatePatchFilesForUnchangedFiles; } return(this); }
/// <exception cref = "IOException">Project root is not empty</exception> public void CreateProject() { if (Directory.Exists(projectRoot)) { if (Directory.GetFileSystemEntries(projectRoot).Length > 0) { throw new IOException(Localization.Get(StringId.E_DirectoryXIsNotEmpty, projectRoot)); } } Directory.CreateDirectory(versionsPath); Directory.CreateDirectory(outputPath); Directory.CreateDirectory(utilitiesPath); Directory.CreateDirectory(selfPatcherPath); ProjectInfo projectInfo = new ProjectInfo(); projectInfo.IgnoredPaths.Add("*" + PatchParameters.LOG_FILE_NAME); PatchUtils.SerializeProjectInfoToXML(projectInfo, projectInfoPath); File.WriteAllText(downloadLinksPath, "PASTE DOWNLOAD LINKS OF THE PATCH FILES HERE"); Result = PatchResult.Success; }
protected override PatchResult Execute() { if (comms.Cancel) { return(PatchResult.Failed); } if (comms.IsUnderMaintenance()) { return(PatchResult.Failed); } Stopwatch timer = Stopwatch.StartNew(); comms.Stage = PatchStage.CalculatingFilesToUpdate; comms.Log(Localization.Get(StringId.CalculatingNewOrChangedFiles)); List <VersionItem> filesToUpdate = comms.FindFilesToUpdate(); if (filesToUpdate.Count == 0) { return(PatchResult.AlreadyUpToDate); } if (comms.Cancel) { return(PatchResult.Failed); } comms.Log(Localization.Get(StringId.CalculatingFilesToDownload)); List <VersionItem> filesToDownload = FindFilesToDownload(filesToUpdate); if (comms.Cancel) { return(PatchResult.Failed); } long estimatedDownloadSize = 0L; for (int i = 0; i < filesToDownload.Count; i++) { estimatedDownloadSize += filesToDownload[i].CompressedFileSize; } InitializeProgress(filesToUpdate.Count, estimatedDownloadSize); if (filesToDownload.Count > 0 && comms.VerifyFiles) { comms.Stage = PatchStage.VerifyingFilesOnServer; for (int i = 0; i < filesToDownload.Count; i++) { if (comms.Cancel) { return(PatchResult.Failed); } VersionItem item = filesToDownload[i]; long fileSize; if (!comms.DownloadManager.FileExistsAtUrl(comms.VersionInfo.GetDownloadURLFor(item), out fileSize)) { comms.FailReason = PatchFailReason.FileDoesNotExistOnServer; comms.FailDetails = Localization.Get(StringId.E_FileXDoesNotExistOnServer, item.Path); return(PatchResult.Failed); } else if (fileSize > 0L && fileSize != item.CompressedFileSize) { comms.FailReason = PatchFailReason.FileIsNotValidOnServer; comms.FailDetails = Localization.Get(StringId.E_FileXIsNotValidOnServer, item.Path); return(PatchResult.Failed); } } } if (filesToDownload.Count > 0) { comms.Log(Localization.Get(StringId.DownloadingXFiles, filesToDownload.Count)); } if (filesToUpdate.Count > 0) { comms.Log(Localization.Get(StringId.UpdatingXFiles, filesToUpdate.Count)); } Stopwatch downloadTimer = Stopwatch.StartNew(); if (!DownloadAndUpdateFiles(filesToDownload, filesToUpdate)) { return(PatchResult.Failed); } comms.Log(Localization.Get(StringId.AllFilesAreDownloadedInXSeconds, downloadTimer.ElapsedSeconds())); PatchUtils.DeleteDirectory(comms.DownloadsPath); comms.Log(Localization.Get(StringId.PatchAppliedInXSeconds, timer.ElapsedSeconds())); return(PatchResult.Success); }
public void UpdateVersion(VersionCode version) { PatchUtils.SetVersion(SelfPatching ? DecompressedFilesPath : RootPath, VersionInfo.Name, version); }
public void SaveChanges() { PatchUtils.SerializeVersionInfoToXML(VersionInfo, versionInfoPath); }
private PatchResult Patch() { PatchStage = PatchStage.CheckingUpdates; Stopwatch timer = Stopwatch.StartNew(); comms.Log(Localization.Get(StringId.RetrievingVersionInfo)); if (!FetchVersionInfo()) { return(PatchResult.Failed); } if (comms.IsUnderMaintenance()) { return(PatchResult.Failed); } if (!currentVersion.IsValid) { currentVersion = new VersionCode(0); } VersionCode rootVersion = currentVersion; if (comms.SelfPatching) { VersionCode patchedVersion = PatchUtils.GetVersion(comms.DecompressedFilesPath, comms.VersionInfo.Name); if (patchedVersion > currentVersion) { currentVersion = patchedVersion; } } PatchStage = PatchStage.CheckingFileIntegrity; if (CheckLocalFilesUpToDate(true, false)) { return(PatchResult.AlreadyUpToDate); } if (!PatchUtils.CheckWriteAccessToFolder(comms.RootPath)) { FailReason = PatchFailReason.RequiresAdminPriviledges; FailDetails = Localization.Get(StringId.E_AccessToXIsForbiddenRunInAdminMode, comms.RootPath); return(PatchResult.Failed); } if (!PatchUtils.CheckWriteAccessToFolder(comms.CachePath)) { FailReason = PatchFailReason.RequiresAdminPriviledges; FailDetails = Localization.Get(StringId.E_AccessToXIsForbiddenRunInAdminMode, comms.CachePath); return(PatchResult.Failed); } if (checkForMultipleRunningInstances) { string currentExecutablePath = PatchUtils.GetCurrentExecutablePath(); if (PatchUtils.GetNumberOfRunningProcesses(currentExecutablePath) > 1) { FailReason = PatchFailReason.MultipleRunningInstances; FailDetails = Localization.Get(StringId.E_AnotherInstanceOfXIsRunning, Path.GetFileName(currentExecutablePath)); return(PatchResult.Failed); } } if (comms.Cancel) { return(PatchResult.Failed); } // Add a date holder file to the cache to save the last access time reliably DateTime dateTimeNow = DateTime.UtcNow; File.WriteAllText(comms.CachePath + PatchParameters.CACHE_DATE_HOLDER_FILENAME, dateTimeNow.ToString("O")); // Check if there are any leftover files from other SimplePatchTool integrated apps in cache DirectoryInfo[] patcherCaches = new DirectoryInfo(comms.CachePath).Parent.GetDirectories(); for (int i = 0; i < patcherCaches.Length; i++) { DirectoryInfo cacheDir = patcherCaches[i]; if (cacheDir.Name.Equals(comms.VersionInfo.Name, StringComparison.OrdinalIgnoreCase)) { continue; } FileInfo dateHolder = new FileInfo(PatchUtils.GetPathWithTrailingSeparatorChar(cacheDir.FullName) + PatchParameters.CACHE_DATE_HOLDER_FILENAME); if (dateHolder.Exists && dateHolder.Length > 0L) { DateTime lastAccessTime; if (DateTime.TryParseExact(File.ReadAllText(dateHolder.FullName), "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out lastAccessTime)) { if ((dateTimeNow - lastAccessTime).TotalDays <= PatchParameters.CACHE_DATE_EXPIRE_DAYS) { continue; } } } // This cache directory doesn't have a date holder file or is older than CACHE_DATE_EXPIRE_DAYS, delete it cacheDir.Delete(true); } bool canRepairPatch = this.canRepairPatch; bool canIncrementalPatch = this.canIncrementalPatch; bool canInstallerPatch = this.canInstallerPatch; List <PatchMethodHolder> preferredPatchMethods = new List <PatchMethodHolder>(3); List <VersionItem> versionInfoFiles = comms.VersionInfo.Files; if (canRepairPatch) { for (int i = 0; i < versionInfoFiles.Count; i++) { VersionItem item = versionInfoFiles[i]; if (item.CompressedFileSize == 0L && string.IsNullOrEmpty(item.CompressedMd5Hash)) { canRepairPatch = false; break; } } if (canRepairPatch) { long repairPatchSize = 0L; for (int i = 0; i < versionInfoFiles.Count; i++) { VersionItem item = versionInfoFiles[i]; FileInfo localFile = new FileInfo(comms.RootPath + item.Path); if (localFile.Exists && localFile.MatchesSignature(item.FileSize, item.Md5Hash)) { continue; } FileInfo downloadedFile = new FileInfo(comms.DownloadsPath + item.Path); if (downloadedFile.Exists && downloadedFile.MatchesSignature(item.CompressedFileSize, item.CompressedMd5Hash)) { continue; } if (comms.SelfPatching) { FileInfo decompressedFile = new FileInfo(comms.DecompressedFilesPath + item.Path); if (decompressedFile.Exists && decompressedFile.MatchesSignature(item.FileSize, item.Md5Hash)) { continue; } } repairPatchSize += item.CompressedFileSize; } preferredPatchMethods.Add(new PatchMethodHolder(PatchMethod.RepairPatch, repairPatchSize)); } } if (canIncrementalPatch) { // Find incremental patches to apply VersionCode thisVersion = rootVersion; List <IncrementalPatch> versionInfoPatches = comms.VersionInfo.IncrementalPatches; for (int i = 0; i < versionInfoPatches.Count; i++) { if (thisVersion == comms.VersionInfo.Version) { break; } IncrementalPatch patch = versionInfoPatches[i]; if (thisVersion == patch.FromVersion) { thisVersion = patch.ToVersion; incrementalPatches.Add(patch); } } if (thisVersion != comms.VersionInfo.Version) { incrementalPatches.Clear(); } if (incrementalPatches.Count == 0) { canIncrementalPatch = false; } else { long incrementalPatchSize = 0L; for (int i = 0; i < incrementalPatches.Count; i++) { IncrementalPatch incrementalPatch = incrementalPatches[i]; if (currentVersion > incrementalPatch.FromVersion) { continue; } FileInfo patchFile = new FileInfo(comms.GetDownloadPathForPatch(incrementalPatch.PatchVersion())); if (patchFile.Exists && patchFile.MatchesSignature(incrementalPatch.PatchSize, incrementalPatch.PatchMd5Hash)) { continue; } incrementalPatchSize += incrementalPatch.PatchSize; } preferredPatchMethods.Add(new PatchMethodHolder(PatchMethod.IncrementalPatch, incrementalPatchSize)); } } if (canInstallerPatch) { InstallerPatch installerPatch = comms.VersionInfo.InstallerPatch; if (installerPatch.PatchSize == 0L && string.IsNullOrEmpty(installerPatch.PatchMd5Hash)) { canInstallerPatch = false; } else { preferredPatchMethods.Add(new PatchMethodHolder(PatchMethod.InstallerPatch, installerPatch.PatchSize)); } } preferredPatchMethods.Sort((p1, p2) => p1.size.CompareTo(p2.size)); if (preferredPatchMethods.Count == 0) { FailReason = PatchFailReason.NoSuitablePatchMethodFound; FailDetails = Localization.Get(StringId.E_NoSuitablePatchMethodFound); return(PatchResult.Failed); } // Check if there is enough free disk space long requiredFreeSpaceInCache = preferredPatchMethods[0].size, requiredFreeSpaceInRoot = 0L; for (int i = 0; i < versionInfoFiles.Count; i++) { VersionItem item = versionInfoFiles[i]; FileInfo localFile = new FileInfo(comms.RootPath + item.Path); if (!localFile.Exists) { requiredFreeSpaceInCache += item.FileSize; requiredFreeSpaceInRoot += item.FileSize; } else if (!localFile.MatchesSignature(item.FileSize, item.Md5Hash)) { requiredFreeSpaceInCache += item.FileSize; long deltaSize = item.FileSize - localFile.Length; if (deltaSize > 0L) { requiredFreeSpaceInRoot += deltaSize; } } } requiredFreeSpaceInCache += requiredFreeSpaceInCache / 3; // Require additional 33% free space (might be needed by compressed files and/or incremental patches) requiredFreeSpaceInCache += 1024 * 1024 * 1024L; // Require additional 1 GB of free space, just in case string rootDrive = new DirectoryInfo(comms.RootPath).Root.FullName; string cacheDrive = new DirectoryInfo(comms.CachePath).Root.FullName; if (rootDrive.Equals(cacheDrive, StringComparison.OrdinalIgnoreCase)) { if (!CheckFreeSpace(rootDrive, requiredFreeSpaceInCache + requiredFreeSpaceInRoot)) { return(PatchResult.Failed); } } else { if (!CheckFreeSpace(rootDrive, requiredFreeSpaceInRoot)) { return(PatchResult.Failed); } if (!CheckFreeSpace(cacheDrive, requiredFreeSpaceInCache)) { return(PatchResult.Failed); } } for (int i = 0; i < preferredPatchMethods.Count; i++) { comms.LogToFile(Localization.Get(StringId.PatchMethodXSizeY, preferredPatchMethods[i].method, preferredPatchMethods[i].size.ToMegabytes() + "MB")); } // Start patching for (int i = 0; i < preferredPatchMethods.Count; i++) { PatchMethod patchMethod = preferredPatchMethods[i].method; bool success; if (patchMethod == PatchMethod.RepairPatch) { PatchMethod = PatchMethod.RepairPatch; comms.ListenerCallPatchMethodChanged(PatchMethod); success = PatchUsingRepairPatch(); } else if (patchMethod == PatchMethod.IncrementalPatch) { PatchMethod = PatchMethod.IncrementalPatch; comms.ListenerCallPatchMethodChanged(PatchMethod); success = PatchUsingIncrementalPatches(); } else { PatchMethod = PatchMethod.InstallerPatch; comms.ListenerCallPatchMethodChanged(PatchMethod); success = PatchUsingInstallerPatch(); } if (comms.Cancel) { return(PatchResult.Failed); } if (success) { break; } else { comms.LogToFile(string.Concat(comms.FailReason, ": ", comms.FailDetails)); if (i == preferredPatchMethods.Count - 1) { return(PatchResult.Failed); } } } PatchStage = PatchStage.CheckingFileIntegrity; if (!CheckLocalFilesUpToDate(false, comms.SelfPatching)) { comms.Log(Localization.Get(StringId.SomeFilesAreStillNotUpToDate)); if (canRepairPatch) { if (!PatchUsingRepairPatch()) { return(PatchResult.Failed); } } else { FailReason = PatchFailReason.FilesAreNotUpToDateAfterPatch; FailDetails = Localization.Get(StringId.E_FilesAreNotUpToDateAfterPatch); return(PatchResult.Failed); } } comms.UpdateVersion(comms.VersionInfo.Version); PatchStage = PatchStage.DeletingObsoleteFiles; comms.Log(Localization.Get(StringId.CalculatingObsoleteFiles)); List <string> obsoleteFiles = FindFilesToDelete(comms.RootPath); if (!comms.SelfPatching) { if (obsoleteFiles.Count > 0) { comms.Log(Localization.Get(StringId.DeletingXObsoleteFiles, obsoleteFiles.Count)); for (int i = 0; i < obsoleteFiles.Count; i++) { comms.Log(Localization.Get(StringId.DeletingX, obsoleteFiles[i])); File.Delete(comms.RootPath + obsoleteFiles[i]); } } else { comms.Log(Localization.Get(StringId.NoObsoleteFiles)); } PatchUtils.DeleteDirectory(comms.CachePath); } else { // Delete obsolete self patching files List <string> obsoleteSelfPatchingFiles = FindFilesToDelete(comms.DecompressedFilesPath); if (obsoleteSelfPatchingFiles.Count > 0) { comms.Log(Localization.Get(StringId.DeletingXObsoleteFiles, obsoleteSelfPatchingFiles.Count)); for (int i = 0; i < obsoleteSelfPatchingFiles.Count; i++) { comms.Log(Localization.Get(StringId.DeletingX, obsoleteSelfPatchingFiles[i])); File.Delete(comms.DecompressedFilesPath + obsoleteSelfPatchingFiles[i]); } } else { comms.Log(Localization.Get(StringId.NoObsoleteFiles)); } // Self patcher executable, if exists, can't self patch itself, so patch it manually here // This assumes that self patcher and any related files are located at SELF_PATCHER_DIRECTORY string selfPatcherFiles = comms.DecompressedFilesPath + PatchParameters.SELF_PATCHER_DIRECTORY; if (Directory.Exists(selfPatcherFiles)) { PatchUtils.MoveDirectory(selfPatcherFiles, comms.RootPath + PatchParameters.SELF_PATCHER_DIRECTORY); } string separator = PatchParameters.SELF_PATCH_OP_SEPARATOR; StringBuilder sb = new StringBuilder(500); // Append current version to the beginning of the file sb.Append(rootVersion); // 1. Rename files if (incrementalPatchesInfo.Count > 0) { sb.Append(separator).Append(PatchParameters.SELF_PATCH_MOVE_OP); for (int i = 0; i < incrementalPatchesInfo.Count; i++) { IncrementalPatchInfo incrementalPatch = incrementalPatchesInfo[i]; for (int j = 0; j < incrementalPatch.RenamedFiles.Count; j++) { PatchRenamedItem renamedItem = incrementalPatch.RenamedFiles[j]; sb.Append(separator).Append(comms.RootPath + renamedItem.BeforePath).Append(separator).Append(comms.RootPath + renamedItem.AfterPath); } } } // 2. Update files sb.Append(separator).Append(PatchParameters.SELF_PATCH_MOVE_OP); DirectoryInfo updatedFilesDir = new DirectoryInfo(comms.DecompressedFilesPath); DirectoryInfo[] updatedSubDirectories = updatedFilesDir.GetDirectories(); for (int i = 0; i < updatedSubDirectories.Length; i++) { sb.Append(separator).Append(comms.DecompressedFilesPath).Append(updatedSubDirectories[i].Name).Append(Path.DirectorySeparatorChar).Append(separator).Append(comms.RootPath).Append(updatedSubDirectories[i].Name).Append(Path.DirectorySeparatorChar); } string versionHolderFilename = comms.VersionInfo.Name + PatchParameters.VERSION_HOLDER_FILENAME_POSTFIX; FileInfo[] updatedFiles = updatedFilesDir.GetFiles(); for (int i = 0; i < updatedFiles.Length; i++) { // Don't update the version holder file until everything else is updated properly if (updatedFiles[i].Name != versionHolderFilename) { sb.Append(separator).Append(comms.DecompressedFilesPath).Append(updatedFiles[i].Name).Append(separator).Append(comms.RootPath).Append(updatedFiles[i].Name); } } // Update the version holder now sb.Append(separator).Append(comms.DecompressedFilesPath).Append(versionHolderFilename).Append(separator).Append(comms.RootPath).Append(versionHolderFilename); // 3. Delete obsolete files if (obsoleteFiles.Count > 0) { string selfPatcherDirectory = PatchParameters.SELF_PATCHER_DIRECTORY + Path.DirectorySeparatorChar; sb.Append(separator).Append(PatchParameters.SELF_PATCH_DELETE_OP); comms.Log(Localization.Get(StringId.DeletingXObsoleteFiles, obsoleteFiles.Count)); for (int i = 0; i < obsoleteFiles.Count; i++) { // Delete the obsolete files inside SELF_PATCHER_DIRECTORY manually string absolutePath = comms.RootPath + obsoleteFiles[i]; if (obsoleteFiles[i].StartsWith(selfPatcherDirectory, StringComparison.OrdinalIgnoreCase)) { comms.Log(Localization.Get(StringId.DeletingX, obsoleteFiles[i])); if (File.Exists(absolutePath)) { File.Delete(absolutePath); } else if (Directory.Exists(absolutePath)) { PatchUtils.DeleteDirectory(absolutePath); } } else { // '-->' indicates that the file will be deleted by the self patcher executable comms.LogToFile(Localization.Get(StringId.DeletingX, "--> " + obsoleteFiles[i])); sb.Append(separator).Append(absolutePath); } } } else { comms.Log(Localization.Get(StringId.NoObsoleteFiles)); } sb.Append(separator).Append(comms.CachePath); File.Delete(comms.CachePath + PatchParameters.SELF_PATCH_COMPLETED_INSTRUCTIONS_FILENAME); File.WriteAllText(comms.CachePath + PatchParameters.SELF_PATCH_INSTRUCTIONS_FILENAME, sb.Append(separator).ToString()); } comms.Log(Localization.Get(StringId.PatchCompletedInXSeconds, timer.ElapsedSeconds())); return(PatchResult.Success); }
private bool PatchUsingIncrementalPatches() { if (comms.Cancel) { return(false); } comms.LogToFile(Localization.Get(StringId.ApplyingIncrementalPatch)); if (incrementalPatches.Count == 0) { return(false); } if (comms.VerifyFiles) { PatchStage = PatchStage.VerifyingFilesOnServer; for (int i = 0; i < incrementalPatches.Count; i++) { if (comms.Cancel) { return(false); } IncrementalPatch incrementalPatch = incrementalPatches[i]; long fileSize; if (!comms.DownloadManager.FileExistsAtUrl(comms.VersionInfo.GetInfoURLFor(incrementalPatch), out fileSize)) { FailReason = PatchFailReason.FileDoesNotExistOnServer; FailDetails = Localization.Get(StringId.E_FileXDoesNotExistOnServer, incrementalPatch.PatchVersion() + PatchParameters.INCREMENTAL_PATCH_INFO_EXTENSION); return(false); } if (incrementalPatch.Files > 0) { if (!comms.DownloadManager.FileExistsAtUrl(comms.VersionInfo.GetDownloadURLFor(incrementalPatch), out fileSize)) { FailReason = PatchFailReason.FileDoesNotExistOnServer; FailDetails = Localization.Get(StringId.E_FileXDoesNotExistOnServer, incrementalPatch.PatchVersion() + PatchParameters.INCREMENTAL_PATCH_FILE_EXTENSION); return(false); } else if (fileSize > 0L && fileSize != incrementalPatch.PatchSize) { FailReason = PatchFailReason.FileIsNotValidOnServer; FailDetails = Localization.Get(StringId.E_FileXIsNotValidOnServer, incrementalPatch.PatchVersion() + PatchParameters.INCREMENTAL_PATCH_FILE_EXTENSION); return(false); } } } } for (int i = 0; i < incrementalPatches.Count; i++) { if (comms.Cancel) { return(false); } IncrementalPatch incrementalPatch = incrementalPatches[i]; string patchInfoXML = comms.DownloadManager.DownloadTextFromURL(comms.VersionInfo.GetInfoURLFor(incrementalPatch)); if (patchInfoXML == null) { FailReason = PatchFailReason.DownloadError; FailDetails = Localization.Get(StringId.E_CouldNotDownloadPatchInfoX, incrementalPatch.PatchVersionBrief()); return(false); } if (PatchInfoVerifier != null && !PatchInfoVerifier(ref patchInfoXML)) { FailReason = PatchFailReason.CantVerifyPatchInfo; FailDetails = Localization.Get(StringId.E_PatchInfoCouldNotBeVerified); return(false); } IncrementalPatchInfo patchInfo; try { patchInfo = PatchUtils.DeserializeXMLToIncrementalPatchInfo(patchInfoXML); } catch (Exception e) { comms.LogToFile(e); FailReason = PatchFailReason.XmlDeserializeError; FailDetails = Localization.Get(StringId.E_InvalidPatchInfoX, incrementalPatch.PatchVersionBrief()); return(false); } patchInfo.FromVersion = incrementalPatch.FromVersion; patchInfo.ToVersion = incrementalPatch.ToVersion; patchInfo.DownloadURL = comms.VersionInfo.GetDownloadURLFor(incrementalPatch); patchInfo.CompressedFileSize = incrementalPatch.PatchSize; patchInfo.CompressedMd5Hash = incrementalPatch.PatchMd5Hash; patchInfo.CompressionFormat = incrementalPatch.CompressionFormat; incrementalPatchesInfo.Add(patchInfo); if (currentVersion > incrementalPatch.FromVersion) { continue; } if (new IncrementalPatchApplier(comms, patchInfo).Run() == PatchResult.Failed) { return(false); } } return(true); }
private PatchResult CreateRepairPatch() { if (cancel) { return(PatchResult.Failed); } Directory.CreateDirectory(repairPatchOutputPath); Log(Localization.Get(StringId.CreatingRepairPatch)); Stopwatch timer = Stopwatch.StartNew(); // Compress repair patch files and move them to the destination Log(Localization.Get(StringId.CompressingFilesToDestination)); Stopwatch compressTimer = Stopwatch.StartNew(); // Check if we can use existing repair patch files for files that didn't change since the last patch string previousRepairPatchFilesRoot = null; if (previousVersionInfo != null && previousVersionInfo.CompressionFormat == compressionFormatRepairPatch) { previousRepairPatchFilesRoot = previousPatchFilesRoot + PatchParameters.REPAIR_PATCH_DIRECTORY + Path.DirectorySeparatorChar; if (!Directory.Exists(previousRepairPatchFilesRoot)) { previousRepairPatchFilesRoot = null; } } for (int i = 0; i < versionInfo.Files.Count; i++) { if (cancel) { return(PatchResult.Failed); } VersionItem patchItem = versionInfo.Files[i]; string fromAbsolutePath = rootPath + patchItem.Path; string toAbsolutePath = repairPatchOutputPath + patchItem.Path + PatchParameters.REPAIR_PATCH_FILE_EXTENSION; if (previousRepairPatchFilesRoot != null) { VersionItem previousPatchItem = previousVersionInfo.Files.Find((item) => item.Path == patchItem.Path); if (previousPatchItem != null && previousPatchItem.FileSize == patchItem.FileSize && previousPatchItem.Md5Hash == patchItem.Md5Hash) { if (dontCreatePatchFilesForUnchangedFiles) { patchItem.CompressedFileSize = previousPatchItem.CompressedFileSize; patchItem.CompressedMd5Hash = previousPatchItem.CompressedMd5Hash; continue; } FileInfo previousCompressedFile = new FileInfo(previousRepairPatchFilesRoot + patchItem.Path + PatchParameters.REPAIR_PATCH_FILE_EXTENSION); if (previousCompressedFile.Exists && previousCompressedFile.MatchesSignature(previousPatchItem.CompressedFileSize, previousPatchItem.CompressedMd5Hash)) { Log(Localization.Get(StringId.CopyingXToPatch, previousCompressedFile.FullName)); PatchUtils.CopyFile(previousCompressedFile.FullName, toAbsolutePath); patchItem.CompressedFileSize = previousPatchItem.CompressedFileSize; patchItem.CompressedMd5Hash = previousPatchItem.CompressedMd5Hash; continue; } } } Log(Localization.Get(StringId.CompressingXToY, fromAbsolutePath, toAbsolutePath)); compressTimer.Reset(); compressTimer.Start(); ZipUtils.CompressFile(fromAbsolutePath, toAbsolutePath, compressionFormatRepairPatch); Log(Localization.Get(StringId.CompressionFinishedInXSeconds, compressTimer.ElapsedSeconds())); patchItem.OnCompressed(new FileInfo(toAbsolutePath)); } if (cancel) { return(PatchResult.Failed); } Log(Localization.Get(StringId.PatchCreatedInXSeconds, timer.ElapsedSeconds())); // Calculate compression ratio long uncompressedTotal = 0L, compressedTotal = 0L; for (int i = 0; i < versionInfo.Files.Count; i++) { uncompressedTotal += versionInfo.Files[i].FileSize; compressedTotal += versionInfo.Files[i].CompressedFileSize; } Log(Localization.Get(StringId.CompressionRatioIsX, ((double)compressedTotal * 100 / uncompressedTotal).ToString("F2"))); return(PatchResult.Success); }
/// <exception cref = "DirectoryNotFoundException">Root path does not exist</exception> /// <exception cref = "UnauthorizedAccessException">A path needs admin priviledges to write</exception> /// <exception cref = "IOException">Output path is not empty</exception> /// <exception cref = "ArgumentException">An argument is empty</exception> /// <exception cref = "FormatException">Version is invalid</exception> public PatchCreator(string rootPath, string outputPath, string projectName, VersionCode version) { rootPath = rootPath.Trim(); outputPath = outputPath.Trim(); projectName = projectName.Trim(); if (string.IsNullOrEmpty(rootPath)) { throw new ArgumentException(Localization.Get(StringId.E_XCanNotBeEmpty, "'rootPath'")); } if (string.IsNullOrEmpty(outputPath)) { throw new ArgumentException(Localization.Get(StringId.E_XCanNotBeEmpty, "'outputPath'")); } if (string.IsNullOrEmpty(projectName)) { throw new ArgumentException(Localization.Get(StringId.E_XCanNotBeEmpty, "'projectName'")); } if (!version.IsValid) { throw new FormatException(Localization.Get(StringId.E_VersionCodeXIsInvalid, version)); } if (!Directory.Exists(rootPath)) { throw new DirectoryNotFoundException(Localization.Get(StringId.E_XDoesNotExist, rootPath)); } if (!PatchUtils.CheckWriteAccessToFolder(rootPath)) { throw new UnauthorizedAccessException(Localization.Get(StringId.E_AccessToXIsForbiddenRunInAdminMode, rootPath)); } if (!PatchUtils.CheckWriteAccessToFolder(outputPath)) { throw new UnauthorizedAccessException(Localization.Get(StringId.E_AccessToXIsForbiddenRunInAdminMode, outputPath)); } if (Directory.Exists(outputPath)) { if (Directory.GetFileSystemEntries(outputPath).Length > 0) { throw new IOException(Localization.Get(StringId.E_DirectoryXIsNotEmpty, outputPath)); } } Localization.Get(StringId.Done); // Force the localization system to be initialized with the current culture/language previousPatchFilesRoot = null; previousVersionInfo = null; dontCreatePatchFilesForUnchangedFiles = false; generateRepairPatch = true; generateInstallerPatch = true; previousVersionRoot = null; diffQuality = 0; this.previousVersion = null; this.version = version; this.rootPath = PatchUtils.GetPathWithTrailingSeparatorChar(rootPath); this.outputPath = PatchUtils.GetPathWithTrailingSeparatorChar(outputPath); this.projectName = projectName; repairPatchOutputPath = this.outputPath + PatchParameters.REPAIR_PATCH_DIRECTORY + Path.DirectorySeparatorChar; installerPatchOutputPath = this.outputPath + PatchParameters.INSTALLER_PATCH_DIRECTORY + Path.DirectorySeparatorChar; incrementalPatchOutputPath = this.outputPath + PatchParameters.INCREMENTAL_PATCH_DIRECTORY + Path.DirectorySeparatorChar; incrementalPatchTempPath = this.outputPath + "Temp" + Path.DirectorySeparatorChar; ignoredPaths = new HashSet <string>(); ignoredPathsRegex = new List <Regex>() { PatchUtils.WildcardToRegex("*" + PatchParameters.VERSION_HOLDER_FILENAME_POSTFIX), // Ignore any version holder files PatchUtils.WildcardToRegex("*" + PatchParameters.LOG_FILE_NAME) // Or log files }; compressionFormatRepairPatch = CompressionFormat.LZMA; compressionFormatInstallerPatch = CompressionFormat.LZMA; compressionFormatIncrementalPatch = CompressionFormat.LZMA; baseDownloadURL = ""; maintenanceCheckURL = ""; logs = new Queue <string>(); cancel = false; silentMode = false; IsRunning = false; Result = PatchResult.Failed; }
public static void CalculateDelta(string sourcePath, string targetPath, string deltaPath, int quality = 3, FilePatchProgress progressReporter = null) { // Try different chunk sizes to find the smallest diff file if (quality < 1) { quality = 1; } int[] chunkSizes = new int[quality * 2 - 1]; chunkSizes[0] = SignatureBuilder.DefaultChunkSize; int validChunkSizes = 1; int currentChunkSize = chunkSizes[0]; for (int i = 1; i < quality; i++) { currentChunkSize /= 2; if (currentChunkSize < SignatureBuilder.MinimumChunkSize) { break; } chunkSizes[validChunkSizes++] = currentChunkSize; } currentChunkSize = chunkSizes[0]; for (int i = 1; i < quality; i++) { currentChunkSize *= 2; if (currentChunkSize > SignatureBuilder.MaximumChunkSize) { break; } chunkSizes[validChunkSizes++] = currentChunkSize; } string deltaPathTemp = deltaPath + ".detmp"; string signaturePathTemp = deltaPath + ".sgtmp"; long deltaSize = 0L; for (int i = 0; i < validChunkSizes; i++) { if (i == 0) { CalculateDeltaInternal(sourcePath, targetPath, deltaPath, signaturePathTemp, chunkSizes[i], progressReporter); deltaSize = new FileInfo(deltaPath).Length; } else { CalculateDeltaInternal(sourcePath, targetPath, deltaPathTemp, signaturePathTemp, chunkSizes[i], progressReporter); long newDeltaSize = new FileInfo(deltaPathTemp).Length; if (newDeltaSize < deltaSize) { PatchUtils.MoveFile(deltaPathTemp, deltaPath); deltaSize = newDeltaSize; } } } File.Delete(deltaPathTemp); File.Delete(signaturePathTemp); }