pax_global_header00006660000000000000000000000064152665462360014531gustar00rootroot0000000000000052 comment=a1e82ed2a0abbed24b5d95fa721d6ca8aca5c5b0 icoextract-0.3.1/000077500000000000000000000000001526654623600136775ustar00rootroot00000000000000icoextract-0.3.1/.gitignore000066400000000000000000000033411526654623600156700ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ pip-wheel-metadata/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # celery beat schedule file celerybeat-schedule # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # Autogenerated docs html/ .vscode icoextract-0.3.1/.woodpecker/000077500000000000000000000000001526654623600161175ustar00rootroot00000000000000icoextract-0.3.1/.woodpecker/check.yml000066400000000000000000000010371526654623600177200ustar00rootroot00000000000000steps: - name: Compile Win16 binaries image: ghcr.io/arlaneenalra/watcom-docker commands: - apk add --no-cache imagemagick - cd tests && make win16 - name: Run integration tests image: python:${PYTHON_VERSION}-alpine commands: - apk add --no-cache imagemagick make mingw-w64-gcc i686-mingw-w64-gcc - pip install .[thumbnailer,win16] - cd tests && make - python3 -m unittest discover . --verbose when: event: [tag, push, pull_request] matrix: PYTHON_VERSION: - 3.10 - 3.15 icoextract-0.3.1/.woodpecker/doc-upload.yml000066400000000000000000000013651526654623600206760ustar00rootroot00000000000000clone: git: image: woodpeckerci/plugin-git settings: # Fetch the full history so that `git describe --tags` is not empty depth: 0 tags: true steps: - commands: - pip install .[dev] awscli - pdoc --no-show-source -o ./html/ icoextract/*.py '!icoextract.version' --footer-text "icoextract $(git describe --tags)" - aws s3 cp html/ s3://jlu5-ci-doc/ --recursive image: python:3.14 name: Upload API docs environment: AWS_ACCESS_KEY_ID: from_secret: AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY: from_secret: AWS_SECRET_ACCESS_KEY AWS_ENDPOINT_URL_S3: https://s3.us-west-000.backblazeb2.com when: - event: tag - event: push branch: wip/doc-* depends_on: - check icoextract-0.3.1/.woodpecker/pypi-upload.yml000066400000000000000000000004511526654623600211050ustar00rootroot00000000000000steps: - commands: - pip install build twine - python3 -m build - twine upload --username __token__ dist/* image: python:3.14 name: Upload release to PyPI environment: TWINE_PASSWORD: from_secret: pypi_token when: event: [tag] depends_on: - check icoextract-0.3.1/CHANGELOG.md000066400000000000000000000053521526654623600155150ustar00rootroot00000000000000# Changelog ## icoextract 0.3.1 (2026-10-22) - Packaging: fix setuptools configuration to only install `icoextract/` - This should fix build issues in multi-Python version setups. ## icoextract 0.3.0 (2026-06-06) - Add support for Win16 NE EXEs via the nefile module (optional dependency) - Add support for string resource IDs in addition to integers (previously string IDs were always read as raw numbers) - `icolist` is now more verbose and shows each icon's image info (dimensions, size, and offsets) - `exe_thumbnailer` now prefers icons with higher bit depths. In practice, this mostly affects older programs like Internet Explorer 6 and Windows 3.x system apps. - **Notes for developers**: - `icoextract.IconExtractor()` is now a factory function instead of a class. By default it will autoselect the `IconExtractor` type based on the input executable format - this can be overridden using the `exe_type` parameter. - `list_group_icons()` now returns `(resource ID, group icon dir entry)` tuples instead of a simple ID and offset pair. - ResourceID is now a class type with `int()` and `str()` conversions, as these IDs can be either a string or a number. ## icoextract 0.2.0 (2025-06-02) - Add `-i/--id` option to extract icons by resource ID - Refactor / streamline extraction code - Add new `IconNotFoundError` exception, raised when the requested icon index or resource ID does not exist - cli: warn when extracting from Windows system DLLs for which icons have been moved to the SystemResources directory - Bump minimum supported Python version to 3.9 ## icoextract 0.1.6 (2025-03-02) - exe-thumbnailer: add `--force-resize` convenience option - exe-thumbnailer: fix handling of icons containing non-standard sizes like 192x192 - cli: warn when exporting images with a wrong extension (.jpg or .png). This aims to address a common source of confusion ## icoextract 0.1.5 (2024-04-28) - Add `application/vnd.microsoft.portable-executable` to supported MIME types - Bump minimum supported Python version to 3.8 - Add pdoc3 config, for autogenerated API docs ## icoextract 0.1.4 (2022-08-08) - IconExtractor: support raw bytes as input, in addition to a filename - Refresh function descriptions ## icoextract 0.1.3 (2022-06-12) - Fix thumbnail resizing; use native 128x128 icons when available (GH-7) - Clarify installation steps for thumbnailer - setup.py: exclude `tests` from installed packages (GH-9) ## icoextract 0.1.2 (2020-12-22) - Declare Pillow as an optional dependency (for icoextract-thumbnailer) - Fix autodiscovery for tests ## icoextract 0.1.1 (2020-07-01) - Refactor scripts to use setuptools entrypoints (adds Windows support) - Raise an error when seeing invalid icon definitions ## icoextract 0.1.0 (2019-11-22) - Initial release icoextract-0.3.1/LIB-USAGE.md000066400000000000000000000014221526654623600154700ustar00rootroot00000000000000For help on icoextract's frontend scripts, see `icoextract --help` and `icolist --help`. ## Using icoextract as a library ```python from icoextract import IconExtractor, IconExtractorError try: extractor = IconExtractor('/path/to/your.exe') # Export the first group icon to a .ico file extractor.export_icon('/path/to/your.ico', num=0) # Or read the .ico into a buffer, to pass it into other code data = extractor.get_icon(num=0) from PIL import Image im = Image.open(data) # ... manipulate a copy of the icon # In icoextract 0.2.0+, you can also extract icons by resource ID extractor.export_icon('/path/to/your.ico', resource_id=1234) except IconExtractorError: # No icons available, or the icon resource is malformed pass ``` icoextract-0.3.1/LICENSE000066400000000000000000000021751526654623600147110ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015-2016 Fadhil Mandaga Copyright (c) 2019-2026 James Lu 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. icoextract-0.3.1/README.md000066400000000000000000000061711526654623600151630ustar00rootroot00000000000000# icoextract [![Build Status](https://woodpecker.overdrivenetworks.com/api/badges/5/status.svg)](https://woodpecker.overdrivenetworks.com/repos/5) [![Packaging status](https://repology.org/badge/tiny-repos/icoextract.svg)](https://repology.org/project/icoextract/versions) **icoextract** is an icon extractor library for Windows executables (.exe/.dll/.mun). It also includes a thumbnailer script (`exe-thumbnailer`) for Linux desktops. This project is inspired by [extract-icon-py](https://github.com/firodj/extract-icon-py), [icoutils](https://www.nongnu.org/icoutils/), and others. icoextract aims to be: - Lightweight - Portable (cross-platform) - Fast on large files ## Installation ### Dependencies - Python 3.10+ - [pefile](https://github.com/erocarrera/pefile) - [Pillow](https://pillow.readthedocs.io/en/stable/) - optional, for exe-thumbnailer - [nefile](https://github.com/npjg/nefile) - optional, for Win16 / NE EXE support ### Installing from source You can install the project via pip: `pip3 install icoextract[thumbnailer]` On Linux, you can activate the thumbnailer by copying [`exe-thumbnailer.thumbnailer`](/exe-thumbnailer.thumbnailer) into the thumbnailers directory: - `/usr/local/share/thumbnailers/` if you installed `icoextract` globally - `~/.local/share/thumbnailers` if you installed `icoextract` for your user only The thumbnailer should work with any file manager that implements the [Freedesktop Thumbnails Standard](https://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html): this includes Nautilus, Caja, Nemo, Thunar (when Tumbler is installed), and PCManFM. KDE / Dolphin uses a different architecture and is *not* supported here. ### Distribution packages You can install icoextract from any of these distribution repositories: [![Packaging status](https://repology.org/badge/vertical-allrepos/icoextract.svg?columns=5)](https://repology.org/project/icoextract/versions) ## Usage icoextract ships `icoextract` and `icolist` scripts to extract and list icon resources inside a file. **Note**: recent versions of Windows (Windows 10 1903+) have moved icons from system libraries (`shell32.dll`, etc.) into a new [`C:\Windows\SystemResources`](https://superuser.com/questions/1480268/) folder. icoextract can extract these `.mun` files natively, but the `.dll`s themselves no longer contain icons. For API docs, see https://projects.jlu5.com/icoextract.html ``` usage: icoextract [-h] [-V] [-n NUM] [-i ID] [-v] input output Windows executable icon extractor. positional arguments: input input filename (.exe/.dll/.mun) output output filename (.ico) options: -h, --help show this help message and exit -V, --version show program's version number and exit -n, --num NUM index of icon to extract -i, --id ID resource ID of icon to extract -v, --verbose enables debug logging ``` ``` usage: icolist [-h] [-V] [-v] input Lists group icons present in a program. positional arguments: input input filename options: -h, --help show this help message and exit -V, --version show program's version number and exit -v, --verbose enables debug logging ``` icoextract-0.3.1/exe-thumbnailer.thumbnailer000066400000000000000000000002771526654623600212320ustar00rootroot00000000000000[Thumbnailer Entry] Exec=exe-thumbnailer -v -s %s %i %o MimeType=application/x-ms-dos-executable;application/x-dosexec;application/x-msdownload;application/vnd.microsoft.portable-executable icoextract-0.3.1/icoextract/000077500000000000000000000000001526654623600160445ustar00rootroot00000000000000icoextract-0.3.1/icoextract/__init__.py000066400000000000000000000057421526654623600201650ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT # Copyright (c) 2015-2016 Fadhil Mandaga # Copyright (c) 2019-2026 James Lu """ Windows executable icon extractor. .. include:: ../LIB-USAGE.md """ from contextlib import ExitStack import enum import logging import mmap from .base_extractor import BaseIconExtractor from .exceptions import ( IconExtractorError, IconNotFoundError, NoIconsAvailableError, InvalidIconDefinitionError, UnknownExecutableError, ) from .ne_extractor import NEIconExtractor from .pe_extractor import PEIconExtractor logger = logging.getLogger("icoextract") logging.basicConfig() try: from .version import __version__ except ImportError: __version__ = 'unknown' logger.info('icoextract: failed to read program version') __all__ = [ 'IconExtractor', 'IconExtractorError', 'IconNotFoundError', 'NoIconsAvailableError', 'InvalidIconDefinitionError', 'UnknownExecutableError', 'ExecutableType', ] class ExecutableType(enum.Enum): """Enum for supported executable formats.""" AUTO = 0 PE = 1 NE = 2 def detect_executable_type(buf: bytearray | mmap.mmap) -> ExecutableType: if buf[:2] != b'MZ': raise UnknownExecutableError("Unknown executable type (no MZ header)") # Get the offset to the real header (located at 0x3C) e_lfanew = int.from_bytes(buf[0x3C:0x40], byteorder='little') signature = buf[e_lfanew:e_lfanew+4] if signature.startswith(b'PE\x00\x00'): return ExecutableType.PE if signature.startswith(b'NE'): return ExecutableType.NE raise UnknownExecutableError("Unknown / unsupported executable type") def IconExtractor(filename: str | None = None, data: bytearray | mmap.mmap | None = None, exe_type: ExecutableType = ExecutableType.AUTO) -> BaseIconExtractor: """ IconExtractor factory function. When `exe_type` is set to *AUTO*, this will return an `icoextract.pe_extractor.PEIconExtractor` for Win32/Win64 PE executables and an `icoextract.ne_extractor.NEIconExtractor` for Win16 NE executables. """ if filename is None and data is None: raise ValueError("filename and data cannot both be None") if exe_type == ExecutableType.AUTO: if data is not None: exe_type = detect_executable_type(data) else: assert filename with ExitStack() as stack: f = stack.enter_context(open(filename, 'rb')) mm = stack.enter_context(mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)) exe_type = detect_executable_type(mm) logger.debug("Detected executable type as %s", exe_type) if exe_type == ExecutableType.PE: return PEIconExtractor(filename, data) if exe_type == ExecutableType.NE: return NEIconExtractor(filename, data) raise UnknownExecutableError("Unknown executable type") __pdoc__ = { 'scripts': False, 'version': False, } icoextract-0.3.1/icoextract/base_extractor.py000066400000000000000000000053021526654623600214230ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT # Copyright (c) 2019-2026 James Lu import abc import io import struct from .types import ExtractedGroupIcon, GroupIconWithIconOffsets, ResourceID class BaseIconExtractor(abc.ABC): """Base icon extractor class containing .ico writing logic.""" @abc.abstractmethod def _extract_icon(self, index: int = 0, resource_id: int | str | None = None) -> ExtractedGroupIcon: """Extract an icon by its index or resource ID.""" @abc.abstractmethod def list_group_icons(self) -> list[tuple[ResourceID, GroupIconWithIconOffsets]]: """ Returns all group icon entries as a list of (resource ID, group icon dir entry) tuples. """ def _write_ico(self, fd, icons: ExtractedGroupIcon): """Writes ICO data to a file descriptor.""" fd.write(b"\x00\x00") # 2 reserved bytes fd.write(struct.pack(" None: """ Exports ICO data for the requested group icon to `filename`. Icons can be selected by index (`num`) or resource ID. By default, the first icon in the binary is exported. """ group_icon = self._extract_icon(index=num, resource_id=resource_id) with open(filename, 'wb') as f: self._write_ico(f, group_icon) def get_icon(self, num: int = 0, resource_id: int | str | None = None) -> io.BytesIO: """ Exports ICO data for the requested group icon as a `io.BytesIO` instance. Icons can be selected by index (`num`) or resource ID. By default, the first icon in the binary is exported. """ group_icon = self._extract_icon(index=num, resource_id=resource_id) f = io.BytesIO() self._write_ico(f, group_icon) return f icoextract-0.3.1/icoextract/exceptions.py000066400000000000000000000013531526654623600206010ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT # Copyright (c) 2019-2026 James Lu class IconExtractorError(Exception): """Superclass for exceptions raised by IconExtractor.""" class IconNotFoundError(IconExtractorError): """Exception raised when extracting an icon index or resource ID that does not exist.""" class NoIconsAvailableError(IconExtractorError): """Exception raised when the input program has no icon resources.""" class InvalidIconDefinitionError(IconExtractorError): """Exception raised when the input program has an invalid icon resource.""" class UnknownExecutableError(IconExtractorError): """Exception raised when the executable type is invalid or not supported.""" icoextract-0.3.1/icoextract/ne_extractor.py000066400000000000000000000106621526654623600211200ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT # Copyright (c) 2019-2026 James Lu import logging try: import nefile except ImportError: nefile = None from .base_extractor import BaseIconExtractor from .exceptions import ( IconNotFoundError, NoIconsAvailableError ) from .types import ( ExtractedGroupIcon, GroupIconDirEntry, GroupIconWithIconOffsets, ResourceID, ) logger = logging.getLogger("icoextract") class NEIconExtractor(BaseIconExtractor): """Win16 New Executable (NE) icon extractor.""" def __init__(self, filename=None, data=None): """ Loads an Win16 New Executable from the given `filename` or `data` (raw buffer). If both `filename` and `data` are given, `filename` takes precedence. If the executable has contains no icons, this will raise `NoIconsAvailableError`. """ if nefile is None: raise ImportError("nefile module must be installed to extract NE programs") self._ne = nefile.NE(filename, data) if not self._ne.resource_table: raise NoIconsAvailableError("Executable does not have any resources") if not (group_icon_resources := self._ne.resource_table.resources.get( nefile.resource_table.ResourceType.RT_GROUP_ICON)): raise NoIconsAvailableError("Executable does not have any group icons") self._group_icon_resources = group_icon_resources @staticmethod def _convert_nefile_icon_dir_entry(nefile_icon_dir_entry) -> GroupIconDirEntry: assert nefile is not None assert isinstance(nefile_icon_dir_entry, nefile.resources.icon.IconDirectoryEntry) return GroupIconDirEntry( Width=nefile_icon_dir_entry.width, Height=nefile_icon_dir_entry.height, ColorCount=nefile_icon_dir_entry.total_palette_colors, Planes=nefile_icon_dir_entry.color_planes, BitCount=nefile_icon_dir_entry.bits_per_pixel, BytesInRes=nefile_icon_dir_entry.icon_size_in_bytes, ID=nefile_icon_dir_entry.icon_resource_id, ) def list_group_icons(self) -> list[tuple[ResourceID, GroupIconWithIconOffsets]]: assert nefile is not None result = [] resources_offset = self._ne.resource_table.resource_table_start_offset icon_rtt = self._ne.resource_table.resource_type_tables[ nefile.resource_table.ResourceType.RT_ICON ] icon_resource_declarations = { rdecl.id: rdecl for rdecl in icon_rtt.resource_declarations } for resource_id, nefile_group_icon in self._group_icon_resources.items(): if isinstance(resource_id, str): resource_id_wrapper = ResourceID(raw_id=None, name=resource_id) else: resource_id_wrapper = ResourceID(resource_id) icon_infos = [] for nefile_icon_dir_entry in nefile_group_icon.icon_directory.directory_entries: grp_icon_dir_entry = self._convert_nefile_icon_dir_entry(nefile_icon_dir_entry) icon_rdecl = icon_resource_declarations[grp_icon_dir_entry.ID] offset = icon_rdecl.data_start_offset file_offset = offset + resources_offset icon_infos.append((grp_icon_dir_entry, offset, file_offset)) result.append((resource_id_wrapper, icon_infos)) return result def _extract_icon(self, index: int = 0, resource_id: int | str | None = None) -> ExtractedGroupIcon: if resource_id is not None: try: nefile_group_icon = self._group_icon_resources[resource_id] except KeyError: raise IconNotFoundError(f"No icon exists with resource ID {resource_id!r}") from None else: icons_by_index = list(self._group_icon_resources.values()) try: nefile_group_icon = icons_by_index[index] except IndexError: raise IconNotFoundError(f"No icon exists at index {index}") from None results = [] for nefile_icon_dir_entry in nefile_group_icon.icon_directory.directory_entries: resource_id = nefile_icon_dir_entry.icon_resource_id grp_icon_dir_entry = self._convert_nefile_icon_dir_entry(nefile_icon_dir_entry) icondata = nefile_group_icon.icons[resource_id].data results.append((grp_icon_dir_entry, icondata)) return results icoextract-0.3.1/icoextract/pe_extractor.py000066400000000000000000000167001526654623600211210ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT # Copyright (c) 2015-2016 Fadhil Mandaga # Copyright (c) 2019-2026 James Lu import ctypes import logging import os import pathlib import platform import pefile from .base_extractor import BaseIconExtractor from .exceptions import ( IconNotFoundError, InvalidIconDefinitionError, NoIconsAvailableError ) from .types import ( ExtractedGroupIcon, GroupIconDir, GroupIconDirEntry, GroupIconWithIconOffsets, ResourceID, ) logger = logging.getLogger("icoextract") class PEIconExtractor(BaseIconExtractor): """Win32/Win64 Portable Executable (PE) icon extractor.""" def __init__(self, filename=None, data=None): """ Loads an Win32/Win64 Portable Executable from the given `filename` or `data` (raw buffer). If both `filename` and `data` are given, `filename` takes precedence. If the executable has contains no icons, this will raise `NoIconsAvailableError`. """ # Use fast loading and explicitly load the RESOURCE directory entry. This saves a LOT of time # on larger files self._pe = pefile.PE(name=filename, data=data, fast_load=True) self._pe.parse_data_directories(pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE']) if not hasattr(self._pe, 'DIRECTORY_ENTRY_RESOURCE'): raise NoIconsAvailableError("File has no resources") # Reverse the list of entries before making the mapping so that earlier values take precedence # When an executable includes multiple icon resources, we should use only the first one. # pylint: disable=no-member resources = {rsrc.id: rsrc for rsrc in reversed(self._pe.DIRECTORY_ENTRY_RESOURCE.entries)} self._groupiconres = resources.get(pefile.RESOURCE_TYPE["RT_GROUP_ICON"]) if not self._groupiconres: if filename and platform.system() == "Windows": self._print_windows_usage_hint(filename) raise NoIconsAvailableError("File has no group icon resources") self._rticonres = resources.get(pefile.RESOURCE_TYPE["RT_ICON"]) self._group_icons = self._groupiconres.directory.entries self._group_icons_by_id: dict[int | str, pefile.ResourceDirEntryData] = {} for entry in self._group_icons: self._group_icons_by_id[entry.struct.Name] = entry # For resources with a string name, track them by both the string and the underlying # numerical index if entry.name: self._group_icons_by_id[str(entry.name)] = entry self._icons = {icon_entry_list.id: icon_entry_list.directory.entries[0] # Select first language for icon_entry_list in self._rticonres.directory.entries} def list_group_icons(self) -> list[tuple[ResourceID, GroupIconWithIconOffsets]]: results = [] for entry in self._group_icons: resource_id = ResourceID( raw_id=entry.struct.Name, name=str(entry.name) if entry.name else None ) grp_icon_resource = self._group_icons_by_id[entry.struct.Name] grp_icon_data_full = self._extract_from_pefile(grp_icon_resource, skip_data=True) # Return only the metadata and offsets grp_icon_data = [ (grp_icon_dir_entry, resource_offset, file_offset) for (grp_icon_dir_entry, _, resource_offset, file_offset) in grp_icon_data_full ] results.append((resource_id, grp_icon_data)) return results def _extract_from_pefile( self, resource_dir_entry_data: pefile.ResourceDirEntryData, skip_data: bool = False, ) -> list[tuple[GroupIconDirEntry, bytes, int, int]]: """ Returns the specified group icon in the binary. Result is a list of (group icon dir entry, icon data) tuples. """ icon_lang = None if resource_dir_entry_data.struct.DataIsDirectory: # Select the first language from subfolders as needed. resource_dir_entry_data = resource_dir_entry_data.directory.entries[0] icon_lang = resource_dir_entry_data.struct.Name logger.debug("Picking first language %s", icon_lang) # Read the data pointed to by the group icon directory (GRPICONDIR) struct. rva = resource_dir_entry_data.data.struct.OffsetToData grp_icon_data = self._pe.get_data(rva, resource_dir_entry_data.data.struct.Size) grp_icon_dir = GroupIconDir.from_buffer_copy(grp_icon_data) logger.debug("Group icon has %d images: %s", grp_icon_dir.Count, grp_icon_dir) # pylint: disable=no-member if grp_icon_dir.Reserved: # pylint: disable=no-member raise InvalidIconDefinitionError("Invalid group icon definition (got Reserved=%s instead of 0)" % hex(grp_icon_dir.Reserved)) # For each group icon entry (GRPICONDIRENTRY) that immediately follows, read the struct and look up the # corresponding icon image extracted_grp_icons = [] icon_offset = ctypes.sizeof(grp_icon_dir) for grp_icon_index in range(grp_icon_dir.Count): grp_icon_dir_entry = GroupIconDirEntry.from_buffer_copy(grp_icon_data, icon_offset) icon_offset += ctypes.sizeof(grp_icon_dir_entry) logger.debug("Got group icon entry %d: %s", grp_icon_index, grp_icon_dir_entry) icon_entry = self._icons[grp_icon_dir_entry.ID] logger.debug("Got icon data for ID %d: %s", grp_icon_dir_entry.ID, icon_entry.data.struct) icon_res_offset = icon_entry.data.struct.OffsetToData icon_file_offset = self._pe.get_offset_from_rva(icon_res_offset) icon_data = b'' if not skip_data: icon_data = self._pe.get_data(icon_res_offset, icon_entry.data.struct.Size) extracted_grp_icons.append((grp_icon_dir_entry, icon_data, icon_res_offset, icon_file_offset)) return extracted_grp_icons def _extract_icon(self, index: int = 0, resource_id: int | str | None = None) -> ExtractedGroupIcon: if resource_id is not None: try: resource_dir_entry_data = self._group_icons_by_id[resource_id] except KeyError: raise IconNotFoundError(f"No icon exists with resource ID {resource_id!r}") from None else: try: resource_dir_entry_data = self._group_icons[index] except IndexError: raise IconNotFoundError(f"No icon exists at index {index}") from None # Return only the GroupIconDirEntry metadata and icon bytes return [ (grp_icon_dir_entry, icon_data) for (grp_icon_dir_entry, icon_data, _, _) in self._extract_from_pefile(resource_dir_entry_data) ] @staticmethod def _print_windows_usage_hint(filename): path = pathlib.Path(filename) systemroot = pathlib.Path(os.getenv('SYSTEMROOT')) if path.is_relative_to(systemroot / 'System32') or \ path.is_relative_to(systemroot / 'SysWOW64'): mun_path = pathlib.Path(systemroot / 'SystemResources' / (path.name + '.mun')) if mun_path.is_file(): logger.warning( 'System DLL files in Windows 10 1903+ no longer contain icons. ' 'Try extracting from %s instead.', mun_path) icoextract-0.3.1/icoextract/scripts/000077500000000000000000000000001526654623600175335ustar00rootroot00000000000000icoextract-0.3.1/icoextract/scripts/__init__.py000066400000000000000000000000601526654623600216400ustar00rootroot00000000000000# Stub so that scripts/ is treated as a package icoextract-0.3.1/icoextract/scripts/extract.py000077500000000000000000000027221526654623600215650ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT # Copyright (c) 2019-2026 James Lu """ Windows executable icon extractor. """ import argparse import logging import os.path from icoextract import IconExtractor, logger, __version__ _WRONG_EXTENSIONS_HINT = {'.jpg', '.jpeg', '.png'} def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("-V", "--version", action='version', version=f'icoextract {__version__}') parser.add_argument("-n", "--num", type=int, help="index of icon to extract", default=0) parser.add_argument("-i", "--id", type=str, help="resource ID of icon to extract", default=None) parser.add_argument("-v", "--verbose", action="store_true", help="enables debug logging") parser.add_argument("input", help="input filename (.exe/.dll/.mun)") parser.add_argument("output", help="output filename (.ico)") args = parser.parse_args() if args.verbose: logger.setLevel(logging.DEBUG) extractor = IconExtractor(args.input) try: resource_id = int(args.id) except (ValueError, TypeError): resource_id = args.id extractor.export_icon(args.output, num=args.num, resource_id=resource_id) file_ext = os.path.splitext(args.output)[1].lower() if file_ext in _WRONG_EXTENSIONS_HINT: logger.warning('This tool outputs .ico files, not %s. The resulting file will have the wrong file extension.', file_ext) icoextract-0.3.1/icoextract/scripts/icolist.py000077500000000000000000000031401526654623600215540ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT # Copyright (c) 2019-2026 James Lu """ Lists group icons present in a program. """ import argparse import logging from icoextract import IconExtractor, logger, __version__ def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("-V", "--version", action='version', version=f'icoextract {__version__}') parser.add_argument("-v", "--verbose", action="store_true", help="enables debug logging") parser.add_argument("input", help="input filename") args = parser.parse_args() if args.verbose: logger.setLevel(logging.DEBUG) extractor = IconExtractor(args.input) for idx, entry in enumerate(extractor.list_group_icons()): resource_id, grp_icon_dir_entries = entry print(f"Group Icon Index: {idx} ", end='') print(f"ID: {resource_id}", end='') if resource_id.raw_id: print(f"({hex(resource_id.raw_id)})", end='') print(" ", end='') print(f"Count: {len(grp_icon_dir_entries)}") for (grp_icon_dir_entry, resource_offset, file_offset) in grp_icon_dir_entries: print(f" Icon ID: {grp_icon_dir_entry.ID}" # Width and Height are u8 values where 0 means 256 f" Width: {grp_icon_dir_entry.Width or 256}" f" Height: {grp_icon_dir_entry.Height or 256}" f" Resource Offset: {hex(resource_offset)}" f" File Offset: {hex(file_offset)}" f" Size: {grp_icon_dir_entry.BytesInRes}") icoextract-0.3.1/icoextract/scripts/thumbnailer.py000077500000000000000000000065411526654623600224300ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT # Copyright (c) 2019-2026 James Lu """ Linux (freedesktop.org) thumbnailer for Windows PE files (.exe/.dll) """ import argparse import logging import sys from PIL import Image from icoextract import IconExtractor, IconExtractorError, logger, __version__ def generate_thumbnail(inputfile, outfile, size=256, force_resize=False): """ Generates a thumbnail for an .exe file. inputfile: the input file path (%i) outfile: output filename (%o) size: determines the thumbnail output size (%s) """ try: extractor = IconExtractor(inputfile) except RuntimeError: logger.debug("Failed to extract icon for %s:", inputfile, exc_info=True) sys.exit(1) data = extractor.get_icon() im = Image.open(data) # Open up the .ico from memory if force_resize: logger.debug("Force resizing icon to %dx%d", size, size) im = im.resize((size, size)) else: if size > 256: logger.warning('Icon sizes over 256x256 are not supported') size = 256 elif size not in (128, 256): logger.warning('Unsupported size %d, falling back to 128x128', size) size = 128 if size == 128: # If large size thumbnail wasn't requested but one is available, pick an 128x128 icon if available; # otherwise scale down from 256x256 to 128x128. 128x128 is the largest resolution allowed for # "normal" size thumbnails. if (128, 128) in im.info['sizes']: logger.debug("Using native 128x128 icon") im.size = (128, 128) elif im.size > (128, 128): logger.debug("Downsizing icon to 128x128") im = im.resize((128, 128)) # For legacy apps, try to export the icon with the highest size & bit depth # Some programs (e.g. Internet Explorer 6) put a 16 color icon earlier in the file if im.size <= (48, 48): ico_frames = list(enumerate(im.ico.entry)) best_frame_idx, ico_header = max(ico_frames, key=lambda pair: (pair[1].square, pair[1].color_depth)) logger.debug("Legacy app: selecting icon %s", ico_header) im = im.ico.frame(best_frame_idx) logger.debug("Writing size %s thumbnail for %s to %s", size, inputfile, outfile) im.save(outfile, "PNG") def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("-V", "--version", action='version', version=f'exe-thumbnailer, part of icoextract {__version__}') parser.add_argument("-s", "--size", type=int, help="size of desired thumbnail", default=256) parser.add_argument("-v", "--verbose", action="store_true", help="enables debug logging") parser.add_argument("-f", "--force-resize", action="store_true", help="force resize thumbnail to the specified size") parser.add_argument("inputfile", help="input file name (.exe/.dll/.mun)") parser.add_argument("outfile", help="output file name (.png)") args = parser.parse_args() if args.verbose: logger.setLevel(logging.DEBUG) try: generate_thumbnail(args.inputfile, args.outfile, size=args.size, force_resize=args.force_resize) except IconExtractorError as e: logger.error("Failed to thumbnail %s: %s", args.inputfile, e) logger.debug("Backtrace:", exc_info=True) icoextract-0.3.1/icoextract/types.py000066400000000000000000000040071526654623600175630ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT # Copyright (c) 2019-2026 James Lu """ icoextract common types. See https://devblogs.microsoft.com/oldnewthing/20120720-00/?p=7083 for a reference on ICO structures. """ import ctypes BYTE = ctypes.c_uint8 WORD = ctypes.c_uint16 DWORD = ctypes.c_uint32 class ResourceID: """Resource ID wrapper. For resources with a string ID, str() will return its string value (e.g. "IDI_MAIN_ICON") and int() will return its underlying numeric ID. Numerical icon resource IDs can be accessed via int(), and str() will return the number casted to a string. """ def __init__(self, raw_id: int | None = None, name: str | None = None): if raw_id is None and name is None: raise ValueError("raw_id and name cannot both be None") self.raw_id = raw_id self.name = name def __str__(self): return self.name or str(self.raw_id) def __int__(self): if self.raw_id is not None: return self.raw_id raise ValueError(f"Resource ID {self.name!r} cannot be converted to an int") def __repr__(self): if self.name: return f'{self.__class__.__name__}({self.name!r})' return f'{self.__class__.__name__}({self.raw_id})' class GroupIconDirEntry(ctypes.LittleEndianStructure): """ICO GRPICONDIRENTRY / RESDIR structure""" _pack_ = 1 # Ensures tight packing (no padding) _fields_ = [ ("Width", BYTE), ("Height", BYTE), ("ColorCount", BYTE), ("Reserved", BYTE), ("Planes", WORD), ("BitCount", WORD), ("BytesInRes", DWORD), ("ID", WORD), ] class GroupIconDir(ctypes.LittleEndianStructure): """ICO GRPICONDIR / NEWHEADER structure""" _pack_ = 1 _fields_ = [ ("Reserved", WORD), ("Type", WORD), ("Count", WORD), ] ExtractedGroupIcon = list[tuple[GroupIconDirEntry, bytes]] GroupIconWithIconOffsets = list[tuple[GroupIconDirEntry, int, int]] icoextract-0.3.1/icoextract/version.py000066400000000000000000000000261526654623600201010ustar00rootroot00000000000000__version__ = '0.3.1' icoextract-0.3.1/pyproject.toml000066400000000000000000000027111526654623600166140ustar00rootroot00000000000000[build-system] requires = ["setuptools>=61.0.0"] build-backend = "setuptools.build_meta" [project] name = "icoextract" description = "Windows EXE icon extractor" readme = "README.md" requires-python = ">=3.10" authors = [ { name = "James Lu", email = "james@overdrivenetworks.com" } ] license = "MIT" license-files = ["LICENSE"] classifiers = [ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Intended Audience :: End Users/Desktop', 'Topic :: Software Development :: Libraries :: Python Modules', 'Operating System :: OS Independent', 'Operating System :: POSIX', 'Programming Language :: Python :: 3 :: Only', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: 3.13', 'Programming Language :: Python :: 3.14', ] dependencies = [ "pefile", ] dynamic = ["version"] [project.optional-dependencies] thumbnailer = ["Pillow"] win16 = ["nefile"] dev = [ "icoextract[thumbnailer]", "icoextract[win16]", "pdoc", ] [project.scripts] icoextract = "icoextract.scripts.extract:main" icolist = "icoextract.scripts.icolist:main" exe-thumbnailer = "icoextract.scripts.thumbnailer:main [thumbnailer]" [tool.setuptools.packages.find] include = ["icoextract", "icoextract.*"] [tool.setuptools.dynamic] version = { attr = "icoextract.version.__version__" } [tool.pylint] max-line-length = 120 icoextract-0.3.1/tests/000077500000000000000000000000001526654623600150415ustar00rootroot00000000000000icoextract-0.3.1/tests/.gitignore000066400000000000000000000000501526654623600170240ustar00rootroot00000000000000tmp*.* *.ico *.exe testapp.rc *.o *.res icoextract-0.3.1/tests/Makefile000066400000000000000000000113601526654623600165020ustar00rootroot00000000000000CFLAGS=-mwindows -g MINGW64=x86_64-w64-mingw32- MINGW32=i686-w64-mingw32- .PHONY: all win64 win32 win16 clean WATCOM ?= /opt/watcom WCL_EXE := $(shell command -v wcl 2>/dev/null) DOCKER_EXE := $(shell command -v docker 2>/dev/null) WCL_FLAGS := -k -bt=windows -ml -l=windows WRC_FLAGS := -q -bt=windows # OpenWatcom is not available in most distros. For convenience, this will try a # community Docker image if it is not already installed ifneq ($(WCL_EXE),) WCL = INCLUDE="$(WATCOM)/h:$(WATCOM)/h/win" wcl $(WCL_FLAGS) WRC = INCLUDE="$(WATCOM)/h:$(WATCOM)/h/win" wrc $(WRC_FLAGS) else ifneq ($(DOCKER_EXE),) OPENWATCOM_DOCKER = docker run --rm \ -u "$(shell id -u):$(shell id -g)" \ --network none \ -v $(CURDIR):/src \ -e INCLUDE="$(WATCOM)/h:$(WATCOM)/h/win" \ ghcr.io/arlaneenalra/watcom-docker WCL = $(OPENWATCOM_DOCKER) wcl $(WCL_FLAGS) WRC = $(OPENWATCOM_DOCKER) wrc $(WRC_FLAGS) endif # Win16 is excluded by default as it requires a different compiler all: win64 win32 win64: testapp64.exe testapp64-nores.exe testapp64-noicon.exe \ testapp64-with128.exe testapp64-with192.exe \ testapp64-string-res.exe win32: testapp32.exe testapp32-nores.exe testapp32-noicon.exe \ testapp32-smallonly.exe testapp32-bpp.exe win16: testapp16.exe testapp16-nores.exe testapp16-noicon.exe tmp-testapp-16.bmp: testapp.png convert testapp.png -resize 16x16 tmp-testapp-16.bmp tmp-testapp-32.bmp: testapp.png convert testapp.png -resize 32x32 tmp-testapp-32.bmp tmp-testapp-48.bmp: testapp.png convert testapp.png -resize 48x48 tmp-testapp-48.bmp # icon with standard sizes: 16x16, 32x32, 48x48, 256x256 testapp.ico: testapp.png tmp-testapp-16.bmp tmp-testapp-32.bmp tmp-testapp-48.bmp convert testapp.png tmp-testapp*.bmp testapp.ico # Small icon (only up to 48x48) testapp-smallonly.ico: testapp.png tmp-testapp-16.bmp tmp-testapp-32.bmp tmp-testapp-48.bmp convert tmp-testapp-*.bmp testapp-smallonly.ico # All standard sizes + 128x128 testapp-with128.ico: testapp.png tmp-testapp-16.bmp tmp-testapp-32.bmp tmp-testapp-48.bmp convert testapp.png -resize 128x128 tmp-testapp-128.png convert testapp.png tmp-testapp*.bmp tmp-testapp*.png testapp-with128.ico # All small sizes + 128x128 + 192x192 (excluding 256x256) testapp-with192.ico: testapp-with128.ico convert testapp.png -resize 192x192 tmp-testapp-192.png convert tmp-testapp*.bmp tmp-testapp-128.png tmp-testapp-192.png testapp-with192.ico # $(1) = Toolchain prefix # $(2) = Base output name (e.g., testapp64-with128) define build-mingw-target = $(1)windres tmp-$(2).rc -O coff -o $(2).o $(1)gcc $(CFLAGS) -o $(2).exe testapp.c $(2).o endef # Build with (optional) icon + version resource # $(1) = architecture (64, 32, or empty for both) # $(2) = resource ID (defaults to 2) define build-testapp = $(eval ICON_FILE := $(filter %.ico,$^)) $(eval APP_NAME := $(basename $@)) $(eval RC_NAME := tmp-$(APP_NAME).rc) cp testapp-base.rc $(RC_NAME) $(if $(ICON_FILE), \ echo "$(if $(2),$(2),2) ICON $(ICON_FILE)" >> $(RC_NAME) \ ) $(if $(filter 64 $(1),$(if $(1),$(1),64)), \ $(call build-mingw-target,$(MINGW64),$(APP_NAME)) \ ) $(if $(filter 32 $(1),$(if $(1),$(1),32)), \ $(call build-mingw-target,$(MINGW32),$(APP_NAME)) \ ) endef testapp64.exe testapp32.exe: testapp.c testapp.ico $(call build-testapp,,) testapp32-smallonly.exe: testapp.c testapp-smallonly.ico $(call build-testapp,32,) testapp64-with128.exe: testapp.c testapp-with128.ico $(call build-testapp,64,) testapp64-with192.exe: testapp.c testapp-with192.ico $(call build-testapp,64,) testapp64-string-res.exe: testapp.c testapp.ico $(call build-testapp,32,MY_APP_ICON) # Build with only version resource testapp64-noicon.exe testapp32-noicon.exe: testapp.c $(call build-testapp,,) # Build with no resource info at all testapp64-nores.exe testapp32-nores.exe: testapp.c $(MINGW64)gcc $(CFLAGS) -o testapp64-nores.exe testapp.c $(MINGW32)gcc $(CFLAGS) -o testapp32-nores.exe testapp.c ## BPP tests (monochrome et al.) testapp-bpp.ico: bmp-monochrome.bmp bmp-color.bmp convert bmp-monochrome.bmp bmp-color.bmp testapp-bpp.ico testapp32-bpp.exe: testapp-bpp.ico testapp.c $(call build-testapp,32,3) ## Win16 testapp16-nores.exe: testapp.c $(WCL) testapp.c -fe=testapp16-nores.exe -fo=testapp16.o testapp16-noicon.exe: testapp16-nores.exe cp testapp16-nores.exe tmp-testapp16-noicon.exe $(WRC) testapp16-base.rc tmp-testapp16-noicon.exe mv tmp-testapp16-noicon.exe testapp16-noicon.exe testapp16.exe: testapp16-nores.exe testapp-bpp.ico cp testapp16-base.rc tmp-testapp16.rc echo 'IDI_APPICON ICON "testapp-bpp.ico"' >> tmp-testapp16.rc cp testapp16-nores.exe tmp-testapp16.exe $(WRC) tmp-testapp16.rc tmp-testapp16.exe mv tmp-testapp16.exe testapp16.exe clean: $(RM) tmp*.* *.ico *.exe *.o *.res icoextract-0.3.1/tests/README.md000066400000000000000000000014141526654623600163200ustar00rootroot00000000000000# Tests for icoextract ## Basic tests To compile these tests you need MinGW (x86_64 + i686) and imagemagick. On Debian/Ubuntu this is `apt install gcc-mingw-w64 imagemagick`. ```bash make python3 -m unittest discover . ``` ## Win16 tests To compile the binaries for `test_win16.py` you need the OpenWatcom C compiler (wcl, wrc). This target is not enabled by default as OpenWatcom is not available in most distros. If Docker is installed and wcl/wrc are not, the Makefile will try to build the Win16 test files with a public OpenWatcom Docker image instead. ``` bash make win16 python3 -m unittest discover . ``` ## 3rd Party Credits The `testapp.png` icon file is sourced from the public domain [Tango icon theme](http://tango-project.org/) (`internet-web-browser.svg`). icoextract-0.3.1/tests/__init__.py000066400000000000000000000000401526654623600171440ustar00rootroot00000000000000# stub to make tests/ a package icoextract-0.3.1/tests/bmp-color.bmp000066400000000000000000000060661526654623600174430ustar00rootroot00000000000000BM6 6(    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªªªªªªªªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªªªªªªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªªªªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿicoextract-0.3.1/tests/bmp-monochrome.bmp000066400000000000000000000002761526654623600204700ustar00rootroot00000000000000BM¾>( €  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü?ÿÿðÿÿàÿÿÁ‡ÿÿÇãÿÿÇáÿýÇñÿðÇñÿáÃñÿããñÿÃðÿÇøþÇüüGÿÿøÇÿÿñãçÿãðÿÃøÿ�üÿÿÿÿ¼{ÿÿþ1ÿÿÿÿÿÿ‡ÿÿÿ�ÿÿÿÿÿþ?ÿÿÿÿÿÿÿÿÿicoextract-0.3.1/tests/test_extract.py000077500000000000000000000146541526654623600201410ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT # Copyright (c) 2019-2026 James Lu import filecmp import os.path import unittest try: import nefile except ImportError: nefile = None import icoextract class _BaseIconExtractorTestCase(unittest.TestCase): def get_test_file(self, filename): # Read/write test files in tests/ folder, regardless of where working directory is tests_dir = os.path.dirname(__file__) return os.path.join(tests_dir, filename) def _test_extract(self, infile, compare_against=None, **kwargs): """ Wrapper to test extracting a single icon from infile, and comparing the output with an existing .ico file """ inpath = self.get_test_file(infile) ie = icoextract.IconExtractor(inpath) outpath = self.get_test_file(f"tmp-extract-{infile}.ico") ie.export_icon(outpath, **kwargs) assert compare_against, \ "Successful extractions should have a file to compare against" compare_against = self.get_test_file(compare_against) self.assertTrue(filecmp.cmp(outpath, compare_against), f"{outpath} and {compare_against} should be equal") return ie class PEIconExtractorTestCase(_BaseIconExtractorTestCase): def test_basic(self): """Test basic extraction cases""" for app in ["testapp64.exe", "testapp32.exe"]: with self.subTest(app=app): ie = self._test_extract(app, "testapp.ico") # Nonexistent icon index with self.assertRaises(icoextract.IconNotFoundError): self._test_extract(app, num=10) def test_list(self): """Test list_group_icons() behaviour""" for app in ["testapp64.exe", "testapp32.exe"]: with self.subTest(app=app): inpath = self.get_test_file(app) ie = icoextract.IconExtractor(inpath) icon_list = ie.list_group_icons() self.assertEqual(len(icon_list), 1) resource_id, grp_icons_with_offsets = icon_list[0] self.assertEqual(int(resource_id), 2) # ID self.assertEqual(len(grp_icons_with_offsets), 4) # number of icons expected_sizes = [(0, 0), (16, 16), (32, 32), (48, 48)] real_sizes = [ (grp_icons_dir_entry.Width, grp_icons_dir_entry.Height) for (grp_icons_dir_entry, resource_offset, file_offset) in grp_icons_with_offsets ] self.assertCountEqual(expected_sizes, real_sizes) def test_no_icons(self): """Test that NoIconsAvailableError is raised when the input binary has no icons""" cases = [ # App has only version resource "testapp64-noicon.exe", "testapp32-noicon.exe", # App has no resource info at all "testapp32-nores.exe", "testapp32-nores.exe" ] for app in cases: with self.subTest(app=app): with self.assertRaises(icoextract.NoIconsAvailableError): self._test_extract(app) def test_fd_as_input(self): """Test passing binary input into IconExtractor directly""" tests_dir = os.path.dirname(__file__) with open(os.path.join(tests_dir, "testapp64.exe"), 'rb') as f: ie = icoextract.IconExtractor(data=f.read()) self.assertEqual(len(ie.list_group_icons()), 1) def test_extract_icon_id(self): """Test extracting an icon by its resource ID""" self._test_extract("testapp64.exe", "testapp.ico", resource_id=2) # ID does not exist with self.assertRaises(icoextract.IconNotFoundError): self._test_extract("testapp64.exe", resource_id=1337) # ID is not an icon with self.assertRaises(icoextract.IconNotFoundError): self._test_extract("testapp64.exe", resource_id=1) def test_extract_icon_id_string(self): """Test extracting an icon with a string resource ID""" # Default index should pick up the icon self._test_extract("testapp64-string-res.exe", "testapp.ico") ie = self._test_extract("testapp64-string-res.exe", "testapp.ico", resource_id="MY_APP_ICON") # ID does not exist with self.assertRaises(icoextract.IconNotFoundError): self._test_extract("testapp64.exe", resource_id="nonexistent") with self.assertRaises(icoextract.IconNotFoundError): self._test_extract("testapp64.exe", resource_id=5) icon_list = ie.list_group_icons() self.assertEqual(len(icon_list), 1) self.assertEqual(str(icon_list[0][0]), "MY_APP_ICON") self.assertGreater(int(icon_list[0][0]), 0) @unittest.skipUnless(nefile, "nefile library is not installed") class NEIconExtractorTestCase(_BaseIconExtractorTestCase): def test_win16_basic(self): """Test basic extraction cases""" self._test_extract("testapp16.exe", "testapp-bpp.ico") self._test_extract("testapp16.exe", "testapp-bpp.ico", resource_id="IDI_APPICON") # Invalid IDs with self.assertRaises(icoextract.IconNotFoundError): self._test_extract("testapp64.exe", resource_id=1337) with self.assertRaises(icoextract.IconNotFoundError): self._test_extract("testapp64.exe", resource_id="FOO") def test_win16_no_icons(self): """Test programs with no icons""" with self.assertRaises(icoextract.NoIconsAvailableError): self._test_extract("testapp64-noicon.exe") with self.assertRaises(icoextract.NoIconsAvailableError): self._test_extract("testapp64-nores.exe") def test_list(self): """Test that list_group_icons() returns the right metadata""" inpath = self.get_test_file("testapp16.exe") ie = icoextract.IconExtractor(inpath) icon_list = ie.list_group_icons() self.assertEqual(len(icon_list), 1) resource_id, grp_icons_with_offsets = icon_list[0] self.assertEqual(str(resource_id), "IDI_APPICON") # ID self.assertEqual(len(grp_icons_with_offsets), 2) # number of icons for (grp_icons_dir_entry, _, _) in grp_icons_with_offsets: self.assertEqual(grp_icons_dir_entry.Width, 32) self.assertEqual(grp_icons_dir_entry.Height, 32) if __name__ == '__main__': unittest.main() icoextract-0.3.1/tests/test_resource_id.py000077500000000000000000000017641526654623600207700ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT # Copyright (c) 2019-2026 James Lu import unittest import icoextract.types class ResourceIDTestCase(unittest.TestCase): def test_num_resource_id(self): numeric_id = icoextract.types.ResourceID(123) self.assertEqual(int(numeric_id), 123) self.assertEqual(str(numeric_id), '123') def test_string_resource_id(self): string_id = icoextract.types.ResourceID(456, 'MY_APP_ICON') self.assertEqual(int(string_id), 456) self.assertEqual(str(string_id), 'MY_APP_ICON') def test_error(self): with self.assertRaises(ValueError): icoextract.types.ResourceID(None, None) def test_string_only_id(self): str_only_id = icoextract.types.ResourceID(None, 'MY_APP_ICON') self.assertEqual(str(str_only_id), 'MY_APP_ICON') with self.assertRaises(ValueError): int(str_only_id) if __name__ == '__main__': unittest.main() icoextract-0.3.1/tests/test_thumbnailer.py000077500000000000000000000131771526654623600210000ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT # Copyright (c) 2019-2026 James Lu import os.path import unittest from PIL import Image, ImageChops from icoextract.scripts.thumbnailer import generate_thumbnail TESTS_DIR = os.path.dirname(__file__) COMPARE_LENGTH = 1024 # for efficiency class ThumbnailerTestCase(unittest.TestCase): def _generate_thumbnail(self, infile, outfile, **kwargs): infile_path = os.path.join(TESTS_DIR, infile) outfile_path = os.path.join(TESTS_DIR, outfile) generate_thumbnail(infile_path, outfile_path, **kwargs) return outfile_path def _compare_equal(self, im, orig): with Image.open(os.path.join(TESTS_DIR, orig)) as im_orig: self.assertEqual(im.size, im_orig.size, "Images are not the same size") self.assertEqual(im.mode, im_orig.mode, "Images are not the same size") diff = ImageChops.difference(im, im_orig) self.assertIsNone(diff.getbbox(), "Diff should be empty") def test_thumbnailer_normal(self): outfile = self._generate_thumbnail("testapp64.exe", "tmp-thumbnail-test-normal.png", size=128) with Image.open(outfile) as im: self.assertEqual(im.width, 128) self.assertEqual(im.height, 128) def test_thumbnailer_large(self): outfile = self._generate_thumbnail("testapp64.exe", "tmp-thumbnail-test-large.png", size=256) with Image.open(outfile) as im: self.assertEqual(im.width, 256) self.assertEqual(im.height, 256) self._compare_equal(im, "testapp.png") def test_thumbnailer_with128_large(self): outfile = self._generate_thumbnail("testapp64-with128.exe", "tmp-thumbnail-test-with-128-large.png", size=256) with Image.open(outfile) as im: self.assertEqual(im.width, 256) self.assertEqual(im.height, 256) self._compare_equal(im, "testapp.png") def test_thumbnailer_with128_normal(self): outfile = self._generate_thumbnail("testapp64-with128.exe", "tmp-thumbnail-test-with-128-normal.png", size=128) with Image.open(outfile) as im: self.assertEqual(im.width, 128) self.assertEqual(im.height, 128) self._compare_equal(im, "tmp-testapp-128.png") def test_thumbnailer_smallonly(self): outfile = self._generate_thumbnail("testapp32-smallonly.exe", "tmp-thumbnail-test-smallonly.png", size=128) with Image.open(outfile) as im: self.assertEqual(im.width, 48) self.assertEqual(im.height, 48) self._compare_equal(im, "tmp-testapp-48.bmp") def test_thumbnailer_force_resize(self): outfile = self._generate_thumbnail("testapp32-smallonly.exe", "tmp-thumbnail-force-resize.png", size=128, force_resize=True) with Image.open(outfile) as im: self.assertEqual(im.width, 128) self.assertEqual(im.height, 128) def test_192_normal(self): """Test that exe files with oddly sized icons (192x192) are wrapped to the expected dimensions""" outfile = self._generate_thumbnail("testapp64-with192.exe", "tmp-thumbnail-192-normal.png", size=128) with Image.open(outfile) as im: self.assertEqual(im.width, 128) self.assertEqual(im.height, 128) self._compare_equal(im, "tmp-testapp-128.png") def test_192_large(self): """Test that exe files with oddly sized icons (192x192) are wrapped to the expected dimensions""" outfile = self._generate_thumbnail("testapp64-with192.exe", "tmp-thumbnail-192-large.png", size=256) with Image.open(outfile) as im: self.assertEqual(im.width, 192) self.assertEqual(im.height, 192) def test_unsupported_output_size_too_large(self): """Test an invalid requested icon size (> 256)""" outfile = self._generate_thumbnail("testapp64.exe", "tmp-thumbnail-test-unsupported-size-too-large.png", size=300) with Image.open(outfile) as im: self.assertEqual(im.width, 256) self.assertEqual(im.height, 256) self._compare_equal(im, "testapp.png") def test_unsupported_output_size_too_small(self): """Test an invalid requested icon size (< 128)""" outfile = self._generate_thumbnail("testapp64-with128.exe", "tmp-thumbnail-test-unsupported-size-too-small.png", size=64) with Image.open(outfile) as im: self.assertEqual(im.width, 128) self.assertEqual(im.height, 128) self._compare_equal(im, "tmp-testapp-128.png") def test_unsupported_output_size_between(self): """Test an invalid requested icon size (> 128, < 256)""" outfile = self._generate_thumbnail("testapp64-with128.exe", "tmp-thumbnail-test-unsupported-size-between.png", size=200) with Image.open(outfile) as im: self.assertEqual(im.width, 128) self.assertEqual(im.height, 128) self._compare_equal(im, "tmp-testapp-128.png") def test_color_depth_sort(self): """ Test that exe-thumbnailer prefers outputting the icon with the highest bit depth if there are multiple images with the same size. """ outfile = self._generate_thumbnail("testapp32-bpp.exe", "tmp-thumbnail-test-bpp.png") with Image.open(outfile) as im: self.assertEqual(im.width, 32) self.assertEqual(im.height, 32) rgb_img = im.convert("RGB") # The input file has no transparency self._compare_equal(rgb_img, "bmp-color.bmp") if __name__ == '__main__': unittest.main() icoextract-0.3.1/tests/testapp-base.rc000066400000000000000000000007721526654623600177650ustar00rootroot00000000000000#include 1 VERSIONINFO FILEVERSION 0,1,0,0 PRODUCTVERSION 0,1,0,0 FILEOS VOS_NT FILETYPE VFT_APP { BLOCK "StringFileInfo" { BLOCK "040904b0" { VALUE "CompanyName", "The icoextract authors" VALUE "FileDescription", "icoextract Test Application" VALUE "FileVersion", "0.1.0" VALUE "InternalName", "testapp" VALUE "ProductName", "icoextract" VALUE "OriginalFilename", "testapp.exe" VALUE "ProductVersion", "0.1.0" } } BLOCK "VarFileInfo" { VALUE "Translation", 0x0409, 1252 } } icoextract-0.3.1/tests/testapp.c000066400000000000000000000005461526654623600166720ustar00rootroot00000000000000#include #ifdef __WATCOMC__ /* Windows 3.x (Win16) */ int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) #else int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) #endif { MessageBox(NULL, "Hello world!", "Test Application", MB_ICONASTERISK); return 0; } icoextract-0.3.1/tests/testapp.png000066400000000000000000001067251526654623600172420ustar00rootroot00000000000000‰PNG  IHDR\r¨fƒiCCPICC profile(‘}‘=HÃ@Å_S¥R*"v�â�¡:YqÔ*¡B¨Zu0¹ô š’GÁµààÇbÕÁÅYWWAüqquRt‘ÿ—ZÄxpÜ�w÷wï¡QešÕ5hºmfRI1—_C¯#ýˆËÌ2f%) ßñu�_ï<ËÿÜŸ£W-X ˆÄ3Ì0mâuâ©MÛà¼OeeY%>'5é‚Ä�\W<~ã\rYà™Q3›™#Ž‹¥V:˜•M�x’8®j:å 9�UÎ[œµj�µîÉ_)èËK\§9„° "ÔPA6´ê¤XÈÐ~ÒÇsý¹rUÀÈ1� h�]?øüîÖ*NŒ{I‘$Ðýâ8Ã@hhÖçûØqš'@ð¸ÒÛþ�0ýIz½­Å�€¾mà⺭){Àå0ødȦìJAšB±¼ŸÑ7å��[ ¼êõÖÚÇé�¥®Ò7ÀÁ!0R¢ì5Ÿw÷tööï™V?Kr—¥ÝñbKGDÿÿÿ ½§“ pHYs × ×B(›xtIMEã 8QO#6‹ÓIDATxÚì�w|յǿwf›vµZuÉ*–,÷nãnW0`:„–@€Ð )„— )/í…šH�„¡WSlƒ�ÁlÀ÷nõ¶uîûcÛÌ쬚å"ØãÏZ»ÓwvÎïœó;çž ÉHF2’‘Œd$#ÉHF2’‘Œd$#ÉHF2’‘Œd$#ÉHF2’‘Œd$#ÉHF2’‘Œd$#ÉHF2’‘Œd$#ÉHF2’‘Œd$#ÉHF2’‘Œd$#ÉHF2’‘Œd$#ÉHFŽ¢ˆÌ-øtʬY×äE"¶j)e¥¢P %RŠB!d!HˆÜئN!p›‹)¥?ö¡d#PÊ N­N±+’[V®¼§.s·3�‘£ £GßêÈË«©ir¬”r(ä”ÕBP øŽÌUÈ[An•’ ÀûÀû.WÛú¥KÿäÏüJÈHŸÈ­ÊŒGK)g ¡Ì9Ø�Ñ �5R²ä²ÊÊýï=òÈ#‘Ìo™€ŒtCákk‰p¢bÈ™@N?ÿR­ ÞB¾ò¹òòý+3€�€ŒÄdÒ¤+ Çç¤d‘|(þ”åz)yä³RÚž}ë­;öež‚ |¦dêÔë l6å,)åyÀ<@íËãÛí*¹y9äøÈ+Ì%//‡œÜlÜž,²\„ª D"Z$B{G)5:Ú47µÐØÐLS}3õu�45´ …ûúVhÀëBðÏHDýw 2ði¶ôn»Ý~®Êù jïpØ:ªŠ£SVQDqq.ùù9äùí½Ê¾/¿ ‚Z½ú¾öÌÓ•€cÙÍ÷9ŽK�oå]m?oÞ&LBEE!55eùè„ F@U•«�¢æ1e€ª ìªÀ¦(Šˆ*xw%ŽÐÔð#h2éú›A éÈÔðÁdìX‘ˆÆŽ]u¬[»‰·—¿Ç¶M; ÞI7¤IJùg!"?[±â·»2O[Ž™>ý+ÕŠù¦”ò0WØYËùçÏcÉ’3£?ˆIS›P�žTÅ×é‹D^§ô}!š&éFð‡4¡HzN@ÊA&>ËN—K �­¬yw+^y›ÍoïÉ%„�DÄÏÞzëÎ�™§/GM¦M[R¡ªü�”òrÀÑ�}j†”só-1vd¥5¦i´¶´ÒNWV 6E`Sªª XüŠ=´¨]ÊÁ¦v¶ °e_ª"p;UܷӆǥâqÚ&¾AZz ÉåFî! uõ-¬yçc^}f{víïîå…¥ä¯`ûÁ›oþzkæiÌÀ“ã�_R ñ5!ä @V§ _3€3Ψå¸ã†SQYˆÃn³°º~Ú;:ðû ÀápáÍñ¡ª1¥×i|_+zg"¥ä;w>BþÀQØ΄kÞ,>��œ,;yùÙTE¤*~¯ MJ¶m?À²¥ï²ü¥·w/íñ°ª†o}ã�ß|’y:3pØdîÜK]~¿÷&!ä-@vgÛ^É¿ø9æÎŸââ'žÜPˆ¶övÚÛ;�Rb³;°;œxÜY8ìjâ‡:’ o–e+V²äk·pþ5ß!@5ÁˆF0¤%bû(_ aA®ÇN¡×I�×I±Ï‰Ó®@À’ë�’¶ö«V~ÈóÿYJý�†î\j‡”ò—ííY?]»öm™§5}ç/Y,„ü5PÓÙvÇ7”%KÎdøðJKk‡ CtøýƒAEÁîpápe¡( š&Év©ÇÌ÷¾ù;·òá¦í\ËO ¯iH£=¦#¡-&ÑLJ. ‘ë±S’ë¢$7‹b_¬FAR¢Å=i$5Mãã�¶óÌcKÙôá–î\òn!¸mùòÂàV-óäfà�¤¶öºÑš&îætµíç/ZÈ ×.N±øáp˜æ–VB¡RJ„¢ ª6ì'6»ƒÆö0{›‚8l‚1åžcæ»×Õ×sÚ9qâ2}Ή ¯éYÿX8Ð ÓÚ¢¹=L I(p|}œ6•ŠÂ,* =û\qjÑPW`ô’áÂ'Ÿìæ©¿ÂÇk»Åý½%¥¼öÍ7ïy'óg Ç2iÒ•v§Ó~“”â6ÀÙÙ¶ãŽÎ5ןňÁ¥$‘HUU±©*‘H„Ʀfœ.7ªÍŽ¢(´ø#ìj°¯)ˆ?ÍÁÛTAEV;†U÷àÏ{˜ûÿønþñ½¸²Ü …Nq%†5šÛC´´‡èE’aBbÛè{—]¡²ÐMM‰—<¯#©ø:^@ÆÏ[§IÉ'Ÿìæ¿ÿ|�ÍvÉý…¥”÷º\òÛK—ÞÛšyª3Ð-™1㆙ ÝŒêl»a#«¹á¦Ï3r耄Õ�D"DÂ!"‘0Z$‚¦i¡�åÉF …½�Av6ij·&¸üíìýhœ6Ÿ¢Â‚£v¤”œwÑ—((«áÜK®MZc™tÏÍq¼frý¥Œ¦Ú‚4µ…Ð4ÍÈè!/ÛÁàR/ÕÅÙØm"…+0ŸSJɆ ;xâ¡çÙ±¥Ë²€MBpÕòåw¿œyº3�V¢|ò'À�€’n;_®—›¾u1µÓ†£(]ßζ@˜õ!ö4 [Wö «ÄÇ® ön~�¡ÅYÔΜvTîÅêw×pÝ�ßàʯ~�ê¡#�.¹¥BZ­O‚BD“4¶…hh Dë ,¶‘R¢(‚êâlFTä�ãv΃î<ñ}´ˆÆÛ+?䉇ž¡µ¹ÓBA ÜßÖæºé³NªU·"ù®ˆg€3Ó�¤ª*|áòSùá¾DMuq´Ú.¶±"ªM×i1oñGØ´ÏÏG{:höG‹j„¯xž_!Ïc#?ÛNYY9AÅÅ Ï>Í�š*œNç½÷=ð'šÚüœ|ÖE:NCèîŒ0þ©ë¥ÎæY•\�§]�UKšA II}K€ »šÙר�Ó®âuÛiì–‚²Š"jçMÁ‘íaËÇ[Ñ4™ÎðMr8ÂTVN_µsç[;2@FÄôé×_/?\é6š0i·|û dcSRJB EQp¨ÆJ¼÷Öo¢Õ^Bc{بiD¯K!Û¥Z<è’uï¾Åð�L?îˆÜ�ææŸ} N=‡9 ˆ¸dìµÂèÜù¸ÞÅ]|ý{sü/¥¤±-È�f?¡pÌÐdjzQB¾×Á¸ê|Ê ²LÞGŒ“� !‘ìßßÀ?ÿô$[>î”ß[±¢ðgŸÅLAƈÉܹ_É0`ò#Bp#`³ÚÆétrË÷/ãª+O!Ûíª¢ (‚€?€ÃnO)Òy÷Ýw).€×íÄnSPDdÜ&_Š"ÈɲQèµã²+o ù¨ ±=›Ë^eôÈáië úJòiÞ|kç^r NW–ÉÀÇ-{ôZe:Û»ÆèX¡ pDb½Ón#×í@þ`™r¤èçö@˜-ûZÙ]ߎˮâó¤/ºt{\Lž1–¼’6¸…p8’NN¨¬l?~РiÏlß¾ò35È(ãÓ¦]3NQÔCôË r¨¬,fçÎŒ7„›¾v>¯Ñ1°«E�‡#–×–]�jkìh±å@‡M�ë¶aWE'!«‰'�’‘e.TE9¬÷æ’+®Æé-ä WÝdÊËë­º‘±7“q�ÀXä# ÛiºýýÁ{Úé„ œ€&£–=é=HJr³˜9²˜’\­þ0Mî$/ÑÔØÊ£>Çúw?L¯ ‚RŠsV¬¸ke>#2cÆ’ @Þ¤$ݯ»ît.¼pA"¾Œh’ˆÉITV¿§Uz~€ß=ø O^œÉ×)m•øÅ¢õ¨Y.  ñø“Ïð«;îæòþ‡á£ÇéÝB™Á¨ô¦Ïš4¯·ÎD ~¢Ûìkô1e r=vz¨,t1°ÐM¡7šNUT­þ0õ­!šÚChš1Ì�Ròîê�ø÷ÿK(J÷ÕÀÍ+VÜ}Çgáù·}vUÿVeæÌºŸK)o²Zk·ÛX¼x¦ÁÅV�"$¡Hªºš•¿±©‰\_ç=?\.'Ehjh 7?ßÚUH’Gàp)~]}=Ï<ûÿzôq<ÈðÑ>z|…„žø“€ˆÞ‹ø÷�:‡_¦€["lСšŒíw I|YN•]ud»lÌu±�S�¥ã|�†¦iD"<¾b7¡p„¦ö0m�0í�í�a-Êß”W–ðÐý�³{ÛnËHøõôé×�…‚×­^}_(ã|ÊdÒ¤+݇ãAàŒtÛÜvû—Y8ßšhkniÅåÊ¢#u1}YI*¥¥¥•Ÿÿúnži)Æ�eþìZæÏ�MaA¾å±ÚÚÛ¹çsÚ9UDX{ÿqñ8*óú®#øÚ÷?à_�=Ák¯/!7i:sO<�²òª¨Õ»öº0DKëú§Zz,¼-áI˜C™6\Sêeòà|TƒÒ›ßÇÿ !°Ûí±.II¢2ÖhDhó‡inòßÇ—ñÜcK; ^‚ç®^}_S>%2eʵ¥6›úÈ)Vë·þøjfÏžv˜mCC#:ò³ñêjõW­~—þälhä„Åç1ë„Å|òñ:>xgÕ%^®½âRÜîÔƒ[¶nCqçĉ&»Ž² ¹v”CüõB¡o,[ÁÃÿz”uë?¤ °„Úù'1íøùd¹Ü®¼¹$W¦ $©l?2Í:#ÛOŒÈqÛ™6¬€ÂgâÞ§Sz«e6› »ÝnðÐô5MòäÒùݯ&HÛ²l�ò”åËïÙ–€~.Ó§_;Rñ4ˆj«õã& 凷_A~n–�Í×K0âû·ÿ/ßûÎ-¸ìJlY�{ïû#ÿø÷c•VpÞe7R^U£Kƒ d$ÄŽu+?´‚ñãÆ¤QÈ0-IS€X˜aÁl;Ê|¶^+¿”’uë?ä¹^â¥W^¥¥¥•AC‡3ç„S7iB()ƒs’Vë?ú˜`0Äø±£Ë®¹ñë¬yo3œÌ¢³.Ææp&Kcb-¹òÜ*6Ep`ïvl|‡3/ÂeQгúÝ5¼úæ{ÔÎ]H~a‘Žö§MP™gïUü¿mÛv^^ú/¼ô2;wí&¿°ˆ©3æ0¥v.…% .z‚í71öv›ÂŸƒ]õ~‚ñ|=&/ÀÂÒ›óÿF�ÔÚ$ùnjG¥1+eO7o%ŽPZZ”ØÿÀÁz¶lÛAvA9Ùå8D„â¬?¸í¯|òÉA«Ó6‚rÊŠw.ÿ4éÅg¢ ¶öúÉÑ ((4¯»è¢ÜxãÙ¸\Ž.-g8æŸÿþ‹O>1A¾÷þÜÿÇ¿qî¥×3ïä³QÕ¨uŽ6àŒ¶é*óÙ©ÈU£ =ÜÙ”Tá…ç_DÈ%Åɹ?žñen»ýÇ´473sÎ<²\žXu¡ˆµö†—èVÉ1ÀÖmÛyêÙç¸ÿ�?ò›ûϦÍ[>fç\t9g_p)ÃF�Ó��´"iâgÈõØÐ¤ 4×ɰR7>·‚-ì©oÅæp™L‰Ð~1ŠOXÙ“U·X—ãŽ2ý]ÉŸ|„o~ïÇlÛ±‹ÙµÖ¥ÒªªróÉ󯿃Çã¡¡¹�æ¶²r È)ª@Q`d¹—ÜÓ¦ cíÚ�¨¯OiZêyAyù”U»v­ú$ýDfμn¶”âY ϼîâ‹ríµ§uYL#¥$ ñÝü„!55Œ=2±îŽ{~Gs[€s/½.šŠŠöp؆;(ó©8T�Ç¡àQ#ØU•â�CØßØÁò×—2bØþóÄ“üßwS=x(7Ýr>_^¢øGÄ*�$)#xœ]ÿlo­\Åu7~��6l¢¼j§�{^zã'O�y±F¡Â¤|&S‘Í€\¾,JlÛœlu»·âY¨ªšTàt¥Á˜ tç3ƒ„ sœW“‡Ó®¦!ç¢;üá/ñ³_ÿOQ57ßt=>wJ¬àt8˜?k›Z½ìä3p`%%Êqz|`H©�KE�Û�ÅñÇ�eíÚõÔÕu¤$‡„çUVNY·sçª�2!À1¯ü×Ï—’'1µêBpýõgpÁó»3·´¶ò�[¾ÏG6ñì�àrF½…Ý{örîE—qêy_bÖÂScº#bã <×N¹OEµ¨Õùïó¯Q3ê8ìË^y–ÿôc'Lâ꾉Íá0TÏÇŸeÓÏÙZâ¬Äï÷ó¥+®AufqË~ŽªÚ Ãw¥Ém×/Ót=ü6ÁØŠT+üÉ–­üè Žl.¹ö†á¹`,ìÁ”)Ð$_²¸'þY‚å^†Wätú`J)ùÅ÷òׇaÄ„™ÌXt!מ<,6/�–6D¨khäÞ§>¦•l& -dteUE. ²í)$ckkߺå^6o²L„„�ç._~Ïãà˜uû—L—R>ÂcVþ›o>ŸsΙݥâÔ74pÃ×naýǹbÉ×)+Ȳ7^çƒuëyô?O²÷@ç_~öX +ëÍs«hÀÇ›¶²këF*+Œ�Áï¿ï>l•Õƒ¨<Œ©3�gÙm(q·Ÿ¨åBŠl…·#ýO÷Ûû~Ϫ·WsíWo¡ Æ#aaáEªµB?9ˆ@‘‘hÃO!‡Ãüé¯ç¶ý”¶Ž N>“Ò² ̃}¬¼�ÄGaµø‚Â'µ# )ËÏêTùC¡ß¾õÇ<úÄSL˜±€y§]̼q¥z» •år1¬ÄÉ;ö°½>ÌÐR#+sS<@!‡�9s&°ú�÷i¨Xè�8«¢bêê�;WnÊÀ1&³f-¯iò)•87Ýt.gž9«Kå—RòÔ³/pËwogß�ƒ\|ÍÍ ;…MÛ÷ðä“O²iÛüa˜rü Ô …"Å^þ�ÄçR±Û �íóV³Xúò äùrhko§µµ�¥¯½ÆÀšaTTV!€loj|D¡bôÈãžz<E.%ŒÝnK†1ùèã üâ×wrÂI§1kÞ„GbpÄ…QQõÙ ½òJ ›ýü÷ñÇøÛ¹õöŸòâKK™0u—ßð-*ªj̾¹î,±RŸD«`a*о³© ãª}Œ«ÎÅa뺬ù÷þ;=ò(Sf/âøE硪 sÇDGcZ¥ú̳=nj lªàøQ…„C!l6[âêï¥ÝngÎì ¬z{-��© gVVN[¶sçÊ~›"üÔ…µµ×�–RY*¥L!ü®»î .ºhA—ÊÿцMüêŽ{X»n=5ÃÆpú…WPZ^sÁ£#âÊû[’£RS`£¥ÍOKÄNGȨSRÂk/=Ëã�ü�p8ºòKWÝÀ¤i3;ýqô½ô³ôä8$…^c!P$áªën¤®¡‰Û~~7.§KׇßTȆ?ù9Yœ£ßfýûkxî‰rÂ)g1rìq±zÝ~-M��ëofûó³7(—£{v¨±©™SϾ�Ê¡ã8ñÜË‘( |aî N3]ýðx<–©Âx8pó·~Ë–O­.«Yí„åËï]•€£Ïö—IÉ›R’Ò…óò+qùe'wºss üéo<òèãdûr9ñÌ‹9núE7:�ä(½8‘fSÊìØÕ¤5>ØØBžD>_ïev6bΑLö[&¦ú*qGÈr%]ß¿?ü÷=ð®ûú·™pÜÔ”x?ñPcÑ·ßÜžÛÔxC³*ôÁ¢–ŸÔæ °Ðô@ ©,t3®ªûS$®Yû—_ûU.ýê�ÈÉ+@‹ÀçÕôZùõéC�ǃªª)|D+<¿öõ{عò›X�¦)3úãä$ʧGù¿éÕ4ž²Rþ /\ЩòK)yôñ'9óüKxä?ÿeú¼EÜôƒ»˜4cžQù E‚ñ/÷©8lŠÁ},ÌõRæ•hþ¦Ûndô¡è^"Iò^º}â\€ˆeÚBÑŸ¯¹¥…ûøüÓ_˜2c'M‹]‹H€T»7y."Á.?'¼�x˜?ÃgRc•Ä Ã÷ á9‘ø> m=›0tüØÑŒ;_~¡if$A$aãæOxåÕe<ùìK„Bá4 W¬_­­­„ÃaËŒCv¶‡Ÿýì ‹,¹†EÑž9þø%Eà(ȹ瞫67;ÿ�E·ÞÓO¯å«7�“ÖÕiljâÚ¿ÁcO<Í�‘ã¹dÉÿ0aÚlì6‡…Ò§þͲ+ -´.ÎQ…;ïø5m~ªª'#ã8xXý‹CR/u*•T„¤`Û'›Y²äÖ¬ý€�Æy_–(f…´ÊÉ £‚š=]oMòIýX €‘�)ç†4ª‹ÝÝÒ,„ îà~ê;ÀãÍI+í8ØÎe_¼˜‡þù(R¨\tÞ™xO±×ŽË&°ÉÏ<÷<Õƒ†è\åDÒ/iõÓZ~aÕfcÂäéd{¼I«­ã'Ãv-Ø# ¤¦ Óò¦ÏR$Ú$Š‚Š}Žäc`‘›ÍABñF ÒXùçvªÄRxëÖĵ_ù&O?û'.œO–+µ+Û�÷ÞÏž}5if`Š|Y|~á{»Tðî‚A0Œ¶s·¥Ž˜Ïõå0rô^~i­�Šq••ÓÚvî\Ù/J†•~®üSA¦4n()Éã®»oÀfS;µÓ&O¤°¤ Uµu¡üÖ!ÀÀÜÎñ3//—[¾ù5r³³t©%²£‰–æ&£²'L|ôçO„Ù±ÿâ¼ú8 ­Äôn†~lBÒË�P0• 4ÒaBÊ4ãÆ²aýy”9jW�[Þaßî)çhh‹N�òÇ¿þ�˯¹‘–¶sN¿”×?Ø‹ßo,Ç=XWÏò·ÞfÄøi†kœ2´€·ÝÏw篙0ÿmmm%hòõãëÆŽÎõ7žˆ”VmåOfÎ\ò¹ F™9óêb�ÿÆ4Q‡ÃaçŽ;—�íquéΚ9��ëÞ±Tþ”÷�­—wïÌÓóòsOv¹�ÔÁ�‚ tdú°!Î3` 'Œõ©ABƒ‚êI<!…!º 1w`èœtÂüÙ¬}ki¢Hò8‚ƶ0ÿ}íîùÝï>vW~ãG ¬NHõðüªM´´$Y÷§Ÿ{¡( 7Ù=ùº" ÎH¾®À „B!ò¸,œ?“ÓÎkéYK)ÿ>}úWª3p˜H?MSR¦Í¹ýG—1°²¨Kå().bÔ R„Œ˜špêYy³ò'ׇzÙCÖãñ°zå2Zš›ºúš Ð_Wt¬¾ 00ø阓»¯WtÃg J­Ï(l òÑ–=QP¶Û3¸œ†º1N ù„#š3—Ó/¼’³.¹žÊÒ<—Fãÿ êã÷�/çŸ�>A8æ©gžgȨ‰¸\n †MR�R@Ôïj)%---D"Ö¼Þe—œÉØñ…–™!ÂÿX´h‰3},;w[1ϼüâ‹2«vL·”?.7\}9Ó«]Tç«dÙ�bhTþ|·ÒIÏÎeÛ¶ílÛº�}{vw{©{€SšM¯FÖPç ãý>ÆUB¿»QÑ1‘ŒB¤ÀÇ>¿“ƦN;õ$V½ö|zb’l6;§Íabu.µ# !ÜÎ;+^åƒFÒš5ˆ[~|?ÙÂØIµQP±),WÊèªÜ.•¿/@ ©© MÓRÎ¥ª*ÿó­/QPhNml”·g ¥¶öúÉ ¾c^>~ü`®¹fq�”?.Y.UN&t2¼ò=ª¥7`W¥^…1%6å)8zI¡îÞ³‡â’Rj†ïÕþRǬÇÚP¥‰ÊŸ”|¿RB‰D¯„%Y(t9¿8Pæzì .É"'˽(ÁárLj3¹Y’`Пš[ñ& Qö~íª7ؽý“ÄFÙ^“æ�Á¥7ÞÆÀa£ðºí,:®Œòëþ ‡éÖÅA@êîk‚Ètgqû�¾ŒÍfÙ>ðkÓ§_¿(“è7îë‡#üö�KeEgœv N› "ãóô^ ;ºø^kKX¹ücóa ]ÔŠ&S{y.†”fáuÙØ½c+›¶î—_@yž {ì†åçæ²tÙ[TTÕ$2ú¯nW sœ„B!ü�0…å5±éÓ¢`âöæPšçfþØRCê0t– èéºxÕ`(Âe‘�Èö¸)˜Ã¯­GÕ|9óJJ&üeÏžÕí8Àn‰aµ¹¾lþò·¿3jüdqpïNl¾²èÛ ÅiWq9"”çgQ6 ”“Ç1¼,›Š‚¬èb•Ÿ‹éà °«ÝGàÞ(zgË"‘ètçñb+½‡PY>€u;غ%…Ûñªª:tçΕÿÈ„½”éÓ¯_$¥¸Ô¼ü 0a\ÍáœêJÞxáIT%Ù‡çV9ìv†U±kë†C>V"†7—íšÉ¤‘éIW“k®óó�“#ÐÔt!•‹òÂlZ[šSz+žuÚ"Üþè ¾#ʳ©QÀñ# 8~d!S‡ä¥|Ÿ\�ƒÁ¥ÙLšÏÄš¼DS’žcw\ýî.kkk#Xnwõ•çSTditΜ>}ÉEèUÜÿM¯¢ð;óòªª’^Çý]ZR ¢¼Œú{(ô(Ø£¯äpØùÛïcÝïÑ(`ÌÛ“†#0ûÆ_ÚŒ€¾ÜWOûu„"‰N»//}�w׬åƒÕ+pÚS±ÙÓ'¢Õm¤²À}Øîigñ~wù�Îömnn6dâëßÿþ@»Å5É;¢éë ôH4­ã'æA>Š"øßŸ_…’¦ŽüP•?þyÐÀr ²õõ ¬_ÿ!þŽCÚ¯w|€ñ‰…û¯b½�¤ž Dšd¡Ž<”v7ùòu_á;·ÞŽT�Œ9ÂòŸéÊ‹sG`,jOˆÀž` õRVVÊ•W-DÓÂæUšfûe†è‘õ_2]Jù³9ºâŠS˜;güaSþ¸ <˜¢‚¼ÃòÝÚÛÛyõõ7øÃŸþÂ�º:.ýòõØìö4]�œ€Ónˆñ­j^)ˆ•ÚŠTbÐâAc{˜m[¶°pñ¹,:ëB*ËŠ(òëòŸ{áeV®\ÍyçœqDžŸÞÄùVˬÖÇ=‡#uìAMÍ@Þ^ý&� ÒüÛŒ«¬œ²zçÎUŽý:¦ûÌ�{«-8¸mvý|ð«õ?\xgÍ{¼úÚë¼öú2Á ÃGŽa΂Ï1söü>ý±âýö,gíÑ7ïÀª‡¿qyb¦žøôÛéZz뚌TdQ]ä¢ÃïgÆM¼µêmÞ\µšßÜñKœNÇQ~óØ«~]-ëj}nn.v»=9ajlÝþý¹âŠŸb·§Ì µÅélµtéŸüG[ÇŽé¹ýþƒ×aT~!?úÉÇ´ò××7ðÄSOSWWoX^WWÇ;ï®! 1dØHξàÌŸ�×ë¥=Ü�ºÇè. Uw‘z«.H™xPÊ®�•:0>:¤ ¾5ÄÒ×_ç¾»~�Ô$£ÆŒãŽ_üôˆ(üw�O¦Ïß§[fµowöinn&???e}qq!q=øŠbðìùýÞ›€g<€42kÖ5y‘ˆº(Ð/?ýôZn¾ùü>Sþ¾€={÷òïÇçɧŸAjP;gE%¥†m²½9Lœ< Ÿ/ZÁfW Ô ÛvïGõ'¨O­`œK‰špM× Xß•—D÷s /Ó„ZêÌÀš¦ë_§I¶mÙˆ¿£�ÁC†1cd YŽ#uö••ïj½ÓéÄçó™¦=—„Ba®½îûÔ×¥tWnU†/[v÷gǬ‰¨?0+¿Çãâ†Ï:,$Ñ¡ÈæO>áŸÿ~Œ—^^ŠÃád΂Eœ|ú9äåtiÍC±6YUeÅlÞ²%§,9µÀ@èªïEtÒÝXÚ/fÕtDa4÷/Ñ:õd¬Ñ§ÎkˆSC bÀ’åTÉvª ™1ŸÛŽÛ©Uks¨V¾«õ~¿—Ë•ÂØl*ßüæåÜôÕßâpÊ–³5�Ÿ—d<“DçðSÖšê»ßý‹M=¦¬ÿ®Ý»ywÍ{8NÊ*R1°Úr yK ýà¡Òlp¨ð£ŸþŒÝû¸ù{?N½®C¸Ìt�:­xM79gÊ\Zò}¼ÏŸ¦é¦‹qvE0¦ÒÓiëò#é)/@Aaa¡á¼ñׯþïw,_vÐ\%(…Ц͆¢ÇhPù¡Yù‡ )礓¦s®yY§ž¼ˆÏ�0Ÿ1#†�ë¶‘íÀðj¬ÛÏ}„];¬»G#ÑîD/¾¼”QcÆ[_—<„—E„… ÝäÌ�@Œ}¾oƒ�7?Ü˶;�>ËÝÍ"Ÿž¬Ow|MÓhii±¼ŽK/9�ŽŽÝ7_ùÑQÕ´cMõg̸~¬œi^~ó-1¦¾o‰L?7ßr o,}‘â’ÖÛ„á�Uï1°º†E§Ÿsȵ]:ziz&C Í?;=b²G˜a½Ýåfc�dÛŽ]ÇÄïЕòvõluÚÚÚR‹B´9Ì9çO$4ƒèÂ3–ÌÉ@ÒÈÜn¾® 3zäÀc’øëJ~{ÿïÙ³g/—]}#v‡5ûÝ‚âÒ ®¸ö«†ð!­!ï�ú[tò±ôLŸd—ч¹n ¹ÜáÊf‡ý˜ôº³¾· ÑÜÜl¹íçÏ; MÛgõß–`Ú´&IIJmï·¾uÌ•PwKÞ}o-O<ù4'Ÿv6CGŒêtÛIÓfR5hpçñì!¼ôcÐma�."9'@ºx µÂPè¶ HG,ûÀ§ :Û/$Æ èÅårñÅKN&J™ttÎŒ×-øÌ€¢h·š Μ9ã8°ïFú Ù·o?Ë–¿ÉÏõkJ”sæy}�Dv?BÀdÉ»#…©U8@8"iéS¿O_¸þ]í×ÜÜlÙ;`ÑIóQ”VgúþQ¹ÇÊ�R[{ãpM‹¬×ƒ’‚Gû%ŹǬû¿mû6lÜȦ͟°qÓf6mþ„––'«¹ø²«�?ŽêÍN°ü$Ùý´U€±œ¿fªÔ¢L¿aÆ_] " 8ÇAyž‡­çß¶¡±‘÷×}H}}g,6Nä¢i’??ø0>_g.>¹G¿aºL@OXÿÞì—““CVVVââ…ýÏÓüõÏoâpd›  ˜±lÙ]oÉÇ☩ˆD"_Âè‘Ìš5ºÏ•¿/å½µïóÕ¯ �×KUõ`ªj†2mΉT LÉ€ò”jEyQWZ@¿�U€ñOÒÚ\ Z*oC±×AE¾gº¥´´¶òÂKKyÿƒõ¬ý`=ÛvìDUUJÊ«°¹²9uál½r0~ìhîýÛÙ¹¿™+.:wVV·¿³Ü~_ï—ø~-- Ðï·èÄyüá�ÿàp 1ƒÜMÀyŸ9`æÌ«‹¥´m ­Vzè»TU÷9ô…ÐÚÚÆåW]‹ÍáâÖŸÞ‰Ãéìñ]G¤Îˆõë*÷ŒÖœXA�~ÒϤ÷�´ªn‡JM‘“,GÏ£Éû~ÿgþø·‡wmâ̹cXYÑ­sëß�cø|>Ün·¡&@Ó4þü×òä°Ù �|D1|ùò»6¦8)í×™•üøÁ‡EùûJ~uÇ]Ô×7ðåë¾fTþ®âí4¤Þ‘ôd"$HÝBZ}iMòxœJ¯”ÃÆMüë±'?eçù&j,¦rðÃhÈmíÙ|¸áÃ~ÇϘLAd¹ÃyüÍíìØµç°Äþ}uŒ––Ë ÄÓŸDkëóæ*È?S$àܹ·Ú@~Ù¼üškOçX•gž{�W–¾ÆYç‘AC†u®Ý›`�ÊXœ\ö„IìžøýîüÍý\òå눠2sþ©÷!95ù{ûUîøÝ_Ø®+*:uálDýr Ðqu¢°‹Ð–¶¶¶”å¹¹9,X8!¥g€”|©¶ö›ÞÏ ƒO 2ƒ•2vì ÃbýåÇܹs¿ùÝÜy÷o6j '.>«k•è…VËÃÝ8˜ìÌc°XÓÓkû𣠜ÿÅ+xð¡G˜4s×ûç—¦ñFⱿJÑÈãùÍ#¯ð‹»’M¡¶nø€w—¿HK µ¾zÏÞ}üòÎßòÜ‹¯1Eïìx­­–SŠsî9‹ilÜn^œ­iç~fH@)I±þç}~Þ1“ž…ü±lÿ}êYÖ¬YC–ÇÃì'qÊ™ç%H¾n‘{‡À^¯@vmøM®¿”½»¦ûþðg:‚®øÚ­TV Ñe",@G&—¨ª�qSæ0¸$ÚB¬½£ƒgŸ™q3?GCkê4½~éšZÚ;u•«5¬&-¹×WÞBgÇ ‡Ãtttàr¹ ÛUV”1¨&‡¦Æ#÷eàŸz`Ö¬'è—9�vN:±okþ{+ï°žs.ø"ßûáOØßЯ¼žÿûí_¹àÒ+ÉñåöÎØ5__O/M—aeîe'¼A¯ÏÞ}¼¹rsN<ƒÊê¡)ƒÕO_&etžÆø$ Ï>ÿ2í~?£&Í¢¾5hx.ÚÛ;¨«oàô/}�)óÏbÙ'ê›ú¼Î¢§ÇKçœsÎÉ´µ0›é³f-ÿ©€H$r¦¶d'œpNçÑ/xø‘G±9Ü|÷ïå[?º“¹',J%üÅë?Â` Ó¾‘é•O·NZݽô'žz»ÃŸÉ3õ£ˆ­÷×ñëTêÁã´±}çnþñïÿP5d4^_>�0ËÖïO†i»v#¥$· J w„à¹wv÷I5â¡€H 0L4­™3¦ °ð´Ë> Àæ]´ð˜°þëêXþæJ枸˜²Êj´^ÆòÇ (Mš­WýD±f2Pšõ±Wß-‰ðß§žeüäZìWŠ‹Ÿ€išdÿ½6„»~‰Óν˜í»ör\íÂÄú�v·ò²hGå»v“åñbs$�z«äõ5›9ÚbE:�>wÒÔ”ABBˆ ¢ù§¢S|a¨„2¤œêê’cÂú?ñä3Eaê¬dŸ¾Þ‘ëu—Ãz{)ô]ÀRš¼‚äÍa^{{=?ÿ¿»8x°.å´--­Üõ›û9p°ŽÉ³IŠ!Óx{ê;hhjã¢ë¾ÃÕßþ5UÃŒs@nmó°|õ:vìÜMN^QÊqßÛbë®}Gõ™joo·4d§œ²�¦¦”¡ÓE�À�Ã>J𨑀šÆçÍËN>yÚ1¡ü‘H„ÿ>ý,Sjç‘åI–kFä¡Ý0ýO/õ½ØÍÏKË8\¦%e2$ÐÅæÁõ~…€ÍÇ£Ï/çâ3NÀí΢¹¹…‡y”üë1:üfÌ;‰Õ±#ÒòÚ¤Éí7‡+5#Ç#µd¡’¤¤„:Ø¼í ¾ü"ýÕF›•h’¥63t×n²6¼Þl|9^r¼ÙÝ¿�‡è…jšFGGn·q>„šê�x²­æç/}@€<Ç0äDN<éØ ÿ^}c9ÖqÙ‚E K"DtÎ{©öMß!ƒAÏ&-¬nZ#fÐÂM—@qIsO<)%«6ìaó{+øÛÃ�8nÆ\æœx¾Ü�CÁiWȲ«8m‚,GtZ°,‡J{ ÂÁæ{›Ô·Œ `" ¥E¥’”0pü|Ôœ )Þ @C{˜ßßÏÊçþF°£� ¿�HÐOAA.W^rgŸvÒaƒÖÖÖBpò)syúÉ 8�^ýò³&MºòúÕ«ï }ª`úô릂¨Ö/5ªŠ¼Ül޶H)yôñ'©<‚Êê!ÈÄ4–"VKgMtŽI006ö´PvÙ‰‡ �™ƒÔ팂på#};©–¹‹N§b@ y;ù;¹nŠ’þf»lûœŒª„`(Âþæoon =ÆR£uŠ EU)¯‘ø®ú‹‡5œÞ|jÏ^ó$@j/okeùO~ÏÕç-`ð �=~fº �@€p8œ2NdîÜ™<ü÷¥ ׫ÐétΞÿ´y§šœxbßöúë�„B!~ú‹;xwÍZ¾pÕM±¦™Éá°k`WßÀ™Þe:TÛ¯sáÍJŸìù/¥¥÷m­õ)ñ{ôÓñÓ'qÚüiäyìØÔÞ]¹Ã®RQàF„Zye]Ø=‰�f¾@‹-ÓÒe&b Úƒ‘d¸ÿÞB`wyPn¤¯ˆûŸßÈ/+ÃfSûxeZ. ;ÛhìV”“_`EÉÉS'PaðµEð¹ÏM:bÞJêêê¹æ†¯óì /sÂ)g3¹v®‰ “ à�¾Cãðzþ’Æ¿©n½‘‘7l#M.8Ƭä]M‘‹a¥nŠr½V~½”—3kh2Øž…h9N!1 ŠñzÍK0bQo s$\~útìv[�¬zOŸ»öv«y'-š�ßßd¾¯'ÎçêˆÀñÇ/)ŽÓ/;¶¯7‹£%oØÄå×ÜÀÆÍ[øÂÕ7qêy—D-¼¾®snDKS´ÂQªãïÊ÷Oü‘‚Oß8¹V÷.dÊ÷Nöˆ)k¾“Š‚¾¯Í¯XÁ¬¡^d°Ýp^djb&ÿâß5¢iCË.þµNœXÂð2oŸ¹û�…ú‰EãÇš:å8ZZR‡�9sÉàOM‰h'�0ÏÌ™£ŽZÃÏW^{ƒþø8Ý–|û§Ñ¸_Æ›ÚèB:sá�v)³Á0êëm¥µ"'²2 é'“jB¹t¼B�×NMñáñAUH¹�W7´‚ê2x ånò³x\6¼.û›lØÕL}K ñ]Z;BÑì©�¢ÀYÓÊ™:$/Ñ´£§ žÒ£££�Ç8YHÍ j„h·8¶vpϧ¤).ÍìÙŽxü/¥äo=Âoø*qÙ�ß&¯ (Öë.þà �C™|Òv¥s-?²,¿µõ7†îÒä-묷قÊtä 4‚@l¹×¥2¼4ë°Ïšê�Àv^ù¸T'’!¼Ìk¬TêeÚ°B6µ³qOíjd÷Á6ËÉaS¸hö@FVx Ê8,¿9 0€ª*ÔΚÀïw`·ëÁT,úÔ0[ÿ!?ß›¶çßá#ûÂÜö“ŸóâKK7¥– .¿‡Ó¥›)'nëeb6\Ýü:h„ã5Ýxê�´W uƒlôÖßð^RlÀ�aŠ0#¸ì £Ë=¨Ê‘ùv5ÕÑä6–~ÜJyQsƧݶÐç¦ÐçfúðBvhfÛ�v"šnÂPMRY˜Ei®3­åïJÙ{jùãâ÷ûSf¨�5�7Wü›¼¼*Cä|î¹çª�<òH¤_À¬Y7ŒD"†.S§Ž<âîÿö;yñåWYxú,8õ\TEINû”PÖdÄU#Øl=3õÍw›ò'Ýÿ¸Rëãæä{¬R{Þ_'½›£Ë=ØÕ#ûû T…¢ì ¢¼¥›ÏNYA6ò=i»úв÷&<�Râ÷ûqšÆ–Œ=’ööýfÈÙ¾½p4°¶_“€‘Hd–yÙ´i#ޏû_ßЀ';‡ù§ž�WG§Ë’ «ilœ M¦d¥ ˆìåO�áA¦dñ ¤ Äò»ÂiŒùãè5|€»WÝ€úĨªÄÑÃT]_Xö¾ô::RÚƒãóå�—ï´è"¤Ôöû@Jjõ€-„`ê´‘}rlM“œsÑ—PZÂø±c9b(Ç�%;Û“ õ x¼91ËØ¹»o´}ÂàÐû÷ݢ;~/ €4}8{ üz+­2Rgù•†Š@#\è¹�„÷` ¹ÈuÓ³Ëw[){¢ÀÝÙvϾ”t¹ŸÕÜQ£8‘•o$++/i©j�ßôk@fè?WW—�ëóôɱ×ô1Ûvì¢zâö‡¼üýÉ7¸øê›¸ôªØºÍØ{­¡± �×§³˜Rg‰õ¼î½…gÑ ‘�[ïC4ïÝËõK;ožðS¯üIÞäæK™ã¼Ý6e¹NJ}ú“ Å�Ë+o¬ä_O¾Üå¶Á`Ð’{˜8qííu)Ƴ_{‹-q66JîQ£ª{ÿoß±‹U«ß5˜ÈU«×�“[ÀàQQ…qSg#„ƒ–}¸‹¢¢B<î¬4’íõéXíd¬/LÌâ×)�ÓéÃÑXXÖ3ånÀ¢ O饂B"þ7 ï1€èS|¦RÛ\�Ju¡³_*¾U¬¯ßÔÜ‚¦ixu^coAâÑ×7PZ>�éc«Ón+¥$ ¦L)>tè ÚÛë)08¢zêÔë V®¼§®_@SSd$(†N£GWõ8þiéëÜöã_–‚ª¡£ S^MŸ*±y­�"ªÌv§‹Ü²!¼µá sÇE{õ×Õ7$<arñ­Ü}Cï|!  !„%.[l!º§Û‡ ÏÅìʧL޹Ä'%‡oÌHCX '³ì øéoS´vÇ¢‡B!¾ò­"�{~q+vý�½ð4)ybƒ�÷.F �vÛ@ €Ãá0,/).BÊÔzEc�¥ý¤TÇ™Ÿú1cu{ÿ`(Ä÷Üσÿø7*kXü…%ä#D´]”PDÌ›ˆ?ÈÆ¿âeåúíLSM}}Ù%Ct1³ží: Õsi&ÑCŠT!±©a1T_RWú–¢à:å7„úôž”w>•0†ñ6U0ªÜ�Úÿ&hî–õ—RòñæO¨<îþðìz¾|ò˜^‡ Ò[“<¼Fã ê^ÐBìÞ³Ÿ]{÷±{Ï~NZ0‹êÊ2�^¯×¤è 5ƒ+ðwøÍóô_9VÿÉår0hPi·öܽwßøöm¬[¿�‰µŸcΩDëµõ.{'9üxU_½ÌeíÇ[¨oh¤pHމ-'v,�GsŠ„0tÖD§øv‹h( EâW"z?’ÇôV-?¦4�Ô§Ó¸ÿüÂÐÈÄ�cP¡ —]9fçc<ÔÀn·3yÂXÂÃØØ`ãï/¬åÂ…ãzBH™ÚP1†ß½ÑÊæ×þJØßŠÓ“‡Ã“ËæÇVsÙÉaÆ d¹ÿ šjÞ]]Ovv ôÛ,À8ý‡áÃ+S†DZÉó/¿Ê­?ú9! N¹øz†��U2SºK$ݘÉuÍ÷†óÉ0O¶OwÃÓXza;�ù/R¢‰hVÀeÓ±B$ª WÓÍ¡ÄÒü'Š�Ô“��)¿1·/¥9€%jS¡Ðkï·Êß]%ž>e"O¼»‰âšñ¼³×NÎëprí¨[ÿ…sfðá?ñ6 ŠÍÉÐy_JÜ|)5��¬ ÐÞñ!§•–&<Æø±JŠ‹…v˜Iôqý9 0ÜHVuk§ÿýÕÝdç—rñ ·3dô¤¤uÒEµz6<ÅzI£‹ŽHæŸv1*ªSrà) º4g¬¶K.E$Ápt¹¦›fKÆF£%§ØŠOΙœˆ3þO‹½âÇפDjÆï¡™>åXff™ÒÀ¸­D¤¡CPiŽAÿRþÞ€ÀŒ)©ß¹!�ÿK·ª¼½nK�¬¿”’‚ü\¾såbj«´˜÷' a&ìŠ@ ;O}dcãÖ]†cí;PÇö]{Sú#ú%Lšt¥0TÖÔ è2NnkoçÀ�:æ�~ Ù¹ùKeTt Å•ÆÔžþÕnÇ››ŸT»4@© `Ñ89Þã>–ÑÔ`ˆÍ²kR|�²kúušL´½ÒL©ºÄl½f‰�_³LëIë4!ÒçÂÝ6¥¹ú³tñ÷+Êp†› DÞÒÍ2eÿª(œ6m çOP£3%ë@u|•›3§ ¥†*¯m ²ï`tr€Æ¦^[þ6ù…%äåÕ˜¿ŠoÖ¬kòúde©•˜ÚWU•t³mÛ¾ø JŒ¹lCÎÚ<Ì5M1Œ%Hûài– �Îêë•.>}vT1ý!IX3Zî¤òF�AÓbËÌ/CNr™¦�´`…8$×iiî)^B, ÀÒeo¡IAåˆi¨jj{üHÄVÝï@ÓÔ”‹®¬,îr¿­Ûv��“�ÍîL©HÓ�QOçî[¯ëÂÒC§n¾¥ oð$š&éjhšq-å1Ym£…×âWHÝýy1÷è€)¦øšá>˜¿·Ôñư Ôgï×V¿§aÀg/Æ_·ÕÐ'`éG-½"…=#1¿ª•RŸ�ó§ Š(½tÚì±´Õ=@D¦:•ˆâ&¯bÙ>ªªXe�õ;0_t^ž—œw—ûmÛ±_a‰�ÔJëî[zÝñR§ÆNzW_Z)›î½&%m!I$’T8´Î%ùÂ&˜Ï!-Ý} Ð"Í6Xm“zo²J¿)÷=ÀÀ¾WUð•ój‘áŽDȹe›÷4ÌŸ>Ž+æ2)cG £0²ƒ¶Ž`"�[>›ËŠÀ““Z%«(ý„ RÿyàÀâní×ÞÞ�/¿ØÒ’§¸û¤z�–÷vfeÔdŒÄ³ð4MZXø¤o j„´$¨Yp�ó ÑP"jšŽ[@I³ô2L ‘Nù áCôý€\û§NùÓ)¨~ûñ£‡qê�‘p" ´ô£–nñ��ák5ÿ;ª¦„½Û?Nõ^¤Äã˶ð¦µ�ý@w'þ7K®/g–ÇDâÅÓaÝs÷Iëî§«ñïœð³"þ Š£ÅÉ< ÙÐEHµø˜^i•X§ô2UQ¥!˜ÖâKsW#ÿ÷°T!(Éq|ꔿ»À0wúf7$BÏw¶±ó@K�¾»Ëöm]G$bêx(!Ëã²0¦¢¨ß€”ãÿî}‡¼¼\:ÚZÒZ‘RR–ëä„1…,]ÀÐRwׄŸYqÒ¥ùè&ñ'“ ¯WLM·."‰�€ÉuOCJÓ6úc™‰@djˆŒÌ eœOC%¹Ž#Öäãp³þÝQ~«u§/œNý†×!æm½¾±½O>¥v@B N©'pdYöW,臀,ÔÚ¸qW÷ ×GG[‹q`Š®îÝíT9~d>òœ Ès²aÍrV¿ñ\§#ø:�ô쿦IƒKžª<Ò¨¨¦ð "%í�@Hë~ ¥ŠÔ��ÿ Ø~óuk4 n N(BZ¿+üéKå—RbSUF—ªø[£Y€Õ[›¨kîèqxÑ5H±–å PGâtYº*ì÷ÀK/½CCck—ûèhK�çcÝ{æ�.ÀeO~…5«WÒÒÔ�Êê[‚Ö%0˜ãêΉ?½’IͬtIEô‡4ÚƒZBµ”L€îœè->)™€D–@ë!(Í!†NAæzˆ}MA–¿·ñSãô>7¯–ƒ[ß�Þï¼¾¡µÇçélÙ®ÝÑù ¡ÔÉœ.û§ÃPò�¹L�6v=Qãè‘Ãù¿Û¾N‰ÏeˆçU!˜94—â#BîÚ½‡œÜ‚nôÝyM§â¯PHæýuŠ¥‘bq“ÞBry(,i DÇÚQÉ” iÒ�æT^Ò¢ÇÃ,öEZwÿ±únèx†°« Øo•_ÿê�òŒ1§O"¬zsS3Ímþ>ñže9/¾º{–76g�¾a£Äátv°! %ŸÑØê§¥=€×í´Ž‹ 0”QR²nËÖí QU”Ÿ*ngj;¨Ý{ö2¢¶ÈPãŸ2¼WWߟ¬ûh±Ñ�E½ñ‚ΔžA±6âB·�”!’ãë…ˆ_OŒÄŒ@[DâP[rÀ�L9#¦©°t‹Ì“yê¦Ã’¦n?˜Ê›IŒô3‡@Éý‡”dár:ú•òwG黫üñmfOÉ›wáÎ-#–¼òa3§N,è5Á(¥äí5ëøÓC�â-ª¢xð4B¡°î7Œ�ÁpXª§»O�óúmR‚UUih‹é¤³ŽùdLM1Ÿ¯-gú°|Kåoko§±¹…œ¼‚.sýÝcÿ»AüÉtÄ_jEž¦¥òš”øÃ­�HŒ ´à,<ˆÄß4\GJl¯a‘µÐgSRÏ›—m£<¯ÿ4þèŽ"÷Ä+ÐosÒ‚ã©ßú~"}mC ¯­ÙÒ-E·†Í[¶sçýÃé-¢rÂ"„¢Äô@šô®¿08ÿéÈÏh>>’Âv!°Û„4�hb˜±[_&�↘‰HígP× ÀÛÿöÅßt@áqg1±ÊË®@½7N»Âøªì´Ç ƒ<ôèÓüçé±eåR3ó<²rK“^:?Pï�Å—¥«Ãòûƒ}¦·‡ÝÏózË,óH‘ˆf|@%¸ÜnŠFŸÀïžú€þâ^›º}žÊŠ2ª+�åP1ô½ß¿š–Æú”ñï]—ýZ§5‹T U^ÞœÔ,Ї:‘ #‘(QØâ�Ðê�ÄŽ˜êûuã¤n|i*µNR�š”ì¨~ê­o¼‚SÎæàÖ÷Ë&ôwÐÌÛîܽ—¯}ïç<úÔ‹ø*Ç1äø‹pç 0’9ºßIÑŸ6hk‚Ün×ý–.½5 ¤|“p8¬+B‰Ý„˜+\Z5{õ¾Ï£<úÄ3„B¡C|8º7¾_jV]xôJlÍú›Ëu5]õ qp�$º®4ï„Âm�MÚ‚«ì�,’É!¿š´œ´§ÁOkG ß)_{æsŒ:_xoÂpMä¶ÜvûÎ=üÏíw°ë@ÕÓΡ|ì (ª]߇ ódlFÆ?V=š�¾€#•†À2ŽèâZ‘pyºªP1z´4óð ? ÀfÆÔã˜9}2Cjº7"ÒС£å¥å¶ñPÀaSÐâÃxu3$C‚8_@¼ ¹Ž ŒžÇH-ŠD×A=‰gYt ­>'õD‚?"!}@ìª@U6%^Œ Súbÿø!­Ê c¸»1̰,g¿€Ãá ¤–Åó§ð¯÷¶á.È››Z8{ržaÛ]{öñÝŸÞE«ÿÿÛ{Ï(GÎóLô©B!çÐ9wOèÉ‘ÃL*P%Ñkk%[Òµ÷ì¹iïî¹¾wïݵÏ&Ÿ»²×¶d‹’h™”d‘¦•dQ¤$Ši8Ãáprîé ÝÓ3�Ð�FÎõÝ@¾*Ð@‡açÔAu¡ºPÏóæ÷Í¢çþß�Éî-ªûD6˜¼ôȲâÿGM°ÛÓëŽRRÈfs¥Bò 4Eΰ<ðÌV=þ%¤œº}?;òH‡¦±gû&Üè î?t>¯‡{4èEžqå0_Éñg3±x¤ß�LŽÇ‰¡E$2DþÝ@–ü’1¢Â„ )î‰2qÉÃÊQ€$_ºIÙB›tN—Ô-ù ˆ¸Dšúnûãèk4S›R¬5»¿Ž>­�yè^|ûÀÛ�S#1Ò~|ö¾MEðÿéWžD$žAÏ¿£Ý'¶õ…€@ý_Ò~!$æÂðRºVÝ�^� Že¦s¥ðY:–„æÅ=ú�f;zw܃¾�‡À2 BóÓxíä5<óã×ñþÑoá{Dö¡ß/ø¬¸ÊŠÐºú¾�ËÊá#Ûݰò!Ê�osàä�üIqÿaªPžÈ%°, ùF‡D5”¼!¢¬À,Ÿÿ#•)iKÎd“Ï1Xr€Ê¾�€â)óqt5ÚÖ•í_ 9T³éXÝ×…cþè-n7Áiº…v� ú•'‘åYÜû™?„ÏçƒÃÈÀb`püFˆªö ã5¥ÊËÒï‘ÍQ§�g€?ã×0  �¨'@4—ä «žÆ+hßíô¶ÀåkÁÎ{?Ž¡4°ur�mâ9ŸùÔÇñòÑìò|Täý§­·�ô{D£®Í&>º§—oN`8l@6' $A.Q>ñ‡)h2…o\0 $�øg¨6‘€›¢ɘn)1Ha©ˆ…‚´(,(O ’š±5IjÀ¯eøOéñ‰O~=#Óp¸aäŒ``²xæoþLFƒhüx2�Åkoƒ«c»@ØHgB \ѵôƒ§é~˜`-¯å U’€ôX4$>äofÇÒ;ø”©ÜËòÀák1ÄbâqJÜ{3·¯–õþon¶âcÛ½ŠsîwoiÇ},¬z"¨ô+$Ó£ž…ÛÊ¡Åm(˜RÏ¿¤”X�š+ï>´Ô€þZzš±rz²¼SÝñGës8Lbf!º.$%€_îf6±¿¿}�´ºôðX9XL˜MFÙçñÙ ¦®¼½Ä‚îÀb“@_u^JJÑ ±µ¼ž+eÈFÂ1�ÍçŠýÒ$ƒ:DÁô|Œœ•Øóá4ðË3ãøÂ#[ŠUŽã°¿¿ ‘ÅyØ]t?ÁÞnöv;Ëþ­M>ø<ÜœX„Ýn‡ÅÈÂbÐÁlЉⵯDRç‚^¯—A›÷¢8©,/š(òÐ4�‰¸ _}îøƒODwW~üØ�ù¬Bž£øÃ)øÃiô5Õ¾àåáîÃó?ý3lßÿ€8(ðÈ®�šcPèñ§8‰H�gøÒ4aB-’Z4Âtæ*¾gǺ�þZœ„+¡BpøØI8š7åÓzEÙ¥�aŽc%?;A2™¤]Š…õ蘖››ž£Ä§ ,&Ž>ÞJ¡¤WÚÀƒ'¶¶]xþÝ!¼øÊëâ–aàµÐßfÇÃÛ¼uéy§×sØÞÓ‚Pp^Þe˜'Ѻ ‹†ŒH&ñ{_dû+ùdÒ^ž¤T®í9!ÀåyÎ^¾±nìþj€_k­àâ•똞 ÀÓ¹ ò^Õ%v·šõÅ8ø ‘„&,­y`YfTzÌ?3_Ìtz®-F� ?Q'Z†_áýœ �Gþö»?A$š/œJ¦R¹=ª_­ÕzâñÇ0pö}qAI»p^pÚqõLDz‡ 48%iÐ|ï^LŽ¡¥s“r$ <$À^�çÈíÈûHç@e(*€$Ïàüø=›{uÑ�P8‚‘ۣطg§fà…ýöWÚG Fo=Ζ­‚±àÔ9p°š ¢²nÂÄ¢qê5ÉfQS `EL€'¾¾@VÚ,Šâþ €Ã¬§V¨%såºúæ_ouxàmé„Þ`ÎWrm߬Øq¥&fÀ§ÃØõóâ–`|%“�ÚˆI[’óò6äJ-Êäûò"ZûséÉpxá½a¤ÒÊ©×oãò€J±ö­mõ�mû|^|ëÿûcÄSYÌ…3ð‡Ó˜�f§ó DŒº×Ÿ’ 7„m¿¨ùbM ôœ|*°pЪ´{-G ˜6á…wð¥Oí“õ®{ïøIüñŸüœnþéé¿A{k&&§ñßþÇßà½÷OÁ×ÖƒŸø]üô|GO¼€x$(ÒnûèCسsÛŠ@-͈s—° ¡oÛ§ÒŠ~‡ÍXúŒÂ`†ðb˜vwŒž;÷�Ìz%€)Œßž*^ॠ{=ÇÀlàOg©j>qi^?£b,åîÛŒ:}æù§-FÝ ºòŸ‰§ñós~äxhçyA�4D¨zqý øbu’�Š!BB0•0㧇/à·+ý¬?|ñgøó¿þì¾VÜóÄÿ‚Ÿ‚;u ßü»g�åxì‹èÙ•‘Æ3CÉܼrMô¾§†_Ã_ý{'ºÚ›«VóWS+xóÈ XœM0;ijÞ)“+õ ³Q/;/4O#æJ­ïÏ•$Ù—+fòÅ~€Ó¦Gl>K·õKzÕýÂ"ž­möRõÕ /»Å€Ð�wÁu‚ÁXž„ä-Ĉü�u RÊT>$ƒB•¤¾¨åxáï¡E#^?v�?´O}ç9<õ̳héÙŽ‡þÅ¿†NoD$C0r¡eÓ>ìxð³0Y%€ÞlÁöž�5cyöÈ8þígLp;튾€z�\‹�@éœéY?® ÞDëÎ�©&þ¥¿ÕXòüó¥ãá…0Å'ÂלV¬Ö“a˜Ë²Œ†@‰xZ\œÀe5È&Õ¨æóCÅŽ¦ ×èo³c5×ïá70xúݲߙޘ¤4yˆÖ†œ§N-._@ýŸŠðùó3þô«yð÷í~�|î…No(¾ŸÕéÃÁOþŒðÓ»KχS,¾óë›H§3*MSø5c&,mo~¬NgËfªä—έvÚL2éŸËå Å5 Ñu£ètº+ÙlVæå�� ¢«» øBo¯ÝXð ,OÚ3”çœF48V·Ñ…ÛåD_ƒ‰DF“EÉN7ˆØ'@$š€’?@d&P½þr¡ Qžjî<€Gÿ¥­}»Ú�•›Å(OûNÁ§ÓøZF ª�œ8s¯¿ó>¼]{Á²\YéBàqX%=+€h8¦�«BÖ¯pìØ“Óæ¤Ço�LJJX�§I2ÖòÑÞ m¯x…$¡%鸣cu¥ÿÒúòï~×Î)TJ’€xAe 4�Haæ ¡L%Vn[Õ�%JR_$Zúv)ŒV#Á/œÿlow”¦K€¼¤¿�oÜÂßýÃ�av6¡q˽”Eéx]Qö!󳋴[&a25­[È›8)=64x[|Q  ×±°™õ‚ðŸº*Ê«†ÓJé­:ØÖî\àv9ÑßbF"Óöý©åÁ©ü¼th¨JãR5àÓ2I™y‰r’Ö6¨TZ|$<¶­Ã® ìõ$-ŽÀñÉ|í©ïƒ1XÑyà �ô'ªÒßdÐÃl2’»òŸ·0G-ù?S诹~ €çq\zløê0E:ø&JÝÊnB©mû`¿�:VlµÖ¿þòÑe C¯c;—€J…¡|t¸ôeêÔüô Êʽ”7%³Aüz‹Þ&+ÜZM�• ……`ùäw‘ÌÝ÷ü&8ƒ™÷§KŸË*& ä×ùéyŠð$ï×ãäVøž—@4’@ ‚¯Á…¥„†š\& O‡¡<ÆKÝs.Î`Ðê1áÞ-^¬¥e6›ð›�݇d*��ñ0.�E‘ÊðR7d1"�Yž€ÐÛ/Ž(Fd>•cŠ¡Cº�@­5;OÑò¶¶Ú¡cPà«I<Ï㯿õ}Ãè¾÷ó0X\…n.´¬?¹àuYeÒ?•H"KÒ´çãëžL¦è¹TÊ–‚dXèí‘iø|®BÎ|Þñ×ì6'©§ûÊÒ\–>{ µª‰C+r]ŒÜäö6®ŒEpy,Št–§çÁ,M *zi®€2ðê–ÞK!D(aJ/š†¦`âõ6Yj èz�Á¹‹W1rg ûŸ€ÅÙ¤àõUú�^[�0 ÒŸf©ê?áyÃÉzÜ{+j9ò\ ç¤Ç/Ý×±ƒÀ¨×Ác7–íå¯Åñç0syÇâ_V³ ÷mmÀï=ÐŒ=]v°,(-Áÿ//)÷•Ùþ Ó‹y¹‰@ÊL@&”ð£j³B©¶Ð¤SNã&xûÊ,ü aUWë¬U”à•_†ÉÙGsJó×¥í¾h`5`1E pÀ?Ií÷1XH§_ßPøÈ·eÿݹkÈf²%u²À-‹ìÆPë CsüB0IãÆDëeÙ­f<¼½_z°{:í`PJ„!³÷ %2 T \+ /=æ&ivj*é‡ C± ¾óÆ0BáHUேÄÚþW¯ãæ­Ûðõ”´ú¦yþå>�fŸ_ÔRp’ÍMЀy«nh\qø³x]ßH¤01áÏO]Ü ­sIb@¹Y…L­¤�§ã°Þ–ÃfÆ£;›ð‡�´á@¯ :ô¶`’Â*èyZT@¥aÕ!¨ÖL´Ü¸5¢bÐÄóI¾ûÆ Y2P¥`¯¼úúaè-N8—¤¿p“5ÿo„4y‚ Àüñp0‚$µ0ùõ]C­­3g@ilxýêmÙ´Z�ÝXhR^j¨õÔÓët5Z±^W^#hÀ—nÅÞng‘è3 i ‡¼a¯Ð$„WŽÿ«g*id(3—Q�Ì &£<ÿæ•"�µ€¿ÞŽÁ;c“¸xõzä}M²r_ç­Ë´ƒ^¯Ë&"=‚ÙIj¿Ï ï®!€—^z)àMéñ‹§®ˆo–‚¯«Á¦(Mx•›R;ïm¶�Ó±XïËí°âc»šðå‡[±¯Ç †�ðJI@òGð AJ0Ð$õµikåÄÂ×OÄ,š�^Ï(ÁÒù¯¾~œÁ W[¿ä”°…ZÅf²ùŒ/BæÆýT×Ù‰_OÜ5P°id*ÍôØ ææeÕh��¶ŠìOšÍº¹Õ�»iyœ6||w3þè‘Vì.t5:é¥_ ÒTcåü€2A ù¼J=†Úo'ý,�5-È 0ĉs—àíÞ†ÕU~¢âðç´5ºÅýÿ@�ˆ%±à_¤„ÿ˜_×ó^Z0u¯"?0T´._¼).CE¾.Àfâ48þÄM(7 Jî¦åuÙñø¾üá#-hvåý%R[iº±bÃ^îXŽC°%*åB/–ÏË«Tí_NÈPú¾þù Ü�;)µþ4ÇE œ§E¤!B0=6G»? ˲¯ÜupäÈ“‹„@ 8÷Þy‰ã)™ºšìê’ƒ§û!hv›a7s²ônZM^'n�þæ&nÉ~ÒÑå¢ð¡¤>€W~ñÚò(}áµDÊ�k/… u £üzhå6ÎdƒNg Çý´�¥cÍ®BÙ;•qOÝ™¡Iÿãï¿ÿ�±»Ž ÿÜ Òc³S~ÌÌ.ˆÂ-„›Zìâ"·{•Uv3W¼yhÌ~·¬áƒðOÝQN æ´aé0¯ÞM˜/[ 5UX©n€îÔ±P~-À_§ýªdü�® t¶xD S ñHAˆê2«{TnµnXžÇ+d9�N n€üo5rhv›ËN´á)ZÀµñFg‚¢æn$‚©™YXn±S�§9ýè*Ù¹‚<%Èkpmá[µÈ�4%X øË•ôZΧ‚_šïOIò8,°[L¥×4œ‰ÛSTˆ0 yù®%€S§¾˜7¤ÇO¾{™LNÒËØÔêP¼™¨š€ÀÞ}óò‚ ô›Ez㬷•Íf1çŸ‡Åæ¦˜§Í¨°aH9‡ B!� ´˜¥ÏdY†JÞÕ„—ë, ©×_Mõ/œÓÕê)i·K©Ö<�±!*¼wüøSSw-äÍþYé±X$†×F%j!Ðá³Â¤×•MBá)7ïÈl#³TéO#�õ¦ÌÌú‘Ëñ0ÛÝ;åSƒyµ¨€x®`åAu_�bu'Ä wüqDâée™µŒˆ“}ÔTÿÒ¹z�- NIBàŸ^@"FëÌ<»÷Ϊ€ÁÐðK2–;ñîYISÊ|E`»³lÆŸR9ð[WCÈfse�¿µ‚©™Yè8=ô&«\ºËƇ•iÂ+_M ûaÊ�+”þŽ© ÁÑkAMÀ¯&9¨RS ¤šjSýAº[½…\"Œß¢ ùP:�zù®'€#Gþ,Ë0ä9éñ¡�a,̇dé¡[Ú�yu�êt5Þ½t3Î-&qúÆŒ"ÀÕH€¦~®¥õó_¾ ‹Ã­Ð�Ò%H­aQ¾ŠôçË$k)w"šâøP ¡XYà×à@ª?Ë=í^QÓHÆ“˜¥$ÿ‚ž;÷�ø]Oyû•ù^|N½Pja]¸pzŽÅ¦»jÓ µœö£×#H$Sªf€Ú1Úãj“ÁÛïËÿüÚ6ï‘;ùx-õ4 áÕëÔ¤~uÅC¥-�åñÞÍhEÀ¯•) ÿ­’~´�¶&Œ½ˆáqçæ$xJ{x†a¾·R÷ϪÀéÓOÝä9'ŸF,ž(Ü4´‰r�·Ê"’2YšÔ ÅÓxpN“ý¯•”œS+±æ‚øÏ_ù\�íØvïãòò`­©ÁåŠòJ’^ž&¬æ Zº:�(N‚>y+ŠùP\1¢S‹¡Ó@¨âj�xëíh(†—î�\&‡Ñã4Ûÿ̉ߺð¡!€BÄãëÒ#™tgOŠì%Bò³{šì*½êè}ô–Î{ÿfÁP´"PÛ´œ§¬FV·–>ó?ü×?G0Á¡OÿOÃJʃA·ýy…©ÂÂTb”H½™”©Ç¨2�L:KsŒÿíJ"oMÀ‰ß~ùÉA¢uü×Ç }ÄfO�·Ð%ŒPÕP,=¦2<Þ\ÐäÔú¨u¿Ò(G(ÿø£—qäØIìzä·`u7RÂ~e¢Ô(M{ÐÚ0„ ¬Ì!Hm‚ÒÿÒîÑ£ÃcPw¥f@5¦€´„W~‰6À€`Kw“ˆÉîík4é�Qƒ¡áå óEÂQ\8{]Ð_>ÜjâÐ×âK^9v-Í~;w;‚‰¹`E`-§úk?í†UÓ”ι9|_ûæ3hêêGÏÎû/Sïy ˆê”‚Z†hsjIŸûÐfGYÓ«VNA¥­¹É—Ÿ7A+÷•BKƒ « €¸ëõÔ�YÄ£´?òd=:ÿ®€ÓÉüÀ´ôøáŸA&�•Ü À®nO±H„טIJ´ŸÍñ8z=º, - Üûi•fKÇÿËW¾ žå°ÿ“¿Oé E)N« ²úzT@© HÑ!È—sjMÊ?:Í:lk5ijþQÏ„!‹Ù�ÝBr"ÐK³YØÚÓ,òI�är<†îÐ æyöû+�»5Ó#{xøt®£ã^À'„ÇSÉ4l'Ú:š†)æ_é99ž`&¸Ä¤LÑOPÜr«èƒ@$��Ã,›ù&Ü/7ZíX¹ÞòÂÏÒ~~ôÒÏѾ÷“p7uA8'H4%Ha`h鉇±Ð¦•²Ö°ì¹‚œC›0¥æ!éy_Ÿ }�ƪÓwk%˜œžÁBœ…Þh¥Úý ]m^´7¹‹vÿ’‰012ƒ‰Zá¾vòäS¯¯4îÖT—ŒXÌøm³²|�׎"•ÊH$9°³Ë³AG×x‰iÀ‹gíåx‚ŸœšÇ¥Áን:•$~µ>¯Ç½ÑBõsð”‰?´P/íPM}ÀÒçóµuÒL‹�©ÈÉZ­SP‹°¯»±à´b€ãXléjØýùóø�[W©Ò?DˆþoWskŠ._þZ )OŽãôñË’ÑQ:–Á¾>/½©¥è¦£Û¶ñ4�Ÿœ�àðñsëÆÈó<\.±�¢J/óÞ—s òê½I™4aªCp‚Ò¾„9Fs˜®3 MÂçs#¾8£ØÚÓ ƒA'núA€±á)Ä£IZâÏ×ëÕõw]�±§Lд€p8–W©DÐÛ쀯Ð>œ§¦«ßØàLxû¶?zõݪcÿµ%7¶×íD2¡Nâ& ©çŸ§·£w†,\¨XÀWâÔž&¬×A“ƒ´ÞÚ@<‘À_| ©è5Èf5¢«Å+²û !H'ÓºL•þ‹&÷�ÕÂÛš#€#GžKBþ‚–pøWÈn(0ÀýÛš ñÕr±yP¼YYƒñV<÷³#Èf³šÕúåzÿµÜд÷p¹œHÆ#”8½r4„Wj¦±>@šM¨6PT{† Ö1c€A‡Š³!Âx"�§Ÿ}Ó³´ô?Tœ$ŒìÙÒ‘ICÄ%¿Cw�Igh·ü_9òäâ–ÉÔð]Z^ÀÅã0=éLÎÍ_h—Í€í�nIúX“ í»ƒÍÓ* š ì솎edE?�_¥ÚþBQý3Ï�9óýg%7/C±Z?”À 7ž'§œÎößfÆ-‰'ÿþYôîÿ¾6™êO.É Ô ú2š€ð…ZUÝ_"[©/HD§—ƒÏÊjºvÕvÿ™õÏã›ßù'Œ�O£yËýhìÙ_P¥àϟϸww/ÌFƒÈé¦pãÒmš736vòËÑèLZd%Á¶fÁ€�š:—J&CÿŽvQ^ÿÉë-FeÉ&N«÷lmRôT«Ï‡Ò¦s¸1ª8ö_� @žçñ£—_«7¢yÓ>ÕÑá¼$ËOúµ�©` ¨Z}¯$†Jv6Bx\‹WT¢]Mx0—Í!–È sï§áiÛV 8^Þð£ð÷Öžæ‚vÈ‹Ê}ÓÉ4®œºI½Á“ÉÅ?™ž¾]Òr›ôÞÿÐi,eÓ`gf.O´¶îïdYv—ðÙL³SìØ×_¬XR¼¢É ÂI‰4“hDnH£û{p[¹º:Ë-BBá¾òµo£¥ÿxÚ·¿¡Àá,²Ó!Ò ¤^þòõ4»XF}€HPŽ @Úz‹±vÀ±L]ÔB¬V3º;Ú01‚0¬üKsA“×�[ÚK©¾Kµþ„Çå× Fe¿a.—ùå¹sÏ*¥ü2«¡ èÖ:øK{Úáhý†al†B0X-hëlyü €6¯“�bɬ8@ 2çYé½îéµÁnÒU]ü£f hõÀO~ú œ¾p;>òE°z£¤4†HuWˆ(É�…‚!­‘‡ „„=óo ž'ðXuhrèj>ê[H.§ †Ì<ƃLRæØÌÚÝ[˜ðËÀ?><…Û7&išž>ÿ¯B¡É˜ØW…Ö‚ À(€h›œ<ŠÇL» G_=ŠéÉ€¬¶œa<º»FŽ-Ó‹–SRa9š“€´”Wc d2üó/ßDcïèÍö|b¯”¬PÎK®TÄ« %* CdãÇ$ C”’ 4p£`€ð¸2‘¨)à•̆�[»±Ýx^ò�xp,ƒ;{ò~%’?Šáú…êM�þtlì”_˽¾’æ€n �_WN ˜�½:ÞÒ²ËɲúýRé9rí6vØ�^/úƒžEƒÓŒ‘™0øbØL¢ F"ÅNÂûûì0ØŠ€ÕæB�Ëå0rgï}p/½úk|û~ˆÙ¹úþ< &;¤C©¡ª (;üèá@y‚í9ªC�jI›wÁç©H}))„YìnÕÃÀ1uÑ„ä0rk×®_‡ÍÝV?‚ý;ºáqæË€yž/‚?›ÎâìѤ’rß^&ùÂ…<­d¥5€Õ&�ºÚ/?‰ŒŸöù¶}œaØá¥“)ÌNпgKѰ乷šô°›õ��H<á4³€‘ÄÍ�·Ø`Ð1+’ŽDñ_þâxòéçðâ+¯ãì囘OéakÙ†®½‡Í×.Ën$„vq’“<þ_6+�÷W"u¿€0gA<\%ÇˆÌæ!°›thuqu3r¹^ùÅÛxëÈÐ̰û:‹FåŽMmhorÀ_’üà .�¼A�ïÇó¹ñ¡¡7ÿ�d2”©èd¥¸ì~�Ö¿Óé81™¬ç­Ö¦ @$îCó‹ ¬½í²«ç²åmæ™…„Äñ'�‘‹�j >²Õ–�fÀkõМÿãÉgðÁé‹è<øôÝóiôøºwÃÑØ£Õ)ˆøCt)Ø©iÀÒLAEÐKŸ_Fà YМ|(÷\a?‘ⱻø¬�ª’C0•Jãù¿‚s¯ÂÕ²-[(ЇÞöl.Ôøó|©¿Á­kcž¦ÝãY¿ÿÆÿ>=}a¢Bɯô7¹[€¡]3 ƒ£A·»sÖ`°}BúÆ“#ðµ5Âãs¼†f�¥P‹@ 0>²ÍVJ·­±Pxìí#àù^A×þO ­ÿ~p‹ àòؾ4 Ì '@‰£ZA¯–X5ð!Qõ•¤¾Äˆ&sØÚ¬™dµØ¢±8¾÷�/bhd ¾î=hì=�¿AKƒ»·t�‘Bðû§ƒ¸zv˜z“G£þ¿¾~ýµwªúŠi«Ej`×DssƒCMMÛÝ:�a—ôÍG®£cK7l›Œw:|6„bi£iÅ(€T:–ÁÛ­gVj øçðŸþüë0:[°éþÏÉ@)È¥@§d;Ê¢M õ_AP�ÈHB|°!êž#½ðÜ2§ \;�jÉ4�n¯6Sír¬3Î Ü‚ÕÕ —Â{÷ô�£€?›Éáì{W‹$(ñ~><6öÞ¿ ÇjîºIþÕ"M1ÿ2Ä :/ $t:Ó‡£éq€9“±¦îLcË®Í`Y¶š<¸Y†Aw“�p áxZpó32º?šÃÁ.c!ñ£ö@B8N‡éñÛX€,§!� H#b+ŸR$&RôeÔ%‡ vàCAÝW ŠÈ!Ç2èôêË&OiuÎÌúqúÌYxZ·Àë²áЮ^p:VêC!]øÂƒX Dœ~ƒ26vúF �^WBЭ¢ô¯f£C(46o±xFÌfïc #Î¦Š†"˜™ôcÓÎM`YVtõX–Aw³‹Ñ£)QHPH©,�Ë 4;õusBð«·Žbrnö†nATéÝ@Ä£è!;½ ˆÿ'”(©¨aˆà¨ø•™¥.»Ü³»� ~Âó¸xòSAª'?¼ýWCCo¾_ÐWM#ЭAé_1ÌÏßšp:Û‚F£ãAé‡BÌΣoG¯¨–éKu5ÚIdÑyNÀBœÇ�Nc]€„,Cxê{?‚³u+lÞv¹í/ ó)›€Jµjã¢ì ”÷‘ÕWÓœ{tàS½ÿ" TS²°`:Ãc—yÙÕ–áHßùÇ‘LeqïǾ€Cû¶‚e!Êð[Êø»tê&fÇ ÿ¹ç_ùY•&«þ•&€%°2îkÞüþë7}¾-v½Þ´Cúá‹� !ôô÷€aXÙìh´!ËÌ-Æ‹¦‚Pñ�-\1üTK !?ý0.]FÏ}Ÿ£ã¨Ò_˜;C‹™6 i"t ’jêˆD]  ê>¨R_xxž`[«qYY�Éd ß{þ%,ÃxøñßÅ=ûwägNòâÏ#<�+g†1=:G½±“ÉÅ__ºôãgVô5Ë X-  °WD33—Ïù|[}z½i‹ô çæáŸ  w[¡�£–¼¤oõXa1r˜œ�‰\€Ó�m³ âëËw–¼Æ<þöéçaj胧k—ÜÆ'òŠ?% Ëµ¢É)(#B$‡Xò‰°<àÓ4ºPH í=f¶*éŸÍæðO/ücSxì‰ßÇþ=ùëNÿ¦îÐÁŸJE?¸|ù…¯"Š’õ¢ èVüåÌTp®â{ÌÌ žnlÜÒÁqÆé 1;9‡Þí½`u: ¼¼Æü…¶Û…ã]n=v·êâ8y¿|ë=tø,ôf‡¸üœ ”Däá>µz@�ÄCQñÅæ‘�²>h³ó!É PyÎgçÐä䪊ûÿôÕ7p}h¿ñù/cûŽ]…:1ø³™.Ÿ¼Žµ?�Ž]xñ/²Ùd¦I½iOÛ_7P È«|ÏÌÍ]?ÕаµO§3´Ó|Óc³èÙÖ–Ó¯æ’_Àa1 ³Ñ†©ùRé@ìj3 ÛË- üJÚÀ³?~�hm»£€]îý—›rÉNKö!4?�Ì[(qȉŽC.íeZØí@îݧ�ƒÔ9H‰X�¬( Uú¿qø8®^Âo}áгi» Ì':‹ \ƒ:¨þ+W®¼ôßÓéXJX— ò»J`*yÕ¯#$‡……›'=žÍ›8ÎÐ&sÚ,†1zs ][»a0ê!ì#@õ:lju²Sx°×·…­yPp1Œgž{ �Û�ÅÓªBí„öSŽ”+’Iy‘)¡z9Y�r>*ðiv¾²)@ r,°¥ÅXQOÅSg/áÒÕøìŸC[÷6™§È¤=l�ZÜ“|ppð•?O¥ÂI�`%€œT°¿n ¦àÖzn.—!ssƒx½›;8ÎØ)ýR‰XÃW†ÐÖÛ‹Í$»œ,à«ÉË`o –©}à«oÁ•·ÑuÏoŠœ¢MfS × HŠDŽÿÔÈÈÑ�—³«¹ Ø¡L DôT  ÁNA=¨ú2 AD‘Êš›„J»±ºÛ|8°³'?¤³ òI%a&�Áõ #¸yåø?ÙlræÖ­Ã_›ž¾<´‚ ¯ö=Ö`µOÛO$‚1¿ÿúq·»³Q¯·tоôb ˆS'oÀæfÑÖÜ ¨â›L&Øl6d³Y¤ÓéÒ€Ètß~öEøÂèØ÷8VWFÊW"ù¥j¼6é!z.€†p (²PÆPð馭�hsƒwv£µÑ v©sEå·qîèUÌÏ-*Þ¤‰DðâÕ«?ûF$2½° À¯æsÖ T VÚÆÖêýx>ÃÏÌ œ3›�!‹Å»`d¢>�Êàâ¹Q\Fk‹ n—C¦ BÀ²,l6, 2™ bñ8¾õ½áòµ!´îþ8,îV�‘"'QìPVúËÀ.•Ì")¯=HÕ.”‚ ’¬¥]µ3àqX°w{ú:¡çtG_þµÙt7î`ðì02…¡±rm|(4úË��Ÿý —K¦WÔË!�uC¨¡¯ù¶°02šËenØíÍÛXVg¡ñׂ?‰“'o –� ½¥F£�jèt:X­V;uç. ÂÖuž®½Pô+Hu¢ÅT (�]*áiö¿Ы…!wÞU |¨ªû.»»¶´£¿·f£¾�SA—úósA\8>ÿÔ‚J˜8³81qꙑ‘£ïç§�È@¶VÉ`Ý„…„£¹å×2÷«:ÏhtØúû?óe‹Åw¯Ú?ÒÜåÄo|vvoïƒÉh”›¡0;{s¼Wï03-õËr:Ñ”†Iá@:èÕÃ�TPÓÌMÀWz]Vô¶7 ÑëÕ*ˆ¤~á=’ñn^¾ƒ©Ñ9Õ«™H/ݸñú‰…°Š—=WÅs¹xýºÓê!Í¡°_ÉyÅ¿s¹Tvvvà<Çf¬Ö†í ÃêiÿH4”ÂÅó£¸62 ‹E·ËNP]h21åÂìë…Íl@O‹ �Ž|¶Z4Yª†Sô NB¹ô'2¿�šI@7DSpAʆ•¥== P ð4y�ØÛ߉M]M°š�ES /«ÅRŸÏŒMáâ‰kŠá½Ydæç‡~68øÊËÙl"¥"e•$0©ò¹Z¾ë� ¨Ë0e¿’çŠÇǦ£Ñ™³v{K+Ç™ìF,’¸:8ƒ©à"L›ÕŽÓ!O`tlÐÛÁê Lm>;z[]Ðs ¢ñ2Ùœ¦h¡�£$r`+:…€×ÛöPL gª›Bà› ºÛ|ØÛ߉®V/L®x5®`zÌ� Ç13îWͬL¥"·††Þúû©©ó ó*À×J |�_ïÿj™@ Ú�£Ša"ÿ¦žÓÕõÐÁææ�¿Ç²œMísùìØ¼£›z<‡ÂÈÀÕ¾³ƒšLL"¸5¹€)$ckM "eL™Y W÷‰fs@Cz°Š_@½yäV4ºèhñ ÉëÈß�DÜ}¨¤î^Ïæ‚¾2ŠÅùˆúU#¹äüü𯆆Þ9ðjj¹Öc•ü]k³bÝS0×èjˆuf³ËÖ×÷‰ÏÙíM•û½ÍnlÝӉ¡3˜dZm¥39L"Ÿ a:°D¤Œ+€h Ø+³ÿ˃ž&íÕÃ�Â÷w;,hip¡¥Á “A/Ó¨À' �0†¯ŒbA%¬'°õ¯¿ób4:³ ìõ �Z‘ƺÖPc©]+ð—mA€mkÛßßÚºÿ gj+÷O6´6`û¡hïiÃ2eI ¨¢¦³˜˜ aÒÆÜB 9ž—€QYú�Ò_þEˆ¬‰© HïD?�exœV4zíhmpÁhàÚ‚øÒØ?àŸžÇȵ Åz}áÊf“sSS_™œ<{µ�/W3¨7`½Vغj€®¾±ºžž‡ïklìÿ-–ÕÛËý£6§ ÛnCß¶èô\%.ärs‹QL"˜��"KRÍ 5éOÕd^ØVL½:Pz¼l× Ø,Fø—G�Œ9’ͦÒ€¾­ Z¨†p·³»¼R]ÐÕÏ1›�Öžž�~Úáh}X)Z \£½»ú°iGn‡ �ç¤39c˜ÅXŒc1/8µ…5^Í�5‘ƒžÓ±pÚMp;¬ð8­ð8,Ðë¹¢'„žH~<’ÄÄíLÜšA:•Öp[ñ¹pxæä­[‡�L.F4½žÄPkm�ÜM€KðåHu-d ¸ÙlMî®®‡>åp4=°š"+ m Ø´k:z[%Z�‚`'ÊtM¤Ž&Š&Š&K¤O¤�\Êz“ÌÔRH“èÒÎAN«Ù‹Ù»Å‡Í‡Õ ‹É ¢Zø¥À@.›ÅìÄ<&nM«¦íŠ}+„�FgÏŽ�3žž¯øË%‡z˜´sp·VPª/ äZ6‡£ÕÛÙùÀãv{Ó!€ÑDœ^‡¶¾vtnî@KGX«r“Wb÷Ù�X<�D*�T:‹T&‹t&‹t:‹t&Wj‘�ˉ^¯cY,¹,ôz zŽÍïs:˜�XÌzp,+n'.ë)P žPÆBà“ÿlÓ£~ÌMú‘Íä4|,6walìÄ›¡Ð¸¿À¯1TC ¸[ õpÖ¡ºqcå6-K›­ÙÓÙyè#GÛ £3i½z‡ö¾´õ¶¢±Õ½QO“t ¦?Ñt.!êÝ�x•H€ðµâ Bô4á’OXzm.“E`v3~Ì��Éd5ß0<ŸKG"Ó§ÇÇϼ‰Lâø´�Ô ìõ$Üí€:‚½–’Ÿ©ä9£Ñnêè¸ÿA�§ûQ�ÎஈYÞfZº[ÐÜÑ—7ß?€@+I{º÷_–{ Í,äé­ÅˆBš°ª´ ÆàŸY€rA¨Ø•WëÊåÒ‘ÅÅÑ÷ÇÆN|�L†c*À&u"�å’C¥ÎEòa!f }¹àÍ*`Y=×Úºo§Ï·é~“ɳ�aÀVzq F¼-^øZ|ð6¹áö9¡Óë(¸&ŠvQÒ /ù“(hâ� Â0!¡t æ³<ÂÂΜ #]UK=B‰ÐЭSç¯ð|*[ðÉ ’@µä°*à_kPŽVÄŽ¯üªÀ§íÛl�®¶¶ƒ÷;�m÷êtFOÕ6ËÀá²ÃáuÂésÀévÀá¶Ád1Ë~U"�"4)‰œDÒ]h"(�ž’É$"‹q„#ˆ,F ÆYŒU,á%Ò>Ož™œ¼xº æÓJbù2ûk…*%²R€[‹« ×Ú–W;—Ñø7£v¬©iG�Ï·eŸÕÚ¸W§Ó;kâLaYXXìVX&XlV˜ÌFŒzè zMz èõ¬¨/ª¨ _J³M§³H§3Ȥ2H¥ÒH'3H§ÒHĈGˆ…ˆG˺ضÏD£Qÿåùù›gfG )»JÀ¯h!‚•3V ük™€úKf•€OydÙ––�}Oß>‹Å»�ãLÞ»È,�dFb.Ç× Ð%}0\ŸŸ¿uyvöÊÏË@¯•êEõÖêîí_�€5dÓ—›h\%ðéû.W»ÏëÝÜo³5o1›]ÛFgÄ]´!|:‹Fg®.,ŒÞ ®�£òJ¹zÁj’¿¿³î«gÓkµók|Úß,kä¶t9­½‹¯×d²w—«J\k‹ç³ñT*r'Ÿ¿OŽÃcÙl" zôZ&‚z‘YMpa�“ƒú;ôê|EЗÛ\®®&‡£­Ýjõ¶�ŽƒÁÚ¢Ó›N‡b©T(–JŤS4ˆÊ£ÒËzh+Aµ$²–À´^W-œy« üåöFD% ²¯Y (³_éxëå�Áz!²Ö¤þÝBÕ˜ ªOâ©øZAÏ¢2‰¿\¨ô÷'È Úùö•šµö Ô›jÖÄsƒÊ›ÕzòküJ¤}5ÒU€¬$T þJÉ@ Ð@]K" kUÝ¿› @É?°ÚÀ¯µÊ¯FZ ZÐþJ Öæ@µ£»êMë0wãZ/À¯Fí/§îWcû3€¹ þjÍ�Z Þ,G•ÁºÊݼVøË ï-×ö—ÓêPºH…Î@…Ùdu‰ hÑ*5 jAë †ÅÔøõ¶õ—òcjhûk5ª!€Z˜µ6 *%²Þ�ñaZŒ  µH÷z«ü•ªý•Ÿ©”s¢ _P½Cp%‰€Ü-€ø°®Z¿V*¿©_�䯅Pi$@K„ ^æ@­}5›È»Ak[+X®­_¯Á§ËUý™eþþZ��ÕÀJ™Õ¹›oþ�UÞDXNx¯Vj½T¦à_.(�Xž°\“àCqÃo¬Ê5ƒ•RûWÊö¯4 ¸\_Àj˜+6r{ƒî^2¨èk‘íW+Û¿š0`= –怓àC}co¬êˆËQû«QýëMkÁ¸æÀÆÚ €š®zªýõPý™:ƒ¿–P+m`cmÀª˜ kÕögÖ Ô ükƒÖ,!TªöWBJÇj¥T[PkÀà7ànÔ°‚Péï_M(p9€ ɾAöße9 Å Ðò\¥EAÕ6€¾A«r‚€F2Ðò{k jõ”;¶±6`c­Ãß|Äkcm¬�µ±6ÖÆÚXkcm¬�µþÖÿÜ�% 2{}IEND®B`‚icoextract-0.3.1/tests/testapp16-base.rc000066400000000000000000000014261526654623600201310ustar00rootroot00000000000000#include 1 VERSIONINFO FILEVERSION 0,1,0,0 PRODUCTVERSION 0,1,0,0 FILEOS 0x00010001L // VOS_DOS_WINDOWS16 FILETYPE 0x00000001L // VFT_APP BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" // 04E4 (Windows ANSI) BEGIN VALUE "CompanyName", "The icoextract authors\0" VALUE "FileDescription", "icoextract Test Application (Win16)\0" VALUE "FileVersion", "0.1.0\0" VALUE "InternalName", "testapp16\0" VALUE "ProductName", "icoextract\0" VALUE "OriginalFilename", "testapp16.exe\0" VALUE "ProductVersion", "0.1.0\0" END END BLOCK "VarFileInfo" BEGIN // 0x04E4 (Multilingual ANSI) VALUE "Translation", 0x0409, 0x04E4 END END