コード例 #1
0
        public void RenameWizardsWebpageImages(string htmlFile, string targetSubdir)
        {
            FsPath htmlPath  = HtmlDir.Join(htmlFile);
            FsPath targetDir = DevPaths.GathererOriginalDir.Join(targetSubdir);

            string htmlFileName  = htmlPath.Basename(extension: false);
            FsPath directoryName = htmlPath.Parent();

            if (!directoryName.HasValue())
            {
                throw new ArgumentException(htmlPath.Value, nameof(htmlPath));
            }

            FsPath filesDirectory = directoryName.Join(htmlFileName + "_files");

            string content = htmlPath.ReadAllText();
            var    matches = _imgTagPattern.Matches(content);

            targetDir.CreateDirectory();
            foreach (Match match in matches)
            {
                string originalFileName = match.Groups["file"].Value;
                string ext = Path.GetExtension(originalFileName);

                FsPath filePath = filesDirectory.Join(originalFileName);

                string name = HttpUtility.HtmlDecode(match.Groups["name"].Value)
                              .Replace(" // ", "");

                FsPath defaultTargetPath = targetDir.Join(name + ext);

                bool defaultTargetExists = defaultTargetPath.IsFile();

                if (defaultTargetExists || getTargetPath(1).IsFile())
                {
                    if (defaultTargetExists)
                    {
                        defaultTargetPath.MoveFileTo(getTargetPath(1));
                    }

                    for (int i = 2; i < 12; i++)
                    {
                        FsPath targetPath = getTargetPath(i);
                        if (!targetPath.IsFile())
                        {
                            filePath.CopyFileTo(targetPath, overwrite: false);
                            break;
                        }
                    }
                }
                else
                {
                    filePath.CopyFileTo(defaultTargetPath, overwrite: false);
                }

                FsPath getTargetPath(int num) =>
                targetDir.Join(name + num + ext);
            }
        }
コード例 #2
0
        public Dictionary <string, Dictionary <string, int> > GetOrderByCard()
        {
            FsPath sortFile = _resourcesDir.Join("tcg.sort.json");

            var json   = sortFile.ReadAllText();
            var result = JsonConvert.DeserializeObject <Dictionary <string, Dictionary <string, int> > >(json);

            return(result);
        }
コード例 #3
0
ファイル: Installer.cs プロジェクト: NikolayXHD/Mtgdb
        public void CreateApplicationShortcut(FsPath shortcutLocation)
        {
            string appVersionInstalled = GetAppVersionInstalled();
            // Mtgdb.Gui.v1.3.5.10.zip
            var prefix     = "Mtgdb.Gui.";
            var postfix    = ".zip";
            var versionDir = appVersionInstalled.Substring(prefix.Length, appVersionInstalled.Length - prefix.Length - postfix.Length);

            FsPath currentBin = AppDir.BinVersion.Parent().Join(versionDir);
            // may be different from currently running executable because of just installed upgrade
            FsPath execPath = currentBin.Join(ExecutableFileName);
            FsPath iconPath = currentBin.Join("mtg64.ico");

            FsPath shortcutPath = shortcutLocation.Join(ShortcutFileName);

            if (createApplicationShortcut(shortcutPath, execPath, iconPath))
            {
                return;
            }

            // workaround a problem with WshShell unable to create the link within desktop directory
            // due to a mismatch between physical and localized directory names
            var    tempLocation = new FsPath(Path.GetTempPath());
            FsPath tempPath     = tempLocation.Join(ShortcutFileName);

            if (createApplicationShortcut(tempPath, execPath, iconPath))
            {
                try
                {
                    if (shortcutPath.IsFile())
                    {
                        shortcutPath.DeleteFile();
                    }

                    tempPath.MoveFileTo(shortcutPath);
                    Console.WriteLine("Moved application shortcut from {0} to {1}",
                                      tempLocation, shortcutLocation);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Failed to move application shortcut from {0} to {1}: {2}",
                                      tempLocation, shortcutLocation, ex);

                    try
                    {
                        tempPath.DeleteFile();
                    }
                    catch (Exception cleanupEx)
                    {
                        Console.WriteLine("Failed to remove application shortcut from {0}: {1}",
                                          tempLocation, cleanupEx);
                    }
                }
            }
        }
