Windows resource file: Rust 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.

Rust source code to parse Windows resource file

windows_resource_file.rs

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#![allow(unused_imports)]
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![allow(irrefutable_let_patterns)]
#![allow(unused_comparisons)]

extern crate kaitai;
use kaitai::*;
use std::convert::{TryFrom, TryInto};
use std::cell::{Ref, Cell, RefCell};
use std::rc::{Rc, Weak};

/**
 * 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.
 */

#[derive(Default, Debug, Clone)]
pub struct WindowsResourceFile {
    pub _root: SharedType<WindowsResourceFile>,
    pub _parent: SharedType<WindowsResourceFile>,
    pub _self: SharedType<Self>,
    resources: RefCell<Vec<OptRc<WindowsResourceFile_Resource>>>,
    _io: RefCell<BytesReader>,
}
impl KStruct for WindowsResourceFile {
    type Root = WindowsResourceFile;
    type Parent = WindowsResourceFile;

    fn read<S: KStream>(
        self_rc: &OptRc<Self>,
        _io: &S,
        _root: SharedType<Self::Root>,
        _parent: SharedType<Self::Parent>,
    ) -> KResult<()> {
        *self_rc._io.borrow_mut() = _io.clone();
        self_rc._root.set(_root.get());
        self_rc._parent.set(_parent.get());
        self_rc._self.set(Ok(self_rc.clone()));
        let _rrc = self_rc._root.get_value().borrow().upgrade();
        let _prc = self_rc._parent.get_value().borrow().upgrade();
        let _r = _rrc.as_ref().unwrap();
        *self_rc.resources.borrow_mut() = Vec::new();
        {
            let mut _i = 0;
            while !_io.is_eof() {
                let t = Self::read_into::<_, WindowsResourceFile_Resource>(&*_io, Some(self_rc._root.clone()), Some(self_rc._self.clone()))?.into();
                self_rc.resources.borrow_mut().push(t);
                _i += 1;
            }
        }
        Ok(())
    }
}
impl WindowsResourceFile {
}
impl WindowsResourceFile {
    pub fn resources(&self) -> Ref<'_, Vec<OptRc<WindowsResourceFile_Resource>>> {
        self.resources.borrow()
    }
}
impl WindowsResourceFile {
    pub fn _io(&self) -> Ref<'_, BytesReader> {
        self._io.borrow()
    }
}

/**
 * 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.
 * \sa https://learn.microsoft.com/en-us/windows/win32/menurc/resourceheader Source
 */

#[derive(Default, Debug, Clone)]
pub struct WindowsResourceFile_Resource {
    pub _root: SharedType<WindowsResourceFile>,
    pub _parent: SharedType<WindowsResourceFile>,
    pub _self: SharedType<Self>,
    value_size: RefCell<u32>,
    header_size: RefCell<u32>,
    type: RefCell<OptRc<WindowsResourceFile_UnicodeOrId>>,
    name: RefCell<OptRc<WindowsResourceFile_UnicodeOrId>>,
    padding1: RefCell<Vec<u8>>,
    format_version: RefCell<u32>,
    flags: RefCell<u16>,
    language: RefCell<u16>,
    value_version: RefCell<u32>,
    characteristics: RefCell<u32>,
    value: RefCell<Vec<u8>>,
    padding2: RefCell<Vec<u8>>,
    _io: RefCell<BytesReader>,
    f_type_as_predef: Cell<bool>,
    type_as_predef: RefCell<WindowsResourceFile_Resource_PredefTypes>,
}
impl KStruct for WindowsResourceFile_Resource {
    type Root = WindowsResourceFile;
    type Parent = WindowsResourceFile;

