Windows resource file: Go parsing library

Windows resource file (.res) are binary bundles of "resources". Resource has some sort of ID (numerical or string), type (predefined or user-defined), and raw value. Resource files can be seen standalone (as .res file), or embedded inside PE executable (.exe, .dll) files.

Typical use cases include:

  • providing information about the application (such as title, copyrights, etc)
  • embedding icon(s) to be displayed in file managers into .exe
  • adding non-code data into the binary, such as menus, dialog forms, cursor images, fonts, various misc bitmaps, and locale-aware strings

Windows provides special API to access "resources" from a binary.

Normally, resources files are created with rc compiler: it takes a .rc file (so called "resource-definition script") + all the raw resource binary files for input, and outputs .res file. That .res file can be linked into an .exe / .dll afterwards using a linker.

Internally, resource file is just a sequence of individual resource definitions. RC tool ensures that first resource (#0) is always empty.

File extension

res

KS implementation details

License: CC0-1.0

References

This page hosts a formal specification of Windows resource file using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Go source code to parse Windows resource file

windows_resource_file.go

// Code generated by kaitai-struct-compiler from a .ksy source file. DO NOT EDIT.

import (
	"github.com/kaitai-io/kaitai_struct_go_runtime/kaitai"
	"io"
	"golang.org/x/text/encoding/unicode"
)


/**
 * Windows resource file (.res) are binary bundles of
 * "resources". Resource has some sort of ID (numerical or string),
 * type (predefined or user-defined), and raw value. Resource files can
 * be seen standalone (as .res file), or embedded inside PE executable
 * (.exe, .dll) files.
 * 
 * Typical use cases include:
 * 
 * * providing information about the application (such as title, copyrights, etc)
 * * embedding icon(s) to be displayed in file managers into .exe
 * * adding non-code data into the binary, such as menus, dialog forms,
 *   cursor images, fonts, various misc bitmaps, and locale-aware
 *   strings
 * 
 * Windows provides special API to access "resources" from a binary.
 * 
 * Normally, resources files are created with `rc` compiler: it takes a
 * .rc file (so called "resource-definition script") + all the raw
 * resource binary files for input, and outputs .res file. That .res
 * file can be linked into an .exe / .dll afterwards using a linker.
 * 
 * Internally, resource file is just a sequence of individual resource
 * definitions. RC tool ensures that first resource (#0) is always
 * empty.
 */
type WindowsResourceFile struct {
	Resources []*WindowsResourceFile_Resource
	_io *kaitai.Stream
	_root *WindowsResourceFile
	_parent interface{}
}
func NewWindowsResourceFile() *WindowsResourceFile {
	return &WindowsResourceFile{
	}
}

func (this *WindowsResourceFile) Read(io *kaitai.Stream, parent interface{}, root *WindowsResourceFile) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	for i := 1;; i++ {
		tmp1, err := this._io.EOF()
		if err != nil {
			return err
		}
		if tmp1 {
			break
		}
		tmp2 := NewWindowsResourceFile_Resource()
		err = tmp2.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Resources = append(this.Resources, tmp2)
	}
	return err
}

/**
 * Each resource has a `type` and a `name`, which can be used to
 * identify it, and a `value`. Both `type` and `name` can be a
 * number or a string.
 * @see <a href="https://learn.microsoft.com/en-us/windows/win32/menurc/resourceheader">Source</a>
 */