コード例 #4
0
ファイル: NewsService.cs プロジェクト: NikolayXHD/Mtgdb
        [UsedImplicitly]         // by ninject
        public NewsService(AppSourceConfig appSourceConfig)
        {
            _appSourceConfig = appSourceConfig;
            FsPath newsDir = AppDir.Update.Join("notifications");

            _newsArchive = newsDir.Join("archive.zip");

            _unzippedNewsDir = newsDir.Join("archive");
            _unreadNewsDir   = newsDir.Join("new");
            _readNewsDir     = newsDir.Join("read");

            _unzippedNewsDir.CreateDirectory();
            _unreadNewsDir.CreateDirectory();
            _readNewsDir.CreateDirectory();
        }
コード例 #5
0
 public IndexVersion(FsPath root, string indexVersion)
 {
     IndexDirectory       = root.Join(indexVersion);
     _root                = root;
     _indexVersion        = indexVersion;
     _completionLabelFile = IndexDirectory.Join("indexing.done");
 }
コード例 #6
0
        public void ZipImages(string setCodesStr, bool small, bool zoom, bool nonToken, bool token)
        {
            var setCodes = setCodesStr?.Split(',').ToHashSet(StringComparer.OrdinalIgnoreCase);

            foreach (FsPath qualityDir in getQualities(small, zoom))
            {
                foreach ((_, _, FsPath tokenSuffix) in getIsToken(nonToken, token))
                {
                    FsPath compressedRoot = TargetDir.Join(qualityDir).Concat(tokenSuffix).Concat(ZipDirSuffix);
                    compressedRoot.CreateDirectory();

                    FsPath sourceRoot = TargetDir.Join(qualityDir).Concat(tokenSuffix);
                    foreach (var subdir in sourceRoot.EnumerateDirectories())
                    {
                        string subdirRelative = subdir.Basename();
                        if (setCodes?.Contains(subdirRelative) == false)
                        {
                            continue;
                        }

                        var targetFile = compressedRoot.Join(subdirRelative).Concat(SevenZipExtension);
                        if (targetFile.IsFile())
                        {
                            targetFile.DeleteFile();
                        }

                        new SevenZip(false).Compress(subdir, targetFile)
                        .Should().BeTrue();
                    }
                }
            }
        }
コード例 #7
0
        public static void WriteExistingSignatures(ImageDownloadProgress progress, IEnumerable <FileSignature> signatures = null)
        {
            FsPath targetSubdirectory     = progress.TargetSubdirectory;
            FsPath existingSignaturesFile = targetSubdirectory.Join(Signer.SignaturesFile);

            signatures ??= Signer.CreateSignatures(targetSubdirectory);
            Signer.WriteToFile(existingSignaturesFile, signatures);
        }
コード例 #8
0
        // ReSharper restore StringLiteralTypo
        public void Resample()
        {
            FsPath file      = DevPaths.XlhqDir.Join("XLN - Ixalan", "300DPI Cards", "Adanto, the First Fort.xlhq.jpg");
            string name      = file.Basename(extension: false);
            FsPath targetDir = DevPaths.DataDrive.Join("temp", "img");

            using (var image = new MagickImage(file.Value))
            {
                image.Resize(new Percentage(50));
                image.Write(targetDir.Join(name).Concat("resized.jpg").Value);
            }

            using (var image = new MagickImage(file.Value))
            {
                image.Scale(new Percentage(50));
                image.Write(targetDir.Join(name).Concat("scaled.jpg").Value);
            }
        }
コード例 #9
0
ファイル: Installer.cs プロジェクト: NikolayXHD/Mtgdb
        private void updateApplicationShortcut(FsPath shortcutLocation)
        {
            FsPath shortcutPath = shortcutLocation.Join(ShortcutFileName);

            if (shortcutPath.IsFile())
            {
                CreateApplicationShortcut(shortcutLocation);
            }
        }
コード例 #10
0
ファイル: ImageExport.cs プロジェクト: NikolayXHD/Mtgdb
        private static FsPath getTargetPath(ImageFile imageFile, FsPath subdir)
        {
            var fileName = imageFile.FullPath.Basename();

            while (true)
            {
                if (removeExtension(ref fileName, ".jpg"))
                {
                    continue;
                }

                if (removeExtension(ref fileName, ".png"))
                {
                    continue;
                }

                if (removeExtension(ref fileName, ".xlhq"))
                {
                    continue;
                }

                // ReSharper disable once StringLiteralTypo
                if (removeExtension(ref fileName, ".xhlq"))
                {
                    continue;
                }

                if (removeExtension(ref fileName, ".full"))
                {
                    continue;
                }

                break;
            }

            string targetFileName;

            if (imageFile.FullPath.Value.EndsWith(".jpg", Str.Comparison))
            {
                targetFileName = fileName + ".jpg";
            }
            else if (imageFile.FullPath.Value.EndsWith(".png", Str.Comparison))
            {
                targetFileName = fileName + ".png";
            }
            else
            {
                throw new NotSupportedException("only .png .jpg extensions are supported");
            }

            var targetFullPath = subdir.Join(targetFileName);

            return(targetFullPath);
        }