    fn read<S: KStream>(
        self_rc: &OptRc<Self>,
        _io: &S,
        _root: SharedType<Self::Root>,
        _parent: SharedType<Self::Parent>,
    ) -> KResult<()> {
        *self_rc._io.borrow_mut() = _io.clone();
        self_rc._root.set(_root.get());
        self_rc._parent.set(_parent.get());
        self_rc._self.set(Ok(self_rc.clone()));
        let _rrc = self_rc._root.get_value().borrow().upgrade();
        let _prc = self_rc._parent.get_value().borrow().upgrade();
        let _r = _rrc.as_ref().unwrap();
        *self_rc.value_size.borrow_mut() = _io.read_u4le()?.into();
        *self_rc.header_size.borrow_mut() = _io.read_u4le()?.into();
        let t = Self::read_into::<_, WindowsResourceFile_UnicodeOrId>(&*_io, Some(self_rc._root.clone()), Some(self_rc._self.clone()))?.into();
        *self_rc.type.borrow_mut() = t;
        let t = Self::read_into::<_, WindowsResourceFile_UnicodeOrId>(&*_io, Some(self_rc._root.clone()), Some(self_rc._self.clone()))?.into();
        *self_rc.name.borrow_mut() = t;
        *self_rc.padding1.borrow_mut() = _io.read_bytes(modulo(((4 as i32) - (_io.pos() as i32)) as i64, 4 as i64) as usize)?.into();
        *self_rc.format_version.borrow_mut() = _io.read_u4le()?.into();
        *self_rc.flags.borrow_mut() = _io.read_u2le()?.into();
        *self_rc.language.borrow_mut() = _io.read_u2le()?.into();
        *self_rc.value_version.borrow_mut() = _io.read_u4le()?.into();
        *self_rc.characteristics.borrow_mut() = _io.read_u4le()?.into();
        *self_rc.value.borrow_mut() = _io.read_bytes(*self_rc.value_size() as usize)?.into();
        *self_rc.padding2.borrow_mut() = _io.read_bytes(modulo(((4 as i32) - (_io.pos() as i32)) as i64, 4 as i64) as usize)?.into();
        Ok(())
    }
}
impl WindowsResourceFile_Resource {

    /**
     * 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.
     */
    pub fn type_as_predef(
        &self
    ) -> KResult<Ref<'_, WindowsResourceFile_Resource_PredefTypes>> {
        let _io = self._io.borrow();
        let _rrc = self._root.get_value().borrow().upgrade();
        let _prc = self._parent.get_value().borrow().upgrade();
        let _r = _rrc.as_ref().unwrap();
        if self.f_type_as_predef.get() {
            return Ok(self.type_as_predef.borrow());
        }
        self.f_type_as_predef.set(true);
        if  ((!(*self.type().is_string()?)) && (((*self.type().as_numeric() as u16) <= (255 as u16))))  {
            *self.type_as_predef.borrow_mut() = (*self.type().as_numeric() as i64).try_into()?;
        }
        Ok(self.type_as_predef.borrow())
    }
}

/**
 * Size of resource value that follows the header
 */
impl WindowsResourceFile_Resource {
    pub fn value_size(&self) -> Ref<'_, u32> {
        self.value_size.borrow()
    }
}

/**
 * Size of this header (i.e. every field except `value` and an
 * optional padding after it)
 */
impl WindowsResourceFile_Resource {
    pub fn header_size(&self) -> Ref<'_, u32> {
        self.header_size.borrow()
    }
}
impl WindowsResourceFile_Resource {
    pub fn type(&self) -> Ref<'_, OptRc<WindowsResourceFile_UnicodeOrId>> {
        self.type.borrow()
    }
}
impl WindowsResourceFile_Resource {
    pub fn name(&self) -> Ref<'_, OptRc<WindowsResourceFile_UnicodeOrId>> {
        self.name.borrow()
    }
}
impl WindowsResourceFile_Resource {
    pub fn padding1(&self) -> Ref<'_, Vec<u8>> {
        self.padding1.borrow()
    }
}
impl WindowsResourceFile_Resource {
    pub fn format_version(&self) -> Ref<'_, u32> {
        self.format_version.borrow()
    }
}
impl WindowsResourceFile_Resource {
    pub fn flags(&self) -> Ref<'_, u16> {
        self.flags.borrow()
    }
}
impl WindowsResourceFile_Resource {
    pub fn language(&self) -> Ref<'_, u16> {
        self.language.borrow()
    }
}

