Microsoft Windows uses specific file format to store applications icons - ICO. This is a container that contains one or more image files (effectively, DIB parts of BMP files or full PNG files are contained inside).
This page hosts a formal specification of Microsoft Windows icon file using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
All parsing code for C# generated by Kaitai Struct depends on the C# runtime library. You have to install it before you can parse data.
The C# runtime library is available in the NuGet Gallery. Installation instructions can also be found there.
Parse a local file and get structure in memory:
var data = Ico.FromFile("path/to/local/file.ico");
Or parse structure from a byte array:
byte[] someArray = new byte[] { ... };
var data = new Ico(new KaitaiStream(someArray));
After that, one can get various attributes from the structure by accessing properties like:
data.NumImages // => Number of images contained in this file
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
using System.Collections.Generic;
namespace Kaitai
{
/// <summary>
/// Microsoft Windows uses specific file format to store applications
/// icons - ICO. This is a container that contains one or more image
/// files (effectively, DIB parts of BMP files or full PNG files are
/// contained inside).
/// </summary>
/// <remarks>
/// Reference: <a href="https://learn.microsoft.com/en-us/previous-versions/ms997538(v=msdn.10)">Source</a>
/// </remarks>
public partial class Ico : KaitaiStruct
{
public static Ico FromFile(string fileName)
{
return new Ico(new KaitaiStream(fileName));
}
public Ico(KaitaiStream p__io, KaitaiStruct p__parent = null, Ico p__root = null) : base(p__io)
{
m_parent = p__parent;
m_root = p__root ?? this;
_read();
}
private void _read()
{
_magic = m_io.ReadBytes(4);
if (!((KaitaiStream.ByteArrayCompare(Magic, new byte[] { 0, 0, 1, 0 }) == 0)))
{
throw new ValidationNotEqualError(new byte[] { 0, 0, 1, 0 }, Magic, M_Io, "/seq/0");
}
_numImages = m_io.ReadU2le();
_images = new List<IconDirEntry>();
for (var i = 0; i < NumImages; i++)
{
_images.Add(new IconDirEntry(m_io, this, m_root));
}
}
public partial class IconDirEntry : KaitaiStruct
{
public static IconDirEntry FromFile(string fileName)
{
return new IconDirEntry(new KaitaiStream(fileName));
}
public IconDirEntry(KaitaiStream p__io, Ico p__parent = null, Ico p__root = null) : base(p__io)
{
m_parent = p__parent;
m_root = p__root;
f_img = false;
f_pngHeader = false;
f_isPng = false;
_read();
}
private void _read()
{
_width = m_io.ReadU1();
_height = m_io.ReadU1();
_numColors = m_io.ReadU1();
_reserved = m_io.ReadBytes(1);
if (!((KaitaiStream.ByteArrayCompare(Reserved, new byte[] { 0 }) == 0)))
{
throw new ValidationNotEqualError(new byte[] { 0 }, Reserved, M_Io, "/types/icon_dir_entry/seq/3");
}
_numPlanes = m_io.ReadU2le();
_bpp = m_io.ReadU2le();
_lenImg = m_io.ReadU4le();
_ofsImg = m_io.ReadU4le();
}
private bool f_img;
private byte[] _img;
/// <summary>
/// Raw image data. Use `is_png` to determine whether this is an
/// embedded PNG file (true) or a DIB bitmap (false) and call a
/// relevant parser, if needed to parse image data further.
/// </summary>
public byte[] Img
{
get
{
if (f_img)
return _img;
long _pos = m_io.Pos;
m_io.Seek(OfsImg);
_img = m_io.ReadBytes(LenImg);
m_io.Seek(_pos);
f_img = true;
return _img;
}
}
private bool f_pngHeader;
private byte[] _pngHeader;
/// <summary>
/// Pre-reads first 8 bytes of the image to determine if it's an
/// embedded PNG file.
/// </summary>
public byte[] PngHeader
{
get
{
if (f_pngHeader)
return _pngHeader;
long _pos = m_io.Pos;
m_io.Seek(OfsImg);
_pngHeader = m_io.ReadBytes(8);
m_io.Seek(_pos);
f_pngHeader = true;
return _pngHeader;
}
}
private bool f_isPng;
private bool _isPng;
/// <summary>
/// True if this image is in PNG format.
/// </summary>
public bool IsPng
{
get
{
if (f_isPng)
return _isPng;
_isPng = (bool) ((KaitaiStream.ByteArrayCompare(PngHeader, new byte[] { 137, 80, 78, 71, 13, 10, 26, 10 }) == 0));
f_isPng = true;
return _isPng;
}
}
private byte _width;
private byte _height;
private byte _numColors;
private byte[] _reserved;
private ushort _numPlanes;
private ushort _bpp;
private uint _lenImg;
private uint _ofsImg;
private Ico m_root;
private Ico m_parent;
/// <summary>
/// Width of image, px
/// </summary>
public byte Width { get { return _width; } }
/// <summary>
/// Height of image, px
/// </summary>
public byte Height { get { return _height; } }
/// <summary>
/// Number of colors in palette of the image or 0 if image has
/// no palette (i.e. RGB, RGBA, etc)
/// </summary>
public byte NumColors { get { return _numColors; } }
public byte[] Reserved { get { return _reserved; } }
/// <summary>
/// Number of color planes
/// </summary>
public ushort NumPlanes { get { return _numPlanes; } }
/// <summary>
/// Bits per pixel in the image
/// </summary>
public ushort Bpp { get { return _bpp; } }
/// <summary>
/// Size of the image data
/// </summary>
public uint LenImg { get { return _lenImg; } }
/// <summary>
/// Absolute offset of the image data start in the file
/// </summary>
public uint OfsImg { get { return _ofsImg; } }
public Ico M_Root { get { return m_root; } }
public Ico M_Parent { get { return m_parent; } }
}
private byte[] _magic;
private ushort _numImages;
private List<IconDirEntry> _images;
private Ico m_root;
private KaitaiStruct m_parent;
public byte[] Magic { get { return _magic; } }
/// <summary>
/// Number of images contained in this file
/// </summary>
public ushort NumImages { get { return _numImages; } }
public List<IconDirEntry> Images { get { return _images; } }
public Ico M_Root { get { return m_root; } }
public KaitaiStruct M_Parent { get { return m_parent; } }
}
}