コード例 #11
0
        public async Task <bool> DownloadAndExtract(string remotePath, FsPath targetDirectory, FsPath fileName, CancellationToken token)
        {
            if (!Str.Equals(".7z", fileName.Extension()))
            {
                throw new ArgumentException();
            }

            FsPath archiveFileName = targetDirectory.Join(fileName);

            if (archiveFileName.IsFile())
            {
                try
                {
                    archiveFileName.DeleteFile();
                }
                catch (Exception ex)
                {
                    lock (_syncOutput)
                        Console.WriteLine($"Failed to remove {archiveFileName}: {ex.Message}");
                    return(false);
                }
            }

            bool downloaded = await TryDownloadFile(remotePath, archiveFileName, token);

            if (!downloaded)
            {
                return(false);
            }

            if (!archiveFileName.IsFile())
            {
                lock (_syncOutput)
                    Console.WriteLine($"Failed to download {archiveFileName} from {remotePath}");
                return(false);
            }

            var sevenZip = new SevenZip(silent: true);

            sevenZip.Extract(archiveFileName, targetDirectory, Enumerable.Empty <FsPath>());

            try
            {
                archiveFileName.DeleteFile();
            }
            catch (Exception ex)
            {
                lock (_syncOutput)
                    Console.WriteLine($"Failed to remove {archiveFileName}: {ex.Message}");
            }

            return(true);
        }
コード例 #12
0
        private void convertToJpg(FsPath sourceImage, FsPath targetDir, bool isZoomDir)
        {
            FsPath targetImage = targetDir.Join(sourceImage.Basename(extension: false)).Concat(".jpg");

            if (_keepExisting && targetImage.IsFile() && (isZoomed(targetImage) || !isZoomDir))
            {
                return;
            }

            using var original = new Bitmap(sourceImage.Value);
            new BmpAlphaToBackgroundColorTransformation(original, Color.White)
            .Execute();

            original.Save(targetImage.Value, _jpegCodec, _jpegEncoderParams);
        }
コード例 #13
0
        public void MigrateHistoryFiles()
        {
            FsPath firstFormDirectory = AppDir.History.Join(0.ToString());

            if (firstFormDirectory.IsDirectory())
            {
                return;
            }

            firstFormDirectory.CreateDirectory();

            foreach (FsPath file in AppDir.History.EnumerateFiles())
            {
                file.CopyFileTo(firstFormDirectory.Join(file.Basename()));
            }
        }