/**
 * Version for value, as specified by a user.
 */
impl WindowsResourceFile_Resource {
    pub fn value_version(&self) -> Ref<'_, u32> {
        self.value_version.borrow()
    }
}

/**
 * Extra 4 bytes that can be used by user for any purpose.
 */
impl WindowsResourceFile_Resource {
    pub fn characteristics(&self) -> Ref<'_, u32> {
        self.characteristics.borrow()
    }
}
impl WindowsResourceFile_Resource {
    pub fn value(&self) -> Ref<'_, Vec<u8>> {
        self.value.borrow()
    }
}
impl WindowsResourceFile_Resource {
    pub fn padding2(&self) -> Ref<'_, Vec<u8>> {
        self.padding2.borrow()
    }
}
impl WindowsResourceFile_Resource {
    pub fn _io(&self) -> Ref<'_, BytesReader> {
        self._io.borrow()
    }
}
#[derive(Debug, PartialEq, Clone)]
pub enum WindowsResourceFile_Resource_PredefTypes {
    Cursor,
    Bitmap,
    Icon,
    Menu,
    Dialog,
    String,
    Fontdir,
    Font,
    Accelerator,
    Rcdata,
    Messagetable,
    GroupCursor,
    GroupIcon,
    Version,
    Dlginclude,
    Plugplay,
    Vxd,
    Anicursor,
    Aniicon,
    Html,
    Manifest,
    Unknown(i64),
}

impl TryFrom<i64> for WindowsResourceFile_Resource_PredefTypes {
    type Error = KError;
    fn try_from(flag: i64) -> KResult<WindowsResourceFile_Resource_PredefTypes> {
        match flag {
            1 => Ok(WindowsResourceFile_Resource_PredefTypes::Cursor),
            2 => Ok(WindowsResourceFile_Resource_PredefTypes::Bitmap),
            3 => Ok(WindowsResourceFile_Resource_PredefTypes::Icon),
            4 => Ok(WindowsResourceFile_Resource_PredefTypes::Menu),
            5 => Ok(WindowsResourceFile_Resource_PredefTypes::Dialog),
            6 => Ok(WindowsResourceFile_Resource_PredefTypes::String),
            7 => Ok(WindowsResourceFile_Resource_PredefTypes::Fontdir),
            8 => Ok(WindowsResourceFile_Resource_PredefTypes::Font),
            9 => Ok(WindowsResourceFile_Resource_PredefTypes::Accelerator),
            10 => Ok(WindowsResourceFile_Resource_PredefTypes::Rcdata),
            11 => Ok(WindowsResourceFile_Resource_PredefTypes::Messagetable),
            12 => Ok(WindowsResourceFile_Resource_PredefTypes::GroupCursor),
            14 => Ok(WindowsResourceFile_Resource_PredefTypes::GroupIcon),
            16 => Ok(WindowsResourceFile_Resource_PredefTypes::Version),
            17 => Ok(WindowsResourceFile_Resource_PredefTypes::Dlginclude),
            19 => Ok(WindowsResourceFile_Resource_PredefTypes::Plugplay),
            20 => Ok(WindowsResourceFile_Resource_PredefTypes::Vxd),
            21 => Ok(WindowsResourceFile_Resource_PredefTypes::Anicursor),
            22 => Ok(WindowsResourceFile_Resource_PredefTypes::Aniicon),
            23 => Ok(WindowsResourceFile_Resource_PredefTypes::Html),
            24 => Ok(WindowsResourceFile_Resource_PredefTypes::Manifest),
            _ => Ok(WindowsResourceFile_Resource_PredefTypes::Unknown(flag)),
        }
    }
}

