/// <inheritdoc cref="IDeltaUpdate.ApplyDeltaFile"/> public async Task <bool> ApplyDeltaFile( TemporaryFolder tempFolder, string originalFileLocation, string newFileLocation, string deltaFileName, Stream deltaFileStream, Action <double>?progress = null) { if (OSHelper.ActiveOS != OSPlatform.Windows) { _logger.Error("We aren't on Windows so can't apply MSDiff update"); return(false); } if (string.IsNullOrWhiteSpace(deltaFileName)) { _logger.Error("Wasn't given a file location for the delta file"); return(false); } //Put the delta file onto disk using var tmpDeltaFile = tempFolder.CreateTemporaryFile(deltaFileName); var tmpFileStream = tmpDeltaFile.GetStream(); await deltaFileStream.CopyToAsync(tmpFileStream); tmpFileStream.Dispose(); deltaFileStream.Dispose(); //If baseFile + outputLocation are the same, copy it to a tmp file //and then give it that (deleting it after) TemporaryFile?tmpBaseFile = null; if (originalFileLocation == newFileLocation) { tmpBaseFile = tempFolder.CreateTemporaryFile(); File.Copy(originalFileLocation, tmpBaseFile.Location); originalFileLocation = tmpBaseFile.Location; } //Make the updated file! File.Create(newFileLocation).Dispose(); var wasApplySuccessful = ApplyDelta(ApplyFlags.None, originalFileLocation, tmpDeltaFile.Location, newFileLocation); tmpBaseFile?.Dispose(); return(wasApplySuccessful); }
/// <summary> /// Creates a delta file by going through the different ways of creating delta files and grabbing the one that made the smallest file /// </summary> /// <param name="tempFolder">Where the temp folder is located</param> /// <param name="baseFileLocation">Where the older version of the file exists</param> /// <param name="newFileLocation">Where the newer version of the file exists</param> /// <param name="deltaFileLocation">Where the delta file should be stored (If we are unable to store it in a stream)</param> /// <param name="intendedOs">What OS this delta file will be intended for</param> /// <param name="extension">Extension of the delta file</param> /// <param name="deltaFileStream">The contents of the delta file</param> /// <param name="progress">Progress of making the delta file (If we can report the progress back)</param> public static bool CreateDeltaFile( TemporaryFolder tempFolder, string baseFileLocation, string newFileLocation, string deltaFileLocation, OSPlatform?intendedOs, out string extension, out Stream?deltaFileStream, Action <double>?progress = null) { var os = intendedOs ?? OSHelper.ActiveOS; IReadOnlyList <IDeltaUpdate>?deltaUpdaters = null; lock (_updaterLock) { if (CachedUpdaters.ContainsKey(os)) { deltaUpdaters = CachedUpdaters[os]; } else { deltaUpdaters ??= DeltaUpdaters.GetUpdatersBasedOnOS(intendedOs ?? OSHelper.ActiveOS); CachedUpdaters.Add(os, deltaUpdaters); } } var updaterCount = deltaUpdaters.Count; var progresses = new double[updaterCount]; var deltaResults = new List <DeltaCreationResult>(updaterCount); Parallel.For(0, updaterCount, i => { var deltaUpdater = deltaUpdaters[i]; var deltaFile = tempFolder.CreateTemporaryFile(); var successful = deltaUpdater.CreateDeltaFile( tempFolder, baseFileLocation, newFileLocation, deltaFile.Location, out var stream, pro => { progresses[i] = pro; progress?.Invoke(progresses.Sum() / updaterCount); }); if (successful) { deltaResults.Add( new DeltaCreationResult(deltaUpdater.Extension, deltaFile, stream)); } else { Logging.Warning("{0} was unable to make a delta file for {1}", nameof(deltaUpdater), baseFileLocation.GetRelativePath(newFileLocation)); deltaFile.Dispose(); } if (progresses[i] < 1d) { progresses[i] = 1d; progress?.Invoke(progresses.Sum() / updaterCount); } });
/// <summary> /// Creates a delta file and then adds it the <see cref="ZipArchive"/> /// </summary> /// <param name="tempFolder">Where the temp folder is located</param> /// <param name="zipArchive"><see cref="ZipArchive"/> to add the file too</param> /// <param name="baseFileLocation">Old file</param> /// <param name="newFileLocation">New file</param> /// <param name="intendedOs">What OS this delta file will be intended for</param> /// <param name="progress">Progress of creating delta file (If possible)</param> /// <param name="extensionEnd">What to add onto the end of the extension (If needed)</param> /// <returns>If we was able to create the delta file</returns> private bool AddDeltaFile( TemporaryFolder tempFolder, ZipArchive zipArchive, string baseFileLocation, string newFileLocation, OSPlatform?intendedOs = null, Action <double>?progress = null, string?extensionEnd = null) { //Create where the delta file can be stored to grab once made using var tmpDeltaFile = tempFolder.CreateTemporaryFile(); //Try to create diff file, outputting extension (and maybe a stream) based on what was used to make it if (!DeltaCreation.CreateDeltaFile( tempFolder, baseFileLocation, newFileLocation, tmpDeltaFile.Location, intendedOs, out var extension, out var deltaFileStream)) { //Wasn't able to create delta file, report back as fail _logger.Error("Wasn't able to create delta file"); return(false); } //Check that we got something to work with if (deltaFileStream == null && !File.Exists(tmpDeltaFile.Location)) { _logger.Error("We have no delta file/stream to work off somehow"); return(false); } //Get hash and filesize to add to file var newFileStream = File.OpenRead(newFileLocation); var newFilesize = newFileStream.Length; /*Check that the delta file will take up less space, if not fail here * as it's not worth making it a delta file (Will take more time and processing power then just copying on the user's device)*/ if ((deltaFileStream?.Length ?? new FileInfo(tmpDeltaFile.Location).Length) >= newFilesize) { _logger.Warning("{0} will take up more space as a delta file then as a \"new\" file, failing to trigger it being added as a new file", baseFileLocation.GetRelativePath(newFileLocation)); newFileStream.Dispose(); deltaFileStream?.Dispose(); return(false); } var hash = SHA256Util.CreateSHA256Hash(newFileStream); newFileStream.Dispose(); //Grab file and add it to the set of files deltaFileStream ??= tmpDeltaFile.GetStream(FileMode.Open); var addSuccessful = AddFile( zipArchive, deltaFileStream, baseFileLocation.GetRelativePath(newFileLocation) + extension + extensionEnd, filesize: newFilesize, sha256Hash: hash); //Dispose stream and report back if we was able to add file deltaFileStream.Dispose(); return(addSuccessful); }
private bool AddLoaderFile( TemporaryFolder temporaryFolder, ApplicationMetadata applicationMetadata, ZipArchive zipArchive, SemanticVersion newVersion, string applicationLocation, OSPlatform?intendedOs = null, SemanticVersion?oldVersion = null, string?outputLocation = null) { //TODO: See why this is making another .shasum file (but working) using var loaderLocation = temporaryFolder.CreateTemporaryFile(applicationMetadata.ApplicationName + ".exe"); // ReSharper disable once LocalFunctionHidesMethod bool AddFile() => BinaryCreator.AddFile( zipArchive, loaderLocation.GetStream(FileMode.Open), applicationMetadata.ApplicationName + ".exe.load", false); //TODO: Grab metadata from .exe and drop it into Loader var iconLocation = Path.Combine(applicationLocation, "app.ico"); iconLocation = File.Exists(iconLocation) ? iconLocation : null; var successful = LoaderCreator.CreateLoader( temporaryFolder, $"{newVersion.GetApplicationFolder()}\\{applicationMetadata.ApplicationName}.exe", iconLocation, loaderLocation.Location, applicationMetadata.ApplicationName, intendedOs); if (successful != LoadCreateStatus.Successful) { var canContinue = successful == LoadCreateStatus.UnableToCreate; _logger.Error("We wasn't able to create loader! (Going to fail file creation?: {0})", canContinue); return(canContinue); } if (oldVersion == null || !Directory.Exists(outputLocation)) { return(AddFile()); } //If we get here then we might also have the old loader, try to diff by using it foreach (var file in Directory.EnumerateFiles(outputLocation !, "*" + Extension)) { if (string.IsNullOrWhiteSpace(file)) { _logger.Warning("We somehow got an entry for {0} which was nothing", outputLocation); continue; } /*Don't try it with delta file, more then likely going to * have diff loader itself and we can't work with that*/ var fileName = Path.GetFileNameWithoutExtension(file); if (fileName.EndsWith("-delta") || fileName.ToVersion() != oldVersion) { continue; } Stream? stream = null; ZipArchive fileArch; try { stream = File.OpenRead(file); fileArch = new ZipArchive(stream, ZipArchiveMode.Read); } catch (Exception e) { stream?.Dispose(); _logger.Error(e); continue; } //We want to grab the loader file var loaderFileIndex = fileArch.Entries.IndexOf(x => x?.Name == applicationMetadata.ApplicationName + ".exe.load"); if (loaderFileIndex == -1) { fileArch.Dispose(); continue; } var fileEntry = fileArch.Entries[loaderFileIndex]; using var tmpFile = temporaryFolder.CreateTemporaryFile(Path.GetFileNameWithoutExtension(fileEntry.Name)); fileEntry.ExtractToFile(tmpFile.Location, true); var deltaSuccessful = AddDeltaFile(temporaryFolder, zipArchive, tmpFile.Location, loaderLocation.Location, extensionEnd: "load"); if (!deltaSuccessful) { _logger.Warning("Wasn't able to diff loader, just going to add the load in as normal"); } fileArch.Dispose(); return(deltaSuccessful || AddFile()); } //If we get here then we can't do any kind of diff logic return(AddFile()); }