コード例 #14
0
        public async Task DownloadGathererImages(string setCodesStr, bool nonToken, bool token)
        {
            var clients = new List <ImageDownloaderBase>(2)
            {
                // new GathererClient(),
                new ScryfallClient(),
            };

            var setCodes = setCodesStr?.Split(',').ToHashSet(StringComparer.OrdinalIgnoreCase);
            var repo     = new CardRepository(new CardFormatter(), () => null)
            {
                FilterSetCode = setCode => setCodes?.Contains(setCode) != false,
            };

            repo.LoadFile();
            repo.Load();

            foreach (Set set in repo.SetsByCode.Values)
            {
                if (setCodes?.Contains(set.Code) == false)
                {
                    continue;
                }

                foreach ((bool isToken, FsPath typeSubdir, _) in getIsToken(nonToken, token))
                {
                    var cards = set.List(isToken);
                    if (cards.Count == 0)
                    {
                        continue;
                    }

                    var missingCardsByClient = clients.ToDictionary(_ => _, _ => 0);

                    FsPath setSubdir        = new FsPath(set.Code + (Str.Equals(set.Code, "con") ? " escape" : string.Empty));
                    FsPath downloadRootDir  = DevPaths.MtgContentDir.Join(_createZoom ? OriginalSubdir : PreProcessedSubdir);
                    FsPath rootDirZoom      = DevPaths.MtgContentDir.Join(PreProcessedSubdir);
                    FsPath setDirectory     = downloadRootDir.Join(typeSubdir, setSubdir);
                    FsPath setDirectoryZoom = rootDirZoom.Join(typeSubdir, setSubdir);
                    FsPath setDirectoryPng  = setDirectory.Concat(".png");

                    bool dirExisted = setDirectoryPng.IsDirectory();
                    if (!dirExisted)
                    {
                        setDirectoryPng.CreateDirectory();
                    }

                    foreach (var card in cards)
                    {
                        FsPath targetFile        = setDirectoryPng.Join(card.ImageName + ".png");
                        FsPath processedFile     = setDirectory.Join(card.ImageName + ".jpg");
                        FsPath processedFileZoom = setDirectoryZoom.Join(card.ImageName + ".jpg");
                        if (targetFile.IsFile() || processedFile.IsFile() && processedFileZoom.IsFile())
                        {
                            continue;
                        }

                        if (targetFile.Basename(extension: false).EndsWith("1"))
                        {
                            var unnumbered = targetFile.WithName(_ => _.Replace("1.png", ".png"));
                            if (unnumbered.IsFile())
                            {
                                unnumbered.MoveFileTo(targetFile);
                                continue;
                            }
                        }

                        foreach (ImageDownloaderBase client in clients)
                        {
                            int attempts = 5;

                            for (int i = 0; i < attempts; i++)
                            {
                                var cancellation = new CancellationTokenSource();
                                var time         = DateTime.UtcNow;

                                var downloadTask = client.DownloadCardImage(card, targetFile, cancellation.Token);
                                var waitTask     = Task.Delay(TimeSpan.FromSeconds(5), cancellation.Token);

                                await Task.WhenAny(downloadTask, waitTask);

                                cancellation.Cancel();

                                var elapsed = DateTime.UtcNow - time;
                                var delta   = TimeSpan.FromSeconds(0.5) - elapsed;
                                if (delta.TotalSeconds > 0)
                                {
                                    await Task.Delay(delta);
                                }

                                if (targetFile.IsFile())
                                {
                                    break;
                                }
                            }

                            if (targetFile.IsFile())
                            {
                                break;
                            }

                            missingCardsByClient[client]++;
                        }
                    }

                    if (!dirExisted && !setDirectoryPng.EnumerateFiles().Any())
                    {
                        setDirectoryPng.DeleteDirectory();
                    }
                }
            }
        }
