From a17821977960f50e37e86b9fc26e788793e43611 Mon Sep 17 00:00:00 2001 From: rackover Date: Thu, 29 Apr 2021 21:55:59 +0200 Subject: [PATCH] Initial project commit --- ImgFormatHelper.sln | 31 + ImgFormatHelper/App.config | 6 + ImgFormatHelper/EasyFilters.cs | 47 ++ ImgFormatHelper/FFImgHeader.cs | 164 ++++ ImgFormatHelper/FodyWeavers.xml | 12 + ImgFormatHelper/FodyWeavers.xsd | 141 ++++ ImgFormatHelper/FormatHelper.cs | 31 + .../Game/Enums/GfxImageFileFormat.cs | 36 + ImgFormatHelper/Game/Enums/ImageCategory.cs | 22 + ImgFormatHelper/Game/Enums/IwiFlags.cs | 23 + ImgFormatHelper/Game/Enums/_D3DFORMAT.cs | 46 ++ .../Game/Structs/CoD4_GfxImageFileHeader.cs | 167 +++++ .../Game/Structs/CoD6_GfxImageFileHeader.cs | 169 +++++ .../Game/Structs/GfxImageLoadDef.cs | 18 + ImgFormatHelper/IImageFileHeader.cs | 42 ++ ImgFormatHelper/IW4XImageHeader.cs | 121 +++ ImgFormatHelper/ImgFormatHelper.csproj | 298 ++++++++ ImgFormatHelper/MainWindow.Designer.cs | 102 +++ ImgFormatHelper/MainWindow.cs | 125 ++++ ImgFormatHelper/MainWindow.resx | 703 ++++++++++++++++++ ImgFormatHelper/MapType.cs | 19 + ImgFormatHelper/Program.cs | 22 + ImgFormatHelper/Properties/AssemblyInfo.cs | 36 + .../Properties/Resources.Designer.cs | 73 ++ ImgFormatHelper/Properties/Resources.resx | 124 +++ .../Properties/Settings.Designer.cs | 26 + ImgFormatHelper/Properties/Settings.settings | 7 + ImgFormatHelper/Reader.cs | 388 ++++++++++ .../Resources/cardicon_juggernaut_1.png | Bin 0 -> 5015 bytes ImgFormatHelper/Viewport.Designer.cs | 85 +++ ImgFormatHelper/Viewport.cs | 75 ++ ImgFormatHelper/Viewport.resx | 123 +++ ImgFormatHelper/Writer.cs | 31 + .../cardicon_juggernaut_1_MGP_icon.ico | Bin 0 -> 34494 bytes ImgFormatHelper/packages.config | 53 ++ 35 files changed, 3366 insertions(+) create mode 100644 ImgFormatHelper.sln create mode 100644 ImgFormatHelper/App.config create mode 100644 ImgFormatHelper/EasyFilters.cs create mode 100644 ImgFormatHelper/FFImgHeader.cs create mode 100644 ImgFormatHelper/FodyWeavers.xml create mode 100644 ImgFormatHelper/FodyWeavers.xsd create mode 100644 ImgFormatHelper/FormatHelper.cs create mode 100644 ImgFormatHelper/Game/Enums/GfxImageFileFormat.cs create mode 100644 ImgFormatHelper/Game/Enums/ImageCategory.cs create mode 100644 ImgFormatHelper/Game/Enums/IwiFlags.cs create mode 100644 ImgFormatHelper/Game/Enums/_D3DFORMAT.cs create mode 100644 ImgFormatHelper/Game/Structs/CoD4_GfxImageFileHeader.cs create mode 100644 ImgFormatHelper/Game/Structs/CoD6_GfxImageFileHeader.cs create mode 100644 ImgFormatHelper/Game/Structs/GfxImageLoadDef.cs create mode 100644 ImgFormatHelper/IImageFileHeader.cs create mode 100644 ImgFormatHelper/IW4XImageHeader.cs create mode 100644 ImgFormatHelper/ImgFormatHelper.csproj create mode 100644 ImgFormatHelper/MainWindow.Designer.cs create mode 100644 ImgFormatHelper/MainWindow.cs create mode 100644 ImgFormatHelper/MainWindow.resx create mode 100644 ImgFormatHelper/MapType.cs create mode 100644 ImgFormatHelper/Program.cs create mode 100644 ImgFormatHelper/Properties/AssemblyInfo.cs create mode 100644 ImgFormatHelper/Properties/Resources.Designer.cs create mode 100644 ImgFormatHelper/Properties/Resources.resx create mode 100644 ImgFormatHelper/Properties/Settings.Designer.cs create mode 100644 ImgFormatHelper/Properties/Settings.settings create mode 100644 ImgFormatHelper/Reader.cs create mode 100644 ImgFormatHelper/Resources/cardicon_juggernaut_1.png create mode 100644 ImgFormatHelper/Viewport.Designer.cs create mode 100644 ImgFormatHelper/Viewport.cs create mode 100644 ImgFormatHelper/Viewport.resx create mode 100644 ImgFormatHelper/Writer.cs create mode 100644 ImgFormatHelper/cardicon_juggernaut_1_MGP_icon.ico create mode 100644 ImgFormatHelper/packages.config 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 + 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 + 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 0000000000000000000000000000000000000000..039a7e194ff07bd50e699b3b40654817c28e756f GIT binary patch literal 5015 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 WdcL*wZQ|rJ4d`kaTr1SX2mcRyS1hLh literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..6d0adcf9ec0f321f1fa73ba5ae1b6c353246dac9 GIT binary patch literal 34494 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 CnNZdM literal 0 HcmV?d00001 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