diff --git a/exeinfogui.XamMac/exeinfogui.XamMac.csproj b/exeinfogui.XamMac/exeinfogui.XamMac.csproj
index 3eb2af0..8e66985 100644
--- a/exeinfogui.XamMac/exeinfogui.XamMac.csproj
+++ b/exeinfogui.XamMac/exeinfogui.XamMac.csproj
@@ -9,6 +9,7 @@
v2.0
Xamarin.Mac
Resources
+ 0.2
false
diff --git a/iconviewer/iconviewer.Desktop/Info.plist b/iconviewer/iconviewer.Desktop/Info.plist
new file mode 100644
index 0000000..8d03c79
--- /dev/null
+++ b/iconviewer/iconviewer.Desktop/Info.plist
@@ -0,0 +1,20 @@
+
+
+
+
+ CFBundleName
+ iconviewer
+ CFBundleIdentifier
+ com.example.iconviewer
+ CFBundleShortVersionString
+ 1.0
+ LSMinimumSystemVersion
+ 10.7
+ CFBundleDevelopmentRegion
+ en
+ NSHumanReadableCopyright
+
+ CFBundleIconFile
+ MacIcon.icns
+
+
diff --git a/iconviewer/iconviewer.Desktop/MacIcon.icns b/iconviewer/iconviewer.Desktop/MacIcon.icns
new file mode 100644
index 0000000..8f385bb
Binary files /dev/null and b/iconviewer/iconviewer.Desktop/MacIcon.icns differ
diff --git a/iconviewer/iconviewer.Desktop/Program.cs b/iconviewer/iconviewer.Desktop/Program.cs
new file mode 100644
index 0000000..f8e1c81
--- /dev/null
+++ b/iconviewer/iconviewer.Desktop/Program.cs
@@ -0,0 +1,15 @@
+using System;
+using Eto.Forms;
+using Eto.Drawing;
+
+namespace iconviewer.Desktop
+{
+ class Program
+ {
+ [STAThread]
+ static void Main(string[] args)
+ {
+ new Application(Eto.Platform.Detect).Run(new MainForm());
+ }
+ }
+}
\ No newline at end of file
diff --git a/iconviewer/iconviewer.Desktop/iconviewer.Desktop.csproj b/iconviewer/iconviewer.Desktop/iconviewer.Desktop.csproj
new file mode 100644
index 0000000..43d70f2
--- /dev/null
+++ b/iconviewer/iconviewer.Desktop/iconviewer.Desktop.csproj
@@ -0,0 +1,17 @@
+
+
+
+ WinExe
+ net461
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iconviewer/iconviewer/MainForm.xeto b/iconviewer/iconviewer/MainForm.xeto
new file mode 100644
index 0000000..e8b1a0a
--- /dev/null
+++ b/iconviewer/iconviewer/MainForm.xeto
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/iconviewer/iconviewer/MainForm.xeto.cs b/iconviewer/iconviewer/MainForm.xeto.cs
new file mode 100644
index 0000000..ff8954c
--- /dev/null
+++ b/iconviewer/iconviewer/MainForm.xeto.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using Eto.Forms;
+using Eto.Drawing;
+using Eto.Serialization.Xaml;
+using Bitmap = libexeinfo.Os2.Bitmap;
+
+namespace iconviewer
+{
+ public class MainForm : Form
+ {
+ ImageView imgIcon;
+ TextBox txtPath;
+
+ public MainForm()
+ {
+ XamlReader.Load(this);
+ }
+
+ protected void OnBtnPathClick(object sender, EventArgs e)
+ {
+ OpenFileDialog dlgOpenFileDialog = new OpenFileDialog {MultiSelect = false};
+ dlgOpenFileDialog.Filters.Add(new FileFilter {Extensions = new[] {".ico"}});
+ DialogResult result = dlgOpenFileDialog.ShowDialog(this);
+
+ if(result != DialogResult.Ok)
+ {
+ txtPath.Text = "";
+ imgIcon.Image = null;
+ return;
+ }
+
+ txtPath.Text = dlgOpenFileDialog.FileName;
+ FileStream fstream = new FileStream(dlgOpenFileDialog.FileName, FileMode.Open);
+ byte[] data = new byte[fstream.Length];
+ fstream.Read(data, 0, data.Length);
+ fstream.Dispose();
+
+ Bitmap.DecodedBitmap[] icons = libexeinfo.Os2.Bitmap.DecodeBitmap(data);
+ imgIcon.Image = new Eto.Drawing.Bitmap((int)icons[0].Width, (int)icons[0].Height, PixelFormat.Format32bppRgba,
+ icons[0].Pixels);
+ }
+
+ protected void HandleAbout(object sender, EventArgs e)
+ {
+ new AboutDialog().ShowDialog(this);
+ }
+
+ protected void HandleQuit(object sender, EventArgs e)
+ {
+ Application.Instance.Quit();
+ }
+ }
+}
diff --git a/iconviewer/iconviewer/iconviewer.csproj b/iconviewer/iconviewer/iconviewer.csproj
new file mode 100644
index 0000000..b931614
--- /dev/null
+++ b/iconviewer/iconviewer/iconviewer.csproj
@@ -0,0 +1,24 @@
+
+
+ netstandard2.0
+ iconviewer
+ 1.0
+ iconviewer
+ Copyright © 2018
+ Description of iconviewer
+ 0.2
+
+
+ false
+
+
+ false
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libexeinfo.sln b/libexeinfo.sln
index c0343ad..8149280 100644
--- a/libexeinfo.sln
+++ b/libexeinfo.sln
@@ -11,6 +11,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "exeinfogui.Desktop", "exein
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "exeinfogui.XamMac", "exeinfogui.XamMac\exeinfogui.XamMac.csproj", "{E2DC1857-A942-419B-849E-58AC8BBB94CD}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iconviewer", "iconviewer\iconviewer\iconviewer.csproj", "{D6282B50-9A8D-44C5-8674-43BED27B5B53}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iconviewer.Desktop", "iconviewer\iconviewer.Desktop\iconviewer.Desktop.csproj", "{3206160A-B6AB-4B90-AC29-888C7E64A2CE}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
@@ -37,6 +41,14 @@ Global
{E2DC1857-A942-419B-849E-58AC8BBB94CD}.Debug|x86.Build.0 = Debug|Any CPU
{E2DC1857-A942-419B-849E-58AC8BBB94CD}.Release|x86.ActiveCfg = Release|Any CPU
{E2DC1857-A942-419B-849E-58AC8BBB94CD}.Release|x86.Build.0 = Release|Any CPU
+ {D6282B50-9A8D-44C5-8674-43BED27B5B53}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D6282B50-9A8D-44C5-8674-43BED27B5B53}.Debug|x86.Build.0 = Debug|Any CPU
+ {D6282B50-9A8D-44C5-8674-43BED27B5B53}.Release|x86.ActiveCfg = Release|Any CPU
+ {D6282B50-9A8D-44C5-8674-43BED27B5B53}.Release|x86.Build.0 = Release|Any CPU
+ {3206160A-B6AB-4B90-AC29-888C7E64A2CE}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {3206160A-B6AB-4B90-AC29-888C7E64A2CE}.Debug|x86.Build.0 = Debug|Any CPU
+ {3206160A-B6AB-4B90-AC29-888C7E64A2CE}.Release|x86.ActiveCfg = Release|Any CPU
+ {3206160A-B6AB-4B90-AC29-888C7E64A2CE}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
version = 0.2
diff --git a/libexeinfo/Os2/Bitmap.cs b/libexeinfo/Os2/Bitmap.cs
new file mode 100644
index 0000000..ee77247
--- /dev/null
+++ b/libexeinfo/Os2/Bitmap.cs
@@ -0,0 +1,268 @@
+//
+// Accelerator.cs
+//
+// Author:
+// Natalia Portillo
+//
+// Copyright (c) 2017 Copyright © Claunia.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+
+namespace libexeinfo.Os2
+{
+ public class Bitmap
+ {
+ ///
+ /// 'IC', OS/2 only, icon
+ ///
+ public const ushort TYPE_ICON = 0x4349;
+ ///
+ /// 'BM', OS/2 and Windows, bitmap
+ ///
+ public const ushort TYPE_BITMAP = 0x4D42;
+ ///
+ /// 'PT', OS/2 only, cursor
+ ///
+ public const ushort TYPE_POINTER = 0x5450;
+ ///
+ /// 'CI', OS/2 only, color icon
+ ///
+ public const ushort TYPE_COLOR_ICON = 0x4943;
+ ///
+ /// 'CP', OS/2 only, color cursor
+ ///
+ public const ushort TYPE_COLOR_POINTER = 0x5043;
+ ///
+ /// 'BA', OS/2 only, bitmap array
+ ///
+ public const ushort TYPE_BITMAP_ARRAY = 0x4142;
+
+ ///
+ /// This will decode a bitmap array, or if data is not an array, return an array with the single bitmap element
+ ///
+ /// Data
+ /// Array of , one per bitmap, null if the bitmap could not be decoded
+ public static DecodedBitmap[] DecodeBitmap(byte[] data)
+ {
+ long pos = 0;
+ BitmapArrayHeader bitmapArrayHeader;
+ BitmapInfoHeader bitmapFileHeader;
+ byte[] buffer = new byte[Marshal.SizeOf(typeof(BitmapInfoHeader))];
+
+ Array.Copy(data, pos, buffer, 0, buffer.Length);
+ bitmapArrayHeader = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer);
+ bitmapFileHeader = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer);
+
+ List headers = new List();
+ List palettes = new List();
+ List datas = new List();
+
+ do
+ {
+ Array.Copy(data, pos, buffer, 0, buffer.Length);
+ bitmapArrayHeader = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer);
+ long remaining;
+
+ if(bitmapArrayHeader.Type == TYPE_BITMAP_ARRAY)
+ {
+ remaining = bitmapArrayHeader.Size - Marshal.SizeOf(typeof(BitmapArrayHeader));
+ pos += Marshal.SizeOf(typeof(BitmapArrayHeader));
+ }
+ else
+ {
+ remaining = 1;
+ pos = 0;
+ }
+
+ while(remaining > 0)
+ {
+ buffer = new byte[Marshal.SizeOf(typeof(BitmapInfoHeader))];
+ Array.Copy(data, pos, buffer, 0, buffer.Length);
+ bitmapFileHeader = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer);
+
+ // Stop at unknown header
+ if(bitmapFileHeader.Size != Marshal.SizeOf(typeof(BitmapInfoHeader))) break;
+
+ // Multiplanes not supported
+ if(bitmapFileHeader.Planes != 1) break;
+
+ // TODO: Non paletted?
+ pos += bitmapFileHeader.Size;
+ RGB[] palette = new RGB[1 << bitmapFileHeader.BitsPerPlane];
+ buffer = new byte[Marshal.SizeOf(typeof(RGB))];
+ for(int i = 0; i < palette.Length; i++)
+ {
+ Array.Copy(data, pos, buffer, 0, buffer.Length);
+ pos += buffer.Length;
+ palette[i] = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer);
+ }
+
+ headers.Add(bitmapFileHeader);
+ palettes.Add(palette);
+ remaining -= bitmapFileHeader.Fix;
+ // rgb[1];
+ remaining -= 1;
+
+ buffer = new byte[bitmapFileHeader.X * bitmapFileHeader.Y * bitmapFileHeader.BitsPerPlane / 8];
+ Array.Copy(data, bitmapFileHeader.Offset, buffer, 0, buffer.Length);
+ datas.Add(buffer);
+ }
+
+ pos = bitmapArrayHeader.Next;
+ }
+ while(bitmapArrayHeader.Next != 0);
+
+ DecodedBitmap[] bitmaps = new DecodedBitmap[datas.Count];
+
+ for(int b = 0; b < bitmaps.Length; b++) bitmaps[b] = DecodeBitmap(headers[b], palettes[b], datas[b]);
+
+ return bitmaps;
+ }
+
+ static DecodedBitmap DecodeBitmap(BitmapInfoHeader header, IList palette, byte[] data)
+ {
+ DecodedBitmap bitmap = new DecodedBitmap
+ {
+ BitsPerPixel = header.BitsPerPlane,
+ Height = header.Y,
+ Width = header.X,
+ XHotspot = header.XHotspot,
+ YHostpot = header.YHostpot,
+ Pixels = new int[header.X * header.Y]
+ };
+
+ switch(header.Type)
+ {
+ case TYPE_ICON:
+ bitmap.Type = "Icon";
+ break;
+ case TYPE_BITMAP:
+ bitmap.Type = "Bitmap";
+ break;
+ case TYPE_POINTER:
+ bitmap.Type = "Pointer";
+ break;
+ case TYPE_COLOR_ICON:
+ bitmap.Type = "Color Icon";
+ break;
+ case TYPE_COLOR_POINTER:
+ bitmap.Type = "Color Pointer";
+ break;
+ default: return null;
+ }
+
+ const int VISIBLE = -16777216;
+ int[] argbPalette = new int[palette.Count];
+
+ for(int c = 0; c < palette.Count; c++)
+ argbPalette[c] = (palette[c].Red << 16) + (palette[c].Green << 8) + palette[c].Blue;
+
+ long pos = 0;
+
+ for(int y = 0; y < bitmap.Height; y++)
+ {
+ int x = 0;
+ while(x < bitmap.Width)
+ {
+ for(int k = (int)(8 - bitmap.BitsPerPixel); k >= 0; k -= (int)bitmap.BitsPerPixel)
+ {
+ bitmap.Pixels[y * bitmap.Width + x] =
+ argbPalette[(data[pos] >> k) & ((1 << (int)bitmap.BitsPerPixel) - 1)];
+ x++;
+ }
+
+ pos++;
+ }
+
+ pos += pos % 2;
+ }
+
+ // First image, then mask
+ if(header.Type == TYPE_ICON || header.Type == TYPE_POINTER)
+ {
+ int[] icon = new int[bitmap.Width * bitmap.Height / 2];
+ int[] mask = new int[bitmap.Width * bitmap.Height / 2];
+ Array.Copy(bitmap.Pixels, 0, icon, 0, icon.Length);
+ Array.Copy(bitmap.Pixels, icon.Length, mask, 0, mask.Length);
+
+ bitmap.Pixels = new int[bitmap.Width * bitmap.Height / 2];
+ bitmap.Height /= 2;
+ for(int px = 0; px < bitmap.Pixels.Length; px++)
+ bitmap.Pixels[px] = icon[px] + (mask[px] == argbPalette[0] ? VISIBLE : 0);
+ }
+
+ // OS/2 coordinate 0,0 is lower,left, so need to reverse first all pixels then by line
+ int[] pixels = bitmap.Pixels.Reverse().ToArray();
+ for(int y = 0; y < bitmap.Height; y++)
+ for(int x = 0; x < bitmap.Width; x++)
+ bitmap.Pixels[y * bitmap.Width + (bitmap.Width - x - 1)] = pixels[y * bitmap.Width + x];
+
+ return bitmap;
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct BitmapArrayHeader
+ {
+ public ushort Type;
+ public uint Size;
+ public uint Next;
+ public ushort XDisplay;
+ public ushort YDisplay;
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct BitmapInfoHeader
+ {
+ public ushort Type;
+ public uint Size;
+ public short XHotspot;
+ public short YHostpot;
+ public uint Offset;
+ public uint Fix;
+ public ushort X;
+ public ushort Y;
+ public ushort Planes;
+ public ushort BitsPerPlane;
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct RGB
+ {
+ public byte Blue;
+ public byte Green;
+ public byte Red;
+ }
+
+ public class DecodedBitmap
+ {
+ public uint BitsPerPixel;
+ public uint Height;
+ public int[] Pixels;
+ public string Type;
+ public uint Width;
+ public short XHotspot;
+ public short YHostpot;
+ }
+ }
+}
\ No newline at end of file
diff --git a/libexeinfo/libexeinfo.csproj b/libexeinfo/libexeinfo.csproj
index 69655d5..8400588 100644
--- a/libexeinfo/libexeinfo.csproj
+++ b/libexeinfo/libexeinfo.csproj
@@ -58,6 +58,7 @@
+