Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTiago Conceição <Tiago_caza@hotmail.com>2020-05-19 18:57:04 +0300
committerTiago Conceição <Tiago_caza@hotmail.com>2020-05-19 18:57:04 +0300
commitd91986f8031ece932712c42fc9c810060b8428fb (patch)
tree66eb33c616f0ac803228489e677def0e5100cdac
parente756162d7021c1022041c66d22084ec109c0ed57 (diff)
v0.3.3v0.3.3
* (Add) PHZ file format * (Add) "Phrozen Sonic Mini" printer * (Add) Convert Chitubox files to PHZ files and otherwise * (Add) Convert Chitubox and PHZ files to ZCodex * (Add) Elapsed seconds to convertion and extract dialog * (Improvement) "Convert To" menu now only show available formats to convert to, if none menu is disabled * (Fixed) Enforce cbt encryption * (Fixed) Not implemented convertions stay processing forever
-rw-r--r--CHANGELOG.md12
-rw-r--r--PrusaSL1Reader/ChituboxFile.cs162
-rw-r--r--PrusaSL1Reader/FileFormat.cs18
-rw-r--r--PrusaSL1Reader/IFileFormat.cs5
-rw-r--r--PrusaSL1Reader/PHZFile.cs1228
-rw-r--r--PrusaSL1Reader/PrusaSL1Reader.csproj6
-rw-r--r--PrusaSL1Reader/SL1File.cs101
-rw-r--r--PrusaSL1Reader/ZCodexFile.cs2
-rw-r--r--PrusaSL1Viewer/FrmLoading.cs2
-rw-r--r--PrusaSL1Viewer/FrmMain.cs75
-rw-r--r--PrusaSL1Viewer/Properties/AssemblyInfo.cs4
-rw-r--r--PrusaSL1Viewer/PrusaSL1Viewer.csproj10
-rw-r--r--PrusaSL1Viewer/packages.config2
-rw-r--r--PrusaSlicer/printer/Phrozen Sonic Mini.ini37
14 files changed, 1591 insertions, 73 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 075ae24..aae89f4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,17 @@
# Changelog
+## ? - v0.3.3 - Beta
+
+* (Add) PHZ file format
+* (Add) "Phrozen Sonic Mini" printer
+* (Add) Convert Chitubox files to PHZ files and otherwise
+* (Add) Convert Chitubox and PHZ files to ZCodex
+* (Add) Elapsed seconds to convertion and extract dialog
+* (Improvement) "Convert To" menu now only show available formats to convert to, if none menu is disabled
+* (Fixed) Enforce cbt encryption
+* (Fixed) Not implemented convertions stay processing forever
+
+
## 11/05/2020 - v0.3.2 - Beta
* (Add) Show layer differences where daker pixels were also present on previous layer and the white pixels the difference between previous and current layer.
diff --git a/PrusaSL1Reader/ChituboxFile.cs b/PrusaSL1Reader/ChituboxFile.cs
index ecc9df0..794f2ed 100644
--- a/PrusaSL1Reader/ChituboxFile.cs
+++ b/PrusaSL1Reader/ChituboxFile.cs
@@ -11,6 +11,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
+using System.Linq;
using System.Threading.Tasks;
using BinarySerialization;
using PrusaSL1Reader.Extensions;
@@ -29,7 +30,7 @@ namespace PrusaSL1Reader
private const ushort REPEATRGB15MASK = 0x20;
private const byte RLE8EncodingLimit = 125;
- private const ushort RLE16EncodingLimit = 0x1000;
+ private const ushort RLE16EncodingLimit = 0xFFF;
#endregion
#region Sub Classes
@@ -438,10 +439,7 @@ namespace PrusaSL1Reader
public List<byte> Read(List<byte> input)
{
List<byte> data = new List<byte>(input.Count);
- for (int i = 0; i < input.Count; i++)
- {
- data[i] = (byte)(input[i] ^ Next());
- }
+ data.AddRange(input.Select(t => (byte) (t ^ Next())));
return data;
}
@@ -482,6 +480,12 @@ namespace PrusaSL1Reader
new FileExtension("photon", "Photon Files"),
};
+ public override Type[] ConvertToFormats { get; } =
+ {
+ typeof(PHZFile),
+ typeof(ZCodexFile),
+ };
+
public override PrintParameterModifier[] PrintParameterModifiers { get; } =
{
PrintParameterModifier.InitialLayerCount,
@@ -564,11 +568,19 @@ namespace PrusaSL1Reader
HeaderSettings.Magic = fileFullPath.EndsWith(".ctb") ? MAGIC_CBT : MAGIC_CBDDLP;
HeaderSettings.PrintParametersSize = (uint)Helpers.Serializer.SizeOf(PrintParametersSettings);
+
if (IsCbtFile)
{
PrintParametersSettings.Padding4 = 0x1234;
- SlicerInfoSettings.EncryptionMode = 0xf;
+ //SlicerInfoSettings.EncryptionMode = 0xf;
+ SlicerInfoSettings.EncryptionMode = 7;
SlicerInfoSettings.Unknown1 = 0x200;
+
+ if (HeaderSettings.EncryptionKey == 0)
+ {
+ Random rnd = new Random();
+ HeaderSettings.EncryptionKey = (uint)rnd.Next(short.MaxValue, int.MaxValue);
+ }
}
uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings);
@@ -854,10 +866,7 @@ namespace PrusaSL1Reader
for (int x = 0; x < image.Width; x++)
{
var grey7 = (byte)(pixelRowSpan[x].PackedValue >> 1);
- if (pixelRowSpan[x].PackedValue > 0)
- {
- }
if (grey7 == color)
{
stride++;
@@ -1389,7 +1398,140 @@ namespace PrusaSL1Reader
public override bool Convert(Type to, string fileFullPath)
{
- throw new NotImplementedException();
+ if (to == typeof(PHZFile))
+ {
+ PHZFile file = new PHZFile
+ {
+ Layers = Layers
+ };
+
+
+ file.HeaderSettings.Version = 2;
+ file.HeaderSettings.BedSizeX = HeaderSettings.BedSizeX;
+ file.HeaderSettings.BedSizeY = HeaderSettings.BedSizeY;
+ file.HeaderSettings.BedSizeZ = HeaderSettings.BedSizeZ;
+ file.HeaderSettings.OverallHeightMilimeter = TotalHeight;
+ file.HeaderSettings.BottomExposureSeconds = InitialExposureTime;
+ file.HeaderSettings.BottomLayersCount = InitialLayerCount;
+ file.HeaderSettings.BottomLightPWM = HeaderSettings.BottomLightPWM;
+ file.HeaderSettings.LayerCount = LayerCount;
+ file.HeaderSettings.LayerExposureSeconds = LayerExposureTime;
+ file.HeaderSettings.LayerHeightMilimeter = LayerHeight;
+ file.HeaderSettings.LayerOffTime = HeaderSettings.LayerOffTime;
+ file.HeaderSettings.LightPWM = HeaderSettings.LightPWM;
+ file.HeaderSettings.PrintTime = HeaderSettings.PrintTime;
+ file.HeaderSettings.ProjectorType = HeaderSettings.ProjectorType;
+ file.HeaderSettings.ResolutionX = ResolutionX;
+ file.HeaderSettings.ResolutionY = ResolutionY;
+
+ file.HeaderSettings.BottomLayerCount = InitialLayerCount;
+ file.HeaderSettings.BottomLiftHeight = PrintParametersSettings.BottomLiftHeight;
+ file.HeaderSettings.BottomLiftSpeed = PrintParametersSettings.BottomLiftSpeed;
+ file.HeaderSettings.BottomLightOffDelay = PrintParametersSettings.BottomLightOffDelay;
+ file.HeaderSettings.CostDollars = MaterialCost;
+ file.HeaderSettings.LiftHeight = PrintParametersSettings.LiftHeight;
+ file.HeaderSettings.LiftingSpeed = PrintParametersSettings.LiftingSpeed;
+ file.HeaderSettings.LayerOffTime = HeaderSettings.LayerOffTime;
+ file.HeaderSettings.RetractSpeed = PrintParametersSettings.RetractSpeed;
+ file.HeaderSettings.VolumeMl = UsedMaterial;
+ file.HeaderSettings.WeightG = PrintParametersSettings.WeightG;
+
+ file.HeaderSettings.MachineName = MachineName;
+ file.HeaderSettings.MachineNameSize = (uint) MachineName.Length;
+
+ file.SetThumbnails(Thumbnails);
+ file.Encode(fileFullPath);
+
+ return true;
+ }
+
+ if (to == typeof(ZCodexFile))
+ {
+ TimeSpan ts = new TimeSpan(0, 0, (int)PrintTime);
+ ZCodexFile file = new ZCodexFile
+ {
+ ResinMetadataSettings = new ZCodexFile.ResinMetadata
+ {
+ MaterialId = 2,
+ Material = MaterialName,
+ AdditionalSupportLayerTime = 0,
+ BottomLayersNumber = InitialLayerCount,
+ BottomLayersTime = (uint)(InitialExposureTime * 1000),
+ LayerTime = (uint)(LayerExposureTime * 1000),
+ DisableSettingsChanges = false,
+ LayerThickness = LayerHeight,
+ PrintTime = (uint)PrintTime,
+ TotalLayersCount = LayerCount,
+ TotalMaterialVolumeUsed = UsedMaterial,
+ TotalMaterialWeightUsed = UsedMaterial,
+ },
+ UserSettings = new ZCodexFile.UserSettingsdata
+ {
+ Printer = MachineName,
+ BottomLayersCount = InitialLayerCount,
+ PrintTime = $"{ts.Hours}h {ts.Minutes}m",
+ LayerExposureTime = (uint)(LayerExposureTime * 1000),
+ BottomLayerExposureTime = (uint)(InitialExposureTime * 1000),
+ MaterialId = 2,
+ LayerThickness = $"{LayerHeight} mm",
+ AntiAliasing = 0,
+ CrossSupportEnabled = 1,
+ ExposureOffTime = (uint) HeaderSettings.LayerOffTime,
+ HollowEnabled = 0,
+ HollowThickness = 0,
+ InfillDensity = 0,
+ IsAdvanced = 0,
+ MaterialType = MaterialName,
+ MaterialVolume = UsedMaterial,
+ MaxLayer = LayerCount - 1,
+ ModelLiftEnabled = 0,
+ ModelLiftHeight = 0,
+ RaftEnabled = 0,
+ RaftHeight = 0,
+ RaftOffset = 0,
+ SupportAdditionalExposureEnabled = 0,
+ SupportAdditionalExposureTime = 0,
+ XCorrection = 0,
+ YCorrection = 0,
+ ZLiftDistance = PrintParametersSettings.LiftHeight,
+ ZLiftFeedRate = PrintParametersSettings.LiftingSpeed,
+ ZLiftRetractRate = PrintParametersSettings.RetractSpeed,
+ },
+ ZCodeMetadataSettings = new ZCodexFile.ZCodeMetadata
+ {
+ PrintTime = (uint)PrintTime,
+ PrinterName = MachineName,
+ Materials = new List<ZCodexFile.ZCodeMetadata.MaterialsData>
+ {
+ new ZCodexFile.ZCodeMetadata.MaterialsData
+ {
+ Name = MaterialName,
+ ExtruderType = "MAIN",
+ Id = 0,
+ Usage = 0,
+ Temperature = 0
+ }
+ },
+ },
+ Layers = Layers
+ };
+
+ float usedMaterial = UsedMaterial / LayerCount;
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ file.ResinMetadataSettings.Layers.Add(new ZCodexFile.ResinMetadata.LayerData
+ {
+ Layer = layerIndex,
+ UsedMaterialVolume = usedMaterial
+ });
+ }
+
+ file.SetThumbnails(Thumbnails);
+ file.Encode(fileFullPath);
+ return true;
+ }
+
+ return false;
}
#endregion
}
diff --git a/PrusaSL1Reader/FileFormat.cs b/PrusaSL1Reader/FileFormat.cs
index eaf71ca..150d529 100644
--- a/PrusaSL1Reader/FileFormat.cs
+++ b/PrusaSL1Reader/FileFormat.cs
@@ -6,16 +6,11 @@
* of this license document, but changing it is not allowed.
*/
using System;
-using System.Diagnostics;
-using System.Drawing;
using System.IO;
-using System.IO.Compression;
using System.Linq;
-using System.Threading;
using System.Threading.Tasks;
using PrusaSL1Reader.Extensions;
using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Size = System.Drawing.Size;
@@ -133,6 +128,7 @@ namespace PrusaSL1Reader
{
new SL1File(), // Prusa SL1
new ChituboxFile(), // cbddlp, cbt, photon
+ new PHZFile(), // phz
new ZCodexFile(), // zcodex
};
@@ -172,6 +168,17 @@ namespace PrusaSL1Reader
{
return (from fileFormat in AvaliableFormats where fileFormat.IsExtensionValid(extension, isFilePath) select createNewInstance ? (FileFormat) Activator.CreateInstance(fileFormat.GetType()) : fileFormat).FirstOrDefault();
}
+
+ /// <summary>
+ /// Find <see cref="FileFormat"/> by an type
+ /// </summary>
+ /// <param name="type">Type to find</param>
+ /// <param name="createNewInstance">True to create a new instance of found file format, otherwise will return a pre created one which should be used for read-only purpose</param>
+ /// <returns><see cref="FileFormat"/> object or null if not found</returns>
+ public static FileFormat FindByType(Type type, bool createNewInstance = false)
+ {
+ return (from t in AvaliableFormats where type == t.GetType() select createNewInstance ? (FileFormat) Activator.CreateInstance(type) : t).FirstOrDefault();
+ }
#endregion
#region Properties
@@ -179,6 +186,7 @@ namespace PrusaSL1Reader
public abstract FileFormatType FileType { get; }
public abstract FileExtension[] FileExtensions { get; }
+ public abstract Type[] ConvertToFormats { get; }
public abstract PrintParameterModifier[] PrintParameterModifiers { get; }
diff --git a/PrusaSL1Reader/IFileFormat.cs b/PrusaSL1Reader/IFileFormat.cs
index d5663d7..62303a3 100644
--- a/PrusaSL1Reader/IFileFormat.cs
+++ b/PrusaSL1Reader/IFileFormat.cs
@@ -31,6 +31,11 @@ namespace PrusaSL1Reader
FileExtension[] FileExtensions { get; }
/// <summary>
+ /// Gets the implemented file formats able to convert to
+ /// </summary>
+ Type[] ConvertToFormats { get; }
+
+ /// <summary>
/// Gets the available <see cref="FileFormat.PrintParameterModifier"/>
/// </summary>
FileFormat.PrintParameterModifier[] PrintParameterModifiers { get; }
diff --git a/PrusaSL1Reader/PHZFile.cs b/PrusaSL1Reader/PHZFile.cs
new file mode 100644
index 0000000..e6c24a3
--- /dev/null
+++ b/PrusaSL1Reader/PHZFile.cs
@@ -0,0 +1,1228 @@
+/*
+ * 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.
+ */
+
+// https://github.com/cbiffle/catibo/blob/master/doc/cbddlp-ctb.adoc
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using BinarySerialization;
+using PrusaSL1Reader.Extensions;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace PrusaSL1Reader
+{
+ public class PHZFile : FileFormat
+ {
+ #region Constants
+ private const uint MAGIC_PHZ = 0x9FDA83AE;
+ private const int SPECIAL_BIT = 1 << 1;
+ private const int SPECIAL_BIT_MASK = ~SPECIAL_BIT;
+ private const ushort REPEATRGB15MASK = 0x20;
+
+ private const byte RLE8EncodingLimit = 125;
+ private const ushort RLE16EncodingLimit = 0x1000;
+ #endregion
+
+ #region Sub Classes
+ #region Header
+ public class Header
+ {
+
+ /// <summary>
+ /// Gets a magic number identifying the file type.
+ /// 0x12fd_0019 for cbddlp
+ /// 0x12fd_0086 for ctb
+ /// 0x9FDA83AE for phz
+ /// </summary>
+ [FieldOrder(0)] public uint Magic { get; set; } = MAGIC_PHZ;
+
+ /// <summary>
+ /// Gets the software version
+ /// </summary>
+ [FieldOrder(1)] public uint Version { get; set; } = 2;
+
+ /// <summary>
+ /// Gets the layer height setting used at slicing, in millimeters. Actual height used by the machine is in the layer table.
+ /// </summary>
+ [FieldOrder(2)] public float LayerHeightMilimeter { get; set; }
+
+ /// <summary>
+ /// Gets the exposure time setting used at slicing, in seconds, for normal (non-bottom) layers, respectively. Actual time used by the machine is in the layer table.
+ /// </summary>
+ [FieldOrder(3)] public float LayerExposureSeconds { get; set; }
+
+ /// <summary>
+ /// Gets the exposure time setting used at slicing, in seconds, for bottom layers. Actual time used by the machine is in the layer table.
+ /// </summary>
+ [FieldOrder(4)] public float BottomExposureSeconds { get; set; }
+
+ /// <summary>
+ /// Gets number of layers configured as "bottom." Note that this field appears in both the file header and ExtConfig..
+ /// </summary>
+ [FieldOrder(5)] public uint BottomLayersCount { get; set; } = 10;
+
+ /// <summary>
+ /// Gets the printer resolution along X axis, in pixels. This information is critical to correctly decoding layer images.
+ /// </summary>
+ [FieldOrder(6)] public uint ResolutionX { get; set; }
+
+ /// <summary>
+ /// Gets the printer resolution along Y axis, in pixels. This information is critical to correctly decoding layer images.
+ /// </summary>
+ [FieldOrder(7)] public uint ResolutionY { get; set; }
+
+ /// <summary>
+ /// Gets the file offsets of ImageHeader records describing the larger preview images.
+ /// </summary>
+ [FieldOrder(8)] public uint PreviewLargeOffsetAddress { get; set; }
+
+ /// <summary>
+ /// Gets the file offset of a table of LayerHeader records giving parameters for each printed layer.
+ /// </summary>
+ [FieldOrder(9)] public uint LayersDefinitionOffsetAddress { get; set; }
+
+ /// <summary>
+ /// Gets the number of records in the layer table for the first level set. In ctb files, that’s equivalent to the total number of records, but records may be multiplied in antialiased cbddlp files.
+ /// </summary>
+ [FieldOrder(10)] public uint LayerCount { get; set; }
+
+ /// <summary>
+ /// Gets the file offsets of ImageHeader records describing the smaller preview images.
+ /// </summary>
+ [FieldOrder(11)] public uint PreviewSmallOffsetAddress { get; set; }
+
+ /// <summary>
+ /// Gets the estimated duration of print, in seconds.
+ /// </summary>
+ [FieldOrder(12)] public uint PrintTime { get; set; }
+
+ /// <summary>
+ /// Gets the records whether this file was generated assuming normal (0) or mirrored (1) image projection. LCD printers are "mirrored" for this purpose.
+ /// </summary>
+ [FieldOrder(13)] public uint ProjectorType { get; set; }
+
+ /// <summary>
+ /// Gets the number of times each layer image is repeated in the file.
+ /// This is used to implement antialiasing in cbddlp files. When greater than 1,
+ /// the layer table will actually contain layer_table_count * level_set_count entries.
+ /// See the section on antialiasing for details.
+ /// </summary>
+ [FieldOrder(14)] public uint AntiAliasLevel { get; set; } = 1;
+
+ /// <summary>
+ /// Gets the PWM duty cycle for the UV illumination source on normal levels, respectively.
+ /// This appears to be an 8-bit quantity where 0xFF is fully on and 0x00 is fully off.
+ /// </summary>
+ [FieldOrder(15)] public ushort LightPWM { get; set; } = 255;
+
+ /// <summary>
+ /// Gets the PWM duty cycle for the UV illumination source on bottom levels, respectively.
+ /// This appears to be an 8-bit quantity where 0xFF is fully on and 0x00 is fully off.
+ /// </summary>
+ [FieldOrder(16)] public ushort BottomLightPWM { get; set; } = 255;
+
+ [FieldOrder(17)] public uint Padding1 { get; set; }
+ [FieldOrder(18)] public uint Padding2 { get; set; }
+
+ /// <summary>
+ /// Gets the height of the model described by this file, in millimeters.
+ /// </summary>
+ [FieldOrder(19)] public float OverallHeightMilimeter { get; set; }
+
+ /// <summary>
+ /// Gets dimensions of the printer’s X output volume, in millimeters.
+ /// </summary>
+ [FieldOrder(20)] public float BedSizeX { get; set; }
+
+ /// <summary>
+ /// Gets dimensions of the printer’s Y output volume, in millimeters.
+ /// </summary>
+ [FieldOrder(21)] public float BedSizeY { get; set; }
+
+ /// <summary>
+ /// Gets dimensions of the printer’s Z output volume, in millimeters.
+ /// </summary>
+ [FieldOrder(22)] public float BedSizeZ { get; set; }
+
+ /// <summary>
+ /// Gets the key used to encrypt layer data, or 0 if encryption is not used.
+ /// </summary>
+ [FieldOrder(23)] public uint EncryptionKey { get; set; }
+
+ /// <summary>
+ /// Gets the light off time setting used at slicing, for bottom layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig.
+ /// </summary>
+ [FieldOrder(24)] public float BottomLightOffDelay { get; set; } = 1;
+
+ /// <summary>
+ /// Gets the light off time setting used at slicing, for normal layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig.
+ /// </summary>
+ [FieldOrder(25)] public float LayerOffTime { get; set; } = 1;
+
+ /// <summary>
+ /// Gets number of layers configured as "bottom." Note that this field appears in both the file header and ExtConfig.
+ /// </summary>
+ [FieldOrder(26)] public uint BottomLayerCount { get; set; } = 10;
+
+ [FieldOrder(27)] public uint Padding3 { get; set; }
+
+ /// <summary>
+ /// Gets the distance to lift the build platform away from the vat after bottom layers, in millimeters.
+ /// </summary>
+ [FieldOrder(28)] public float BottomLiftHeight { get; set; } = 5;
+
+ /// <summary>
+ /// Gets the speed at which to lift the build platform away from the vat after bottom layers, in millimeters per minute.
+ /// </summary>
+ [FieldOrder(29)] public float BottomLiftSpeed { get; set; } = 300;
+
+ /// <summary>
+ /// Gets the distance to lift the build platform away from the vat after normal layers, in millimeters.
+ /// </summary>
+ [FieldOrder(30)] public float LiftHeight { get; set; } = 5;
+
+ /// <summary>
+ /// Gets the speed at which to lift the build platform away from the vat after normal layers, in millimeters per minute.
+ /// </summary>
+ [FieldOrder(31)] public float LiftingSpeed { get; set; } = 300;
+
+ /// <summary>
+ /// Gets the speed to use when the build platform re-approaches the vat after lift, in millimeters per minute.
+ /// </summary>
+ [FieldOrder(32)] public float RetractSpeed { get; set; } = 300;
+
+ /// <summary>
+ /// Gets the estimated required resin, measured in milliliters. The volume number is derived from the model.
+ /// </summary>
+ [FieldOrder(33)] public float VolumeMl { get; set; }
+
+ /// <summary>
+ /// Gets the estimated grams, derived from volume using configured factors for density.
+ /// </summary>
+ [FieldOrder(34)] public float WeightG { get; set; }
+
+ /// <summary>
+ /// Gets the estimated cost based on currency unit the user had configured. Derived from volume using configured factors for density and cost.
+ /// </summary>
+ [FieldOrder(35)] public float CostDollars { get; set; }
+
+ [FieldOrder(36)] public uint Padding4 { get; set; }
+
+ /// <summary>
+ /// Gets the machine name offset to a string naming the machine type, and its length in bytes.
+ /// </summary>
+ [FieldOrder(37)] public uint MachineNameAddress { get; set; }
+
+ /// <summary>
+ /// Gets the machine size in bytes
+ /// </summary>
+ [FieldOrder(38)] public uint MachineNameSize { get; set; }
+
+ /// <summary>
+ /// Gets the machine name. string is not nul-terminated.
+ /// The character encoding is currently unknown — all observed files in the wild use 7-bit ASCII characters only.
+ /// Note that the machine type here is set in the software profile, and is not the name the user assigned to the machine.
+ /// </summary>
+ [Ignore] public string MachineName { get; set; }
+
+ [FieldOrder(39)] public uint Padding5 { get; set; }
+ [FieldOrder(40)] public uint Padding6 { get; set; }
+ [FieldOrder(41)] public uint Padding7 { get; set; }
+ [FieldOrder(42)] public uint Padding8 { get; set; }
+ [FieldOrder(43)] public uint Padding9 { get; set; }
+ [FieldOrder(44)] public uint Padding10 { get; set; }
+
+ /// <summary>
+ /// Gets the parameter used to control encryption.
+ /// Not totally understood. 0 for cbddlp files, 0xF for ctb files.
+ /// </summary>
+ [FieldOrder(45)] public uint EncryptionMode { get; set; } = 0xF;
+
+ /// <summary>
+ /// Gets a number that increments with time or number of models sliced, or both. Zeroing it in output seems to have no effect. Possibly a user tracking bug.
+ /// </summary>
+ [FieldOrder(46)] public uint MysteriousId { get; set; }
+
+ /// <summary>
+ /// Gets a number that increments with time or number of models sliced, or both. Zeroing it in output seems to have no effect. Possibly a user tracking bug.
+ /// </summary>
+ [FieldOrder(47)] public uint AntiAliasLevel2 { get; set; }
+
+ [FieldOrder(48)] public uint SoftwareVersion { get; set; } = 0x01060300;
+
+ [FieldOrder(49)] public uint Padding11 { get; set; }
+ [FieldOrder(50)] public uint Padding12 { get; set; }
+ [FieldOrder(51)] public uint Padding13 { get; set; }
+ [FieldOrder(52)] public uint Padding14 { get; set; }
+ [FieldOrder(53)] public uint Padding15 { get; set; }
+ [FieldOrder(54)] public uint Padding16{ get; set; }
+
+ public override string ToString()
+ {
+ return $"{nameof(Magic)}: {Magic}, {nameof(Version)}: {Version}, {nameof(LayerHeightMilimeter)}: {LayerHeightMilimeter}, {nameof(LayerExposureSeconds)}: {LayerExposureSeconds}, {nameof(BottomExposureSeconds)}: {BottomExposureSeconds}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(PreviewLargeOffsetAddress)}: {PreviewLargeOffsetAddress}, {nameof(LayersDefinitionOffsetAddress)}: {LayersDefinitionOffsetAddress}, {nameof(LayerCount)}: {LayerCount}, {nameof(PreviewSmallOffsetAddress)}: {PreviewSmallOffsetAddress}, {nameof(PrintTime)}: {PrintTime}, {nameof(ProjectorType)}: {ProjectorType}, {nameof(AntiAliasLevel)}: {AntiAliasLevel}, {nameof(LightPWM)}: {LightPWM}, {nameof(BottomLightPWM)}: {BottomLightPWM}, {nameof(Padding1)}: {Padding1}, {nameof(Padding2)}: {Padding2}, {nameof(OverallHeightMilimeter)}: {OverallHeightMilimeter}, {nameof(BedSizeX)}: {BedSizeX}, {nameof(BedSizeY)}: {BedSizeY}, {nameof(BedSizeZ)}: {BedSizeZ}, {nameof(EncryptionKey)}: {EncryptionKey}, {nameof(BottomLightOffDelay)}: {BottomLightOffDelay}, {nameof(LayerOffTime)}: {LayerOffTime}, {nameof(BottomLayerCount)}: {BottomLayerCount}, {nameof(Padding3)}: {Padding3}, {nameof(BottomLiftHeight)}: {BottomLiftHeight}, {nameof(BottomLiftSpeed)}: {BottomLiftSpeed}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftingSpeed)}: {LiftingSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(VolumeMl)}: {VolumeMl}, {nameof(WeightG)}: {WeightG}, {nameof(CostDollars)}: {CostDollars}, {nameof(Padding4)}: {Padding4}, {nameof(MachineNameAddress)}: {MachineNameAddress}, {nameof(MachineNameSize)}: {MachineNameSize}, {nameof(MachineName)}: {MachineName}, {nameof(Padding5)}: {Padding5}, {nameof(Padding6)}: {Padding6}, {nameof(Padding7)}: {Padding7}, {nameof(Padding8)}: {Padding8}, {nameof(Padding9)}: {Padding9}, {nameof(Padding10)}: {Padding10}, {nameof(EncryptionMode)}: {EncryptionMode}, {nameof(MysteriousId)}: {MysteriousId}, {nameof(AntiAliasLevel2)}: {AntiAliasLevel2}, {nameof(SoftwareVersion)}: {SoftwareVersion}, {nameof(Padding11)}: {Padding11}, {nameof(Padding12)}: {Padding12}, {nameof(Padding13)}: {Padding13}, {nameof(Padding14)}: {Padding14}, {nameof(Padding15)}: {Padding15}, {nameof(Padding16)}: {Padding16}";
+ }
+ }
+ #endregion
+
+ #region Preview
+ /// <summary>
+ /// The files contain two preview images.
+ /// These are shown on the printer display when choosing which file to print, sparing the poor printer from needing to render a 3D image from scratch.
+ /// </summary>
+ public class Preview
+ {
+ /// <summary>
+ /// Gets the X dimension of the preview image, in pixels.
+ /// </summary>
+ [FieldOrder(0)] public uint ResolutionX { get; set; }
+
+ /// <summary>
+ /// Gets the Y dimension of the preview image, in pixels.
+ /// </summary>
+ [FieldOrder(1)] public uint ResolutionY { get; set; }
+
+ /// <summary>
+ /// Gets the image offset of the encoded data blob.
+ /// </summary>
+ [FieldOrder(2)] public uint ImageOffset { get; set; }
+
+ /// <summary>
+ /// Gets the image length in bytes.
+ /// </summary>
+ [FieldOrder(3)] public uint ImageLength { get; set; }
+
+ [FieldOrder(4)] public uint Unknown1 { get; set; }
+ [FieldOrder(5)] public uint Unknown2 { get; set; }
+ [FieldOrder(6)] public uint Unknown3 { get; set; }
+ [FieldOrder(7)] public uint Unknown4 { get; set; }
+
+ public override string ToString()
+ {
+ return $"{nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(ImageOffset)}: {ImageOffset}, {nameof(ImageLength)}: {ImageLength}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}";
+ }
+ }
+
+ #endregion
+
+ #region Layer
+ public class Layer
+ {
+ /// <summary>
+ /// Gets the build platform Z position for this layer, measured in millimeters.
+ /// </summary>
+ [FieldOrder(0)] public float LayerPositionZ { get; set; }
+
+ /// <summary>
+ /// Gets the exposure time for this layer, in seconds.
+ /// </summary>
+ [FieldOrder(1)] public float LayerExposure { get; set; }
+
+ /// <summary>
+ /// Gets how long to keep the light off after exposing this layer, in seconds.
+ /// </summary>
+ [FieldOrder(2)] public float LayerOffTimeSeconds { get; set; }
+
+ /// <summary>
+ /// Gets the layer image offset to encoded layer data, and its length in bytes.
+ /// </summary>
+ [FieldOrder(3)] public uint DataAddress { get; set; }
+
+ /// <summary>
+ /// Gets the layer image length in bytes.
+ /// </summary>
+ [FieldOrder(4)] public uint DataSize { get; set; }
+ [FieldOrder(5)] public uint Unknown1 { get; set; }
+ [FieldOrder(6)] public uint Unknown2 { get; set; }
+ [FieldOrder(7)] public uint Unknown3 { get; set; }
+ [FieldOrder(8)] public uint Unknown4 { get; set; }
+
+ [Ignore] public byte[] EncodedRle { get; set; }
+
+ public override string ToString()
+ {
+ return $"{nameof(LayerPositionZ)}: {LayerPositionZ}, {nameof(LayerExposure)}: {LayerExposure}, {nameof(LayerOffTimeSeconds)}: {LayerOffTimeSeconds}, {nameof(DataAddress)}: {DataAddress}, {nameof(DataSize)}: {DataSize}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}";
+ }
+ }
+ #endregion
+
+ #region KeyRing
+
+ public class KeyRing
+ {
+ public ulong Init { get; }
+ public ulong Key { get; private set; }
+ public uint Index { get; private set; }
+
+ public KeyRing(uint seed, uint layerIndex)
+ {
+ seed %= 0x4324;
+ Init = seed * 0x34a32231;
+ Key = (layerIndex ^ 0x3fad2212) * seed * 0x4910913d;
+ }
+
+ public byte Next()
+ {
+ byte k = (byte)(Key >> (int)(8 * Index));
+ Index++;
+
+ if ((Index & 3) == 0)
+ {
+ Key += Init;
+ Index = 0;
+ }
+
+ return k;
+ }
+
+ public List<byte> Read(List<byte> input)
+ {
+ List<byte> data = new List<byte>(input.Count);
+ data.AddRange(input.Select(t => (byte)(t ^ Next())));
+
+ return data;
+ }
+
+ public byte[] Read(byte[] input)
+ {
+ byte[] data = new byte[input.Length];
+ for (int i = 0; i < input.Length; i++)
+ {
+ data[i] = (byte) (input[i] ^ Next());
+ }
+ return data;
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Properties
+
+ public Header HeaderSettings { get; protected internal set; } = new Header();
+
+ public Preview[] Previews { get; protected internal set; }
+
+ public Layer[,] LayersDefinitions { get; private set; }
+
+ public Dictionary<string, Layer> LayersHash { get; } = new Dictionary<string, Layer>();
+
+ public override FileFormatType FileType => FileFormatType.Binary;
+
+ public override FileExtension[] FileExtensions { get; } = {
+ new FileExtension("phz", "PHZ Files"),
+ };
+
+ public override Type[] ConvertToFormats { get; } =
+ {
+ typeof(ChituboxFile),
+ typeof(ZCodexFile),
+ };
+
+ public override PrintParameterModifier[] PrintParameterModifiers { get; } =
+ {
+ PrintParameterModifier.InitialLayerCount,
+ PrintParameterModifier.InitialExposureSeconds,
+ PrintParameterModifier.ExposureSeconds,
+
+ PrintParameterModifier.BottomLayerOffTime,
+ PrintParameterModifier.LayerOffTime,
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.RetractSpeed,
+
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM,
+ };
+
+ public override byte ThumbnailsCount { get; } = 2;
+
+ public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = {new System.Drawing.Size(400, 300), new System.Drawing.Size(200, 125)};
+
+ public override uint ResolutionX => HeaderSettings.ResolutionX;
+
+ public override uint ResolutionY => HeaderSettings.ResolutionY;
+
+ public override float LayerHeight => HeaderSettings.LayerHeightMilimeter;
+
+ public override uint LayerCount => HeaderSettings.LayerCount;
+
+ public override ushort InitialLayerCount => (ushort)HeaderSettings.BottomLayersCount;
+
+ public override float InitialExposureTime => HeaderSettings.BottomExposureSeconds;
+
+ public override float LayerExposureTime => HeaderSettings.LayerExposureSeconds;
+ public override float LiftHeight => HeaderSettings.LiftHeight;
+ public override float LiftSpeed => HeaderSettings.LiftingSpeed;
+ public override float RetractSpeed => HeaderSettings.RetractSpeed;
+
+ public override float PrintTime => HeaderSettings.PrintTime;
+
+ public override float UsedMaterial => (float) Math.Round(HeaderSettings.VolumeMl, 2);
+
+ public override float MaterialCost => (float) Math.Round(HeaderSettings.CostDollars, 2);
+
+ public override string MaterialName => "Unknown";
+ public override string MachineName => HeaderSettings.MachineName;
+
+ public override object[] Configs => new object[] { HeaderSettings };
+
+ #endregion
+
+ #region Constructors
+ public PHZFile()
+ {
+ Previews = new Preview[ThumbnailsCount];
+ }
+ #endregion
+
+ #region Methods
+ public override void Clear()
+ {
+ base.Clear();
+
+ for (byte i = 0; i < ThumbnailsCount; i++)
+ {
+ Previews[i] = new Preview();
+ }
+
+ LayersDefinitions = null;
+ }
+
+ public override void Encode(string fileFullPath)
+ {
+ base.Encode(fileFullPath);
+ LayersHash.Clear();
+
+ uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings);
+ LayersDefinitions = new Layer[HeaderSettings.LayerCount, HeaderSettings.AntiAliasLevel];
+ using (var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write))
+ {
+
+ outputFile.Seek((int) currentOffset, SeekOrigin.Begin);
+
+ List<byte> rawData = new List<byte>();
+ ushort color15 = 0;
+ uint rep = 0;
+
+ void rleRGB15()
+ {
+ switch (rep)
+ {
+ case 0:
+ return;
+ case 1:
+ rawData.Add((byte) (color15 & ~REPEATRGB15MASK));
+ rawData.Add((byte) ((color15 & ~REPEATRGB15MASK) >> 8));
+ break;
+ case 2:
+ for (int i = 0; i < 2; i++)
+ {
+ rawData.Add((byte) (color15 & ~REPEATRGB15MASK));
+ rawData.Add((byte) ((color15 & ~REPEATRGB15MASK) >> 8));
+ }
+
+ break;
+ default:
+ rawData.Add((byte) (color15 | REPEATRGB15MASK));
+ rawData.Add((byte) ((color15 | REPEATRGB15MASK) >> 8));
+ rawData.Add((byte) ((rep - 1) | 0x3000));
+ rawData.Add((byte) (((rep - 1) | 0x3000) >> 8));
+ break;
+ }
+ }
+
+ for (byte i = 0; i < ThumbnailsCount; i++)
+ {
+ var image = Thumbnails[i];
+
+ color15 = 0;
+ rep = 0;
+ rawData.Clear();
+
+
+ for (int y = 0; y < image.Height; y++)
+ {
+ Span<Rgba32> pixelRowSpan = image.GetPixelRowSpan(y);
+ for (int x = 0; x < image.Width; x++)
+ {
+ var ncolor15 =
+ (pixelRowSpan[x].B >> 3)
+ | ((pixelRowSpan[x].G >> 2) << 5)
+ | ((pixelRowSpan[x].R >> 3) << 11);
+
+ if (ncolor15 == color15)
+ {
+ rep++;
+ if (rep == RLE16EncodingLimit)
+ {
+ rleRGB15();
+ rep = 0;
+ }
+ }
+ else
+ {
+ rleRGB15();
+ color15 = (ushort) ncolor15;
+ rep = 1;
+ }
+ }
+ }
+
+ rleRGB15();
+
+ if (rawData.Count == 0) continue;
+
+ if (i == (byte) FileThumbnailSize.Small)
+ {
+ HeaderSettings.PreviewSmallOffsetAddress = currentOffset;
+ }
+ else
+ {
+ HeaderSettings.PreviewLargeOffsetAddress = currentOffset;
+ }
+
+
+
+ Preview preview = new Preview
+ {
+ ResolutionX = (uint) image.Width,
+ ResolutionY = (uint) image.Height,
+ ImageLength = (uint) rawData.Count,
+ };
+
+ currentOffset += (uint) Helpers.Serializer.SizeOf(preview);
+ preview.ImageOffset = currentOffset;
+
+ Helpers.SerializeWriteFileStream(outputFile, preview);
+
+ currentOffset += (uint) rawData.Count;
+ outputFile.Write(rawData.ToArray(), 0, rawData.Count);
+ }
+
+
+
+ HeaderSettings.MachineNameSize = string.IsNullOrWhiteSpace(HeaderSettings.MachineName) ? 0 : (uint)HeaderSettings.MachineName.Length;
+ if (HeaderSettings.MachineNameSize > 0)
+ {
+ HeaderSettings.MachineNameAddress = currentOffset;
+ var machineBytes = Encoding.ASCII.GetBytes(HeaderSettings.MachineName);
+ outputFile.Write(machineBytes, 0, machineBytes.Length);
+ currentOffset += (uint)machineBytes.Length;
+ }
+
+ HeaderSettings.LayersDefinitionOffsetAddress = currentOffset;
+ uint layerDataCurrentOffset = currentOffset + (uint) Helpers.Serializer.SizeOf(new Layer()) * HeaderSettings.LayerCount * HeaderSettings.AntiAliasLevel;
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ Layer layer = new Layer();
+ Layer layerHash = null;
+ var image = GetLayerImage(layerIndex);
+ rawData = EncodePhzImage(image, layerIndex);
+
+ var byteArr = rawData.ToArray();
+
+ if (HeaderSettings.EncryptionKey == 0)
+ {
+ string hash = Helpers.ComputeSHA1Hash(byteArr);
+ if (!LayersHash.TryGetValue(hash, out layerHash))
+ {
+ LayersHash.Add(hash, layer);
+ }
+ }
+
+ //layer.DataAddress = CurrentOffset + (uint)Helpers.Serializer.SizeOf(layer);
+ layer.DataAddress = layerHash?.DataAddress ?? layerDataCurrentOffset;
+ layer.DataSize = layerHash?.DataSize ?? (uint)byteArr.Length;
+ layer.LayerPositionZ = layerIndex * HeaderSettings.LayerHeightMilimeter;
+ layer.LayerOffTimeSeconds = layerIndex < HeaderSettings.BottomLayersCount ? HeaderSettings.BottomLightOffDelay : HeaderSettings.LayerOffTime;
+ layer.LayerExposure = layerIndex < HeaderSettings.BottomLayersCount ? HeaderSettings.BottomExposureSeconds : HeaderSettings.LayerExposureSeconds;
+ LayersDefinitions[layerIndex, 0] = layer;
+
+ currentOffset += Helpers.SerializeWriteFileStream(outputFile, layer);
+
+ if (!ReferenceEquals(layerHash, null)) return;
+
+ outputFile.Seek(layerDataCurrentOffset, SeekOrigin.Begin);
+ layerDataCurrentOffset += Helpers.WriteFileStream(outputFile, byteArr);
+ outputFile.Seek(currentOffset, SeekOrigin.Begin);
+ }
+
+ outputFile.Seek(0, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+
+ outputFile.Close();
+ outputFile.Dispose();
+
+ Debug.WriteLine("Encode Results:");
+ Debug.WriteLine(HeaderSettings);
+ Debug.WriteLine(Previews[0]);
+ Debug.WriteLine(Previews[1]);
+ Debug.WriteLine("-End-");
+ }
+ }
+
+ private List<byte> EncodePhzImage(Image<L8> image, uint layerIndex)
+ {
+ List<byte> rawData = new List<byte>();
+ //byte color = byte.MaxValue >> 1;
+ byte color = byte.MaxValue;
+ uint stride = 0;
+
+ void AddRep()
+ {
+ rawData.Add((byte)(color|0x80));
+ stride--;
+ int done = 0;
+ while(done < stride)
+ {
+ int todo = 0x7d;
+
+ if (stride - done < todo) {
+ todo = (int)(stride - done);
+ }
+
+ rawData.Add((byte)(todo));
+
+ done += todo;
+ }
+ }
+
+ int halfWidth = image.Width / 2;
+
+ for (int y = 0; y < image.Height; y++)
+ {
+ var pixelRowSpan = image.GetPixelRowSpan(y);
+ for (int x = 0; x < image.Width; x++)
+ {
+ var grey7 = (byte)((pixelRowSpan[x].PackedValue >> 1) & 0x7c);
+
+ if (color == byte.MaxValue)
+ {
+ color = grey7;
+ stride = 1;
+ }
+ else if (grey7 != color || x == halfWidth)
+ {
+ AddRep();
+ color = grey7;
+ stride = 1;
+ }
+ else
+ {
+ stride++;
+ }
+ }
+
+ AddRep();
+ color = byte.MaxValue;
+ }
+
+
+ if (HeaderSettings.EncryptionKey > 0)
+ {
+ List<byte> encodedData = new List<byte>();
+ KeyRing kr = new KeyRing(HeaderSettings.EncryptionKey, layerIndex);
+ return kr.Read(rawData);
+ }
+
+ return rawData;
+ }
+
+ public override void Decode(string fileFullPath)
+ {
+ base.Decode(fileFullPath);
+
+ var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read);
+
+ //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile);
+ //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header))));
+ HeaderSettings = Helpers.Deserialize<Header>(inputFile);
+ if (HeaderSettings.Magic != MAGIC_PHZ)
+ {
+ throw new FileLoadException("Not a valid PHZ file!", fileFullPath);
+ }
+
+ HeaderSettings.AntiAliasLevel = 1;
+
+ FileFullPath = fileFullPath;
+
+
+
+ Debug.Write("Header -> ");
+ Debug.WriteLine(HeaderSettings);
+
+ for (byte i = 0; i < ThumbnailsCount; i++)
+ {
+ uint offsetAddress = i == 0 ? HeaderSettings.PreviewSmallOffsetAddress : HeaderSettings.PreviewLargeOffsetAddress;
+ if (offsetAddress == 0) continue;
+
+ inputFile.Seek(offsetAddress, SeekOrigin.Begin);
+ Previews[i] = Helpers.Deserialize<Preview>(inputFile);
+
+ Debug.Write($"Preview {i} -> ");
+ Debug.WriteLine(Previews[i]);
+
+ Thumbnails[i] = new Image<Rgba32>((int)Previews[i].ResolutionX, (int)Previews[i].ResolutionY);
+
+ inputFile.Seek(Previews[i].ImageOffset, SeekOrigin.Begin);
+ byte[] rawImageData = new byte[Previews[i].ImageLength];
+ inputFile.Read(rawImageData, 0, (int)Previews[i].ImageLength);
+ int x = 0;
+ int y = 0;
+ for (int n = 0; n < Previews[i].ImageLength; n++)
+ {
+ uint dot = (uint)(rawImageData[n] & 0xFF | ((rawImageData[++n] & 0xFF) << 8));
+ //uint color = ((dot & 0xF800) << 8) | ((dot & 0x07C0) << 5) | ((dot & 0x001F) << 3);
+ byte red = (byte)(((dot >> 11) & 0x1F) << 3);
+ byte green = (byte)(((dot >> 6) & 0x1F) << 3);
+ byte blue = (byte)((dot & 0x1F) << 3);
+ int repeat = 1;
+ if ((dot & 0x0020) == 0x0020)
+ {
+ repeat += rawImageData[++n] & 0xFF | ((rawImageData[++n] & 0x0F) << 8);
+ }
+
+
+ for (int j = 0; j < repeat; j++)
+ {
+ Thumbnails[i][x, y] = new Rgba32(red, green, blue, 255);
+ x++;
+
+ if (x == Previews[i].ResolutionX)
+ {
+ x = 0;
+ y++;
+ }
+ }
+ }
+ }
+
+ if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0)
+ {
+ inputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin);
+ byte[] buffer = new byte[HeaderSettings.MachineNameSize];
+ inputFile.Read(buffer, 0, (int) HeaderSettings.MachineNameSize);
+ HeaderSettings.MachineName = Encoding.ASCII.GetString(buffer);
+ }
+
+
+ LayersDefinitions = new Layer[HeaderSettings.LayerCount, HeaderSettings.AntiAliasLevel];
+
+ uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress;
+
+
+ for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++)
+ {
+ Debug.WriteLine($"-Image GROUP {aaIndex}-");
+ for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
+ {
+ inputFile.Seek(layerOffset, SeekOrigin.Begin);
+ Layer layer = Helpers.Deserialize<Layer>(inputFile);
+ LayersDefinitions[layerIndex, aaIndex] = layer;
+
+ layerOffset += (uint)Helpers.Serializer.SizeOf(layer);
+ Debug.Write($"LAYER {layerIndex} -> ");
+ Debug.WriteLine(layer);
+
+ layer.EncodedRle = new byte[layer.DataSize];
+ inputFile.Seek(layer.DataAddress, SeekOrigin.Begin);
+ inputFile.Read(layer.EncodedRle, 0, (int)layer.DataSize);
+ }
+ }
+
+ Layers = new byte[LayerCount][];
+
+ Parallel.For(0, LayerCount, layerIndex => {
+ var image = DecodePhzImage((uint) layerIndex);
+ using (var ms = new MemoryStream())
+ {
+ image.Save(ms, Helpers.PngEncoder);
+ Layers[layerIndex] = CompressLayer(ms.ToArray());
+ }
+ });
+
+ /*byte[,][] rleArr = new byte[LayerCount, HeaderSettings.AntiAliasLevel][];
+ for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
+ {
+ //byte[][] rleArr = new byte[HeaderSettings.AntiAliasLevel][];
+ for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++)
+ {
+ Layer layer = LayersDefinitions[layerIndex, aaIndex];
+ inputFile.Seek(layer.DataAddress, SeekOrigin.Begin);
+ rleArr[layerIndex, aaIndex] = new byte[(int)layer.DataSize];
+ inputFile.Read(rleArr[layerIndex, aaIndex], 0, (int)layer.DataSize);
+ }
+
+ var image = IsCbtFile ? DecodeCbtImage(rleArr[0], layerIndex) : DecodeCbddlpImage(rleArr, layerIndex);
+ using (var ms = new MemoryStream())
+ {
+ image.Save(ms, Helpers.PngEncoder);
+ Layers[layerIndex] = ms.ToArray();
+ }
+ //}
+
+ /*for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
+ {
+ var image = IsCbtFile ? DecodeCbtImage(layerIndex) : DecodeCbddlpImage(layerIndex);
+ using (var ms = new MemoryStream())
+ {
+ image.Save(ms, Helpers.BmpEncoder);
+ Layers[layerIndex] = CompressLayer(ms.ToArray());
+ }
+ }*/
+ }
+
+ private Image<L8> DecodePhzImage(uint layerIndex)
+ {
+ Image<L8> image = new Image<L8>((int)HeaderSettings.ResolutionX, (int)HeaderSettings.ResolutionY);
+ var rawImageData = LayersDefinitions[layerIndex, 0].EncodedRle;
+
+ if (HeaderSettings.EncryptionKey > 0)
+ {
+ KeyRing kr = new KeyRing(HeaderSettings.EncryptionKey, layerIndex);
+ rawImageData = kr.Read(rawImageData);
+ }
+
+ int limit = image.Width * image.Height;
+ int index = 0;
+ byte lastColor = 0;
+
+ image.TryGetSinglePixelSpan(out var span);
+
+ for (var n = 0; n < rawImageData.Length; n++)
+ {
+ byte code = rawImageData[n];
+
+ if ((code & 0x80) != 0)
+ {
+ lastColor = (byte) (code << 1);
+ if (index < limit)
+ {
+ span[index].PackedValue = lastColor;
+ }
+ else
+ {
+ Debug.WriteLine("Corrupted RLE data.");
+ }
+
+ index++;
+ }
+ else
+ {
+ for (uint i = 0; i < code; i++)
+ {
+ if (index < limit)
+ {
+ span[index].PackedValue = lastColor;
+ }
+ else
+ {
+ Debug.WriteLine("Corrupted RLE data.");
+ }
+ index++;
+ }
+ }
+ }
+
+ return image;
+ }
+
+ public override object GetValueFromPrintParameterModifier(PrintParameterModifier modifier)
+ {
+ var baseValue = base.GetValueFromPrintParameterModifier(modifier);
+ if (!ReferenceEquals(baseValue, null)) return baseValue;
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLayerOffTime)) return HeaderSettings.BottomLightOffDelay;
+ if (ReferenceEquals(modifier, PrintParameterModifier.LayerOffTime)) return HeaderSettings.LayerOffTime;
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight)) return HeaderSettings.BottomLiftHeight;
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed)) return HeaderSettings.BottomLiftSpeed;
+ /*if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight)) return PrintParametersSettings.LiftHeight;
+ if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed)) return PrintParametersSettings.LiftingSpeed;
+ if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed)) return PrintParametersSettings.RetractSpeed;*/
+
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightPWM)) return HeaderSettings.BottomLightPWM;
+ if (ReferenceEquals(modifier, PrintParameterModifier.LightPWM)) return HeaderSettings.LightPWM;
+
+
+
+ return null;
+ }
+
+ public override bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, string value)
+ {
+ void UpdateLayers()
+ {
+ for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++)
+ {
+ for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
+ {
+ // Bottom : others
+ LayersDefinitions[layerIndex, aaIndex].LayerExposure = layerIndex < HeaderSettings.BottomLayersCount ? HeaderSettings.BottomExposureSeconds : HeaderSettings.LayerExposureSeconds;
+ LayersDefinitions[layerIndex, aaIndex].LayerOffTimeSeconds = layerIndex < HeaderSettings.BottomLayersCount ? HeaderSettings.BottomLightOffDelay : HeaderSettings.LayerOffTime;
+ }
+ }
+ }
+
+ if (ReferenceEquals(modifier, PrintParameterModifier.InitialLayerCount))
+ {
+ HeaderSettings.BottomLayersCount =
+ HeaderSettings.BottomLayerCount = value.Convert<uint>();
+ UpdateLayers();
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.InitialExposureSeconds))
+ {
+ HeaderSettings.BottomExposureSeconds = value.Convert<float>();
+ UpdateLayers();
+ return true;
+ }
+
+ if (ReferenceEquals(modifier, PrintParameterModifier.ExposureSeconds))
+ {
+ HeaderSettings.LayerExposureSeconds = value.Convert<float>();
+ UpdateLayers();
+ return true;
+ }
+
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLayerOffTime))
+ {
+ HeaderSettings.BottomLightOffDelay = value.Convert<float>();
+ UpdateLayers();
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.LayerOffTime))
+ {
+ HeaderSettings.LayerOffTime =
+ HeaderSettings.LayerOffTime = value.Convert<float>();
+ UpdateLayers();
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight))
+ {
+ HeaderSettings.BottomLiftHeight = value.Convert<float>();
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed))
+ {
+ HeaderSettings.BottomLiftSpeed = value.Convert<float>();
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight))
+ {
+ HeaderSettings.LiftHeight = value.Convert<float>();
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed))
+ {
+ HeaderSettings.LiftingSpeed = value.Convert<float>();
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed))
+ {
+ HeaderSettings.RetractSpeed = value.Convert<float>();
+ return true;
+ }
+
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightPWM))
+ {
+ HeaderSettings.BottomLightPWM = value.Convert<ushort>();
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.LightPWM))
+ {
+ HeaderSettings.LightPWM = value.Convert<ushort>();
+ return true;
+ }
+
+ return false;
+ }
+
+ public override void SaveAs(string filePath = null)
+ {
+ if (!string.IsNullOrEmpty(filePath))
+ {
+ File.Copy(FileFullPath, filePath, true);
+ FileFullPath = filePath;
+ }
+
+ using (var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write))
+ {
+
+ outputFile.Seek(0, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+
+ /*if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0)
+ {
+ outputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin);
+ byte[] buffer = new byte[HeaderSettings.MachineNameSize];
+ outputFile.Write(Encoding.ASCII.GetBytes(HeaderSettings.MachineName), 0, (int)HeaderSettings.MachineNameSize);
+ }*/
+
+ uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress;
+ for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++)
+ {
+ for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
+ {
+ outputFile.Seek(layerOffset, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, LayersDefinitions[layerIndex, aaIndex]);
+ layerOffset += (uint)Helpers.Serializer.SizeOf(LayersDefinitions[layerIndex, aaIndex]);
+ }
+ }
+ outputFile.Close();
+ }
+
+ Decode(FileFullPath);
+ }
+
+ public override bool Convert(Type to, string fileFullPath)
+ {
+ if (to == typeof(ChituboxFile))
+ {
+ ChituboxFile file = new ChituboxFile
+ {
+ Layers = Layers
+ };
+
+
+ file.HeaderSettings.Version = 2;
+ file.HeaderSettings.BedSizeX = HeaderSettings.BedSizeX;
+ file.HeaderSettings.BedSizeY = HeaderSettings.BedSizeY;
+ file.HeaderSettings.BedSizeZ = HeaderSettings.BedSizeZ;
+ file.HeaderSettings.OverallHeightMilimeter = TotalHeight;
+ file.HeaderSettings.BottomExposureSeconds = InitialExposureTime;
+ file.HeaderSettings.BottomLayersCount = InitialLayerCount;
+ file.HeaderSettings.BottomLightPWM = HeaderSettings.BottomLightPWM;
+ file.HeaderSettings.LayerCount = LayerCount;
+ file.HeaderSettings.LayerExposureSeconds = LayerExposureTime;
+ file.HeaderSettings.LayerHeightMilimeter = LayerHeight;
+ file.HeaderSettings.LayerOffTime = HeaderSettings.LayerOffTime;
+ file.HeaderSettings.LightPWM = HeaderSettings.LightPWM;
+ file.HeaderSettings.PrintTime = HeaderSettings.PrintTime;
+ file.HeaderSettings.ProjectorType = HeaderSettings.ProjectorType;
+ file.HeaderSettings.ResolutionX = ResolutionX;
+ file.HeaderSettings.ResolutionY = ResolutionY;
+
+ file.PrintParametersSettings.BottomLayerCount = InitialLayerCount;
+ file.PrintParametersSettings.BottomLiftHeight = HeaderSettings.BottomLiftHeight;
+ file.PrintParametersSettings.BottomLiftSpeed = HeaderSettings.BottomLiftSpeed;
+ file.PrintParametersSettings.BottomLightOffDelay = HeaderSettings.BottomLightOffDelay;
+ file.PrintParametersSettings.CostDollars = MaterialCost;
+ file.PrintParametersSettings.LiftHeight = HeaderSettings.LiftHeight;
+ file.PrintParametersSettings.LiftingSpeed = HeaderSettings.LiftingSpeed;
+ file.PrintParametersSettings.LightOffDelay = HeaderSettings.LayerOffTime;
+ file.PrintParametersSettings.RetractSpeed = HeaderSettings.RetractSpeed;
+ file.PrintParametersSettings.VolumeMl = UsedMaterial;
+ file.PrintParametersSettings.WeightG = HeaderSettings.WeightG;
+
+ file.SlicerInfoSettings.MachineName = MachineName;
+ file.SlicerInfoSettings.MachineNameSize = (uint)MachineName.Length;
+
+ file.SetThumbnails(Thumbnails);
+ file.Encode(fileFullPath);
+
+ return true;
+ }
+
+ if (to == typeof(ZCodexFile))
+ {
+ TimeSpan ts = new TimeSpan(0, 0, (int)PrintTime);
+ ZCodexFile file = new ZCodexFile
+ {
+ ResinMetadataSettings = new ZCodexFile.ResinMetadata
+ {
+ MaterialId = 2,
+ Material = MaterialName,
+ AdditionalSupportLayerTime = 0,
+ BottomLayersNumber = InitialLayerCount,
+ BottomLayersTime = (uint)(InitialExposureTime * 1000),
+ LayerTime = (uint)(LayerExposureTime * 1000),
+ DisableSettingsChanges = false,
+ LayerThickness = LayerHeight,
+ PrintTime = (uint)PrintTime,
+ TotalLayersCount = LayerCount,
+ TotalMaterialVolumeUsed = UsedMaterial,
+ TotalMaterialWeightUsed = UsedMaterial,
+ },
+ UserSettings = new ZCodexFile.UserSettingsdata
+ {
+ Printer = MachineName,
+ BottomLayersCount = InitialLayerCount,
+ PrintTime = $"{ts.Hours}h {ts.Minutes}m",
+ LayerExposureTime = (uint)(LayerExposureTime * 1000),
+ BottomLayerExposureTime = (uint)(InitialExposureTime * 1000),
+ MaterialId = 2,
+ LayerThickness = $"{LayerHeight} mm",
+ AntiAliasing = 0,
+ CrossSupportEnabled = 1,
+ ExposureOffTime = (uint)HeaderSettings.LayerOffTime,
+ HollowEnabled = 0,
+ HollowThickness = 0,
+ InfillDensity = 0,
+ IsAdvanced = 0,
+ MaterialType = MaterialName,
+ MaterialVolume = UsedMaterial,
+ MaxLayer = LayerCount - 1,
+ ModelLiftEnabled = 0,
+ ModelLiftHeight = 0,
+ RaftEnabled = 0,
+ RaftHeight = 0,
+ RaftOffset = 0,
+ SupportAdditionalExposureEnabled = 0,
+ SupportAdditionalExposureTime = 0,
+ XCorrection = 0,
+ YCorrection = 0,
+ ZLiftDistance = HeaderSettings.LiftHeight,
+ ZLiftFeedRate = HeaderSettings.LiftingSpeed,
+ ZLiftRetractRate = HeaderSettings.RetractSpeed,
+ },
+ ZCodeMetadataSettings = new ZCodexFile.ZCodeMetadata
+ {
+ PrintTime = (uint)PrintTime,
+ PrinterName = MachineName,
+ Materials = new List<ZCodexFile.ZCodeMetadata.MaterialsData>
+ {
+ new ZCodexFile.ZCodeMetadata.MaterialsData
+ {
+ Name = MaterialName,
+ ExtruderType = "MAIN",
+ Id = 0,
+ Usage = 0,
+ Temperature = 0
+ }
+ },
+ },
+ Layers = Layers
+ };
+
+ float usedMaterial = UsedMaterial / LayerCount;
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ file.ResinMetadataSettings.Layers.Add(new ZCodexFile.ResinMetadata.LayerData
+ {
+ Layer = layerIndex,
+ UsedMaterialVolume = usedMaterial
+ });
+ }
+
+ file.SetThumbnails(Thumbnails);
+ file.Encode(fileFullPath);
+ return true;
+ }
+
+ return false;
+ }
+ #endregion
+ }
+}
diff --git a/PrusaSL1Reader/PrusaSL1Reader.csproj b/PrusaSL1Reader/PrusaSL1Reader.csproj
index ebca1fb..77e77d0 100644
--- a/PrusaSL1Reader/PrusaSL1Reader.csproj
+++ b/PrusaSL1Reader/PrusaSL1Reader.csproj
@@ -7,9 +7,9 @@
<PackageProjectUrl>https://github.com/sn4k3/PrusaSL1Viewer</PackageProjectUrl>
<PackageIcon></PackageIcon>
<RepositoryUrl>https://github.com/sn4k3/PrusaSL1Viewer</RepositoryUrl>
- <AssemblyVersion>0.3.2.0</AssemblyVersion>
- <FileVersion>0.3.2.0</FileVersion>
- <Version>0.3.2</Version>
+ <AssemblyVersion>0.3.3.0</AssemblyVersion>
+ <FileVersion>0.3.3.0</FileVersion>
+ <Version>0.3.3</Version>
<Description>Open, view, edit, extract and convert DLP/SLA files generated from Slicers</Description>
</PropertyGroup>
diff --git a/PrusaSL1Reader/SL1File.cs b/PrusaSL1Reader/SL1File.cs
index 55ea029..deff50e 100644
--- a/PrusaSL1Reader/SL1File.cs
+++ b/PrusaSL1Reader/SL1File.cs
@@ -8,13 +8,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Drawing;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
-using System.Threading.Tasks;
using PrusaSL1Reader.Extensions;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
@@ -278,6 +276,13 @@ namespace PrusaSL1Reader
new FileExtension("sl1", "Prusa SL1 Files")
};
+ public override Type[] ConvertToFormats { get; } =
+ {
+ typeof(ChituboxFile),
+ typeof(PHZFile),
+ typeof(ZCodexFile),
+ };
+
public override PrintParameterModifier[] PrintParameterModifiers { get; } = {
PrintParameterModifier.InitialLayerCount,
PrintParameterModifier.InitialExposureSeconds,
@@ -356,6 +361,7 @@ namespace PrusaSL1Reader
: memberName[i].ToString();
}
+
if (iniKey.EndsWith("_"))
iniKey.Remove(iniKey.Length - 1);
@@ -623,21 +629,18 @@ namespace PrusaSL1Reader
file.HeaderSettings.BedSizeY = PrinterSettings.DisplayHeight;
file.HeaderSettings.BedSizeZ = PrinterSettings.MaxPrintHeight;
file.HeaderSettings.OverallHeightMilimeter = TotalHeight;
- file.HeaderSettings.BottomExposureSeconds = MaterialSettings.InitialExposureTime;
- file.HeaderSettings.BottomLayersCount = PrintSettings.FadedLayers;
+ file.HeaderSettings.BottomExposureSeconds = InitialExposureTime;
+ file.HeaderSettings.BottomLayersCount = InitialLayerCount;
file.HeaderSettings.BottomLightPWM = LookupCustomValue<ushort>("BottomLightPWM", file.HeaderSettings.BottomLightPWM);
file.HeaderSettings.LayerCount = LayerCount;
- file.HeaderSettings.LayerExposureSeconds = MaterialSettings.ExposureTime;
- file.HeaderSettings.LayerHeightMilimeter = PrintSettings.LayerHeight;
+ file.HeaderSettings.LayerExposureSeconds = LayerExposureTime;
+ file.HeaderSettings.LayerHeightMilimeter = LayerHeight;
file.HeaderSettings.LayerOffTime = LookupCustomValue<float>("LayerOffTime", file.HeaderSettings.LayerOffTime);
file.HeaderSettings.LightPWM = LookupCustomValue<ushort>("LightPWM", file.HeaderSettings.LightPWM);
file.HeaderSettings.PrintTime = (uint) OutputConfigSettings.PrintTime;
file.HeaderSettings.ProjectorType = PrinterSettings.DisplayMirrorX ? 1u : 0u;
- file.HeaderSettings.ResolutionX = PrinterSettings.DisplayPixelsX;
- file.HeaderSettings.ResolutionY = PrinterSettings.DisplayPixelsY;
-
- //file.HeaderSettings.AntiAliasLevel = LookupCustomValue<uint>("AntiAliasLevel", 1);
-
+ file.HeaderSettings.ResolutionX = ResolutionX;
+ file.HeaderSettings.ResolutionY = ResolutionY;
file.PrintParametersSettings.BottomLayerCount = PrintSettings.FadedLayers;
file.PrintParametersSettings.BottomLiftHeight = LookupCustomValue<float>("BottomLiftHeight", file.PrintParametersSettings.BottomLiftHeight);
@@ -648,13 +651,13 @@ namespace PrusaSL1Reader
file.PrintParametersSettings.LiftingSpeed = LookupCustomValue<float>("LiftingSpeed", file.PrintParametersSettings.LiftingSpeed);
file.PrintParametersSettings.LightOffDelay = LookupCustomValue<float>("LightOffDelay", file.PrintParametersSettings.LightOffDelay);
file.PrintParametersSettings.RetractSpeed = LookupCustomValue<float>("RetractSpeed", file.PrintParametersSettings.RetractSpeed);
- file.PrintParametersSettings.VolumeMl = OutputConfigSettings.UsedMaterial;
+ file.PrintParametersSettings.VolumeMl = UsedMaterial;
file.PrintParametersSettings.WeightG = (float) Math.Round(OutputConfigSettings.UsedMaterial * MaterialSettings.MaterialDensity, 2);
+
+ file.SlicerInfoSettings.MachineName = MachineName;
+ file.SlicerInfoSettings.MachineNameSize = (uint)MachineName.Length;
- file.SetThumbnails(Thumbnails);
- file.SlicerInfoSettings.MachineName = PrinterSettings.PrinterSettingsId;
- file.SlicerInfoSettings.MachineNameSize = (uint) PrinterSettings.PrinterSettingsId.Length;
-
+ file.Layers = Layers;
if (LookupCustomValue<bool>("FLIP_XY", false, true))
{
@@ -662,12 +665,67 @@ namespace PrusaSL1Reader
file.HeaderSettings.ResolutionY = PrinterSettings.DisplayPixelsX;
}
- file.Layers = Layers;
+ file.SetThumbnails(Thumbnails);
file.Encode(fileFullPath);
return true;
}
+ if (to == typeof(PHZFile))
+ {
+ PHZFile file = new PHZFile
+ {
+ Layers = Layers
+ };
+
+
+ file.HeaderSettings.Version = 2;
+ file.HeaderSettings.BedSizeX = PrinterSettings.DisplayWidth;
+ file.HeaderSettings.BedSizeY = PrinterSettings.DisplayHeight;
+ file.HeaderSettings.BedSizeZ = PrinterSettings.MaxPrintHeight;
+ file.HeaderSettings.OverallHeightMilimeter = TotalHeight;
+ file.HeaderSettings.BottomExposureSeconds = MaterialSettings.InitialExposureTime;
+ file.HeaderSettings.BottomLayersCount = PrintSettings.FadedLayers;
+ file.HeaderSettings.BottomLightPWM = LookupCustomValue<ushort>("BottomLightPWM", file.HeaderSettings.BottomLightPWM);
+ file.HeaderSettings.LayerCount = LayerCount;
+ file.HeaderSettings.LayerExposureSeconds = MaterialSettings.ExposureTime;
+ file.HeaderSettings.LayerHeightMilimeter = PrintSettings.LayerHeight;
+ file.HeaderSettings.LayerOffTime = LookupCustomValue<float>("LayerOffTime", file.HeaderSettings.LayerOffTime);
+ file.HeaderSettings.LightPWM = LookupCustomValue<ushort>("LightPWM", file.HeaderSettings.LightPWM);
+ file.HeaderSettings.PrintTime = (uint)OutputConfigSettings.PrintTime;
+ file.HeaderSettings.ProjectorType = PrinterSettings.DisplayMirrorX ? 1u : 0u;
+ file.HeaderSettings.ResolutionX = PrinterSettings.DisplayPixelsX;
+ file.HeaderSettings.ResolutionY = PrinterSettings.DisplayPixelsY;
+
+
+ file.HeaderSettings.BottomLayerCount = PrintSettings.FadedLayers;
+ file.HeaderSettings.BottomLiftHeight = LookupCustomValue<float>("BottomLiftHeight", file.HeaderSettings.BottomLiftHeight);
+ file.HeaderSettings.BottomLiftSpeed = LookupCustomValue<float>("BottomLiftSpeed", file.HeaderSettings.BottomLiftSpeed);
+ file.HeaderSettings.BottomLightOffDelay = LookupCustomValue<float>("BottomLightOffDelay", file.HeaderSettings.BottomLightOffDelay);
+ file.HeaderSettings.CostDollars = MaterialCost;
+ file.HeaderSettings.LiftHeight = LookupCustomValue<float>("LiftHeight", file.HeaderSettings.LiftHeight);
+ file.HeaderSettings.LiftingSpeed = LookupCustomValue<float>("LiftingSpeed", file.HeaderSettings.LiftingSpeed);
+ file.HeaderSettings.LayerOffTime = LookupCustomValue<float>("LayerOffTime", file.HeaderSettings.LayerOffTime);
+ file.HeaderSettings.RetractSpeed = LookupCustomValue<float>("RetractSpeed", file.HeaderSettings.RetractSpeed);
+ file.HeaderSettings.VolumeMl = OutputConfigSettings.UsedMaterial;
+ file.HeaderSettings.WeightG = (float)Math.Round(OutputConfigSettings.UsedMaterial * MaterialSettings.MaterialDensity, 2);
+
+
+ file.HeaderSettings.MachineName = MachineName;
+ file.HeaderSettings.MachineNameSize = (uint)MachineName.Length;
+
+ if (LookupCustomValue<bool>("FLIP_XY", false, true))
+ {
+ file.HeaderSettings.ResolutionX = ResolutionX;
+ file.HeaderSettings.ResolutionY = ResolutionY;
+ }
+
+ file.SetThumbnails(Thumbnails);
+ file.Encode(fileFullPath);
+
+ return true;
+ }
+
if (to == typeof(ZCodexFile))
{
TimeSpan ts = new TimeSpan(0, 0, (int)PrintTime);
@@ -724,7 +782,7 @@ namespace PrusaSL1Reader
{
PrintTime = (uint)PrintTime,
PrinterName = MachineName,
- Materials = new List<ZCodexFile.ZCodeMetadata.MaterialsData>()
+ Materials = new List<ZCodexFile.ZCodeMetadata.MaterialsData>
{
new ZCodexFile.ZCodeMetadata.MaterialsData
{
@@ -736,11 +794,9 @@ namespace PrusaSL1Reader
}
},
},
+ Layers = Layers
};
-
- file.SetThumbnails(Thumbnails);
- file.Layers = Layers;
-
+
float usedMaterial = UsedMaterial / LayerCount;
for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
@@ -751,6 +807,7 @@ namespace PrusaSL1Reader
});
}
+ file.SetThumbnails(Thumbnails);
file.Encode(fileFullPath);
return true;
}
diff --git a/PrusaSL1Reader/ZCodexFile.cs b/PrusaSL1Reader/ZCodexFile.cs
index 32471b1..081dea1 100644
--- a/PrusaSL1Reader/ZCodexFile.cs
+++ b/PrusaSL1Reader/ZCodexFile.cs
@@ -149,6 +149,8 @@ namespace PrusaSL1Reader
new FileExtension("zcodex", "ZCodex/Z-Suite Files")
};
+ public override Type[] ConvertToFormats { get; } = null;
+
public override PrintParameterModifier[] PrintParameterModifiers { get; } = {
PrintParameterModifier.InitialLayerCount,
PrintParameterModifier.InitialExposureSeconds,
diff --git a/PrusaSL1Viewer/FrmLoading.cs b/PrusaSL1Viewer/FrmLoading.cs
index 060959c..d815dbf 100644
--- a/PrusaSL1Viewer/FrmLoading.cs
+++ b/PrusaSL1Viewer/FrmLoading.cs
@@ -6,7 +6,7 @@ namespace PrusaSL1Viewer
{
public partial class FrmLoading : Form
{
- private Stopwatch StopWatch { get; } = new Stopwatch();
+ public Stopwatch StopWatch { get; } = new Stopwatch();
public FrmLoading()
{
InitializeComponent();
diff --git a/PrusaSL1Viewer/FrmMain.cs b/PrusaSL1Viewer/FrmMain.cs
index 7fe931d..3c01050 100644
--- a/PrusaSL1Viewer/FrmMain.cs
+++ b/PrusaSL1Viewer/FrmMain.cs
@@ -216,7 +216,7 @@ namespace PrusaSL1Viewer
FrmLoading.ShowDialog();
if (MessageBox.Show(
- $"Extraction was successful, browser folder to see it contents.\n{finalPath}\nPress 'Yes' if you want open the target folder, otherwise select 'No' to continue.",
+ $"Extraction was successful ({FrmLoading.StopWatch.ElapsedMilliseconds/1000}s), browser folder to see it contents.\n{finalPath}\nPress 'Yes' if you want open the target folder, otherwise select 'No' to continue.",
"Extraction completed", MessageBoxButtons.YesNo, MessageBoxIcon.Question) ==
DialogResult.Yes)
{
@@ -514,24 +514,42 @@ namespace PrusaSL1Viewer
DisableGUI();
FrmLoading.SetDescription($"Converting {Path.GetFileName(SlicerFile.FileFullPath)} to {Path.GetExtension(dialog.FileName)}");
- var task = Task.Factory.StartNew(() =>
+ Task<bool> task = Task<bool>.Factory.StartNew(() =>
{
- SlicerFile.Convert(fileFormat, dialog.FileName);
- Invoke((MethodInvoker)delegate {
- // Running on the UI thread
- EnableGUI(true);
- });
+ bool result = false;
+ try
+ {
+ result = SlicerFile.Convert(fileFormat, dialog.FileName);
+ }
+ catch (Exception)
+ {
+ }
+ finally
+ {
+ Invoke((MethodInvoker)delegate {
+ // Running on the UI thread
+ EnableGUI(true);
+ });
+ }
+
+ return result;
});
FrmLoading.ShowDialog();
-
- if (MessageBox.Show($"Convertion is completed: {Path.GetFileName(dialog.FileName)}\n" +
- "Do you want open the converted file in a new window?",
- "Convertion completed", MessageBoxButtons.YesNo,
- MessageBoxIcon.Information) == DialogResult.Yes)
+ if (task.Result)
{
- Program.NewInstance(dialog.FileName);
+ if (MessageBox.Show($"Convertion is completed: {Path.GetFileName(dialog.FileName)} in {FrmLoading.StopWatch.ElapsedMilliseconds/1000}s\n" +
+ "Do you want open the converted file in a new window?",
+ "Convertion completed", MessageBoxButtons.YesNo,
+ MessageBoxIcon.Information) == DialogResult.Yes)
+ {
+ Program.NewInstance(dialog.FileName);
+ }
+ }
+ else
+ {
+ MessageBox.Show("Convertion was unsuccessful! Maybe not implemented...", "Convertion unsuccessful", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
@@ -682,18 +700,27 @@ namespace PrusaSL1Viewer
return;
}
- foreach (var fileFormat in FileFormat.AvaliableFormats)
+ if (!ReferenceEquals(SlicerFile.ConvertToFormats, null))
{
- if (fileFormat.GetType() == SlicerFile.GetType()) continue;
-
- string extensions = fileFormat.FileExtensions.Length > 0 ? $" ({fileFormat.GetFileExtensions()})" : string.Empty;
- ToolStripMenuItem menuItem = new ToolStripMenuItem(fileFormat.GetType().Name.Replace("File", extensions))
+ foreach (var fileFormatType in SlicerFile.ConvertToFormats)
{
- Tag = fileFormat,
- Image = Properties.Resources.layers_16x16
- };
- menuItem.Click += ConvertToItemOnClick;
- menuFileConvert.DropDownItems.Add(menuItem);
+ FileFormat fileFormat = FileFormat.FindByType(fileFormatType);
+ //if (fileFormat.GetType() == SlicerFile.GetType()) continue;
+
+ string extensions = fileFormat.FileExtensions.Length > 0
+ ? $" ({fileFormat.GetFileExtensions()})"
+ : string.Empty;
+ ToolStripMenuItem menuItem =
+ new ToolStripMenuItem(fileFormat.GetType().Name.Replace("File", extensions))
+ {
+ Tag = fileFormat,
+ Image = Properties.Resources.layers_16x16
+ };
+ menuItem.Click += ConvertToItemOnClick;
+ menuFileConvert.DropDownItems.Add(menuItem);
+ }
+
+ menuFileConvert.Enabled = menuFileConvert.DropDownItems.Count > 0;
}
scLeft.Panel1Collapsed = SlicerFile.CreatedThumbnailsCount == 0;
@@ -726,7 +753,7 @@ namespace PrusaSL1Viewer
menuFileReload.Enabled =
menuFileClose.Enabled =
menuFileExtract.Enabled =
- menuFileConvert.Enabled =
+
sbLayers.Enabled =
pbLayers.Enabled =
menuEdit.Enabled =
diff --git a/PrusaSL1Viewer/Properties/AssemblyInfo.cs b/PrusaSL1Viewer/Properties/AssemblyInfo.cs
index 49b1cf7..8fcd116 100644
--- a/PrusaSL1Viewer/Properties/AssemblyInfo.cs
+++ b/PrusaSL1Viewer/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("0.3.2.0")]
-[assembly: AssemblyFileVersion("0.3.2.0")]
+[assembly: AssemblyVersion("0.3.3.0")]
+[assembly: AssemblyFileVersion("0.3.3.0")]
diff --git a/PrusaSL1Viewer/PrusaSL1Viewer.csproj b/PrusaSL1Viewer/PrusaSL1Viewer.csproj
index 1a8830c..12b991e 100644
--- a/PrusaSL1Viewer/PrusaSL1Viewer.csproj
+++ b/PrusaSL1Viewer/PrusaSL1Viewer.csproj
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <Import Project="..\packages\Microsoft.Net.Compilers.Toolset.3.6.0-3.final\build\Microsoft.Net.Compilers.Toolset.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.Toolset.3.6.0-3.final\build\Microsoft.Net.Compilers.Toolset.props')" />
+ <Import Project="..\packages\Microsoft.Net.Compilers.Toolset.3.6.0-4.final\build\Microsoft.Net.Compilers.Toolset.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.Toolset.3.6.0-4.final\build\Microsoft.Net.Compilers.Toolset.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -259,13 +259,13 @@
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <PropertyGroup>
+ <PostBuildEvent>xcopy /y $(ProjectDir)..\LICENSE $(ProjectDir)$(OutDir)</PostBuildEvent>
+ </PropertyGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
- <Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.Toolset.3.6.0-3.final\build\Microsoft.Net.Compilers.Toolset.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.Toolset.3.6.0-3.final\build\Microsoft.Net.Compilers.Toolset.props'))" />
+ <Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.Toolset.3.6.0-4.final\build\Microsoft.Net.Compilers.Toolset.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.Toolset.3.6.0-4.final\build\Microsoft.Net.Compilers.Toolset.props'))" />
</Target>
- <PropertyGroup>
- <PostBuildEvent>xcopy /y $(ProjectDir)..\LICENSE $(ProjectDir)$(OutDir)</PostBuildEvent>
- </PropertyGroup>
</Project> \ No newline at end of file
diff --git a/PrusaSL1Viewer/packages.config b/PrusaSL1Viewer/packages.config
index b03c8cc..c735344 100644
--- a/PrusaSL1Viewer/packages.config
+++ b/PrusaSL1Viewer/packages.config
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BinarySerializer" version="8.5.1" targetFramework="net48" />
- <package id="Microsoft.Net.Compilers.Toolset" version="3.6.0-3.final" targetFramework="net48" developmentDependency="true" />
+ <package id="Microsoft.Net.Compilers.Toolset" version="3.6.0-4.final" targetFramework="net48" developmentDependency="true" />
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net48" />
<package id="SixLabors.Core" version="1.0.0-beta0008" targetFramework="net48" />
<package id="SixLabors.ImageSharp" version="1.0.0-rc0001" targetFramework="net48" />
diff --git a/PrusaSlicer/printer/Phrozen Sonic Mini.ini b/PrusaSlicer/printer/Phrozen Sonic Mini.ini
new file mode 100644
index 0000000..1586013
--- /dev/null
+++ b/PrusaSlicer/printer/Phrozen Sonic Mini.ini
@@ -0,0 +1,37 @@
+# generated by PrusaSlicer 2.2.0+win64 on 2020-05-19 at 02:50:50 UTC
+absolute_correction = 0
+area_fill = 50
+bed_custom_model =
+bed_custom_texture =
+bed_shape = 0x0,120x0,120x66,0x66
+default_sla_material_profile = Prusa Orange Tough 0.05
+default_sla_print_profile = 0.05 Normal
+display_height = 66.04
+display_mirror_x = 1
+display_mirror_y = 0
+display_orientation = portrait
+display_pixels_x = 1920
+display_pixels_y = 1080
+display_width = 120.96
+elefant_foot_compensation = 0.2
+elefant_foot_min_width = 0.2
+fast_tilt_time = 5
+gamma_correction = 1
+inherits = Original Prusa SL1
+max_exposure_time = 120
+max_initial_exposure_time = 300
+max_print_height = 130
+min_exposure_time = 1
+min_initial_exposure_time = 1
+print_host =
+printer_model = SL1
+printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_PHROZEN\nPRINTER_MODEL_SONICMINI\nSTART_CUSTOM_VALUES\nFLIP_XY\nLayerOffTime_7\nBottomLiftHeight_6\nBottomLiftSpeed_100\nLiftHeight_5\nLiftingSpeed_100\nRetractSpeed_200\nBottomLightOffDelay_7\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
+printer_settings_id =
+printer_technology = SLA
+printer_variant = default
+printer_vendor =
+printhost_apikey =
+printhost_cafile =
+relative_correction = 1,1
+slow_tilt_time = 8
+thumbnails = 400x400,800x480