mirror of
https://github.com/riegera2412/UVtools.git
synced 2026-07-08 17:42:31 +02:00
22897ea389
- **Benchmark:**
- (Add) PNG, GZip, Deflate and LZ4 compress tests
- (Change) Test against a known image instead of random noise
- (Change) Single-thread tests from 100 to 200 and multi-thread tests from 1000 to 5000
- (Improvement) Same image instance is shared between tests instead of create new per test
- (Fix) Encode typo
- **Core:**
- (Add) Layer compression method: Allow to choose the compression method for layer image
- **PNG:** Compression=High Speed=Slow (Use with low RAM)
- **GZip:** Compression=Medium Speed=Medium (Optimal)
- **Deflate:** Compression=Medium Speed=Medium (Optimal)
- **LZ4:** Compression=Low Speed=Fast (Use with high RAM)
- (Improvement) Better handling on cancel operations and more immediate response
- (Fix) Extract: Zip Slip Vulnerability (CWE-22)
- **File formats:**
- (Improvement) Better handling of encode/decoding layers from zip files
- (Fix) ZCode: Canceling the file load can trigger an error
- (Fix) VDA: Unable to open vda zip files
- **Tools:**
- (Improvement) Allow operations to be aware of ROI and Masks before execution (#436)
- (Improvement) Scripting: Allow save and load profiles (#436)
- (Fix) Adjust layer height: When using the Offset type the last layer in the range was not taken in account (#435)
- **UI:**
- (Improvement) Allow layer zoom levels of 0.1x and 64x but constrain minimum zoom to the level of image fit
- (Improvement) Update change log now shows with markdown style and more readable
- (Fix) Windows MSI upgrade to this version (#432)
- (Fix) Auto-updater for Mac ARM, was downloading x64 instead
426 lines
16 KiB
C#
426 lines
16 KiB
C#
/*
|
|
* GNU AFFERO GENERAL PUBLIC LICENSE
|
|
* Version 3, 19 November 2007
|
|
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
|
* Everyone is permitted to copy and distribute verbatim copies
|
|
* of this license document, but changing it is not allowed.
|
|
*/
|
|
|
|
using System;
|
|
using UVtools.Core.Scripting;
|
|
using System.IO;
|
|
using System.Collections.Generic;
|
|
using UVtools.Core.FileFormats;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
|
|
namespace UVtools.ScriptSample;
|
|
|
|
/// <summary>
|
|
/// Performs a black inset around objects
|
|
/// </summary>
|
|
public class ScriptCloneSettings : ScriptGlobals
|
|
{
|
|
readonly ScriptCheckBoxInput Recursive = new()
|
|
{
|
|
Label = "Recursive",
|
|
ToolTip = "If unchecked, only files in the same folder are modified; if checked, files in all sub-folders are modified too",
|
|
Value = true
|
|
};
|
|
|
|
readonly ScriptCheckBoxInput Report = new()
|
|
{
|
|
Label = "Generate report file",
|
|
ToolTip = "Optionally generate a report file in the same directory as the open file",
|
|
Value = true
|
|
};
|
|
|
|
readonly ScriptCheckBoxInput OpenReport = new()
|
|
{
|
|
Label = "Open report file",
|
|
ToolTip = "Optionally open the result file when completed",
|
|
Value = true
|
|
};
|
|
|
|
readonly ScriptOpenFolderDialogInput FolderPath = new()
|
|
{
|
|
Label = "Folder path",
|
|
ToolTip = "The folder path to process",
|
|
};
|
|
|
|
private enum ResultStatus
|
|
{
|
|
Exception,
|
|
UnknownFileType,
|
|
DifferentMachineTypes,
|
|
Success,
|
|
Unchanged,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set configurations here, this function trigger just after load a script
|
|
/// </summary>
|
|
public void ScriptInit()
|
|
{
|
|
Script.Name = "Clone Settings";
|
|
Script.Description = "Copies the print settings from the current file to all files in the same directory and, optionally, recursively below it";
|
|
Script.Author = "Gina Venolia";
|
|
Script.Version = new Version(0, 2);
|
|
Script.UserInputs.Add(Recursive);
|
|
Script.UserInputs.Add(Report);
|
|
Script.UserInputs.Add(OpenReport);
|
|
Script.UserInputs.Add(FolderPath);
|
|
FolderPath.Value = SlicerFile.DirectoryPath;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate user inputs here, this function trigger when user click on execute
|
|
/// </summary>
|
|
/// <returns>A error message, empty or null if validation passes.</returns>
|
|
public string? ScriptValidate()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execute the script, this function trigger when when user click on execute and validation passes
|
|
/// </summary>
|
|
/// <returns>True if executes successfully to the end, otherwise false.</returns>
|
|
public bool ScriptExecute()
|
|
{
|
|
Progress.CanCancel = true;
|
|
|
|
// Gather the list of directories to operate on
|
|
Progress.Reset("Processing files...");
|
|
|
|
var directoryPaths = new List<string> {Path.GetDirectoryName(SlicerFile.FileFullPath)!};
|
|
|
|
if (Recursive.Value)
|
|
{
|
|
for (var i = 0; i < directoryPaths.Count; i++)
|
|
{
|
|
directoryPaths.AddRange(Directory.EnumerateDirectories(directoryPaths[i]));
|
|
}
|
|
}
|
|
|
|
// Gather the list of files to operate on
|
|
var filePaths = new List<string>();
|
|
foreach (var directoryPath in directoryPaths)
|
|
{
|
|
filePaths.AddRange(Directory.EnumerateFiles(directoryPath));
|
|
}
|
|
|
|
// Except for the file we started with
|
|
var normalizedFilePath = Path.GetFullPath(SlicerFile.FileFullPath!);
|
|
filePaths.RemoveAll(x => Path.GetFullPath(x) == normalizedFilePath);
|
|
|
|
// Process the files
|
|
Progress.Reset("Processing files...", (uint)filePaths.Count, 0);
|
|
|
|
var results = new List<Tuple<ResultStatus, string, List<string>?>>();
|
|
foreach (var filePath in filePaths)
|
|
{
|
|
Progress.ThrowIfCancellationRequested();
|
|
|
|
Tuple<ResultStatus, string, List<string>?> result;
|
|
try
|
|
{
|
|
result = ProcessFile(filePath);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
var deets = new List<string>
|
|
{
|
|
ex.GetType().Name,
|
|
ex.Message
|
|
};
|
|
result = new Tuple<ResultStatus, string, List<string>?>(ResultStatus.Exception, filePath, deets);
|
|
}
|
|
|
|
results.Add(result);
|
|
|
|
Progress++;
|
|
}
|
|
|
|
// Generate the report
|
|
if (Report.Value)
|
|
{
|
|
var reportDirectory = Path.GetDirectoryName(SlicerFile.FileFullPath);
|
|
var reportFilePath = Path.Combine(reportDirectory!, "CloneSettingsReport.txt");
|
|
using (var writer = new StreamWriter(reportFilePath))
|
|
{
|
|
writer.WriteLine(Script.Name + " " + Script.Version.ToString() + " by " + Script.Author);
|
|
writer.WriteLine("Source file: " + SlicerFile.FileFullPath);
|
|
writer.WriteLine(DateTime.Now.ToLongDateString() + ", " + DateTime.Now.ToLongTimeString());
|
|
writer.WriteLine("Recursive: " + Recursive.Value.ToString());
|
|
writer.WriteLine("Directories: " + directoryPaths.Count);
|
|
writer.WriteLine("Files: " + filePaths.Count);
|
|
writer.WriteLine();
|
|
|
|
foreach (var statusGroup in results.GroupBy(x => x.Item1))
|
|
{
|
|
writer.WriteLine(statusGroup.Key.ToString());
|
|
foreach (var result in statusGroup.OrderBy(x => x.Item2))
|
|
{
|
|
writer.WriteLine("\t" + result.Item2);
|
|
if (result.Item3 is not null)
|
|
{
|
|
foreach (var detail in result.Item3)
|
|
{
|
|
writer.WriteLine("\t\t" + detail);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OpenReport.Value)
|
|
{
|
|
var startInfo = new ProcessStartInfo(reportFilePath)
|
|
{
|
|
UseShellExecute = true
|
|
};
|
|
Process.Start(startInfo);
|
|
}
|
|
}
|
|
|
|
// return true if not cancelled by user
|
|
return true;
|
|
}
|
|
|
|
private Tuple<ResultStatus, string, List<string>?> ProcessFile(string filePath)
|
|
{
|
|
// Determine the type of file
|
|
var file = FileFormat.FindByExtensionOrFilePath(filePath, true);
|
|
if (file is null)
|
|
{
|
|
return new Tuple<ResultStatus, string, List<string>?>(ResultStatus.UnknownFileType, filePath, null);
|
|
}
|
|
|
|
// Load the file
|
|
file.Decode(filePath);
|
|
if (string.Compare(file.MachineName, SlicerFile.MachineName, true) != 0)
|
|
{
|
|
var deets = new List<string>
|
|
{
|
|
"Source machine type: " + SlicerFile.MachineName,
|
|
"Destination machine type: " + file.MachineName
|
|
};
|
|
return new Tuple<ResultStatus, string, List<string>?>(ResultStatus.DifferentMachineTypes, filePath, deets);
|
|
}
|
|
|
|
// TODO: Validate that some parameters are the same in both files?
|
|
|
|
// Change the parameters
|
|
var changed = false;
|
|
var details = new List<string>();
|
|
|
|
if (file.CanUseBottomLayerCount && file.BottomLayerCount != SlicerFile.BottomLayerCount)
|
|
{
|
|
details.Add($"Bottom Layer Count: {file.BottomLayerCount} to {SlicerFile.BottomLayerCount}");
|
|
file.BottomLayerCount = SlicerFile.BottomLayerCount;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomLightOffDelay && file.BottomLightOffDelay != SlicerFile.BottomLightOffDelay)
|
|
{
|
|
details.Add($"Bottom Light Off Delay: {file.BottomLightOffDelay} to {SlicerFile.BottomLightOffDelay}");
|
|
file.BottomLightOffDelay = SlicerFile.BottomLightOffDelay;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseLightOffDelay && file.LightOffDelay != SlicerFile.LightOffDelay)
|
|
{
|
|
details.Add($"Light Off Delay: {file.LightOffDelay} to {SlicerFile.LightOffDelay}");
|
|
file.LightOffDelay = SlicerFile.LightOffDelay;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomWaitTimeBeforeCure && file.BottomWaitTimeBeforeCure != SlicerFile.BottomWaitTimeBeforeCure)
|
|
{
|
|
details.Add($"Bottom Wait Time Before Cure: {file.BottomWaitTimeBeforeCure} to {SlicerFile.BottomWaitTimeBeforeCure}");
|
|
file.BottomWaitTimeBeforeCure = SlicerFile.BottomWaitTimeBeforeCure;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseWaitTimeBeforeCure && file.WaitTimeBeforeCure != SlicerFile.WaitTimeBeforeCure)
|
|
{
|
|
details.Add($"Wait Time Before Cure: {file.WaitTimeBeforeCure} to {SlicerFile.WaitTimeBeforeCure}");
|
|
file.WaitTimeBeforeCure = SlicerFile.WaitTimeBeforeCure;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomExposureTime && file.BottomExposureTime != SlicerFile.BottomExposureTime)
|
|
{
|
|
details.Add($"Bottom Exposure Time: {file.BottomExposureTime} to {SlicerFile.BottomExposureTime}");
|
|
file.BottomExposureTime = SlicerFile.BottomExposureTime;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseExposureTime && file.ExposureTime != SlicerFile.ExposureTime)
|
|
{
|
|
details.Add($"Exposure Time: {file.ExposureTime} to {SlicerFile.ExposureTime}");
|
|
file.ExposureTime = SlicerFile.ExposureTime;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomWaitTimeAfterCure && file.BottomWaitTimeAfterCure != SlicerFile.BottomWaitTimeAfterCure)
|
|
{
|
|
details.Add($"Bottom Wait Time After Cure: {file.BottomWaitTimeAfterCure} to {SlicerFile.BottomWaitTimeAfterCure}");
|
|
file.BottomWaitTimeAfterCure = SlicerFile.BottomWaitTimeAfterCure;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseWaitTimeAfterCure && file.WaitTimeAfterCure != SlicerFile.WaitTimeAfterCure)
|
|
{
|
|
details.Add($"Wait Time After Cure: {file.WaitTimeAfterCure} to {SlicerFile.WaitTimeAfterCure}");
|
|
file.WaitTimeAfterCure = SlicerFile.WaitTimeAfterCure;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomLiftHeight && file.BottomLiftHeight != SlicerFile.BottomLiftHeight)
|
|
{
|
|
details.Add($"Bottom Lift Height: {file.BottomLiftHeight} to {SlicerFile.BottomLiftHeight}");
|
|
file.BottomLiftHeight = SlicerFile.BottomLiftHeight;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomLiftSpeed && file.BottomLiftSpeed != SlicerFile.BottomLiftSpeed)
|
|
{
|
|
details.Add($"Bottom Lift Speed: {file.BottomLiftSpeed} to {SlicerFile.BottomLiftSpeed}");
|
|
file.BottomLiftSpeed = SlicerFile.BottomLiftSpeed;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseLiftHeight && file.LiftHeight != SlicerFile.LiftHeight)
|
|
{
|
|
details.Add($"Lift Height: {file.LiftHeight} to {SlicerFile.LiftHeight}");
|
|
file.LiftHeight = SlicerFile.LiftHeight;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseLiftSpeed && file.LiftSpeed != SlicerFile.LiftSpeed)
|
|
{
|
|
details.Add($"Lift Speed: {file.LiftSpeed} to {SlicerFile.LiftSpeed}");
|
|
file.LiftSpeed = SlicerFile.LiftSpeed;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomLiftHeight2 && file.BottomLiftHeight2 != SlicerFile.BottomLiftHeight2)
|
|
{
|
|
details.Add($"Bottom Lift Height 2: {file.BottomLiftHeight2} to {SlicerFile.BottomLiftHeight2}");
|
|
file.BottomLiftHeight2 = SlicerFile.BottomLiftHeight2;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomLiftSpeed2 && file.BottomLiftSpeed2 != SlicerFile.BottomLiftSpeed2)
|
|
{
|
|
details.Add($"Bottom Lift Speed2: {file.BottomLiftSpeed2} to {SlicerFile.BottomLiftSpeed2}");
|
|
file.BottomLiftSpeed2 = SlicerFile.BottomLiftSpeed2;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseLiftHeight2 && file.LiftHeight2 != SlicerFile.LiftHeight2)
|
|
{
|
|
details.Add($"Lift Height 2: {file.LiftHeight2} to {SlicerFile.LiftHeight2}");
|
|
file.LiftHeight2 = SlicerFile.LiftHeight2;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseLiftSpeed2 && file.LiftSpeed2 != SlicerFile.LiftSpeed2)
|
|
{
|
|
details.Add($"Lift Speed 2: {file.LiftSpeed2} to {SlicerFile.LiftSpeed2}");
|
|
file.LiftSpeed2 = SlicerFile.LiftSpeed2;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomWaitTimeAfterLift && file.BottomWaitTimeAfterLift != SlicerFile.BottomWaitTimeAfterLift)
|
|
{
|
|
details.Add($"Bottom Wait Time After Lift: {file.BottomWaitTimeAfterLift} to {SlicerFile.BottomWaitTimeAfterLift}");
|
|
file.BottomWaitTimeAfterLift = SlicerFile.BottomWaitTimeAfterLift;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseWaitTimeAfterLift && file.WaitTimeAfterLift != SlicerFile.WaitTimeAfterLift)
|
|
{
|
|
details.Add($"Wait Time After Lift: {file.WaitTimeAfterLift} to {SlicerFile.WaitTimeAfterLift}");
|
|
file.WaitTimeAfterLift = SlicerFile.WaitTimeAfterLift;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomRetractSpeed && file.BottomRetractSpeed != SlicerFile.BottomRetractSpeed)
|
|
{
|
|
details.Add($"Bottom Retract Speed: {file.BottomRetractSpeed} to {SlicerFile.BottomRetractSpeed}");
|
|
file.BottomRetractSpeed = SlicerFile.BottomRetractSpeed;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseRetractSpeed && file.RetractSpeed != SlicerFile.RetractSpeed)
|
|
{
|
|
details.Add($"Retract Speed: {file.RetractSpeed} to {SlicerFile.RetractSpeed}");
|
|
file.RetractSpeed = SlicerFile.RetractSpeed;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomRetractHeight2 && file.BottomRetractHeight2 != SlicerFile.BottomRetractHeight2)
|
|
{
|
|
details.Add($"Bottom Retract Height 2: {file.BottomRetractHeight2} to {SlicerFile.BottomRetractHeight2}");
|
|
file.BottomRetractHeight2 = SlicerFile.BottomRetractHeight2;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomRetractSpeed2 && file.BottomRetractSpeed2 != SlicerFile.BottomRetractSpeed2)
|
|
{
|
|
details.Add($"Bottom Retract Speed2: {file.BottomRetractSpeed2} to {SlicerFile.BottomRetractSpeed2}");
|
|
file.BottomRetractSpeed2 = SlicerFile.BottomRetractSpeed2;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseRetractHeight2 && file.RetractHeight2 != SlicerFile.RetractHeight2)
|
|
{
|
|
details.Add($"Retract Height 2: {file.RetractHeight2} to {SlicerFile.RetractHeight2}");
|
|
file.RetractHeight2 = SlicerFile.RetractHeight2;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseRetractSpeed2 && file.RetractSpeed2 != SlicerFile.RetractSpeed2)
|
|
{
|
|
details.Add($"Retract Speed 2: {file.RetractSpeed2} to {SlicerFile.RetractSpeed2}");
|
|
file.RetractSpeed2 = SlicerFile.RetractSpeed2;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomLightPWM && file.BottomLightPWM != SlicerFile.BottomLightPWM)
|
|
{
|
|
details.Add($"Bottom Light PWM: {file.BottomLightPWM} to {SlicerFile.BottomLightPWM}");
|
|
file.BottomLightPWM = SlicerFile.BottomLightPWM;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseLightPWM && file.LightPWM != SlicerFile.LightPWM)
|
|
{
|
|
details.Add($"Light PWM: {file.LightPWM} to {SlicerFile.LightPWM}");
|
|
file.LightPWM = SlicerFile.LightPWM;
|
|
changed = true;
|
|
}
|
|
|
|
// if (file.CanUseXXX && file.XXX != SlicerFile.XXX)
|
|
// {
|
|
// details.Add($"XXX: {file.XXX} to {SlicerFile.XXX}");
|
|
// file.XXX = SlicerFile.XXX;
|
|
// changed = true;
|
|
// }
|
|
|
|
// Bail out if not changed
|
|
if (!changed)
|
|
{
|
|
return new Tuple<ResultStatus, string, List<string>?>(ResultStatus.Unchanged, filePath, null);
|
|
}
|
|
|
|
// Save the changed file
|
|
file.Save();
|
|
|
|
return new Tuple<ResultStatus, string, List<string>?>(ResultStatus.Success, filePath, details);
|
|
}
|
|
|
|
} |