Ejemplo n.º 1
0
        /// <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);
        }
Ejemplo n.º 2
0
        /// <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);
        }
Ejemplo n.º 3
0
        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);
        }
        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);
        }
Ejemplo n.º 5
0
        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);
        }
Ejemplo n.º 6
0
        /// <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);

            // Preserve previous incremental patch info
            if (previousVersionInfo != null)
            {
                versionInfo.IncrementalPatches.AddRange(previousVersionInfo.IncrementalPatches);

                if (!dontCreatePatchFilesForUnchangedFiles)
                {
                    string previousIncrementalPatches = previousPatchFilesRoot + PatchParameters.INCREMENTAL_PATCH_DIRECTORY + Path.DirectorySeparatorChar;
                    if (Directory.Exists(previousIncrementalPatches))
                    {
                        PatchUtils.CopyDirectory(previousIncrementalPatches, incrementalPatchOutputPath);
                    }
                }
            }

            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);
        }
Ejemplo n.º 7
0
        private PatchResult GeneratePatchInternal()
        {
            if (!Directory.Exists(versionsPath))
            {
                Log(Localization.Get(StringId.E_XDoesNotExist, versionsPath));
                return(PatchResult.Failed);
            }

            if (!File.Exists(projectInfoPath))
            {
                Log(Localization.Get(StringId.E_XDoesNotExist, projectInfoPath));
                return(PatchResult.Failed);
            }

            string[] versions = Directory.GetDirectories(versionsPath);
            if (versions.Length == 0)
            {
                Log(Localization.Get(StringId.E_DirectoryXIsEmpty, versionsPath));
                return(PatchResult.Failed);
            }

            string validationResult = ValidateProject();

            if (!string.IsNullOrEmpty(validationResult))
            {
                Log(validationResult);
                return(PatchResult.Failed);
            }

            Stopwatch timer = Stopwatch.StartNew();

            // Here's how it works:
            // Move previous incremental patch files to Temp\IncrementalFormer
            // Generate repair patch and installer patch files on Temp\Output
            // Foreach incremental patch to generate:
            //   Generate incremental patch files on Temp\Incremental
            //   Move the incremental patch files from there to Temp\IncrementalFormer
            // Replace the contents of outputPath with Temp\Output:
            //   Delete outputPath directory
            //   Move Temp\Output to outputPath
            //   Move Temp\IncrementalFormer to outputPath
            // Delete Temp
            string tempRoot                   = projectRoot + "Temp" + Path.DirectorySeparatorChar;
            string tempOutput                 = tempRoot + "Output" + Path.DirectorySeparatorChar;
            string tempIncrementalOutput      = tempRoot + "Incremental" + Path.DirectorySeparatorChar;
            string tempPrevIncrementalPatches = tempRoot + "IncrementalFormer" + Path.DirectorySeparatorChar;

            PatchUtils.DeleteDirectory(tempOutput);
            PatchUtils.DeleteDirectory(tempIncrementalOutput);

            // Preserve the previous incremental patches
            string versionInfoPath        = outputPath + PatchParameters.VERSION_INFO_FILENAME;
            string incrementalPatchesPath = outputPath + PatchParameters.INCREMENTAL_PATCH_DIRECTORY;

            if (Directory.Exists(incrementalPatchesPath))
            {
                PatchUtils.MoveDirectory(incrementalPatchesPath, tempPrevIncrementalPatches);
            }

            List <IncrementalPatch> incrementalPatches = new List <IncrementalPatch>();

            if (File.Exists(versionInfoPath))
            {
                VersionInfo oldVersionInfo = PatchUtils.GetVersionInfoFromPath(versionInfoPath);
                if (oldVersionInfo != null)
                {
                    incrementalPatches.AddRange(oldVersionInfo.IncrementalPatches);
                }
            }

            Array.Sort(versions, new VersionComparer());

            string      latestVersion = versions[versions.Length - 1];
            ProjectInfo projectInfo   = PatchUtils.GetProjectInfoFromPath(projectInfoPath);

            if (projectInfo.IsSelfPatchingApp && Directory.Exists(selfPatcherPath) && Directory.GetFileSystemEntries(selfPatcherPath).Length > 0)
            {
                PatchUtils.CopyDirectory(selfPatcherPath, Path.Combine(latestVersion, PatchParameters.SELF_PATCHER_DIRECTORY));
            }

            patchCreator = new PatchCreator(latestVersion, tempOutput, projectInfo.Name, Path.GetFileName(latestVersion)).SetListener(this).
                           SetCompressionFormat(projectInfo.CompressionFormatRepairPatch, projectInfo.CompressionFormatInstallerPatch, projectInfo.CompressionFormatIncrementalPatch).
                           CreateRepairPatch(projectInfo.CreateRepairPatch).CreateInstallerPatch(projectInfo.CreateInstallerPatch).CreateIncrementalPatch(false).
                           AddIgnoredPaths(projectInfo.IgnoredPaths).SilentMode(silentMode).SetBaseDownloadURL(projectInfo.BaseDownloadURL).SetMaintenanceCheckURL(projectInfo.MaintenanceCheckURL);

            // Generate repair patch and installer patch files
            if (cancel || !ExecuteCurrentPatch())
            {
                return(PatchResult.Failed);
            }

            if (projectInfo.CreateIncrementalPatch && versions.Length > 1)
            {
                string incrementalPatchesGenerated = tempIncrementalOutput + PatchParameters.INCREMENTAL_PATCH_DIRECTORY;
                string versionInfoGenerated        = tempIncrementalOutput + PatchParameters.VERSION_INFO_FILENAME;

                patchCreator = new PatchCreator(latestVersion, tempIncrementalOutput, projectInfo.Name, Path.GetFileName(latestVersion)).SetListener(this).
                               SetCompressionFormat(projectInfo.CompressionFormatRepairPatch, projectInfo.CompressionFormatInstallerPatch, projectInfo.CompressionFormatIncrementalPatch).
                               AddIgnoredPaths(projectInfo.IgnoredPaths).SilentMode(silentMode).CreateRepairPatch(false).CreateInstallerPatch(false);

                for (int i = versions.Length - 2; i >= 0; i--)
                {
                    Log(Localization.Get(StringId.CreatingIncrementalPatchX, Path.GetFileName(versions[i]) + "->" + Path.GetFileName(latestVersion)));

                    // Generate incremental patch files
                    patchCreator.CreateIncrementalPatch(true, versions[i], projectInfo.BinaryDiffQuality);
                    if (cancel || !ExecuteCurrentPatch())
                    {
                        return(PatchResult.Failed);
                    }

                    List <IncrementalPatch> newIncrementalPatches = PatchUtils.GetVersionInfoFromPath(versionInfoGenerated).IncrementalPatches;
                    for (int j = incrementalPatches.Count - 1; j >= 0; j--)
                    {
                        // Don't allow duplicate IncrementalPatch entries
                        for (int k = newIncrementalPatches.Count - 1; k >= 0; k--)
                        {
                            if (incrementalPatches[j].FromVersion == newIncrementalPatches[k].FromVersion && incrementalPatches[j].ToVersion == newIncrementalPatches[k].ToVersion)
                            {
                                incrementalPatches.RemoveAt(j);
                                break;
                            }
                        }
                    }

                    incrementalPatches.AddRange(newIncrementalPatches);

                    // Move incremental patch files to Temp
                    PatchUtils.MoveDirectory(incrementalPatchesGenerated, tempPrevIncrementalPatches);
                    PatchUtils.DeleteDirectory(tempIncrementalOutput);

                    if (!projectInfo.CreateAllIncrementalPatches)
                    {
                        break;
                    }
                }
            }

            PatchUtils.DeleteDirectory(outputPath);
            PatchUtils.MoveDirectory(tempOutput, outputPath);

            if (Directory.Exists(tempPrevIncrementalPatches))
            {
                PatchUtils.MoveDirectory(tempPrevIncrementalPatches, incrementalPatchesPath);
            }

            VersionInfo versionInfo = PatchUtils.GetVersionInfoFromPath(versionInfoPath);

            incrementalPatches.Sort(PatchUtils.IncrementalPatchComparison);
            versionInfo.IncrementalPatches = incrementalPatches;
            PatchUtils.SerializeVersionInfoToXML(versionInfo, versionInfoPath);

            PatchUtils.DeleteDirectory(tempRoot);

            Log(Localization.Get(StringId.AllPatchesCreatedInXSeconds, timer.ElapsedSeconds()));
            return(PatchResult.Success);
        }
        public PatchResult Run()
        {
            if (comms.Cancel)
            {
                return(PatchResult.Failed);
            }

            if (comms.IsUnderMaintenance())
            {
                return(PatchResult.Failed);
            }

            if (patchInfo.Files.Count > 0)
            {
                PatchUtils.DeleteDirectory(patchDecompressPath);
                Directory.CreateDirectory(patchDecompressPath);
            }

            if (comms.Cancel)
            {
                return(PatchResult.Failed);
            }

            Stopwatch timer = Stopwatch.StartNew();

            if (patchInfo.Files.Count > 0)
            {
                FileInfo patchFile = new FileInfo(patchDownloadPath);
                if (!patchFile.Exists || !patchFile.MatchesSignature(patchInfo.CompressedFileSize, patchInfo.CompressedMd5Hash))
                {
                    comms.Stage = PatchStage.DownloadingFiles;

                    Stopwatch downloadTimer = Stopwatch.StartNew();
                    comms.Log(Localization.Get(StringId.DownloadingPatchX, patchInfo.PatchVersion()));

                    patchFile = comms.DownloadManager.DownloadFileFromURLToPath(patchInfo.DownloadURL, patchDownloadPath, patchInfo.CompressedFileSize);
                    if (patchFile == null)
                    {
                        comms.FailReason  = PatchFailReason.DownloadError;
                        comms.FailDetails = Localization.Get(StringId.E_PatchXCouldNotBeDownloaded, patchInfo.PatchVersion());

                        return(PatchResult.Failed);
                    }
                    else if (!patchFile.MatchesSignature(patchInfo.CompressedFileSize, patchInfo.CompressedMd5Hash))
                    {
                        comms.FailReason  = PatchFailReason.CorruptDownloadError;
                        comms.FailDetails = Localization.Get(StringId.E_DownloadedFileXIsCorrupt, patchInfo.PatchVersion());

                        return(PatchResult.Failed);
                    }
                    else
                    {
                        comms.Log(Localization.Get(StringId.XDownloadedInYSeconds, patchInfo.PatchVersion(), downloadTimer.ElapsedSeconds()));
                    }
                }

                if (comms.Cancel)
                {
                    return(PatchResult.Failed);
                }

                comms.Stage = PatchStage.ExtractingFilesFromArchive;
                comms.Log(Localization.Get(StringId.DecompressingPatchX, patchInfo.PatchVersion()));

                ZipUtils.DecompressFolderLZMA(patchFile.FullName, patchDecompressPath);

                comms.Stage = PatchStage.UpdatingFiles;
                comms.Log(Localization.Get(StringId.UpdatingXFiles, patchInfo.Files.Count));

                int failedItemCount = 0;
                for (int i = 0; i < patchInfo.Files.Count; i++)
                {
                    if (comms.Cancel)
                    {
                        return(PatchResult.Failed);
                    }

                    string fileRelativePath     = patchInfo.Files[i].Path;
                    string diffFileAbsolutePath = patchDecompressPath + fileRelativePath;
                    if (!File.Exists(diffFileAbsolutePath))
                    {
                        comms.Log(Localization.Get(StringId.E_DiffOfXDoesNotExist, Path.GetFileName(fileRelativePath)));

                        failedItemCount++;
                        continue;
                    }

                    string decompressAbsolutePath = comms.DecompressedFilesPath + fileRelativePath;
                    if (comms.SelfPatching && ApplyDiffToFile(decompressAbsolutePath, diffFileAbsolutePath, decompressAbsolutePath, i))
                    {
                        continue;
                    }

                    string localFileAbsolutePath = comms.RootPath + fileRelativePath;
                    string targetPath            = comms.SelfPatching ? decompressAbsolutePath : localFileAbsolutePath;
                    if (!ApplyDiffToFile(localFileAbsolutePath, diffFileAbsolutePath, targetPath, i))
                    {
                        failedItemCount++;
                    }
                }

                comms.Log(Localization.Get(StringId.XFilesUpdatedSuccessfully, patchInfo.Files.Count - failedItemCount, patchInfo.Files.Count));
            }

            if (patchInfo.RenamedFiles.Count > 0)
            {
                comms.Log(Localization.Get(StringId.RenamingXFiles, patchInfo.RenamedFiles.Count));
                if (!RenameItems(patchInfo.RenamedFiles))
                {
                    return(PatchResult.Failed);
                }
            }

            // Updating version code to the latest one will be done by SimplePatchTool, after checking
            // whether or not all files are correctly updated
            if (patchInfo.ToVersion < comms.VersionInfo.Version)
            {
                comms.UpdateVersion(patchInfo.ToVersion);
            }

            if (patchInfo.Files.Count > 0)
            {
                PatchUtils.DeleteDirectory(patchDecompressPath);
                File.Delete(patchDownloadPath);
            }

            comms.Log(Localization.Get(StringId.PatchAppliedInXSeconds, timer.ElapsedSeconds()));
            return(PatchResult.Success);
        }
        protected override PatchResult Execute()
        {
            if (comms.Cancel)
            {
                return(PatchResult.Failed);
            }

            if (comms.IsUnderMaintenance())
            {
                return(PatchResult.Failed);
            }

            string rootPath            = comms.SelfPatching ? comms.DecompressedFilesPath : comms.RootPath;
            string patchDecompressPath = comms.GetDecompressPathForPatch(PatchParameters.INSTALLER_PATCH_DIRECTORY);
            string patchDownloadPath   = comms.CachePath + PatchParameters.INSTALLER_PATCH_FILENAME;

            InstallerPatch patchInfo = comms.VersionInfo.InstallerPatch;

            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);
            }

            InitializeProgress(filesToUpdate.Count, patchInfo.PatchSize);

            PatchUtils.DeleteDirectory(patchDecompressPath);
            Directory.CreateDirectory(patchDecompressPath);

            FileInfo patchFile = new FileInfo(patchDownloadPath);

            if (!patchFile.Exists || !patchFile.MatchesSignature(patchInfo.PatchSize, patchInfo.PatchMd5Hash))
            {
                comms.Stage = PatchStage.DownloadingFiles;

                Stopwatch downloadTimer = Stopwatch.StartNew();
                comms.Log(Localization.Get(StringId.DownloadingPatchX, PatchParameters.INSTALLER_PATCH_FILENAME));

                patchFile = comms.DownloadManager.DownloadFileFromURLToPath(comms.VersionInfo.GetDownloadURLFor(patchInfo), patchDownloadPath, patchInfo.PatchSize);
                if (patchFile == null)
                {
                    comms.FailReason  = PatchFailReason.DownloadError;
                    comms.FailDetails = Localization.Get(StringId.E_XCouldNotBeDownloaded, PatchParameters.INSTALLER_PATCH_FILENAME);

                    return(PatchResult.Failed);
                }
                else if (!patchFile.MatchesSignature(patchInfo.PatchSize, patchInfo.PatchMd5Hash))
                {
                    comms.FailReason  = PatchFailReason.CorruptDownloadError;
                    comms.FailDetails = Localization.Get(StringId.E_DownloadedFileXIsCorrupt, PatchParameters.INSTALLER_PATCH_FILENAME);

                    return(PatchResult.Failed);
                }
                else
                {
                    comms.Log(Localization.Get(StringId.XDownloadedInYSeconds, PatchParameters.INSTALLER_PATCH_FILENAME, downloadTimer.ElapsedSeconds()));
                }
            }
            else
            {
                ReportProgress(0, patchInfo.PatchSize);
            }

            if (comms.Cancel)
            {
                return(PatchResult.Failed);
            }

            comms.Stage = PatchStage.ExtractingFilesFromArchive;
            comms.Log(Localization.Get(StringId.DecompressingPatchX, PatchParameters.INSTALLER_PATCH_FILENAME));

            ZipUtils.DecompressFolder(patchFile.FullName, patchDecompressPath, patchInfo.CompressionFormat);

            comms.Stage = PatchStage.UpdatingFiles;
            comms.Log(Localization.Get(StringId.UpdatingXFiles, filesToUpdate.Count));

            for (int i = 0; i < filesToUpdate.Count; i++)
            {
                if (comms.Cancel)
                {
                    return(PatchResult.Failed);
                }

                string fileRelativePath   = filesToUpdate[i].Path;
                string targetAbsolutePath = rootPath + fileRelativePath;

                comms.Log(Localization.Get(StringId.UpdatingXthFile, i + 1, filesToUpdate.Count, fileRelativePath));

                Directory.CreateDirectory(Path.GetDirectoryName(targetAbsolutePath));
                PatchUtils.CopyFile(patchDecompressPath + fileRelativePath, targetAbsolutePath);

                ReportProgress(1, 0L);
            }

            PatchUtils.DeleteDirectory(patchDecompressPath);
            File.Delete(patchDownloadPath);

            comms.Log(Localization.Get(StringId.PatchAppliedInXSeconds, timer.ElapsedSeconds()));
            return(PatchResult.Success);
        }