# -*- coding: utf-8 -*-
"""Windows Resource file."""
import logging
import pyexe
import pywrc
[docs]
class WindowsResourceFile(object):
"""Windows Resource file.
Attributes:
windows_path (str): Windows path of the resource file.
"""
_MESSAGE_TABLE_RESOURCE_IDENTIFIER = 0x0b
_VERSION_INFORMATION_RESOURCE_IDENTIFIER = 0x10
[docs]
def __init__(
self, windows_path, ascii_codepage='cp1252',
preferred_language_identifier=0x0409):
"""Initializes the Windows Resource file.
Args:
windows_path (str): normalized version of the Windows path.
ascii_codepage (Optional[str]): ASCII string codepage.
preferred_language_identifier (Optional[int]): preferred language
identifier (LCID).
"""
super(WindowsResourceFile, self).__init__()
self._ascii_codepage = ascii_codepage
self._exe_file = pyexe.file()
self._exe_file.set_ascii_codepage(self._ascii_codepage)
self._exe_section = None
self._file_object = None
self._file_version = None
self._is_open = False
self._preferred_language_identifier = preferred_language_identifier
self._product_version = None
# TODO: wrc stream set codepage?
self._wrc_stream = pywrc.stream()
self.windows_path = windows_path
def _GetVersionInformation(self):
"""Determines the file and product version."""
version_information_resource = self._GetVersionInformationResource()
if not version_information_resource:
return
file_version = version_information_resource.file_version
major_version = (file_version >> 48) & 0xffff
minor_version = (file_version >> 32) & 0xffff
build_number = (file_version >> 16) & 0xffff
revision_number = file_version & 0xffff
self._file_version = (
f'{major_version:d}.{minor_version:d}.{build_number:d}.'
f'{revision_number:d}')
product_version = version_information_resource.product_version
major_version = (product_version >> 48) & 0xffff
minor_version = (product_version >> 32) & 0xffff
build_number = (product_version >> 16) & 0xffff
revision_number = product_version & 0xffff
self._product_version = (
f'{major_version:d}.{minor_version:d}.{build_number:d}.'
f'{revision_number:d}')
if file_version != product_version:
logging.warning((
f'Mismatch between file version: {self._file_version:s} and product '
f'version: {self._product_version:s} in resource file: '
f'{self.windows_path:s}.'))
def _GetVersionInformationResource(self):
"""Retrieves the version information resource.
Returns:
pywrc.version_information_resource: version information resource or None
if not available.
"""
preferred_wrc_resource_sub_item = None
wrc_resource = self._wrc_stream.get_resource_by_identifier(
self._VERSION_INFORMATION_RESOURCE_IDENTIFIER)
if wrc_resource:
first_wrc_resource_sub_item = None
for wrc_resource_item in wrc_resource.items:
for wrc_resource_sub_item in wrc_resource_item.sub_items:
if not first_wrc_resource_sub_item:
first_wrc_resource_sub_item = wrc_resource_sub_item
language_identifier = wrc_resource_sub_item.identifier
if language_identifier == self._preferred_language_identifier:
if not preferred_wrc_resource_sub_item:
preferred_wrc_resource_sub_item = wrc_resource_sub_item
if not preferred_wrc_resource_sub_item:
preferred_wrc_resource_sub_item = first_wrc_resource_sub_item
if not preferred_wrc_resource_sub_item:
return None
resource_data = preferred_wrc_resource_sub_item.read()
version_information_resource = pywrc.version_information_resource()
version_information_resource.copy_from_byte_stream(resource_data)
return version_information_resource
@property
def file_version(self):
"""str: the file version."""
if self._file_version is None:
self._GetVersionInformation()
return self._file_version
@property
def product_version(self):
"""str: the product version."""
if self._product_version is None:
self._GetVersionInformation()
return self._product_version
[docs]
def Close(self):
"""Closes the Windows Resource file.
Raises:
IOError: if not open.
OSError: if not open.
"""
if not self._is_open:
raise IOError('Not opened.')
if self._exe_section:
self._wrc_stream.close()
self._exe_file.close()
self._file_object = None
self._is_open = False
[docs]
def GetMessageTableResource(self):
"""Retrieves the message table resource.
Returns:
pywrc.resource: resource containing the message table resource or None
if not available.
"""
return self._wrc_stream.get_resource_by_identifier(
self._MESSAGE_TABLE_RESOURCE_IDENTIFIER)
[docs]
def GetMUILanguage(self):
"""Retrieves the MUI language.
Returns:
str: MUI language or None if not available.
"""
mui_resource = self.GetMUIResource()
if not mui_resource:
return None
return mui_resource.language
[docs]
def GetMUIResource(self):
"""Retrieves the MUI resource.
Returns:
pywrc.mui_resource: MUI resource or None if not available.
"""
preferred_wrc_resource_sub_item = None
wrc_resource = self._wrc_stream.get_resource_by_name('MUI')
if wrc_resource:
first_wrc_resource_sub_item = None
for wrc_resource_item in wrc_resource.items:
for wrc_resource_sub_item in wrc_resource_item.sub_items:
if not first_wrc_resource_sub_item:
first_wrc_resource_sub_item = wrc_resource_sub_item
language_identifier = wrc_resource_sub_item.identifier
if language_identifier == self._preferred_language_identifier:
if not preferred_wrc_resource_sub_item:
preferred_wrc_resource_sub_item = wrc_resource_sub_item
if not preferred_wrc_resource_sub_item:
preferred_wrc_resource_sub_item = first_wrc_resource_sub_item
if not preferred_wrc_resource_sub_item:
return None
resource_data = preferred_wrc_resource_sub_item.read()
mui_resource = pywrc.mui_resource()
mui_resource.copy_from_byte_stream(resource_data)
return mui_resource
[docs]
def HasMessageTableResource(self):
"""Determines if the resource file as a message table resource.
Returns:
bool: True if the resource file as a message table resource.
"""
wrc_resource = None
if self._wrc_stream:
try:
wrc_resource = self._wrc_stream.get_resource_by_identifier(
self._MESSAGE_TABLE_RESOURCE_IDENTIFIER)
except IOError:
pass
return bool(wrc_resource)
[docs]
def OpenFileObject(self, file_object):
"""Opens the Windows Resource file using a file-like object.
Args:
file_object (file): file-like object.
Raises:
IOError: if already open.
OSError: if already open.
"""
if self._is_open:
raise IOError('Already open.')
self._exe_file.open_file_object(file_object)
self._exe_section = self._exe_file.get_section_by_name('.rsrc')
if self._exe_section:
self._wrc_stream.set_virtual_address(self._exe_section.virtual_address)
self._wrc_stream.open_file_object(self._exe_section)
self._file_object = file_object
self._is_open = True