mirror of
https://github.com/riegera2412/UVtools.git
synced 2026-07-08 17:42:31 +02:00
ea4f7e0400
- **UI:** - (Improvement) Layer navigation load time by parallel `Mat` to `Bitmap` conversion - (Improvement) Allow to show exceptions without the stack trace and detailed trigger action by using the `MessageExceiption` (#644) - (Improvement) Allow progress to have and display a detailed log (#644) - (Improvement) Convert format to another with multiple versions will now only show the possible versions for the extension - **Suggestion - Wait time before cure:** - (Improvement) Set the first wait time based on first valid layer mass rather than use the fixed limit - (Improvement) Set zero time to empty and dummy layers - (Improvement) When creating the dummy layer also increment the bottom layer count as the created layer count as one - **PCB Exposure:** - (Add) Excellon Drill Format (drl) to cut off holes (Implementation may lack some advanced features, please confirm the result) (#646) - (Fix) Arc (G03) with negative offsets (I-/J-) was not drawing the shape correctly - (Fix) Implement the rotation for the outline primitive (#645) - **File formats:** - (Improvement) Formats now sanitize the selected version before encode given the file extension, if version is out of range it will force the last known version - (Fix) CBDDLP: Remove a table from the file that might cause layer corruption - (Add) Operations - `AfterCompleteReport` property: Gets or sets an report to show to the user after complete the operation with success - (Improvement) Suggestion - Wait time after cure: Set zero time to empty and dummy layers - (Improvement) Slight improvement on the contour intersection check, yields better performance on resin and suction cup detection - (Improvement) Allow to trigger message boxes from operations and scripts (#644) - (Upgrade) .NET from 6.0.12 to 6.0.13
379 lines
14 KiB
C#
379 lines
14 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 System.Collections.Generic;
|
|
using System.IO;
|
|
using System.IO.Compression;
|
|
using System.Linq;
|
|
|
|
namespace UVtools.Core.Extensions;
|
|
|
|
public static class ZipArchiveExtensions
|
|
{
|
|
/// <summary>
|
|
/// Used to specify what our overwrite policy
|
|
/// is for files we are extracting.
|
|
/// </summary>
|
|
public enum Overwrite
|
|
{
|
|
Always,
|
|
IfNewer,
|
|
Never
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to identify what we will do if we are
|
|
/// trying to create a zip file and it already
|
|
/// exists.
|
|
/// </summary>
|
|
public enum ArchiveAction
|
|
{
|
|
Merge,
|
|
Replace,
|
|
Error,
|
|
Ignore
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unzips the specified file to the given folder in a safe
|
|
/// manner. This plans for missing paths and existing files
|
|
/// and handles them gracefully.
|
|
/// </summary>
|
|
/// <param name="sourceArchiveFileName">
|
|
/// The name of the zip file to be extracted
|
|
/// </param>
|
|
/// <param name="destinationDirectoryName">
|
|
/// The directory to extract the zip file to
|
|
/// </param>
|
|
/// <param name="overwriteMethod">
|
|
/// Specifies how we are going to handle an existing file.
|
|
/// The default is IfNewer.
|
|
/// </param>
|
|
public static void ImprovedExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName, Overwrite overwriteMethod = Overwrite.IfNewer)
|
|
{
|
|
//Opens the zip file up to be read
|
|
using var archive = ZipFile.OpenRead(sourceArchiveFileName);
|
|
archive.ImprovedExtractToDirectory(destinationDirectoryName, overwriteMethod);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unzips the specified file to the given folder in a safe
|
|
/// manner. This plans for missing paths and existing files
|
|
/// and handles them gracefully.
|
|
/// </summary>
|
|
/// <param name="archive">
|
|
/// The zip file to be extracted
|
|
/// </param>
|
|
/// <param name="destinationDirectoryName">
|
|
/// The directory to extract the zip file to
|
|
/// </param>
|
|
/// <param name="overwriteMethod">
|
|
/// Specifies how we are going to handle an existing file.
|
|
/// The default is IfNewer.
|
|
/// </param>
|
|
/// <returns>The number of extracted files</returns>
|
|
public static int ImprovedExtractToDirectory(this ZipArchive archive, string destinationDirectoryName, Overwrite overwriteMethod = Overwrite.IfNewer)
|
|
{
|
|
int count = 0;
|
|
//Loops through each file in the zip file
|
|
foreach (var file in archive.Entries)
|
|
{
|
|
if(!string.IsNullOrEmpty(file.ImprovedExtractToFile(destinationDirectoryName, true, overwriteMethod))) count++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Safely extracts a single file from a zip file
|
|
/// </summary>
|
|
/// <param name="entry">
|
|
/// The zip entry we are pulling the file from
|
|
/// </param>
|
|
/// <param name="destinationPath">
|
|
/// The root of where the file is going
|
|
/// </param>
|
|
/// <param name="preserveFullName">True to preserve full name and create all directories up to the file, otherwise false to extract the file just to <paramref name="destinationPath"/></param>
|
|
/// <param name="overwriteMethod">
|
|
/// Specifies how we are going to handle an existing file.
|
|
/// The default is Overwrite.IfNewer.
|
|
/// </param>
|
|
/// <returns>The extracted file path</returns>
|
|
public static string? ImprovedExtractToFile(this ZipArchiveEntry entry, string destinationPath, bool preserveFullName = true, Overwrite overwriteMethod = Overwrite.IfNewer)
|
|
{
|
|
//Gets the complete path for the destination file, including any
|
|
//relative paths that were in the zip file
|
|
var destFileName = Path.GetFullPath(Path.Combine(destinationPath, preserveFullName ? entry.FullName : entry.Name));
|
|
var fullDestDirPath = Path.GetFullPath(Path.Combine(destinationPath, (preserveFullName ? Path.GetDirectoryName(entry.FullName) : string.Empty)!) + Path.DirectorySeparatorChar);
|
|
if (!destFileName.StartsWith(fullDestDirPath)) return null; // Entry is outside the target dir
|
|
|
|
//Creates the directory (if it doesn't exist) for the new path
|
|
Directory.CreateDirectory(fullDestDirPath);
|
|
|
|
//Determines what to do with the file based upon the
|
|
//method of overwriting chosen
|
|
switch (overwriteMethod)
|
|
{
|
|
case Overwrite.Always:
|
|
//Just put the file in and overwrite anything that is found
|
|
entry.ExtractToFile(destFileName, true);
|
|
break;
|
|
case Overwrite.IfNewer:
|
|
//Checks to see if the file exists, and if so, if it should
|
|
//be overwritten
|
|
if (!File.Exists(destFileName) || File.GetLastWriteTime(destFileName) < entry.LastWriteTime)
|
|
{
|
|
//Either the file didn't exist or this file is newer, so
|
|
//we will extract it and overwrite any existing file
|
|
entry.ExtractToFile(destFileName, true);
|
|
}
|
|
break;
|
|
case Overwrite.Never:
|
|
//Put the file in if it is new but ignores the
|
|
//file if it already exists
|
|
if (!File.Exists(destFileName))
|
|
{
|
|
entry.ExtractToFile(destFileName);
|
|
}
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(overwriteMethod), overwriteMethod, null);
|
|
}
|
|
|
|
return destFileName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allows you to add files to an archive, whether the archive
|
|
/// already exists or not
|
|
/// </summary>
|
|
/// <param name="archiveFullName">
|
|
/// The name of the archive to you want to add your files to
|
|
/// </param>
|
|
/// <param name="files">
|
|
/// A set of file names that are to be added
|
|
/// </param>
|
|
/// <param name="action">
|
|
/// Specifies how we are going to handle an existing archive
|
|
/// </param>
|
|
/// <param name="fileOverwrite"></param>
|
|
/// <param name="compression">
|
|
/// Specifies what type of compression to use - defaults to Optimal
|
|
/// </param>
|
|
public static void AddToArchive(string archiveFullName,
|
|
List<string> files,
|
|
ArchiveAction action = ArchiveAction.Replace,
|
|
Overwrite fileOverwrite = Overwrite.IfNewer,
|
|
CompressionLevel compression = CompressionLevel.Optimal)
|
|
{
|
|
//Identifies the mode we will be using - the default is Create
|
|
ZipArchiveMode mode = ZipArchiveMode.Create;
|
|
|
|
//Determines if the zip file even exists
|
|
bool archiveExists = File.Exists(archiveFullName);
|
|
|
|
//Figures out what to do based upon our specified overwrite method
|
|
switch (action)
|
|
{
|
|
case ArchiveAction.Merge:
|
|
//Sets the mode to update if the file exists, otherwise
|
|
//the default of Create is fine
|
|
if (archiveExists)
|
|
{
|
|
mode = ZipArchiveMode.Update;
|
|
}
|
|
break;
|
|
case ArchiveAction.Replace:
|
|
//Deletes the file if it exists. Either way, the default
|
|
//mode of Create is fine
|
|
if (archiveExists)
|
|
{
|
|
File.Delete(archiveFullName);
|
|
}
|
|
break;
|
|
case ArchiveAction.Error:
|
|
//Throws an error if the file exists
|
|
if (archiveExists)
|
|
{
|
|
throw new IOException($"The zip file {archiveFullName} already exists.");
|
|
}
|
|
break;
|
|
case ArchiveAction.Ignore:
|
|
//Closes the method silently and does nothing
|
|
if (archiveExists)
|
|
{
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//Opens the zip file in the mode we specified
|
|
using ZipArchive zipFile = ZipFile.Open(archiveFullName, mode);
|
|
//This is a bit of a hack and should be refactored - I am
|
|
//doing a similar foreach loop for both modes, but for Create
|
|
//I am doing very little work while Update gets a lot of
|
|
//code. This also does not handle any other mode (of
|
|
//which there currently wouldn't be one since we don't
|
|
//use Read here).
|
|
if (mode == ZipArchiveMode.Create)
|
|
{
|
|
foreach (string file in files)
|
|
{
|
|
//Adds the file to the archive
|
|
zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (string file in files)
|
|
{
|
|
var fileInZip = (from f in zipFile.Entries
|
|
where f.Name == Path.GetFileName(file)
|
|
select f).FirstOrDefault();
|
|
|
|
switch (fileOverwrite)
|
|
{
|
|
case Overwrite.Always:
|
|
//Deletes the file if it is found
|
|
if (fileInZip != null)
|
|
{
|
|
fileInZip.Delete();
|
|
}
|
|
|
|
//Adds the file to the archive
|
|
zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
|
|
|
|
break;
|
|
case Overwrite.IfNewer:
|
|
//This is a bit trickier - we only delete the file if it is
|
|
//newer, but if it is newer or if the file isn't already in
|
|
//the zip file, we will write it to the zip file
|
|
if (fileInZip != null)
|
|
{
|
|
//Deletes the file only if it is older than our file.
|
|
//Note that the file will be ignored if the existing file
|
|
//in the archive is newer.
|
|
if (fileInZip.LastWriteTime < File.GetLastWriteTime(file))
|
|
{
|
|
fileInZip.Delete();
|
|
|
|
//Adds the file to the archive
|
|
zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//The file wasn't already in the zip file so add it to the archive
|
|
zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
|
|
}
|
|
break;
|
|
case Overwrite.Never:
|
|
//Don't do anything - this is a decision that you need to
|
|
//consider, however, since this will mean that no file will
|
|
//be written. You could write a second copy to the zip with
|
|
//the same name (not sure that is wise, however).
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get or put a file into archive
|
|
/// </summary>
|
|
/// <param name="input"><see cref="ZipArchive"/></param>
|
|
/// <param name="filename">Filename to create</param>
|
|
/// <returns>Created <see cref="ZipArchiveEntry"/></returns>
|
|
public static ZipArchiveEntry GetPutFile(this ZipArchive input, string filename)
|
|
{
|
|
return input.GetEntry(filename) ?? input.CreateEntry(filename);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create or update a file into archive and write content to it
|
|
/// </summary>
|
|
/// <param name="input"><see cref="ZipArchive"/></param>
|
|
/// <param name="filename">Filename to create</param>
|
|
/// <param name="content">Content to write</param>
|
|
/// <param name="mode"></param>
|
|
/// <returns>Created <see cref="ZipArchiveEntry"/></returns>
|
|
public static ZipArchiveEntry PutFileContent(this ZipArchive input, string filename, string? content, ZipArchiveMode mode)
|
|
{
|
|
ZipArchiveEntry entry;
|
|
if (mode == ZipArchiveMode.Update)
|
|
{
|
|
entry = input.GetEntry(filename) ?? input.CreateEntry(filename);
|
|
}
|
|
else
|
|
{
|
|
entry = input.CreateEntry(filename);
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(content)) return entry;
|
|
using var stream = entry.Open();
|
|
if (mode == ZipArchiveMode.Update) stream.SetLength(0);
|
|
using TextWriter tw = new StreamWriter(stream);
|
|
tw.Write(content);
|
|
return entry;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create or update a file into archive and write content to it
|
|
/// </summary>
|
|
/// <param name="input"><see cref="ZipArchive"/></param>
|
|
/// <param name="filename">Filename to create</param>
|
|
/// <param name="content">Content to write</param>
|
|
/// <param name="mode"></param>
|
|
/// <returns>Created <see cref="ZipArchiveEntry"/></returns>
|
|
public static ZipArchiveEntry PutFileContent(this ZipArchive input, string filename, byte[]? content, ZipArchiveMode mode)
|
|
{
|
|
ZipArchiveEntry entry;
|
|
if (mode == ZipArchiveMode.Update)
|
|
{
|
|
entry = input.GetEntry(filename) ?? input.CreateEntry(filename);
|
|
}
|
|
else
|
|
{
|
|
entry = input.CreateEntry(filename);
|
|
}
|
|
|
|
if (content is null) return entry;
|
|
using var stream = entry.Open();
|
|
if (mode == ZipArchiveMode.Update) stream.SetLength(0);
|
|
stream.Write(content, 0, content.Length);
|
|
return entry;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create or update a file into archive and write content to it
|
|
/// </summary>
|
|
/// <param name="input"><see cref="ZipArchive"/></param>
|
|
/// <param name="filename">Filename to create</param>
|
|
/// <param name="content">Content to write</param>
|
|
/// <param name="mode"></param>
|
|
/// <returns>Created <see cref="ZipArchiveEntry"/></returns>
|
|
public static ZipArchiveEntry PutFileContent(this ZipArchive input, string filename, Stream? content, ZipArchiveMode mode)
|
|
{
|
|
ZipArchiveEntry entry;
|
|
if (mode == ZipArchiveMode.Update)
|
|
{
|
|
entry = input.GetEntry(filename) ?? input.CreateEntry(filename);
|
|
}
|
|
else
|
|
{
|
|
entry = input.CreateEntry(filename);
|
|
}
|
|
|
|
if (content is null) return entry;
|
|
using var stream = entry.Open();
|
|
if (mode == ZipArchiveMode.Update) stream.SetLength(0);
|
|
content.CopyTo(stream);
|
|
return entry;
|
|
}
|
|
} |