impl From<&WindowsResourceFile_Resource_PredefTypes> for i64 {
    fn from(v: &WindowsResourceFile_Resource_PredefTypes) -> Self {
        match *v {
            WindowsResourceFile_Resource_PredefTypes::Cursor => 1,
            WindowsResourceFile_Resource_PredefTypes::Bitmap => 2,
            WindowsResourceFile_Resource_PredefTypes::Icon => 3,
            WindowsResourceFile_Resource_PredefTypes::Menu => 4,
            WindowsResourceFile_Resource_PredefTypes::Dialog => 5,
            WindowsResourceFile_Resource_PredefTypes::String => 6,
            WindowsResourceFile_Resource_PredefTypes::Fontdir => 7,
            WindowsResourceFile_Resource_PredefTypes::Font => 8,
            WindowsResourceFile_Resource_PredefTypes::Accelerator => 9,
            WindowsResourceFile_Resource_PredefTypes::Rcdata => 10,
            WindowsResourceFile_Resource_PredefTypes::Messagetable => 11,
            WindowsResourceFile_Resource_PredefTypes::GroupCursor => 12,
            WindowsResourceFile_Resource_PredefTypes::GroupIcon => 14,
            WindowsResourceFile_Resource_PredefTypes::Version => 16,
            WindowsResourceFile_Resource_PredefTypes::Dlginclude => 17,
            WindowsResourceFile_Resource_PredefTypes::Plugplay => 19,
            WindowsResourceFile_Resource_PredefTypes::Vxd => 20,
            WindowsResourceFile_Resource_PredefTypes::Anicursor => 21,
            WindowsResourceFile_Resource_PredefTypes::Aniicon => 22,
            WindowsResourceFile_Resource_PredefTypes::Html => 23,
            WindowsResourceFile_Resource_PredefTypes::Manifest => 24,
            WindowsResourceFile_Resource_PredefTypes::Unknown(v) => v
        }
    }
}

impl Default for WindowsResourceFile_Resource_PredefTypes {
    fn default() -> Self { WindowsResourceFile_Resource_PredefTypes::Unknown(0) }
}


/**
 * 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.
 */

#[derive(Default, Debug, Clone)]
pub struct WindowsResourceFile_UnicodeOrId {
    pub _root: SharedType<WindowsResourceFile>,
    pub _parent: SharedType<WindowsResourceFile_Resource>,
    pub _self: SharedType<Self>,
    first: RefCell<u16>,
    as_numeric: RefCell<u16>,
    rest: RefCell<Vec<u16>>,
    noop: RefCell<Vec<u8>>,
    _io: RefCell<BytesReader>,
    f_as_string: Cell<bool>,
    as_string: RefCell<String>,
    f_is_string: Cell<bool>,
    is_string: RefCell<bool>,
    f_save_pos1: Cell<bool>,
    save_pos1: RefCell<i32>,
    f_save_pos2: Cell<bool>,
    save_pos2: RefCell<i32>,
}
impl KStruct for WindowsResourceFile_UnicodeOrId {
    type Root = WindowsResourceFile;
    type Parent = WindowsResourceFile_Resource;