type WindowsResourceFile_Resource_PredefTypes int
const (
	WindowsResourceFile_Resource_PredefTypes__Cursor WindowsResourceFile_Resource_PredefTypes = 1
	WindowsResourceFile_Resource_PredefTypes__Bitmap WindowsResourceFile_Resource_PredefTypes = 2
	WindowsResourceFile_Resource_PredefTypes__Icon WindowsResourceFile_Resource_PredefTypes = 3
	WindowsResourceFile_Resource_PredefTypes__Menu WindowsResourceFile_Resource_PredefTypes = 4
	WindowsResourceFile_Resource_PredefTypes__Dialog WindowsResourceFile_Resource_PredefTypes = 5
	WindowsResourceFile_Resource_PredefTypes__String WindowsResourceFile_Resource_PredefTypes = 6
	WindowsResourceFile_Resource_PredefTypes__Fontdir WindowsResourceFile_Resource_PredefTypes = 7
	WindowsResourceFile_Resource_PredefTypes__Font WindowsResourceFile_Resource_PredefTypes = 8
	WindowsResourceFile_Resource_PredefTypes__Accelerator WindowsResourceFile_Resource_PredefTypes = 9
	WindowsResourceFile_Resource_PredefTypes__Rcdata WindowsResourceFile_Resource_PredefTypes = 10
	WindowsResourceFile_Resource_PredefTypes__Messagetable WindowsResourceFile_Resource_PredefTypes = 11
	WindowsResourceFile_Resource_PredefTypes__GroupCursor WindowsResourceFile_Resource_PredefTypes = 12
	WindowsResourceFile_Resource_PredefTypes__GroupIcon WindowsResourceFile_Resource_PredefTypes = 14
	WindowsResourceFile_Resource_PredefTypes__Version WindowsResourceFile_Resource_PredefTypes = 16
	WindowsResourceFile_Resource_PredefTypes__Dlginclude WindowsResourceFile_Resource_PredefTypes = 17
	WindowsResourceFile_Resource_PredefTypes__Plugplay WindowsResourceFile_Resource_PredefTypes = 19
	WindowsResourceFile_Resource_PredefTypes__Vxd WindowsResourceFile_Resource_PredefTypes = 20
	WindowsResourceFile_Resource_PredefTypes__Anicursor WindowsResourceFile_Resource_PredefTypes = 21
	WindowsResourceFile_Resource_PredefTypes__Aniicon WindowsResourceFile_Resource_PredefTypes = 22
	WindowsResourceFile_Resource_PredefTypes__Html WindowsResourceFile_Resource_PredefTypes = 23
	WindowsResourceFile_Resource_PredefTypes__Manifest WindowsResourceFile_Resource_PredefTypes = 24
)
type WindowsResourceFile_Resource struct {
	ValueSize uint32
	HeaderSize uint32
	Type *WindowsResourceFile_UnicodeOrId
	Name *WindowsResourceFile_UnicodeOrId
	Padding1 []byte
	FormatVersion uint32
	Flags uint16
	Language uint16
	ValueVersion uint32
	Characteristics uint32
	Value []byte
	Padding2 []byte
	_io *kaitai.Stream
	_root *WindowsResourceFile
	_parent *WindowsResourceFile
	_f_typeAsPredef bool
	typeAsPredef WindowsResourceFile_Resource_PredefTypes
}
func NewWindowsResourceFile_Resource() *WindowsResourceFile_Resource {
	return &WindowsResourceFile_Resource{
	}
}

func (this *WindowsResourceFile_Resource) Read(io *kaitai.Stream, parent *WindowsResourceFile, root *WindowsResourceFile) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp3, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.ValueSize = uint32(tmp3)
	tmp4, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.HeaderSize = uint32(tmp4)
	tmp5 := NewWindowsResourceFile_UnicodeOrId()
	err = tmp5.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Type = tmp5
	tmp6 := NewWindowsResourceFile_UnicodeOrId()
	err = tmp6.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Name = tmp6
	tmp8, err := this._io.Pos()
	if err != nil {
		return err
	}
	tmp7 := (4 - tmp8) % 4
	if tmp7 < 0 {
		tmp7 += 4
	}
	tmp9, err := this._io.ReadBytes(int(tmp7))
	if err != nil {
		return err
	}
	tmp9 = tmp9
	this.Padding1 = tmp9
	tmp10, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.FormatVersion = uint32(tmp10)
	tmp11, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.Flags = uint16(tmp11)
	tmp12, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.Language = uint16(tmp12)
	tmp13, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.ValueVersion = uint32(tmp13)
	tmp14, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.Characteristics = uint32(tmp14)
	tmp15, err := this._io.ReadBytes(int(this.ValueSize))
	if err != nil {
		return err
	}
	tmp15 = tmp15
	this.Value = tmp15
	tmp17, err := this._io.Pos()
	if err != nil {
		return err
	}
	tmp16 := (4 - tmp17) % 4
	if tmp16 < 0 {
		tmp16 += 4
	}
	tmp18, err := this._io.ReadBytes(int(tmp16))
	if err != nil {
		return err
	}
	tmp18 = tmp18
	this.Padding2 = tmp18
	return err
}

/**
 * Numeric type IDs in range of [0..0xff] are reserved for
 * system usage in Windows, and there are some predefined,
 * well-known values in that range. This instance allows to get
 * it as enum value, if applicable.
 */
func (this *WindowsResourceFile_Resource) TypeAsPredef() (v WindowsResourceFile_Resource_PredefTypes, err error) {
	if (this._f_typeAsPredef) {
		return this.typeAsPredef, nil
	}
	tmp19, err := this.Type.IsString()
	if err != nil {
		return nil, err
	}
	if ( ((!(tmp19)) && (this.Type.AsNumeric <= 255)) ) {
		this.typeAsPredef = WindowsResourceFile_Resource_PredefTypes(WindowsResourceFile_Resource_PredefTypes(this.Type.AsNumeric))
	}
	this._f_typeAsPredef = true
	return this.typeAsPredef, nil
}