コード例 #15
0
        public void PreProcessImages(string setCodesStr, bool nonToken, bool token)
        {
            setupImageConversion();

            FsPath smallDir    = DevPaths.GathererOriginalDir;
            FsPath zoomDir     = DevPaths.GathererPreprocessedDir;
            FsPath smallDirBak = BakDir.Join(smallDir.Basename());
            FsPath zoomDirBak  = BakDir.Join(zoomDir.Basename());

            var setCodes = setCodesStr?.Split(',').ToHashSet(StringComparer.OrdinalIgnoreCase);

            IEnumerable <FsPath> getSetSubdirs(FsPath typeSubdir)
            {
                FsPath typeDir = smallDir.Join(typeSubdir);

                return(typeDir
                       .EnumerateDirectories(_fromPng ? "*.png" : "*", SearchOption.TopDirectoryOnly)
                       .Select(_ => new FsPath(
                                   Regex.Replace(
                                       _.Basename(),
                                       @"\.png$",
                                       string.Empty))));
            }

            foreach ((_, FsPath typeSubdir, _) in getIsToken(nonToken, token))
            {
                foreach (FsPath setSubdir in getSetSubdirs(typeSubdir))
                {
                    if (setCodes?.Contains(setSubdir.Value) == false)
                    {
                        continue;
                    }

                    FsPath smallJpgDir = smallDir.Join(typeSubdir, setSubdir);
                    FsPath zoomJpgDir  = zoomDir.Join(typeSubdir, setSubdir);

                    if (!_fromPng)
                    {
                        if (!_createZoom)
                        {
                            return;
                        }

                        zoomJpgDir.CreateDirectory();
                        foreach (FsPath smallImg in smallJpgDir.EnumerateFiles())
                        {
                            FsPath zoomImg = smallImg.ChangeDirectory(smallDir, zoomDir);
                            if (_keepExisting && zoomImg.IsFile() && isZoomed(zoomImg))
                            {
                                continue;
                            }

                            scale(smallImg, zoomImg);
                        }
                    }
                    else
                    {
                        FsPath setPngSubdir   = setSubdir.Concat(".png");
                        FsPath smallPngDir    = smallDir.Join(typeSubdir, setPngSubdir);
                        FsPath smallPngDirBak = smallDirBak.Join(typeSubdir, setPngSubdir);
                        FsPath zoomPngDir     = zoomDir.Join(typeSubdir, setPngSubdir);
                        FsPath zoomPngDirBak  = zoomDirBak.Join(typeSubdir, setPngSubdir);

                        var dirs = new List <(FsPath pngDir, FsPath pngDirBak, FsPath jpgDir, bool isZoom)>();
                        if (_createZoom)
                        {
                            zoomPngDir.CreateDirectory();
                            foreach (FsPath smallImg in smallPngDir.EnumerateFiles())
                            {
                                FsPath zoomImg      = smallImg.ChangeDirectory(smallDir, zoomDir);
                                FsPath convertedImg = zoomImg.ChangeDirectory(zoomPngDir, zoomJpgDir)
                                                      .WithName(_ => _.Replace(".png", ".jpg"));

                                if (_keepExisting && (
                                        zoomImg.IsFile() && isZoomed(zoomImg) ||
                                        convertedImg.IsFile() && isZoomed(convertedImg)))
                                {
                                    continue;
                                }

                                scale(smallImg, zoomImg);
                            }

                            dirs.Add((pngDir: zoomPngDir, pngDirBak: zoomPngDirBak, jpgDir: zoomJpgDir, isZoom: true));
                        }

                        dirs.Add((pngDir: smallPngDir, pngDirBak: smallPngDirBak, jpgDir: smallJpgDir, isZoom: false));

                        foreach ((FsPath pngDir, FsPath pngDirBak, FsPath jpgDir, bool isZoom) in dirs)
                        {
                            jpgDir.CreateDirectory();

                            var pngImages = pngDir.EnumerateFiles();
                            foreach (FsPath sourceImage in pngImages)
                            {
                                convertToJpg(sourceImage, jpgDir, isZoom);
                            }

                            moveDirectoryToBackup(pngDir, pngDirBak);
                        }
                    }
                }
            }
        }
コード例 #16
0
 private static FsPath getSignaturesDir(QualityGroupConfig qualityGroup) =>
 _updateImgDir.Join(qualityGroup.Name);
コード例 #17
0
        private static (FsPath Directory, FsPath File) getSignaturesFile(QualityGroupConfig qualityGroup)
        {
            FsPath dir = getSignaturesDir(qualityGroup);

            return(Directory : dir, File : dir.Join(Signer.SignaturesFile));
        }