    fn read<S: KStream>(
        self_rc: &OptRc<Self>,
        _io: &S,
        _root: SharedType<Self::Root>,
        _parent: SharedType<Self::Parent>,
    ) -> KResult<()> {
        *self_rc._io.borrow_mut() = _io.clone();
        self_rc._root.set(_root.get());
        self_rc._parent.set(_parent.get());
        self_rc._self.set(Ok(self_rc.clone()));
        let _rrc = self_rc._root.get_value().borrow().upgrade();
        let _prc = self_rc._parent.get_value().borrow().upgrade();
        let _r = _rrc.as_ref().unwrap();
        if ((*self_rc.save_pos1()? as i32) >= (0 as i32)) {
            *self_rc.first.borrow_mut() = _io.read_u2le()?.into();
        }
        if !(*self_rc.is_string()?) {
            *self_rc.as_numeric.borrow_mut() = _io.read_u2le()?.into();
        }
        if *self_rc.is_string()? {
            *self_rc.rest.borrow_mut() = Vec::new();
            {
                let mut _i = 0;
                while {
                    self_rc.rest.borrow_mut().push(_io.read_u2le()?.into());
                    let _t_rest = self_rc.rest.borrow();
                    let _tmpa = *_t_rest.last().unwrap();
                    _i += 1;
                    let x = !(((_tmpa as u16) == (0 as u16)));
                    x
                } {}
            }
        }
        if  ((*self_rc.is_string()?) && (((*self_rc.save_pos2()? as i32) >= (0 as i32))))  {
            *self_rc.noop.borrow_mut() = _io.read_bytes(0 as usize)?.into();
        }
        Ok(())
    }
}
impl WindowsResourceFile_UnicodeOrId {
    pub fn as_string(
        &self
    ) -> KResult<Ref<'_, String>> {
        let _io = self._io.borrow();
        let _rrc = self._root.get_value().borrow().upgrade();
        let _prc = self._parent.get_value().borrow().upgrade();
        let _r = _rrc.as_ref().unwrap();
        if self.f_as_string.get() {
            return Ok(self.as_string.borrow());
        }
        self.f_as_string.set(true);
        if *self.is_string()? {
            let _pos = _io.pos();
            _io.seek(*self.save_pos1()? as usize)?;
            *self.as_string.borrow_mut() = bytes_to_str(&_io.read_bytes(((((*self.save_pos2()? as i32) - (*self.save_pos1()? as i32)) as i32) - (2 as i32)) as usize)?.into(), "UTF-16LE")?;
            _io.seek(_pos)?;
        }
        Ok(self.as_string.borrow())
    }
    pub fn is_string(
        &self
    ) -> KResult<Ref<'_, bool>> {
        let _io = self._io.borrow();
        let _rrc = self._root.get_value().borrow().upgrade();
        let _prc = self._parent.get_value().borrow().upgrade();
        let _r = _rrc.as_ref().unwrap();
        if self.f_is_string.get() {
            return Ok(self.is_string.borrow());
        }
        self.f_is_string.set(true);
        *self.is_string.borrow_mut() = (((*self.first() as i32) != (65535 as i32))) as bool;
        Ok(self.is_string.borrow())
    }
    pub fn save_pos1(
        &self
    ) -> KResult<Ref<'_, i32>> {
        let _io = self._io.borrow();
        let _rrc = self._root.get_value().borrow().upgrade();
        let _prc = self._parent.get_value().borrow().upgrade();
        let _r = _rrc.as_ref().unwrap();
        if self.f_save_pos1.get() {
            return Ok(self.save_pos1.borrow());
        }
        self.f_save_pos1.set(true);
        *self.save_pos1.borrow_mut() = (_io.pos()) as i32;
        Ok(self.save_pos1.borrow())
    }
    pub fn save_pos2(
        &self
    ) -> KResult<Ref<'_, i32>> {
        let _io = self._io.borrow();
        let _rrc = self._root.get_value().borrow().upgrade();
        let _prc = self._parent.get_value().borrow().upgrade();
        let _r = _rrc.as_ref().unwrap();
        if self.f_save_pos2.get() {
            return Ok(self.save_pos2.borrow());
        }
        self.f_save_pos2.set(true);
        *self.save_pos2.borrow_mut() = (_io.pos()) as i32;
        Ok(self.save_pos2.borrow())
    }
}
impl WindowsResourceFile_UnicodeOrId {
    pub fn first(&self) -> Ref<'_, u16> {
        self.first.borrow()
    }
}
impl WindowsResourceFile_UnicodeOrId {
    pub fn as_numeric(&self) -> Ref<'_, u16> {
        self.as_numeric.borrow()
    }
}
impl WindowsResourceFile_UnicodeOrId {
    pub fn rest(&self) -> Ref<'_, Vec<u16>> {
        self.rest.borrow()
    }
}
impl WindowsResourceFile_UnicodeOrId {
    pub fn noop(&self) -> Ref<'_, Vec<u8>> {
        self.noop.borrow()
    }
}
impl WindowsResourceFile_UnicodeOrId {
    pub fn _io(&self) -> Ref<'_, BytesReader> {
        self._io.borrow()
    }
}