/**
 * Size of resource value that follows the header
 */

/**
 * Size of this header (i.e. every field except `value` and an
 * optional padding after it)
 */

/**
 * Version for value, as specified by a user.
 */

/**
 * Extra 4 bytes that can be used by user for any purpose.
 */

/**
 * Resources use a special serialization of names and types: they
 * can be either a number or a string.
 * 
 * Use `is_string` to check which kind we've got here, and then use
 * `as_numeric` or `as_string` to get relevant value.
 */
type WindowsResourceFile_UnicodeOrId struct {
	First uint16
	AsNumeric uint16
	Rest []uint16
	Noop []byte
	_io *kaitai.Stream
	_root *WindowsResourceFile
	_parent *WindowsResourceFile_Resource
	_f_savePos1 bool
	savePos1 int
	_f_savePos2 bool
	savePos2 int
	_f_isString bool
	isString bool
	_f_asString bool
	asString string
}
func NewWindowsResourceFile_UnicodeOrId() *WindowsResourceFile_UnicodeOrId {
	return &WindowsResourceFile_UnicodeOrId{
	}
}

func (this *WindowsResourceFile_UnicodeOrId) Read(io *kaitai.Stream, parent *WindowsResourceFile_Resource, root *WindowsResourceFile) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp20, err := this.SavePos1()
	if err != nil {
		return err
	}
	if (tmp20 >= 0) {
		tmp21, err := this._io.ReadU2le()
		if err != nil {
			return err
		}
		this.First = uint16(tmp21)
	}
	tmp22, err := this.IsString()
	if err != nil {
		return err
	}
	if (!(tmp22)) {
		tmp23, err := this._io.ReadU2le()
		if err != nil {
			return err
		}
		this.AsNumeric = uint16(tmp23)
	}
	tmp24, err := this.IsString()
	if err != nil {
		return err
	}
	if (tmp24) {
		for i := 1;; i++ {
			tmp25, err := this._io.ReadU2le()
			if err != nil {
				return err
			}
			_it := tmp25
			this.Rest = append(this.Rest, _it)
			if _it == 0 {
				break
			}
		}
	}
	tmp26, err := this.IsString()
	if err != nil {
		return err
	}
	tmp27, err := this.SavePos2()
	if err != nil {
		return err
	}
	if ( ((tmp26) && (tmp27 >= 0)) ) {
		tmp28, err := this._io.ReadBytes(int(0))
		if err != nil {
			return err
		}
		tmp28 = tmp28
		this.Noop = tmp28
	}
	return err
}
func (this *WindowsResourceFile_UnicodeOrId) SavePos1() (v int, err error) {
	if (this._f_savePos1) {
		return this.savePos1, nil
	}
	tmp29, err := this._io.Pos()
	if err != nil {
		return 0, err
	}
	this.savePos1 = int(tmp29)
	this._f_savePos1 = true
	return this.savePos1, nil
}
func (this *WindowsResourceFile_UnicodeOrId) SavePos2() (v int, err error) {
	if (this._f_savePos2) {
		return this.savePos2, nil
	}
	tmp30, err := this._io.Pos()
	if err != nil {
		return 0, err
	}
	this.savePos2 = int(tmp30)
	this._f_savePos2 = true
	return this.savePos2, nil
}
func (this *WindowsResourceFile_UnicodeOrId) IsString() (v bool, err error) {
	if (this._f_isString) {
		return this.isString, nil
	}
	this.isString = bool(this.First != 65535)
	this._f_isString = true
	return this.isString, nil
}
func (this *WindowsResourceFile_UnicodeOrId) AsString() (v string, err error) {
	if (this._f_asString) {
		return this.asString, nil
	}
	tmp31, err := this.IsString()
	if err != nil {
		return "", err
	}
	if (tmp31) {
		_pos, err := this._io.Pos()
		if err != nil {
			return "", err
		}
		tmp32, err := this.SavePos1()
		if err != nil {
			return "", err
		}
		_, err = this._io.Seek(int64(tmp32), io.SeekStart)
		if err != nil {
			return "", err
		}
		tmp33, err := this.SavePos2()
		if err != nil {
			return "", err
		}
		tmp34, err := this.SavePos1()
		if err != nil {
			return "", err
		}
		tmp35, err := this._io.ReadBytes(int(((tmp33 - tmp34) - 2)))
		if err != nil {
			return "", err
		}
		tmp35 = tmp35
		tmp36, err := kaitai.BytesToStr(tmp35, unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder())
		if err != nil {
			return "", err
		}
		this.asString = tmp36
		_, err = this._io.Seek(_pos, io.SeekStart)
		if err != nil {
			return "", err
		}
		this._f_asString = true
	}
	this._f_asString = true
	return this.asString, nil
}