コード例 #18
0
ファイル: ImageExport.cs プロジェクト: NikolayXHD/Mtgdb
        private void export(
            FsPath directory,
            string setCodesStr,
            ISet <FsPath> exportedSmall,
            ISet <FsPath> exportedZoomed,
            bool small,
            bool zoomed,
            FsPath smallSubdir,
            FsPath zoomedSubdir,
            bool matchingSet,
            bool forceRemoveCorner,
            bool token)
        {
            var setCodes = setCodesStr?.Split(',').ToHashSet(StringComparer.OrdinalIgnoreCase);

            foreach ((string setCode, Set set) in _cardRepo.SetsByCode)
            {
                Console.WriteLine(setCode);

                if (setCodes?.Contains(setCode) == false)
                {
                    continue;
                }

                FsPath smallSetSubdir  = FsPath.None;
                FsPath zoomedSetSubdir = FsPath.None;

                if (small)
                {
                    if (smallSubdir.HasValue())
                    {
                        smallSetSubdir = directory.Join(smallSubdir).Join(setCode);
                    }
                    else
                    {
                        smallSetSubdir = directory.Join(setCode);
                    }

                    smallSetSubdir = ensureSetSubdirectory(smallSetSubdir);
                }

                if (zoomed)
                {
                    if (zoomedSubdir.HasValue())
                    {
                        zoomedSetSubdir = directory.Join(zoomedSubdir).Join(setCode);
                    }
                    else
                    {
                        zoomedSetSubdir = directory.Join(setCode);
                    }

                    zoomedSetSubdir = ensureSetSubdirectory(zoomedSetSubdir);
                }

                foreach (var card in set.Cards)
                {
                    if (card.IsSingleSide() && card.Faces.Main != card)
                    {
                        continue;
                    }

                    if (card.IsToken != token)
                    {
                        continue;
                    }

                    Bitmap     original   = null;
                    ImageModel modelSmall = null;

                    if (small)
                    {
                        modelSmall = _imageRepo.GetSmallImage(card, _cardRepo.GetReleaseDateSimilarity);

                        if (modelSmall != null &&
                            Str.Equals(card.SetCode, modelSmall.ImageFile.SetCode) == matchingSet &&
                            exportedSmall.Add(modelSmall.ImageFile.FullPath))
                        {
                            FsPath smallPath = getTargetPath(modelSmall.ImageFile, smallSetSubdir);

                            if (!smallPath.IsFile() || card.Faces.Count > 1)
                            {
                                original = ImageLoader.Open(modelSmall);
                                addFile(original, modelSmall.ImageFile, smallPath, small: true, forceRemoveCorner);
                            }
                        }
                    }

                    if (zoomed)
                    {
                        var modelZoom = _imageRepo.GetImagePrint(card, _cardRepo.GetReleaseDateSimilarity);

                        if (modelZoom != null &&
                            Str.Equals(card.SetCode, modelZoom.ImageFile.SetCode) == matchingSet &&
                            exportedZoomed.Add(modelZoom.ImageFile.FullPath))
                        {
                            FsPath zoomedPath = getTargetPath(modelZoom.ImageFile, zoomedSetSubdir);

                            if (!zoomedPath.IsFile() || card.Faces.Count > 1)
                            {
                                if (original == null || modelSmall.ImageFile.FullPath != modelZoom.ImageFile.FullPath)
                                {
                                    original?.Dispose();
                                    original = ImageLoader.Open(modelZoom);
                                }

                                addFile(original, modelZoom.ImageFile, zoomedPath, small: false, forceRemoveCorner);
                            }
                        }
                    }

                    original?.Dispose();
                }

                smallSetSubdir.DeleteEmptyDirectory();
                zoomedSetSubdir.DeleteEmptyDirectory();
            }
        }
コード例 #19
0
ファイル: ImageDownloader.cs プロジェクト: NikolayXHD/Mtgdb
        private static bool isAlreadyDownloaded(ImageDownloadProgress progress)
        {
            FsPath targetSubdirectory = progress.TargetSubdirectory;

            targetSubdirectory.CreateDirectory();

            if (progress.FilesOnline == null)
            {
                return(false);
            }

            bool alreadyDownloaded = true;

            var existingFiles = new HashSet <FsPath>(
                targetSubdirectory.EnumerateFiles("*", SearchOption.AllDirectories));

            var existingSignatures = new Dictionary <FsPath, FileSignature>();

            foreach (var fileOnline in progress.FilesOnline.Values)
            {
                FsPath filePath = targetSubdirectory.Join(fileOnline.Path);
                if (!existingFiles.Contains(filePath))
                {
                    alreadyDownloaded = false;
                    continue;
                }

                FileSignature tempQualifier     = Signer.CreateSignature(filePath, useAbsolutePath: true);
                var           existingSignature =
                    progress.FilesCorrupted.TryGet(fileOnline.Path) ??
                    progress.FilesDownloaded.TryGet(fileOnline.Path) ??
                    new FileSignature
                {
                    Path    = tempQualifier.Path.RelativeTo(targetSubdirectory).Intern(true),
                    Md5Hash = tempQualifier.Md5Hash
                };

                if (existingSignature.Md5Hash != fileOnline.Md5Hash)
                {
                    alreadyDownloaded = false;
                    Console.WriteLine("Deleting modified or corrupted file {0}", filePath);

                    lock (ImageLoader.SyncIo)
                    {
                        try
                        {
                            filePath.DeleteFile();
                        }
                        catch (IOException ex)
                        {
                            Console.WriteLine($"Failed to remove {filePath}. {ex.Message}");
                        }
                    }
                }
                else
                {
                    existingSignatures.Add(existingSignature.Path, existingSignature);
                }
            }

            foreach (FsPath file in existingFiles)
            {
                var relativePath = file.RelativeTo(targetSubdirectory);
                if (!progress.FilesOnline.ContainsKey(relativePath) && relativePath != Signer.SignaturesFile)
                {
                    Console.WriteLine("Deleting {0}", file);
                    file.DeleteFile();
                }
            }

            if (alreadyDownloaded)
            {
                ImageDownloadProgressReader.WriteExistingSignatures(progress, existingSignatures.Values);
            }

            return(alreadyDownloaded);
        }