diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ca2efe0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +################################################################################ +# This .gitignore file was automatically created by Microsoft(R) Visual Studio. +################################################################################ + +.vs +**/bin +**/obj +**/Safetensors.Inspector.UX/Properties/Resources.Designer.cs +**/Safetensors.Inspector.UX/Properties/Resources.resx +**/Safetensors.Inspector.UX/Properties/Settings.Designer.cs +**/Safetensors.Inspector.UX/Properties/Settings.settings +**/Safetensors.Inspector.UX/Properties/PublishProfiles/FolderProfile.pubxml.user +**/publish/* \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5c1d78e --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +Safetensors Inspector +===================== + +Safetensors Inspector is a tool that I created to learn the file structures and workings of +safetensors files. + +## Important + +At present, the following safetensors are supported: + +* LoRA + * Metadata + +## Examples + +### Reading a single file + +![](https://github.com/christogreeff/safetensors-inspector/blob/main/documentation/example01.gif) + +### Reading a folder full of files + +![](https://github.com/christogreeff/safetensors-inspector/blob/main/documentation/example02.gif) + +## Releases + +See [latest](https://github.com/christogreeff/safetensors-inspector/releases/tag/latest) releases. + +## Tools + +Additional tooling is (or will be) available in the **tools/** directory. + +## Documentation + +The offline documentation is (or will be) available in the **documentation/** directory. + +## License + +Safetensors Inspetor codebase is GPL-3.0 licensed. Please refer to the LICENSE file for detailed information. + +## Contributing + +Please do. diff --git a/documentation/example01.gif b/documentation/example01.gif new file mode 100644 index 0000000..0ccccf4 Binary files /dev/null and b/documentation/example01.gif differ diff --git a/documentation/example02.gif b/documentation/example02.gif new file mode 100644 index 0000000..10a9193 Binary files /dev/null and b/documentation/example02.gif differ diff --git a/publish.ps1 b/publish.ps1 new file mode 100644 index 0000000..4de3e89 --- /dev/null +++ b/publish.ps1 @@ -0,0 +1,15 @@ +$signTool="C:\Program Files (x86)\Microsoft SDKs\ClickOnce\SignTool\signtool.exe" +$certFolder = "C:\temp\cert\CodeSign\" +$cert = "$($certFolder)kd.pfx" +$pass = Get-Content "$($certFolder)kd.pass" +$exe = ".\publish\Safetensors.Inspector.UX.exe" +$md5 = ".\publish\Safetensors.Inspector.UX.md5" + +Remove-Item ".\publish\*" -Recurse -Force + +dotnet publish ".\solution\Safetensors.Inspector.sln" /p:PublishProfile=FolderProfile -v q + +Remove-Item ".\publish\*.config" -Force + +& $signTool sign /tr http://timestamp.digicert.com /f $cert /p $pass /td SHA256 /fd SHA256 $exe +(Get-FileHash $exe -Algorithm MD5).Hash > $md5 \ No newline at end of file diff --git a/solution/Safetensors.Core/Helpers/IntHelper.cs b/solution/Safetensors.Core/Helpers/IntHelper.cs new file mode 100644 index 0000000..7e5f910 --- /dev/null +++ b/solution/Safetensors.Core/Helpers/IntHelper.cs @@ -0,0 +1,42 @@ +namespace Safetensors.Core +{ + /// + /// Int Helper + /// + public static class IntHelper + { + /// + /// Size suffixes for file sizes + /// + private static readonly string[] SizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; + + /// + /// Size suffix for file sizes + /// + public static string SizeSuffix(this long value, int decimalPlaces = 1) + { + if (decimalPlaces < 0) { throw new ArgumentOutOfRangeException("decimalPlaces"); } + if (value < 0) { return "-" + SizeSuffix(-value, decimalPlaces); } + if (value == 0) { return string.Format("{0:n" + decimalPlaces + "} bytes", 0); } + + // mag is 0 for bytes, 1 for KB, 2, for MB, etc. + int mag = (int)Math.Log(value, 1024); + + // 1L << (mag * 10) == 2 ^ (10 * mag) + // [i.e. the number of bytes in the unit corresponding to mag] + decimal adjustedSize = (decimal)value / (1L << (mag * 10)); + + // make adjustment when the value is large enough that + // it would round up to 1000 or more + if (Math.Round(adjustedSize, decimalPlaces) >= 1000) + { + mag += 1; + adjustedSize /= 1024; + } + + return string.Format("{0:n" + decimalPlaces + "} {1}", + adjustedSize, + SizeSuffixes[mag]); + } + } +} diff --git a/solution/Safetensors.Core/Interfaces/IConfigurationReader.cs b/solution/Safetensors.Core/Interfaces/IConfigurationReader.cs new file mode 100644 index 0000000..d5ef9f0 --- /dev/null +++ b/solution/Safetensors.Core/Interfaces/IConfigurationReader.cs @@ -0,0 +1,15 @@ +namespace Safetensors.Core +{ + /// + /// Safetensor configuration reader interface + /// + public interface IConfigurationReader + { + /// + /// Load metadata property configuration + /// + /// List of Safetensor metadata properties + + Task?> LoadMetadataPropertyConfigAsync(); + } +} \ No newline at end of file diff --git a/solution/Safetensors.Core/Interfaces/ISafetensorsFile.cs b/solution/Safetensors.Core/Interfaces/ISafetensorsFile.cs new file mode 100644 index 0000000..02bdb65 --- /dev/null +++ b/solution/Safetensors.Core/Interfaces/ISafetensorsFile.cs @@ -0,0 +1,9 @@ +namespace Safetensors.Core +{ + public interface ISafetensorsFile + { + string Header { get; } + ISafetensorsMetadata Metadata { get; } + string Path { get; } + } +} \ No newline at end of file diff --git a/solution/Safetensors.Core/Interfaces/ISafetensorsMetadata.cs b/solution/Safetensors.Core/Interfaces/ISafetensorsMetadata.cs new file mode 100644 index 0000000..c9675d1 --- /dev/null +++ b/solution/Safetensors.Core/Interfaces/ISafetensorsMetadata.cs @@ -0,0 +1,10 @@ + +namespace Safetensors.Core +{ + public interface ISafetensorsMetadata + { + string Description { get; set; } + List Properties { get; set; } + string PropertyName { get; set; } + } +} \ No newline at end of file diff --git a/solution/Safetensors.Core/Interfaces/ISafetensorsMetadataProperty.cs b/solution/Safetensors.Core/Interfaces/ISafetensorsMetadataProperty.cs new file mode 100644 index 0000000..c82e087 --- /dev/null +++ b/solution/Safetensors.Core/Interfaces/ISafetensorsMetadataProperty.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Nodes; + +namespace Safetensors.Core +{ + public interface ISafetensorsMetadataProperty + { + string Description { get; set; } + string? MetadataType { get; set; } + string PropertyName { get; set; } + string Value { get; set; } + JsonNode? ValueEx { get; } + } +} \ No newline at end of file diff --git a/solution/Safetensors.Core/Interfaces/ISafetensorsReader.cs b/solution/Safetensors.Core/Interfaces/ISafetensorsReader.cs new file mode 100644 index 0000000..f66216a --- /dev/null +++ b/solution/Safetensors.Core/Interfaces/ISafetensorsReader.cs @@ -0,0 +1,20 @@ +namespace Safetensors.Core +{ + /// + /// Safeensor reader interface + /// + public interface ISafetensorsReader + { + /// + /// Initialize the Safetenso reader + /// + Task InitializeAsync(); + + /// + /// Async read of .safetensor file + /// + /// File path of the .safetensor file + /// SafetensorFile object + Task ReadAsync(string path); + } +} \ No newline at end of file diff --git a/solution/Safetensors.Core/Models/Safetensor/SafetensorsFile.cs b/solution/Safetensors.Core/Models/Safetensor/SafetensorsFile.cs new file mode 100644 index 0000000..dc94f19 --- /dev/null +++ b/solution/Safetensors.Core/Models/Safetensor/SafetensorsFile.cs @@ -0,0 +1,28 @@ +namespace Safetensors.Core +{ + /// + /// Safetensor file + /// + /// + /// https://huggingface.co/docs/safetensors/en/metadata_parsing + /// https://cran.r-project.org/web/packages/safetensors/safetensors.pdf + /// https://civitai.com/articles/827/safetensors-inspector-google-colab + /// + public class SafetensorsFile(string path, string header) : ISafetensorsFile + { + /// + /// Safetensor file location + /// + public string Path { get; private set; } = path; + + /// + /// Safetensor raw metadata + /// + public string Header { get; private set; } = header; + + /// + /// Safetensor metadata + /// + public ISafetensorsMetadata Metadata { get; private set; } = new SafetensorsMetadata(); + } +} \ No newline at end of file diff --git a/solution/Safetensors.Core/Models/Safetensor/SafetensorsMetadata.cs b/solution/Safetensors.Core/Models/Safetensor/SafetensorsMetadata.cs new file mode 100644 index 0000000..725186d --- /dev/null +++ b/solution/Safetensors.Core/Models/Safetensor/SafetensorsMetadata.cs @@ -0,0 +1,23 @@ +namespace Safetensors.Core +{ + /// + /// Safetensor metadata + /// + public class SafetensorsMetadata : ISafetensorsMetadata + { + /// + /// Default __metadata__ property name + /// + public string PropertyName { get; set; } = "__metadata__"; + + /// + /// Safetensor metadata description + /// + public string Description { get; set; } = string.Empty; + + /// + /// Safeensor metadata properties + /// + public List Properties { get; set; } = []; + } +} \ No newline at end of file diff --git a/solution/Safetensors.Core/Models/Safetensor/SafetensorsMetadataProperty.cs b/solution/Safetensors.Core/Models/Safetensor/SafetensorsMetadataProperty.cs new file mode 100644 index 0000000..ae9ab42 --- /dev/null +++ b/solution/Safetensors.Core/Models/Safetensor/SafetensorsMetadataProperty.cs @@ -0,0 +1,62 @@ +using System.Text.Json.Nodes; + +namespace Safetensors.Core +{ + /// + /// Safeensor metadata property + /// + public class SafetensorsMetadataProperty : ISafetensorsMetadataProperty + { + /// + /// Safeensor metadata property name + /// + public string PropertyName { get; set; } = string.Empty; + + /// + /// Safetenso metadata property description + /// + public string Description { get; set; } = string.Empty; + + /// + /// Safeensor metadata property value + /// + public string Value { get; set; } = string.Empty; + + /// + /// Safeensor metadata property value extended + /// + public JsonNode? ValueEx + { + get + { + return Convert.ToString(MetadataType)?.ToLower() switch + { + "tagfrequency" or + "datasetdirs" => ParseJson(), + _ => null, + }; + } + } + + /// + /// Safeensor metadata property type + /// + public string? MetadataType { get; set; } = string.Empty; + + /// + /// Try parse Json + /// + /// + private JsonNode? ParseJson() + { + if (!string.IsNullOrEmpty(Value)) + { + var node = JsonNode.Parse(Value); + + return node; + } + + return null; + } + } +} \ No newline at end of file diff --git a/solution/Safetensors.Core/Safetensors.Core.csproj b/solution/Safetensors.Core/Safetensors.Core.csproj new file mode 100644 index 0000000..b97451d --- /dev/null +++ b/solution/Safetensors.Core/Safetensors.Core.csproj @@ -0,0 +1,10 @@ + + + + net8.0 + enable + enable + none + + + diff --git a/solution/Safetensors.Inspector.UX/App.config b/solution/Safetensors.Inspector.UX/App.config new file mode 100644 index 0000000..49cc43e --- /dev/null +++ b/solution/Safetensors.Inspector.UX/App.config @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/solution/Safetensors.Inspector.UX/Assets/HashGray.ico b/solution/Safetensors.Inspector.UX/Assets/HashGray.ico new file mode 100644 index 0000000..e6785e4 Binary files /dev/null and b/solution/Safetensors.Inspector.UX/Assets/HashGray.ico differ diff --git a/solution/Safetensors.Inspector.UX/Assets/HashMagenta.ico b/solution/Safetensors.Inspector.UX/Assets/HashMagenta.ico new file mode 100644 index 0000000..72adc07 Binary files /dev/null and b/solution/Safetensors.Inspector.UX/Assets/HashMagenta.ico differ diff --git a/solution/Safetensors.Inspector.UX/Assets/PropertyGray.ico b/solution/Safetensors.Inspector.UX/Assets/PropertyGray.ico new file mode 100644 index 0000000..52b791d Binary files /dev/null and b/solution/Safetensors.Inspector.UX/Assets/PropertyGray.ico differ diff --git a/solution/Safetensors.Inspector.UX/Assets/PropertyGreen.ico b/solution/Safetensors.Inspector.UX/Assets/PropertyGreen.ico new file mode 100644 index 0000000..9384931 Binary files /dev/null and b/solution/Safetensors.Inspector.UX/Assets/PropertyGreen.ico differ diff --git a/solution/Safetensors.Inspector.UX/Assets/XtraGray.ico b/solution/Safetensors.Inspector.UX/Assets/XtraGray.ico new file mode 100644 index 0000000..f8a2d6e Binary files /dev/null and b/solution/Safetensors.Inspector.UX/Assets/XtraGray.ico differ diff --git a/solution/Safetensors.Inspector.UX/Assets/XtraRed.ico b/solution/Safetensors.Inspector.UX/Assets/XtraRed.ico new file mode 100644 index 0000000..643c4d8 Binary files /dev/null and b/solution/Safetensors.Inspector.UX/Assets/XtraRed.ico differ diff --git a/solution/Safetensors.Inspector.UX/Assets/safetensor.ico b/solution/Safetensors.Inspector.UX/Assets/safetensor.ico new file mode 100644 index 0000000..75d5022 Binary files /dev/null and b/solution/Safetensors.Inspector.UX/Assets/safetensor.ico differ diff --git a/solution/Safetensors.Inspector.UX/Assets/safetensor.png b/solution/Safetensors.Inspector.UX/Assets/safetensor.png new file mode 100644 index 0000000..9179692 Binary files /dev/null and b/solution/Safetensors.Inspector.UX/Assets/safetensor.png differ diff --git a/solution/Safetensors.Inspector.UX/Program.cs b/solution/Safetensors.Inspector.UX/Program.cs new file mode 100644 index 0000000..9e63980 --- /dev/null +++ b/solution/Safetensors.Inspector.UX/Program.cs @@ -0,0 +1,17 @@ +namespace Safetensor.Inspector.UX +{ + internal static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + ApplicationConfiguration.Initialize(); + Application.Run(new formSafetensors()); + } + } +} \ No newline at end of file diff --git a/solution/Safetensors.Inspector.UX/Properties/PublishProfiles/FolderProfile.pubxml b/solution/Safetensors.Inspector.UX/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..7ebbd63 --- /dev/null +++ b/solution/Safetensors.Inspector.UX/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,18 @@ + + + + + Release + Any CPU + ..\..\Publish + FileSystem + <_TargetId>Folder + net8.0-windows + win-x64 + false + true + true + + \ No newline at end of file diff --git a/solution/Safetensors.Inspector.UX/Safetensors.Inspector.UX.csproj b/solution/Safetensors.Inspector.UX/Safetensors.Inspector.UX.csproj new file mode 100644 index 0000000..11fe269 --- /dev/null +++ b/solution/Safetensors.Inspector.UX/Safetensors.Inspector.UX.csproj @@ -0,0 +1,57 @@ + + + + WinExe + net8.0-windows + enable + true + enable + none + Safetensors Inspector (LoRAs) + Safetensors Inspector (LoRAs) + True + false + + + + + + + + + + + + + + + + True + True + Resources.resx + + + True + True + Settings.settings + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + Always + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + \ No newline at end of file diff --git a/solution/Safetensors.Inspector.UX/Safetensors.Inspector.UX.csproj.user b/solution/Safetensors.Inspector.UX/Safetensors.Inspector.UX.csproj.user new file mode 100644 index 0000000..3212809 --- /dev/null +++ b/solution/Safetensors.Inspector.UX/Safetensors.Inspector.UX.csproj.user @@ -0,0 +1,16 @@ + + + + <_LastSelectedProfileId>D:\git\private\safetensors-inspector\Safetensors.Inspector.UX\Properties\PublishProfiles\FolderProfile.pubxml + + + + Form + + + + + Designer + + + \ No newline at end of file diff --git a/solution/Safetensors.Inspector.UX/formSafetensors.Designer.cs b/solution/Safetensors.Inspector.UX/formSafetensors.Designer.cs new file mode 100644 index 0000000..cf2a66a --- /dev/null +++ b/solution/Safetensors.Inspector.UX/formSafetensors.Designer.cs @@ -0,0 +1,86 @@ + +namespace Safetensor.Inspector.UX +{ + partial class formSafetensors + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(formSafetensors)); + treeProperties = new TreeView(); + imageList = new ImageList(components); + SuspendLayout(); + // + // treeProperties + // + treeProperties.AllowDrop = true; + treeProperties.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; + treeProperties.ImageIndex = 0; + treeProperties.ImageList = imageList; + treeProperties.Location = new Point(12, 12); + treeProperties.Name = "treeProperties"; + treeProperties.SelectedImageIndex = 0; + treeProperties.ShowNodeToolTips = true; + treeProperties.Size = new Size(899, 593); + treeProperties.TabIndex = 0; + treeProperties.DragDrop += treeProperties_DragDrop; + treeProperties.DragEnter += treeProperties_DragEnter; + // + // imageList + // + imageList.ColorDepth = ColorDepth.Depth32Bit; + imageList.ImageStream = (ImageListStreamer)resources.GetObject("imageList.ImageStream"); + imageList.TransparentColor = Color.Transparent; + imageList.Images.SetKeyName(0, "safetensor.ico"); + imageList.Images.SetKeyName(1, "PropertyGray.ico"); + imageList.Images.SetKeyName(2, "PropertyGreen.ico"); + imageList.Images.SetKeyName(3, "XtraGray.ico"); + imageList.Images.SetKeyName(4, "XtraRed.ico"); + imageList.Images.SetKeyName(5, "HashGray.ico"); + imageList.Images.SetKeyName(6, "HashMagenta.ico"); + // + // formSafetensors + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(932, 617); + Controls.Add(treeProperties); + Icon = (Icon)resources.GetObject("$this.Icon"); + MaximizeBox = false; + Name = "formSafetensors"; + SizeGripStyle = SizeGripStyle.Hide; + StartPosition = FormStartPosition.CenterScreen; + Text = "Safetensors Inspector"; + ResumeLayout(false); + } + + #endregion + + private TreeView treeProperties; + private ImageList imageList; + } +} diff --git a/solution/Safetensors.Inspector.UX/formSafetensors.cs b/solution/Safetensors.Inspector.UX/formSafetensors.cs new file mode 100644 index 0000000..42f7332 --- /dev/null +++ b/solution/Safetensors.Inspector.UX/formSafetensors.cs @@ -0,0 +1,169 @@ +using Safetensors.Core; +using Safetensors.Storage; +namespace Safetensor.Inspector.UX +{ + /// + /// Safetensors Inspector UX form + /// + public partial class formSafetensors : Form + { + /// + /// Constructor + /// + public formSafetensors() + { + InitializeComponent(); + } + + /// + /// Drag and drop event handler for the tree view control. + /// + private void treeProperties_DragEnter(object sender, DragEventArgs e) + { + if (e?.Data != null) + if (e.Data.GetDataPresent(DataFormats.FileDrop)) e.Effect = DragDropEffects.Copy; + } + + /// + /// Drag and drop event handler for the tree view control + /// + private async void treeProperties_DragDrop(object sender, DragEventArgs e) + { + if ((e != null) && (e.Data != null)) + { + // list of potential folders/files to be added + List listFiles = []; + + if (e.Data.GetData(DataFormats.FileDrop) is string[] droppedItems) + { + foreach (string droppedItem in droppedItems) + { + FileAttributes attr = File.GetAttributes(droppedItem); + + // check if a folder/directory + if ((attr & FileAttributes.Directory) == FileAttributes.Directory) + { + if (Path.Exists(droppedItem)) + listFiles.AddRange(Directory.GetFiles(droppedItem, "*.safetensors")); + } + else if (File.Exists(droppedItem)) + listFiles.Add(droppedItem); + } + + // if there are files to be added + if (listFiles.Count > 0) + await PopulateTreeViewWithFilesAsync([.. listFiles]); + } + } + } + + /// + /// Populate the treeview + /// + /// + /// Yes, this can be improved. For now it works well enough. + /// + private async Task PopulateTreeViewWithFilesAsync(string[] files) + { + // clear UX tree view + treeProperties.Nodes.Clear(); + + // create temp list of nodes + List nodes = []; + + foreach (string file in files) + { + var fileNode = treeProperties.Nodes.Find(file, false); + if (fileNode.Length == 0) + { + // get file size in human readable format + var fiSize = new FileInfo(file).Length.SizeSuffix(2); + + TreeNode safetensorsNode = new() + { + Name = file, + Text = $"{file} ({fiSize})", + ImageIndex = 0, + ToolTipText = $"The safetensors file {Path.GetFileName(file)} is {fiSize}" + }; + + // read a file and get its properties + SafetensorReader sr = new(); + await sr.InitializeAsync(); + var safetensor = await sr.ReadAsync(file); + + if (safetensor != null) + { + foreach (var prop in safetensor.Metadata.Properties) + { + var propProps = prop?.ValueEx?.AsObject().ToList(); + { + if (propProps == null) + { + TreeNode propNode = new() + { + Name = prop?.PropertyName, + ToolTipText = prop?.PropertyName, + Text = $"{prop?.Description}: {prop?.Value}", + ImageIndex = 1, + SelectedImageIndex = 2 + }; + + safetensorsNode.Nodes.Add(propNode); + } + else + { + TreeNode propNode = new() + { + Name = prop?.PropertyName, + ToolTipText = prop?.PropertyName, + Text = $"{prop?.Description}", + ImageIndex = 3, + SelectedImageIndex = 4 + }; + + propProps?.ForEach(propChild => + { + TreeNode propChildNode = new() + { + Name = prop?.PropertyName, + ToolTipText = $"{prop?.PropertyName} : {propChild.Key}", + Text = $"{propChild.Key}", + ImageIndex = 5, + SelectedImageIndex = 6 + }; + + propNode.Nodes.Add(propChildNode); + + var propChildProps = propChild.Value?.AsObject().ToList(); + propChildProps? + .Select(z => new KeyValuePair(Convert.ToString(z.Key).Trim(), Convert.ToInt32(z.Value?.GetValue()))) + .OrderByDescending(z => z.Value).ThenBy(z => z.Key).ToList() + .ForEach(y => + { + propChildNode.Nodes.Add(new TreeNode() + { + Name = prop?.PropertyName, + ToolTipText = $"{y.Key}", + Text = $"({y.Value}) {y.Key}", + ImageIndex = 5, + SelectedImageIndex = 6 + }); + }); + }); + + safetensorsNode.Nodes.Add(propNode); + } + } + } + } + + nodes.Add(safetensorsNode); + } + } + + // add list of nodes to UX tree + treeProperties.Nodes.AddRange([.. nodes]); + } + } +} \ No newline at end of file diff --git a/solution/Safetensors.Inspector.UX/formSafetensors.resx b/solution/Safetensors.Inspector.UX/formSafetensors.resx new file mode 100644 index 0000000..2b3ba06 --- /dev/null +++ b/solution/Safetensors.Inspector.UX/formSafetensors.resx @@ -0,0 +1,778 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs + LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu + SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAiAwAAAJNU0Z0AUkBTAIBAQcB + AAEwAQABMAEAARABAAEQAQAD/wEAASEBAAj/AUIBTQE2BwABNgMAASgDAAFAAwABIAMAAQEBAAEgBgAB + IP8A/wBoAAMdASgDEgEYBAADHQEoAwwBECwAAx0BKAMSARgEAAMdASgDDAEQZAADGwElAgAB/gH/AkoB + eAHADAACSQFRAY8CAAH9Af8oAAM3AVgDVAGXBAADdgHlAz8BaCwAATkBNQE5AVgBZAFFAWQBlwQAAccB + JAHHAeUBQgE8AUIBaGgAAyABLgIAAf4B/wMgAS0IAAJNAWcBrwJAAZAB0igAAzcBWAOAAf8EAANNAYcD + UgGSLAABOQE1ATkBWAH/AQAC/wQAAVcBQwFXAYcBYAFFAWABkmwAAyQBNAIAAf4B/wMiATEDHwEsAgAB + /QH/AyoBPyQAAzcBWAM3AVgDfwH/AzcBWANgAbEDgAH/AzcBWAM3AVggAAE5ATUBOQFYATkBNQE5AVgB + /gEAAf4B/wE5ATUBOQFYAX4BQwF+AbEB/wEAAv8BOQE1ATkBWAE5ATUBOQFYaAADOgFgAgAB/gH/Ak0B + awGzAkkBfQHFKAADVQGXA1UBlwOAAf8DVQGXA1UBlwOAAf8DVQGXA1UBlwMYASEcAAFkAUUBZAGXAWQB + RQFkAZcB/wEAAv8BZAFFAWQBlwFkAUUBZAGXAf8BAAL/AWQBRQFkAZcBZAFFAWQBlwMYASFoAAJKAXIB + vAIAAf4B/wJAAUEBcDAAA38B/wNNAYcEAAOAAf8DNwFYLAAB/gEAAf4B/wFXAUMBVwGHBAAB/wEAAv8B + OQE1ATkBWHAAAkABQQFwAgAC/wJKAXIBvDAAAz8BaANNAYcEAANVAZcDNwFYLAABQgE8AUIBaAFXAUMB + VwGHBAABZAFFAWQBlwE5ATUBOQFYcgAB/gH/AkUBSQGAAkoBcgG8AksBdgG+JAADLwFIA4AB/wOAAf8D + gAH/A38B/wOAAf8DfwH/A4AB/wNfAa4cAAEwAS4BMAFIAf8BAAP/AQAD/wEAAv8B/gEAAf4C/wEAAv8B + /gEAAf4C/wEAAv8BegFDAXoBrmQAAzoBYAIAAf4B/wgAAkoBeAHAAkkBfAHDKAADGgEkA4AB/wQAAyQB + NAN/Af8sAAMaASQB/wEAAv8EAAMkATQB/gEAAf4B/24AAv8CSQFRAY8IAAMgAS0CAAH+Af8DIAEuKAAD + gAH/AzYBVgQAA38B/zAAAf8BAAL/ATgBNQE4AVYEAAH+AQAB/gH/bAADJgE4AxcBHwwAAxsEJgE4KAAD + gAH/A0cBeAQAA38B/wMvAUgsAAH/AQAC/wFNAUABTQF4BAAB/gEAAf4B/wEwAS4BMAFI/wD/AP8AXwAD + EQEWATsCPAFkAz0BZwQCDAADJQE3AUECQgFyAy4BR9AAAVUBVwFaAbEBDAGfAv8BFwG9Av8BHgHQAv8B + HgHQAv8BDgGmAv8BEQGRAfMB/gELAZ0C/wEfAY8B2AH5ARkBwgL/AR4B0gL/ARsByQL/ARIBrwL/AREB + kQHzAf4DHgEqwAADTwGZAR0BzgL/ARIBrgL/AR4B0gL/AR4B0gL/AR4B0gL/AR4B0gL/AQ0BogL/AR4B + 0gL/ARIBsgL/AR4B0gL/AR4B0gL/AR4B0gL/ARgBuwL/ARsByQL/AQ4BpAL/wAADUQGeARwBzAL/ARYB + ugL/ARkBxgL/AR4B0gL/AR4B0gL/ARsBywL/ARIBsgL/AR4B0gL/AQsBnwL/AR4B0gL/AR4B0gL/AR0B + 0QL/AQ4BpQL/AR0B0AL/AQ4BpAL/FAADKQE+PAADCAEKAyABLTAAAw4BEgM6AWADKwFCDAADJQE2AzoB + YBQAAx8BLAEOAaUC/wEdAdEC/wENAaUC/wESAbAC/wEdAdEC/wEMAZ8C/wEeAdIC/wEeAdIC/wEbAcoC + /wEQAawC/wESAbAC/wEcAc0C/wEVAboC/wEbAcgC/wELAZ0C/xgAA8AB/z0AAZkBAAH/NAADVQGgA38B + /wwAA00BjwN/Af8UAAMQARUBDQGiAv8BCwGeAv8BHQHQAv8BEAGtAv8BEAGrAv8BHQHRAv8BJgGkAv8B + KgGPAv8BHgHSAv8BDgGoAv8BHgHSAv8BDQGkAv8BGwHKAv8BCwGeAv8BKwGFAcIB9RgAA8AB/zwAAVEB + XwFRAbwBJgEnASYBOTQAA04BkgNsAdsIAAN/Af8DOgFgGAADSQGHAQsBnQL/AQsBnQL/AREBrwL/ARMB + tQL/AUABTQFSAf8BPwE0AcUB/wFAATUBqgH/AUQBOgFCAf8BHQHRAv8BDQGjAv8BFgG9Av8BCwGeAv8B + CwGdAv8cAAPAAf8DwAH/A8AB/wPAAf8wAAMuAUcBAAGZAQAB/wEAAZkBAAH/AQABmQEAAf8DFgEeLAAD + SgGIA28B4wNHBH8B/xwAAVUBaQF5AdoBHQHRAf4B/wEdAdEB/gH/AR0B0QH+Af8BHQHRAf4B/wFEAToB + OQH/ATUBcAGBAf8BLwGHAZ8B/wFDATsBOgH/ASwBkwGuAf8BHQHRAf4B/wEdAdEB/gH/AR0B0QH+Af8B + EAGqAf4B/yAAA8AB/wQAA0gBgwPAAf8xAAGZAQAB/wQAAzMBUAFCAXIBQgHdAxABFSgAAwkBDAN/Af8D + gAH/A0EBcBwAAUwBbwGJAeMBHQHRAf4B/wEdAdEB/gH/AR0B0QH+Af8BHQHRAf4B/wEdAdEB/gH/AR0B + 0QH+Af8BHQHRAf4B/wEdAdEB/gH/AR0B0QH+Af8BHQHRAf4B/wEdAdEB/gH/AR0B0QH+Af8BEQGsAf4B + /yAAA8AB/wwAA8AB/ywAAVEBVwFRAaoBLQEuAS0BRgkAAZkBAAH/LAADQQFwA4AB/wNFAXkcAANSAaAB + HQHRAf4B/wEVAbsB/gH/AQ0BpAH+Af8BNgFtAXwB/wEfAcYB7wH/AR0B0QH+Af8BHQHRAf4B/wEeAcoB + 9QH/AS0BkAGpAf8BHQHRAf4B/wEKAZwB/gH/AR0B0QH+Af8BCwGeAf4B/yAAA8AB/wgAA1IBmwPAAf8s + AAM7AWMDRgF/BAABQAFBAUABcAEAAZkBAAH/LAADfgH/A0cBgANyAeoDRwF+GAADEAIVAbgB/gH/AR0B + 0QH+Af8BHQHRAf4B/wFEAToBOQH/AUABSQFNAf8BHQHRAf4B/wEdAdEB/gH/ASwBlAGvAf8BRAE6ATkB + /wEdAdEB/gH/AR0B0QH+Af8BHQHRAf4B/wFIAXUBkgHnJAADwAH/A8AB/wPAAf8DNQFVMQABmQEAAf8B + AAGZAQAB/wEAAZkBAAH/AUABQQFAAXEoAAM6AWADfgH/CAADbwHhA0oBhxgAAR0BjQHbAfoBHgHSAv8B + HgHSAv8BHgHSAv8BHgHSAv8BHgHSAv8BHgHSAv8BHgHSAv8BHgHSAv8BHgHSAv8BHgHSAv8BEgGuAv8D + DQERnAADfwH/A00BjwgAAzYBWQN+Af8DFQEcFAADAwEEAQwBnwL/AR4B0gL/AR4B0gL/AR4B0gL/AR4B + 0gL/AR4B0gL/AR4B0gL/AR4B0gL/AR4B0gL/ARgBwAL/AVACUQGdoAADQQFwAyoBPwwAAzEBTQNBAXAY + AAMJAQwBCwGdAv8BHAHKAv8BHgHSAv8BHgHSAv8BHgHSAv8BHgHSAv8BHgHSAv8BEAGpAv8DTQGS4AAB + QAJBAXABCwGcAf4B/wEMAZ4B/gH/AQ4BogH+Af8BCgGcAf4B/wFYAV0BYwHFAwUBBv8A0QABQgFNAT4H + AAE+AwABKAMAAUADAAEgAwABAQEAAQEGAAEBFgAD/wEABv8CAAb/AgAC/wH5AT8B+QE/AgAB4wGfAfkB + PwH5AT8CAAHxAZ8B+QE/AfkBPwIAAfgBHwHwAQ8B8AEPAgAB/AE/AfABBwHwAQcCAAH+AT8B/AGfAfwB + nwIAAf4BPwH8AZ8B/AGfAgAB/gEfAfABBwHwAQcCAAH8Ac8B/AGfAfwBnwIAAfwBxwH+AV8B/gFfAgAB + /AHnAf4BTwH+AU8CAAb/AgAG/wIABv8CAAHhAccG/wGAAQAG/wIABv8CAAH7Af8B+QH/AeMBnwIAAf0B + /wH9Af8B8wGfAgAB/QH/AfwB/wH5AZ8BgAEBAfwBPwH8AR8B/AE/AYABAQH+AZ8B/gGPAfwBPwGAAQEB + /gHvAf4BbwH+AT8BgAEBAf4BzwH+AU8B/gEfAYABAQH/AQ8B/wEPAfwBzwHAAQEE/wH8AccBwAEDBP8B + /AHnAeABBwb/AfgBDw7/Cw== + + + + True + + + + + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGqL+AhWg/i4Rn/6CDp7+zwud/v4KnP7/ + Cpz+/wqc/v8KnP7/Cpz+/wqc/v8KnP7/Cpz+/wyd/uISn/5aI6b+AQAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAWof5SDZ7+3Qqc/v8KnP7/Cpz+/wqc/v8KnP7/Cpz+/wqc/v8KnP7/C53+/g+e/tQRn/6H + F6H+MySm/gMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACyp/gsWof5kDp7+ywud/v4KnP7/ + Cpz+/wud/v8Oov7/Eav+/xSz/v8WuP7/F7n+/xW2/v8Srv7/DaD+/wqc/v8Lnf7+EJ7+mBKf/oQPnv6c + C53+pAyd/qQPnv6XFKD+fheh/o0Lnf79Cpz+/wyg/v8Rrf7/Fbb+/xe5/v8WuP7/FLP+/xGs/v8Oo/7/ + C53+/wqc/v8KnP7/C53+/w6e/tEUoP5qI6b+DQAAAAAAAAAAAAAAAAAAAAAAAAAAFKD+VBCf/uEKnP7/ + Cpz+/wud/v8Rq/7/F7z+/xzL/v8e0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/Hc7+/xKy/v8Knf7/ + Cpz+/wqc/v8KnP7/Cpz+/wqc/v8KnP7/Cpz+/wqc/v8KnP7/EbD+/x3O/v8d0f7/HdH+/x3R/v8d0f7/ + HdH+/x3R/v8e0f7/HMv+/xi9/v8SrP7/C57+/wqc/v8KnP7/DZ7+5ROg/l0AAAAAAAAAAAAAAAARn/6A + C53+/gqc/v8Nof7/Frf+/x3M/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/ + HdH+/x3R/v8Rr/7/Cpz+/wue/v8Utf7/Frj+/xa3/v8UtP7/DJ/+/wqc/v8QrP7/HdH+/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/Hc3+/xa4/v8Nov7/Cpz+/wud/v8Qn/6N + JKf+ARSg/jILnf78C53+/xSz/v8dzv7/HdH+/x3Q/v8ZwP7/G8f+/x3R/v8d0f7/HdH+/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/HdH+/x3R/v8by/7/C57+/wud/v8ayP7/HdH+/x3R/v8byv7/C53+/wud/v8ayf7/ + HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/xvI/v8Yv/7/HdD+/x3R/v8dz/7/ + FbX+/wud/v8Lnf7+G6P+QBuj/nIKnP7/Ear+/x7R/v8e0f7/GcD+/w+l/v8Oov7/G8b+/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/D6j+/wqc/v8Wvv7/HdH+/x3R/v8Xwf7/ + Cpz+/w6l/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/xzI/v8OpP7/ + DqP+/xi+/v8e0f7/HtH+/xKt/v8KnP7/E6D+gRKf/lMKnP7/DJ/+/xOx/v8Ppf7/Ear+/xm//v8e0P7/ + HtH+/x3M/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/D6n+/wqc/v8Wvf7/ + HdH+/x3R/v8XwP7/Cpz+/w6m/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/ + Hcz+/x7R/v8e0P7/GcD+/xGr/v8PpP7/E7H+/wyg/v8KnP7/FKD+YRyj/jQLnf7+Cpz+/xKs/v8byP7/ + HtH+/x3R/v8cyv7/Eq3+/w6n/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8byv7/ + C57+/wud/v8ax/7/HdH+/x3R/v8byv7/C53+/wud/v8ayP7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/D6n+/xKs/v8cyf7/HdH+/x7R/v8cyf7/Eq7+/wqc/v8KnP7/GKH+QxGf/n4KnP7/ + Eav+/x7R/v8d0f7/Hc/+/xa2/v8Mn/7/D6n+/xvL/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/ + HdH+/x3Q/v8Pq/7/Cpz+/w+o/v8d0f7/HdH+/x3R/v8d0f7/EKv+/wqc/v8PqP7/Hc/+/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HMz+/w+r/v8Mnv7/FbT+/x3O/v8d0f7/HtH+/xKt/v8KnP7/ + EJ/+jhCf/nsKnP7/EKf+/xrD/v8Vtv7/DaH+/w2h/v8WvP7/HdH+/x3R/v8Ttv7/D6r+/x3R/v8ayP7/ + F77+/x3R/v8d0f7/HdH+/xGv/v8KnP7/C57+/xrH/v8d0f7/HdH+/x3R/v8d0f7/Gsn+/wuf/v8KnP7/ + EKz+/x3Q/v8d0f7/HdH+/xe//v8ax/7/HdH+/xCs/v8Ts/7/HdD+/x3R/v8Yvv7/DaL+/w2g/v8Vtf7/ + GcP+/xGp/v8KnP7/EJ7+ihKf/iUMnf70Cpz+/wqc/v8Lnf7/E7H+/x3N/v8d0f7/Hc/+/xGv/v8MoP7/ + Gsf+/xzN/v8Mof7/C57+/xzN/v8d0f7/FLj+/wqc/v8Lnf7/F7/+/x3R/v8d0f7/HdH+/x3R/v8d0f7/ + HdH+/xjB/v8Lnv7/Cpz+/xO0/v8d0f7/Hc/+/wuf/v8MoP7/G8z+/xrJ/v8Mof7/EKz+/xzP/v8d0f7/ + Hc7+/xSy/v8Lnf7/Cpz+/wqc/v8Mnf74G6P+MQAAAAAQn/6pCpz+/w2g/v8awv7/HtH+/x3R/v8by/7/ + Dqj+/wuf/v8ZxP7/HdH+/xKz/v8Ln/7/GMH+/x3R/v8ax/7/C57+/wud/v8Vuf7/HdH+/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/HdH+/x3R/v8WvP7/C53+/wud/v8Zxf7/HdH+/xjD/v8MoP7/EbD+/x3R/v8Zxv7/ + DKD+/w6m/v8byv7/HdH+/x7R/v8axP7/DaL+/wqc/v8Qn/64AAAAACKm/gINnf7iCpz+/xnB/v8d0f7/ + HdH+/xe//v8MoP7/C5/+/xnE/v8d0f7/Gsf+/wud/v8WvP7/HdH+/x3R/v8Pqf7/Cpz+/xCr/v8d0f7/ + HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/Ea7+/wqc/v8Op/7/HdH+/x3R/v8Xv/7/ + C53+/xnF/v8d0f7/Gcb+/wyg/v8Mn/7/Fr3+/x3R/v8d0f7/GsT+/wqc/v8Mnf7rF6H+BjSt/gEPnv7W + Cpz+/xW0/v8ZwP7/D6r+/wud/v8Lnf7/F8H+/x3R/v8d0P7/Dqj+/wue/v8czv7/HdH+/xjD/v8Lnf7/ + C57+/xvJ/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/G8z+/wuf/v8KnP7/ + F8D+/x3R/v8d0P7/C5/+/w6m/v8d0P7/HdH+/xjD/v8Lnv7/Cpz+/w+p/v8Zv/7/Frb+/wqc/v8Onv7i + I6b+BAAAAAARn/5tCpz+/wqc/v8KnP7/Cpz+/wqc/v8Ttf7/HdH+/x3R/v8UuP7/C53+/wue/v8czf7/ + HdH+/xCt/v8KnP7/EbD+/x3R/v8d0f7/H8v+/yiY/v8wcv7/M2T+/zJo/v8tgf7/I7P+/x3R/v8d0f7/ + HdH+/xOz/v8KnP7/D6r+/x3R/v8cz/7/C5/+/wqc/v8Ttf7/HdH+/x3R/v8Ut/7/Cpz+/wqc/v8KnP7/ + Cpz+/wuc/v8UoP59AAAAAAAAAAAKnP4BEJ/+bBGf/tILnf78Cpz+/wqc/v8ZxP7/HdH+/xjC/v8Lnv7/ + Cpz+/wqc/v8Ssf7/Fbr+/wud/v8Lnf7/Gcb+/x7R/v8ootb/OUH+/zwx/v88Mf7/PDH+/zwx/v88Mf7/ + PDL+/zJo9/8gx/L/HdH+/xrJ/v8Lnf7/Cp3+/xS4/v8Ss/7/Cpz+/wqc/v8Lnf7/F8D+/x3R/v8ax/7/ + Cpz+/wqc/v8Lnf73D57+1BOg/ncvqv4DAAAAAAAAAAAAAAAAAAAAABeh/h0Lnf78Cpz+/wqc/v8Rrv7/ + E7X+/wuf/v8KnP7/DqX+/wud/v8KnP7/Cpz+/wqc/v8Ssf7/HdH+/yuevP9EPT7/QDWw/zwx/v88Mf7/ + PjPV/z0z5v88Mf7/PTL9/0E3iv88WWP/H8n0/x3R/v8TtP7/Cp3+/wqc/v8KnP7/Cp3+/w6m/v8KnP7/ + C57+/xO0/v8RsP7/Cpz+/wqc/v8Nnv7fHaT+BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABih/loKnP7/ + DJ/+/wqd/v8KnP7/Cpz+/wqc/v8Qq/7/Hc/+/xe+/v8PqP7/Dqb+/xS3/v8d0P7/IsHq/0JCRP9EOjn/ + RDo8/0E2kP8+M9v/QzlS/0M4Z/8+M9b/Qjh4/0Q6Ov9EOjn/NHqN/x3R/v8d0f7/Fbn+/w6m/v8OqP7/ + Fr3+/x3Q/v8Qrf7/Cpz+/wqc/v8KnP7/Cpz+/wud/v8Lnf7/IKX+JQAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAABGf/o4KnP7/E7H+/xfB/v8Qrf7/EK3+/xa9/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/ + NHuP/0Q6Of9EOjn/RDo5/0Q6Of9EOjn/RDo5/0Q6Of9EOjn/RDo5/0Q6Of9EOjn/RD09/yLA6f8d0f7/ + HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/F7/+/xGu/v8Qrf7/F7/+/w+l/v8KnP7/E5/+VwAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAABCe/rMKnP7/Frn+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/ + HdH+/x3R/v8dzvr/QEhM/0Q6Of9EOjn/RDo5/0Q6Of9EOjn/RDo5/0Q6Of9EOjn/RDo5/0Q6Of9EOjn/ + RDo5/y6OqP8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/xKt/v8KnP7/ + E6D+fQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+e/c0Jm/3/GL/9/xzQ/f8c0P3/HND9/xzQ/f8c0P3/ + HND9/xzQ/f8c0P3/HND9/xzQ/f8ivOP/Qzo6/0M5OP9DOTj/P0tR/zGCmP8nqMr/Irvi/yS02v8rmLX/ + N2l4/0M8PP9DOTj/Qzk4/zZtfv8c0P3/HND9/xzQ/f8c0P3/HND9/xzQ/f8c0P3/HND9/xzQ/f8c0P3/ + HND9/xSz/f8Jm/3/DZ39lwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6d/dsJm/3/GcL9/xzQ/f8c0P3/ + HND9/xzQ/f8c0P3/HND9/xzQ/f8c0P3/HND9/xzQ/f8ktNr/Qzo5/z1VXv8qnLr/Hc77/xzQ/f8c0P3/ + HND9/xzQ/f8c0P3/HND9/yG+5v8zeIz/QkBC/zhldP8c0P3/HND9/xzQ/f8c0P3/HND9/xzQ/f8c0P3/ + HND9/xzQ/f8c0P3/HND9/xW2/f8Jm/3/FqD9pAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyd/tsKnP7/ + GcL+/x3Q/v8d0f7/HdH+/x3Q/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8dz/v/I7bd/x3P+/8d0f7/ + HdH+/x3R/v8d0f7/HND9/xzQ/f8c0P3/HND9/xzQ/f8c0P3/IMTv/yG/6P8c0P3/HND9/xzQ/f8c0P3/ + HND9/xzQ/f8c0P3/HND9/xzQ/f8c0P3/HND9/xW3/f8Jm/3/G6P9pgAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAABOf/tEKnP7/GL/+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HND9/xzQ/f8c0P3/HND9/xzQ/f8c0P3/HND9/xzQ/f8c0P3/ + HND9/xzQ/f8c0P3/HND9/xzQ/f8c0P3/HND9/xzQ/f8c0P3/HND9/xS0/f8Jm/3/C5z9mgAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAA2d/rkKnP7/Frr+/x3R/v8d0f7/HdH+/x3R/v8bzP7/Fr7+/xrK/v8d0f7/ + HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HND9/xzQ/f8c0P3/HND9/xzQ/f8c0P3/ + HND9/xzQ/f8c0P3/HND9/xzQ/f8c0P3/G8v9/xa+/f8by/3/HND9/xzQ/f8c0P3/HND9/xKv/f8Jm/3/ + EJ79gwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6d/pUKnP7/E7L+/x3Q/v8d0f7/HdH+/xzP/v8Mo/7/ + Cpz+/wug/v8by/7/HdH+/yDF7/8ey/f/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HND9/xzQ/f8c0P3/ + HND9/xzQ/f8c0P3/HND9/x3Q/f8hwen/HdD9/xzQ/f8bzf3/C6H9/wmb/f8Mov3/G879/xzQ/f8c0P3/ + HND9/w+m/f8Jm/3/D579XgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABOf/mUKnP7/D6b+/x3R/v8d0f7/ + HdH+/xrJ/v8KnP7/Cpz+/wqc/v8Xwv7/Irrg/0BHSv82b37/Hc/8/yHA5/8fyfT/HdH+/x3R/v8d0f7/ + HND9/xzQ/f8c0P3/HND9/x3Q/P8juuD/HdD9/yyVsP9CPj7/Kpy5/xzQ/f8Yxf3/CZv9/wmb/f8Jm/3/ + Gcb9/xzQ/f8c0P3/Hc/9/wud/f8Jm/3/FqD9LgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmh/icKnP7+ + Cpz+/xzM/v8d0f7/HdH+/xzQ/v8Qrf7/Cpz+/w6o/v8czv7/NXOD/0Q6Of9EOjn/P0xQ/0M7Ov8whp3/ + HdH+/x3R/v8d0f7/HND9/xzQ/f8c0P3/HND9/yWv0f9DOjn/P0tP/0M7O/9DOTj/P0pP/x3Q/f8cz/3/ + D6r9/wqc/f8Pq/3/HND9/xzQ/f8c0P3/GcH9/wmb/f8Mnf3oH6P9BgAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAACio/gMPnv7XCpz+/xa7/v8d0f7/HdH+/x3Q/v8c0P7/G8z+/xzQ/v8d0f7/OGVy/0Q6Of9EOjn/ + RDo5/0Q6Of8zeYv/HdH+/x3R/v8d0f7/HND9/xzQ/f8c0P3/HND9/ymhwP9DOTj/Qzk4/0M5OP9DOTj/ + Qz09/x3Q/f8c0P3/HND9/xvM/f8c0P3/HND9/xzQ/f8c0P3/ErD9/wmb/f8Onf2iAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAVoP6ACpz+/w+m/v8d0P7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/ + LJSv/0Q6Of9EOjn/RDo5/0M6Ov8op8f/HdH+/x3R/v8d0f7/HND9/xzQ/f8c0P3/HND9/yDF7v9BRUf/ + Qzk4/0M5OP9DOTj/N2x6/xzQ/f8c0P3/HND9/xzQ/f8c0P3/HND9/xzQ/f8czf3/C579/wqc/f8WoP1J + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXof4dC5z+9wqc/v8Zwf7/HdH+/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/HdD9/y+Kov8+UFb/PVNa/yuYtP8d0P3/HdH+/x3R/v8d0f7/HND9/xzQ/f8c0P3/ + HND9/xzQ/f8lsdT/Ol9p/z9LT/81dIX/H8fx/xzQ/f8c0P3/HND9/xzQ/f8c0P3/HND9/xzQ/f8Vtv3/ + CZv9/w2d/dkqqP0FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEp/+mwqc/v8Ppv7/ + HdD+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/ + HdH+/xzM/v8MoP7/Cpz+/x2j/mQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + Jab/Gwyd//MLnf//Frn//x7S//8e0v//HtL//x7S//8e0v//HtL//x7S//8e0v//HtL//x7S//8e0v// + HtL//x7S//8e0v//HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/HtH+/xKu/v8KnP7/Dp7+1B6k/gUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAABmi/m8Lnf7/C57+/xvG/v8d0f7/HdH+/x7S//8e0v//HtL//x7S//8e0v// + HtL//x7S//8e0v//HtL//x7S//8e0v//HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/GLz+/wqc/v8Lnf77FqH+PQAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABeh/gMPnv63Cpz+/w2i/v8cyv7/HdH+/x7S//8e0v// + HtL//x7S//8e0v//HtL//x7S//8e0v//HtL//x7S//8e0v//HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8aw/7/C57+/wqc/v8VoP6FAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfpP4VDp7+2gqc/v8OpP7/ + HMr+/x7S//8e0f//HtL//x7S//8e0f//HtL//x7S//8e0f//HtL//x7S//8e0f//HdH+/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/xrE/v8Mn/7/Cpz+/xCf/rQaov4F + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + FaD+Ig2e/uQKnP7/DaH+/xvF//8e0f//HtL//x7S//8e0v//HtL//x7S//8e0v//HtL//x7S//8e0v// + HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8e0f7/GL7+/wue/v8KnP7/ + DJ3+wiio/gwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAABWh/iQNnv7YCpz+/wue//8WuP//HdD//x7S//8e0v//HtL//x7S//8e0v// + HtL//x7S//8e0v//HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3N/v8TsP7/ + C53+/wqc/v8Pnv63GKL+DgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXof4UE6D+tgud//8Lnf//D6b//xnB//8e0f// + HtL//x7S//8e0f//HtL//x7S//8e0f//HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0P7/ + F7v+/w2h/v8KnP7/C53+/w+e/o4kpv4GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJqb+ARGf/2wMnf/v + C53//wud//8Ppf//F7r//xzL//8e0f//HtL//x7S//8e0v//HdH+/x3R/v8d0f7/HdH+/x3R/v8e0f7/ + G8j+/xW2/v8Nov7/Cpz+/wqc/v8Onv7dGqL+SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAgpf4aEZ/+lAyd/vUKnP7/Cpz+/wud/v8Ppf7/E7D+/xa5/v8Yvv7/GcD+/xnA/v8Xvf7/ + Fbf+/xKu/v8Oo/7/Cpz+/wqc/v8KnP7/DJ3+6ROg/ngipv4MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+k/hkPnv52Dp3+0Quc/v8KnP7/Cpz+/wqc/v8KnP7/ + Cpz+/wqc/v8KnP7/Cpz+/wqc/v8KnP7/C5z+/A+e/r8Pnv5iHaP+DAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACSm/h4Rn/5a + Ep/+iw+e/q8Pnv7GEJ7+0g6d/s8Sn/7DDp3+qROf/oIUoP5PHqT+EgAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAP///////+AE////////4AT/gB/4Af/gBPgAB/AAH+AE4AAAAAAH4ATAAAAA + AAPgBIAAAAAAAOAEAAAAAAAA4AQAAAAAAADgBAAAAAAAAOAEAAAAAAAA4AQAAAAAAADgBAAAAAAAAOAE + AAAAAAAA4ASAAAAAAAHgBAAAAAAAAOAEAAAAAAAA4ASAAAAAAAHgBIAAAAAAAeAE4AAAAAAH4ATgAAAA + AAfgBOAAAAAAB+AE4AAAAAAH4ATgAAAAAAfgBOAAAAAAB+AE4AAAAAAH4ATgAAAAAAfgBOAAAAAAB+AE + 4AAAAAAH4ATgAAAAAAfgBOAAAAAAB+AE4AAAAAAP4ATwAAAAAA/gBPAAAAAAD+AE+AAAAAAf4AT4AAAA + AB/gBPwAAAAAP+AE/AAAAAB/4AT+AAAAAH/gBP8AAAAA/+AE/4AAAAH/4AT/wAAAA//gBP/gAAAP/+AE + //gAAB//4AT//gAAf//gBP//wAP//+AE////////4AT////////gBCgAAAAgAAAAQAAAAAEAIAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCl/xYToP9F + E6D/ZA2e/3IRn/9nE6D/OSOm/wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAao/8BE6D/Nw+e/2YNnv9y + Ep//ZRKf/0chpv8YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFqH/JA+e/4ENnv/T + C53//wud//8Lnf//DJ7//wud//8Lnf//DZ3/1xOg/zUkp/8IEZ//GBKf/xcsqv8GHaT/MA6e/9MLnf// + C53//wye//8Lnf//C53//wud//8Onv/WEZ//hRmi/ygAAAAAAAAAAAAAAAAAAAAAFKD/JhOg/7ELnf/+ + DJ///xKv//8Xvf//G8j//x7Q//8e0v//HtD//xnD//8Opv//C53/+wud//4Lnf//C53//wud//0Lnf/5 + DqX//xnC//8dz///HtL//x7Q//8byf//F77//xKv//8Mn///C53//g+e/7YToP8qAAAAABOg/y4Lnf/x + DaP//xe8//8dz///HtH//x7S//8e0v//HtL//x7S//8e0v//HtL//xzN//8Mof//D6f//xjA//8YwP// + D6j//wyg//8czP//HtL//x7S//8e0v//HtL//x7S//8e0v//HtH//x3P//8Xvf//DqP//wud//QZov81 + E6D/mQ2i//8dzv//HMn//xKu//8Xuf//HtL//x7S//8e0v//HtL//x7S//8e0v//HtL//xO0//8Nov// + HtL//x7S//8NpP//ErL//x7S//8e0v//HtL//x7S//8e0v//HtL//x7S//8Yu///Eq3//xvJ//8dz/// + DqT//w+e/6MQn/+AC53//xGr//8UtP//HMj//x3N//8ZxP//HtL//x7S//8e0v//HtL//x7S//8e0v// + E7T//w2i//8e0v//HtL//w2k//8Ssv//HtL//x7S//8e0v//HtL//x7S//8e0v//GsX//x3M//8cyf// + FbX//xGr//8Lnv//EJ//iRCf/54Oo///HMz//x7Q//8Wuv//DqX//xnG//8d0f//HtL//x7S//8e0v// + HtL//xvL//8MoP//ErL//x7S//8e0v//E7T//wuf//8byv//HtL//x7S//8e0v//HtL//x3R//8ax/// + DqX//xa5//8d0P//Hc3//w6k//8Pnv+pDZ7/mg2h//8UtP//DqT//xSz//8czv//GMP//xCt//8czf// + E7T//x3Q//8czv//DaX//w2i//8czf//HtL//x7S//8czv//DaP//w2j//8czf//HdH//xO0//8bzP// + Ea///xjB//8dz///FLT//w6j//8UtP//DaL//w+e/6QXof8sC53//w6l//8bx///HdH//xW7//8Npf// + HMz//xKw//8Rrv//HdH//xGu//8Mn///Gsn//x7S//8e0v//HtL//x7S//8byv//DKD//xCs//8d0f// + ErD//xGv//8czf//Dqb//xW6//8d0f//G8j//w+m//8Lnf//FaH/NhOg/0QLnf//G8f//xvM//8Rrv// + DaP//xvM//8Zxf//DaX//x3R//8YxP//C53//xa9//8e0v//HtL//x7S//8e0v//HtL//x7S//8Xv/// + C53//xjC//8d0f//Dqb//xjE//8czP//DaT//xCs//8by///G8j//wud//8Pnv9MF6H/FQud//ENov// + C57//wue//8ayP//HdD//w6l//8Qrf//HtL//xCr//8Opv//HdH//x7P//8mpP//K4r//yqP//8jtP// + HtL//x3R//8OqP//D6n//x7S//8Rr///DaT//x3P//8byv//C57//wue//8Nov//C53/9R+k/xwAAAAA + EJ//MBCf/7MLnP//DaT//x3Q//8Rr///C53//wue//8Ssf//C53//xa9//8kueD/OUzU/z0y//89Mv7/ + PTL//zwy//8za9D/H834/xe///8Lnf//ErD//wuf//8Lnf//EK3//x3Q//8Opv//C53//w6e/6UUoP82 + AAAAAAAAAAAAAAAAEZ//hwue//8Lnf//C57//wud//8WvP//Ea///w2i//8Ttf//H8r1/0BNUv9EOkv/ + PzTF/0E2mP9ANar/PzW2/0Q6Qv81dYj/HdH//xS2//8No///Ea7//xa9//8Lnv//C57//wud//8Lnf// + E6D/YwAAAAAAAAAAAAAAAAAAAAAPnv66Eav+/xjC/v8Uuf7/Gsj+/x3R/v8d0f7/HdH+/x3R/v8ujqf/ + RDo5/0Q6Of9EOjn/RDo5/0Q6Of9EOjn/RDo5/0Q7O/8iveX/HdH+/x3R/v8d0f7/HdH+/xrJ/v8Uuf7/ + F8H+/w6k/v8Qn/6VAAAAAAAAAAAAAAAAAAAAAA2d/toTsv7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/ + HdH+/zljb/9EOjn/QUFE/zVwgf8ujab/L4ef/zlhbv9DOzr/RDo5/yyTrv8d0f7/HdH+/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/EKr+/wyd/rYAAAAAAAAAAAAAAAAAAAAAC5z+5xS1/v8d0f7/HdH+/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/NHWH/y+Jof8fyPP/HdH+/x3R/v8d0f7/HdH+/yO54P81c4X/K5m3/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/HdH+/x3R/v8Rrv7/FKD+xAAAAAAAAAAAAAAAAAAAAAAPnv7jE7T+/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/xGs/v8Nnf69AAAAAAAAAAAAAAAAAAAAAAyd/ssRrv7/ + HdH+/x3R/v8czv7/E7X+/xjD/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/HdH+/x3R/v8d0f7/ + HdH+/x3R/v8d0f7/HdH+/xjE/v8Ttf7/G83+/x3R/v8d0f7/D6f+/w2d/qYAAAAAAAAAAAAAAAAAAAAA + Dp3+oA6l/v8d0f7/HdH+/xW7/v8KnP7/DaT+/x/H8f82bXz/IMTt/x/G7/8d0f7/HdH+/x3R/v8d0f7/ + HdH+/x7K9f8ezPj/LZCp/yuXsv8d0f7/Dab+/wqc/v8Uuf7/HdH+/x3Q/v8Lnv7/D57+fAAAAAAAAAAA + AAAAAAAAAAASn/5iCpz+/xvL/v8d0f7/G8v+/xCt/v8Wvf7/Lo6n/0Q6Of9BQkP/Qz09/x7L9v8d0f7/ + HdH+/x3R/v8d0f7/MISZ/0JBQ/9DOjr/QkFC/x3Q/f8Wv/7/EKz+/xrK/v8d0f7/GcT+/wqc/v8Rn/4+ + AAAAAAAAAAAAAAAAAAAAACWn/hUKnP75Fbj+/x3R/v8d0f7/HdH+/x3R/v8pn73/RDo5/0Q6Of9ASU3/ + Hc/7/x3R/v8d0f7/HdH+/x3R/v8slK//RDo5/0Q6Of89Ulj/HdH+/x3R/v8d0f7/HdH+/x3R/v8SsP7/ + DJ3+5yOm/gMAAAAAAAAAAAAAAAAAAAAAAAAAAA6d/qUMof7/HM/+/x3R/v8d0f7/HdH+/x3R/f8slrL/ + M3uO/yO43f8d0f7/HdH+/x3R/v8d0f7/HdH+/x3Q/P8tkav/M32Q/yK84/8d0f7/HdH+/x3R/v8d0f7/ + G8v+/wud/v8Rn/6BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHaT/Jwud//oUtf//HtL//x7S//8e0v// + HtL//x7S//8e0v//HtL//x7S//8e0v//HtL//x7S//8e0v//HtL//x7S//8e0v//HtL//x7S//8e0v// + HtL//x7R//8Srv//DJ3/7CGl/xEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE6D/ggud//8Zw/// + HtL//x7S//8e0v//HtL//x7S//8e0v//HtL//x7S//8e0v//HtL//x7S//8e0v//HtL//x7S//8e0v// + HtL//x7S//8e0v//F7z//wud//8Sn/9eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjpv8E + D57/vQyf//8axv//HtL//x7S//8e0v//HtL//x7S//8e0v//HtL//x7S//8e0v//HtL//x7S//8e0v// + HtL//x7S//8e0v//HtL//xjA//8Lnf//EJ//nQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAVoP8PDZ7/ygyf//8Yv///HtL//x7S//8e0v//HtL//x7S//8e0v//HtL//x7S//8e0v// + HtL//x7S//8e0v//HtL//x7R//8Wuf//C53//w2d/68pqP8GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAbo/8MD57/rgud//8Srv//HMr//x7S//8e0v//HtL//x7S//8e0v// + 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 + + + \ No newline at end of file diff --git a/solution/Safetensors.Inspector.UX/metadata.json b/solution/Safetensors.Inspector.UX/metadata.json new file mode 100644 index 0000000..f1a6d4c --- /dev/null +++ b/solution/Safetensors.Inspector.UX/metadata.json @@ -0,0 +1,217 @@ +[ + { + "PropertyName": "ss_sd_model_name", + "Description": "Model Name", + "MetadataType": null + }, + { + "PropertyName": "ss_clip_skip", + "Description": "Clip Skip", + "MetadataType": null + }, + { + "PropertyName": "ss_num_train_images", + "Description": "Number of Training Images", + "MetadataType": null + }, + { + "PropertyName": "ss_tag_frequency", + "Description": "Tag Frequency", + "MetadataType": "TagFrequency" + }, + { + "PropertyName": "ss_epoch", + "Description": "Epochs", + "MetadataType": null + }, + { + "PropertyName": "ss_face_crop_aug_range", + "Description": "Face Crop Augmentation Range", + "MetadataType": null + }, + { + "PropertyName": "ss_full_fp16", + "Description": "Full FP16", + "MetadataType": null + }, + { + "PropertyName": "ss_gradient_accumulation_steps", + "Description": "Gradient Accumulation Steps", + "MetadataType": null + }, + { + "PropertyName": "ss_gradient_checkpointing", + "Description": "Gradient Checkpointing", + "MetadataType": null + }, + { + "PropertyName": "ss_learning_rate", + "Description": "Learning Rate", + "MetadataType": null + }, + { + "PropertyName": "ss_lowram", + "Description": "Low RAM", + "MetadataType": null + }, + { + "PropertyName": "ss_lr_scheduler", + "Description": "Learning Rate Scheduler", + "MetadataType": null + }, + { + "PropertyName": "ss_lr_warmup_steps", + "Description": "Learning Rate Warmup Steps", + "MetadataType": null + }, + { + "PropertyName": "ss_max_grad_norm", + "Description": "Max Gradient Norm", + "MetadataType": null + }, + { + "PropertyName": "ss_max_token_length", + "Description": "Max Token Length", + "MetadataType": null + }, + { + "PropertyName": "ss_max_train_steps", + "Description": "Max Training Steps", + "MetadataType": null + }, + { + "PropertyName": "ss_min_snr_gamma", + "Description": "Min SNR Gamma", + "MetadataType": null + }, + { + "PropertyName": "ss_mixed_precision", + "Description": "Mixed Precision", + "MetadataType": null + }, + { + "PropertyName": "ss_network_alpha", + "Description": "Network Alpha", + "MetadataType": null + }, + { + "PropertyName": "ss_network_dim", + "Description": "Network Dimension", + "MetadataType": null + }, + { + "PropertyName": "ss_network_module", + "Description": "Network Module", + "MetadataType": null + }, + { + "PropertyName": "ss_new_sd_model_hash", + "Description": "New SD Model Hash", + "MetadataType": null + }, + { + "PropertyName": "ss_noise_offset", + "Description": "Noise Offset", + "MetadataType": null + }, + { + "PropertyName": "ss_num_batches_per_epoch", + "Description": "Number of Batches per Epoch", + "MetadataType": null + }, + { + "PropertyName": "ss_cache_latents", + "Description": "Cache Latents", + "MetadataType": null + }, + { + "PropertyName": "ss_caption_dropout_every_n_epochs", + "Description": "Caption Dropout Every N Epochs", + "MetadataType": null + }, + { + "PropertyName": "ss_caption_dropout_rate", + "Description": "Caption Dropout Rate", + "MetadataType": null + }, + { + "PropertyName": "ss_caption_tag_dropout_rate", + "Description": "Caption Tag Dropout Rate", + "MetadataType": null + }, + { + "PropertyName": "ss_dataset_dirs", + "Description": "Dataset Directories", + "MetadataType": "DatasetDirs" + }, + { + "PropertyName": "ss_num_epochs", + "Description": "Number of Epochs", + "MetadataType": null + }, + { + "PropertyName": "ss_num_reg_images", + "Description": "Number of Regression Images", + "MetadataType": null + }, + { + "PropertyName": "ss_optimizer", + "Description": "Optimizer", + "MetadataType": null + }, + { + "PropertyName": "ss_output_name", + "Description": "Output Name", + "MetadataType": null + }, + { + "PropertyName": "ss_prior_loss_weight", + "Description": "Prior Loss Weight", + "MetadataType": null + }, + { + "PropertyName": "ss_sd_model_hash", + "Description": "SD Model Hash", + "MetadataType": null + }, + { + "PropertyName": "ss_sd_scripts_commit_hash", + "Description": "SD Scripts Commit Hash", + "MetadataType": null + }, + { + "PropertyName": "ss_seed", + "Description": "Seed", + "MetadataType": null + }, + { + "PropertyName": "ss_session_id", + "Description": "Session ID", + "MetadataType": null + }, + { + "PropertyName": "ss_text_encoder_lr", + "Description": "Text Encoder Learning Rate", + "MetadataType": null + }, + { + "PropertyName": "ss_unet_lr", + "Description": "UNet Learning Rate", + "MetadataType": null + }, + { + "PropertyName": "ss_v2", + "Description": "Version 2", + "MetadataType": null + }, + { + "PropertyName": "sshs_legacy_hash", + "Description": "SSHS Legacy Hash", + "MetadataType": null + }, + { + "PropertyName": "sshs_model_hash", + "Description": "SSHS Model Hash", + "MetadataType": null + } +] diff --git a/solution/Safetensors.Inspector.sln b/solution/Safetensors.Inspector.sln new file mode 100644 index 0000000..aa53b6a --- /dev/null +++ b/solution/Safetensors.Inspector.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34622.214 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Safetensors.Core", "Safetensors.Core\Safetensors.Core.csproj", "{54B18D7C-81C8-4A7D-B76A-7D7D7E56DCA4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Safetensors.Storage", "Safetensors.Storage\Safetensors.Storage.csproj", "{5F68C471-F3AB-4CE9-9B9B-4C4B7E64620B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Safetensors.Inspector.UX", "Safetensors.Inspector.UX\Safetensors.Inspector.UX.csproj", "{C8AE8BE9-3348-4266-ACE7-41135095C77C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {54B18D7C-81C8-4A7D-B76A-7D7D7E56DCA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {54B18D7C-81C8-4A7D-B76A-7D7D7E56DCA4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {54B18D7C-81C8-4A7D-B76A-7D7D7E56DCA4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {54B18D7C-81C8-4A7D-B76A-7D7D7E56DCA4}.Release|Any CPU.Build.0 = Release|Any CPU + {5F68C471-F3AB-4CE9-9B9B-4C4B7E64620B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F68C471-F3AB-4CE9-9B9B-4C4B7E64620B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F68C471-F3AB-4CE9-9B9B-4C4B7E64620B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F68C471-F3AB-4CE9-9B9B-4C4B7E64620B}.Release|Any CPU.Build.0 = Release|Any CPU + {C8AE8BE9-3348-4266-ACE7-41135095C77C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8AE8BE9-3348-4266-ACE7-41135095C77C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8AE8BE9-3348-4266-ACE7-41135095C77C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C8AE8BE9-3348-4266-ACE7-41135095C77C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E916A7EE-2FD7-4833-86C8-17746EE48951} + EndGlobalSection +EndGlobal diff --git a/solution/Safetensors.Storage/ConfigurationReader.cs b/solution/Safetensors.Storage/ConfigurationReader.cs new file mode 100644 index 0000000..463cbe9 --- /dev/null +++ b/solution/Safetensors.Storage/ConfigurationReader.cs @@ -0,0 +1,24 @@ +using Safetensors.Core; +using System.Text.Json; + +namespace Safetensors.Storage +{ + /// + /// Configuration reader + /// + public class ConfigurationReader : IConfigurationReader + { + /// + /// Load metadata property configuration + /// + /// List of Safetensor metadata properties + public async Task?> LoadMetadataPropertyConfigAsync() + { + using var reader = new StreamReader(@"./metadata.json"); + + var results = await JsonSerializer.DeserializeAsync>(reader.BaseStream); + + return results; + } + } +} diff --git a/solution/Safetensors.Storage/Safetensors.Storage.csproj b/solution/Safetensors.Storage/Safetensors.Storage.csproj new file mode 100644 index 0000000..faaad8a --- /dev/null +++ b/solution/Safetensors.Storage/Safetensors.Storage.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + none + + + + + + + diff --git a/solution/Safetensors.Storage/SafetensorsReader.cs b/solution/Safetensors.Storage/SafetensorsReader.cs new file mode 100644 index 0000000..5956dee --- /dev/null +++ b/solution/Safetensors.Storage/SafetensorsReader.cs @@ -0,0 +1,107 @@ +using Safetensors.Core; +using System.Text.Json.Nodes; + +namespace Safetensors.Storage +{ + /// + /// Safeensor reader + /// + public class SafetensorReader : ISafetensorsReader + { + /// + /// Safetensor header size in bytes + /// + private const int HEADER_SIZE = 8; + + /// + /// Metadata configuration + /// + private IList? _metadataConfig { get; set; } + + /// + /// Initialize the Safetenso reader + /// + public async Task InitializeAsync() + { + var cr = new ConfigurationReader(); + _metadataConfig = await cr.LoadMetadataPropertyConfigAsync(); + } + + /// + /// Async read of .safetensor file + /// + /// File path of the .safetensor file + /// SafetensorFile object + public async Task ReadAsync(string path) + { + if (File.Exists(path)) + { + // open file + using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + + // get size of the header from first 8 bytes + byte[] arrHeaderSize = new byte[HEADER_SIZE]; + await fs.ReadAsync(arrHeaderSize); + + // get header size + UInt64 headerSize = BitConverter.ToUInt64(arrHeaderSize, 0); + + // reader header into buffer + byte[] arrHeader = new byte[headerSize]; + await fs.ReadAsync(arrHeader); + + // convert header to string + var strHeader = System.Text.Encoding.ASCII.GetString(arrHeader); + + // create safetensor file object + var sf = new SafetensorsFile(path, strHeader); + + // parse the header + JsonNode? jsonNode = JsonNode.Parse(strHeader); + if (jsonNode != null) + { + // get the default __metadata__ property + var nodeMetadata = jsonNode[sf.Metadata.PropertyName]; + + // if property exists and metadata configuration exists + if ((nodeMetadata != null) && (_metadataConfig != null)) + { + await PopulateMetadataProperties(sf.Metadata, nodeMetadata); + } + } + + return sf; + } + + return null; + } + + /// + /// Populate metadata properties for the safetensor file object + /// + /// Safetensor metadata + /// Json node data loaded from the safetensor file + /// + private async Task PopulateMetadataProperties(ISafetensorsMetadata metadata, JsonNode nodeMetadata) + { + await Task.Run(() => + _metadataConfig?.ToList().ForEach(p => + { + // try to get string value + var data = nodeMetadata[p.PropertyName]?.ToString(); + + // check if data exists and add if it does + if (!string.IsNullOrEmpty(data)) + metadata.Properties.Add( + new SafetensorsMetadataProperty + { + PropertyName = p.PropertyName, + Description = p.Description, + Value = data, + MetadataType = p.MetadataType + }); + }) + ); + } + } +}