From e171130aeccf7ad0f8f625aef1a12d09d4154618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Concei=C3=A7=C3=A3o?= Date: Sat, 16 Sep 2023 14:02:24 +0100 Subject: [PATCH] Fixes - (Fix) CTB: Error when open file in partial model with malformed file due invalid checksum - (Improvement) Improve "extract" command to allow extract specific thumbnails or layers in a range or indexes (#754) --- CHANGELOG.md | 4 +- UVtools.Cmd/Symbols/ExtractCommand.cs | 140 ++++++++++++++++++- UVtools.Core/FileFormats/CTBEncryptedFile.cs | 9 +- UVtools.Core/FileFormats/FileFormat.cs | 5 +- 4 files changed, 147 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d31b5e4..327f137b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,17 +29,19 @@ - (Fix) Accessing `BoundingRectangleMillimeters` was not calculating the `BoundingRectangle` if necessary - (Fix) SL1: Files with thumbnails size different from original SL1 would not display under the UI - (Fix) Anycubic: Model information is not set after an auto-conversion from SL1 + - (Fix) CTB: Error when open file in partial model with malformed file due invalid checksum - (Fix) Image files: Could not find a writer for the specified extension (#755) - **UVtoolsCmd:** - (Add) "compare" command to compare two files and output the differences - (Add) "print-formats" command to print the available formats + - (Improvement) Improve "extract" command to allow extract specific thumbnails or layers in a range or indexes (#754) - **Project:** - (Change) Rename UVtools.WPF to UVtools.UI - (Change) Make UVtools.UI nullable enabled - (Remove) UVtools.GUI - (Improvement) Use CompiledBinding instead of ReflectionBinding - (Improvement) Sign the libraries and project with a strong key - - (Upgrade) .NET from 6.0.18 to 6.0.21 + - (Upgrade) .NET from 6.0.18 to 6.0.22 - (Upgrade) AvaloniaUI from 0.10.21 to 11.0.4 - (Upgrade) OpenCV from 4.7.0 to 4.8.0 - (Add) PrusaSlicer printer: Elegoo Saturn S diff --git a/UVtools.Cmd/Symbols/ExtractCommand.cs b/UVtools.Cmd/Symbols/ExtractCommand.cs index da47d5f4..594a784a 100644 --- a/UVtools.Cmd/Symbols/ExtractCommand.cs +++ b/UVtools.Cmd/Symbols/ExtractCommand.cs @@ -6,25 +6,46 @@ * of this license document, but changing it is not allowed. */ +using Emgu.CV.Flann; +using Emgu.CV; +using System; +using System.Collections.Generic; using System.CommandLine; using System.IO; +using System.Linq; +using System.Threading.Tasks; +using UVtools.Core.FileFormats; namespace UVtools.Cmd.Symbols; internal static class ExtractCommand { + + internal enum ExtractContentType + { + File, + Thumbnails, + Layers + } + internal static Command CreateCommand() { var noOverwriteOption = new Option("--no-overwrite", "If the output folder exists do not overwrite"); + var contentTypeOption = new Option(new []{ "-c", "--content" }, () => ExtractContentType.File, "Set the type of content to extract"); + var indexesOption = new Option(new []{ "-i", "--index" }, "Sets the thumbnail or layer index to extract"); + var rangeOption = new Option(new []{ "-r", "--range" }, "Sets the layer range to extract"); var command = new Command("extract", "Extract file contents to a folder") { GlobalArguments.InputFileArgument, GlobalArguments.OutputDirectoryArgument, noOverwriteOption, + contentTypeOption, + indexesOption, + rangeOption }; - command.SetHandler((inputFile, outputDirectory, noOverwrite) => + command.SetHandler((inputFile, outputDirectory, noOverwrite, contentType, indexes, range) => { var path = outputDirectory is null ? Path.Combine(inputFile.DirectoryName!, Path.GetFileNameWithoutExtension(inputFile.Name)) @@ -36,15 +57,126 @@ internal static Command CreateCommand() return; } - var slicerFile = Program.OpenInputFile(inputFile); + var slicerFile = Program.OpenInputFile(inputFile, contentType == ExtractContentType.Thumbnails ? FileFormat.FileDecodeType.Partial : FileFormat.FileDecodeType.Full); Program.ProgressBarWork($"Extracting to {Path.GetFileName(path)}", () => { - slicerFile.Extract(path, progress: Program.Progress); + if (contentType == ExtractContentType.File) + { + slicerFile.Extract(path, progress: Program.Progress); + } + else + { + var indexesList = new List(); + + if (contentType == ExtractContentType.Layers) + { + if (slicerFile.LayerCount == 0) + { + Program.WriteLineWarning("File have no valid layers to extract."); + return; + } + + if (!string.IsNullOrWhiteSpace(range)) + { + if (slicerFile.TryParseLayerIndexRange(range, out var layerIndexStart, out var layerIndexEnd)) + { + for (var layerIndex = layerIndexStart; layerIndex <= layerIndexEnd; layerIndex++) + { + indexesList.Add(layerIndex); + } + } + else + { + Program.WriteLineError($"The specified layer range '{range}' is malformed, use startindex:endindex with positive numbers"); + } + } + } + else if (contentType == ExtractContentType.Thumbnails) + { + if (slicerFile.ThumbnailsCount == 0) + { + Program.WriteLineWarning("File have no valid thumbnails to extract."); + return; + } + } + + if (indexes.Length == 0 && indexesList.Count == 0) + { + if (contentType == ExtractContentType.Layers) + { + for (uint i = 0; i < slicerFile.LayerCount; i++) + { + indexesList.Add(slicerFile.SanitizeLayerIndex(i)); + } + } + else if (contentType == ExtractContentType.Thumbnails) + { + for (uint i = 0; i < slicerFile.Thumbnails.Count; i++) + { + indexesList.Add(i); + } + } + } + else + { + if (contentType == ExtractContentType.Layers) + { + foreach (var index in indexes) + { + if (index < slicerFile.LayerCount) + { + indexesList.Add(index); + } + } + } + else if (contentType == ExtractContentType.Thumbnails) + { + foreach (var index in indexes) + { + if (index < slicerFile.ThumbnailsCount) + { + indexesList.Add(index); + } + } + } + } + + indexesList = indexesList.Distinct().OrderBy(layerIndex => layerIndex).ToList(); + if (indexesList.Count == 0) + { + Program.WriteLineWarning("No valid indexes to extract."); + return; + } + + Directory.CreateDirectory(path); + + if (contentType == ExtractContentType.Layers) + { + Parallel.ForEach(indexesList, layerIndex => + { + using var mat = slicerFile[layerIndex].LayerMat; + mat.Save(Path.Combine(path, slicerFile[layerIndex].Filename)); + }); + } + else if (contentType == ExtractContentType.Thumbnails) + { + foreach (var index in indexesList) + { + var thumbnail = slicerFile.Thumbnails[(int)index]; + if (thumbnail.IsEmpty) + { + continue; + } + + thumbnail.Save(Path.Combine(path, $"Thumbnail{index}.png")); + } + } + } }); - }, GlobalArguments.InputFileArgument, GlobalArguments.OutputDirectoryArgument, noOverwriteOption); + }, GlobalArguments.InputFileArgument, GlobalArguments.OutputDirectoryArgument, noOverwriteOption, contentTypeOption, indexesOption, rangeOption); return command; } diff --git a/UVtools.Core/FileFormats/CTBEncryptedFile.cs b/UVtools.Core/FileFormats/CTBEncryptedFile.cs index f7f668c4..f7244994 100644 --- a/UVtools.Core/FileFormats/CTBEncryptedFile.cs +++ b/UVtools.Core/FileFormats/CTBEncryptedFile.cs @@ -1199,10 +1199,13 @@ protected override void DecodeInternally(OperationProgress progress) "Please increase the portion of the plate in use and re-slice the file."); } - var hash = inputFile.ReadBytes(HASH_LENGTH); - if (!hash.SequenceEqual(encryptedHash)) + if (DecodeType == FileDecodeType.Full) { - throw new FileLoadException("The file checksum does not match, malformed file.", FileFullPath); + var hash = inputFile.ReadBytes(HASH_LENGTH); + if (!hash.SequenceEqual(encryptedHash)) + { + throw new FileLoadException("The file checksum does not match, malformed file.", FileFullPath); + } } for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index b2fa9029..2a8e644b 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -3937,7 +3937,7 @@ public void Encode(string? fileFullPath, OperationProgress? progress = null) EncodeInternally(progress); // Move temporary output file in place - File.Move(tempFile, fileFullPath, true); + if (File.Exists(tempFile)) File.Move(tempFile, fileFullPath, true); IsModified = false; RequireFullEncode = false; @@ -4332,8 +4332,7 @@ public void DecodeLayersFromZipIgnoreFilename(ZipArchive zipArchive, OperationPr /// /// /// - public virtual void Extract(string path, bool genericConfigExtract = true, bool genericLayersExtract = true, - OperationProgress? progress = null) + public virtual void Extract(string path, bool genericConfigExtract = true, bool genericLayersExtract = true, OperationProgress? progress = null) { progress ??= new OperationProgress(); progress.ItemName = OperationProgress.StatusExtracting;