diff --git a/ImgFormatHelper.sln b/ImgFormatHelper.sln
new file mode 100644
index 0000000..49ab6bd
--- /dev/null
+++ b/ImgFormatHelper.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31129.286
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImgFormatHelper", "ImgFormatHelper\ImgFormatHelper.csproj", "{02B056CA-BC9B-4BE3-8730-F7506DABD8BB}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {02B056CA-BC9B-4BE3-8730-F7506DABD8BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {02B056CA-BC9B-4BE3-8730-F7506DABD8BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {02B056CA-BC9B-4BE3-8730-F7506DABD8BB}.Debug|x86.ActiveCfg = Debug|x86
+ {02B056CA-BC9B-4BE3-8730-F7506DABD8BB}.Debug|x86.Build.0 = Debug|x86
+ {02B056CA-BC9B-4BE3-8730-F7506DABD8BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {02B056CA-BC9B-4BE3-8730-F7506DABD8BB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {02B056CA-BC9B-4BE3-8730-F7506DABD8BB}.Release|x86.ActiveCfg = Release|x86
+ {02B056CA-BC9B-4BE3-8730-F7506DABD8BB}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {D7107476-F580-470C-9D9A-B9F14DD1E31F}
+ EndGlobalSection
+EndGlobal
diff --git a/ImgFormatHelper/App.config b/ImgFormatHelper/App.config
new file mode 100644
index 0000000..5754728
--- /dev/null
+++ b/ImgFormatHelper/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ImgFormatHelper/EasyFilters.cs b/ImgFormatHelper/EasyFilters.cs
new file mode 100644
index 0000000..78fae07
--- /dev/null
+++ b/ImgFormatHelper/EasyFilters.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IWImgViewer
+{
+ class EasyFilters
+ {
+ struct Filter
+ {
+ public string extension;
+ public string description;
+ }
+
+ List filters = new List();
+
+ public EasyFilters(params (string description, string ext)[] textFilters)
+ {
+ Add(textFilters);
+ }
+
+ public void Add(params (string description, string ext)[] textFilters)
+ {
+ for (int i = 0; i < textFilters.Length; i++)
+ {
+ filters.Add(new Filter() { extension = textFilters[i].ext, description = textFilters[i].description });
+ }
+ }
+
+ public string GetFilterExtensionByOneBasedIndex(int index)
+ {
+ return filters[index-1].extension;
+ }
+
+ public string GetFilterDescriptionByOneBasedIndex(int index)
+ {
+ return filters[index - 1].description;
+ }
+
+ public override string ToString()
+ {
+ return string.Join("|", filters.Select(o => $"{o.description} (*.{o.extension})|*.{o.extension}"));
+ }
+ }
+}
diff --git a/ImgFormatHelper/FFImgHeader.cs b/ImgFormatHelper/FFImgHeader.cs
new file mode 100644
index 0000000..e399142
--- /dev/null
+++ b/ImgFormatHelper/FFImgHeader.cs
@@ -0,0 +1,164 @@
+using System.IO;
+using System.Text;
+using System.Linq;
+
+namespace IWImgViewer
+{
+ public class FFImgHeader : IImageFileHeader
+ {
+ public MapType mapType;
+ public byte semantic;
+ public ImageCategory category;
+ public byte gfxImageFlags;
+ public int cardMemory;
+ public int dataLen1;
+ public int dataLen2;
+ public int height;
+ public int width;
+ public int depth;
+ public string cName;
+ public byte mipLevels;
+ public byte flags;
+ public int wrongHeight;
+ public int wrongWidth;
+ public int wrongDepth;
+ public _D3DFORMAT format;
+ public int resourceSize;
+
+ public System.Collections.Generic.IReadOnlyList CompatibleDestinations => new System.Collections.Generic.List()
+ {
+ new IW4XImageHeader(),
+ new CoD4_GfxImageFileHeader(),
+ new CoD6_GfxImageFileHeader()
+ }.AsReadOnly();
+
+ public string FormatDescription => $"Zonetool-compatible (29/04/2021) ({FormatExtension})";
+
+ public string FormatExtension => "ffImg";
+
+ public byte IwiVersion => 8;
+
+ public int Flags => flags;
+
+ public byte MipLevels => mipLevels;
+
+ public byte Semantic => semantic;
+
+ public int Width => width;
+
+ public int Height => height;
+
+ public int Depth => depth;
+
+ public int DataLength => resourceSize;
+
+ public string Name => cName;
+
+ public MapType MapType => mapType;
+
+ public _D3DFORMAT D3dFormat => format;
+
+ public GfxImageFileFormat GfxFormat
+ {
+ get
+ {
+ switch (format)
+ {
+ default:
+ return GfxImageFileFormat.IMG_FORMAT_BITMAP_RGBA;
+
+ case _D3DFORMAT.D3DFMT_A8R8G8B8:
+ return GfxImageFileFormat.IMG_FORMAT_BITMAP_RGBA; // Channels will be swapped
+
+ case _D3DFORMAT.D3DFMT_L8:
+ return GfxImageFileFormat.IMG_FORMAT_BITMAP_ALPHA;
+ }
+ }
+ }
+
+ public ImageCategory Category => category;
+
+ public void Deserialize(BinaryReader br)
+ {
+
+ mapType = (MapType)br.ReadByte();
+ semantic = br.ReadByte();
+ category = (ImageCategory)br.ReadByte();
+
+ flags = br.ReadByte(); // Flags (gfximage)
+ cardMemory = br.ReadInt32(); // CardMemory
+ dataLen1 = br.ReadInt32(); // dataLen1
+ dataLen2 = br.ReadInt32(); // dataLen2
+ height = br.ReadInt32(); // height
+ width = br.ReadInt32(); // width
+ depth = br.ReadInt32(); // depth
+
+ // Read string
+ StringBuilder sb = new StringBuilder();
+ byte b;
+ while ((b = br.ReadByte()) != 0)
+ {
+ sb.Append((char)b);
+ // Skip!
+ }
+ cName = sb.ToString();
+
+ mipLevels = br.ReadByte();
+ flags = br.ReadByte(); // Flags again (loaddef)
+
+ // We read wrong dimensions here
+ wrongWidth = br.ReadInt32();
+ wrongHeight = br.ReadInt32();
+ wrongDepth = br.ReadInt32();
+
+ format = (_D3DFORMAT)br.ReadInt32();
+
+ resourceSize = br.ReadInt32();
+ }
+
+ public void Serialize(BinaryWriter bw)
+ {
+ bw.Write((byte)mapType);
+ bw.Write(semantic);
+ bw.Write((byte)category);
+ bw.Write(flags);
+
+ bw.Write(cardMemory);
+ bw.Write(dataLen1);
+ bw.Write(dataLen2);
+ bw.Write(height);
+ bw.Write(width);
+ bw.Write(depth);
+
+ // Null-terminated string
+ bw.Write(cName.ToCharArray().Append('\0').ToArray());
+
+ bw.Write(mipLevels);
+ bw.Write(flags);
+ bw.Write(wrongWidth);
+ bw.Write(wrongHeight);
+ bw.Write(wrongDepth);
+ bw.Write((int)format);
+ bw.Write(resourceSize);
+ }
+
+ public void From(IImageFileHeader header)
+ {
+ mapType = header.MapType;
+ semantic = header.Semantic;
+ category = header.Category;
+ gfxImageFlags = (byte)header.Flags;
+ height = header.Height;
+ width = header.Width;
+ depth = header.Depth;
+ cName = header.Name;
+ mipLevels = header.MipLevels;
+ flags = (byte)header.Flags;
+ wrongDepth = 1;
+ wrongHeight = 1;
+ wrongWidth = 1;
+ format = header.D3dFormat;
+ resourceSize = header.DataLength;
+ }
+ }
+}
diff --git a/ImgFormatHelper/FodyWeavers.xml b/ImgFormatHelper/FodyWeavers.xml
new file mode 100644
index 0000000..a53c582
--- /dev/null
+++ b/ImgFormatHelper/FodyWeavers.xml
@@ -0,0 +1,12 @@
+
+
+
+ NativeSquish_x86
+ OpenJpegDotNetNative
+
+
+ NativeSquish_x64
+ OpenJpegDotNetNative
+
+
+
\ No newline at end of file
diff --git a/ImgFormatHelper/FodyWeavers.xsd b/ImgFormatHelper/FodyWeavers.xsd
new file mode 100644
index 0000000..ff119f7
--- /dev/null
+++ b/ImgFormatHelper/FodyWeavers.xsd
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+ A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks
+
+
+
+
+ A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.
+
+
+
+
+ A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks
+
+
+
+
+ A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.
+
+
+
+
+ A list of unmanaged 32 bit assembly names to include, delimited with line breaks.
+
+
+
+
+ A list of unmanaged 64 bit assembly names to include, delimited with line breaks.
+
+
+
+
+ The order of preloaded assemblies, delimited with line breaks.
+
+
+
+
+
+ This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.
+
+
+
+
+ Controls if .pdbs for reference assemblies are also embedded.
+
+
+
+
+ Controls if runtime assemblies are also embedded.
+
+
+
+
+ Controls whether the runtime assemblies are embedded with their full path or only with their assembly name.
+
+
+
+
+ Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.
+
+
+
+
+ As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.
+
+
+
+
+ Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.
+
+
+
+
+ Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.
+
+
+
+
+ A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |
+
+
+
+
+ A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.
+
+
+
+
+ A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with |
+
+
+
+
+ A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |.
+
+
+
+
+ A list of unmanaged 32 bit assembly names to include, delimited with |.
+
+
+
+
+ A list of unmanaged 64 bit assembly names to include, delimited with |.
+
+
+
+
+ The order of preloaded assemblies, delimited with |.
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/ImgFormatHelper/FormatHelper.cs b/ImgFormatHelper/FormatHelper.cs
new file mode 100644
index 0000000..11b4653
--- /dev/null
+++ b/ImgFormatHelper/FormatHelper.cs
@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+
+namespace IWImgViewer
+{
+ public static class FormatHelper
+ {
+ public static Dictionary ChannelCount = new Dictionary()
+ {
+ {GfxImageFileFormat.IMG_FORMAT_BITMAP_ALPHA, 1 },
+ {GfxImageFileFormat.IMG_FORMAT_BITMAP_LUMINANCE, 1 },
+ {GfxImageFileFormat.IMG_FORMAT_BITMAP_RGB, 3 },
+ {GfxImageFileFormat.IMG_FORMAT_BITMAP_RGBA, 4 },
+ {GfxImageFileFormat.IMG_FORMAT_DXT1, 4 },
+ {GfxImageFileFormat.IMG_FORMAT_DXT3, 4 },
+ {GfxImageFileFormat.IMG_FORMAT_DXT5, 4 }
+ };
+
+ public static Dictionary GfxFormatToSquishFormat = new Dictionary()
+ {
+ { GfxImageFileFormat.IMG_FORMAT_DXT1, ManagedSquish.SquishFlags.Dxt1 },
+ { GfxImageFileFormat.IMG_FORMAT_DXT3, ManagedSquish.SquishFlags.Dxt3 },
+ { GfxImageFileFormat.IMG_FORMAT_DXT5, ManagedSquish.SquishFlags.Dxt5 },
+ };
+
+ public static byte GetBlockSize(GfxImageFileFormat format)
+ {
+ return (byte)(format.ToString().Contains("DXT") ? 4 : 1);
+ }
+
+ }
+}
diff --git a/ImgFormatHelper/Game/Enums/GfxImageFileFormat.cs b/ImgFormatHelper/Game/Enums/GfxImageFileFormat.cs
new file mode 100644
index 0000000..91c3f28
--- /dev/null
+++ b/ImgFormatHelper/Game/Enums/GfxImageFileFormat.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IWImgViewer
+{
+ public enum GfxImageFileFormat : byte
+ {
+ IMG_FORMAT_INVALID = 0x0,
+ IMG_FORMAT_BITMAP_RGBA = 0x1,
+ IMG_FORMAT_BITMAP_RGB = 0x2,
+ IMG_FORMAT_BITMAP_LUMINANCE_ALPHA = 0x3,
+ IMG_FORMAT_BITMAP_LUMINANCE = 0x4,
+ IMG_FORMAT_BITMAP_ALPHA = 0x5,
+ IMG_FORMAT_WAVELET_RGBA = 0x6,
+ IMG_FORMAT_WAVELET_RGB = 0x7,
+ IMG_FORMAT_WAVELET_LUMINANCE_ALPHA = 0x8,
+ IMG_FORMAT_WAVELET_LUMINANCE = 0x9,
+ IMG_FORMAT_WAVELET_ALPHA = 0xA,
+ IMG_FORMAT_DXT1 = 0xB,
+ IMG_FORMAT_DXT3 = 0xC,
+ IMG_FORMAT_DXT5 = 0xD,
+ IMG_FORMAT_DXN = 0xE,
+ IMG_FORMAT_DXT3A_AS_LUMINANCE = 0xF,
+ IMG_FORMAT_DXT5A_AS_LUMINANCE = 0x10,
+ IMG_FORMAT_DXT3A_AS_ALPHA = 0x11,
+ IMG_FORMAT_DXT5A_AS_ALPHA = 0x12,
+ IMG_FORMAT_DXT1_AS_LUMINANCE_ALPHA = 0x13,
+ IMG_FORMAT_DXN_AS_LUMINANCE_ALPHA = 0x14,
+ IMG_FORMAT_DXT1_AS_LUMINANCE = 0x15,
+ IMG_FORMAT_DXT1_AS_ALPHA = 0x16,
+ IMG_FORMAT_COUNT = 0x17,
+ };
+}
diff --git a/ImgFormatHelper/Game/Enums/ImageCategory.cs b/ImgFormatHelper/Game/Enums/ImageCategory.cs
new file mode 100644
index 0000000..ffc1787
--- /dev/null
+++ b/ImgFormatHelper/Game/Enums/ImageCategory.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IWImgViewer
+{
+ public enum ImageCategory : byte
+ {
+ IMG_CATEGORY_UNKNOWN = 0x0,
+ IMG_CATEGORY_AUTO_GENERATED = 0x1,
+ IMG_CATEGORY_LIGHTMAP = 0x2,
+ IMG_CATEGORY_LOAD_FROM_FILE = 0x3,
+ IMG_CATEGORY_RAW = 0x4,
+ IMG_CATEGORY_FIRST_UNMANAGED = 0x5,
+ IMG_CATEGORY_WATER = 0x5,
+ IMG_CATEGORY_RENDERTARGET = 0x6,
+ IMG_CATEGORY_TEMP = 0x7,
+ };
+
+}
diff --git a/ImgFormatHelper/Game/Enums/IwiFlags.cs b/ImgFormatHelper/Game/Enums/IwiFlags.cs
new file mode 100644
index 0000000..1c5c744
--- /dev/null
+++ b/ImgFormatHelper/Game/Enums/IwiFlags.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IWImgViewer
+{
+ enum IwiFlags : int
+ {
+ IMG_FLAG_NOPICMIP = 1 << 0,
+ IMG_FLAG_NOMIPMAPS = 1 << 1,
+ IMG_FLAG_CUBEMAP = 1 << 2,
+ IMG_FLAG_VOLMAP = 1 << 3,
+ IMG_FLAG_STREAMING = 1 << 4,
+ IMG_FLAG_LEGACY_NORMALS = 1 << 5,
+ IMG_FLAG_CLAMP_U = 1 << 6,
+ IMG_FLAG_CLAMP_V = 1 << 7,
+ IMG_FLAG_DYNAMIC = 1 << 16,
+ IMG_FLAG_RENDER_TARGET = 1 << 17,
+ IMG_FLAG_SYSTEMMEM = 1 << 18
+ };
+}
diff --git a/ImgFormatHelper/Game/Enums/_D3DFORMAT.cs b/ImgFormatHelper/Game/Enums/_D3DFORMAT.cs
new file mode 100644
index 0000000..272742c
--- /dev/null
+++ b/ImgFormatHelper/Game/Enums/_D3DFORMAT.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IWImgViewer
+{
+ public enum _D3DFORMAT : int
+ {
+ D3DFMT_UNKNOWN = 0,
+
+ D3DFMT_R8G8B8 = 20,
+ D3DFMT_A8R8G8B8 = 21,
+ D3DFMT_X8R8G8B8 = 22,
+ D3DFMT_R5G6B5 = 23,
+ D3DFMT_X1R5G5B5 = 24,
+ D3DFMT_A1R5G5B5 = 25,
+ D3DFMT_A4R4G4B4 = 26,
+ D3DFMT_R3G3B2 = 27,
+ D3DFMT_A8 = 28,
+ D3DFMT_A8R3G3B2 = 29,
+ D3DFMT_X4R4G4B4 = 30,
+ D3DFMT_A2B10G10R10 = 31,
+ D3DFMT_A8B8G8R8 = 32,
+ D3DFMT_X8B8G8R8 = 33,
+ D3DFMT_G16R16 = 34,
+ D3DFMT_A2R10G10B10 = 35,
+ D3DFMT_A16B16G16R16 = 36,
+
+ D3DFMT_A8P8 = 40,
+ D3DFMT_P8 = 41,
+
+ D3DFMT_L8 = 50,
+ D3DFMT_A8L8 = 51,
+ D3DFMT_A4L4 = 52,
+
+ D3DFMT_V8U8 = 60,
+ D3DFMT_L6V5U5 = 61,
+ D3DFMT_X8L8V8U8 = 62,
+ D3DFMT_Q8W8V8U8 = 63,
+ D3DFMT_V16U16 = 64,
+ D3DFMT_A2W10V10U10 = 67,
+ }
+
+}
diff --git a/ImgFormatHelper/Game/Structs/CoD4_GfxImageFileHeader.cs b/ImgFormatHelper/Game/Structs/CoD4_GfxImageFileHeader.cs
new file mode 100644
index 0000000..714c11e
--- /dev/null
+++ b/ImgFormatHelper/Game/Structs/CoD4_GfxImageFileHeader.cs
@@ -0,0 +1,167 @@
+using System;
+using System.IO;
+using System.Linq;
+
+namespace IWImgViewer
+{
+ public class CoD4_GfxImageFileHeader : IImageFileHeader
+ {
+ public char[] tag = new char[] { 'I', 'W', 'i' }; // 3 bytes
+ public byte version = 8;
+ public GfxImageFileFormat format = GfxImageFileFormat.IMG_FORMAT_BITMAP_RGBA;
+ public byte flags;
+ public short[] dimensions; // 3 shorts
+ public int[] fileSizeForPicmip = new int[4]; // 4 ints
+
+ [NonSerialized] string name = "iwi6";
+ [NonSerialized] int dataLength = 0;
+
+ public System.Collections.Generic.IReadOnlyList CompatibleDestinations => new System.Collections.Generic.List()
+ {
+ new CoD4_GfxImageFileHeader(),
+ new CoD6_GfxImageFileHeader()
+ }.AsReadOnly();
+
+ public string FormatDescription => "Call Of Duty 4 IWI (version 6)";
+
+ public string FormatExtension => "iwi";
+
+ public byte IwiVersion => version;
+
+ public int Flags => flags;
+
+ public byte MipLevels => (byte)((flags & (byte)IwiFlags.IMG_FLAG_NOMIPMAPS) == 0 ? 0 : 1 + Math.Floor(Math.Log(Math.Max(dimensions[0], Math.Max(dimensions[1], dimensions[2])), 2)));
+
+ public byte Semantic => 0;
+
+ public int Width => dimensions[0];
+
+ public int Height => dimensions[1];
+
+ public int Depth => dimensions[2];
+
+ public int DataLength => dataLength; // :shrug:
+
+ public string Name => name;
+
+ public MapType MapType => (flags & (int) IwiFlags.IMG_FLAG_CUBEMAP) == 0 ? MapType.MAPTYPE_2D : MapType.MAPTYPE_CUBE;
+
+ public _D3DFORMAT D3dFormat => _D3DFORMAT.D3DFMT_A8B8G8R8;
+
+ public GfxImageFileFormat GfxFormat => format;
+
+ public ImageCategory Category => ImageCategory.IMG_CATEGORY_LOAD_FROM_FILE;
+
+ public void Deserialize(BinaryReader br)
+ {
+
+ tag = br.ReadChars(3);
+
+ if (new string(tag) != "IWi")
+ {
+ throw new FormatException();
+ }
+
+ version = br.ReadByte();
+
+ if (version > 6)
+ {
+ throw new FormatException();
+ }
+ else if (version != 6)
+ {
+ System.Windows.Forms.MessageBox.Show($"Invalid IWI version {version} (expected 6), I will try to deserialize regardless.");
+ }
+
+ format = (GfxImageFileFormat)br.ReadByte();
+ flags = br.ReadByte();
+ dimensions = new short[]
+ {
+ br.ReadInt16(),
+ br.ReadInt16(),
+ br.ReadInt16()
+ };
+
+ fileSizeForPicmip = new int[]
+ {
+ br.ReadInt32(),
+ br.ReadInt32(),
+ br.ReadInt32(),
+ br.ReadInt32()
+ };
+ }
+
+ public void Serialize(BinaryWriter bw)
+ {
+ bw.Write(tag[0]);
+ bw.Write(tag[1]);
+ bw.Write(tag[2]);
+ bw.Write(version);
+ bw.Write((byte)format);
+ bw.Write(flags);
+ bw.Write(dimensions[0]);
+ bw.Write(dimensions[1]);
+ bw.Write(dimensions[2]);
+ for (int i = 0; i < 4; i++)
+ {
+ bw.Write(fileSizeForPicmip[i]);
+ }
+ }
+
+ public CoD4_GfxImageFileHeader()
+ {
+
+ }
+
+ public CoD4_GfxImageFileHeader(string name)
+ {
+ this.name = name;
+ }
+
+ public void From(CoD6_GfxImageFileHeader header)
+ {
+ flags = (byte) header.flags;
+ format = header.format;
+ dimensions = header.dimensions;
+ fileSizeForPicmip = header.fileSizeForPicmip;
+ }
+
+ public void From(IImageFileHeader header)
+ {
+ if (header is CoD6_GfxImageFileHeader)
+ {
+ From(header as CoD6_GfxImageFileHeader);
+ return;
+ }
+
+ format = header.GfxFormat;
+ flags = (byte)header.Flags;
+ dimensions = new short[]{
+ (short)header.Width,
+ (short)header.Height,
+ (short)header.Depth
+ };
+
+ // Compute mips filesizes
+ var currentFileSize = 28; // IWI Header Size
+ var textureMipCount = (flags & (uint)IwiFlags.IMG_FLAG_NOMIPMAPS) == 0 ? 1 : header.MipLevels;
+ for (var currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--)
+ {
+ var blockSize = FormatHelper.GetBlockSize(format);
+ var mipLevelSize =
+ Math.Max(blockSize, Width / Math.Pow(2, currentMipLevel)) * Math.Max(blockSize, Height / Math.Pow(2, currentMipLevel))
+ * FormatHelper.ChannelCount[format]
+ * (header.MapType == MapType.MAPTYPE_CUBE ? 6 : 1);
+
+ currentFileSize += (int)mipLevelSize;
+
+ if (currentMipLevel < fileSizeForPicmip.Length)
+ {
+ fileSizeForPicmip[currentMipLevel] = currentFileSize;
+ }
+ }
+
+ name = header.Name;
+ }
+ }
+}
diff --git a/ImgFormatHelper/Game/Structs/CoD6_GfxImageFileHeader.cs b/ImgFormatHelper/Game/Structs/CoD6_GfxImageFileHeader.cs
new file mode 100644
index 0000000..2f9dbfe
--- /dev/null
+++ b/ImgFormatHelper/Game/Structs/CoD6_GfxImageFileHeader.cs
@@ -0,0 +1,169 @@
+using System;
+using System.IO;
+
+namespace IWImgViewer
+{
+ public class CoD6_GfxImageFileHeader : IImageFileHeader
+ {
+ public char[] tag = new char[] { 'I', 'W', 'i' }; // 3 bytes
+ public byte version = 8;
+ public uint flags;
+ public GfxImageFileFormat format = GfxImageFileFormat.IMG_FORMAT_BITMAP_RGBA;
+ public byte unused;
+ public short[] dimensions; // 3 shorts
+ public int[] fileSizeForPicmip = new int[4]; // 4 ints
+
+ [NonSerialized] string name = "iwi8";
+ [NonSerialized] int dataLength = 0;
+
+ public System.Collections.Generic.IReadOnlyList CompatibleDestinations => new System.Collections.Generic.List()
+ {
+ new CoD4_GfxImageFileHeader(),
+ new CoD6_GfxImageFileHeader()
+ }.AsReadOnly();
+
+ public string FormatDescription => "Modern Warfare 2 IWI (version 8)";
+
+ public string FormatExtension => "iwi";
+
+ public byte IwiVersion => version;
+
+ public int Flags => (int)flags;
+
+ public byte MipLevels => (byte)((flags & (byte)IwiFlags.IMG_FLAG_NOMIPMAPS) == 0 ? 0 : 1 + Math.Floor(Math.Log(Math.Max(dimensions[0], Math.Max(dimensions[1], dimensions[2])), 2)));
+
+ public byte Semantic => 0;
+
+ public int Width => dimensions[0];
+
+ public int Height => dimensions[1];
+
+ public int Depth => dimensions[2];
+
+ public int DataLength => dataLength; // :shrug:
+
+ public string Name => name;
+
+ public MapType MapType => (flags & (int)IwiFlags.IMG_FLAG_CUBEMAP) == 0 ? MapType.MAPTYPE_2D : MapType.MAPTYPE_CUBE;
+
+ public _D3DFORMAT D3dFormat => _D3DFORMAT.D3DFMT_A8B8G8R8;
+
+ public GfxImageFileFormat GfxFormat => format;
+
+ public ImageCategory Category => ImageCategory.IMG_CATEGORY_LOAD_FROM_FILE;
+
+ public void Deserialize(BinaryReader br)
+ {
+ tag = br.ReadChars(3);
+
+ if (new string(tag) != "IWi")
+ {
+ throw new FormatException();
+ }
+
+ version = br.ReadByte();
+
+ if (version < 8)
+ {
+ throw new FormatException();
+ }
+ else if (version != 8)
+ {
+ System.Windows.Forms.MessageBox.Show($"Invalid IWI version {version} (expected 8), I will try to deserialize regardless.");
+ }
+
+ flags = br.ReadUInt32();
+ format = (GfxImageFileFormat)br.ReadByte();
+ unused = br.ReadByte();
+ dimensions = new short[]
+ {
+ br.ReadInt16(),
+ br.ReadInt16(),
+ br.ReadInt16()
+ };
+
+ fileSizeForPicmip = new int[]
+ {
+ br.ReadInt32(),
+ br.ReadInt32(),
+ br.ReadInt32(),
+ br.ReadInt32()
+ };
+
+ dataLength = (int)br.BaseStream.Length;
+ }
+
+ public void Serialize(BinaryWriter bw)
+ {
+ bw.Write(tag[0]);
+ bw.Write(tag[1]);
+ bw.Write(tag[2]);
+ bw.Write(version);
+ bw.Write(flags);
+ bw.Write((byte)format);
+ bw.Write(unused);
+ bw.Write(dimensions[0]);
+ bw.Write(dimensions[1]);
+ bw.Write(dimensions[2]);
+ for (int i = 0; i < 4; i++)
+ {
+ bw.Write(fileSizeForPicmip[i]);
+ }
+ }
+
+ public void From(CoD4_GfxImageFileHeader header)
+ {
+ flags = header.flags;
+ format = header.format;
+ dimensions = header.dimensions;
+ fileSizeForPicmip = header.fileSizeForPicmip;
+ }
+
+ public void From(IImageFileHeader header)
+ {
+ if (header is CoD4_GfxImageFileHeader)
+ {
+ From(header as CoD4_GfxImageFileHeader);
+ return;
+ }
+
+ format = header.GfxFormat;
+ flags = (byte)header.Flags;
+ dimensions = new short[]{
+ (short)header.Width,
+ (short)header.Height,
+ (short)header.Depth
+ };
+ name = header.Name;
+
+ // Compute mips filesizes
+ var currentFileSize = 32; // IWI Header Size
+ var textureMipCount = (flags & (uint)IwiFlags.IMG_FLAG_NOMIPMAPS) == 0 ? 1 : header.MipLevels;
+ for (var currentMipLevel = textureMipCount - 1; currentMipLevel >= 0; currentMipLevel--)
+ {
+ var blockSize = FormatHelper.GetBlockSize(format);
+ var mipLevelSize =
+ Math.Max(blockSize, Width / Math.Pow(2, currentMipLevel)) * Math.Max(blockSize, Height / Math.Pow(2, currentMipLevel))
+ * FormatHelper.ChannelCount[format]
+ * (header.MapType == MapType.MAPTYPE_CUBE ? 6 : 1);
+
+ currentFileSize += (int)mipLevelSize;
+
+ if (currentMipLevel < fileSizeForPicmip.Length)
+ {
+ fileSizeForPicmip[currentMipLevel] = currentFileSize;
+ }
+ }
+ }
+
+ public CoD6_GfxImageFileHeader()
+ {
+
+ }
+
+ public CoD6_GfxImageFileHeader(string name)
+ {
+ this.name = name;
+ }
+ }
+}
diff --git a/ImgFormatHelper/Game/Structs/GfxImageLoadDef.cs b/ImgFormatHelper/Game/Structs/GfxImageLoadDef.cs
new file mode 100644
index 0000000..b5cc0b9
--- /dev/null
+++ b/ImgFormatHelper/Game/Structs/GfxImageLoadDef.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IWImgViewer
+{
+ struct GfxImageLoadDef
+ {
+ public byte levelCount;
+ public byte flags;
+ public short[] dimensions;
+ public _D3DFORMAT format;
+ public int resourceSize;
+ public byte[] data;
+ };
+}
diff --git a/ImgFormatHelper/IImageFileHeader.cs b/ImgFormatHelper/IImageFileHeader.cs
new file mode 100644
index 0000000..23297ed
--- /dev/null
+++ b/ImgFormatHelper/IImageFileHeader.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IWImgViewer
+{
+ public interface IImageFileHeader
+ {
+ string FormatDescription { get; }
+ string FormatExtension { get; }
+
+ IReadOnlyList CompatibleDestinations { get; }
+
+ byte IwiVersion { get; }
+ int Flags { get; }
+ byte MipLevels { get; }
+ byte Semantic { get; }
+
+ int Width { get; }
+ int Height { get; }
+ int Depth { get; }
+
+ int DataLength { get; }
+
+ string Name { get; }
+
+ MapType MapType { get; }
+ _D3DFORMAT D3dFormat { get; }
+ GfxImageFileFormat GfxFormat { get; }
+
+ ImageCategory Category { get; }
+
+ void Deserialize(BinaryReader reader);
+
+ void Serialize(BinaryWriter writer);
+
+ void From(IImageFileHeader header);
+ }
+}
diff --git a/ImgFormatHelper/IW4XImageHeader.cs b/ImgFormatHelper/IW4XImageHeader.cs
new file mode 100644
index 0000000..9755f55
--- /dev/null
+++ b/ImgFormatHelper/IW4XImageHeader.cs
@@ -0,0 +1,121 @@
+using System.Collections.Generic;
+using System.IO;
+
+namespace IWImgViewer
+{
+ public class IW4XImageHeader : IImageFileHeader
+ {
+ public MapType mapType;
+ public byte semantic;
+ public ImageCategory category;
+ public int size;
+ public byte levelCount;
+ public byte flags;
+ public short[] dimensions; // 3
+ public _D3DFORMAT format;
+ public int resourceSize;
+
+ public IReadOnlyList CompatibleDestinations => new List()
+ {
+ new FFImgHeader(),
+ new CoD4_GfxImageFileHeader(),
+ new CoD6_GfxImageFileHeader()
+ }.AsReadOnly();
+
+ public string FormatDescription => $"Zonebuilder-compatible ({FormatExtension})";
+
+ public string FormatExtension => "iw4xImage";
+
+ public byte IwiVersion => 8;
+
+ public int Flags => flags;
+
+ public byte MipLevels => levelCount;
+
+ public byte Semantic => semantic;
+
+ public int Width => dimensions[0];
+
+ public int Height => dimensions[1];
+
+ public int Depth => dimensions[2];
+
+ public int DataLength => resourceSize;
+
+ public string Name => "iw4ximage";
+
+ public MapType MapType => mapType;
+
+ public _D3DFORMAT D3dFormat => format;
+
+ public GfxImageFileFormat GfxFormat
+ {
+ get
+ {
+ switch (format)
+ {
+ default:
+ return GfxImageFileFormat.IMG_FORMAT_BITMAP_RGBA;
+
+ case _D3DFORMAT.D3DFMT_A8R8G8B8:
+ return GfxImageFileFormat.IMG_FORMAT_BITMAP_RGBA; // Channels will be swapped
+
+ case _D3DFORMAT.D3DFMT_L8:
+ return GfxImageFileFormat.IMG_FORMAT_BITMAP_ALPHA;
+ }
+ }
+ }
+
+ public ImageCategory Category => category;
+
+ public void Deserialize(BinaryReader br)
+ {
+ mapType = (MapType)br.ReadByte();
+ semantic = br.ReadByte();
+ category = (ImageCategory)br.ReadByte();
+ size = br.ReadInt32();
+ levelCount = br.ReadByte();
+ flags = br.ReadByte();
+ dimensions = new short[]
+ {
+ br.ReadInt16(),
+ br.ReadInt16(),
+ br.ReadInt16()
+ };
+ format = (_D3DFORMAT)br.ReadInt32();
+ resourceSize = br.ReadInt32();
+ }
+
+ public void Serialize(BinaryWriter bw)
+ {
+ bw.Write((byte)mapType);
+ bw.Write(semantic);
+ bw.Write((byte)category);
+ bw.Write(size);
+ bw.Write(levelCount);
+ bw.Write(flags);
+ bw.Write(dimensions[0]);
+ bw.Write(dimensions[1]);
+ bw.Write(dimensions[2]);
+ bw.Write((int)format);
+ bw.Write(resourceSize);
+ }
+
+ public void From(IImageFileHeader header)
+ {
+ mapType = header.MapType;
+ semantic = header.Semantic;
+ category = header.Category;
+ size = header.DataLength;
+ levelCount = header.MipLevels;
+ flags = (byte)header.Flags;
+ dimensions = new short[]{
+ (short)header.Width,
+ (short)header.Height,
+ (short)header.Depth
+ };
+ format = header.D3dFormat;
+ resourceSize = header.DataLength; // Yes, we have the same info twice
+ }
+ }
+}
diff --git a/ImgFormatHelper/ImgFormatHelper.csproj b/ImgFormatHelper/ImgFormatHelper.csproj
new file mode 100644
index 0000000..a5cfc1b
--- /dev/null
+++ b/ImgFormatHelper/ImgFormatHelper.csproj
@@ -0,0 +1,298 @@
+
+
+
+
+
+ Debug
+ AnyCPU
+ {02B056CA-BC9B-4BE3-8730-F7506DABD8BB}
+ WinExe
+ IWImgViewer
+ iw4 image helper
+ v4.7.2
+ 512
+ true
+ true
+
+
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ true
+
+
+ cardicon_juggernaut_1_MGP_icon.ico
+
+
+ true
+ bin\x86\Debug\
+ DEBUG;TRACE
+ full
+ x86
+ 7.3
+ prompt
+ true
+
+
+ bin\x86\Release\
+ TRACE
+ true
+ true
+ pdbonly
+ x86
+ 7.3
+ prompt
+ true
+
+
+
+ ..\packages\Costura.Fody.5.3.0\lib\netstandard1.0\Costura.dll
+
+
+ ..\packages\ManagedSquish.2.0.0.0\lib\net40\ManagedSquish.dll
+
+
+ ..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll
+ True
+ True
+
+
+ ..\packages\ManagedSquish.2.0.0.0\lib\net40\NativeSquish_x64.dll
+
+
+ ..\packages\ManagedSquish.2.0.0.0\lib\net40\NativeSquish_x86.dll
+
+
+
+ ..\packages\System.AppContext.4.3.0\lib\net463\System.AppContext.dll
+ True
+ True
+
+
+
+ ..\packages\System.Console.4.3.0\lib\net46\System.Console.dll
+ True
+ True
+
+
+
+ ..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll
+
+
+ ..\packages\System.Diagnostics.Tracing.4.3.0\lib\net462\System.Diagnostics.Tracing.dll
+ True
+ True
+
+
+ ..\packages\System.Drawing.Common.5.0.2\lib\net461\System.Drawing.Common.dll
+
+
+ ..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll
+ True
+ True
+
+
+ ..\packages\System.IO.4.3.0\lib\net462\System.IO.dll
+ True
+ True
+
+
+ ..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll
+ True
+ True
+
+
+
+ ..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll
+ True
+ True
+
+
+ ..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll
+ True
+ True
+
+
+ ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll
+ True
+ True
+
+
+ ..\packages\System.Linq.4.3.0\lib\net463\System.Linq.dll
+ True
+ True
+
+
+ ..\packages\System.Linq.Expressions.4.3.0\lib\net463\System.Linq.Expressions.dll
+ True
+ True
+
+
+ ..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll
+ True
+ True
+
+
+ ..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll
+ True
+ True
+
+
+
+ ..\packages\System.Reflection.4.3.0\lib\net462\System.Reflection.dll
+ True
+ True
+
+
+ ..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll
+ True
+ True
+
+
+ ..\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll
+ True
+ True
+
+
+ ..\packages\System.Runtime.InteropServices.4.3.0\lib\net463\System.Runtime.InteropServices.dll
+ True
+ True
+
+
+ ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll
+ True
+ True
+
+
+ ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net463\System.Security.Cryptography.Algorithms.dll
+ True
+ True
+
+
+ ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll
+ True
+ True
+
+
+ ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll
+ True
+ True
+
+
+ ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll
+ True
+ True
+
+
+ ..\packages\System.Text.RegularExpressions.4.3.0\lib\net463\System.Text.RegularExpressions.dll
+ True
+ True
+
+
+
+
+
+
+
+
+
+
+ ..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll
+ True
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+ MainWindow.cs
+
+
+
+
+
+
+ Form
+
+
+ Viewport.cs
+
+
+
+
+ MainWindow.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+ True
+
+
+ Viewport.cs
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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}.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ImgFormatHelper/MainWindow.Designer.cs b/ImgFormatHelper/MainWindow.Designer.cs
new file mode 100644
index 0000000..048ea6f
--- /dev/null
+++ b/ImgFormatHelper/MainWindow.Designer.cs
@@ -0,0 +1,102 @@
+
+namespace IWImgViewer
+{
+ partial class MainWindow
+ {
+ ///
+ /// 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()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainWindow));
+ this.MetaDataLabel = new System.Windows.Forms.Label();
+ this.closeAllButton = new System.Windows.Forms.Button();
+ this.saveFileDialog1 = new System.Windows.Forms.SaveFileDialog();
+ this.SplashPictureBox = new System.Windows.Forms.PictureBox();
+ ((System.ComponentModel.ISupportInitialize)(this.SplashPictureBox)).BeginInit();
+ this.SuspendLayout();
+ //
+ // MetaDataLabel
+ //
+ this.MetaDataLabel.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.MetaDataLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.MetaDataLabel.Location = new System.Drawing.Point(0, 0);
+ this.MetaDataLabel.Name = "MetaDataLabel";
+ this.MetaDataLabel.RightToLeft = System.Windows.Forms.RightToLeft.No;
+ this.MetaDataLabel.Size = new System.Drawing.Size(284, 261);
+ this.MetaDataLabel.TabIndex = 1;
+ this.MetaDataLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // closeAllButton
+ //
+ this.closeAllButton.Dock = System.Windows.Forms.DockStyle.Bottom;
+ this.closeAllButton.Location = new System.Drawing.Point(0, 238);
+ this.closeAllButton.Name = "closeAllButton";
+ this.closeAllButton.Size = new System.Drawing.Size(284, 23);
+ this.closeAllButton.TabIndex = 4;
+ this.closeAllButton.Text = "Close all viewports";
+ this.closeAllButton.UseVisualStyleBackColor = true;
+ this.closeAllButton.Click += new System.EventHandler(this.closeAllButton_Click);
+ //
+ // saveFileDialog1
+ //
+ this.saveFileDialog1.FileOk += new System.ComponentModel.CancelEventHandler(this.saveFileDialog1_FileOk);
+ //
+ // SplashPictureBox
+ //
+ this.SplashPictureBox.Anchor = System.Windows.Forms.AnchorStyles.None;
+ this.SplashPictureBox.Image = global::IWImgViewer.Properties.Resources.cardicon_juggernaut_1;
+ this.SplashPictureBox.InitialImage = global::IWImgViewer.Properties.Resources.cardicon_juggernaut_1;
+ this.SplashPictureBox.Location = new System.Drawing.Point(108, 12);
+ this.SplashPictureBox.Name = "SplashPictureBox";
+ this.SplashPictureBox.Size = new System.Drawing.Size(64, 64);
+ this.SplashPictureBox.TabIndex = 5;
+ this.SplashPictureBox.TabStop = false;
+ this.SplashPictureBox.WaitOnLoad = true;
+ //
+ // MainWindow
+ //
+ this.AllowDrop = true;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(284, 261);
+ this.Controls.Add(this.SplashPictureBox);
+ this.Controls.Add(this.closeAllButton);
+ this.Controls.Add(this.MetaDataLabel);
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.Name = "MainWindow";
+ this.Text = "IW4 Image Format Helper";
+ ((System.ComponentModel.ISupportInitialize)(this.SplashPictureBox)).EndInit();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+ private System.Windows.Forms.Label MetaDataLabel;
+ private System.Windows.Forms.Button closeAllButton;
+ private System.Windows.Forms.SaveFileDialog saveFileDialog1;
+ private System.Windows.Forms.PictureBox SplashPictureBox;
+ }
+}
+
diff --git a/ImgFormatHelper/MainWindow.cs b/ImgFormatHelper/MainWindow.cs
new file mode 100644
index 0000000..041bda3
--- /dev/null
+++ b/ImgFormatHelper/MainWindow.cs
@@ -0,0 +1,125 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace IWImgViewer
+{
+ public partial class MainWindow : Form
+ {
+ public string ImageName { get; private set; }
+
+ Reader reader;
+
+ Queue viewports = new Queue();
+
+ EasyFilters convertFilters = new EasyFilters();
+
+ public MainWindow()
+ {
+ InitializeComponent();
+
+ this.DragEnter += new DragEventHandler(Form1_DragEnter);
+ this.DragDrop += new DragEventHandler(Form1_DragDrop);
+
+ Reader.OnBitmapRead += Viewer_OnBitmapRead;
+
+ MetaDataLabel.Text = "Ready!\nDrag and drop an image\nover here to start!";
+ }
+
+ private void Viewer_OnBitmapRead(Bitmap arg1)
+ {
+ var viewport = new Viewport(this);
+ viewport.Show();
+ viewport.ShowImage(arg1);
+
+ viewports.Enqueue(viewport);
+ }
+
+ void Form1_DragEnter(object sender, DragEventArgs e)
+ {
+ if (e.Data.GetDataPresent(DataFormats.FileDrop)) e.Effect = DragDropEffects.Copy;
+ }
+
+ void Form1_DragDrop(object sender, DragEventArgs e)
+ {
+ string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
+ string file = files[0];
+ ShowImage(file);
+ }
+ void ShowImage(string path)
+ {
+ try
+ {
+ var v = new Reader(path);
+ reader = v;
+ ImageName = System.IO.Path.GetFileNameWithoutExtension(path);
+
+ MetaDataLabel.Text = $"{ImageName}\n{reader.ImageFormat}";
+ }
+ catch (FormatException e)
+ {
+ MessageBox.Show($"Format not supported:{e.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
+ }
+
+ private void closeAllButton_Click(object sender, EventArgs e)
+ {
+ while(viewports.Count > 0)
+ {
+ viewports.Dequeue().Close();
+ }
+ }
+
+ private void saveFileDialog1_FileOk(object sender, CancelEventArgs e)
+ {
+ int index = saveFileDialog1.FilterIndex - 1;
+ if (index < 0 || index > reader.CurrentHeader.CompatibleDestinations.Count)
+ {
+ return;
+ }
+
+ var format = reader.CurrentHeader.CompatibleDestinations[index];
+ format.From(reader.CurrentHeader);
+
+ using(FileStream fs = new FileStream(saveFileDialog1.FileName, FileMode.Create))
+ {
+ using(BinaryWriter bw = new BinaryWriter(fs))
+ {
+ format.Serialize(bw);
+ bw.Write(reader.Data);
+ }
+ }
+ }
+
+ protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ {
+ if (keyData == Keys.C)
+ {
+ convertFilters = new EasyFilters();
+ IEnumerable compatibleDestinationFormats = reader.CurrentHeader.CompatibleDestinations;
+ foreach (var format in compatibleDestinationFormats)
+ {
+ convertFilters.Add((format.FormatDescription, format.FormatExtension));
+ }
+
+ saveFileDialog1.DefaultExt = convertFilters.GetFilterExtensionByOneBasedIndex(1);
+ saveFileDialog1.FileName = System.IO.Path.GetFileNameWithoutExtension(ImageName);
+ saveFileDialog1.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
+ saveFileDialog1.Filter = convertFilters.ToString();
+ saveFileDialog1.ShowDialog(this);
+ return true;
+ }
+
+ return base.ProcessCmdKey(ref msg, keyData);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ImgFormatHelper/MainWindow.resx b/ImgFormatHelper/MainWindow.resx
new file mode 100644
index 0000000..76f82c3
--- /dev/null
+++ b/ImgFormatHelper/MainWindow.resx
@@ -0,0 +1,703 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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
+
+
+
+
+ AAABAAUAEBAAAAEAIABoBAAAVgAAABgYAAABACAAiAkAAL4EAAAgIAAAAQAgAKgQAABGDgAAMDAAAAEA
+ IACoJQAA7h4AAEBAAAABACAAKEIAAJZEAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAADDDgAAww4AAAAA
+ AAAAAAAAbn18/3R/f/9ER0j/GRMZ/xAJEP8QCA//EQkO/xEKEf8QCxD/EQoQ/yIgJP9ZXmH/eIKE/3B2
+ e/93gIL/dH1+/3iJiP9gZmb/GhUa/wwEC/8MBgv/EAgM/xAHDP8PBgv/DwUL/w0FCP8RChD/KSIp/2ls
+ bv9+hon/hI2O/4OOjv9sdXf/MC0y/xMJDv8pICD/NS8u/zMrKv8pHh7/IhYY/x0TGP8TCQ//DAQG/yAY
+ HP8vKy7/ZWlr/36Ghv9zf4D/UVhZ/yYdIP8jFRT/FQwR/wwEC/8NAQn/DAAJ/w0ACv8OAQz/DQIK/wUA
+ Av8hGhz/KiMo/zk0Nv9+h4f/c31//0lKS/8VDBL/CgII/w4IDf8bEhj/Ixgc/yASF/8YDBP/FgcT/xgK
+ E/8NAgj/FxAW/ywjKf8pIiX/cnl4/4GOjf85Nzv/EQkP/zAlJf8sJCf/Miks/1ZQTf9TTEr/TkdH/05F
+ Rf87MTD/FwwP/xIID/8xIyj/LyYp/1lbW/9xenj/Ojc6/y8nJ/9BOTf/MCgs/09ISf9pZmT/Yl9f/2Bd
+ XP9jXVz/V1NT/0lDRf8pHiH/PzQ3/15ja/9HSEz/YGJh/0dJSv84MTT/MSos/0pHSf9XUVP/Rj4//1dS
+ U/9eWFj/SEJD/0E8QP9TT0//Rj4//01GRv9SUVT/TUxN/01PT/9DREPyUE9P/yQeI/9QTVH/Y2Bi/2xp
+ av+CgoH/f4J//2tsbP9ZWFv/XFZY/1hVVf9bVVT/VVRU/11eXf9IR0f/KykqoUJBQv8mISX/i46L/7zD
+ v//G0s7/xNLO/7zIwv+turf/oKup/5eem/+PlpX/i5CP/1ZWVf9DQUL/V1dX/yUiJHg/PDv7MSsq/4SF
+ gf+2v7j/t8O+/7vIwv+3wbr/r7q0/6CppP+co5//o66y/7nO1f9eY2T/IBod/zYzNv8hHh4VLSgnjTQw
+ Lu9iY2H/ucO9/7fGv/+6ycL/t8G6/7G7tP+iq6X/oail/7jL0f+rwMT/PDo7/y8rLP8yMTH/FhEUABkT
+ FQcTERJDREJFrbW6uf3C08z/zd7V/7/Kwv+uubT/pK2n/6Krp//K2dr/s8TE/0I/Pf86OTj1Ly8t0AAA
+ AAAAAAAAGxcWABQQDy2UmZbj0+Lb/8LSyv+3w77/q7Sw/6Otqf+rtLL/y9fU/3t/fc8jICB5GRcYSBYV
+ FRkAAAAAAAAAABgVEQAAAAACQ0RCaZ2lou65w77/sLax/7C2s/+8xsH/wMjF/4SIhtoqKig9RUtIAEpG
+ SAAAAAAAAAAAAAAAAAAAAAAABwUFAAAAAAY5OTpXZWdlzYODgv+IiIf/goWD+mhmZbYqKyk9AAAAABUW
+ EwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAA
+ AADgAAAA4AcAAPAPAAAoAAAAGAAAADAAAAABACAAAAAAAAAJAADDDgAAww4AAAAAAAAAAAAAW2dl/3yL
+ i/9ocHH/W2Ji/y8uMP8VDxX/DgUO/xAIEP8SChL/FAsQ/xMLEf8TCxP/EQ0S/w4IDf8QCQ//JCMm/0VJ
+ S/9ob3L/c3x+/211ef9nbXL/anJ0/3mDhP9gZ2f/bH18/5Chof9udnX/OTk7/xkTGf8SCxL/EgwS/w4J
+ Dv8MBAv/DQQK/w4FC/8PBg3/DggN/xILD/8TCw//Eg0S/xsWHP9cXmP/hY6P/3yEh/99hIr/gouN/5Cc
+ nf9/iYr/bn9+/4ORkP9GR0X/FQ8U/wsDC/8HAAb/BQAE/wcBBf8NBAn/DAQK/wwDCf8MAgf/DQAI/wwB
+ Bv8JAQP/DwgO/xcOF/8ZDxj/VFNU/4OLjP97goX/gIiI/4+amv97hYX/bHV3/2x0d/8ZFBr/CQEI/xEJ
+ Df8oIB7/Pjk3/0U/PP9EOzf/OS0q/zImJP8qHx7/Jh0f/yAVG/8SBw//DQIF/xMLDv8pICT/Ih0h/1RX
+ Wf93f4H/dnt8/4OPkP9rd3n/Ymts/05QU/8SCRD/JRUW/zMkIv80Kyv/Ih0e/xsVFv8XDhD/EggM/xIG
+ DP8RBQv/EAYO/xEID/8MBAn/BwED/wsFB/8sIyb/KCMn/yMcH/9cXF3/gImJ/36Ji/9ncXT/XGZn/y8u
+ Mv8nGx3/KRkW/w8FCP8HAAf/BwAH/woACP8NAAn/DAAL/wwACv8OAAv/DwAN/w8BC/8PAgr/BwIE/wUA
+ Af8vJin/My4y/x0UGf86NDb/f4eH/4KNjv9sdXj/X2Rj/yomKv8UChD/CQEI/wUABf8HAAf/CwMK/xAH
+ Dv8VCxD/EwML/w8CCf8KAAn/DgAO/xEDEP8TBxH/EAML/wYABf8fFh7/LiYu/ysiJf8jHSD/cXd1/46b
+ mv93hIL/U1ZX/xsVG/8GAAf/DgYM/xgQFf8qIyT/OC4v/0Q6Of9GPDn/QjY0/0A1NP81Ky3/Khwj/y0f
+ Iv8iFhj/FAcM/wcABf8aERj/LiIo/ywgI/8kHCD/WFlY/5Genf94hIL/SUpN/xcSGP8RCQ//PTAt/0M4
+ Nf8lHiT/HBMZ/0hBQP9fW1j/W1VS/1NMTP9XUlH/XlZV/1JKSP8+NTL/HBES/woBBv8SCA//MyUq/zcr
+ Lv8sIyb/PTs7/4KLiv9VW1j/Q0BD/yEcH/81LCv/RDw5/zgwMf8cEhr/PzY5/2VgXv9wbmv/ZmRj/05H
+ Sf9hXl7/bWdl/19ZWP9ZVVT/T0hK/y8lKP8dEhb/Lx8i/1dTWP9gZ3H/Ly0z/2lsbP9bXVv/RkdI/z04
+ Of80Kiv/MCoq/0xJSP9AOz3/Y15f/2djYf9YU1L/Yl9g/29ubf9mY2L/VU9P/1NOUP9PS03/XVpb/0ZA
+ Qf84LjD/Sj8//1ZUWP9yf4j/QUFG/1RVVf9aXVz/Sk5O/0FAQv82LzL/KCAj/zgyNf9QTVD/WlVX/0M6
+ Pf9FPD3/SkJE/2ZhYf9WTU3/S0ZH/zozOP9DPkP/TUhJ/1dUU/9COTr/Vk9O/0I7PP9KRUf/T0tL/0tL
+ S/9GSkr/RUdG/1ZYVv9WVVX/HRYc/zErMf9RT1T/TUdK/zszNv9HQEL/YFpZ/29sav9gXlz/UU1O/z03
+ PP9BPkL/TUNG/1JMTf9MSUr/WlJR/1NOT/9ZWlr/X2Fe/1ZXVv88Ojn/Ih0esEdHRvpOTU7/GBIZ/z47
+ PP90dXX/naCg/73Ewv/Ay8f/wcvI/7zIxP+4xsD/qbaz/6i1s/+MlJP/g4SE/3Fycv94fHn/ZmBe/2xs
+ av9KSEj/Xl5d/2FhYf9IRkb/Ew4SVD08PPc+Oz3/IBgf/0hHRv+5wbv/v8W//7zFwP/I1dH/xtfS/7zI
+ w/+6xr//q7e0/6Ktq/+gqqj/nqej/56ppv+VnZz/lZ2d/4+Wlf87ODf/JyEm/2JiYv9VVVX/FhIWTTk3
+ NvNGREH/KSIi/z86Of+ztrH/srqz/7G6tf+4xMD/vMrF/7jEvf+4wbn/s8C6/6i0rv+epqH/mJ2Y/6Gs
+ rP+dpqr/uc3V/7vQ0/89PT7/GxQX/ykkKP9BPkL/GBUZFSQhIag9ODb7RUA9/y0nJf+EhoP/usS9/7bB
+ u/+4xcD/ucjB/7bCu/+2v7j/sLqy/6mzrv+fp6H/m5+a/6eztP+ptrz/vNff/52srv8rKCr/KiMl/ysm
+ KP8sKiz/b2BfAD43NxciGhuOLisq6zk2NP90d3X/v8rD/7XAuf+4yMH/u8vD/7nFvv+2v7n/tr+3/6u0
+ rv+gqKP/nKGc/7LBw/++0tn/vdje/2ZpaP8kHh//Lyss/zQyM/81NjT/AAAAAC0oKQAqJCUIDw4PQRYS
+ FZ9KSEzlwcbF/7nCv//C1s7/0+fe/8bSyf+/yMD/s7+5/6eyrf+kq6b/naWg/7O/vf/L2tz/1/L1/2tt
+ bf8/Ojf/RURD/0ZFRP8qKijyAAAAAAAAAAAAAAAAW1ZbAIaEigMVEBNrnJ+e/Nbj3v/C1Mz/y93U/8nY
+ zv+5xL3/q7Ww/6exrP+jraj/naWi/7K7uf/b6+n/zNnZ/05OTPsyLy7vIyIizxkYGJ4XFxVLAAAAAAAA
+ AAAAAAAAAAAAABQRDgALBwUqXV9c3Nvm4v/R4Nn/vc3F/7/Ox/+wurj/rrax/6myrv+irKj/oqmo/8PP
+ zP/T4Nz/bW1q4SAcG2wNCw06Hh0eGSMkIwUAAAAAAAAAAAAAAAAAAAAAAAAAACsnIwAtIBUBGhgXXnB1
+ c+zO2tb/x9PN/7O9uP+utrL/paqn/7C6t/+2wbv/xdHM/9HX1f95fXrzHh8dbTAbGwFKSUYAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8OzoARUJABB0dHWBgZGPYoaek/qOlov+6u7b/t7q3/8jR
+ zf+/xsH/trq4/29xb+chJCBzQkRCCDo9OgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAABAMDAAAAAAEbGBsyJiYml1VWVfBnaGf/ZF9h/19dXf9YWFf3NjAwph0XF0MHDQoEDQ4NAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAwAAAAPAAAAD4AAEA+AAPAPwAHwD+AD8AKAAAACAA
+ AABAAAAAAQAgAAAAAAAAEAAAww4AAMMOAAAAAAAAAAAAAENLSP9ufXz/anN0/1dbXP9eZmb/Ozw8/ykl
+ KP8QChD/CQEJ/wsDC/8NBQ3/EAgQ/xIKDv8RCAz/EgkR/w8HD/8OCg7/CwgL/wgBCP8RChD/JyYp/zs9
+ P/9TV1v/WF1g/2Ztb/9lbW//X2Ro/1tgZP9bYWP/Z29w/3B5ev9FSUj/YW9t/5eurv+Wpaf/d4GC/1xk
+ ZP81NTb/FxEX/xUOFf8YEBj/GRIa/xMME/8TCxL/FAsR/xIKD/8VDRX/FQ0V/xMPFP8bFRr/Fg0S/xML
+ Ef8TDxP/HBoe/0hKTv+Mlpn/jpud/4aSlf+AiI//gIiP/4SPkf+Ll5f/nqyv/3F7e/9fbmz/hpua/4eS
+ kP9SVVL/JSEl/xAJEP8TChP/DAUM/wgECP8HAwf/BwEF/wkABv8IAAf/CAAH/woBB/8LAQf/CAAF/wkD
+ Bv8MBAX/EgkN/xUOFP8RChH/EAYR/z87Qf98gYH/en+A/3h+gv97gob/foaI/4WPjv+YpKT/b3h4/2Ny
+ cP+Im5v/dn99/zU0Mv8WEBX/DQYN/wgAB/8HAAb/BAAE/wQAA/8IAQX/CwMI/woDCP8KAgj/CgEG/woB
+ Bv8MAAf/DwAK/woBA/8HAAH/CgMJ/xkSGP8ZDxn/EgcR/z05Ov+CiIj/hY2R/3mAg/9+hYb/ipSS/5aj
+ o/9td3f/XmVl/4eUl/9NTlL/EAkQ/wkBCf8HAAf/DQUG/xwUE/80Li3/QTw5/0Y+O/9JQT3/QDMw/z4w
+ Lf8zJyT/MCUj/yohIv8jGh7/GA0V/w8FDP8OAwX/CgID/yEYHf8oHyT/Hxkd/zo4O/9/iYv/dXx+/29y
+ c/+DjY7/g5KT/2BrbP9WXFz/hZCT/zYzOP8KAwr/DwUL/yMWGP9BNjL/UUhE/0ZBPv9APDn/Ni4s/ywj
+ Iv8iFxb/IRYV/xwPEv8aDhH/GBAU/x4WGv8ZDxb/EQcO/xEFCP8HAAH/GREV/zIpLP8oIyf/GBQX/0NA
+ Qf9xd3n/eX+A/4OMjf9+jI7/Xmlq/1VeX/9bY2T/Hxgc/xULEP83Ix//Mh8b/yEVF/8SChD/BgIF/wQA
+ A/8IAAT/CgAG/wkACP8JAAj/DAAJ/w0ACf8LAAr/CwAK/woBB/8IAQX/AgAB/wEAAP8TCwz/Migr/y0o
+ LP8lICT/HhQY/1xbW/+Aioj/gouL/3uHiv9dZWb/XGZn/0tSVP8WDhT/Nygo/ygZFv8PBgb/BgAG/wgA
+ CP8JAAn/CwAK/w8ACv8PAAr/DgAM/w0ADP8NAAv/DwAL/xABDf8QAQ3/EAIL/xEDC/8LAwf/AQAA/wsD
+ BP88Mjb/OTQ4/yIcIf8ZDhL/Pjk7/4CIh/+Bi4v/hpOW/2Jpa/9gZWT/TE9P/xYNE/8aDxX/CAEH/wcA
+ Bv8FAQX/CAEI/wcAB/8HAAf/CQEJ/wwCCv8PAAj/DgAI/wgACP8JAAn/DwAP/xAAD/8QAw//EgUQ/xID
+ DP8GAQT/CAEH/yUcJP8yKTL/KR8l/yghI/8gGh3/cXZ1/4uWlP+RoaD/anVz/15iYv89Oz3/CAAH/wcA
+ B/8HAAj/BgAH/wgCCf8RCw//JRsf/zIoKv87MC//OS0r/y0eHv8pGRr/JBcZ/xYLEf8SAhD/GQgU/yAT
+ Gf8dEBf/FQUO/wkBBv8IAQj/IRgg/zAmLv8kGR7/Ni8v/xkRFv9XWVf/i5aU/5Kjof9teHb/TlBT/zMx
+ Nv8GAAb/CAAI/xkQE/8mHB3/ODEx/0I6Ov87MTL/PzU1/01FQv9QSEb/VExI/1RNSf9UTk3/TUZG/0c8
+ Pf9CNTb/Nysp/yQWFv8XCQz/CQED/wcAB/8bERf/NSgt/ysbH/8sIib/JBwg/z89Pf+JkpD/k6Kg/2Rs
+ av9KS0//MjA1/wYABv8XDhP/QTQw/1JDO/80LC//FA4X/w4FDf8yKiz/XllW/2NeW/9eWFb/U01L/1FL
+ S/9XU1L/YVtZ/1lRT/9TTkr/Qjo4/yEUFv8NBAX/CAAH/xIID/8zJSr/Oiww/zYsL/8sIiX/KyYn/3F4
+ dv96hIL/PkA9/0JAQ/8xLjH/EQwP/zcvLf9IQT7/Qzs5/yUdIv8WDBb/NCkt/1dQT/9kYF7/cG5r/2hn
+ Zv9GPUH/Rz9B/2NiYP9vamj/ZF1c/1dTUv9VUVD/TkZI/zowM/8eFBf/FgoO/ykYHP9DNjn/YmJp/1pd
+ Zf8jHiT/UlVW/3+Cgf9OT03/QD5B/z48Pv85MC7/SD05/yoiIf9LRkT/OzQ0/zEoLP9XUFD/cm9r/29t
+ av9mYmH/b3Bu/25vbv9qamn/amtp/2ZgX/9cVVX/WFRU/1pWVv9lYmT/VE5Q/y4lJ/84Ly//Oywu/1BG
+ Sf9zfYf/gpqr/zU3Pv9CQkT/b3Jx/1FTUv9AQkP/UFNT/ywjKP8lGiD/JR0f/01NS/9MSkv/SUVI/2ll
+ Z/9dV1n/WlRS/0xFRf9NRkj/aWdo/3Brav9dVlb/R0BB/1FNTv9LR0v/R0JG/01JSv9gX17/OC4w/z41
+ Nv9WT07/RDw8/01JTP9jaGv/TUtM/zs2OP9kaWj/S1BP/0FERP9SVlX/OTM2/z85Ov8pISP/HxUa/0M/
+ Q/9QTVD/WFFU/1BITP8uIib/T0hI/0U9P/9eWVv/Z2Fg/1BISP9NSEj/SENF/yUcJP9OSk7/SEJD/1tY
+ V/9NR0f/Qzs8/11XVf9EPDz/RT9C/0ZAQv9ST0//SEVF/09SUv86Pj3/RkhH/1dcWv9YWFf/VVVV/yUf
+ Jf8SChH/UE1S/1RSVv9QSk3/Pzc6/x0RFf80Ki3/QDY2/2BaWf9aVVL/UEpI/z83Ov88NTj/IRkg/0hF
+ Sf9EOj3/Y1xc/0I9QP9PTU3/WlFQ/1dQUP9PTU7/YGFg/1teW/9dX13/UFBQ/zIwL/8mISHmOjk4+mNk
+ Y/9PT0//GhMa/xUPFf9ZVln/R0RK/2BeYP96enr/naGg/6yysP+zuLb/tby5/6i0rv+lsaz/kpqY/5Ob
+ mv+Bh4b/YGFj/15XWf9cVFb/S0lM/2tua/9bUlH/Y19e/1VUVf9TUlL/Z2ln/11fXP9WVVX/Ojc3/zMt
+ Lk4ZFhjfYGJg/zw5PP8eGB//GhYZ/3R2cv+jqab/0NbV/9vk4f/T39v/wtDL/8PRzv+/zMn/vszF/77L
+ xf+uu7j/ssG//7C+vP+lsK//o62p/5qin/+RmZf/h4qH/21pZv97e3r/dHVx/zItLf9IRkf/b3Bv/2Vm
+ Zf9DQkP/LCgsJBoYGt9XV1b/Mi4x/yUdJP8dGBz/goWB/8LKxf+ytq//tLmz/7TAu//T4Nz/zN7Y/73L
+ x/+9ycL/u8a//626t/+hq6r/nKak/5ympP+aoZ3/nKWh/5ynp/+Vnp7/naen/6Gtrf99gH3/KiUl/xUN
+ FP9FQ0X/dHV0/0lISf84NDYmGRYY3VdVUv87OTf/KiMj/x8YGP96eXb/wMS//7C4sv+0vLb/rbe0/7nF
+ wf+8ycT/ucfB/7jBuf+4wLn/tcO9/6+8t/+iq6b/nqag/5idmP+coqD/qLW5/5qjp/+3ytL/wdnh/6Gv
+ r/8iHyL/Hhca/xcRFf88OTz/REFE/zMvMgsXExaiQT48/0pIRf86NDL/KCEg/0I8O/+vs67/tsC5/7fB
+ uv+zvrr/usjD/7zKxf+2xL3/uMG5/7e/uP+wu7T/rbmy/6Swqv+epqD/mp+a/5qgnP+jr7P/mqGl/7zU
+ 3v/I4+v/m6en/xwYHP8lHiD/KiIk/ywnK/8vKy//DQoKABAODi4iGxurLign+U9MSP9FQj7/Jx8f/4mP
+ jP+8x8D/tsC5/7bDvf+2xb//uMfA/7bEvv+2wLn/tb+4/7O8tP+utq//pK+q/56loP+doZ3/nqSi/73U
+ 2v+wvcT/u9jg/67Bw/9ERET/IRwe/y4oK/8tKCr/Kysq/yorKv////8AAAAAAFdQTxocFRWOHBga5Dk3
+ Nf4+Ozr/i5KP/8TQyf+zurX/tsS+/7nJwv+7y8T/usnC/7fBu/+1v7n/t8C4/7K7tP+kr6r/oKeh/56j
+ n/+gpqT/wdje/7rM1P/C4+v/naim/ywoKP8qJSX/Lyor/y4rLf89Pjz/MzQz/wAAAAAAAAAAPzs8AD44
+ OAkXFhc/CwkLnRgSGOBYVlr80dfW/7q/vf+3xsD/xdrS/9Po3//M2tL/v8i//7/JwP+2w73/qrex/6Wu
+ qf+kqqX/nqei/6Krp//I19j/yNbX/9z6/v+jsrL/NS4t/zw3Nf9CQUH/Tk5O/05NSv8lJCP+AAAAAAAA
+ AAAAAAAA////ACAeIAAZGBkKMi0yMhsTGqygoKH/0djW/7vKxP/D18//1enh/9Dh1//H0sj/vce//625
+ s/+msq3/pbCr/6SsqP+epqL/oaqn/7W6u//b7u7/5v///5+rqv9LR0P/T01I/zk4OP8qKSr6HBsa1RIS
+ EYsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKBwYAAAAAYm5wbvrs9vP/2Ofh/8DSyf/C0sr/xdbN/8HP
+ yP+wurf/rLWw/6u0r/+kr6r/oq2o/52jov+rtLP/z93Z/+T08f/JzMv/SUhF9RcTFdsUEBPGExITkgYG
+ BmMlJCQqjoyEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0ZFgAhHBomMC8s07W9uf/s+fX/ytrT/7zL
+ xP+9zMX/ucbB/6y1s/+utrH/rLSw/6WxrP+iq6f/nKKh/7bCwP/I09D/1+Th/2ZmY/MgHRl4REJCKA4M
+ DhJLS0wFNDM0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOTUxAP///AERDw1TPT4947nD
+ wP/c6uX/yNTO/7rFwP++y8b/tb+6/6uyr/+ut7X/q7Wy/7S/uf+5xcD/ztjV/9jb2f90d3X9FhYVnygm
+ JQ4fHh0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUE9OAK2r
+ qQMYGBhfPUA/5qWuq//S3dj/qa2q/6Kjnv+goZz/l5WS/7S8uf/I1M3/y9bP/+Dp5v/V29r/cnVz/Rsf
+ G6s2ODYdKi0qAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAVlVVAKCgoAQSERJHKikrtGdqaPaBg4D/qqyp/8/Sz//Jz8z/xczJ/7q/u/+bnZr/hYKC/UtG
+ RuMYGReJJCYlGgAAAAD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAgHt/AAAAAAA7ODoYDAwMYzQ0NMRLTEv/Pj0+/zsyNf87MzX/PDw8/0pK
+ SfMjHR2RHBQUQmxqaQdRTEsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAgAAAAMAAAADgAAAA+AAAAP4AAAD+AAAH/gAAP/8AAH//gAD//+AB/ygA
+ AAAwAAAAYAAAAAEAIAAAAAAAACQAAMMOAADDDgAAAAAAAAAAAAAYFxT/QEdF/0JIRv9ERUb/Ozs7/zIw
+ Mf8/QUH/Nzg3/x8bHP8iHSH/GhQa/woCCv8IAAj/CAAI/wgACP8JAQn/CQEJ/wwEC/8NBQf/CwME/wwE
+ Bv8LAwr/CwIL/wgBCP8FAwX/BQQF/wQCBP8HAAf/CAAI/xEJD/8eHB//JSQn/yssLv84OT3/Li8y/zIy
+ Nv8+QEH/P0BB/z5AQP86PDz/Ojs8/zg5Ov8yMzb/P0FE/z9CRP9ER0b/Q0ZF/xkXFP9AR0X/tNLS/6fB
+ wf+zyMv/kZ+i/4WRk/+Up6b/cX18/0lNTf8yMDP/Ix8j/wsEC/8NBA3/EAgQ/xQLFP8XDhf/Fg4W/xsT
+ G/8dFBz/GhIZ/xgQFv8dFB3/HhUe/xoSGv8dFh3/Hhge/xUQFf8MBAv/CwIK/xcPFP8pKSv/NDY3/01S
+ Uv91fX//ipWY/52qrP+fsLL/nq+x/52tsP+SnaX/kZuk/4+Zov+JlZb/nqys/5+urv+qur3/vM/S/0NG
+ Rf80OTb/j6Wl/4SZmP+OnJ7/go6Q/2Nqa/9NUVH/Pj9A/yAdHv8IAAj/EQoR/xoVGv8gFyD/IRkh/x0V
+ Hf8TCxP/DwcP/w8HDv8PBwr/DwcK/w4FCP8RCRD/EQkR/xEJEf8LCQv/FBAU/yEaIf8eFRj/Fg0S/xEJ
+ EP8JBAj/BgEF/wwIDP8fHSL/YGVp/5aipP+Ej5H/fYiK/32Hif91fIL/dn2D/3d+hP9+iIr/foiK/4CL
+ jP+Ik5X/rLy//0RIRv87Qz//la6u/3yPjv+SnZv/eoKA/0hKR/8zMTP/EwwT/w0GDf8aERr/FAwU/woD
+ Cv8HAwf/BQMF/wQCBP8DAAL/BgAE/wgABf8HAAb/CAAH/wgAB/8IAAX/CgEG/wkBBv8EAAL/BQED/wUD
+ BP8KAwL/EgkN/xkRF/8XERf/FQ8V/woDCv8OAw7/HxYg/1taXf+KkpH/en5//3J2d/92fH//f4aL/3yD
+ h/99hIb/gIiJ/4iRkP+Ml5f/sb/A/0FGRv88RkH/l7Cx/3yPjv+OmZf/YGVi/zw9Of8cFhv/EQoR/xEK
+ Ef8MBAz/CAAI/wcAB/8DAAP/BgAG/wgAB/8LAAf/DgAJ/w0ACP8KAAr/CgAK/woACv8NAgj/EAIK/xAC
+ Cv8QAAr/EAAK/w0ACP8IAAH/CAAA/wgAAP8MBQz/Hhge/xsUG/8RBRH/EAMQ/x4UHv9XVlf/iI6O/4aM
+ jP94foD/e4KF/3l/gv9+hof/ho+O/4qTkv+Ml5f/scDA/0FGRv8+RkL/mbGy/32Pjv+Fj43/UlVS/yYk
+ Iv8WEBb/FA0U/wkBCf8IAAf/CAAH/wgAB/8GAAb/BAAD/wIAAf8EAAL/BwIE/wcCBP8GAQX/BgEG/wcB
+ Bv8IAAX/BAAC/wgABP8JAAX/CwAH/xQADf8MAwb/CAAA/wgAAf8HAAb/CgMK/xwWG/8iGCH/EgcS/w8E
+ D/8hGRr/VlZW/5Obm/+LlZn/foWJ/3h+gf97goT/ho+O/4yXlf+Klpb/r729/0BGRv8/QUD/lqWp/3+K
+ jf9gZWj/KCUq/wgBCP8KAgr/CQEJ/wgAB/8HAAH/BgAA/w4GBv8aExP/IRoa/yYgH/8tJiT/QDc0/0A3
+ NP86LSr/OSwp/zgrKP8qHh7/KB0c/ygdHP8hGBv/HBMY/xcOFP8QBw7/DQQM/wkBCP8LAQP/CAAA/woC
+ A/8gGBz/LyUp/xkRF/8fGx//Gxcb/1RTVv+Nm57/h5OV/2hsbv9ucHL/fYSF/4mUlP94hYf/lKWo/ztB
+ Qv8xMS//kZ6i/4eTlv9GR0v/GBIZ/wcAB/8IAAj/CAAI/wkBCP8bExL/MSgm/0lAPP9lYVz/cW9p/3p3
+ cf9pYFv/aF5Z/1lPSv9LPDj/Sjs2/0k6Nv89MCz/PC8r/zsuK/82LSz/Ny8u/zgwL/8qHCT/HxIb/xQJ
+ Ef8eChD/DQID/wcAAP8PBw3/Myot/zoxM/8rJyr/GBQY/xYSFv9WVlf/gYyO/3N5e/9lZWb/e4GB/4KL
+ jP91goT/kaOl/zpBQv8wMC7/nauw/3N8f/9BP0P/FxEY/wcAB/8LAgn/HA8U/zUjI/9JPTn/YlhS/1RL
+ R/86NTL/JyEf/yUfHv8cFBP/GxMS/xMLC/8PBgf/DwYH/w8GBv8OAgb/DQEG/w0BBv8IAgj/DgYN/xQM
+ Ev8SCRD/DgYN/w0EC/8QBAf/CgEC/wcAAP8QCA7/LiUo/ysiJf8oJCj/JSEl/xMOEv8zKiz/VFNU/3qC
+ g/94fX7/jZmZ/4CIiP90gIL/kKKl/zpBQf80NjX/e4uP/09VVv8vJij/CQEJ/xAHDf81IR3/QSok/zci
+ Hv8sHBz/GxAU/w0FDP8EAQP/AQAA/wEAAP8FAAL/CQAE/wkABf8JAAj/CAAI/wkACP8NAAr/DQAK/w0A
+ Cv8KAAn/CgAJ/wkACP8IAAX/BAAD/wQAA/8AAAD/AAAA/wEAAP8MBAX/LSMl/y0jJv8lICT/MS0x/yId
+ Iv8ZDxL/OzU2/3Bycf98hoT/hpCQ/4SNjf9zfX//jJqe/z1AP/9BRkb/eIiM/z5CQv8WDxT/DwcN/zAj
+ JP9DLCb/LxwY/xgLCv8IAAj/CAAI/wgACP8IAAj/CgAK/woACv8PAAr/EAAK/xAACv8MAAv/CwAL/wsA
+ C/8NAAr/DQAK/w0ACv8QAQ3/EAEN/xABDf8QAgr/EAIK/w8CCv8HAgT/AAAA/wEAAP8GAAD/Ixob/0k+
+ Qf83Mzb/KSUp/yIcIf8QBQn/IRgb/15dXP+Bion/gYuK/32Hhv9yfH//i5me/z1AP/9CRkb/eIiL/zxA
+ Qf8LBQz/LiIk/0U2NP8kFRP/EAYG/wkBAv8HAAf/CAAI/wgACP8KAAr/CgAK/woACv8PAAr/DwAK/w8A
+ Cv8OAA3/DwAO/w0ADP8NAAr/EAAM/xAADP8QAQ3/EAEN/xABDf8QAgv/EAIK/xEDC/8SBQz/AwEC/wEA
+ AP8HAAD/GQ8R/0xBRf9GQkX/Ihwh/yIdIv8iGRz/FAkN/0hERf+BiYn/hI2N/4SNjf9+io3/nq60/z0/
+ Pv9GRUb/e4aD/0FAQf8QCRD/KRog/xoPFf8GAAX/CAAH/wYABf8CAAL/BgEG/woCCv8IAAj/CAAI/wgA
+ CP8IAAj/CAAI/wgACP8PAAj/EAAJ/w8ACf8JAAj/CQAI/woACf8PAA//EAAQ/xAAEP8QBBD/EAQP/xAE
+ D/8VBA7/CgEG/wQAA/8HAAf/DwcO/y0kLP80KzT/LCIr/yYcIf8yKyv/DQUM/ychJP9vdHL/i5WU/4uV
+ lP+FlJL/prq5/0FGQ/9HRUb/e4aD/zEsLv8LAwv/CAAI/wgACP8IAAj/CQAJ/wkACf8JAgn/CgIK/wYB
+ Bv8HAAf/CAAI/wgACP8NBAv/GRAU/xgOE/8RAQn/EAAI/w8ACP8JAAj/CAAI/wgACf8PAA//EAAQ/xAA
+ EP8QBBD/EwcS/xYKE/8TAw3/EgMM/wQAAv8HAAf/DwcP/yohKv8yKTL/KiEp/yQZH/8+NzX/IBkc/xcQ
+ FP9WVlT/jJaU/4uWlP+FlJL/pbq4/0JGQ/9FQ0X/eYSB/yAXGv8IAAf/CAAI/wgACP8JAAn/BQAF/wYA
+ B/8GAQf/BwQH/xEKEP8pHyP/OS4w/0c8O/9QRD//VEhB/0U5Nf8/LSn/NSQi/zQjIf8yIB//KBka/xII
+ Dv8RAhD/FAMR/yAPGf8qHSD/IhYb/x4SGP8UBQ3/FAMN/wUAA/8HAAf/DgYO/ykgKf8zKTL/Jhsi/yAU
+ Gf8/ODb/KCEj/w4GDf9EQkD/gIiG/42Xlf+GlZP/p7u6/0NIRf8zMDX/bHJz/xkTGf8HAAf/CAAI/wcA
+ CP8MBAv/FQsQ/xYNEv8lHyP/Pjc3/1NJRv9TSkb/TkVC/1VLSP9GPjz/Qjo5/0I5OP9NRkL/UElF/1BI
+ Rf9SS0r/UUpJ/0k/P/9AMjT/NyYp/zUkJ/80JyX/KBoa/ycaGf8cDhH/EgYJ/wcAAP8IAAf/CQEJ/yIX
+ Hf84LDH/LR0i/yUTF/8nHiL/Ny4w/xoTGP82MzP/am9t/5agnv+Qnp3/qLq6/0lNSv8xLzT/a3By/xgS
+ Gf8HAAf/CAAI/woCCf8vIyL/QjQu/1FCOv9VTEf/RT08/ykiJ/8VDRP/CAAI/yIaHv9OSEb/aGRg/2Bc
+ WP9hW1j/XlhV/11YVf9bWVr/V1RV/1lWV/9iXVr/Y11a/1xVUv9QSkX/Ny0q/x4QD/8SBgj/DQME/wgA
+ AP8IAAf/CAAI/x4TGf84KzD/MyQp/zYoLf8tIyf/KiAk/yEYHf8nIyP/XWBe/5CamP+Kl5b/cn17/zMz
+ L/8wLjP/am9x/xgSGf8GAAb/CQIJ/yUZHf87Liz/V0c+/1NEO/8sJSn/EAsV/wYCDv8KAgr/GBAW/01F
+ Q/9mYl7/Yl1a/19aV/9iXFn/UktJ/1JLSv9RS0v/TEJA/1lVVf9gWlj/Vk1M/1NKSf9UT0v/XVpV/05I
+ RP8vHiL/EggJ/woCA/8IAAf/CAAI/xAGDf8yJCn/OCov/zsuMv89MzT/Migr/ywjJf8dFhf/QD8+/36G
+ hP+Djoz/VltZ/yopJf8pJCn/YWJi/xoUGv8IAwj/HBYY/zUsK/9NRkT/TkdE/zoyMP8rIib/FgsV/xQJ
+ E/8sICX/RTs9/1pTUf9QSEf/XVhX/2lmZf9tbWz/TERH/zMlK/84LDD/TEVH/2dnZv9xbGr/aWJh/1hP
+ UP9JRUb/RkJE/0xISf9ORUj/QTY6/zQoK/8YDRH/EQUK/xAECP8rGx7/NCQn/0g7Pv9dWV3/XVld/1VR
+ Vf8cFhz/Liwx/19lZf+JjIv/foB+/y8vLv8oIyj/YGFg/xsVG/8bFRb/NCwq/1dLRf89NjT/OTIx/0tF
+ Qv80LC3/HBIa/yQaIP86LzL/XFVU/2VgXf+AgX3/hoeD/319ev90dnT/bW1s/1xZWv9aV1f/Zmdm/2xt
+ a/9ybWv/c21r/3Nta/9wbmn/aWZi/2JfXP9eWVv/VlBT/0tDRv8sIyX/MSgp/yAVF/8fDRH/MyMm/0s/
+ Qv9weoX/eoua/3eLnP8YEhv/LSsw/1NXWP91d3b/goOC/y8wL/8oJCj/YWJi/zEtMP9BNzT/VElD/0U6
+ Nv8WDQ3/OjQy/1JMSf9BOzn/OTMz/zUtL/9RSkr/eHZx/3h2cf9raGb/VU9P/19aWf9vcG7/cnRy/3N1
+ c/9tb23/bG1r/2pqaf9kXl3/WlJT/01DRf9LR0j/SkZI/1tXVv9pZ2j/aGZo/1FKTf8oHiD/MCco/0Y/
+ Pf9DNjj/Rzo8/1tSVf9xe4X/eYiV/5G1zP9HTFL/Ih0j/0pNT/9jZWT/gYOC/y8wL/8lJCf/aXBu/0VH
+ R/8yKS3/LyUq/xwRGf8aEBP/REJB/1teW/9QT0//RkJF/0M/Q/9zcHL/amVn/1ZPUv9bVlT/T0dG/z40
+ Nv9GPUH/YF1f/21rbP9ybWv/bGZl/11WVv9IQkP/QTo8/1NQUP9WU1b/SUVJ/z03PP9FQEH/ZWZl/15d
+ XP89MzX/JRcd/0lBQf9WT07/UElH/0Q8Pf9TUVT/ZGhq/3+Njf9bW1v/KCAj/0VBQv9gZmX/fISC/y4w
+ MP8lJCb/anJw/0RGR/8cEhv/Jhwj/ykfJf8jGRz/Lico/zo2N/9LSUr/RkJG/1JQUv9raGr/UEdL/11X
+ Wv9TTEz/WlVS/19aV/9ANjn/UkxP/2pnaf9ybGr/Y1tb/1FISf89Njj/WFZW/1dVVf81LjX/PDY8/11b
+ Xf9BOzz/RkBB/2lqaP9PSUj/LCAk/0hAQP9cVlT/T0hH/y8nKP87Mjb/MSQp/z83Ov9RTk//SkVG/zcw
+ Mv9NUVH/bnZ1/yUnJ/8mJSf/aXFv/0ZJSf9FQED/SkRD/0lDQ/80Li//FgoP/xYKD/89OD3/R0NH/1RS
+ VP9XUFP/U0tP/0xDR/8ZChH/Jxsf/2BbWP9FPD//S0NG/2ViZP9pY2L/XFRU/05FRv9GQEH/X19e/zQr
+ Lv8lHCT/HxUf/2JhYv9VUVH/ODAy/2hoZv9dWlb/Myks/0tERP9jXVr/V1BO/0A4OP9PTU//TEhL/0xI
+ Sv9ST0//VFFR/0pHR/8/QkL/YGZl/x0fHv8wLi3/b3h2/0NEQ/9WVlX/WFhY/1VVVP8yLTL/DgYO/w8H
+ Dv9VUVb/T0xR/1tZXf9VUFL/Uk1P/z42Of8VCAz/FQkN/zcuMP8hExP/Qzk5/1xWVf9TTkv/UkxK/0xF
+ Q/8xJyr/VFFT/y8lKf8YEBf/IBkh/2ZoaP9KQET/QjY6/3Vwbv9RT1D/Miwx/1xdW/9cVFP/WVBP/1dP
+ T/9LSEr/X2Bf/2JkY/9ZW1j/XF5c/2JkYf9LSkr/T05P/yAdG/8kIB//X2Zk/1JVVP9iZGL/Y2Rj/05O
+ Tv8nISf/CAAI/xEJEf9gXWD/T0xS/05MUv9YVVf/NCou/0A4O/86MjX/TEZI/11ZWv9mYWD/gH18/358
+ ev9ra2j/aWlm/2ZlYv9COz3/TklM/0dCRf81MTb/RURH/z06Pv8xJCr/WlBS/2dfX/8xKjD/Ozc7/2Nl
+ Yv9VSkn/VUlJ/2BdXP9HREf/TkxO/2NkY/9jZmP/XF9c/11gXf9UU1P/U1FS/x4bGP8XEBDaHxkZ8yom
+ JvtnaWb/YWJh/0lISf8aFBr/DgcO/xQNE/9iX2H/Uk9U/zAtNv9gXmD/dXZ2/5mgnf/Dy8j/zdbT/9Pe
+ 2v/S29j/0tvY/9Da1//E1s//xNfR/8PVz/+1xMH/tMLA/7bFwv+lsa3/ho2L/11eYP9nYWH/a2Rl/0xC
+ Rf9FQUT/cHZy/3F1cP9aUVD/XldW/2hoZv9fYF//SUZI/1VVVf9qbGr/aWxp/1dZV/9NS0z/ZGJk/yQg
+ Hv9+eXc8GBMWoBIOEflwdHD/VFVU/zk2Of8eGB7/GxUb/xAND/9ubWn/jZCM/5Walv/S2dj/6vLy/+bu
+ 7f/l8u3/z9zX/7PAvP+5xcL/vcnG/7rGxP+5xr7/xdLM/7/Mxf+turj/sb+9/73Py/++zcv/sr++/666
+ uf+ptbL/prGu/5eem/+IjIn/obCx/4CBff9pY2H/bWhm/3x8ev97fXn/XFtY/x8ZGf9MSkv/d3h3/2do
+ Z/9ZWln/Zmdm/yooKv8AAAAADwsPdhMQE/5scG3/S0pL/ykkKf8fGB//JR0l/xEPEP9vb2r/srq1/9Le
+ 2f/N1ND/yc/L/8TKxv+yvrn/t8S//9Dd2f/S5uD/wdDN/7zKx/+5xb7/ucS9/7O+tv+ksK7/n6qp/5ym
+ pf+cpqX/nKal/5ympf+aop7/pbCs/6axrv+PlZL/l6Cg/4CBfP94dnT/g4WE/46Uk/9+gHz/Xl1a/xYP
+ D/8WDxb/TkxO/3h6eP92eHb/cXJx/yooKv8AAAAAFxMWhhMQE/9naWb/Q0FC/ysmKv8lHSX/KSAo/xIP
+ Ef90dnD/vMS//7jAu/+prKT/o6ad/7K3sP+0wLv/xNHM/+Tx7f/P4tz/v87K/73Kx/+/y8T/wMvE/7/L
+ xP+zwr7/qba0/6Crqf+cpqX/nKal/5ympP+aop3/lZuW/5eemf+mtrf/kZiW/6W0tv+qurr/q7m4/6y8
+ vP+Dh4P/XV1a/xYPEP8PBw7/GBEY/z47Pv9zdHP/e317/yooKv////8CHhodhxEOEf9gXlr/S0pG/zAu
+ LP8qIyP/KyQj/xUODv9STkz/3uTf/6+wrf+vs63/tsK6/7O5s/+ttrT/rri2/7vHw/+5x8H/u8jD/7nG
+ wf+3wLj/t764/7fAuP+1w73/tMK9/667tf+iq6b/n6ii/52kn/+aoJv/lZmV/5yjoP+91Nr/kpaY/56o
+ rf++1t//sbzE/8Xi6/+xwL//WFtc/w4KDf8hGh3/GhMX/w8KDv8yLjL/a2tr/y4pLv8AAAAAEAwQahIO
+ EfZlY17/VlVR/zw7OP8sJST/KyQj/ycfH/87NTT/vcC7/7K1sf+1wrr/t8O7/7W9t/+strP/r7q3/7nG
+ wv+/zMf/ucfB/7fEvv+4wbj/t7+3/7e/uP+0wrv/ssC5/7G+uP+kr6r/oKmk/56loP+YnZj/l5yX/5id
+ mv+ntbn/kZWY/5mgpP+3ytP/vNLc/8rm8P/V6ef/WFxd/w4JDf8jHB7/Jx8h/x4XGv8aExr/RkJG/ykj
+ Kf9PTE0AT0tMIRANEMMrKij/TUxI/01LSP9CPDr/Ligm/ykiIf8ZERH/fnx6/7a6tv+1wLn/tsC5/7fC
+ u/+1wbz/uMXA/77Mxv+/zcf/t8a//7XDvf+5w7r/t7+3/7e/uP+vu7T/qLKq/625sv+msq3/oKmk/52k
+ nv+boJv/mZ6Z/5mfm/+ntbn/naWp/5qgpP++1+D/xeTv/8Xe5v++zsz/OTo9/xEMEP8lHiD/LSUm/y4m
+ KP8sJyv/PTo9/yAbIP////8AAAAAAAcFBloeFxe9HBQU+D85OP9dWlT/VVNN/zMwLv8eFRX/PDc3/7XC
+ v/+8x7//t8C4/7jCu/+1w73/tcO9/7fFv/+4yMH/uMfA/7XDvf+3wLr/tsC5/7bAuf+0vbX/r7Wv/62z
+ rv+lsaz/oKmk/52jnf+fo5//nJ+c/5yioP/E3uX/t8fP/6iutP+82+P/vd3l/7TLz/9gXlv/EAwQ/yci
+ I/8sJyr/Miwt/ywoKv8kJCT/Njg2/xwcHP8AAAAAqKOgAP///wFqZGIzIxsbphgREfYsKSj/VFJM/1hV
+ UP86NDP/IRgY/6m1sv+/y8T/sLaw/7bAuf+1w73/tcO9/7bFv/+2xb//uMjB/7bFv/+1wLr/s764/7O+
+ t/+3wLf/t8C3/7G4sf+lsaz/oKmk/56ln/+eop7/mp2a/5qgnv/F4ej/wNbe/7PByP+93+j/sMPE/5+n
+ o/8vLCz/HBcZ/zItLP8uKSv/Likr/ywoKv8iIyL/PkA+/yIjIv8AAAAAAAAAAO3q5QD///8CXldWIBsT
+ FJIMCAvSFxMV/TIwL/9TUlH/SUZG/7jGwv/M3dP/sbex/7W9t/+2xL7/t8fA/7vMxP+8zcX/vMzE/7rK
+ wv+6wrz/tr+5/7a/uf+3wLj/t8C3/7G6s/+lsaz/oaqk/5+nof+gpaD/n6Of/52jof/I4+n/ucnQ/7rN
+ 1P/H8fv/utXZ/5yinv8tKSn/HRgZ/zEsK/8wLCz/Likr/y4qLf89Pjz/U1VS/yAhIP8AAAAAAAAAAAAA
+ AAAAAAAA4+rpAE5IRwk5OTg7BAQFlAUEBdsVDhX5GBAY/4yOjv/o7+7/wMTD/7a6uf+2xL7/vc/I/8bb
+ 0//Q4tn/0ODY/8jUy/+9xb3/vsa+/77Hv/+4xL//sr65/6i0r/+lr6r/paum/6Sqpf+gqaT/mqKc/52l
+ oP/Z7u3/vcXG/8jW2P/d+v//0Ort/5ehoP8kGx3/MCko/zIrKv8xLzD/QUFB/01NTP9YVlP/VFNP/xcW
+ Ff8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACanJoAmZyZCBAPEEUiHCKCGBEYxB0WHfe8v8D/2N3d/7W5
+ uf+3xsD/u8zF/8zk2//d9u7/2vDo/8/e1f/EzsT/wcvB/8TQxf+3xL//q7iz/6azrv+lr6r/pa2o/6Wr
+ pv+gqqX/maGc/6ezr//F0NH/vcXF/9vv7//g/f//3vv//5Ocmv80LCv/VVFM/1VSTP9YWVf/W11b/1JT
+ Uv8+PTr/ISEf+wYGBuoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wCoqagIRUFFKhMM
+ Erxva2z/7vX1/8fMzP+8zcb/vM7H/8LVzf/S5Nz/0+Xd/87c0//L2Mz/wcvC/7nBuv+qtrH/pbKt/6ay
+ rf+lsaz/pa6p/6StqP+gqqX/maCb/6ezsf+koqP/yNTU/+b+/v/i/f//4fv9/4aLiP9RTUj/XFlT/01K
+ Rf8vLi//Gxkb/hYUFvwMDAzgFxYVmSAfHkYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA+5U8AAAAAJpCQj7/5/Lu/+v38//W5d7/xtfP/7zOxf/C0sr/xdbN/8bXzv/G1s3/u8jC/664
+ tv+stbH/rLSv/6y0r/+lsKv/oqyn/6KsqP+gp6b/lpmZ/6iwsP/L19T/1+fk/+j69//y9vX/rq+u/zMw
+ Lf4OCg39EAwP9A4KDe8NDA3JBAQEnwQEBIk+PDtBPT09Cf///wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAABwZGVwnJSLwtb25//P/+//u+vb/1uXe/77Qx/+/z8f/vczF/8LS
+ yv/A0Mj/r7m3/622tf+ttbD/rbSv/6y0r/+lsav/o6+q/6Otqf+fpaX/n6Wl/8fY1f/Z6ub/0N7a/+X1
+ 8f/Jysj/JSIe8iIeG8QmIiSQEAwQZgsHC1pQUFAura6tCKmpqQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEdCQBoNCgbFSElF/87X0//x/fr/3uzn/8PU
+ zP+7ysP/ucjB/7vKw/++zcb/rba0/6y0s/+ttrH/rbSw/620r/+msq3/pK+r/5+no/+boKD/mZ2d/8LR
+ zv+us7D/2OXi/9rq5/9oZ2X/GRUStVtZVjHU1s4KCgUIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWlZSAAAAAAAjISA9BgYFz1pb
+ Wf7O2db/3+3o/9rn4v/I1c//x9TN/7vGwf/E087/wc/K/7K7tv+xurj/sbq4/663tf+lrq3/qbKw/7K8
+ uP+2wr3/v8rG/8rV0v/S09L/6evq/2tvbP8KCgrkEA8PVAAAAAChoZ4AAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI6M
+ iQDs6OQELi0sTAkKCdVgZWT/zNfU/97r5//L19H/ucK9/6ivq/+rsqz/tb65/6mwq/+am5n/n6Kg/7bA
+ vv+7xsD/ucS+/8POxv/Azcf/0dzZ/+Dp5//p6un/b3Fv/xEVEeshIiFrsbGxCKqqqgAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAADe29YA8u/qBTQzMlcNDQ3aSU1M/7W/vP/h7+j/v8fC/5WXlP+WlI//lJKM/5OQ
+ i/+Mh4T/k5CO/77KyP/L187/ws3G/9Db0v/q8/H/7PTz/9rg4P9qbWr/ERUR6ycrJ3W+wL4MKCwoAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUFBQAP///wMmJCVCDQoNrCEfIuyChYP/r7Ow/73A
+ vf/O0c7/6ezo//P48//t+PT/1t/c/+Pt6v/l6ub/09fT/8XIxf/DwsL/jouL/zQuLvoOEA3RExUTZqGj
+ oQhKTUoA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZVVkAWVZZFSEe
+ IWgRERHEAAAA7SEhIfpMTEz/enx6/42Ojf+IiIj/iIeH/4eGhv9rbGv/TU5N/zAwMP0ZEhLwGBAQ2CUe
+ HotMTEoqBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAZmdmAKeppwI1NjUlFhYWRmdpZ89WVlb/NTQ1/xkXGf8bDhL/Gw0R/x0QFP8tLCz/TU5N/2ts
+ a/FQS0tgFw8PMlFLSwwAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAA
+ AAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAADAAAAAAAAAAMAAAAAAAAAA4AAAAAAA
+ AAD4AAAAAAAAAP4AAAAAAAAA/4AAAAAAAAD/4AAAAAAAAP/gAAAADwAA/+AAAAD/AAD/8AAAA/8AAP/w
+ AAAD/wAA//gAAAf/AAD//AAAD/8AAP//AAA//wAA//+AAH//AAAoAAAAQAAAAIAAAAABACAAAAAAAABA
+ AADDDgAAww4AAAAAAAAAAAAACAQA/wgEAP8IBAD/CAQA/xAICP8QCAj/EAgI/xAICP8IAAD/CAAA/wgA
+ AP8IAAD/CAAI/wgACP8IAAj/CAAI/wgACP8IAAj/CAAI/wgACP8IAAj/CAAI/wgACP8IAAj/CAAA/wgA
+ AP8IAAD/CAAA/wgACP8IAAj/CAAI/wgACP8AAAD/AAAA/wAAAP8AAAD/CAAI/wgACP8IAAj/CAAI/wgE
+ CP8IBAj/CAQI/wgECP8IBAj/CAQI/wgECP8IBAj/CAAA/wgAAP8IAAD/CAAA/wgEAP8IBAD/CAQA/wgE
+ AP8IBAj/CAQI/wgECP8IBAj/CAQA/wgEAP8IBAD/CAQA/wgEAP/G5+f/xufn/8bn5/+909b/vdPW/4OP
+ kf+Dj5H/vdfW/73X1v+Aj47/REdH/1pdWv9aXVr/Ix8j/wgACP8IAAj/CAAI/wgACP8IAAj/EAgQ/wgA
+ CP8QCBD/GBAY/xgQFv8YEBb/EAgL/xgQFv8TChP/EwoT/xMKE/8IAAj/DQoN/xsVG/8NCg3/DQoN/wgA
+ CP8IAAj/CAAI/ykgIf9PVFT/T1RU/3N9e/9zfXv/pbK1/6Wytf9weHv/pbK1/73T1v+909b/vdPW/73T
+ 1v+tusb/rbrG/626xv+tusb/gIuM/73Pzv+9z87/vc/O/87j5//O4+f/zuPn/wgEAP8IBAD/xufn/4ab
+ mv+Gm5r/vdPW/4OPkf+Dj5H/g4+R/4CPjv9ER0f/REdH/0RHR/8IAAj/CAAI/wgACP8IAAj/EwoT/xMK
+ E/8eFR7/HhUe/yEYIf8hGCH/IRgh/yEYIf8hGCH/IRgh/yEYIf8YEBb/KSAp/ykgKf8pICn/KSAp/ykg
+ Kf8pICn/KSAp/xsVG/8TChD/CAAI/xMKEP8IAAj/CAQI/wgECP8IBAj/Kywu/zw+Qf9weHv/pbK1/6Wy
+ tf+AjI7/gIyO/4CMjv+AjI7/dn2E/3Z9hP92fYT/dn2E/4CLjP+Ai4z/gIuM/4CLjP+MmJr/jJia/87j
+ 5/8IBAD/CAQA/4abmv+Gm5r/hpua/4OPkf+Dj5H/g4+R/0lLTP9ER0f/REdH/0RHR/8IAAD/CAAI/wgA
+ CP8jHyP/Ix8j/ykgKf8pICn/KSAp/x4VHv8QCBD/CAAI/wgACP8IAAj/CAAA/wgAAP8IAAD/CAAA/wgA
+ CP8IAAj/CAAI/wgACP8AAAD/AAAA/xsVG/8pICn/KSAh/x4VGP8TChD/EwoQ/wgECP8IBAj/CAQI/wgE
+ CP8IBAj/PD5B/3B4e/+lsrX/gIyO/4CMjv+AjI7/gIyO/3Z9hP92fYT/dn2E/3Z9hP+Ai4z/gIuM/4CL
+ jP+Ai4z/jJia/4yYmv/O4+f/CAQA/wgIAP+109b/e4+O/3uPjv+Unpz/lJ6c/2pwbf9BQj7/QkFC/xsV
+ G/8IAAj/CAAI/x4VHv8pICn/CAAI/wgACP8FAAX/AAAA/wAAAP8AAAD/AAAA/wAAAP8IAAX/CAAF/wUA
+ Bf8KAAr/BQAF/woACv8IAQX/CAEF/wgBBf8IAQX/AAAA/wAAAP8AAAD/AAAA/wgAAP8IAAD/IRgh/yEY
+ If8eGB7/EwwT/wgACP8IAAj/EAQQ/xAEEP8xKTH/c3Vz/5ympf9zdnb/c3Z2/3N2dv9zeXv/foWJ/4mR
+ l/9+hYn/e4KE/3uChP+AiIn/jJaU/46amv+Ompr/1ufn/wAAAP8ICAD/tdPW/3uPjv97j47/lJ6c/5Se
+ nP9BQj7/QUI+/y4rLv8IAAj/CAAI/xsVG/8TChP/CAAI/wgACP8IAAj/AAAA/wUABf8FAAX/BQAF/wgA
+ Bf8IAAX/EAAK/wgABf8KAAr/CgAK/woACv8KAAr/CAEF/xACCv8QAgr/EAIK/xAACv8QAAr/EAAK/wAA
+ AP8IAAD/CAAA/wgAAP8IAAD/CAAI/ykkKf8pJCn/CAAI/xAEEP8QBBD/EAQQ/zEpMf9zdnb/nKal/3N2
+ dv9zdnb/c3l7/36Fif9zeXv/c3l7/4CIif+Gj47/ho+O/4aPjv+Ompr/jpqa/9bn5/8AAAD/CAgA/7XT
+ 1v97j47/e4+O/5SenP9qcG3/QUI+/0FCPv8IAAj/GxUb/xsVG/8IAAj/CAAI/wgACP8IAAj/CAAI/wUA
+ Bf8FAAX/CgAK/woACv8QAAr/EAAK/xAACv8QAAr/CgAK/woACv8KAAr/CgAK/xACCv8QAgr/EAIK/xAC
+ Cv8QAAr/EAAK/xAACv8YABD/CAAA/wgAAP8IAAD/CAAA/wgACP8IAAj/Hhge/ykkKf8QBBD/EAQQ/xAE
+ EP8QBBD/IRgY/3N2dv+cpqX/nKal/3N5e/9+hYn/foWJ/36Fif97goT/gIiJ/4yWlP+MlpT/jpqa/46a
+ mv/W5+f/AAAA/wgIAP+109b/e4+O/3uPjv+Unpz/anBt/0FCPv8YFBD/GxUb/xsVG/8IAAj/CAAI/wgA
+ CP8IAAj/CAAI/wgACP8KAAr/BQAF/wUABf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8FAAX/AAAA/wUA
+ Bf8IAQX/AAAA/wAAAP8IAQX/CAAF/wgABf8QAAr/GAAQ/xAIC/8IAAD/CAAA/wgAAP8IAAj/CAAI/wgA
+ CP8eGB7/MSkx/xAEEP8QBBD/EAQQ/yEYGP8hGBj/c3Z2/5ympf+UnqX/foWJ/36Fif9zeXv/e4KE/4CI
+ if+MlpT/jJaU/46amv+Ompr/1ufn/wAAAP8QDAj/vc/W/4OOkf+DjpH/a3Fz/0pLT/8IAAj/CAAI/wgA
+ CP8IAAj/CAAI/wgACP8IAAD/CAAA/wgAAP8IAAD/CAAA/wgAAP8IAAD/CAAA/wgAAP8uJSP/LiUj/y4l
+ I/8pHRv/KR0b/ykdG/8pHRv/EAQI/xAECP8QBAj/EAQI/wgACP8IAAj/CAAI/wgACP8IAAj/CAAI/wgA
+ CP8IAAj/CAAA/wgAAP8IAAD/CAAA/xsSGP9CODn/GxIY/wgACP8pJSn/CAQI/yklKf9raWv/pbq9/4GM
+ jv+BjI7/XV5f/3Z6e/92env/iZSU/4mUlP92hIb/doSG/63Dxv8ICAj/EAwI/4OOkf+DjpH/g46R/0pL
+ T/8pJSv/CAAI/wgACP8IAAj/CAAI/wgACP8IAAj/CAAA/wgAAP8IAAD/KSAe/1RQTP9UUEz/e3lz/3t5
+ c/97cWv/e3Fr/3txa/97cWv/a1lS/2tZUv9rWVL/a1lS/2haT/9oWk//aFpP/2haT/9aUUr/WlFK/z42
+ NP8+NjT/IxUe/yMVHv8VChP/CAAI/xsJDf8IAAD/CAAA/wgAAP8IAAj/LiUo/0I4Of8uJSj/KSUp/ykl
+ Kf8IBAj/KSUp/11eX/+lur3/gYyO/11eX/9jYWP/Y2Fj/4mUlP+JlJT/doSG/3aEhv+tw8b/CAgI/xAM
+ CP+DjpH/vc/W/4OOkf9KS0//KSUr/wgACP8IAAj/CAAI/wgACP8IAAj/CAAI/ykgHv9KQDz/a2Fa/2th
+ Wv97eXP/e3lz/3t5c/97eXP/VEtH/1RLR/9US0f/LiUj/ykdG/8pHRv/KR0b/ykdG/8QBAj/EAQI/xAE
+ CP8QBAj/CAAI/yMbHv8jGx7/PjY0/zEgKf8jFR7/IxUe/xUKE/8uEhv/GwkN/wgAAP8IAAD/CAAI/xsS
+ GP9CODn/Qjg5/yklKf8pJSn/CAQI/wgECP85MDH/XV5f/4GMjv+BjI7/Y2Fj/3Z6e/+JlJT/dnp7/3aE
+ hv92hIb/rcPG/wgICP8QDAj/g46R/73P1v9JTUz/SktP/yklK/8IAAj/CAAI/wgACP8IAAj/NCIj/0o0
+ Mf9KQDz/a2Fa/2thWv9KQDz/Ligm/wgAAP8IAAD/CAAA/wgAAP8IAAD/CAAA/wgAAP8IAAD/CAAA/wgA
+ AP8IAAD/EAQI/xAECP8QBAj/EAQI/wgACP8IAAj/CAAI/wgACP8IAAj/CAAI/wgACP8IAAj/CAAA/wgA
+ AP8IAAD/CAAA/wgACP8uJSj/LiUo/xsSGP8pJSn/KSUp/yklKf8IBAj/OTAx/zkwMf9dXl//gYyO/3Z6
+ e/+JlJT/nK6t/3Z6e/92hIb/doSG/63Dxv8ICAj/EAwI/3iIjP94iIz/REpK/0Q2NP8IAAj/CAAI/wgA
+ CP80IBv/SjAp/0owKf80IBv/OSQh/ygYGP8IAAj/CAAI/wAAAP8AAAD/AAAA/wAAAP8AAAD/CAAF/wgA
+ Bf8IAAX/CgAK/woACv8IAAj/CAAI/w0ACv8NAAr/DQAK/w0ACv8IAAj/CAAI/wgACP8IAAj/CAEF/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8IAAD/Ixkb/z4zNv8jGRv/IRwh/zEtMf8xLTH/IRwh/xAE
+ CP8zLC7/V1RU/3t9e/97hoT/e4aE/5SanP+DjIz/c32A/3N9gP+ltr3/EAwI/xAMCP+tx87/REpK/0RK
+ Sv8mGx7/CAAI/wgACP8mGx7/SjAp/zQgG/80IBv/HhAN/wgACP8IAAj/CAAI/wgACP8FAAX/CgAK/woA
+ Cv8KAAr/EAAK/xAACv8QAAr/EAAK/woACv8KAAr/CgAK/w0ADf8NAAr/DQAK/w0ACv8NAAr/EAEN/xAB
+ Df8QAQ3/EAEN/xACCv8QAgr/EAIK/xACCv8AAAD/AAAA/wAAAP8AAAD/CAAA/wgAAP8+Mzb/PjM2/yEc
+ If8xLTH/MS0x/yEcIf8QBAj/EAQI/zMsLv97fXv/e4aE/3uGhP+DjIz/e4aE/3N9gP9zfYD/pba9/xAM
+ CP8QDAj/rcfO/0RKSv9ESkr/CAAI/wgACP8mGx7/RDY0/0owKf80IBv/HhAN/wgAAP8IAAj/CAAI/wgA
+ CP8IAAj/CgAK/woACv8KAAr/CgAK/xAACv8QAAr/EAAK/xAACv8NAA3/DQAN/woACv8KAAr/DQAK/w0A
+ Cv8NAAr/DQAK/xABDf8QAQ3/EAEN/xABDf8QAgr/EAIK/xACCv8QAgr/EAUK/wAAAP8AAAD/AAAA/wgA
+ AP8IAAD/PjM2/1pNUv9SUVL/IRwh/yEcIf8hHCH/EAQI/xAECP8zLC7/V1RU/4uTlP+DjIz/g4yM/3uG
+ hP9zfYD/c32A/6W2vf8QDAj/EAwI/63Hzv9ESkr/REpK/wgACP8IAAj/Y1FK/0Q2NP8eEA3/CAAA/wgA
+ AP8IAAD/CAAI/wgACP8IAAj/CAAI/woACv8KAAr/CgAK/woACv8QAAr/EAAK/xAACv8QAAr/DQAN/xAA
+ EP8QABD/DQAN/w0ACv8SAA3/EgAN/xIADf8QAQ3/EAEN/xABDf8QAQ3/EAIK/xACCv8QAgr/EAIK/xgI
+ EP8IAgX/AAAA/wAAAP8IAAD/CAAA/yMZG/9aTVL/UlFS/yEcIf8hHCH/IRwh/zMsLv8QBAj/EAQI/1dU
+ VP+DjIz/g4yM/4OMjP+Lk5T/c32A/6W2vf+ltr3/EAwI/xgMEP+tw73/SUlJ/0lJSf8IAAj/HhEY/0o0
+ Of8IAAj/BQAF/wUABf8KAAr/BQAF/wAAAP8AAAD/CgIK/woCCv8IAAj/CAAI/wgACP8IAAj/CAAI/wgA
+ CP8IAAj/CAAI/xAACP8QAAj/EAAI/xAACP8IAAj/CAAI/wgACP8IAAj/EAAQ/xAAEP8QABD/EAAQ/xAE
+ EP8QBBD/EAQQ/xAEEP8YBBD/EAIK/wAAAP8IAQX/CAAI/wgACP8VDRX/MSgx/zEoMf8xKDH/KB4m/yAV
+ G/9CPDn/CAAI/wgACP8uKCj/cHVz/4yWlP+MlpT/jJaU/4aWlP+GlpT/xt/e/wgEAP8YDBD/rcO9/0lJ
+ Sf9JSUn/CAAI/wgACP8IAAj/CAAI/wUABf8KAAr/CgAK/wUABf8FAQX/BQEF/woCCv8KAgr/CAAI/wgA
+ CP8IAAj/CAAI/wgACP8IAAj/CAAI/wgACP8QAAj/EAAI/xAACP8QAAj/CAAI/wgACP8IAAj/CAAI/xAA
+ EP8QABD/EAAQ/xAAEP8QBBD/EAQQ/xAEEP8QBBD/EAIK/xgEEP8IAQX/AAAA/wgACP8IAAj/FQ0V/zEo
+ Mf8xKDH/MSgx/ygeJv8gFRv/Qjw5/y4oKP8IAAj/GxQY/1RUUv+MlpT/jJaU/4yWlP+GlpT/hpaU/8bf
+ 3v8IBAD/GAwQ/63Dvf9JSUn/GAwQ/wgACP8IAAj/CAAI/wgACP8KAAr/CgAK/wUABf8QABD/CgIK/xAE
+ EP8KAgr/AAAA/wgACP8IAAj/CAAI/wgACP8IAAj/KR8g/ykfIP8pHyD/EAAI/xAACP8QAAj/EAAI/wgA
+ CP8IAAj/CAAI/wgACP8QABD/EAAQ/xAAEP8QABD/EAQQ/xAEEP8dERj/HREY/xACCv8YBBD/EAIK/wAA
+ AP8IAAj/CAAI/xUNFf8xKDH/MSgx/zEoMf8gFRv/IBUb/0I8Of9CPDn/CAAI/xsUGP85NDH/jJaU/4yW
+ lP+MlpT/hpaU/4aWlP/G397/CAQA/xgMEP+tw73/SUlJ/xgMEP8IAAj/CAAI/wgACP8IAAj/CgAK/wUA
+ Bf8FAAX/BQAF/wUBBf8AAAD/AAAA/xAEEP8mHCD/RDg5/0Q4Of9jVVL/a11S/2tdUv9rXVL/Sj45/1pF
+ Of9BLij/QS4o/0EuKP8+KCP/Pigj/yMUFf8IAAj/EAAQ/xAAEP8YCBX/KRgh/zksKf8rHiD/HREY/x0R
+ GP8QAgr/GAQQ/xACCv8AAAD/CAAI/wgACP8VDRX/MSgx/zEoMf8xKDH/GAwQ/yAVG/9CPDn/Qjw5/wgA
+ CP8IAAj/OTQx/3B1c/+MlpT/jJaU/4aWlP+GlpT/xt/e/wgEAP8IAAj/jJeW/0pLT/8IAAj/CAAI/wgA
+ CP8IAAj/CAAI/wgACP8IAAj/CAAI/wgACP8IBBD/JiAm/0Q8PP9jWVL/a2Fa/2thWv9rYVr/a2Fa/0dA
+ P/8xKCn/MSgp/zEoKf9CPDn/TUdE/01HRP9NR0T/T0VE/1RPT/9PRUT/Sjw5/zwsLv8pFBj/KRQY/ykU
+ GP8xJSP/MSUj/zElI/8xJSP/Gw0Q/xsNEP8IAAD/CAAA/wgACP8IAAj/CAAI/ykcIf85LDH/OSwx/yEM
+ EP8hDBD/GxIY/0I4Of9CODn/CAAI/zw6Of88Ojn/lJ6c/5SenP+RoJ//kaCf/87n5/8YFBD/CAAI/4yX
+ lv9KS0//CAAI/wgACP8IAAj/CAAI/wgACP8IAAj/Sjs0/ykdHv9KOzT/Y1lS/2NZUv9jWVL/RDw8/ykg
+ I/8IAAj/CAAI/ykgI/8xKCn/XVhV/11YVf9dWFX/Y11a/1hST/9YUk//WFJP/1pZWv9aWVr/VE9P/1pZ
+ Wv9jXVr/Y11a/2NdWv9PRET/SkM+/xgICP8YCAj/GAgI/xsNEP8bDRD/CAAA/wgAAP8IAAj/CAAI/wgA
+ CP8pHCH/OSwx/zksMf8pFhv/OSwx/y4lKP8IAAj/Qjg5/wgACP88Ojn/PDo5/5SenP+Unpz/kaCf/5Gg
+ n/+RoJ//GBQQ/wgACP+Ml5b/SktP/wgACP8IAAj/CAAI/wgACP8IAAj/Sjs0/0o7NP9KOzT/a1lK/0Q8
+ PP9EPDz/CAQQ/wgEEP8IAAj/CAAI/wgACP8pICP/XVhV/3Nxa/9zcWv/XVhV/2NdWv9jXVr/Y11a/2Nd
+ Wv9aWVr/Wlla/1RPT/9aWVr/Y11a/2NdWv9jXVr/Y11a/2NhWv9jYVr/SkM+/xgICP8IAAD/CAAA/wgA
+ AP8IAAD/CAAI/wgACP8IAAj/HhIY/zksMf85LDH/MSEm/zksMf9CODn/GxIY/0I4Of8bEhj/EAgI/zw6
+ Of9obGr/lJ6c/5Ggn/9UWlf/VFpX/xgUEP8IAAj/jJeW/0pLT/8IAAj/CAAI/wgACP8IAAj/Py4x/ykd
+ Hv9KOzT/a1lK/0o7NP8mICb/CAQQ/wgEEP8IBBD/CAAI/wgACP8pICP/a2Fa/3Nxa/9dWFX/XVhV/11Y
+ Vf9jXVr/WFJP/0I8Of9YUk//VE9P/0o8Of9KPDn/Wlla/2NdWv9PRET/T0RE/09ERP9KQz7/Y2Fa/2Nh
+ Wv9jYVr/Qigx/xsNEP8IAAD/CAAA/wgACP8IAAj/CAAI/wgACP8xISb/OSwx/zksMf85LDH/Qjg5/y4l
+ KP8uJSj/LiUo/xAICP88Ojn/PDo5/5SenP+RoJ//VFpX/1RaV/8YFBD/CAAI/3N1c/9PTk//CAAI/wgE
+ CP8IBAj/KyQj/yskI/9HQD7/Y11a/0dAPv8rJCP/MSkr/yAWHf8QBBD/EAQQ/zEkKf8xJCn/Ylxa/2Jc
+ Wv9CODn/Qjg5/2BbWv9gW1r/c3Vz/1pWV/8pGCH/KRgh/zEkKf8xJCn/V1RV/2tta/9zbWv/c21r/19X
+ V/9MQUT/OTQ5/zk0Of85NDn/TEhJ/09HSv9PR0r/MyUp/zMlKf8QBAj/EAQI/xAECP8QBAj/MyMm/zMj
+ Jv8zIyb/T0JE/1pRUv9aUVL/WlFS/1pRUv8QCBD/Liwx/y4sMf9rdXP/nJ6c/2hpaP+cnpz/AAAA/wgA
+ CP9zdXP/T05P/wgACP8IBAj/KyQj/yskI/9PRD7/Y11a/yskI/9HQD7/R0A+/zEpK/8gFh3/EAQQ/yAW
+ Hf8xJCn/SUBB/0lAQf9JQEH/YFta/35+e/9+fnv/fn57/3N1c/9zdXP/WlZX/0E3PP9EPD//V1RV/2tt
+ a/9rbWv/c21r/3Nta/9zbWv/c21r/3Nxa/9fXFr/X1xa/0xISf9PR0r/T0dK/09HSv9PR0r/Jhwe/yYc
+ Hv8QBAj/EAQI/xgECP8zIyb/MyMm/09CRP9zf4v/c3+L/3N/i/9zf4v/EAgQ/xAIEP9MUFL/TFBS/5ye
+ nP9oaWj/nJ6c/wAAAP8IAAj/c3Vz/09OT/8IAAj/KyQj/yskI/9PRD7/c2Va/yskI/8QCAj/Y11a/0dA
+ Pv9CPDn/IBYd/yAWHf8xKSv/MSQp/2JcWv97eXP/e3lz/5yinP+copz/fn57/35+e/9zdXP/c3Vz/3N1
+ c/9zdXP/a21r/2tta/9rbWv/a21r/3Nta/9zbWv/c21r/3Nta/9zcWv/c3Fr/3Nxa/9zcWv/a2lr/2tp
+ a/9PR0r/T0dK/yYcHv88NDT/Uk1K/yYcHv8YBAj/MyMm/zMjJv9PQkT/c3+L/3N/i/+MrcX/jK3F/xAI
+ EP8QCBD/TFBS/0xQUv9oaWj/aGlo/5yenP8AAAD/CAAI/3N1c/9PTk//Kycr/09EPv9PRD7/c2Va/ysk
+ I/8QCAj/EAgI/2NdWv9HQD7/Qjw5/0I8Of9CPDn/MSkr/0lAQf97eXP/e3lz/3t5c/9gW1r/Qjg5/0I4
+ Of9gW1r/c3Vz/3N1c/9zdXP/c3Vz/2tta/9rbWv/a21r/2tta/9fV1f/X1dX/0xBRP85LDH/OTQ5/zk0
+ Of85NDn/X1xa/2tpa/9raWv/a2lr/09HSv8mHB7/EAQI/zw0NP9STUr/T0JE/09CRP9PQkT/a2Fj/3N/
+ i/9zf4v/c3+L/6Xb//9rdXP/EAgQ/y4sMf9MUFL/aGlo/2hpaP+cnpz/AAAA/wgECP9qcnD/anJw/zk7
+ PP88NDb/PDQ2/yYcI/8QBBD/GAwQ/y4nKP9aXVr/Wl1a/09OT/9PTk//PDc8/zw3PP97eXv/e3l7/2Jc
+ X/9JQET/UktK/1JLSv8xJSn/MSUp/0Q7P/9XUlX/a2lr/2tpa/9zbWv/c21r/2hhYP9dVVX/Uk9P/zkx
+ M/85MTP/Uk9P/1dUV/9XVFf/Mywz/zMsM/85MTP/a21r/2tta/9ST0//T0hH/xgIEP8zKCv/T0hH/09I
+ R/9jXVr/PDQ0/09IR/9UU1X/eIKB/3iCgf+csq3/c3Vz/xAECP8xKSv/Uk9P/2JpaP9iaWj/lJ6c/wAA
+ AP8IBAj/anJw/2pycP85Ozz/EAQQ/xAEEP8mHCP/Jhwj/xgMEP8uJyj/Wl1a/1pdWv9PTk//T05P/zw3
+ PP9PTk//e3l7/2JcX/9JQET/Ylxf/3Nxa/9zcWv/c3Fr/1JLSv8xJCn/RDs//2tpa/9raWv/c21r/3Nt
+ a/9dVVX/UklK/zkxM/85MTP/a21r/2tta/9XVFf/V1RX/1dUV/9XVFf/OTEz/zkxM/9rbWv/a21r/09I
+ R/8YCBD/Mygr/09IR/9PSEf/Y11a/zw0NP8pICH/MSQp/zEkKf8xJCn/VFNV/1JPT/9ST0//MSkr/zEp
+ K/9iaWj/Ymlo/5SenP8AAAD/CAQI/2pycP9qcnD/OTs8/yYcI/8mHCP/PDQ2/yYcI/8uJyj/GAwQ/xgM
+ EP8YDBD/PDc8/2NlY/8pICn/Y2Vj/3t5e/8xJCn/Ylxf/2JcX/8xJSn/MSUp/1JLSv9zcWv/RDs//0Q7
+ P/9XUlX/a2lr/3Nta/9oYWD/UklK/1JJSv85MTP/Uk9P/2tta/85MTP/EAQQ/xAEEP8zLDP/e317/1JP
+ T/8hFBj/Uk9P/2tta/9raWP/Mygr/zMoK/9PSEf/Y11a/2NdWv88NDT/KSAh/1RTVf8xJCn/MSQp/zEk
+ Kf9ST0//Uk9P/1JPT/8xKSv/MTQ0/2JpaP9iaWj/AAAA/wgECP9qcnD/anJw/zk7PP9STUr/Uk1K/1JN
+ Sv9STUr/REJB/xgMEP8YDBD/GAwQ/ykgKf9jZWP/KSAp/2NlY/9iXF//MSQp/3t5e/8xJCn/EAAI/xAA
+ CP8xJSn/c3Fr/0Q7P/9EOz//V1JV/2tpa/9oYWD/aGFg/1JJSv9SSUr/OTEz/2tta/9ST0//IRQY/zMs
+ M/8zLDP/EAQQ/3t9e/9rbWv/IRQY/1JPT/9rbWv/a2lj/zMoK/8zKCv/T0hH/2NdWv9jXVr/T0hH/zw0
+ NP9UU1X/VFNV/1RTVf9UU1X/Uk9P/1JPT/9ST0//Uk9P/zE0NP9iaWj/Ymlo/wAAAP8QCAj/e4aE/1dc
+ Wv8zMjH/YGFg/0pJSv9gYWD/VVVV/zk0Of8YERj/CAAI/wgACP9PTFL/a2lr/zMwOf9raWv/V1NV/ykc
+ If+Gi4n/KRwh/xgMEP8YDBD/GAwQ/11ZWv8YCAj/GAgI/11WVf9dVlX/Uk1K/1JNSv9STUr/Uk1K/yEU
+ GP9dW13/XVtd/yEUGP8YEBj/GBAY/xgQGP+Rmpb/WlBS/xgIEP9aUFL/e3Vz/2hsaP8hGCH/REJE/2hs
+ aP9gW1r/VUlJ/2BbWv9VSUn/R0RH/2NlY/9jZWP/Y2Vj/1pdWv9SVVL/a21r/2JlYv9EQkL/REJC/2Nh
+ Y/8IBAD/EAgI/3uGhP97hoT/MzIx/2BhYP9gYWD/YGFg/0pJSv85NDn/CAAI/wgACP8IAAj/a2lr/2tp
+ a/8zMDn/a2lr/4aLif8pHCH/V1NV/ykcIf8YDBD/GAwQ/xgMEP8YDBD/GAgI/11WVf9dVlX/XVZV/1JN
+ Sv9STUr/Uk1K/1JNSv8hFBj/IRQY/11bXf8hFBj/GBAY/xgQGP9UVVf/VFVX/zksMf85LDH/e3Vz/3t1
+ c/9EQkT/IRgh/0RCRP9obGj/VUlJ/0o4Of9gW1r/YFta/zk0Of9HREf/Y2Vj/2NlY/9iZWL/UlVS/2Jl
+ Yv9iZWL/Y2Fj/0RCQv9EQkL/CAQA/xAICP8zMjH/V1xa/1dcWv9rbWv/a21r/2BhYP9KSUr/KCIo/wgA
+ CP8IAAj/CAAI/2tpa/9PTFL/T0xS/zMwOf9XU1X/KRwh/ykcIf9XU1X/XVla/11ZWv+ipqT/oqak/6Kk
+ ov+ipKL/oqSi/6Kkov+AhYH/gIWB/4CFgf+AhYH/XVtd/11bXf9dW13/XVtd/1RVV/9UVVf/VFVX/xgQ
+ GP8YCBD/WlBS/1pQUv9aUFL/IRgh/yEYIf9obGj/aGxo/1VJSf9VSUn/VUlJ/2tta/9VVFX/OTQ5/1VU
+ Vf9jZWP/a21r/2JlYv9aXVr/Wl1a/0RCQv9jYWP/Y2Fj/wgEAP8QCAjMEAgI/xAICO4QCAj/a21r/2tt
+ a/9VVVX/SklK/xgRGP8IAAj/GBEY/wgACP9raWv/a2lr/zMwOf8YFCH/V1NV/4aLif+Gi4n/tcO9/+fz
+ 7//n8+//5/Pv/+fz7//n8+//5/Pv/+fz7//n8+//3vfv/9737//e9+//3vfv/9br5//W6+f/1uvn/9br
+ 5//O39b/kZqW/5Galv9UVVf/e3Vz/3t1c/9aUFL/OSwx/0RCRP9obGj/jJaM/2hsaP9VSUn/YFta/2Bb
+ Wv9rbWv/Y2Vj/1VUVf85NDn/Y2Vj/2tta/9rbWv/a21r/1JVUv9EQkL/Y2Fj/2NhY/8IBAD/5+PeM1JO
+ T3cIBAjdCAQI/4SKhP9jZWP/QkBC/0JAQv8bFRv/GxUb/xsVG/8AAAD/a2lj/46RjP9raWP/jpGM/9DW
+ 0//v9/f/7/f3/+/39//v+/f/7/v3/73Kxf+lsq3/rba1/622tf+9y8j/rba1/7K9tf/AzMX/ztvW/8DM
+ xf+qt7X/qre1/7jJxf/G29b/zt/e/73My/+9zMv/vczL/6+9uv+vvbr/oaun/5SalP97eXP/p7e6/6e3
+ uv97eXP/a2Vj/2tlY/9rZWP/g4WE/3t8eP97fHj/QT48/wgAAP9XVlf/e317/3t9e/9XVlf/VFRU/1RU
+ VP97fXv/CAQI/+fj3gDn494ACAQI3QgECP+EioT/Y2Vj/0JAQv8hHCH/GxUb/ykgKf8bFRv/AAAA/2tp
+ Y/+yurX/1uPe/9bj3v/v9/f/7/f3/+/39//Q1tP/vcrF/73Kxf+lsq3/vcrF/83h2/+9y8j/vcvI/73L
+ yP+yvbX/sr21/7K9tf+lrqX/nKal/5ympf+cpqX/nKal/5ympf+cpqX/nKal/5ympf+hq6f/oaun/73P
+ zv+9z87/e3lz/73X3v97eXP/e3lz/2tlY/9rZWP/g4WE/4OFhP97fHj/e3x4/0E+PP8IAAD/EAgQ/1dW
+ V/97fXv/e317/3t9e/9UVFT/e317/wgECP/n494A5+PeEQgECO4IBAj/hIqE/0JAQv9CQEL/IRwh/xsV
+ G/8pICn/KSAp/wAAAP9raWP/jpGM/7K6tf/W497/srav/5SWjP+ytq//srav/6Wyrf+lsq3/1uLe/+/7
+ 9//e9+//zeHb/73LyP+9y8j/wMzF/8DMxf/AzMX/wMzF/6q3tf+qt7X/nKal/5ympf+cpqX/nKal/5ym
+ pf+cpqX/lJqU/5SalP+hq6f/lJqU/3t5c/+nt7r/e3lz/5GYlv+DhYT/g4WE/5ympf+cpqX/e3x4/3t8
+ eP9BPjz/CAAA/xAIEP8QCBD/V1ZX/3t9e/97fXv/e317/3t9e/8IBAj/5+PeAOfj3hEIBAj/CAQI/4SK
+ hP9CQEL/QkBC/yEcIf8pICn/KSAp/ykgKf8AAAD/a2lj/9bj3v+yurX/srq1/7K2r/+Uloz/srav/7K2
+ r/+9ysX/vcrF/9bi3v/v+/f/zeHb/73LyP+9y8j/vcvI/8DMxf/AzMX/wMzF/8DMxf+4ycX/qre1/6q3
+ tf+cpqX/nKal/5ympf+cpqX/nKal/6Grp/+UmpT/lJqU/5SalP+9197/kZiW/5GYlv+9197/tcfG/7XH
+ xv+1x8b/tcfG/3t8eP97fHj/QT48/wgAAP8QCBD/EAgQ/xAIEP8zLzP/e317/3t9e/97fXv/CAQI/97b
+ 1gDe29YiCAQI7ggECP9zcWv/TEtH/0xLR/8mJSP/KyQj/yskI/8rJCP/CAAA/xgQEP/e497/3uPe/5yc
+ mf+tqqX/t8K7/7fCu/+ytrD/rbe1/623tf+tt7X/vcvG/7XDvf+1w73/wM3I/7XDvf+3v7f/t7+3/7W6
+ tf+3v7f/tcO9/7XDvf+1w73/r7y1/6Ksp/+fp6H/n6eh/5yinP+copz/lpqW/5aalv+Znpn/1vf//5+p
+ rf+EgoT/n6mt/87z//+lpq3/sr/I/87z//+fq6r/n6uq/xAMEP8QDBD/Ixwe/yMcHv8IBAj/CAQI/zEs
+ Mf9aWVr/hIaE/wgACP/e29YA3tvWEQgECO4IBAj/c3Fr/0xLR/9MS0f/JiUj/yskI/8rJCP/KyQj/ysk
+ I/8YEBD/3uPe/97j3v+cnJn/t8K7/73Pxv+3wrv/sraw/6Wurf+lrq3/rbe1/7XBvf/Azcj/tcO9/8DN
+ yP+1w73/t7+3/7e/t/+3v7f/t7+3/7XDvf+1w73/tcO9/6+8tf+irKf/n6eh/6Ksp/+copz/mZ6Z/5aa
+ lv+Wmpb/lJaU/7rQ1v+fqa3/hIKE/5+prf/A2eP/sr/I/8DZ4//O8///5/v3/5+rqv8QDBD/EAwQ/yMc
+ Hv8xKCn/Ixwe/xUQE/8IAAj/MSwx/4SGhP8IAAj/3tvWAN7b1gAIBAiqCAQI/3Nxa/9zcWv/TEtH/0xL
+ R/8rJCP/KyQj/yskI/8rJCP/GBAQ/1pWVP/e497/nJyZ/73Pxv+ytrD/t8K7/7fCu/+1wb3/rbe1/73L
+ xv+9y8b/xtPO/7rIwv+1w73/tcO9/7rFuv+3v7f/t7+3/7e/t/+1w73/r7y1/6+8tf+1w73/pbKt/6Ks
+ p/+fp6H/n6eh/5memf+Wmpb/nKKc/5aalv+fqa3/n6mt/4SChP+fqa3/sr/I/8DZ4//A2eP/wNnj/+f7
+ 9/+fq6r/EAwQ/xAMEP8jHB7/Ixwe/yMcHv8jHB7/MSwx/wgACP9aWVr/CAAI/97b1gDe29YA3tvWIggE
+ CO4AAAD/TEtH/0xLR/9MS0f/T0hH/yskI/8rJCP/KyQj/xgQEP8YEBD/3uPe/5ycmf+3wrv/t8K7/7fC
+ u/+3wrv/tcG9/7XBvf+9y8b/vcvG/8DNyP+6yML/tcO9/7XDvf+6xbr/t7+3/7e/t/+3v7f/r7y1/6Wu
+ pf+lrqX/r7y1/6Wyrf+irKf/n6eh/5yinP+copz/mZ6Z/5memf+Znpn/n6mt/7rQ1v+EgoT/n6mt/8DZ
+ 4//O8///wNnj/8DZ4//n+/f/V1td/xAMEP8QDBD/Ixwe/zEoKf8xKCn/MSgp/zEsMf8xLDH/Wlla/wgA
+ CP////8A////AP///wAAAACZGBAQzBgQEP8YEBD/X1lX/2NhWv9jYVr/REI+/yYjI/8hGBj/IRgY/2Bh
+ X//e8+//uMK6/7jCuv+4wrr/uMK6/7XDvf+1w73/tcO9/7fHwP+3x8D/usvD/7XDvf+1w73/t8C6/7fA
+ uv+3wLr/t8C6/7fAt/+tsq3/rbKt/62yrf+lsq3/oqyn/5+nof+copz/n6Of/5+jn/+ZnJn/mZyZ/87v
+ 9//A1t7/paat/6Wmrf+72uH/xvP//7va4f+72uH/hH1z/wgECP8IBAj/MSwr/ykkKf8zLi7/My4u/ykk
+ Kf8jJCP/IyQj/z5APv8ICAj/////AP///wD///8A////AKein0QYEBC7GBAQ/xgQEP9EQj7/Y2Fa/2Nh
+ Wv9EQj7/IRgY/yEYGP8hGBj/3vPv/7jCuv+tsq3/uMK6/7jCuv+1w73/tcO9/7XDvf+1w73/tcO9/7rL
+ w/+3x8D/tcO9/7fAuv+yvbf/t8C6/7K9t/+3wLf/t8C3/7fAt/+tsq3/pbKt/6Ksp/+fp6H/nKKc/5+j
+ n/+fo5//mZyZ/5SWlP/O7/f/wNbe/87v9/+lpq3/u9rh/7va4f+lqqX/sMLD/zEsK/8IBAj/MSwr/zEs
+ K/8pJCn/Likr/y4pK/8uKSv/IyQj/yMkI/9aXVr/CAgI/////wD///8A////AP///wDv6+cAp6KfMxgQ
+ EKoYEBD/CAQI/yYjI/9jYVr/Y2Fa/2BhX/8hGBj/IRgY/97z7//D0sj/rbKt/62yrf+4wrr/tcO9/7XD
+ vf+1w73/usvD/7XDvf+3x8D/t8fA/7fHwP+3wLr/sr23/7K9t/+yvbf/t8C3/7fAt/+3wLf/srmy/6Wy
+ rf+irKf/n6eh/5+nof+fo5//mZyZ/5mcmf+UlpT/zu/3/8DW3v+yvsX/sr7F/8bz//+72uH/paql/6Wq
+ pf8xLCv/CAQI/zEsK/8xLCv/Likr/zMuLv8pJCn/Likr/yMkI/8jJCP/Wl1a/wgICP////8A////AP//
+ /wD///8A7+vnAO/r5wDv6+cRGBAQqggECLsIBAj/CAQI/yYjI/9gYV//YGFf/2BhX//e8+//zuPW/7jC
+ uv+tsq3/uMK6/7XDvf+3x8D/t8fA/73Pxv+9z8b/vc/G/7rLw/+6y8P/vcO9/7fAuv+3wLr/t8C6/7fA
+ t/+3wLf/t8C3/7K5sv+lsq3/oqyn/5+nof+fp6H/n6Of/6Wqpf+fo5//mZyZ/87v9//A1t7/sr7F/8DW
+ 3v/G8///xvP//7DCw/+lqqX/MSwr/wgECP8xLCv/MSwr/zMuLv8zLi7/KSQp/y4pK/8+QD7/Wl1a/1pd
+ Wv8ICAj/////AP///wD///8A////AP///wD///8A////AP///wDn6+ciAAAAqgAAAMwAAAD/EAgQ/xAI
+ EP8QCBD/5+/n//f///+3urr/t7q6/7e6uv+1w73/vc/I/73PyP/F29P/ztzT/87c0//O3NP/xs/G/73F
+ vf+9xb3/vcW9/73Fvf+1wr3/tcK9/626tf+lsq3/pa+q/6Wsp/+lqqX/paql/5+ppP+fqaT/lJqU/5Sa
+ lP/n////y9jY/6+xsv/L2Nj/3vv//977//+6zc3/lp+c/xAECP8rIyP/KyMj/ysjI/8mJCb/JiQm/0RE
+ RP9ERET/WFZS/1hWUv9YVlL/AAAA/////wD///8A////AP///wD///8A////AP///wD///8A5+vnAOfr
+ 5wCanJoiAAAAqhAIEMwQCBDuEAgQ/xAIEP/3////9////7e6uv+3urr/tcO9/7XDvf/F29P/zufe/973
+ 7//e9+//1unh/87c0/+9xb3/vcW9/8XQxf/F0MX/vcvG/7XCvf+turX/pbKt/6Wvqv+lrKf/payn/6Wq
+ pf+fqaT/n6mk/5SalP+fqaT/5////8vY2P+vsbL/5////977///e+///3vv//5afnP8QBAj/R0I+/0dC
+ Pv9HQj7/RERE/2NlY/9jZWP/Y2Vj/1hWUv9YVlL/LCsp/wAAAP////8A////AP///wD///8A////AP//
+ /wD///8A////AOfr5wDn6+cA5+vnAOfr5wCfop8iV1VXZhAIELsQCBD/eHV1//f///+3urr/t7q6/73P
+ yP+1w73/vc/I/87n3v/e9+//3vfv/9bp4f/O3NP/ztvO/73Fvf/F0MX/xdDF/7XCvf+lsq3/pbKt/6Wy
+ rf+lr6r/pa+q/6Wsp/+lrKf/n6mk/5+ppP+UmpT/tcfG/6+xsv+vsbL/y9jY/+f////e+///3vv//977
+ //9zcWv/KyMj/2NhWv9jYVr/Y2Fa/2NlY/9jZWP/RERE/0RERP8sKyn/AAAA/wAAAO4AAADM////AP//
+ /wD///8A////AP///wD///8A////AP///wDn6+cA5+vnAOfr5wDn6+cA5+/nAOfv5wDn7+cREAgQ3Tkw
+ Mf/3////9////7e6uv+9z8j/vc/I/73PyP+9z8j/ztzT/9bp4f/O3NP/ztzT/87bzv/F0MX/vcW9/7W6
+ tf+lsq3/pbKt/6Wyrf+lsq3/pbKt/6Wyrf+lrKf/pa+q/5+ppP+fqaT/lJqU/7XHxv+Uioz/r7Gy/+f/
+ ///n////3vv//977///e+///c3Fr/2NhWv9jYVr/Y2Fa/0dCPv8mJCb/CAQI/wgECP8IBAj/AAAA7gAA
+ ALuEgntVhIJ7AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM7H
+ xgDOx8YAzsfGAAAAAN0IBAD/7/v3/+/79//v+/f/zd3W/83d1v+9z8b/vc/G/8DQyP/G187/xtfO/8bX
+ zv/G187/xtfO/7XBvf+ttrX/rbey/620r/+ttK//rbSv/6Wyrf+irKf/oqyn/6Ksp/+fpaX/n6Wl/4SC
+ hP+fpaX/yNTQ/8jU0P/n+/f/5/v3////////////r66s/xAMCP8IBAj/CAQI/wgECP8IBAj/AAAA7gAA
+ AN0AAAC7AAAAu3VycFX///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP//
+ /wD///8A////AP///wDOx8YAzsfGAM7HxgAAAACZCAQA/6KopP/v+/f/7/v3/+/79//e7Ob/vc/G/73P
+ xv/A0Mj/wNDI/8DQyP/G187/xtfO/7XBvf+ttrX/rba1/623sv+ttK//rbSv/620r/+lsq3/n6eh/6Wy
+ rf+irKf/n6Wl/5+lpf+fpaX/1uvn/+f79//n+/f/yNTQ/+f79///////X11a/xAMCP8QDAjuCAQI7ggE
+ CMwIBAi7CAQIu1VVVWaqqqoi////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP//
+ /wD///8A////AP///wD///8A////AP///wD///8AzsfGAM7HxgDOx8YAiYSERAgEAO5VVlL/7/v3/+/7
+ 9//v+/f/7/v3/83d1v+9z8b/wNDI/7rJwv+6ycL/wNDI/8bXzv+ttrX/rba1/622tf+ttK//rbSv/620
+ r/+ttK//pbKt/6Wyrf+lsq3/oqyn/5+lpf+fpaX/n6Wl/9br5//I1ND/yNTQ/8jU0P/n+/f//////xAM
+ CP8QDAjMX11aiJaWkUTe39YR3t/WAN7f1gD///8A////AP///wD///8A////AP///wD///8A////AP//
+ /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM7HxgDOx8YAzsfGAM7H
+ xgAIBADMCAQA/1VWUv/v+/f/7/v3/+/79//N3db/vc/G/7rJwv+1w73/usnC/7rJwv/G187/rba1/622
+ tf+ttrX/rbey/620r/+ttK//rbSv/6Wyrf+lsq3/oqyn/5yinP+fpaX/hIKE/5+lpf/W6+f/jIaE/8jU
+ 0P/n+/f/5/v3/19dWv8QDAj/X11aVf///wDe39YA3t/WAN7f1gDe39YA////AP///wD///8A////AP//
+ /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP//
+ /wD///8A////AP///wD///8A1tPOEQAAAMwAAAD/joyJ/97r5//e6+f/3uvn/97r5//I1M7/yNTO/8jU
+ zv+qsa3/wtDL/8LQy/+nraf/p62n/7C5t/+wubf/sLm3/7C5t/+lrq3/pa6t/6Wurf+lrq3/tcO9/7XD
+ vf/L19P/y9fT/6yurP///////////1pdWv8AAAD/AAAAzP///wD///8A////AP///wD///8A////AP//
+ /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP//
+ /wD///8A////AP///wD///8A////AP///wD///8A////ANbTzgCOjIkzAAAAzAAAAP+UnJr/3uvn/97r
+ 5//e6+f/yNTO/8jUzv/I1M7/yNTO/8LQy//e8+//3vPv/8LQy/+wubf/sLm3/7C5t/+wubf/pa6t/6Wu
+ rf/F0cj/1uPW/8vX0/+1w73/4evp/8vX0////////////1pdWv8IDAj/AAAAzKqqqiL///8A////AP//
+ /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP//
+ /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDW084A1tPOAI6M
+ iTMAAADdAAAA/5Scmv/e6+f/3uvn/8jUzv/I1M7/jI6M/4yOjP+MioT/jIqE/4yKhP+MioT/hH17/4R9
+ e/+am5n/xtfW/9bj1v/F0cj/tb+6/7W/uv+1w73/y9fT/+Hr6f/h6+n//////1pdWv8IDAj/CAwI3aqq
+ qjP///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP//
+ /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP//
+ /wD///8A1tPOANbTzgDW084AjoyJRAAAAN0AAAD/Sk5N/97r5//n9+//5/fv/4yOjP+Mjoz/jIqE/4yK
+ hP+MioT/jIqE/4R9e/+EfXv/mpuZ/8bX1v/F0cj/tb+6/8XRyP/W49b/9/////f////3////9////1pd
+ Wv8IDAj/CAwI3ayurDP///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP//
+ /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP//
+ /wD///8A////AP///wD///8A////AP///wD///8A////AP///wCsq6wiCAQIzAgECN0IBAj/pKek//f7
+ 9//3+/f/9/v3//f79//3+/f/9/v3//f79//v+/f/7/v3/6qurP/v+/f/5+vn/+fr5//n6+f/5+vn////
+ ////////ZV9f/xgQEP8ABAD/AAQA3aqrqiL///8A////AP///wD///8A////AP///wD///8A////AP//
+ /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP//
+ /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP//
+ /wBaV1pVCAQI3QAAAP8AAAD/AAAA/1JTUv9oaWj/r7Kv//f79//3+/f/7/v3/+/79//v+/f/7/v3/+fr
+ 5/+anJr/mpya/01OTf8YEBD/GBAQ/xgQEP8YEBDdVVdVZv///wD///8A////AP///wD///8A////AP//
+ /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP//
+ /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP//
+ /wD///8A////AP///wD///8A////AP///xFSU1J3AAAA3QAAAN0AAAD/ISAh/yEgIf8hICH/ISAh/yEU
+ GP8hFBj/IRQY/yEUGP8AAAD/AAAA/wAAAP8AAAD/GBAQ3RgQEN0YEBCIsq+vIv///wD///8A////AP//
+ /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP//
+ /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP//
+ /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A9/v3APf79wD3+/cRpKek/2hp
+ aP9oaWj/ISAh/yEgIf8hFBj/IRQY/yEUGP8hFBj/TU5N/01OTf+anJr/mpya/////yL///8A////AP//
+ /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP//
+ /wD///8A////AP///wD///8A////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAACAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAMAA
+ AAAAAAAAwAAAAAAAAADgAAAAAAAAAPAAAAAAAAAA+AAAAAAAAAD8AAAAAAAAAP8AAAAAAAAA/8AAAAAA
+ AAD/8AAAAAAAAP/8AAAAAAAB//4AAAAAAAf//gAAAAAAP//+AAAAAAP///8AAAAAH////wAAAAA/////
+ gAAAAD/////AAAAAf////+AAAAD/////8AAAAf/////8AAAH//////4AAA///////8AAf///
+
+
+
\ No newline at end of file
diff --git a/ImgFormatHelper/MapType.cs b/ImgFormatHelper/MapType.cs
new file mode 100644
index 0000000..cb9abd6
--- /dev/null
+++ b/ImgFormatHelper/MapType.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IWImgViewer
+{
+ public enum MapType : byte
+ {
+ MAPTYPE_NONE = 0x0,
+ MAPTYPE_INVALID1 = 0x1,
+ MAPTYPE_INVALID2 = 0x2,
+ MAPTYPE_2D = 0x3,
+ MAPTYPE_3D = 0x4,
+ MAPTYPE_CUBE = 0x5,
+ MAPTYPE_COUNT = 0x6,
+ };
+}
diff --git a/ImgFormatHelper/Program.cs b/ImgFormatHelper/Program.cs
new file mode 100644
index 0000000..fd51a09
--- /dev/null
+++ b/ImgFormatHelper/Program.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace IWImgViewer
+{
+ static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new MainWindow());
+ }
+ }
+}
diff --git a/ImgFormatHelper/Properties/AssemblyInfo.cs b/ImgFormatHelper/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..7b6ab28
--- /dev/null
+++ b/ImgFormatHelper/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("FFImgViewer")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("FFImgViewer")]
+[assembly: AssemblyCopyright("Copyright © 2021")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("02b056ca-bc9b-4be3-8730-f7506dabd8bb")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// 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("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/ImgFormatHelper/Properties/Resources.Designer.cs b/ImgFormatHelper/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..12f412f
--- /dev/null
+++ b/ImgFormatHelper/Properties/Resources.Designer.cs
@@ -0,0 +1,73 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace IWImgViewer.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("IWImgViewer.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap cardicon_juggernaut_1 {
+ get {
+ object obj = ResourceManager.GetObject("cardicon_juggernaut_1", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+ }
+}
diff --git a/ImgFormatHelper/Properties/Resources.resx b/ImgFormatHelper/Properties/Resources.resx
new file mode 100644
index 0000000..9d27506
--- /dev/null
+++ b/ImgFormatHelper/Properties/Resources.resx
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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
+
+
+
+ ..\Resources\cardicon_juggernaut_1.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
\ No newline at end of file
diff --git a/ImgFormatHelper/Properties/Settings.Designer.cs b/ImgFormatHelper/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..bd87f66
--- /dev/null
+++ b/ImgFormatHelper/Properties/Settings.Designer.cs
@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace IWImgViewer.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/ImgFormatHelper/Properties/Settings.settings b/ImgFormatHelper/Properties/Settings.settings
new file mode 100644
index 0000000..abf36c5
--- /dev/null
+++ b/ImgFormatHelper/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/ImgFormatHelper/Reader.cs b/ImgFormatHelper/Reader.cs
new file mode 100644
index 0000000..007d0df
--- /dev/null
+++ b/ImgFormatHelper/Reader.cs
@@ -0,0 +1,388 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IWImgViewer
+{
+ public class Reader
+ {
+ public string ImageName => imagePath;
+ public string ImageFormat { get; private set; }
+
+ public byte[] Data { get; set; }
+
+ public IImageFileHeader CurrentHeader {get; private set;}
+
+ private readonly string imagePath;
+
+ public Reader(string imagePath)
+ {
+ if (!File.Exists(imagePath))
+ {
+ throw new FileNotFoundException();
+ }
+
+ this.imagePath = imagePath;
+
+ string ext = System.IO.Path.GetExtension(imagePath).Substring(1).ToUpper();
+
+ switch (ext.ToUpper())
+ {
+ case "FFIMG":
+ DecodeFFImage();
+ break;
+
+ case "IW4XIMAGE":
+ DecodeIW4XImage();
+ break;
+
+ case "IWI":
+ DecodeIWI4();
+ break;
+ }
+ }
+
+ public static event Action OnBitmapRead;
+
+ void DecodeIWI4()
+ {
+ using (FileStream fs = new FileStream(imagePath, FileMode.Open))
+ {
+ using (BinaryReader br = new BinaryReader(fs))
+ {
+ CoD6_GfxImageFileHeader header;
+
+ try
+ {
+ header = new CoD6_GfxImageFileHeader(Path.GetFileNameWithoutExtension(imagePath));
+ header.Deserialize(br);
+ CurrentHeader = header;
+ }
+ catch (FormatException)
+ {
+ br.Dispose();
+ fs.Dispose();
+
+ // Maybe cod4?
+ DecodeIWI3();
+ return;
+ }
+
+ bool hasMipMaps = (header.flags & (byte)IwiFlags.IMG_FLAG_NOMIPMAPS) == 0;
+ Data = br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position));
+
+ CreateBitmap(Data, (short)header.Width, (short)header.Height, header.format, header.MapType, hasMipMaps);
+ }
+ }
+ }
+
+ void DecodeIWI3()
+ {
+ using (FileStream fs = new FileStream(imagePath, FileMode.Open))
+ {
+ using (BinaryReader br = new BinaryReader(fs))
+ {
+ CoD4_GfxImageFileHeader header = new CoD4_GfxImageFileHeader(Path.GetFileNameWithoutExtension(imagePath));
+ header.Deserialize(br);
+ CurrentHeader = header;
+ bool hasMipMaps = (header.flags & (byte)IwiFlags.IMG_FLAG_NOMIPMAPS) == 0;
+ Data = br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position));
+
+ CreateBitmap(Data, header.dimensions[0], header.dimensions[1], header.format, header.MapType, hasMipMaps);
+ }
+ }
+ }
+
+ void DecodeFFImage()
+ {
+ using (FileStream fs = new FileStream(imagePath, FileMode.Open))
+ {
+ using (BinaryReader br = new BinaryReader(fs))
+ {
+ FFImgHeader header = new FFImgHeader();
+ header.Deserialize(br);
+ CurrentHeader = header;
+ bool hasMipMaps = (header.flags & (byte)IwiFlags.IMG_FLAG_NOMIPMAPS) == 0;
+ Data = br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position));
+
+ if (header.format == _D3DFORMAT.D3DFMT_UNKNOWN)
+ {
+ throw new FormatException(header.format.ToString());
+ }
+
+ CreateBitmap(Data, (short)header.Width, (short)header.Height, header.D3dFormat, header.MapType);
+ }
+ }
+ }
+
+ void DecodeIW4XImage()
+ {
+ using (FileStream fs = new FileStream(imagePath, FileMode.Open))
+ {
+ using (BinaryReader br = new BinaryReader(fs))
+ {
+ if (new string(br.ReadChars(8)) != "IW4xImg0")
+ {
+ throw new FormatException();
+ }
+
+ IW4XImageHeader header = new IW4XImageHeader();
+ header.Deserialize(br);
+ CurrentHeader = header;
+ bool hasMipMaps = (header.flags & (byte)IwiFlags.IMG_FLAG_NOMIPMAPS) == 0;
+ Data = br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position));
+
+ if (header.format == _D3DFORMAT.D3DFMT_UNKNOWN)
+ {
+ throw new FormatException(header.format.ToString());
+ }
+
+ CreateBitmap(Data, (short)header.Width, (short)header.Height, header.D3dFormat, header.MapType);
+ }
+ }
+ }
+
+ void CreateBitmap(byte[] data, short width, short height, GfxImageFileFormat format, MapType mapType=MapType.MAPTYPE_2D, bool hasMips=false)
+ {
+ if (hasMips)
+ {
+ CreateBitmapsWithMips(data, width, height, format, mapType);
+ return;
+ }
+
+ Bitmap bitmap = null;
+
+ ImageFormat = format.ToString();
+
+ byte channels;
+ int maxI;
+
+ switch (format)
+ {
+ case GfxImageFileFormat.IMG_FORMAT_BITMAP_ALPHA:
+ bitmap = new Bitmap(width, height);
+ BitmapSetPixels(bitmap, width, height, data, 1);
+ break;
+
+ case GfxImageFileFormat.IMG_FORMAT_DXT1:
+ case GfxImageFileFormat.IMG_FORMAT_DXT3:
+ case GfxImageFileFormat.IMG_FORMAT_DXT5:
+
+ byte[] pixels;
+ bitmap = new Bitmap(width, height);
+
+ pixels = ManagedSquish.Squish.DecompressImage(data, width, height, FormatHelper.GfxFormatToSquishFormat[format]);
+
+ channels = 4;
+ maxI = channels * width * height;
+
+ if (maxI > pixels.Length)
+ {
+ if (pixels.Length > (channels - 1) * width * height)
+ {
+ System.Windows.Forms.MessageBox.Show($"Too many channels! Expected {channels} channels but there is only data for {channels - 1} channels.");
+ channels--;
+ }
+ else
+ {
+ System.Windows.Forms.MessageBox.Show($"Wrong number of channels! For format {format}, {channels} channels is too much.");
+ }
+ }
+
+ BitmapSetPixels(bitmap, width, height, SwapChannels(pixels, new Dictionary()
+ {
+ {0, 2}, // R -> B
+ {1, 1}, // G -> G
+ {2, 0}, // B -> R
+ {3, 3} // A -> A
+ }, channels), channels);
+
+ break;
+
+ //case GfxImageFileFormat.IMG_FORMAT_WAVELET_RGB:
+ //case GfxImageFileFormat.IMG_FORMAT_WAVELET_RGBA:
+ // using (OpenJpegDotNet.IO.Reader reader = new OpenJpegDotNet.IO.Reader(data))
+ // {
+ // reader.ReadHeader();
+ // bitmap = reader.ReadData();
+ // Console.WriteLine("Read data");
+ // }
+ // break;
+
+ case GfxImageFileFormat.IMG_FORMAT_BITMAP_RGB:
+ channels = 3;
+ bitmap = new Bitmap(width, height);
+ BitmapSetPixels(bitmap, width, height, data, channels);
+ break;
+
+ case GfxImageFileFormat.IMG_FORMAT_BITMAP_RGBA:
+ channels = 4;
+ bitmap = new Bitmap(width, height);
+ BitmapSetPixels(bitmap, width, height, data, channels);
+ break;
+
+
+ default:
+ throw new FormatException(format.ToString());
+
+ }
+
+ OnBitmapRead(bitmap);
+ }
+
+ void CreateBitmapsWithMips(byte[] data, short width, short height, GfxImageFileFormat format, MapType mapType = MapType.MAPTYPE_2D)
+ {
+ // Generate mips
+ List<(int width, int height)> mips = new List<(int width, int height)>();
+
+ int sides = mapType == MapType.MAPTYPE_CUBE ? 6 : 1;
+
+ short maxDimension = Math.Max(height, width);
+ int mipmapFactor = 1;
+ int minBlockSize = FormatHelper.GetBlockSize(format);
+
+ while (maxDimension != 0)
+ {
+ maxDimension >>= 1;
+ mipmapFactor *= 2;
+ mips.Add((Math.Max(width / mipmapFactor, minBlockSize), Math.Max(height / mipmapFactor, minBlockSize)));
+ }
+
+ mips.Reverse();
+
+
+ int compressedLengthToSkip = 0;
+
+ for (int i = 0; i <= mips.Count; i++)
+ {
+ for (int side = 0; side < sides; side++)
+ {
+ short thisWidth, thisHeight;
+
+ if (i < mips.Count)
+ {
+ var mip = mips[i];
+ thisWidth = (short)mip.width;
+ thisHeight = (short)mip.height;
+
+ if (thisWidth <= 16 && thisHeight <= 16)
+ {
+ // We skip them because at this point the windows form is bigger than the image
+ continue;
+ }
+ }
+ else
+ {
+ thisWidth = width;
+ thisHeight = height;
+ }
+
+ CreateBitmap(data.Skip(compressedLengthToSkip).ToArray(), thisWidth, thisHeight, format);
+
+ if (i < mips.Count)
+ {
+ compressedLengthToSkip += ManagedSquish.Squish.GetStorageRequirements(thisWidth, thisHeight, FormatHelper.GfxFormatToSquishFormat[format]);
+ }
+ }
+ }
+ }
+
+ void CreateBitmap(byte[] data, short width, short height, _D3DFORMAT format, MapType mapType = MapType.MAPTYPE_2D)
+ {
+ int sides = mapType == MapType.MAPTYPE_CUBE ? 6 : 1;
+ ImageFormat = format.ToString();
+
+ for (int side = 0; side < sides; side++)
+ {
+ Bitmap bitmap = new Bitmap(width, height);
+ byte[] truncatedData;
+ int sizeOfOneSide;
+
+ switch (format)
+ {
+ case _D3DFORMAT.D3DFMT_L8:
+ sizeOfOneSide = width * height * 1;
+ truncatedData = data.Skip(sizeOfOneSide * side).Take(sizeOfOneSide).ToArray();
+ BitmapSetPixels(bitmap, width, height, truncatedData, 1);
+ break;
+
+ case _D3DFORMAT.D3DFMT_A8R8G8B8:
+ sizeOfOneSide = width * height * 4;
+ truncatedData = data.Skip(sizeOfOneSide * side).Take(sizeOfOneSide).ToArray();
+ BitmapSetPixels(bitmap, width, height, truncatedData, 4);
+ break;
+
+
+ default:
+ throw new FormatException(format.ToString());
+ }
+
+ OnBitmapRead(bitmap);
+ }
+ }
+
+ byte[] SwapChannels(byte[] data, Dictionary swapMap, byte channels)
+ {
+ byte[] newData = new byte[data.Length];
+ for (int i = 0; i < data.Length; i += channels)
+ {
+ try
+ {
+ for (byte channel = 0; channel < channels; channel++)
+ {
+ newData[i + channel] = data[i + swapMap[channel]];
+ }
+ }
+ catch (IndexOutOfRangeException)
+ {
+ System.Windows.Forms.MessageBox.Show($"Wrong number of channels! {channels} channels is not correct!");
+ }
+ }
+
+ return newData;
+ }
+
+ void BitmapSetPixels(Bitmap bitmap, short width, short height, byte[] pixels, byte channels)
+ {
+ int maxI = channels * width * height;
+ bool hasWarned = false;
+ int x, y;
+
+ if (pixels.Length != maxI)
+ {
+ System.Windows.Forms.MessageBox.Show($"Unexpected number of pixels: Expected {maxI} pixels, found {pixels.Length} (difference of {pixels.Length - maxI})");
+ }
+
+ for (int i = 0; i < pixels.Length - channels; i++)
+ {
+ if (i >= maxI)
+ {
+ if (!hasWarned)
+ {
+ hasWarned = true;
+ System.Windows.Forms.MessageBox.Show($"Found {pixels.Length - maxI} extra bytes of information at the end of data (?)");
+ }
+ }
+ else
+ {
+ if (i % channels == 0)
+ {
+ x = (i / channels) % width;
+ y = i / (width * channels);
+
+ bitmap.SetPixel(x, y, Color.FromArgb(
+ channels > 3 ? pixels[i + 3] : 255,
+ channels > 2 ? pixels[i + 2] : pixels[i],
+ channels > 1 ? pixels[i + 1] : pixels[i],
+ pixels[i])
+ );
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/ImgFormatHelper/Resources/cardicon_juggernaut_1.png b/ImgFormatHelper/Resources/cardicon_juggernaut_1.png
new file mode 100644
index 0000000..039a7e1
Binary files /dev/null and b/ImgFormatHelper/Resources/cardicon_juggernaut_1.png differ
diff --git a/ImgFormatHelper/Viewport.Designer.cs b/ImgFormatHelper/Viewport.Designer.cs
new file mode 100644
index 0000000..bcd79fc
--- /dev/null
+++ b/ImgFormatHelper/Viewport.Designer.cs
@@ -0,0 +1,85 @@
+
+namespace IWImgViewer
+{
+ partial class Viewport
+ {
+ ///
+ /// 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()
+ {
+ this.PreviewBox = new System.Windows.Forms.PictureBox();
+ this.saveFileDialog1 = new System.Windows.Forms.SaveFileDialog();
+ this.MetaDataLabel = new System.Windows.Forms.Label();
+ ((System.ComponentModel.ISupportInitialize)(this.PreviewBox)).BeginInit();
+ this.SuspendLayout();
+ //
+ // PreviewBox
+ //
+ this.PreviewBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.PreviewBox.BackColor = System.Drawing.Color.Transparent;
+ this.PreviewBox.Location = new System.Drawing.Point(0, 0);
+ this.PreviewBox.Name = "PreviewBox";
+ this.PreviewBox.Size = new System.Drawing.Size(282, 259);
+ this.PreviewBox.TabIndex = 1;
+ this.PreviewBox.TabStop = false;
+ //
+ // MetaDataLabel
+ //
+ this.MetaDataLabel.AutoSize = true;
+ this.MetaDataLabel.Location = new System.Drawing.Point(12, 9);
+ this.MetaDataLabel.Name = "MetaDataLabel";
+ this.MetaDataLabel.RightToLeft = System.Windows.Forms.RightToLeft.No;
+ this.MetaDataLabel.Size = new System.Drawing.Size(0, 13);
+ this.MetaDataLabel.TabIndex = 2;
+ //
+ // Viewport
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(280, 257);
+ this.Controls.Add(this.MetaDataLabel);
+ this.Controls.Add(this.PreviewBox);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "Viewport";
+ this.ShowIcon = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.WindowsDefaultBounds;
+ this.Text = "Viewport";
+ ((System.ComponentModel.ISupportInitialize)(this.PreviewBox)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.PictureBox PreviewBox;
+ private System.Windows.Forms.SaveFileDialog saveFileDialog1;
+ private System.Windows.Forms.Label MetaDataLabel;
+ }
+}
\ No newline at end of file
diff --git a/ImgFormatHelper/Viewport.cs b/ImgFormatHelper/Viewport.cs
new file mode 100644
index 0000000..df863a9
--- /dev/null
+++ b/ImgFormatHelper/Viewport.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace IWImgViewer
+{
+ public partial class Viewport : Form
+ {
+ MainWindow parent;
+
+ EasyFilters exportFilters = new EasyFilters(
+ ("Portable Graphics Network", "PNG"),
+ ("Windows Bitmap", "BMP"),
+ ("Tag Image File Format", "TIFF"),
+ ("Joint Photographics Experts", "JPEG"),
+ ("Windows Icon", "ICO")
+ );
+
+ Dictionary exportFormatMap = new Dictionary()
+ {
+ { "PNG", ImageFormat.Png },
+ { "BMP", ImageFormat.Bmp },
+ { "TIFF", ImageFormat.Tiff },
+ { "JPEG", ImageFormat.Jpeg },
+ { "ICO", ImageFormat.Icon }
+ };
+
+ public Viewport(MainWindow mainWindow)
+ {
+ parent = mainWindow;
+ InitializeComponent();
+
+ saveFileDialog1.FileOk += saveFileDialog1_FileOk;
+ }
+
+ protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ {
+ if (keyData == Keys.X)
+ {
+ saveFileDialog1.DefaultExt = exportFilters.GetFilterExtensionByOneBasedIndex(1);
+ saveFileDialog1.FileName = System.IO.Path.GetFileNameWithoutExtension(parent.ImageName);
+ saveFileDialog1.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
+ saveFileDialog1.Filter = exportFilters.ToString();
+ saveFileDialog1.ShowDialog(this);
+ return true;
+ }
+
+ return base.ProcessCmdKey(ref msg, keyData);
+ }
+
+ public void ShowImage(Bitmap bmp)
+ {
+ PreviewBox.Image = bmp;
+ this.Width = PreviewBox.Image.Width + 16;
+ this.Height = PreviewBox.Image.Height + 39;
+
+ this.MetaDataLabel.Text = $"{bmp.Width}x{bmp.Height}";
+
+ Refresh();
+ }
+
+ private void saveFileDialog1_FileOk(object sender, CancelEventArgs e)
+ {
+ var format = exportFilters.GetFilterExtensionByOneBasedIndex(saveFileDialog1.FilterIndex);
+ PreviewBox.Image.Save(saveFileDialog1.FileName, exportFormatMap[format.ToUpper()]);
+ }
+ }
+}
diff --git a/ImgFormatHelper/Viewport.resx b/ImgFormatHelper/Viewport.resx
new file mode 100644
index 0000000..83ebf8a
--- /dev/null
+++ b/ImgFormatHelper/Viewport.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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
+
+
\ No newline at end of file
diff --git a/ImgFormatHelper/Writer.cs b/ImgFormatHelper/Writer.cs
new file mode 100644
index 0000000..4c80c72
--- /dev/null
+++ b/ImgFormatHelper/Writer.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IWImgViewer
+{
+ public static class Writer
+ {
+ public static void WriteIWI3(IImageFileHeader header, byte[] data, string outputPath)
+ {
+
+ }
+
+ public static void WriteIWI4(IImageFileHeader header, byte[] data, string outputPath)
+ {
+
+ }
+
+ public static void WriteFFImg(IImageFileHeader header, byte[] data, string outputPath)
+ {
+
+ }
+
+ public static void WriteIW4xImage(IImageFileHeader header, byte[] data, string outputPath)
+ {
+
+ }
+ }
+}
diff --git a/ImgFormatHelper/cardicon_juggernaut_1_MGP_icon.ico b/ImgFormatHelper/cardicon_juggernaut_1_MGP_icon.ico
new file mode 100644
index 0000000..6d0adcf
Binary files /dev/null and b/ImgFormatHelper/cardicon_juggernaut_1_MGP_icon.ico differ
diff --git a/ImgFormatHelper/packages.config b/ImgFormatHelper/packages.config
new file mode 100644
index 0000000..b45b644
--- /dev/null
+++ b/ImgFormatHelper/packages.config
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file