Ejemplo n.º 1
0
        /// <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;
        }
Ejemplo n.º 2
0
        /// <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;
        }
Ejemplo n.º 3
0
        /// <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;
        }
Ejemplo n.º 4
0
        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);
        }
Ejemplo n.º 5
0
        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));
            }
        }
Ejemplo n.º 6
0
        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);
        }
Ejemplo n.º 7
0
        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);
        }
Ejemplo n.º 8
0
        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);
        }
Ejemplo n.º 9
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.º 10
0
        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);
                }
            }
        }
Ejemplo n.º 11
0
        public bool Run()
        {
            if (!IsRunning)
            {
                IsRunning = true;
                cancel    = false;

                PatchUtils.CreateBackgroundThread(new ThreadStart(ThreadCreatePatchFunction)).Start();
                return(true);
            }

            return(false);
        }
Ejemplo n.º 12
0
        // 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);
            }
        }
Ejemplo n.º 13
0
        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);
        }
Ejemplo n.º 14
0
        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);
        }
Ejemplo n.º 15
0
        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);
        }
Ejemplo n.º 16
0
        /// <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;
        }
Ejemplo n.º 17
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.º 18
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);
        }
Ejemplo n.º 19
0
        /// <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);
        }
Ejemplo n.º 20
0
        // 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()));
        }
Ejemplo n.º 21
0
        /// <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);
        }
Ejemplo n.º 22
0
        /// <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;
        }
Ejemplo n.º 23
0
        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.º 24
0
 public void UpdateVersion(VersionCode version)
 {
     PatchUtils.SetVersion(SelfPatching ? DecompressedFilesPath : RootPath, VersionInfo.Name, version);
 }
Ejemplo n.º 25
0
 public void SaveChanges()
 {
     PatchUtils.SerializeVersionInfoToXML(VersionInfo, versionInfoPath);
 }
Ejemplo n.º 26
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.º 27
0
        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);
        }
Ejemplo n.º 28
0
        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);
        }
Ejemplo n.º 29
0
        /// <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;
        }
Ejemplo n.º 30
0
        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);
        }