pax_global_header00006660000000000000000000000064150112046340014506gustar00rootroot0000000000000052 comment=9b96659df7fb9e0f229d6af49c2b1d572cb49166 android-file-transfer-linux-4.5/000077500000000000000000000000001501120463400166725ustar00rootroot00000000000000android-file-transfer-linux-4.5/.editorconfig000066400000000000000000000003521501120463400213470ustar00rootroot00000000000000# EditorConfig is awesome: https://EditorConfig.org # top-most EditorConfig file root = true # Unix-style newlines with a newline ending every file [*] end_of_line = lf insert_final_newline = true indent_style = tab indent_size = 4 android-file-transfer-linux-4.5/.github/000077500000000000000000000000001501120463400202325ustar00rootroot00000000000000android-file-transfer-linux-4.5/.github/.editorconfig000066400000000000000000000003551501120463400227120ustar00rootroot00000000000000# EditorConfig is awesome: https://EditorConfig.org # top-most EditorConfig file root = false # Unix-style newlines with a newline ending every file [*] end_of_line = lf insert_final_newline = true indent_style = space indent_size = 2 android-file-transfer-linux-4.5/.github/workflows/000077500000000000000000000000001501120463400222675ustar00rootroot00000000000000android-file-transfer-linux-4.5/.github/workflows/actions.yml000066400000000000000000000106731501120463400244610ustar00rootroot00000000000000name: Android File Transfer for Linux (and macOS!) on: [push] jobs: Linux: runs-on: ubuntu-22.04 steps: - name: Creating contiguous... uses: ncipollo/release-action@v1 if: github.ref_name == 'master' with: name: "The latest and greatest" body: This is the latest build of the current development branch. Please try any issues using this build before reporting any problems. token: ${{ secrets.GITHUB_TOKEN }} tag: continuous prerelease: true allowUpdates: true removeArtifacts: true - name: Creating release... uses: ncipollo/release-action@v1 if: github.ref_type == 'tag' with: token: ${{ secrets.GITHUB_TOKEN }} omitBody: true omitName: true allowUpdates: true removeArtifacts: true makeLatest: true - name: Install Dependencies... run: | sudo apt-get update sudo apt-get -y install qtbase5-dev qt5-qmake qttools5-dev qttools5-dev-tools libgtk2.0-dev libfuse3-dev libfuse2 libmagic-dev libtag1-dev libssl-dev ninja-build cmake - name: Checking out sources... uses: actions/checkout@v4 - name: Configuring... run: cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=appdir/usr -B build -GNinja . - name: Building... run: ninja -j$(nproc) -C build - name: Installing... run: ninja -C build install - name: Creating AppImage... run: | sed -i -e 's|^Name=.*|Name=Android File Transfer For Linux|g' build/appdir/usr/share/applications/android-file-transfer.desktop wget -c -q "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" chmod a+x linuxdeployqt-continuous-x86_64.AppImage unset QTDIR unset QT_PLUGIN_PATH unset LD_LIBRARY_PATH export VERSION=$(git rev-parse --short HEAD) ./linuxdeployqt-continuous-x86_64.AppImage build/appdir/usr/share/applications/*.desktop -bundle-non-qt-libs -appimage find build/appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq - name: Uploading contiguous artifacts... uses: ncipollo/release-action@v1 if: github.ref_name == 'master' with: allowUpdates: true omitBody: true omitName: true token: ${{ secrets.GITHUB_TOKEN }} artifacts: ./Android*.AppImage* tag: continuous - name: Uploading release artifacts... uses: ncipollo/release-action@v1 if: github.ref_type == 'tag' with: allowUpdates: true omitBody: true omitName: true token: ${{ secrets.GITHUB_TOKEN }} artifacts: ./Android*.AppImage* MacOSX: runs-on: macos-13 steps: - name: Install Dependencies... run: | brew tap homebrew/cask brew install qt@5 homebrew/cask/macfuse taglib openssl@1.1 cmake ninja libmagic - name: Checking out sources... uses: actions/checkout@v4 - name: Configuring... run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=$(brew --prefix qt@5) -DOPENSSL_ROOT_DIR=$(brew --prefix openssl@1.1) -DOPENSSL_LIBRARIES=$(brew --prefix openssl@1.1)/lib -DCMAKE_INSTALL_PREFIX=appdir -B build -G Ninja . - name: Building... run: ninja -j$(sysctl -n hw.ncpu) -C build - name: Installing... run: ninja -C build install - name: Packaging... run: | mv appdir/android-file-transfer.app appdir/Android\ File\ Transfer\ for\ Linux.app git clone https://github.com/andreyvit/create-dmg.git cd create-dmg ./create-dmg --volicon "../osx/android-file-transfer.icns" --icon-size 96 --icon "Android File Transfer for Linux" 110 100 --app-drop-link 380 100 AndroidFileTransferForLinux.dmg ../appdir/Android\ File\ Transfer\ for\ Linux.app - name: Uploading contiguous artifacts... uses: ncipollo/release-action@v1 if: github.ref_name == 'master' with: allowUpdates: true omitBody: true omitName: true token: ${{ secrets.GITHUB_TOKEN }} artifacts: create-dmg/*.dmg tag: continuous - name: Uploading contiguous artifacts... uses: ncipollo/release-action@v1 if: github.ref_type == 'tag' with: allowUpdates: true omitBody: true omitName: true token: ${{ secrets.GITHUB_TOKEN }} artifacts: create-dmg/*.dmg android-file-transfer-linux-4.5/.gitignore000066400000000000000000000001141501120463400206560ustar00rootroot00000000000000*.ninja* cmake_* CMakeFiles* CMakeCache* nbproject build *.user doc .vscode android-file-transfer-linux-4.5/CMakeLists.txt000066400000000000000000000174471501120463400214470ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10) project(android-file-transfer) set (CMAKE_CXX_STANDARD 11) file(READ ${CMAKE_SOURCE_DIR}/README.md README_MD) string(REGEX MATCH "version-([0-9a-fA-F.]+)-" _VERSION ${README_MD}) set(AFT_BASE_VERSION ${CMAKE_MATCH_1}) find_package(Git) execute_process(COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_SOURCE_DIR} rev-parse --short HEAD OUTPUT_VARIABLE AFT_REVISION OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE GIT_STATUS WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) if (GIT_STATUS EQUAL 0) set(AFT_VERSION "${AFT_BASE_VERSION}-${AFT_REVISION}") else() set(AFT_VERSION "${AFT_BASE_VERSION}-snapshot") endif() message(STATUS "version: ${AFT_VERSION}, base version: ${AFT_BASE_VERSION}") message(STATUS "building for ${CMAKE_SYSTEM_NAME}") if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(USB_BACKEND_DARWIN TRUE) set(CMAKE_OSX_DEPLOYMENT_TARGET 10.13) find_program(OTOOL_BIN otool) endif() if (${CMAKE_SYSTEM_NAME} MATCHES "Haiku") set(USB_BACKEND_HAIKU TRUE) endif() set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) include(FindPkgConfig) include(CheckFunctionExists) include(CheckIncludeFiles) find_package ( Threads ) option(BUILD_FUSE "Build fuse mount helper" ON) option(BUILD_MTPZ "Build with MTPZ support" ON) option(BUILD_PYTHON "Build python module" ON) set(BUILD_PYTHON_VERSION "" CACHE STRING "Force specific python version to build") option(BUILD_TAGLIB "Build with taglib support" ON) if (BUILD_PYTHON) find_package (Python ${BUILD_PYTHON_VERSION} COMPONENTS Interpreter Development QUIET) endif() if (BUILD_SHARED_LIB) set(LIB_NAME mtp-ng) else() set(LIB_NAME mtp-ng-static) endif() if (BUILD_FUSE) pkg_check_modules ( FUSE fuse3 ) endif() if (BUILD_TAGLIB) pkg_check_modules (TAGLIB taglib IMPORTED_TARGET) if (TAGLIB_FOUND) add_definitions(-DHAVE_TAGLIB=1) endif() endif() if (FUSE_FOUND) message(STATUS "fuse found, building mount helper") add_definitions(${FUSE_CFLAGS} -DFUSE_USE_VERSION=35 -DFUSE_DARWIN_ENABLE_EXTENSIONS=0) endif() if (PYTHON_FOUND) if (NOT HAIKU) set(CMAKE_POSITION_INDEPENDENT_CODE ON) endif() find_package(pybind11) if (NOT pybind11_FOUND) set(CMAKE_REQUIRED_INCLUDES ${Python_INCLUDE_DIRS}) check_include_files("pybind11/pybind11.h" pybind11_FOUND LANGUAGE CXX) set(CMAKE_REQUIRED_INCLUDES) endif() if (pybind11_FOUND) add_subdirectory(python) else() message(WARNING "pybind11 not found, skipping python bindings") endif() endif() check_include_files (magic.h HAVE_MAGIC_H) check_library_exists(magic magic_open "" HAVE_LIBMAGIC) if (HAVE_MAGIC_H AND HAVE_LIBMAGIC) message(STATUS "libmagic found") add_definitions(-DHAVE_LIBMAGIC) list(APPEND MTP_LIBRARIES magic) list(APPEND MTP_SHARED_LIBRARIES magic) endif() option(BUILD_QT_UI "Build reference Qt application" ON) option(BUILD_SHARED_LIB "Build shared library" OFF) option(USB_BACKEND_LIBUSB "Use libusb-1.0" OFF) if (USB_BACKEND_LIBUSB) message(WARNING "WARNING! You're using libusb, this is known to be broken -- large memory consumption, violating kernel memory limits and bugs. Continue at your own risk") endif() add_definitions(-Wall -pthread) add_definitions(-D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64) if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() if (CMAKE_BUILD_TYPE STREQUAL Debug) add_definitions(-ggdb) endif() set(SOURCES mtp/log.cpp mtp/ptp/DataTypeCode.cpp mtp/ptp/Device.cpp mtp/ptp/DeviceProperty.cpp mtp/ptp/EventCode.cpp mtp/ptp/Messages.cpp mtp/ptp/ObjectFormat.cpp mtp/ptp/ObjectProperty.cpp mtp/ptp/OperationCode.cpp mtp/ptp/PipePacketer.cpp mtp/ptp/Response.cpp mtp/ptp/Session.cpp mtp/usb/DeviceBusyException.cpp mtp/usb/BulkPipe.cpp mtp/usb/Request.cpp mtp/backend/posix/FileHandler.cpp mtp/backend/posix/Exception.cpp mtp/metadata/Metadata.cpp mtp/metadata/Library.cpp mtp/mtpz/TrustedApp.cpp ) set(VERSION_SRC ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/version.cpp) configure_file(mtp/version.cpp.in ${VERSION_SRC}) list(APPEND SOURCES ${VERSION_SRC}) if (USB_BACKEND_LIBUSB) pkg_check_modules(LIBUSB libusb-1.0 REQUIRED) add_definitions(-DUSB_BACKEND_LIBUSB) list(APPEND SOURCES mtp/backend/libusb/usb/Context.cpp mtp/backend/libusb/usb/Device.cpp mtp/backend/libusb/usb/DeviceDescriptor.cpp mtp/backend/libusb/usb/Exception.cpp ) list(APPEND MTP_LIBRARIES ${LIBUSB_LIBRARIES}) elseif (USB_BACKEND_DARWIN) find_library(CORE_LIBRARY CoreFoundation) find_library(IOKIT_LIBRARY IOKit) list(APPEND MTP_LIBRARIES ${IOKIT_LIBRARY} ${CORE_LIBRARY}) list(APPEND SOURCES mtp/backend/darwin/usb/Context.cpp mtp/backend/darwin/usb/Device.cpp mtp/backend/darwin/usb/DeviceDescriptor.cpp mtp/backend/darwin/usb/Exception.cpp mtp/backend/darwin/usb/Interface.cpp ) elseif (USB_BACKEND_HAIKU) add_definitions(-DUSB_BACKEND_HAIKU) list(APPEND SOURCES mtp/backend/haiku/usb/Context.cpp mtp/backend/haiku/usb/Device.cpp mtp/backend/haiku/usb/DeviceDescriptor.cpp ) list(APPEND MTP_LIBRARIES device) else() list(APPEND SOURCES mtp/backend/linux/usb/Endpoint.cpp mtp/backend/linux/usb/Context.cpp mtp/backend/linux/usb/Device.cpp mtp/backend/linux/usb/Interface.cpp mtp/backend/linux/usb/DeviceDescriptor.cpp ) endif() #Always add TrustedApp to avoid polluting client code. list(APPEND SOURCES mtp/mtpz/TrustedApp.cpp ) if (BUILD_MTPZ) include(FindOpenSSL) if (OPENSSL_FOUND) set(_BUILD_MTPZ TRUE) message(STATUS "building with MTPZ support") add_definitions(-DMTPZ_ENABLED) list(APPEND MTP_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY}) list(APPEND MTP_SHARED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY}) else() message(WARNING "building without MTPZ support, openssl not found") endif() endif() if (BUILD_SHARED_LIB) set(LIB_NAME mtp-ng) else() set(LIB_NAME mtp-ng-static) endif() if (BUILD_SHARED_LIB) add_library(${LIB_NAME} SHARED ${SOURCES}) set_target_properties(${LIB_NAME} PROPERTIES SOVERSION ${AFT_BASE_VERSION}) target_link_libraries(${LIB_NAME} PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${MTP_SHARED_LIBRARIES}) if (USB_BACKEND_LIBUSB) target_link_libraries(${LIB_NAME} ${LIBUSB_LIBRARIES}) endif() else () add_library(${LIB_NAME} STATIC ${SOURCES}) target_link_libraries(${LIB_NAME} PRIVATE ${MTP_LIBRARIES}) endif () list(INSERT MTP_LIBRARIES 0 ${LIB_NAME}) install(TARGETS ${LIB_NAME} LIBRARY DESTINATION "lib${LIB_SUFFIX}" ARCHIVE DESTINATION "lib${LIB_SUFFIX}") target_include_directories(${LIB_NAME} PUBLIC ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/mtp/backend/posix) if (_BUILD_MTPZ) target_include_directories(${LIB_NAME} PRIVATE ${OPENSSL_INCLUDE_DIR}) endif() if (TAGLIB_FOUND) target_include_directories(${LIB_NAME} PRIVATE ${TAGLIB_INCLUDE_DIRS}) target_link_libraries(${LIB_NAME} PRIVATE PkgConfig::TAGLIB) endif() if (USB_BACKEND_LIBUSB) target_include_directories(${LIB_NAME} PUBLIC ${LIBUSB_INCLUDE_DIRS}) target_include_directories(${LIB_NAME} PUBLIC mtp/backend/libusb) elseif (USB_BACKEND_DARWIN) target_include_directories(${LIB_NAME} PUBLIC mtp/backend/darwin) elseif (USB_BACKEND_HAIKU) target_include_directories(${LIB_NAME} PUBLIC mtp/backend/haiku) else() target_include_directories(${LIB_NAME} PUBLIC mtp/backend/linux) endif() if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(MACOSX_BUNDLE_LIBS) if (OPENSSL_FOUND) list(APPEND MACOSX_BUNDLE_LIBS /usr/local/opt/openssl@1.1/lib/libcrypto.1.1.dylib) endif() if (TAGLIB_FOUND) list(APPEND MACOSX_BUNDLE_LIBS /usr/local/opt/taglib/lib/libtag.2.dylib) endif() if (FUSE_FOUND) list(APPEND MACOSX_BUNDLE_LIBS /usr/local/lib/libosxfuse.2.dylib) endif() set(MACOSX_BUNDLE_LIBS_INSTALL) set(MACOSX_BUNDLE_ROOT_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_PROJECT_NAME}.app") message(STATUS "bundle root dir: ${MACOSX_BUNDLE_ROOT_DIR}") endif() add_subdirectory(cli) if (FUSE_FOUND) add_subdirectory(fuse) endif() if (BUILD_QT_UI) add_subdirectory(qt) endif() add_custom_target(uninstall COMMAND xargs rm < install_manifest.txt) android-file-transfer-linux-4.5/Doxyfile000066400000000000000000000256511501120463400204110ustar00rootroot00000000000000# Doxyfile 1.8.9.1 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "Android File Transfer for Linux (and Mac OS X)" PROJECT_NUMBER = 3.0 PROJECT_BRIEF = "Android File Transfer for Linux — reliable MTP client with minimalistic UI similar to Android File Transfer for Mac." PROJECT_LOGO = OUTPUT_DIRECTORY = CREATE_SUBDIRS = NO ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = NO STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 4 ALIASES = TCL_SUBST = OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES AUTOLINK_SUPPORT = YES BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES INLINE_GROUPED_CLASSES = NO INLINE_SIMPLE_STRUCTS = NO TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = NO EXTRACT_PRIVATE = NO EXTRACT_PACKAGE = NO EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO HIDE_COMPOUND_REFERENCE= NO SHOW_INCLUDE_FILES = YES SHOW_GROUPED_MEMB_INC = NO FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_MEMBERS_CTORS_1ST = NO SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO STRICT_PROTO_MATCHING = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_FILES = YES SHOW_NAMESPACES = YES FILE_VERSION_FILTER = LAYOUT_FILE = CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- INPUT = mtp INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.h *.cpp RECURSIVE = YES EXCLUDE = mtp/backend EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = mtp::impl EXAMPLE_PATH = EXAMPLE_PATTERNS = EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = YES INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO REFERENCES_LINK_SOURCE = YES SOURCE_TOOLTIPS = YES USE_HTAGS = NO VERBATIM_HEADERS = YES CLANG_ASSISTED_PARSING = NO CLANG_OPTIONS = #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = doc/html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 220 HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 HTML_TIMESTAMP = NO HTML_DYNAMIC_SECTIONS = NO HTML_INDEX_NUM_ENTRIES = 100 GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project DOCSET_PUBLISHER_ID = org.doxygen.Publisher DOCSET_PUBLISHER_NAME = Publisher GENERATE_HTMLHELP = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO CHM_INDEX_ENCODING = BINARY_TOC = NO TOC_EXPAND = NO GENERATE_QHP = NO QCH_FILE = QHP_NAMESPACE = org.doxygen.Project QHP_VIRTUAL_FOLDER = doc QHP_CUST_FILTER_NAME = QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = QHG_LOCATION = GENERATE_ECLIPSEHELP = NO ECLIPSE_DOC_ID = org.doxygen.Project DISABLE_INDEX = NO GENERATE_TREEVIEW = NO ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES USE_MATHJAX = NO MATHJAX_FORMAT = HTML-CSS MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest MATHJAX_EXTENSIONS = MATHJAX_CODEFILE = SEARCHENGINE = YES SERVER_BASED_SEARCH = NO EXTERNAL_SEARCH = NO SEARCHENGINE_URL = SEARCHDATA_FILE = searchdata.xml EXTERNAL_SEARCH_ID = EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4 EXTRA_PACKAGES = LATEX_HEADER = LATEX_FOOTER = LATEX_EXTRA_STYLESHEET = LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = RTF_SOURCE_CODE = NO #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_SUBDIR = MAN_LINKS = NO #--------------------------------------------------------------------------- # Configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook DOCBOOK_PROGRAMLISTING = NO #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = . INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration options related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES MSCGEN_PATH = DIA_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES DOT_NUM_THREADS = 0 DOT_FONTNAME = Helvetica DOT_FONTSIZE = 10 DOT_FONTPATH = CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO UML_LIMIT_NUM_FIELDS = 10 TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png INTERACTIVE_SVG = NO DOT_PATH = DOTFILE_DIRS = MSCFILE_DIRS = DIAFILE_DIRS = PLANTUML_JAR_PATH = PLANTUML_INCLUDE_PATH = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES android-file-transfer-linux-4.5/FAQ.md000066400000000000000000000052261501120463400176300ustar00rootroot00000000000000# FAQ ## Is it possible to use cli tool for automated file management? Yes. There's two options of achieving this. 1. Pass every command as **single** command line argument, e.g. ```aft-mtp-cli "get Pictures" "cd Pictures" "ls"```. If you need quoted argument for commands, you have to escape them, e.g. ```aft-mtp-cli "get \"Pictures\""```. 2. Pass -b command line option and feed your script to stdin. ## How to unmount my device? Run ```fusermount -u ``` ## I'm getting «ioctl(_fd, USBDEVFS_CLAIMINTERFACE, &interfaceNumber): Device or resource busy» or «Device is already used by another process» exception/message box right after you started the application. This clearly indicates that some other process is accessing MTP device right now. You could do the following steps to find the offending process: * Open you console emulator (gnome-terminal, konsole, whatever) and type: ```lsusb``` (sudo apt-get install usbutils if it did not start) and find your device in its output, for example ``` Bus 006 Device 070: ID 18d1:4ee2 Google Inc. Nexus 4 (debug) ``` * Start fuser ```sudo fuser /dev/bus/usb//``` (sudo apt-get install psmisc if it did not start) * It should output something like this: ```/dev/bus/usb/006/070: 23253 24377``` (actually, there could be more of them after semicolon, like : 24377, 24378, …) so, 23253 and 24377 are the pids of the processes which opened your device. * So, finally run: ``` ps -x -q 23253 23253 ? Sl 0:00 /usr/local/bin/android-file-transfer ps -x -q 24377 24377 ? Sl 21:14 adb -P 5037 fork-server server ``` Usually, adb is not offending process, because it uses another interface, so the /usr/local/bin/android-file-transfer is the one ## No MTP device found, but it's listed in lsusb Maybe you don't have sufficient privileges to access usb device under /dev/bus/usb First, try checking what bus and device numbers your device has by running lsusb command and finding your device in its output. For instance, my old Nexus 5 phone: ``` Bus 010 Device 003: ID 18d1:4ee2 Google Inc. Nexus Device (debug) ``` Note the bus/device number in row with your device Then, go /dev/bus/usb/ and check file there. (010 and 003 respectively in my case). ``` ls -l /dev/bus/usb/010/ crw-rw-r-- 1 root usb 189, 1152 Jan 5 21:30 001 crw-rw----+ 1 root plugdev 189, 1154 Jan 5 22:46 003 ``` plugdev group may have different name for different distros, consult your distro's manual for details. You can check what groups you're in by running 'id' command. If device's group is 'root', then you have to add udev rule: http://reactivated.net/writing_udev_rules.html Please find some examples there. android-file-transfer-linux-4.5/LICENSE000066400000000000000000000636421501120463400177120ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! android-file-transfer-linux-4.5/README.md000066400000000000000000000225001501120463400201500ustar00rootroot00000000000000# Android File Transfer For Linux (FreeBSD and macOS, too!) [![License](https://img.shields.io/:license-LGPLv2.1-blue.svg)](https://github.com/whoozle/android-file-transfer-linux/blob/master/LICENSE) [![Version](https://img.shields.io/:version-4.5-green.svg)](https://github.com/whoozle/android-file-transfer-linux) [![Android File Transfer for Linux (and macOS!)](https://github.com/whoozle/android-file-transfer-linux/actions/workflows/actions.yml/badge.svg)](https://github.com/whoozle/android-file-transfer-linux/actions/workflows/actions.yml) Android File Transfer for Linux — a reliable [MTP](https://en.wikipedia.org/wiki/Media_Transfer_Protocol) client with minimalistic UI similar to [Android File Transfer](https://www.android.com/intl/en_us/filetransfer/). ![AFTL Screenshot](screenshot.png "Nintendo Switch MTP connection and device-generated thumbnails.") It just works™. ## Do I need it? If you're happy with `gmtp`/`gvfs`/`mtpfs` or any other MTP software, you might not need this software (but give it a try!). If you're suffering from crashes, missing tags, album covers, USB freezes, and corrupted files however, this software is right for you. ## Pre-built Packages If your distribution does not provide an `android-file-transfer-linux` package, you can still install it on your system. There's quite a few packages available: - AppImage: https://github.com/whoozle/android-file-transfer-linux/releases - macOS DMG image: https://github.com/whoozle/android-file-transfer-linux/releases - macOS Homebrew: `brew install --cask whoozle-android-file-transfer` or `brew install --cask whoozle-android-file-transfer-nightly` ## Support me If you want to help me with development, click on the link below and follow the instructions. I'm working on this project in my spare time and I try to fix everything as fast as possible, sometimes adding features in realtime (more than 100 tickets closed by now). Any amount would help relieving the pain of using MTP. :D https://www.paypal.me/whoozle ## Features * Simple Qt UI with progress dialogs. * FUSE wrapper (if you prefer mounting your device), supporting partial read/writes, allowing instant access to your files. * No file size limits. * Automatically renames album cover to make it visible from media player. * Supports Zune and Zune HD. * USB [zerocopy](https://docs.kernel.org/networking/msg_zerocopy.html) support found in recent Linux kernels (no user/kernel data copying). * No extra dependencies (e.g. `libptp` or `libmtp`). * Available as a static/shared library. * Command line tool [`aft-mtp-cli`](https://manpages.debian.org/testing/android-file-transfer/aft-mtp-cli.1.en.html). * Python bindings. ## FAQ [Please take a look at the FAQ if you have issues with your operating system](FAQ.md). It's not that big, but those are the questions asked very often. ## Installation ### Debian/Ubuntu ``` sudo apt-get install android-file-transfer ``` ### Gentoo Android File Transfer for Linux is now included in Gentoo. You don't have to build anything, just run ``` sudo emerge -av sys-fs/android-file-transfer-linux ``` If you need a FUSE mount helper to mount MTP filesystems, you have to enable the FUSE use flag, e.g. adding the following in `/etc/portage/package.use` (which can either be a directory or a file): ``` sys-fs/android-file-transfer-linux fuse ``` You can use the `sys-fs/android-file-transfer-linux-9999` ebuild if you want the latest Git version by adding the following entry to `/etc/portage/package.accept_keywords (which can either be a directory or a file): ``` =sys-fs/android-file-transfer-linux-9999 ** ``` ### Arch ``` sudo pacman -S android-file-transfer ``` ## Building from source ### Prerequisites * You will need the Qt libraries for building the UI program. If you're planning to use only the library (*Qt is not needed*), you could turn the option ```BUILD_QT_UI``` off. * For Ubuntu and other Debian-based distros, use the following command: ```shell sudo apt-get install build-essential cmake qt5-default ninja-build libfuse-dev libreadline-dev qttools5-dev ``` For Fedora: ``` dnf install make automake gcc gcc-c++ kernel-devel cmake fuse fuse-devel qt-devel readline-devel libqt5-linguist-devel ``` * Basically * you need `libqtX-dev` or `libqt5-dev` for the UI, * `libfuse-dev` for the FUSE interface, * and `cmake`, `ninja`, or `make` for building the project. ### Building with Ninja ```shell mkdir build cd build cmake -G Ninja .. ninja ./qt/android-file-transfer ``` ### Building with make ```shell mkdir build cd build cmake .. make ./qt/android-file-transfer ``` ### Installing binary package on macOS There is a binary package that can be installed via Homebrew: * First, install [`brew`](https://brew.sh) if you don't have it already installed. * Then, the stable package may be installed via: ```shell brew install homebrew/cask/whoozle-android-file-transfer ``` * The nightly build may be installed via: ```shell brew install homebrew/cask-versions/whoozle-android-file-transfer-nightly ``` * Please note: Stable and nightly are in conflict, so please make sure to uninstall one of them when you want to switch between stable and nightly. ### Building app package on macOS You'll need Qt installed to build the GUI app. Here are the build instructions with Qt5 from Homebrew (`brew install qt5`): ```shell mkdir build cd build cmake .. -DCMAKE_INSTALL_PREFIX=~/Applications -DCMAKE_PREFIX_PATH=/usr/local/opt/qt5 make make install open ~/Applications/android-file-transfer.app ``` ### Installation `sudo ninja install` or `sudo make install` will install the program into the cmake prefix/bin directory (usually `/usr/local/bin`). ## How to use ### FUSE interface ```shell mkdir ~/my-device ./aft-mtp-mount ~/my-device ``` Remember, if you want album art to be displayed, it must be named 'albumart.xxx' and placed *first* in the destination folder. Then copy other files. Also, note that FUSE could be 7-8 times slower than UI/CLI file transfer. ### ZUNE firmware flashing/recovery 1. Find Zune-Firmware-x86.msi on the internet 2. Unpack it `7z x Zune-Firmware-x86.msi` in some directory, you should get the following files there: ``` DracoBaselineCab FirmwareUpdateXml KeelBaselineCab PavoBaselineCab ScorpiusBaselineCab ``` 3. Find the name of your update in FirmwareUpdateXml. If you're not sure, open [Zune Specifications](https://en.wikipedia.org/wiki/Zune#Specifications) wikipedia page, find your model in "Official Model Numbers" row, and match with "Codename". For instance Zune 4 model 1124 has codename "Scorpius". 4. Unpack update for your device into some folder using cabextract: `cabextract XXXXBaselineCab`. Here's content of all cabs: ``` ├── Draco │   ├── EBoot.bin │   ├── Games.cab │   ├── nk.bin │   └── recovery.bin ├── Keel │   ├── EBoot.bin │   ├── Games.cab │   ├── nk.bin │   └── recovery.bin ├── Pavo │   ├── EXT.bin │   ├── NK.bin │   ├── Recovery.bin │   └── ZBoot.bin └── Scorpius     ├── EBoot.bin     ├── Games.cab     ├── nk.bin     ├── recovery.bin     └── xldr.bin ``` 5. Flash firmware files using cli tool. Generally you don't need to flash anything called `*boot*` or `*recovery*`. Original software starts with nk.bin, then EXT or Games. Here's an example of how I flash model 1395: ``` aft-mtp-cli -v -d 045e:063e # finds Zune HD 16Gb (model 1395) flash zune/Pavo/NK.bin flash zune/Pavo/EXT.bin device-reboot ``` 6. Wait until your Zune restarts, it can take a minute or two. 7. Voila, you don't need to fiddle with Zune software on windows anymore. ### Qt user interface 1. Start application, choose destination folder and click any button on toolbar. 2. The options available are: `Upload Album`, `Upload Directory`, and `Upload Files`. The latter two are self-explanatory. `Upload Album` tries searching the source directory for album covers and sets the best available cover. 3. You could drop any files or folders right into the application window: the transfer will start automatically. ### Known problems * Samsung removed Android extensions from MTP, so FUSE will be available read-only, sorry. Feel free to post your complaints to https://forum.developer.samsung.com/ * Sometimes downloading fails with a USB timeout, after which the phone becomes unresponsive: [Android bug #75259](https://code.google.com/p/android/issues/detail?id=75259) * Objects created in the UI will not show up in the FUSE filesystem: [Android bug #169547](https://code.google.com/p/android/issues/detail?id=169547) Up-to-date list of all known problems and bugs are available [here](https://github.com/whoozle/android-file-transfer-linux/issues). ## Contacts Please do not hesitate to contact me if you have any further questions. My email address is . ## Special thanks * All who filed bugs on GitHub and wrote emails. Many features came to be only because of your feedback. Thanks! * Alexey [gazay](https://github.com/gazay) Gaziev for useful suggestions, support, and invaluable help with the MacBook and macOS port. * @ssnjrthegr8 for the new logo! ## License Android File Transfer for Linux is released under the [GNU LGPLv2.1 License](https://github.com/whoozle/android-file-transfer-linux/blob/master/LICENSE). Copyright © 2015-2022 Vladimir Menshakov android-file-transfer-linux-4.5/_config.yml000066400000000000000000000000331501120463400210150ustar00rootroot00000000000000theme: jekyll-theme-minimalandroid-file-transfer-linux-4.5/android-file-transfer-linux.json000066400000000000000000000007171501120463400251060ustar00rootroot00000000000000{ "id": "io.github.whoozle.android-file-transfer-linux", "runtime": "org.kde.Platform", "runtime-version": "5.10", "sdk": "org.kde.Sdk", "modules" : [ { "name": "android-file-transfer-linux", "buildsystem": "cmake", "config-opts": [ "-DCMAKE_BUILD_TYPE=Release" ], "sources": [{ "type": "git", "tag": "v4.5", "url": "https://github.com/whoozle/android-file-transfer-linux.git" }] } ] } android-file-transfer-linux-4.5/cli/000077500000000000000000000000001501120463400174415ustar00rootroot00000000000000android-file-transfer-linux-4.5/cli/CMakeLists.txt000066400000000000000000000054771501120463400222160ustar00rootroot00000000000000find_package(Readline) include(CheckCSourceCompiles) set(CLI_SOURCES Command.cpp Session.cpp Tokenizer.cpp arg_lexer.l.cpp cli.cpp) if (READLINE_FOUND) set(CMAKE_REQUIRED_INCLUDES ${Readline_INCLUDE_DIR}) set(CMAKE_REQUIRED_LIBRARIES ${Readline_LIBRARY}) set(READLINE_TEST_SRC " #include #include int main(int argc, char **argv) { char *line = readline(\">\"); return 0; } ") check_c_source_compiles("${READLINE_TEST_SRC}" READLINE_NO_CURSES_TEST) if (NOT READLINE_NO_CURSES_TEST) message(STATUS "readline test failed - some systems expect you to link with termcap or curses libraries") message(STATUS "please find an example there: https://bugzilla.redhat.com/show_bug.cgi?id=499837") message(STATUS "trying to find and link curses library...") # set(CURSES_NEED_NCURSES TRUE) include(FindCurses) list(APPEND CMAKE_REQUIRED_LIBRARIES ${CURSES_LIBRARY}) check_c_source_compiles("${READLINE_TEST_SRC}" READLINE_CURSES_TEST) if (READLINE_CURSES_TEST) list(APPEND Readline_LIBRARY ${CURSES_LIBRARIES}) else() set(READLINE_FOUND FALSE) #disabling readline completely endif() endif() set(CMAKE_REQUIRED_INCLUDES) set(CMAKE_REQUIRED_LIBRARIES) endif() if (READLINE_FOUND) list(APPEND CLI_SOURCES CommandLine.cpp) list(APPEND CLI_LIBRARIES ${Readline_LIBRARY}) else() list(APPEND CLI_SOURCES CommandLineStub.cpp) message(WARNING "no readline library found, using stub") endif() add_executable(aft-mtp-cli ${CLI_SOURCES}) target_include_directories(aft-mtp-cli SYSTEM PRIVATE ${Readline_INCLUDE_DIR}) target_link_libraries(aft-mtp-cli ${MTP_LIBRARIES} ${CLI_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) install(TARGETS aft-mtp-cli RUNTIME DESTINATION bin) if (BUILD_QT_UI AND (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")) add_custom_command(TARGET aft-mtp-cli POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../../Frameworks/" $) add_custom_command(TARGET aft-mtp-cli POST_BUILD COMMAND ${OTOOL_BIN} -L $) set(AFT_MTP_CLI_PATH "${MACOSX_BUNDLE_ROOT_DIR}/Contents/SharedSupport/bin/aft-mtp-cli") set(MACOSX_BUNDLE_LIBS_INSTALL " ${MACOSX_BUNDLE_LIBS_INSTALL} file(INSTALL \"${CMAKE_CURRENT_BINARY_DIR}/aft-mtp-cli\" DESTINATION \"${MACOSX_BUNDLE_ROOT_DIR}/Contents/SharedSupport/bin\") ") foreach(DYNLIB ${MACOSX_BUNDLE_LIBS}) get_filename_component(DYNLIB_NAME "${DYNLIB}" NAME) set(MACOSX_BUNDLE_LIBS_INSTALL " ${MACOSX_BUNDLE_LIBS_INSTALL} message(STATUS \"cli: fixing ${DYNLIB_NAME}\") execute_process(COMMAND \"${CMAKE_INSTALL_NAME_TOOL}\" -change \"${DYNLIB}\" \"@executable_path/../../Frameworks/${DYNLIB_NAME}\" \"${AFT_MTP_CLI_PATH}\") ") endforeach() set(MACOSX_BUNDLE_LIBS_INSTALL " ${MACOSX_BUNDLE_LIBS_INSTALL} execute_process(COMMAND \"${OTOOL_BIN}\" -L \"${AFT_MTP_CLI_PATH}\") " PARENT_SCOPE) endif() android-file-transfer-linux-4.5/cli/Command.cpp000066400000000000000000000022301501120463400215200ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include namespace cli { void impl::Completer::Complete(CompletionContext & ctx) { ctx.Session.CompletePath(ctx.Prefix, ctx.Result); } void impl::Completer::Complete(CompletionContext & ctx) { ctx.Session.CompleteStoragePath(ctx.Prefix, ctx.Result); } } android-file-transfer-linux-4.5/cli/Command.h000066400000000000000000000104761501120463400212000ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_CLI_COMMAND_H #define AFTL_CLI_COMMAND_H #include #include #include #include #include #include #include #include namespace cli { class Session; struct Path : public std::string { Path(const std::string &path = std::string()): std::string(path) { } }; struct LocalPath : public std::string { LocalPath(const std::string &path = std::string()): std::string(path) { } }; struct StoragePath : public std::string { StoragePath(const std::string &path = std::string()): std::string(path) { } }; inline std::string EscapePath(std::string name) { if (name.find(' ') != name.npos) return '"' + name +'"'; else return name; } typedef std::list CompletionResult; struct CompletionContext { cli::Session & Session; size_t Index; std::string Prefix; CompletionResult & Result; CompletionContext(cli::Session &s, size_t i, const std::string &p, std::list & r): Session(s), Index(i), Prefix(p), Result(r) { } }; namespace impl { template<> struct ValueConverter { static LocalPath Convert(const Path &text) { return text; } }; template<> struct ValueConverter { static Path Convert(const Path &text) { return text; } }; template<> struct ValueConverter { static StoragePath Convert(const Path &text) { return text; } }; template struct Completer { static void Complete(CompletionContext & ctx) { } }; template<> struct Completer { static void Complete(CompletionContext & ctx); }; template<> struct Completer { static void Complete(CompletionContext & ctx); }; template struct CompletionForwarder { static void Complete(CompletionContext & ctx, size_t index) { } }; template struct CompletionForwarder { static void Complete(CompletionContext & ctx, size_t index) { if (index == 0) Completer::type>::Complete(ctx); else CompletionForwarder::Complete(ctx, index - 1); } }; }; struct ICommand { virtual ~ICommand() = default; virtual void Execute(const Tokens &tokens) const = 0; virtual size_t GetArgumentCount() const = 0; virtual std::string GetHelpString() const = 0; virtual void Complete(CompletionContext &ctx) const = 0; }; DECLARE_PTR(ICommand); class BaseCommand : public virtual ICommand { std::string _help; public: BaseCommand(const std::string &help): _help(help) { } virtual std::string GetHelpString() const { return _help; } }; template struct Command : public BaseCommand { typedef std::function FuncType; FuncType _func; Command(const std::string &help, FuncType && func) : BaseCommand(help), _func(func) { } template static void Execute(std::function func, const Tokens & tokens) { auto args = cli::MakeTuple(tokens.begin(), tokens.end()); mtp::invoke(func, args); } virtual void Execute(const Tokens &tokens) const { Execute(_func, tokens); } virtual size_t GetArgumentCount() const { return sizeof...(Args); } virtual void Complete(CompletionContext &ctx) const { impl::CompletionForwarder::Complete(ctx, ctx.Index); } }; } #endif android-file-transfer-linux-4.5/cli/CommandLine.cpp000066400000000000000000000047301501120463400223370ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #if HAVE_READLINE_HISTORY_H #include #endif namespace cli { CommandLine::CommandLine() { rl_readline_name = const_cast("AFT"); rl_attempted_completion_function = &CommandLine::CompletionCallback; char *home = getenv("HOME"); if (home) { _historyPath = std::string(home) + "/.config"; mkdir(_historyPath.c_str(), 0700); _historyPath += "/whoozle.github.io"; mkdir(_historyPath.c_str(), 0700); _historyPath += "/aft-linux-cli.history"; read_history(_historyPath.c_str()); } } CommandLine::~CommandLine() { write_history(_historyPath.c_str()); } CommandLine & CommandLine::Get() { static CommandLine cmd; return cmd; } char * CommandLine::CompletionGenerator(const char *text, int state) { return NULL; } char ** CommandLine::CompletionCallback(const char *text, int start, int end) { try { if (Get()._callback) return Get()._callback(text, start, end); else return NULL; } catch(const std::exception &ex) { mtp::error(ex.what()); } return NULL; } bool CommandLine::ReadLine(const std::string &prompt, std::string &input) { char *line = readline(prompt.c_str()); if (!line) return false; input.assign(line); add_history(input.c_str()); return true; } bool CommandLine::ReadRawLine(std::string &input) { char buf[4096]; char *r = fgets(buf, sizeof(buf), stdin); if (r) input.assign(r); else input.clear(); return r; } std::string CommandLine::GetLineBuffer() const { return rl_line_buffer; } } android-file-transfer-linux-4.5/cli/CommandLine.h000066400000000000000000000030721501120463400220020ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_CLI_COMMANDLINE_H #define AFTL_CLI_COMMANDLINE_H #include #include namespace cli { class CommandLine { public: typedef std::function Callback; protected: CommandLine(); ~CommandLine(); private: static char ** CompletionCallback(const char *text, int start, int end); static char * CompletionGenerator(const char *text, int state); std::string _historyPath; Callback _callback; public: static CommandLine & Get(); bool ReadLine(const std::string &prompt, std::string &input); bool ReadRawLine(std::string &input); void SetCallback(const Callback &callback) { _callback = callback; } std::string GetLineBuffer() const; }; } #endif android-file-transfer-linux-4.5/cli/CommandLineStub.cpp000066400000000000000000000027271501120463400232010ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include namespace cli { CommandLine::CommandLine() { } CommandLine::~CommandLine() { } CommandLine & CommandLine::Get() { static CommandLine cmd; return cmd; } bool CommandLine::ReadLine(const std::string &prompt, std::string &input) { fputs(prompt.c_str(), stdout); fflush(stdout); return ReadRawLine(input); } bool CommandLine::ReadRawLine(std::string &input) { char buf[4096]; char *r = fgets(buf, sizeof(buf), stdin); if (r) input.assign(r); else input.clear(); return r; } std::string CommandLine::GetLineBuffer() const { return std::string(); } } android-file-transfer-linux-4.5/cli/MakeTuple.h000066400000000000000000000054511501120463400215060ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_CLI_MAKETUPLE_H #define AFTL_CLI_MAKETUPLE_H #include #include #include #include namespace cli { namespace impl { template struct ValueConverter; template struct StringStreamConverter { static T Convert(const std::string &text) { std::stringstream ss(text); T value; ss >> std::noskipws >> value; return value; } }; template<> struct ValueConverter : StringStreamConverter { }; template<> struct ValueConverter { static std::string Convert(const std::string &text) { return text; } }; template struct TupleBuilder { std::tuple<> Result; TupleBuilder(IteratorType begin, IteratorType end) { } }; template struct TupleBuilder { typedef typename std::decay::type ValueType; std::string _text; TupleBuilder _next; typedef std::tuple::type ... > ResultType; ResultType Result; static IteratorType Next(IteratorType & begin, IteratorType end) { if (begin == end) throw std::runtime_error("not enough arguments"); return begin++; } TupleBuilder(IteratorType begin, IteratorType end): _text(*Next(begin, end)), _next(begin, end) { Result = std::tuple_cat(std::make_tuple(ValueConverter::Convert(_text)), _next.Result); } }; } template std::tuple<> MakeTuple(IteratorType, IteratorType) { return std::tuple<>(); } template auto MakeTuple(IteratorType begin, IteratorType end) -> typename impl::TupleBuilder::ResultType { impl::TupleBuilder b(begin, end); return b.Result; } }; #endif android-file-transfer-linux-4.5/cli/PosixStreams.h000066400000000000000000000063001501120463400222520ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_CLI_POSIXSTREAMS_H #define AFTL_CLI_POSIXSTREAMS_H #include #include #include #include #include #include #include #include namespace cli { class BaseObjectStream: public mtp::CancellableStream { std::function _progressReporter; mtp::u64 _current, _total; public: BaseObjectStream(): _current(0), _total(0) { } virtual ~BaseObjectStream() = default; void SetProgressReporter(const decltype(_progressReporter) & pr) { _progressReporter = pr; } void SetTotal(mtp::u64 total) { _current = 0; _total = total; } protected: void Report(mtp::u64 delta) { if (_progressReporter) { _current += delta; _progressReporter(_current, _total); } } }; class ObjectInputStream final: public BaseObjectStream, public virtual mtp::IObjectInputStream { int _fd; mtp::u64 _size; public: ObjectInputStream(const std::string &fname) : _fd(open(fname.c_str(), O_RDONLY)) { if (_fd < 0) throw std::runtime_error("cannot open file: " + fname); struct stat st; if (stat(fname.c_str(), &st) != 0) throw std::runtime_error("stat failed"); _size = st.st_size; } ~ObjectInputStream() { close(_fd); } mtp::u64 GetSize() const { return _size; } virtual size_t Read(mtp::u8 *data, size_t size) { CheckCancelled(); ssize_t r = read(_fd, data, size); if (r < 0) throw std::runtime_error("read failed"); Report(r); return r; } }; class ObjectOutputStream final: public BaseObjectStream, public mtp::IObjectOutputStream { int _fd; public: ObjectOutputStream(const std::string &fname) : _fd(open(fname.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644)) { if (_fd < 0) throw std::runtime_error("cannot open file: " + fname); } ~ObjectOutputStream() { close(_fd); } virtual size_t Write(const mtp::u8 *data, size_t size) { CheckCancelled(); ssize_t r = write(_fd, data, size); if (r < 0) throw std::runtime_error("write failed"); Report(r); return r; } static void SetModificationTime(const std::string &fname, time_t mtime) { struct utimbuf buf = {}; buf.actime = time(nullptr); buf.modtime = mtime; if (utime(fname.c_str(), &buf) != 0) throw mtp::system_error("utime"); } }; } #endif android-file-transfer-linux-4.5/cli/ProgressBar.h000066400000000000000000000051321501120463400220440ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_CLI_PROGRESSBAR_H #define AFTL_CLI_PROGRESSBAR_H #include #include #include #include namespace cli { class EventProgressBar { std::string _title; int _steps; mtp::u64 _current; public: EventProgressBar(const std::string &title, int steps = 1000): _title(title), _steps(steps), _current(0) { } void operator()(mtp::u64 offset, mtp::u64 total) { auto current = offset * _steps / total; if (current != _current || offset >= total) { _current = current; mtp::print(":progress ", _title, " ", offset, " ", total); } } }; class ProgressBar { static const unsigned Junk = 9; std::string _title; int _width; int _maxWidth; unsigned _percentage; public: ProgressBar(const std::string & title, int w, int max): _width(w), _percentage(-1) { _maxWidth = max - _width - Junk; if (_maxWidth < 1) throw std::runtime_error("insufficient space for progress bar"); int titleSize = mtp::OutputStream::Utf8Length(title); if (titleSize > _maxWidth) { int chompLeft = _maxWidth / 2; //utf unaware, fixme int chompRight = _maxWidth - chompLeft; _title = title.substr(0, chompLeft) + "…" + title.substr(title.size() - chompRight); } else _title = title; } void operator()(mtp::u64 current, mtp::u64 total) { unsigned percentage = total? current * 100 / total: 100; if (_percentage != percentage) { _percentage = percentage; printf("%3u%% [", percentage ); unsigned width = total? current * _width / total: _width; unsigned spaces = _width - width; while(width--) fputc('=', stdout); while(spaces--) fputc(' ', stdout); printf("] %s\n\033[1A\033[2K", _title.c_str()); } } }; } #endif android-file-transfer-linux-4.5/cli/Session.cpp000066400000000000000000001101441501120463400215710ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { bool BeginsWith(const std::string &str, const std::string &prefix) { if (prefix.size() > str.size()) return false; const char *a = str.c_str(); const char *b = prefix.c_str(); return strncasecmp(a, b, prefix.size()) == 0; } mtp::u64 FromHex(const std::string & str) { return strtoll(str.c_str(), NULL, 16); } } namespace cli { Session::Session(const mtp::SessionPtr &session, bool showPrompt): _session(session), _trustedApp(mtp::TrustedApp::Create(_session, GetMtpzDataPath())), _gdi(_session->GetDeviceInfo()), _cs(mtp::Session::AllStorages), _cd(mtp::Session::Root), _running(true), _interactive(isatty(STDOUT_FILENO)), _showEvents(false), _showPrompt(showPrompt), _terminalWidth(80), _batterySupported(false) { using namespace mtp; using namespace std::placeholders; { const char *cols = getenv("COLUMNS"); _terminalWidth = cols? atoi(cols): 80; #ifdef TIOCGSIZE struct ttysize ts; ioctl(STDIN_FILENO, TIOCGSIZE, &ts); _terminalWidth = ts.ts_cols; #elif defined(TIOCGWINSZ) struct winsize ts; ioctl(STDIN_FILENO, TIOCGWINSZ, &ts); _terminalWidth = ts.ws_col; #endif } try { if (_trustedApp) _trustedApp->Authenticate(); } catch(const std::exception & ex) { error("mtpz authentication failed: ", ex.what()); } AddCommand("help", "shows this help", make_function([this]() -> void { Help(); })); AddCommand("ls", "lists current directory", make_function([this]() -> void { List(false, false); })); AddCommand("ls", " lists objects in ", make_function([this](const Path &path) -> void { List(path, false, false); })); AddCommand("ls-r", "lists current directory [recursive]", make_function([this]() -> void { List(false, true); })); AddCommand("ls-r", " lists objects in [recursive]", make_function([this](const Path &path) -> void { List(path, false, true); })); AddCommand("lsext", "lists current directory [extended info]", make_function([this]() -> void { List(true, false); })); AddCommand("lsext", " lists objects in [extended info]", make_function([this](const Path &path) -> void { List(path, true, false); })); AddCommand("lsext-r", "lists current directory [extended info, recursive]", make_function([this]() -> void { List(true, true); })); AddCommand("lsext-r", " lists objects in [extended info, recursive]", make_function([this](const Path &path) -> void { List(path, true, true); })); AddCommand("ls-objects", "", make_function([this](const std::string &format) -> void { ListObjects(format); })); AddCommand("put", "put uploads file to directory", make_function([this](const LocalPath &path, const Path &dst) -> void { Put(path, dst); })); AddCommand("put", " uploads file", make_function([this](const LocalPath &path) -> void { Put(path); })); AddCommand("flash", " sends file but set file format to UndefinedFirmware(0xb802).", make_function([this](const LocalPath &path) -> void { Flash(path); })); AddCommand("get", " downloads file", make_function([this](const Path &path) -> void { Get(path); })); AddCommand("get", " downloads file to ", make_function([this](const Path &path, const LocalPath &dst) -> void { Get(dst, path); })); AddCommand("get-id", " get object by id", make_function([this](mtp::u32 id) -> void { Get(mtp::ObjectId(id)); })); AddCommand("get-id", " get object by id to ", make_function([this](mtp::u32 id, const LocalPath &dst) -> void { Get(dst, mtp::ObjectId(id)); })); AddCommand("get-thumb", " downloads thumbnail for file", make_function([this](const Path &path) -> void { GetThumb(path); })); AddCommand("get-thumb", " downloads thumbnail to ", make_function([this](const Path &path, const LocalPath &dst) -> void { GetThumb(dst, path); })); AddCommand("cat", " outputs file", make_function([this](const Path &path) -> void { Cat(path); })); AddCommand("quit", "quits program", make_function([this]() -> void { Quit(); })); AddCommand("exit", "exits program", make_function([this]() -> void { Quit(); })); AddCommand("cd", " change directory to ", make_function([this](const Path &path) -> void { ChangeDirectory(path); })); AddCommand("storage", "", make_function([this](const StoragePath &path) -> void { ChangeStorage(path); })); AddCommand("pwd", "resolved current object directory", make_function([this]() -> void { CurrentDirectory(); })); AddCommand("rm", " removes object (WARNING: RECURSIVE, be careful!)", make_function([this](const Path &path) -> void { Delete(path); })); AddCommand("rm-id", " removes object by id (WARNING: RECURSIVE, be careful!)", make_function([this](mtp::u32 id) -> void { Delete(mtp::ObjectId(id)); })); AddCommand("mkdir", " makes directory", make_function([this](const Path &path) -> void { MakeDirectory(path); })); AddCommand("mkpath", " create directory structure specified in path", make_function([this](const Path &path) -> void { MakePath(path); })); AddCommand("type", " shows type of file (recognized by libmagic/extension)", make_function([](const LocalPath &path) -> void { ShowType(path); })); AddCommand("rename", "renames object", make_function([this](const Path & path, const std::string & newName) -> void { Rename(path, newName); })); AddCommand("storage-list", "shows available MTP storages", make_function([this]() -> void { ListStorages(); })); AddCommand("properties", " lists properties for ", make_function([this](const Path &path) -> void { ListProperties(path); })); AddCommand("device-properties", "shows device's MTP properties", make_function([this]() -> void { ListDeviceProperties(); })); AddCommand("device-info", "displays device's information", make_function([this]() -> void { DisplayDeviceInfo(); })); AddCommand("storage-info", " displays storage information", make_function([this](const StoragePath &path) -> void { DisplayStorageInfo(path); })); AddCommand("get-refs", "returns object-associated refs", make_function([this](const StoragePath &path) -> void { GetObjectReferences(path); })); if (Library::Supported(_session)) { AddCommand("zune-init", "load media library", make_function([this]() -> void { ZuneInit(); })); AddCommand("zune-import", " import file using metadata", make_function([this](const LocalPath &path) -> void { ZuneImport(path); })); } if (_session->GetDeviceInfo().Supports(mtp::OperationCode::RebootDevice)) { AddCommand("device-reboot", "reboots device (Microsoft specific?)", make_function([this]() -> void { RebootDevice(); })); } AddCommand("test-property-list", "test GetObjectPropList on given object", make_function([this](const Path &path) -> void { TestObjectPropertyList(path); })); AddCommand("test-lexer", "tests lexer", make_function([](const std::string &input) -> void { Tokens tokens; Tokenizer(input, tokens); print(input); for(auto t : tokens) print("\t", t); })); } Session::~Session() { } std::string Session::GetMtpzDataPath() { char * home = getenv("HOME"); return std::string(home? home: ".") + "/.mtpz-data"; } bool Session::SetFirstStorage() { auto ids = _session->GetStorageIDs(); if (ids.StorageIDs.empty()) return false; auto id = std::to_string(ids.StorageIDs.front().Id); ChangeStorage(StoragePath(id)); return true; } char ** Session::CompletionCallback(const char *text, int start, int end) { Tokens tokens; Tokenizer(CommandLine::Get().GetLineBuffer(), tokens); if (tokens.size() < 2) //0 or 1 { std::string command = !tokens.empty()? tokens.back(): std::string(); char **comp = static_cast(calloc(sizeof(char *), _commands.size() + 1)); auto it = _commands.begin(); size_t i = 0, n = _commands.size(); for(; n--; ++it) { if (end != 0 && !BeginsWith(it->first, command)) continue; comp[i++] = strdup(it->first.c_str()); } if (i == 0) //readline silently dereference matches[0] { free(comp); comp = NULL; }; return comp; } else { //completion const std::string &commandName = tokens.front(); auto b = _commands.lower_bound(commandName); auto e = _commands.upper_bound(commandName); if (b == e) return NULL; size_t idx = tokens.size() - 2; decltype(b) i; for(i = b; i != e; ++i) { if (idx < i->second->GetArgumentCount()) break; } if (i == e) return NULL; //mtp::print("COMPLETING ", commandName, " ", idx, ":", commandName, " with ", i->second->GetArgumentCount(), " args"); ICommandPtr command = i->second; std::list matches; CompletionContext ctx(*this, idx, text, matches); command->Complete(ctx); if (ctx.Result.empty()) return NULL; char **comp = static_cast(calloc(sizeof(char *), ctx.Result.size() + 1)); size_t dst = 0; for(auto i : ctx.Result) comp[dst++] = strdup(i.c_str()); return comp; } return NULL; } void Session::ProcessCommand(const std::string &input) { Tokens tokens; Tokenizer(input, tokens); if (!tokens.empty()) ProcessCommand(std::move(tokens)); if (_showEvents) mtp::print(":done"); } void Session::ProcessCommand(Tokens && tokens_) { Tokens tokens(tokens_); if (tokens.empty()) throw std::runtime_error("no token passed to ProcessCommand"); std::string cmdName = tokens.front(); tokens.pop_front(); auto b = _commands.lower_bound(cmdName); auto e = _commands.upper_bound(cmdName); if (b == e) throw std::runtime_error("invalid command " + cmdName); size_t args = tokens.size(); for(auto i = b; i != e; ++i) { ICommandPtr cmd = i->second; if (i->second->GetArgumentCount() == args) { cmd->Execute(tokens); return; } } throw std::runtime_error("invalid argument count (" + std::to_string(args) + ")"); } void Session::UpdatePrompt() { if (_showPrompt) { std::stringstream ss; ss << _gdi.Manufacturer << " " << _gdi.Model; if (_deviceFriendlyNameSupported) { auto name = _session->GetDeviceStringProperty(mtp::DeviceProperty::DeviceFriendlyName); if (!name.empty()) ss << " / " << name; } if (_batterySupported) { auto level = _session->GetDeviceIntegerProperty(mtp::DeviceProperty::BatteryLevel); ss << " [" << level << "%]"; } if (!_csName.empty()) ss << ":" << _csName; ss << "> "; _prompt = ss.str(); } else _prompt.clear(); } void Session::InteractiveInput() { using namespace mtp; if (_interactive && _showPrompt) { print("android file transfer for linux version ", GetVersion()); print(_gdi.Manufacturer, " ", _gdi.Model, " ", _gdi.DeviceVersion); print("extensions: ", _gdi.VendorExtensionDesc); //print(_gdi.SerialNumber); //non-secure std::stringstream ss; ss << "supported op codes: "; for(OperationCode code : _gdi.OperationsSupported) ss << ToString(code) << " "; ss << "\nsupported capture formats: "; for(auto code : _gdi.CaptureFormats) ss << ToString(code) << " "; ss << "\nsupported image formats: "; for(auto code : _gdi.ImageFormats) ss << ToString(code) << " "; ss << "\nsupported properties: "; _batterySupported = _gdi.Supports(mtp::OperationCode::GetDevicePropValue) && _gdi.Supports(mtp::DeviceProperty::BatteryLevel); _deviceFriendlyNameSupported = _gdi.Supports(mtp::OperationCode::GetDevicePropValue) && _gdi.Supports(mtp::DeviceProperty::DeviceFriendlyName); for(DeviceProperty code : _gdi.DevicePropertiesSupported) ss << ToString(code) << " "; ss << "\n"; debug(ss.str()); } cli::CommandLine::Get().SetCallback([this](const char *text, int start, int end) -> char ** { return CompletionCallback(text, start, end); }); UpdatePrompt(); std::string input; while (_showPrompt? cli::CommandLine::Get().ReadLine(_prompt, input): cli::CommandLine::Get().ReadRawLine(input)) { try { ProcessCommand(input); if (!_running) //do not put newline return; } catch (const mtp::InvalidResponseException &ex) { mtp::error("error: ", ex.what()); if (ex.Type == mtp::ResponseType::InvalidStorageID) error("\033[1mYour device might be locked or in usb-charging mode, please unlock it and put it in MTP or PTP mode\033[0m\n"); } catch(const std::exception &ex) { error("error: ", ex.what()); } if (_batterySupported) UpdatePrompt(); } if (_showPrompt) print(""); } mtp::ObjectId Session::ResolveObjectChild(mtp::ObjectId parent, const std::string &entity) { //fixme: replace with prop list! auto objectList = _session->GetObjectHandles(_cs, mtp::ObjectFormat::Any, parent); for(auto object : objectList.ObjectHandles) { std::string name = _session->GetObjectStringProperty(object, mtp::ObjectProperty::ObjectFilename); if (name == entity) { return object; } } throw std::runtime_error("could not find " + entity + " in path"); } mtp::ObjectId Session::Resolve(const Path &path, bool create) { mtp::ObjectId id = BeginsWith(path, "/")? mtp::Session::Root: _cd; for(size_t p = 0; p < path.size(); ) { size_t next = path.find('/', p); if (next == path.npos) next = path.size(); std::string entity(path.substr(p, next - p)); if (entity.empty() || entity == ".") { } else if (entity == "..") { id = _session->GetObjectParent(id); if (id == mtp::Session::Device) id = mtp::Session::Root; } else { try { id = ResolveObjectChild(id, entity); } catch(const std::exception &ex) { if (!create) throw; id = MakeDirectory(id, entity); } } p = next + 1; } return id; } std::string Session::GetFilename(const std::string &path) { size_t pos = path.rfind('/'); return (pos == path.npos)? path: path.substr(pos + 1); } std::string Session::GetDirname(const std::string &path) { size_t pos = path.rfind('/'); return (pos == path.npos)? std::string(): path.substr(0, pos); } std::string Session::FormatTime(const std::string ×pec) { if (timespec.empty()) return "????" "-??" "-?? ??:??:??"; //bloody trigraphs, abomination of the 70s if (timespec.size() != 15 || timespec[8] != 'T') return timespec; return timespec.substr(0, 4) + "-" + timespec.substr(4, 2) + "-" + timespec.substr(6, 2) + " " + timespec.substr(9, 2) + ":" + timespec.substr(11, 2) + ":" + timespec.substr(13, 2); } mtp::ObjectId Session::ResolvePath(const std::string &path, std::string &file) { size_t pos = path.rfind('/'); if (pos == path.npos) { file = path; return _cd; } else { file = path.substr(pos + 1); return Resolve(path.substr(0, pos)); } } void Session::CurrentDirectory() { std::string path; mtp::ObjectId id = _cd; while(id != mtp::Session::Device && id != mtp::Session::Root) { std::string filename = _session->GetObjectStringProperty(id, mtp::ObjectProperty::ObjectFilename); path = filename + "/" + path; id = _session->GetObjectParent(id); if (id == mtp::Session::Device) break; } path = "/" + path; mtp::print(path); } void Session::List(mtp::ObjectId parent, bool extended, bool recursive, const std::string &prefix) { using namespace mtp; if (!extended && !recursive && _cs == mtp::Session::AllStorages && _session->GetObjectPropertyListSupported()) { ByteArray data = _session->GetObjectPropertyList(parent, ObjectFormat::Any, ObjectProperty::ObjectFilename, 0, 1); ObjectPropertyListParser parser; //HexDump("list", data, true); parser.Parse(data, [&prefix](ObjectId objectId, ObjectProperty property, const std::string &name) { print(std::left, width(objectId, 10), " ", prefix + name); }); } else { msg::ObjectHandles handles = _session->GetObjectHandles(_cs, mtp::ObjectFormat::Any, parent); for(auto objectId : handles.ObjectHandles) { try { std::string filename; if (extended) { msg::ObjectInfo info = _session->GetObjectInfo(objectId); filename = info.Filename; print( std::left, width(objectId, 10), " ", width(info.StorageId.Id, 10), " ", std::right, ToString(info.ObjectFormat), " ", width(info.ObjectCompressedSize, 10), " ", std::left, width(!info.CaptureDate.empty()? FormatTime(info.CaptureDate): FormatTime(info.ModificationDate), 20), " ", prefix + info.Filename, " " ); } else { filename = _session->GetObjectStringProperty(objectId, ObjectProperty::ObjectFilename); print(std::left, width(objectId, 10), " ", prefix + filename); } if (recursive) { auto format = mtp::ObjectFormat(_session->GetObjectIntegerProperty(objectId, ObjectProperty::ObjectFormat)); if (format == mtp::ObjectFormat::Association) List(objectId, extended, recursive, prefix + filename + "/"); } } catch(const std::exception &ex) { mtp::error("error: ", ex.what()); } } } } void Session::CompletePath(const Path &path, CompletionResult &result) { std::string filePrefix; mtp::ObjectId parent = ResolvePath(path, filePrefix); std::string dir = GetDirname(path); auto objectList = _session->GetObjectHandles(_cs, mtp::ObjectFormat::Any, parent); for(auto object : objectList.ObjectHandles) { std::string name = _session->GetObjectStringProperty(object, mtp::ObjectProperty::ObjectFilename); if (BeginsWith(name, filePrefix)) { if (!dir.empty()) name = dir + '/' + name; mtp::ObjectFormat format = (mtp::ObjectFormat)_session->GetObjectIntegerProperty(object, mtp::ObjectProperty::ObjectFormat); if (format == mtp::ObjectFormat::Association) name += '/'; result.push_back(EscapePath(name)); } } } void Session::CompleteStoragePath(const StoragePath &path, CompletionResult &result) { using namespace mtp; msg::StorageIDs list = _session->GetStorageIDs(); for(size_t i = 0; i < list.StorageIDs.size(); ++i) { auto id = list.StorageIDs[i]; msg::StorageInfo si = _session->GetStorageInfo(id); auto idStr = std::to_string(id.Id); if (BeginsWith(idStr, path)) result.push_back(idStr); if (BeginsWith(si.VolumeLabel, path)) result.push_back(EscapePath(si.VolumeLabel)); if (BeginsWith(si.StorageDescription, path)) result.push_back(EscapePath(si.StorageDescription)); } } void Session::ListStorages() { using namespace mtp; msg::StorageIDs list = _session->GetStorageIDs(); for(size_t i = 0; i < list.StorageIDs.size(); ++i) { msg::StorageInfo si = _session->GetStorageInfo(list.StorageIDs[i]); print( std::left, width(list.StorageIDs[i], 8), " volume: ", si.VolumeLabel, ", description: ", si.StorageDescription); } } mtp::StorageId Session::GetStorageByPath(const StoragePath &path, mtp::msg::StorageInfo &si, bool allowAll) { using namespace mtp; if (allowAll && (path == "All" || path == "all" || path == "*")) return mtp::Session::AllStorages; msg::StorageIDs list = _session->GetStorageIDs(); for(size_t i = 0; i < list.StorageIDs.size(); ++i) { auto id = list.StorageIDs[i]; si = _session->GetStorageInfo(id); auto idStr = std::to_string(id.Id); if (idStr == path || si.StorageDescription == path || si.VolumeLabel == path) return id; } throw std::runtime_error("storage " + path + " could not be found"); } void Session::ChangeStorage(const StoragePath &path) { using namespace mtp; msg::StorageInfo si; auto storageId = GetStorageByPath(path, si, true); _cs = storageId; if (storageId != mtp::Session::AllStorages) { _csName = si.GetName(); print("selected storage ", _cs.Id, " ", si.VolumeLabel, " ", si.StorageDescription); } else _csName.clear(); UpdatePrompt(); } void Session::Help() { using namespace mtp; print("Available commands are:"); for(auto i : _commands) { print("\t", std::left, width(i.first, 20), i.second->GetHelpString()); } } void Session::Get(const LocalPath &dst, mtp::ObjectId srcId, bool thumb) { mtp::ObjectFormat format = static_cast(_session->GetObjectIntegerProperty(srcId, mtp::ObjectProperty::ObjectFormat)); if (format == mtp::ObjectFormat::Association) { mkdir(dst.c_str(), 0700); auto obj = _session->GetObjectHandles(_cs, mtp::ObjectFormat::Any, srcId); for(auto id : obj.ObjectHandles) { auto info = _session->GetObjectInfo(id); LocalPath dstFile = dst + "/" + info.Filename; Get(dstFile, id); } } else { auto stream = std::make_shared(dst); if (IsInteractive() || _showEvents) { mtp::u64 size = _session->GetObjectIntegerProperty(srcId, mtp::ObjectProperty::ObjectSize); stream->SetTotal(size); if (_showEvents) { try { stream->SetProgressReporter(EventProgressBar(dst)); } catch(const std::exception &ex) { } } else if (_showPrompt) { try { stream->SetProgressReporter(ProgressBar(dst, _terminalWidth / 3, _terminalWidth)); } catch(const std::exception &ex) { } } } if (thumb) _session->GetThumb(srcId, stream); else _session->GetObject(srcId, stream); stream.reset(); try { cli::ObjectOutputStream::SetModificationTime(dst, _session->GetObjectModificationTime(srcId)); } catch(const std::exception &ex) { mtp::debug("GetObjectModificationTime failed: ", ex.what()); } } } void Session::Get(mtp::ObjectId srcId) { auto info = _session->GetObjectInfo(srcId); Get(LocalPath(info.Filename), srcId); } void Session::Cancel() { _session->AbortCurrentTransaction(); } void Session::Quit() { _running = false; } void Session::GetThumb(mtp::ObjectId srcId) { auto info = _session->GetObjectInfo(srcId); GetThumb(LocalPath(info.Filename), srcId); } void Session::Cat(const Path &path) { mtp::ByteArrayObjectOutputStreamPtr stream(new mtp::ByteArrayObjectOutputStream); _session->GetObject(Resolve(path), stream); const mtp::ByteArray & data = stream->GetData(); std::string text(data.begin(), data.end()); fputs(text.c_str(), stdout); if (text.empty() || text[text.size() - 1] == '\n') fputc('\n', stdout); } void Session::Rename(const Path &path, const std::string &newName) { auto objectId = Resolve(path); _session->SetObjectProperty(objectId, mtp::ObjectProperty::ObjectFilename, newName); } namespace { struct stat Stat(const std::string &path) { struct stat st = {}; if (stat(path.c_str(), &st)) throw std::runtime_error(std::string("stat failed: ") + strerror(errno)); return st; } } void Session::Put(mtp::ObjectId parentId, const LocalPath &src, const std::string &targetFilename, mtp::ObjectFormat format) { using namespace mtp; struct stat st = Stat(src); if (S_ISDIR(st.st_mode)) { std::string name = GetFilename(src.back() == '/'? src.substr(0, src.size() - 1): static_cast(src)); try { mtp::ObjectId existingObject = ResolveObjectChild(parentId, name); ObjectFormat format = ObjectFormat(_session->GetObjectIntegerProperty(existingObject, ObjectProperty::ObjectFormat)); if (format != ObjectFormat::Association) { _session->DeleteObject(existingObject); throw std::runtime_error("target is not a directory"); } parentId = existingObject; } catch(const std::exception &ex) { parentId = MakeDirectory(parentId, name); } DIR *dir = opendir(src.c_str()); if (!dir) { perror("opendir"); return; } while(true) { dirent *result = readdir(dir); if (!result) break; if (strcmp(result->d_name, ".") == 0 || strcmp(result->d_name, "..") == 0) continue; std::string fname = result->d_name; Put(parentId, src + "/" + fname); } closedir(dir); } else if (S_ISREG(st.st_mode)) { std::string filename = targetFilename.empty()? GetFilename(src): targetFilename; try { mtp::ObjectId objectId = ResolveObjectChild(parentId, filename); _session->DeleteObject(objectId); } catch(const std::exception &ex) { } auto stream = std::make_shared(src); stream->SetTotal(stream->GetSize()); if (format == mtp::ObjectFormat::Any) format = ObjectFormatFromFilename(src); msg::ObjectInfo oi; oi.Filename = filename; oi.ObjectFormat = format; oi.ObjectCompressedSize = stream->GetSize(); if (_showEvents) { try { stream->SetProgressReporter(EventProgressBar(src)); } catch(const std::exception &ex) { } } else if (IsInteractive()) { try { stream->SetProgressReporter(ProgressBar(src, _terminalWidth / 3, _terminalWidth)); } catch(const std::exception &ex) { } } _session->SendObjectInfo(oi, GetUploadStorageId(), parentId); _session->SendObject(stream); } } void Session::Put(const LocalPath &src, const Path &dst) { using namespace mtp; std::string targetFilename; //handle put case: try { ObjectId parentDir; struct stat st = Stat(src); if (S_ISREG(st.st_mode)) { std::string filename; parentDir = ResolvePath(dst, filename); try { auto objectId = ResolveObjectChild(parentDir, filename); ObjectFormat format = ObjectFormat(_session->GetObjectIntegerProperty(objectId, ObjectProperty::ObjectFormat)); print("format ", ToString(format)); if (format != ObjectFormat::Association) targetFilename = filename; //path exists and it's not directory } catch(const std::exception &ex) { targetFilename = filename; //target object does not exists } } if (!targetFilename.empty()) { Put(parentDir, src, targetFilename); return; } } catch(const std::exception &ex) { } Put(Resolve(dst, true), src, targetFilename); //upload to folder } mtp::ObjectId Session::MakeDirectory(mtp::ObjectId parentId, const std::string & name) { auto noi = _session->CreateDirectory(name, _cd, GetUploadStorageId()); return noi.ObjectId; } void Session::ShowType(const LocalPath &src) { mtp::ObjectFormat format = mtp::ObjectFormatFromFilename(src); mtp::print("mtp object format = ", ToString(format)); } static void PrintFormat(const mtp::ByteArray & format, const mtp::ByteArray & value) { using namespace mtp; InputStream is(format); u16 prop = is.Read16(); DataTypeCode type = DataTypeCode(is.Read16()); u16 rw = is.Read8(); std::string defValue; u32 groupCode = 0; u8 formFlag = 0; try { switch(type) { #define CASE(BITS) \ case mtp::DataTypeCode::Uint##BITS: \ case mtp::DataTypeCode::Int##BITS: \ defValue = std::to_string(is.Read##BITS ()); break CASE(8); CASE(16); CASE(32); CASE(64); CASE(128); #undef CASE case mtp::DataTypeCode::String: defValue = is.ReadString(); break; case mtp::DataTypeCode::ArrayUint8: { u32 size = is.Read32(); std::stringstream ss; HexDump(ss, "raw bytes", size, is); defValue = ss.str(); } break; default: throw std::runtime_error("invalid type " + std::to_string((u16)type)); } groupCode = is.Read32(); formFlag = is.Read8(); } catch(const std::exception & ex) { defValue = ""; } print("property ", mtp::ToString(ObjectProperty(prop)), ", type: ", ToString(type), ", rw: ", rw, ", default: ", defValue, ", groupCode: ", groupCode, ", form flag: ", formFlag, ", value: ", ToString(type, value)); //HexDump("raw", format, true); } void Session::ListProperties(mtp::ObjectId id) { mtp::ObjectFormat format = mtp::ObjectFormat(_session->GetObjectIntegerProperty(id, mtp::ObjectProperty::ObjectFormat)); mtp::print("querying supported properties for format ", mtp::ToString(format)); auto ops = _session->GetObjectPropertiesSupported(format); mtp::print("properties supported: "); for(mtp::ObjectProperty prop: ops.ObjectPropertyCodes) { PrintFormat(_session->GetObjectPropertyDesc(prop, format), _session->GetObjectProperty(id, prop)); } } void Session::ListDeviceProperties() { using namespace mtp; for(DeviceProperty code : _gdi.DevicePropertiesSupported) { auto desc = _session->GetDevicePropertyDesc(code); if (code == DeviceProperty::PerceivedDeviceType) { auto value = _session->GetDeviceIntegerProperty(code); print("property: ", ToString(code), " ", ToString(desc.Type), " ", desc.Writeable? "rw ": "ro ", ToString(PerceivedDeviceType(value))); } else { ByteArray value = _session->GetDeviceProperty(code); print("property: ", ToString(code), " ", ToString(desc.Type), " ", desc.Writeable? "rw ": "ro ", ToString(desc.Type, value)); } } } namespace { template struct DummyPropertyListParser { static mtp::ByteArray Parse(mtp::InputStream &stream, mtp::DataTypeCode dataType) { switch(dataType) { #define CASE(BITS) \ case mtp::DataTypeCode::Uint##BITS: \ case mtp::DataTypeCode::Int##BITS: \ stream.Skip(BITS / 8); break CASE(8); CASE(16); CASE(32); CASE(64); CASE(128); #undef CASE case mtp::DataTypeCode::String: stream.ReadString(); break; default: throw std::runtime_error("got invalid data type code"); } return mtp::ByteArray(); } }; } void Session::GetObjectPropertyList(mtp::ObjectId parent, const std::set &originalObjectList, const mtp::ObjectProperty property) { using namespace mtp; print("testing property ", ToString(property), "..."); std::set objectList; ByteArray data = _session->GetObjectPropertyList(parent, ObjectFormat::Any, property, 0, 1); print("got ", data.size(), " bytes of reply"); HexDump("property list", data); ObjectPropertyListParser parser; bool ok = true; parser.Parse(data, [&objectList, property, &ok](mtp::ObjectId objectId, ObjectProperty p, const ByteArray & ) { if ((p == property || property == ObjectProperty::All)) objectList.insert(objectId); else { print("extra property 0x", ToString(p), " returned for object ", objectId.Id, ", while querying property list ", mtp::ToString(property)); ok = false; } }); std::set extraData; std::set_difference(objectList.begin(), objectList.end(), originalObjectList.begin(), originalObjectList.end(), std::inserter(extraData, extraData.end())); if (!extraData.empty()) { print("inconsistent GetObjectPropertyList for property 0x", mtp::ToString(property)); for(auto objectId : extraData) { print("missing 0x", mtp::ToString(property), " for object ", objectId); ok = false; } } print("getting object property list of type 0x", ToString(property), " ", ok? "PASSED": "FAILED"); } void Session::TestObjectPropertyList(const Path &path) { using namespace mtp; ObjectId id = Resolve(path); msg::ObjectHandles oh = _session->GetObjectHandles(_cs, ObjectFormat::Any, id); std::set objectList; for(auto id : oh.ObjectHandles) objectList.insert(id); print("GetObjectHandles ", id, " returns ", oh.ObjectHandles.size(), " objects, ", objectList.size(), " unique"); GetObjectPropertyList(id, objectList, ObjectProperty::ObjectFilename); GetObjectPropertyList(id, objectList, ObjectProperty::ObjectFormat); GetObjectPropertyList(id, objectList, ObjectProperty::ObjectSize); GetObjectPropertyList(id, objectList, ObjectProperty::DateModified); GetObjectPropertyList(id, objectList, ObjectProperty::DateAdded); GetObjectPropertyList(id, objectList, ObjectProperty::All); } void Session::DisplayDeviceInfo() { using namespace mtp; print(_gdi.Manufacturer); print(_gdi.Model); print(_gdi.DeviceVersion); print(_gdi.SerialNumber); print(_gdi.VendorExtensionDesc); } void Session::DisplayStorageInfo(const StoragePath &path) { using namespace mtp; msg::StorageInfo si; GetStorageByPath(path, si, false); s64 usedBytes = si.MaxCapacity - si.FreeSpaceInBytes; int usedPercents = (1.0 * usedBytes / si.MaxCapacity) * 100; print("used ", usedBytes, " (", usedPercents, "%), free ", si.FreeSpaceInBytes, " bytes of ", si.MaxCapacity); } void Session::ListObjects(const std::string & format) { ListObjects(mtp::ObjectFormat(FromHex(format))); } void Session::ListObjects(mtp::ObjectFormat format) { mtp::print("querying all objects of format ", mtp::ToString(format)); auto objects = _session->GetObjectHandles(mtp::Session::AllStorages, format, mtp::Session::Device); auto & handles = objects.ObjectHandles; for(auto id : handles) { std::string filename, name; try { filename = _session->GetObjectStringProperty(id, mtp::ObjectProperty::ObjectFilename); } catch(const std::exception & ex) { mtp::error("error getting filename: ", ex.what()); } try { name = _session->GetObjectStringProperty(id, mtp::ObjectProperty::Name); } catch(const std::exception & ex) { mtp::error("error getting name: ", ex.what()); } mtp::print(id.Id, "\t", filename, "\t", name); } } void Session::GetObjectReferences(const Path & src) { using namespace mtp; auto refs = _session->GetObjectReferences(Resolve(src)); for (auto id: refs.ObjectHandles) { debug(id.Id, "\t", _session->GetObjectStringProperty(id, ObjectProperty::ObjectFilename)); } } void Session::RebootDevice() { _session->RebootDevice(); } void Session::ZuneInit() { if (!_library) _library = std::make_shared(_session); } void Session::ZuneImport(const LocalPath & path) { ZuneInit(); if (!_library) throw std::runtime_error("library failed to initialise"); using namespace mtp; auto stream = std::make_shared(path); stream->SetTotal(stream->GetSize()); try { stream->SetProgressReporter(ProgressBar(path, _terminalWidth / 3, _terminalWidth)); } catch(const std::exception &ex) { } auto meta = Metadata::Read(path); if (!meta) throw std::runtime_error("no metadata"); print("metadata: ", meta->Artist, " / ", meta->Album, " (", meta->Year, ") / ", meta->Title, " picture description: ", meta->Picture.Description); auto artist = _library->GetArtist(meta->Artist); if (!artist) artist = _library->CreateArtist(meta->Artist); if (!artist) throw std::runtime_error("can't create artist with name " + meta->Artist); debug("got artist record"); auto album = _library->GetAlbum(artist, meta->Album); if (!album) album = _library->CreateAlbum(artist, meta->Album, meta->Year); if (!album) throw std::runtime_error("can't create album with name " + meta->Album); debug("got album record"); ObjectFormat format = ObjectFormatFromFilename(path); auto slashpos = path.rfind('/'); auto filename = slashpos != path.npos? path.substr(slashpos + 1): std::string(path); debug("track format: " + ToString(format)); auto songId = _library->CreateTrack(artist, album, format, meta->Title, meta->Genre, meta->Track, filename, stream->GetSize()); _session->SendObject(stream); if (!meta->Picture.Data.empty()) _library->AddCover(album, meta->Picture.Data); _library->AddTrack(album, songId); } } android-file-transfer-linux-4.5/cli/Session.h000066400000000000000000000136161501120463400212440ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_CLI_SESSION_H #define AFTL_CLI_SESSION_H #include #include #include #include #include #include #include #include namespace mtp { class TrustedApp; DECLARE_PTR(TrustedApp); class Library; DECLARE_PTR(Library); } namespace cli { class Session { mtp::DevicePtr _device; mtp::SessionPtr _session; mtp::TrustedAppPtr _trustedApp; mtp::LibraryPtr _library; mtp::msg::DeviceInfo _gdi; mtp::StorageId _cs; //current storage std::string _csName; //current storage name mtp::ObjectId _cd; //current directory std::atomic _running; bool _interactive; bool _showEvents; bool _showPrompt; std::string _prompt; unsigned _terminalWidth; bool _batterySupported; bool _deviceFriendlyNameSupported; std::multimap _commands; char ** CompletionCallback(const char *text, int start, int end); mtp::ObjectId ResolvePath(const std::string &path, std::string &file); mtp::ObjectId ResolveObjectChild(mtp::ObjectId parent, const std::string &entity); static std::string GetFilename(const std::string &path); static std::string GetDirname(const std::string &path); static std::string FormatTime(const std::string ×pec); void GetObjectPropertyList(mtp::ObjectId parent, const std::set &originalObjectList, const mtp::ObjectProperty property); mtp::StorageId GetUploadStorageId() { return _cs == mtp::Session::AllStorages? mtp::Session::AnyStorage: _cs; } static std::string GetMtpzDataPath(); public: Session(const mtp::SessionPtr &session, bool showPrompt); ~Session(); bool SetFirstStorage(); void UpdatePrompt(); bool IsInteractive() const { return _interactive; } void ShowEvents(bool show) { _showEvents = show; } void InteractiveInput(); void ProcessCommand(const std::string &input); void ProcessCommand(Tokens &&tokens); mtp::ObjectId Resolve(const Path &path, bool create = false); void Help(); void Quit(); void CompletePath(const Path &path, CompletionResult &result); void CompleteStoragePath(const StoragePath &path, CompletionResult &result); mtp::StorageId GetStorageByPath(const StoragePath &path, mtp::msg::StorageInfo &si, bool allowAll); void DisplayDeviceInfo(); void List(mtp::ObjectId parent, bool extended, bool recursive, const std::string &prefix = std::string()); void ListStorages(); void ChangeStorage(const StoragePath &path); void DisplayStorageInfo(const StoragePath &path); void Get(const LocalPath &dst, mtp::ObjectId srcId, bool thumb = false); void Get(const mtp::ObjectId srcId); void GetThumb(const mtp::ObjectId srcId); void GetThumb(const LocalPath &dst, mtp::ObjectId srcId) { Get(dst, srcId, true); } void Cat(const Path &path); void Rename(const Path & path, const std::string & newName); void Put(mtp::ObjectId parentId, const LocalPath &src, const std::string &targetFilename = std::string(), mtp::ObjectFormat format = mtp::ObjectFormat::Any); void Put(const LocalPath &src, const Path &dst); mtp::ObjectId MakeDirectory(mtp::ObjectId parentId, const std::string & name); void ListObjects(const std::string & format); void ListObjects(mtp::ObjectFormat format); void ListProperties(mtp::ObjectId id); void ListDeviceProperties(); void TestObjectPropertyList(const Path &path); void Cancel(); void ChangeDirectory(const Path &path) { _cd = Resolve(path); } void CurrentDirectory(); void List(bool extended, bool recursive) { return List(_cd, extended, recursive); } void List(const Path &path, bool extended, bool recursive) { return List(Resolve(path), extended, recursive); } void Put(const LocalPath &src) { Put(_cd, src); } void Flash(const LocalPath &src) { Put(_cd, src, {}, mtp::ObjectFormat::UndefinedFirmware); } void Get(const Path &src) { Get(Resolve(src)); } void Get(const LocalPath &dst, const Path &src) { Get(dst, Resolve(src)); } void GetThumb(const Path &src) { GetThumb(Resolve(src)); } void GetThumb(const LocalPath &dst, const Path &src) { GetThumb(dst, Resolve(src)); } void MakeDirectory(const Path &path) { std::string name; mtp::ObjectId parent = ResolvePath(path, name); MakeDirectory(parent, name); } void MakePath(const Path &path) { Resolve(path, true); } void MakeArtist(const std::string & name); void Delete(const Path &path) { Delete(Resolve(path)); } void Delete(mtp::ObjectId id) { _session->DeleteObject(id); } void ListProperties(const Path &path) { ListProperties(Resolve(path)); } void GetObjectReferences(const Path & src); static void ShowType(const LocalPath &src); void ZuneInit(); void ZuneImport(const LocalPath & path); void RebootDevice(); template void AddCommand(const std::string &name, const std::string &help, std::function && callback) { _commands.insert(std::make_pair(name, ICommandPtr(new Command(help, std::move(callback))))); } }; DECLARE_PTR(Session); } #endif android-file-transfer-linux-4.5/cli/Tokenizer.cpp000066400000000000000000000031341501120463400221200ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include namespace cli { Tokenizer::Tokenizer(const std::string &text, Tokens &tokens): _text(text), _tokens(tokens), _inputPosition(0) { void *scanner; args_lex_init_extra(this, &scanner); //token_set_debug(1, scanner); args_lex(scanner); args_lex_destroy(scanner); //not exception safe NextArgument(); } void Tokenizer::NextArgument() { std::string arg = _tempString.str(); if (!arg.empty()) { _tokens.push_back(arg); _tempString.str(std::string()); } } size_t Tokenizer::Input(char *buf, size_t max_size) { size_t n = std::min(max_size, _text.size() - _inputPosition); std::copy_n(_text.begin() + _inputPosition, n, buf); _inputPosition += n; return n; } } android-file-transfer-linux-4.5/cli/Tokenizer.h000066400000000000000000000026071501120463400215710ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_CLI_TOKENIZER_H #define AFTL_CLI_TOKENIZER_H #include #include #include namespace cli { class Tokenizer { public: const std::string & _text; Tokens & _tokens; size_t _inputPosition; std::stringstream _tempString; public: Tokenizer(const std::string &text, Tokens &tokens); void Write(const char *text, size_t size) { _tempString.write(text, size); } void Write(const char ch) { _tempString << ch; } void NextArgument(); size_t Input(char *buf, size_t max_size); }; } #endif android-file-transfer-linux-4.5/cli/Tokens.h000066400000000000000000000017611501120463400210620ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_CLI_TOKENS_H #define AFTL_CLI_TOKENS_H #include #include namespace cli { typedef std::list Tokens; } #endif android-file-transfer-linux-4.5/cli/arg_lexer.l000066400000000000000000000021621501120463400215670ustar00rootroot00000000000000%option noyywrap nounput %option reentrant %option 8bit %option nounistd %option batch %option never-interactive %option prefix="args_" %top { # include # define register } %{ # include # include # include # include # define LVAL (yylval_param) # define PARSER (reinterpret_cast(yyget_extra(yyscanner))) # define YY_INPUT(buf,result,max_size) result = PARSER->Input(buf, max_size); static void yy_fatal_error(const char*, yyscan_t); void (*hide_warning)(const char*, yyscan_t) = &yy_fatal_error; %} %x STRING %% \" { BEGIN(STRING); } \\n { PARSER->Write('\n'); } \\t { PARSER->Write('\t'); } \\f { PARSER->Write('\f'); } \\r { PARSER->Write('\r'); } \\b { PARSER->Write('\b'); } \\. { PARSER->Write(yytext[1]); } \" { BEGIN(INITIAL); } [^\\\"]+ { PARSER->Write(yytext, yyleng); } "\\ " { PARSER->Write(' '); } [[:space:]]+ { PARSER->NextArgument(); } <*>. { PARSER->Write(yytext[0]); } %% android-file-transfer-linux-4.5/cli/arg_lexer.l.cpp000066400000000000000000002354131501120463400223570ustar00rootroot00000000000000#line 10 "arg_lexer.l" # include # define register #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 6 #define YY_FLEX_SUBMINOR_VERSION 1 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ /* TODO: this is always defined, so inline it */ #define yyconst const #if defined(__GNUC__) && __GNUC__ >= 3 #define yynoreturn __attribute__((__noreturn__)) #else #define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* An opaque pointer. */ #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif /* For convenience, these vars (plus the bison vars far below) are macros in the reentrant scanner. */ #define yyin yyg->yyin_r #define yyout yyg->yyout_r #define yyextra yyg->yyextra_r #define yyleng yyg->yyleng_r #define yytext yyg->yytext_r #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) #define yy_flex_debug yyg->yy_flex_debug_r /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE args_restart(yyin ,yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) #define YY_LINENO_REWIND_TO(ptr) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ yy_size_t yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via args_restart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] void args_restart (FILE *input_file ,yyscan_t yyscanner ); void args__switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); YY_BUFFER_STATE args__create_buffer (FILE *file,int size ,yyscan_t yyscanner ); void args__delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void args__flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void args_push_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); void args_pop_buffer_state (yyscan_t yyscanner ); static void args_ensure_buffer_stack (yyscan_t yyscanner ); static void args__load_buffer_state (yyscan_t yyscanner ); static void args__init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner ); #define YY_FLUSH_BUFFER args__flush_buffer(YY_CURRENT_BUFFER ,yyscanner) YY_BUFFER_STATE args__scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); YY_BUFFER_STATE args__scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); YY_BUFFER_STATE args__scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner ); void *args_alloc (yy_size_t ,yyscan_t yyscanner ); void *args_realloc (void *,yy_size_t ,yyscan_t yyscanner ); void args_free (void * ,yyscan_t yyscanner ); #define yy_new_buffer args__create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ args_ensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ args__create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ args_ensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ args__create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ #define args_wrap(yyscanner) (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP typedef unsigned char YY_CHAR; typedef yyconst struct yy_trans_info *yy_state_type; #define yytext_ptr yytext_r static yy_state_type yy_get_previous_state (yyscan_t yyscanner ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner); static int yy_get_next_buffer (yyscan_t yyscanner ); static void yynoreturn yy_fatal_error (yyconst char* msg ,yyscan_t yyscanner ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ yyg->yytext_ptr = yy_bp; \ yyleng = (int) (yy_cp - yy_bp); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; #define YY_NUM_RULES 13 #define YY_END_OF_BUFFER 14 struct yy_trans_info { flex_int16_t yy_verify; flex_int16_t yy_nxt; }; static yyconst struct yy_trans_info yy_transition[2399] = { { 0, 0 }, { 0,2143 }, { 0, 0 }, { 0,2141 }, { 1,1032 }, { 2,1032 }, { 3,1032 }, { 4,1032 }, { 5,1032 }, { 6,1032 }, { 7,1032 }, { 8,1032 }, { 9,1034 }, { 10,1039 }, { 11,1034 }, { 12,1034 }, { 13,1034 }, { 14,1032 }, { 15,1032 }, { 16,1032 }, { 17,1032 }, { 18,1032 }, { 19,1032 }, { 20,1032 }, { 21,1032 }, { 22,1032 }, { 23,1032 }, { 24,1032 }, { 25,1032 }, { 26,1032 }, { 27,1032 }, { 28,1032 }, { 29,1032 }, { 30,1032 }, { 31,1032 }, { 32,1034 }, { 33,1032 }, { 34,1036 }, { 35,1032 }, { 36,1032 }, { 37,1032 }, { 38,1032 }, { 39,1032 }, { 40,1032 }, { 41,1032 }, { 42,1032 }, { 43,1032 }, { 44,1032 }, { 45,1032 }, { 46,1032 }, { 47,1032 }, { 48,1032 }, { 49,1032 }, { 50,1032 }, { 51,1032 }, { 52,1032 }, { 53,1032 }, { 54,1032 }, { 55,1032 }, { 56,1032 }, { 57,1032 }, { 58,1032 }, { 59,1032 }, { 60,1032 }, { 61,1032 }, { 62,1032 }, { 63,1032 }, { 64,1032 }, { 65,1032 }, { 66,1032 }, { 67,1032 }, { 68,1032 }, { 69,1032 }, { 70,1032 }, { 71,1032 }, { 72,1032 }, { 73,1032 }, { 74,1032 }, { 75,1032 }, { 76,1032 }, { 77,1032 }, { 78,1032 }, { 79,1032 }, { 80,1032 }, { 81,1032 }, { 82,1032 }, { 83,1032 }, { 84,1032 }, { 85,1032 }, { 86,1032 }, { 87,1032 }, { 88,1032 }, { 89,1032 }, { 90,1032 }, { 91,1032 }, { 92,1041 }, { 93,1032 }, { 94,1032 }, { 95,1032 }, { 96,1032 }, { 97,1032 }, { 98,1032 }, { 99,1032 }, { 100,1032 }, { 101,1032 }, { 102,1032 }, { 103,1032 }, { 104,1032 }, { 105,1032 }, { 106,1032 }, { 107,1032 }, { 108,1032 }, { 109,1032 }, { 110,1032 }, { 111,1032 }, { 112,1032 }, { 113,1032 }, { 114,1032 }, { 115,1032 }, { 116,1032 }, { 117,1032 }, { 118,1032 }, { 119,1032 }, { 120,1032 }, { 121,1032 }, { 122,1032 }, { 123,1032 }, { 124,1032 }, { 125,1032 }, { 126,1032 }, { 127,1032 }, { 128,1032 }, { 129,1032 }, { 130,1032 }, { 131,1032 }, { 132,1032 }, { 133,1032 }, { 134,1032 }, { 135,1032 }, { 136,1032 }, { 137,1032 }, { 138,1032 }, { 139,1032 }, { 140,1032 }, { 141,1032 }, { 142,1032 }, { 143,1032 }, { 144,1032 }, { 145,1032 }, { 146,1032 }, { 147,1032 }, { 148,1032 }, { 149,1032 }, { 150,1032 }, { 151,1032 }, { 152,1032 }, { 153,1032 }, { 154,1032 }, { 155,1032 }, { 156,1032 }, { 157,1032 }, { 158,1032 }, { 159,1032 }, { 160,1032 }, { 161,1032 }, { 162,1032 }, { 163,1032 }, { 164,1032 }, { 165,1032 }, { 166,1032 }, { 167,1032 }, { 168,1032 }, { 169,1032 }, { 170,1032 }, { 171,1032 }, { 172,1032 }, { 173,1032 }, { 174,1032 }, { 175,1032 }, { 176,1032 }, { 177,1032 }, { 178,1032 }, { 179,1032 }, { 180,1032 }, { 181,1032 }, { 182,1032 }, { 183,1032 }, { 184,1032 }, { 185,1032 }, { 186,1032 }, { 187,1032 }, { 188,1032 }, { 189,1032 }, { 190,1032 }, { 191,1032 }, { 192,1032 }, { 193,1032 }, { 194,1032 }, { 195,1032 }, { 196,1032 }, { 197,1032 }, { 198,1032 }, { 199,1032 }, { 200,1032 }, { 201,1032 }, { 202,1032 }, { 203,1032 }, { 204,1032 }, { 205,1032 }, { 206,1032 }, { 207,1032 }, { 208,1032 }, { 209,1032 }, { 210,1032 }, { 211,1032 }, { 212,1032 }, { 213,1032 }, { 214,1032 }, { 215,1032 }, { 216,1032 }, { 217,1032 }, { 218,1032 }, { 219,1032 }, { 220,1032 }, { 221,1032 }, { 222,1032 }, { 223,1032 }, { 224,1032 }, { 225,1032 }, { 226,1032 }, { 227,1032 }, { 228,1032 }, { 229,1032 }, { 230,1032 }, { 231,1032 }, { 232,1032 }, { 233,1032 }, { 234,1032 }, { 235,1032 }, { 236,1032 }, { 237,1032 }, { 238,1032 }, { 239,1032 }, { 240,1032 }, { 241,1032 }, { 242,1032 }, { 243,1032 }, { 244,1032 }, { 245,1032 }, { 246,1032 }, { 247,1032 }, { 248,1032 }, { 249,1032 }, { 250,1032 }, { 251,1032 }, { 252,1032 }, { 253,1032 }, { 254,1032 }, { 255,1032 }, { 256,1032 }, { 0, 0 }, { 0,1883 }, { 1, 774 }, { 2, 774 }, { 3, 774 }, { 4, 774 }, { 5, 774 }, { 6, 774 }, { 7, 774 }, { 8, 774 }, { 9, 776 }, { 10, 781 }, { 11, 776 }, { 12, 776 }, { 13, 776 }, { 14, 774 }, { 15, 774 }, { 16, 774 }, { 17, 774 }, { 18, 774 }, { 19, 774 }, { 20, 774 }, { 21, 774 }, { 22, 774 }, { 23, 774 }, { 24, 774 }, { 25, 774 }, { 26, 774 }, { 27, 774 }, { 28, 774 }, { 29, 774 }, { 30, 774 }, { 31, 774 }, { 32, 776 }, { 33, 774 }, { 34, 778 }, { 35, 774 }, { 36, 774 }, { 37, 774 }, { 38, 774 }, { 39, 774 }, { 40, 774 }, { 41, 774 }, { 42, 774 }, { 43, 774 }, { 44, 774 }, { 45, 774 }, { 46, 774 }, { 47, 774 }, { 48, 774 }, { 49, 774 }, { 50, 774 }, { 51, 774 }, { 52, 774 }, { 53, 774 }, { 54, 774 }, { 55, 774 }, { 56, 774 }, { 57, 774 }, { 58, 774 }, { 59, 774 }, { 60, 774 }, { 61, 774 }, { 62, 774 }, { 63, 774 }, { 64, 774 }, { 65, 774 }, { 66, 774 }, { 67, 774 }, { 68, 774 }, { 69, 774 }, { 70, 774 }, { 71, 774 }, { 72, 774 }, { 73, 774 }, { 74, 774 }, { 75, 774 }, { 76, 774 }, { 77, 774 }, { 78, 774 }, { 79, 774 }, { 80, 774 }, { 81, 774 }, { 82, 774 }, { 83, 774 }, { 84, 774 }, { 85, 774 }, { 86, 774 }, { 87, 774 }, { 88, 774 }, { 89, 774 }, { 90, 774 }, { 91, 774 }, { 92, 783 }, { 93, 774 }, { 94, 774 }, { 95, 774 }, { 96, 774 }, { 97, 774 }, { 98, 774 }, { 99, 774 }, { 100, 774 }, { 101, 774 }, { 102, 774 }, { 103, 774 }, { 104, 774 }, { 105, 774 }, { 106, 774 }, { 107, 774 }, { 108, 774 }, { 109, 774 }, { 110, 774 }, { 111, 774 }, { 112, 774 }, { 113, 774 }, { 114, 774 }, { 115, 774 }, { 116, 774 }, { 117, 774 }, { 118, 774 }, { 119, 774 }, { 120, 774 }, { 121, 774 }, { 122, 774 }, { 123, 774 }, { 124, 774 }, { 125, 774 }, { 126, 774 }, { 127, 774 }, { 128, 774 }, { 129, 774 }, { 130, 774 }, { 131, 774 }, { 132, 774 }, { 133, 774 }, { 134, 774 }, { 135, 774 }, { 136, 774 }, { 137, 774 }, { 138, 774 }, { 139, 774 }, { 140, 774 }, { 141, 774 }, { 142, 774 }, { 143, 774 }, { 144, 774 }, { 145, 774 }, { 146, 774 }, { 147, 774 }, { 148, 774 }, { 149, 774 }, { 150, 774 }, { 151, 774 }, { 152, 774 }, { 153, 774 }, { 154, 774 }, { 155, 774 }, { 156, 774 }, { 157, 774 }, { 158, 774 }, { 159, 774 }, { 160, 774 }, { 161, 774 }, { 162, 774 }, { 163, 774 }, { 164, 774 }, { 165, 774 }, { 166, 774 }, { 167, 774 }, { 168, 774 }, { 169, 774 }, { 170, 774 }, { 171, 774 }, { 172, 774 }, { 173, 774 }, { 174, 774 }, { 175, 774 }, { 176, 774 }, { 177, 774 }, { 178, 774 }, { 179, 774 }, { 180, 774 }, { 181, 774 }, { 182, 774 }, { 183, 774 }, { 184, 774 }, { 185, 774 }, { 186, 774 }, { 187, 774 }, { 188, 774 }, { 189, 774 }, { 190, 774 }, { 191, 774 }, { 192, 774 }, { 193, 774 }, { 194, 774 }, { 195, 774 }, { 196, 774 }, { 197, 774 }, { 198, 774 }, { 199, 774 }, { 200, 774 }, { 201, 774 }, { 202, 774 }, { 203, 774 }, { 204, 774 }, { 205, 774 }, { 206, 774 }, { 207, 774 }, { 208, 774 }, { 209, 774 }, { 210, 774 }, { 211, 774 }, { 212, 774 }, { 213, 774 }, { 214, 774 }, { 215, 774 }, { 216, 774 }, { 217, 774 }, { 218, 774 }, { 219, 774 }, { 220, 774 }, { 221, 774 }, { 222, 774 }, { 223, 774 }, { 224, 774 }, { 225, 774 }, { 226, 774 }, { 227, 774 }, { 228, 774 }, { 229, 774 }, { 230, 774 }, { 231, 774 }, { 232, 774 }, { 233, 774 }, { 234, 774 }, { 235, 774 }, { 236, 774 }, { 237, 774 }, { 238, 774 }, { 239, 774 }, { 240, 774 }, { 241, 774 }, { 242, 774 }, { 243, 774 }, { 244, 774 }, { 245, 774 }, { 246, 774 }, { 247, 774 }, { 248, 774 }, { 249, 774 }, { 250, 774 }, { 251, 774 }, { 252, 774 }, { 253, 774 }, { 254, 774 }, { 255, 774 }, { 256, 774 }, { 0, 0 }, { 0,1625 }, { 1, 559 }, { 2, 559 }, { 3, 559 }, { 4, 559 }, { 5, 559 }, { 6, 559 }, { 7, 559 }, { 8, 559 }, { 9, 559 }, { 10, 817 }, { 11, 559 }, { 12, 559 }, { 13, 559 }, { 14, 559 }, { 15, 559 }, { 16, 559 }, { 17, 559 }, { 18, 559 }, { 19, 559 }, { 20, 559 }, { 21, 559 }, { 22, 559 }, { 23, 559 }, { 24, 559 }, { 25, 559 }, { 26, 559 }, { 27, 559 }, { 28, 559 }, { 29, 559 }, { 30, 559 }, { 31, 559 }, { 32, 559 }, { 33, 559 }, { 34, 538 }, { 35, 559 }, { 36, 559 }, { 37, 559 }, { 38, 559 }, { 39, 559 }, { 40, 559 }, { 41, 559 }, { 42, 559 }, { 43, 559 }, { 44, 559 }, { 45, 559 }, { 46, 559 }, { 47, 559 }, { 48, 559 }, { 49, 559 }, { 50, 559 }, { 51, 559 }, { 52, 559 }, { 53, 559 }, { 54, 559 }, { 55, 559 }, { 56, 559 }, { 57, 559 }, { 58, 559 }, { 59, 559 }, { 60, 559 }, { 61, 559 }, { 62, 559 }, { 63, 559 }, { 64, 559 }, { 65, 559 }, { 66, 559 }, { 67, 559 }, { 68, 559 }, { 69, 559 }, { 70, 559 }, { 71, 559 }, { 72, 559 }, { 73, 559 }, { 74, 559 }, { 75, 559 }, { 76, 559 }, { 77, 559 }, { 78, 559 }, { 79, 559 }, { 80, 559 }, { 81, 559 }, { 82, 559 }, { 83, 559 }, { 84, 559 }, { 85, 559 }, { 86, 559 }, { 87, 559 }, { 88, 559 }, { 89, 559 }, { 90, 559 }, { 91, 559 }, { 92,1075 }, { 93, 559 }, { 94, 559 }, { 95, 559 }, { 96, 559 }, { 97, 559 }, { 98, 559 }, { 99, 559 }, { 100, 559 }, { 101, 559 }, { 102, 559 }, { 103, 559 }, { 104, 559 }, { 105, 559 }, { 106, 559 }, { 107, 559 }, { 108, 559 }, { 109, 559 }, { 110, 559 }, { 111, 559 }, { 112, 559 }, { 113, 559 }, { 114, 559 }, { 115, 559 }, { 116, 559 }, { 117, 559 }, { 118, 559 }, { 119, 559 }, { 120, 559 }, { 121, 559 }, { 122, 559 }, { 123, 559 }, { 124, 559 }, { 125, 559 }, { 126, 559 }, { 127, 559 }, { 128, 559 }, { 129, 559 }, { 130, 559 }, { 131, 559 }, { 132, 559 }, { 133, 559 }, { 134, 559 }, { 135, 559 }, { 136, 559 }, { 137, 559 }, { 138, 559 }, { 139, 559 }, { 140, 559 }, { 141, 559 }, { 142, 559 }, { 143, 559 }, { 144, 559 }, { 145, 559 }, { 146, 559 }, { 147, 559 }, { 148, 559 }, { 149, 559 }, { 150, 559 }, { 151, 559 }, { 152, 559 }, { 153, 559 }, { 154, 559 }, { 155, 559 }, { 156, 559 }, { 157, 559 }, { 158, 559 }, { 159, 559 }, { 160, 559 }, { 161, 559 }, { 162, 559 }, { 163, 559 }, { 164, 559 }, { 165, 559 }, { 166, 559 }, { 167, 559 }, { 168, 559 }, { 169, 559 }, { 170, 559 }, { 171, 559 }, { 172, 559 }, { 173, 559 }, { 174, 559 }, { 175, 559 }, { 176, 559 }, { 177, 559 }, { 178, 559 }, { 179, 559 }, { 180, 559 }, { 181, 559 }, { 182, 559 }, { 183, 559 }, { 184, 559 }, { 185, 559 }, { 186, 559 }, { 187, 559 }, { 188, 559 }, { 189, 559 }, { 190, 559 }, { 191, 559 }, { 192, 559 }, { 193, 559 }, { 194, 559 }, { 195, 559 }, { 196, 559 }, { 197, 559 }, { 198, 559 }, { 199, 559 }, { 200, 559 }, { 201, 559 }, { 202, 559 }, { 203, 559 }, { 204, 559 }, { 205, 559 }, { 206, 559 }, { 207, 559 }, { 208, 559 }, { 209, 559 }, { 210, 559 }, { 211, 559 }, { 212, 559 }, { 213, 559 }, { 214, 559 }, { 215, 559 }, { 216, 559 }, { 217, 559 }, { 218, 559 }, { 219, 559 }, { 220, 559 }, { 221, 559 }, { 222, 559 }, { 223, 559 }, { 224, 559 }, { 225, 559 }, { 226, 559 }, { 227, 559 }, { 228, 559 }, { 229, 559 }, { 230, 559 }, { 231, 559 }, { 232, 559 }, { 233, 559 }, { 234, 559 }, { 235, 559 }, { 236, 559 }, { 237, 559 }, { 238, 559 }, { 239, 559 }, { 240, 559 }, { 241, 559 }, { 242, 559 }, { 243, 559 }, { 244, 559 }, { 245, 559 }, { 246, 559 }, { 247, 559 }, { 248, 559 }, { 249, 559 }, { 250, 559 }, { 251, 559 }, { 252, 559 }, { 253, 559 }, { 254, 559 }, { 255, 559 }, { 256, 559 }, { 0, 0 }, { 0,1367 }, { 1, 301 }, { 2, 301 }, { 3, 301 }, { 4, 301 }, { 5, 301 }, { 6, 301 }, { 7, 301 }, { 8, 301 }, { 9, 301 }, { 10, 559 }, { 11, 301 }, { 12, 301 }, { 13, 301 }, { 14, 301 }, { 15, 301 }, { 16, 301 }, { 17, 301 }, { 18, 301 }, { 19, 301 }, { 20, 301 }, { 21, 301 }, { 22, 301 }, { 23, 301 }, { 24, 301 }, { 25, 301 }, { 26, 301 }, { 27, 301 }, { 28, 301 }, { 29, 301 }, { 30, 301 }, { 31, 301 }, { 32, 301 }, { 33, 301 }, { 34, 280 }, { 35, 301 }, { 36, 301 }, { 37, 301 }, { 38, 301 }, { 39, 301 }, { 40, 301 }, { 41, 301 }, { 42, 301 }, { 43, 301 }, { 44, 301 }, { 45, 301 }, { 46, 301 }, { 47, 301 }, { 48, 301 }, { 49, 301 }, { 50, 301 }, { 51, 301 }, { 52, 301 }, { 53, 301 }, { 54, 301 }, { 55, 301 }, { 56, 301 }, { 57, 301 }, { 58, 301 }, { 59, 301 }, { 60, 301 }, { 61, 301 }, { 62, 301 }, { 63, 301 }, { 64, 301 }, { 65, 301 }, { 66, 301 }, { 67, 301 }, { 68, 301 }, { 69, 301 }, { 70, 301 }, { 71, 301 }, { 72, 301 }, { 73, 301 }, { 74, 301 }, { 75, 301 }, { 76, 301 }, { 77, 301 }, { 78, 301 }, { 79, 301 }, { 80, 301 }, { 81, 301 }, { 82, 301 }, { 83, 301 }, { 84, 301 }, { 85, 301 }, { 86, 301 }, { 87, 301 }, { 88, 301 }, { 89, 301 }, { 90, 301 }, { 91, 301 }, { 92, 817 }, { 93, 301 }, { 94, 301 }, { 95, 301 }, { 96, 301 }, { 97, 301 }, { 98, 301 }, { 99, 301 }, { 100, 301 }, { 101, 301 }, { 102, 301 }, { 103, 301 }, { 104, 301 }, { 105, 301 }, { 106, 301 }, { 107, 301 }, { 108, 301 }, { 109, 301 }, { 110, 301 }, { 111, 301 }, { 112, 301 }, { 113, 301 }, { 114, 301 }, { 115, 301 }, { 116, 301 }, { 117, 301 }, { 118, 301 }, { 119, 301 }, { 120, 301 }, { 121, 301 }, { 122, 301 }, { 123, 301 }, { 124, 301 }, { 125, 301 }, { 126, 301 }, { 127, 301 }, { 128, 301 }, { 129, 301 }, { 130, 301 }, { 131, 301 }, { 132, 301 }, { 133, 301 }, { 134, 301 }, { 135, 301 }, { 136, 301 }, { 137, 301 }, { 138, 301 }, { 139, 301 }, { 140, 301 }, { 141, 301 }, { 142, 301 }, { 143, 301 }, { 144, 301 }, { 145, 301 }, { 146, 301 }, { 147, 301 }, { 148, 301 }, { 149, 301 }, { 150, 301 }, { 151, 301 }, { 152, 301 }, { 153, 301 }, { 154, 301 }, { 155, 301 }, { 156, 301 }, { 157, 301 }, { 158, 301 }, { 159, 301 }, { 160, 301 }, { 161, 301 }, { 162, 301 }, { 163, 301 }, { 164, 301 }, { 165, 301 }, { 166, 301 }, { 167, 301 }, { 168, 301 }, { 169, 301 }, { 170, 301 }, { 171, 301 }, { 172, 301 }, { 173, 301 }, { 174, 301 }, { 175, 301 }, { 176, 301 }, { 177, 301 }, { 178, 301 }, { 179, 301 }, { 180, 301 }, { 181, 301 }, { 182, 301 }, { 183, 301 }, { 184, 301 }, { 185, 301 }, { 186, 301 }, { 187, 301 }, { 188, 301 }, { 189, 301 }, { 190, 301 }, { 191, 301 }, { 192, 301 }, { 193, 301 }, { 194, 301 }, { 195, 301 }, { 196, 301 }, { 197, 301 }, { 198, 301 }, { 199, 301 }, { 200, 301 }, { 201, 301 }, { 202, 301 }, { 203, 301 }, { 204, 301 }, { 205, 301 }, { 206, 301 }, { 207, 301 }, { 208, 301 }, { 209, 301 }, { 210, 301 }, { 211, 301 }, { 212, 301 }, { 213, 301 }, { 214, 301 }, { 215, 301 }, { 216, 301 }, { 217, 301 }, { 218, 301 }, { 219, 301 }, { 220, 301 }, { 221, 301 }, { 222, 301 }, { 223, 301 }, { 224, 301 }, { 225, 301 }, { 226, 301 }, { 227, 301 }, { 228, 301 }, { 229, 301 }, { 230, 301 }, { 231, 301 }, { 232, 301 }, { 233, 301 }, { 234, 301 }, { 235, 301 }, { 236, 301 }, { 237, 301 }, { 238, 301 }, { 239, 301 }, { 240, 301 }, { 241, 301 }, { 242, 301 }, { 243, 301 }, { 244, 301 }, { 245, 301 }, { 246, 301 }, { 247, 301 }, { 248, 301 }, { 249, 301 }, { 250, 301 }, { 251, 301 }, { 252, 301 }, { 253, 301 }, { 254, 301 }, { 255, 301 }, { 256, 301 }, { 0, 12 }, { 0,1109 }, { 0, 11 }, { 0,1107 }, { 0, 1 }, { 0,1105 }, { 0, 0 }, { 0, 11 }, { 0,1102 }, { 0, 12 }, { 0,1100 }, { 0, 0 }, { 9, 815 }, { 10, 815 }, { 11, 815 }, { 12, 815 }, { 13, 815 }, { 9, 810 }, { 10, 810 }, { 11, 810 }, { 12, 810 }, { 13, 810 }, { 0, 8 }, { 0,1087 }, { 0, 10 }, { 0,1085 }, { 0, 7 }, { 0,1083 }, { 0, 6 }, { 0,1081 }, { 0, 4 }, { 0,1079 }, { 0, 2 }, { 0,1077 }, { 0, 0 }, { 32, 815 }, { 0, 5 }, { 0,1073 }, { 0, 3 }, { 0,1071 }, { 32, 810 }, { 0, 0 }, { 32, 15 }, { 0, 9 }, { 0,1066 }, { 1, 808 }, { 2, 808 }, { 3, 808 }, { 4, 808 }, { 5, 808 }, { 6, 808 }, { 7, 808 }, { 8, 808 }, { 9, 808 }, { 10, 808 }, { 11, 808 }, { 12, 808 }, { 13, 808 }, { 14, 808 }, { 15, 808 }, { 16, 808 }, { 17, 808 }, { 18, 808 }, { 19, 808 }, { 20, 808 }, { 21, 808 }, { 22, 808 }, { 23, 808 }, { 24, 808 }, { 25, 808 }, { 26, 808 }, { 27, 808 }, { 28, 808 }, { 29, 808 }, { 30, 808 }, { 31, 808 }, { 32, 808 }, { 33, 808 }, { 0, 0 }, { 35, 808 }, { 36, 808 }, { 37, 808 }, { 38, 808 }, { 39, 808 }, { 40, 808 }, { 41, 808 }, { 42, 808 }, { 43, 808 }, { 44, 808 }, { 45, 808 }, { 46, 808 }, { 47, 808 }, { 48, 808 }, { 49, 808 }, { 50, 808 }, { 51, 808 }, { 52, 808 }, { 53, 808 }, { 54, 808 }, { 55, 808 }, { 56, 808 }, { 57, 808 }, { 58, 808 }, { 59, 808 }, { 60, 808 }, { 61, 808 }, { 62, 808 }, { 63, 808 }, { 64, 808 }, { 65, 808 }, { 66, 808 }, { 67, 808 }, { 68, 808 }, { 69, 808 }, { 70, 808 }, { 71, 808 }, { 72, 808 }, { 73, 808 }, { 74, 808 }, { 75, 808 }, { 76, 808 }, { 77, 808 }, { 78, 808 }, { 79, 808 }, { 80, 808 }, { 81, 808 }, { 82, 808 }, { 83, 808 }, { 84, 808 }, { 85, 808 }, { 86, 808 }, { 87, 808 }, { 88, 808 }, { 89, 808 }, { 90, 808 }, { 91, 808 }, { 0, 0 }, { 93, 808 }, { 94, 808 }, { 95, 808 }, { 96, 808 }, { 97, 808 }, { 98, 808 }, { 99, 808 }, { 100, 808 }, { 101, 808 }, { 102, 808 }, { 103, 808 }, { 104, 808 }, { 105, 808 }, { 106, 808 }, { 107, 808 }, { 108, 808 }, { 109, 808 }, { 110, 808 }, { 111, 808 }, { 112, 808 }, { 113, 808 }, { 114, 808 }, { 115, 808 }, { 116, 808 }, { 117, 808 }, { 118, 808 }, { 119, 808 }, { 120, 808 }, { 121, 808 }, { 122, 808 }, { 123, 808 }, { 124, 808 }, { 125, 808 }, { 126, 808 }, { 127, 808 }, { 128, 808 }, { 129, 808 }, { 130, 808 }, { 131, 808 }, { 132, 808 }, { 133, 808 }, { 134, 808 }, { 135, 808 }, { 136, 808 }, { 137, 808 }, { 138, 808 }, { 139, 808 }, { 140, 808 }, { 141, 808 }, { 142, 808 }, { 143, 808 }, { 144, 808 }, { 145, 808 }, { 146, 808 }, { 147, 808 }, { 148, 808 }, { 149, 808 }, { 150, 808 }, { 151, 808 }, { 152, 808 }, { 153, 808 }, { 154, 808 }, { 155, 808 }, { 156, 808 }, { 157, 808 }, { 158, 808 }, { 159, 808 }, { 160, 808 }, { 161, 808 }, { 162, 808 }, { 163, 808 }, { 164, 808 }, { 165, 808 }, { 166, 808 }, { 167, 808 }, { 168, 808 }, { 169, 808 }, { 170, 808 }, { 171, 808 }, { 172, 808 }, { 173, 808 }, { 174, 808 }, { 175, 808 }, { 176, 808 }, { 177, 808 }, { 178, 808 }, { 179, 808 }, { 180, 808 }, { 181, 808 }, { 182, 808 }, { 183, 808 }, { 184, 808 }, { 185, 808 }, { 186, 808 }, { 187, 808 }, { 188, 808 }, { 189, 808 }, { 190, 808 }, { 191, 808 }, { 192, 808 }, { 193, 808 }, { 194, 808 }, { 195, 808 }, { 196, 808 }, { 197, 808 }, { 198, 808 }, { 199, 808 }, { 200, 808 }, { 201, 808 }, { 202, 808 }, { 203, 808 }, { 204, 808 }, { 205, 808 }, { 206, 808 }, { 207, 808 }, { 208, 808 }, { 209, 808 }, { 210, 808 }, { 211, 808 }, { 212, 808 }, { 213, 808 }, { 214, 808 }, { 215, 808 }, { 216, 808 }, { 217, 808 }, { 218, 808 }, { 219, 808 }, { 220, 808 }, { 221, 808 }, { 222, 808 }, { 223, 808 }, { 224, 808 }, { 225, 808 }, { 226, 808 }, { 227, 808 }, { 228, 808 }, { 229, 808 }, { 230, 808 }, { 231, 808 }, { 232, 808 }, { 233, 808 }, { 234, 808 }, { 235, 808 }, { 236, 808 }, { 237, 808 }, { 238, 808 }, { 239, 808 }, { 240, 808 }, { 241, 808 }, { 242, 808 }, { 243, 808 }, { 244, 808 }, { 245, 808 }, { 246, 808 }, { 247, 808 }, { 248, 808 }, { 249, 808 }, { 250, 808 }, { 251, 808 }, { 252, 808 }, { 253, 808 }, { 254, 808 }, { 255, 808 }, { 256, 808 }, { 0, 9 }, { 0, 808 }, { 1, 550 }, { 2, 550 }, { 3, 550 }, { 4, 550 }, { 5, 550 }, { 6, 550 }, { 7, 550 }, { 8, 550 }, { 9, 550 }, { 10, 550 }, { 11, 550 }, { 12, 550 }, { 13, 550 }, { 14, 550 }, { 15, 550 }, { 16, 550 }, { 17, 550 }, { 18, 550 }, { 19, 550 }, { 20, 550 }, { 21, 550 }, { 22, 550 }, { 23, 550 }, { 24, 550 }, { 25, 550 }, { 26, 550 }, { 27, 550 }, { 28, 550 }, { 29, 550 }, { 30, 550 }, { 31, 550 }, { 32, 550 }, { 33, 550 }, { 0, 0 }, { 35, 550 }, { 36, 550 }, { 37, 550 }, { 38, 550 }, { 39, 550 }, { 40, 550 }, { 41, 550 }, { 42, 550 }, { 43, 550 }, { 44, 550 }, { 45, 550 }, { 46, 550 }, { 47, 550 }, { 48, 550 }, { 49, 550 }, { 50, 550 }, { 51, 550 }, { 52, 550 }, { 53, 550 }, { 54, 550 }, { 55, 550 }, { 56, 550 }, { 57, 550 }, { 58, 550 }, { 59, 550 }, { 60, 550 }, { 61, 550 }, { 62, 550 }, { 63, 550 }, { 64, 550 }, { 65, 550 }, { 66, 550 }, { 67, 550 }, { 68, 550 }, { 69, 550 }, { 70, 550 }, { 71, 550 }, { 72, 550 }, { 73, 550 }, { 74, 550 }, { 75, 550 }, { 76, 550 }, { 77, 550 }, { 78, 550 }, { 79, 550 }, { 80, 550 }, { 81, 550 }, { 82, 550 }, { 83, 550 }, { 84, 550 }, { 85, 550 }, { 86, 550 }, { 87, 550 }, { 88, 550 }, { 89, 550 }, { 90, 550 }, { 91, 550 }, { 0, 0 }, { 93, 550 }, { 94, 550 }, { 95, 550 }, { 96, 550 }, { 97, 550 }, { 98, 550 }, { 99, 550 }, { 100, 550 }, { 101, 550 }, { 102, 550 }, { 103, 550 }, { 104, 550 }, { 105, 550 }, { 106, 550 }, { 107, 550 }, { 108, 550 }, { 109, 550 }, { 110, 550 }, { 111, 550 }, { 112, 550 }, { 113, 550 }, { 114, 550 }, { 115, 550 }, { 116, 550 }, { 117, 550 }, { 118, 550 }, { 119, 550 }, { 120, 550 }, { 121, 550 }, { 122, 550 }, { 123, 550 }, { 124, 550 }, { 125, 550 }, { 126, 550 }, { 127, 550 }, { 128, 550 }, { 129, 550 }, { 130, 550 }, { 131, 550 }, { 132, 550 }, { 133, 550 }, { 134, 550 }, { 135, 550 }, { 136, 550 }, { 137, 550 }, { 138, 550 }, { 139, 550 }, { 140, 550 }, { 141, 550 }, { 142, 550 }, { 143, 550 }, { 144, 550 }, { 145, 550 }, { 146, 550 }, { 147, 550 }, { 148, 550 }, { 149, 550 }, { 150, 550 }, { 151, 550 }, { 152, 550 }, { 153, 550 }, { 154, 550 }, { 155, 550 }, { 156, 550 }, { 157, 550 }, { 158, 550 }, { 159, 550 }, { 160, 550 }, { 161, 550 }, { 162, 550 }, { 163, 550 }, { 164, 550 }, { 165, 550 }, { 166, 550 }, { 167, 550 }, { 168, 550 }, { 169, 550 }, { 170, 550 }, { 171, 550 }, { 172, 550 }, { 173, 550 }, { 174, 550 }, { 175, 550 }, { 176, 550 }, { 177, 550 }, { 178, 550 }, { 179, 550 }, { 180, 550 }, { 181, 550 }, { 182, 550 }, { 183, 550 }, { 184, 550 }, { 185, 550 }, { 186, 550 }, { 187, 550 }, { 188, 550 }, { 189, 550 }, { 190, 550 }, { 191, 550 }, { 192, 550 }, { 193, 550 }, { 194, 550 }, { 195, 550 }, { 196, 550 }, { 197, 550 }, { 198, 550 }, { 199, 550 }, { 200, 550 }, { 201, 550 }, { 202, 550 }, { 203, 550 }, { 204, 550 }, { 205, 550 }, { 206, 550 }, { 207, 550 }, { 208, 550 }, { 209, 550 }, { 210, 550 }, { 211, 550 }, { 212, 550 }, { 213, 550 }, { 214, 550 }, { 215, 550 }, { 216, 550 }, { 217, 550 }, { 218, 550 }, { 219, 550 }, { 220, 550 }, { 221, 550 }, { 222, 550 }, { 223, 550 }, { 224, 550 }, { 225, 550 }, { 226, 550 }, { 227, 550 }, { 228, 550 }, { 229, 550 }, { 230, 550 }, { 231, 550 }, { 232, 550 }, { 233, 550 }, { 234, 550 }, { 235, 550 }, { 236, 550 }, { 237, 550 }, { 238, 550 }, { 239, 550 }, { 240, 550 }, { 241, 550 }, { 242, 550 }, { 243, 550 }, { 244, 550 }, { 245, 550 }, { 246, 550 }, { 247, 550 }, { 248, 550 }, { 249, 550 }, { 250, 550 }, { 251, 550 }, { 252, 550 }, { 253, 550 }, { 254, 550 }, { 255, 550 }, { 256, 550 }, { 0, 12 }, { 0, 550 }, { 1,-533 }, { 2,-533 }, { 3,-533 }, { 4,-533 }, { 5,-533 }, { 6,-533 }, { 7,-533 }, { 8,-533 }, { 9,-533 }, { 0, 0 }, { 11,-533 }, { 12,-533 }, { 13,-533 }, { 14,-533 }, { 15,-533 }, { 16,-533 }, { 17,-533 }, { 18,-533 }, { 19,-533 }, { 20,-533 }, { 21,-533 }, { 22,-533 }, { 23,-533 }, { 24,-533 }, { 25,-533 }, { 26,-533 }, { 27,-533 }, { 28,-533 }, { 29,-533 }, { 30,-533 }, { 31,-533 }, { 32,-533 }, { 33,-533 }, { 34,-533 }, { 35,-533 }, { 36,-533 }, { 37,-533 }, { 38,-533 }, { 39,-533 }, { 40,-533 }, { 41,-533 }, { 42,-533 }, { 43,-533 }, { 44,-533 }, { 45,-533 }, { 46,-533 }, { 47,-533 }, { 48,-533 }, { 49,-533 }, { 50,-533 }, { 51,-533 }, { 52,-533 }, { 53,-533 }, { 54,-533 }, { 55,-533 }, { 56,-533 }, { 57,-533 }, { 58,-533 }, { 59,-533 }, { 60,-533 }, { 61,-533 }, { 62,-533 }, { 63,-533 }, { 64,-533 }, { 65,-533 }, { 66,-533 }, { 67,-533 }, { 68,-533 }, { 69,-533 }, { 70,-533 }, { 71,-533 }, { 72,-533 }, { 73,-533 }, { 74,-533 }, { 75,-533 }, { 76,-533 }, { 77,-533 }, { 78,-533 }, { 79,-533 }, { 80,-533 }, { 81,-533 }, { 82,-533 }, { 83,-533 }, { 84,-533 }, { 85,-533 }, { 86,-533 }, { 87,-533 }, { 88,-533 }, { 89,-533 }, { 90,-533 }, { 91,-533 }, { 92,-533 }, { 93,-533 }, { 94,-533 }, { 95,-533 }, { 96,-533 }, { 97,-533 }, { 98,-531 }, { 99,-533 }, { 100,-533 }, { 101,-533 }, { 102,-529 }, { 103,-533 }, { 104,-533 }, { 105,-533 }, { 106,-533 }, { 107,-533 }, { 108,-533 }, { 109,-533 }, { 110,-527 }, { 111,-533 }, { 112,-533 }, { 113,-533 }, { 114,-523 }, { 115,-533 }, { 116,-521 }, { 117,-533 }, { 118,-533 }, { 119,-533 }, { 120,-533 }, { 121,-533 }, { 122,-533 }, { 123,-533 }, { 124,-533 }, { 125,-533 }, { 126,-533 }, { 127,-533 }, { 128,-533 }, { 129,-533 }, { 130,-533 }, { 131,-533 }, { 132,-533 }, { 133,-533 }, { 134,-533 }, { 135,-533 }, { 136,-533 }, { 137,-533 }, { 138,-533 }, { 139,-533 }, { 140,-533 }, { 141,-533 }, { 142,-533 }, { 143,-533 }, { 144,-533 }, { 145,-533 }, { 146,-533 }, { 147,-533 }, { 148,-533 }, { 149,-533 }, { 150,-533 }, { 151,-533 }, { 152,-533 }, { 153,-533 }, { 154,-533 }, { 155,-533 }, { 156,-533 }, { 157,-533 }, { 158,-533 }, { 159,-533 }, { 160,-533 }, { 161,-533 }, { 162,-533 }, { 163,-533 }, { 164,-533 }, { 165,-533 }, { 166,-533 }, { 167,-533 }, { 168,-533 }, { 169,-533 }, { 170,-533 }, { 171,-533 }, { 172,-533 }, { 173,-533 }, { 174,-533 }, { 175,-533 }, { 176,-533 }, { 177,-533 }, { 178,-533 }, { 179,-533 }, { 180,-533 }, { 181,-533 }, { 182,-533 }, { 183,-533 }, { 184,-533 }, { 185,-533 }, { 186,-533 }, { 187,-533 }, { 188,-533 }, { 189,-533 }, { 190,-533 }, { 191,-533 }, { 192,-533 }, { 193,-533 }, { 194,-533 }, { 195,-533 }, { 196,-533 }, { 197,-533 }, { 198,-533 }, { 199,-533 }, { 200,-533 }, { 201,-533 }, { 202,-533 }, { 203,-533 }, { 204,-533 }, { 205,-533 }, { 206,-533 }, { 207,-533 }, { 208,-533 }, { 209,-533 }, { 210,-533 }, { 211,-533 }, { 212,-533 }, { 213,-533 }, { 214,-533 }, { 215,-533 }, { 216,-533 }, { 217,-533 }, { 218,-533 }, { 219,-533 }, { 220,-533 }, { 221,-533 }, { 222,-533 }, { 223,-533 }, { 224,-533 }, { 225,-533 }, { 226,-533 }, { 227,-533 }, { 228,-533 }, { 229,-533 }, { 230,-533 }, { 231,-533 }, { 232,-533 }, { 233,-533 }, { 234,-533 }, { 235,-533 }, { 236,-533 }, { 237,-533 }, { 238,-533 }, { 239,-533 }, { 240,-533 }, { 241,-533 }, { 242,-533 }, { 243,-533 }, { 244,-533 }, { 245,-533 }, { 246,-533 }, { 247,-533 }, { 248,-533 }, { 249,-533 }, { 250,-533 }, { 251,-533 }, { 252,-533 }, { 253,-533 }, { 254,-533 }, { 255,-533 }, { 256,-533 }, { 0, 11 }, { 0, 292 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 9, 0 }, { 10, 0 }, { 11, 0 }, { 12, 0 }, { 13, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 32, 0 }, { 0, 9 }, { 0, 258 }, { 1, 0 }, { 2, 0 }, { 3, 0 }, { 4, 0 }, { 5, 0 }, { 6, 0 }, { 7, 0 }, { 8, 0 }, { 9, 0 }, { 10, 0 }, { 11, 0 }, { 12, 0 }, { 13, 0 }, { 14, 0 }, { 15, 0 }, { 16, 0 }, { 17, 0 }, { 18, 0 }, { 19, 0 }, { 20, 0 }, { 21, 0 }, { 22, 0 }, { 23, 0 }, { 24, 0 }, { 25, 0 }, { 26, 0 }, { 27, 0 }, { 28, 0 }, { 29, 0 }, { 30, 0 }, { 31, 0 }, { 32, 0 }, { 33, 0 }, { 0, 0 }, { 35, 0 }, { 36, 0 }, { 37, 0 }, { 38, 0 }, { 39, 0 }, { 40, 0 }, { 41, 0 }, { 42, 0 }, { 43, 0 }, { 44, 0 }, { 45, 0 }, { 46, 0 }, { 47, 0 }, { 48, 0 }, { 49, 0 }, { 50, 0 }, { 51, 0 }, { 52, 0 }, { 53, 0 }, { 54, 0 }, { 55, 0 }, { 56, 0 }, { 57, 0 }, { 58, 0 }, { 59, 0 }, { 60, 0 }, { 61, 0 }, { 62, 0 }, { 63, 0 }, { 64, 0 }, { 65, 0 }, { 66, 0 }, { 67, 0 }, { 68, 0 }, { 69, 0 }, { 70, 0 }, { 71, 0 }, { 72, 0 }, { 73, 0 }, { 74, 0 }, { 75, 0 }, { 76, 0 }, { 77, 0 }, { 78, 0 }, { 79, 0 }, { 80, 0 }, { 81, 0 }, { 82, 0 }, { 83, 0 }, { 84, 0 }, { 85, 0 }, { 86, 0 }, { 87, 0 }, { 88, 0 }, { 89, 0 }, { 90, 0 }, { 91, 0 }, { 0, 0 }, { 93, 0 }, { 94, 0 }, { 95, 0 }, { 96, 0 }, { 97, 0 }, { 98, 0 }, { 99, 0 }, { 100, 0 }, { 101, 0 }, { 102, 0 }, { 103, 0 }, { 104, 0 }, { 105, 0 }, { 106, 0 }, { 107, 0 }, { 108, 0 }, { 109, 0 }, { 110, 0 }, { 111, 0 }, { 112, 0 }, { 113, 0 }, { 114, 0 }, { 115, 0 }, { 116, 0 }, { 117, 0 }, { 118, 0 }, { 119, 0 }, { 120, 0 }, { 121, 0 }, { 122, 0 }, { 123, 0 }, { 124, 0 }, { 125, 0 }, { 126, 0 }, { 127, 0 }, { 128, 0 }, { 129, 0 }, { 130, 0 }, { 131, 0 }, { 132, 0 }, { 133, 0 }, { 134, 0 }, { 135, 0 }, { 136, 0 }, { 137, 0 }, { 138, 0 }, { 139, 0 }, { 140, 0 }, { 141, 0 }, { 142, 0 }, { 143, 0 }, { 144, 0 }, { 145, 0 }, { 146, 0 }, { 147, 0 }, { 148, 0 }, { 149, 0 }, { 150, 0 }, { 151, 0 }, { 152, 0 }, { 153, 0 }, { 154, 0 }, { 155, 0 }, { 156, 0 }, { 157, 0 }, { 158, 0 }, { 159, 0 }, { 160, 0 }, { 161, 0 }, { 162, 0 }, { 163, 0 }, { 164, 0 }, { 165, 0 }, { 166, 0 }, { 167, 0 }, { 168, 0 }, { 169, 0 }, { 170, 0 }, { 171, 0 }, { 172, 0 }, { 173, 0 }, { 174, 0 }, { 175, 0 }, { 176, 0 }, { 177, 0 }, { 178, 0 }, { 179, 0 }, { 180, 0 }, { 181, 0 }, { 182, 0 }, { 183, 0 }, { 184, 0 }, { 185, 0 }, { 186, 0 }, { 187, 0 }, { 188, 0 }, { 189, 0 }, { 190, 0 }, { 191, 0 }, { 192, 0 }, { 193, 0 }, { 194, 0 }, { 195, 0 }, { 196, 0 }, { 197, 0 }, { 198, 0 }, { 199, 0 }, { 200, 0 }, { 201, 0 }, { 202, 0 }, { 203, 0 }, { 204, 0 }, { 205, 0 }, { 206, 0 }, { 207, 0 }, { 208, 0 }, { 209, 0 }, { 210, 0 }, { 211, 0 }, { 212, 0 }, { 213, 0 }, { 214, 0 }, { 215, 0 }, { 216, 0 }, { 217, 0 }, { 218, 0 }, { 219, 0 }, { 220, 0 }, { 221, 0 }, { 222, 0 }, { 223, 0 }, { 224, 0 }, { 225, 0 }, { 226, 0 }, { 227, 0 }, { 228, 0 }, { 229, 0 }, { 230, 0 }, { 231, 0 }, { 232, 0 }, { 233, 0 }, { 234, 0 }, { 235, 0 }, { 236, 0 }, { 237, 0 }, { 238, 0 }, { 239, 0 }, { 240, 0 }, { 241, 0 }, { 242, 0 }, { 243, 0 }, { 244, 0 }, { 245, 0 }, { 246, 0 }, { 247, 0 }, { 248, 0 }, { 249, 0 }, { 250, 0 }, { 251, 0 }, { 252, 0 }, { 253, 0 }, { 254, 0 }, { 255, 0 }, { 256, 0 }, { 257, 14 }, { 1, 0 }, }; static yyconst struct yy_trans_info *yy_start_state_list[5] = { &yy_transition[1], &yy_transition[3], &yy_transition[261], &yy_transition[519], &yy_transition[777], } ; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET #define YY_NO_UNISTD_H 1 # include # include # include # include # define LVAL (yylval_param) # define PARSER (reinterpret_cast(args_get_extra(yyscanner))) # define YY_INPUT(buf,result,max_size) result = PARSER->Input(buf, max_size); static void yy_fatal_error(const char*, yyscan_t); void (*hide_warning)(const char*, yyscan_t) = &yy_fatal_error; #define INITIAL 0 #define STRING 1 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif /* Holds the entire state of the reentrant scanner. */ struct yyguts_t { /* User-defined. Not touched by flex. */ YY_EXTRA_TYPE yyextra_r; /* The rest are the same as the globals declared in the non-reentrant scanner. */ FILE *yyin_r, *yyout_r; size_t yy_buffer_stack_top; /**< index of top of stack. */ size_t yy_buffer_stack_max; /**< capacity of stack. */ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ char yy_hold_char; int yy_n_chars; int yyleng_r; char *yy_c_buf_p; int yy_init; int yy_start; int yy_did_buffer_switch_on_eof; int yy_start_stack_ptr; int yy_start_stack_depth; int *yy_start_stack; yy_state_type yy_last_accepting_state; char* yy_last_accepting_cpos; int yylineno_r; int yy_flex_debug_r; char *yytext_r; int yy_more_flag; int yy_more_len; }; /* end struct yyguts_t */ static int yy_init_globals (yyscan_t yyscanner ); int args_lex_init (yyscan_t* scanner); int args_lex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int args_lex_destroy (yyscan_t yyscanner ); int args_get_debug (yyscan_t yyscanner ); void args_set_debug (int debug_flag ,yyscan_t yyscanner ); YY_EXTRA_TYPE args_get_extra (yyscan_t yyscanner ); void args_set_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); FILE *args_get_in (yyscan_t yyscanner ); void args_set_in (FILE * _in_str ,yyscan_t yyscanner ); FILE *args_get_out (yyscan_t yyscanner ); void args_set_out (FILE * _out_str ,yyscan_t yyscanner ); int args_get_leng (yyscan_t yyscanner ); char *args_get_text (yyscan_t yyscanner ); int args_get_lineno (yyscan_t yyscanner ); void args_set_lineno (int _line_number ,yyscan_t yyscanner ); int args_get_column (yyscan_t yyscanner ); void args_set_column (int _column_no ,yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int args_wrap (yyscan_t yyscanner ); #else extern int args_wrap (yyscan_t yyscanner ); #endif #endif #ifndef YY_NO_UNPUT #endif #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner ); #else static int input (yyscan_t yyscanner ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k */ #define YY_READ_BUF_SIZE 16384 #else #define YY_READ_BUF_SIZE 8192 #endif /* __ia64__ */ #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ errno=0; \ while ( (result = (int) read( fileno(yyin), buf, (yy_size_t) max_size )) < 0 ) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int args_lex (yyscan_t yyscanner); #define YY_DECL int args_lex (yyscan_t yyscanner) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK /*LINTED*/break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { yy_state_type yy_current_state; char *yy_cp, *yy_bp; int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( !yyg->yy_init ) { yyg->yy_init = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! yyg->yy_start ) yyg->yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { args_ensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = args__create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } args__load_buffer_state(yyscanner ); } { while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { yy_cp = yyg->yy_c_buf_p; /* Support of yytext. */ *yy_cp = yyg->yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = yy_start_state_list[yyg->yy_start]; yy_match: { yyconst struct yy_trans_info *yy_trans_info; YY_CHAR yy_c; for ( yy_c = YY_SC_TO_UI(*yy_cp); (yy_trans_info = &yy_current_state[(unsigned int) yy_c])-> yy_verify == yy_c; yy_c = YY_SC_TO_UI(*++yy_cp) ) yy_current_state += yy_trans_info->yy_nxt; } yy_find_action: yy_act = yy_current_state[-1].yy_nxt; YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 1: YY_RULE_SETUP { BEGIN(STRING); } YY_BREAK case 2: YY_RULE_SETUP { PARSER->Write('\n'); } YY_BREAK case 3: YY_RULE_SETUP { PARSER->Write('\t'); } YY_BREAK case 4: YY_RULE_SETUP { PARSER->Write('\f'); } YY_BREAK case 5: YY_RULE_SETUP { PARSER->Write('\r'); } YY_BREAK case 6: YY_RULE_SETUP { PARSER->Write('\b'); } YY_BREAK case 7: YY_RULE_SETUP { PARSER->Write(yytext[1]); } YY_BREAK case 8: YY_RULE_SETUP { BEGIN(INITIAL); } YY_BREAK case 9: /* rule 9 can match eol */ YY_RULE_SETUP { PARSER->Write(yytext, yyleng); } YY_BREAK case 10: YY_RULE_SETUP { PARSER->Write(' '); } YY_BREAK case 11: /* rule 11 can match eol */ YY_RULE_SETUP { PARSER->NextArgument(); } YY_BREAK case 12: YY_RULE_SETUP { PARSER->Write(yytext[0]); } YY_BREAK case 13: YY_RULE_SETUP ECHO; YY_BREAK case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(STRING): yyterminate(); case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * args_lex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_c_buf_p; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( args_wrap(yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of user's declarations */ } /* end of args_lex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; char *source = yyg->yytext_ptr; yy_size_t number_to_move, i; int ret_val; if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (yy_size_t) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ args_realloc((void *) b->yy_ch_buf,(yy_size_t) (b->yy_buf_size + 2) ,yyscanner ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; args_restart(yyin ,yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((int) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) args_realloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,(yy_size_t) new_size ,yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); } yyg->yy_n_chars += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { yy_state_type yy_current_state; char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_current_state = yy_start_state_list[yyg->yy_start]; for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { yy_current_state += yy_current_state[(*yy_cp ? YY_SC_TO_UI(*yy_cp) : 256)].yy_nxt; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { int yy_is_jam; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ int yy_c = 256; yyconst struct yy_trans_info *yy_trans_info; yy_trans_info = &yy_current_state[(unsigned int) yy_c]; yy_current_state += yy_trans_info->yy_nxt; yy_is_jam = (yy_trans_info->yy_verify != yy_c); (void)yyg; return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_UNPUT #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *yyg->yy_c_buf_p = yyg->yy_hold_char; if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ int offset = yyg->yy_c_buf_p - yyg->yytext_ptr; ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ args_restart(yyin ,yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( args_wrap(yyscanner ) ) return 0; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ yyg->yy_hold_char = *++yyg->yy_c_buf_p; return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ void args_restart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ args_ensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = args__create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } args__init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); args__load_buffer_state(yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * @param yyscanner The scanner object. */ void args__switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* TODO. We should be able to replace this entire function body * with * args_pop_buffer_state(); * args_push_buffer_state(new_buffer); */ args_ensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; args__load_buffer_state(yyscanner ); /* We don't actually know whether we did this switch during * EOF (args_wrap()) processing, but the only time this flag * is looked at is after args_wrap() is called, so it's safe * to go ahead and always set it. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void args__load_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * @param yyscanner The scanner object. * @return the allocated buffer state. */ YY_BUFFER_STATE args__create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) args_alloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in args__create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) args_alloc((yy_size_t) (b->yy_buf_size + 2) ,yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in args__create_buffer()" ); b->yy_is_our_buffer = 1; args__init_buffer(b,file ,yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with args__create_buffer() * @param yyscanner The scanner object. */ void args__delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) args_free((void *) b->yy_ch_buf ,yyscanner ); args_free((void *) b ,yyscanner ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a args_restart() or at EOF. */ static void args__init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; args__flush_buffer(b ,yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then args__init_buffer was _probably_ * called from args_restart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * @param yyscanner The scanner object. */ void args__flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) args__load_buffer_state(yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * @param yyscanner The scanner object. */ void args_push_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; args_ensure_buffer_stack(yyscanner); /* This block is copied from args__switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from args__switch_to_buffer. */ args__load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * @param yyscanner The scanner object. */ void args_pop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; args__delete_buffer(YY_CURRENT_BUFFER ,yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { args__load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void args_ensure_buffer_stack (yyscan_t yyscanner) { int num_to_alloc; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ yyg->yy_buffer_stack = (struct yy_buffer_state**)args_alloc (num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in args_ensure_buffer_stack()" ); memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; yyg->yy_buffer_stack_top = 0; return; } if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ yy_size_t grow_size = 8 /* arbitrary grow size */; num_to_alloc = yyg->yy_buffer_stack_max + grow_size; yyg->yy_buffer_stack = (struct yy_buffer_state**)args_realloc (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in args_ensure_buffer_stack()" ); /* zero only the new slots.*/ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE args__scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return NULL; b = (YY_BUFFER_STATE) args_alloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in args__scan_buffer()" ); b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = NULL; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; args__switch_to_buffer(b ,yyscanner ); return b; } /** Setup the input buffer state to scan a string. The next call to args_lex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * args__scan_bytes() instead. */ YY_BUFFER_STATE args__scan_string (yyconst char * yystr , yyscan_t yyscanner) { return args__scan_bytes(yystr,(int) strlen(yystr) ,yyscanner); } /** Setup the input buffer state to scan the given bytes. The next call to args_lex() will * scan from a @e copy of @a bytes. * @param yybytes the byte buffer to scan * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE args__scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n; yy_size_t i; /* Get memory for full buffer, including space for trailing EOB's. */ n = (yy_size_t) _yybytes_len + 2; buf = (char *) args_alloc(n ,yyscanner ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in args__scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = args__scan_buffer(buf,n ,yyscanner); if ( ! b ) YY_FATAL_ERROR( "bad buffer in args__scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yynoreturn yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ yy_size_t yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = yyg->yy_hold_char; \ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ yyg->yy_hold_char = *yyg->yy_c_buf_p; \ *yyg->yy_c_buf_p = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the user-defined data for this scanner. * @param yyscanner The scanner object. */ YY_EXTRA_TYPE args_get_extra (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyextra; } /** Get the current line number. * @param yyscanner The scanner object. */ int args_get_lineno (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yylineno; } /** Get the current column number. * @param yyscanner The scanner object. */ int args_get_column (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yycolumn; } /** Get the input stream. * @param yyscanner The scanner object. */ FILE *args_get_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *args_get_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ int args_get_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ char *args_get_text (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yytext; } /** Set the user-defined data. This data is never touched by the scanner. * @param user_defined The data to be associated with this scanner. * @param yyscanner The scanner object. */ void args_set_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current line number. * @param _line_number line number * @param yyscanner The scanner object. */ void args_set_lineno (int _line_number , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* lineno is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) YY_FATAL_ERROR( "args_set_lineno called with no buffer" ); yylineno = _line_number; } /** Set the current column. * @param _column_no column number * @param yyscanner The scanner object. */ void args_set_column (int _column_no , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* column is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) YY_FATAL_ERROR( "args_set_column called with no buffer" ); yycolumn = _column_no; } /** Set the input stream. This does not discard the current * input buffer. * @param _in_str A readable stream. * @param yyscanner The scanner object. * @see args__switch_to_buffer */ void args_set_in (FILE * _in_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyin = _in_str ; } void args_set_out (FILE * _out_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyout = _out_str ; } int args_get_debug (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void args_set_debug (int _bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = _bdebug ; } /* Accessor methods for yylval and yylloc */ /* User-visible API */ /* args_lex_init is special because it creates the scanner itself, so it is * the ONLY reentrant function that doesn't take the scanner as the last argument. * That's why we explicitly handle the declaration, instead of using our macros. */ int args_lex_init(yyscan_t* ptr_yy_globals) { if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) args_alloc ( sizeof( struct yyguts_t ), NULL ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); return yy_init_globals ( *ptr_yy_globals ); } /* args_lex_init_extra has the same functionality as args_lex_init, but follows the * convention of taking the scanner as the last argument. Note however, that * this is a *pointer* to a scanner, as it will be allocated by this call (and * is the reason, too, why this function also must handle its own declaration). * The user defined value in the first argument will be available to args_alloc in * the yyextra field. */ int args_lex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals ) { struct yyguts_t dummy_yyguts; args_set_extra (yy_user_defined, &dummy_yyguts); if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) args_alloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); args_set_extra (yy_user_defined, *ptr_yy_globals); return yy_init_globals ( *ptr_yy_globals ); } static int yy_init_globals (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Initialization is the same as for the non-reentrant scanner. * This function is called from args_lex_destroy(), so don't allocate here. */ yyg->yy_buffer_stack = NULL; yyg->yy_buffer_stack_top = 0; yyg->yy_buffer_stack_max = 0; yyg->yy_c_buf_p = NULL; yyg->yy_init = 0; yyg->yy_start = 0; yyg->yy_start_stack_ptr = 0; yyg->yy_start_stack_depth = 0; yyg->yy_start_stack = NULL; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = NULL; yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by * args_lex_init() */ return 0; } /* args_lex_destroy is for both reentrant and non-reentrant scanners. */ int args_lex_destroy (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ args__delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; args_pop_buffer_state(yyscanner); } /* Destroy the stack itself. */ args_free(yyg->yy_buffer_stack ,yyscanner); yyg->yy_buffer_stack = NULL; /* Destroy the start condition stack. */ args_free(yyg->yy_start_stack ,yyscanner ); yyg->yy_start_stack = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * args_lex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ args_free ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) { int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *args_alloc (yy_size_t size , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; return malloc(size); } void *args_realloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return realloc(ptr, size); } void args_free (void * ptr , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; free( (char *) ptr ); /* see args_realloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" android-file-transfer-linux-4.5/cli/arg_lexer.l.h000066400000000000000000000213751501120463400220240ustar00rootroot00000000000000#ifndef args_HEADER_H #define args_HEADER_H 1 #define args_IN_HEADER 1 #line 10 "arg_lexer.l" # include # define register #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 6 #define YY_FLEX_SUBMINOR_VERSION 1 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ /* TODO: this is always defined, so inline it */ #define yyconst const #if defined(__GNUC__) && __GNUC__ >= 3 #define yynoreturn __attribute__((__noreturn__)) #else #define yynoreturn #endif /* An opaque pointer. */ #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif /* For convenience, these vars (plus the bison vars far below) are macros in the reentrant scanner. */ #define yyin yyg->yyin_r #define yyout yyg->yyout_r #define yyextra yyg->yyextra_r #define yyleng yyg->yyleng_r #define yytext yyg->yytext_r #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) #define yy_flex_debug yyg->yy_flex_debug_r /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ void args_restart (FILE *input_file ,yyscan_t yyscanner ); void args__switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); YY_BUFFER_STATE args__create_buffer (FILE *file,int size ,yyscan_t yyscanner ); void args__delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void args__flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void args_push_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); void args_pop_buffer_state (yyscan_t yyscanner ); YY_BUFFER_STATE args__scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); YY_BUFFER_STATE args__scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); YY_BUFFER_STATE args__scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner ); void *args_alloc (yy_size_t ,yyscan_t yyscanner ); void *args_realloc (void *,yy_size_t ,yyscan_t yyscanner ); void args_free (void * ,yyscan_t yyscanner ); /* Begin user sect3 */ #define args_wrap(yyscanner) (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP #define yytext_ptr yytext_r #ifdef YY_HEADER_EXPORT_START_CONDITIONS #define INITIAL 0 #define STRING 1 #endif #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif int args_lex_init (yyscan_t* scanner); int args_lex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int args_lex_destroy (yyscan_t yyscanner ); int args_get_debug (yyscan_t yyscanner ); void args_set_debug (int debug_flag ,yyscan_t yyscanner ); YY_EXTRA_TYPE args_get_extra (yyscan_t yyscanner ); void args_set_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); FILE *args_get_in (yyscan_t yyscanner ); void args_set_in (FILE * _in_str ,yyscan_t yyscanner ); FILE *args_get_out (yyscan_t yyscanner ); void args_set_out (FILE * _out_str ,yyscan_t yyscanner ); int args_get_leng (yyscan_t yyscanner ); char *args_get_text (yyscan_t yyscanner ); int args_get_lineno (yyscan_t yyscanner ); void args_set_lineno (int _line_number ,yyscan_t yyscanner ); int args_get_column (yyscan_t yyscanner ); void args_set_column (int _column_no ,yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int args_wrap (yyscan_t yyscanner ); #else extern int args_wrap (yyscan_t yyscanner ); #endif #endif #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k */ #define YY_READ_BUF_SIZE 16384 #else #define YY_READ_BUF_SIZE 8192 #endif /* __ia64__ */ #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int args_lex (yyscan_t yyscanner); #define YY_DECL int args_lex (yyscan_t yyscanner) #endif /* !YY_DECL */ /* yy_get_previous_state - get the state just before the EOB char was reached */ #undef YY_NEW_FILE #undef YY_FLUSH_BUFFER #undef yy_set_bol #undef yy_new_buffer #undef yy_set_interactive #undef YY_DO_BEFORE_ACTION #ifdef YY_DECL_IS_OURS #undef YY_DECL_IS_OURS #undef YY_DECL #endif #line 611 "arg_lexer.l.h" #undef args_IN_HEADER #endif /* args_HEADER_H */ android-file-transfer-linux-4.5/cli/cli.cpp000066400000000000000000000134341501120463400207210ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { std::atomic gSession(nullptr); static void SigIntHandler(int) { cli::Session * session = gSession.load(); if (!session) exit(0); try { session->Cancel(); } catch(const std::exception & ex) { mtp::debug("cancellation failed: ", ex.what()); session->Quit(); exit(0); //fixme: use readline alternate interface (manually feeding data from poll loop) } } } int main(int argc, char **argv) { using namespace mtp; bool forceInteractive = false; bool showHelp = false; bool showPrompt = true; bool showVersion = false; bool claimInterface = true; bool showEvents = false; bool resetDevice = false; bool listDevices = false; std::string deviceFilter; const char *fileInput = nullptr; if (!isatty(STDIN_FILENO)) showPrompt = false; static struct option long_options[] = { {"verbose", no_argument, 0, 'v' }, {"interactive", no_argument, 0, 'i' }, {"batch", no_argument, 0, 'b' }, {"events", no_argument, 0, 'e' }, {"help", no_argument, 0, 'h' }, {"version", no_argument, 0, 'V' }, {"no-claim", no_argument, 0, 'C' }, {"input-file", required_argument, 0, 'f' }, {"reset-device", no_argument , 0, 'R' }, {"device-name", required_argument, 0, 'd' }, {"device-list", no_argument, 0, 'l' }, {0, 0, 0, 0 } }; struct sigaction newHandler = { }; newHandler.sa_handler = &SigIntHandler; newHandler.sa_flags = SA_RESTART; if (sigaction(SIGINT, &newHandler, nullptr) != 0) perror("sigaction(SIGINT)"); while(true) { int optionIndex = 0; //index of matching option int c = getopt_long(argc, argv, "ibehvVCRf:ld:", long_options, &optionIndex); if (c == -1) break; switch(c) { case 'f': fileInput = optarg; //falling back to batch processing here case 'b': showPrompt = false; //no break here, prompt = false, interactive = true case 'i': forceInteractive = true; break; case 'v': g_debug = true; break; case 'V': showVersion = true; break; case 'C': claimInterface = false; break; case 'e': showEvents = true; break; case 'R': resetDevice = true; break; case 'l': listDevices = true; break; case 'd': deviceFilter = optarg; break; case '?': case 'h': default: showHelp = true; } } if (fileInput) { close(STDIN_FILENO); int fd = open(fileInput, O_RDONLY); if (fd == -1) { perror("open"); exit(1); } if (fd != STDIN_FILENO) { fprintf(stderr, "failed to reopen stdin\n"); exit(1); } } if (showHelp) { error( "usage:\n" "-h\t--help\t\tshow this help\n" "-v\t--verbose\tshow debug output\n" "-i\t--interactive\tforce interactive mode\n" "-b\t--batch\t\tbatch command processing\n" "-e\t--events\tallow event processing\n" "-f\t--input-file\tuse file to read input commands\n" "-C\t--no-claim\tno usb interface claim\n" "-R\t--reset-device\treset usb device before connecting\n" "-d\t--device-name\tuse device name (could be partial name, e.g. model or manufacturer)\n" "-l\t--device-list\tlist devices\n" "-V\t--version\tshow version information" ); exit(0); } if (showVersion) { print(GetVersion()); exit(0); } usb::ContextPtr ctx(new usb::Context); if (listDevices) { for (usb::DeviceDescriptorPtr desc : ctx->GetDevices()) { try { auto device = Device::Open(ctx, desc, claimInterface, resetDevice); if (device) { auto di = device->GetInfo(); if (di.Matches(deviceFilter)) { print(di.GetFilesystemFriendlyName()); } } } catch (const std::exception & ex) { error("Device::Find failed:", ex.what()); } } return 0; } cli::SessionPtr session; try { auto device = Device::FindFirst(ctx, deviceFilter, claimInterface, resetDevice); if (!device) { error("device not found (filter = '", deviceFilter, "'"); exit(1); } session = std::make_shared(device->OpenSession(1), showPrompt); if (!session->SetFirstStorage()) { error("your device may be locked or does not have any storage available"); session.reset(); device.reset(); } } catch(const std::exception &ex) { error("Device::Find failed:", ex.what()); } ctx.reset(); if (!session) exit(1); try { bool hasCommands = optind >= argc; gSession.store(session.get()); session->ShowEvents(showEvents); if (forceInteractive || (session->IsInteractive() && hasCommands)) { session->InteractiveInput(); } else { for(int i = optind; i < argc; ++i) { session->ProcessCommand(argv[i]); } } gSession.store(nullptr); session.reset(); exit(0); } catch (const std::exception &ex) { error("error: ", ex.what()); exit(1); } } android-file-transfer-linux-4.5/cli/update.sh000077500000000000000000000001211501120463400212540ustar00rootroot00000000000000#!/bin/sh flex -L -F -o arg_lexer.l.cpp --header-file=arg_lexer.l.h arg_lexer.l android-file-transfer-linux-4.5/cmake/000077500000000000000000000000001501120463400177525ustar00rootroot00000000000000android-file-transfer-linux-4.5/cmake/FindReadline.cmake000066400000000000000000000040331501120463400233000ustar00rootroot00000000000000# - Try to find readline include dirs and libraries # # Usage of this module as follows: # # find_package(Readline) # # Variables used by this module, they can change the default behaviour and need # to be set before calling find_package: # # Readline_ROOT_DIR Set this variable to the root installation of # readline if the module has problems finding the # proper installation path. # # Variables defined by this module: # # READLINE_FOUND System has readline, include and lib dirs found # Readline_INCLUDE_DIR The readline include directories. # Readline_LIBRARY The readline library. find_path(Readline_ROOT_DIR NAMES include/readline/readline.h includes/editline/readline.h develop/headers/x86/readline/readline.h develop/headers/x86/editline/readline.h develop/headers/readline/readline.h develop/headers/editline/readline.h ) find_path(Readline_INCLUDE_DIR NAMES readline.h HINTS ${Readline_ROOT_DIR}/include/readline ${Readline_ROOT_DIR}/develop/headers/x86/readline ${Readline_ROOT_DIR}/develop/headers/readline ${Readline_ROOT_DIR}/include/editline ${Readline_ROOT_DIR}/develop/headers/x86/editline ${Readline_ROOT_DIR}/develop/headers/editline ) if(EXISTS ${Readline_INCLUDE_DIR}/history.h) add_definitions(-DHAVE_READLINE_HISTORY_H=1) endif() find_library(Readline_LIBRARY NAMES readline edit HINTS ${Readline_ROOT_DIR}/lib ${Readline_ROOT_DIR}/develop/lib/x86 ${Readline_ROOT_DIR}/develop/lib ) if(Readline_INCLUDE_DIR AND Readline_LIBRARY) set(READLINE_FOUND TRUE) else(Readline_INCLUDE_DIR AND Readline_LIBRARY) FIND_LIBRARY(Readline_LIBRARY NAMES readline edit) include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_DIR Readline_LIBRARY ) MARK_AS_ADVANCED(Readline_INCLUDE_DIR Readline_LIBRARY) endif(Readline_INCLUDE_DIR AND Readline_LIBRARY) mark_as_advanced( Readline_ROOT_DIR Readline_INCLUDE_DIR Readline_LIBRARY ) android-file-transfer-linux-4.5/fuse/000077500000000000000000000000001501120463400176345ustar00rootroot00000000000000android-file-transfer-linux-4.5/fuse/CMakeLists.txt000066400000000000000000000025421501120463400223770ustar00rootroot00000000000000add_executable(aft-mtp-mount fuse.cpp) target_link_libraries(aft-mtp-mount ${MTP_LIBRARIES} ${FUSE_LDFLAGS} ${CMAKE_THREAD_LIBS_INIT}) install(TARGETS aft-mtp-mount RUNTIME DESTINATION bin) if (BUILD_QT_UI AND (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")) add_custom_command(TARGET aft-mtp-mount POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../../Frameworks/" $) add_custom_command(TARGET aft-mtp-mount POST_BUILD COMMAND ${OTOOL_BIN} -L $) set(AFT_MTP_MOUNT_PATH "${MACOSX_BUNDLE_ROOT_DIR}/Contents/SharedSupport/bin/aft-mtp-mount") set(MACOSX_BUNDLE_LIBS_INSTALL " ${MACOSX_BUNDLE_LIBS_INSTALL} file(INSTALL \"${CMAKE_CURRENT_BINARY_DIR}/aft-mtp-mount\" DESTINATION \"${MACOSX_BUNDLE_ROOT_DIR}/Contents/SharedSupport/bin\") ") foreach(DYNLIB ${MACOSX_BUNDLE_LIBS}) get_filename_component(DYNLIB_NAME "${DYNLIB}" NAME) set(MACOSX_BUNDLE_LIBS_INSTALL " ${MACOSX_BUNDLE_LIBS_INSTALL} message(STATUS \"fuse: fixing ${DYNLIB_NAME}\") execute_process(COMMAND \"${CMAKE_INSTALL_NAME_TOOL}\" -change \"${DYNLIB}\" \"@executable_path/../../Frameworks/${DYNLIB_NAME}\" \"${AFT_MTP_MOUNT_PATH}\") ") endforeach() set(MACOSX_BUNDLE_LIBS_INSTALL " ${MACOSX_BUNDLE_LIBS_INSTALL} execute_process(COMMAND \"${OTOOL_BIN}\" -L \"${AFT_MTP_MOUNT_PATH}\") " PARENT_SCOPE) endif() android-file-transfer-linux-4.5/fuse/Exception.h000066400000000000000000000034131501120463400217440ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_FUSE_EXCEPTION_H #define AFTL_FUSE_EXCEPTION_H #include #include #include #include namespace mtp { namespace fuse { class Exception : public std::runtime_error { public: Exception(const std::string &what) throw() : std::runtime_error(what + ": " + GetErrorMessage(errno)) { } Exception(const std::string &what, int returnCode) throw() : std::runtime_error(what + ": " + GetErrorMessage(returnCode)) { } static std::string GetErrorMessage(int returnCode) { char buf[1024]; #if defined(_GNU_SOURCE) && defined(__GLIBC__) std::string text(strerror_r(returnCode, buf, sizeof(buf))); #else int r = strerror_r(returnCode, buf, sizeof(buf)); std::string text(r == 0? buf: "strerror_r() failed"); #endif return text; } }; #define FUSE_CALL(...) do { int _r = __VA_ARGS__ ; if (_r < 0) throw mtp::fuse::Exception(#__VA_ARGS__ " failed", -_r); } while(false) }} #endif android-file-transfer-linux-4.5/fuse/FuseDirectory.h000066400000000000000000000035641501120463400226040ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_FUSE_FUSEDIRECTORY_H #define AFTL_FUSE_FUSEDIRECTORY_H #include #include namespace mtp { namespace fuse { using CharArray = std::vector; struct FuseDirectory { fuse_req_t Request; FuseDirectory(fuse_req_t request): Request(request) { } void Add(CharArray & data, const std::string &name, const struct stat & entry) { if (data.empty()) data.reserve(4096); size_t size = fuse_add_direntry(Request, NULL, 0, name.c_str(), NULL, 0); size_t offset = data.size(); data.resize(data.size() + size); fuse_add_direntry(Request, data.data() + offset, size, name.c_str(), &entry, data.size()); //request is not used inside fuse here, so we could cache resulting dirent data } static void Reply(fuse_req_t req, const CharArray &data, off_t off, size_t size) { if (off >= (off_t)data.size()) FUSE_CALL(fuse_reply_buf(req, NULL, 0)); else { FUSE_CALL(fuse_reply_buf(req, data.data() + off, std::min(size, data.size() - off))); } } }; }} #endif android-file-transfer-linux-4.5/fuse/FuseEntry.h000066400000000000000000000046361501120463400217420ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_FUSE_FUSEENTRY_H #define AFTL_FUSE_FUSEENTRY_H #include #include #include #include namespace mtp { namespace fuse { struct FuseEntry : fuse_entry_param { static constexpr const double Timeout = 60.0; static constexpr unsigned FileMode = S_IFREG | 0644; static constexpr unsigned DirectoryMode = S_IFDIR | 0755; fuse_req_t Request; FuseEntry(fuse_req_t req): fuse_entry_param(), Request(req) { generation = 1; attr_timeout = entry_timeout = Timeout; }; void SetId(FuseId id) { ino = id.Inode; attr.st_ino = id.Inode; } void SetFileMode() { attr.st_mode = FileMode; } void SetDirectoryMode() { attr.st_mode = DirectoryMode; } static mode_t GetMode(mtp::ObjectFormat format) { switch(format) { case mtp::ObjectFormat::Association: return DirectoryMode; default: return FileMode; } } void Reply() { if (attr.st_mode == 0) throw std::runtime_error("uninitialized attr in FuseEntry::Reply"); FUSE_CALL(fuse_reply_entry(Request, this)); } void ReplyCreate(fuse_file_info *fi) { if (attr.st_mode == 0) throw std::runtime_error("uninitialized attr in FuseEntry::Reply"); FUSE_CALL(fuse_reply_create(Request, this, fi)); } void ReplyAttr() { if (attr.st_mode == 0) throw std::runtime_error("uninitialized attr in FuseEntry::ReplyAttr"); FUSE_CALL(fuse_reply_attr(Request, &attr, Timeout)); } void ReplyError(int err) { FUSE_CALL(fuse_reply_err(Request, err)); } }; }} #endif android-file-transfer-linux-4.5/fuse/FuseId.h000066400000000000000000000025601501120463400211670ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_FUSE_FUSEID_H #define AFTL_FUSE_FUSEID_H #include namespace mtp { namespace fuse { struct FuseId { static const FuseId Root; fuse_ino_t Inode; //generation here? explicit FuseId(fuse_ino_t inode): Inode(inode) { } bool operator == (const FuseId &o) const { return Inode == o.Inode; } bool operator != (const FuseId &o) const { return !((*this) == o); } bool operator < (const FuseId &o) const { return Inode < o.Inode; } }; const FuseId FuseId::Root(FUSE_ROOT_ID); }} #endif android-file-transfer-linux-4.5/fuse/fuse.cpp000066400000000000000000000751261501120463400213150ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { using namespace mtp::fuse; class FuseWrapper { std::mutex _mutex; std::string _deviceFilter; bool _claimInterface; bool _resetDevice; mtp::DevicePtr _device; mtp::SessionPtr _session; bool _editObjectSupported; bool _getObjectPropertyListSupported; bool _getPartialObjectSupported; time_t _connectTime; size_t _partialObjectCacheSize; static constexpr size_t DefaultPartialObjectCacheSize = 3; //just 3 object entries. using ChildrenObjects = std::map; using Files = std::map; Files _files; typedef std::map ObjectAttrs; ObjectAttrs _objectAttrs; typedef mtp::Session::ObjectEditSessionPtr ObjectEditSessionPtr; typedef std::map OpenedFiles; OpenedFiles _openedFiles; typedef std::map DirectoryCache; DirectoryCache _directoryCache; static const size_t MtpStorageShift = FUSE_ROOT_ID + 1; static const size_t MtpObjectShift = 999998 + MtpStorageShift; std::vector _storageIdList; std::map _storageToName; std::map _storageFromName; struct ObjectCacheEntry { mtp::ObjectId Id; mtp::ByteArray Data; ObjectCacheEntry() { } ObjectCacheEntry(mtp::ObjectId id, mtp::ByteArray && data): Id(id), Data(std::move(data)) { } }; using ObjectCache = std::list; ObjectCache _objectCache; private: static FuseId ToFuse(mtp::ObjectId id) { return FuseId(id.Id + MtpObjectShift); } static mtp::ObjectId FromFuse(FuseId id) { return mtp::ObjectId(id.Inode - MtpObjectShift); } static bool IsStorage(FuseId id) { return id.Inode >= MtpStorageShift && id.Inode <= MtpObjectShift; } static void SetSize(struct stat & st, size_t size) { st.st_size = size; st.st_blocks = (size + 511) / 512; } mtp::StorageId FuseIdToStorageId(FuseId id) const { if (!IsStorage(id)) throw std::runtime_error("converting non-storage inode to storage id"); size_t i = id.Inode - MtpStorageShift; if (i >= _storageIdList.size()) throw std::runtime_error("invalid storage id"); return _storageIdList[i]; } FuseId FuseIdFromStorageId(mtp::StorageId storageId) const { auto i = std::find(_storageIdList.begin(), _storageIdList.end(), storageId); if (i == _storageIdList.end()) throw std::runtime_error("invalid storage id"); return FuseId(MtpStorageShift + std::distance(_storageIdList.begin(), i)); } void GetObjectInfo(ChildrenObjects &cache, mtp::ObjectId id) { auto oi = _session->GetObjectInfo(id); FuseId inode = ToFuse(id); cache.emplace(oi.Filename, inode); struct stat &attr = _objectAttrs[id]; attr.st_uid = getuid(); attr.st_gid = getgid(); attr.st_ino = inode.Inode; attr.st_mode = FuseEntry::GetMode(oi.ObjectFormat); attr.st_atime = attr.st_mtime = mtp::ConvertDateTime(oi.ModificationDate); attr.st_ctime = mtp::ConvertDateTime(oi.CaptureDate); SetSize(attr, oi.ObjectCompressedSize != mtp::MaxObjectSize? oi.ObjectCompressedSize: _session->GetObjectIntegerProperty(id, mtp::ObjectProperty::ObjectSize)); attr.st_nlink = 1; } struct stat GetObjectAttr(FuseId inode) { if (inode == FuseId::Root) { struct stat attr = { }; attr.st_uid = getuid(); attr.st_gid = getgid(); attr.st_ino = inode.Inode; attr.st_mtime = attr.st_ctime = attr.st_atime = _connectTime; attr.st_mode = FuseEntry::DirectoryMode; attr.st_nlink = 1; return attr; } if (IsStorage(inode)) { struct stat attr = { }; attr.st_uid = getuid(); attr.st_gid = getgid(); attr.st_ino = inode.Inode; attr.st_mtime = attr.st_ctime = attr.st_atime = _connectTime; attr.st_mode = FuseEntry::DirectoryMode; attr.st_nlink = 1; return attr; } mtp::ObjectId id = FromFuse(inode); auto i = _objectAttrs.find(id); if (i != _objectAttrs.end()) return i->second; //populate cache for parent auto parent = GetParentObject(inode); GetChildren(parent); //populate cache i = _objectAttrs.find(id); if (i != _objectAttrs.end()) return i->second; else throw std::runtime_error("no such object"); } template void GetObjectPropertyList(mtp::ObjectId parent, const std::set &originalObjectList, const mtp::ObjectProperty property, const std::function &callback) { std::set objectList(originalObjectList); mtp::ByteArray data = _session->GetObjectPropertyList(parent, mtp::ObjectFormat::Any, property, 0, 1); mtp::ObjectPropertyListParser parser; parser.Parse(data, [&objectList, &callback, property](mtp::ObjectId objectId, mtp::ObjectProperty p, const PropertyValueType & value) { auto it = objectList.find(objectId); if (property == p && it != objectList.end()) { objectList.erase(it); try { callback(objectId, value); } catch(const std::exception &ex) { mtp::error("callback for property list 0x", mtp::hex(property, 4), " failed: ", ex.what()); } } else mtp::debug("extra property 0x", hex(p, 4), " returned for object ", objectId.Id, ", while querying property list 0x", mtp::hex(property, 4)); }); if (!objectList.empty()) { mtp::error("inconsistent GetObjectPropertyList for property 0x", mtp::hex(property, 4)); for(auto objectId : objectList) { mtp::debug("querying property 0x", mtp::hex(property, 4), " for object ", objectId); try { mtp::ByteArray data = _session->GetObjectProperty(objectId, property); mtp::InputStream stream(data); PropertyValueType value = PropertyValueType(); stream >> value; callback(objectId, value); } catch(const std::exception &ex) { mtp::error("fallback query/callback for property 0x", mtp::hex(property, 4), " failed: ", ex.what()); } } } } ChildrenObjects & GetChildren(FuseId inode) { if (inode == FuseId::Root) { PopulateStorages(); ChildrenObjects & cache = _files[inode]; cache.clear(); for(size_t i = 0; i < _storageIdList.size(); ++i) { mtp::StorageId storageId = _storageIdList[i]; auto name = _storageToName.find(storageId); if (name != _storageToName.end()) cache.emplace(name->second, FuseId(MtpStorageShift + i)); else mtp::error("no storage name for ", storageId); } return cache; } { auto i = _files.find(inode); if (i != _files.end()) return i->second; } ChildrenObjects & cache = _files[inode]; using namespace mtp; if (inode == FuseId::Root) { return cache; } msg::ObjectHandles oh; if (IsStorage(inode)) { mtp::StorageId storageId = FuseIdToStorageId(inode); oh = _session->GetObjectHandles(storageId, mtp::ObjectFormat::Any, mtp::Session::Root); } else { mtp::ObjectId parent = FromFuse(inode); oh = _session->GetObjectHandles(mtp::Session::AllStorages, mtp::ObjectFormat::Any, parent); if (_getObjectPropertyListSupported) { std::set objects; for(auto id : oh.ObjectHandles) objects.insert(id); //populate filenames GetObjectPropertyList(parent, objects, mtp::ObjectProperty::ObjectFilename, [&cache](ObjectId objectId, const std::string &name) { cache.emplace(name, ToFuse(objectId)); }); //format GetObjectPropertyList(parent, objects, mtp::ObjectProperty::ObjectFormat, [this](ObjectId objectId, mtp::ObjectFormat format) { struct stat & attr = _objectAttrs[objectId]; attr.st_ino = ToFuse(objectId).Inode; attr.st_mode = FuseEntry::GetMode(format); attr.st_nlink = 1; }); //size GetObjectPropertyList(parent, objects, mtp::ObjectProperty::ObjectSize, [this](ObjectId objectId, mtp::u64 size) { SetSize(_objectAttrs[objectId], size); }); //mtime try { GetObjectPropertyList(parent, objects, mtp::ObjectProperty::DateModified, [this](ObjectId objectId, const std::string & mtime) { _objectAttrs[objectId].st_mtime = mtp::ConvertDateTime(mtime); }); } catch(const std::exception &ex) { } //ctime try { GetObjectPropertyList(parent, objects, mtp::ObjectProperty::DateAdded, [this](ObjectId objectId, const std::string & ctime) { _objectAttrs[objectId].st_ctime = mtp::ConvertDateTime(ctime); }); } catch(const std::exception &ex) { } return cache; } } for(auto id : oh.ObjectHandles) { try { GetObjectInfo(cache, id); } catch(const std::exception &ex) { } } return cache; } FuseId CreateObject(FuseId parentInode, const std::string &filename, mtp::ObjectFormat format) { mtp::ObjectId parentId = FromFuse(parentInode); mtp::StorageId storageId; if (IsStorage(parentInode)) { storageId = FuseIdToStorageId(parentInode); parentId = mtp::Session::Root; } else storageId = _session->GetObjectStorage(parentId); mtp::debug(" creating object in storage ", mtp::hex(storageId.Id), ", parent: ", mtp::hex(parentId.Id, 8)); mtp::msg::NewObjectInfo noi; if (format != mtp::ObjectFormat::Association) { mtp::msg::ObjectInfo oi; oi.Filename = filename; oi.ObjectFormat = format; noi = _session->SendObjectInfo(oi, storageId, parentId); _session->SendObject(std::make_shared(mtp::ByteArray())); } else noi = _session->CreateDirectory(filename, parentId, storageId); mtp::debug(" new object id ", noi.ObjectId.Id); { //update cache: auto i = _files.find(parentInode); if (i != _files.end()) GetObjectInfo(i->second, noi.ObjectId); //insert object info into cache _directoryCache.erase(parentInode); } return ToFuse(noi.ObjectId); } void CreateObject(mtp::ObjectFormat format, fuse_req_t req, FuseId parentId, const char *name, mode_t mode, fuse_file_info *createInfo = NULL) { if (parentId == FuseId::Root) { FUSE_CALL(fuse_reply_err(req, EPERM)); //cannot create files in the same level with storages return; } auto objectId = CreateObject(parentId, name, format); FuseEntry entry(req); entry.SetId(objectId); entry.attr = GetObjectAttr(objectId); if (createInfo) entry.ReplyCreate(createInfo); else entry.Reply(); } FuseId GetParentObject(FuseId inode) { if (inode == FuseId::Root) return inode; if (IsStorage(inode)) return FuseId::Root; mtp::ObjectId id = FromFuse(inode); mtp::ObjectId parent = _session->GetObjectParent(id); if (parent == mtp::Session::Device || parent == mtp::Session::Root) //parent == root -> storage { return FuseIdFromStorageId(_session->GetObjectStorage(id)); } else return ToFuse(parent); } bool FillEntry(FuseEntry &entry, FuseId id) { try { entry.attr = GetObjectAttr(id); } catch(const std::exception &ex) { return false; } entry.SetId(id); return true; } public: FuseWrapper(const std::string & deviceFilter, bool claimInterface, bool resetDevice): _deviceFilter(deviceFilter), _claimInterface(claimInterface), _resetDevice(resetDevice), _partialObjectCacheSize(DefaultPartialObjectCacheSize) { Connect(); } void Connect() { mtp::scoped_mutex_lock l(_mutex); _openedFiles.clear(); _files.clear(); _objectAttrs.clear(); _directoryCache.clear(); _session.reset(); _device.reset(); _device = mtp::Device::FindFirst(_deviceFilter, _claimInterface, _resetDevice); if (!_device) throw std::runtime_error("no MTP device found"); _session = _device->OpenSession(1); _editObjectSupported = _session->EditObjectSupported(); if (!_editObjectSupported) mtp::error("your device does not have android EditObject extension, you will not be able to write into individual files"); _getObjectPropertyListSupported = _session->GetObjectPropertyListSupported(); if (!_getObjectPropertyListSupported) mtp::error("your device does not have GetObjectPropertyList extension, expect slow enumeration of big directories"); _getPartialObjectSupported = _session->GetDeviceInfo().Supports(mtp::OperationCode::GetPartialObject); if (!_getPartialObjectSupported) mtp::error("your device does not have GetPartialObject extension (beware, this will download the latest N objects you accessed and keep them in a small in-memory cache)"); _connectTime = time(NULL); PopulateStorages(); } void PopulateStorages() { _storageIdList.clear(); _storageFromName.clear(); _storageToName.clear(); mtp::msg::StorageIDs ids = _session->GetStorageIDs(); _storageIdList.reserve(ids.StorageIDs.size()); for(size_t i = 0; i < ids.StorageIDs.size(); ++i) { mtp::StorageId id = ids.StorageIDs[i]; mtp::msg::StorageInfo si = _session->GetStorageInfo(id); std::string path = si.GetName(); if (path.empty()) { char buf[64]; std::snprintf(buf, sizeof(buf), "sdcard%u", (unsigned)i); path = buf; } FuseId inode(MtpStorageShift + i); _storageIdList.push_back(id); _storageFromName[path] = id; _storageToName[id] = path; } } std::string GetMountpoint() { return _session->GetDeviceInfo().GetFilesystemFriendlyName(); } void Init(void *, fuse_conn_info *conn) { mtp::scoped_mutex_lock l(_mutex); } void Lookup (fuse_req_t req, FuseId parent, const char *name) { mtp::scoped_mutex_lock l(_mutex); FuseEntry entry(req); const ChildrenObjects & children = GetChildren(parent); auto it = children.find(name); if (it != children.end()) { if (FillEntry(entry, it->second)) { entry.Reply(); return; } } entry.ReplyError(ENOENT); } void ReadDir(fuse_req_t req, FuseId ino, size_t size, off_t off, struct fuse_file_info *fi) { mtp::scoped_mutex_lock l(_mutex); if (!(GetObjectAttr(ino).st_mode & S_IFDIR)) { FUSE_CALL(fuse_reply_err(req, ENOTDIR)); return; } FuseDirectory dir(req); auto it = _directoryCache.find(ino); if (it == _directoryCache.end()) { const ChildrenObjects & cache = GetChildren(ino); it = _directoryCache.insert(std::make_pair(ino, CharArray())).first; CharArray &data = it->second; dir.Add(data, ".", GetObjectAttr(FuseId::Root)); dir.Add(data, "..", GetObjectAttr(GetParentObject(ino))); for(auto entry : cache) { dir.Add(data, entry.first, GetObjectAttr(entry.second)); } } dir.Reply(req, it->second, off, size); } void GetAttr(fuse_req_t req, FuseId ino, struct fuse_file_info *fi) { mtp::scoped_mutex_lock l(_mutex); FuseEntry entry(req); if (FillEntry(entry, ino)) entry.ReplyAttr(); else entry.ReplyError(ENOENT); } mtp::Session::ObjectEditSessionPtr GetTransaction(FuseId inode) { mtp::Session::ObjectEditSessionPtr tr; { auto it = _openedFiles.find(inode); if (it != _openedFiles.end()) tr = it->second; else { tr = mtp::Session::EditObject(_session, FromFuse(inode)); _openedFiles[inode] = tr; } } return NOT_NULL(tr); } void Read(fuse_req_t req, FuseId ino, size_t size, off_t begin, struct fuse_file_info *fi) { mtp::scoped_mutex_lock l(_mutex); ReleaseTransaction(ino); struct stat attr = GetObjectAttr(ino); off_t rsize = std::min(attr.st_size - begin, size); mtp::debug("reading ", rsize, " bytes"); mtp::ByteArray data; auto objectId = FromFuse(ino); if (rsize > 0) { if (!_getPartialObjectSupported) { std::size_t size = 0; ObjectCache::iterator it; for(it = _objectCache.begin(); it != _objectCache.end(); ++it, ++size) { auto & entry = *it; if (entry.Id == objectId) { mtp::debug("in-memory cache hit"); auto src = entry.Data.data() + begin; std::copy(src, src + rsize, std::back_inserter(data)); if (it != _objectCache.begin()) std::swap(*_objectCache.begin(), *it); break; } } if (it == _objectCache.end()) { mtp::debug("in-memory cache miss"); auto stream = std::make_shared(); _session->GetObject(objectId, stream); auto src = stream->GetData().data() + begin; std::copy(src, src + rsize, std::back_inserter(data)); _objectCache.emplace_front(objectId, std::move(stream->GetData())); ++size; } while (size > _partialObjectCacheSize) { mtp::debug("purging last entry from cache..."); _objectCache.pop_back(); --size; } } else data = _session->GetPartialObject(objectId, begin, rsize); } mtp::debug("read ", data.size(), " bytes of data"); FUSE_CALL(fuse_reply_buf(req, static_cast(static_cast(data.data())), data.size())); } void Write(fuse_req_t req, FuseId inode, const char *buf, size_t size, off_t off, struct fuse_file_info *fi) { mtp::scoped_mutex_lock l(_mutex); struct stat attr = GetObjectAttr(inode); mtp::ObjectId objectId = FromFuse(inode); ObjectEditSessionPtr tr = GetTransaction(inode); off_t newSize = off + size; if (newSize > attr.st_size) { mtp::debug("truncating file to ", newSize); tr->Truncate(newSize); SetSize(_objectAttrs[objectId], newSize); } tr->Send(off, mtp::ByteArray(buf, buf + size)); FUSE_CALL(fuse_reply_write(req, size)); } void Create(fuse_req_t req, FuseId parent, const char *name, mode_t mode, struct fuse_file_info *fi) { mtp::scoped_mutex_lock l(_mutex); CreateObject(mtp::ObjectFormat::Undefined, req, parent, name, mode, fi); } void MakeNode(fuse_req_t req, FuseId parent, const char *name, mode_t mode, dev_t rdev) { mtp::scoped_mutex_lock l(_mutex); CreateObject(mtp::ObjectFormat::Undefined, req, parent, name, mode); } void MakeDir(fuse_req_t req, FuseId parent, const char *name, mode_t mode) { mtp::scoped_mutex_lock l(_mutex); CreateObject(mtp::ObjectFormat::Association, req, parent, name, mode); } void Open(fuse_req_t req, FuseId ino, struct fuse_file_info *fi) { mtp::scoped_mutex_lock l(_mutex); mtp::ObjectFormat format; try { format = static_cast(_session->GetObjectIntegerProperty(FromFuse(ino), mtp::ObjectProperty::ObjectFormat)); } catch(const std::exception &ex) { FUSE_CALL(fuse_reply_err(req, ENOENT)); return; } if (format == mtp::ObjectFormat::Association) { FUSE_CALL(fuse_reply_err(req, EISDIR)); return; } FUSE_CALL(fuse_reply_open(req, fi)); } void ReleaseTransaction(FuseId ino) { auto i = _openedFiles.find(ino); if (i != _openedFiles.end()) _openedFiles.erase(i); } void Release(fuse_req_t req, FuseId ino, struct fuse_file_info *fi) { mtp::scoped_mutex_lock l(_mutex); ReleaseTransaction(ino); FUSE_CALL(fuse_reply_err(req, 0)); } void SetAttr(fuse_req_t req, FuseId inode, struct stat *attr, int to_set, struct fuse_file_info *fi) { mtp::scoped_mutex_lock l(_mutex); FuseEntry entry(req); if (FillEntry(entry, inode)) { if (to_set & FUSE_SET_ATTR_SIZE) { off_t newSize = attr->st_size; ObjectEditSessionPtr tr = GetTransaction(inode); tr->Truncate(newSize); SetSize(entry.attr, newSize); SetSize(_objectAttrs[FromFuse(inode)], newSize); } entry.ReplyAttr(); } else entry.ReplyError(ENOENT); } void UnlinkImpl(FuseId inode) { mtp::debug(" unlinking inode ", inode.Inode); mtp::ObjectId id = FromFuse(inode); _session->DeleteObject(id); _openedFiles.erase(inode); _objectAttrs.erase(id); } void Unlink(fuse_req_t req, FuseId parent, const char *name) { mtp::scoped_mutex_lock l(_mutex); ChildrenObjects &children = GetChildren(parent); auto i = children.find(name); if (i == children.end()) { FUSE_CALL(fuse_reply_err(req, ENOENT)); return; } FuseId inode = i->second; if (GetObjectAttr(inode).st_mode & S_IFDIR) { auto &dirChildren = GetChildren(inode); if (!dirChildren.empty()) { FUSE_CALL(fuse_reply_err(req, ENOTEMPTY)); return; } } UnlinkImpl(inode); _directoryCache.erase(parent); children.erase(i); FUSE_CALL(fuse_reply_err(req, 0)); } void RemoveDir (fuse_req_t req, FuseId parent, const char *name) { Unlink(req, parent, name); } void Rename(fuse_req_t req, FuseId parent, const char *name, FuseId newparent, const char *newName, unsigned int flags) { if (parent != newparent) { //no renames across directory boundary, sorry //return cross-device link, so user space should re-create file and copy it FUSE_CALL(fuse_reply_err(req, EXDEV)); return; } mtp::scoped_mutex_lock l(_mutex); ChildrenObjects &children = GetChildren(parent); auto i = children.find(name); if (i == children.end()) { FUSE_CALL(fuse_reply_err(req, ENOENT)); return; } FuseId inode = i->second; mtp::debug(" renaming inode ", inode.Inode, " to ", newName); auto old = children.find(newName); if (old != children.end()) { mtp::debug(" unlinking target inode ", old->second.Inode); UnlinkImpl(old->second); children.erase(old); } mtp::ObjectId id = FromFuse(inode); _session->SetObjectProperty(id, mtp::ObjectProperty::ObjectFilename, std::string(newName)); children.erase(i); children.emplace(newName, inode); _directoryCache.erase(parent); FUSE_CALL(fuse_reply_err(req, 0)); } void StatFS(fuse_req_t req, FuseId ino) { mtp::scoped_mutex_lock l(_mutex); struct statvfs stat = { }; stat.f_namemax = 254; mtp::u64 freeSpace = 0, capacity = 0; if (ino == FuseId::Root) { for(auto storageId : _storageIdList) { mtp::msg::StorageInfo si = _session->GetStorageInfo(storageId); freeSpace += si.FreeSpaceInBytes; capacity += si.MaxCapacity; } } else { mtp::StorageId storageId; if (IsStorage(ino)) storageId = FuseIdToStorageId(ino); else storageId = _session->GetObjectStorage(FromFuse(ino)); mtp::msg::StorageInfo si = _session->GetStorageInfo(storageId); freeSpace = si.FreeSpaceInBytes; capacity = si.MaxCapacity; } stat.f_frsize = stat.f_bsize = 1024 * 1024; stat.f_blocks = capacity / stat.f_frsize; stat.f_bfree = stat.f_bavail = freeSpace / stat.f_frsize; FUSE_CALL(fuse_reply_statfs(req, &stat)); } }; std::unique_ptr g_wrapper; #define WRAP_EX(...) do { \ try { return __VA_ARGS__ ; } \ catch (const mtp::usb::DeviceNotFoundException &) \ { \ g_wrapper->Connect(); \ __VA_ARGS__ ; \ } \ catch (const std::exception &ex) \ { mtp::error(#__VA_ARGS__ " failed: ", ex.what()); fuse_reply_err(req, EIO); } \ } while(false) void Init (void *userdata, struct fuse_conn_info *conn) { mtp::debug("Init: fuse proto version: ", conn->proto_major, ".", conn->proto_minor, ", capability: 0x", mtp::hex(conn->capable, 8), ", max readahead: ", conn->max_readahead, ", max write: ", conn->max_write ); //If synchronous reads are chosen, Fuse will wait for reads to complete before issuing any other requests. //mtp is completely synchronous. you cannot have two transaction in parallel, so you have to wait any operation to finish before starting another one conn->want &= ~FUSE_CAP_ASYNC_READ; try { g_wrapper->Init(userdata, conn); } catch (const std::exception &ex) { mtp::error("init failed:", ex.what()); } } void Lookup (fuse_req_t req, fuse_ino_t parent, const char *name) { mtp::debug(" Lookup ", parent, " ", name); WRAP_EX(g_wrapper->Lookup(req, FuseId(parent), name)); } void ReadDir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { mtp::debug(" Readdir ", ino, " ", size, " ", off); WRAP_EX(g_wrapper->ReadDir(req, FuseId(ino), size, off, fi)); } void GetAttr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { mtp::debug(" GetAttr ", ino); WRAP_EX(g_wrapper->GetAttr(req, FuseId(ino), fi)); } void SetAttr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi) { mtp::debug(" SetAttr ", ino, " 0x", mtp::hex(to_set, 8)); WRAP_EX(g_wrapper->SetAttr(req, FuseId(ino), attr, to_set, fi)); } void Read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { mtp::debug(" Read ", ino, " ", size, " ", off); WRAP_EX(g_wrapper->Read(req, FuseId(ino), size, off, fi)); } void Write(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi) { mtp::debug(" Write ", ino, " ", size, " ", off); WRAP_EX(g_wrapper->Write(req, FuseId(ino), buf, size, off, fi)); } void MakeNode(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev) { mtp::debug(" MakeNode ", parent, " ", name, " 0x", mtp::hex(mode, 8)); WRAP_EX(g_wrapper->MakeNode(req, FuseId(parent), name, mode, rdev)); } void Create(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi) { mtp::debug(" Create ", parent, " ", name, " 0x", mtp::hex(mode, 8)); WRAP_EX(g_wrapper->Create(req, FuseId(parent), name, mode, fi)); } void Open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { mtp::debug(" Open ", ino); WRAP_EX(g_wrapper->Open(req, FuseId(ino), fi)); } void Rename(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname, unsigned int flags) { mtp::debug(" Rename ", parent, " ", name, " -> ", newparent, " ", newname); WRAP_EX(g_wrapper->Rename(req, FuseId(parent), name, FuseId(newparent), newname, flags)); } void Release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { mtp::debug(" Release ", ino); WRAP_EX(g_wrapper->Release(req, FuseId(ino), fi)); } void MakeDir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode) { mtp::debug(" MakeDir ", parent, " ", name, " 0x", mtp::hex(mode, 8)); WRAP_EX(g_wrapper->MakeDir(req, FuseId(parent), name, mode)); } void RemoveDir (fuse_req_t req, fuse_ino_t parent, const char *name) { mtp::debug(" RemoveDir ", parent, " ", name); WRAP_EX(g_wrapper->RemoveDir(req, FuseId(parent), name)); } void Unlink(fuse_req_t req, fuse_ino_t parent, const char *name) { mtp::debug(" Unlink ", parent, " ", name); WRAP_EX(g_wrapper->Unlink(req, FuseId(parent), name)); } void StatFS(fuse_req_t req, fuse_ino_t ino) { mtp::debug(" StatFS ", ino); WRAP_EX(g_wrapper->StatFS(req, FuseId(ino))); } } int main(int argc, char **argv) { std::string deviceFilter; bool claimInterface = true; bool resetDevice = false; std::vector args; args.push_back(argv[0]); for(int i = 1; i < argc; ++i) { if (strcmp(argv[i], "-R") == 0) resetDevice = true; else if (strcmp(argv[i], "-C") == 0) claimInterface = false; else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "-odebug") == 0) { args.push_back(argv[i]); mtp::g_debug = true; } else if (strcmp(argv[i], "-o") == 0 && strcmp(argv[i + 1], "debug") == 0) { if (i + 1 == argc) { mtp::error("-o requires an argument"); return 1; } mtp::g_debug = true; args.push_back(argv[i]); } else if (strcmp(argv[i], "-D") == 0) { if (i + 1 == argc) { mtp::error("-D requires an argument"); return 1; } deviceFilter = argv[i + 1]; ++i; } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { mtp::print(); mtp::print("Additional AFT options: "); mtp::print(" -R reset device"); mtp::print(" -C do not claim USB interface"); mtp::print(" -d / -o debug show MTP debug output"); mtp::print(" -D filter by manufacturer/model/serial"); mtp::print(" -v AFT verbose output"); return 0; } else args.push_back(argv[i]); } args.push_back(nullptr); try { g_wrapper.reset(new FuseWrapper(deviceFilter, claimInterface, resetDevice)); } catch(const std::exception &ex) { mtp::error("connect failed: ", ex.what()); return 1; } struct fuse_lowlevel_ops ops = {}; ops.init = &Init; ops.lookup = &Lookup; ops.readdir = &ReadDir; ops.getattr = &GetAttr; ops.setattr = &SetAttr; ops.mknod = &MakeNode; ops.open = &Open; ops.create = &Create; ops.read = &Read; ops.write = &Write; ops.mkdir = &MakeDir; ops.rename = &Rename; ops.release = &Release; ops.rmdir = &RemoveDir; ops.unlink = &Unlink; ops.statfs = &StatFS; struct fuse_args fuse_args = FUSE_ARGS_INIT(static_cast(args.size() - 1), args.data()); struct fuse_cmdline_opts opts = {}; int err = -1; if (fuse_parse_cmdline(&fuse_args, &opts) == 0) { if (opts.show_version) { fuse_lowlevel_version(); exit(0); } if (opts.show_help) { fuse_cmdline_help(); fuse_lowlevel_help(); exit(0); } if (opts.mountpoint != NULL) { struct fuse_session *se = fuse_session_new(&fuse_args, &ops, sizeof(ops), NULL); if (se != NULL) { if (fuse_set_signal_handlers(se) == 0) { if (fuse_session_mount(se, opts.mountpoint) == 0) { if (fuse_daemonize(opts.foreground) == -1) perror("fuse_daemonize"); if (opts.singlethread) err = fuse_session_loop(se); else err = fuse_session_loop_mt(se, nullptr); fuse_session_unmount(se); } fuse_remove_signal_handlers(se); } fuse_session_destroy(se); } } } else { mtp::error("fuse_parse_cmdline failed"); } free(opts.mountpoint); fuse_opt_free_args(&fuse_args); return err ? 1 : 0; } android-file-transfer-linux-4.5/mtp/000077500000000000000000000000001501120463400174725ustar00rootroot00000000000000android-file-transfer-linux-4.5/mtp/ByteArray.cpp000066400000000000000000000016541501120463400221060ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include namespace mtp { } android-file-transfer-linux-4.5/mtp/ByteArray.h000066400000000000000000000021371501120463400215500ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BYTEARRAY_H #define AFTL_MTP_BYTEARRAY_H #include #include #include namespace mtp { using ByteArray = std::vector; //! \typedef ByteArray : vector of bytes DECLARE_PTR(ByteArray); } #endif /* BYTEARRAY_H */ android-file-transfer-linux-4.5/mtp/Demangle.h000066400000000000000000000022431501120463400213600ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_DEMANGLE_H #define AFTL_MTP_DEMANGLE_H #include #include namespace mtp { //! demangles c++ symbol inline std::string Demangle(const char *abiName) { int status; char *ret = abi::__cxa_demangle(abiName, 0, 0, &status); std::string name(ret); free(ret); return name; } } #endif android-file-transfer-linux-4.5/mtp/Token.h000066400000000000000000000021631501120463400207250ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_TOKEN_H #define AFTL_MTP_TOKEN_H #include namespace mtp { //! Token, base class for ref counted objects which hold some resources while they are alive struct IToken : Noncopyable { virtual ~IToken() = default; }; DECLARE_PTR(IToken); } #endif android-file-transfer-linux-4.5/mtp/backend/000077500000000000000000000000001501120463400210615ustar00rootroot00000000000000android-file-transfer-linux-4.5/mtp/backend/darwin/000077500000000000000000000000001501120463400223455ustar00rootroot00000000000000android-file-transfer-linux-4.5/mtp/backend/darwin/usb/000077500000000000000000000000001501120463400231365ustar00rootroot00000000000000android-file-transfer-linux-4.5/mtp/backend/darwin/usb/Context.cpp000066400000000000000000000056051501120463400252740ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include namespace { int usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 location) { CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName); if (!matchingDict) return kIOReturnError; if (location) { CFMutableDictionaryRef propertyMatchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (propertyMatchDict) { /* there are no unsigned CFNumber types so treat the value as signed. the os seems to do this internally (CFNumberType of locationID is 3) */ CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberSInt32Type, &location); CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBDevicePropertyLocationID), locationCF); /* release our reference to the CFNumber (CFDictionarySetValue retains it) */ CFRelease (locationCF); CFDictionarySetValue (matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict); /* release out reference to the CFMutableDictionaryRef (CFDictionarySetValue retains it) */ CFRelease (propertyMatchDict); } /* else we can still proceed as long as the caller accounts for the possibility of other devices in the iterator */ } return IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, deviceIterator); } } namespace mtp { namespace usb { Context::Context() { io_iterator_t deviceIterator; USB_CALL(usb_setup_device_iterator (&deviceIterator, 0)); io_service_t service; while ((service = IOIteratorNext (deviceIterator))) { try { _devices.push_back(std::make_shared(service)); } catch(const std::exception &ex) { error(ex.what()); } IOObjectRelease(service); } IOObjectRelease(deviceIterator); } Context::~Context() { } void Context::Wait() { } }} android-file-transfer-linux-4.5/mtp/backend/darwin/usb/Context.h000066400000000000000000000024721501120463400247400ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_DARWIN_USB_CONTEXT_H #define AFTL_MTP_BACKEND_DARWIN_USB_CONTEXT_H #include #include #include namespace mtp { namespace usb { class Context : Noncopyable { public: typedef std::vector Devices; private: Devices _devices; public: Context(); ~Context(); void Wait(); const Devices & GetDevices() const { return _devices; } }; DECLARE_PTR(Context); }} #endif android-file-transfer-linux-4.5/mtp/backend/darwin/usb/Device.cpp000066400000000000000000000064031501120463400250440ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include namespace mtp { namespace usb { Device::Device(ContextPtr context, IOUSBDeviceType ** dev): _context(context), _dev(dev) { } Device::~Device() { (*_dev)->USBDeviceClose(_dev); } void Device::Reset() { } int Device::GetConfiguration() const { return 0; } void Device::SetConfiguration(int idx) { USB_CALL((*_dev)->SetConfiguration(_dev, idx)); } void Device::ClearHalt(const EndpointPtr & ep) { IOUSBInterfaceInterface ** interface = ep->GetInterfaceHandle(); USB_CALL((*interface)->ClearPipeStall(interface, ep->GetRefIndex())); } void Device::WriteBulk(const EndpointPtr & ep, const IObjectInputStreamPtr &inputStream, int timeout) { IOUSBInterfaceInterface ** interface = ep->GetInterfaceHandle(); size_t transferSize = ep->GetMaxPacketSize(); ByteArray buffer(transferSize); size_t r; do { r = inputStream->Read(buffer.data(), buffer.size()); USB_CALL((*interface)->WritePipe(interface, ep->GetRefIndex(), buffer.data(), r)); } while(r == transferSize); } void Device::ReadBulk(const EndpointPtr & ep, const IObjectOutputStreamPtr &outputStream, int timeout) { IOUSBInterfaceInterface ** interface = ep->GetInterfaceHandle(); size_t transferSize = ep->GetMaxPacketSize(); ByteArray buffer(transferSize); size_t r; do { UInt32 readBytes = buffer.size(); USB_CALL((*interface)->ReadPipe(interface, ep->GetRefIndex(), buffer.data(), &readBytes)); r = outputStream->Write(buffer.data(), readBytes); } while(r == transferSize); } void Device::ReadControl(IOUSBDeviceType **dev, u8 type, u8 req, u16 value, u16 index, ByteArray &data, int timeout) { IOUSBDevRequest request = {}; request.bmRequestType = type; request.bRequest = req; request.wValue = value; request.wIndex = index; request.pData = data.data(); request.wLength = data.size(); USB_CALL((*dev)->DeviceRequest(dev, &request)); data.resize(request.wLenDone); } void Device::ReadControl(u8 type, u8 req, u16 value, u16 index, ByteArray &data, int timeout) { ReadControl(_dev, type, req, value, index, data, timeout); } void Device::WriteControl(u8 type, u8 req, u16 value, u16 index, const ByteArray &data, int timeout) { error("WriteControl stub"); } InterfaceTokenPtr Device::ClaimInterface(const InterfacePtr &interface) { return interface->Claim(); } }} android-file-transfer-linux-4.5/mtp/backend/darwin/usb/Device.h000066400000000000000000000044411501120463400245110ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_DARWIN_USB_DEVICE_H #define AFTL_MTP_BACKEND_DARWIN_USB_DEVICE_H #include #include #include #include #include #include #include "Interface.h" namespace mtp { namespace usb { class Context; DECLARE_PTR(Context); class Endpoint; DECLARE_PTR(Endpoint); class Interface; DECLARE_PTR(Interface); class InterfaceToken; DECLARE_PTR(InterfaceToken); class Device : Noncopyable { private: ContextPtr _context; IOUSBDeviceType ** _dev; public: Device(ContextPtr ctx, IOUSBDeviceType **dev); //must be opened ~Device(); InterfaceTokenPtr ClaimInterface(const InterfacePtr &interface); int GetConfiguration() const; void SetConfiguration(int idx); void Reset(); void WriteBulk(const EndpointPtr & ep, const IObjectInputStreamPtr &inputStream, int timeout); void ReadBulk(const EndpointPtr & ep, const IObjectOutputStreamPtr &outputStream, int timeout); void ClearHalt(const EndpointPtr & ep); static void ReadControl(IOUSBDeviceType **dev, u8 type, u8 req, u16 value, u16 index, ByteArray &data, int timeout); //result buffer must be allocated void ReadControl(u8 type, u8 req, u16 value, u16 index, ByteArray &data, int timeout); //result buffer must be allocated void WriteControl(u8 type, u8 req, u16 value, u16 index, const ByteArray &data, int timeout); }; DECLARE_PTR(Device); }} #endif /* DEVICE_H */ android-file-transfer-linux-4.5/mtp/backend/darwin/usb/DeviceDescriptor.cpp000066400000000000000000000107131501120463400271020ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include //https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/USBBook/USBDeviceInterfaces/USBDevInterfaces.html namespace mtp { namespace usb { Configuration::Configuration(IOUSBDeviceType ** dev, IOUSBConfigurationDescriptorPtr conf): _conf(conf) { IOUSBFindInterfaceRequest request = { }; request.bInterfaceClass = kIOUSBFindInterfaceDontCare; request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; request.bAlternateSetting = kIOUSBFindInterfaceDontCare; io_iterator_t iterator; USB_CALL((*dev)->CreateInterfaceIterator(dev, &request, &iterator)); io_service_t interface; while ((interface = IOIteratorNext(iterator))) { IOCFPlugInInterface **plugInInterface = NULL; SInt32 score; USB_CALL(IOCreatePlugInInterfaceForService(interface, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score)); IOObjectRelease(interface); IOUSBInterfaceInterface **usbInterface = NULL; USB_CALL((*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID *) &usbInterface)); (*plugInInterface)->Release(plugInInterface); if (usbInterface) _interfaces.push_back(usbInterface); } } DeviceDescriptor::DeviceDescriptor(io_service_t desc): _dev() { IOCFPlugInInterface **plugInInterface = NULL; SInt32 score; USB_CALL( IOCreatePlugInInterfaceForService(desc, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score) ); //wrap desc, so it cannot leak here USB_CALL(IOObjectRelease(desc)); (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(DeviceInterfaceID), (LPVOID *)&_dev); (*plugInInterface)->Release(plugInInterface); if (!_dev) throw std::runtime_error("cannot create device"); bool wakeup = true; if (GetVendorId() == kIOUSBVendorIDAppleComputer) wakeup = false; #if defined (kIOUSBInterfaceInterfaceID500) if (wakeup) { UInt32 info = 0; int r = (*_dev)->GetUSBDeviceInformation (_dev, &info); if (r == kIOReturnSuccess && (info & (1 << kUSBInformationDeviceIsSuspendedBit)) == 0) wakeup = false; } #endif if (wakeup) { (*_dev)->USBDeviceSuspend (_dev, 0); } } u16 DeviceDescriptor::GetVendorId() const { UInt16 vendor; USB_CALL((*_dev)->GetDeviceVendor(_dev, &vendor)); return vendor; } u16 DeviceDescriptor::GetProductId() const { UInt16 product; USB_CALL((*_dev)->GetDeviceProduct(_dev, &product)); return product; } int DeviceDescriptor::GetConfigurationsCount() const { UInt8 numConfig; USB_CALL((*_dev)->GetNumberOfConfigurations(_dev, &numConfig)); return numConfig; } ConfigurationPtr DeviceDescriptor::GetConfiguration(int conf) { IOUSBConfigurationDescriptorPtr configDesc; USB_CALL((*_dev)->GetConfigurationDescriptorPtr(_dev, conf, &configDesc)); return std::make_shared(_dev, configDesc); } DevicePtr DeviceDescriptor::Open(ContextPtr context) { USB_CALL((*_dev)->USBDeviceOpen(_dev)); return std::make_shared(context, _dev); } DevicePtr DeviceDescriptor::TryOpen(ContextPtr context) { int r = (*_dev)->USBDeviceOpen(_dev); if (r == kIOReturnSuccess) return std::make_shared(context, _dev); debug("USBDeviceOpen failed: ", Exception::GetErrorMessage(r)); return nullptr; } DeviceDescriptor::~DeviceDescriptor() { } ByteArray DeviceDescriptor::GetDescriptor() { ByteArray data(255); Device::ReadControl(_dev, 0x80, 6, 0x200, 0, data, 1000); return data; } }} android-file-transfer-linux-4.5/mtp/backend/darwin/usb/DeviceDescriptor.h000066400000000000000000000042141501120463400265460ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_DARWIN_USB_DEVICEDESCRIPTOR_H #define AFTL_MTP_BACKEND_DARWIN_USB_DEVICEDESCRIPTOR_H #include #include #include #include #include namespace mtp { namespace usb { class Configuration : Noncopyable { //IOUSBDeviceType **_dev; IOUSBConfigurationDescriptorPtr _conf; std::vector _interfaces; public: Configuration(IOUSBDeviceType ** dev, IOUSBConfigurationDescriptorPtr conf); int GetIndex() const { return _conf->bConfigurationValue; } int GetInterfaceCount() const { return _interfaces.size(); } int GetInterfaceAltSettingsCount(int idx) const { return 1; } InterfacePtr GetInterface(DevicePtr device, ConfigurationPtr config, int idx, int settings) const { return std::make_shared(device, config, _interfaces.at(idx)); } }; class DeviceDescriptor { private: IOUSBDeviceType **_dev; public: DeviceDescriptor(io_service_t desc); ~DeviceDescriptor(); u16 GetVendorId() const; u16 GetProductId() const; DevicePtr Open(ContextPtr context); DevicePtr TryOpen(ContextPtr context); int GetConfigurationsCount() const; ConfigurationPtr GetConfiguration(int conf); ByteArray GetDescriptor(); }; DECLARE_PTR(DeviceDescriptor); }} #endif android-file-transfer-linux-4.5/mtp/backend/darwin/usb/Exception.cpp000066400000000000000000000023551501120463400256050ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include namespace mtp { namespace usb { Exception::Exception(const std::string &what, int returnCode) throw() : std::runtime_error(what + ": " + GetErrorMessage(returnCode)) { } std::string Exception::GetErrorMessage(int returnCode) { char buf[128]; snprintf(buf, sizeof(buf), "error 0x%08x", returnCode); return std::string(buf); } }}android-file-transfer-linux-4.5/mtp/backend/darwin/usb/Exception.h000066400000000000000000000022431501120463400252460ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_DARWIN_USB_EXCEPTION_H #define AFTL_MTP_BACKEND_DARWIN_USB_EXCEPTION_H #include namespace mtp { namespace usb { class Exception : public std::runtime_error { public: Exception(const std::string &what, int retCode) throw(); static std::string GetErrorMessage(int retCode); }; }} #endif android-file-transfer-linux-4.5/mtp/backend/darwin/usb/Interface.cpp000066400000000000000000000061311501120463400255430ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include namespace mtp { namespace usb { Endpoint::Endpoint(IOUSBInterfaceInterface **interface, int idx): _interface(interface), _refIndex(idx) { UInt8 direction; UInt8 number; UInt8 transferType; UInt16 maxPacketSize; UInt8 interval; USB_CALL((*interface)->GetPipeProperties(interface, idx, &direction, &number, &transferType, &maxPacketSize, &interval)); switch (direction) { case kUSBOut: _direction = EndpointDirection::Out; break; case kUSBIn: _direction = EndpointDirection::In; break; case kUSBAnyDirn: _direction = EndpointDirection::Both; break; default: throw std::runtime_error("invalid endpoint direction"); } _address = number; switch(transferType) { case kUSBControl: _type = EndpointType::Control; break; case kUSBIsoc: _type = EndpointType::Isochronous; break; case kUSBBulk: _type = EndpointType::Bulk; break; case kUSBInterrupt: _type = EndpointType::Interrupt; break; default: throw std::runtime_error("invalid endpoint type"); } _maxPacketSize = maxPacketSize; } Interface::Interface(DevicePtr device, ConfigurationPtr config, IOUSBInterfaceInterface **interface): _device(device), _config(config), _interface(interface) { } Interface::~Interface() { } u8 Interface::GetClass() const { UInt8 intfClass; USB_CALL((*_interface)->GetInterfaceClass(_interface, &intfClass)); return intfClass; } u8 Interface::GetSubclass() const { UInt8 intfSubClass; USB_CALL((*_interface)->GetInterfaceSubClass(_interface, &intfSubClass)); return intfSubClass; } int Interface::GetIndex() const { UInt8 intfNumber; USB_CALL((*_interface)->GetInterfaceNumber(_interface, &intfNumber)); return intfNumber; } int Interface::GetEndpointsCount() const { UInt8 intfNumEndpoints; USB_CALL((*_interface)->GetNumEndpoints(_interface, &intfNumEndpoints)); return intfNumEndpoints; } InterfaceToken::InterfaceToken(IOUSBInterfaceInterface **interface): _interface(interface) { USB_CALL((*_interface)->USBInterfaceOpen(_interface)); } InterfaceToken::~InterfaceToken() { (*_interface)->USBInterfaceClose(_interface); } InterfaceTokenPtr Interface::Claim() { return std::make_shared(_interface); } }} android-file-transfer-linux-4.5/mtp/backend/darwin/usb/Interface.h000066400000000000000000000050121501120463400252050ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_DARWIN_USB_INTERFACE_H #define AFTL_MTP_BACKEND_DARWIN_USB_INTERFACE_H #include #include namespace mtp { namespace usb { class Endpoint { private: IOUSBInterfaceInterface ** _interface; int _refIndex; int _address; int _maxPacketSize; EndpointDirection _direction; EndpointType _type; public: Endpoint(IOUSBInterfaceInterface **interface, int idx); IOUSBInterfaceInterface ** GetInterfaceHandle() { return _interface; } int GetRefIndex() { return _refIndex; } u8 GetAddress() const { return _address; } int GetMaxPacketSize() const { return _maxPacketSize; } EndpointDirection GetDirection() const { return _direction; } EndpointType GetType() const { return _type; } }; DECLARE_PTR(Endpoint); class Configuration; DECLARE_PTR(Configuration); class Device; DECLARE_PTR(Device); class InterfaceToken : public IToken { IOUSBInterfaceInterface **_interface; public: InterfaceToken(IOUSBInterfaceInterface **interface); ~InterfaceToken(); }; DECLARE_PTR(InterfaceToken); class Interface { DevicePtr _device; ConfigurationPtr _config; IOUSBInterfaceInterface ** _interface; public: Interface(DevicePtr device, ConfigurationPtr config, IOUSBInterfaceInterface **interface); ~Interface(); IOUSBInterfaceInterface ** GetInterfaceHandle() { return _interface; } u8 GetClass() const; u8 GetSubclass() const; int GetIndex() const; int GetEndpointsCount() const; InterfaceTokenPtr Claim(); EndpointPtr GetEndpoint(int idx) const { return std::make_shared(_interface, idx + 1); } }; DECLARE_PTR(Interface); }} #endif android-file-transfer-linux-4.5/mtp/backend/darwin/usb/call.h000066400000000000000000000026241501120463400242260ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_DARWIN_USB_CALL_H #define AFTL_MTP_BACKEND_DARWIN_USB_CALL_H #include #include #include #include #define USB_CALL(...) \ do { int _r_ = (__VA_ARGS__); \ switch(_r_) { \ case kIOReturnSuccess : break; \ case kIOReturnNoDevice : throw mtp::usb::DeviceNotFoundException(); \ case kIOReturnExclusiveAccess: throw mtp::usb::DeviceBusyException(); \ default: throw mtp::usb::Exception(#__VA_ARGS__, _r_) ; \ } \ } while(false) #endif android-file-transfer-linux-4.5/mtp/backend/darwin/usb/usb.h000066400000000000000000000023521501120463400241020ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include namespace mtp { namespace usb { #if defined (kIOUSBDeviceInterfaceID500) typedef IOUSBDeviceInterface500 IOUSBDeviceType; #define DeviceInterfaceID kIOUSBDeviceInterfaceID500 #else typedef IOUSBDeviceInterface IOUSBDeviceType; #define DeviceInterfaceID kIOUSBDeviceInterface #endif }}android-file-transfer-linux-4.5/mtp/backend/haiku/000077500000000000000000000000001501120463400221625ustar00rootroot00000000000000android-file-transfer-linux-4.5/mtp/backend/haiku/usb/000077500000000000000000000000001501120463400227535ustar00rootroot00000000000000android-file-transfer-linux-4.5/mtp/backend/haiku/usb/Context.cpp000066400000000000000000000010171501120463400251020ustar00rootroot00000000000000/* * Context.cpp * Copyright (C) 2022 pulkomandy * * Distributed under terms of the MIT license. */ #include "Context.h" namespace mtp { namespace usb { Context::Context(int debugLevel) : BUSBRoster() { Start(); } Context::~Context() { Stop(); } status_t Context::DeviceAdded(BUSBDevice* device) { _devices.emplace_back(std::make_shared(device)); return B_OK; } void Context::DeviceRemoved(BUSBDevice* device) { // TODO _devices.remove(device); } } }; android-file-transfer-linux-4.5/mtp/backend/haiku/usb/Context.h000066400000000000000000000027061501120463400245550ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_LIBUSB_USB_CONTEXT_H #define AFTL_MTP_BACKEND_LIBUSB_USB_CONTEXT_H #include #include #include namespace mtp { namespace usb { class Context : public BUSBRoster, Noncopyable { public: typedef std::vector Devices; private: Devices _devices; status_t DeviceAdded(BUSBDevice*) final override; void DeviceRemoved(BUSBDevice*) final override; public: Context(int debugLevel = 3); ~Context(); void Wait(); const Devices & GetDevices() const { return _devices; } }; DECLARE_PTR(Context); }} #endif android-file-transfer-linux-4.5/mtp/backend/haiku/usb/Device.cpp000066400000000000000000000037501501120463400246630ustar00rootroot00000000000000/* * Device.cpp * Copyright (C) 2022 pulkomandy * * Distributed under terms of the MIT license. */ #include "Device.h" namespace mtp { namespace usb { Device::Device(ContextPtr ctx, BUSBDevice* handle) : _context(ctx) , _handle(handle) { } Device::~Device() { } void Device::Reset() { } void Device::ReadControl(u8 type, u8 req, u16 value, u16 index, ByteArray &data, int timeout) { fprintf(stderr, "Control read(type %x req %x value %x index %d size %lu) with timeout %d…", type, req, value, index, data.size(), timeout); ssize_t result = _handle->ControlTransfer(type, req, value, index, data.size(), const_cast(data.data())); if (result >= 0) data.resize(result); else throw std::runtime_error("read fail"); fprintf(stderr, " complete (%ld).\n", result); } void Device::WriteControl(u8 type, u8 req, u16 value, u16 index, const ByteArray &data, int timeout) { fprintf(stderr, "Control write with timeout %d\n", timeout); _handle->ControlTransfer(USB_REQTYPE_DEVICE_IN, req, value, index, data.size(), const_cast(data.data())); } void Device::SetConfiguration(int idx) { _handle->SetConfiguration(_handle->ConfigurationAt(idx)); } void Device::WriteBulk(const EndpointPtr & ep, const IObjectInputStreamPtr &inputStream, int timeout) { ByteArray data(inputStream->GetSize()); inputStream->Read(data.data(), data.size()); ssize_t tr = ep->_endpoint.BulkTransfer(data.data(), data.size()); if (tr != (int)data.size()) throw std::runtime_error("short write"); } void Device::ReadBulk(const EndpointPtr & ep, const IObjectOutputStreamPtr &outputStream, int timeout) { ByteArray data(ep->GetMaxPacketSize()); ssize_t tr; { tr = ep->_endpoint.BulkTransfer(data.data(), data.size()); outputStream->Write(data.data(), tr); } while(tr == (int)data.size()); } void Device::ClearHalt(const EndpointPtr & ep) { ep->_endpoint.ClearStall(); } InterfaceTokenPtr Device::ClaimInterface(const InterfacePtr & interface) { return InterfaceTokenPtr(); } }} android-file-transfer-linux-4.5/mtp/backend/haiku/usb/Device.h000066400000000000000000000055221501120463400243270ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_LIBUSB_USB_DEVICE_H #define AFTL_MTP_BACKEND_LIBUSB_USB_DEVICE_H #include #include #include #include #include #include namespace mtp { namespace usb { class Context; DECLARE_PTR(Context); class Endpoint { const BUSBEndpoint & _endpoint; public: Endpoint(const BUSBEndpoint & endpoint) : _endpoint(endpoint) { } u8 GetAddress() const { return _endpoint.Index(); } int GetMaxPacketSize() const { return _endpoint.MaxPacketSize(); } EndpointDirection GetDirection() const { if (_endpoint.IsInput()) return EndpointDirection::In; else return EndpointDirection::Out; } EndpointType GetType() const { if (_endpoint.IsBulk()) return EndpointType::Bulk; if (_endpoint.IsInterrupt()) return EndpointType::Interrupt; if (_endpoint.IsIsochronous()) return EndpointType::Isochronous; return EndpointType::Control; } friend class Device; }; DECLARE_PTR(Endpoint); class Interface; DECLARE_PTR(Interface); class InterfaceToken; DECLARE_PTR(InterfaceToken); class Device : Noncopyable { private: ContextPtr _context; BUSBDevice * _handle; public: Device(ContextPtr ctx, BUSBDevice * handle); ~Device(); BUSBDevice * GetHandle() { return _handle; } InterfaceTokenPtr ClaimInterface(const InterfacePtr & interface); void Reset(); int GetConfiguration() const; void SetConfiguration(int idx); void WriteBulk(const EndpointPtr & ep, const IObjectInputStreamPtr &inputStream, int timeout); void ReadBulk(const EndpointPtr & ep, const IObjectOutputStreamPtr &outputStream, int timeout); void ReadControl(u8 type, u8 req, u16 value, u16 index, ByteArray &data, int timeout); void WriteControl(u8 type, u8 req, u16 value, u16 index, const ByteArray &data, int timeout); void ClearHalt(const EndpointPtr & ep); std::string GetString(int idx) const; }; DECLARE_PTR(Device); }} #endif /* DEVICE_H */ android-file-transfer-linux-4.5/mtp/backend/haiku/usb/DeviceDescriptor.cpp000066400000000000000000000015761501120463400267260ustar00rootroot00000000000000/* * DeviceDescriptor.cpp * Copyright (C) 2022 pulkomandy * * Distributed under terms of the MIT license. */ #include "DeviceDescriptor.h" namespace mtp { namespace usb { DeviceDescriptor::DeviceDescriptor(BUSBDevice* dev) :_dev(dev) { } DeviceDescriptor::~DeviceDescriptor() { } ConfigurationPtr DeviceDescriptor::GetConfiguration(int conf) { return std::make_shared(_dev->ConfigurationAt(conf)); } ByteArray DeviceDescriptor::GetDescriptor() const { const usb_device_descriptor* descriptor = _dev->Descriptor(); ByteArray out; out.reserve(sizeof(usb_device_descriptor)); memcpy(out.data(), descriptor, sizeof(usb_device_descriptor)); return out; } DevicePtr DeviceDescriptor::TryOpen(ContextPtr context) { if (_dev->InitCheck() != B_OK) return nullptr; return std::make_shared(context, _dev); } }} android-file-transfer-linux-4.5/mtp/backend/haiku/usb/DeviceDescriptor.h000066400000000000000000000044531501120463400263700ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_LIBUSB_USB_DEVICEDESCRIPTOR_H #define AFTL_MTP_BACKEND_LIBUSB_USB_DEVICEDESCRIPTOR_H #include #include #include namespace mtp { namespace usb { class Configuration : Noncopyable { const BUSBConfiguration *_config; public: Configuration(const BUSBConfiguration *config) : _config(config) { } ~Configuration() { /* configuration is owned by its parent device */ } int GetIndex() const { return _config->Index(); } int GetInterfaceCount() const { return _config->CountInterfaces(); } int GetInterfaceAltSettingsCount(int idx) const { return _config->InterfaceAt(idx)->CountAlternates(); } // TODO figure out what to do with "int settings" InterfacePtr GetInterface(DevicePtr device, ConfigurationPtr config, int idx, int settings) const { return std::make_shared(device, config, *_config->InterfaceAt(idx)->AlternateAt(settings)); } }; class DeviceDescriptor { private: BUSBDevice * _dev; public: DeviceDescriptor(BUSBDevice *dev); ~DeviceDescriptor(); u16 GetVendorId() const { return _dev->VendorID(); } u16 GetProductId() const { return _dev->ProductID(); } DevicePtr Open(ContextPtr context); DevicePtr TryOpen(ContextPtr context); int GetConfigurationsCount() const { return _dev->CountConfigurations(); } ConfigurationPtr GetConfiguration(int conf); ByteArray GetDescriptor() const; }; DECLARE_PTR(DeviceDescriptor); }} #endif android-file-transfer-linux-4.5/mtp/backend/haiku/usb/Interface.h000066400000000000000000000037611501120463400250330ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_LIBUSB_USB_INTERFACE_H #define AFTL_MTP_BACKEND_LIBUSB_USB_INTERFACE_H #include #include namespace mtp { namespace usb { class Configuration; DECLARE_PTR(Configuration); class InterfaceToken : public IToken { BUSBDevice * _handle; int _index; public: InterfaceToken(BUSBDevice *handle, int index); ~InterfaceToken(); }; DECLARE_PTR(InterfaceToken); class Interface { DevicePtr _device; ConfigurationPtr _config; const BUSBInterface & _interface; public: Interface(DevicePtr device, ConfigurationPtr config, const BUSBInterface &interface): _device(device), _config(config), _interface(interface) { } u8 GetClass() const { return _interface.Class(); } u8 GetSubclass() const { return _interface.Subclass(); } int GetIndex() const { return _interface.Index(); } EndpointPtr GetEndpoint(int idx) const { return std::make_shared(*_interface.EndpointAt(idx)); } int GetEndpointsCount() const { return _interface.CountEndpoints(); } std::string GetName() const { return _interface.InterfaceString(); } }; DECLARE_PTR(Interface); }} #endif android-file-transfer-linux-4.5/mtp/backend/libusb/000077500000000000000000000000001501120463400223415ustar00rootroot00000000000000android-file-transfer-linux-4.5/mtp/backend/libusb/usb/000077500000000000000000000000001501120463400231325ustar00rootroot00000000000000android-file-transfer-linux-4.5/mtp/backend/libusb/usb/Context.cpp000066400000000000000000000027311501120463400252650ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include namespace mtp { namespace usb { Context::Context(int debugLevel) { USB_CALL(libusb_init(&_ctx)); libusb_set_debug(_ctx, debugLevel); libusb_device **devs; int count = libusb_get_device_list(_ctx, &devs); if (count < 0) throw Exception("libusb_get_device_list", count); _devices.reserve(count); for(int i = 0; i < count; ++i) _devices.push_back(std::make_shared(devs[i])); libusb_free_device_list(devs, 0); } Context::~Context() { libusb_exit(_ctx); } void Context::Wait() { USB_CALL(libusb_handle_events(_ctx)); } }} android-file-transfer-linux-4.5/mtp/backend/libusb/usb/Context.h000066400000000000000000000026051501120463400247320ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_LIBUSB_USB_CONTEXT_H #define AFTL_MTP_BACKEND_LIBUSB_USB_CONTEXT_H #include #include #include #include namespace mtp { namespace usb { class Context : Noncopyable { private: libusb_context * _ctx; public: typedef std::vector Devices; private: Devices _devices; public: Context(int debugLevel = 3); ~Context(); void Wait(); const Devices & GetDevices() const { return _devices; } }; DECLARE_PTR(Context); }} #endif android-file-transfer-linux-4.5/mtp/backend/libusb/usb/Device.cpp000066400000000000000000000070151501120463400250400ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include namespace mtp { namespace usb { Device::Device(ContextPtr context, libusb_device_handle * handle): _context(context), _handle(handle) { } void Device::Reset() { try { USB_CALL(libusb_reset_device(_handle)); } catch (std::exception & ex) { error("libusb_reset_device failed: ", ex.what()); } } Device::~Device() { libusb_close(_handle); } int Device::GetConfiguration() const { int config; USB_CALL(libusb_get_configuration(_handle, &config)); return config; } void Device::SetConfiguration(int idx) { USB_CALL(libusb_set_configuration(_handle, idx)); } InterfaceToken::InterfaceToken(libusb_device_handle *handle, int index): _handle(handle), _index(index) { USB_CALL(libusb_claim_interface(handle, index)); } InterfaceToken::~InterfaceToken() { libusb_release_interface(_handle, _index); } InterfaceTokenPtr Device::ClaimInterface(const InterfacePtr & interface) { return std::make_shared(_handle, interface->GetIndex()); } void Device::WriteBulk(const EndpointPtr & ep, const IObjectInputStreamPtr &inputStream, int timeout) { ByteArray data(inputStream->GetSize()); inputStream->Read(data.data(), data.size()); int tr = 0; USB_CALL(libusb_bulk_transfer(_handle, ep->GetAddress(), const_cast(data.data()), data.size(), &tr, timeout)); if (tr != (int)data.size()) throw std::runtime_error("short write"); } void Device::ReadBulk(const EndpointPtr & ep, const IObjectOutputStreamPtr &outputStream, int timeout) { ByteArray data(ep->GetMaxPacketSize() * 1024); int tr; do { USB_CALL(libusb_bulk_transfer(_handle, ep->GetAddress(), data.data(), data.size(), &tr, timeout)); outputStream->Write(data.data(), tr); } while(tr == (int)data.size()); } void Device::ReadControl(u8 type, u8 req, u16 value, u16 index, ByteArray &data, int timeout) { USB_CALL(libusb_control_transfer(_handle, type, req, value, index, data.data(), data.size(), timeout)); } void Device::WriteControl(u8 type, u8 req, u16 value, u16 index, const ByteArray &data, int timeout) { USB_CALL(libusb_control_transfer(_handle, type, req, value, index, const_cast(data.data()), data.size(), timeout)); } void Device::ClearHalt(const EndpointPtr & ep) { USB_CALL(libusb_clear_halt(_handle, ep->GetAddress())); } std::string Device::GetString(int idx) const { unsigned char buffer[4096]; int r = libusb_get_string_descriptor_ascii(_handle, idx, buffer, sizeof(buffer)); if (r < 0) throw Exception("libusb_get_string_descriptor_ascii", r); return std::string(buffer, buffer + r); } }} android-file-transfer-linux-4.5/mtp/backend/libusb/usb/Device.h000066400000000000000000000053751501120463400245140ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_LIBUSB_USB_DEVICE_H #define AFTL_MTP_BACKEND_LIBUSB_USB_DEVICE_H #include #include #include #include #include #include namespace mtp { namespace usb { class Context; DECLARE_PTR(Context); class Endpoint { const libusb_endpoint_descriptor & _endpoint; public: Endpoint(const libusb_endpoint_descriptor & endpoint) : _endpoint(endpoint) { } u8 GetAddress() const { return _endpoint.bEndpointAddress; } int GetMaxPacketSize() const { return _endpoint.wMaxPacketSize; } EndpointDirection GetDirection() const { u8 dir = GetAddress() & LIBUSB_ENDPOINT_DIR_MASK; if (dir == LIBUSB_ENDPOINT_IN) return EndpointDirection::In; else return EndpointDirection::Out; } EndpointType GetType() const { return EndpointType(_endpoint.bmAttributes & 3); } }; DECLARE_PTR(Endpoint); class Interface; DECLARE_PTR(Interface); class InterfaceToken; DECLARE_PTR(InterfaceToken); class Device : Noncopyable { private: ContextPtr _context; libusb_device_handle * _handle; public: Device(ContextPtr ctx, libusb_device_handle * handle); ~Device(); libusb_device_handle * GetHandle() { return _handle; } InterfaceTokenPtr ClaimInterface(const InterfacePtr & interface); void Reset(); int GetConfiguration() const; void SetConfiguration(int idx); void WriteBulk(const EndpointPtr & ep, const IObjectInputStreamPtr &inputStream, int timeout); void ReadBulk(const EndpointPtr & ep, const IObjectOutputStreamPtr &outputStream, int timeout); void ReadControl(u8 type, u8 req, u16 value, u16 index, ByteArray &data, int timeout); void WriteControl(u8 type, u8 req, u16 value, u16 index, const ByteArray &data, int timeout); void ClearHalt(const EndpointPtr & ep); std::string GetString(int idx) const; }; DECLARE_PTR(Device); }} #endif /* DEVICE_H */ android-file-transfer-linux-4.5/mtp/backend/libusb/usb/DeviceDescriptor.cpp000066400000000000000000000035121501120463400270750ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include namespace mtp { namespace usb { DeviceDescriptor::DeviceDescriptor(libusb_device *dev): _dev(dev) { USB_CALL(libusb_get_device_descriptor(_dev, &_descriptor)); } ConfigurationPtr DeviceDescriptor::GetConfiguration(int conf) { libusb_config_descriptor *desc; USB_CALL(libusb_get_config_descriptor(_dev, conf, &desc)); return std::make_shared(desc); } DevicePtr DeviceDescriptor::Open(ContextPtr context) { libusb_device_handle *handle; USB_CALL(libusb_open(_dev, &handle)); return std::make_shared(context, handle); } DevicePtr DeviceDescriptor::TryOpen(ContextPtr context) { libusb_device_handle *handle; int r = libusb_open(_dev, &handle); return r == 0? std::make_shared(context, handle): nullptr; } DeviceDescriptor::~DeviceDescriptor() { libusb_unref_device(_dev); } ByteArray DeviceDescriptor::GetDescriptor() const { throw std::runtime_error("not possible with libusb"); } }} android-file-transfer-linux-4.5/mtp/backend/libusb/usb/DeviceDescriptor.h000066400000000000000000000044721501120463400265500ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_LIBUSB_USB_DEVICEDESCRIPTOR_H #define AFTL_MTP_BACKEND_LIBUSB_USB_DEVICEDESCRIPTOR_H #include #include #include #include namespace mtp { namespace usb { class Configuration : Noncopyable { libusb_config_descriptor *_config; public: Configuration(libusb_config_descriptor *config) : _config(config) { } ~Configuration() { libusb_free_config_descriptor(_config); } int GetIndex() const { return _config->bConfigurationValue; } int GetInterfaceCount() const { return _config->bNumInterfaces; } int GetInterfaceAltSettingsCount(int idx) const { return _config->interface[idx].num_altsetting; } InterfacePtr GetInterface(DevicePtr device, ConfigurationPtr config, int idx, int settings) const { return std::make_shared(device, config, _config->interface[idx].altsetting[settings]); } }; class DeviceDescriptor { private: libusb_device * _dev; libusb_device_descriptor _descriptor; public: DeviceDescriptor(libusb_device *dev); ~DeviceDescriptor(); u16 GetVendorId() const { return _descriptor.idVendor; } u16 GetProductId() const { return _descriptor.idProduct; } DevicePtr Open(ContextPtr context); DevicePtr TryOpen(ContextPtr context); int GetConfigurationsCount() const { return _descriptor.bNumConfigurations; } ConfigurationPtr GetConfiguration(int conf); ByteArray GetDescriptor() const; }; DECLARE_PTR(DeviceDescriptor); }} #endif android-file-transfer-linux-4.5/mtp/backend/libusb/usb/Exception.cpp000066400000000000000000000022561501120463400256010ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include namespace mtp { namespace usb { Exception::Exception(const std::string &what, int returnCode) throw() : std::runtime_error(what + ": " + GetErrorMessage(returnCode)) { } std::string Exception::GetErrorMessage(int returnCode) { return libusb_error_name(returnCode); } }}android-file-transfer-linux-4.5/mtp/backend/libusb/usb/Exception.h000066400000000000000000000022431501120463400252420ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_LIBUSB_USB_EXCEPTION_H #define AFTL_MTP_BACKEND_LIBUSB_USB_EXCEPTION_H #include namespace mtp { namespace usb { class Exception : public std::runtime_error { public: Exception(const std::string &what, int retCode) throw(); static std::string GetErrorMessage(int retCode); }; }} #endif android-file-transfer-linux-4.5/mtp/backend/libusb/usb/Interface.cpp000066400000000000000000000015561501120463400255450ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "Interface.h" android-file-transfer-linux-4.5/mtp/backend/libusb/usb/Interface.h000066400000000000000000000041731501120463400252100ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_LIBUSB_USB_INTERFACE_H #define AFTL_MTP_BACKEND_LIBUSB_USB_INTERFACE_H #include #include #include namespace mtp { namespace usb { class Configuration; DECLARE_PTR(Configuration); class InterfaceToken : public IToken { libusb_device_handle * _handle; int _index; public: InterfaceToken(libusb_device_handle *handle, int index); ~InterfaceToken(); }; DECLARE_PTR(InterfaceToken); class Interface { DevicePtr _device; ConfigurationPtr _config; const libusb_interface_descriptor & _interface; public: Interface(DevicePtr device, ConfigurationPtr config, const libusb_interface_descriptor &interface): _device(device), _config(config), _interface(interface) { } u8 GetClass() const { return _interface.bInterfaceClass; } u8 GetSubclass() const { return _interface.bInterfaceSubClass; } int GetIndex() const { return _interface.bInterfaceNumber; } EndpointPtr GetEndpoint(int idx) const { return std::make_shared(_interface.endpoint[idx]); } int GetEndpointsCount() const { return _interface.bNumEndpoints; } std::string GetName() const { return _interface.iInterface? _device->GetString(_interface.iInterface): std::string(); } }; DECLARE_PTR(Interface); }} #endif android-file-transfer-linux-4.5/mtp/backend/libusb/usb/call.h000066400000000000000000000021411501120463400242140ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_LIBUSB_USB_CALL_H #define AFTL_MTP_BACKEND_LIBUSB_USB_CALL_H #include #include #define USB_CALL(...) do { int r = (__VA_ARGS__); if (r != 0) throw mtp::usb::Exception(#__VA_ARGS__, r) ; } while(false) #endif android-file-transfer-linux-4.5/mtp/backend/linux/000077500000000000000000000000001501120463400222205ustar00rootroot00000000000000android-file-transfer-linux-4.5/mtp/backend/linux/usb/000077500000000000000000000000001501120463400230115ustar00rootroot00000000000000android-file-transfer-linux-4.5/mtp/backend/linux/usb/BufferAllocator.h000066400000000000000000000066051501120463400262430ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_LINUX_USB_BUFFERALLOCATOR_H #define AFTL_MTP_BACKEND_LINUX_USB_BUFFERALLOCATOR_H #include #include #include #include #include #include #include namespace mtp { namespace usb { class BufferAllocator; class Buffer { u8 * _data; size_t _size; public: Buffer(u8 *data, size_t size): _data(data), _size(size) { } u8 * GetData() const { return _data; } size_t GetSize() const { return _size; } }; class BufferAllocator : Noncopyable { static constexpr size_t Buffers = 16; //for parallel endpoint access static constexpr size_t BufferSize = 64 * 1024; std::mutex _mutex; int _fd; long _pageSize; u8 * _buffer; size_t _bufferSize; ByteArray _normalBuffer; std::array _bufferAllocated; void AllocateNormalBuffer() { _fd = -1; _normalBuffer.resize(Buffers * BufferSize); _buffer = _normalBuffer.data(); _bufferSize = _normalBuffer.size(); } public: BufferAllocator(int fd): _fd(fd), _pageSize(sysconf(_SC_PAGESIZE)), _buffer(nullptr), _bufferSize(0), _bufferAllocated() { if (_pageSize <= 0) throw posix::Exception("sysconf(_SC_PAGESIZE)"); debug("page size = ", _pageSize); } ~BufferAllocator() { if (_fd >= 0) munmap(_buffer, _bufferSize); } void Free(Buffer &buffer) { scoped_mutex_lock l(_mutex); size_t index = (buffer.GetData() - _buffer) / BufferSize; _bufferAllocated.at(index) = false; } Buffer Allocate(size_t size) { scoped_mutex_lock l(_mutex); if (!_buffer) { _bufferSize = (BufferSize + _pageSize - 1) / _pageSize * _pageSize; if (_fd >= 0) { try { auto buffer = static_cast(mmap(nullptr, _bufferSize * Buffers, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0)); if (buffer == MAP_FAILED) throw posix::Exception("mmap failed"); _buffer = buffer; debug("mapped buffer of ", _bufferSize * Buffers, " bytes to ", static_cast(_buffer)); } catch(const std::exception &ex) { error("zerocopy allocator failed: ", ex.what()); AllocateNormalBuffer(); } } else AllocateNormalBuffer(); } if (size > BufferSize) size = BufferSize; for(size_t i = 0; i < _bufferAllocated.size(); ++i) { if (!_bufferAllocated[i]) { _bufferAllocated[i] = true; return Buffer(_buffer + BufferSize * i, size); } } throw std::runtime_error("BufferAllocator::Allocate: out of mapped memory"); } }; }} #endif android-file-transfer-linux-4.5/mtp/backend/linux/usb/Context.cpp000066400000000000000000000037671501120463400251560ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include namespace mtp { namespace usb { Context::Context() { std::string rootPath("/sys/bus/usb/devices"); Directory usbDir(rootPath); std::map > devices; while(true) { std::string entry = usbDir.Read(); if (entry.empty()) break; try { unsigned busId, conf, interface; char portBuf[256]; if (sscanf(entry.c_str(), "%u-%255[0-9.]:%u.%u", &busId, portBuf, &conf, &interface) == 4) { std::string port = portBuf; if ((port.size() == 1 && port[0] == '0')) continue; DeviceDescriptorPtr &device = devices[busId][port]; if (!device) { char deviceRoot[64]; snprintf(deviceRoot, sizeof(deviceRoot), "%u-%s", busId, port.c_str()); device = std::make_shared(busId, rootPath + "/" + std::string(deviceRoot)); _devices.push_back(device); } device->AddInterface(conf, interface, rootPath + "/" + entry); } } catch(const std::exception &ex) { error(ex.what()); } } } Context::~Context() { } }} android-file-transfer-linux-4.5/mtp/backend/linux/usb/Context.h000066400000000000000000000024631501120463400246130ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_LINUX_USB_CONTEXT_H #define AFTL_MTP_BACKEND_LINUX_USB_CONTEXT_H #include #include #include namespace mtp { namespace usb { class Context : Noncopyable { private: public: typedef std::vector Devices; private: Devices _devices; public: Context(); ~Context(); const Devices & GetDevices() const { return _devices; } }; DECLARE_PTR(Context); }} #endif android-file-transfer-linux-4.5/mtp/backend/linux/usb/Device.cpp000066400000000000000000000251131501120463400247160ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include "linux/usbdevice_fs.h" #define IOCTL(FD, ...) do \ { \ int r = ioctl(FD, __VA_ARGS__); \ if (r < 0) \ { \ if (errno == EBUSY) \ throw DeviceBusyException(FD); \ else if (errno == ENODEV) \ throw DeviceNotFoundException(); \ else \ throw posix::Exception("ioctl(" #__VA_ARGS__ ")"); \ } \ } while(false) namespace mtp { namespace usb { InterfaceToken::InterfaceToken(int fd, unsigned interfaceNumber): _fd(fd), _interfaceNumber(interfaceNumber) { usbdevfs_disconnect_claim claim = {}; claim.interface = interfaceNumber; IOCTL(_fd, USBDEVFS_DISCONNECT_CLAIM, &claim); } InterfaceToken::~InterfaceToken() { ioctl(_fd, USBDEVFS_RELEASEINTERFACE, &_interfaceNumber); } #define PRINT_CAP(CAP, NAME) \ if (capabilities & (CAP)) \ { \ debug(NAME " "); \ capabilities &= ~(CAP); \ } Device::Device(int fd, const EndpointPtr &controlEp, u8 configuration): _fd(fd), _capabilities(0), _controlEp(controlEp), _configuration(configuration) { try { IOCTL(_fd.Get(), USBDEVFS_GET_CAPABILITIES, &_capabilities); } catch(const std::exception &ex) { error("get usbfs capabilities failed: ", ex.what()); } debug("capabilities = 0x", hex(_capabilities, 8)); bool mmap = _capabilities & USBDEVFS_CAP_MMAP; //disable mmap allocation for now, see https://github.com/whoozle/android-file-transfer-linux/issues/194 #if 1 mmap = false; #endif _bufferAllocator = std::make_shared(mmap? fd: -1); if (_capabilities) { u32 capabilities = _capabilities; PRINT_CAP(USBDEVFS_CAP_ZERO_PACKET, ""); PRINT_CAP(USBDEVFS_CAP_BULK_CONTINUATION, ""); PRINT_CAP(USBDEVFS_CAP_NO_PACKET_SIZE_LIM, ""); PRINT_CAP(USBDEVFS_CAP_BULK_SCATTER_GATHER, ""); PRINT_CAP(USBDEVFS_CAP_REAP_AFTER_DISCONNECT, ""); PRINT_CAP(USBDEVFS_CAP_MMAP, ""); PRINT_CAP(USBDEVFS_CAP_DROP_PRIVILEGES, ""); PRINT_CAP(USBDEVFS_CAP_CONNINFO_EX, ""); PRINT_CAP(USBDEVFS_CAP_SUSPEND, ""); if (capabilities) debug(""); } else debug("[none]\n"); } Device::~Device() { } void Device::Reset() { debug("resetting device..."); try { IOCTL(_fd.Get(), USBDEVFS_RESET); SetConfiguration(_configuration); } catch(const std::exception &ex) { error("resetting device failed: ", ex.what()); } } int Device::GetConfiguration() const { return _configuration; } void Device::SetConfiguration(int idx) { debug("SetConfiguration(", idx, ")"); IOCTL(_fd.Get(), USBDEVFS_SETCONFIGURATION, &idx); _configuration = idx; } struct Device::Urb : usbdevfs_urb, Noncopyable { static const int MaxBufferSize = 4096; BufferAllocator & Allocator; int Fd; int PacketSize; Buffer DataBuffer; Urb(BufferAllocator & allocator, int fd, u8 urbType, const EndpointPtr & ep): usbdevfs_urb(), Allocator(allocator), Fd(fd), PacketSize(ep->GetMaxPacketSize()), DataBuffer(Allocator.Allocate(std::max(PacketSize, MaxBufferSize / PacketSize * PacketSize))) { type = urbType; endpoint = ep->GetAddress(); buffer = DataBuffer.GetData(); buffer_length = DataBuffer.GetSize(); } usbdevfs_urb *GetKernelUrb() { return static_cast(this); } ~Urb() { Allocator.Free(DataBuffer); } size_t GetTransferSize() const { return DataBuffer.GetSize(); } void Submit() { IOCTL(Fd, USBDEVFS_SUBMITURB, GetKernelUrb()); } void Discard() { int r = ioctl(Fd, USBDEVFS_DISCARDURB, GetKernelUrb()); if (r != 0) { perror("ioctl(USBDEVFS_DISCARDURB)"); } } size_t Send(const IObjectInputStreamPtr &inputStream, size_t size) { if (size > DataBuffer.GetSize()) throw std::logic_error("invalid size passed to Send"); auto data = DataBuffer.GetData(); size_t r = inputStream->Read(data, size); //HexDump("write", ByteArray(data, data + r), true); buffer_length = r; return r; } size_t Send(const ByteArray &inputData) { size_t r = std::min(DataBuffer.GetSize(), inputData.size()); std::copy(inputData.data(), inputData.data() + r, DataBuffer.GetData()); buffer_length = r; return r; } size_t Recv(const IObjectOutputStreamPtr &outputStream) { auto data = DataBuffer.GetData(); //HexDump("read", ByteArray(data, data + actual_length), true); return outputStream->Write(data, actual_length); } template void SetFlag(bool value) { if (value) flags |= Flag; else flags &= ~Flag; } void SetContinuationFlag(bool continuation) { SetFlag(continuation); } void SetZeroPacketFlag(bool zero) { SetFlag(zero); } }; void * Device::Reap(int timeout) { auto urb = AsyncReap(); //attempt to pick up old urbs if (urb) return urb; timeval started = {}; if (gettimeofday(&started, NULL) == -1) throw posix::Exception("gettimeofday"); pollfd fd = {}; fd.fd = _fd.Get(); fd.events = POLLOUT | POLLWRNORM; int r = poll(&fd, 1, timeout); if (r < 0) throw posix::Exception("poll"); timeval now = {}; if (gettimeofday(&now, NULL) == -1) throw posix::Exception("gettimeofday"); if (r == 0 && timeout > 0) { int ms = (now.tv_sec - started.tv_sec) * 1000 + (now.tv_usec - started.tv_usec) / 1000; error(ms, " ms since the last poll call"); } urb = AsyncReap(); if (urb) return urb; else throw TimeoutException("timeout reaping usb urb"); } void * Device::AsyncReap() { usbdevfs_urb *urb; int r = ioctl(_fd.Get(), USBDEVFS_REAPURBNDELAY, &urb); if (r == 0) return urb; else if (errno == EAGAIN) return nullptr; else throw posix::Exception("ioctl(USBDEVFS_REAPURBNDELAY)"); } void Device::ClearHalt(const EndpointPtr & ep) { try { unsigned index = ep->GetAddress(); IOCTL(_fd.Get(), USBDEVFS_CLEAR_HALT, &index); } catch(const std::exception &ex) { error("clearing halt status for ep ", hex(ep->GetAddress(), 2), ": ", ex.what()); } } void Device::Submit(Urb *urb, int timeout) { urb->Submit(); try { while(true) { usbdevfs_urb * completedKernelUrb = static_cast(Reap(timeout)); if (urb->GetKernelUrb() != completedKernelUrb) { error("got unknown urb: ", completedKernelUrb, " of size ", completedKernelUrb->buffer_length); continue; } else break; } } catch(const TimeoutException &ex) { urb->Discard(); throw; } catch(const std::exception &ex) { error("error while submitting urb: ", ex.what()); urb->Discard(); throw; } } void Device::WriteBulk(const EndpointPtr & ep, const IObjectInputStreamPtr &inputStream, int timeout) { Urb urb(*_bufferAllocator, _fd.Get(), USBDEVFS_URB_TYPE_BULK, ep); size_t transferSize = urb.GetTransferSize(); size_t r; bool continuation = false; do { r = urb.Send(inputStream, transferSize); if (_capabilities & USBDEVFS_CAP_ZERO_PACKET) urb.SetZeroPacketFlag(r != transferSize); if (_capabilities & USBDEVFS_CAP_BULK_CONTINUATION) { urb.SetContinuationFlag(continuation); continuation = true; } Submit(&urb, timeout); } while(r == transferSize); } void Device::ReadBulk(const EndpointPtr & ep, const IObjectOutputStreamPtr &outputStream, int timeout) { Urb urb(*_bufferAllocator, _fd.Get(), USBDEVFS_URB_TYPE_BULK, ep); size_t transferSize = urb.GetTransferSize(); size_t r; bool continuation = false; do { if (_capabilities & USBDEVFS_CAP_BULK_CONTINUATION) { urb.SetContinuationFlag(continuation); continuation = true; } Submit(&urb, timeout); r = urb.Recv(outputStream); } while(r == transferSize); } u8 Device::TransactionType(const EndpointPtr &ep) { EndpointType type = ep->GetType(); switch(type) { case EndpointType::Control: return USBDEVFS_URB_TYPE_CONTROL; case EndpointType::Isochronous: return USBDEVFS_URB_TYPE_ISO; case EndpointType::Bulk: return USBDEVFS_URB_TYPE_BULK; case EndpointType::Interrupt: return USBDEVFS_URB_TYPE_INTERRUPT; default: throw std::runtime_error("invalid endpoint type"); } } void Device::ReadControl(u8 type, u8 req, u16 value, u16 index, ByteArray &data, int timeout) { debug("read control ", hex(type, 2), " ", hex(req, 2), " ", hex(value, 4), " ", hex(index, 4)); usbdevfs_ctrltransfer ctrl = { }; ctrl.bRequestType = type; ctrl.bRequest = req; ctrl.wValue = value; ctrl.wIndex = index; ctrl.wLength = data.size(); ctrl.data = const_cast(data.data()); ctrl.timeout = timeout; int fd = _fd.Get(); int r = ioctl(fd, USBDEVFS_CONTROL, &ctrl); if (r >= 0) data.resize(r); else if (errno == EAGAIN) throw TimeoutException("timeout sending control transfer"); else throw posix::Exception("ioctl"); } void Device::WriteControl(u8 type, u8 req, u16 value, u16 index, const ByteArray &data, int timeout) { debug("write control ", hex(type, 2), " ", hex(req, 2), " ", hex(value, 4), " ", hex(index, 4)); usbdevfs_ctrltransfer ctrl = { }; ctrl.bRequestType = type; ctrl.bRequest = req; ctrl.wValue = value; ctrl.wIndex = index; ctrl.wLength = data.size(); ctrl.data = const_cast(data.data()); ctrl.timeout = timeout; int fd = _fd.Get(); int r = ioctl(fd, USBDEVFS_CONTROL, &ctrl); if (r >= 0) return; else if (errno == EAGAIN) throw TimeoutException("timeout sending control transfer"); else throw posix::Exception("ioctl"); } }} android-file-transfer-linux-4.5/mtp/backend/linux/usb/Device.h000066400000000000000000000052731501120463400243700ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_LINUX_USB_DEVICE_H #define AFTL_MTP_BACKEND_LINUX_USB_DEVICE_H #include #include #include #include #include #include #include #include #include namespace mtp { namespace usb { class Context; DECLARE_PTR(Context); class BufferAllocator; DECLARE_PTR(BufferAllocator); class InterfaceToken : public IToken { int _fd; unsigned _interfaceNumber; public: InterfaceToken(int fd, unsigned interfaceNumber); ~InterfaceToken(); }; DECLARE_PTR(InterfaceToken); class Device : Noncopyable { private: posix::FileHandler _fd; u32 _capabilities; EndpointPtr _controlEp; u8 _configuration; BufferAllocatorPtr _bufferAllocator; struct Urb; //DECLARE_PTR(Urb); std::queue> _controls; public: Device(int fd, const EndpointPtr &controlEp, u8 configuration); ~Device(); InterfaceTokenPtr ClaimInterface(const InterfacePtr & interface) { return std::make_shared(_fd.Get(), interface->GetIndex()); } void Reset(); int GetConfiguration() const; void SetConfiguration(int idx); void ClearHalt(const EndpointPtr & ep); void WriteBulk(const EndpointPtr & ep, const IObjectInputStreamPtr &inputStream, int timeout); void ReadBulk(const EndpointPtr & ep, const IObjectOutputStreamPtr &outputStream, int timeout); void WriteControl(u8 type, u8 req, u16 value, u16 index, const ByteArray &data, int timeout); void ReadControl(u8 type, u8 req, u16 value, u16 index, ByteArray &data, int timeout); //result buffer must be allocated private: static u8 TransactionType(const EndpointPtr &ep); void * Reap(int timeout); void * AsyncReap(); void Submit(Urb *urb, int timeout); }; DECLARE_PTR(Device); }} #endif /* DEVICE_H */ android-file-transfer-linux-4.5/mtp/backend/linux/usb/DeviceDescriptor.cpp000066400000000000000000000042141501120463400267540ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include namespace mtp { namespace usb { DeviceDescriptor::DeviceDescriptor(int busId, const std::string &path): _busId(busId), _path(path) { debug("creating device descriptor at ", path); _vendor = Directory::ReadInt(path + "/idVendor"); _product = Directory::ReadInt(path + "/idProduct"); _deviceNumber = Directory::ReadInt(path + "/devnum", 10); _controlEp = std::make_shared(path + "/ep_00"); _descriptor = Directory::ReadAll(path + "/descriptors"); _configurationValue = Directory::ReadInt(path + "/bConfigurationValue", 10); } DevicePtr DeviceDescriptor::Open(ContextPtr context) { DevicePtr device = TryOpen(context); if (!device) throw std::runtime_error("cannot open device at " + _path); return device; } DevicePtr DeviceDescriptor::TryOpen(ContextPtr context) { char fname[256]; snprintf(fname, sizeof(fname), "/dev/bus/usb/%03d/%03u", _busId, _deviceNumber); int fd = open(fname, O_RDWR); if (fd != -1) return std::make_shared(fd, _controlEp, _configurationValue); debug("error: ", posix::Exception::GetErrorMessage(errno)); return nullptr; } DeviceDescriptor::~DeviceDescriptor() { } }} android-file-transfer-linux-4.5/mtp/backend/linux/usb/DeviceDescriptor.h000066400000000000000000000053541501120463400264270ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_LINUX_USB_DEVICEDESCRIPTOR_H #define AFTL_MTP_BACKEND_LINUX_USB_DEVICEDESCRIPTOR_H #include #include #include #include namespace mtp { namespace usb { class Configuration : Noncopyable { std::map _interfaces; public: Configuration() { } int GetIndex() const { return 0; } int GetInterfaceCount() const { return _interfaces.size(); } int GetInterfaceAltSettingsCount(int idx) const { return 1; } InterfacePtr GetInterface(DevicePtr device, ConfigurationPtr config, int idx, int settings) const { return _interfaces.at(idx); } void AddInterface(int index, const std::string &path) { _interfaces[index] = std::make_shared(index, path); } }; class DeviceDescriptor { int _busId; std::string _path; u16 _vendor, _product; int _deviceNumber; int _configurationValue; std::map _configurationMap; std::vector _configuration; EndpointPtr _controlEp; ByteArray _descriptor; public: DeviceDescriptor(int busId, const std::string &path); ~DeviceDescriptor(); u16 GetVendorId() const { return _vendor; } u16 GetProductId() const { return _product; } DevicePtr Open(ContextPtr context); DevicePtr TryOpen(ContextPtr context); int GetConfigurationsCount() const { return _configuration.size(); } ConfigurationPtr GetConfiguration(int conf) { return _configuration.at(conf); } void AddInterface(int confIndex, int interface, const std::string &path) { ConfigurationPtr &conf = _configurationMap[confIndex]; if (!conf) { conf = std::make_shared(); _configuration.push_back(conf); } conf->AddInterface(interface, path); } ByteArray GetDescriptor() const { return _descriptor; } }; DECLARE_PTR(DeviceDescriptor); }} #endif android-file-transfer-linux-4.5/mtp/backend/linux/usb/Directory.h000066400000000000000000000067521501120463400251400ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_LINUX_USB_DIRECTORY_H #define AFTL_MTP_BACKEND_LINUX_USB_DIRECTORY_H #include #include #include #include #include #include #include #include namespace mtp { namespace usb { class File : Noncopyable { FILE *_f; public: File(const std::string &path) : _f(fopen(path.c_str(), "rb")) { if (!_f) throw posix::Exception("open " + path); } ~File() { fclose(_f); } FILE *GetHandle() { return _f; } std::string ReadLine(size_t bufSize = 1024) { std::vector buf(bufSize); if (!fgets(buf.data(), buf.size(), _f)) throw posix::Exception("fgets"); return buf.data(); } ByteArray ReadAll() { static const size_t step = 4096; fseek(_f, 0, SEEK_SET); ByteArray buf; size_t r; do { size_t offset = buf.size(); buf.resize(offset + step); r = fread(buf.data() + offset, 1, step, _f); } while(r == step); buf.resize(buf.size() - step + r); return buf; } int ReadInt(int base) { int r; switch(base) { case 16: if (fscanf(_f, "%x", &r) != 1) throw std::runtime_error("cannot read number"); break; case 10: if (fscanf(_f, "%d", &r) != 1) throw std::runtime_error("cannot read number"); break; default: throw std::runtime_error("invalid base"); } return r; } }; class Directory : Noncopyable { private: DIR * _dir; std::vector _buffer; public: Directory(const std::string &path): _dir(opendir(path.c_str())) { if (!_dir) throw posix::Exception("opendir"); long name_max = pathconf(path.c_str(), _PC_NAME_MAX); if (name_max == -1) /* Limit not defined, or error */ name_max = 255; /* Take a guess */ _buffer.resize(offsetof(struct dirent, d_name) + name_max + 1); } std::string Read() { dirent *entry; #if 0 dirent *buffer = static_cast(static_cast(_buffer.data())); //thread safety is not in posix yet, but those shitheads already deprecated it int r = readdir_r(_dir, buffer, &entry); #endif entry = readdir(_dir); return entry? entry->d_name: ""; } ~Directory() { closedir(_dir); } static int ReadInt(const std::string &path, int base = 16) { File f(path); return f.ReadInt(base); } static std::string ReadString(const std::string &path) { File f(path); std::string str = f.ReadLine(); size_t end = str.find_last_not_of(" \t\r\n\f"); return end != str.npos? str.substr(0, end + 1): str; } static ByteArray ReadAll(const std::string &path) { File f(path); return f.ReadAll(); } }; }} #endif /* DIRECTORY_H */ android-file-transfer-linux-4.5/mtp/backend/linux/usb/Endpoint.cpp000066400000000000000000000040551501120463400253010ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include namespace mtp { namespace usb { Endpoint::Endpoint(const std::string &path): _maxPacketSize(0) { _addr = Directory::ReadInt(path + "/bEndpointAddress"); std::string type = Directory::ReadString(path + "/type"); if (type == "Bulk") _type = EndpointType::Bulk; else if (type == "Control") _type = EndpointType::Control; else if (type == "Interrupt") _type = EndpointType::Interrupt; else if (type == "Isoc") _type = EndpointType::Isochronous; else throw std::runtime_error("invalid endpoint type " + type); std::string dir = Directory::ReadString(path + "/direction"); if (dir == "out") _direction = EndpointDirection::Out; else if (dir == "in") _direction = EndpointDirection::In; else if (dir == "both") _direction = EndpointDirection::Both; else throw std::runtime_error("invalid endpoint direction " + dir); _maxPacketSize = Directory::ReadInt(path + "/wMaxPacketSize"); } EndpointPtr Endpoint::TryOpen(const std::string &path) { try { return std::make_shared(path); } catch(const std::exception &ex) { error("failed adding endpoint: ", ex.what()); } return nullptr; } }} android-file-transfer-linux-4.5/mtp/backend/linux/usb/Endpoint.h000066400000000000000000000030471501120463400247460ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_LINUX_USB_ENDPOINT_H #define AFTL_MTP_BACKEND_LINUX_USB_ENDPOINT_H #include #include #include namespace mtp { namespace usb { class Endpoint; DECLARE_PTR(Endpoint); class Endpoint { EndpointDirection _direction; EndpointType _type; u8 _addr; u16 _maxPacketSize; public: Endpoint(const std::string &path); u8 GetAddress() const { return _addr; } int GetMaxPacketSize() const { return _maxPacketSize; } EndpointDirection GetDirection() const { return _direction; } EndpointType GetType() const { return _type; } static EndpointPtr TryOpen(const std::string &path); }; DECLARE_PTR(Endpoint); }} #endif android-file-transfer-linux-4.5/mtp/backend/linux/usb/Interface.cpp000066400000000000000000000027341501120463400254230ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include namespace mtp { namespace usb { Interface::Interface(int index, const std::string &path): _path(path) { _class = Directory::ReadInt(path + "/bInterfaceClass"); _subclass = Directory::ReadInt(path + "/bInterfaceSubClass"); _index = Directory::ReadInt(path + "/bInterfaceNumber"); Directory dir(path); while(true) { std::string entry = dir.Read(); if (entry.empty()) break; if (entry.compare(0, 3, "ep_") == 0) { EndpointPtr ep = Endpoint::TryOpen(path + "/" + entry); if (ep) _endpoints.push_back(ep); } } } }} android-file-transfer-linux-4.5/mtp/backend/linux/usb/Interface.h000066400000000000000000000033421501120463400250640ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_LINUX_USB_INTERFACE_H #define AFTL_MTP_BACKEND_LINUX_USB_INTERFACE_H #include #include #include //remove me later, when Endpoint.h will be available in all ports #include namespace mtp { namespace usb { class Endpoint; DECLARE_PTR(Endpoint); class Configuration; DECLARE_PTR(Configuration); class Interface { std::string _path; std::vector _endpoints; u8 _class; u8 _subclass; int _index; public: Interface(int index, const std::string &path); u8 GetClass() const { return _class; } u8 GetSubclass() const { return _subclass; } int GetIndex() const { return _index; } EndpointPtr GetEndpoint(int idx) const { return _endpoints.at(idx); } int GetEndpointsCount() const { return _endpoints.size(); } }; DECLARE_PTR(Interface); }} #endif android-file-transfer-linux-4.5/mtp/backend/linux/usb/linux/000077500000000000000000000000001501120463400241505ustar00rootroot00000000000000android-file-transfer-linux-4.5/mtp/backend/linux/usb/linux/magic.h000066400000000000000000000054371501120463400254120ustar00rootroot00000000000000#ifndef __LINUX_MAGIC_H__ #define __LINUX_MAGIC_H__ #define ADFS_SUPER_MAGIC 0xadf5 #define AFFS_SUPER_MAGIC 0xadff #define AFS_SUPER_MAGIC 0x5346414F #define AUTOFS_SUPER_MAGIC 0x0187 #define CODA_SUPER_MAGIC 0x73757245 #define CRAMFS_MAGIC 0x28cd3d45 /* some random number */ #define CRAMFS_MAGIC_WEND 0x453dcd28 /* magic number with the wrong endianess */ #define DEBUGFS_MAGIC 0x64626720 #define SECURITYFS_MAGIC 0x73636673 #define SELINUX_MAGIC 0xf97cff8c #define SMACK_MAGIC 0x43415d53 /* "SMAC" */ #define RAMFS_MAGIC 0x858458f6 /* some random number */ #define TMPFS_MAGIC 0x01021994 #define HUGETLBFS_MAGIC 0x958458f6 /* some random number */ #define SQUASHFS_MAGIC 0x73717368 #define ECRYPTFS_SUPER_MAGIC 0xf15f #define EFS_SUPER_MAGIC 0x414A53 #define EXT2_SUPER_MAGIC 0xEF53 #define EXT3_SUPER_MAGIC 0xEF53 #define XENFS_SUPER_MAGIC 0xabba1974 #define EXT4_SUPER_MAGIC 0xEF53 #define BTRFS_SUPER_MAGIC 0x9123683E #define NILFS_SUPER_MAGIC 0x3434 #define F2FS_SUPER_MAGIC 0xF2F52010 #define HPFS_SUPER_MAGIC 0xf995e849 #define ISOFS_SUPER_MAGIC 0x9660 #define JFFS2_SUPER_MAGIC 0x72b6 #define PSTOREFS_MAGIC 0x6165676C #define EFIVARFS_MAGIC 0xde5e81e4 #define HOSTFS_SUPER_MAGIC 0x00c0ffee #define MINIX_SUPER_MAGIC 0x137F /* minix v1 fs, 14 char names */ #define MINIX_SUPER_MAGIC2 0x138F /* minix v1 fs, 30 char names */ #define MINIX2_SUPER_MAGIC 0x2468 /* minix v2 fs, 14 char names */ #define MINIX2_SUPER_MAGIC2 0x2478 /* minix v2 fs, 30 char names */ #define MINIX3_SUPER_MAGIC 0x4d5a /* minix v3 fs, 60 char names */ #define MSDOS_SUPER_MAGIC 0x4d44 /* MD */ #define NCP_SUPER_MAGIC 0x564c /* Guess, what 0x564c is :-) */ #define NFS_SUPER_MAGIC 0x6969 #define OPENPROM_SUPER_MAGIC 0x9fa1 #define QNX4_SUPER_MAGIC 0x002f /* qnx4 fs detection */ #define QNX6_SUPER_MAGIC 0x68191122 /* qnx6 fs detection */ #define REISERFS_SUPER_MAGIC 0x52654973 /* used by gcc */ /* used by file system utilities that look at the superblock, etc. */ #define REISERFS_SUPER_MAGIC_STRING "ReIsErFs" #define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" #define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" #define SMB_SUPER_MAGIC 0x517B #define CGROUP_SUPER_MAGIC 0x27e0eb #define STACK_END_MAGIC 0x57AC6E9D #define V9FS_MAGIC 0x01021997 #define BDEVFS_MAGIC 0x62646576 #define BINFMTFS_MAGIC 0x42494e4d #define DEVPTS_SUPER_MAGIC 0x1cd1 #define FUTEXFS_SUPER_MAGIC 0xBAD1DEA #define PIPEFS_MAGIC 0x50495045 #define PROC_SUPER_MAGIC 0x9fa0 #define SOCKFS_MAGIC 0x534F434B #define SYSFS_MAGIC 0x62656572 #define USBDEVICE_SUPER_MAGIC 0x9fa2 #define MTD_INODE_FS_MAGIC 0x11307854 #define ANON_INODE_FS_MAGIC 0x09041934 #define BTRFS_TEST_MAGIC 0x73727279 #define NSFS_MAGIC 0x6e736673 #endif /* __LINUX_MAGIC_H__ */ android-file-transfer-linux-4.5/mtp/backend/linux/usb/linux/types.h000066400000000000000000000002541501120463400254660ustar00rootroot00000000000000#ifndef LINUX_TYPES_STUB_H #define LINUX_TYPES_STUB_H #include typedef uint8_t __u8; typedef uint16_t __u16; typedef uint32_t __u32; #define __user #endif android-file-transfer-linux-4.5/mtp/backend/linux/usb/linux/usbdevice_fs.h000066400000000000000000000201521501120463400267620ustar00rootroot00000000000000/*****************************************************************************/ /* * usbdevice_fs.h -- USB device file system. * * Copyright (C) 2000 * Thomas Sailer (sailer@ife.ee.ethz.ch) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * History: * 0.1 04.01.2000 Created */ /*****************************************************************************/ #ifndef _UAPI_LINUX_USBDEVICE_FS_H #define _UAPI_LINUX_USBDEVICE_FS_H #include "types.h" #include "magic.h" /* --------------------------------------------------------------------- */ /* usbdevfs ioctl codes */ struct usbdevfs_ctrltransfer { __u8 bRequestType; __u8 bRequest; __u16 wValue; __u16 wIndex; __u16 wLength; __u32 timeout; /* in milliseconds */ void __user *data; }; struct usbdevfs_bulktransfer { unsigned int ep; unsigned int len; unsigned int timeout; /* in milliseconds */ void __user *data; }; struct usbdevfs_setinterface { unsigned int interface; unsigned int altsetting; }; struct usbdevfs_disconnectsignal { unsigned int signr; void __user *context; }; #define USBDEVFS_MAXDRIVERNAME 255 struct usbdevfs_getdriver { unsigned int interface; char driver[USBDEVFS_MAXDRIVERNAME + 1]; }; struct usbdevfs_connectinfo { unsigned int devnum; unsigned char slow; }; struct usbdevfs_conninfo_ex { __u32 size; /* Size of the structure from the kernel's */ /* point of view. Can be used by userspace */ /* to determine how much data can be */ /* used/trusted. */ __u32 busnum; /* USB bus number, as enumerated by the */ /* kernel, the device is connected to. */ __u32 devnum; /* Device address on the bus. */ __u32 speed; /* USB_SPEED_* constants from ch9.h */ __u8 num_ports; /* Number of ports the device is connected */ /* to on the way to the root hub. It may */ /* be bigger than size of 'ports' array so */ /* userspace can detect overflows. */ __u8 ports[7]; /* List of ports on the way from the root */ /* hub to the device. Current limit in */ /* USB specification is 7 tiers (root hub, */ /* 5 intermediate hubs, device), which */ /* gives at most 6 port entries. */ }; #define USBDEVFS_URB_SHORT_NOT_OK 0x01 #define USBDEVFS_URB_ISO_ASAP 0x02 #define USBDEVFS_URB_BULK_CONTINUATION 0x04 #define USBDEVFS_URB_NO_FSBR 0x20 /* Not used */ #define USBDEVFS_URB_ZERO_PACKET 0x40 #define USBDEVFS_URB_NO_INTERRUPT 0x80 #define USBDEVFS_URB_TYPE_ISO 0 #define USBDEVFS_URB_TYPE_INTERRUPT 1 #define USBDEVFS_URB_TYPE_CONTROL 2 #define USBDEVFS_URB_TYPE_BULK 3 struct usbdevfs_iso_packet_desc { unsigned int length; unsigned int actual_length; unsigned int status; }; struct usbdevfs_urb { unsigned char type; unsigned char endpoint; int status; unsigned int flags; void __user *buffer; int buffer_length; int actual_length; int start_frame; union { int number_of_packets; /* Only used for isoc urbs */ unsigned int stream_id; /* Only used with bulk streams */ }; int error_count; unsigned int signr; /* signal to be sent on completion, or 0 if none should be sent. */ void __user *usercontext; struct usbdevfs_iso_packet_desc iso_frame_desc[0]; }; /* ioctls for talking directly to drivers */ struct usbdevfs_ioctl { int ifno; /* interface 0..N ; negative numbers reserved */ int ioctl_code; /* MUST encode size + direction of data so the * macros in give correct values */ void __user *data; /* param buffer (in, or out) */ }; /* You can do most things with hubs just through control messages, * except find out what device connects to what port. */ struct usbdevfs_hub_portinfo { char nports; /* number of downstream ports in this hub */ char port [127]; /* e.g. port 3 connects to device 27 */ }; /* System and bus capability flags */ #define USBDEVFS_CAP_ZERO_PACKET 0x01 #define USBDEVFS_CAP_BULK_CONTINUATION 0x02 #define USBDEVFS_CAP_NO_PACKET_SIZE_LIM 0x04 #define USBDEVFS_CAP_BULK_SCATTER_GATHER 0x08 #define USBDEVFS_CAP_REAP_AFTER_DISCONNECT 0x10 #define USBDEVFS_CAP_MMAP 0x20 #define USBDEVFS_CAP_DROP_PRIVILEGES 0x40 #define USBDEVFS_CAP_CONNINFO_EX 0x80 #define USBDEVFS_CAP_SUSPEND 0x100 /* USBDEVFS_DISCONNECT_CLAIM flags & struct */ /* disconnect-and-claim if the driver matches the driver field */ #define USBDEVFS_DISCONNECT_CLAIM_IF_DRIVER 0x01 /* disconnect-and-claim except when the driver matches the driver field */ #define USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER 0x02 struct usbdevfs_disconnect_claim { unsigned int interface; unsigned int flags; char driver[USBDEVFS_MAXDRIVERNAME + 1]; }; struct usbdevfs_streams { unsigned int num_streams; /* Not used by USBDEVFS_FREE_STREAMS */ unsigned int num_eps; unsigned char eps[0]; }; /* * USB_SPEED_* values returned by USBDEVFS_GET_SPEED are defined in * linux/usb/ch9.h */ #define USBDEVFS_CONTROL _IOWR('U', 0, struct usbdevfs_ctrltransfer) #define USBDEVFS_CONTROL32 _IOWR('U', 0, struct usbdevfs_ctrltransfer32) #define USBDEVFS_BULK _IOWR('U', 2, struct usbdevfs_bulktransfer) #define USBDEVFS_BULK32 _IOWR('U', 2, struct usbdevfs_bulktransfer32) #define USBDEVFS_RESETEP _IOR('U', 3, unsigned int) #define USBDEVFS_SETINTERFACE _IOR('U', 4, struct usbdevfs_setinterface) #define USBDEVFS_SETCONFIGURATION _IOR('U', 5, unsigned int) #define USBDEVFS_GETDRIVER _IOW('U', 8, struct usbdevfs_getdriver) #define USBDEVFS_SUBMITURB _IOR('U', 10, struct usbdevfs_urb) #define USBDEVFS_SUBMITURB32 _IOR('U', 10, struct usbdevfs_urb32) #define USBDEVFS_DISCARDURB _IO('U', 11) #define USBDEVFS_REAPURB _IOW('U', 12, void *) #define USBDEVFS_REAPURB32 _IOW('U', 12, __u32) #define USBDEVFS_REAPURBNDELAY _IOW('U', 13, void *) #define USBDEVFS_REAPURBNDELAY32 _IOW('U', 13, __u32) #define USBDEVFS_DISCSIGNAL _IOR('U', 14, struct usbdevfs_disconnectsignal) #define USBDEVFS_DISCSIGNAL32 _IOR('U', 14, struct usbdevfs_disconnectsignal32) #define USBDEVFS_CLAIMINTERFACE _IOR('U', 15, unsigned int) #define USBDEVFS_RELEASEINTERFACE _IOR('U', 16, unsigned int) #define USBDEVFS_CONNECTINFO _IOW('U', 17, struct usbdevfs_connectinfo) #define USBDEVFS_IOCTL _IOWR('U', 18, struct usbdevfs_ioctl) #define USBDEVFS_IOCTL32 _IOWR('U', 18, struct usbdevfs_ioctl32) #define USBDEVFS_HUB_PORTINFO _IOR('U', 19, struct usbdevfs_hub_portinfo) #define USBDEVFS_RESET _IO('U', 20) #define USBDEVFS_CLEAR_HALT _IOR('U', 21, unsigned int) #define USBDEVFS_DISCONNECT _IO('U', 22) #define USBDEVFS_CONNECT _IO('U', 23) #define USBDEVFS_CLAIM_PORT _IOR('U', 24, unsigned int) #define USBDEVFS_RELEASE_PORT _IOR('U', 25, unsigned int) #define USBDEVFS_GET_CAPABILITIES _IOR('U', 26, __u32) #define USBDEVFS_DISCONNECT_CLAIM _IOR('U', 27, struct usbdevfs_disconnect_claim) #define USBDEVFS_ALLOC_STREAMS _IOR('U', 28, struct usbdevfs_streams) #define USBDEVFS_FREE_STREAMS _IOR('U', 29, struct usbdevfs_streams) #define USBDEVFS_DROP_PRIVILEGES _IOW('U', 30, __u32) #define USBDEVFS_GET_SPEED _IO('U', 31) /* * Returns struct usbdevfs_conninfo_ex; length is variable to allow * extending size of the data returned. */ #define USBDEVFS_CONNINFO_EX(len) _IOC(_IOC_READ, 'U', 32, len) #define USBDEVFS_FORBID_SUSPEND _IO('U', 33) #define USBDEVFS_ALLOW_SUSPEND _IO('U', 34) #define USBDEVFS_WAIT_FOR_RESUME _IO('U', 35) #endif /* _UAPI_LINUX_USBDEVICE_FS_H */ android-file-transfer-linux-4.5/mtp/backend/posix/000077500000000000000000000000001501120463400222235ustar00rootroot00000000000000android-file-transfer-linux-4.5/mtp/backend/posix/Exception.cpp000066400000000000000000000030311501120463400246620ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include namespace mtp { namespace posix { Exception::Exception(const std::string &what) throw() : std::runtime_error(what + ": " + GetErrorMessage(errno)) { } Exception::Exception(const std::string &what, int returnCode) throw() : std::runtime_error(what + ": " + GetErrorMessage(returnCode)) { } std::string Exception::GetErrorMessage(int returnCode) { char buf[1024]; #if defined(_GNU_SOURCE) && defined(__GLIBC__) std::string text(strerror_r(returnCode, buf, sizeof(buf))); #else int r = strerror_r(returnCode, buf, sizeof(buf)); std::string text(r == 0? buf: "strerror_r() failed"); #endif return text; } }} android-file-transfer-linux-4.5/mtp/backend/posix/Exception.h000066400000000000000000000023111501120463400243270ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_POSIX_EXCEPTION_H #define AFTL_MTP_BACKEND_POSIX_EXCEPTION_H #include namespace mtp { namespace posix { class Exception : public std::runtime_error { public: Exception(const std::string &what) throw(); Exception(const std::string &what, int retCode) throw(); static std::string GetErrorMessage(int retCode); }; }} #endif android-file-transfer-linux-4.5/mtp/backend/posix/FileHandler.cpp000066400000000000000000000017321501120463400251070ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include namespace mtp { namespace posix { FileHandler::~FileHandler() { close(_fd); } }} android-file-transfer-linux-4.5/mtp/backend/posix/FileHandler.h000066400000000000000000000022411501120463400245500ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_BACKEND_POSIX_FILEHANDLER_H #define AFTL_MTP_BACKEND_POSIX_FILEHANDLER_H #include #include namespace mtp { namespace posix { class FileHandler : Noncopyable { int _fd; public: FileHandler(int fd): _fd(fd) { } ~FileHandler(); int Get() const { return _fd; } }; }} #endif android-file-transfer-linux-4.5/mtp/function_invoker.h000066400000000000000000000032651501120463400232330ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_FUNCTION_INVOKER_H #define AFTL_MTP_FUNCTION_INVOKER_H #include namespace mtp { namespace impl { template struct seq {}; template struct gens : gens {}; template struct gens<0, S...>{ typedef seq type; }; template struct function_invoker { std::tuple params; std::function func; R dispatch() { return call(typename gens::type()); } template R call(seq) { return func(std::get(params) ...); } }; } template void invoke(const std::function &func, const TupleType &tuple) { impl::function_invoker invoker = { tuple, func }; invoker.dispatch(); } } #endif android-file-transfer-linux-4.5/mtp/log.cpp000066400000000000000000000034631501120463400207650ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include namespace mtp { bool g_debug = false; void HexDump(std::stringstream & ss, const std::string &prefix, size_t size, InputStream & is) { ss << prefix << "[" << size << "]:\n"; size_t i; std::string chars; chars.reserve(16); for(i = 0; i < size; ++i) { bool first = ((i & 0xf) == 0); bool last = ((i & 0xf) == 0x0f); if (first) ss << hex(i, 8) << ": "; u8 value = is.Read8(); ss << hex(value, 2); chars.push_back(value < 0x20 || value >= 0x7f? '.': value); if (last) { ss << " " << chars << "\n"; chars.clear(); } else ss << " "; } if (chars.size()) { ss << std::string((size_t)(16 - chars.size()) * 3, ' ') << chars << "\n"; } } void HexDump(const std::string &prefix, const ByteArray &data, bool force) { if (!g_debug && !force) return; std::stringstream ss; InputStream is(data); HexDump(ss, prefix, data.size(), is); error(ss.str()); } } android-file-transfer-linux-4.5/mtp/log.h000066400000000000000000000063041501120463400204270ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_LOG_H #define AFTL_MTP_LOG_H #include #include #include #include #include #include //I love libstdc++! inline std::ostream & operator << (std::ostream & stream, unsigned char v) { stream << (mtp::u16)v; return stream; } namespace mtp { class InputStream; inline std::ostream & operator << (std::ostream & stream, unsigned char v) { stream << (u16)v; return stream; } namespace impl { template struct Format { T Value; unsigned Width; Format(const T & value, unsigned width = 0): Value(value), Width(width) { } template Stream & operator >> (Stream & stream) const { std::ios::fmtflags oldFlags = stream.flags(); char oldFill = stream.fill(); if (Hex) stream << std::setw(Width) << std::setfill('0') << std::hex << Value; else stream << std::setw(Width) << std::setfill(' ') << Value; stream.flags(oldFlags); stream.fill(oldFill); return stream; } std::string ToString() const { std::stringstream ss; (*this) >> ss; return ss.str(); } }; } template Stream & operator << (Stream &stream, const impl::Format &format) { format >> stream; return stream; } template impl::Format hex(const T & value, unsigned width = 0) { return impl::Format(value, width); } template impl::Format width(const T & value, unsigned width) { return impl::Format(value, width); } inline void print() { std::cout << std::endl; } template void print(const ValueType & value, Args ... args) { std::cout << value; print(args...); } extern bool g_debug; inline void error() { std::cerr << std::endl; } template void error(const ValueType & value, Args ... args) { std::cerr << value; error(args...); } template void debug(const ValueType & value, Args ... args) { if (g_debug) error(value, args...); } //! output hex dump to debug channel void HexDump(const std::string &prefix, const ByteArray &data, bool force = false); void HexDump(std::stringstream & ss, const std::string &prefix, size_t size, InputStream & is); } #endif android-file-transfer-linux-4.5/mtp/make_function.h000066400000000000000000000042641501120463400224730ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_MAKE_FUNCTION_H #define AFTL_MTP_MAKE_FUNCTION_H #include namespace mtp { template struct remove_class { }; template struct remove_class { using type = R(A...); }; template struct remove_class { using type = R(A...); }; template struct remove_class { using type = R(A...); }; template struct remove_class { using type = R(A...); }; template struct get_signature_impl { using type = typename remove_class< decltype(&std::remove_reference::type::operator())>::type; }; template struct get_signature_impl { using type = R(A...); }; template struct get_signature_impl { using type = R(A...); }; template struct get_signature_impl { using type = R(A...); }; template using get_signature = typename get_signature_impl::type; template using make_function_type = std::function>; template make_function_type make_function(F &&f) { return make_function_type(std::forward(f)); } } #endif android-file-transfer-linux-4.5/mtp/metadata/000077500000000000000000000000001501120463400212525ustar00rootroot00000000000000android-file-transfer-linux-4.5/mtp/metadata/Library.cpp000066400000000000000000000317331501120463400233710ustar00rootroot00000000000000#include #include #include #include #include #include namespace mtp { namespace { const std::string UknownArtist ("UknownArtist"); const std::string UknownAlbum ("UknownAlbum"); const std::string VariousArtists ("VariousArtists"); } Library::NameToObjectIdMap Library::ListAssociations(ObjectId parentId) { NameToObjectIdMap list; ByteArray data = _session->GetObjectPropertyList(parentId, ObjectFormat::Association, ObjectProperty::ObjectFilename, 0, 1); ObjectPropertyListParser parser; parser.Parse(data, [&](ObjectId id, ObjectProperty property, const std::string &name) { list.insert(std::make_pair(name, id)); }); return list; } ObjectId Library::GetOrCreate(ObjectId parentId, const std::string &name) { auto objects = _session->GetObjectHandles(_storage, mtp::ObjectFormat::Association, parentId); for (auto id : objects.ObjectHandles) { auto oname = _session->GetObjectStringProperty(id, ObjectProperty::ObjectFilename); if (name == oname) return id; } return _session->CreateDirectory(name, parentId, _storage).ObjectId; } Library::Library(const mtp::SessionPtr & session, ProgressReporter && reporter): _session(session) { auto storages = _session->GetStorageIDs(); if (storages.StorageIDs.empty()) throw std::runtime_error("no storages found"); u64 progress = 0, total = 0; if (reporter) reporter(State::Initialising, progress, total); _artistSupported = _session->GetDeviceInfo().Supports(ObjectFormat::Artist); debug("device supports ObjectFormat::Artist: ", _artistSupported? "yes": "no"); { auto propsSupported = _session->GetObjectPropertiesSupported(ObjectFormat::AbstractAudioAlbum); _albumDateAuthoredSupported = propsSupported.Supports(ObjectProperty::DateAuthored); _albumCoverSupported = propsSupported.Supports(ObjectProperty::RepresentativeSampleData); mtp::debug("abstract album supports date authored: ", _albumDateAuthoredSupported, ", cover: ", _albumCoverSupported); } _storage = storages.StorageIDs[0]; //picking up first storage. //zune fails to create artist/album without storage id { ByteArray data = _session->GetObjectPropertyList(Session::Root, ObjectFormat::Association, ObjectProperty::ObjectFilename, 0, 1); ObjectStringPropertyListParser::Parse(data, [&](ObjectId id, ObjectProperty property, const std::string &name) { if (name == "Artists") _artistsFolder = id; else if (name == "Albums") _albumsFolder = id; else if (name == "Music") _musicFolder = id; }); } if (_artistSupported && _artistsFolder == ObjectId()) _artistsFolder = _session->CreateDirectory("Artists", Session::Root, _storage).ObjectId; if (_albumsFolder == ObjectId()) _albumsFolder = _session->CreateDirectory("Albums", Session::Root, _storage).ObjectId; if (_musicFolder == ObjectId()) _musicFolder = _session->CreateDirectory("Music", Session::Root, _storage).ObjectId; debug("artists folder: ", _artistsFolder != ObjectId()? _artistsFolder.Id: 0); debug("albums folder: ", _albumsFolder.Id); debug("music folder: ", _musicFolder.Id); auto musicFolders = ListAssociations(_musicFolder); using namespace mtp; ByteArray artists, albums; if (_artistSupported) { debug("getting artists..."); if (reporter) reporter(State::QueryingArtists, progress, total); artists = _session->GetObjectPropertyList(Session::Root, mtp::ObjectFormat::Artist, mtp::ObjectProperty::Name, 0, 1); HexDump("artists", artists); total += ObjectStringPropertyListParser::GetSize(artists); } { debug("getting albums..."); if (reporter) reporter(State::QueryingAlbums, progress, total); albums = _session->GetObjectPropertyList(Session::Root, mtp::ObjectFormat::AbstractAudioAlbum, mtp::ObjectProperty::Name, 0, 1); HexDump("albums", artists); total += ObjectStringPropertyListParser::GetSize(albums); } if (_artistSupported) { if (reporter) reporter(State::LoadingArtists, progress, total); ObjectStringPropertyListParser::Parse(artists, [&](ObjectId id, ObjectProperty property, const std::string &name) { debug("artist: ", name, "\t", id.Id); auto artist = std::make_shared(); artist->Id = id; artist->Name = name; auto it = musicFolders.find(name); if (it != musicFolders.end()) artist->MusicFolderId = it->second; else artist->MusicFolderId = _session->CreateDirectory(name, _musicFolder, _storage).ObjectId; _artists.insert(std::make_pair(name, artist)); if (reporter) reporter(State::LoadingArtists, ++progress, total); }); } if (reporter) reporter(State::LoadingAlbums, progress, total); std::unordered_map albumFolders; ObjectStringPropertyListParser::Parse(albums, [&](ObjectId id, ObjectProperty property, const std::string &name) { auto artistName = _session->GetObjectStringProperty(id, ObjectProperty::Artist); std::string albumDate; if (_albumDateAuthoredSupported) albumDate = _session->GetObjectStringProperty(id, ObjectProperty::DateAuthored); auto artist = GetArtist(artistName); if (!artist) artist = CreateArtist(artistName); debug("album: ", artistName, " -- ", name, "\t", id.Id, "\t", albumDate); auto album = std::make_shared(); album->Name = name; album->Artist = artist; album->Id = id; album->Year = !albumDate.empty()? ConvertDateTime(albumDate): 0; if (albumFolders.find(artist) == albumFolders.end()) { albumFolders[artist] = ListAssociations(artist->MusicFolderId); } auto it = albumFolders.find(artist); if (it == albumFolders.end()) throw std::runtime_error("no iterator after insert, internal error"); const auto & albums = it->second; auto alit = albums.find(name); if (alit != albums.end()) album->MusicFolderId = alit->second; else album->MusicFolderId = _session->CreateDirectory(name, artist->MusicFolderId, _storage).ObjectId; _albums.insert(std::make_pair(std::make_pair(artist, name), album)); if (reporter) reporter(State::LoadingAlbums, ++progress, total); }); if (reporter) reporter(State::Loaded, progress, total); } Library::~Library() { } Library::ArtistPtr Library::GetArtist(std::string name) { if (name.empty()) name = UknownArtist; auto it = _artists.find(name); return it != _artists.end()? it->second: ArtistPtr(); } Library::ArtistPtr Library::CreateArtist(std::string name) { if (name.empty()) name = UknownArtist; auto artist = std::make_shared(); artist->Name = name; artist->MusicFolderId = GetOrCreate(_musicFolder, name); if (_artistSupported) { ByteArray propList; OutputStream os(propList); os.Write32(2); //number of props os.Write32(0); //object handle os.Write16(static_cast(ObjectProperty::Name)); os.Write16(static_cast(DataTypeCode::String)); os.WriteString(name); os.Write32(0); //object handle os.Write16(static_cast(ObjectProperty::ObjectFilename)); os.Write16(static_cast(DataTypeCode::String)); os.WriteString(name + ".art"); auto response = _session->SendObjectPropList(_storage, _artistsFolder, ObjectFormat::Artist, 0, propList); artist->Id = response.ObjectId; } _artists.insert(std::make_pair(name, artist)); return artist; } Library::AlbumPtr Library::GetAlbum(const ArtistPtr & artist, std::string name) { if (name.empty()) name = UknownAlbum; auto it = _albums.find(std::make_pair(artist, name)); return it != _albums.end()? it->second: AlbumPtr(); } Library::AlbumPtr Library::CreateAlbum(const ArtistPtr & artist, std::string name, int year) { if (!artist) throw std::runtime_error("artists is required"); if (name.empty()) name = UknownAlbum; ByteArray propList; OutputStream os(propList); bool sendYear = year != 0 && _albumDateAuthoredSupported; os.Write32(3 + (sendYear? 1: 0)); //number of props if (_artistSupported) { os.Write32(0); //object handle os.Write16(static_cast(ObjectProperty::ArtistId)); os.Write16(static_cast(DataTypeCode::Uint32)); os.Write32(artist->Id.Id); } else { os.Write32(0); //object handle os.Write16(static_cast(ObjectProperty::Artist)); os.Write16(static_cast(DataTypeCode::String)); os.WriteString(artist->Name); } os.Write32(0); //object handle os.Write16(static_cast(ObjectProperty::Name)); os.Write16(static_cast(DataTypeCode::String)); os.WriteString(name); os.Write32(0); //object handle os.Write16(static_cast(ObjectProperty::ObjectFilename)); os.Write16(static_cast(DataTypeCode::String)); os.WriteString(artist->Name + "--" + name + ".alb"); if (sendYear) { os.Write32(0); //object handle os.Write16(static_cast(ObjectProperty::DateAuthored)); os.Write16(static_cast(DataTypeCode::String)); os.WriteString(ConvertYear(year)); } auto album = std::make_shared(); album->Artist = artist; album->Name = name; album->Year = year; album->MusicFolderId = GetOrCreate(artist->MusicFolderId, name); auto response = _session->SendObjectPropList(_storage, _albumsFolder, ObjectFormat::AbstractAudioAlbum, 0, propList); album->Id = response.ObjectId; _albums.insert(std::make_pair(std::make_pair(artist, name), album)); return album; } bool Library::HasTrack(const AlbumPtr & album, const std::string &name, int trackIndex) { if (!album) return false; LoadRefs(album); auto & tracks = album->Tracks; auto range = tracks.equal_range(name); for(auto i = range.first; i != range.second; ++i) { if (i->second == trackIndex) return true; } return false; } Library::NewTrackInfo Library::CreateTrack(const ArtistPtr & artist, const AlbumPtr & album, ObjectFormat type, std::string name, const std::string & genre, int trackIndex, const std::string &filename, size_t size) { ByteArray propList; OutputStream os(propList); os.Write32(3 + (!genre.empty()? 1: 0) + (trackIndex? 1: 0)); //number of props if (_artistSupported) { os.Write32(0); //object handle os.Write16(static_cast(ObjectProperty::ArtistId)); os.Write16(static_cast(DataTypeCode::Uint32)); os.Write32(artist->Id.Id); } else { os.Write32(0); //object handle os.Write16(static_cast(ObjectProperty::Artist)); os.Write16(static_cast(DataTypeCode::String)); os.WriteString(artist->Name); } os.Write32(0); //object handle os.Write16(static_cast(ObjectProperty::Name)); os.Write16(static_cast(DataTypeCode::String)); os.WriteString(name); if (trackIndex) { os.Write32(0); //object handle os.Write16(static_cast(ObjectProperty::Track)); os.Write16(static_cast(DataTypeCode::Uint16)); os.Write16(trackIndex); } if (!genre.empty()) { os.Write32(0); //object handle os.Write16(static_cast(ObjectProperty::Genre)); os.Write16(static_cast(DataTypeCode::String)); os.WriteString(genre); } os.Write32(0); //object handle os.Write16(static_cast(ObjectProperty::ObjectFilename)); os.Write16(static_cast(DataTypeCode::String)); os.WriteString(filename); auto response = _session->SendObjectPropList(_storage, album->MusicFolderId, type, size, propList); NewTrackInfo ti; ti.Id = response.ObjectId; ti.Name = name; ti.Index = trackIndex; return ti; } void Library::LoadRefs(AlbumPtr album) { if (!album || album->RefsLoaded) return; auto refs = _session->GetObjectReferences(album->Id).ObjectHandles; std::copy(refs.begin(), refs.end(), std::inserter(album->Refs, album->Refs.begin())); for(auto trackId : refs) { auto name = _session->GetObjectStringProperty(trackId, ObjectProperty::Name); auto index = _session->GetObjectIntegerProperty(trackId, ObjectProperty::Track); debug("[", index, "]: ", name); album->Tracks.insert(std::make_pair(name, index)); } album->RefsLoaded = true; } void Library::AddTrack(AlbumPtr album, const NewTrackInfo & ti) { if (!album) return; LoadRefs(album); auto & refs = album->Refs; auto & tracks = album->Tracks; msg::ObjectHandles handles; std::copy(refs.begin(), refs.end(), std::back_inserter(handles.ObjectHandles)); handles.ObjectHandles.push_back(ti.Id); _session->SetObjectReferences(album->Id, handles); refs.insert(ti.Id); tracks.insert(std::make_pair(ti.Name, ti.Index)); } void Library::AddCover(AlbumPtr album, const mtp::ByteArray &data) { if (!album || !_albumCoverSupported) return; mtp::debug("sending ", data.size(), " bytes of album cover..."); _session->SetObjectPropertyAsArray(album->Id, mtp::ObjectProperty::RepresentativeSampleData, data); } bool Library::Supported(const mtp::SessionPtr & session) { auto & gdi = session->GetDeviceInfo(); return gdi.Supports(OperationCode::GetObjectPropList) && gdi.Supports(OperationCode::SendObjectPropList) && gdi.Supports(OperationCode::SetObjectReferences) && gdi.Supports(ObjectFormat::AbstractAudioAlbum); ; } } android-file-transfer-linux-4.5/mtp/metadata/Library.h000066400000000000000000000053731501120463400230370ustar00rootroot00000000000000#ifndef AFTL_MTP_METADATA_LIBRARY_H #define AFTL_MTP_METADATA_LIBRARY_H #include #include #include #include #include #include #include #include #include namespace mtp { class Session; DECLARE_PTR(Session); class Library { SessionPtr _session; StorageId _storage; public: enum struct State { Initialising, QueryingArtists, LoadingArtists, QueryingAlbums, LoadingAlbums, Loaded }; using ProgressReporter = std::function; struct Artist { ObjectId Id; ObjectId MusicFolderId; std::string Name; }; DECLARE_PTR(Artist); struct Album { ObjectId Id; ObjectId MusicFolderId; ArtistPtr Artist; std::string Name; time_t Year = 0; bool RefsLoaded = false; void LoadRefs(); std::unordered_set Refs; std::unordered_multimap Tracks; }; DECLARE_PTR(Album); struct NewTrackInfo { ObjectId Id; std::string Name; int Index; }; private: ObjectId _artistsFolder; ObjectId _albumsFolder; ObjectId _musicFolder; bool _artistSupported; bool _albumDateAuthoredSupported; bool _albumCoverSupported; using ArtistMap = std::unordered_map; ArtistMap _artists; using AlbumKey = std::pair; struct AlbumKeyHash { size_t operator() (const AlbumKey & key) const { return std::hash()(key.first) + std::hash()(key.second); }}; using AlbumMap = std::unordered_map; AlbumMap _albums; using NameToObjectIdMap = std::unordered_map; NameToObjectIdMap ListAssociations(ObjectId parentId); ObjectId GetOrCreate(ObjectId parentId, const std::string &name); void LoadRefs(AlbumPtr album); public: Library(const mtp::SessionPtr & session, ProgressReporter && reporter = ProgressReporter()); ~Library(); static bool Supported(const mtp::SessionPtr & session); //search by Metadata? ArtistPtr GetArtist(std::string name); ArtistPtr CreateArtist(std::string name); AlbumPtr GetAlbum(const ArtistPtr & artist, std::string name); AlbumPtr CreateAlbum(const ArtistPtr & artist, std::string name, int year); bool HasTrack(const AlbumPtr & album, const std::string &name, int trackIndex); NewTrackInfo CreateTrack(const ArtistPtr & artist, const AlbumPtr & album, ObjectFormat type, std::string name, const std::string & genre, int trackIndex, const std::string &filename, size_t size); void AddTrack(AlbumPtr album, const NewTrackInfo &ti); void AddCover(AlbumPtr album, const mtp::ByteArray &data); }; DECLARE_PTR(Library); } #endif android-file-transfer-linux-4.5/mtp/metadata/Metadata.cpp000066400000000000000000000034021501120463400234750ustar00rootroot00000000000000#include #include #ifdef HAVE_TAGLIB # include # include # include #endif namespace mtp { #ifdef HAVE_TAGLIB MetadataPtr Metadata::Read(const std::string & path) { TagLib::FileRef f(path.c_str()); auto tag = f.tag(); if (f.isNull() || !tag) return nullptr; auto meta = std::make_shared(); auto artist = tag->artist(); const auto & props = tag->properties(); auto album_it = props.find("ALBUMARTIST"); if (album_it == props.end()) album_it = props.find("ALBUM ARTIST"); if (album_it == props.end()) album_it = props.find("MUSICBRAINZ_ALBUMARTIST"); if (album_it != props.end()) artist = album_it->second.toString(); meta->Title = tag->title().to8Bit(true); meta->Artist = artist.to8Bit(true); meta->Album = tag->album().to8Bit(true); meta->Genre = tag->genre().to8Bit(true); meta->Year = tag->year(); meta->Track = tag->track(); #if TAGLIB_MAJOR_VERSION >= 2 for(auto & props : tag->complexProperties("PICTURE")) { auto &picture = meta->Picture; for(auto &kv : props) { auto &name = kv.first; auto &value = kv.second; if (name == "data") { auto data = value.toByteVector(); picture.Data.assign(data.begin(), data.end()); } else if (name == "pictureType") { picture.Type = value.toString().to8Bit(true); } else if (name == "mimeType") { picture.MimeType = value.toString().to8Bit(true); } else if (name == "description") { picture.Description = value.toString().to8Bit(true); } else { mtp::debug("unhandled PICTURE property ", name.toCString(true)); } } } #endif return meta; } #else MetadataPtr Metadata::Read(const std::string & path) { return nullptr; } #endif } android-file-transfer-linux-4.5/mtp/metadata/Metadata.h000066400000000000000000000011231501120463400231400ustar00rootroot00000000000000#ifndef AFTL_MTP_METADATA_METADATA_H #define AFTL_MTP_METADATA_METADATA_H #include #include #include #include namespace mtp { struct Metadata; DECLARE_PTR(Metadata); struct MetadataPicture { std::string Type; std::string MimeType; std::string Description; mtp::ByteArray Data; }; struct Metadata { std::string Title; std::string Artist; std::string Album; std::string Genre; unsigned Year; unsigned Track; MetadataPicture Picture; static MetadataPtr Read(const std::string & path); }; } #endif android-file-transfer-linux-4.5/mtp/mtpz/000077500000000000000000000000001501120463400204645ustar00rootroot00000000000000android-file-transfer-linux-4.5/mtp/mtpz/TrustedApp.cpp000066400000000000000000000324571501120463400232760ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #ifdef MTPZ_ENABLED # include # include # include # include # include # include # include # include #endif namespace mtp { std::once_flag crypto_init; TrustedApp::TrustedApp(const SessionPtr & session, const std::string &mtpzDataPath): _session(session), _keys(LoadKeys(mtpzDataPath)) {} TrustedApp::~TrustedApp() { try { _session->GenericOperation(OperationCode::DisableTrustedFilesOperations); } catch(const std::exception& ex) { error("DisableTrustedFilesOperations failed: ", ex.what()); } try { _session->GenericOperation(OperationCode::EndTrustedAppSession); } catch(const std::exception& ex) { error("EndTrustedAppSession failed: ", ex.what()); } } TrustedAppPtr TrustedApp::Create(const SessionPtr & session, const std::string &mtpzDataPath) { return TrustedAppPtr(Probe(session)? new TrustedApp(session, mtpzDataPath): nullptr); } #ifdef MTPZ_ENABLED struct TrustedApp::Keys { ByteArray skey; //session key BIGNUM *exp, *mod, *pkey; RSA * rsa; ByteArray certificate; Keys(): exp(), mod(), pkey(), rsa(RSA_new()) { } void Update() { #if OPENSSL_VERSION_NUMBER < 0x10100000L BN_free(rsa->n); rsa->n = mod; BN_free(rsa->e); rsa->e = exp; BN_free(rsa->d); rsa->d = pkey; #else if (RSA_set0_key(rsa, mod, exp, pkey)) { debug("created RSA key"); } else throw std::runtime_error("failed to create RSA key"); #endif mod = exp = pkey = NULL; } ~Keys() { if (exp) BN_free(exp); if (mod) BN_free(mod); if (pkey) BN_free(pkey); if (rsa) RSA_free(rsa); } static ByteArray HKDF(const u8 * message, size_t messageSize, size_t keySize) { static constexpr size_t blockSize = SHA_DIGEST_LENGTH; size_t blocks = (keySize + blockSize - 1) / blockSize; ByteArray key(blocks * blockSize); ByteArray ctr(messageSize + 4); const auto ctrPtr = std::copy(message, message + messageSize, ctr.data()); u8 * dst = key.data(); for(size_t i = 0; i < blocks; ++i, dst += blockSize) { ctrPtr[0] = i >> 24; ctrPtr[1] = i >> 16; ctrPtr[2] = i >> 8; ctrPtr[3] = i; SHA1(ctr.data(), ctr.size(), dst); } return key; } std::tuple GenerateCertificateMessage() { static const size_t messageSize = 156 + certificate.size(); ByteArray challenge(16); RAND_bytes(challenge.data(), challenge.size()); ByteArray message(messageSize); auto dst = message.data(); *dst++ = 0x02; *dst++ = 0x01; *dst++ = 0x01; *dst++ = 0x00; *dst++ = 0x00; *dst++ = certificate.size() >> 8; *dst++ = certificate.size(); dst = std::copy(certificate.begin(), certificate.end(), dst); *dst++ = challenge.size() >> 8; *dst++ = challenge.size(); dst = std::copy(challenge.begin(), challenge.end(), dst); ByteArray hash(SHA_DIGEST_LENGTH); { ByteArray salt(SHA_DIGEST_LENGTH + 8); SHA1(message.data() + 2, dst - message.data() - 2, salt.data() + 8); SHA1(salt.data(), salt.size(), hash.data()); } ByteArray key = HKDF(hash.data(), hash.size(), 107); //HexDump("key", key); ByteArray signature(RSA_size(rsa)); signature[106] = 1; for(size_t i = 0; i < hash.size(); ++i) signature[i + 107] = hash[i]; for(size_t i = 0; i < 107; ++i) signature[i] ^= key[i]; signature[0] &= 127; signature[127] = 188; //HexDump("signature", signature); *dst++ = 1; *dst++ = 0; *dst++ = signature.size(); if (RSA_private_decrypt(signature.size(), signature.data(), dst, rsa, RSA_NO_PADDING) == -1) throw std::runtime_error("RSA_private_encrypt failed"); return std::make_tuple(challenge, message); } static ByteArray Decrypt(const ByteArray & key, const u8 * src, size_t size) { AES_KEY aesKey; AES_set_decrypt_key(key.data(), key.size() * 8, &aesKey); ByteArray iv(16); ByteArray result(size); AES_cbc_encrypt(src, result.data(), size, &aesKey, iv.data(), 0); return result; } void CMAC(const u8 * key, size_t keySize, const u8 * src, size_t size, u8 *dst) { CMAC_CTX *ctx = CMAC_CTX_new(); if (!ctx) throw std::runtime_error("CMAC_CTX_new failed"); if (!CMAC_Init(ctx, key, keySize, EVP_aes_128_cbc(), NULL)) error("CMAC_Init failed"); if (!CMAC_Update(ctx, src, size)) error("CMAC_Update failed"); size_t len = 0; if (!CMAC_Final(ctx, dst, &len)) error("CMAC_Final failed"); CMAC_CTX_free(ctx); } ByteArray SignResponse(const ByteArray & key) { ByteArray text(16); text[15] = 1; ByteArray message(20); u8 * dst = message.data(); *dst++ = 2; *dst++ = 3; *dst++ = 0; *dst++ = 16; CMAC(key.data(), 16, text.data(), text.size(), dst); return message; } void SignSessionRequest(u32 cmac[4], const ByteArray & key) { u8 signature[16]; CMAC(key.data(), 16, key.data() + 16, 4, signature); //HexDump("signature", ByteArray(signature, signature + 16)); const u8 *src = signature; u32 *dst = cmac; for(size_t i = 0; i < 4; ++i) { u32 value = 0; for(size_t j = 0; j < 4; ++j) value |= static_cast(*src++) << ((3 - j) << 3); *dst++ = value; } debug("secure session enabler: ", hex(cmac[0]), "-", hex(cmac[1]), "-", hex(cmac[2]), "-", hex(cmac[3])); } ByteArray VerifyResponse(const ByteArray & message, const ByteArray & originalChallenge) { #define CHECK_MORE(ARRAY, SIZE) if (src - (ARRAY).data() + static_cast(SIZE) > (ARRAY).size()) \ throw std::runtime_error("input buffer overrun"); const u8 * src = message.data(); CHECK_MORE(message, 4); if (*src++ != 2) throw std::runtime_error("invalid tag"); if (*src++ != 2) throw std::runtime_error("invalid tag"); size_t signatureSize = *src++ << 8; signatureSize += *src++; if (signatureSize < 0x80 || signatureSize != static_cast(RSA_size(rsa))) throw std::runtime_error("invalid signature size"); CHECK_MORE(message, signatureSize); ByteArray signature(signatureSize); if (RSA_private_decrypt(signatureSize, src, signature.data(), rsa, RSA_NO_PADDING) == -1) throw std::runtime_error("RSA_private_decrypt failed"); src += signatureSize; { ByteArray hash = HKDF(signature.data() + 21, 107, 20); for(size_t i = 0; i < 20; ++i) signature[1 + i] ^= hash[i]; ByteArray key = HKDF(signature.data() + 1, 20, 107); for(size_t i = 0; i < 107; ++i) signature[21 + i] ^= key[i]; } //HexDump("signature out", signature); ByteArray key = ByteArray(signature.begin() + 0x70, signature.end()); //HexDump("key", key); CHECK_MORE(message, 4); if (*src++ != 0) throw std::runtime_error("invalid record"); if (*src++ != 0) throw std::runtime_error("invalid record"); size_t payloadSize = *src++ << 8; payloadSize += *src++; CHECK_MORE(message, payloadSize); //debug("payload size ", payloadSize); ByteArray payload = Decrypt(key, src, payloadSize); //HexDump("payload", payload); src = payload.data(); if (*src++ != 1) throw std::runtime_error("decryption failed"); size_t certificateSize = *src++ << 24; certificateSize |= *src++ << 16; certificateSize |= *src++ << 8; certificateSize |= *src++; //debug("certificate size ", certificateSize); CHECK_MORE(payload, certificateSize); src += certificateSize; CHECK_MORE(payload, 2); size_t challengeSize = *src++ << 8; challengeSize |= *src++; //debug("challenge size ", challengeSize); if (challengeSize != originalChallenge.size()) throw std::runtime_error("invalid challenge size"); CHECK_MORE(payload, challengeSize); if (memcmp(src, originalChallenge.data(), challengeSize) != 0) throw std::runtime_error("challenge does not match"); src += challengeSize; //debug("challenge matches..."); CHECK_MORE(payload, 2); challengeSize = *src++ << 8; challengeSize |= *src++; //debug("device challenge size ", challengeSize); CHECK_MORE(payload, challengeSize); src += challengeSize; CHECK_MORE(payload, 3); if (*src++ != 1) throw std::runtime_error("invalid signature"); signatureSize = *src++ << 8; signatureSize |= *src++; //debug("signature size: ", signatureSize); CHECK_MORE(payload, signatureSize); src += signatureSize; CHECK_MORE(payload, 3); if (*src++ != 1) throw std::runtime_error("invalid cmac record"); challengeSize = *src++ << 8; challengeSize |= *src++; //debug("cmac size: ", challengeSize); CHECK_MORE(payload, challengeSize); return ByteArray(src, src + challengeSize); } static u8 FromHex(char ch) { if (ch >= '0' && ch <= '9') return ch - '0'; if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; throw std::runtime_error(std::string("invalid hex character ") + ch); } static ByteArray FromHex(const char *buf, size_t bufsize) { ByteArray data; data.reserve((bufsize + 1) / 2); while(buf[0] && buf[1]) { u8 h = FromHex(*buf++); u8 l = FromHex(*buf++); data.push_back((h << 4) | l); } if (buf[0] != 0 && !isspace(buf[0])) throw std::runtime_error("tailing character"); return data; } }; bool TrustedApp::Probe(const SessionPtr & session) { auto & di = session->GetDeviceInfo(); bool supported = di.Supports(OperationCode::SendWMDRMPDAppRequest) && di.Supports(OperationCode::GetWMDRMPDAppResponse) && di.Supports(OperationCode::EnableTrustedFilesOperations) && di.Supports(OperationCode::DisableTrustedFilesOperations) && di.Supports(OperationCode::EndTrustedAppSession); debug("MTPZ supported: " , supported? "yes": "no"); return supported; } void TrustedApp::Authenticate() { if (!_keys) return; auto & di = _session->GetDeviceInfo(); if (di.Supports(DeviceProperty::SessionInitiatorVersionInfo)) _session->SetDeviceProperty(DeviceProperty::SessionInitiatorVersionInfo, "Android File Transfer for Linux - MTPZClassDriver"); _session->GenericOperation(OperationCode::EndTrustedAppSession); ByteArray challenge, message; std::tie(challenge, message) = _keys->GenerateCertificateMessage(); //HexDump("certificate payload", message); _session->GenericOperation(OperationCode::SendWMDRMPDAppRequest, message); ByteArray response = _session->GenericOperation(OperationCode::GetWMDRMPDAppResponse); //HexDump("device response", response); ByteArray cmacKey = _keys->VerifyResponse(response, challenge); debug("validated MTPZ device response..."); //HexDump("cmac key", cmacKey); ByteArray signature = _keys->SignResponse(cmacKey); //HexDump("signature", signature); _session->GenericOperation(OperationCode::SendWMDRMPDAppRequest, signature); debug("authentication finished, enabling secure session..."); u32 cmac[4]; _keys->SignSessionRequest(cmac, cmacKey); _session->EnableSecureFileOperations(cmac); debug("handshake finished"); } TrustedApp::KeysPtr TrustedApp::LoadKeys(const std::string & path) { BIO * bio = BIO_new_file(path.c_str(), "rt"); if (bio == NULL) { error("could not open ", path); return nullptr; } try { auto keys = std::make_shared(); char buf[4096]; //public exp BIO_gets(bio, buf, sizeof(buf)); if (BN_hex2bn(&keys->exp, buf) <= 0) throw std::runtime_error("can't read public exponent"); //session key auto r = BIO_gets(bio, buf, sizeof(buf)); if (r <= 0) throw std::runtime_error("BIO_gets: short read"); keys->skey = Keys::FromHex(buf, r); //HexDump("session key", keys->skey); //public mod BIO_gets(bio, buf, sizeof(buf)); if (BN_hex2bn(&keys->mod, buf) <= 0) throw std::runtime_error("can't read public modulus"); //private exponent BIO_gets(bio, buf, sizeof(buf)); if (BN_hex2bn(&keys->pkey, buf) <= 0) throw std::runtime_error("can't read private key"); r = BIO_gets(bio, buf, sizeof(buf)); if (r <= 0) throw std::runtime_error("BIO_gets: short read"); keys->certificate = Keys::FromHex(buf, r); //HexDump("certificate", keys->certificate); keys->Update(); BIO_free(bio); return keys; } catch(const std::exception & ex) { BIO_free(bio); error("loading keys failed: ", ex.what()); } return nullptr; } #else bool TrustedApp::Probe(const SessionPtr & session) { return false; } TrustedApp::KeysPtr TrustedApp::LoadKeys(const std::string & path) { return nullptr; } void TrustedApp::Authenticate() { } #endif } android-file-transfer-linux-4.5/mtp/mtpz/TrustedApp.h000066400000000000000000000030611501120463400227300ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_MTPZ_TRUSTEDAPP_H #define AFTL_MTP_MTPZ_TRUSTEDAPP_H #include #include namespace mtp { class Session; DECLARE_PTR(Session); class TrustedApp; DECLARE_PTR(TrustedApp); class TrustedApp { private: struct Keys; DECLARE_PTR(Keys); private: SessionPtr _session; KeysPtr _keys; public: static bool Probe(const SessionPtr & session); ~TrustedApp(); static TrustedAppPtr Create(const SessionPtr & session, const std::string &mtpzDataPath); void Authenticate(); bool KeysLoaded() const { return !!_keys; } private: TrustedApp(const SessionPtr & session, const std::string & mtpzDataPath); static KeysPtr LoadKeys(const std::string & path); }; } #endif android-file-transfer-linux-4.5/mtp/ptp/000077500000000000000000000000001501120463400202755ustar00rootroot00000000000000android-file-transfer-linux-4.5/mtp/ptp/ByteArrayObjectStream.h000066400000000000000000000056261501120463400246640ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_BYTEARRAYOBJECTSTREAM_H #define AFTL_MTP_PTP_BYTEARRAYOBJECTSTREAM_H #include #include namespace mtp { class ByteArrayObjectInputStream final: public IObjectInputStream, public CancellableStream //! stream constructed from \ref ByteArray data { ByteArray _data; size_t _offset; public: ByteArrayObjectInputStream(const ByteArray & data) noexcept: _data(data), _offset(0) { } ByteArrayObjectInputStream(ByteArray && data) noexcept: _data(data), _offset(0) { } const ByteArray &GetData() const { return _data; } virtual u64 GetSize() const { return _data.size(); } virtual size_t Read(u8 *data, size_t size) { CheckCancelled(); size_t n = std::min(size, _data.size() - _offset); std::copy(_data.data() + _offset, _data.data() + _offset + n, data); _offset += n; return n; } }; DECLARE_PTR(ByteArrayObjectInputStream); class ByteArrayObjectOutputStream final: public IObjectOutputStream, public CancellableStream //! stream inserting into \ref ByteArray { ByteArray _data; public: ByteArrayObjectOutputStream(): _data() { } const ByteArray &GetData() const { return _data; } ByteArray &GetData() { return _data; } size_t Write(const u8 *data, size_t size) override { CheckCancelled(); std::copy(data, data + size, std::back_inserter(_data)); return size; } }; DECLARE_PTR(ByteArrayObjectOutputStream); class FixedSizeByteArrayObjectOutputStream final: public IObjectOutputStream, public CancellableStream //! stream writing into fixed size \ref ByteArray { ByteArray _data; size_t _offset; public: FixedSizeByteArrayObjectOutputStream(size_t size): _data(size), _offset(0) { } const ByteArray &GetData() const { return _data; } size_t Write(const u8 *data, size_t size) override { CheckCancelled(); size_t n = std::min(size, _data.size() - _offset); std::copy(data, data + n, _data.data() + _offset); _offset += n; return n; } }; DECLARE_PTR(FixedSizeByteArrayObjectOutputStream); } #endif /* BYTEARRAYOBJECTSTREAM_H */ android-file-transfer-linux-4.5/mtp/ptp/Container.h000066400000000000000000000035661501120463400224020ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_CONTAINER_H #define AFTL_MTP_PTP_CONTAINER_H #include #include #include #include #include namespace mtp { struct Container //! MTP container for MTP message, appends type and size { ByteArray Data; void WriteSize(OutputStream &stream, u64 size) { if (size > MaxObjectSize) size = MaxObjectSize; stream << (u32)size; } template Container(const Message &msg) { OutputStream stream(Data); WriteSize(stream, msg.Data.size() + 6); stream << Message::Type; std::copy(msg.Data.begin(), msg.Data.end(), std::back_inserter(Data)); } template Container(const Message &msg, IObjectInputStreamPtr inputStream) { OutputStream stream(Data); WriteSize(stream, inputStream->GetSize() + msg.Data.size() + 6); stream << Message::Type; std::copy(msg.Data.begin(), msg.Data.end(), std::back_inserter(Data)); } }; } #endif /* CONTAINER_H */ android-file-transfer-linux-4.5/mtp/ptp/DataTypeCode.cpp000066400000000000000000000011211501120463400233020ustar00rootroot00000000000000#include #include namespace mtp { std::string ToString(DataTypeCode type) { switch(type) { # define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_TO_STRING(DataTypeCode, NAME, VALUE) # include ENUM_VALUE_TO_STRING_DEFAULT(DataTypeCode, type, 4); } } bool IsArray(DataTypeCode type) { switch(type) { # define CASE(BITS) case DataTypeCode::ArrayInt##BITS : case DataTypeCode::ArrayUint##BITS : CASE(8) CASE(16) CASE(32) CASE(64) CASE(128) return true; default: return false; } } } android-file-transfer-linux-4.5/mtp/ptp/DataTypeCode.h000066400000000000000000000006361501120463400227610ustar00rootroot00000000000000#ifndef AFTL_MTP_PTP_DATATYPECODE_H #define AFTL_MTP_PTP_DATATYPECODE_H #include #include namespace mtp { enum struct DataTypeCode : u16 { #define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_DECL(NAME, VALUE) # include #undef ENUM_VALUE }; DECLARE_ENUM(DataTypeCode, u16); bool IsArray(DataTypeCode type); std::string ToString(DataTypeCode type); } #endif android-file-transfer-linux-4.5/mtp/ptp/DataTypeCode.values.h000066400000000000000000000011751501120463400242560ustar00rootroot00000000000000ENUM_VALUE(Undefined, 0x0000) ENUM_VALUE(Int8, 0x0001) ENUM_VALUE(Uint8, 0x0002) ENUM_VALUE(Int16, 0x0003) ENUM_VALUE(Uint16, 0x0004) ENUM_VALUE(Int32, 0x0005) ENUM_VALUE(Uint32, 0x0006) ENUM_VALUE(Int64, 0x0007) ENUM_VALUE(Uint64, 0x0008) ENUM_VALUE(Int128, 0x0009) ENUM_VALUE(Uint128, 0x000a) ENUM_VALUE(ArrayInt8, 0x4001) ENUM_VALUE(ArrayUint8, 0x4002) ENUM_VALUE(ArrayInt16, 0x4003) ENUM_VALUE(ArrayUint16, 0x4004) ENUM_VALUE(ArrayInt32, 0x4005) ENUM_VALUE(ArrayUint32, 0x4006) ENUM_VALUE(ArrayInt64, 0x4007) ENUM_VALUE(ArrayUint64, 0x4008) ENUM_VALUE(ArrayInt128, 0x4009) ENUM_VALUE(ArrayUint128, 0x400a) ENUM_VALUE(String, 0xffff) android-file-transfer-linux-4.5/mtp/ptp/Device.cpp000066400000000000000000000162101501120463400222000ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include namespace mtp { Device::Device(usb::BulkPipePtr pipe): _packeter(pipe) { } SessionPtr Device::OpenSession(u32 sessionId, int timeout) { OperationRequest req(OperationCode::OpenSession, 0, sessionId); Container container(req); _packeter.Write(container.Data, timeout); ByteArray data, response; ResponseType code; _packeter.Read(0, data, code, response, timeout); //HexDump("payload", data); return std::make_shared(_packeter.GetPipe(), sessionId); } u8 Device::GetInterfaceStringIndex(usb::DeviceDescriptorPtr desc, u8 number) { static const u16 DT_INTERFACE = 4; ByteArray descData = desc->GetDescriptor(); HexDump("descriptor", descData); size_t offset = 0; while(offset < descData.size()) { u8 len = descData.at(offset + 0); u8 type = descData.at(offset + 1); if (len < 2) throw std::runtime_error("invalid descriptor length"); if (type == DT_INTERFACE && len >= 9 && descData.at(offset + 2) == number) return descData.at(offset + 8); offset += len; } throw std::runtime_error("no interface descriptor found"); } DevicePtr Device::Open(usb::ContextPtr ctx, usb::DeviceDescriptorPtr desc, bool claimInterface, bool resetDevice) { debug("probing device ", hex(desc->GetVendorId(), 4), ":", hex(desc->GetProductId(), 4)); usb::DevicePtr device = desc->TryOpen(ctx); if (resetDevice && device) { device->Reset(); device.reset(); device = desc->TryOpen(ctx); } if (!device) { debug("descriptor->TryOpen() failed"); return nullptr; } int confs = desc->GetConfigurationsCount(); debug("configurations: ", confs); for(int i = 0; i < confs; ++i) { usb::ConfigurationPtr conf = desc->GetConfiguration(i); int interfaces = conf->GetInterfaceCount(); if (interfaces == 0) { debug("device not configured (no interfaces), setting configuration now..."); int index = conf->GetIndex(); conf.reset(); device->SetConfiguration(index); debug("device configured, retrieving configuration descriptor again..."); conf = desc->GetConfiguration(i); interfaces = conf->GetInterfaceCount(); } debug("interfaces: ", interfaces); for(int j = 0; j < interfaces; ++j) { usb::InterfacePtr iface = conf->GetInterface(device, conf, j, 0); debug("Device usb interface: ", i, ':', j, ", index: ", iface->GetIndex(), ", endpoints: ", iface->GetEndpointsCount()); #ifdef USB_BACKEND_LIBUSB std::string name = iface->GetName(); #else ByteArray data = usb::DeviceRequest(device).GetDescriptor(usb::DescriptorType::String, 0, 0); HexDump("languages", data); if (data.size() < 4 || data[1] != (u8)usb::DescriptorType::String) continue; u16 langId = data[2] | ((u16)data[3] << 8); std::string name; try { data = usb::DeviceRequest(device).GetDescriptor(usb::DescriptorType::String, static_cast(usb::DeviceRequest::Request::GetOSStringDescriptor), langId); HexDump("OSStringDescriptor", data); if (data.size() < 0x12 || data.at(2) != 'M' || data.at(4) != 'S' || data.at(6) != 'F' || data.at(8) != 'T') throw std::runtime_error("invalid OSString descriptor"); u8 command = data.at(0x10); debug("vendor code: 0x", hex(command, 2)); //getting extended OS compat descriptor data.resize(255); device->ReadControl(static_cast(usb::RequestType::DeviceToHost | usb::RequestType::Vendor | usb::RequestType::Device), command, 0, 4, data, usb::BaseRequest::DefaultTimeout); HexDump("extended compat id os feature desctriptor", data); if (data.at(0x12) == 'M' && data.at(0x13) == 'T' && data.at(0x14) == 'P') name = "MTP"; } catch (const std::exception & ex) { debug("winusb handshake failed: ", ex.what()); } if (name.find("MTP") == name.npos) { auto interfaceStringIndex = GetInterfaceStringIndex(desc, j); data = usb::DeviceRequest(device).GetDescriptor(usb::DescriptorType::String, interfaceStringIndex, langId); HexDump("interface name", data); if (data.size() < 4 || data[1] != (u8)usb::DescriptorType::String) continue; u8 len = data[0]; InputStream stream(data, 2); name = stream.ReadString((len - 2) / 2); } #endif //device->SetConfiguration(i); if (name.find("MTP") != name.npos) { usb::InterfaceTokenPtr token = claimInterface? device->ClaimInterface(iface): nullptr; usb::BulkPipePtr pipe = usb::BulkPipe::Create(device, conf, iface, token); return std::make_shared(pipe); } else debug("skipping interface with name ", name); if (iface->GetClass() == 6 && iface->GetSubclass() == 1) { usb::InterfaceTokenPtr token = claimInterface? device->ClaimInterface(iface): nullptr; usb::BulkPipePtr pipe = usb::BulkPipe::Create(device, conf, iface, token); return std::make_shared(pipe); } } } return nullptr; } DevicePtr Device::FindFirst(const std::string & filter, bool claimInterface, bool resetDevice) { usb::ContextPtr ctx(new usb::Context); return FindFirst(ctx, filter, claimInterface, resetDevice); } DevicePtr Device::FindFirst(usb::ContextPtr ctx, const std::string & filter, bool claimInterface, bool resetDevice) { int vendor, product; bool useUsbVendorProduct = true; if (sscanf(filter.c_str(), "%x:%x", &vendor, &product) != 2) { vendor = product = -1; useUsbVendorProduct = false; } for (usb::DeviceDescriptorPtr desc : ctx->GetDevices()) try { if (vendor >= 0 && product >= 0) { if (desc->GetVendorId() != vendor || desc->GetProductId() != product) continue; } auto device = Open(ctx, desc, claimInterface, resetDevice); if (device && (useUsbVendorProduct || device->Matches(filter))) return device; } catch(const std::exception &ex) { error("Device::Find failed:", ex.what()); } return nullptr; } msg::DeviceInfo Device::GetInfo() { return Session::GetDeviceInfo(_packeter, /*transactionId=*/0); } bool Device::Matches(const std::string & filter) { if (filter.empty()) return true; auto di = GetInfo(); return di.Matches(filter); } } android-file-transfer-linux-4.5/mtp/ptp/Device.h000066400000000000000000000037001501120463400216450ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_DEVICE_H #define AFTL_MTP_PTP_DEVICE_H #include #include #include #include namespace mtp { class Device //! Generic MTP Device class representing physical device, creates \ref Session { PipePacketer _packeter; private: static u8 GetInterfaceStringIndex(usb::DeviceDescriptorPtr desc, u8 number); public: Device(usb::BulkPipePtr pipe); msg::DeviceInfo GetInfo(); ///This could be very expensive, it calls GetDeviceInfo bool Matches(const std::string & filter); SessionPtr OpenSession(u32 sessionId, int timeout = Session::DefaultTimeout); static DevicePtr Open(usb::ContextPtr context, usb::DeviceDescriptorPtr desc, bool claimInterface = true, bool resetDevice = false); //fixme: add flags here static DevicePtr FindFirst(usb::ContextPtr context, const std::string & filter = std::string(), bool claimInterface = true, bool resetDevice = false); static DevicePtr FindFirst(const std::string & filter = std::string(), bool claimInterface = true, bool resetDevice = false); }; } #endif android-file-transfer-linux-4.5/mtp/ptp/DeviceProperty.cpp000066400000000000000000000012551501120463400237500ustar00rootroot00000000000000#include #include namespace mtp { std::string ToString(DeviceProperty property) { switch(property) { # define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_TO_STRING(DeviceProperty, NAME, VALUE) # include ENUM_VALUE_TO_STRING_DEFAULT(DeviceProperty, property, 4); # undef ENUM_VALUE } } std::string ToString(PerceivedDeviceType property) { switch(property) { # define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_TO_STRING(PerceivedDeviceType, NAME, VALUE) # include ENUM_VALUE_TO_STRING_DEFAULT(PerceivedDeviceType, property, 8); # undef ENUM_VALUE } } } android-file-transfer-linux-4.5/mtp/ptp/DeviceProperty.h000066400000000000000000000027161501120463400234200ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_DEVICEPROPERTY_H #define AFTL_MTP_PTP_DEVICEPROPERTY_H #include #include namespace mtp { enum struct PerceivedDeviceType : u32 { #define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_DECL(NAME, VALUE) # include "PerceivedDeviceType.values.h" #undef ENUM_VALUE }; std::string ToString(PerceivedDeviceType property); enum struct DeviceProperty : u16 { #define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_DECL(NAME, VALUE) # include "DeviceProperty.values.h" #undef ENUM_VALUE }; DECLARE_ENUM(DeviceProperty, u16); std::string ToString(DeviceProperty property); } #endif /* OBJECTPROPERTY_H */ android-file-transfer-linux-4.5/mtp/ptp/DeviceProperty.values.h000066400000000000000000000034741501120463400247200ustar00rootroot00000000000000ENUM_VALUE(Undefined, 0x5000) ENUM_VALUE(BatteryLevel, 0x5001) ENUM_VALUE(FunctionalMode, 0x5002) //0 - normal, 1 - sleeping ENUM_VALUE(ImageSize, 0x5003) ENUM_VALUE(CompressionSetting, 0x5004) ENUM_VALUE(WhiteBalance, 0x5005) ENUM_VALUE(RgbGain, 0x5006) ENUM_VALUE(FNumber, 0x5007) ENUM_VALUE(FocalLength, 0x5008) ENUM_VALUE(FocusDistance, 0x5009) ENUM_VALUE(FocusMode, 0x500a) ENUM_VALUE(ExposureMeteringMode, 0x500b) ENUM_VALUE(FlashMode, 0x500c) ENUM_VALUE(ExposureTime, 0x500d) ENUM_VALUE(ExposureProgramMode, 0x500e) ENUM_VALUE(ExposureIndex, 0x500f) ENUM_VALUE(ExposureBiasCompensation, 0x5010) ENUM_VALUE(Datetime, 0x5011) ENUM_VALUE(CaptureDelay, 0x5012) ENUM_VALUE(StillCaptureMode, 0x5013) ENUM_VALUE(Contrast, 0x5014) ENUM_VALUE(Sharpness, 0x5015) ENUM_VALUE(DigitalZoom, 0x5016) ENUM_VALUE(EffectMode, 0x5017) ENUM_VALUE(BurstNumber, 0x5018) ENUM_VALUE(BurstInterval, 0x5019) ENUM_VALUE(TimelapseNumber, 0x501a) ENUM_VALUE(TimelapseInterval, 0x501b) ENUM_VALUE(FocusMeteringMode, 0x501c) ENUM_VALUE(UploadUrl, 0x501d) ENUM_VALUE(Artist, 0x501e) ENUM_VALUE(CopyrightInfo, 0x501f) ENUM_VALUE(SecureTime, 0xd101) ENUM_VALUE(DeviceCertificate, 0xd102) ENUM_VALUE(RevocationInfo, 0xd103) ENUM_VALUE(PlaysForSureID, 0xd131) ENUM_VALUE(DeviceEUI64, 0xd210) ENUM_VALUE(FirmwareVersion, 0xd233) ENUM_VALUE(SerialNumber, 0xd235) ENUM_VALUE(FunctionalID, 0xd301) ENUM_VALUE(ModelID, 0xd302) ENUM_VALUE(UseDeviceStage, 0xd303) ENUM_VALUE(SynchronizationPartner, 0xd401) ENUM_VALUE(DeviceFriendlyName, 0xd402) ENUM_VALUE(Volume, 0xd403) ENUM_VALUE(SupportedFormatsOrdered, 0xd404) ENUM_VALUE(DeviceIcon, 0xd405) ENUM_VALUE(SessionInitiatorVersionInfo, 0xd406) ENUM_VALUE(PerceivedDeviceType, 0xd407) ENUM_VALUE(PlaybackRate, 0xd410) ENUM_VALUE(PlaybackObject, 0xd411) ENUM_VALUE(PlaybackContainerIndex, 0xd412) ENUM_VALUE(PlaybackPosition, 0xd413) android-file-transfer-linux-4.5/mtp/ptp/EventCode.cpp000066400000000000000000000004741501120463400226620ustar00rootroot00000000000000#include #include namespace mtp { std::string ToString(EventCode code) { switch(code) { # define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_TO_STRING(EventCode, NAME, VALUE) # include ENUM_VALUE_TO_STRING_DEFAULT(OperationCode, code, 4); } } } android-file-transfer-linux-4.5/mtp/ptp/EventCode.h000066400000000000000000000022551501120463400223260ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2025 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_EVENTCODE_H #define AFTL_MTP_PTP_EVENTCODE_H #include namespace mtp { enum struct EventCode : u16 { #define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_DECL(NAME, VALUE) # include #undef ENUM_VALUE }; DECLARE_ENUM(EventCode, u16); std::string ToString(EventCode code); } #endif android-file-transfer-linux-4.5/mtp/ptp/EventCode.values.h000066400000000000000000000011601501120463400236160ustar00rootroot00000000000000ENUM_VALUE(TransactionCancelled, 0x4001) ENUM_VALUE(ObjectAdded, 0x4002) ENUM_VALUE(ObjectRemoved, 0x4003) ENUM_VALUE(StoreAdded, 0x4004) ENUM_VALUE(StoreRemoved, 0x4005) ENUM_VALUE(DevicePropChanged, 0x4006) ENUM_VALUE(ObjectInfoChanged, 0x4007) ENUM_VALUE(DeviceInfoChanged, 0x4008) ENUM_VALUE(RequestObjectTransfer, 0x4009) ENUM_VALUE(StoreFull, 0x400a) ENUM_VALUE(DeviceReset, 0x400b) ENUM_VALUE(StorageInfoChanged, 0x400c) ENUM_VALUE(CaptureComplete, 0x400d) ENUM_VALUE(UnreportedStatus, 0x400e) ENUM_VALUE(ObjectPropChanged, 0xc801) ENUM_VALUE(ObjectPropDescChanged, 0xc802) ENUM_VALUE(ObjectReferenceChanged, 0xc803) android-file-transfer-linux-4.5/mtp/ptp/IObjectStream.h000066400000000000000000000043061501120463400231440ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_IOBJECTSTREAM_H #define AFTL_MTP_PTP_IOBJECTSTREAM_H #include #include #include namespace mtp { struct ICancellableStream //! interfaces for streams with cancellation functionality { virtual ~ICancellableStream() = default; virtual void Cancel() = 0; }; DECLARE_PTR(ICancellableStream); struct OperationCancelledException : public std::runtime_error //! exception thrown if stream was cancelled { OperationCancelledException(): std::runtime_error("operation cancelled") { } }; class CancellableStream : public virtual ICancellableStream //! default implementation of \ref ICancellableStream { std::atomic_bool _cancelled; public: CancellableStream(): _cancelled(false) { } void Cancel() { _cancelled.store(true); } void CheckCancelled() { bool cancelled = _cancelled.load(); if (cancelled) throw OperationCancelledException(); } }; struct IObjectInputStream : public virtual ICancellableStream //! Basic input stream interface { virtual u64 GetSize() const = 0; virtual size_t Read(u8 *data, size_t size) = 0; }; DECLARE_PTR(IObjectInputStream); struct IObjectOutputStream : public virtual ICancellableStream //! Basic output stream interface { virtual size_t Write(const u8 *data, size_t size) = 0; }; DECLARE_PTR(IObjectOutputStream); } #endif /* IOBJECTSTREAM_H */ android-file-transfer-linux-4.5/mtp/ptp/InputStream.h000066400000000000000000000107711501120463400227270ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_INPUTSTREAM_H #define AFTL_MTP_PTP_INPUTSTREAM_H #include #include namespace mtp { class InputStream //! MTP data decoding input stream { const ByteArray & _data; size_t _offset; public: InputStream(const ByteArray & data, size_t offset = 0): _data(data), _offset(offset) { } size_t GetOffset() const { return _offset; } void Skip(size_t size) { _offset += size; } const ByteArray & GetData() const { return _data; } bool AtEnd() const { return _offset >= _data.size(); } u8 Read8() { return _data.at(_offset++); } u16 Read16() { u8 l = Read8(); u8 h = Read8(); return l | ((u16)h << 8); } u32 Read32() { u16 l = Read16(); u16 h = Read16(); return l | ((u32)h << 16); } u64 Read64() { u32 l = Read32(); u32 h = Read32(); return l | ((u64)h << 32); } u64 Read128() { u64 l = Read64(); Skip(8); //fixme: truncated 128 bit value return l; } std::string ReadString() { return ReadString(Read8()); } std::string ReadString(unsigned len) { std::string str; str.reserve(2 * len); while(len--) { u16 ch = Read16(); if (ch == 0) continue; if (ch <= 0x7f) str += (char)ch; else if (ch <= 0x7ff) { str += (char) ((ch >> 6) | 0xc0); str += (char) ((ch & 0x3f) | 0x80); } else //if (ch <= 0xffff) { str += (char)((ch >> 12) | 0xe0); str += (char)(((ch & 0x0fc0) >> 6) | 0x80); str += (char)( (ch & 0x003f) | 0x80); } } return str; } template std::vector ReadArray() { std::vector array; if (AtEnd()) return array; u32 size = Read32(); while(size--) { ElementType el; (*this) >> el; array.push_back(el); } return array; } }; inline InputStream & operator >> (InputStream &stream, u8 &value) { value = stream.Read8(); return stream; } inline InputStream & operator >> (InputStream &stream, u16 &value) { value = stream.Read16(); return stream; } inline InputStream & operator >> (InputStream &stream, u32 &value) { value = stream.Read32(); return stream; } inline InputStream & operator >> (InputStream &stream, u64 &value) { value = stream.Read64(); return stream; } inline InputStream & operator >> (InputStream &stream, s8 &value) { value = stream.Read8(); return stream; } inline InputStream & operator >> (InputStream &stream, s16 &value) { value = stream.Read16(); return stream; } inline InputStream & operator >> (InputStream &stream, s32 &value) { value = stream.Read32(); return stream; } inline InputStream & operator >> (InputStream &stream, s64 &value) { value = stream.Read64(); return stream; } inline InputStream & operator >> (InputStream &stream, std::string &value) { value = stream.ReadString(); return stream; } template inline InputStream & operator >> (InputStream &stream, std::vector &value) { value = stream.template ReadArray(); return stream; } inline u64 ReadSingleInteger(const ByteArray &data) { InputStream s(data); switch(data.size()) { case 8: return s.Read64(); case 4: return s.Read32(); case 2: return s.Read16(); case 1: return s.Read8(); default: throw std::runtime_error("unexpected length for numeric property"); } } inline std::string ReadSingleString(const ByteArray &data) { InputStream s(data); std::string value; s >> value; return value; } template ResponseType ParseResponse(const ByteArray & data) { ResponseType response; InputStream is(data); response.Read(is); return response; } } #endif /* STREAM_H */ android-file-transfer-linux-4.5/mtp/ptp/JoinedObjectStream.h000066400000000000000000000071351501120463400241670ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_JOINEDOBJECTSTREAM_H #define AFTL_MTP_PTP_JOINEDOBJECTSTREAM_H #include namespace mtp { class JoinedObjectInputStreamBase : public IObjectInputStream, public CancellableStream { protected: bool _stream1Exhausted; virtual IObjectInputStreamPtr GetStream1() const = 0; virtual IObjectInputStreamPtr GetStream2() const = 0; virtual void OnStream1Exhausted() { } public: JoinedObjectInputStreamBase(): _stream1Exhausted(false) { } virtual size_t Read(u8 *data, size_t size) { CheckCancelled(); size_t r; if (!_stream1Exhausted) { r = GetStream1()->Read(data, size); if (r < size) { _stream1Exhausted = true; OnStream1Exhausted(); r += GetStream2()->Read(data + r, size - r); } } else r = GetStream2()->Read(data, size); return r; } }; class JoinedObjectInputStream final: public JoinedObjectInputStreamBase //! Input stream made of two subsequent stream { IObjectInputStreamPtr _stream1, _stream2; size_t _stream1Size, _stream2Size; private: virtual IObjectInputStreamPtr GetStream1() const { return _stream1; } virtual IObjectInputStreamPtr GetStream2() const { return _stream2; } public: JoinedObjectInputStream(IObjectInputStreamPtr s1, IObjectInputStreamPtr s2): _stream1(s1), _stream2(s2), _stream1Size(s1->GetSize()), _stream2Size(s2->GetSize()) { } virtual u64 GetSize() const { return _stream1Size + _stream2Size; } }; DECLARE_PTR(JoinedObjectInputStream); class JoinedObjectOutputStreamBase : public IObjectOutputStream, public CancellableStream { protected: bool _stream1Exhausted; virtual IObjectOutputStreamPtr GetStream1() const = 0; virtual IObjectOutputStreamPtr GetStream2() const = 0; virtual void OnStream1Exhausted() { } public: JoinedObjectOutputStreamBase(): _stream1Exhausted(false) { } size_t Write(const u8 *data, size_t size) override { CheckCancelled(); size_t r; if (!_stream1Exhausted) { r = GetStream1()->Write(data, size); if (r < size) { _stream1Exhausted = true; OnStream1Exhausted(); r += GetStream2()->Write(data + r, size - r); } } else r = GetStream2()->Write(data, size); return r; } }; class JoinedObjectOutputStream final: public JoinedObjectOutputStreamBase //! Output stream made of two subsequent stream { IObjectOutputStreamPtr _stream1, _stream2; private: virtual IObjectOutputStreamPtr GetStream1() const { return _stream1; } virtual IObjectOutputStreamPtr GetStream2() const { return _stream2; } public: JoinedObjectOutputStream(IObjectOutputStreamPtr s1, IObjectOutputStreamPtr s2): _stream1(s1), _stream2(s2) { } }; DECLARE_PTR(JoinedObjectOutputStream); } #endif /* JOINEDOBJECTSTREAM_H */ android-file-transfer-linux-4.5/mtp/ptp/Messages.cpp000066400000000000000000000033201501120463400225460ustar00rootroot00000000000000#include #include #include #include namespace mtp { namespace msg { bool DeviceInfo::Supports(OperationCode opcode) const { auto i = std::find(OperationsSupported.begin(), OperationsSupported.end(), opcode); return i != OperationsSupported.end(); } bool DeviceInfo::Supports(DeviceProperty property) const { auto i = std::find(DevicePropertiesSupported.begin(), DevicePropertiesSupported.end(), property); return i != DevicePropertiesSupported.end(); } bool DeviceInfo::Supports(EventCode event) const { auto i = std::find(EventsSupported.begin(), EventsSupported.end(), event); return i != EventsSupported.end(); } bool DeviceInfo::Supports(ObjectFormat format) const { auto i = std::find(ImageFormats.begin(), ImageFormats.end(), format); return i != ImageFormats.end(); } bool DeviceInfo::Matches(const std::string & haystack, const std::string & needle) { return strcasestr(haystack.c_str(), needle.c_str()); } bool DeviceInfo::Matches(const std::string & filter) const { if (filter.empty()) return true; auto fsname = GetFilesystemFriendlyName(); return Matches(fsname, filter); } namespace { std::string Strip(std::string str) { str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end()); return str; } } std::string DeviceInfo::GetFilesystemFriendlyName() const { std::stringstream ss; ss << Strip(Manufacturer); ss << '-'; ss << Strip(Model); ss << '-'; ss << Strip(SerialNumber); return ss.str(); } bool ObjectPropertiesSupported::Supports(ObjectProperty prop) const { return std::find(ObjectPropertyCodes.begin(), ObjectPropertyCodes.end(), prop) != ObjectPropertyCodes.end(); } }} android-file-transfer-linux-4.5/mtp/ptp/Messages.h000066400000000000000000000161731501120463400222250ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_MESSAGES_H #define AFTL_MTP_PTP_MESSAGES_H #include #include #include #include #include #include #include #include #include #include namespace mtp { namespace msg { struct DeviceInfo //! MTP DeviceInfo message { u16 StandardVersion; u32 VendorExtensionId; u16 VendorExtensionVersion; std::string VendorExtensionDesc; u16 FunctionalMode; std::vector OperationsSupported; std::vector EventsSupported; std::vector DevicePropertiesSupported; std::vector CaptureFormats; std::vector ImageFormats; std::string Manufacturer; std::string Model; std::string DeviceVersion; std::string SerialNumber; void Read(InputStream &stream) { stream >> StandardVersion; stream >> VendorExtensionId; stream >> VendorExtensionVersion; stream >> VendorExtensionDesc; stream >> FunctionalMode; stream >> OperationsSupported; stream >> EventsSupported; stream >> DevicePropertiesSupported; stream >> CaptureFormats; stream >> ImageFormats; stream >> Manufacturer; stream >> Model; stream >> DeviceVersion; stream >> SerialNumber; } bool Supports(OperationCode opcode) const; bool Supports(DeviceProperty property) const; bool Supports(EventCode event) const; bool Supports(ObjectFormat format) const; std::string GetFilesystemFriendlyName() const; static bool Matches(const std::string & haystack, const std::string & needle); bool Matches(const std::string & filter) const; }; struct ObjectHandles //! MTP ObjectHandles message { std::vector ObjectHandles; void Read(InputStream &stream) { stream >> ObjectHandles; } void Write(OutputStream & stream) const { stream.WriteArray(ObjectHandles); } }; struct StorageIDs //! MTP StorageIDs message { std::vector StorageIDs; void Read(InputStream &stream) { stream >> StorageIDs; } }; struct StorageInfo //! MTP StorageInfo message { u16 StorageType; u16 FilesystemType; u16 AccessCapability; u64 MaxCapacity; u64 FreeSpaceInBytes; u32 FreeSpaceInImages; std::string StorageDescription; std::string VolumeLabel; std::string GetName() const { return !StorageDescription.empty()? StorageDescription: VolumeLabel; } void Read(InputStream &stream) { stream >> StorageType; stream >> FilesystemType; stream >> AccessCapability; stream >> MaxCapacity; stream >> FreeSpaceInBytes; stream >> FreeSpaceInImages; stream >> StorageDescription; stream >> VolumeLabel; } }; struct ObjectInfo //! MTP ObjectInfo message { mtp::StorageId StorageId; mtp::ObjectFormat ObjectFormat; u16 ProtectionStatus; u64 ObjectCompressedSize; u16 ThumbFormat; u32 ThumbCompressedSize; u32 ThumbPixWidth; u32 ThumbPixHeight; u32 ImagePixWidth; u32 ImagePixHeight; u32 ImageBitDepth; ObjectId ParentObject; mtp::AssociationType AssociationType; u32 AssociationDesc; u32 SequenceNumber; std::string Filename; std::string CaptureDate; std::string ModificationDate; std::string Keywords; ObjectInfo(): StorageId(), ObjectFormat(), ProtectionStatus(), ObjectCompressedSize(), ThumbFormat(), ThumbCompressedSize(), ThumbPixWidth(), ThumbPixHeight(), ImagePixWidth(), ImagePixHeight(), ImageBitDepth(), ParentObject(), AssociationType(), AssociationDesc(), SequenceNumber() { } void Read(InputStream &stream) { stream >> StorageId; stream >> ObjectFormat; stream >> ProtectionStatus; u32 objectCompressedSize; stream >> objectCompressedSize; ObjectCompressedSize = objectCompressedSize; stream >> ThumbFormat; stream >> ThumbCompressedSize; stream >> ThumbPixWidth; stream >> ThumbPixHeight; stream >> ImagePixWidth; stream >> ImagePixHeight; stream >> ImageBitDepth; stream >> ParentObject; stream >> AssociationType; stream >> AssociationDesc; stream >> SequenceNumber; stream >> Filename; stream >> CaptureDate; stream >> ModificationDate; stream >> Keywords; } //fixme: make me template void Write(OutputStream &stream) const { stream << StorageId; stream << ObjectFormat; stream << ProtectionStatus; u32 objectCompressedSize = (ObjectCompressedSize > MaxObjectSize)? MaxObjectSize: ObjectCompressedSize; stream << objectCompressedSize; stream << ThumbFormat; stream << ThumbCompressedSize; stream << ThumbPixWidth; stream << ThumbPixHeight; stream << ImagePixWidth; stream << ImagePixHeight; stream << ImageBitDepth; stream << ParentObject; stream << AssociationType; stream << AssociationDesc; stream << SequenceNumber; stream << Filename; stream << CaptureDate; stream << ModificationDate; stream << Keywords; } }; DECLARE_PTR(ObjectInfo); struct ObjectPropertiesSupported //! MTP ObjectPropSupported message { std::vector ObjectPropertyCodes; bool Supports(ObjectProperty prop) const; void Read(InputStream &stream) { stream >> ObjectPropertyCodes; } }; struct SendObjectPropListResponse { mtp::StorageId StorageId; mtp::ObjectId ParentObjectId; mtp::ObjectId ObjectId; u32 FailedPropertyIndex; SendObjectPropListResponse(): StorageId(), ParentObjectId(), ObjectId(), FailedPropertyIndex() { } void Read(InputStream &stream) { stream >> StorageId; stream >> ParentObjectId; stream >> ObjectId; stream >> FailedPropertyIndex; } }; struct DevicePropertyDesc { DeviceProperty Property; DataTypeCode Type; bool Writeable; DevicePropertyDesc(): Property(), Type(DataTypeCode::Undefined), Writeable() { } void Read(InputStream &stream) { stream >> Property; stream >> Type; u8 writeable; stream >> writeable; Writeable = writeable; } }; struct NewObjectInfo { mtp::StorageId StorageId; mtp::ObjectId ParentObjectId; mtp::ObjectId ObjectId; void Read(InputStream & is) { is >> StorageId; is >> ParentObjectId; is >> ObjectId; } }; }} #endif /* MESSAGES_H */ android-file-transfer-linux-4.5/mtp/ptp/ObjectFormat.cpp000066400000000000000000000132421501120463400233620ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #ifdef HAVE_LIBMAGIC # include #endif namespace mtp { namespace { std::string GetExtension(const std::string &filename) { size_t extPos = filename.rfind('.'); std::string ext = (extPos != filename.npos)? filename.substr(extPos + 1): std::string(); std::transform(ext.begin(), ext.end(), ext.begin(), tolower); return ext; } } #ifdef HAVE_LIBMAGIC namespace { class Magic { magic_t _magic; std::map _types; public: Magic(): _magic(magic_open(MAGIC_MIME_TYPE | MAGIC_SYMLINK | MAGIC_ERROR)) { #define MAP_TYPE(name, format) _types[name] = (format) magic_load(_magic, NULL); MAP_TYPE("inode/directory", ObjectFormat::Association); MAP_TYPE("audio/mpeg", ObjectFormat::Mp3); MAP_TYPE("text/plain", ObjectFormat::Text); MAP_TYPE("image/jpeg", ObjectFormat::ExifJpeg); MAP_TYPE("image/gif", ObjectFormat::Gif); MAP_TYPE("image/x-ms-bmp", ObjectFormat::Bmp); MAP_TYPE("image/png", ObjectFormat::Png); MAP_TYPE("audio/x-ms-wma", ObjectFormat::Wma); MAP_TYPE("audio/ogg", ObjectFormat::Ogg); MAP_TYPE("audio/x-flac", ObjectFormat::Flac); MAP_TYPE("audio/x-m4a", ObjectFormat::Aac); MAP_TYPE("audio/audio/x-wav", ObjectFormat::Aiff); MAP_TYPE("audio/mp4", ObjectFormat::Mp4); MAP_TYPE("application/x-mpegurl", ObjectFormat::M3uPlaylist); #undef MAP_TYPE } ObjectFormat GetType(const std::string &path) { const char *type = _magic? magic_file(_magic, path.c_str()): NULL; if (!type) return ObjectFormat::Undefined; //debug("MAGIC MIME: ", type); auto it = _types.find(type); return it != _types.end()? it->second: ObjectFormat::Undefined; } ~Magic() { if (_magic) magic_close(_magic); } }; } #else namespace { struct Magic { const ObjectFormat GetType(const std::string &) { return ObjectFormat::Undefined; } }; } #endif ObjectFormat ObjectFormatFromFilename(const std::string &filename) { //libmagic missing mime type for m3u files auto ext = GetExtension(filename); if (ext == "m3u") return mtp::ObjectFormat::M3uPlaylist; static Magic magic; { ObjectFormat magicType = magic.GetType(filename); if (magicType != ObjectFormat::Undefined) return magicType; } if (ext == "mp3") return mtp::ObjectFormat::Mp3; else if (ext == "txt") return mtp::ObjectFormat::Text; else if (ext == "jpeg" || ext == "jpg") return mtp::ObjectFormat::ExifJpeg; else if (ext == "gif") return mtp::ObjectFormat::Gif; else if (ext == "bmp") return mtp::ObjectFormat::Bmp; else if (ext == "png") return mtp::ObjectFormat::Png; else if (ext == "wma") return mtp::ObjectFormat::Wma; else if (ext == "ogg") return mtp::ObjectFormat::Ogg; else if (ext == "flac") return mtp::ObjectFormat::Flac; else if (ext == "aac") return mtp::ObjectFormat::Aac; else if (ext == "wav") return mtp::ObjectFormat::Aiff; else if (ext == "wmv") return mtp::ObjectFormat::Wmv; else if (ext == "mp4") return mtp::ObjectFormat::Mp4; else if (ext == "m4a") return mtp::ObjectFormat::M4a; else if (ext == "3gp") return mtp::ObjectFormat::_3gp; else if (ext == "asf") return mtp::ObjectFormat::Asf; else return ObjectFormat::Undefined; } bool IsAudioFormat(ObjectFormat format) { switch(format) { case ObjectFormat::Aiff: case ObjectFormat::Wav: case ObjectFormat::Mp3: case ObjectFormat::M4a: case ObjectFormat::UndefinedAudio: case ObjectFormat::Wma: case ObjectFormat::Ogg: case ObjectFormat::Aac: case ObjectFormat::Audible: case ObjectFormat::Flac: return true; default: return false; } } bool IsImageFormat(ObjectFormat format) { u16 type = static_cast(format) >> 8; return type == 0x38; } time_t ConvertDateTime(const std::string ×pec) { struct tm time = {}; time.tm_isdst = -1; char *end = strptime(timespec.c_str(), "%Y%m%dT%H%M%S", &time); if (!end) return 0; return mktime(&time); } std::string ConvertDateTime(time_t time) { struct tm bdt = {}; if (!gmtime_r(&time, &bdt)) throw std::runtime_error("gmtime_r failed"); char buf[64]; size_t r = strftime(buf, sizeof(buf), "%Y%m%dT%H%M%SZ", &bdt); return std::string(buf, r); } std::string ConvertYear(int year) { struct tm bdt = {}; bdt.tm_mday = 1; bdt.tm_year = year - 1900; auto ts = mktime(&bdt); if (ts == (time_t) -1) throw std::runtime_error("mktime failed"); return ConvertDateTime(ts); } std::string ToString(ObjectFormat property) { switch(property) { # define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_TO_STRING(ObjectFormat, NAME, VALUE) # include ENUM_VALUE_TO_STRING_DEFAULT(ObjectFormat, property, 4); } } } android-file-transfer-linux-4.5/mtp/ptp/ObjectFormat.h000066400000000000000000000041171501120463400230300ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_OBJECTFORMAT_H #define AFTL_MTP_PTP_OBJECTFORMAT_H #include #include #include namespace mtp { static const u64 MaxObjectSize = 0xffffffffull; //please look here: https://msdn.microsoft.com/en-us/library/jj584872(v=winembedded.70).aspx enum struct ObjectFormat : u16 { #define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_DECL(NAME, VALUE) # include #undef ENUM_VALUE }; DECLARE_ENUM(ObjectFormat, u16); std::string ToString(ObjectFormat property); enum struct AssociationType : u16 { GenericFolder = 0x0001, Album = 0x0002, TimeSequence = 0x0003, HorizontalPanoramic = 0x0004, VerticalPanoramic = 0x0005, Panoramic2D = 0x0006, AncillaryData = 0x0007 }; DECLARE_ENUM(AssociationType, u16); ObjectFormat ObjectFormatFromFilename(const std::string &filename); bool IsAudioFormat(ObjectFormat format); bool IsImageFormat(ObjectFormat format); time_t ConvertDateTime(const std::string ×pec); std::string ConvertDateTime(time_t); std::string ConvertYear(int year); } namespace std { template<> struct hash { public: size_t operator()(mtp::ObjectFormat format) const { return static_cast(format); } }; } #endif android-file-transfer-linux-4.5/mtp/ptp/ObjectFormat.values.h000066400000000000000000000050621501120463400243260ustar00rootroot00000000000000ENUM_VALUE(Any, 0x0000) ENUM_VALUE(Undefined, 0x3000) ENUM_VALUE(Association, 0x3001) ENUM_VALUE(Script, 0x3002) ENUM_VALUE(Executable, 0x3003) ENUM_VALUE(Text, 0x3004) ENUM_VALUE(Html, 0x3005) ENUM_VALUE(Dpof, 0x3006) ENUM_VALUE(Aiff, 0x3007) ENUM_VALUE(Wav, 0x3008) ENUM_VALUE(Mp3, 0x3009) ENUM_VALUE(Avi, 0x300a) ENUM_VALUE(Mpeg, 0x300b) ENUM_VALUE(Asf, 0x300c) ENUM_VALUE(UndefinedImage, 0x3800) ENUM_VALUE(ExifJpeg, 0x3801) ENUM_VALUE(TiffEp, 0x3802) ENUM_VALUE(Flashpix, 0x3803) ENUM_VALUE(Bmp, 0x3804) ENUM_VALUE(Ciff, 0x3805) ENUM_VALUE(Gif, 0x3807) ENUM_VALUE(Jfif, 0x3808) ENUM_VALUE(Pcd, 0x3809) ENUM_VALUE(Pict, 0x380a) ENUM_VALUE(Png, 0x380b) ENUM_VALUE(Tiff, 0x380d) ENUM_VALUE(TiffIt, 0x380e) ENUM_VALUE(Jp2, 0x380f) ENUM_VALUE(Jpx, 0x3810) ENUM_VALUE(Dng, 0x3811) ENUM_VALUE(Heif, 0x3812) ENUM_VALUE(NetworkAssociation, 0xb102) ENUM_VALUE(M4a, 0xb215) ENUM_VALUE(Artist, 0xb218) ENUM_VALUE(UndefinedFirmware, 0xb802) ENUM_VALUE(WindowsImageFormat, 0xb881) ENUM_VALUE(UndefinedAudio, 0xb900) ENUM_VALUE(Wma, 0xb901) ENUM_VALUE(Ogg, 0xb902) ENUM_VALUE(Aac, 0xb903) ENUM_VALUE(Audible, 0xb904) ENUM_VALUE(Flac, 0xb906) ENUM_VALUE(UndefinedVideo, 0xb980) ENUM_VALUE(Wmv, 0xb981) ENUM_VALUE(Mp4, 0xb982) ENUM_VALUE(Mp2, 0xb983) ENUM_VALUE(_3gp, 0xb984) ENUM_VALUE(UndefinedCollection, 0xba00) ENUM_VALUE(AbstractMultimediaAlbum, 0xba01) ENUM_VALUE(AbstractImageAlbum, 0xba02) ENUM_VALUE(AbstractAudioAlbum, 0xba03) ENUM_VALUE(AbstractVideoAlbum, 0xba04) ENUM_VALUE(AbstractAVPlaylist, 0xba05) ENUM_VALUE(AbstractContactGroup, 0xba06) ENUM_VALUE(AbstractMessageFolder, 0xba07) ENUM_VALUE(AbstractChapteredProduction, 0xba08) ENUM_VALUE(AbstractAudioPlaylist, 0xba09) ENUM_VALUE(AbstractVideoPlaylist, 0xba0a) ENUM_VALUE(AbstractMediacast, 0xba0b) ENUM_VALUE(WplPlaylist, 0xba10) ENUM_VALUE(M3uPlaylist, 0xba11) ENUM_VALUE(MplPlaylist, 0xba12) ENUM_VALUE(AsxPlaylist, 0xba13) ENUM_VALUE(PlsPlaylist, 0xba14) ENUM_VALUE(UndefinedDocument, 0xba80) ENUM_VALUE(AbstractDocument, 0xba81) ENUM_VALUE(XmlDocument, 0xba82) ENUM_VALUE(MicrosoftWordDocument, 0xba83) ENUM_VALUE(MhtCompiledHtmlDocument, 0xba84) ENUM_VALUE(MicrosoftExcelSpreadsheet, 0xba85) ENUM_VALUE(MicrosoftPowerPointPresentation, 0xba86) ENUM_VALUE(UndefinedMessage, 0xbb00) ENUM_VALUE(AbstractMessage, 0xbb01) ENUM_VALUE(UndefinedContact, 0xbb80) ENUM_VALUE(AbstractContact, 0xbb81) ENUM_VALUE(VCard2, 0xbb82) ENUM_VALUE(VCard3, 0xbb83) ENUM_VALUE(UndefinedCalendarItem, 0xbe00) ENUM_VALUE(AbstractCalendarItem, 0xbe01) ENUM_VALUE(VCalendar1, 0xbe02) ENUM_VALUE(VCalendar2, 0xbe03) ENUM_VALUE(UndefinedWindowsExecutable, 0xbe80) android-file-transfer-linux-4.5/mtp/ptp/ObjectId.h000066400000000000000000000037671501120463400221460ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_OBJECTID_H #define AFTL_MTP_PTP_OBJECTID_H #include namespace mtp { #define DECLARE_ID(NAME, TYPE, MEMBER) \ struct NAME { \ TYPE MEMBER; \ static const TYPE Canary = 0xaaaaaaaau; \ \ NAME ( ): MEMBER ( Canary ) { } \ explicit NAME ( TYPE id ): MEMBER ( id ) { } \ \ bool operator == (const NAME &o) const \ { return MEMBER == o.MEMBER; } \ bool operator != (const NAME &o) const \ { return !((*this) == o); } \ bool operator < (const NAME &o) const \ { return MEMBER < o.MEMBER; } \ }; \ template Stream & operator >> (Stream &stream, NAME & value) \ { stream >> value. MEMBER ; return stream; } \ template Stream & operator << (Stream &stream, const NAME &value) \ { stream << value. MEMBER ; return stream; } \ DECLARE_ID(ObjectId, mtp::u32, Id); DECLARE_ID(StorageId, mtp::u32, Id); #undef DECLARE_ID } namespace std { template<> struct hash { public: size_t operator()(const mtp::ObjectId &id) const { return id.Id; } }; template<> struct hash { public: size_t operator()(const mtp::StorageId &id) const { return id.Id; } }; } #endif android-file-transfer-linux-4.5/mtp/ptp/ObjectProperty.cpp000066400000000000000000000042611501120463400237570ustar00rootroot00000000000000#include #include #include namespace mtp { std::string ToString(ObjectProperty property) { switch(property) { # define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_TO_STRING(ObjectProperty, NAME, VALUE) # include ENUM_VALUE_TO_STRING_DEFAULT(ObjectProperty, property, 4); } } namespace { template void ArrayToString(std::stringstream & ss, InputStream & is, u32 size) { ss << "[ "; while(size--) { T value; is >> value; ss << value; if (size) ss << " "; } ss << "]"; } void ToString(std::stringstream & ss, InputStream & is, DataTypeCode type) { if (IsArray(type)) { u32 size = is.Read32(); switch(type) { #define CASE(CODE, TYPE) case CODE : ArrayToString(ss, is, size); break CASE(DataTypeCode::ArrayInt8, s8); CASE(DataTypeCode::ArrayInt16, s16); CASE(DataTypeCode::ArrayInt32, s32); CASE(DataTypeCode::ArrayInt64, s64); CASE(DataTypeCode::ArrayUint16, u16); CASE(DataTypeCode::ArrayUint32, u32); CASE(DataTypeCode::ArrayUint64, u64); case DataTypeCode::ArrayUint8: HexDump(ss, "value", size, is); break; #undef CASE default: ss << "(value of unknown type " << ToString(type) << ")"; } } else { switch(type) { #define CASE(BITS) \ case DataTypeCode::Uint##BITS: \ case DataTypeCode::Int##BITS: \ ss << is.Read##BITS (); break; CASE(8); CASE(16); CASE(32); CASE(64); #undef CASE case mtp::DataTypeCode::Uint128: case mtp::DataTypeCode::Int128: HexDump(ss, "value", 16, is); break; case mtp::DataTypeCode::String: ss << is.ReadString(); break; default: ss << "(value of unknown type " << ToString(type) << ")"; } } } bool IsString(const ByteArray & value) { return true; } } std::string ToString(DataTypeCode type, const ByteArray & value) { std::stringstream ss; InputStream is(value); if (type == DataTypeCode::ArrayUint16 && IsString(value)) { u32 size = is.Read32(); ss << is.ReadString(size); } else ToString(ss, is, type); return ss.str(); } } android-file-transfer-linux-4.5/mtp/ptp/ObjectProperty.h000066400000000000000000000025721501120463400234270ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_OBJECTPROPERTY_H #define AFTL_MTP_PTP_OBJECTPROPERTY_H #include #include #include #include namespace mtp { enum struct ObjectProperty : u16 { #define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_DECL(NAME, VALUE) # include #undef ENUM_VALUE }; DECLARE_ENUM(ObjectProperty, u16); std::string ToString(ObjectProperty property); std::string ToString(DataTypeCode type, const ByteArray & value); } #endif /* OBJECTPROPERTY_H */ android-file-transfer-linux-4.5/mtp/ptp/ObjectProperty.values.h000066400000000000000000000067001501120463400247220ustar00rootroot00000000000000ENUM_VALUE(BuyFlag, 0xd901) ENUM_VALUE(HostEUI64Array, 0xd920) ENUM_VALUE(ContentTypeUUID, 0xda97) //see WPD_OBJECT_CONTENT_TYPE ENUM_VALUE(ArtistId, 0xdab9) ENUM_VALUE(AlbumId, 0xdabb) ENUM_VALUE(StorageId, 0xdc01) ENUM_VALUE(ObjectFormat, 0xdc02) ENUM_VALUE(ProtectionStatus, 0xdc03) ENUM_VALUE(ObjectSize, 0xdc04) ENUM_VALUE(AssociationType, 0xdc05) ENUM_VALUE(AssociationDesc, 0xdc06) ENUM_VALUE(ObjectFilename, 0xdc07) ENUM_VALUE(DateCreated, 0xdc08) ENUM_VALUE(DateModified, 0xdc09) ENUM_VALUE(Keywords, 0xdc0a) ENUM_VALUE(ParentObject, 0xdc0b) ENUM_VALUE(AllowedFolderContents, 0xdc0c) ENUM_VALUE(Hidden, 0xdc0d) ENUM_VALUE(SystemObject, 0xdc0e) ENUM_VALUE(PersistentUniqueObjectId, 0xdc41) ENUM_VALUE(SyncId, 0xdc42) ENUM_VALUE(PropertyBag, 0xdc43) ENUM_VALUE(Name, 0xdc44) ENUM_VALUE(CreatedBy, 0xdc45) ENUM_VALUE(Artist, 0xdc46) ENUM_VALUE(DateAuthored, 0xdc47) ENUM_VALUE(Description, 0xdc48) ENUM_VALUE(UrlReference, 0xdc49) ENUM_VALUE(LanguageLocale, 0xdc4a) ENUM_VALUE(CopyrightInformation, 0xdc4b) ENUM_VALUE(Source, 0xdc4c) ENUM_VALUE(OriginLocation, 0xdc4d) ENUM_VALUE(DateAdded, 0xdc4e) ENUM_VALUE(NonConsumable, 0xdc4f) ENUM_VALUE(CorruptedNonPlayable, 0xdc50) ENUM_VALUE(ProducerSerialNumber, 0xdc51) ENUM_VALUE(RepresentativeSampleFormat, 0xdc81) ENUM_VALUE(RepresentativeSampleSize, 0xdc82) ENUM_VALUE(RepresentativeSampleHeight, 0xdc83) ENUM_VALUE(RepresentativeSampleWidth, 0xdc84) ENUM_VALUE(RepresentativeSampleDuration, 0xdc85) ENUM_VALUE(RepresentativeSampleData, 0xdc86) ENUM_VALUE(Width, 0xdc87) ENUM_VALUE(Height, 0xdc88) ENUM_VALUE(Duration, 0xdc89) ENUM_VALUE(UserRating, 0xdc8a) ENUM_VALUE(Track, 0xdc8b) ENUM_VALUE(Genre, 0xdc8c) ENUM_VALUE(Credits, 0xdc8d) ENUM_VALUE(Lyrics, 0xdc8e) ENUM_VALUE(SubscriptionContentId, 0xdc8f) ENUM_VALUE(ProducedBy, 0xdc90) ENUM_VALUE(UseCount, 0xdc91) ENUM_VALUE(SkipCount, 0xdc92) ENUM_VALUE(LastAccessed, 0xdc93) ENUM_VALUE(ParentalRating, 0xdc94) ENUM_VALUE(MetaGenre, 0xdc95) ENUM_VALUE(Composer, 0xdc96) ENUM_VALUE(EffectiveRating, 0xdc97) ENUM_VALUE(Subtitle, 0xdc98) ENUM_VALUE(OriginalReleaseDate, 0xdc99) ENUM_VALUE(AlbumName, 0xdc9a) ENUM_VALUE(AlbumArtist, 0xdc9b) ENUM_VALUE(Mood, 0xdc9c) ENUM_VALUE(DrmProtectionStatus, 0xdc9d) ENUM_VALUE(Subdescription, 0xdc9e) ENUM_VALUE(IsCropped, 0xdcd1) ENUM_VALUE(IsColourCorrected, 0xdcd2) ENUM_VALUE(ImageBitDepth, 0xdcd3) ENUM_VALUE(FNumber, 0xdcd4) ENUM_VALUE(ExposureTime, 0xdcd5) ENUM_VALUE(ExposureIndex, 0xdcd6) ENUM_VALUE(TotalBitrate, 0xde91) ENUM_VALUE(BitrateType, 0xde92) ENUM_VALUE(SampleRate, 0xde93) ENUM_VALUE(NumberOfChannels, 0xde94) ENUM_VALUE(AudioBitDepth, 0xde95) ENUM_VALUE(BlockAlignment, 0xde96) ENUM_VALUE(ScanType, 0xde97) ENUM_VALUE(ColourRange, 0xde98) ENUM_VALUE(AudioWaveCodec, 0xde99) ENUM_VALUE(AudioBitrate, 0xde9a) ENUM_VALUE(VideoFourCCCodec, 0xde9b) ENUM_VALUE(VideoBitrate, 0xde9c) ENUM_VALUE(FramesPerMilliseconds, 0xde9d) ENUM_VALUE(KeyframeDistance, 0xde9e) ENUM_VALUE(BufferSize, 0xde9f) ENUM_VALUE(EncodingQuality, 0xdea0) ENUM_VALUE(EncodingProfile, 0xdea1) ENUM_VALUE(DisplayName, 0xdce0) ENUM_VALUE(BodyText, 0xdce1) ENUM_VALUE(Subject, 0xdce2) ENUM_VALUE(Priority, 0xdce3) ENUM_VALUE(Owner, 0xdd5d) ENUM_VALUE(Editor, 0xdd5e) ENUM_VALUE(WebMaster, 0xdd5f) ENUM_VALUE(UrlSource, 0xdd60) ENUM_VALUE(UrlDestination, 0xdd61) ENUM_VALUE(TimeBookmark, 0xdd62) ENUM_VALUE(ObjectBookmark, 0xdd63) ENUM_VALUE(ByteBookmark, 0xdd64) ENUM_VALUE(LastBuildDate, 0xdd70) ENUM_VALUE(TimeToLive, 0xdd71) ENUM_VALUE(MediaGUID, 0xdd72) ENUM_VALUE(All, 0xffff) android-file-transfer-linux-4.5/mtp/ptp/ObjectPropertyListParser.h000066400000000000000000000055771501120463400254500ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_OBJECTPROPERTYLISTPARSER_H #define AFTL_MTP_PTP_OBJECTPROPERTYLISTPARSER_H #include #include #include #include #include namespace mtp { namespace impl { template struct ObjectPropertyParser { static PropertyType Parse(InputStream &stream, DataTypeCode dataType) { switch(dataType) { #define HANDLE_TYPE(TYPE, METHOD) case DataTypeCode::TYPE : { return static_cast (stream . METHOD ()) ; } HANDLE_TYPE(Uint8, Read8) HANDLE_TYPE(Uint16, Read16) HANDLE_TYPE(Uint32, Read32) HANDLE_TYPE(Uint64, Read64) HANDLE_TYPE(Int8, Read8) HANDLE_TYPE(Int16, Read16) HANDLE_TYPE(Int32, Read32) HANDLE_TYPE(Int64, Read64) #undef HANDLE_TYPE default: throw std::runtime_error("got invalid type"); } } }; template<> struct ObjectPropertyParser { static std::string Parse(InputStream &stream, DataTypeCode dataType) { if (dataType != DataTypeCode::String) throw std::runtime_error("got invalid type"); std::string value; stream >> value; return value; } }; } template class Parser = impl::ObjectPropertyParser> struct ObjectPropertyListParser { static u32 GetSize(const ByteArray & data) { InputStream stream(data); u32 n; stream >> n; return n; } static void Parse(const ByteArray & data, const std::function &func) { InputStream stream(data); u32 n; stream >> n; while(n--) { ObjectId objectId; ObjectProperty property; DataTypeCode dataType; stream >> objectId; stream >> property; stream >> dataType; PropertyValueType value = Parser::Parse(stream, dataType); func(objectId, property, value); } } }; using ObjectStringPropertyListParser = ObjectPropertyListParser; } #endif android-file-transfer-linux-4.5/mtp/ptp/OperationCode.cpp000066400000000000000000000005301501120463400235320ustar00rootroot00000000000000#include #include namespace mtp { std::string ToString(OperationCode property) { switch(property) { # define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_TO_STRING(OperationCode, NAME, VALUE) # include ENUM_VALUE_TO_STRING_DEFAULT(OperationCode, property, 4); } } } android-file-transfer-linux-4.5/mtp/ptp/OperationCode.h000066400000000000000000000023651501120463400232070ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_OPERATIONCODE_H #define AFTL_MTP_PTP_OPERATIONCODE_H #include namespace mtp { enum class OperationCode : u16 //! MTP operation code { #define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_DECL(NAME, VALUE) # include #undef ENUM_VALUE }; DECLARE_ENUM(OperationCode, u16); std::string ToString(OperationCode property); } #endif /* OPERATIONCODE_H */ android-file-transfer-linux-4.5/mtp/ptp/OperationCode.values.h000066400000000000000000000061401501120463400245000ustar00rootroot00000000000000ENUM_VALUE(GetDeviceInfo, 0x1001) ENUM_VALUE(OpenSession, 0x1002) ENUM_VALUE(CloseSession, 0x1003) ENUM_VALUE(GetStorageIDs, 0x1004) ENUM_VALUE(GetStorageInfo, 0x1005) ENUM_VALUE(GetNumObjects, 0x1006) ENUM_VALUE(GetObjectHandles, 0x1007) ENUM_VALUE(GetObjectInfo, 0x1008) ENUM_VALUE(GetObject, 0x1009) ENUM_VALUE(GetThumb, 0x100a) ENUM_VALUE(DeleteObject, 0x100b) ENUM_VALUE(SendObjectInfo, 0x100c) ENUM_VALUE(SendObject, 0x100d) ENUM_VALUE(InitiateCapture, 0x100e) ENUM_VALUE(FormatStore, 0x100f) ENUM_VALUE(ResetDevice, 0x1010) ENUM_VALUE(SelfTest, 0x1011) ENUM_VALUE(SetObjectProtection, 0x1012) ENUM_VALUE(PowerDown, 0x1013) ENUM_VALUE(GetDevicePropDesc, 0x1014) ENUM_VALUE(GetDevicePropValue, 0x1015) ENUM_VALUE(SetDevicePropValue, 0x1016) ENUM_VALUE(ResetDevicePropValue, 0x1017) ENUM_VALUE(TerminateOpenCapture, 0x1018) ENUM_VALUE(MoveObject, 0x1019) ENUM_VALUE(CopyObject, 0x101a) ENUM_VALUE(GetPartialObject, 0x101b) ENUM_VALUE(InitiateOpenCapture, 0x101c) ENUM_VALUE(CancelTransaction, 0x4001) ENUM_VALUE(JanusGetSecureTimeChallenge, 0x9101) ENUM_VALUE(JanusSetSecureTimeResponse, 0x9102) ENUM_VALUE(JanusSetLicenseResponse, 0x9103) ENUM_VALUE(JanusGetSyncList, 0x9104) ENUM_VALUE(JanusSendMeterChallengeQuery, 0x9105) ENUM_VALUE(JanusGetMeterChallenge, 0x9106) ENUM_VALUE(JanusSetMeterResponse, 0x9107) ENUM_VALUE(JanusCleanDataStore, 0x9108) ENUM_VALUE(JanusGetLicenseState, 0x9109) ENUM_VALUE(JanusSendCommand, 0x910a) ENUM_VALUE(JanusSendRequest, 0x910b) ENUM_VALUE(ProcessWCFObject, 0x9122) ENUM_VALUE(OpenMediaSession, 0x9170) ENUM_VALUE(CloseMediaSession, 0x9171) ENUM_VALUE(GetNextDataBlock, 0x9172) ENUM_VALUE(SetCurrentTimePosition, 0x9173) ENUM_VALUE(WMPMetadataRoundTrip, 0x9201) ENUM_VALUE(WmpGetAcquiredContent, 0x9202) ENUM_VALUE(RebootDevice, 0x9204) ENUM_VALUE(SendWMDRMPDAppRequest, 0x9212) ENUM_VALUE(GetWMDRMPDAppResponse, 0x9213) ENUM_VALUE(EnableTrustedFilesOperations, 0x9214) ENUM_VALUE(DisableTrustedFilesOperations, 0x9215) ENUM_VALUE(EndTrustedAppSession, 0x9216) ENUM_VALUE(GetServiceIDs, 0x9301) ENUM_VALUE(GetServiceInfo, 0x9302) ENUM_VALUE(GetServiceCapabilities, 0x9303) ENUM_VALUE(GetServicePropDesc, 0x9304) ENUM_VALUE(GetServicePropList, 0x9305) ENUM_VALUE(SetServicePropList, 0x9306) ENUM_VALUE(UpdateObjectPropList, 0x9307) ENUM_VALUE(DeleteObjectPropList, 0x9308) ENUM_VALUE(DeleteServicePropList, 0x9309) ENUM_VALUE(GetFormatCapabilities, 0x930a) ENUM_VALUE(SendTinyCLRData, 0x9401) ENUM_VALUE(GetTinyCLRData, 0x9402) ENUM_VALUE(GetPartialObject64, 0x95c1) ENUM_VALUE(SendPartialObject, 0x95c2) ENUM_VALUE(TruncateObject, 0x95c3) ENUM_VALUE(BeginEditObject, 0x95c4) ENUM_VALUE(EndEditObject, 0x95c5) ENUM_VALUE(GetObjectPropsSupported, 0x9801) ENUM_VALUE(GetObjectPropDesc, 0x9802) ENUM_VALUE(GetObjectPropValue, 0x9803) ENUM_VALUE(SetObjectPropValue, 0x9804) ENUM_VALUE(GetObjectPropList, 0x9805) ENUM_VALUE(SetObjectPropList, 0x9806) ENUM_VALUE(GetInterdependentPropDesc, 0x9807) ENUM_VALUE(SendObjectPropList, 0x9808) ENUM_VALUE(GetObjectReferences, 0x9810) ENUM_VALUE(SetObjectReferences, 0x9811) ENUM_VALUE(UpdateDeviceFirmware, 0x9812) ENUM_VALUE(ResetObjectPropValue, 0x9813) ENUM_VALUE(Skip, 0x9820) android-file-transfer-linux-4.5/mtp/ptp/OperationRequest.h000066400000000000000000000060271501120463400237640ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_OPERATIONREQUEST_H #define AFTL_MTP_PTP_OPERATIONREQUEST_H #include #include #include #include namespace mtp { struct RequestBase //! base class for Operation and Data requests { ByteArray Data; //!< resulting data RequestBase(OperationCode opcode, u32 transaction) { OutputStream stream(Data); stream << opcode; //Write(session); //just one field in mtp stream << transaction; } void Append(const ByteArray &data) { std::copy(data.begin(), data.end(), std::back_inserter(Data)); } }; struct OperationRequest : RequestBase //! Operation (Command) class, constructs operation request data in \ref Data { static const ContainerType Type = ContainerType::Command; OperationRequest(OperationCode opcode, u32 transaction): RequestBase(opcode, transaction) { } OperationRequest(OperationCode opcode, u32 transaction, u32 par1): RequestBase(opcode, transaction) { OutputStream stream(Data); stream << par1; } OperationRequest(OperationCode opcode, u32 transaction, u32 par1, u32 par2): RequestBase(opcode, transaction) { OutputStream stream(Data); stream << par1; stream << par2; } OperationRequest(OperationCode opcode, u32 transaction, u32 par1, u32 par2, u32 par3): RequestBase(opcode, transaction) { OutputStream stream(Data); stream << par1; stream << par2; stream << par3; } OperationRequest(OperationCode opcode, u32 transaction, u32 par1, u32 par2, u32 par3, u32 par4): RequestBase(opcode, transaction) { OutputStream stream(Data); stream << par1; stream << par2; stream << par3; stream << par4; } OperationRequest(OperationCode opcode, u32 transaction, u32 par1, u32 par2, u32 par3, u32 par4, u32 par5): RequestBase(opcode, transaction) { OutputStream stream(Data); stream << par1; stream << par2; stream << par3; stream << par4; stream << par5; } }; struct DataRequest : RequestBase //! MTP Data request { static const ContainerType Type = ContainerType::Data; DataRequest(OperationCode opcode, u32 transaction): RequestBase(opcode, transaction) { } }; } #endifandroid-file-transfer-linux-4.5/mtp/ptp/OutputStream.h000066400000000000000000000073111501120463400231240ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_OUTPUTSTREAM_H #define AFTL_MTP_PTP_OUTPUTSTREAM_H #include #include namespace mtp { class OutputStream //! MTP format encoding output stream { ByteArray & _data; public: OutputStream(ByteArray &data): _data(data) { _data.reserve(512); } ByteArray & GetData() { return _data; } const ByteArray & GetData() const { return _data; } void Write8(u8 value) { _data.push_back(value); } void Write16(u16 value) { Write8(value); Write8(value >> 8); } void Write32(u32 value) { Write16(value); Write16(value >> 16); } void Write64(u32 value) { Write32(value); Write32(value >> 16); } static size_t Utf8Length(const std::string &value) { size_t size = 0; for(char c: value) { if ((c & 0x80) == 0 || (c & 0xc0) != 0x80) ++size; } return size; } void WriteData(const ByteArray & data) { _data.reserve(data.size() + _data.size()); for(auto b : data) Write8(b); } void WriteString(const std::string &value) { if (value.empty()) { Write8(0); return; } size_t len = 1 + Utf8Length(value); if (len > 255) throw std::runtime_error("string is too big (only 255 chars allowed, including null terminator)"); Write8(len); for(size_t i = 0, p = 0; i < len && p < value.size(); ++i) { u8 c0 = value[p++]; u16 uni; if (c0 == 0xc0 || c0 == 0xc1 || c0 >= 0xf5) { uni = '?'; } else if (c0 < 0x80) { uni = c0; } else { u8 c1 = value[p++]; if (c0 >= 0xc2 && c0 <= 0xdf) uni = ((c0 & 0x1f) << 6) | (c1 & 0x3f); else { u8 c2 = value[p++]; if (c0 >= 0xe0 && c0 <= 0xef) uni = ((c0 & 0x0f) << 12) | ((c1 & 0x3f) << 6) | (c2 & 0x3f); else uni = '?'; } } Write16(uni); } Write16(0); } template void WriteArray(const ArrayType & array) { Write32(array.size()); for(const auto & el : array) (*this) << el; } }; inline OutputStream & operator << (OutputStream &stream, char value) { stream.Write8(value); return stream; } inline OutputStream & operator << (OutputStream &stream, u8 value) { stream.Write8(value); return stream; } inline OutputStream & operator << (OutputStream &stream, u16 value) { stream.Write16(value); return stream; } inline OutputStream & operator << (OutputStream &stream, u32 value) { stream.Write32(value); return stream; } inline OutputStream & operator << (OutputStream &stream, u64 value) { stream.Write64(value); return stream; } inline OutputStream & operator << (OutputStream &stream, const std::string &value) { stream.WriteString(value); return stream; } template inline OutputStream & operator << (OutputStream &stream, const std::vector &value) { stream.template WriteArray(value); return stream; } } #endif android-file-transfer-linux-4.5/mtp/ptp/PerceivedDeviceType.values.h000066400000000000000000000003121501120463400256300ustar00rootroot00000000000000ENUM_VALUE(GenericMtpDevice, 0) ENUM_VALUE(DigitalStillCamera, 1) ENUM_VALUE(MediaPlayer, 2) ENUM_VALUE(MobilePhone, 3) ENUM_VALUE(DigitalVideoCamera, 4) ENUM_VALUE(PDA, 5) ENUM_VALUE(AudioRecorder, 6) android-file-transfer-linux-4.5/mtp/ptp/PipePacketer.cpp000066400000000000000000000164521501120463400233650ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include namespace mtp { void PipePacketer::Write(const IObjectInputStreamPtr &inputStream, int timeout) { _pipe->Write(inputStream, timeout); } void PipePacketer::Write(const ByteArray &data, int timeout) { Write(std::make_shared(data), timeout); } namespace { class MessageParsingStream final: public JoinedObjectOutputStreamBase { FixedSizeByteArrayObjectOutputStreamPtr _header; IObjectOutputStreamPtr _stream; u64 _offset; u64 _size; private: IObjectOutputStreamPtr GetStream1() const override { return _header; } IObjectOutputStreamPtr GetStream2() const override { return _stream; } public: MessageParsingStream(IObjectOutputStreamPtr stream): _header(new FixedSizeByteArrayObjectOutputStream(4)), _stream(stream), _offset(0), _size(4) { } bool Finished() const { return _offset >= _size; } void OnStream1Exhausted() override { InputStream is(_header->GetData()); u32 size; is >> size; if (size < 4) throw std::runtime_error("invalid size/malformed message"); _size = size; } size_t Write(const u8 *data, size_t size) override { size_t r = JoinedObjectOutputStreamBase::Write(data, size); _offset += r; if (!_stream1Exhausted && _offset >= _header->GetData().size()) { _stream1Exhausted = true; OnStream1Exhausted(); } return r; } }; DECLARE_PTR(MessageParsingStream); } void PipePacketer::ReadMessage(const IObjectOutputStreamPtr &outputStream, int timeout) { _pipe->Read(outputStream, timeout); } void PipePacketer::PollEvent(int timeout) { ByteArray interruptData = _pipe->ReadInterrupt(timeout); if (interruptData.empty()) return; HexDump("interrupt", interruptData); InputStream stream(interruptData); ContainerType containerType; u32 size; EventCode eventCode; u32 sessionId; u32 transactionId; stream >> size; stream >> containerType; stream >> eventCode; stream >> sessionId; stream >> transactionId; if (containerType != ContainerType::Event) throw std::runtime_error("not an event"); debug("event ", hex(eventCode, 8)); } namespace { struct DummyOutputStream final: IObjectOutputStream, public CancellableStream { size_t Write(const u8 *data, size_t size) override { return size; } }; class HeaderParserObjectOutputStream final: public JoinedObjectOutputStreamBase { size_t _offset; u32 _transaction; FixedSizeByteArrayObjectOutputStreamPtr _header; ByteArrayObjectOutputStreamPtr _response; IObjectOutputStreamPtr _dataOutput; IObjectOutputStreamPtr _output; bool _valid; bool _finished; ResponseType _responseCode; private: virtual IObjectOutputStreamPtr GetStream1() const override { return _header; } virtual IObjectOutputStreamPtr GetStream2() const override { if (!_output) throw std::runtime_error("no data stream"); return _output; } void OnStream1Exhausted() override { InputStream stream(_header->GetData()); Response header; header.Read(stream); if (_transaction && _transaction != header.Transaction) { error("drop message ", hex(header.ContainerType, 4), ", response: ", hex(header.ResponseType, 4), ", transaction: ", hex(header.Transaction, 8), ", transaction: ", hex(_transaction, 8)); _valid = false; _output = std::make_shared(); return; } switch(header.ContainerType) { case ContainerType::Data: _output = _dataOutput; break; case ContainerType::Response: _output = _response; _responseCode = header.ResponseType; _finished = true; break; default: _valid = false; _output = std::make_shared(); } } public: HeaderParserObjectOutputStream(u32 transaction, IObjectOutputStreamPtr dataOutput): _offset(0), _transaction(transaction), _header(new FixedSizeByteArrayObjectOutputStream(Response::Size)), _response(new ByteArrayObjectOutputStream), _dataOutput(dataOutput), _valid(true), _finished(false) { } ResponseType GetResponseCode() const { return _responseCode; } const ByteArray &GetResponse() const { return _response->GetData(); } bool Valid() const { return _valid; } bool Finished() const { return _finished; } size_t Write(const u8 *data, size_t size) override { size_t r = JoinedObjectOutputStreamBase::Write(data, size); _offset += r; if (!_stream1Exhausted && _offset >= _header->GetData().size()) { _stream1Exhausted = true; OnStream1Exhausted(); } return r; } }; DECLARE_PTR(HeaderParserObjectOutputStream); } void PipePacketer::Read(u32 transaction, const IObjectOutputStreamPtr &object, ResponseType &code, ByteArray &response, int timeout) { response.clear(); HeaderParserObjectOutputStreamPtr parser; MessageParsingStreamPtr output; while(true) { if (!parser) parser.reset(new HeaderParserObjectOutputStream(transaction, object)); if (!output) output.reset(new MessageParsingStream(parser)); ReadMessage(output, timeout); if (parser->Finished()) { response = parser->GetResponse(); code = parser->GetResponseCode(); break; } if (output->Finished()) { parser.reset(); output.reset(); } } //HexDump("response", response); } void PipePacketer::Read(u32 transaction, ByteArray &data, ResponseType &code, ByteArray &response, int timeout) { ByteArrayObjectOutputStreamPtr stream(new ByteArrayObjectOutputStream); Read(transaction, stream, code, response, timeout); data = stream->GetData(); } void PipePacketer::Abort(u32 transaction, int timeout) { _pipe->Cancel(); OperationRequest req(OperationCode::CancelTransaction, transaction); HexDump("abort control message", req.Data); /* 0x21: host-to-device, class specific, recipient - interface, 0x64: cancel request */ _pipe->GetDevice()->WriteControl( (u8)(usb::RequestType::HostToDevice | usb::RequestType::Class | usb::RequestType::Interface), 0x64, 0, _pipe->GetInterface()->GetIndex(), req.Data, timeout); } } android-file-transfer-linux-4.5/mtp/ptp/PipePacketer.h000066400000000000000000000035471501120463400230330ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_PIPEPACKETER_H #define AFTL_MTP_PTP_PIPEPACKETER_H #include #include #include namespace mtp { class Device; DECLARE_PTR(Device); class PipePacketer //! BulkPipe high-level controller class, package all read/write operation into streams and send it to BulkPipe { usb::BulkPipePtr _pipe; public: PipePacketer(const usb::BulkPipePtr &pipe): _pipe(pipe) { } usb::BulkPipePtr GetPipe() const { return _pipe; } void Write(const IObjectInputStreamPtr &inputStream, int timeout); void Write(const ByteArray &data, int timeout); void Read(u32 transaction, const IObjectOutputStreamPtr &outputStream, ResponseType &code, ByteArray &response, int timeout); void Read(u32 transaction, ByteArray &data, ResponseType &code, ByteArray &response, int timeout); void PollEvent(int timeout); void Abort(u32 transaction, int timeout); private: void ReadMessage(const IObjectOutputStreamPtr &outputStream, int timeout); }; } #endif android-file-transfer-linux-4.5/mtp/ptp/Response.cpp000066400000000000000000000032121501120463400225750ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include namespace mtp { # define R(NAME) case ResponseType::NAME: return #NAME std::string ToString(ResponseType responseType) { switch(responseType) { # define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_TO_STRING(ResponseType, NAME, VALUE) # include ENUM_VALUE_TO_STRING_DEFAULT(ResponseType, responseType, 4); } } namespace { std::string FormatMessage(ResponseType r) { char buf[1024]; snprintf(buf, sizeof(buf), "invalid response code %s (0x%04hx)", ToString(r).c_str(), static_cast(r)); return buf; } } InvalidResponseException::InvalidResponseException(const std::string &where, ResponseType type): std::runtime_error(where + ": " + FormatMessage(type)), Type(type) { } } android-file-transfer-linux-4.5/mtp/ptp/Response.h000066400000000000000000000037301501120463400222470ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_RESPONSE_H #define AFTL_MTP_PTP_RESPONSE_H #include #include namespace mtp { enum struct ContainerType : u16 { Command = 1, Data = 2, Response = 3, Event = 4, }; DECLARE_ENUM(ContainerType, u16); enum struct ResponseType : u16 { #define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_DECL(NAME, VALUE) # include #undef ENUM_VALUE }; std::string ToString(ResponseType response); DECLARE_ENUM(ResponseType, u16); struct Response //! MTP Response class { static const size_t Size = 8; mtp::ContainerType ContainerType; mtp::ResponseType ResponseType; u32 Transaction; Response() { } template Response(Stream &stream) { Read(stream); } template void Read(Stream &stream) { stream >> ContainerType; stream >> ResponseType; stream >> Transaction; } }; struct InvalidResponseException : public std::runtime_error //!Invalid MTP Response Exception { ResponseType Type; InvalidResponseException(const std::string &where, ResponseType type); }; } #endif /* RESPONSE_H */ android-file-transfer-linux-4.5/mtp/ptp/ResponseType.values.h000066400000000000000000000034231501120463400244060ustar00rootroot00000000000000ENUM_VALUE(OK, 0x2001) ENUM_VALUE(GeneralError, 0x2002) ENUM_VALUE(SessionNotOpen, 0x2003) ENUM_VALUE(InvalidTransaction, 0x2004) ENUM_VALUE(OperationNotSupported, 0x2005) ENUM_VALUE(ParameterNotSupported, 0x2006) ENUM_VALUE(IncompleteTransfer, 0x2007) ENUM_VALUE(InvalidStorageID, 0x2008) ENUM_VALUE(InvalidObjectHandle, 0x2009) ENUM_VALUE(DevicePropNotSupported, 0x200a) ENUM_VALUE(InvalidObjectFormatCode, 0x200b) ENUM_VALUE(StoreFull, 0x200c) ENUM_VALUE(ObjectWriteProtected, 0x200d) ENUM_VALUE(StoreReadOnly, 0x200e) ENUM_VALUE(AccessDenied, 0x200f) ENUM_VALUE(NoThumbnailPresent, 0x2010) ENUM_VALUE(SelfTestFailed, 0x2011) ENUM_VALUE(PartialDeletion, 0x2012) ENUM_VALUE(StoreNotAvailable, 0x2013) ENUM_VALUE(SpecificationByFormatUnsupported, 0x2014) ENUM_VALUE(NoValidObjectInfo, 0x2015) ENUM_VALUE(InvalidCodeFormat, 0x2016) ENUM_VALUE(UnknownVendorCode, 0x2017) ENUM_VALUE(CaptureAlreadyTerminated, 0x2018) ENUM_VALUE(DeviceBusy, 0x2019) ENUM_VALUE(InvalidParentObject, 0x201a) ENUM_VALUE(InvalidDevicePropFormat, 0x201b) ENUM_VALUE(InvalidDevicePropValue, 0x201c) ENUM_VALUE(InvalidParameter, 0x201d) ENUM_VALUE(SessionAlreadyOpen, 0x201e) ENUM_VALUE(TransactionCancelled, 0x201f) ENUM_VALUE(SpecificationOfDestinationUnsupported, 0x2020) ENUM_VALUE(InvalidServiceID, 0xa301) ENUM_VALUE(InvalidServicePropCode, 0xa302) ENUM_VALUE(TinyCLRNotResponding, 0xa401) ENUM_VALUE(NoDataWaiting, 0xa402) ENUM_VALUE(InvalidObjectPropCode, 0xa801) ENUM_VALUE(InvalidObjectPropFormat, 0xa802) ENUM_VALUE(InvalidObjectPropValue, 0xa803) ENUM_VALUE(InvalidObjectReference, 0xa804) ENUM_VALUE(GroupNotSupported, 0xa805) ENUM_VALUE(InvalidDataset, 0xa806) ENUM_VALUE(UnsupportedSpecByGroup, 0xa807) ENUM_VALUE(UnsupportedSpecByDepth, 0xa808) ENUM_VALUE(ObjectTooLarge, 0xa809) ENUM_VALUE(ObjectPropNotSupported, 0xa80) android-file-transfer-linux-4.5/mtp/ptp/Session.cpp000066400000000000000000000522651501120463400224360ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include namespace mtp { const StorageId Session::AnyStorage(0); const StorageId Session::AllStorages(0xffffffffu); const ObjectId Session::Device(0); const ObjectId Session::Root(0xffffffffu); #define CHECK_RESPONSE(RCODE) do { \ if ((RCODE) != ResponseType::OK && (RCODE) != ResponseType::SessionAlreadyOpen) \ throw InvalidResponseException(__func__, (RCODE)); \ } while(false) class Session::Transaction { Session * _session; public: u32 Id; Transaction(Session *session): _session(session) { session->SetCurrentTransaction(this); } ~Transaction() { _session->SetCurrentTransaction(0); } }; Session::Session(const PipePacketer & packeter, u32 sessionId): _packeter(packeter), _sessionId(sessionId), _nextTransactionId(1), _transaction(), _getObjectModificationTimeBuggy(false), _separateBulkWrites(false), _defaultTimeout(DefaultTimeout) { { Transaction tr(this); _deviceInfo = GetDeviceInfo(_packeter, tr.Id, _defaultTimeout); } if (_deviceInfo.Manufacturer == "Microsoft") _separateBulkWrites = true; _getPartialObject64Supported = _deviceInfo.Supports(OperationCode::GetPartialObject64); _getObjectPropertyListSupported = _deviceInfo.Supports(OperationCode::GetObjectPropList); _getObjectPropValueSupported = _deviceInfo.Supports(OperationCode::GetObjectPropValue); _editObjectSupported = _deviceInfo.Supports(OperationCode::BeginEditObject) && _deviceInfo.Supports(OperationCode::EndEditObject) && _deviceInfo.Supports(OperationCode::TruncateObject) && _deviceInfo.Supports(OperationCode::SendPartialObject); } Session::~Session() { try { Close(); } catch(const std::exception &ex) { } } void Session::SetCurrentTransaction(Transaction *transaction) { scoped_mutex_lock l(_transactionMutex); _transaction = transaction; if (_transaction) _transaction->Id = _nextTransactionId++; } void Session::Send(PipePacketer &packeter, const OperationRequest &req, int timeout) { if (timeout <= 0) timeout = DefaultTimeout; Container container(req); packeter.Write(container.Data, timeout); } void Session::Send(const OperationRequest &req, int timeout) { if (timeout <= 0) timeout = _defaultTimeout; Send(_packeter, req, timeout); } void Session::Close() { scoped_mutex_lock l(_mutex); Transaction transaction(this); Send(OperationRequest(OperationCode::CloseSession, transaction.Id)); ByteArray data, response; ResponseType responseCode; _packeter.Read(0, data, responseCode, response, _defaultTimeout); //HexDump("payload", data); } ByteArray Session::Get(PipePacketer &packeter, u32 transaction, ByteArray & response, int timeout) { if (timeout <= 0) timeout = DefaultTimeout; ByteArray data; ResponseType responseCode; packeter.Read(transaction, data, responseCode, response, timeout); CHECK_RESPONSE(responseCode); return data; } ByteArray Session::Get(u32 transaction, ByteArray &response, int timeout) { if (timeout <= 0) timeout = _defaultTimeout; return Get(_packeter, transaction, response, timeout); } template ByteArray Session::RunTransactionWithDataRequest(int timeout, OperationCode code, ByteArray & response, const IObjectInputStreamPtr & inputStream, Args && ... args) { #if 0 try { _packeter.PollEvent(_defaultTimeout); } catch(const std::exception &ex) { error("exception in interrupt: ", ex.what()); } #endif scoped_mutex_lock l(_mutex); if (!_deviceInfo.Supports(code)) throw std::runtime_error("Operation code " + ToString(code) + " not supported."); Transaction transaction(this); Send(OperationRequest(code, transaction.Id, std::forward(args) ... ), timeout); if (inputStream) { DataRequest req(code, transaction.Id); Container container(req, inputStream); if (_separateBulkWrites) { _packeter.Write(std::make_shared(container.Data), timeout); _packeter.Write(inputStream, timeout); } else _packeter.Write(std::make_shared(std::make_shared(container.Data), inputStream), timeout); } return Get(transaction.Id, response); } msg::DeviceInfo Session::GetDeviceInfo(PipePacketer& packeter, u32 transactionId, int timeout) { ByteArray response; Send(packeter, OperationRequest(OperationCode::GetDeviceInfo, transactionId), timeout); auto info = Get(packeter, 0, response, timeout); return ParseResponse(info); } msg::ObjectHandles Session::GetObjectHandles(StorageId storageId, ObjectFormat objectFormat, ObjectId parent, int timeout) { return ParseResponse(RunTransaction(timeout, OperationCode::GetObjectHandles, storageId.Id, static_cast(objectFormat), parent.Id)); } msg::StorageIDs Session::GetStorageIDs() { return ParseResponse(RunTransaction(_defaultTimeout, OperationCode::GetStorageIDs)); } msg::StorageInfo Session::GetStorageInfo(StorageId storageId) { return ParseResponse(RunTransaction(_defaultTimeout, OperationCode::GetStorageInfo, storageId.Id)); } msg::SendObjectPropListResponse Session::SendObjectPropList(StorageId storageId, ObjectId parentId, ObjectFormat format, u64 objectSize, const ByteArray & propList) { ByteArray responseData; IObjectInputStreamPtr inputStream = std::make_shared(propList); RunTransactionWithDataRequest(_defaultTimeout, OperationCode::SendObjectPropList, responseData, inputStream, storageId.Id, parentId.Id, static_cast(format), static_cast(objectSize >> 32), static_cast(objectSize)); return ParseResponse(responseData); } msg::NewObjectInfo Session::CreateDirectory(const std::string &name, ObjectId parentId, StorageId storageId, AssociationType type) { if (_deviceInfo.Supports(OperationCode::SendObjectPropList)) { //modern way of creating objects ByteArray propList; { OutputStream os(propList); os.Write32(1); //number of props os.Write32(0); //object handle os.Write16(static_cast(ObjectProperty::ObjectFilename)); os.Write16(static_cast(DataTypeCode::String)); os.WriteString(name); } auto response = SendObjectPropList(storageId, parentId, ObjectFormat::Association, 0, propList); msg::NewObjectInfo noi; noi.StorageId = response.StorageId; noi.ParentObjectId = response.ParentObjectId; noi.ObjectId = response.ObjectId; return noi; } else { mtp::msg::ObjectInfo oi; oi.Filename = name; oi.ParentObject = parentId; oi.StorageId = storageId; oi.ObjectFormat = mtp::ObjectFormat::Association; oi.AssociationType = type; return SendObjectInfo(oi, storageId, parentId); } } msg::ObjectInfo Session::GetObjectInfo(ObjectId objectId) { return ParseResponse(RunTransaction(_defaultTimeout, OperationCode::GetObjectInfo, objectId.Id)); } msg::ObjectPropertiesSupported Session::GetObjectPropertiesSupported(ObjectFormat format) { return ParseResponse(RunTransaction(_defaultTimeout, OperationCode::GetObjectPropsSupported, static_cast(format))); } ByteArray Session::GetObjectPropertyDesc(ObjectProperty code, ObjectFormat format) { return RunTransaction(_defaultTimeout, OperationCode::GetObjectPropDesc, static_cast(code), static_cast(format)); } void Session::GetObject(ObjectId objectId, const IObjectOutputStreamPtr &outputStream) { scoped_mutex_lock l(_mutex); Transaction transaction(this); Send(OperationRequest(OperationCode::GetObject, transaction.Id, objectId.Id)); ByteArray response; ResponseType responseCode; _packeter.Read(transaction.Id, outputStream, responseCode, response, _defaultTimeout); CHECK_RESPONSE(responseCode); } void Session::GetThumb(ObjectId objectId, const IObjectOutputStreamPtr &outputStream) { scoped_mutex_lock l(_mutex); Transaction transaction(this); Send(OperationRequest(OperationCode::GetThumb, transaction.Id, objectId.Id)); ByteArray response; ResponseType responseCode; _packeter.Read(transaction.Id, outputStream, responseCode, response, _defaultTimeout); CHECK_RESPONSE(responseCode); } ByteArray Session::GetPartialObject(ObjectId objectId, u64 offset, u32 size) { if (_getPartialObject64Supported) return RunTransaction(_defaultTimeout, OperationCode::GetPartialObject64, objectId.Id, offset, offset >> 32, size); else { if (offset + size > std::numeric_limits::max()) throw std::runtime_error("32 bit overflow for GetPartialObject"); return RunTransaction(_defaultTimeout, OperationCode::GetPartialObject, objectId.Id, offset, size); } } msg::NewObjectInfo Session::SendObjectInfo(const msg::ObjectInfo &objectInfo, StorageId storageId, ObjectId parentObject) { if (objectInfo.Filename.empty()) throw std::runtime_error("object filename must not be empty"); if (_deviceInfo.Supports(OperationCode::SendObjectPropList)) { //modern way of creating objects ByteArray propList; { OutputStream os(propList); os.Write32(1); //number of props os.Write32(0); //object handle os.Write16(static_cast(ObjectProperty::ObjectFilename)); os.Write16(static_cast(DataTypeCode::String)); os.WriteString(objectInfo.Filename); } auto response = SendObjectPropList(storageId, parentObject, objectInfo.ObjectFormat, objectInfo.ObjectCompressedSize, propList); msg::NewObjectInfo noi; noi.StorageId = response.StorageId; noi.ParentObjectId = response.ParentObjectId; noi.ObjectId = response.ObjectId; return noi; } scoped_mutex_lock l(_mutex); Transaction transaction(this); Send(OperationRequest(OperationCode::SendObjectInfo, transaction.Id, storageId.Id, parentObject.Id)); { DataRequest req(OperationCode::SendObjectInfo, transaction.Id); if (_separateBulkWrites) { ByteArray data; OutputStream os(data); objectInfo.Write(os); auto is = std::make_shared(data); Container container(req, is); _packeter.Write(container.Data, _defaultTimeout); _packeter.Write(is, _defaultTimeout); } else { OutputStream stream(req.Data); objectInfo.Write(stream); Container container(req); _packeter.Write(container.Data, _defaultTimeout); } } ByteArray data, response; ResponseType responseCode; _packeter.Read(transaction.Id, data, responseCode, response, _defaultTimeout); //HexDump("response", response); CHECK_RESPONSE(responseCode); return ParseResponse(response); } void Session::SendObject(const IObjectInputStreamPtr &inputStream, int timeout) { scoped_mutex_lock l(_mutex); Transaction transaction(this); Send(OperationRequest(OperationCode::SendObject, transaction.Id)); { DataRequest req(OperationCode::SendObject, transaction.Id); Container container(req, inputStream); if (_separateBulkWrites) { _packeter.Write(container.Data, timeout); _packeter.Write(inputStream, timeout); } else _packeter.Write(std::make_shared(std::make_shared(container.Data), inputStream), timeout); } ByteArray response; Get(transaction.Id, response); } void Session::BeginEditObject(ObjectId objectId) { RunTransaction(_defaultTimeout, OperationCode::BeginEditObject, objectId.Id); } void Session::SendPartialObject(ObjectId objectId, u64 offset, const ByteArray &data) { IObjectInputStreamPtr inputStream = std::make_shared(data); ByteArray response; RunTransactionWithDataRequest(_defaultTimeout, OperationCode::SendPartialObject, response, inputStream, objectId.Id, offset, offset >> 32, data.size()); } void Session::TruncateObject(ObjectId objectId, u64 size) { RunTransaction(_defaultTimeout, OperationCode::TruncateObject, objectId.Id, size, size >> 32); } void Session::EndEditObject(ObjectId objectId) { RunTransaction(_defaultTimeout, OperationCode::EndEditObject, objectId.Id); } void Session::SetObjectProperty(ObjectId objectId, ObjectProperty property, const ByteArray &value) { IObjectInputStreamPtr inputStream = std::make_shared(value); ByteArray response; RunTransactionWithDataRequest(_defaultTimeout, OperationCode::SetObjectPropValue, response, inputStream, objectId.Id, (u16)property); } StorageId Session::GetObjectStorage(mtp::ObjectId id) { StorageId storageId(GetObjectIntegerProperty(id, ObjectProperty::StorageId)); if (storageId == AnyStorage || storageId == AllStorages) throw std::runtime_error("returned wildcard storage id as storage for object"); return storageId; } ObjectId Session::GetObjectParent(mtp::ObjectId id) { return ObjectId(GetObjectIntegerProperty(id, ObjectProperty::ParentObject)); } void Session::SetObjectProperty(ObjectId objectId, ObjectProperty property, const std::string &value) { ByteArray data; data.reserve(value.size() * 2 + 1); OutputStream stream(data); stream << value; SetObjectProperty(objectId, property, data); } ByteArray Session::GetObjectProperty(ObjectId objectId, ObjectProperty property) { return RunTransaction(_defaultTimeout, OperationCode::GetObjectPropValue, objectId.Id, (u16)property); } u64 Session::GetObjectIntegerProperty(ObjectId objectId, ObjectProperty property) { if (!_getObjectPropValueSupported) { auto info = GetObjectInfo(objectId); switch(property) { case ObjectProperty::StorageId: return info.StorageId.Id; case ObjectProperty::ObjectFormat: return static_cast(info.ObjectFormat); case ObjectProperty::ProtectionStatus: return info.ProtectionStatus; case ObjectProperty::ObjectSize: return info.ObjectCompressedSize; case ObjectProperty::RepresentativeSampleFormat: return info.ThumbFormat; case ObjectProperty::RepresentativeSampleSize: return info.ThumbCompressedSize; case ObjectProperty::RepresentativeSampleWidth: return info.ThumbPixWidth; case ObjectProperty::RepresentativeSampleHeight: return info.ThumbPixHeight; case ObjectProperty::Width: return info.ImagePixWidth; case ObjectProperty::Height: return info.ImagePixHeight; case ObjectProperty::ImageBitDepth: return info.ImageBitDepth; case ObjectProperty::ParentObject: return info.ParentObject.Id; case ObjectProperty::AssociationType: return static_cast(info.AssociationType); case ObjectProperty::AssociationDesc: return info.AssociationDesc; default: throw std::runtime_error("Device does not support object properties and no ObjectInfo fallback for " + ToString(property) + "."); } } return ReadSingleInteger(GetObjectProperty(objectId, property)); } void Session::SetObjectProperty(ObjectId objectId, ObjectProperty property, u64 value) { std::array data; std::fill(data.begin(), data.end(), 0); size_t i; for(i = 0; i < data.size() && value != 0; ++i, value >>= 8) { data[i] = value; } if (i <= 4) i = 4; else i = 8; SetObjectProperty(objectId, property, ByteArray(data.begin(), data.begin() + i)); } void Session::SetObjectPropertyAsArray(ObjectId objectId, ObjectProperty property, const ByteArray &value) { auto n = value.size(); ByteArray array; OutputStream out(array); array.reserve(n + 4); out.WriteArray(value); SetObjectProperty(objectId, property, array); } std::string Session::GetObjectStringProperty(ObjectId objectId, ObjectProperty property) { if (!_getObjectPropValueSupported) { auto info = GetObjectInfo(objectId); switch(property) { case ObjectProperty::ObjectFilename: return info.Filename; case ObjectProperty::DateCreated: case ObjectProperty::DateAuthored: case ObjectProperty::DateAdded: return info.CaptureDate; case ObjectProperty::DateModified: return info.ModificationDate; default: throw std::runtime_error("Device does not support object properties and no ObjectInfo fallback for " + ToString(property) + "."); } } return ReadSingleString(GetObjectProperty(objectId, property)); } time_t Session::GetObjectModificationTime(ObjectId id) { if (!_getObjectModificationTimeBuggy) { try { auto mtimeStr = GetObjectStringProperty(id, mtp::ObjectProperty::DateModified); auto mtime = mtp::ConvertDateTime(mtimeStr); if (mtime != 0) //long standing Android bug return mtime; } catch(const std::exception &ex) { debug("exception while getting mtime: ", ex.what()); } _getObjectModificationTimeBuggy = true; } auto oi = GetObjectInfo(id); return mtp::ConvertDateTime(oi.ModificationDate); } u64 Session::GetDeviceIntegerProperty(DeviceProperty property) { return ReadSingleInteger(GetDeviceProperty(property)); } std::string Session::GetDeviceStringProperty(DeviceProperty property) { return ReadSingleString(GetDeviceProperty(property)); } void Session::SetDeviceProperty(DeviceProperty property, const ByteArray &value) { IObjectInputStreamPtr inputStream = std::make_shared(value); ByteArray response; RunTransactionWithDataRequest(_defaultTimeout, OperationCode::SetDevicePropValue, response, inputStream, (u16)property); } void Session::SetDeviceProperty(DeviceProperty property, const std::string &value) { ByteArray data; data.reserve(value.size() * 2 + 1); OutputStream stream(data); stream << value; SetDeviceProperty(property, data); } ByteArray Session::GetObjectPropertyList(ObjectId objectId, ObjectFormat format, ObjectProperty property, u32 groupCode, u32 depth, int timeout) { return RunTransaction(timeout, OperationCode::GetObjectPropList, objectId.Id, (u32)format, property != ObjectProperty::All? (u32)property: 0xffffffffu, groupCode, depth); } void Session::DeleteObject(ObjectId objectId, int timeout) { RunTransaction(timeout, OperationCode::DeleteObject, objectId.Id, 0); } msg::DevicePropertyDesc Session::GetDevicePropertyDesc(DeviceProperty property) { return ParseResponse(RunTransaction(_defaultTimeout, OperationCode::GetDevicePropDesc, static_cast(property))); } ByteArray Session::GetDeviceProperty(DeviceProperty property) { return RunTransaction(_defaultTimeout, OperationCode::GetDevicePropValue, static_cast(property)); } ByteArray Session::GenericOperation(OperationCode code) { return RunTransaction(_defaultTimeout, code); } ByteArray Session::GenericOperation(OperationCode code, const ByteArray & payload) { IObjectInputStreamPtr inputStream = std::make_shared(payload); ByteArray response; return RunTransactionWithDataRequest(_defaultTimeout, code, response, inputStream); } void Session::EnableSecureFileOperations(u32 cmac[4]) { RunTransaction(_defaultTimeout, OperationCode::EnableTrustedFilesOperations, cmac[0], cmac[1], cmac[2], cmac[3]); } void Session::RebootDevice() { RunTransaction(_defaultTimeout, OperationCode::RebootDevice); } Session::ObjectEditSession::ObjectEditSession(const SessionPtr & session, ObjectId objectId): _session(session), _objectId(objectId) { session->BeginEditObject(objectId); } Session::ObjectEditSession::~ObjectEditSession() { _session->EndEditObject(_objectId); } void Session::ObjectEditSession::Truncate(u64 size) { _session->TruncateObject(_objectId, size); } void Session::ObjectEditSession::Send(u64 offset, const ByteArray &data) { _session->SendPartialObject(_objectId, offset, data); } void Session::AbortCurrentTransaction(int timeout) { u32 transactionId; { scoped_mutex_lock l(_transactionMutex); if (!_transaction) throw std::runtime_error("no transaction in progress"); transactionId = _transaction->Id; } _packeter.Abort(transactionId, timeout); } void Session::SetObjectReferences(ObjectId objectId, const msg::ObjectHandles & objects) { ByteArray data; OutputStream out(data); objects.Write(out); IObjectInputStreamPtr inputStream = std::make_shared(data); ByteArray response; RunTransactionWithDataRequest(_defaultTimeout, OperationCode::SetObjectReferences, response, inputStream, objectId.Id); } msg::ObjectHandles Session::GetObjectReferences(ObjectId objectId) { return ParseResponse(RunTransaction(_defaultTimeout, OperationCode::GetObjectReferences, objectId.Id)); } } android-file-transfer-linux-4.5/mtp/ptp/Session.h000066400000000000000000000157531501120463400221040ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_PTP_SESSION_H #define AFTL_MTP_PTP_SESSION_H #include #include #include #include #include #include #include #include namespace mtp { struct OperationRequest; class Session; DECLARE_PTR(Session); class Session //! Main MTP interaction / object manipulation class { class Transaction; std::mutex _mutex, _transactionMutex; PipePacketer _packeter; u32 _sessionId; u32 _nextTransactionId; Transaction * _transaction; msg::DeviceInfo _deviceInfo; bool _getPartialObject64Supported; bool _editObjectSupported; bool _getObjectPropertyListSupported; bool _getObjectPropValueSupported; bool _getObjectModificationTimeBuggy; bool _separateBulkWrites; int _defaultTimeout; public: static constexpr int DefaultTimeout = 10000; static constexpr int LongTimeout = 30000; static const StorageId AllStorages; static const StorageId AnyStorage; static const ObjectId Device; static const ObjectId Root; ///sub-session object which handles partial writes and truncation class ObjectEditSession : Noncopyable { SessionPtr _session; ObjectId _objectId; public: ObjectEditSession(const SessionPtr & session, ObjectId objectId); ~ObjectEditSession(); void Truncate(u64 size); void Send(u64 offset, const ByteArray &data); }; DECLARE_PTR(ObjectEditSession); Session(const PipePacketer & packeter, u32 sessionId); ~Session(); const msg::DeviceInfo & GetDeviceInfo() const { return _deviceInfo; } msg::ObjectHandles GetObjectHandles(StorageId storageId = AllStorages, ObjectFormat objectFormat = ObjectFormat::Any, ObjectId parent = Device, int timeout = LongTimeout); msg::StorageIDs GetStorageIDs(); msg::StorageInfo GetStorageInfo(StorageId storageId); msg::NewObjectInfo CreateDirectory(const std::string &name, ObjectId parentId, StorageId storageId = AnyStorage, AssociationType type = AssociationType::GenericFolder); msg::ObjectInfo GetObjectInfo(ObjectId objectId); void GetObject(ObjectId objectId, const IObjectOutputStreamPtr &outputStream); void GetThumb(ObjectId objectId, const IObjectOutputStreamPtr &outputStream); ByteArray GetPartialObject(ObjectId objectId, u64 offset, u32 size); msg::NewObjectInfo SendObjectInfo(const msg::ObjectInfo &objectInfo, StorageId storageId = AnyStorage, ObjectId parentObject = Device); void SendObject(const IObjectInputStreamPtr &inputStream, int timeout = LongTimeout); void DeleteObject(ObjectId objectId, int timeout = LongTimeout); bool EditObjectSupported() const { return _editObjectSupported; } bool GetObjectPropertyListSupported() const { return _getObjectPropertyListSupported; } static ObjectEditSessionPtr EditObject(const SessionPtr &session, ObjectId objectId) { return std::make_shared(session, objectId); } msg::ObjectPropertiesSupported GetObjectPropertiesSupported(ObjectFormat format); ByteArray GetObjectPropertyDesc(ObjectProperty code, ObjectFormat format); void SetObjectProperty(ObjectId objectId, ObjectProperty property, const ByteArray &value); void SetObjectProperty(ObjectId objectId, ObjectProperty property, u64 value); void SetObjectProperty(ObjectId objectId, ObjectProperty property, const std::string &value); void SetObjectPropertyAsArray(ObjectId objectId, ObjectProperty property, const ByteArray &value); time_t GetObjectModificationTime(ObjectId id); //common properties shortcuts StorageId GetObjectStorage(ObjectId id); ObjectId GetObjectParent(ObjectId id); ByteArray GetObjectProperty(ObjectId objectId, ObjectProperty property); u64 GetObjectIntegerProperty(ObjectId objectId, ObjectProperty property); std::string GetObjectStringProperty(ObjectId objectId, ObjectProperty property); ByteArray GetObjectPropertyList(ObjectId objectId, ObjectFormat format, ObjectProperty property, u32 groupCode, u32 depth, int timeout = LongTimeout); msg::SendObjectPropListResponse SendObjectPropList(StorageId storageId, ObjectId parentId, ObjectFormat format, u64 objectSize, const ByteArray & propList); msg::DevicePropertyDesc GetDevicePropertyDesc(DeviceProperty property); ByteArray GetDeviceProperty(DeviceProperty property); u64 GetDeviceIntegerProperty(DeviceProperty property); std::string GetDeviceStringProperty(DeviceProperty property); void SetDeviceProperty(DeviceProperty property, const ByteArray & value); void SetDeviceProperty(DeviceProperty property, const std::string & value); void AbortCurrentTransaction(int timeout = DefaultTimeout); ByteArray GenericOperation(OperationCode code); ByteArray GenericOperation(OperationCode code, const ByteArray & payload); void SetObjectReferences(ObjectId objectId, const msg::ObjectHandles &objects); msg::ObjectHandles GetObjectReferences(ObjectId objectId); //windows specific void EnableSecureFileOperations(u32 cmac1[4]); void RebootDevice(); static msg::DeviceInfo GetDeviceInfo(PipePacketer& packeter, u32 transactionId, int timeout = 0); private: template ByteArray RunTransaction(int timeout, OperationCode code, Args && ... args) { ByteArray response; return RunTransactionWithDataRequest(timeout, code, response, nullptr, std::forward(args) ... ); } template ByteArray RunTransactionWithDataRequest(int timeout, OperationCode code, ByteArray & response, const IObjectInputStreamPtr & inputStream, Args && ... args); void SetCurrentTransaction(Transaction *); void BeginEditObject(ObjectId objectId); void SendPartialObject(ObjectId objectId, u64 offset, const ByteArray &data); void TruncateObject(ObjectId objectId, u64 size); void EndEditObject(ObjectId objectId); ByteArray Get(u32 transaction, ByteArray & response, int timeout = 0); static ByteArray Get(PipePacketer &packeter, u32 transaction, ByteArray & response, int timeout = 0); void Send(const OperationRequest &req, int timeout = 0); static void Send(PipePacketer &packeter, const OperationRequest &req, int timeout = 0); void Close(); }; } #endif /* SESSION_H */ android-file-transfer-linux-4.5/mtp/scope_guard.h000066400000000000000000000005211501120463400221340ustar00rootroot00000000000000#ifndef AFTL_MTP_SCOPE_GUARD_H #define AFTL_MTP_SCOPE_GUARD_H #include #include namespace mtp { class scope_guard : Noncopyable { using Callback = std::function; Callback _callback; public: scope_guard(Callback && c): _callback(c) { } ~scope_guard() { _callback(); } }; } #endif android-file-transfer-linux-4.5/mtp/types.h000066400000000000000000000056311501120463400210140ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_TYPES_H #define AFTL_MTP_TYPES_H #include #include #include #include #include #include #include #include namespace mtp { using scoped_mutex_lock = std::unique_lock; using u8 = uint8_t; using u16 = uint16_t; using u32 = uint32_t; using u64 = uint64_t; using s8 = int8_t; using s16 = int16_t; using s32 = int32_t; using s64 = int64_t; #define DECLARE_PTR(C) using C##Ptr = std::shared_ptr template inline T RequireNotNull(const T &t, const std::string &expr) { if (!t) throw std::runtime_error("null check failed " + expr); return t; } struct Noncopyable { Noncopyable() = default; Noncopyable(const Noncopyable&) = delete; Noncopyable& operator=(const Noncopyable &) = delete; }; class system_error : public std::exception { std::string _message; public: system_error(const std::string &prefix, int err = 0) : _message(prefix + ": " + strerror(err? err: errno)){ } virtual const char * what() const throw() { return _message.c_str(); } }; } #define NOT_NULL(x) (mtp::RequireNotNull((x), #x)) #define ASSERT(expr) if (!(expr)) throw std::runtime_error("assertion " #expr " failed") #define CATCH(WHAT, ...) catch(const std::exception &ex) { LOG(WARNING) << WHAT << ": " << ex.what(); __VA_ARGS__ } //fixme: c++17 will allow implement it in stream with std::is_enum(c++11) + std::underlying_type(c++17) #define DECLARE_ENUM(TYPE, BASETYPE) \ template \ Stream &operator << (Stream & stream, TYPE format) \ { stream << static_cast(format); return stream; } \ \ template \ Stream &operator >> (Stream & stream, TYPE &format) \ { BASETYPE value; stream >> value; format = static_cast(value); return stream; } #define ENUM_VALUE_DECL(NAME, VALUE) NAME = VALUE , #define ENUM_VALUE_TO_STRING(TYPE, NAME, VALUE) case TYPE :: NAME : return #NAME ; #define ENUM_VALUE_TO_STRING_DEFAULT(TYPE, VALUE, PADDING) default: return hex(static_cast( VALUE ), PADDING ).ToString(); #endif android-file-transfer-linux-4.5/mtp/usb/000077500000000000000000000000001501120463400202635ustar00rootroot00000000000000android-file-transfer-linux-4.5/mtp/usb/BulkPipe.cpp000066400000000000000000000103251501120463400225030ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include namespace mtp { namespace usb { BulkPipe::BulkPipe(DevicePtr device, ConfigurationPtr conf, InterfacePtr interface, EndpointPtr in, EndpointPtr out, EndpointPtr interrupt, ITokenPtr claimToken, bool clearHalt): _device(device), _conf(conf), _interface(interface), _in(in), _out(out), _interrupt(interrupt), _claimToken(claimToken) { if (clearHalt) { try { device->ClearHalt(_interrupt); } catch(const std::exception & ex) { error("clearing halt for in ep: ", ex.what()); } try { device->ClearHalt(in); } catch(const std::exception & ex) { error("clearing halt for in ep: ", ex.what()); } try { device->ClearHalt(out); } catch(const std::exception & ex) { error("clearing halt for in ep: ", ex.what()); } } } BulkPipe::~BulkPipe() { } DevicePtr BulkPipe::GetDevice() const { return _device; } InterfacePtr BulkPipe::GetInterface() const { return _interface; } ByteArray BulkPipe::ReadInterrupt(int timeout) { ByteArrayObjectOutputStreamPtr s(new ByteArrayObjectOutputStream()); try { _device->ReadBulk(_interrupt, s, timeout); } catch(const TimeoutException &ex) { return ByteArray(); } return s->GetData(); } void BulkPipe::SetCurrentStream(const ICancellableStreamPtr &stream) { scoped_mutex_lock l(_mutex); _currentStream = stream; } ICancellableStreamPtr BulkPipe::GetCurrentStream() { scoped_mutex_lock l(_mutex); return _currentStream; } class BulkPipe::CurrentStreamSetter { BulkPipe * _owner; public: CurrentStreamSetter(BulkPipe *owner, ICancellableStreamPtr stream): _owner(owner) { _owner->SetCurrentStream(stream); } ~CurrentStreamSetter() { _owner->SetCurrentStream(nullptr); } }; void BulkPipe::Read(const IObjectOutputStreamPtr &outputStream, int timeout) { CurrentStreamSetter s(this, std::dynamic_pointer_cast(outputStream)); _device->ReadBulk(_in, outputStream, timeout); } void BulkPipe::Write(const IObjectInputStreamPtr &inputStream, int timeout) { CurrentStreamSetter s(this, std::dynamic_pointer_cast(inputStream)); _device->WriteBulk(_out, inputStream, timeout); } void BulkPipe::Cancel() { ICancellableStreamPtr stream = GetCurrentStream(); print("cancelling stream ", stream.get()); if (stream) stream->Cancel(); } BulkPipePtr BulkPipe::Create(const usb::DevicePtr & device, const ConfigurationPtr & conf, const usb::InterfacePtr & interface, ITokenPtr claimToken) { int epn = interface->GetEndpointsCount(); usb::EndpointPtr out, in, interrupt; //debug("endpoints: ", epn); for(int i = 0; i < epn; ++i) { usb::EndpointPtr ep = interface->GetEndpoint(i); //debug("endpoint: %d: %02x", i, ep->GetAddress()); //check for bulk here if (ep->GetDirection() == usb::EndpointDirection::Out) { if (ep->GetType() == usb::EndpointType::Bulk) { //debug("OUT"); out = ep; } } else { if (ep->GetType() == usb::EndpointType::Bulk) { //debug("IN"); in = ep; } else { //debug("INTERRUPT"); interrupt = ep; } } } if (!in || !out || !interrupt) throw std::runtime_error("invalid endpoint"); return std::make_shared(device, conf, interface, in, out, interrupt, claimToken); } }} android-file-transfer-linux-4.5/mtp/usb/BulkPipe.h000066400000000000000000000045321501120463400221530ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_USB_BULKPIPE_H #define AFTL_MTP_USB_BULKPIPE_H #include #include #include namespace mtp { namespace usb { class Device; DECLARE_PTR(Device); class Configuration; DECLARE_PTR(Configuration); class Interface; DECLARE_PTR(Interface); class Endpoint; DECLARE_PTR(Endpoint); class BulkPipe; DECLARE_PTR(BulkPipe); class BulkPipe //! USB BulkPipe, incapsulate three (in, out, interrupt) endpoints, allowing easier data transfer { std::mutex _mutex; DevicePtr _device; ConfigurationPtr _conf; InterfacePtr _interface; EndpointPtr _in, _out, _interrupt; ITokenPtr _claimToken; ICancellableStreamPtr _currentStream; private: void SetCurrentStream(const ICancellableStreamPtr &stream); ICancellableStreamPtr GetCurrentStream(); class CurrentStreamSetter; public: BulkPipe(DevicePtr device, ConfigurationPtr conf, InterfacePtr interface, EndpointPtr in, EndpointPtr out, EndpointPtr interrupt, ITokenPtr claimToken, bool clearHalt = false); ~BulkPipe(); DevicePtr GetDevice() const; InterfacePtr GetInterface() const; ByteArray ReadInterrupt(int timeout); void Read(const IObjectOutputStreamPtr &outputStream, int timeout = 10000); void Write(const IObjectInputStreamPtr &inputStream, int timeout = 10000); void Cancel(); static BulkPipePtr Create(const usb::DevicePtr & device, const ConfigurationPtr & conf, const usb::InterfacePtr & owner, ITokenPtr claimToken); }; }} #endif /* BULKPIPE_H */ android-file-transfer-linux-4.5/mtp/usb/DeviceBusyException.cpp000066400000000000000000000076401501120463400247170ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #ifdef __linux__ # include # include # include # include //mtp # include # include #endif namespace mtp { namespace usb { #ifdef __linux__ namespace { std::string ReadLink(const std::string &path) { char buf[NAME_MAX]; auto pathSize = readlink(path.c_str(), buf, sizeof(buf)); if (pathSize >= 0) return std::string(buf, pathSize); debug("Readlink ", path, ": ", posix::Exception::GetErrorMessage(errno)); return buf; } } #endif DeviceBusyException::DeviceBusyException(int fd, const std::string &msg): std::runtime_error(msg) { try { //only linux for now #ifdef __linux__ auto myPid = getpid(); do { if (fd < 0) break; debug("trying to find processes which are holding fd ", fd); #if 0 //apple char filePath[PATH_MAX]; if (fcntl(fd, F_GETPATH, filePath) == -1) { error("error getting file path for fd ", fd, ": ", Exception::GetErrorMessage(errno)); break; } #else std::string filePath = ReadLink("/proc/self/fd/" + std::to_string(fd)); if (filePath.empty()) break; #endif debug("mapped ", fd, " to ", filePath); Directory proc("/proc"); while(true) { auto pidPath = proc.Read(); if (pidPath.empty()) break; int pid; if (sscanf(pidPath.c_str(), "%d", &pid) != 1) continue; if (pid == myPid) continue; // debug("got process dir ", pidPath); auto fdsRoot = "/proc/" + pidPath + "/fd"; if (access(fdsRoot.c_str(), R_OK) != 0) { // debug("skipping inaccessible path ", fdsRoot); continue; } try { Directory fds(fdsRoot); while(true) { auto fdPath = fds.Read(); if (fdPath.empty()) break; if (fdPath[0] == '.') continue; auto fdTarget = ReadLink(fdsRoot + "/" + fdPath); if (fdTarget.empty()) continue; //debug("read mapping to ", fdTarget); if (fdTarget == filePath) { debug("process ", pid, " is holding file descriptor to ", filePath); auto image = ReadLink("/proc/" + pidPath + "/exe"); Processes.push_back({ pid, image }); } } } catch(const std::exception & ex) { debug("error reading ", fdsRoot, ": ", ex.what()); } } } while(false); #endif } catch(const std::exception &ex) { debug("DeviceBusyException error: ", ex.what()); } } void DeviceBusyException::Kill() const { Kill(Processes); } void DeviceBusyException::Kill(const std::vector & processes) { for(auto & desc : processes) { try { Kill(desc); } catch(const std::exception & ex) { error("Kill: ", ex.what()); } } } void DeviceBusyException::Kill(const ProcessDescriptor & desc) { #ifdef __linux__ if (kill(desc.Id, SIGTERM) != 0) throw posix::Exception("kill(" + std::to_string(desc.Id) + " (" + desc.Name + "), SIGTERM)"); sleep(1); kill(desc.Id, SIGKILL); //assuming we can do it #else throw std::runtime_error("not implemented"); #endif } }} android-file-transfer-linux-4.5/mtp/usb/DeviceBusyException.h000066400000000000000000000030031501120463400243510ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_USB_DEVICEBUSYEXCEPTION_H #define AFTL_MTP_USB_DEVICEBUSYEXCEPTION_H #include #include #include namespace mtp { namespace usb { struct DeviceBusyException : public std::runtime_error //! Exception thrown when device is busy (claimed by other process) { struct ProcessDescriptor { int Id; std::string Name; }; std::vector Processes; DeviceBusyException(int fd = -1, const std::string &msg = "Device is already used by another process"); void Kill() const; static void Kill(const ProcessDescriptor & desc); static void Kill(const std::vector & processes); }; }} #endif android-file-transfer-linux-4.5/mtp/usb/DeviceNotFoundException.h000066400000000000000000000023141501120463400251670ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_USB_DEVICENOTFOUNDEXCEPTION_H #define AFTL_MTP_USB_DEVICENOTFOUNDEXCEPTION_H #include namespace mtp { namespace usb { struct DeviceNotFoundException : public std::runtime_error //! Exception throw when device was disconnected or was not found { DeviceNotFoundException(): std::runtime_error("device was disconnected") { } }; }} #endif android-file-transfer-linux-4.5/mtp/usb/Request.cpp000066400000000000000000000107371501120463400224270ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include namespace mtp { namespace usb { BaseRequest::BaseRequest(const DevicePtr & device, int timeout): _device(device), _timeout(timeout) { } BaseRequest::~BaseRequest() { } u16 DeviceRequest::GetStatus() { ByteArray data(2); _device->ReadControl((u8)Type::DeviceIn, (u8)Request::GetStatus, 0, 0, data, _timeout); return data[0] | ((u16)data[1] << 8); } void DeviceRequest::ClearFeature(u16 feature) { _device->WriteControl((u8)Type::DeviceOut, (u8)Request::ClearFeature, feature, 0, ByteArray(), _timeout); } void DeviceRequest::SetFeature(u16 feature) { _device->WriteControl((u8)Type::DeviceOut, (u8)Request::SetFeature, feature, 0, ByteArray(), _timeout); } void DeviceRequest::SetAddress(u16 address) { _device->WriteControl((u8)Type::DeviceOut, (u8)Request::SetAddress, address, 0, ByteArray(), _timeout); } ByteArray DeviceRequest::GetDescriptor(DescriptorType type, u8 index, u16 lang) { ByteArray data(255); _device->ReadControl((u8)Type::DeviceIn, (u8)Request::GetDescriptor, ((u16)type << 8) | index, lang, data, _timeout); return data; } void DeviceRequest::SetDescriptor(DescriptorType type, u8 index, u16 lang, const ByteArray &data) { _device->WriteControl((u8)Type::DeviceOut, (u8)Request::SetDescriptor, ((u16)type << 8) | index, lang, data, _timeout); } u8 DeviceRequest::GetConfiguration() { ByteArray data(1); _device->ReadControl((u8)Type::DeviceIn, (u8)Request::GetConfiguration, 0, 0, data, _timeout); return data[0]; } void DeviceRequest::SetConfiguration(u8 index) { _device->WriteControl((u8)Type::DeviceOut, (u8)Request::SetConfiguration, index, 0, ByteArray(), _timeout); } //interface requests InterfaceRequest::InterfaceRequest(const DevicePtr & device, u16 interface, int timeout): BaseRequest(device, timeout), _interface(interface) {} u16 InterfaceRequest::GetStatus() { ByteArray data(2); _device->ReadControl((u8)Type::InterfaceIn, (u8)Request::GetStatus, 0, _interface, data, _timeout); return data[0] | ((u16)data[1] << 8); } void InterfaceRequest::ClearFeature(u16 feature) { _device->WriteControl((u8)Type::InterfaceOut, (u8)Request::ClearFeature, feature, _interface, ByteArray(), _timeout); } void InterfaceRequest::SetFeature(u16 feature) { _device->WriteControl((u8)Type::InterfaceOut, (u8)Request::SetFeature, feature, _interface, ByteArray(), _timeout); } u8 InterfaceRequest::GetInterface() { ByteArray data(1); _device->ReadControl((u8)Type::InterfaceIn, (u8)Request::GetInterface, 0, _interface, data, _timeout); return data[0]; } void InterfaceRequest::SetInterface(u8 alt) { _device->WriteControl((u8)Type::InterfaceOut, (u8)Request::SetInterface, alt, _interface, ByteArray(), _timeout); } //endpoint requests EndpointRequest::EndpointRequest(const DevicePtr & device, u16 endpoint, int timeout): BaseRequest(device, timeout), _endpoint(endpoint) { } u16 EndpointRequest::GetStatus() { ByteArray data(2); _device->ReadControl((u8)Type::EnpointIn, (u8)Request::GetStatus, 0, _endpoint, data, _timeout); return data[0] | ((u16)data[1] << 8); } void EndpointRequest::ClearFeature(u16 feature) { _device->WriteControl((u8)Type::EnpointOut, (u8)Request::ClearFeature, feature, _endpoint, ByteArray(), _timeout); } void EndpointRequest::SetFeature(u16 feature) { _device->WriteControl((u8)Type::EnpointOut, (u8)Request::SetFeature, feature, _endpoint, ByteArray(), _timeout); } void EndpointRequest::SynchFrame(u16 frameIndex) { ByteArray data(2); data[0] = frameIndex; data[1] = frameIndex >> 8; _device->WriteControl((u8)Type::EnpointOut, (u8)Request::SynchFrame, 0, _endpoint, ByteArray(), _timeout); } }} android-file-transfer-linux-4.5/mtp/usb/Request.h000066400000000000000000000103471501120463400220710ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_USB_REQUEST_H #define AFTL_MTP_USB_REQUEST_H #include #include namespace mtp { namespace usb { class Device; DECLARE_PTR(Device); enum struct RequestType : u8 { HostToDevice = 0x00, DeviceToHost = 0x80, Standard = 0x00, Class = 0x20, Vendor = 0x40, Device = 0x00, Interface = 0x01, Endpoint = 0x02, Other = 0x03 }; constexpr RequestType operator | (RequestType r1, RequestType r2) { return static_cast(static_cast(r1) | static_cast(r2)); }; class BaseRequest //! Base USB Standard Request { public: static const int DefaultTimeout = 1000; BaseRequest(const DevicePtr & device, int timeout = DefaultTimeout); ~BaseRequest(); protected: DevicePtr _device; int _timeout; }; enum struct DescriptorType : u8 { Device = 1, Configuration = 2, String = 3, Interface = 4, Endpoint = 5, DeviceQualifier = 6, OtherSpeedConfiguration = 7, InterfacePower = 8, OnTheGo = 9 }; struct DeviceRequest : BaseRequest //! USB Standard Request for Device { using BaseRequest::BaseRequest; enum struct Type : u8 { DeviceOut = static_cast(RequestType::HostToDevice | RequestType::Standard | RequestType::Device), DeviceIn = static_cast(RequestType::DeviceToHost | RequestType::Standard | RequestType::Device) }; enum struct Request : u8 { GetStatus = 0, ClearFeature = 1, SetFeature = 3, SetAddress = 5, GetDescriptor = 6, SetDescriptor = 7, GetConfiguration = 8, SetConfiguration = 9, GetOSStringDescriptor = 0xee }; u16 GetStatus(); void ClearFeature(u16 feature); void SetFeature(u16 feature); void SetAddress(u16 address); ByteArray GetDescriptor(DescriptorType type, u8 index, u16 lang = 0); void SetDescriptor(DescriptorType type, u8 index, u16 lang, const ByteArray &data); u8 GetConfiguration(); void SetConfiguration(u8 index); }; class InterfaceRequest : public BaseRequest //! USB Standard Request for Interface { u16 _interface; public: enum struct Type : u8 { InterfaceOut = static_cast(RequestType::HostToDevice | RequestType::Standard | RequestType::Interface), InterfaceIn = static_cast(RequestType::DeviceToHost | RequestType::Standard | RequestType::Interface) }; enum struct Request : u8 { GetStatus = 0, ClearFeature = 1, SetFeature = 3, GetInterface = 10, SetInterface = 17 }; InterfaceRequest(const DevicePtr & device, u16 interface, int timeout = DefaultTimeout); u16 GetStatus(); void ClearFeature(u16 feature); void SetFeature(u16 feature); u8 GetInterface(); void SetInterface(u8 alt); }; class EndpointRequest : public BaseRequest //! USB Standard Request for Endpoint { u16 _endpoint; public: enum struct Type : u8 { EnpointOut = static_cast(RequestType::HostToDevice | RequestType::Standard | RequestType::Endpoint), EnpointIn = static_cast(RequestType::DeviceToHost | RequestType::Standard | RequestType::Endpoint) }; enum struct Request : u8 { GetStatus = 0, ClearFeature = 1, SetFeature = 3, SynchFrame = 18 }; EndpointRequest(const DevicePtr & device, u16 endpoint, int timeout = DefaultTimeout); u16 GetStatus(); void ClearFeature(u16 feature); void SetFeature(u16 feature); void SynchFrame(u16 frameIndex); }; }} #endif android-file-transfer-linux-4.5/mtp/usb/TimeoutException.h000066400000000000000000000022461501120463400237450ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_USB_TIMEOUTEXCEPTION_H #define AFTL_MTP_USB_TIMEOUTEXCEPTION_H #include namespace mtp { namespace usb { struct TimeoutException : public std::runtime_error //! Exception throw if USB URB transfer timeout occured { TimeoutException(const std::string &msg): std::runtime_error(msg) { } }; }} #endif android-file-transfer-linux-4.5/mtp/usb/types.h000066400000000000000000000021161501120463400216000ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_USB_TYPES_H #define AFTL_MTP_USB_TYPES_H namespace mtp { namespace usb { enum struct EndpointType { Control = 0, Isochronous = 1, Bulk = 2, Interrupt = 3 }; enum struct EndpointDirection { In, Out, Both }; }} #endif android-file-transfer-linux-4.5/mtp/version.cpp.in000066400000000000000000000001611501120463400222660ustar00rootroot00000000000000#include namespace mtp { std::string GetVersion() { return std::string("${AFT_VERSION}"); } } android-file-transfer-linux-4.5/mtp/version.h000066400000000000000000000017241501120463400213340ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_MTP_VERSION_H #define AFTL_MTP_VERSION_H #include namespace mtp { std::string GetVersion(); } #endifandroid-file-transfer-linux-4.5/osx/000077500000000000000000000000001501120463400175035ustar00rootroot00000000000000android-file-transfer-linux-4.5/osx/android-file-transfer.icns000066400000000000000000024336571501120463400245640ustar00rootroot00000000000000icns 7ic08PNG  IHDR\rf iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx eY{ݳ4Q-HB2`,0ab'*Sr" SUI)A2hi4ϻ}ϻswOwO9}y;=s?>~v{Iz[Ȣ ZK. d0\r*X4g$xbY`Hzɖ!@f y1*|F&jJ/=[ΒMGyșᒀ]1DE00 SSx#u{l["rmss= mՅ>xIz5k% g^9~a{{ͶEmIS!j_'%8A)eŊO|F'Fao:UАY+*$hL}՚)(&UB|1OEɦsyșᒀy)p} SSx#uK} (rA\o[kd|~_}o`֟>şo[X\j[[ M]=9<.yB9Ϙz 9%O̩!f!e ;6)y_ z}2O93,^In("Y sF:6ZqotsNm[Oλߍ$?Cɗ77h{ži7X >T N~ iML8j/29Q숫 Pa&9`i1'41 'Ӵ)STq"gK>R3"}IrHð%Aɡz;~RK'/,Zō͵w|^uc:}//,~:_vhnbX!^GMspSҪ Ñٙy۪V-~TqRqA)1,$A ez5;S\ʢ'Ha5tHG*n D=5L+tŁ޳#ݻ?i=|O}mccU KaFx15 NȎx1'&Mޣ#tXGkY$.(GJI|f-+dPT)!mX2Ze> 4b_T`|>Fq3Y HPIuK' cA#&F1Bo ߿ֿ{L?} nn^l<̉Te2I-Aɑ"z*{`54ڣ-P3[Hj -%KJ-33C9qRsR7)Jnij0 15z%: lܿ=?odE___x&~n]K[s GH'&#)Y,?0%-X<8PHfZeL(R)^;]TQØ y.[ZIe ?#2 0oR$(9@}G'%`RO]G NGmkҭssx*NCz䐗 UAozU>PI7u T  )C.k{7!T 8L@(PZM\+J垄92#|j= BjJ`|zY5,) a7QtbW|0<@2sf}Vlrl4E7$%R,}¶ +aNXf@.["cP&b_<(Vx#uKe>`Qc\ U.1@9sS'DAMKܪ#Ezr(X4PFܮЋzґPTǖ`z3 x<hJ-;hJO1rik9 'P0P3Е喎} CIG JiۢOGIQkc ϓrP']V !4eŊO edM[aJ"qw> Z4#-/f50 @SSY<1w,wRPN5р, }\q B1 Y(_X,hM#T9s@*ij̦UFx18#&&U Kj:Vz P0CLuK܇!$g 9=όT TOkB.nSP_ZwՎEQ! `xMuKaޔ axE'89D[ڨI Ԕyʐ $/ހ52N'p`d μ&9yCRuEn>`eE^''0^[=2Ɛt#e=A s'pE@A񻯽xzZrq{j֕T;nȐq0tVQDM|aX=^Q N;NI Ԕyʐ $/APb0 ub))tA CcXQبpȂ3l41 LC%A %:zB(;$htRk/ \h&jJ/z02 TɼA*FȂ8&%_1hd3YI`w_-ÐS#㷅xj͓&#M`53i\5'5&`A]:S ]DrȌt YPwR@lj5ANhfT J+e>V#|4iM"WFʔ54PzRSڢ 9 ĨRJ]HtbOJ0$PZ TM;bAi~Ԋ}ۆg0Kxƻah#FC9Ϩy|p>\ڏߓ,hFM>zO:8i\dZ!` IY=)mф^SbT c)%x[:1'%`ZzJPritm^L& 4? jžmË3ryA$\3j!\)p.kJh =ۘQS){`tcu yNf|䉡\ x s_-5(@fUjͤ %8tͰ8{Y ZHSF{d>.{ U**129]C;DϜOV(akfTc$yD>A󎝂ZK%!c <["f| @OafH)F*H 20 )=%(9?f$xbYqxIG%FE0,)qK'} )Aɑ ;j2rijԃ4 {Tu*p7рax%ҩ[^jDd@!F zC.ZKrjlA2'6h1'0P3w1K10 I*=%(92ƌ& 9BaAz wDdYŴV!̔T0$n&F:[>;b&XB]e<2155 1dKkECzQA1l_t@3Rr1i>;+3hXLoY`fnt-m} {JPrhudF\ӁAh-_yԊ} xΪ C^i~RyՄ=-~H }a%nFY YTD&갑 aUaD~8K gIuݙk+mvl=^(jgasdե,#9gİ\z-֐xmqDkTmXqxx<;B>/!9̓D!$ЙTGSLqF0M<7]+bpUy_yW>t-~Yc5=!Isu3?#S}G[CT[+)|I1RgnkE{1tؘoϽQ:~; Թf>:]uo3xU<2<1w,0,IjE *sr_vۧ@׍4:DyZ!Qս.6@Z "UUHP cp9,C~l?}?8gx{K/c*F-Mg {Âtױ$f bY`XY 5GzY'(Ee )t<qn'F Kϙ+w71I W-h=@? pB`W;o뼲'(9čOWS5+g崋Rz sǔh|6*#0ҩhǍdCykF3'mnbXi-ꀶ>wKؑN?dI' W;o\ڷZt(͟ŦsJ+ H}尬b7Dkǀ{vO;)OKgcr :]y= y%@@39MX7Q$C'KʘpػMGZkkmSɅzIIYhGZ]w3iB;v`g[yMOv{ .;nglX"X6N蒈f6ۻw-/ʄw'~%o$FM$GL2<1w,;bH'8ȣ ,fF=߿G?i4U!Aa*t9,Ӹ?4Xy&' OL2a/ҭ11-jN*zd_4M'h_=^rJ۷2'*n1WkOMťe~h틍oZߤ DqtKW ;j^5IP+bE MUB#|Ϊe>`Fy4Mp4RUA%9M2`cS[k?coYj/fs$oX?ٽu`RޛSpA6PX3:xpnyْ?T*> > X~e5M MΪe>`$^!{Yhr,s"РY4XH J,KO{>ږ/.$_# s3-"D3fؿ]/u;6ӅJ]ۆJUzІHTQ]}gS 0t\gAϮY O 3&A#i$PƤj BK:f_om5 a3xh`s-VQ|[ v5nW7#jpzW]N',_I97kEi 6F9ͫ!Mw& juZ6#TKqg;1HX/Ú+"9_c+ԓk)*~nC٠3"W_{?~A~%?@W,80F÷QyF=o~Zh8E=ăYJ.%:ȪB4U!XGBxð$41F Too+؁5Sũ*QlЈ|oez)pGھzY@0G'XZNl9ӶqTk=fS Ja~ZhT.5=K̆t.'SjuZ6#TKȨ2l͜Šbb k27G)sP% zf7sF/XsyeFUڧ>!WןJTXA@|iB„.=!?q`R$a^4Қ-%rQLQB|F! XZ9{>*)9G ̓Bx2f/r3,c~NjM,tsV~O$Yat ,B_N[&I.5w?}<(h\5!4B_b> 'yd6CZqlc(Gi՗/BJ >ma$7wdb"f%)*~ϫFEntm+; ,` 0lX;d %Ra 8هb,ȐR.5=tʉf dĚh ޳TF{ ZI5a!d1I8vKS_kO;֔Bak(QYS%I&+ڡC%/~F;1GyZh 90 {>2[ӪhF9hhA'$AgS6r *O& d!g(!Yہ÷7ҝ'N-YCjzf97ox7ciHt|m'×3I~:8 |G'Oΐ, ̰eLc ]k012 BGeaA1%N9;+o MBaY' S/-=I0P3kk[@|ȩK-逼"LS! Z6y.e郟 F٬YUԜ*t9,.FONq#9T}z^SB|=YΒMI8"93R|g}TAh@>5Qog7!z^Y\GFyla|db0s71܄~ɭ/zx!7y/K_ʌN[j"gs:ꖖ%?@  CSRPg߿o_]u7%t'2 \tUD*t9,Ӹ<1`7sPm{VCd|6(2 v$5v=QSB|z:f"g5 r-%s f 0 K NKnˎη7h/{~jC_ xa1l` B,L|*ϽT4sn򅖓{a*"邴*$hPDKq8,d/ZFT||j5-[3hZe>D DoW~zς15 r-lѳ32 J'gxFo~Wrt%_?u']^96O?Dž4RDPZcUy.Q/G1J11`I~wr]N`^>/\0!cum=ua [[z,af_*ەW-#ڢlCS, C2  a7޵v㲜hֳZy֬A}Ov1ƶc 06~"1> fb3+cGr&Ds;u@ZL| >U֬сwх7j}աvK|j"S6"u:D?y~\ $` ðQܗ< xb{kxN}n9`ʍjeQ6ݧQ3=cWv%MtqV$ 5ʓl=BO"1;5>WPb:H;M@A H\kԼ>_~v#]/IiZ<6k?eGp% ׾=_?lĒ WUH5+VjR T, 0|`ĝ |7Joi[78>[>Ǿ~v.9'4\r,/'== i:B3 V#|q"\n1 ;"IyR@3&F|3kE_}҃\xx i<7]q9Ki;'j;= > t-'Ҳ'0ǣڼXJǀpE`ǍSs G'C@64 8O hh#Pr0=of($;r}:Tn RsgI:a$3 >ԏ90Ppl~N`IjE!P@kA-;K}'w>//4U~Yvۉ9B'o.m}it`_ Wƪ3bK82eFzۀ!hڻ;/lCQM{~1~j]+S`{|vA':1Ou;Q\cmbnϨ/^Ao {sj::G@ \}qӘ˖ anXH*qv8Vg'o~TσGh[=p;gۓ~=Gq_hZLto}.//kp.K~V> ЧA?&|loŪ]A~ \n#|JE-uy7620v _xЈ9\z+_r5mʋu5OM>E\OΗ919_ }; }>o߾K*n_ؕ@cEk+QĐq>@"㋐En03As$(N_)cebO|աh'dx}'۟~|ݙuǗor%{# |+A&m.{3̦5ow{r611|pM7.cc|;m;FG1CE!w#9$'S"e/;T /KP/-4  25U>|K~D%v`3XZowk?=ub. cJc6X6ک+?rƶ"7ѨY3=:W%Mcǎ{~?Z0cn$SO{キ?o??[T\ū-,.l~=W/CO5ͧNiﴸ$?A@q<∹7u:QR;B%X R𬫠 <_=tp;Ws@_͡]X?%G(OKfwxZLcguu=ޏ=>tb/lSshV;x=t]w?ұBC݂|W-i};$1ȱ@W*n0J9G;q˽O'Ol'׾5G ^fy:r|_G.W,fnaa}!: ; 'ikþe8=98ZdB8%):@a>V#|% @IDAT$KKZ%>'_?3vcCW9tNKO^Gy~sn}p;~G^Л|"-yr:_<nNC"NYz.rS1[+죓HH(+KaX+—؟, 7KꑠDaR %, 1y YN*~3l֪q%drwFSabzDPT/WӾd [de(;o_~xk=> G$Ooo7|s{'Oeʰ_|^7lM v\ON$ޅwm:/lǸso/--+RgN).Cn{xr%F NDe[6HW/0^ Āje]:c5'H@bMë)BLهrH9-Qub@cHl(=Y/Q5-iYcdf`JO&ܿ}O@of;'|~)p)MLoО<+:ɾز4o _aO3 bz9$,a,aaO!ǠC#A*e%*jה}8gњ]pAAhC% ӕSt%`']_Dr71g͹ϡezss~ tߏqÔ)Z6Sсrຶj:1%e H`B'*f)yY9pm[wE.~ 1;t]5bS%dtg1?urGN6{gҹ&8/ʕ@Gz<2&qEZY!ϱ6\>jO: bNV:JfY͢9б;$"z=^E\5+X1è}-I3CWBEqt8d~RTt/W9v%pgDM_KNxE])nl3D xR~ EJQc5'H@F%#)Me>*j 7-×8t+=Tӳ% Nsx'o7t)<+2>a8\AOHGp;vf 'PL}\E[WsMLƴ鏃*1L* -G@kӏslnRr*4Gg~]RKgt_Xo%z x6igzsJNځ#weʷvԓtã}8|g?2}A]N|$M\wRMTIkUӆ?008@[>d<,N=3KEp\;BBj xb{?bBA#j/W4T?;z4B`rfDO|}|A-w;2atؤ+O`?'0?f ZhM{?DJX_Duy>ZEcZr7tӠfPHBˏt/aonQ]h>K-Pzb=,ʘ9$1a/' *N7W@_>|hSboo5cx:G`RJNPrqKs_-ðᑟH͌lWZ M;z ME36!߸<a=bpP$=g &ª uy4. DgI^Hdq0uB y)* c$'*Br"AaA#jԔQe͏]K_(Әѹ>ӝU+3<%0 m\y@QaUO:FӸL93QD/6^aÒ'0\㰠} CjJO JԌ}w~sDp{Z+85_?N"G`Y}ߍ+yMCLU "?Aj v5l^$`/SVVެh8+V*`ca9{xY`ØYxqsmW^{c@P7Y<4esLbbmp|7|?2 EV|S%w~?>jG`6`=o3nifp(z;N`lLN'f F 0 ~>zdۮ)%Kywup6.hR6m] e5FLÍ@ ;M%uxtŁ* a<;ĸ`QJj {stC!8z{eQ—)$P> sEW줴YtR%"`]MY*RlAC).ۡ㗕(g\߬?񏷇zH?я>'P#){$S$_r8n|=$ Bk)KZR.@є A8J>O[x& hO] ԛNegĔ=||///{bM?J Oj ;麾bS찐 Pkix%s1}dd,}Aag 6l'rf$_YNä2Cɓ[|g[{/~ǂ$pWk:Աwo9yמ da(Aq1|U7g_? oG>"S'9tP{ы^4z LQ%|3#r :*>L d>Lz6 IRh3MD ל?R0eyN{;޳ogLۓÇgvl~2몰ל46kiz7j=T%*|9)X"cw5M0!_F#KD'>Jf|R/|౓z&| GS| _h?'kYQuKF؇  Iu@PJ~̒gD r +|4 #&F_yH> ?8?3T=僟oჰ? ; :9ב#GJǓ׉~9zڧc Ғ܏g*C= dG KPex0F0,+}didH_O r[bî 6!MyW} ׅr*y) ѱ& M)2_ )2Y:493\pBg) ^6|W?`߾yKcog~PZ^N 1qID@|9W ?S{\9>^a=RZ4O@d.,Vf@A~e%_zd[#C(5ß8ѾWMG _SvY]^iE hd|6kg=ܠCT 僟mǛ|4>r{۩Sٚ$o>8gO0~91r,@4u/;x`&aYzq  >z41NRNZC" A#fEea9#;g81 j,ӕ׀K?u5+tEym^4JGj:@DR|(@$h b?c Q0l=y=\g-ӌH{y'NHT0dP\u7 yp -7ko~ӝ%:H xc0ˁYW|F/$ $ҟDpz&^BQ^`/fEc>`RR1h8ӥ @a+aL<s-zcֆ  i S?ͥ́־E;teeUQ Gӳ+@鐧%_x/'s״vy[s?烟_{G9 惗+ ~vgرcy9_:&h7 2}B 3c0Q5 VGo;V Vf/@አ  uK{q_-q?P 7\in>ܖ*{-<Ĕ!Ok_짓ڇ>P; D>y$0!ZYY~feqy?^e10lH?y# >b iE`whk2VjB#HWU0$W+׫6| 'kDv"5y},h CYΰ ,~]ɵ3:PBfgr~%E?)<f}||7Liih'_ kd38`/'?| FpE[ %_oH] 'f ܹ$U K;z>ge,p̨98Kկ˜!m+.?.} ?_gc_:>\s?N\) s%_ B^-u2` \\1*([A#fEjaNj.e 9Ӄ_C--_F'} ;}0De\=@ EpQ"}B$!y I_ ?|wL7qާwin_ڧ޷]W<%8 譔y/%N|PW)()q8lD wF לyQa` WA+坁!O-|◞hgh~)ױ%g}?ނ/wXvuYMоr_o)CSU%FmuQCvy# DYlGZ=A 90 &F/&/8ysHP;hi ҪWe@鐧pU0W7g/ ަ.u|mwCy>j7A(@9}h^3Z# d #FG`#a`"Aɡw-e?M/:p}98(t;S'\ .{S9;OZ|~!2%eëqݚgߦ?=)UߵBH=M IPror_.*F2`$0 djhUA=Ie!@u磗7SOOw>)-H9{G'y{%W $ ]d$SDPmپԾM7a`m6y?iz$ɠͳ*|1OaqxMCIwT+6y(pziGzHdhBz'I ﴒ.qKu?)ɡ!×=WNЕzq6N6IPrd[ A*`J9 ٳY 05!0y?C9NGwǼy0}lчճeQШJ5kEL\t%ķ:g I,~dl$Kn%]TE## hxhDհ)QT|O1(aX$(9S}Gݪ2mJY!ϰfaXHOEW_W;o v,06_ıd|%6= J{5G \h!~PwG-c Ln>!Tb:њpa Ѹ#`.Ҫ ^꯮nWHQfǾg<p >eJ“%[Z}>ǸoDA􇵊8&!_d9D܌ ''uC561#7 k:-]#F ĩa 4I'~l}]-oۮX{34<l /`oB=ȖKpr(]{{ʃTc 0,\Nj.!7c 0 c}c7钋<{sPH"`pАdyWR$/zӼ>ah  Ho!*f =`ڦ}ávˍO{?~}' 0hi}|DŽ䃇c:1wڱr!2APJ~C3 8mqL`Y;[nW],'5{J{ӛs2$[.)= ^Wz>%W~ë/.Fy?7IRD,p-q|S MD?E'$a2R$(9t_VD?{+yV@?76U}Q.j!|׎YÍs<̀a7w ! OD;r_-ZVuKs_-+o )i ^}7m;j9@|m{WI;˗mzAQ990`?Mخ?٤Qnq4/z aR$(96Kgrx*GcKQ&{u7۾\vR]Rɲ$ǔȶ[AbAb0 R AbER`(NbHb!6qQ#HQPJI",6;sg}KY;r̝f];/lgoX7:t֩.CMrL50J#%`R#̳O6GV.ƣ ^m͗pګo|zegSD\XN^xv׎Fi5i1~W u؟k~a{TS88i-;ks("S?hgo~K6yJR{`zb/5u Ɇp`SM ?^ry?o|FZO|ʭö@IHkKDtgz7C<+Ooǃ!6%#GyKVI]jU ꕐMrL5ͫ5*mTr)x/pnFjyvVa0H= 5znۺ8lb[ږ6sri@xtw/ r! 6x_>\wu?<|_{??oFnotW^m^i U%b`SMA5%^* _`,jM ͍]H•:8 (8IX+踑40F_lȮ{n袋m><*,Gvqc,ʞ\p:o޽q`Ғn~BǛјǑ |s)pYzP }E IH&9hE$4PB_`6A4JͳOvsK"sTŝpG{AW=[`{=2۷_Vczvaaðn®?,40. \c>ynJ엟pa0?SđKE~ydž-ۯ6otws&.g S޾ޘC6nZ tC<8e/ 8*9ٷ1+\_ztl_`6u4J͒*&Rh):CQq0|Oܾgtٓ8ۼe>k]0۝XSO>A7q)[A ?%Zc#Ac 8O_Wu#N{08'?ӝjԘƉ$:v(*iQ[7vjt$ GBEż-j]~<4,qLNNx%㻟/}B*<(S/w7-򊰏n{Rw̘.g@uS۔e;p6c$$68(|ÓO>#x+_f(l5f-j^~TDe^eWd^0Hvv;5c9RXёhv76$17~=?ʝzO90{>({R.)殉^dr#9_}H[.Ѝ@m~\ʂ\ݧW}Yo3C;"n˅=?W~] deڄT lc)hs4ZQP;2d'cYfIv2̅& QJg?6}c/=+[E}t#Y'́q/~c'5*fpcz⽿p~r!o {S|v&=*j =tV.6 ǩNuHc Q1I` |كTL1'b2KrHA Rm{gwchC"g7- >p`x9`xx ۇ;t㞁>C5$5Cavy%Ց/}vjpeų=Wm. 2o<7eÊo' 8),*bf/XA_9ű£"0j>[d Iu;I"nJ HmwMY(~nuyV#xlude}CC6]EGeO$;+s%Q^y) \IDw ĪIM$1C<?eQ;o/^W 7\wdN~)͛7k~O}q<#ā>G1=å^:XmpCE`;],m_a71<*@lyI7vjfIvbI1aX/ou˵<M牧?yH9m)jh,P˹D&a\&J{viJTkֲ:N2ؓՋFrsMo~ګϗ#c=l=< l@q7}OvBvx?5A#E8uДO˭"[./X5$TSԺvUĜ5aJCrdRFJMHSXv76>YNw x 0NJ!jPcv F@թH_,29'iқږUVH9 Ç ;~7rn0F zDC7{plp(x';!GwV Ըg=0:=v`;1O7 _dݨD8@Ť曰KXńW )3!9v(hƕ;{T#EY< `e{O`B &N(X14 3aA4faFAhMEUgq k{5, 1p0«Й`_ޫqJ;;A.4TN-mEOV8pd1W1k¯pq?dRvq>SQKpWm}lu_چ]+tyvvc:n4\%1.0PFMIULX9B,uX mH@)L(xEUSoXڮw8F| ߤe]?ƶtpfiñ#IAuӡ 15eNMmEoH ;_~|vQ8B9(2<1K=pKW\E>0 qkzUȅ&N"t%m ~oC:K+qBSM3a"^7md8(gcvPcݴbݻd`[ƽHN>>&Pӱ 2kfd dՐ>v?`Lumσ)l +hSÆ/Y.`tʱ&SZV/r)$mz~P**&>?yKd|3'KKRexQVy%|t]EeҲJ!͎{5PlHw?OxBڗT&6: ?t-F9s8 IJ"rL5}z~++a97 tr)Q/.nR_iTta5s׍6ydÑOJ|E@J1^U7yPN%CNW> އ]WgoD]2EUex t`2)aj H#ẞv76$slc)xFEU&,W, w;{pX~sSv4Yˢo|߲ 9XZdc#aȁGuV69BkdVNjzfڵIo'GsW58 |J'X`c"6BE- Ur)t9VMB+@d!a)TS u1!Y1MrL5uȲW޺z~?., W ptu{ 0۴b N~uv kqqT1M*-.]#rW\~Q-Z,?fMovײ@\ ErMdN+t~ORRSM2T9RPM0.u+u&j!8 p;f\".0ʙNIu{ܞ V[zG @"' p{r# iYk:&c\2n.*@/Gd]{նoP}hRWc{xvRw=޽wg /Q FL GS'հѺ*W 6ˊ-ˈqlhǎKy;d`.%?k {AkKr)IDAT2< N/Qbk6.]w&C^l0aLEݔq\BpҘ4 >́GCTa $B$ kIN]=͗' ~mRZz=μI&iuj/ĄCxǍkG쮉Ef׆xoYWW^ug?ys1b}MTx?3胦^~+~z8CL\Q@\\]vmϲ7T$9pUTmAߺ?Owuco4g,'8I?~7oÙv 8p1k_<qd~[\W>1lTUϦXBْVvKWb^|_>_8||'ל;ʇk5s냏uvH*X6ƋD$큵Gog{1@q5sG0o)'9yò ^Y-/ De/]%ηDa`//_E>w|ߥ~>#%Ā󠜲gJr`t])qG/wA]OZY'iCSheK 5XVpڐЙj EU6Y_ ϊgwtKq3W6`u}ʋ&_zκYw}6 =$_':4O+c# 8M{~a. Z6 .^ܧ!6H;-BM,Kdp\|BQY\Vn5C)e71SѰkm-CL?8t"Of+_~*MٽX&o@5YR16C4XZp rK7}nez MiP1a:6졘pX>`5mHA ١<52m3}t׭emSw^-8ceM|z;]3%q§Li@SmalTPpPyP ]j8XMC%IbG5 ~O?}dx~Q9ND:tL~*o={8|p"i@ Fۆp𔼐ta7l<:}׃{?cOBń5cM l n*6?|VІd.tTMEqO=sx뾸[zFf8{=}1޿K^}ӆbd8!M5Dco9q?|yfg;}tkǏ ®asw- íݭgקRsç>qi@},`t| ~K?h qC-)FlI!9v2sFj0FyGr?ŬCrRYo[tY)ѽþ?';|RqJ9 X'~aQNZ:<'l]sÆb82SeX`0$E]W;߻qB|rl'' 589G6Ǐ?}ṽwVpϷAccc*&-uMy'OZNf{JqCd # 7oZ{ԏ ~zksZF _2zh𝯽`8gN979+xz_p=_w>,gq>hM_%D^Jc뱯\5(P*9υ[~v qCd 3芺1ͷek3= s;we&Y2β  ~ |t?Uaǹ7Go_axk.7ͪa^}go-j}^H }AoO׆z礿ka^1I^B$/8(4LecO2,Ϣ.LOhkD 8-9"xkK_s|y{0p6;˰犚 x{9(o}|w }GDv ]VUuuIAQx ~$2y6nңRZMfYVb΋'Xn],y^}95*_/3 P>\hFni⹰(c~lˌvhB(Ԕ:NU-S"$TS0wѫ&bV#*M lTP0$ĞktT{=fu!gK>.4ZLǸ zAI3)ţB=QMJԽ<<+'7r?rS޺ ,k)cx 5SV/|1*$^ ѱzE&]#/(hѤQfRruKTr i`I2R{)UDC`Yv(Q1tLۑP-bnx Dmu5f5y!,1f*F1*ȷ놫Ŭuz8Xsp3mr($c[؆ۓb64F 5PSD6 P>3 % /=?_ug67JfI*(|e9Dv6(x8IQU2܁~&ɩQԔn_~9X࠰( P5iQ,R|ږq4),uaay\mlr&bw I1rM_9_iGM댜 CA*8(A׉ryȰU~j$&Nr"cڅs6((OTA2cFi;be^ǀxԂ7RhjdS/.mk=BЧipXWLTN|${ bm:ĠZ^zKIl|fB&D 0ѯ4w 6 VQ*&:꒜U~j^PH543+'iUOEj˘ag`ZƊ\9qSԙLa9R roӹ#Q̨=Ov6^\"ۘiĠwl3L7iм&q&uy?PPZU1IЉad,rW m`?i.|@4Iv5EHCypnN.1e``cQ(/!Ј&T.向*ZІd+ͨ>ffI(_?5$8r^=:bZ$1+,H )Z aKpCV5D2%viڊ̞\L WN5*d7)M~uXBBLfޖUhCs(X4iܨcM l|*(x-n+ԗ,Lb0mOQ U10PfJQ.I - j{ WjN864@՜6ZlJYLY 5tLؓjQE"Xj jL mHؤ]-}Q$ І,L(DMUUkxL D!J@FU}zl|S̒*&ގ5[ub3ڐ/paRMAsRUZi*HT@~Oթ,O"kJHɚ TXPIMa9Q9;*uD 3) G<0L6])xQF׵-]g3اhfTN|jhT&l]2jIXOTS`TuphCs&i ^GȺS\Ndk'feb'DJIMծFudb_a,?"8T- ٤Bc'j a P5j$-#mHu&5l9s?FU$5QM!d7)8)د iFPu$;U΅^DvbfN$') PbE HjۗFudb_a,?"8T- ٤Bc'j a P5j$-3;FqZlCF"3A3 JZz'jE5IT ؜쎩KtE] 6>gaF0u8f'QbaS̗JDmC0s}njƓ0 TPpP4;]Pw:Edt]gOfdW1aȥB5眢.@C-I5ͩ5UV 2L !@&\۽PA9K)f \NDR q;i&^BtL$fsRW1JTBǠ+Xa)4Zbk7QDYc; ɒ6)pDc f#(7au4!FaeU 6$Kؤ]ZiԎ3?e-ɳCNJbl~S1 g% ,?J5hD-C|4^9TtLY"o% +phcbF0x_aձq_ѷĞbjuL5, %=1!i(W'qd -LD_ƺtSRqaiyG)/ps)R7i^յ87s\`q4 ?av/oo/ͪ#*M ;@4l6i#ōRiI(&-'UdVHPmHm;%L {R| k=rQp` LoRgW1aЭ]HNP)j (@\Ujca^*j᜘If!\ΉWZ8!xNN364Jn/9q;AWDJxu_D뽤jKpL5tL`]pXIMah)$uLA*614F+&N|OƐɃ0 >v>jjNdc|(-bp*^pLrL +:D5*330^yDHaT ; :9t9v(F+>#&i(kuiȰ8qEvjP(d+'s y; ;7#mt"•:fqZM;GG|,uL'|j#Y&IU!~lj5яs_`z< |)̜}T4DAݴ*&FD [VŎƞ'1UǙLY5hja,uPފWRE!E I(ͶqǨ)n_tLq:1יp*>J#Y&IUi$K5DȧA\7)&)qebꕐMrL5 ъ>c[QHϐ%Lb!luf"ҎhgFN8?]43GɃ*mX^ǏZlP&:FHqRc!Da"W^\H ֨i;8ki3C%~vO- w_ {c//(}ĉ|go{8Cxx*M8;GjZ@+## "ȄAo#j BltF9noآ{yab5u|CjmߢF6-G+4mo,~魾r_z4?w/,z۱~Qh~5󣣧;;֋ l6ێ-ݙ Eڐb%UL8D5K! 6+!74 J^(Z#1*F;}BU::WFPjz1As6aW &\,8GxJ+N[o[Jo_ذqR^m m~F眅a&2=)Ytb/.ca!L%@L8Iɥ(dTĠlS< 3RMk]!GR)5MpQi54LDP6Ae 5fe) bNG`&2x}9~{ر :G~6oU޲|b&y

-@ Vut(3rz1soZ4~Y^*xw&+EzC_z|8Ņ ϊ/fQExB<|۾/?=sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx_U>^C!C@ݤ.'Q)RxH%%}>>TЪ*ERn+56JIBb$@µ={w1Ɵlgkgsn. |ߋronnZ.+[^ֽM K:3\Ys  B)#~:Z[+~*OO,(n#t I w5H Bg$I8&%&/TPPhD{yglr%k/e:?WE@7(}JD hRq%_s"3nt;) i:S? /˳gy{S?_/x-.wԯ}69nݹ DܯL" ,m6Z([j0*OO,(n#t I w5W .a@(D[8hrqV9J) =F&APn6e%KM:DzJ@ Jp4w !Cξ sWg^1$BuاI'zT[e3W49|sQ}7$rss{{M-__q?sou9O޿˟w7\ˍ~3*|k +/lV90ڀI@R-zq\jn*q2S:3\UDR bAkxbTa@(hMDQѢ1D`>>˥bώwm1p] ~Is3= yALj+V5=6` >H"źf⽒/9n[49衼F%[QƷ|0_.//^ç~wy[_}_I^O8N~U Ћ~IE\!Q HSݕŕr ӠB! W"H",=ԛń*`޾BAC ݩtUzvwu|?|o%_gob(۹Ћ>yBxE]+a6TsuZ[+.*OO,оpFIA(h܍P+ބ; ^@S4Ijj[<H7O);)J4Ym v% gw%r'k/ՙtrA@plzm(}JD hRq%_s"3ܶirCyuKB)JS]S|}}g./^7_kn{w/>(aЋfGDW5>b&cq, B! W[ Eb T tb|_ߍON3xeWh&m-ytWkkB靅BڗW9{ BQJśu)DS4Ijj[<Hk4:P: Af QHr&kdTL j6ֺdO 2RVTvc \C3Nҕ@> ,r%6dzpfZ -,e6@.}JDNg<ŝD,K4%衬k`TyATi꽫P|7ůN/ؿW>|矿?/?/"xeWh&m%0]+yw QTq }{U *DS4X#ۤbQCG-$}V(e)sg2qI`.i!Cξ3 tղdm=`(kwQS$bu8A.E'reY(,AeXkRλJS]B_ u'"_|3OGrGUϟ_O7EKb4KDxeWh&m%0]+yw QTq }{U *DRZC PpTk'}V(e)sg2qI`.i!Cξ3 tղdm=`(kwQS$bu8A.!eFDSa`n6X;uD޻ (*tasU7/}gn_{ϻ=?OG _3}Z_plҺ=<# 'L P}>tvh=B! ]3rQxzxJQHZJk3R1ajZo~mFJ=e d0/f+|XK;m/,],Av[jX m]D*v,\g<ŝ#hh*=XLЭOGATi꽫PBƌ\+ N@;]Ra1Ҷc:xhsA <# 'L P}>t]Zavq; (*/3rQxzxJQHZJk3R1ajZo~mF' \)S(e%KeScX̀xtW$q4wې!g_X jY2@5ɻ("TXNx;'G扙ٯ$Ä-%K5`=GXwJ@q4w!g_X jY2@eH-HQh$'Pc:=8A.!'fFDSa`n&}:JS]B͊V~.E|?3?'w< _&h(¡L`*An#ulZ[+Ё\{BEbJI>m=* c DM"g)! Hńu 8>NJ9LbiQT\su\0~wIs rY%3TԂ$e6@.};Ӄ3ybf4O4,P&aۧ 4U(DQѬyvwg~֬l?|_޾ߘ݄-/ph),m6JC9%u =t ^aEEpRxzxJXBE>QHYJk3R1ajjdORXZ,׀a +r~0,]!2W-*!92zQ r(BŎsr`M uh;*Mw QT4+v]"x>?)ܢ߼={ϛvEֱim0;F,о̸AJI>m=* c DM"g)! Hńu xk'}$r+ ZEp] T4JK;u tCuu@We=$gYTB/ jawQEرp wN3y`rv0A>mQB!fྜྷ{/ zg?&\f$?w߿?sM=]>!qeA T-FشVX (*Ĕ }{U*DRZC PpTk'}$rւÜԊ(*\nàrBGH`;aݐDla].UYY! Zm]D*v,\?6Wz`vgF@4>@9;[ l{WEEbqI_{qy? |y/ȟ!<}ڛiDشV} w QTh_f\ n$C6D共|&h볔֐fb:<>NJ9Lbkb}aNjE.a]9#v$]swIs΂n^L"N説,ːJD-6@.> ;Eرp\ٝ]T`n&}:JS]B͊A܎'˟?L>v?!?ph)L`*An#ulZ[+Ё\{BEbJI>m=* c DM"g)! Hńu 8>NJ9Lbkb}aNjE.a]9#v$]swIs΂n^L"N説,ːJD-6@.>%{"v]?6Wz`vgFM ,P&aۧ 4U(DQѬydRݠC|^~kfcZaq/Y(DQ}q }{U*DRZC P4zO쓄RZXrZEmtWNIWl]!2-*!92zQ Oɞu͕!ݙDSak O+9cۧ 4U(DQQR[8]sKkGw{ٳJ=a ./ߵ؊ m PNoI6r]ǦjȵWX(DQQ$ d#&ރ0PO$m}TLXgڀZm>O) &Z CsR+p #*K;utCbuu@We=$gYTB/ jaw):=#;3:h*lm`'BN4vUz*hV<;og+g?`2\;mD2R;kC34NqeY T-FkZ[+Ё\{BEbJI>m=*ihCE>QHtTLX XpTk?Qc4ւRgԊ(*3tWND K;utCZuu@We=$gYTB/ jaw):=#;3:h*lm`'BN4vUz*(V{/?(&Y ͕e6`S z($wʱ9Zġ (*Ĕ }{U*DXCk3R1"3=cU,hbkb3 NjE +'te"v%: !{-º:],C*0ɻHQw\ٝN46@!~[lB!bNww1P-¢o}oC|_?pwo1_n Xz=KÒ=pe6`S z($wʱ9Z"c,е1*q%>n>_@WwY4' RZCx]{g&AX@hK=ZXꌂZEb aswIsn^K"N説,ːJD-6@.>%{"v]?6Wz`vgF=n]u0ɶOfw QTwt&XA((:>^g/ Ap1׃ tvKog]KphFkNqGG'C9%Zɱ9Z"c,е1*q%[VA%{ya,"O j RZCO$MHɖ3PP=QiG[ KQpR+@P]9#,x.iQ kI]eR HEڧdOĎJp 5Mx"ŽZ[+LO,(Vtvj J~/oksx GaXeu*AehfY*suk` (*t03rpi2Gfې .(=ZC9?qG?pRΏbkb}aNjE.a]9#v$]swIs΂n^L"N説,ːJD-6@.>%{"vt]>=#;3:ham{ev(ImZ[+*OO,wt&XA(8J!O|?|vWU[IJvֵh=pf0i PNoI6rcsEY{{5rU&JRcoYm共bw|_|Wߍ0Ga+[L`*An#Wyy-Gv,o0*q%T䱷6Jm$"% rK֐bO$DZCF נʡ':-hbkb3d+6 +'tĎx.iY ؋I]eR HEڧdOĎ+³ Ό6C*9=uq ; (*;AZ+Gbq|?ۻw/? 0>~&,Rv* F0i=s[(#5O`0*1$U< Hm21G3Q@{.bY,G?pR/geX9me< +'tv%:!{!º:],C*^1wu^WH|(+ؤVٝm]d(@ HO ZaTY(DQQ܆|:a}?x~+[yoo:`6P^٥urlRQЁ'3fUbJI>`P^^KH֐bs;ϚdA=r~9S,-Jkue݅ݕ:v%:!{º:],C*^1ɀI}JD`8e8$rЯ"# <03rSJ6JXBE(@@,㞋y& ܧ˙biQT\s,.,,U`;aݐab)^G13ܡ e&y pk[ACY!3Ύ=Bw̨b+- `穵lB!bNww1P-ZDW?0t[MFKe`NdFm.\Ce8$rx% y2SQ,bJI<y-0P' 5b)! ĸ"v5ɂh){$)rXZ, ˺ t3@rxU9؎CX'@7d`Xk3]!zct# &ps>%{"Vg%{"Vt^-Xj82G:%]7G9 (*]q6l Vmv1k×`4znR-2'26;KCyeV w% y2SQ,bJI{js0hIټPXJk`OL, DXDz&ecYwaNwpxfH˷Eg`^t-?l×wcJ3F>%{"x{b락evIq4ފ>ӎ~|XM4`!R.=ahM2衴{Hi?4 LyP*OE{js0hIX "7F<1a@hQ2.5QTjsg`f .U-`; i) *ֺeyLzX&݈cI\Oɞ-].Wf ֹѭNIA@9l.B!bW n$d6tHǰwYWo9]L8.QJ9?(J:dXĔ\7.`AwVy rKW#0ןrTBGl鋚ehV/tC Mዳ]!zct# &ps>%{"Vt^-Xj82G:%]7o?=n%{"Vt^-Xj82G:%]7c(ЭTt1$y#Ƃyx ϹjFѼS z(O}$r[׃tLE1bJI<y-&aM(Ȼw~xw~㛐a4urFhX&݈#nݔi=Ab락*33s{).2 Gt/{+>nj_EJikvy_lW6mE .'23X&X PVw~_uhtZL# S$TF .n@s|tיlř;şvo 6j%{"|\;{EUvgfRVP.%Ƚ~EB  QTqR*։G"\E^(gDrh 3%衼rV wU<(t1$e.VtkYjȉRދ3z-?@! &JJ4Ym ^%X *`yf/l.PCo(zav5ɀI}JD˥w6 4.衜]Jr{h9d((t1$U6ŭ9ɉEJ)w oT,2'2-6;;KCy4>J9? y3БɢJRME{1s94ưD}jg{3fSCrUҢdEX3p8P;ÂW `;am3urZ85tP@;aW AڧdO zҸ-u@j=>-Gw QTmNw"5b븚F"5yG"s"3hS z(KM<< ǵfAgdaXuCg\~dqumt%xRʎb kdD< 8iWNjKz =\wVS47GͺFȃ)䬟ۇҢd1ycyI 7x,}!SNKt=QMZw6v;IA_O)=: п4s8'2Í6~ Ĺ񒪢-G6*T(tq+%}'r{(R:OSI]Er(mG1rR؈)gÚC},td0EBen@v"cv-򉪻@H$$PF)?u)Q%@țـL%Wo*[n[r݃fj!H &H+uV@Kom\%K%Α7g`4䏖+IJ(Bt̲eyAyC rP頯@کID5f{!!36@qۃKuhUDQ_6kNwR'<nTPUtv̮E>Q}3"W6{ KC$MD= of:2Y\Ijco9wDquANL[qNyd6v"ާ~nJ s}|@hK=6wuӖ5'2М7vĮ*eL;"LYX凅=\),Z 9CxIrϮB% ܾ\$F\ 79"|]_L6 TFvH9o:*Az@,td(~SrD%:qFg7M^Y'@p6@{^NjEAw儎ؑx=Z&ȴ/,Q Ez91x@J}NH"¨J^ep5eFaZX y{xIrϮB%bW n$OD6uEJ <QE_p+ʛdQL%ܮ6bmEʙ&yư2G7 ,3LP#ުC3&^ἲh7=]2aom\ר}Aw儎ؑt=y&ciu;Q Ez91x@J}NH"¨J^ep5eFaZX y{xIrϮB%bW n$OD6uEJ <QM 8M2@(PnW "`-Q\2Pp;A"14X@} YEGTpey0 =JbR="9fTk 8)%T䱷 .) aBEP&u;:8i4{<ڧy>Aڣ!wu|q :X ʍBA}qԊ(*8U]9#ڮ8urEJg^14O9oztm&[ej2݌6@CqۃKD-QF!bW n$]k Z ֠ЉQnKu+MYJC]b 7D= of:2Y\Ijco9wu݃f%:G&ӿh{3{%} 9 .MUS3*;rBGiڮ8urEJg^14O9oztmLҰh*ԐSfWÞmb s<^'h9*Mw5 Ttq+%^[Т3ܕWk6!͜Q] ڻ4b*Ae;:H938*A'#<2YQHYJk3R1ajj')0aDkb}Th.Б1GATk9 %b 1kp h m]ӔQw\ٝN46@:OGATi꽫P ϲ}LIWQp#E&蕦I`/r)P^uh4%< QThW9xD@AT^^`P5D[,0#6|?pRM,ևIe62:O Z~IsǰLn^P"N説,ːJD-6@..%{"v]?6Wz`vgFM ,PD}:JS]B͊A'@ BуHI$ʗ&gV4v=[\ױimCr QT ;)% d//%T5D[,0#y^[){$)RZXr8'1ڀD<%tdj-r'2!{Aº:],C*0ɻHQw\ٝN46@!~[h;*Mw QT+vj  j[ ^@|ib?fn0rzKr(J:kPH8AL)GpN*cyJZO;etCuu@We=$gYTB/ jawv):=#;3:h*lm`'BN4vUz*(VtqXA(8AAnʽDW4v=[\-GQЁ\{BEbJI>m=* c yPQ5dzOc͂ʱD*z4M`EN*cDr:2e!r'ókNK8/zM wcLn,(lwv):=#;3:h*lm`}jm0<=P ; BQFu P$:[ Z]:rKd'vڀW.; (*t>3r -G<|*` ! HDЋx{lP$Vԣi(rR Б);я>i\sXyԣ^njcrc Da Kɞu͕!ݙDSak T?mN4SkkD靅Bm8A@V ju5zЭ[r/ 1XkMu*A47ζwY(DQqXhI>`P^^KH֐fb" E`P) aD$DDn)@{.bY,G?pR/g!sR LSBGƨ֢-ܧ ܐJ]eR HEڥdOĎJp 5Mx"eKom2=Pbȭ:Z+G:=-xBo ,p53bmJҖ9:]v,f\ Z*OE{js0d//%TBD XJk1gMn {}J9),ևIe62:O Z~\٧cv Dla].UYY! Zm]]JDB pN*cyJZ>i!{Aº:],C*0ɻHuxBx!ݙѠR{(@!9.C]zkkM靅B{EnNwXA(8AAnʽD|k`d X(ge9Zġ3Ef*4̌Ĕt}D-0P' 5b)! ĸ"v5ɂh){$)rZXr8'1ڀD<%tdj-re4{j͐Dla].UYY! Zm]]JDB 6&*hiSyzzgEEqNw!] ^Oë_r/ ѶK`0%6M -96GQ}}eTW9h4X&`P^^>p' +\Jk1gMn {}J9)%K5:Dz :O ˆ~\'͞@3d`/([XW tUCreH+w6&0ɃKɞ g(#= Of*bJI<y-0P' 5b)! ĸ"v5ɂh){$)rXZ, ˺ /<%td #r'ó\)E(Wh \֜ 7LG pU]JD>qƃ¬C=3m` ^- `Za6QyzzgEEqNww1P-ZDt< Ht̷ZM™  uID!((J:S<=\^qSJRcoYmt7 kD@F5d\γ&7Y=ed!bRqERq αh9=D40h*ȏbyj 6c&݈cI\KɞA,& At̨b+z9N4SkkD靅Bm8A@V ju5zЭ[r/ 1Xj4&}{ ;v]^fU,$u+"em&fB@b)F<1a@#{frChK=XZ,7%˺ +8+я>i^CǜL^T._ZˤqL0ɀ v)˥wl:WÑ?)z8}(l0B!btѮvA(8AAnʽD|k`DLZ ~0vȵAWzFlDSQq؍tE-&aM(Ąh͂qӉU,5Ar ,hrz*,f<(J@ <h95hzi`T9<|ɺcJ3FML2Wv),].W^ !<"P&?}|3IB~;A V [J)u('j^Ki ,p5I> eLDh0fh;,dWcĔTy*[VAMšQh ,p5 zѳ2?y<)&t^Jۄn&\9mU!'͠8(H%2w)&CbcN@%,_J0Mh*.7ϰOlc"Vt^-Xj82G:%]7Sۦ8=P;W8V ju5zл|L"ѶMɤu _>6l_h0'0(Z"i2]z0hkB' XjBb€^Z\^q/r&8k!; (*p"5زDtR艚DmKi ,p5I>A6ԑ ̸6J¨57Ɗ $U<`Aw&AADnZ \xb€^Ѭ// bo|[?˻~^)쾸uHpoCA#Lof֛{~3L^\~?˗?~ OKxgcKnAQK'334Pӑ>>Z5X (*pR* d72&p)}PO$mXj4&}>3x ⾝*[vy,s5)tP5kCZc=1qp~/{vط]Wgf|#\]vyߊ7ˬzoߛɜwc#˭|g֗ɹ8-c_Kc8&oÊ5EEvTٔ\ 97HտYQFc<0C&3,i+;)%钢`Aw&AAݎu;BCn?xa(~B+/O7;w~=zǷ/Q?C v.+:&;33"Crn@NG|ѭOP(DQQ܆ĐTN<2'' {SvmC1'0(VtWڴcc׮r?4XX#DgCJ fO/|3e5xO;9܁! ~#h 8eEʥ?^Wٝ趺rv)=@M(J:_PXĔTN<2''՜OZJk`>1sX{gC ?fd1bJI<y-M'Z,so"s̪|˟^_|Vuu|O;8?,0~wKK}vgfJ]=KIG(J:_PXĔTN<2''՜OZJk`>1sX{#JZC k/fAgda^A\ fX<6<mʗ.G;wO7yQ=Y_| Ȍ1@j?rJ=}|tqO*hkNwnHln[9H_fQڥzcdJ:~xԡX5gd݈i2xW RejH,p5h;<4Sa}u{3?ӯ|ځq燀_Կg"nn+Xʉhh),=xD?Z"wB!b9NwR*fFihEk-,p%ޫ8A?[!&i<D!67 ,J$T䱷;C-j{R56(BOn9/#|~87N}xewOG- Ȍ6qۃKK(J:PR 3bJIl['rӓj'-5h~z=f=|X[}nAT :G@U3БɢJҍ;C-j{R56(BO%#oo{;0^7!#;|ѳݿ4Ow9aj s<^OGQꝅBE;[)IM sn'-5h܃ڃhO_^ڒh0ȞIu;Éi31BO%ϻ]| WGج'y;0)?,k?ה|t7Ȍ0 9o/ ~+֮,@mpPǔo@DۍDzh6gXjO̾w=h>~%L;to\JR /zuSԘXBkhcDD.?iPv>pp#)V2CT+I ]fۯLa"sJ<#H5L M"u '&W@Qt}XǦi[.zEeU9= BҵWO$TLXgڀ1C{^wz=؁q~D~&_?<^Jgwf_Nޝ{WEE}et)k1FG >DP$WI8s@r4u+ܐfd//"I$,5d 67w/}T>;8}/_ZPy[8^Jewf_NػvUz*ocu3%鬢זc^EuDWXj/dm 8én0;F,о̸AJI>m=* c DM"g)! Hńu4Roz凨0|ځϻW&@>|w2aG?3ډh*lX5tKػvUz*(ncV3 P;cxE'{Nqpd"{`Ϻ^iirFQT\f\Z` B,5IMMTLP/OQO|8]C L6@kniڻvUz*(n{Z+G:=-xB*_j5 gZ(S7en+M"{gEW94 FevJځp-D -[C04lK~_˗a/q~W˯dc T^l;!⒦޻ (*iNwo BQFu P$:懡41jΜ6Po` ЁL]PB3̸AL)IGүqЬ6P{! HDЋ8kg6OKo/xy\T>؇N8zSm Ɍ'T6_TN4;VMTY(DQQ܆] T `VQݺO(V![WeQ_Gb q읅BښW9hk8i2GfCB TLfS1aS_;0δ~'!@~)+$32ڀ Pvo;VTY(DQQ܆۩PpTуn݂'{W72ދkݖ7" Y;kgg3r-0i2J!C4g'|A#[ҨP !4.k5}e$Q y&( A!ZJ8Nv\0]%lV t]~Nwj=;5b"*S AUZm$H G zba68Pd(W)U[Ỳ.:5}e$GW@B$R6 :Zi6+EdUmۍOT';ݱ}Q k0$(oP@^޼IAD"e0]' E[9Fm$bkGik;/?m9G:~Iz~JX9F]ħ1;2LR%+@& zx.OC$N'YR4i퀃7 ,aڅj@DZ?i-PQ+L\,OKx&~I"SXQˢs65a '%(GO}f܎XMGx_`yԕu\+@& fC*B$R6 :Zi6+EdUm;ڶ=PtԈMB#nH\Ч4ś68Ht0Zm k8ʁGӓOwMss2u==IՓ?˲Hˀ]YՈΚ2` d#[ūP A!Z)d\4i퀃[W` "C6]xqr:Kcֵ,+D2!Htfo_R D%͚(HLH x jYsnF"־&vW>s>3srݏnWJ:ˤ36IPG*~`/)ep0 D4O x jYpIe4|u:zȩc<wIEeLc|$R+wʺD.Atc xyPd"HdKFӤ so XfX=r(hx65SwDZvZͰBT( љ}I)3}O$K`f,97j#k_pr AvүWљ7v&Wz5LmH*1 AyʺDAtc }xLE"Ihp 8wă\=`>5T=$oūT'OxI"nt4b ߤ82^gg>{.՝<I~8ɯp Ae%]Y5Κ2` d+W!1!(DX_S8 8{S6"x ~|zt!ӝ%5bxJ$:3/:$1"T7$f]%ltH@k_5KVlC|[On$YXs&z{MI.^ h< !#D[P2$KYWL̆U3AAL $:Wjjweܛtă\=`?MA*\t'0d&B) |_R d%D"DF-6CC|"b hzlgw_JG 5mh~>1``]]x>H;D HY2`MaK AX " @d\U6m2@ Q A3Rf W-I 2I$2j1t4X\pi |m+/LO<=m #!'ߥl0`\?fJsIdT.sw XfXr9ļix6UF֜4l$m=p(^(o⁀^+6/V#ZX |9,y]X\y>V,|Yrk]Hy1}|0;<}nx{$"8+?7ΦɼSX!H3W-3ɍW0t6"x ļix6U@N6)ūPz |_R d%D"DZPjESЖG,yk[XWPths)P39R|X͓'O^z މ`oLMϞ<?%2Xtw,]xTԕ̀S6•%5bOVS)OtX$p(^Pۗ䙐b6*0$Bhgd[g! Q}@6ǩEwtx5̀?Lkm=|j:ql9 aIDcwWG^xF}팟;;#!ݽv@?# <'ȸ1!+:wP"J02IūPZ AQXI] 8= ,a3(29ؐsQbye5q*ۍMox+ySYjHj8U h#d[ge}\BƲ85~(![k[Wұ#:+`Νy*^ܒTd.lY9>!LK"\P,,TxJ:zt=|`# iee Ͼ EuGY<c. }r52eMQ ^͌tJ :/ 8+͠|t[D-pK+HgAUwc kUXketJ r+ߏcFtwHkR]bThL_{&$Be%C]J#g!XW83>F0^L ߕ !*B`fzC7-p=VZo\ZQ4cB:&;}2\t;~ @N~ qJ7-G1e/=>Oy{}|4|͟\K7FKn oGxrY},r$ڥxJK!hu@vG 8g0]%ltW ֈ5F>=NX LO>_b,"ls $SP+:Lq) pf:<1h4.mULTCq-\dM 9 '*H1JLbXXG"D-/}+ŗΥ_GO?>9 Ozi2Mt{aAʾ9` dvO5BYj5S5p")#d̾tGlȹuF0^L ߕ !*B`ȴ"νSIs6D5Fi^ HȴE )7>~~4IQw9}qtKN@&[z5T*AjSQg +jxS"#"8l}}9}k/J~:)09^LIP€5I=Q ku ] =!p#uPAt!\YX#֘b'1d(N<5D<ɍ؆s7Uǀ,bQ땩)+DZPjE;N&K8s~ Kx+B"UiE{CN01Yjqh>u}8!4ehG"D h Y]]JW^@h5s]ā{+e1^,k U(A[g8l;HMU)sj a?Rj5ʌʉZCY&C X!*Wۗn@jjq5LYI$90Xdua;GLWƲ85>E(!0d~X V$`#Y@[C5@8'ᠢ< S<Hta ]:Nkk񗾨vpU5O/whW? 'J"ku d`P !hu@>ImASUj[!X{ ! sxk.mjiᇲL3cNX!*~Q,Ϫjf7LH[R mZilEʠP*b6ǩII!$6w4dZ6~ hrrH8Q6爃;1(;%2bCפ5Wv?@m<'Ɠfշc,+$X{Bi#m(߮Ov>8hJ18_܆q6Lޥ иy0q % 'rG(o諲Y6`J"Qm UVudWQ`nù>m4.ma2!$REhCFT\XLbpc'wH2\Ts"=@͔aDW s`蚸q9 ӫ(y N~zs,+$XBq:{a#PޭOx>*`|!K ;D\l |ji0r㳍 ϚBM  o]F)0$qPjXGqs݆U7K@9K'jJ2u| puxZ^لRVjLO!͛W$@ ~($X5Z2oR#dYbdN wEv!M 1O(S4ޚ6KxZl0f\ܖQ[*F)0$qPjXGqs݆U7K@9K'jJ2wxj8xl̓p}Sm3=ePG"D, s`蚸qj5\9U8p@û\en*AKP"6$UQWP(ED>NBQx!{\i\U) Zx4$DNoN dU H'Iph햙 F8H SU sXخ6~:i! e[ӆwiTO ?y1 ͠ͼ|C K陊ȇ31,0I$RJMZuѕ !<:)3IT6Js񣀃 Gj_`:'jD5#BCNx(ّ,&nj) Ht~G_T Qj>H^ےиܘjM㜃ʒ)ꃝGC٠ f@r5S!KL#h7 6 H BU( :d+ 6k jQ-<JgBPb)[3Aʉz5"K q?nmjˆwi4n^C`74@Vկj܊<|_05$يCb.^b8\)hzqjBH9 0PN ԫYfPlruꁵjûXe& XZVl_Hs}kDo0,nhRGQ 3dv鋕# = b ܙ^(G4['e2IiMO^9T2|=p%tSҥ[;3jATsAX5 Sզـq:2>xl0~E[i)/YA! kT[\@]%l&qA=X#]mx6KTl0k:"߾57 7 4BUp2ʑsdt?OT1^Li/L#2' *@L @~Cߗ-m]x1lJm+kyϔs ^MyL~S8ifiR_Q\vfZa:޷6w 7^B$IoAy7PN zRخ6b8X[6K@%*P6m Ў[K9"߾57 7 4BUp2ʑsdt?OT1^Li/L#2'WTn@m4N|wu`8|GZ?t;w/^HwB <ܙN>g9ȼQU(5iIE]"N!&V $5`ʘdM"jf8XjtG||KI,/ݰI%* <>~@+͠^Y]mTk\ûXe& X[.%8"߾57 7 4BUp2ʑsdt?OT1^Li/L#2'WTn@m4lpᗖ`~LXv|\>Zbgq<+!g_ :XNzaբȼ[ByRDdu'9F~W (Dt:حOJ7PN ԫYfPL6A*k5R]b,QỲL.kЙD}`{ikLV3$7ʸ=$P EAإ/V#3g|z)rgJ{drl$5=Q rj+Ǡ8e= 9@z b4^C; wH3A-jn_jW̏l.߳u\Jv} K"I:= 'W%(',a3(֮ nmjy#_xĘ)3$'Hq4  ׀+)DBQx!v鋕# = b ܙ^(G4['e2IiMOB܀J1o fn#7J7IEc'J5U$[`ծnb(js)wZz-ũ:M9 P %$;U ʉz5"K E+i9F4K@%*|W1em:3IH7C|ӫ@"Pt,}92 pf:[, ʉL ŨHd'&WBu6kF0^L ߕebLwҚ)3ILH#ɀ$jzq p0F1*d:'+MbLT19oS!$L0T3*^Cūٜ6`p70S;s L=A؉L#p@+''w)|D;Hz]zM9}MF /?< x)}?"حO@91IG'믬s.S5 ߕebLwp>Im,&YDC݀$jz(\d]V fdDAb^J4|8H%*07Hg] x!6IPG*DaNU(@6) Ԟq =u]3@1؉R !I;$ڙ5S7NN WKi '>>g75wy9pWd Pn} 5n@ZzC#iW2Ab~ԇKN(|W1e$^<P7  ׀+sB2dDQ)|-2SFW>eJ7=OM"Ǒ0 d ? ( ԔqΩh !;dCb8Kɼ ق?pF p6U-1e Wr>XpryP@91; fMh\ƋIỲL.[ZkV&듃bT4dlm9G0Aqi21li$uYUs0(RZ(v:(=5B-@ֵ|zN:om%}˔'& P%aHo`2oTDfb0^C@UO)qrrh;ąI?wםk=xx`SbN0#2@'[ SYKr0Q 6쐭3U>h4.mW]Y&t-Y2.+&@ 5~eg5Ed4ȺR; 1(L4!Z5NXw;Cw<}fkUfⴶJ cPF _*U.WO;,nܻܩ2|PɳLQy' (;QZSP5T\5NX`B}nq9mk[+on,MMlphk:*Z00#48b?^yu7=Nz;{6ߪZ;]#j#J)bj6kp0e%Vҍ7,V3cIA=N/ _IϞI_څœ#}6='NH1؉Қ /;޲>FnݾJS<F7^zcñ^CA.?zsIαv@.4i^h̔AVi IeAb^PiTX5{b F&t-ݰ bB";( x"Y+K _ib$?^^%I?#pP&B N[q={⹒nwܱhK篽4A~ %k'm'Jd3đ}8x"rKe |=<{xM2W<|'N[4)@`v)FLS` aqX{uMҨ64'/kdbLw Sf Ԯ0O \vP\uatdĂWߤI%Ϻ]?^=ʏK' O )-;(n ]',@ֵ|j@1̿7\/:.R|i Ϡ*p>%v~aؠhDI ǖoHӽ_ Fq}EW𥶮2Oy (FljTd\%9["JªޓS521l)3IjTa@pwaEue$mľ*N@fjTXJtz+r-/qC:yC@{Oĭ{?rHP7$!_4vq <㍙9}ӽG>}wJB~ڮq@);dk9[̳=% SGv!j:eKSf ԆTaW _W'Y(0]',ib+MoR쀃eKt7_OoT9=\{x3Qx7OVKQ m?9tA}_d7>o |GÂ=񖁳ͰC]MѤ4*xsL.S`X3c3Paj\ ~j("ʀtdĂWߤI% ;ٹ7|w'iugiK^V;_1a`p]+" o+uf%lbŶ_iTXޓSاLL2Dj0T`i> ~j("ʀtdĂ[ioojЛ SG3Sepo#'7X99@+u )6z;;~hwo2OKxݰ|)#섻IVOvȝt҉-X7)xs;]Fٌ 9$WCŇEM M EPAp07V\YX+8aOr?'6{9`?x.ǵԺPBGj-}ZO?gڨy:1$ x%}tL2 !(^w=޵se3 |2)@&Q*> X-L EPAp07V\YXzOx_7SᮥeRQB Rf#zD~?gp/kn<W<t7╀zPی A ڬd,6.uRô\we]Fd5$WCŇMC,`sc r88Wb;j!P!#1=s8m ՜#pݏ$>xbFgnS6+ )t//  (<2ѧjI4q2(_  7MuN"ʀtd9@2ȺR;H~#ڀ~8_Ww|o?u-P<"˫-XqD8<=#: N<}lVHvȝA ]ߍ0TV4J(VyjY(0]',lRֵaM׃?~66x#uG`dнl8rTV|bC7 nίm4.·Jne6+}rsk>R t 5' >a4-LW @fS}Dʺ6,=ee 濔< m[bep C;Wbi\VN13Fx,u=܂9ݍW9un-LmWUgk>ALS*y+@Rk I$0]',j2퓲5H7)vA,zmFoğTބ]p#g WGX, 5_]xNCxO=p ܀Bb^ô\we]F$8@ICV#+'k I$j[)LW @f>)Z_5|bdzQ؁h ~TV\z~6#u=YJFj!|տ2o|,+d@ @'w+.Rô\"QҕeOwՒh8e&Q*> X-n a8XI"QJaOX2 4 ZiV;+p9z~F#C]#hlz#+Phw&:GAp6XTIOÂI@]N[b^J4|6KPɮD:* n8@IƑPitduڗ1H"Qx 65unԚP'}M64|(9ȶ1UaEίfph_^ĝlT N&lČ^[#U<ͯ?+p!|b ~Epre#i0m@1$]tU@q*#Ň2cDOdr04.־&I-eT:qvxb\Ο?tޅK`Lj wbBe$sr.:o_ۼFsnT|H[eesyQ7|6KPɮD:* n8@IƑP`4&qvK 9 ԒH$ErTL61}n'߃?5mrjy>5d3;pv 6/+oÅ^yuw>t(z-G7] ~ M'G1Vl9m@1dtU@TDl)> ?d $H7qaOdr04.־&Iqd[ Id|f쪍|b:s߾?~^dcƯ'Nx=lDt#F0ݵڏqCD鬔48@I;'Cn?d VlD QZaOdl,Cą8v:ڙo?E_WwWӷ|>=~x?VzZ^٤Ͽwղ>? ~a~qFO677ӑ#G/L렧 Փ}f æ^rΉh0m@W$]tU΀urLQ|*0pS$S@}_M+A.s0ub5 ߤ8X2U ;e2A߾NChJɾ'+/=6\[,~"nܔV֎@'׾u!> 鱹zM7ݔ^zuZXq<,|/Inگ^<{h'I"e lq8@I2Ň\y*HԶVԭTԒ7|^?.k ɝů 7C=gn*V/Vbt̓ɇӫo‹=.j >Sx/ɀ.[o5͏]">6JyزP]uzYElُ,`` X+HH)%miY򍯦F]AɆoROٖ2FR*u >*~3f~]X]]J_ѯڃu8p@v6_=tG? O_MwOÉU\zGO:D'}|-^ Np 'ntûSB eJ#%MdqbwN T Ԕ@=/-LW` rU{ SCQ&C ¬w?2>ֲ(L.7^8tz/ q |gSp3|37DrwУ;'١wĺdz/Bm,]ep6OP$d>K Xf8ifϋIqdzQخL-Ǣg3MɞG`uu9}Ϧ\ Wgz%- f?W^ņē8 z۷ftivA3e+wfq2(> m?djJ B XfP2S.VWU˰t?C&Hg>烈]PWx~|:sfK7^j-K;ˏGx> Ʃ%ڻδ˵_>Y[XO95% .mp A3e+\O3S& z0T`IWotEdJ` 2S.VjyD#D q/y~[Rm/w>\-_, %2jt > _H'fܮx0+}:}?"H~P)c+Xg(4 ;%P|*PL*~ Ԕy* :e&׆BȒ4|fz)۶J&1(?Գcu^~_qyߑgJȢV . qo.\CLY݁Φ2O?^=>! njѾOBV\ >k??>̙3+ 7jkZ{-o/O 歴|`'=A: E%dM٦.˽C$Zʺl? lj|}:P"Jg<sgs'IQ6 SL77#DU E}5"X_[Ns ; p653!QE >0hm7ΧK/L'szxAxfІ)|S?A/3?N@stcƍZI{ΥSp%{B_3G^x8x-,+< ,-kvO\Ô.[˫꡿fad8NKcp? z:d>!#CYJ?wNysx࿰u1/e߁U 2S͵(3Z`$vҿ v)tpWzqG7|gJ?Bܫq_1-6vZ߸}+9; u87cu[q_j ^ 8y$}=p> x=Hn,+ vebLwҬ)3I6|I-j3_dx#̍*~?|Sq^9y<@鼔5o !XU46kG?q,9Vk?jY֓+翸CŃ|=lgӗy._{4OMۙ wHj8nY 1R~2 .\OӇܤ+ ʌ|}]Xdgѵ0ݫ=\SgrQ {mg/d3<y'E_>=_k=j𽶵z!A?v(“u2c˖j@&q=ʖ."$fԚ70^'! Bs,4|(9H%*PTu]$ |6߻߱ne5mn=2vklFpX0!Hc[Cv{g7xlַ_I_g~_?w+n7S4sv~.⃇Hف֛VHwUn\O'Fy38CvgzK\Vl ?JxqNFw^[= ^ƒ>կ~!I@=|RD|=ﵭ+O⣇g'v۝L'd05z读 YXc iOOŶJp8DĹHżTh&Ab.QQt]WL5?w4tqPfG+o9cwH'nؠ1޾mR-Ñ ϕsΥ{,=쳰X.:vcn|=Wz쳞 %+tc eڙ]t$5|I-j3_t ̬mT_"YKK d]73!FVI&t)/Âeҕ+mxAi іxAc:J'mwg 7K7z7StÉy9){PI@ZeW`=SyDtRbyzYXc 70scMbQW|@f.-A,IwN̄UTL[%]ꢪV1z _JpR#`fLAW: ~={?zTckqų_mAo|#=sUDzQ<Xõ7[P? n8c^/YDtY-ry.qc? ڦ,%_F(GjP2^8L} -"8T~2oT&%l0s7\@SG~F1cVx)]8,\U.˯o,aV\HS*|ܳގYܶ j#Fe[2Φ S#6s%@F%݊_9~o:SCI ^7t~{z,{xWX F^;~C^z[S:~V8|hu豼X޷%?Iy2 =@E`*m1Ƽh&N:H%*0}]rICV&xX^hFf8Rd_ 2P'2LnqI:Nz%do*/@`mjCI\# ( ~%ԫϤ:co <n{xo8iz[־|+ںZVN~+V:t~h]k+ L?.@ևQE Af*m1Ƽh&8H%PɮD:*g; 1I:CnA%û2rG| EJr$0T`d-`k0Fpchȧ̃6$ S< $I4\|8M[#p•r_m(,7n'd4ʣhy:m߆'4N'f|5xu& ZW;+,wbyq0T {4o{ s ɼQ ZQ_&3ii 4|P۰|.dW"&{'!6Paj1;<$si=ؿtIs4(!Dmy ֨Aɶ ,ff4Q< 8SN2$0T`h2"?fdިL"pk}M޷MhMpw2ٕHgkfꝄ$SŇ7Qғ5^z ҕ _\ՙ7*T]oxM H" |15j"Mc=y=c2r^MX[T S90|Jjd"Sҧ~U5s g=hخД$AŇw@370'Fe9' kE&wbT@bb(dW"wb`4~L J`౴k'*^"kU{jvˀSQ&$iKbpkԴ6;.u1ƍpbOl_ϥ9 McHTz($^-8 U<W}p+#^ gjmRf gN \c s']|3i3oT&At+Ï7Kcb^4|b'$b(dW"u3$AŇ7P"w<|6M;,IŽ)p@"D5AFWjI~̲~#v`46Rj8t; ,f0Q#QXM%īѯW#Tsܙ@IDAT8Uf},'=ԮPf =U|*0pg4YZIy2 P[(QTaB|\Lv%͎X;s을0'NPa;Nmf%y0*S" MX"p`G iK'|; pm6o6\6As1j$*iwoȨp:>$[矁_Im,& z0T`h2u706Fe9@"<p\Dm1/-5|b'$b(dW"u30AǏ҄*%8=\Jz8Kn4cc \D<K!QHM[3w](tyGFg6Ar`Fi(@?~!]_5;x,ȍ$Aw @OffިL"V/ @\Dm1/-5|b'$IP EJ$IEJ t^JD_)>vwf56IAŇw@ӑ!O:feިLB;W ."ØI'Bf:]tVT을0́҄[+%UxC ~ p{a?-s$Oh:sHԀ/7RӖ)QK0踱bJ،EL'ͣ,B.[dޯid@* yx|@#p V-1Zq)> MGY]3Tq%NGExR7)w(]tT; aHiP΋C87m{%ɰM-xC ,Qzn~iKVT>~7!>GqcŔhx'wIsP7oU+hRpxN zYEqq ,5͠8vBty$YOP7)έv(dW"5%NB@,.R0T"P21N5<F:ʣi{~JCz`-" MX"|1ئ >c𭊷^E(;V\vjٞ7kJR`[Yafp}^k+%;Z`VA%b2!Q 8^&xk)Jۨ]>8e+j9P6T0T`kdb)GO5Դqw+k7CK8<":fD[P*0 ֎'W <,{Lm[s)!Df5q4:㳶~xd^Ea1P s -펵ζ..]ܢۓ)Q#ݮsmMx|Pe#k066  ̆/h: wHn~SkAgq@$Vf4<ilIiEJ$IEJ t^J&Ɖ CG&ߞv^J^.?vwN{3%1Sm\)/mR鿥5xh >(}rBry.q xwڸse Si>{'esfP0-|,˵~Eܳf+^9&)IsQ0+D"l#tdXOMUM' X);㩈%m&Ŧd v(dW"5%NB@,.R0T"P21Nm_l^06>unv;K[˼-mĶVmZ47 ߡH%"j,%c1 t |Ǐ>Gm{Ǿ45ү`}P 9ЕU˕)Wj^7?qW ߤشLEJ$IEJ t^J&Ɖ mX/iv^`'W B"{ dT( @0 w,Mi0*0]p:Y0\a-ZY9'On+# 7} .1:&#%جI~ '%Q`.h: wHt5੅ʔL&Q> }LivU|JRߕHgII(O*M*RP21NV2oTh64b^P_hY4j3hy:0oJd}U^:\5Jc:2 T(OVH;S49GF^I4 @LKj%XPo=;Z 4ӆoRlABaLv%YSY$a" C:/%Da6e,&ɼ*(=bKļ&D bp#,h|jl3Ӵ ) 90A%"ABƯwd\T^n5ͪ%~v&Ms0YAMxIb `dm"Xf.T1%11;%(LFY4j3 *UyWXM_?ZJc9G5)$UB1퀠@`lU"< Ijʹh`c+=k9tjRT ΧeJv>I f2 C_MJ&ƉP捪ԎP!EUJd|5ɀh@6tZ;jFׅ 9 M!M8 eUlo_Jwn<Wq=ūPw(-d(A0k7@9P6T0T`kdb;,dިIK%.B#PgYK"ÄO`MStv40=B@KoRǛ*9y-VӏȉcGGm(MIūQ'pccL_ZIV401p+ 7ASILE %jye. eUoWWZRwkn- !v-5x3a1fl<Ą`&fƒ'  e# EH-z_k眓gs2_U]s;7}TA/CD?v K#8 SEJfjk-ɥt&U;M>z时ĸDŲ"j#жr.Y\ܑw^nn9b`~==0)\v.CS^ҰNn[P)B"a!szN33R("*ǭJ\L Mb>Z̓:THSRTί &F$IN2iim#n 59̆BbY]7x r'|_߲nH4>ٜ{`z@\]{`rN q:M/(bvd?dj(\`f2EtBQ R_|@w[mメtSm\=pz@}Y) Ԑ "fQN|x@q9QN ) Vq%.Ω z1DRI!Plh}fW"SEg=%3iX"҄䠟Xw}>BjaꐨXVW ^ê/[^ڑ?:};Li՟z3fs(sՍ"w985$b$4pqcbIҸX q htH`H`ZN 3W`;5**<)&:98̆4VON&CݎNS;^#6mq'H@t+ AxW@6kҩ6=fey B]G+!hSC,FbR?E̎'n9 pjK@!Ғ֨Wt12LjUK030NͮJE.zJ(N fCr*+%#1RVSNS(2e#l!=0iD,Rr?ui] g 8sZ& B,/^I_tϞ5_q0~xk7+sM@)Y81;rfqQ#vW. H5 ޥ VBIiI3̱sXbe|HRIfߩ S"SEg=%s; iei!-oJ1Ā?aKe9`^iψA/uc6Ěܾ};=Zr/ spyǞ_۷p.>h> M8d1tZ8P8:Ēf4^wiUPR0DZsVbYmPFjNm**;ٳXaPvU_vEz[Åb+i7<pImfH3¶[ ǩS­_s\=]m:w:)(qTX gF4ĪZ()"-}f{9`b+WLz(~"@\(fa GD[)187Û_IWJ_A8TqQp?O&=eGzk=Q]/);kr؜gg)ef$o,XV!SP)1.QXg8$sQFj+.D XLA)V 2' \Ifa GD[)1%_};*oGmslI'ۇ~8;v&:1O5N֚*ycXxk_KSePՐZDʉ0ʸa/(qTX)/Kn.RQ0DZsVbYmPDVQ*0̾*1LRbFqNjܘ)%~Cwz]<揓^;sJ!jaq-U8S8ʄaXQŚjDǬKn.Ī@IiI3̱sXbe|@ZE0Pd0W(>"J9Ł dސՇF0}̥s={?SKKKu{Mxdb~], Mx/'X<ōL ̢5 F䒤Kj-PR0DZsVbYmPDVQ*0̾*1LRbN7oƫҷ|ݕpȿF]90%7n:-mxyc`xa:8wd J ESyBD ?Siq )X*C%%kӫ&)s9tkprl\=\ꫯ'  q6H@Vb(2J'ʄM1_l3k4-n!K)"-}/Y^5I1\?lNo|utC`~=0zN}]Fq}8NAJq;AcL)ne8*ͷ; B\n$P0DuoR,U2P&wkjPd0W(>"ZBQD -߳o#ٻ{bnUW]5Oê8MFC#w2; 72a?>p (^J7?!e8s)#f;A2ESyG?,Ehh HcipfS5І; b@pبr R`*2 %VMq~wUiEzOs{߬oxؿNBxE~7i83kjNLTQ&l-tZ4R,n"aTдL8uҗp!@L--6&@Jp0FPDK8G_LvM4>s={Nyοkx*ĉx^c\ &2nX}GƖ"SE GXFXFGZnП1{`=}nSw NS"rNG罐!C'Zoo5 k /B:cxwlG/;dP8*mVe=r o\l9,BjS F)a6v'4UP|H'?`K>&=I@ssLZ<ʰ#'f mţ8?~w34'诂C3$BKӱcgz=2W{n]/Åatz}:s~c!w3t8 ELL$&2nX}[&"SE8"N5x*W~KTZ^Y3dLo? BS ?^?5~~&Ӳi~@a̎ǸDbM5b"\frNRlؐ˚efC:KqBS)*+0"SEd2e$J8 ~ry?#{,*,pA0_z|\=خ]jɿnF뀵E@}M/_43N #ev8%*k2# ޸se8 .k3x\*@ q M>ZCX0NͮJE.N"֨23''255ۮpI0V]{ǟgoy2X "HGqŚj0FEH7n圤lY;_.kq -Rv+WLP)Vդ 3s Tb𬧄bDAD)0'''q@w 'O!I_Co;na' t.MDv6tLji=iFfʺX~ϰ-]7v]`=hCۯ'&~]w}mMlǃ>He{C1@j/9=r.l/3ɦ)1.QXS بiUQ8B({S,#e!%ԪtCqafa]\LPhD6N!9P6Ͻx4WNytv+-с;ls%Q2ʋQlMUuO׋{Jްz9{q ruX8R:3p H\]N/z'={ Mma=x |ff.[ni?Mu}}taw $l0pDr̭9iqGn1{Mٶ?nCa0hoc)=CϤ?GH"ٟ쭷wW_M䥾o?NͿfuֺ{Pp[EfmɓyNEJ53Ć˟o6tۨN\9S8 J0I ;CT^h+%HT,0')'q񩝦 495MMB%2ԑ7AbJ2VՐ6a EBlbǎK{N_W|ҍ7sJ՗Oox}ZÇ ^"c;q9j_^ݚ_})((IqTXAxm8[Y5/KL,UՔ \1j|@ 4ICc}Ub(2+mef=$. r2?XGlcdaNeXSOC@҈)}D;B`&.رS~o!^R@8>Yo_{E>J> ܗϫLɅE̎^J,T6ƭ֗%Uh7)SF\\歶)H73;s J ESy##Ql:O1RfAgKh*b6NLhdza~uY>(]AST :/v\L?! =/C^ep]vmkZ'Nl[;~ o Q-$ݺ-Br@a̎ǸDbM5hiʪi}Y8]ez`Cިdb+WLqqP)MҐn fffa GD[)1Gb-0pu~rRf< ވmHrY٪$8ʰ2:+6  _Mb x{$=xb/O{7h[6]/)6NPT^턚1$X㒠Č6ƭ,댒*1!بlp)V#..J8D+/R#3P8+mef H1*Ue;_XX U@4<bCԅ[?l3#t\mb Z7n)yp:fqUG z?op.DMchQ8 P3Ub\4цӸyQ8]%vxclTUS6r%j" ĕuUb(LRbD2Zad$Ʃ̜ ܉г1r.-,G hn_eXc&18S$d=VH,qTUqA̿p?xrg 5Y9 N;EX~L7rz. n8Asn'y$X.Am8[Y.?J.@rȘbU%J8+/R#3P8+-Z(9h9̆ʚ2s:MDFٱc n'7ja(M;fr? ipI;Hw{(Xd0I7%O< 7 _~`pg͞}n[M3nj 5c\eI.*%Am8[Y%Uba0AjqESZ-Px"nP̚\J ESy#E+Eb-0pu~2TYSfNgDF6MZ˩ 4 $"Y=Ld`Ty)M]NH+/WA9$q?DC*_m> ZU$0NFNNdq:[K\I:GV%5t,M9z үt W*utˋ12ǪEsRLaV& pyAw9 MÄpIU>e K)S)|c 04(Sjs;?[؋z·tƚnE,\s oGQqP52;r(t, ^sm>!Mi"Ed1)*R>b5Y1QU1. zI\F;Mʄ`˱#T7ـ9x^ZDj:9OOL :Y~n2<%l]'ebj*cZfjdw%Z[\{j¡nRܔJ2H;LV߱/:#o9A3+EU1. zI\Fƛ]q&[0Pfŋw>w ȧz='*<=܅l4,I .ߟy:ok6E/ז7VI-Lna`t2[YŃ5鈱$iTe¡&{uRܔV$02nX}'O"#&LX'^2i*p'$au>B00}{oBBMً m/B*=M?Ntf>P89r&r({#f\{$*KJ7t˳?c.mLBSm>)f!03nX}'O"#&LX'^2i*p'C>"ۈVK4[{ WA jm_D;08[cN grGP$VIT,!6:ogAj]R%9*S,!MiFϸabqQq EzI\*㝦wBGdъq%s//=Ν?iy.f]e7ڽkwkl>MD=[Oh#ojkYż{g|5F)kjD rew% 3t@IDATLC`ب $ 4EaJql~^L)|1Tg긨8 Br@SUt \WHi~Ѓ'm'+K^z:>4d\'xMh9hYf7ǫL$x?̱I؈yq26d@ܕv`D`VwN{Nzo# 9j..jTv)g 4x'X8nd8)RR?ϰp逼 dezC ]zo6ِ`IT,"4f.. w1=s,9sN `,N Ǭa[ݟބC5c GӂfUd)2߸?s\>LN%sx#=u˳|=oqַM8ClH7tK A|S,H,-!R7UulA8% I\@7wx#7}p DŲJopTBKex9 ,NZRmg[[!X~ 8jB$,#Ӏ;Nma7)KZt=7`!<5sNcdig +# ޸sPdq:PbB%b> ֱ!iq;FE.zAәX'^H1hx7&lH^$*W3M@9!|[)&њ;r}kXZ&Q& K`#QDXzS銞{bzݝ2;sl{sA!6\4xόsdq:&1cfi)25Ŧweb(r1UhQQT$h+''qw } )/mqeuE>FQO5i:YĄZʔvA")z (E3cB&CAJqttmә}I1;sliL!6\3~L4i$X3ֲ lpP!tCqɓ9wTTbWU &FE)URgrONF;M i4 F fC%Ƀ<.U {T2ЛPvLd("{ MW6- 2dW.JHbBlLQH,ɫ=H7$c}VJE.zJ(f01"(JD A?9 :4U77}f!QU9O3:}UU &2 g L_W=z@a:8beH7J9P8@kz&= &jP!tCqɓ9wTTbWU &FE)UW29tȂNSZ} f䖨XDh6VtuYĄ4GʔVA")z (E3cB&CAw!|뷾t5+_s{@ac: dH7n8& @kz&A YD;M*r1U⁚BĈq#Ia6 ,NON"'+㝦cBGl757xNrʟHPb peJ+àxL=B{ҢT`B1! 4=׾*Yf==0?N>'# ޸#}t_ @kz&`$c].jrc}G骩T6s2 y2Ba`q~r9Y4@>bw DNGP @2gu6N+SZUc=J'+ЛPvLd(HCz9 ~07}6;5f3FG;Y Ǫ>T0K(!lJR+Dsa}htT6y'$X"ҩ;8~r`u62inL+n 5zD%ۼˬYM, q,D*\Җ0SJX`6VĤ͎ U7veJCl_,NW] XUepK|@9[fa GD[)1Gb-08AO9 sxMW%*jri l~ AĄCFt&Qef I\KNS3WB4¨ {,=w[xQgӋΦPK/Y,4daHt8iĄwۈ۹oB >.neRd~Oz;xL)M`XU-QFh\dqq0X;+@ҹV!mBv.H/tƏqOʿIM`u{MÊzq[fTf+1s\b=0;PLi[Ǫet2`UXQ ǸDŢK u&A)30QfGT^M"Jl:a?q)3?O個`÷{톴z ~ަs{%_tEz$ ]oƸOCm=}S+_s5m/_s=r8 i qAIS8]uL X8bU=qT1.Q(kblkb4H a G`*oz/"Œz0NOFb*kOS9䏇ݖ9qwݹ7?|Gx˩m~҆`:fgt}'f˭:cG'n})2|hd|8ɥt5ACfCVB:!AlT T Syo EI.L aVXq4Kٙ*o{,L_%[`LsG~Uup!#k{`[2SJ. X5AԄBE`㪆_ Tqez2`&(TevCAK,l:|A'gv?7oM_Cԩ K @Ξ=I߽'98/#?P1֐7o,NWI] XUe5!PbظY A L\1gJt R`*2 %Ia6 \$PR91\5:&Ҁ Z@t/[+nMEr? ctq3]X/cs=0@0iRF`,TqIbj  ,kaCpب$@)<(\bfIM*XI&o@zϷáeiz]<~ Y7oۺmWw֬{e!E@5dGK]t] X8b*YІT ޸!Ru J gJs R`*2 %Ia6 \$PR9)&r!w-7Sͻ| |ɕ]oWk~n$(߄暫V{ㆴt0Ժr^=!rN`u`ሥk&dJT,W5GH*Jb\T8xn("HreRp W'5 Tc}&eN"ȡx/}A:/l"[ @@i)Q_ c:{``H-ʹܸ sT\ f0CR4+qX(Ql,-md#3jgUϗJ GlPDQefIM*XIց}]Kly\|*ؗ+(mjS$(xpoOچ&=0CJ`i q+K]8]euL X8bU=AԄBE`.hWĆlQ.PR*LyQf7$2)8̆'m{]ߺ/η?X͇9t~؜A_q7+nop35sddQܰfҥu`ሥ &dJT,n #ϫkBv%+[lT T Syo EI.L al[G|7<~.o8tz%пae4o{Az`lv{`5Y4Fҥu"G\8bj X+EI8?(TevCAK,l:u=Qoe? ċ^y`e>읿o95evd)WV`$%K֫UQ2Nwjt[tde&@)<(\bfΓ?N{9g= ~5A,$\hzN-k&:t'_D{K/ڋc`nHf!>=z4---:W߮]C^ms8nTn湀²3o1;qQ6bncV& V @Jp0FPDˤ0iTӼWk6B_#D|/~n6{x{|񭠍?~P6gjl.X<ϰ8ɾ/׼5 'T|By8` K08(yQ-&Ox #p-G0^N|8tMXZl䏧>Oi̅yp;Uش>pC ƌ1z9NA"@qc ŝxk` UO ߬?r }=|9T6x %>D#I퟇}?Ο;ީtԑtXyeZ}=-  ޻E·;WnU\z'3'_P98VOork>5?qw:"bKC8( T'#9؇ Zv8AS1F/;< DGplQ@9%; ^1/7'ܿxHz蔀lűȋ!m 8\wK/ݟN |~y@ƭXtϯ6pr Uhm?}uѯ0v{￟.ĉ}]PUn'.&.+o iʘ1F|&@}bP1Tk(TPrbذwL9Y|} xwr ;~> 64l6AspQs/B8zxG?". p;<^$Dk"`jDž Jk ^m Ǜ0h3EM1wHe'H{ [7SMw@!6Tbn=GHa6(o̳-Vo NHnG]=b:ҩPr+D:~ 2[s7evDIu05ㄏ? ׁ2mx"q9L8 Q2%, @*WآbޡpD-ClPϦNmrmhW #lp1:\Ǐ7!V!ͅOG>Mੂޏ~O[ { ^Xꩧ̷ޞʇ JAbօ=﬈7S%p80!PlDy̭jG 8 ReIb ETB,;e U_pqy!o>7}\{B/~C?+]f^7lx x,Wl_Bz]+nޕNs7}2x|;i^'QJN[ѵuXjڀ<΂a>\<_I3\6nH,yIb nUL ,ZE;;ŝQA*#dpQGa66NptU05.8ݟ~]= ;pڿOϦ\؈Y:NdgC=&Mx͇j;VB&|DWֻ$m'`]d\myq<ctGoơۈ)nHq<{$PA%CZP)Qb"&P}$#.nxmοK?yß|&~)Nv2@8q`϶v? |2akM:3)_C6<]w74Q}Mozxv;c0wt'ї=bKgBDgסpbC+i[ ? ?{.ON| ) ~IXB|6uOAEj rKqF# ;e xn3<#l;M? bՆxGϦG?zM8ao '`}HO=} ~&\j#ǴaqUΜ|> |<#<>{k/>Z3p]@kЙ|ÇӃ>&ȩNM8ã|+_z{[!nV.$ӫv l L8ֳ? v|7 @8 筆Z$<( "!Tl2Ć`m@P}5gHզtp3r |? ?x:o1p}V'n46J#ho8`<ԋa}/]!6 szK`&`!=C?/2}7ݐ^w>/<6z˺p; <o~u"]EkͿ^~84r1#ڄa=x*àڂi 7kr~4VSB#'*!6%jյ JkK4bJa6Lneu.z^>|G^/W`8 5ŋuCy**x(}g[-Z}uj귥ŭp Ϝ> 2'|> Wwb ߟ5ûڋ6u$'᷾Ϣ>^:OK[H+xcWYz"ϝ9vT@hKΥ?g}tU7߰ $Qp<}yH?PӪF5FΡF^ n~mv/ !ڽ{%:;]yhw:'7^<\ w_oӌKM>=h|A 7@Oěl׾tmğak]v`|f.,.+~'j3t&lوY[)f`= @8KP>+|J(jf8 4Ue?nN鱭c=GfDŲIJhp޼? 8"{_A'%<(%<`~bwH_6D\kЦ3ginkN X.x>!>h@\ EӋK!TgT1k G^L2 XӻHmd)J'/p-WqmzǗN{ui,N>K J@]}ꅋ M^N,n=)}RN`+ǼI*ϝ= >-<7LH˜If`azLi %V·}\rozפwk|88 GUnv]k['jGp0V۳Ü< -֛c#:ܫ1ڎ>OGoơ6Jm}ԩS+l]o8ßms|\\D(&_j6.f}c&?W 6FL00wC$p^kR#d@2Y(!h 'q)=r5lPXVWX6  26[?ZLh0d`0hݲy/n}yu`f$Kp\>rD)Voƍ48qA?y?{R$nQ~G%rQ1G~s#gPw9p{9.v;'vUGoơͳ)S! 2ݐ6 ,^Hk5DX㒠^n3ldO!K# 9̆iDŲ:Mo8iHe~Bg9a0H[y/n 'Q}Q͘If`&HۀG( ɊIIse1}?>O _6Mq:́Cx:rć8b_޵M'vUGoơvۈ)nHqk@oB0FĊL#4kTw Lb9ÆF%+ޜ?79/`uUk !s3`pYOV -Mu^X\ENsq$ ZS0ª ޸F0/|xӟ=ָ|2MPNj́:ga4^LW 6FL00,C$p^kzN9`u:SŚj4 m@3i*"7(We0eup46Ӑ2fc^2 Cm}qi/8J1HoE6o[b1uSYz7Q&Lz^mJτ? dQck}7x|(_?Iz^9.ok{`kjþ8 xs1dơ)Sz> 26  }=ʍtH⭫Af8 8"Rx`˱|q SDŲ:Mo8w&.X_;H$XUTYZ,Ԓ0 JMv?Y'\\A4\ovETʎU[p Y)U5 cOǾ΅ooئ믿m[_w.q6rb7'h>ڣ|x=<]omk7vŀ[R;cTjBO9=FLiNdx8rF2N+ә" VюJNMU4z-zXP#8eue)CoYx'@wwMzMXap'Ý=Ϳ, XA-Red끼Jy ޸F =>aq_/m-p{=n NPx`e\OR(G6~!wP;ʇ> ˫l<ksxo(OLavEr5A2)NgT,Us6 E6I\*㝦wBOXj٠p#Q2l8"!e"T:‹VvM=ܪЍ &;VΫA*CyDp+iS٭\:r6x6- (|,Ȱ]B8 }eo2\? |>@]Xl~Zo;L~:;s'\Sf嶟RgƧPWCrK8wq3Pnt4YړOp3,V\/NMU4z-zXP#8eue)Y8s#M¶Vm=ˌ+$ 9B-8JmhƉ3g̓.;7"D-8Xv)oIdYR*+NʎˉˑlJLdy%ђP A8:^Nwxvs}ܷGr6*[7/pZ_BJzg[nQ{Q8c J^>gÀz3n}+[ZY࿫v[}\;sٹ+i8z( SPsVsB;.&SjeKogl>Woh1E]#*#ρ[϶* ";|z u$7 ,.>z?@2,F$y{x\>x31 _瞥+^5`IBu|J!^Aj@5"3P_X}\ٽ+asNq3PͰj*C`h7=. 9N(A%E4^!slѨZ^fvJ5Jrw,'^,(Б :Ӡ]O>ކГL?q7 1>x ndj$xΪ^FiRK Z ~\=+~6 X/w@;<9“r[}\bcٹqsP͵\Ҙ 9:u-ދ*U($h7AFP~_9r1BMHX כFN67PzQW* _vvoZ{*-W2ti.B?!sCtT&z r DGJR1YMh<[cNKHe`J_e9'J{nRpE_YJW{-(7o?v\f{+tRG=lWwo/Ot]p:viҠ];r<|'pۇWɳL1pm݊1 fM.䜤t' oA\hgpW'^OCBj"R,U@IDAT$֨Z^f{M,ݱt$ Rt_\=n88O9zE?`C^t(;q:sgfZ(В}Ϊ+^ FiR@ Ukm W{'3^ [pgM/}Nh T3L];%9\HKzh(Y.-A83YkRM}< y7XlF.6[)mx*v=7>p LIOm#If;rw+ ^<|, ]۷frl? 4!ڸKɎ;o} Ū!qlr*En䏑&(u p*_;8Vxvcڏ॑bo-}vFK=kv5H+I1P ^;%9\Hl'&jL jY)MF P4$D1cD+5בW;kr K>A_)Jw>CsBogax |ۀ9[9]H+R9!9s>ڳ˹3@F}NZ:=;61ЃC1PCA\M_s^] ~,0.Z{=tUG8c{K f)"p AJ)u\RQ꫅4 "HB}"i^r\jy]ʀLMi kVs.:wwo+'Ǘ_/WvIWgxB_r=pG DB츐ˀ%pzܽ 2ؾDcp/wj<($+8\Jr7I@4]\Kwu%y_+z嗩p"/ㅍf'>W/"{:G5)f 6x푗p!uB@Mp%ږXOQ#}ИH%O@YI1xW ݹs'sgO" T8Wh)E?rwg}{ tpqF2d\RBCzx{v9$$x=1Br"O$Q*plʎV8e1P9;X7"n_mOV%k?g ^k?ū= > }IM\<8ߚ",#&i׎rIRG-[DipP "hSdA^(9vڊ>Q9$,F/J[5?. ;4SS$U .TMcx ߚ)옢9\Zu*9lXaIT*끡 8 &ȡ*רZ^fKw6 bf ;g>OFA{(p̠SSmᵇŦGIgB;GSr@ #fxu$Vޤ|࢏/ M7=:^ ۽`N|Oz+5:+gL fM.Z:CJ`<L $M8o*BרZ^f{-fhyXeK]2Ha !2TqO2p;HIcK.ݺ< @{gGwvvGyd{:.xoE{9 vruk:JϘ$c:Mu=h('ĞtZJu5)İ "HBd\\pGua&zbDž\DZ<= Ǩ8VJvK5\"^I^g9 /NGn\?\1vhv-?` /7n'xK~{:=43& 0[SzI bTF9M01TP$!2bV0*bDLjeE|p+}m?NC8%{rJr;.2Ę2h>7wLe!c 0J6^[ p\BKW! SO=<裴- ~'\n8V^.x,|;uP}>?o8gȻIJcx~a=[q!uԲ52 U5lX,qDZr8s$F2- / UYm׺sZ_ތc8~lcx著:uJyi!+yW3E{W#Y8|+geSX-5? :5x '<wMԘHy @NIc=rXz}"|b{71GƽD1MD'`6^zJܺ츐ZrI^ CClM! U@ H+?|C??,h8-kە₌lşc@{×m\D_GZa yXQv Ec\W4tk & AOXy'PG-1]$$0*'q#$D1⇖PEjMnn^k1ҠRL]:xrI0E CClM! U ܓ%[^O?c̖|PM/UMo9r<ߵv=ctcO>ǷE{9@ pO ׽R-.Ӡ^S7 b|s8:jqD4;}eeN!ohd1- u̿Iħ ,Q g"IY4⚕ .|f.c]gSa6(٣zO[rM|(PJh|cNxM$ +_52Ҡ?'@r'KVkUNqd呰!"jY MͬNq!#FlwJØ'ӐdR-K̜ .䜤iQtR& I'd8Q_<=Ll]r\ xp )͚lC˿ߺ?t||e]C >S?q~9)"j~7_EnP/OsgK{1>:T'_e8~]ɛ߿gL' XT HFw!42| 1BS& 3$5Ճ#ĕ=lQk&\j&в^7qd@$C!,R<`=b<|f c ]5vPxM`t)L-Yn=q}#'u|{O;F'||x@z>HmNY̓ ѥOۋ[*SAC cL@ޅ?~{S=z*x> Z9$l465F(pĉa]oVD- Ťy2)c FqgU[,w~p-{9t*xviC5Vl)<5~-/zfyk'rI8^'NpFR38’!%|óv;o?4G%nwawOngҪ5D̆cͶ!m :ݏ箃g!_Wsc|Jm\qǓ>=}!! xiP9iA#%H.<&AGGWN0 \0]74 iOli)ئ9ⳬp0-{Z>c ~ ~SZMdXHUq,5b]Ϛ_ym^\~׾{5mcJ%vc"(]ضޅq<ԉF&AiU3kkz{Ȉwa̞je߀ e?Ec߻yB޿-;gX?~z9ϸL~q %ICJ^Kؾ㡛Sҳ]x?܆ƞ\<,NE BB=uqf"u)| {6ǂ'AAS%.xsV@\9>ۓ 0o%Ҡ6at,8b=>Cmp$"KX MF|p-3$hKMD5~;3<W7Bw%.o]~1de?8r4$(g ?{m27/"? &0^&#/|EMj^lνq /h!\̢OP{=P+}k:}lgrhzRj"Zq" ;Ik8ljfP2D/Ӊw?>_|!Wpp ݆Pw߶'v˩3~7 ~13(m<"FH,a14gej|jǡdzp?Օ3[·~?{}sl!L=BXmHIYxOۗ@󃿽f_>.l<@bSrpqoq,||oo6~9l9Ӈqjܘ?Z)X.J@8*bCsQ(Sg?<l({ù9ſno~38d+qR+@_x" >G=.sJ@uUtccE}R\qx"ǞzcZu2G;{"^q d"hw@Dܮ9ޛHF"ppYM.)(cD,?Ybo"r?'X<':jҐ|]w~7~~,;N] ;r Vi'E"6vpkMo2hgOX?>53P !М`iT-c!2yŧ<?|"ܡ?=Wg!%I޻7EmT;%,.}ѷ/=wޱc7 Or^|T;8GLX9\f [zL!)FH*a1zF%S ?Bov[n>~C~mBsE~7doǿh1WQ&*N3afXI29.Me\~|{A.~M/Z,~6x]wvg'OYWǪ1cAs|"Ͳ,'}vg C ǂeP, BVؐ[z^=I:o0mc?}!(F(p4YohmfUm[[>8R[=̯}XC6 \o3ljgyysp,[a+'qN61}pzAni;^T `V9I]kR39&D1Q0,J /}$cf@-p X+|*!{@7;+r JUކ3bvv/'GN ~80^$˹y!1o5.bq^=A,:kM`)={B_!aQ3>,J-P[c|{l ߗڰ|ſv3pؿpTT@.(?O5Q~tVzx1B $,u\Aw=?H3Oiºja D(DW [d;g` ڿ~by1~?>Wpg8y14.CYQoH4\K!`$p5tTp_k˯fWN>(Kx`'_~?K ǂ03b BI7o5.R4ck-.vGYr0) 1RרZ^G^K4p:ԶWc^SϜٺ}3Ϟ {½o>)SJspgog`wwg<|w][|5gw2&:S`2""cqz/?dڤ$s'$ߠ G ⏭cw_y5zº*`kw'>yr9 ?? Ni@_~vry9`N[k;fg?.gs9GSaI+vW8z՜9 nLt\*-أ9p-KDרZ^f{5l,hUpqIg>__1ݳXeNvgǖCIQGJOX޷Wqv?\ ;WW8rC^=lD雎$^pHg=G{}ŵ1&MFEP yҨZ^f{leg~r_1m>+O>k{ @GgܙS'\^}k<W9E ?g^^gv8{^_o+k+H'Q-:DQFEuuM/8$z?ג#i2*bCҢQZXrnۚ8xe^>^xw>'+Ϝz~9疓'l2x5_t`[c;g ,_yۿJhG Ǭ<"vu&&jLp8j?y!3@b,OX$Mƶb~ f)f\K? W_?<tKzė/}J zNk_[^}j ~qܣX \3/_We86A±7$ tAs˜Ȏ-ZN Aciqual ץ~)g/rj?'=xd {kj˙׏dkW瞢ݶ~mU|"k7?kW8sO2 >'h +[ߤ]?ÕzL37k/O_{|pM$e˘1-cHHۥ?GʤVF:π^ˋ?B_/zYa)L4g?>A5o}n}b+ ^}|9we_"Ǘ:};`=ǖ \co.|!5+٧Z 5edkۥ\?'In%,x>Dm^O=F,O.kddNh,'_2"}8z >xN66ɴlg`;Wj!X?sO7p_.4p~r"LԘ~""8v7[Q?\CT;Gl&_L ըZDJo]/yv|xS ɓg?_rpdsx_X9 8x=K;c6 exK//Wz*ϣ9pӲsX2$s1'FEV>Rƙ!/ɐX5X@ja&aE7>=+Emb7 _g?_߹ OtѠ~>{g|3>Ӎmw~ē\~\7: u}lx^&_b`桔oL%A_5YWUVj$xՋ?y/q2rT Q MF"ZK`ro?/,ek?[G"p9L<}?=Zw\pLx ']OX[;o? lg.DO,}j/-c~4y $F1OB(uAː4h;Lsr =d-Y,GrN˂M- evŸ>}Anw;zlwy/idK ?wwo{?O?y%"< ƒ/#';xY毜o|NJvGy9|^ ^}|^'ms; q`g}g~yr4^W(H ' !;/nƔfeذ\ĔJ.Xp_nĐňVjQL)97?,|@ ;;`aWƃ^rWn{g,xl'Stbt<uj|bX}+j}|޶|N[o=@o <#VjMzAJz6/y GT Usd `#'!G֨#9GkT-Wq#.}>s^"۷]ǖ]n=^NhI8ß}S pǿr|W~ǿA'/xZ}`}"L`;OQAQ, |X ޻/zp9ztrylpOJNZB9{,x/p$?2$504a4%cIBrGKsHgj90 #. L?ZgBMl'qN]Qp s3R:H8ل&u2w΃Y9 k5)"K+a;`JEd;a4g9 Y ??PQWq-c=F \ BkG&:w%u!$ 0`t8 bf% 9 O#M4QsJDY85p[rWc- {G:hҠS@' Au>WfTk,C֫IeLͣp$M4h RiEl'qavs[MuSpPֻ,Sk8i5@Sny"k#'a4^{i"lp5L齈l',&,G!,V4]#x5 cAD|{-R\j T@lau@~Mr5&{@ʋ?6sr#ihIbRw)vCl'qN!NLMtkIqrWvtHn8iCk >z- ݴ EFNZi  ENk{NXMYB1o?#k[:R0<Mtq5qt>\c󔄉әjP Pj"pI%MX<=>04ZS9hWs&J#\9/="5zD]ߟRFANJNѐ4^­Bk >z5 ݴ EFNZi  ENk{NXMYB{.{'d_$EWq H 3ChA׋(O4LtV@ N@#Y BΡH :T 90Zco1BFBGQ1E=~2`a@E}#8'ܘ qD+$D$ L%*Wc@M;^dm$՜p/Z )E(dyGpCdhzL|VŒ0<Mth !윮4J3p; bf% 9 O#,{T*XP g6H xZM*iK%CKXKeqo1G]X.i&hma.9p7͝v$"3!NB h4I28+Q;ICx &՘2w΃Y9 k5)"K+a;`JEd;a4g9 YQa{ }q)H[Afڣ Q[|н84w# /  Ѧ8R {cq"A\9w hҠSڨ&`dvp՟ k,CmƫͥQK2c,e铽n2a' IrΩ_&jNT)NJNŐ4^­Bk >z5 ݴ EFNZi  ENk{NXMYBM݃w/}+pcck"3RLZ{6A" X;]iG'$`0D~HH쑎uq!P5R&bLm) 9 Ң]'9Ѥf.I"sbJn8LiO&* C9+%P.9kD-‰6ߟd ])IvZSM k1e Y/6rjNSEVX-wZދv"hrpV6r ܘ qD;ݟ&YDL,CxX7T52wDr!m_TY)\n( t? j=C\r%$0 טK-s ݡWE|Zj8Axewv|k p2(Q,S N}yI$A&XNvGI RҚ`3kĐL5%l%$!̙mXaq7NmAiKw!v\HhhIR@9E])9id6ۤcq"A\9E51vM.sJK1E:dO'ײdITZ-FYF !7UOÇ"˰]@IDATK}ˡ޶k sQapc*‰V{J9ܕ$!i[T^*LȃiA&j(p[@r5f ׈!j6J6%X˂IB=la:v:qlUs+H$ B: -I e@jtpfJ@Ag&)cq"A\9E51vM.sJK1E:P,oz&L|Rzv8h.֘% >waj_2P%Ҫ5I׃g^dΑ.01D+MRNJN4^­Bk/c ݴ b5BW8I RҚ`3kĐL5%eI$s琍k: |nI{&<("t-A;.dВZ&IrEc6k䵦γlұN .3cj/i<O8e_>Mj'|6ɟShcQZ9{7`?sg.S8:pv8xq7) Baym)prWv nZSx0m foMɕ֔sF TQ)ZOB=w(oyNJ⏭u|ZMtZw*ï<*K@ & A׬q!iq8j9Q1nN@Nb,$TS'-Ƈ-A!ba>Ő0}E襖.S^98߻"ԉ'~z9~̡ä K-5\"r:(o?'νmljä<%Ndt)d28KUhMUEx´"x7剫8ezߘsN9(|"%GP:BREuJ =l'i#4Dwv40&AR%XŷU|j~ uX09s"5ϲUyrv'!gYt& A׬qa$!SMsM~N ;.dNj;~B/p4<;g`A:O4 VƪAE2BcܘL?b>&7'M5Ca/&!J*=Wjy |[Cxs!WU2 PP̱jgPu `.J׊W~҅t%\ pb7q>AH8oS'E1>DOx8ZN~,uza;pB.CCKjcD*@)L6iS$U .3ޮQ;Qm]vw\Mv\B) mx9Ϋ}՝&MLil9G ܘ/G{1,€Q͡g(xw}o\y9ϵM-`3+ L ،UoT˨YJiP2bE[ѣ)1B%<+Jܭ?":! -9}u׷v6.Rߑk!ej$xTg&Q@vu(o4 gL@YΉðOmA<^.%;.d)pr#; VvRvP P-ǣ<9c}|BI7h$769;JHd`\[-$@t=Da3󨐻ޖ۫gL%٠O) 0 z'Cy&7+3s9x"c%GI :C:1"7*- 6hS$U .dX7# <MQnN 츐Φ r4AAT㾙!510KU8y.СؑF*unP&b)2dqG%8BCUj9#F|pmP-KY$Hq6 Y +2a1 ߚkRH;O6+)Be1}n-,kLA<]ZFM 3# 44)mx*vaSǒMsMvw\Mv\TgS PYȘ1p;B4p3Cl'qN0ܗUދIP&b)6dqG%8BCUj9#F|pmP-KY$Hq6 Y +2a1 ߚkRD7O#/+ Z$MbTF4YDYˁ8K )z 1i23*) @$ihS$U .gS7% <MQnN 츐%ރd{|`,dt3p;B4pj'υ:tUދIP&b)6dqG%8BCUj9#F|pmP-KY$Hq6 Y +2a1 ߚkRH'YFIRG-# 4hU0ƜUj59<ǝoV]'*V`:FF%EZ/hn2& cJ1^ ątmpcҠxNɎ N_<=Huwm4H6A5%x<rkO(y"Dlj8s4164JBq$rG9 jZ HjmVd ÆcLA5פlN.ZFnL iЪ`9G8Kks$xM wZCy54#"  a`ɝZ*vN5,nS[iL'MRd$! J6G%IJ-tC 1:"[F|p%IYj\qR d1. Aߚ HV Q&jLeA{5=Sq :Q:1"7*)"a6gLf0D񬈰L:Ex1kDA:y6hN P/O[QAN\Yb8NNKsD[5B Jit^ e?n""C Qp<*IMUj22[u.IȊV-$䊫!aEf|pY bM@NZ=5Qc*# wd#Ԝ=1!HǝA׉ 1خQI ?` 4HHTS$ 9RO#M4QsJlݝpiq!H4 bLG`N'VYzZw-GKsD['υGހ7G>LtI&gQI;PZH5\[-Ԡ@t]L X9ju=ns\jQJi)ut, 8:jQ평SiW-5 w*nV]'*V`:FFy<}yH:Es(4AФApT$NjDlWLjܨ'mg;1`uq!Pi$I& xNi ZC!,Sjc L{ib'C(lxIc`hq\9*$O(tI&gQI;PZ .yjXP*UrmZVGu"r\jQJi)1>a9@{kTFwdK$4!4s6Dk{%8y.޺xs}{F=L(y"DLw, )Zoh93Zw *AH\ B hUkQ"DFբ˕Ҡ^S7 b|s8:jqD4;Ȗ&ρI)pi"ChLMtju iЌ/O ʔ7f% 9F"N&R*XP g6hBJ4bc L{ibcNx-``hk乐szfkRM's6Dk{%8y.޺<5MШ m:O$o@˙yT#!4T#iK3ZAJ\a-UQרZupR4tk & AOXy'PG-1}90 #.M$xM Q@mx1M.sJ̯Ij;8R  xD * ;l ǘ<%Z@ HoC 9i4. [Lhy"DLw, ).^JJPMX BB*QqEY+ACopuSiw^ߑ-M0RD&Y e=̶` kVs̴ ǘH&9"p(ˀno e1I@&NPY)fk8F&J.l=<rui]ЦDj1XAR ]6o gE;ت^b8}w8ku߶,AY%V6j85e\ RHi1}b[ˁI8{^cwM4i FReifĀ7w8HFΝ3^ąOzz& 9e'P@x:kJ^= {) ,5р.Wt5\",5ZNS$l3Mt1bܱlΊ^wU p0:pV:mYJ +Gmu!Pj8,HxM$ +_52Ҡ)*H$ BgIR@-x\)#U S0tU%[1^ąSucLm' \甖bt¡,>AĘ'5 4!/?NbB.jv 1P%bz\9FM vIgB&bŸc9I!0tp1}J-ԴU\̕Pe (&Iԅ#BE :5x '<wMԘHss"]K.Ď n%ILqTu^ Q#1^ąSucR]'L@N8:=aRuSZw4-`l׎ħZ5\"҃' ɜct4xj`-p&r#,`b=B)[1fOWJK5E:P@g=$^Kw4t r7^V乐szcfk4A&p&⏳5 Axۄn9ꎓ.ɯQ\ACoxJҀZ}CJHss)RDZ!]m7ZPka[﬍Y*n\65>R5=[DrNq1%ߪ Dg$=X H8™>%Z7ӤY0!hl6dY#VbqikȩiT|4 T#$! ݱp1}q_J-K72gpM2WB%:@rz&Qr5,Q $ BP!%A99)"-pɐ.Ď  -I յ-WHU,9 M|9ExF,rNq1J4)-$&B8ӧ$^F  StvX%m%62k@%2 &(NApb&8JI M6o gE_R$5R\̕Pe (&Iԅ#BE%KGAt4I:E*zH itxN1E H \2 8.Ď  -I յX/Zal,gßSRp&_A .?ϔ1)vL*=}VCcL|{-%k9^:CPjxI[ɲ:F,㭩acȩiR]4 & hS$Kgr>#;F\@x8k~۲]ghQqD]9"ԨZn_tDAgH3t,S$Hi@MS %Àno EFCKjʠq4Q q4ڷ,'[_0 NL0gmh,u H .B8ӧ$^Kw4t r7^Vhrxo 95M  Mpd 0I!Ȏ /{Yс3Y6GǓV-$^&QrNc`0%N@Y BP!%A3,s)RDZa7¢G%ILzgmTәli 'ʨIr412r@`} CKe@ Tw@sOM쒰NApb&8JI ]6693J S] 8[u+ Um&i`FRKɛ$m no G}IZ7ӤY> tv𒶒euXG%{W~w-fU[ţU11>#0i"1IL0с@L|1Q#v F0t4 tWskZ[kWnpRm[ksu˙KH <['"7e%E zf`')@%n:'gi["5}*DahCKCI"VN 0DkAW& qeOwIK_Q`*3c ـQDOS 8gДqJ{Hgք,m QZJ# c4G&1 41htkG86h3d!W9LÞ~=9]} W7?cPl(Ʊ0Vf'd %GE\ E蘟P<㇦lC 6ҥ$}ihټG^P3Б13y&FOD΋e"0t]jwZGb')@%ʷI [zV (tLgi["V*2, D&$|yF'6=c=A.-+^.feI^iVPv"p oYHi,2OYi$24!;4:ѵ)aS5ϠS \2Y=d`G]WۉYl_~H[aSb%dBBז @c~j@^<4U1S=V=!, H_XeXn.uk:h)CaPH"F_oXHi,2=C>\&Ԡѭ7(]dM04tSX,뮽Cq62t_{KUOӿ CqNwR 2-D.tm 0 $:p%k+2uMȗc q44qj{"!AwVp_XeXhms kY-2J+y IĈ7vsQFr»g`w{hB‡˄4U00`;F Ǖ9rJX$!qmm ,6^ǿշ>v7W0c[:Z0 i!Jtk[ql 1?5S/CIqJ{H_Z6Zl0\\'l! ֿ4i˰eѢ"0@]Z6y-,eH3 VҧYH"F_oXH9bZԫ9A$24!aQEF',7qN);1:2GN $<t:ҟ>8'?y:~mh BYZ0 c)w]],H_Z8fGabR'l! ֿ4i˰eѢ"0@]Z6y-,eH3 Vҧ^B6ZG]@ d3n#צүKB)v C>X͑IdhB¢X4:ѵ)aDiSa6E298h]6>9yӓ/ڿ `K c"1ւlL Q ]ۂ'czNZl{P"ط>,ZB!}8f1&uRF4i%t"}aiҖaJE D`p{{RkV& i @CzJ{I YLSThkw"&nhɯN,Di0"(# :hL"C5T~甲c&]3sJ2 m ziwpLo?p{]qpk-Kʴ%е-x86蘟PKC#I-SB4}"ĩ&Q{6`(v:HעlEH }&ղka)Caǐ>͖8:= EMMM_(R:moŃ|c4G& Z]xdsJٱ] sJ9% rTq6Rst{x+߽vpA4/w9A_:g)8rj!JtƱFc~j@~j=kn%AOc ,y&N-Y!&uR^|cБEZ&mY@rzd5eR4@i !}-%9*d1qLmSu{  ҋ(IJ.m Qt@ V/hkephtY77qN)6ai H5QZEr@ w?]Q}8ٕu±]}k>J_٥7\D)#"(х. F$Zo+S 8q 1j=kn%AOc ,y&N-Y!&uR^|cБEzhEH ],Z CK=ղka)C@NiXZ:=f73E*ȸATYii["i"(@E݃VUsD+MH,ÅхGڂF^#Qej4+ ϜS:)aV8,1S g^HkNOpg_.T1 ֧h!Jt敭cԕxUb2i6سVV4‚gikbubR';vz Y^^te͢"0s kY-2Y)W6z=8%3,l!Mzӭ4[Y|Sk*aw݃֬O NZ]xdݠѝۥ=r6FiIyd[9Om$/7]qwgwbn_/wo>ΟP.TR 6D:d)҈Dk+S 8q 1j=kn%AOc ,y&N-Y!&uR^|cБEzhEH ],Z CK=ղka)C@%Ckz;*%|gڦBݙ 0dٟ~$DҶEJ y`{24! Fi zy +)g9l8QZA*<@jn2@?tx~{odN`}ͧ :]u RևEyi[H_ԀN#&ZAmuk zaAȳ5qjͺN1;,ҿ4i˰e͢"0s kY-2TR0de"o֛8p066]Pq@0yUn, C.p!jtᑶH좫K\9% ,8ȶ6/-,r;?*՝}d&?Crũdǥ`!ԀN^b@hO}ND7c&\\'l! ֿ4i˰eѢ"0@]Z6y-,eH3Ŀ=yJDvw-STh;3,3Vd۟]_H鴵" wZ:Y@&$\B#mA#(EW& 9ÜFi98rH_ZOƿ;7|Grv_7u 6=j =QHuej@'*ټG^(duֺ `ha \UZZbL'L꤀0hҀKH 9DҤ-B6 C]aZXʐfiٮڟʔĖHh;u4zv_%-^q( j݃h e5H[N;=HDCNqڨe \iv=d)r~/}qw?{'~>˿RDv =j =QHuej@'*ټG^(duֺ ` Y*u%tI[.k-vt֬Q AM ]zFRW?); S=fwf$4 Yv'}]^*KN[\â(&uZ"Y@&$\B#mA#(EW& 9ÜFi98rH_Z͟^{owC_p}x%oz*Z~ l[{\%Y =QH_ԀNK6=i>ط>,ZB5Y*u.3Б!F9iNAX\pfnrޚU=8J1!0iKOWJJNa 13#AaȲ;1SUME9P0LVE MH,ÅхGڂF^#Q.M&r%sJ9% rTc SK OߣGߧw_{ nWo3"G+=.F$/xj@'%F 4[B!gXZ,WN l  ЙKȐl# 4\a,Pt83A79oͪXԴХ'a+~%i0Lfݙ 0dٝc[S^*KN[\â(&uZ"Y@&$\B#mA#(EW& 9ÜFi9*L1D~w<7?.q/_2=KW^|<lFir $y }ò7~N~[_O ny<ᅬo\܅B =QHn+S 8Q nZSsd[-LJK*P)PͰWc dI_X9}p͇Ù ykV (R=`KDZw#u*%Plj- 0dwd| i;nb'-zP KtZ>e(8ͺ~WeكF0ԄNhqe0J+c ! \t}ӓw\͟Wpx〷7_B]Z܅BBOT#=67Nrj@'*|`x-ZbjN}â)rUHIDATiiT\:e2jl3 4^/Pt83A79oͪXPmTqY ֩ĖHh;u4zv_%-^q( j݃h e5H[N;=HM>FCuңe ,(H@"o8Gz?M/C?/m[_EfBǂzn+B(D{R1J+k&!KŭNLj-XC1f哠9}p͇Ù ykV gR4@i `$bDݕN{i[Wqe2# C}Gא&An1+@0Y{к\4! D6OikApSgn$S("^<O?tާO~M c헾_~7h􍂻oRcq``!En+F1 8z~uaURc z1bz"d ՝Df3PLe$hNAD_\pfnrޚU䙳!0PZchjÈ+ҶiKeF64 Y:_Cx=Tfix诒8\Ct 24!(ewOi)zƆgn$˅QZE yHw+}C>D?w:j}Ko}ijK_~9:vEotC/ x+LWʍEPZV%!;Ơ1#׮/mM,-B_ug25m&}aY> SP/|(:f0y,eH3 V<00bJ-E+8R C}Gא&\7+zeO%ًp;A:0rB$i^BG[z Z{ci H5QZE yX?ܾ%z=u??Ş+_?y8=%K;^Iڋ@B$/D80(P&pi]F@^-Ir0N2P3k(g!}aYܡ- dѦk>tfnrޚU䙳!0PZchjÈ+ҶiKeF64 Y:_Cx*sP蕖]>d/ taT4M ;l{ an達kq-?>vw=q/ޟ?i2_?G$~N_ h;8թ  -=Z(i dyҶ2lk՝AwT5O*,f5]:3A79oͪaYʐf(145x`a•N{i[Wq2#,l!M nX=|*=0+:v0,OF;lHp ܧhhyw:o/gΏՇz8,Gt(IENDB`ic104PNG  IHDR+ iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATxk̭u}c;87ڪ$E4R*ݐ4JcsQ7ER!'5#HT-P $54&ԟFj'N%'}.c;1g\\ێm5sk}[?/k_?O=<?q_[jDP 2 ¸%f@ n`LWwڥ},atv@4v.!u"`J,u͂ fT+$* #\B '9U5jnSCɰ֞ qvaYlp! t  IA+KV-'A@Rpks!;f+Sba`VOiE*HN1: &y7 i瀍HK@qHWLِW_n߸xva{广K?p_|s~؞lw]I֣7oAiԃ¸%fe7';>>dv;f rYpPRX%P:fAֆE}CID!)8U~7&m@{XА!?[P23801k^JGA-H_a~ʯƒUDId3i\b($cuȎA-c2X,X}SZ S Hy -|^ vm9`#"Puܟ*RՑM﵄ܿ`/?z7>3oD×KϷ;㻗^K "h3ƠZa rzUP\iKX2]A3,8M]KH]؀}( f}hkâYl> JסP$"~\* ?L6YnhȐğGwj(gu]XV5}E%$s/S0i?hWc*$}2HT nm.1q1@:d v֖1cvsJ, > )H)[$oÄ>\!TJA/Ds؂̍*u^Ǵdڬ9G|ܣ/?x=k^{osi}'=DP?bRAL1 , .>g_u6%1˂ӄڕŊ ( ߇Pl7 6,ŦS$p %H"G qOũÔ߿1iرr>Æ&>EAdKm &+7pԳ$`j~Qɠ(T LZXh=Jd3i\b($cuȎA-c2X,X}SZ S Hy C&@sh>Ds؂FkZ6kg~>yw{WgK?zxr7^GK~SÅDP 2 ¸%f@ n`LWw@fC혁i`jWR+6$|BYG,(ڰhOB0u( }0A ?%S~Ƥ ` z{mHVb n`]XV q .n>hw}RvMXq^;%}2\f B)IH Y[d)0Xt+ܧ"$mĿ5LJ):}@ױɍ*]״lTfv+_GWoKѓw/Ÿl/3ٯj74sZa rzUŸDw;f rڕƊ ( ߇Pl7 6,ŦS$p %H"G qOũÔ߿1iرr>Æ&ޞC*d8:Xu$@BE[iᶔl~eS~5VfNI WP ne.1q1@:d v֖1cvsJ, > )H)[$oÄ⇇k gR 94DDPulArJ}W5-5v߼^S#o?_ok/n+t}-R%7}sj*l%fsD&n]v' o3q7ac"'6v.q$v( ߇Pl7 6,ŦS$p %H"G qOũ>wFLO8ɞd$JHaZ.rof9(x_aCj9jNI sf Ҝ]Qd а)1;eLXݜ O}J+RArI=0P73w߃ЁH)8*]nTp8c٨Ͱ~?ww59HʳѯK/>o ԝδ$ϩu[ o2 <l]vSBϾꐿ6מn DN{PRX%P:fAֆE}CID!)8Un9赕 G;ٓޔR iА0V64 |S,gOT ,|H-G-)铡tb. D[K:!%f+Sba`VOiE*HN1: &~x\\qfN{:;uGEˍ g,5vxxw>?[b~U !}IRus%MUCPMfgYI[Ɔ 4y@b܍g혁;M]K%ɫJ,u͂ fT+$* #\B CSrq(rk+v' )j!aXmh@:$,Y8 d1@X8ZZS'C\4'Du%4CJbgm1Vn7`ӭpҊTbu@F;L0Թ ǝ t w &l%X6k`3rsdS_s%=`kH^274SCPMf-1#2v벛iKW]r:LQڟ>D. NjWR+6$|BYG,(ڰhOB0u( }0A ?%NJZ:j^T"]0$ p4 raS\`3'Yx 0"b%Q~5*Aa\S'C˓`"K-%B!#f+Sba`VOiE*HN1: &~x\\qfN{:;uGEˍ g,5vu|ݿ?pw#Ft_ɒ`! "\F&y3,vȤۭn!.]It$o3 F݋+k <,8M]KH]؀}( f}hkâYl> JסP$"~\+ ja[_eH2z{RtyÐ0рȥiO9 rLY8 d1;ˆDըqNI .O4/L N2hX2&cnNէ[> 9o$wPasrřŏ;A@$M.7*8JlTf-n?j£'g϶^x /f䖔 ::J8›[b;Gde7P$tEӕ?|\&Ԯ%.Vl@I>bYPa,6ujD%aP(A?`@~J.~0u/2$ DaHVuh@RôQ ,gO aD JjTt\Q'C 0~%0$cuHA-c2X,X}nTZ S Hy :!WYDNaQrFu l-:6s>JgSo?<ޞ'yK[Z* S_>d6e9"n.t%%d/u/,9 ҁPdPRX%P:fAֆE}* a eH"2Q;ʒcEcc4|}kY^T2$ &+!SөsQ匡IC!HXI_J7JdH|Ͱrʜ$\AjtrcTp˘r` 2V|rIia쇇%ogDNaQr~qЏH| 3~co|G!=Ox׊I,fm|!wtN A7,vȤۭn!.]It$o3 F݋#K/t "&Ԯ%/Vl@I>bYPa,6ujJ|XC yfulXX M0`Zx$#!` :ɊDvtof9c(xP#R(VW>._`3\2g# W\2&cn*LF6_o\Rww(asrřşSX7qTdܨ(qƲ_#|zo׿$sx>ۿZ$k B "qeNmm+l%fsD&n]v3 uJKN'y^0^4Yr|0v.!}aXB0%ޝ7 xY6kS\32$qwQ;0Oűt>M$ KdHALV4 Bl'S=~0C,<~BB( oɐva7'9I Ǩ1RDT8hFm?I=0P73w߃nu }J3<lpeBl3l*>c=w>~>ŐD9vsMf-1#2v벛iKW]r:Fs*0v.!}aXB0%ޝ7 xY4OqÚ0GP$"~>cEccA:7`Zx$#!` :ɊDvtof9c(xP#R(VW>._`3\2g# W\2&bP #X>-ܨ"1: &~x\\qfN{uM!YIt&Gurt?f>Kz [^}~7]x٬Hf̊@ nP$t3U9!"BaBJ]Bb:ʰ bEtlb2a/!~Brqhl,@[d$",!aX1Yр.rN,g O aD JjTt\Q'C lݔkPl$ R+[D JQ| }Jn8'<u@Na80aP7ll8sG*um֎d@l_#2{܋W̓wso}K{>p_%4e9&y▘wu4ԥ+.9n} bANPdPRX,2>^ƦS\&48!OXX=طy?*HEH%X: C°bb;]<81Y d1;ˆDը|O )cEccA:7`Zx$#!` :ɊDvtof9c(xP#R(VW>._`3\2g# W\2&bP #X>-ݧ"8'<: 0xw0asb̐G*X`v}B/@ݨec8G:1WR϶C/?#Oyx??3+ϼU/vU~㹄($T%T8̩ɜ%f y9"벛ԥ+.9n} bANPdPS)'V9Cw})-<.MݧB#JL'ʐD bDM 漰+ *wo~~jG돔. tS9m\nvW uÕE*';~_S|W_\x;?T'_k J "qeNmmKdN}g<FuҕDNpOj* *u`ylfAEQބQ2$} h!V46DOsP9'J2z{R 0Zg4 ;$,Y8 d1;ˆDը|O )&yC0r ^P׮Jaq1i9jؿ,Fpe.!Uˊw~$I ~G ~㩆O8$T%T8̩ɜ%f y9"벛ԥ+.9L_Rda,1R BYrXofSZx\4*t6ʐD‰ >XX˜y2*'IFBo/B*T jԊxF"A.r/  aD2V~5:b /(铁@a70O(s6p Q-c"p>Y>AK) ɤmFG_1)r9 SjD2%&s*X)\KHݲ2G>s$tq^xקr[sx׹Q_MGDѨ92c&s㖘=34.5Q}BkXZ2z(w`BMX A͂h46U%&l$=!Q} @9dTN)^TB{5Ԩ&D]< a_21.1 =;ˆ8ejt^Q'ͯ6n*aPl$ R+c"ĝ5V&(ݧ&$Y%lFGdcIiސ+aƦT#F-IRܧec~S3re.yGK|?}t</+ͻxn M TAeͩ!(y9qK̞rD 뚨>OID_Rd,@-T=s;0R BYrXofSZx\4*t6ʐD‰ >XX˜y2*'IFBo/B*T jԊxF"A.r/  aD2V~5:b /(铁@a70O(s6p ?RDYR+SX`6#ʱ~EǤ4o0TNAc z{w $C)hrԲXةVt2b&0a2@ďOs-<7ĊƂ tn2*'DIFBo/B*T jԊxF"A.r/  aD2a1jFI +Ͱ[0 W\;1(E$l%2A>5!* f3?"WD}LJ\aC46w0jI2>-G-ۍaE+s,f8*7=yx|KΔ?d+,5!* f3?"WD}LJ\aC46w0jI2>-G-ۍaE+s,f8*>]|S~?{`d-'ϽNbEHAJ3J'E|#".5Ї$/)2L9 PS),V9C}30Fc3]А&̗2$w-7Ċ9?L@够$HEH%W*AZhB$HSE!C!Sݿs}N3f0icuj J쏗뚨:OID_Rd,@-T=s;0R BYrXڧ l|,X4\Ƅ DC8-G-ۍaE+s,f8*>](|gMSw?Z qf41B DP'tZY-W92 -r]Ә }xJ""C`jɤ깟Pށ 5Rb:0Y7h46 9`|y(C?Zj.rChl,dTNA^TB{5Ԩ&D]< a_21.1 =;"NQwr I:eQG)x)  "&bPH9-ҍ kBrhVQYY"cR7 s*Ոh}&&sgH\bDʁO~[KO>}%=琭WM8I+VVU>wBF\4&{BkXZ2z(w`BMX f #6o,|tAC0_ʐDďߥ h+ h0~z$#!^ 5jE< aO9HX cKBgea9% 3nO)CrVF?"K33$OQF''W87d;k,fILPQaMHJٌ*QҼ!WP9M/FD1Z dOQavcfX\b/<ʁO ?7So?o2秠Dz"\2Y9b1ց_ c͂qaf>Lzm1a%!Ms-<7ĊƂ tn2*'DIFBo/B*T jԊxF"A.r/ tRB9;햔9tX>I 3 !yGǯ`5:91v'#PHXc1KjeFmaMHJٌ*QҼ!WP9M/FDL2NhrԲU뷛sp.W{S̞qT|ǒ+py7ɣ٢ N'4C]i}jKdN3Xpj/hA5x%E%[Y&TJA(U^͂E.eL_.@ďfs-<7ĊƂ tn2*'DIFBo/B*T jԊxF"A.r/ t 0ea g,GpSr ~DZ-G-ۍaE+s,f8*>]cYxѓ'So}q$<:0A5k+/0%o2o `, \t4ɠ<"dݭQ,j* *u`}ffAG_2&U/f GwbEccA:7~z$#!^ 5jE< y$4*o :ZP D3nO)9_B?"-g~H+XNNpn#w6X̒Z QiE*`f3rgV+AƦT##&&s`v3sre.y"Gz, 3}7? Sxa=g4!kp?ϸ U4/sBH"q8"*}dtVw 5Rb:0O|i<f>>F0&8P$"~4}7 h!V46DOsP9'J2z{R =ͭx*G4!?F卢A'RP sP[Hzr I:e @GPS<:~?rDYR+7*Hvl[㌘*r9SjDDQ-O!`_9ZXÊ.Wp`|e!='?$o-G-7n~cre.wyGz, 3mw _5lqI7CA_ZVW^l~3#uMTЇ$/)2L9 PS),V9Cx]r{Ys]4 IDh;sChl,@&r'*SSOi< X*Hgr&ZP D3nO)9_B?"-g~H+XNNpn#w6X̒Z QiE*`f3rgV+AƦT#">7sbgd3@Wgr2X:g+{{@?{ron+D$ACA_;VW!w6gΑYh}OSx PK&ULPց!\ |͂E.hCTTs͖bEcc&r)SSOi< X*Hgr&ZP D3nO)9_B?"-g~H+XNNpn#w6X̒Z QiE*`f3rgV+AƦT#">7sbgd3@Wgr2X:g1a-G-& f'%gr7%&OWG~_hsɱه )M>C;ԮuM9]I$8~I>d:v+s;ńJ)ea/l|,U.h466 [a"~4} h!V46DOPyjgH+}JUc*yŚ/,o ԃh9OW{r I:e(яH3P S<:~?rDYR+7*Hvl[㌘*r9SjDDPهfrԢQl2l}\x&|SbT?yxx杬ه/}Me@ڕ,8R㐑?!nli-M>CԮuM9]I$8ec~3~h0,V9CxY"h46х)b|u(C?~Mı z[0CB9UJ}J a>v QȠ(9p( ۭPH$=c9$2U #r(x)  柌@9"qgc, VV;`6#ʰ~EgV+aƦT#">7sbaY?}'J]x&q㔘c>]O}_l~_%2ϐ=i2_kw$c~_u~J)eaj} \dhlm ( Dh ?PǪƂ 4n T*y)3O/hBN 3D! á,lB!t@WЏHˡ(xt V+c2ĝ5V&HoTZ Xٌ~?t%Dp_}9`uX m5YM.h466 [m~@ďf~bUccAпQ f\0*SA>$[@IDATSOiy}A"wڧNP! t0ea g,GpSr ~DZt%Dp_{9`uX 6o*Y4r|„-ȸ^20+G &v.ȳ*2! TSg@_Є)gBEA-(ACYnB"Q7'锁C3?$OQF''W87d;k,fILި"xyվjhvIxYܧ%q ʰvL +uӧb A=䙻k@#ަk'0cs{ g(M>ckߨԮuM9]I$8ec~3zP0aKYh7 },v e[t2Np9ɦjXX f\0*SQȤז`X2i*y%o :ZP D3nO)9_B?"-g~H+XNNpn#w6X̒Z QiE*`f3rgV+AƦT#"s2TkLW2>O8\K=;AQG>D<’}P RWel~όUNv ^kҼ }J".G-s)U&x_Bc\}-fAUt2Npt $rjXXX0CB9DJ̏B& úLS1ϫ,y(dЉԂd8V($u}N*i9,Xeq}lpR(;0{w;n~uoW6 7w/zqTNAj&˜Ї$HrԲ_q?GLnKXl,4է 0ڲh1Z+TE+GjY@"&Uj3T.dC(dkP0K4389V( t 0ea g,GpSr ~DZ:%"Vvnc'%gr̞f% \^y敯㿾 gymQ濽K0@(H\פyЕD]Z6/sAU&WLv\}+,mfQTpNrt>w&Щǚ8T~^Zxb]vթO%Gj ê&D" 3D! á,lB!t@WЏHˡ(xt V+c2ĝ5V&HoTZ X?  yC09Ft "\<1T×LGXeq}lpR(;0{Û(;rechgoqa:du@uu|fh:V* kr]eNCWI$u9jؿ@Q|WnK_3%4SC9>]RUE؎Ajܡ5j!j)0ġ75LW:56uM)cJ̏B^- U+MzN3nO)p~DZώw^/Zh_}JE؎AVҐCw($G5z̏BjQ%*p@"A_-;o LԂd8V($u}N*i9C;ԮuM9]I$8ץ%u\W|^c 2F/W.@Yl k PIb5akPU|+ź즔1'Qb~*jP2ZhBwԫvǞu}NUW#Ҳ(xv)  柌@9"qgc,V5?>0TdK0o قDP'&sJc ;nW39~fbxbtGPlۿozxO\3NCF[d4 z0o4gr jr]eNW]rp쿦}ؗ%דJ2|pLA(U(Uu\4d]J8QS&2 cM qC鰆郶b]vM }$2J1 0, E QȠc(9p( ۭPH$=c9$2U #r(x)  柌r]56(Qu,ܧ"$Af$. ”#g :_@fB_F̍*uK+Vvnc'%gr̞Fw ^Gi/=叞<<*Ӑ%473P9q5xI2'.f8J_>c˒ uQkge8 cr:.v Vk%)V QH&UtXAf.qS&>͏BQ %M s݅e"y(d1Ԃd8V($u}N*i9-_[v 'zmΩŦh&0)G$4rJ3Y2]AAq=ߗkge8 cr:.v Vk%)V QH&UtXAf.qS&>͏BQ %M s݅e"y(d1ԂI | !yGǯ`59PaV>q )HW? yC0 قDPЗQ&sJbÒU +ug1AQt,O_g^=I473z.EMȀ#cu)nt$('וkZU>w^/1T헫OTq,cZktw($GMZ@05q* ھ6uٍ"4'l~n(V4hBd.,Oy3D!f'Á@K٭PH$=c9$2U #r(x) Hrb2&cnCf}J+RAx"|xBaސ+ r-@D|% }e27)>,Xeq}lpR(;0{{;D3p feݣ}t5CR7g[$ɛSӰh&r1Gк6j5C ץ%u\Wi-Wy dR_>]RUE؎Ajܡ5j!j)0ġ7k>h,e7nğGQ(*sdX r>]1 :ZP -eB!t@WЏHˡ(xt V#ɉYb P˘rY>D )H ؋8q yC0/0rhf/eܨRXd:ba6}J]x&Y-;PQ-O}rwp?*uKh$sj -1 , .>g_u߁Z%c K+h ^k+XU>w^Qէ Ph1Z5;pX-DM eƚ8T~J5L}m7EhO#YGEU.7 k31kمe) :ZP -eB!dBΘAGePpvXNը|*~l1VnKf%V0҅¼!WR`lA" (^(QNatx*ÎmcÕDL߁ٳ} (`V}=7ўT$̩1V,t0}!ԵHX’ >3fiܲ]"+V`5*9쩊[d-REc> 9 ?>0#)t0o9[ЁJ42dnTS}X2^ʰv>p.Q]ՖE؎AZ*C '9:TJ9 5q*0ӍRf.<$>j{TW aJF"{NAQP [V($xOr*, n.Q=buȎA-c2 llfAmSZ S3RK \aJA/D3{/LF:ڇ% ;nW39~fb ;YY}gwܿڳI%djqKB' nOWw@]%,ݎ\&Ԯ%.Vl@I>vtFW[b;Yk%P-+QH0ġ L7JYź&QQ^-JU+M8]XV;!gBGA-(No[Hzr}>٪f Ҳ(uDO8\K=7( fe1_g1oSA=Y8ƀZa rzUP"c K+hc" +u PeجommX4d;pCDM PSPk0(ei벛COFG{p(Vg4!waYlp! t0;ZnAjDɺxO'1Q?(5%avY"*DГ%sj -1 , .>g_uu->dv;f rYpPRX%P:fAֆE؎AZ*C '9:TJ9 5q*0ӍRf.<$>j{TW aJF"{NAQP [V($ʯAIdHz"3i~ n.a"_[C\Vn7{sx> (|`FRjBaސ+L)0rhjQ1wtU +ug@1#HD1`Vs侾~S ,ySc@0nY9`au =Ck%1˂ӄڕŊ ( ߇Pl7 6,v JUJ8ѡZV&aCU)5@n4uMt!'QޣʽZ8 V3qܻvB68 :ZP -eB Q~5d]O'CјHSputH kr,# )HAF3RK \aJA/DCW̍*uK#D2>O8\K= uA"4$~^  y`4nKK ;ELWn D. NjWR+6$|BYG,(ڰhOB0u(p^QsW7S~Ƅ,e7nğGwj(&D.vaYmv3D!N$s/S0i?hWc*$}2HT nm.1q1@:d v֖1cvsJ, > )H)?I yC0 قDPЗQ&sJbÒU +ug1AQt,> x*DГ%sj -1 , .>g_uu->dv;f rYpPRX%P:fAֆE}C*@F~7&Lf.qS&>oSCɰF84!rʹ jcK! t  IA+KV-'A@Rpks!;f+Sba`VOiE*HNHJ-](r)9Ft "셾2UkLW2>O8\K=7( fe1[*7'KP+[b:AX`]v}BϾꐿZ},atv@4v.!u"`J,u͂ fT+$* #\B CSrq(0oLڀf.!CBީdXgpaԕcvaY= :ŹL¤_% f R)QH Y[d)0Xt+ܧ"$mĿZrtR)t "ͭc 27yӒi }lpR cv8 fb˦:4F;;*45'KP+[b:AX`]v}BϾꐿZ},atv@4v.!u"`J,u͂ fT+$* #\B CSrq(0oLڀf.!CBީdXgpaԕcvaY= :ŹL¤_% f R)QH Y[d)0Xt+ܧ"$mĿZrtR)t "ͭc 27yӒi }lpR cv8 fb˦:4F7TijAO<Ω1V,t0}!ԵHX’ 혁eiBJ]BbECY(6E[bSVHTF$8TQaߘX9aCBYީdXy %]XV E%$s/S0i?hWc*$}2HT nm.1q1@:d v֖1cvsJ, > )H)[$oÄ⇇k gR 94DDPulArJ}W5-5viJ]bM(?)1i( fe4VV7TlAO<Ω1V,t0}!ԵHX’ 혁eiBJ]BbECY(6E[bSVHTF$8TQaߘX9aCBYީdXy %]XV E%$s/S0i?hWc*$}2HT nm.1q1@:d v֖1cvsJ, > )H)[$oÄ⇇k gR 94DDPulArJ}W5-5viJ]bM(?)1i( fe4V7TlAO<Ω1V,t0}!ԵHX’ 혁eiBJ]BbECY(6E[bSVHTF$8TQaߘX9aCBo!B J  ۺ j !w"ŭp[n6)+KxO+rz DZ(28 acv;k˘r9% VnV7zaBе3P)@"(: Q ]M4p.&q̟˔4v\:ʢQ>Թ xDГ%sj -1 , .>g_uu->dv;f rYpPRX%P:fAֆE}CID!)8U~7&m;Vg}r,9d/2# :R B1u RbU6H4Y#lE܂ņNu#w6wcǹ4wlXg02B%+ -;g7q LP_nTVRBн3H`#O(sAvZ}yMFw 0OPOm$"{9?qG4:'z6a`D 42x\S^+7~JF OWe$'ـ.W;< kQ+ zȲt!au"aŸ-4:憠i{ål> 7T$#qZ\*Ϲabey."ϸR"Zjc#7G|a|qkoSqwuyφ։3N CBITdlжn)9udƠarkܧcݐ? 7T$#qZ+ jczj}ĺaKȵp604=#Et.4I#?fQ$F'hwlhtE`dx% P"cuM#0%ԗ[>8}PԷ = v0w&j%ϸ6k/tkEmmarqcyfiɥ.l߿8~ottC旗,E]Cʓe 2, OPV+6,Bcn7\SP,LpJ%L2Gp(NŹb 6<~(߇PJ+m\ g#KM#>rPD2@,_?aFNntxyφFW$ FW̐ .26h[:2cP[P0Y@}5SYQ SlnH@}KP+,} b#ya⮨vQQk[MG( +V&g1wmvv\œޘ 35XK!3 L t-ђ]+{`'d(1CiHX_H؄gh ͆!hpi6OB0M(0H>͎Bjql.@C~|(蟿w-a[Rl`D;9DX,_?aFNntrQgC 8/5$JbDm떐8XGa q J& /ƍ*+JAq? )?ojŗ{A`$46Ln7*9Jqm׀'hߧ08$ZQgG[idX޵u}rg یSYԔx(2 /%xpKV3}GVA'l&bF8oPv`d%K Aۺ%&֑qr܂B˭qʊRPbuC >w@([Zeg;; wEۍJg\5`?# )  +V&g1wmvv\„q7PZ52?WޒJrȢtZhUܕ=Ym2bԡ4$/Wl[( Glj! ri6OuPXʘdePs`s<~|e_[ɶw ?#3c,O_e+ (]n Qgob8'EՊqr\p<+ƍ*+Jl^,i8; x-uC< v0w&|w)9J tr1#IC5,$ZQgG[idX޵urOF @ ~3_^!ٯ^INI]/%Zr9kqwezObV섌%u(  ÑqcnBC\S]&$82&qY4&\1\k=G;CY;V|a#XAK?SWJaFtW:#¯o٠f" gF 7A"0:xdƠ7d! JqʊR Ǻ!KN@}KP+,3Ɔ"}JRe|>\HPMG( +V&g1wmvFܓхE!i@#(*ፇ#2NtdҶDnyMk\NZ\]ޓ;!#F@JCrEp$~А/F,2&Z+ 70|PEFA>R¥%l&+zG^k?(`b$P 3(vw>ʮX99( VF2H/D7(aKlnH@}KP+,} Bn Z/Qtum׀tkEmmarqcyfn=]Xd/AQor|NZA#l&bF8oPv͹ gF 7A"0:xdİG}!ڸQeE)_buC >w@([Zeg:po(W}ݧkgD#\+jh 3˻6Nw\"$TQP72?{ޒTOrȢtZhUܕ=Ym2bԡ4$/Wl[ m9~ 6w򲀅@3T$#qt~Rs`sA&ws?(߇PJtCmbe#A9k,_?aFNntrQgC 07:'EՊqRF\Bh> h? PN*۾]xRdPuumg?#e!aE׊8;L#<ͮ({2ȶITQXϖa$'ـ.PK-ܵ2'1 vBFÁ:劀 ` -4o@}\S^hʘd$ÏBjql.zCJ nȖXl`D>9>{勑G6^@1HIߍNP7l(bf\3WwZ<2bXʈ #YоmݧǺ!0y=w@KztFƂ~-E/ݨk8gdXOMG( V&g1wmvFܓхE6! TQ›%gE t-ђ]+{`'d(1Tԓ6)sCB6wžXʘd$۳^r`sA&ws?(߇PJtCmbe#A9k,_?aFNntrQgC 07:'EՊqRF\Bh> h`sdϔ!W \` ]-2 P-F]G]#QgXBJqvəFy]]Q.dta-l/20~nrU\Sؓ Sa{0kCl.zCJ nȖXl`D>9>{勑G6^@1HIߍNP7l(bf\3WwZ<2bXʈ #Yоmݧ~|2ҺV@KlR ncuumߏg$y tkEmmarqcyfn=]XdK$/U!I24ЅjZ%WqW$U5*R*A*U~熠SWl>Uu0xT$#qOg#6 L0*DEFA>R"{=%lW h>#勓G6^@1HIߍNP7l(bf\3WwZ<2bXʈ #Yоmݧ"%ƌ.EiP+0R.AՌx} ܧk~d?#|vDPVs-L42c,:N"S!P/2&zx [v]6%Zr9kqwezOX_ @-\L'1~n:u5^SU'yLeL2Gt0kCl./OTdC(%׃[¶zv>#>r X8 ~d5Ì4x}φ+`nuN8s5p1# p= }J+R2hahBX #_X͈jjq~7*e]#_e!a׊8;L#<ͮr<0&_?/JU?Kz\t@ 7'ҵDKNHW7jFcTT6){ Wȥl}@Xx Fh|: !W 6do`rTy'*2 !AFg61$<#勓G6^@1HIߍNP7l(bf\3WwZ<2bXʈ #X`mݧ&%ƌ?{E#cQZ7 8Kl~a5#^߻E:8яY#MW( V&g1wmvp\rtax\Yr˰W`l@nKko)60Loc~F8PK,WlS Wȥl}@Xx Fh= !W 6do`rTy'*2 -Iq@IDAT!AFg61$<#勓G6^@1HIߍNP7l(bf\3WwZ<2bXʈ #X`mݧ&%ƌȟy(jF%6}LE:8,$`12c,:N"S)Yx|U!5gINY\kRe)2d_5|ezCk4:ˁZJ%He*`{_pa$O \ ]3,`׆\1\P_(߇PJd]"H"†=&$va'b,ߍX7l=3ps 3WwZ'<1,e$,X`)JкOiMJJaFWd~GǢnp) jF0n)2RpS+ e!w@kəFY̪OR7+ ԥwP !W 6do`rTy'*2 !AFg61$<#a ]?f$( `wc >d~ \9( V OA K; XJSZR`(jF%6ǻ[ T#ܧk@?n kBYH 2CǪIlpdGHZJPx|gêƌWL@-˵&-x QW&W TT6)N\"F1aI78P ,Ԇ\1\@ P塟0"R"{=&FG|a61! =Ìen QgK8'9`1qRFҪ֤f4Ev9wD,JZr /f"):8?Џ:šPOY~ Tk[idŬ$u:;ߗ?`d7vCo_14D6^kMRYЯ%o)6$L^5J8PK,WlSx盋Mk;K#G^!,֧2&,`׆\1\P_(߇PJd]"H"†=&$va'b,ߍX7l=3ps 3gc`7A"0NxbXH9XRZuҚTÌ.EiP+0R.AՌx=`Rd8>]G]qSVXB)<*?zm 3Un"^g솿o@>ak̠md!q$zZ"bCb!_T>JrU0wشfYs4 ~tb}*c8Z>FxmaU削}Dz%+M$ !j)lcBb{( %|}17pN8s6vcT+ㄧ 䝃 ,U Z)IIU)0hrX #_X͈-ESuԵq~7uj5],$s׶098YI&:aam4u'C{{_}%(L˵&)/x QW&W TT6)sCB66]88Z>FxmaU削}Dz%+M$ !j)lcBb{( xs}ӆ~FFDkwZ'<1,e$,X`)JкOiMJJaFWd~GǢnp) jF0n)2RpS+ e!w@kəFY̪OR7}Fv_J`Mx/R2}]5ICAH{ؐX2z(O@-\Lo.6mY/`]{AcXʘd$χ^r`sA&w@~y"B)t n|f#I#>ro ؅aF?\(!8cʁ|`7նZaR9LA KU ,^՞[)I90+F#cQZ7 8Kl~a5#^aTd@ /pgHBD*?zm 3Un"^g솿=!} qU|^z% dQrI:u "?=Ć^ǪsR RY l?7F*d ,Tg0G﫳^r`sA&w@~y"B)t n|f#I#>ro ؅aF?'!8c!/;%?Q V a; XJnTZR`(jF%6ǻ[ T#ܧk@?n kBYH/(`֤tϺNbw^Loc~F9PK,WlSx N6䉁K#GW\23Y b {; C$9DX7 {LH`O0#AYOuuȐ(y vcS+ㄇ0䝃 ,U Z7*IIU)0hrX #_X͈-ESuԵq~7uj5],$s׶098YI&uv/:nK!/g b+Y kMJ[_$6||?VhtJrU0d!H4 ~tU!a*88Z~_ڐ+ 70<JKpW3IC~SذDŽ. P̸}NF4^ ~)`79"0Nx#XH9XRZQeE)P_]ҺVa\` z;pJ5}6N+SyU&gg1>IDO )Y5=Ps\3^BY\kRg|`';W7jFTT6)' Al+  U,`׆\1\P_(߇PJd]"H"†=&$va'BguuP5JLO~1ȩqrFҪ*+J0h2X #_X͈-ESuԵq~7uj5],$s׶098YI&uv/:nK#C31y,E˵&|֭/ v>zezk4:ρZJ%He*`{_pa$O \?⪐P\H-FxmaU削}Dz%+M$ !j)l1Ɯd¸5 df\G>Y | ~)`79"0Nx#XH9XRZQeE)P_]ҺVa\` z;pJ5}6N+SyU&gg1>IDO )Y5x| f>/(`֤tϺNbw^Loc~F9PK,WlSx N6䉁K#GW\23Y b {; C$9DX7 >F^ИBF̌?'A#/?%?Q V a; XJ7(v0+@#cQZ7 8Kl~a5#^wNFOQ~ԩtxr;O ϵ^L#,f'#/%K&>vPs\3^BY\kRg|`';W7jFTT6)' Al+  U,`׆\1\P_(߇PJd]"Hy$ n4 >Q^ИBF̌?'A#/?%?Q V a; XJ7(v0-^{Zr /f{G,2 OQe@?n|f4],$Ns׶098YI&uv/:nS 5d bF~1sD[bCb!䎯 TT6)Ŧ5ڝ{lk/hS(68^/QdC(%׃.^lb$IhJU7(A/hIF@![PHfuu ׁȑ(y vcS+ㄇ0䝃 ,U UVUaqA^=_EHV3# ܧk27>FPO]~' Tk[idŬ$u:;ߗ?`d7Iy]d! 8˵&Ṱ_UK$RlH,D]\k'pR RY 䶲v!?7.`]e# IeL2Wr.6UNTy(2 !{[?U&FEXh |1'0nB!QOi8_b?##G_~J~5;؍AN3w,V%(oTYQ Ta#[x><#g"_X͈4mnpˀ~txr;N ϵ^L#,f' UMR 5>qAQw1—kM_OK$RlH,D]\k'pR RY pw3K#0:IF,I B- atPm+/QdC(%7~M$ 翉6 zAcN2 aB23pq~FFDkwZ'<,g$,X`)JPި ÌGz}yF*jD%6'i-D>]G]c e!tw@kəFY̪OR7H $qkaӿ b&<YX5y~u-E-Vȕ{گj) 媀 ` .6!4|T$#q|Rڐ+ 70<ӉRSGoblbD&vMa4$#ƭQ($3::m@gdHOO~1ȩqrFҪ*+J08 Ǟg쯢VA\` 1}BB uԥl6a|?03,f;ƥ٪/pFF*A7(Jv/7f>(`֤t7W^*o)6Hq^ެǪ \kU ' KsCЩ6U ?T$#qZڐ+ 7O=@_J}*cQ_!FSc$9(qk ̸}N:9S%n rjE`Fs`*AyʊRj7 399p) jFLPymstui4 xG! e!qp #map?َqiK?J E$@Hgpf){py յDX#W7jFSTT6)<~!?7/`]U!LJeL2G˟Fg#6 )*tʊ|>T89M\n4 >F^ИBF̌?'A#/?%?Q V a; XJ7(v0-^{Zr /f egh 67OQFwP3;2g1Q.V}S42R AQׄ/tnjlr JkM_]Kd<~K1rezk4:ŁZJ%He*`c5`dS\?¦Kg0G˟Fg#6 )*tʊ|>T89M\n4 >F^ИBF̌?'A#/?%?Q V a; XJ7(v0-^{Zr /f egh 67OQFwP3;2g1Q.V}S42R AQ/&|W){py յDyumگQ~ǟ 媀 `  0sCM!ri b.eH-Ĺj {0SE/%>}&F:6( zAcN2 aB23pq~FFDkwZ'<,g$,X`)JPި Ì+~j?#fM(xsa#ُAPa |1'0nB!QOi8_b?##G_~J~5;؍AN3w,V%(oTYQ Tax?&i-D>]G]f;B|JCyqH,nD&ЫU|f72R 3*\z@7eK˵B(,2?)Q~Fo@-\L?7*D.`]aSL@؅L5 "w 3呃.IfȀ4ߧ\Ĉ}#uTg%A/hIF@![PHfuu ׁȑ(y vcS+ㄇ0䝃 ,U UVUa>`O'bĬ egh 67OWQWݬ6\#74$g _LY An^]'V69R E;.\/3{ݳK݃˵&\Я%H^k2hF2XT6)N\"FQaIK5 "wG6saʋ 8U_J}* ̅M(g?RAu)lQƜd¸5 df\G>Y | ~)`79"0Nx#XH9XRZQeE)P V~"F̚Pymstuezj;B|JCyqH,nD&}beq# [% LhM@L˵&)-xRˠJSB sU0?K[Vt A˥l`k^Mw(ӄF#Q@O OLpGVppa#y|APa |1'0nB!QOi8_b?##G_~J~5;؍AN3w,V%(oTYQ Ta#[x><#g"_X͈4am4L׬6<8n{4$g 0$zavO2HJ- ͵` _Ì\kU6k,:8?W(! (U,m!X57A/FyY6ݡL ~Ĺj :/TyjK_tm 61HGI sP(qd6u 32r$'J^Ԋ8?`9#y`KiUF@nf?rпc3rWQ+ R.AՌ>Ls&ONzjÃJCy]INfg$; Iܢ\ۺ} sK?G\k~=-':8_[iCYH@a gi \sZz˥l`ktS5ݡLݪmj8W 6P+fȀC4?m  x S$9(qk ̸}N:9S%n rjE`Fs`*AyʊRj7 399pr%iN2i9\^Ymxp>8BiH4X;aBI,pd~t< [k[wBL_kMPЯ%o)6$L^5J8PK,WlS \"F1aI3,`׆\1\P_(߇PJd]"Hp[Ma4$#ƭQ($3::m@gdHOO~1ȩqrFҪ*+J08 Ǟg쯢VA\`  uԵq~ЏY#MW( .g\-L42bV}x˟0T$n|t?8rYd! trk,WL^@2yU5@-\L7wr`4 ~tM1a2`$?Gy0,!W 6do`rTy'*2 !AFg61$&.FSc$9(qk ̸}N:9S%n rjE`Fs`*AyʊRj7 399p) jFw"ytum_gHBD*?zm 3Un"^g? ]#% xrcfrI D2-ņBԕkF|j) 媀 ` |sisͲvxi ;T$#qYֆ\1\aU!}D~O$7qV7#A/hIF@![PHfuu ׁȑ(y vcS+ㄇ0䝃 ,U UVUaqA^=_EHV3b23hܧk27yl6],$s׶098YI&uv}bUiT$n#(h09MyQ˵ź_eKB[ .ۯLoc~F8PK,WlS," yb>j H-g#6 L0*DEFA>R"{?pso_HpMa4$#ƭQ($3::m@gdHOO~1ȩqrFҪ*+J08 Ǟg쯢VA\` 1}BB uԵq~Џ<POW~ Tk[idŬ$u:;>4@*Aԇx_o;X5IcAH{ؐX2z(O@-\LOYOqi_5pIFVOB- y6_̇TyS4ߧĈM7qV7#A/hIF@![PHfuu ׁȑ(y vcS+ㄇ0䝃 ,U UVUaqA^=_EHV3b23hܧKl; f`( 3gl 3MYvKU_T$nP,%8Ž qckM^cA–he-&{گj) 媀 ` ,u AK#GWU(xRY b {T4ߧ?89M\n4 >F^ИBF̌?'A#/?%?Q V a; XJ7(v0-^{Zr /f egh 67OQFwPg 2gf)Iܠ(YK@q ={'\k~=-~K!uerگQ*ZJ%He*`' KsCЩ6U ?T$#qit0kCl.@O蝡OE!FSc$9(qk ̸}N:9S%n rjE`Fs`*AyʊRj7 399p) jFLPymstui4 xG! e!>bMa4$#ƭQ($3::m@gdHOO~1ȩqrFҪ*+J08 Ǟg쯢VA\` 1}BB uԥl6a|?0DL?LYvKU_T$nP,%8  ƽY5ISAH{rumگTT6)\<6X A6ȥl+l q#qit LpT Q~ S$9(qk ̸}N:9S%n rjE`Fs`*AyʊRj7 3/D5L m!:h6.P3Gbq{, R7^O3JMQ>$X .ך_OK$I6k4z+j) 媀 ` !U!ri b.eH-8 s`sAwaʋ 8U_J}* ̅M(g?RAu)lQƜd¸5 df\G>Y | ~)`79"0Nx#XH9XRZQeE)P V~"F̚Pymstui4 ~`( #=G@VH%H⦏Ϩp }?CUPrIz_"C{?6kԟj)/x\L/80~n'.`]qU@X?.h//ΔG6$5^"|>xsa#ُAPa |1'0nB!QOi8_b?##G_~J~5;؍AN3w,V%(oTYQ Tax?&i-D>]E]vp/Ґh)w|=3g1x xu}pXD\`H%H]{n ZZid?)Q~Fo@-\L7# Wȥl}@Xx;k`j8W 6@#/fȀ4ߧ\Ĉ}#uTg%A/hIF@![PHfuu ׁȑ(y vcS+ㄇ0䝃 ,U UVUa>`O'bĬ egh 67OWQWݬ6\#74$g _LY An^]'V69R E;uWv }R%}\k$Z"OUOE]sV5XSB sU0?K[Vt A˥l`k^Mw(ӄF#t0SE/%ѵl+7\Hh_#uTg%A/hIF@![PHfuu ׁȑ(y vcS+ㄇ0䝃 ,U UVUaqA^=_EHV3b2{XF>::+5 ާG( wqz'9z"L:^.0$qw|sm=7(rݎ,ךvͳid?)Q}JSB sU0?K[Vt A˥l`k^Mw(ӄF#o8W 6D*/2T )돮`[&FB::6( zAcN2 aB23pq~FFDkwZ'<,g$,X`)JPި ÌGz}yF*jD%6'iN2i9\^Ymxp>8BiH4X;aBI,pd~t< [k[w5!bFRr6k,:8_>04$0WlC𳴅`]9-tlwWP nն LNCO  3U^d!R][qÅMuY | ~)`79"0Nx#XH9XRZQeE)PoU 8 Z4am4L׬6<8n{4$g 0$zavO2HJ- ͵ܐfRZkAږbqy?J5_[SBbU)WlC𳴅`]9-tlwWP nն LNCO  3U^d!R][qÅMuY | ~)`79"0Nx#XH9XRZQeE)PoU 8 Z4am4L׬6<8n{4$g 0$zavO2HJ- ͵ܐ J?d$zZ"OuԵq~گ4ա,$0VŌC/I Y9-4lw_ w*ѤF]-B- I:g-6FL_Jk+䈳t/n4 Ɯd¸5 df\G>Y | ~)`79"0Nx#XH9XRZQeE)Po(j\-`| e\ >>:+bVOP3Vb{XhC9z"L8W5^SU.0Ҥ$qw|sm=76# A-:8_vmhCYH@Oς$Nri(lmf]pMjtvЧמ8U5~^^aAnK؝R][ Ie׍I^ИBF̌?'A#/?%?Q V a; XJ7(v0-^uC/LkܧV~ezCjÃJCy]l -q(GO }BFT$nQomռ熜i97%Z~@AH{rumQ5J_X3q^];^vkApMj"rmSU㗩t[M;aAnڔ )돮`[/M,5MZFSO"A/hIF@![PHfuu ׁȑ(y vcS+ㄇ0䝃 ,U UVU0?r8cnpr%ir-`ïLoYmxp>8BiH4Xa%0\xavOUUȻHJ- ͵ܐ3m>W)dIzϐ"C{?6+ר?7gˈU1w~^O"^kN  4l$pMjT`B--BF6~Jӝ΍Â.ܜ96?m61$kˮMa?1'0nB!QOi8_b?##G_~J~5;؍AN3w,V%(oTYQ T WPºVAHNF̗P˵ Ss2!fq~!UU!#M*A(p7׶jsC4s R*X!E yueZH}e2bUĝcx$c}ꎗfZ5ܩDba{T2[xsMuư 7m_o_tm%ۚ~qlbdiX7 zAcN2 aB23pq~FFDkwZ'<,g$,X`),hܧp- ?pTTKnp r%ir-`ïLoYmxp>8BiH4Xa%0\xavOUUȻHJ- ͵ܐ3m{*Xr zZ"OUԕi')'gˈU1w-vWPYi6;*jN%htPn~Wc{]st7dXЅ[ㅿCd!66 pW7 zAcN2 aB23u ׁȑ(y vcS+ㄇ0䝃 ,TV΀ji Ar@v0b2 ^LpZ{ 1 ާG( w=,ġ=&/ yiR E;uWrv\ ćHAH{rueZI}e2bUĝc_<TV_jS&5F߼O1G~]]5?DzO+^e֍E^ИBF̌?'A#/?%?Q V a; XJ+ (\ 9(j\-`| e\ >F:+bVOP3Vb{XhC9z"L8W5^SU.0Ҥ$qw|sm=7L s *Xr Xߒe:ʴ~r_X3qY9-`/hfF4ܩDjEfN^ИBF̌?'A#/?%?Q V aq˻ 6X;6SYQ ðgH7 Z$'ja#K(Ӝ^e nTӬs2^}j?pҐh)z(Nfg$; Iܢ\ۺy 9;ӊ\O/%?[S WˈU1wᝒ];^vkApMj"rmSU Ś ]iS~k+c#K #lKa |1 D F̌?'A#/?%?QF qrD֧h> =| ºVAr@v0b2^FF::+5 ާG( wE9z"Ld1wavO[ru) [k[w5!ggZ9'q饤xK}*ʴ~r_?{*f1SKݾ\sZSuKn-TIB[Z@ΰ=qj =| ºVAr@v0b2^FF::+5 ާG( wE9z"Ld1wavO[ru) [k[w5!ggZ9S_`8ntЂ>Z}*ʴ貮˽X3qzdR/לTlw[ ;hRP3lcOحiӹqXЅS6_o{F5[Ql+jMGV!?0 >N^И"[PHfu}>X+ C*v|`7:7_Ga q˻ 6X;6SYQ ðgH7 Z$'ja#K(Ӝ^e nTӬs2^}j?pҐh)z(Nfg$; Iܢ\ۺy 9;Ϲq ZЧrapע\L+ .ˈU1wwNI.uriN/f{[kS&5 nj96ĩ!5M-M2QYUm%ڀ`#`X#l:6 zAc2nB!Q `g 322$A|ߘ*1(-r,`qظOeE)(Q"(j/Lsz&QNzjÃJCy]>8z"L:^.0$qw|sm=7Lk<| \Чgؠ2, OPV+6,B-לri6;+T w*ѤFj[Z@&'NU:/!@x&ܸB Aѵlk3v#lٷg / ġ7quyφY i!U!"26h[:2cP[P0`am4SYQ SgH7 Z$'ja#K(Ӝ^e nTӬs2^}j?pҐh)z(Nfg$; Iܢ\ۺy 9;Ϲ m7ϐO p*ʴ9e@JCrE&?C[@[.f{[{N%VmP 4ĩ!HQmÂ.>YQbJ MGV!?0 >N^И"[PHfu}>ٺg02$t[vn`1(-(alD()3RuCRr%iN/27i9\^Ymxp>8BiH4XGO ]' }FT$nQomռ熜iF>Z}*ʴ;g0,<CiHX]H؄gh ͆!hpi6;+T w*ѤFj[Z@&'NUu4}4,K{s/=zj+Mlb$A3La4 #H($MTl(z23~ rC"+8AZ7E"P#lDoܧ$??>8(EQX7 (%9 W ;1_BF|-כF:+bVOP3VB}oi݋rDb6,p#S*A(p7׶jsCδs{_7>@Z}*ʴ;g0,<CiHX]H؄gh ͆!hpi6;+T w*ѤFj[Z@&'NUxȼI0,M2QYUm%ڀ`#`X#l:6 zAc2nMnDzyφ'9/7$ҍShK(帑8KH}*+JA3RuCRr%ibq nTsϽ2!fq~!,| FFm!qpAۺe&֑qr܂B˭qʊRP?>8(EQX7 (%9 W ;1_BB/MpZfÕՆmS#DL?ջ8}@qDu0; ']`$O%H]{n_P"PthqעSW9e@JCrE&?C[h6t AK} nBD*Z@F6~LÂ.ܴ)C AS[ɶf_Yja[ V\ S$ŜT",_ز>Y x>p##U88ؠmݲsv8A9nA d!ָOeE)(NQ"(j/Lsz&QNzjÃJCy]>8z"L:^.0$qw|sm=7/m(jྥ,)_RUܕis#Ӂ:劄M( l f+* ܄R /#Sjqh2Â.P!ީd[wa׵ca[̽7 >N^P̉M%¢ߍ-.`g02R%k -;g7q LP_nTVRB?ZaR*A/Lqjuw2mVmS#S?I`. -s0; ܧ=ȓJ- p;x 9KZĵEdl΃]+:c Fu(  P m17M.fsW(T& &_F8Re*1eۇ]B AS[ɶV;îk<¶z{o*|JEK;]'`dJGn`1(-(A,()6X7s6~^ :i9I(sApZL3p~!O(X0CE,;/in.0$q=.sC҆q- 5`R <;Uܕis#Ӂ:劄M( l f+* ܄R /#Sjqh2\y߬ީd[j #lEMesoShiwcjd}6XT B("cuM#0%ԗ[>8}Pt R*A:4GݨVj^ӵ]m5Ӂ#S?I`^Ģqb\Ӗ_]XfJeH▅~pSܐġEP`ߡlzkqweZyȲt!au"aŸ-4:憠i{ål> 7T$#qZ\*L?l ;Wg`h/7wj+V<ڴAjz~S$ŜT",_ز>Y x>p##U88ؠmݲsv8A9nA d!ָOeE)(NǺ!G; xk88Jv0b2_?d7շtmt3p[Mt!O(X#h(fgWRe'57/q(j6T_hESdD<&kqh2V6py݅RbU6H4Y#lE܂ņŵfT">82.W g02B$ y Dm>Sn`1(-(A,()6X7s{ gR 9<FLP8FV󚮍`n)4$?{]v23{.ICR N8\DQZTG[PUTܔ6Ri@iJTB IH$;ql\ݞ:y[Yu{s+`~,]dcٰQLFs 'tiX3=jB X1IoMmp~ͩ6!;-D8\zHȨmRN)$CE$n.O ̔Z\,JĶ+Bl2SvvrٲM}P^!`C%kT4.F1V Ĩ0u଍\~K&czf'g~ݑH1UYDh&mjC6iť& f)l)}I$Я~ʛs:fP*(THN8WXJ;;ѰɎ:&,lmJ1KYX}6lA!\Bf ]1LP:FL[sAp4`EO1$:6)Qۤj3S"-HI!H\0(&;r ) YmW.19d$k% ز_/ lB $jXL|=t!MYؐt~}HѫkMMh& #NK@:fB"M`)lkїAPoP:xqxءFR!'UMǖtF9Bvf~T407`nIHR,LFȩ23@3) :/ca$5ݸIlB̀CvCsM.=$rJd6f | Df7 IΧ\CEfJB.E%b[E@KL)Z ;9 h⑗ߒ Y'fH6a݇l.zl-1(- 9Wgpj/cLUaq?Ɏ-~l¤s \{>xg },玢?t[3(pj&(i$`*5; 2U*H- )'q'W)*Oz`l¥Ħ G!K"+" C  Χ:33OҰgzlձ0bޚМA`lr!S"I5 g8UD[& 5C`PL2v>*,2Sjr)(z( ]b(U`J>5CZ .oz랸k[˃_tVfѬ0_9595wn{Z=V 5+q /!XK VbT$HDH甈8ʓGtFiBAB̴-T4@HG YO:k_7^'hAZWiZwS I@*$B:D{,Fwv>a!; %&,PaF,=DgoC~b֓'23OҰgzlձ0bޚ> > ;-D$&mRN)$CE$n.O ̔Z\,JĶ6?Lb(U@fIxͫ,o{C׼OQ{Mpn`n`n7=&.o?^JCBF_ k_xґb᥏ۡFR!'UMǖ{ i 'HOYÌXX{Lţ߆Ŭ'O̵/df0 3˔BBƾnd[@ ˪@ CvC"C"I5 g8UD[& 5C`PL2v>*,2Sjr)(2>T}w%z|~o|dr~^xӓ|W7gҐ̴їA4Q+oP:C8W#ߓcˈrzrF=đz@,PaF,=DgoC~b֓'23KReqE@\!c_WWˆI- }eySN!!ޝ(LRoLY +aT L!"oJT A'b)n0H1ȥɢD  dQ>Ao hqm{~+ʷ?ܚ_Gc&ss/ |Y~o}Oq7 P9`q rL#;ČKC_B"~O-33JםS6㱕 f0#i3S!a1(23KReqE@\!c_W['{H%{)aŐݐhNR T&7E&,0Lt7%*j tA17SddQ"Kۀ|b(U߀_hzX7˗~t!dn`n`n/x#˝|/!!@/iܯSy@H1W#ߓcs yu%xl¤j<̈GTT}wo>e.[>wY370707Ro*s[r "%W͌*4YmmwdQ>;Z7 M^[_gn`n`n`n`=|o%mć_xґb`3uAyHʞđz(T4@]HG YOvF1ߞY,S+  Z8ٳE. -w uD`f\,JĶ6?]&1gO B^]fR^Xm_Pkk.їA 0śV}᳽TY/-C in¤j4̈GT͌*4YmmwdQ>;Z7 M^k,_j _`bIxn(YnӛkF_e+P}_^kUPH9 8oO gaaaݫKX f0#i3S!a1(23KReqE@\!c_W> Kv`zv2$HJRWP-@/aT $M)j dP2v>͌*4Ym]&1gO B.zYgn`n`n`n kʏܺLLvx^㈸R\} 7Y;tW*(f*0-;?E'4zQRaf#Mtf*67,f=|{BffB 0L9(+d jq q]@$[J촐0 v+R@-$B%%`T}|Q5`a>xu@!([Ƞd|2x0.M%b[>b |Fj/4y?U %fR^X(Z3 bT*6Af-7wO&{ُd=Z3 bT*2lT`ƹ[w~$O=C [ B^"zT4@HG YOvF1ߞY,S+  Z܂k!aɖ雝' ɐhؤ^GuU 5YFՀkle"B&4Ym^l y,N2巶/{tfR aq `RkOz'bT '-ݓ0 5l5,R/mv%&,PaF,=DgoC~b֓Q̷'df, 3˔BBƾ'iHDx' t&C]Iu]1`T%YFՀy.;5CВ*d|R:t&ՏtA1>T}wi Zʟ}OΟO|`&en`n`n ~'^c [M1tO$["'`%jjXX;^"K*LY&ÌXX{Lţ߆Ŭ';oO,YHAf)q}_-n5NӐ dXx`5aE CɐIu*7RUP4YFՀkle"B&4Ym^l y,NѲO孿\27070707Z>0FPz43JWAD%{r2Yȁaa}z`Lc@HG YOvF1ߞY,S+  Z܂k!aɖ9}nBV@ 힛T5J[h泌  AD z3sR]C"z3(gO: D>{~$暔 l?P:qVƨ#ZbU*2HD8?wO;Z4-G+wK0xV6ˏxj=hwW x"=9C԰հ>w~sK$0ifZ 3ba&:3~pN1|,QHAf)q}_-n5NӐ d>nBV@ 힛T5J[h泌  AD z3sR]C"z3(gO: DN};勿l27070707peoXxπ|`_I43JWAD%{r2'aaa}@=frfILYvÌXX{Lţ߆Ŭ'S923KReqE@\!c_W[p4$"ٲO)o&dE!IuZtU f>˨0:}LdP03'%=$b[>b |Fj@ԗ[߿ͯH6)ssss77"+?(.z4; Ud˨0?s h_3U Av2f+dQ"jg 5-K'xPkevdyФ xW=Nص|S*!ӒT j6L%⠼sS$ TkTK_ xW:=x~8f~WzS3uc37vkj@Alr2D|z=bV*-J泌 ]wk%UA!Ȯfʌri(ZHX3J'xYԋco 0xn6 P~'@Mk=&=3Il@TrÀzRK(ʤmzh Pi3S9; Iriл,b&GE@vE}_?J )7>&dEA v&:zl_;*U&cNκJa.T Aɠdp۩U0SdQ"3(gO: DN}oxM7>k;ު@uZOS̠~&C%B=גS&^tқR-13%U&,ta(Dggӌ1(|IzĘI(*M|_//ד.4deHlR];3*,j;Z4-;lZ_Jf870707m׬o]5cM{յẀT '-;%lA԰ՠd\8"PvMY0`9cTY6TΌ"⪱ d>˨0%GоfZR̔+dQ"3(gO4-;D_/k <xQMk^{u#1Ud;hym\c o|-bk^{8clC x"!S"(ٯV njeczmO.j2,KA!|? n]~; f*c],JĶVϧ>Ϝjeуh9/[?ssss ?ޱ7KZb,O x"!S"(ٯV Zؤ^Gu! 2,̕ˆHkȠdp2;0z!Z+>C+g*U5X)NoAe&ssssϿ ]vT~uU U*2HDi"jOPVBQ0xD=ԃ]Af6+WY9ahX6RL$JlW5XaM$b[mNIuZl2,;OC5C&^ [WgKxI!+l:kJ'ȶ Ec;wn-_w:7;꺃R*2HDi"jOPVBQ0xDWMYHd _\}68d$IZ E,WT Ԫ'"/Ǟ:HMugd4}|Q5`a&i 5CQx預dkpRCfޠq3k)lj{yJ'h@,Zx,5wwÊf270707<xg;K=^C+;fJWAD%B|D-HQV3xZECB&C=f ء0faWW0jEV:V}M3ʀI|j)3D.Y-ިִZ>ٯVNoV#ۤ^Gan3bT xtV5C85O+%&M/YjU}R #ĢA9=1?h&sss/ ?R'$' E͌`:9eW8-姪F'XpPL=4x Kы: Y 0GvYg}K]w]X$LyhBP>IE.67070707&;'<ѭ/ L3HNkCvC}R)C CaRBr&Co]NZWD,LS.3CRe⍪I[}_n\oT>!>U炭IG5<Ӿ!>UIqN:Yu9aݳ=ǣT+If/v)zQG1K1A.Kox U" NluX';'^?o e"mkCvC} Rᘵky¤Lހ='u&VK@IDATEdAob&IπТ,bfxjҖ-s_W}~.ۥ&OHOO4JqymYvy:Ey=U5:AbQ;PT dUBv-zł\w]T$Lyh!>UFRMt<Ӿ.SFb&)eY-ިevvISe`&:Jӳ ϴ9eWh,?U5:ʆbٶK]^Qh@LЄ9?>^"AfC}Pޝ[O3^Yxlu7LƏMHD)x>:xEzvRᘵCaRBr&CoQ~uEdAob&IπТ,bfxjҖ-s_W}~.ۥ&OHOmR M(Mς<Ӿ!>UI^7Qy}]y,bdGKe 0V6l(Ͷ^BRBc(lb&̑]YR] 0E,o]>57U@?n@*$B&N8R=[!nBR 6w(LYHd X;H眨,03 Oa/ (2lFդ-[V\KM*@*`&:Jӳ ϴ9eWh,?U5:ʆbٶK]^Qh@LЄ9?>^"AfC}Pޝ[{˯|vPoYxn`}k|'qKDHAO</ճ&$`1kiy¤LހhtΉJ) 1pz,oTMڲe juzԤ r@764YMgڦ?wJx i"NUNxdPL=4״a%.E/hӚR@DAz\w]V$Lyhʻ}ZW7 7'w|Uw4w![D)g`SGpK-Ω[¤Lހ"+iF3 Os>eȥ`3˰UlZs.5<|B}X^ zY饉D;&,P7ToASU^.l(5mX Kы:ڴTa+E#,^t] 0Eߺ7.;Yx^n`qM? +oAvJi'zvRᘵCaRBr&CoQOD:DuEdрI| {Yf8DA=fa7&m2~]~Luv}dFACI4Qۤj3S"-HI*NUNx`PL=4ߘ1juPa+H ɑ]Y]uץE*t;n]S?Ƭ X7ʏ|/tXނ$BrDzxh7!)YO;&,$g2EDsNTZWDNQ ΧE`CcfxjҖ-s_W}~.)S'$Ї@}076i$4mRN)$CE$n.l(5mX Kы:ڴTa+E#,Y/KU" Aywuſ1oYxo`qw샕 )x>;^g=$MHJcI5 ə kG>SFb&)eY-ިeveԔ "AfC}Pޝo]?cZ~=ɬ p6~'ۿ@ނ$BG씈AO</ճ&$`1kiy¤LހhtΉJ) 1pz,oTMڲe V\"GXm&SFb&)eY-ިeխ0bl H2O) vp{_4m05N)$CE$n.&.3dfRdqE$j۫.$?뽁Z(Ω\~g>:ssss/ o|W48I┈AO</ճ&$`1kiy¤LހhtΉJ) 1pz,CFդǾք/#,e%H"wMY>é:%2߂4dbe],JĶ+Bl2gNJH 5.n}*OGN5th^xKŀ :RDSR=A^!nBR 6w(LYHd X;]JȂNELEw)X2丢P /[a$>e@ZSx@}`8@YwMY}SuJdi"PQ3 $ #YmW.19dzZ ;U A_=&yg8_XMNo퓐 LZ@ j3zuҺ"eA< f:.vy5cѥ`3ːBBƾnd[@ }zhMI8aO|:&,MwJdi"PQ3 $~I}vQ?dQ"cB2g=p-=494)>/ď^zX{sr)+񆭆3ehu(LYغՕb0 XB II| effu3=jB X1IoMh?y PAfI5 g8UD[& 5C`PL8-٥ȢDl뽢(v!C~JAd~4 AyC׾iRO}O/^>g>ssss/ <'ʛOGUHE)jjXȾA(2c_F  .aٰ VဘI|b}!3. yGMQ(_}_] #& 3gz4#^?j w3mۤ3S"-HI!H\0(&MdfRdQ"^Q d!?vj% Te^iC 6TơI_G݄^jx5w4;Ht/n3w|: kfQcCo$1׾yBE<ӣ(`믾ք3y=G O m+Pۤj3S"-HI!H\0(&MdfRdQ"^Q d!?vj% Tr{ik qhC_;:7070707_weqƽW"7l5,d|XMfRѯa)k%ɛR+" NLF$03Ұgzlձ0bޚ>|&3hBj,UJ?4JO`R | Df7 Iv2~QɢD4q19dzlfa-0R-)g¯-C2xWDc Bwaa!{BhT'H*\[.lހ$i`Pdt>*3sXֺw z`5E@[~uu,& 53倐y&PCjR9 | Df7 Iv2~QɢD4q1gawز_/= !E6mơI{#ˏOi 6xo 2ްհ=~frf$&m-ŭ_])À7SWH*( i`Pdt>*3sX1fQS tW_WWˆIzkB @^A0CX 4.GlR¡ >)$CE$n.e&EEh:)  c(UԬ/4z9F%ٴA&mȐ>dn`n`nॻ=u=Vqݙ]ojXȞ;sV;J*LYÌXX{Lţ߆Ŭ'O̵/dfХa3) :/ca$5}L^g !,X̃4xQN&,pN̷ M$*j qsd/3.,JDV t(kLdK_/F"l,Oa(-<,ӟ]^X` #6Gwaaa}X聙Y f0#i3S!a1Is %\). YW 2~!IvEW>=>#,é:%2߂4db]̤Nn,JD6C1gOvjo~#Bl C6d%=~|to'MMhn`n`nॶMwg_8{ްհ>wHDK*LYÌXX{Lţ߆Ŭ'O̵/df0 3˔BBƾn1Cl>|&O3#^?l rUT<0+lȘ4PT} u 4%WOՈ̰\,J6V1gOo~=ߒ#(K)i4MڐT V/ dMhiBssss/ <ؽg=W9֓4H4JRa5f#Mtf*67,f=|b}!3! YW 2~!IvEW>=>#,wHDCK*LYÌXX{Lţ߆Ŭ';oO,YHAf)q}_-n,SH\d>=Qx@}yS I5 40Lt7%*j tU8 LKEx0;dQ>Ao h^h~K.!Ȧ 84iCVR)Ȳ,}pGdexl׮ s˝;=yڍ&aa}zD$&,PaF,=DgoC~b֓Q̷'df, 3˔BBƾ7|@)$.e(q<֒S^-OퟠA;^Jj~> )]M!dP4Ymm]&1gO BӯCAf.3M&3H5+]П~=#f67070707o {n4հ>wHD浐T4@h"4KcFQ03KReqE@\!c_W> KvٲOf\~>XH#A}CiR3)+1M%ߦZ AvȥɢDZCl3>T}ʌ BӯAy]BHMqh҆R EVuO-`o|fz .뿷~ _-wIlbH3)i")U5C~2(q],JĶnJ.3J'Xvo~%Bl C6d%(Ȼħ\^;Os8Kqz^s.M}.zw]'?s0y:FpDH:?Xȑ|3Ǜ6#az8RRa5f#Mtf*67,f=|{BffB 0L9(+d jqx띡˖ᔭ;N0~>N ):3 ,jœ/BMkA!Ȯ`&G<&9W@b(Up2~(GP^Rdi!+HDFߔz]w x;M@CrzڒtNĭ(:F .L1[8U:xMKukY^+7s/u^~o|lyͣoe  o}; ОK$&0if3ba&:3~<! 5^j(H j1_s>l٧3JtȯOk£G5@~WAgU|h"h_3M, Av39⁸4Ya*lҔ' |Fuzu#(K)j_^uz~Mz`䙧?=o9|Fт)h{R_;l$|4 +NXͿk/^ܾ}j3x>oZY~KgN>M* f0#i3S!a19|ϣ)aMRY㥦BT)89烰 Xɖ}z0pJ:F,;̒^<@i=Lքr"EG.bAnSPFƘ[8%N[6|F1Rpafٚ_;l$U"|{ǖ{i/_;޴|ۿpyG8ms log˛_\~'@ϛ{jVLLYÌXX{Lţ߆Ŭ=`f5AHAf* RZLW.`%[)gr UgU><4U5C~2(q],JĶn1 $R Zh٩W' <򺄐 s`!gIq] ,_u22b|ȜA_D`6|F\ =0cfqëWuIO,~j-_5>ksϋ ?ܹTr 0ݤ NT4@=HG Y{Mk/5U J ~ȯ9]JӃ%S:ϧՉ5b#~Ef"h[}Q5`a!&5C dWO 0#KEFګV MybgO8[Qh٩W<򺄐 s`!gIq] ,y %MD fgN3fkg8j~U_ﰑT먒gw|ˇ>P: s{zw|^IQA^*fkg4a1+MT+[* "A'b&`̌&)HRSE@@_WJAd>=Qx8C~|ZX#=Wa p:RU&i")U5C~2(q],JĶn1 $R Zh٩W' <򺄐 s`!gIq] ,֦+48Wvg)>ht .L1[8U:xJ$\Hd}#o{?;O.O=Lh3 soMz̲vII3 ~`:_Dggsw\LY{23š KMR}_-&+qra-`FiubXx_Q٩>P*xk䳌 sY4V zZ@\,J0^i>T}ʌ: DN:G%/ 9ÏH"FR(f6{aю,gN 3fkg8j~U_ﰑT $Szj'`_hh7 sxgN;w7yqSRe6_=f4X|4љxٜ׆$SsǦ&)HRSE@@_WJAd>=Qx8C~|ZX#=Wa .2,wT zZ2c\,JĶV?}<R d$=-Oa&, )=w)XO Z0oE x^2|F\=0cfqU}FR%$/] <_6D__~1;ڝ{3W͊II3 [za Pi3S9; I2|#$`F@Q(cPJߪ}e^pJiubXx_QTr$ۙ>˨CfO zZ=KEpR>>T}1i ZvI.!}9X~,Eq6rB1Ġ1 -SB4e7 Q4:B̘Y`&o£ a# HL'>^o]iLYx6kO޹8fYʤmzh Pi3S9; IriЇ$`&ǗP /G~%͓q|d>=Qx8C> ul &(L?@,: XT AɠdWO 0ui(ۺ3(X3J'xPke^(Bڗ́R$a#w)XO ZYu=!oHhgNa3fkg( (wHDuP_叔?|jKF4aHzD7Q3Ѥa;??HV}Jfœ*fF~`:_Dggsw\Lk;)HL/E@R_W5O%ȖLmS:܆5b!PE gU EU Av3{\&O>b |Fj@ԫAy]BHQr93X$8l.bAAknݺ\odfi"n0>ht .L1[8CM߄GFR%$RN?c?|[s֌ TkT˨-M)j dP4Ym^ y,NѲSNxu !EABc)㰑Y'YXn|vg)>ht '\Y~L&<}Jx/P~'?w|NM; pSqS?/0-vFRefW 5Mtf*}6g{ǵ!ɴ_. Pr:(H/C~%~w]KӃ%S:PgXH0@fwd>˨-M)j dP4Ym^ y,NѲSNxu !EABc)㰑Y'Y؈w< ;%4IQvES08KX`K! ;NQ"P[$T~O|pc-O?=L`jqt7~';nO ,;vRe6_=f4X|4љxٜ׆$s4@NCI8#9(*kU2[C9N:yG4iҜ0 6 gU EU Av3{\&ϠQ>;Z4-;DGP^RԾd, ?"8KzbPژ"y;%IQvcz> @2 zkl)d7a~EJDj ]?gQJ;Is>%II3 ~`:_Dggsw\Lk;)'6֯[Vju_BK؅̶]MMط<̪4MR!"̐)+ UDfO zZ=KE%ϠQ>;Z4-;DGP^RԾd, ?"8KzbPژ ޹gҩ_4e7]8ht z<۫R;v xtZHDƒ $T#Xg_.p@mc{l`qo~NS?/0NT4 W 5Mtf*}6g{ǵ!ɴ_. PrHm#JlZv9>_ˑS0ͺ絝iҤWP)F>'U1䋘Q4\=ƬdWO 0cĥɢDI5&>T}BOK; DN6Bta:kBu\"] ,,;,<)&+)E<%7]8ht z<۫R;v xtZHDƒ $T#DgԷ鿿>I'67kˏ|[ֿP~')eGNLYnjˀ&:3>ڐdZ{/( }H9 g$ǶE@~/vIuښnLA0D9S4M4֯ a8ldTo 57ܪuA! UtPSnKEǢϠQ>;Z4-;DGP^RԾd, ?"8KzbPژH~ NXg#p> ()*8Rn<:-$]"P[$TQ'?V~3 {;3۟YT4W 5Mtf*}6g{ǵ!ɴ_. PrHm#JlZv9/`\rORR䍾I]P*8,zz'/bi"Ӵ]ǘU,|ef| .M%ri [4Q>leFe^#Bڗ́R$a#w)XO NRzUS:MSD' ^"Z[ M߀GKDj 3ٟONs_{ky[w6;;FJLY0`ypi3SiW2A$!$5HJll s_ ׆I[5:3 F:4|4U5C~2(Y(I-K%jx+d |FR e^_#( s`!gIq] ,nS:SD' ^"Z[ M߀GKDj 3Jig܏/'S47\mZ|qM9_30ifasG#e|MGqmH2=K>3cۈR.=#LHmm }l=?Eg3 lxFՀ0^A!|?,|Gz(Vs@\!X3J'Ȗ4-; AyՇ/ 9ÏH"vR(fp0^_O9.O4>HD 0N I8 $-3g47\n_;r'~+6ifasGe|MGqmH2=KޥœpFrlQT ԥg~SbmFߤ-[OLn_oTjb!k65Cʠdᣐag.M%Xa+P<R N: DN a:kBu\"|XmĠϤw~\rS)QDS08KX`K! 贐tCm@2SyxF-z,k ը1M)j dPQ!{,J\a+W8R @: DNGP^!E{9`įR$ c;> rB">=7n?g HRvȅƧ`p2HVBv7i! eJXw?7IsV8@_Ĥ!e|MGqmH2=ӥwBR]G~^~KuCB"@*pFߤ|#3)S!FՀi"HQU3'BB'('5E+l Q>AHh٩W ʫ>}Q7_H"w$|&Dˏ/Ē"ڛ/O4>CID 0N I8 $-3gTG+TIx78[I&,lm12CuLţl6$֞I*$q.)THQ)*@WgT:-$ ~.r@ iM7):r|̫nTjr4A!Ȯ^jץɢDlkJgPc(U@uzu#(K)jlMzW)pzAI9!⍙Rǐ"DO,R/g{]jN IHx $jlS,r=? TkTlapZ/} AvRf.M%b[W>b |Fj@ԋV}W}H}9Ш3X$b7;H L* xW-<7Xp> %)*8Rn<:-$]"P[$TQ-|\:RG1Is78W?Tkh̴lkC3>MK"fM+3UT dʃB9Յac }A}p(Onb`1M)j dP0ui(ZRX3J'xPke^LЇ>~ Et|X}ĠϤZzor|҅Ƨ`p4HVBv7i! ecOC9&inW޽M`*,l~i Pi1Sٜ ׆Rp)XL_J@|_R]G!sAOR8HoPNbSb .i"HQU3'BB'^ZʢDZ qc(U [; DN=_>GFn4XTJ^1R?ЅƧ`pdHVBv7i! ebSLi4'@0 Я x3Rs]Y8Ea4(փz+R4rE@O\ ?T9 4& QN_Ud޼`0P&b PU3'BBdٲdQ" [BƱgO-3-n| D$y0=ARANHĎRz_,eR!%,DS08KX`K! 贐tCm@2SyxF]o晤^;~?:a+`.ұlQzPS$<\ 1FN6~/9ږh?SN9-yitȀeKDfO ړbCϖ$i5j"EtmĠ[ '$b(ݾ+P RQ.O4>cID 0N I8 $-3g4{~ז_ȧ) i4ofa;dR 8@IDAT{"+ϦePL0Eå`3h$JlW⿞m3eӒ6A;lqO[ v"h_3U A> 3BEdQ"‘ [ sdU>٦brHC Oa?8HD4!bF1(V* 0J+`+=mʅ8kɈV"*c xtZHDƒ $T#gv}`3x!m0??v~u EV:V}M3ʠZaK"fI>ٯV=S۲?gʐ):#%/mnw; #.i"HQU3'BB[ŵ 6V ԪID.a{n[n-$Dgr|`pHHVBv7i! eBg*>7;hxǯ&À-]dcq4,H~Rjp)X &wF Crj~_WږhMY+{&V)֓OOfO #2Гrq9*U-E6 -<`>ID.a{n)? +8٠\8`(P%RUp eYow{{zF3#'H lBLŎANcJL9TNqI KB  Ǽ5zLkoڏާY ٻc+tM2rwZB]#Uk&[&phuǮ-W ,Ϫ,tu6d)3@H792AwOzi'*R6m]M!zfL)2ST&}&~`e o'3Yl6s^F[4s22VmBbrZLޥxr .∤UlȌ@+PJa+ `̅q=Gld0_Gsm*%Y{:{]0Ԋf"iC" } Y 1'MNLPӱg!ʼns[`Wk}&쟊DORW&)*{ &G8 [2 "fYnT *M.Ȩc&ZAfŪM?3Ĭ KYE-WM#8ޏUlȌ@+PJ;mczoOy9VH-l+tM2rwZ]O /_qy&&.ןk[+\!{ِ{|" qRHJ<vup[ZK^ܮ#RDORMI4EfZ%q  *&"JQoAdr+$bANLFrZ{[-@T5ed6aj'F3Ĭ KYE-WM#8ޏUlȌ@+PJmu5M^q`̵q=Gld0_Gsm*%Y{؎'^,6J! Hf2TA-dR (},x8 V+VN&dT[ Mm,IL7 JHfYh%z4 )ZpGTq؊9V" ᡔ03p؝;ʦUWOy9 #)3[2*WQiaJ$A eE:p]*4Jc!_{ C%ڃ0^ۉ 1''!-IE-QS{erE] Hf2TO%#t-dR LLqANBbڤE}a,*p\F6Y13]VF VLYhޥ,}" Hj~qG&6ߗ9+AH( a4(C_žR[?c#Q/oMm+tS@RQ[Hm ܹ2 H 7 ?&r]sUd=l f=~tS`(eb=4CI)n_uKyd8CʽAe|Ndh'V1k .T߀&MJ3Y9ژxڤE"HS[;ԘʝtQU8EZw)KHZj\đ͢er;fFJD bC)ag %:.Bx]`A]zDf*WQiq$(qݣ,5Mmi7؛ => HE kHQ,} Y 1'MNLț*gDERΧ-rE]vu,r/-zjlS$3 U须, BTC4‚grjRkj"eT[ MmPcZʏz(wb]F VLYhޥ,}" Hj~qG&6ߗ9+AH( a4(amp[/.=hRzD**WQiqG$(qݣ76o' ;*= eNc 3J H*WQiqIPJ9`Zڂ7%o@={B8x 7E'w9*pu6dE3@Rp"J儙\ęfHq")emk.yr:lG_TvH=5O) *fc$PZhФ $>Se}S^*P(*ijkR~C6J gBS>u&$V?ո#EL~\BBAlx(% 㬠@< 7w|}8|Ma߁c{x\ϑ%=<*WQi)È]IPR$Ǥii Pxpp`*4SXBvN"P^EFΆfH PC08 )NT$l:-~-W%/]nWr)ƖI8E2Zl}T64h$gY& &E.ʨ2(#,Q-XMw(*" uNw^EZT"Llub6Ȍ@PJYAC2xZX?-w_m}\hAg4s=G:Uq6ogzfǧ-r/- 8k_~aͤm7Nk",} Y 1H?)R9a&qRHJ<vupƯvE+囟G_TvH=5O) *fc% xPZQhR PjA>Se}ӂR6)rQFyjLKQNl(*" uNw^EZT"Llub6Ȍ@XHQD(V鷇/5 m5zD"R_Qo;xHbXp|ZbjDG&4&]HB^b!#y* đIEa=#*꺱b q_V:npI!!x$"O3;A'*(loQO&Mȷ,냜͔jڤEU ݡUed%? .#%X3Yd)KHZj\đXFR P+ q g 1jڟ?}\.Bsa_ŏG)||}H2n=Ah,)#Jfl̴?H·]_}M+lvNt.x2lِ3@#0$˩IEe' *:Xvup"d]G6ˁ/*;K ['Lvjh ,TC43Y9 I2* -@4-GޒULw(*" uNw^EZT"Llub6Ȍ@XPdK8+h(PVwm=~4`=_?{~(9D05P E{` vUPt dP"Mޟ:%+X7v|[MƞxOH.\.=69{@xb3@Rp*X 0&qRHJ<vup+9)r:lG_TvH=56DZg͐NP"6UQm,9D` LqANX6)rQFlҋ1kZʏzFLwY%X3Yd)KHZj\đXFR P+ q g 1jDXq=G&o5u 1#˾63"J4,؟.Y ח<}8n9~n+&0`}EE(b )8B*0&qRHJ<vup+9)r:lG_TvH=56DZg͐NP"6UQmBI:D;3Y9-ij"ew2-@Fed%?i##7e]`d:z;/}"MHj~qG&6:b1Iq{dFBBAx(p2%4(lS+#V]p@7vudٷѣqfU$C %8ҧ7޹xrKg"w}:lYˆfEI)0 3)3͐DER-[違7 ԊTT+[cOޗ,gZEl=BI:D;3Y9-ij"ew2-@Fed%?i##7e]`d:z;/}"MHj~qG&6:b1Iq{dFBBAx(p2%4(lSamHC-.@ $%v4zT`:Δ&F2pOn ଂk> ǿ^Z8yV<@h)p pb U֣*J /]HI:D;3Y9-ij"ew2-@Fed%?i##7e]`d:z;/}"MHj~qG&6:b1Iq{dFBBAx(p2%4$V<@h)p pb U֣*J\p/`ِav)grZБS &E.(#[4uӴzX1EE:`d:z;/}"MHj~qG&6:b1Iq{dFBBAx(p2%4$!!9)ˁ*sD&_`5%5@$᧱݄Y`:Y % 'K0>}aߚӦ^8~4O "<{/`I^'7EqIe Dm]|j}u*ޮNPxezi 8ጳ U֣*JZ,2 .LqtI2ʻ M-%Kf˺(*" uNw^EZT"Llub6Ȍ@'NzC!.i(рfO_^{p$z`V ^cP<DRM]%d%63 PEYp Jc;:}ZM=t=w/@x$ㅊAI`-Pc\ęfHq")eV vuz5Ŏ[vS## 4E8#T)*dQ%F-t$ ax8VqfzM\Q MM^]4Ӓ?U`™,PG{iBRkS82QH#3rD ӽ/=4sr*ZXA oJJ=*m0gJ 3 0 'KpVO-ow\3}+ &p2~{EO*&6C $GCmRpg!ʼns[`W[d$!u8^GsxeX5,fY'V+[(6j! X6deLq0%^LGu{]e5G`毁p.rot"MHnBG"(Nmh'Ćd}AX$ͤSXpDWLBRxUBVQi8S`InV?-\ iGvzr@8c~@6 0ZIj5k=!.⌳9DER-+2|mJ:~ӓCxe 7%(!!)̐NPíGU@I#X6dD~2Se8d3}&9[A!@OvQ+nxwʽ}4!(ئ#i!/gVBAtea4fO_c}=8]E0uS K<DRmW YIG Lia&aYp *>q+Ok6E݄n"\ũ#-6Kga,M{iq -AX$ͤSXpDWLBRxUBVQi8S`InV?-\ )A}*=//ykhc{{\ 0>f< AG3Wp0/XIEI=)&(pn jr [΀Yp<ܲCxpfR'V֣*JZ X6d̢`8˖f]UI1A!@Pc2p3s-=wʽ}4!)^-$LY9v" NlKޗEL?~9ȎHtM-T,E I]%d%63 f%8 ѧxWݾ@(mm;?|_^f'7Eq6GqDl]M>`C!3Xka;eYׅ,"I'V/[(6jP]! LqbfrM /rD+Bt2^o'VL[{`SGӑ?Z&$d n-$LY9v" NlKޗELyr*ZXA oJJ=*m0gJ 3 0 'KpVA1%OIqZo9Ri66k;sp""E%9 & qƜ@P -&2|'uM>ls|"kp·X3NP"VUQm$x/ flE6ӗjRx#u_dz;v`*޲:z.72@RA@ Y DҎX R%Smh7(.DT5zt0sr ܃#ISP־c ")񶫄ѣq400ݬ`r[gDS8T{pp3ɰ@(mmN_x}4O +3ipgX4[[`Wcd$uF V|؎!p|"kp +LV T])c9xvL{E,[nf"G R.?vbTe6uN9ѥBFH*$ dHSقA6Qj`쓐cѴ "o( @Bh@3/F=8=D0y1 ")ЦЮFJLǙLwmU}L S+G׾n@(mm8v$:;<%~\z\#T0 =jpgX4[[`Wcd$uF V|؎!p|"kp +LV T])`k8V[d3qc;Ф"Gt"(Hw.?vbTe{`SGS q $D6=ȭOAHpbC_\2N ,f}*AvGnjb)HJ*!+it)M0$ |7+؟.Yǔ >M_@'.t}?.WdG(YL8Lǚ-#'ɭ3Zg_k^sg n!- xHڑg.ZEm=ڨH23",[~al2%/&9KJ (HG' ۉSzfmq'' .Հ#W)x }=bcdʷiO-.D)A3/N=8"|ՂՔvf8cd `H*+؟.JDS8z[Љ>> P)ɁԤ"ΐ Rl j d/&tFe/ 2 pV U֣*JZ$#;yg )M$/rDALPT'یXzsO)}!$%d!UQ n}=bdXpkRq gM AHHN:sr܃#Q/W-XMIM I}=ai`7a3F&a2mD1%O ?WO.&&P?H$w݁,YL8Lǚ-#'ɭ3Zg_k^sg n!- xHڑg.ZEm=ڨH2Iw9x)˖f]_7钅4*l3b&`>q9N9] oJ@RBR. \#JF^)q#A}*AA/=8"<ՂPxUBVQi8S`InV?-\ )A}*ckʩpH;/^ d_ M3ipgX4[[`Wcd$uF ka kn-;eI;TGU@IF laѕe/rDҍXb 'یX %C ^ϖm*OnO\yGRz@IȔoEӞ8=5=j4r b܃#JS P_ $%v4zT`:Δ&f`On ଂcJGXq-N[N+6J@*'phݛ }'< PbpgLI v5DOډQ~M>A5eP"iG"tj1h Ɉ~ndtJY0S%L/rdbHa+0''یX r[M)!ޔ ā,\A6BJ1*G 6"8 n R0G  ЃpGZZ[A JJ=*m0gJ 3 0 (KpVA1%Oe7S' &&p5&pppz'@ap.8B9LcLf qA!@Pc[`W[d$!u57'qBђ pV U֣*JZ$#3.FF羔eK3(mM /rDVWd6ڀVo4z()!ޔ ā,\A6BJ1*G 6"8 n R0G  ЃpG02(E0 ,<B vUPt 8$ )أ.\bwĊow+u*jwfmWǏ||'q'x84&'S0gBfF%ў43ɓWvs-;)A Hgl,ujըnm=ڨH2b]_y@72qaIEX[Xv6#Vm@SGpi.7%q )! )pDPGRJp%ł[C8̑ƀ 7G~9p{3/ b3!G !0@׊=mH1%DUIqpso<~{}#k6k0=W* iR0g)Am]n2/B.ȅ#8n!>V<@h)p pb U֣*JZ$#6Tx FZ{$+x!txaƋXv6#Vm@SGpi.7%q )! )pDPGRJp%ł[C8̑ƀ 7G~9p{3/ b3!GMb:΀2 tX)أ.AV<@h)p pb U֣*JZ$#6C[{ 5>,="G7h"Vd6aZM/pi.7%q )! )pDPGRJp%ł[C8̑ƀ 7G~9p{3/ b3!G r!0@֩hp j@1%O^wo~F|[RШQV4cG;W^_ڜ\C5Lf qA!@Pc[`WJnl޲NrxeX5,fY'V+[(6jЃs0VdK3ȌʀIEd'!P ,D'یX r[M)!ޔ ā,\A6BJ1*G 6"8 n R0G  Ѓp΀^(P6`.LYE%(GcJhnozp}IRNx^]@cAG 83EA!@-@[U Ruzd(Sr@8ogo_"ƶUQm$'%aP ɖ AIE 7(l&!6#Vm@SGpi.7%q )! )pDPGRJp%ł[C8̑ƀ 7G~9p{3/ b3!G r!0@)hp J&d${|7 w}fػ}_Tv ၻ;~x *8L|( l]J]#Fi/ߟh±E8}j1h Ɉ PR.Le.!,0i@Bl3b&^ZXo4z)i I 8B5 U C_()hS9"PƖG/}!=p, @셂9@ EQaér$ u Z?-\}>[a3 g &p#|ebEwcĥ`JV&(vv5:v+vAJ޲NrbxQ^7[*ǵNP">UQm$J .Le.!,0i@Bl3b&^ZXo4z)!ޔ ā,\A6BJ1*G 6"8 n R0G  Ѓp΀^(P6`.LYE%(GcJpMk5 'E p8zdO"N-Վ&ĥ`΀ R]h_ ۭԪ)y:=2ˉaFysHVoA#tV"12:AzTE Q dĆ^(0FdKw d&u1ƋѭŗMu،X  z|E9N9 !$%d!ݎo_0tb->a+$+BuSϖS/b;d>%@SBF5ꭺ= +'/]AEITK΀S}P0#= l O'u;oOW'P;B>Xӓr~ mn? nyHDZ8SFj66f"C<~!~'qGER0g'! Kl]f2s/.Ȇ#Fikyc4a6gK&he[(~j % 6z--4&92YH` A،X M/pi.7%q )!wi +o?/cm-hNy=Ҥ_^s\/G'|χsN3΀e}P0#=;^?!f#xzr-5>N_#Z&&@xAʥ`Ψ f jt/'۪x:=2ˉaF*^'cp6(s |֣*JZ$#6BB ` ɖ vIE`; l66JֲR/)-4!)\v:xy3= R=;7·?x$\] ȯ>υg^^8  X sP1ؤa#[߰f-lhhh(@q%AZv)3BǶF7~&[9uzd(U*GO=-lbQ&@GU@gIF+= JHt@fzcƋѭ::m6A`kY͇pi.E A@ґy"~?Cm 1#WkIKV^>/|lO05 qV<Fe` *^>C8z 6sM}8;N__CZie/o~¹sҒПC!|J9hJ"qz`s{%ͳgsXR0gESj[`Wc{~&"n[YN 4W666k0ۿŀz 8+@A!@-љĪx:=2ˉaF*^cp6(s |֣*JZ$#VD \[ 3}M/rDF;H30J[2Vmf JֲR:OᔗOi% 5?͇/{铴stJs-wz}ыgW H=?# ( @셂9@ Ep} c7ߍ٦499kko'͢ ]^stP21,XR0g%d;N]#Fi/=}@#=[,™8lQ%O-@+b<å .86il3-@XC3_`3b&ІB^iZVX?E& LtM?='Bz?|L&0g_,77wtx # t/ b3aӯhhQ%Y-@+b/ lx#5)GZAT6a\q[iZVŕ8N9 }!@_C»q Z& K{+|nO~wgu}P0#x;<=ѿ ;ZݧLQCA!@+ N]#FiU9:)9ngK29"m=dtXxx2[vn7i# |͈U0̸4`-+JRľ L׾z=vǵx| nK~?'OF-"T/LZ7Bg(~Y~ߞU \ 9.<6 d-&x QA[`W ?Yuzd(U*G'8=-lbQ&@GU"8 obX!][ 3}M/rDF;H3*_`3b&+3n+ XJ)!/w ߹5oombȟ/wSvdQ_fsPqp:~7ǟflVmhhh8$=&dՂBl'[`W3?ۉU Ruzd(U*G? z[,_JJbQ +ġ iLxƋѭ ɔe@IDAT6ڀViZV9N9 }!@gSvhq pJiIN=D1#E`*N>Cv=?YVkhhh HX_t 8+A!@-љĪx:=2ˉaF*^cp6Ĩ#W)D u@HXxx t/ $4iR-@H_`3b&4`-+|q"u e 2 b$wvZ[FiZzcp6Ĩ#W)D:d$X,U&9[uekZkYWx84" pab[ῌoom;q@tػ7~FJE`*>Cv{|o{=Vmhh]8]>=_LQ6 *m]Ln'V.H[Y8z,*'MH,!X3\bCbQ +DtiLoox#x]g}͈U`mP4`-+ $wԾx}_S&m$ U3oW:.sj6669#wX|'x"(FlC]*zupgv;V-U-_8zz,=c.ɷ pU $UQ2:X,U5_ZMMMƙ#{/ԶQx MRU;^"MK=IG}E8FT)CUQ2ډX,UCpw2p?Uvo hq{K=l]\zhZ]A_ϲD)2XUQInmTEh#cVq#m;]XzVƋf &C/jP794`-+"|JM@{۶rB-n5H'?NTWy PŻ9<ުmmmmc"w=koHZlWv?Cp{`}Un Q㌄'o@l'ÜKC')e$"NPb6^DPcA j 7(@EMݤ"G!A،X %fYhzr}N_z|iI0ߖ.(<Pl8ǎVmhhhxIO`+>=pON '(O6Cٮ"Pwl&aǂ}Q!Yѓԕjy'V =XN>xv rn0pM/rX` UMyfŪMډ 1k9@Rڌ}ݝ-a& x͢ޱؿoO;+~-oh;1k7q;':l_;eDORCJh@ ;AJp=XN>xv rx@Zek-@-c& 1k9@Rڜ'¡ MF/~^<AlP7>`j@@h?Y9F8ܽ౰҂|U2;60'}Q!ImF$ET5DSqTG€d B R<XVxጪP䂌kQePFYj 1k9@R#nY{Qږpx>CpZ}U dĵ \ :'Ob c?qEe.SRW hr:AJ=XN>xv rkg *M.Ȩ&-@-c& 1k9@Rvڴ[on*p87;VpqlSP[M`%f&s&p0D`ppPTt=NȊVTijD@Bp*;A\`$;X!5 wPN&dT[ Mm,IlX ȋ'3Ĭ KY'iѝܒt[MYl;/S` +-=JAkhX_o_y:\ xo8ŀj]F d4Sd b'VM€d"ٽ,P<XVd;@mR䢌jkylXՓfၖQw);?mf&pO`3 rJ&p0ik'}qqxt*aAN)*t*\`$;X!5 ţ?2E$HSޡe:Ș{_!f-gxe]xW ҂{|{InXUѣGo3mة ps ȟ,34΃;CM  $$'SHr'Vjp{ JX1`.2`ϴe_y墌j[%HSӴzKV1p&,ѧCIb-gqGsyW 1sǽYQ_i)otm8vX v]J y>xS= I'9 U _ Z'c\H/bp]^n)ad(#,Q;.ˣp&,;y>{˨5Vkؽ8\ϋFF},5:\g V:͕z660tW,?gYb4΃;CM  $$'SHr'Vjp{ Jvj,2ZiltEնF` .MKQ/h.p&,;yw5?%k(&'p&^: ]Pt ƅ8Y=Y6k1'O^6GV%̇{2~ ~''?gi24΃{;CM  $$'SHr'Vjp{ Jvj,2Zi|qEնF` .MKQ/h.p&,;yu?D;VxtEx(:LtM8 \ WƭΝV|E9K24΃:CM  $$'SHr'V6uTA Q dĆ^( FcP\es^V&k;&2*/F2(#,Q-XMw(*" uNwޝ'i<~N ,{K<1a:&V=D{љMjN }޽ӛ4f&'p(}'/ȧu}uCM  $$'SHr'V֣*JZ$#6BIMIiϴe};He2 lBMG]tQU8Er{Oyr_ 74vYk&'B8*=žk1O38#.}/\ [ 4 zkkkԩSᩧZuk5 X{!~6ʧuЯh }_vH)AdX3C6:AjTUQm$JehJJ~M/+gSmȩLgAdɏzILwY%X3Yd)λvO#ְZM`NW¹ԯ?   0 2p&vvχe|1&& >}]X0Ϯ@z'@8/~=9u_)=sEe7*@BW XL҃NP"~UQm$JehJJ~M/ #(ن ltMgtteQU8Er|cmO-}Xkέ&p}'υ?2?|ʋŋg=S ~??OާCμ (&& طOwiVm;}"CG|1`5i@wˉxS= Hgl,ujըnm=ڨH2bC/(z˨єL^6OL.&2J![2ed%?Y'0eY`d:z;8'i{>{/w5VkN~~v/=^8S>{/sĀ98v&pm&>p-\fKF|pOʯH[v*d" t ujqh Ɉ P-FSR3mz|1 (ن ltMgtteQU8Er|cmJ?j i9;'u /:L/<ݽ3/VpX4~lz7ޢ665njY76]7'n=<ѻe)B`,™@P'V[(6j %2j4%A?Ӧ-l"dr*(YPF6YQϺ؀.Kp&,;;'v&pCL3¯}?EǀI_|><ó_xt#ɇ?:MM`U _@@?~!~? 8w[vE ,v$,YK'Vc[(6j %2j4%A?Ӧ-w/&2J![2ed 4J gBSoܓ}pb AZ?pT>e_|!w!$d;}! 5MMM;j͸M`7N }{;ix򚽑[vE ,v$,YK'Vc[(6j %2j4%A?Ӧ-mXg-hrQdG=m䦻™,PG{?mc)_ճ [M`L ~}lEǀ q6.*< i[&_~rO/㜆Ԣ6?ǏG^FC.ƞxaO`3phEEҎE8+p*blQ%F-@z$@[F4gEb @D&R&O_O nv~W٧A.tQNߏ xҗd`IM|"} ܈؊xNMW V<@X$HXNP"ƶUQm$JehJJ~M/[$fې/ "OvK?m#_ V-lE'պBl8L/<?}.'OBw!MNԩSWG3o؍Hx/cw?xe7B@pB,™#MNP"ƶUQm$JehJJ~M/[$Po+(L>e\ g` CG&?-/#H:b1;+9mA~z8Z3"'•~ @aڻdn@p8aʶQ~ZZ \"aujh Ɉ P-FSR3mz" {m 't6B(XNw '|d} ?X-Hݒ6; { LǙhg;!b?<>k]8|&p5&pWcqI[vEK ,v$,YK'Vc[(6j %2j4%A?Ӧ-ې?@VBH 5];NYCo@FO:b1[0__JXk`N?׿~Y} LǙTӿ CO嗻/䕆 \ |Wuسgղomz"̹@ܲC~b<@(REp& 0 U93A(6j %2j4%A?Ӧ-Pq@X 'vݡ8T@T( qG,f  /~_|Q7M`gN΄t@|h`:$=>pO/=srHimmn{nkװuje/PǞ-;d YgBZE\l=ڨH2bC/(z˨єL^H\ H}˸'i*6KwʩK"8Qń QQ<ٖIWGφ65??.\OHvGΊI0>pOE+޿3W?^"kT66%xk^חX4m =t*y'ܖB6BKkX3NP"VUQm$JehJJ~M/[$.ކ BL>e숓IBg+r)].@RW.ʢ#W)%qSO>?~i iPY^ ߵeހb:LXdt/>=B3?ԗ ݒ66UL`mm-y睫jm7>`~'B6BKkX3NP"VUQm$JehJJ~M/[$@ZYit>V5e-"8kpʽғ҄)D#߬٢.nO< ^s4iFstJs-w-{l7_Cq =|s=.]/g ^ܞUn szѤ5l?5Gf~;&l܉MQ:MbҜ^s4B"hn99Hsz;(]xO#WTFO4M@XϦ5T[tjYGU@IFl腒Eo5Ҡigmr - al` `)MVq[𻐋8B5CيIm?z1O?.^yFVjjy"ܿrwtqsA2(o}?s!prѪ&ѣEFjxN`л r!zL"etjh Ɉ P-FSR3mz"mX{͛'  YdatB|gCa-Nn3_ ? Γ5Z9 Pb+O=y)|+/we++omm&' W\Yfm7^y'p3a& ,w"tj1h Ɉ P-FSR3mz"m H&\z!S&)ӌ8;b1[1I1x䉋ቧ^ wm]0Jf_ gΒ;m:0ged()|鹰xػv }bH xSm6ؿ O=|lx L ]x˞>?\toy 4!^a,P*blQ%F-@z$@[F4gE.  2MV;PCLRp!qDwbb`#3r.O}|?En8"U\3LǙP$C@ޢ> wu+ǟ:VhXo.g}vEͦMƜ@zMUw.8OK_M@X>e,YK'Vc[(6j %2j4%A?Ӧ-`B.  2MV;PCLRp!qDwbb`#3r0=۟|{'UEm+@¿ c۽._8O7dml oE8yL mXN8Ν;{.}ι h \N"B^ϻ TZ 0LSTGU@IFl腒Eo5ҠiW id _&_z!S&)Մ8;b1[1I9v=y~L[۽ m+KD$Zb:чrڈ`:Fy$ 0弈_ ]|~.eJB] s! k^Ѿ!m+@Rn)<3#+Ҍn tqa3O ,w"tj1h Ɉ P-FSR3mz" !mҴ|`ɆKOvC 4I{c\ꐎX̶L.9jpFhΧ΅ݻN-nX4K_ͿcC _|{*l䘷LS΋iO8q-җ^«qNo3nHj&w"pҥ՘66tݯ|aNnIXNP"ƶUQm$JehJJ~M/[$^6M֐']u8-U.CC iBRgbYȨقAh'~r/ Oş 5ݿJ"BK*'˿H?Lx3\n:b:QKRSzmaO?_W~%_֪m&N>~Ny#lkү?xbsWDy#"tj1h Ɉ P-FSR3mz" !mҴ1?#cQ*;e3qL)^ iBRFg,Q&3+N!CDlyKM=Gw4r}WW>도?{˶J||CS>g׸ܮW>޵}gծ[眾uWcq[b:eO; 񊑼 G禿WG7]ߟ'{z4'봯o"N|~'|Nu}qM=cգ9^_G|[twX3,hVUQm$U^( PJSRJNEIcTPi:C?nl輀*K4;jxvljrfPȈIdiLg-dV%76DAp#ڟ]KGףݣ;)Io׽^k+g?h{KoG[&a\8B MGtxW+=#`2112ś'/|@IC8s8sL={k_>\wA7"9\fAh WBIVRw,MC<-pJ&_C#6;0޲z&72@RA@ Y Dәf D8wA QMT}uu_D@ _J=M<IiNr}'zs 4ݹ#?6_x[c:DTabot?'+G>pqްs((QORxI99=!o X J;5a.YEzTE Q T+z$@[F+MI);m&!RYCʡ:oHb`^~S2Fm_m%`:$>ØLRQŵ><\*tb{q<}w8=>)0mŘE@җ4}򓟜x)W潽zt=qP^O ͥ^m7<zӋfYЬ"˫Q%F-@R%腒o4%Yd;8xHe %‡IWE l3C0L^ qb-C4!)d1pDVLnRf1PډZt۫zGmޟ^y{q#_^|)/}+LPI@FOBWy~3\ ]qC"'\?]~Cʃ*/}fw3m!"7|0 GH&`f%EcE8̑Zwnʝ5Ś䜗ĸG:[[i63#I)$~{7 4DDRTk+~|5nxl0"X߮Xnݺ3oe/".;2~>E⩚wczZX̘iqx}ןOM BgYЬ"u=ڨHP-6lxDFtM!UZgj}`\Ks䈤KLnRf1PډZt}c/L~3/MgYЬ"u=ڨHP-6lxD򡮨p@&C z`\4ٙܤa%EcE8G^u8.d<=8DAjUh8s0ȱ<-ߛ.\|*qυ"iRˉ}O;_e+WD`ojW_o>3ŷMzVQ'_ 裏N=nz4 ?8n.+O "9lfAh WBIVRw,MC<ɇ Qr@fMuBуvq/Mϑ#.a3I!z@j'kX+%sx7noǥ<=p t[ͷm0gQ0 \6Q -{(+AD uE|͝;7;˵o0"Hz]<o~wǿn|z?n'iSOjIycmc ,7^ 8?v+ X ?⌑90 UħGU@IJeҔfm!5H>T?PYSgj}`\Ks䈤KLnRf1PډZtTiz*͚:#T_&#G$]frB0Nע㱢WJ戣1ܟnJq&@P3`;jU`é1$ 0-0:Z7FzHaSZߤXDD* H.$m_yτ7 ۄj FhGL #G$7)3[(YDp-:+za8J//AnR anPl ΀ZpV}Zx r0~"޾ KV6E|@ 6F.B+V:ޟ^~r W\tiz'7W< q 87§ϳ8st*b\ף*JZJ\ % 2ZiJJIi6wp J$*ASZT&لj n0.ϑ#.a3I!z@j'kX+%sP>#Zu:0A.gM-Nw|>-S9bI`Z@k?mk1]We"~ gk1@ vlcQ_W~ӷ}}r> 'ÉY8 7?? 8sLh4q](6j*qE/(xh))%}"$1C*k(|M)V70Zj@Bn" `RxSrG &Ū1PڈZtN[p?gy% Q,ş=ϟh0k" vEn`G _}ދIi⁞^=$"y૯:ݸqc~W ě ^&3^=cjWG.(P=$ͭקǧx1*sFu=HP-6lxwǐL]ZB>YѤNXv|خ1ppr(EPڐ6׸CH %OLϿT<&0&0&p'ps ?s(u=S TD\KIi~a[+CKl{`=2ӴWjрlԋ0T.zت )lsE۪?|qccc߻S ?=su=dm'kxaВGCIKb=΀ZjPL}5z}I `Rľ zE8&`f3#F-PWJQ)0ЃP!#xop~tr3+[ͷl8#8%T"6R^x418mqM΃?SjHA(m,3mh?u[a{Kcݣ<&0&0&p'07#Q%k'\G +Dg<:6JZ\qBT@0et.aLJiD Y#lfrbUG[99T R0Gf T"|t`S΀J-NW ]x7p*Gq JLDp=Cmo#^ܯY{v8x`e`TDYlom n+Y?7>hˑ *nX!:/i~EPⒸX3`"Z&rΈ6`lѽ-tArG*3I!Q R0Gf l? 9H9|ԂO ]x56p*GjG=א%T"Iel{l%K7e:\C;B a"U(nc#i%S-;7 113;w{Q*JVEBKVw^K_%-.8vj!rE`>0[to 0]`\đLnRf>3r@m%*yE8'}MOX||-/JβTz9 ]x#sHS*~8v[#S7ڤ/\z6X/^:δq(B8Дeq$y0PPJT R0Gf 6~߿0?~9"s$EPwm|m;rĜĔJyG6ĐA6C^BMkN K4Aյڐ6WڄW`]r#8?Gnpt"U+nX!::JB~(8d9"0¢)) :V aLBS \đLLnRprP@J^+% #J 6T}@:k%HЋ`ͷ xDN9$A)o{5JW@-r6\/~UWDdZlom nv፛__9~(Y)kxaВGCIKb=΀Zjh!ovtY5Q+2)zMY&pG23I=Sr@m%*yE03'n+{?\ !u9 F"j |%T";JR+q.onlPB4ptEEPsx@?bmkC۪[8?]ڞ#כws{*JV \G +Dg<:6JZ\qBTD 7ZxcΪZhxqTމ㱢WJƙ@IVW659(9 EQ x4Zp:G*%T";*Fj 03"ԭ-X_~(.YW{lc3lsE۪[8Տ}K:ccccn}ϑ&>9ݽs#QUQCעQp }%N+;/ꇒz;9"0BA3⁎8IaLx})rwt<\ϑ@Aa$].wh1ǁ$(1Q[Hm F 7.2[i?m'ty,X66Vmh?u[u oG;cccx Y%+OעQq#ƀ<:ϑ#g;htG$(1Q[Hm l~f''v:jla3慄W3,jH6Nm-T _ģ2&;ѳ0JiZt>*!sВGUy Q?RNzZjh:nT+pF0EZi@RApG*N_^TF䛾Эip,㻒_}%vϑ" x4Zp:GGR`-Mbvt;OK^c,{ԫf*lhcx+OVa}GmL`L`L`ħdl!zh"GUPҶ8vj!rE`@ ^Z3:V 0aF9MJu5DMҪ4#̃qf45>IVSyEWE{B|raGct"jvaF N9$A)BjK0$fka_c")gG Dp9A דmh?u[u ̐nNnT OnO_+-!h GUy-Q?$. ةȩ zp6A)iX3Jsޤto<Ҁ1Dq4.p 4yt[mOg㛒]ns$X j!م<-8#P%T";j{ -pŋ+)XOt=f7ɱ@L5!*nNaqm_ 2Tz^|OOO(,@$ytX-a\ m3qBTD =@ &Q8 qM : uҀ@#lDma?9 k[ң'Ÿ1es$(EPvlGwaF N9 $A)BjK0$f_㟥bĨbQFw uܞW\DRm)lݸqsFC6&0&0&pv'J?:x~2ڶaH:qH좏QɈZjIhH *a'sM :4%Ҁ\Eq45eɣj~=sq=2]}y9NP% B9WX4Y * rM :2IeҀc\ȸOhZGՆ{/¿^}o L怢M]т9bIPbJ% 7ۿ0]y %<-z⳼QFgte/~DoIo} T~v/]_aQnDcccg쩔q\?"k*%րMIrTI0"J=bїH`3 *T< AU8LgЛt* iI!S#zMVyLVS6z[k}x:DG3waF NH; 4 zt_bs yFfyݣ^-lZHA$7ގ/=n=?Og zWK-[111vq4pJ1]H\ìo,7:-atE2T{Ţ// f$T8( wgnIABvK H"E 8? ij@GՆ{@Y}|IKeQ߅<-8#0%T";j{ -J&o$g~Z5}YįWf _.HK>%5PkD26Hxy=S)1< +慸/9 C] %՞rK$0 *Nl 4ΔNء7)TK+$T"`GK<63eYMG⛧I^{a移LmQ| 5\ہGsD^+@%c1KG?+?@EGb3ezdqt/NSPO]v]eqߟ.^7y11o쩔q&"wdjKx 4%Pr.NI0 JpWV%EBy 07ȢΔNء7)TK+$T"`GK<63eYM#^ tt!BK_ ?ԑwF N?V Jї;j{ s!c/<>]7 BVٸY^W! 4.HH; ?{q;a?OS]hY) >3~;d[U_n'/,`5wr5ږɳт96HLi &vG…ca4pڷEG:z^!L+ȹ&@pf [nxSBh?uumC?=~&ٟ٘{*e@VS^ P<XE!H}mQ0` B!Y R ȡ7)T'IJ qG*#gYZcf䠨wVhȕq ɣj~=ss=GoVv{ӝ׿>* CRe~`т9bĔJsGmo۞;;owzWĵk <,{ԫk` 0W&ߗg柿p. .?{7 ?r f]``O> Pw٘i{*e@\_ 䂵mZCYhr-aB2,K$h *7YY,:& ] i@ҺvrGV~&.r" ⡔03GC<63<s$υo+_wvp+aF@t}s2]uB?wn/!Ubw8$9llN>qGrO40&I_gOˮBP *Ph8Δ.2111> 37^tx_r@!ܠzVA8:CoRЩO*$"pUF'ƙv4AHpa9 ma?9灸#GWjq%U=A Eot{*ĔJsG-LԶ?l\CnSl:hFgK Wp$* OԼKdifgˊ+y`ccc'pxQ Ȁx"8#72ڶY*e:e*Yf(  >"W+ Bf(}s3T%T";laO9XO`3B.%\2o7Dh?zR.b9q~R+;Iؘ(CP<8~MزaH:qyH'nїHQjA0 #ABE0p/KqFvЛt*dd Ҁ.\Q:쎙*+J 8s4(ɣj~=s q=GJ<*zP0Ah U$A)2[-{?`=0v @t) d]9Pp'-&Y'/61138t(>%`#y%CAhY=C,9x/SjA0 #ABE0ᴇ8u̡7)TN$% "0Q'ƅEvAHpa9 ma?9灸#GWjq%U=A Eot{*ĔJsG-L`\=@;t4; FgK Wp$* OԼKdifg?צK lL`LM@}:T P<W06VSe9 H( VjA0 #ABE0 |"c,$ J$HfIZ"q$y81"3#C!W<qh(PGՆ{@Xz\ŕTyTc3aт9RHS q?F,0ޠ){m!`3`K>F}0 䞕R~r]$Ks08p7]8[PB+ 7>-'H(,6/\ >"W+ Bf(}s3T%lax[p}S@-`32.%\2o7DhGzR.b9q~Rn+~/\^ٗx&`?OCP</'@MԨ"$ԩG(%B"&THP|,Acz zC҄>.cE `cf7/h4QJ\?NVS9"U+ Bf(} #~mu8Dd0 #O){>`3 .%\2g?I !AZaX/'4111`d}Qx(pT7R~lX8;,k(Qȫ  *% ?2b>T:3IfZ5"(3VL"l{dFB#x(% P$n )́> >"W+ Bf(} T % lapZxrBv @t) d]9svgKh433O}3OOט7>Ry 56â;pGhI-<$T#Kml,(/ABe4!φ88D7[19v" XpTR!8mչb3JG="[TPb%Wu "P7Z`:Έ"JtZq'IP/߆v-szt4; FgK Wp$*iE#\=4]E9q~RS|#o{d01113>>R*p֍?QgEIԩGGS.4l$¶Gfډ $8bC!.P$nHHA]V B^<m0gBB2*Of P]4Dmcy 63ѥ+8uRlc0B;ճt_.b~RSė{?d01113>>R<jr {gmggKVP"BU W-#-<$TKH8;(Uod,(/]Щ1&$JEQ3VL"l{dFB#V<8 JO_y!%Z.@ $%*!+iht)M8d(\q'I0Vw!eGCJlq33ݱQ)_Ѕ..Ѳ,LX4|~y|i ,r. c!FyY :* tG I}6\i&يImAHpĊGC\™@IVpd*!ڋʖU0@$%^*!+iht)M8d(\q'I0VwŇ{Tȼhhw13ѥ+8uRn"QY.Ѣ8h?)+>9[.H&P'PyTBjomUw!FUZ*x$HF> ,P\Bt*THl#Lth'3GCBzv/FZ{p$E0ZSR<DR[Ov:ԛ0 L#kDSTp~2[JX= ;lj @t) d]9|QԷKdifg?o~]L6&0&0&0&N<[)v]Ԩ"$ԣ-%B"&THP|,AX \ c!FyY :* tG I}6\i&يImAHpĊGC\™NO_`"\- A ItUBVh8SpP¹&Of `o{$r4; FgK Wp$*iE#\=4]E9q~RWw__|T2ؘ:luR~l8Sx6R#ABE0arW 1jЩPY;"MH"(N3VL"l{dFB#V<8 JO_y!%Z.@ $%*!+iht)M8d(\q'I0Vo=:lZ3ѥ+8uRn"QY.Ѣ8h?)+~{O?o<& lL`L`L`L@@yRR QgEIVݩG[#.4l$¶Gfډ $8bC!.ѐPd)ˁѧV zj֔&q$ Ṯ&?;ys).;lj @t) d]9|QԷKdifgMn?76&0&0&O<[)~=Ԩ"$ԣ-%B"&THP|,AX ǹJݔB =I#Lth'`T!ݗ2Ѓ%ͤ ;܃#Uc7а@$%*!+iht)M0$ |3s'I0VA!%v2u°; e];r$w2g@XzXQh18άS ,\n%݄kiG' I1W˸#v&يImAH0]KAfR~r*ZhXA z4x4`:Δ&f`9$ r:~alc9TJuQF ,=KfifgVX%]צǦ !i=8ccc*ϳnGu[dp:hţD`QUĤ  *% 8[ɰK+<8<$MHEtnvD8wAX4]Rt0Ѓss?~9U܃#A.V-TzҒA z4x4`:Δ&f`9$ r:~alF)RE0K+8uRlc0B;ճt_.b~Rn+;?^;e`ccc*ϳnu[dp:hţD`QUĤ  *% v7hϑQ^TxR҄;n>rDw*DRzԹrd{p$`"xĻ^%d% 63 ofd0 *>ζS@IDAT1ܮ@_v{T) d]9[Ўr,ݗKDgׯL?#L?}ק5&0&0&0&8lĠF&qX_Y("mT1G`c 9V-J,<D'B@I>@Ff 3+N!+zwB4/eKI;*AvlGnja)HJUBVh8S`IfOf `CJC dawH,HUJu((WҹDB J'=\;~O?קr}2&0&0&P<_EƠPY pmN=ZM("mT1G`c 9V~|@ 3<=I5UeUfe83+E!+zwB4/eKI:# ˁK&GRZۉA z4x4`:Δ&f`9$ r:~alF)RE0K+8uRlc0;կt.Ql쁳I i^1111 T)eNԨ"$GѩG_ XkHvT\frB0Nע㱢WJ戣1=v/^{p$hE0z ")W YIG Liû%\<i·;7]w|1\įw9Wa,.YC&Zp&W&^/z{$hO>I}< L;RkR~l8,Sx6R#ABE0a?n78,`:/Ԑ iBRD.(\ܤl=bdx9h (@B@p{*ˁF ZjAk(HJ6yV#D IUL#pgrB0Nע㱢WJ戣1= wTN jjAo*HJUBVh8M.]ܟ#sקc8 G;>Ԩ"$өG(%B"&THP|,AXt:/ʁMa9P gr#J+S~MR0G^ ޽rHN{p$X`lbKR]FLO=:>rs< |CL@>=zKO:-MpLz4Q"D0ڨ*bRK zr\gZ9)̞@:\LnRz@ceʏBUJKC[»wTމa[` *F6C8x4`:΢q|/=>}GNG8&0&0&0&0&0&PL=N:-Mp\z4Q"D0ڨ*bRK zсT+B4φ 8p&7)Cm=bdx9h (@B@0΀V(P6}/>:O<4ٟ>=zǛu[d:h*ţD`QUĤ  *% tYDݗj$71S‡IM #JV;\NJ^)q#ƀ 3Q_^:{ @l9@ E`é.M==ŗ1111111r 56ñuTGhI-<$T#K.c Kr tS4φ 8p&7)Cm=bdx9h (@B@0΀V(P 6ZscSט LcK/|ݻw]>;88*v}=XqԋrVn||;GxL`(G;ƆQgEIөGRd!$D%(&`f%EcE8Gc@zv/f/=pT B@ Tl"phԂ矟\1e<)<^dST4,d&<tR ׈ 8H#<G v"R0GAfؽrtP sP1S Lx2_{s) lx<ҥK2acpHat&qש+zeFUZTED  * W0.q āp2G v"R0GAfؽrtP sP1S #pL`L`L`LL`\Xɮ&X"$:yXL2ڨ*`P }*1A`!Se4!,¸ā:E&`f%EcE8Gc@zuDU@mihԂ~o<7=yπNzxcdga<5sخy^),6 BE`J },"i7XTMx$%56RZ}9R,M fn(YDp-:+za8 ЃpMgc23@l/ bd3!G bןG8&0&0&N`(2}cO 1!m?':Ub=2hA-T V""}LQЄH)\#:V#Tܤ5U>`s{o?':Ub=2hA-T V""}LQЄH `B]C#63I!Q R0Gf T"1Z   sP1S1rhox0 ?.Owca7Hatm'&U| >pP S--f5k2.{ `B*\Q3I!Q R0Gf lhr(kg@^ZP NNH4;j  {: uC1>0W S--f5k2. J7E7"M gFZD%94S`D{@YG=Ys0n Tl"Lp7p*G֞?O{q11117x|_cgcrɡK ȩ zP3|5TU P `BՍ#LTܤ(A@-PWJQ)po~=sт#CWjq%U=A EoT=_;_y1118Oi܁cM&~.uC1>0W S--f5k2.JcU7T" NfrgFZD%94S4{@Ys=GJ<*zP0Ah=^ÑxL`L`LMap11tV?SuY^pS S--f5k2.Jׂ `Ud?rG*.3I!Q R0Gf 6fr(k`HջZ\IGE`*>6C&8-8#O`\G>tf+pV.x8j!rE`@ v_y 2UFE3:T),7sG*3I!Q R0Gf 6fr(k`HջZ\IGE`*>6C&8-8#g迷m|2Nv>Ν;'; L ٧cn8+@xG9"0BA jYѼ* `B*s.∮^CB0j(Q+zaDiTfL:k%HЋ[-ʣ0=Y6&0&0&0&0&pbtҩ|}l}d4F@:󝎱pVμxW9"0BA jYѼ*T*HƸ#rFgrB0j(Q+za)M 8yt[mx=s`U+s=GVWjq%U=A Eoth=%41111S4矟N_]+L9nӻt?ClKqBTD =@f FdftR0$1.∬љܤl}fDP,@WJ 83)mi&́F z\ŕTyTc3aт9 w]x8^C7IG}<:6#m ^<΀Zjh5ݬh^LQuьU*xPqDVLnRf>3rmDqC)ag%yt[mx=s q=GJ<*zP0Ah玦˗kQx X/)3nЉL e=pW S--f5k2.J¢^ @'qDZBR=MAZ5DqC)ag%yt[mx=s q=GJ<*zP0Ahdhv?oԋu&:X&Of ge #xs9"0BA jYѼ*T*,(Q0."|_Jv4AHpa9 mi&́> >"W+ Bf(}s3T乧 sN0&0&0&0& ww'M yXXӆN/gZX0;B@ xXA7XTU{2:TTXc堊)68G E?ijl <6<9Ph\ϑ!w* T|l"Lp7Zp:G=C5I}I11111]M ~i}ÿO| /t/tEr:<pVa Q S--f5k2.J¢O$qi8MSS<6<9Ph\ϑ!w* T|l"Lp7Zp:G=CH{t05cccc;ߙtsQ:V[$Bo7gD-DNLPnk4AʨhFg* ZhM8"-!aDeHR|7ze B8@ @ ot0$ Omdccccc ċ}CWUqhW_}u|D6s+.ϾU y"1RA JXA7XTU{2:TTXԺG?7ሴu!yKiM=.i(0Al3Yт9"4, xH#:q`xM`9C'e55Hh_($Tr H 2UFI2: -꺇7HeE? ij@Gո_  / vdA)AWHؘsN*:ݛv/M: ,g`< N6.k(`F!eZ#oX`OhӫGѣ;)4lHچ5Hp/ fp,xRբՀcYc=]ԆG*7 G*8nvtAHpa9 m5.*0Cl"Lp7Zp:G=鲹{ nU^+a[ A )N%MtG ڦ$-i i BS&)5-.n!ɀ2lؖeiId [{o{\Ϲ޳?7k3&3믿zzt}n`~זO`9a?1/2f5Ę'(>0b ]|fOQ'@rEmxrx<6.{rݕ)5<.0!f¢ot=CIP=!#{{I.y;wns{yNnmWSiYP"fEƬ1Zr?7zL+lOB'+EmxTe@ )"-#؂9V, XJƙ!CIV0@y"d3aт)-x9A>:ŷċ p OOO=T҅|WP"{1!؋,>11111]N`xvЇ>th> ^cq%{9öe %bTEƬ1Zr?7zL+lOB'+,ӎ&ap\4MMhiSs!վ{C P:EK%{~6 ccccc `u]ry=rr]|a{β1Y 1 b*8~&oN &W؞NVƁ20eQ[F:eHR|2 a! m 3a7ZP:EFB`^=ȸPdcccc{8~jLx}azr?ۻo1Y 1 b*8~&oN &W؞NVƁҡ7)p(-#a2$o)M 0@60-("L# ! J0 03 M3=Bv9k;ۛ)k(өȘc RSgAi+|?ިƋҡ7)  ԁŌO_4UT=&nq T AX=kj3V&N@%$/^>ge¨ M3x =6r9k?5_dj1OS)3ywZ4{IK@VI)t!-8&P䛞h7.i0Al3 т)4샿;n#1111&q3t=omcrVe %bY 1 b*8c2ӂ܏[rGY=gHۛ-0!0EȸOhaIGո_f)  ,G@t}S$AI4L M+{{/b$ lynDs1!_vC=!0.%b[,D1b Nƣ T# ҡ7)2! Z^(3rV# B#V<qhPGzX[PK!ԩ"AʂZ\Iʣ0!Ng" JL)G;{=>'p̙xtMGyD]9qL_=Gz竽ZJX 1 b*<6LṬm/*k+=s`):S$HYpT+Iyc3aт) U$A)z&)wpؘ@6w5O&wnʟ?#|?k;\*%b& c"AG cc1B OQЀDBEЛ07za 'GU}0́TO eR-$Q F J3T%~=~ON.531111~$^Ǻ7;g|C:p7km<~9W+sbQ,0&x$0&>#Pp4 hH$YI1 ?=Ma )趒.s5́CQ"AǂZ\Iʣ0!Ng" JL)%'^T61111C7{ڏg^ǽ3nA[r.X"O,D1b Nƣ T# ҡ7)&!? ?3Q}2S$C+T>1= ( \ uq O|ߛ_״ַ^u"0ya=?ʉZ# +#)e<T(Six4`""!#EЛ.}&Ȍ@G,xp4%92mXcPiTO`Z\Iʣ0(x4`:Θə),-B<!cWFLӀ %<_ElqiojQؿ ӧO=q`o?7v}θrV"XrEĔ#ALaY^jb{dFC6ƪ5Vaɣq-!AN8zhmX/000z0Pht1 xPUԱ !S`=Nf hL`L`'_~n{k W7>O'3\'},'j%b!be$G cc18&sT&̌} dF/ B#<8 Jr\1]ˁt7ԃ"΂F,VxTPVz" iht M(d(\q'&< }=ov1111.o?gޣُ>t=ȟ._7?ާ\]8: T,a%B|UTU&Zx$0F>#,a/G,D[gYo#a:d%G\™D>yt[9.ָ~9XNzP*5eٔ"GUiW+FLǙЄH5~2[>}~?v^2W1cccc\s}7k?nww}{vW-a%T,DZHSaL|,FXqfuE 7Qyp9uG,xp4%9VaE2Rc91A`łXp<**jEh8PPB&Of p_|j|:aL`L`L`&wğNNj{_7Ư=?s^wZ9+Q +#)e<Th\YleQ_ bT%eF`!^hK832e!CT rHbB,8 mVz" iht M0%Hued0 Jt ~?\+1111N`[Ϟ=;u]ɓ'w0m?n_`{%.\ U$yj2R#AL1aY@1*2#΂`Ċ]RF tI3q r!HdK,4,*jEh8`HfOf Vx,x`u4 ^zW_7Ư؋O_q'rV"XXO݇KL V Q0'}@O7_&?^MfwO͵ߩr +#)e<Th\YleQIˬA$0TRY: V%h@B4k /ROBB [Vd! 63 ofd0 **}H+$㍀ydWw釧s736&pN'{{滚~8;_؏?9q:嬩DWi X@ '|7- HxhQz8H"f稤h%k\GE.)D 7'>u) qV%KA5Gԃ"c3аƣ֫YHG Lha"!曙c?-LJ a>wq|nWu׹m_~-gkc5'`믟cWkl p<oċ7M-/bF=@%ZS2 b* wҀ7ʋ/bF~Jh`%k\GE.)D 7Wb[KĨE b_l֫YHG Lha"!曙c?-LJ 1|iz񿧦Guntͧ[_sr'kN^sl>%ҥx5=''z=3{ᱭNd}yE8z2qO9qttϼouL@{@.w%]#ߏM p~"=n;j|,%S,DZHSaL|,FXLD$`D<( Q^]$~3ZsTREB }Xp-:P0EAqƹ~9XNzP$X`l!ʶJ+oZ4x4`:΄&&`9$!CTq1@?i}Cҥ=OwΎcKeA"N^\pٰyĶfϕ[&FT f~ Ν=9:u|3FuӭlzO_ecD1x*`Qzq  ~t\u*/%R,DZHSaL|,FX \JwѢb""!#Ei"Ѣ*RJ7lbd5cxTBq4! 8|jJPl E' ^ba!GUiW+FLǙDB73~2[c>bc]L0.^xްʂhuҮ|cQV۠Lcf[o3jPٞ*B_/<{{? Ϗ/wfzǝ7O_o H';C17q|ݳc7h&P^KT +#)e<TH9+%hE9ѵdQzHR~Jh`R[(YXp-:P0EAfOMfErPZRAǂXhXQAUZyՊ,q&40̱&XEiXnW S /=|Ұʂ|+TPwTUpdcsU M_}NoN_UM/杷;:@N 7pž_M74M3گ\H{owxy.Q."O-VFRx$0&>#,q.WwH۪4Fa!@IDATY:21)&sTRE~@ۄy3UBa[ ЃP3|bT(ˁJ׋ԃ"Xۢ*QAU;[ޤYwmG7"!e0  v (?;5TT[+TPwTUpOڰ r ܈*B ^<ax`7ۿrM`M~78qW[7znXS//%N,DZHSaL|,FX ǹJa7ʻ~.vfh@xB (Y Yp-:P0EAfOMfErPZRAǂXhXQAUZyՊ,q&40̱&XEiXnW S? / NRYvEe@yGu[u 7o2 kMl(A[&f{3WgS?Ϧ8{7\C]-wċ+OUu-{}&X9WŸs8Yjb!be$G cc19V2ߍGW H$`e( Qs93{G DCjȂk)h (@B@0|bm2+BВba$y`$<0!HC(x4`:Θ"P8v$+ C v (?k/ uJVW0ʂ VǸGUgȆ58B71π*Bߋ=?_[^X|?{&^~'>g9e&PZX"ILJ$pQzYWi2G%-5dugxTBq4! a>6!AEhI΀O{1#!<m0gX8v$ XCZ!*F dvo@r KeAHp#OVžNa} 6c;PE@((,lpG+{ ?~mѸ pn3 X^KTS,DZHSaL|,FX ǹJa%,]aw\̞QrPZt<*zDa8 ЃZ\Drm pj/2`d3!GMb:΀ d-[q(I`;҇B UF ȢiյĂU+f[~6=!a p= v lN`Yz='f,  Ķ:m-ܼ $6a66En" = POLyӿ={~kOi Pgr bc 2 b* QDCjǂk)h (@B@0Cpjmr!*%gVȘ 6J`·DB^'d0 Bx'=Fv8vKB5rv,H vTsVGAȆ5&fՠ-t=Ug?y'/>0 Nij{;Ξ=WK}z[?8T-:),T-ХOQuHUa-bJ9*հ f^(Q"jKRzSkŔr0$/=p< @l9@( A`C|H$uOf t_a7WxzPFS8z'kȂ_B ŭ`Gu[u /?gf[o3bpٟ*B߳D対SvqiL`M~zwȑ}u/jѱtA`m. x*@2 4n3VQIE. q/5SBQ["ЃZ+,!y!3^b+dBfBJ0C"!P~R[ĢJ:Cp(L132 3~xp6E[p 0 [&FT f'JZ/=/I3cn]=#G})U  Uo %tihSyT]DqEmx!pdJ**pY0埼v#Bk S2upj a;r`kD sP0R)HԵjQZø!z`CFSf;f>mVW; @6a66zj-tWg;)xdDI5thdL`wuׁ}䠾EA  Uo %tihSyT]DqEmx!pdJ**pY0˯d>,v`MaO (W8^!ceaA{1#!<l("$&5 Oj t%a\Ca!ؐ@Gwo)Fm6׸Cf-l k]mUgꉒ…c6={wx ɘ ě~{ѩSmS+?hϊT-  `qЈ8$A*㊋0MC1cTTB ˫FJH^+zN[d`/t6FݰVȘ 6J`·HoQF=U396;Μt-Tmh?u[u  klͨn:3/g&u'J:,>^:|dL`7hϜ1Q5B3ڀ. x*B2 4n3VQIE.4 fC0EjAh\ s=pT B@ l Lpԇl("zϥetx\ImnR=p M:X: b@\~6}?chfcq1ulF=Qф~xz1L'nvt_'+bJ0S[KC#ʣ%+.j(7 [Č&sTRQ "O76i;B0Ej Ah`\Â~9J{ @l9@( AoT=CIPbJ9k?m"גqn M 9;q`,56hSm-?CkJ7acm`MTV{{UY\~Vֱj kmu} emn8x( o((i0Y>*fl8&pY&oo σ|C_ݲޑͦjЈ򨸬HG2 \-HjeЭf*\AS+˔% S{e=ת\{+'Zx8v B l Lp7P*E$(16jk`ø7 xXUsNqM#ؠnsE۪[álX<_]W'ۺc˨'J:MO=\G!?4$@F 򝠡KC#ʣ+,j(7 [Č&sTRQ F^<6(&CȄ)BT C:V{U/ų PBA` f6C&8l("zPEROjȵd0a\z`CFSBf U<}'Ѵ#5`mh?u[u [GN`7a6zUYV,nfc,YF=Q46/L#,Xo>}?~_֦mt@ ҝ!KC#ʣ +,j(7 [Č&qIE.4 9MPDa 3I 6g@%zX~ ( B,͙T6HJ HrǮQu`:z`CFS@H=Jmml]Im-Ϳyt6ʊGJ'JLVj{g>j2.Mw]wSmkqdYdQЈTB'( 0MC1cqz,8@YN=:nPY(Q"$}M<gY\+$HPE-DG/A` ~6C&8E RDYW \ImnR=_h^FS@tC%ml΁öm-l!7mXXrDeXzii??$4ec.[o'?)0|KWaN.Ca0tihSyT\4@rEmx!p8=^ ,=o&1PELZ!*HpcZ\Iʣ0!Ng" JL)Gp(ZZ2hz M6-]MK msE۪[X}8mX*nfSֹ%B=QU?M{}dL@GMGx ͼ|'hЈ{3,˲+{dzLZï}:ym< =zzhzt=cfO4 xfqEyϢw}atLNy@9JY'n+%9hg6S$Yp5Q+Iyc3aт) U$A)2[ma;X_,=jla3BXz9>9z$®+[mh?u[u ;etZh׫tEi6#N6MA v[=?xxaCz&/CT'_'gjSU&@B'4˚ Ҥ<F*MeRNB 8c\PB֎~{[BHcZ\Iʣ0!Ng" JL)G0w$pm+ {`}en\Ы@ a6C&8-("zPER`(IV0+mL!,=jXC~͵_7?Yf뇫WeE]ndž5d3j6AL=l[/A o5pt~@S׳z+#%.C')Wؕ:XF6|$ѴrB 80yt[S놾{MTO/ A*@ =A( Aot=CIPbJ9&k[a1zC\Ыk` Y03ٗKgmh?mj]mPbẇ^-+!Q|%} Yr3jlzMys]C6&peO?4~c+c G wr]4q`Qsek3hea`}9yf˚`4܅5uJYN_<Щ=sV"Vjq%) |l Lp7ZP:E$(1Qf 0a|Wژb=CСY{Վ5,H~+|l"W. kǍcF^֔&ay擾FYiPӶz`=Ζ^?uӑ \=U=`^T<')W:X.(z9Jy5%E,=KB@rgnHK(age-QB-1v7O"ʂJR=A( Aot=CIPbJ9&k[a1zC\Ыk` YP\O\+/GI- R<]zeM&2]m6h] ꇒ.ͯdOϐ]x?x.2 b*{tN2S&t2\I1 \{pKTL%ͽPqVV%n+tjt7O"ʂJR=A( Aot=CIPbJ9&k[a1zC\Ыk  >rtLNUbj_'Yu)>]zeM&2-+APe}E5 yb,5&p&8 :ќ bxfDB'( ҡ7)&n]E?G%xZ%*ʅ@t *0–(yt[S`RS?HdGA` >6C&8-("zPER`(IVP+ݚbgWc'HGڔ@]c8NIECˮKb.tW&aIfە~(k$]pFӋ/L>w2&pM'1u6XZQu}DBUҡ7)&e⢟2\ 3Bn(taDɣ Z4ߗS?HdGA` >6C&8-("zPER`(IVP+ݚb熺^lZGHR'}iQVD27HqHЫx5(k6ݗm6h] ꇒ.+g"=?:xuN೟|{uyeLS٣;LUK$P尸(zb`[&.9*)Õ.Q1S.  @# 8iBΞ{MTO@fD-$Q F J3T%#;la=JXY{f:K"S,*+e%ZֵAlF^ūAYoAJP?t5o.x 8#dw[+w T\k';LUA$P尸(zb`[&.9*)Õ.Q1S^S΅(8D"mN]gb6}fS=EWGA` >6C&8-("zPER`(I,걇=t0zXqsCC.6 \BYuG›^˾tǏ ..Ѳ R|D0=*^ ʚMdf'} ZnWˮ{tk!X|cW/Lw}t…+0|ʘ G3کCO$ptMI0TsTRC]b\18ˤ yt[S̮yG T\W( Bf}SDHSe0 \[S58lZ<ѹ*(utMӵ7> mO]v]e(wa{U5 enm6h] ꇒ.Eg~3Ƀ9&pL矟/_ziztW1=ZNz"!XCoRL:XL_aZaDɣ Z4?S?HdGA` >6C&8-("zPERJp'ImB+(W}Dx֐tMh$l"3~ҷ5"JZIN \H/;ytNz"!k呴(zb5r(~g2Dɣ Z4^wUt7O"VVp< lmM0gHE!:$vlD.JCo&.*%˗reXKSx[;)9ЃP0S/ ^,{xʶJ+oZ4x4`:΄&&`9$zǵxI=*:lZѹ*(uJ)sZ7t(W,MAhfifgu?.oPT\`(Z_uBhoǢb AP\ędX㕪J!_w=LG<&0&0&0&ºWzҒΑ( Q^Hʺ~JhR,_˕ k`Mpx zzy*@oVE FcvEƣwIճ>Fqt#:Of /T`3PSB's UP$R0E"\=k.b9qA!RłsM^`^0xZBuBhA12`(T .L2JU%?3_cccc-P^X]6z"!s$-JCof.*%˗reXXy1?<ʭs`-VS9 F[KGUieW+FLǙDB73~2[c>bc]L9!t,Lm *kU.J6j)b qBUr利\ędX㕪JkTF>&0&0&MxaդsCO$0Ei"mǢ2\ -fFSɟ`}; xPޅR# ĂBb<*J+oZ4x4`:΄&&`9$!C d:.86q~&ѼդK8fifgjTlPT\`wQҷTKǥ+GL"$TUZ_6> /0U:DBJX(/^.ږy,9*)Õ.bf4;9w& 㝧B_x{J=(X06 *jEh8`HfOf `҇B },+~_%-K"Y2.Qqh18~\ޠb&A/Q0%Z'T\ Oظ!J"$CGER^n?_1 8^XUh( Q^ER&QsTRESD`^lXkʂS `zzy*@oVE FcvEƣwIճ>Fqt#:Of />`خ@h1i?Hn2c EDhGzRq_f)v˩; +`C"_ CxH2f..X+!S+& 睧B_6jzP$`4 jW\d<*J|WkЛT=#oG7"!d0  v qƱq%nn!8gBELMRH-dt ,'Rf KB4ӵ(he?>{0:B(UIC"_ CxH2^sTRESDcUtD40!qPȠ bbAH GUieW+FLǙDB73~2[c>bc]L9Dm=py!q*TZtXA/@,BAP @Oj8l0 i&|_7LzI<1111< kACRw]04ʋe~Jp%KM}>V^=Kg,dPl'B_VJzP$Y0ڊg]b<*J+Z4x4`:΄&&`9v$!C d:.86q~&GUl3 S!BͰ)xdn1xqSi@{d 9>ut~zkp偍 8_X3:/VQ^\-XsTRAlb4X1Kg,dV7@ Pl'B_VJzP$Y0ڊg]b<*J+Z4x4`:΄&&`9v$!C d:.86q~&GUl3 S!BͰ)xdn1xqSi@{d 9;{,ӗY<1111< kACRw]04ʋe~Jp9YM>w+7f,%0(D(ˁJcY{1#!<m0gX8v$ XCZ!*F d5.861m-L)}&q*DXȽ0p5v-=~m!0&p&BQ0TM89לZ8&0&0&0&Nj#ءH+(wYPi2G%%Lh`ʍY: f 8fJ b+>r`Rg@^b+dBfBFL0C"!e+e0 lVVHQY4s .yGu[u ơzgI &rn-L=DeM&uAϼk[$IIg k|W^;o9Zcccc`VaC{l2 re!*M樤%'le4X1Kg,ϼ4A Pl'B_VJ{ @l9@( A`C|H$u"Qf v_ >bc]L9!;Mڑt 6zXp*DXȍ0p5v-=-!0d&p&BQ0T#w~MӟG呏 8^XN:/VQww]XQIKNhsrc΂Y=ς>l}"gVȘ 6J`·DB^'e{;@IDAT0 bPCZ!>t\qlcxCФI0m Bܸa S7QYSh~b(3IfR0g)AE_w/.|L`L`L`LLJ4wH`G/VQw.?3{"qHhsrc΂YY@ɡ֌OrdP sP0R)HԵ&A•>v2*.e.NCS!BnͰ)xdn1 vm $3)3⌂"~c7M~ؑ5 G<&0&0&p&`!XF]dJXr,VFStL=>sc[!c  F6C8x4P*E9 x$W҇BVخ@fk=Y* f `(/|Xnna8TOb>r*DX0p5v-=-!0d&p&BQ0Ъ/{t'Vx< P^TEM]]|2 r"dJXr,VFStL_j#>s`tg@^b+dBfBJ0C"!P~2[ c:|G hj ΂HV'X۫1)RΝ &rn-L=DeM&uAOoK$IIg ]{|oqo~1111¬HvahE*M樤%'le4X1Kg,ٻOr57gVȘ 6J`·DB^'d0 Bz:|Uأ*کvh,Ng5׷W bZ`Gu[u #S^w;"M,F [zʚGM`GߖI2 8L!(*#vƯqwl~?t^OzaHahE*M樤%'le4X1Kg,Ѩk}"18V B@ l phTs>$:u'I0᫨zUN@`,du8jx;۪["ܩib!7fMT$:u'I0}t:~x!wqg^^|&֐cl QVl(W?έ["\_Sc}ԑcә߱cVnY0K`3yt֥8=8ρg@^b+dBfBJ0C"!P~2[ cq#փ?4Ok鍯;3No|8}q_~|v?3}AVwN4l Q^. QV4BȲS֯tǗntk;qw5V?xr֣/M\~鳟^k_cccctxwH$04ʭZJ9*)O'h;M}>VnY0K`3ytցyZÞvֈ 3 j/2`d3!G R! k?-Ԣеq^C ksgOk]Mwl)בW;Ww3ӯLwe p]$d i+P}w7N׸DZwH$04;9's;g}Dl`4X1Kg,YWv$BcY{1#!<l("N@]Im5=Z$PXMõW[Ÿn:yaw_iz/N>5=ԋDA"p]$d iT"y?pM+|6  g !+(Rs&sTRĒf2}ܘ`gcJ tr ZqnKԱ΀VȘ 6J`·HoQF=UU 4W黿/f_:NM;nOfzo~nE"hFyH&h?u[u ً-[;U+n< =111111C?a}2 2wv(dJ*KPY !W,D|u'Z4e\뫢/U~mmp.ް?xW?OO@3cFy1r@Om-/g4ĿNO ttHa/VQn0.nvfڨY >w+7f,%0<:NaW+Mr`): sP00I`C{*Ĕr~RۨE%a:F M_[o^z4ÚgON/$ 5&I (` B?~8_;sm9111111C>404;9'rs;g}Dl`4X1Kg,Yb@'װ&9J{ @l9@( AoT=CIPbJ9k?m"גpVWQh/+ގBo5᯿qѿ}_CwESU?>-ӷ[_ccccccctwA+(W R+/M樤n-7 Vh4X1Kg,YVbzzsxT{ H @ј1#! 6Jg" JL)Gp'ZZ2 a6=2M G?<7L!Į1H;!h?u[u ً^yjɿۿq/>s0&0&0&0&0&p'xm‹ahwdJ-Ԛ@BʍY: f x&:}wkX$BcY{P00#! 6Jg" JL)Gp'ZZ2` V1֛OM?ony)A!?A#y6\crD۪[Uon5g=Tq0&0&0&0&0&0&pX'xHFyg,4,Xˣc,9m%.vQ.>6X0Ƕ|"/kfH b6_30Ar(dD z\}JR=A( AoT=CIPbJ9XSۨEɱ±G W澔i;_6_c>_sC8_[MmD۪[*+U[[g|ccccccqI#[ S| JU`1f!W4{L(%Blj{3H0cZ\Iʣ0!ѢR)ubTm`{ U[tvKMnjGnna~?G_9?7.s=1111112|Pp!j .^ Sta;Aiœd&/zhC\+ǚY613{pg:ks s S$jW( Bf}SDHSGF-J=_h2L7?ti&Ж]iyخ^z5*TRK-Zj5 31W8Nׂ,/I`Z@& !$tC7ti-ZJRWU {޾Ky}޽c50zL%8ñ _m滿h&&&&&&&N?Y? rVNړ)~7;:gyBD\HB c`s=+F"br ? G&#B  pT*5LA{H ,AD];͏нfOڤZWٿiST5q8 wn2?43*J666666e<P[(kz"sj&Ec¹NG5L\ 1X( _HAqws=+F"br ? G&#B  pT*5 Ʀl)Yk~Mۯp|mͅ8p: eq!AB )( a>X128#HxfQ82J`85mPR)Xрf CZf z`~k_W;5p4áso껈#ﭖmhhhhh<ݨ"sQ9iɅx:!T? 7v8bXDL GdttC–_(8"״Y@J`FhCQ+j6ӭf6>}aGƽT}hTC G;m777#mq@@@@2O`ivj =<mmmmmmm'ПdkT%WE }0isك"EW]#A3ϑ8-PqDiJ]fU s:a$36nh1gZ &&2&JԩS`q977gviaawСCٳVb6Ijj]-#Ο?U… i<N3tͱ,G  #V br ? G&#B  pT*X?gڄt( Fg]z4E:O… OF' Icƅs &&&x뭷6?(?i4pxWn.ww{ < Q\Tpm5%# ]s=> <`f A3ϑ8-PqDiJTęNcgU.NF7) f3ihX - 纏9waȑ#ݿBݻ \[tcwݟy`U rSTpm5N$# ]s=> <`à+@Ga19g# q[~\f8*ԥR+d(R GqV3 dv@ 1jw7:IU8gT_Jx@@U3gU/ţ\wzٳr-}D`ẽ?]4^$+#jJ>GX{|#y {q QXL GdttC–_(8"״Y@Jŀf"Ȍ3'#Ni\O|v\%E썫Wgv Hz9|B?0+"‘br ? G&#B \+p"PݤquĨ!>c= ot5p$qv!ϮEV[K&q{8֭[Po 7'M#n\M<2?T ޞzzfלp^s|g)4sB Aj.$ 0<A3ϑI踬 HI % z8pxin4‹8:}:br/j9!/9+ BO4/lrƩ66;{f7w}7뮻xA3tt9X! 58lX@L G$t,A BVP¡GC/F"3Og_}ٳkH5Oͫ._h//kvNJ׸癵֞)1 Ys֞Yo6664{[8kbWk׮5>Si=s55S:?n>Ouطo5?/lA>k.swuVy 5^$+#Ӿ|>3tt9X! 5&Αa2 zg4pdFє#J~2=V4D\-I /2$IQǞlޫՁO=9v:׋hJw*W"\ӓ,P$]cyyr>>H>CLwO Rsy"`alB(o4숒F COe 8(lnR‹8I}:bk|3_0<0'ϛ 1:.|qqcw s[f2^k BN0= رøS۶m33t ېPC :(]Dk&0rk.&N0P3 )WTV<Ϥs|g!ʃ Rs&QHBq)g0ZBS 8F Z?zd4s2g/0qqQs;.x7ؚW̅ G䨇6MMMM`O՚}}$`ſ{WF_ n{oe(&vR=_[|>nF>^y$qa&" )$[l5&n"%g0ZBS 8F \/{q5>bLI 玘G?t^&$ 9~hhhN&sN٦[ |#5S ,zzY1 VsB@Gʈ#@LPCh$ސ qF C)Îh,o.&eœ8zFp'N.?D܀*p}n%=B+͹S/o~~9FjY ,]ci'ObvqXnݤ _e8!̟p!XTOx>I7$Iy$aH #`^%Ң "9B# %.`Of":X=Kſ18X ߆_CatVW1#T4M7|#~ x{#ZMMM`L}K?ÇjwW?&WgRa_TJ@.TijV݉lv=a A,SDA%uw=~`\h|2ǡ_ !{ EFcammmm+?o:]x,n73aLߺzzf[~9X!Ȱ+vŽ{97 qF C)Èzmߍ:\_zɜ:mj߇sTkϕSw߮\t/ZMMMM`&nXwHzek-ꫯ^-Kws|Qʅzzf =>K3'E d2LggRDOςW $Yr63(p1E`QPU-]Nj.W{F:}~瑟ԟ+5'r8k̓~/-&&&|8yy'fnBO>ijoԽF3ur_Ϥ#S6+L [PCKD"3~%ӼaBZ6ydJ\ 9j@8"0((݈=|nCɏÇO/NrmgN07I3Em.lhh&pҥnm.\rZȹc|Zsn;R_ xw3Mٟ>|>O4v] )@H^όbizp ~PzXo̸VP¡aDAWFt,6G~fupy^͙sڐ5\pvn_؉>w?쿾/Phhhp~[ڏ| joGӿzp}]+FY>(i^ٟ>|>nY>>eH:u)Vd!z=3G&JyÏ6~PzXo̸VP¡aDAWFt-]~fYw_ g6?j4gϞzElzx3~$666?6,W4nggg7lbxGsN+e{(>WV~mW3qX$ EM#(_E(gFȤ|i{A7zC&`Ƶ2= # @7p?'>iO7?5c|~)}sS܀e4̉fwYSnv?3~7bqqQ;M~3gCy '4v'p'O7}0vb c\)sk,MBC ,Μ9c?dk׮5?pw ^sss殻2>z7{_7ܹ(_Īy( dSbFю'b]\bח E&Dj^%ӼA\B$r63(p1E`QPU-xfx }7 μlވ%'~?K+mmm>1&-[dm}ݗ _o_Ij*t: Cy '4vқ|g[+m%.?j.-% e{ {).|h|@@@חMnjoo8cyk` g5 e zTC@9|8L?m{?ccGCge/\d~>j9qb).^LS[¹#O3}8bMMMZN}ny= lذaLw='PwR]ut^xk7feO˗#j4z<K43&!uQ3^y( dSbFю'b}PdF i(+]o 从BOyV@Cqb9m?L-AhϿySx_5vmk}j?l~OS}{LqZ*uDNl2 $`e'9i~9ikݏmf2^Ikn@*fo]Dj8n޽ۜXU4`p8r{Ѽ=;_z6~agC` <Ϳ B:_Gf!/0Bϟ1?͆+x/03|d\ΩY~&TX>QmOHźSfٗ E&yDOF^>|& EOCCG@/ߖ:5Jh0Y 6cFю'b]1aDT(2 $z713Ua׈>0vx(PQ# 1`Z RthA@Ƒw*@;x ?ؼi[@m̛6vma>q8{9v99ssF'Yxrڏ |ŋo]1S .O|(sǼٻgٽu3-3[7g"4(K:0*%ojIڐ]M,,iȴnlߺ޼-fÆZxmW{ow w8oknwٳG7_&X^OO^b )D#h' `l YgFD`1PI4o2C!k})fbm+6[,K7F2N}jx?_7l`?8-ݍw]ꫫ@ݫv>Q#9,GIIG;! d!z=3G&JytD]1ؿbF1ɳHk5 d0G"0TA5 XSL т稐/JQI8hyǪ)'G&cyKԢ6ʗB:_GfM K"*2 :ɗ  f*A %rb g&γq"dy qiyW?sb}7/}}WfV_k+m?n{{gj83n̿깎:D $h$, 4̈,*ҽu4}@:"3(Pa 0@fXm? I91֧# dbq$航'ST!#vb3W._-f (v;ЀԣSNj@+ rmh +HC Iҥ@wd7=d1cok2q1|M "Yp&Wl\me5sX^OԐ~WBA#)4v,!T 4όbiޘ5T;:B b:c(P ڌ(ćspQ!_hO)E :'dbq$*EbO^'GʈnQeTOT!#pW]$H\fng1Fpi /1_` UhL5N{‘rYACH:-n6k0wXxWfcW+e\b}F Y 6cFю'b)0}PdWI4o]!#"kX Ԙh2bQ~ @:QthA@Ƒw*@;x*'SFtB:_GLme3lƨp!^ЀCQbSNj@O)ϥEdu"N" B*.|?@w1਽w@{ {WVJ;d'O4.\Hͅ(x15(])F# f#@%ӼMEdHޝ!:«/P!KA@Nʶbce$ġF9*'yRT#@#$ Z`48EbORu+nXG2YHoDGmP6QvJJ``’rrw!Ѐ#crSNj@)Edu""&+y?3~W/ہds>pwUW߳#]r}WB1 }Uc{Px1e:D wE\krdpdgIG;¥QȌ`f%Ӽ1ykfۦF4!:? d0GZQPMXhb1)&qQ~ x+ ';#1>'NST5\L|n3#RC/uf6QwL 0Fpi בa_u|5*Y"2AκMPEqEсACHA ;q7}ûw||O҄] {gk/"vm殻.wף[칯ti~_ b'j$.A,Gmth$8Bj쟳f#@%ӼQWFĦ 2E2#l1(E`12bSLP\cyhJclik̸[ zPQ,"mMy+k>{‘<%hXAݢ>#G~诚͛i0~ǯ:m^zɜ9sƼ]/xGMeT*+fuyi=VfMldqGQ,GH: )X i?2X T={%*hDAl 2d$ őX be$ġF9*'yRT#@#$ Z`48EbORh"zdblY33V#_&jr4+#ۏ3j>OzPz b g& i"l.#h)NMy) pIꩧ=?*uji.gͧ>)ʻZ"1.$8/}F{{}&ˑARFN@H@H^όbi(+QWA# b3,fD~ ZPR)XQˈIN1CsTO\^AGFH8h~cT-UMՁ?|9 w g29/Q(3?WZxE 5NL-5.|$ GF!FvorV -M;sop8qDD7/}F Y FѲ d'!3#~dTUI4o3$Jd`((E ~R,֒w2bK(?G|UtL FHS4 vT-UM }Ȅ:1/gq7 M׬I?)e5d@f46`dsb gmSk\n%8XACHסo>iNz<8wwNp(ڣM@@&4ŸO(,GCFN@H@H^όbi(+QWA# bE,"$ d0G} PR)XQˈIN1CsTO\^AGFH8h~cT-UMU_3t+s2_Ҋ>Mv}L|'l-mF%yOg뢲%AY2XD&Y״(_Aj"(2IV^H2"c]sy rpw_ ]WY= _;D 7{Rd92HH: )X i?2X T=c2h ,D-Pqbub#e$ġF9*'yRT#@#$ Z`4UST5q|̭͊!Bɤ̗`fgec"tP`UZ4f.~A 4h+B2A׸5<մB^+3łE^8pLW%+Ro~~h\@{rb_'5JN^RlmYHAl YgFD`1PI4o앤$®HY(z_(8–Q kӕˈIJAsT' !^AG&H8h~cT-UMU{oY̆a/glr6+"FNWZ5fJǜHVO CkiUPJ:L ;d~W-W0;vtݺufbh_ۣM g"!W#y,@GV ]DEHAl YgFD`1PI4o앤$D[VpQPMXhj @&1Q~ x+ ';#1>xU4 vT-UM~&!xm/x`q !. Rl`W0Em若J@런ƌ㾡 y4+b!g׸$&/t~ I돚O>$rpߟN=$oټyF6ւ+rO^}]B}Fɒp^8W6Ҭ3#~d"$z7JJQHea!1 :0a80LkO-($5Q!?sz!dbq$8$Cy-yX82_NeU(f%yOFן܀|A#X>Yg 90O3Qkk\\TJ:L$c^|ȏĜ>sAj ΚxL8]˨AvinV2ם8@ZpUO`u} X;D w&j-&2%@H^Ziިl8`7:qip!qPmXXj @&1Q~ x+ ';#1>xU4 vT-UM~&!zy |ْrVVۏN v7ۨ4$)U ?Z$k] 9C2C `9;4IhWu$dR$qccg/'P+'}v#z׻'ְ{]vh'pjA8Cz5R.ǖbHFQf*-h^ƪV=Jl"vY(NoPĊ`#18$\ VKJ5AsTOur֕N-v0Gb|^8n|;CĂ',l`'MfO|}_A88']cn֘:?!$.C/EB2XMcr,WWJ:L$.| e YO=hTKU\d-AIɼ:{q$A@NʶՓk jB~FA貆ur:%dbqㅔtF6ORa7@>ȶ}pJE!l$Cl`WFX;^(>*RbCMp< 9AoYkCO "!,g_XFWңʴHi_1b=z7tyᇍQr>j/b,gQ}ȶAp]/Ծ}Qy&gB"__(UDf6 4p<@>ce^AF Ck/1hDSLtR$qDz'} Xȁp +rvhsR0M4[I$nR 9?yj(={k]mQڬYu/u2(-AG; !jd!~Ӿ1%4ҕX+2w¹;p 9EB2XmmO{ Up RI `}4M` '=V>%}E2[Ve3% Mn%BY(jb~UeD3[,TBc84@\kG-($5!?ݎ.kYWBGFH8hx!1 ST%ӼQEn[Y_֐T< 9'p/]Mwsu9= `9;Tš-t&4#ZN$nX  2} 4M` &_ԩSK$WEǒ^]@ v%@H~ܞ=hTKUӧ,c.a|D?mpI(`lF,(ZBPI jC~"]֐p2h8Bc:#i*)G"F4ȇv+~r6i'vE`bY3cqgM;@Ǔ|3 EIȇHEB2Xm+Y':HEi# fR$q)XyipE&Lॗ^nk5m h.-AG;PY}PV=J"r2{a|τy ,4xPi pJzrP@y(!]֐NNG$ Z`4xBJWToTn:rCE"0q|Ff;&b,8 C[XXwC@ȇ#.rvh5g+X eEPB+i$WH$nd g~C|e 'pQs`{gIؖj$r^]ZZvB*xbAZ-FfE7%$b C{&scqZ-}jC~FA貆ur:%dbqㅔtې STSš4La]"5YCF2YD,Ggd֭mL;L|Q@%N$:a=/-M zw"EB2X*!訬gHRdɀmȘg> t ϯ6mK*nZtRF(gRh(A#XIa|M9fOH`cqPh5\%TCttt=ttYCκu ';#/ %y9;5WJ:L$ @ VrGL={ּfqqK|z r̙3رck%/lʟuV~K*xu-RLrjAژgqgRĨSd0>g*QBv)33H Z-CjC~FA貆ur:Epp C9X!2jbt 4QC2Y"O߀5 * ` 6<`V$ϐvXDӘ/~9Y~Yg 0>џEd)aշC@=QA&I)|%UQ, `+qpA~wQZC>J.tVV;Uk:x:!NiOP`Vİ -F2J{H&KI>Fe]4ː6kAZ-ljC~FA貆ur:Epp Ӿ=cXY{{K|qKsWյJm&O3گb9X!rjb4 ʩ=b2Y"O򙱾{QLÑ i pHTO*@kF9*g!gEr0GH cVS^^))$à0LÑ i pHTO*@iF9*+q:^AGN9#'<&!o)f]EE<]zi=/`P6{rشA3;[p_M6Lȸ_W.{ #zxff"W&ٱf*9ͤJ _IבQȔ8(>Y/wϴj"ۍ 麯\U7G dn4 q%=6sB&jb52"N(I 0y 8B>As`F*Rb @v3 thQ)3pLSVPH j@/G &qv~f~ϓS݀XL ){2k̘ >Q|$aDLB/έb?񠙛ʼnpxb,׹XsMDGk)|%_GBFE"Sb\  ZgrfU~5-[uR~$/R|g_CO5sB Bhyk+e2E\dFHVGr (<'Z-V0k(Ea:^AGN9#'uc 6x|s›"Ѡ6gf}zͅzT" 8j<"& 5{o7/]йx;~"/gp\";ɥ{ί#!")1p.Q|$my(oNs1sy{6y͗@ s9oקՐ6;v,[W8uJm'O3i@X9PMNe{zLtd±@48۠pt.BdZ%T _~5` (W GRJHTotq(BDfO 0ex^8-&//U^c>?vqwwffjB<:Q9@$X|qig1us`դL#eZ|V@j* z{z-&9nOby3Y v'a.LGd+O3sB#ĶOj/TЀq3N*@iQR3 t((8yBcj `BAkz78 VMdV`L*v |wAC'(: Gp<1p$ȁELBL +qS4a͚9ݿC3}$ucI#g47~VZZ#C8$AQ#u$v#hn^|Zƻ曯"]:Y 1@$Vih+e2E4$#F3N*@`< b}#R{b :t(8yBc^U3"".0X(3aj+-G I&U# 8|7H"& 񄕸)2Ajsnɪ t2 / @Z#C8$AQ#m{כ["h2NXصkluU:Y|&Wb9X!rjbרWd0:IG0H&L))#F3N*@`D OcDjO!^AGr0Gw-FX`VDJLF'(5Ʉ)h " kSk P|Q2 t(8yBc^U3"".0X(3aj+:Xz$^9I#%#A&B`z`OX/젖:Ƒ*˕0 5@-Ή4"C8$AQ#̗v7p?f,Tou=9}to%}-vq/Pj#x:;T#Q,0+"TYMNC{zLt dXop\XqjPJ+O ^$ 2(8yBc #*f[U 5֣ e]A9 S[j`Pf*^A(xJH'Vh9;eq$'re|/ ECM+Ps"8Ih,/ky;6{mK9Jo2~w_m6{\{S 뻯GtBx}Rpfgg}d/<']'q(0+bX]MNC{zLt dXp-1qjPbёqѧcb :4(8yBc^U3"".0X(3aj+-G I&U# 8|7H"& 񄕸)2A:5A, 2>(ECM+_G2"Ȕ0IЃH, 5_q7@IDAT]Psd+O<$Z+Y}vO{]XX0{q7g?}UbV={^rgi/¯WF e50+VTw@H.d3,M.iC҉x(܌Sk P||ҨHp;Dˑ<>=#8 8`xsBʪV)B (BDWP:`V48+W# 8Rl?8"& 񄕸)2A:5A, 2>(ECM+_G2"Ȕ0IЃH, _;A9mK5._j[8ݻwロ]Ǽ;vx vLbJә*ze0k6QsBa@%zI6؄8$/@@RHs3N*@i"[4*hc>`ȌuD&N V 3pRC(\nTotq(BDfO S[A!4b!L Gp<%n$ȑDLB㎑7FӶLN"t]Gjߗ{pˮ)+H"To2Z%TInQ!]x/m*z d0G"^HE9z!EjD*Pze=ʯ`P61 0 &*NŪj Rgb^$$9G0Ќe 1Z'_V,0 5@-42C8$AQd T k6"ݖ ;v}惹'x޽vmy8?y晖"''N]#0}~Ʈ? p0NףLzK*Og2Lwg"biX4Z|D PJ"CTdv*@{(gQ Ҷ =P2#/"fPܢB=ST}Hj@G &r6@ՠDũXU\<%nOlI<ГD B3~ r6(zKh~YDzP4Դ8'L=FP*d;:w/y;@.6|Ikr^)~s|{<@^@ms1,9yP[+ښ>$6KCpa\c ̌, b~J&*z"4P91c3 j R[D~H3(Ki[H(D}Rt^(nQzrf5*V e'~LJ#1)S331(`z#DhF2AEAt/uXVdɡq(2YL=UE-Gr@:W OnAS?DX'=9c@zIl MC!0!{#3g&FbEcnHhk$B%+vp܊2Z%TNnQ!]x/m*z d0G"^HE9zEj&fԀ^[FFP61 f)43Zܞ"J&" &'9Af4,lPD:ZǏehijqN! z"U$ V7,@ݭ@<r^?w ԃWm,  6 'ZOXY#A($6uCP@H@HbiX4Z|D PJ\"W2J,îZB(| RAVJQy3p$T,[^fb@ JUZ j xT\Tm$f=f?8" &'9Af4,lPD:ZǏehijqN! z"U@5ګ7+ĺM \o9^FAf,4>\gn*X>왬Y; G8AV ӂ/54 AtiX0!DEO)dvJeqưkP,|z{ $ F e`DR&,m-Th&f+ j@Yƌ#l""i1 upH  Ah$f=Ʌl&&eГD B3~ r6(zKh~YDzP4Դ8'L=FP*d O|cm&AQK/i_Ykf-'  'z•:nͅ#ttXP!aBFfLHƂݐ*3e%IJV`9ҩlv*@n\b=4ex+XH R0E73*ԀU,MB80FLk&g5֙Da0 =It "4g gG t :~, ECM+Ps28IhլB/v.0]2~ktWVVBԠjMȦ`87My_txꩧj(k=O4N"tt =>潑3@#1Ң`7$iLY@|;GS12Z%TԀ| RAVJQy3p$T,[^f>Ԁ^[F cd3B'N5@'Q/Y͸=euf&D$8SV"Dd%>˾aך`*v wz{ mP %<8B*b -T/UNV3q 0GR522X:qa =yzLj)S331B zh'D(g`[&٠::ZhiHF zIzUHAn~ovWRU;Nu{ݢQկ'furDZ?Ikx#qW}v8/͆+Iyu!+flBFfLHƂݐ*3e%IJV`3SrvQiI{,g>KtD%d0R(3p$T2a)+۞Lh *j 5qaԌ &bfNjBO^^&5֘GS!=N4 "3-W3eDdEGP ֑L#3%A B5/|%:};}Y0)}8 '{W:{޽okozӛwW_{9 /pm83{w۽{۱c+@{>+7}o>F_uY!5g3Ҽ723~f"h$FZ4R&Vy>)+H"ToHɒ ' Y|nLؘw a CAm 8 8`D|kJ* lQ|ih q(Bل'τ4D rFۓ\HfJ$82^ Dc_&٠::ZhiHF zIzUHa׽JMqZC[3 p;O~8.˷jszW Ɋ]vqኃnm8}'Y\O $@1`XZ?Ε9ˍ_Q OӳXIYhBc)`d06Qkh0a4fГ d*Ov"pT#N#"QdPP#@N9#'"ڢRd-Tm""!W0X(00+ dzU, 43 ؞Ji rx*} gG \Dj@#FfJ $A$ jV!;/Xq1a/vx;\8|KQ?\x8 A7ܹi§n[ܵ^;\ jZ.[rGM gj_'?•{6dpm3QsRa@#FEdalb׈`x ^8`صfR JZ߻Ed`=^*HG#(SfH 1,eآL5K&(+Q~ cO SKajs"8QTә !0 =N0 "3-lP˂H~YQ4Դu$#Ls=$P* 57r^﮽ȭԺעpߪm=ِ.cj+j'׎o}[?|fp`9e9ByL 615F&fFbEcnH҈E,MTDh!1fH֛v" C( /-!Ma0K]HHr0G"OHEa)eY4FH^Y+,M{bLZmKOˑrFo f\$836n Mw>K$$w=p3Ef2Lf3Q 4#- vCfal'B qkQs}{0<\Z'RZ .)F#20G"OHELweWL̨PzT2fd01iXJτ%ȆSLKO`p#16a0 =It "4g gG t :~, ECM+Ps28Ih6֞+wk]o~n7\Ql ;L`۶m ."Zx饗\upEfxЯttX^!aBڙTREcAOI,MTDh!p`3|T˝aZ-][DNg +(JB`PfH 1,eVܢL(fb0cwõ;__uw6'~ ;e ]649>;zʽq3GcO;V1^ܞ'I" &'9Af4,lP ] ]]w]SÈ(-`ZحuxQ*q: wU[u.\,8&pԩF@pnʷgϞ^$@8N tt+ 43KFHƂFY B:]}/roEov w^ ǖZ-I玸;y}/%c! F#a0 =It "4g g?nB7soﺯnJjY-P^okz'p.Os 'cM7,:'½xe͜l=)@H^;jTh,)i "#}Oj30ZETU?| ˙ q,W[TT31pwO~ū- [ l;Kÿ{;ߞqgK9N21IC$$9G0Ќeݾ}/wbmtF.ң}}}>zs׍W\n&dpM\so9 bޙ}gB mBZM ,=DZ4]`eR*{=B3>'Zy:аkQ\S{(g~?c51i4K5j@/I}~I?(JcYw 7u_Kq~ }}> PJ/h" &W. B$5e_rX}}}l/ԧ>[D( ܯ}kUW]-9n<-30!{RQ)ҢQv|&*z"48v0> Hަj,c("QZ9zx,L67\zl 9.}ݿ'~iAS/T@"]AHj ʄ2~[Z_~pyGЍ>>r;@-|^>)0` رcW^>/Gq? '}{z۵kWOjLm߾r-*YB*Cy ERHƙ_TX6QA™kHY-L8d7 w @OX8Gr.}ύ+hG l9 &uMiгtC0$[ݷkܷk]?6z)8=_|n_8tP^h~Kr};7pr8 VQ> ^z鰽_'A5ځd -@pPn]B95&mpӆy$|u;憆:dL |=#܌QK;y2_\ qu8 B92!φw]q %<'|0~wwmnݬf;'YLOl ݻw^׺+rwa 'IpI]veÍ׺ߙ.Dvf)ըiXS(> c=D;G hW詀_߻E$~=i \Ti&[; 8f;_ŗq{Хa{R!=tL "!l{jR6=wg{w}w??G6SzX}EAĒ )6!{RQ)ҢQv|&*z"48v0> M׈ҸLSdX,-"383 ,LG?/~ݹ{[/aMS Ȅ9{U;ě?_ l ,9'xI2I{i//prp oji3)3KFHtR-Q$DEO #}O''A5ڱWH%I/B9'$JQp2h+ŘQz ( +7_¯M-w뼙~õn/ph`!;Geœ}-['sy0%|I*cgpʋK.d8  .擞ߴ%3R;jThE4dal'Bͭg;G %Q̜A= ORT#@$ Z`4xƢhAz{<@'cOr =@24Ad%0g_u[u'Og· =3]b7x S={ ?+#P[ 0j.hؘ8q┱`Ld0"-C|MT&*z"4PQ8s`3|2řd@nj(ᘾl RvHP)HbC q5,t6W.}w|@'[fW %o "CF&]p,׳}}7o?Fr#`'=y}brNʆܹӅ+{݇AZ7=wv&pIC- BH(я DUK&O,MXxY;;G $j'D>P{F=:4N FH/ RQ4CeAH [opwЃ~o߾%;R98rg "ıǸLȳnp'o@駟vNO kNÁf8w W#8p?~\=z dRaY RYljZ[D>aơ/\eC? <{Sˢ)kgJ ۡHY~,MTDhlHᓸ$4'JvADiD9GSR8 ';#9޶(@;{;xzd3OM^8 Ljń#(D0Lȳk;}}}v=|bf堷`Z\Vks9pXZZ?8xP w`DY5*EZ4l"eOdz06QAV#}O(@(i8NaSK#@$ Z`4xƢhAlăDR]NUW][,3#DHV y6\׾nk<Ҟxe{]̲NRE폗x?iaalBByX7efɗ3PC9' !^AG&H8hUhAlԃܩs˻oM(@رcŽn̖]@/& B,}HȄ2_w{ݫw˒=''''''!&@noYhThE4dal'Bͭg;G %Q'BsyOPQÉ|z drq$ h$ ika!O'-pO Љ+"(ȑ \O|Un߅8{OOOOO`NGOwwvfݨi8KJ &*z"4PQ8s`3|2ř 2>PH9' RthA@ƑoX sګw1|?lW\rvx@WDH RkUt l <~ߏ.,u"-g~iRb!DEO* gL#CYl OWBq8DORT#@$ Z`4xƢhA윋a)_EG Hۃ"BLq~y='''''&/47$ԉ6*EZ4l"dsILA&*z"4 ~ii 2RÉE|z drq$۶nEb\=_ǯ #}{vC]!O>;.{OOOOOO`MoI/6*EZ4}c|  >A 8v0tT2('rN,=+ ';#9fA~p Ls?,M㮾<L l;W\CWDH)g2E_hR!uOOOOOONӟyiڨidFdalgtv0t. dP7Bq8Z i!(쐒E FHm݊ι|Ouo}WD~LHP(D3qyiy@eP(GSgL@5l#@/$ Z`4x[f8Ku;]*='&p;WwEpd[~0 l :u}. ߺh T"5M"O]ńsf`9Ed*#Z dP!N@~/S a`!qFf9]rDeW$z|DהrvR-^XCoU{OOOOOO`cMq/tXmʴop&UbP#_I>wàpLL#}4$d*#Z 2>5~PGaLB!]֐ANG$ Z`4D0!m6XW_O+&pįHT`9;J^~wj:OOOOOO`'?4I@4j4a|0(&3mHj5L񩁟]S }ԌFO!@5lPN-w0G"^HE`6yg?K48p# \sU {M, r &zU߾x@@@@@?(f7BrJFCF|'L b93v0Q:JVDEOGNPG- ydtYCΆ*:B FH  3&o3E۽;;WE.y SrvKxaݻo>>>>>3O{ClC}='Dj4fg#_I>ɠpLL#}N 0QƧ>tQ} @>]֐AZGH8hx!1aBmփxE>v]0OM٩-uܗi):OOOOOOlO}l }='Dj4fg#_I>ɠpLL#}N 0QƧ>i_:UQI = #gCK:4N FH  3&o3E\=X7] ?5,g /@z>>>>>9?0قi߀d*\6E3y d4GD*Z e|jࣞF<`G-dH]֐͐p2h8B*b a1ۤG n/y%RNm}6߾=zb7@2@%RQ}#_I>ǠpLL#}N 0QƧ> j _<5fRP$Ḱ.kPFGfH8hx!1aBm}$j&pu;I9;uTzȩ>>>>>>4O_}* T"57E3y{ d4GD*Z e|jࣞ֊}I:R`r6 ';#/"f0@H?,n?@n=X9v$]٩փ}}}}}}0{}*  0E3y7 d4GD*Z e|jࣞh[O"貆 :'drq$T iAHFA>S!'Jϝ8xH;ЖI9;M?L8`_>>>>>7'O_A}* !f0>gΙɶi҉TB&G=_EP=*0e 9Du(M FH  3&|;> q.]I?0_xQKhmܳϽ "3Dj4"Ja|m1(&3mHj5L񩁏z5'dT tYCAʒp2h8B*be0ǃ0GZcni>qrvl<ǟ<Йe>>>>>>@{(g7hX(q#_I>ŠpLL#}N 0QƧ>Ii E-]֐AOG$ Z`4DeHha>tO@c XN%>r=Q8;xzPܴo@%RVF|&A1᜙l;F(H%TA`O |ԓ1*Z!gU<PPJq$rTTOda +Э ;~=̑+IjD(n@@@@@Og_Mj T"5j5`0>gnΙɶi҉TB&G=)YZ `B .kPCGNr0G"OHEL2$b0O _]q.=`F-[ @IDATVne?`{?{}ޛ/8ٟ=V7? 0(/${aPL8g&>J'R j(Shi@BEaX#@N9#'"fBjhVih qiCBO; ˼;?;v)nhCnV^VV/t۶K(*}u}}}}}}goG܇xtU*z"O{l`#OBv0Q:7 e|j࣎ "M|@BBOY8Glw8s@>&fkɓIݡs/qwO'NTW/58|y5‘ 3|'Lb93v0Q:JVD'EI'*4s*,'O&F>mGpwۏ̯s+d',VAW9z씻ޗE>>>>>>uϼgMz  |F'CΔ]0(&\G(t.GF&G}(B:; PA"09W(8yB*boQ^!+f5FHV?+,&^`)1}`GXN>:w)wv^?p`cw?EAd@@@@@o?T1au"54N"NQ!Ʉ)/3|. e|jN((T0;9J#r=*^ 3p$RC(yj>~VP sq~"DSI}wh >''''''h<id-`ratR0H&L)PGn>IIl. %O(T ;zk;|!hԉ ۟~`F>m_<>Ye\DOcthʥek{?ǟ?iѠqjatR 1H&L)XxItIme=%F@TAv>jDo(""v1Z<=_'NW~.wM҆9ps7 Wp ~7ý OOOOOO`'piSǗxڴA/^FƩQ#_I:J5 0`=r%}O%QAm' ;jk U !1ː(Go>Q9p׹*=8s'?'d@@@@@@~|Ƚ6qɴȳ/$ZcLR>'( 6k{PKvS<@ǀ` 8 8`DTJ~?Jx}~1W̓§/wϷ'mxcDJ},r%zOOOOOOLMW= FFƩQ#_I:J5 0@0^g$ l5h(y9@y#Q)3p$T,ߢBV(3S j"!=~W0X(`{׿;ofYA-c_嗷 nxC/>.%@ w?/|ta!]#RAԨ/$bLR_>'( 6k{PKVwKEMZf]@!GMژ#("fBjhVih qiCBلSq? Xtg+omӠ sKGiOYU>>>>>i#g~lK`p#5L -l0:IG~$I$6 #O(pYt#8 8`D[W efYMQ4R1ġ  eÏqnY٬xwmunfv3J'9TP@@@@@@¯_c7R؆/$dLR_>'( 6k{PKf;`)  -GRJ)AA;??yO lWwXbx[酞~;L& ;W#sOX {_ Հ6΄hTatbL8rQ>'B 6Q7 bL:,;SfH YEyPfDE#5CzZ`P6a)?Wx}a>G0C0aR{$*cyt>|)wx*@;OOOOO M \~!=?wvX | ֖( 0Ξ ̣|OEP:R4< &0kFq$31GpL%C'hy<,lv7M>~1 S0%r)^#?s_Dt Wܿ-\ݼB.RQAΡF Ç*% pQ>'A%ieECICPa.^s3b N9#'"fBjhVih qiCBل'icǧҖո1O?sO{|R :W0tOIw@8ȧO>>>>>n='߻o&HAFV^`L0H&!Dl`3|. l*lK0tZv9+H VFV?G#\ 1}wSԹ9'?xfk oxeS8q=4Y$?ᏽ@{OOOOOOxԽ>}g]fT?RRho-+GAѰUQ>') 6Sqo*d.>`) 般b+ !2<yr6h0"]%7s{=w yYЋaxmQ'=z wQHI{G@@@@@6p9WWM#v)VFC!pi# Ul6S$Q>'  lQKV(b= 8 8`D ".g`Ÿ?zOw;ϛosC>v1\p$,螸)wclN{O~`}}}}}~>Џ}Eo007f-[r{3${fępFLG pf#mԈ-*dX3aX/#Rio!8 8`DTJ~?J  U%?1l_8_>2,Y#0έpO!y!`q?v۟ 8w ?|5-hp>iaU#΄NECKd, sk9pN_ؾ2"=jKGϹ [^P@@@@7|A裏.yv#51UgFX^;Pmmݺ=6#΄xIa-AE @tBj€hv+]?([/#.SfH i(W efQ&ҨS2Xq6a0T+Oܾ Wݭ7]Hz_^WYxZ9r8"F9f q"ys>'Bs#mԈ]*dX4F/br4SfH YE 21>nz-zZ|ߺg^7,&.#/vn\߸=ѵʲ|%^7ce곀[n [89_7vq}O_|'G]C%5*prd7e׸է9g& A3װZɠlsPKFL` %x 8%$ F e`D֍" xPffl"!=|-g KŃmvO^7N賅YZ~SOW;gG&ϸX mo{/~۲u;/˛JMڅ]/v%ǯ6za^o=ko=[Z}abDR`ԽO #}Z݅V{wEcAF #,o$l].=ؐ#*YɷyTɠlFW!#@Ś +E#"H RS ~?C  U 7s=sok7%Z8Z o87S83=B+gHms/%.ڷⷾ}W^O_ hYh 6z}aѲ֋܌Z"m}gng~g7#a#fQ)ҢaF #,/)+܎.>L"gIF-7AF؆BF 5K3 bd0R(3p$T,ۢxo*'A@ JU`lb(+gA0azYCŷ"z%ݥ$;Fi(grqsCiAiApIn7ToW|_OpsepBzqֳ2o=kYi>ymK2z:[?~}g}wɴ&Ky;Dx>ШiK(CM $ʵmHᓰ5'Jb&, |2T !t:"H( ~7?r􃯯iAvkÑzΛҤt|}p̯W+ͯŀu [/zZ淞g>0g1Ou)[fYZ/v?;xXZ)hmThHEQˋq vm]ٕA $iHE=m$_kB+tD$P2)8yB*bmQLܹ6KL R%ު`P61 0,U`O۶/z%Kro]oV> pZK=_pL?״G N)H0صF#J||}B箿v{]vM _в[8zqֳ2o=kYi>ymK2z:=+3=¿|~aZըiP+a)жyIX%1EP *`n<P 9I$ F e`De[ wMRd5?T*,M el0&L/K8o38m|D&NnP߉O~#ϻz}㏺'|iLaMjX'1w\vy(ӠD|}Qپ}p߸]{wRfUZ8[/zZ淞g>0g1Ou)[fYZK;t;pۿ{gp= ^p> FHFEj0ruunwWOpIrmh>ܩmP8\9!ҁ)h8u{_wQw wrU9;-2H5&קi1VV]v{Vy?v)w|3&z,]T'Dl/ԲB#H몡/&B11iИBDƨY kicRmqmxdוN=NH%YFQ:ӽ/*~REC#FX^IH"kC>'a9k OLYB *d0Y3mYGD0 %B#'"f XqTP'=f32D4L3r aS'>v ^4N:^~1plR~5gEdA6p956tڟ@ڲeO,’H= >]Ԁe)?,Ԁ^5fLaTDM&,#&!R44u@|}NV$3*N+1YVkcY+u l" R`Cj*Xo5)dh6pb\Bc Nda|i~[QJTk.Sjͬ6V 5jh:SH9!{Y"-po0vimu_NrM%X&y^.hj OLV2 |2TW,\F\`dEJC3p$ KiwM(f4Wn32XE:qa =yzL5O?k jB 5jV9Ơ"8³QOhԋO)Lu.$d;='簉$_H PcHn薧UDBs )Dd8 n!G)EPIͺL4XQ`/ԨݦttXN!P4K fhThQ )s+O]ɾ#}O 䉒b(a_vR0 Dq@A> !@ l׺Y8A`>ƋDӌЉS 3PkDfTs<@ZԀe)?,Ԁ^5fLaTDM&,#&!R44<&@diat6Iۤ zNg9HRSz#-OI=$@Sp" K+5(BR^u:T%hf@_QM/,G谜B*Ρi3@2Шi(+|SF'9v0>bDIdeĄBF c(ҦDv@A>fHRl2>fNVW?fFn43کRĨ)5gj g}? >BubdL3= }5I4=+$_H PcHn薧UDBs )Dd8 n!G)EPIͺL4XQ`/ԨݦttXN!P4K fhThQ )3rK催mHlh OLVҭ |2TV,\%,N2؄ !&Nl׺Y8Ǒ1m,Mr80FLk&g5-j403 B A٫ rwu>C 2L#3 Io]PC6$:pr16[ )A!5j7 C 481Yq.1 ' U4(BR^u:T%hf@_QM/,G谜B*Ρi3@2Шi(+|SreB«CN>eHᓔTk OLV!nt |2TQ,\]vY ) CD%=M $ٮuT3q|IӖD,׌ЉS 3PkDfrVsޢn f&Fdm!=BU9ql{FmdLKE!iT%1\M BJlHM덴FrC<%&NLBKhL!"É,E "f]r Ym(0jn t::,sB% 4*EZ4J ߨa\qy!?yR%1E0 |2TM,\#@@,B! zԸ(H ̇j>,Ljb>}D,ԌЉS 3PkDfrV3ۢJm531+ rַzi?4Kd9?C5j-\M BJlHM덴FrC<%&NLB0֢JF[QJT%N-ƊR}F6Mr 8: y\1@REFX^N-;r)ņ| #}OJS2$1p&ßLkE "K؝Z'Bmza9NGRqu@HbFHF)\5++{O] 6s`3| 䉒ʄ+L_vU0 &W`#L`4x)';Tkj&N#|zcF cH3B'N5@'Q/Ylt[9/p|zhwEd M{F$#F &hL[Wj.ٛY̜3$Aodoٲ͟#S,ԁ+$ B RSƚ#kH!DL3fXc|-4h4PA%|]r>ym(5jn t::,sB% 4*EZ4J ߨa\Ni>E>')"@(B*d0Y, $hēAP&w0GrmcQ4 vZTɑf8"w`bD,܌ЉS 3PkDfrV3ۢ2ܺp Icj@(QªEdo?k?Sig՟! zu+Ă-PN&r`3|&,-HMkO]C* &FLf"0kQA-(*vkcEPv^Xa9TCҼg.eQ)ҢQ WF #,/lmOw和M)0>IIDIdFW!#@ei AB>@ (;#9޶(@;@Y8A`>a`al"iFĩ(5e^39 Ly6-[fxĈ-5gS@j g}~VA lo5Wd̤Y=̥ONh,SUDlτXcMiAcT<260b2L?,'_*  nG)EPI/_;O^+J r4)ꀐ=s ,JR7jaya;Fnq+nu$c| #}ORREb5k^4Pa˨l"`d&™  3YN1U4(R^.awj96V 5jh:SH9!{Y"-po0v ܺu۶zq.K<Hᓥ 7'Jb&(`*d0Y, $hRM' FAhE;BR{#F c\3B'N5@'Q/YMMeʜm{y Hjj@O%Ӏj g} ^E>T? u%7= b[lG 6 8>A 5kYQ ^Fc#&3dSrbҠ@pRu SA䵱@_QM/,G谜B*Ρi3@2Шi(+|S$t;֝װ@k OLQ2UPal7pYH$*:2kKGYE%z̢6!zJ6X*0a?3p`4f*Hh`pED5W5Ed5mElۣ`igBҐ FxvU =g gτ Xc͖ק& lzMD8&ЂZTihp 9J)J:+RWˑzm(5j_Uh::RS3Ҽ,"-po0v\*gd'a%'n%balb(*gY zu2ZF-[Vܶ|&ƢԀe"2A?"pxuut=]ٴ3$AL>V_ӱLάI`FJTfSe 6&FLf"IPfrk_*  naG)EPI/_;O^+J r4)ꀐ=s ,JRgM$[qR(D{7w9-g6s2+7dFR&-Ç?ѦR̕ᤌ [sVdH' B8MKmiX+2WMX٨/گ;W']TF:ghk^Bg3;F}uQU"_~W(}tn;7l烱R+(hU|_s]5ITsA&WAd覬;isY"NX(qi\R$JS63.;h  /\һ 3v?0a;FJߜPJwQI6 '3298o}7DJF>FF-9+0 %"i]k%3-R$׫QnڔHF}!tul/giIQ.(QvkZT2bx'ՈֶlG '`}>-;neo>pzۜ:^^wb %.KVD^bfƦ<:d BRoޝFT?}L?tqViäa IDwo_}7?xQG)pr-9+:%~W2Yiqgf@F>zETݴ)B8ϨzuEeC}ދ|5-tv*{1sjDkgˣJveX;\Ҫk;7txpl]'vzP^"O e DrV\tRh_N7jjdJ2 ݔa3kS(qi\R$JS636![\?zp5acpcӡ}j[#m6xOf$erp+_t0/., 'os=0wK\6J]$g$4m ѩYy3QcQ_cc]-PW#Ou4 پ'~w nN#hO2bv:Jt Wű~k [jjno2y%NnÙ5oszy݉)4.)BX % x)-. Ke8N{wQ0c1tTYG`ӡ}j[#m6xOf$erp+?|yC_xQ[Z\.eD+䬖!` ̅Θu+F5|E-6%RQ_GBu'ꤋ(QQvkZT2bx'ՈֶlG lǑYko!Ao^6]m =bv_?7CL& UsO M&$MYv8SmN~\/;1%E+_@d/_1e3cSc iy\#f<*s:Ը{Bm+]zaҰoɌL[b/{aeG1y[Y=/%2\$g 4m`.47pƤXWD MTlBQ~m:2Jԡ>{ET]:;9I53%z]c"3cV=3b3$#?Xwh~9Zy=>޽fiuڤ;o_[~_uQTsL^I>pzۜ:^^wb %.KVD^bfƦ<:d BRoޝFT?yL?UtqViäa I\Xy}//VvY=/;%2\$g 4m`.47pƤSXWD MTlBQ~m:2Jԡ>{ET]:;9I53%;/eꗴ$ d;8#'AJF )|"j'Iw*rT͋ jjn?o2y%NnÙ5oszy݉)4.)BX % x)-. Ke8N{wQ0c1tTYG`ӡ}j[#m6xOf$erpΒS]zO{V.q톕Ťwҿ]t4sf T5ldB߽}?s-bsJ8fNԒ Gig|v8S-^^w"%E+_@d/_1e3cSc iy\#f<*s:Ը{Bm+]zaҰ'?)CsmοöOKW2YQogW:~` _|@7mJbe::6NnIQ{/"״٩dNm.( ?\?WD!B|E'h|~U񋟕 "h4j jvO̔ww]jjn9o2y%NnÙ5oszy݉)4.)BX % x)-. Kx]@IDATe8N{wQ0c1tTYG`ӡ}j[#äa ިr/Em 'H ,+"sE.~AIji\hnI .XWD MTlBQ~m:2Jԡ>{ET]:;9I53%;$䟗3B}O2h 9Qɟi jv?*_bh&ӡp\A2Ua3kC(qi\R$JS636![\?zp5ac騲8h=C'ԶҥG,N θXYPLܶ [lp%2$Ӵ|"cE9DSn*Pj qQu!,2Jldx=EZT2bnkÌIJy[_>L}g%d'= 9?sVw߾T~G3iVLTö D׿ W~ʯҒA0ǃHs;yYkzy݉hJ\!~Ŕ͌Myt2;sj~:,#0ZP>ԔpZDu!dRA('bߔ2gԎ}qmA@ˁ*ȸhW2Y-C8MQɯ7;"-Զީ/6'He؉dx1-tv*{1fCsǓIη\&}/K̯:)}$dG% A?sW͜YTTö /7?(*AT䶨;,#AwKK"/ Q௘)Ѐ~[㴼wgU3vOGqF9j:yJ# eXGNÂ?)3rX?~VK;qmʂb%.e #M0N*~Θ7cjhjj[ԛC㰍t<R%6v_ZHvbnS'o;M݇Ͽ}Y pJ|O2 A@qʛޣ4+Kjv8јK7` {I\v0Yfg䔸t.iP %2{Ŕ͌Myt2;sj~:,#0ZP>UjQ):p$Iwᆲߗ_ģM3^XYPLHkPtK7!.o8M{8c6.XWdqEZmSo֎6'He dƗ5#+<4ч~L"ÕhRw?O@ie*jI]'A09]o͜YTTö ͔ouWb2ʐɖd8)f6ˁqsX>wp B \@)Ѐv0C>B׈C&usJtpjM):;t'evx޽?k_byڃń6A@ˁ'hyB2Ӵ_3&k154H w߷ſ>{e\ =qnߋ1kC"J49qσ'7\dSg{b;a4:?gU~m_܃z_ߕgO6a'^qIF/,r`|-j0+0a./_1e3cS:sz0cQtTYG`ӡ} ?Ru4,H2;#g~RQ~q/OжcJ3+ kwniM&e iorgD%:cgCUMt}~0>ALH5܎яD+Ѥ?!ۯ߼W*(ի/t$hNY_~ߕ_O֟;9MZgB] g,_x߁,doo>`f_{6wrN0 ƚ_ԿbfƦ<:h BZ>y>C\#%W;lHǎV)є{2C7IRflXu/?l,qgu>BE}-r[d t}i[ůVE)0R| quUi7J)Q^ p%/,ǓIη"\&u/I/~&̛|P>w|S=+q;a4w—>'/w'} 1-04HX[ .3fˏ94į_}fD ^nq;Uf90zGaV a5Ť͌Mw5+WΖjc 8P3:%rS=saRuw5OmU]??{rښ!YYceA1!A}-r_6 2ZބP̾4WnHRtٿ8J wqԡqFQ\ۀ*Dr''9@ob*faQ'oE 3Mjۯxm#|]^Ŕ>0h.w?w=p4sfܧ] g,\q~`fb 4/kr`|waV7'a%`XC636gԬ>&o8wQ/yaC:v4h:ԌNTw؃ayM2;z;?3Ĵ~NӬn.Vö Y+ow_y&W~f ѷ׾Y,F/;  B C)ڢ$xk(|^ &]b,(&<tK57!.o8M{8#Ҫ(&[6+FO ֪̀7kGQG{xIgm RKB6LʁYX$[LDc7_ W~{5s>/b vZFC6aa񋟿}o5CLV4*lm Wǜ]_}޾o+$#Ev0Y/ ; 9'A@xc/_1i3cSE%CI(Pyҹ.׫6G`Qigw΃ayM2;߁߁뗧}I˷6~t~Q/ƿ2~#&~>?o? dէC3?߇>XVߩZ_ƽO__]Iw}ܝ>`MyGfjFJUiQܑ4;RJ$w"~Sm7/Ƭr`x2V2+ѤXō˴zw_ꛥO-W/ux'z9 tw}~GC_#RE4*t] g(F52R7VoG9:l aVFK& Ca!v %IκK^6vؐ.7$F8~BӦ=HQd'er\oޗ÷_g=rƬsb* ':Pc>AF@7!-s%`ܦJ\sgD9n@nYެf9G22̀4jc(R{LM&8cxjiIIg$+ok: [D>Y\UNAN}moB([pV+6`$7RۤDRo֎LmaEu7wՆJ)܉ ϲ%'ݿcV90 z<|+ɕhRw,eZ^{0Nv1T+B{/c]+@5҈Vtn]_}bU#c(!ENuC_l G90nQ%dkyda8G.A{W;$ B+t56`tu?.3ڷ8/&DBQܑ-2֝WdAHD^p![bʁYX$[LDc7.ZihRSx^EOсhbW\t]&+V52RԎP*ucg4w>A{K]XQ! 't&/*< ¶eVA3%lp_ނ4se>9Bs ~|grtjPcxjjII+$F JqC0QYN<PcsxPL͕ pH:0o3:KHYK zE.RJ$w"~SmCb*faQ'oE 3MZܸLkk|9_L2x^E@0xМ6N+aW .C )rj(l:C 13\G; ՠ.,(`@BNO:Qa[Ų EDd ZXBRY$Jb|-~:{5cx(ӕa%Q!(, 'FÌ]J NYY+P<O(Lkeهuݜī R"eK >ݿcV90 77gMj a!&i%ae|bU#c(!ENQg0_pHsYԅ HȩqBIg0*lkX`9@蔈lq_:KDsFkW#+9gg09W0ʨ=6R8OJ:]&1 Q%plY=% B٘i u0x妁,#s!akf(QT\wvco8ɝktGVYX g@Dc7.ZihRS=REҁ@b%S6nU/H5lLWjd %ȩ?T1 5T4# pi0+|T C 90Nh}ҙ4nh5g\twds?gJ)$B~$ ^u:{5EF2ΓNWI;B^Bp%#pjVc-PvJCI2pJ`FApw(7R9? K\R!Jϣ#SQT\wz6TJNO(ktGVYX g@Dc7.ZihRS=REҁ@b%S6nU/H5lLWjd %ȩ?T1 5T4# pi0+|T C 90Nh}ҙ4nh5g\twds?gJ)$B~$ ^u:{5EF2ΓNWI;B^Bp%#pjVc-PvJCI2pJ`FApw(7Vo41Qܑ5\J)܉ O%ʁYX$[LDc7.ZihRS=REҁ@b%S6nU/H5lLWjd %ȩ?T1 5T4# pi0+|T C 90Nh}ҙ4nh5g\twds?gJ)$B~$ ^u:{5EF2ΓNWI;B^Bp%#pjVc-PvJCI2pJ`FApw(7mJ Qo֎LmaEu`A)@ƧOߋ1E=LrJ4;hq2Uo+(b:V!{ڞ_Mҁ` !Jϡ9mҝV®]&+V52RԎP*ucg4w>A{K]XQ! '>LTxFUe3tOM.AM:;23Hy!y?Wbx/:CFAP&TF 7[K2FN%#pjVc-PvJCI2pJ`FApw(7mza~`|N7kGqGհ HN ~SmcŘU¢O&9ߊ@fr% q֪7VhymϯF&@0xМ6N+aW .C )rj(l:C 13\G; ՠ.,(`@BNZt&g*< ƪŲZtŧD&&{R| + C`1ib^M`QZ aN*Л%^#BgYW85(;% B NQ{% i ;6TP? P>eԛ#SiXGQqݹwrAHDƧOߋ1E=LrJ4;(qto+(b:V!{ڞ_Mҁ` !Jϡ9mҝV®]&+V52RԎP*ucg4w>A{K]XQ! '>LTxFUe3tOM.AM:;23Hy!y?Wbx/:C㰍fxgg+2Jl2ˋ0shVc3vvG4A(3m.cFvWݡܴE0(Ff(QT\wtuAl8ɝbyQnIňWy`x2V2+ѤXā@ұ־gEXk{~5z4AFH1+D(=IwZ jv(߯XJHS;~@dcj?iG:aV-ua9DAFr*`39Si5V.zk->% 75($~#SH\IL tj6 |᝝(//p̡Y=!l̴pȺt m^wrӆjwXЛM_ǛcX_g.s R.೗(O%/Ƭr`x2Vy&WIy7|J,XGt/`}^󏱪2B:Yd&ݭmU9J6FØf{d~#fjRCdt0 ! O:3QcbYw SpKPLb}=R^)>DHϕ!˴p6rqFo;&gƍ+Qbc' Wb, 'ÌML['K؀Qpw(7mFq'6_Ǜe@6(>!t2{l8u$DA`|-1{1fǓIͷ"32M 9ЭWWbQ"{%E,£>XU!,ֈ Q2kVTö~ŪFPB @%PCuaL=2 בG5ho ! 2:S'ə Oèv,;]sn)Q%IGqG&>s)B"$J%(eZm)\.phQ<>EQFJ n1 0.n ⱅ$I[sV29_' B] 8D+PnPNO7k:']b,N^ 0>ՖLʡQI"32M 9ЭWWbQ"% 75($~#TY`%Fϡdxܵb(cTmJg᪩; b, 'e:g5b?4A({iM؄Qelpw(7mFq'.MxF0.BgOhAecH J~;6$RJ]&upVo+(b=нym?ƪ"@fFdW\t]&+V52RԎP*ucg4w>A{K]XQ! '>LTxFUe3tOM.AMڎ6]j r,P2W~nk<1H[*6 L%3pTF@~y1W|`2[EJ ѽӴԦl¨2f ;VUP 1.͚7(>!t։+tP%ƨcEl3Bg3bx<ԽRyFWIy7|J,XGt/`}^󏱪2B:Yd&ݭmU9J6FØf{d~#fjRCdt0 ! O:3QcbYw SpKP z`h.=" h9̕6B e얊C㰍BA{K]XQ! '>LTxFUe3|,8jlMoncCs9 Y`%Fϡdg!RψC㰍B{E,Z*d/`}^ѣ 2B:9^!2D94MJUöD~ŪFPB @%PCuaL=2 בG5ho ! 2:S'ə Oèv,;]sgT@!mmml`h.=" h9}l# QQwhQghgeTOe dH+ I5 7nY&et4-)0xUBs~@U! WČ\ڤ(1&+bc:;wl3#J"B莅J8$k|}'{udmuAFHgD('Uڤ[jv`߯XJHS;~@dcj?iG:aV-ua9DAFr*`39Si5V*j.5wI$sHfK6`h.=" h9}l# QQwhQghgeTOe dH+ I5 7nY&et4-)0xUB#? ުA WDeAeӎH J~;6%rwJlD tB% >[>̽:2: #3yR"C*m]-|5lLWjd %ȩ?T1 5T4# pi0+|T C 90Nh}ҙ4nE5Z;hXH$9V%z ot@04SPaVbJ>xb(;4(3U4322Jl2QM^̕ᤚsLMCI2qMUl^wrӪ J!xu9*IQb}q. 3šY#*"  %Z;*qtoْ dבᖷչ!ɓ|Ti"oae}bU#c(!ENQg0_pHsYԅ HȩCſ3 ,qml #BhLCj5ۄltn04SPaVbJ>m$J=#6nKe dsea8G-9U$ B8MKm&*ck/(rӪ J! }3? ު啁n]++2JNJgN%c?fL;cPcIZ-iI}ny[kҁ<)!G6.]&+V52RԎP*ucg4w>A{K]XQ! ;TXQ>0ʢ׆2,$9VMx/ɶACs9 Y`%Fϡd{FA38lxȘTF@~y1W|`2[EJ ѽӴԦl¨2f""(7ʋoՎbHt(1+bc:;wl3#J"B莅J8$k|}'{udmuNC݁OW${F|&6~e}bU#c(!ENQg0_pHsYԅ J&IɩIſCRYTj޺ t9VMlgpFTY`%Fϡdg!RψC㰍bS,53 [*N @z!\YNa>0wtΊ ?4A(iM؄QelE2EPnZUA)? >;6Oտ"coR$TF1X٩dnjc)Q"waJ@w,T Xk%>ܻ#-osu2|x Q36.v.C )rj(l:C 13\G; ՠ.,_P0aMJNeL-,ŨʢU dM!-uhb<32&(+1Z}%s|<1HzFm;faR%6v ./pR#[sVdH' B8MKm&*ck/(rӪ J! U| qޱBQ{&2JYNJgN%c?fL;cPcIZ-iI}ny[Pwӕ$_Iw{GG )rj(l:C 13m(q]'{0aM`NeL-,ŨʢU dM!-uhb<32&(+1Z}%s|<1HzFm;faR%6v ./pR#[sVdH' B8MKm&*ck/(rӪ J! 5oG(&lbHDAeH J~;6߬0*CN%8D >[">}udmuNC݁OW${F|&!E``Wjd%ȩ?T1 5T4#OFetɞ]Ä5917"bZ*jVm4w[73jԡm Șj8" h9}l# QQwhQ|f!tKe dH/$+ I9nY!&et4-)0YHM*(`4-ccX+b^ڤ(1&+bc:;wl3Yab3U-НJ" p;|D|'p\ ?$1HM C+#-osu2|x Q36.p(}bU#(!ENM[0;S51Ǐ'5O5zT4.)BXI& ˩IſCRYTj޺ t9VMlgpFTY`%Fϡdg!RψC㰍bS,53 [*N @z!\YNa>0wtΊ ?4A(iM؄QelE2EPnZUA)__\B-տ"]*x}FZT2cFޱYO>m$J=#6OQ0n; dsea8)-9+2$Pnb6E`F5@AI!`n\MV_IQb #-tv*1#,fq=a3E(ݙ,ugK'kpض(}q2~x'voIEu7a5ģUL95a"t&T8:~O>m$J=#6OQ0n; dsea8)-9+2$Pnb6E`F5.OOj( XAܿ<xj2(.*Ę{}FZT2cFޱY8ug?<ՓqѰQ@Y ~ŪNM&$# arzèǓQ2dO./ Q:!9(,(YeQkCdsmBcR&d۠sj r,P2ɽl# QQwhQz0nYح"%M^iZj7pf[rE6:AP3K.2J!>{AT]:;9I53"l`%Z;[7l y$s-NV"_5NCOdw->f4xcqda"LNoquxRT:Cw4J\F$J'$ſ3P+";譛RR& 38#crr,P2dzDgDݡqF)j-Qbc' b, @IDAT'0Re:gEpJ&9`BipgLӓC!hOAگ;,(QQvkZT2bx'ՈV8ug?<ՓqѰ-WTsA&W09zaIS I(q]'{(Fb\ @h #nZbsHfK6;ɩ!8,JVC'6B uaΧYjfBTF@Bˋ0H}` +䬂 m] 1!.OOj(YkJoUuLKuo׼N%c/fwRhmvy@NCn]E@O"gK'kp\8uۏ߬$f(IwC*r{RTs !x+HDd=0fk{ui.ٓ HNHN#K1Jg.VD4w[7-Q9cvS CpXd>O>m$J=#6OQ0n; dsea8)-r_6IdIji\hnI={r!Im[(Oe4WժbNu{o׼N%c/fwRhmvy@NCn]E@O"gK'kp\8uۏ߬$f(IwC*r{RTs !x+HDd=0fk{ui.ٓ HNHN#K1Jg.VD4w[7-Q9cvS CpXd>O>m$J=#6OQ0n; dsea8)-r_6IdIji\hnI={r!Im[('2}ժ52J>{FT]:;9I53%;%S0umb|^PᏯl>*$fJI+X 1퐹65UL^IF&&QǏ'5O5[۫3t'Metɞ]_@tBr*PXQ:s/"޺ h!g/OGxD МW:g6vN(6ǧ,ey~ QL$ ]N1r'4m 1gO"$=q('2ժ˄XL)Q3}KT]:;9I53%;%S0umb|^PᏯl>*$fJI+X 1퐹65UL^IF&&QǏ'5O5[۫3t'Metɞ]_@tBr*PXQ:s/"޺ h!g/OGxD МW:g6vN(6ǧ,ey~ QL$ ]N1r'4m 1gO"$=q(ߡ7ej]pIQ`N%c/^_.c?LƀD\|ϖO2v}EZv?7kc73ĴCڤ;?[g(V5wo2y%iӛgF?\SNb ̔?Ep 7mhm]P[g(V5wo2y%iӛgF}~lp$90v7"t"_jEDsuC^:j99 (u>+14+l쀝bQZmOY9H)-* ɋSdZq608cRϞ\EHzR!(fDew?p@Wx tQ%4"*߮yM J^h)պ6G @1 dƶb ̔?ۄYMM2TVPj8dJ20&7Y8 H\s*`PoD`)FEԊ2z&%*6tL?38#Cs*s\P|VbԟiV;Z?4(߳sR[TXYGceG19ŁY=/ %2\$g 4m`.47pƤ=-CP̈Г F#Z_wIQ{%"״٩dNm.( (YksT'|V;--8E ):kiC{LӬrNrcf)'+V5wo2y%iӛgF}~lp$90v7"t"_jEDsuC^:j99 (u>+14+l쀝}g[C4ӿf*Dr''٪y1VvxIji\hnI={r!Im[(ߡ'*;w1ZLuދo׼N%c/fwRhmvy@NCj]#->̵ZiYl)2eOс(\MګԢ,g(V5wo2y%iӛgF}~lp$90v7"t"_jEDsuC^:j99 (u>+14+l쀝}g[C4ӿf*Dr''٪y1VvxIji\hnI={r!Im[(ߡ'5+;r1ZLuދo׼N%c/fwRhmvy@N]&u^/ٞ db З^D*o^4=] g,\qsS.re܊,΋c}dZq608cRϞ\EHzR!(fDewIꚶկCt.*D9s I J^̽|]3& +ѤƐ._=>\Nb +A7TzfՊmhFB ``zWgF}~Qp$90v7"t"_jEDsuC^:j99 (u>+14+l쀝}gQܾ1+=.WIȭ9+;9ƭj'J&9eHis3&U'mbFT~Ԭi{?P:DwwltMu޻o׼N%c/fwRhm/UbDS|O$sM;--?2dDpߴRiW+]*5SE[)蛻gG3cV`a %] sy1VsDXtK7!.o8M{8cΞ\EHzR{@9Ō =YiO| (4LH~o׼(ݿ3ǓT#hxNdMw#g{'|&֝>`St 71~ӆ*Y&R'/\950^YWƝP B \@FT`߈RA] 0޺ t9cvSUBPP5VcTqV QF̘XBI+$gBDE`u^V.e #M0N*~Θ'W6PA1#*COjVdﮟm;;IQ"xY?9E莬y>DS|O$sM;--?2dDpߴRiW+]*5/ѷ}gaűQܔ1+8I+$g:DE`u^V.e #M0N*~ΘyΨA6PA0Aߡ'5+6y_d7;N[xtQ%;?9b*1ЯHdMN r>S?5,Ȑ ):4~J%iV\jvY+` ;TeS;?+<'A@8Kmnj28cv/1ND琳w02KϩqFjb|^dMo*c)3cVp.WIu9+ 9"]NAF@˛J7`U 1iQ6==l`COjVlJۯhxGQi)i6TJNO`|-1x>ʡy>-DS|O$sM;--?2dDpߴRiW+]*5.a; 5 J)Q%! g%/fՁYX}/hni})2X)C_Ջe>WEߴ iV9gDvp0 pA `v<[߁㕪lqgbrg$`y%(dab+d?H&֤#y'qs%gy Z343fP8QX:gceA11GA@ˁ)hyB2Ӵ_3"RJae3;~wof_[~䆃J)QKBgnLʁYX>o)7$>ۅS")SS }):iC{LӬrPQ ( MIGqGzO+&J[)蛻gGCif ,L˕q3r!"t:/ʂbbniS&e iorgDZd˼gwuy'=+nߋ+T#X[1rC]|1%25ez,Q6W4*lR.3f89D2 x;p`RM5LLNa5>ş2| ͙P lk5[# }zuEm;+9[lq1ۥy|L JQGERIC&8!?F-hş ~\ ( l**V"m(~u$ss(Bvd!?Y-c;j{%]&mظe 5/ʂbBqf O&ivS]e_mͰSR2.~{\>COwK4UxF7ē~?W#GQgJMLCb( ff`1؆-ffyOvS:Į>ۄS,)SS[a):YV\'URava쌉#`x˷;aCOwK4UxF2Q~p_~4WDSjgCax%1k1j 3o')bWmB)唩)ÏǭeQ,`~VZj*ӰbgLQNpA #zov6GƝIS$o`902d$c l\N =R ( &YQ떉r7'}J3k*2jf'%&mCeIu^f S&ivS]e_mͰSR2.~{\>COwK4UxF2+on?d! #Pݻ%1k1}xn%~8ز@ЕHRvVƕKZ=g[<!)ÈǭeQ,`~VZj*Ӱb7>G22 ow`x hܙ$?U@i&B,d40 $Fd a 2@3%ؠ?+j q2Q.ro~[i3x&݂Z9~-U=9[ERF}=֌ӕaJF `vdal$r#YeOp r606 K=E)%323;~:DSgD(gGhPiCaz/_QlŇ~;Ml:?L(LM~crJ#`x˷;auԟ8/yR8>{H X$-? 3o')bWmB)唩)ÏǭeQ,`~VZj*ӰbM~4Qdjd@b]VpzN)ΔCb( ՒLl}-3t]} XS ?1FSt Yi1NFrBOòk11E)EcL0,DvA ;z: (,RUy.-o`@zN *,(0efTgJBAVvAe\ y%~fLrZzrm#zII+$F=1 tCؤIF OA8 ivS]e_mͰSR2.~<@zzCJ"T7+a8_MIn=$€JT/_gafd7CM0Ţ25e񸵌6јeJKq2Rz]S)J.c\F7 f!pdX]2M?4p]b߀  ʩA%#eerL1LCI(060ϊ.H\L|>9V IV_KUOVQmd_5c?)te('ҀQ!nYt3c74<<)GTA(r@U˕ 6ÒNQJL_Op_@DeR?ܱ ί&?j13g0`LlʆY^|﷓ݔ6rԔ2F(DcU0?+-5HVNiXvM13(EhxIpS~#TIT.K撺PUB1]*kAFr*`$x$c 4Te CՙP(ϊڡ-o 'O+&﷕6g-RՓU(encOJ:]&14`njG&L"7 ? Ox UʦPnr%@1Rg\JN|xxv2t Jm2X___Dx5鳇P0sI &6ZZe,/>nJgaE9ejqk#l?E1*d +'4,S"]4<$ȩ?7ufYTKiv\6c K!LB`r2s*`$?tgQf2@3%ؠ?+j q2QZJ%D;`Tړl&i<*S3NWIa( %ڑI7ȍ1vOpDi.[\kl(Hpq@m@iC57X:u t Jm2XnKO)bgCap7+ [7t]} /?hRSVxZEOсh̲ g8 = .s ǘ938Rq8.K_DT A0t  ʩ !UxFm.'΄?cuDkO+ao+mϤ[P+ǯ'gPʨ6NOJ:]&14`njG&L"7 ? Ox Uʦ8ʭڮ؀Q<ڀrӆj=n:pu2Ҏ(`aֳ/ϔsTCLCb( x+9Y^|﷗ݔ6Mj [a):YV'#UX9a٥b|`EcL @%PE5fL]2?#T* ! 2S#AC<42\N =R ( &YQ떉r2W/!Ҟd3xOaEV6mdt2Lb Ci(L7Ԏ,MDn~# Ms;j|6TqЉQz J^$՟I<}bg0`h`bŌ03ˋv!v&IM[qk#l?E1*z9 .+c\ԎP*u*7cpv U5]XQ  YarL1 HcӀ -:e>94﷕6g-RՓU(en'% EP0J`7 # cn&b솟L p;0:gPPnPM@'NFrD-X+]/j~~A ?g)€N`I &6ZZe,/>nJg&5elǭeQ,`~VZ& 9 .+c\ԎP*u*7cpv U5]XQ  YarL1 ]S"c&YQx4e'ȴO=frÊ"mpJ5c?)te(ҀQ!nYt3c74T (P&8s />{c,@ȡ_3 )%e1볇P0-ī%1k1|J%Z6Ň~gMl:?L(LM~{H }D$-Fl`CD )bWmB)唩)ÏǭeQcW VzIva٥|`EcL @%PE5fL]2?#T* ! 2S#AC<42\N =R?ktJdlq?+jL5$O+ّA~[i3x&_KUOVQmdot2LbCi(L7Ԏ,M4c?TA(3u.cF_wrӆj=q:pu2R#Jo2X;_|}?g5  ,۰:|,/>nJg&5el5;k#l?,Cr$;iXv(/X"vPƨ3TQͿSm̏pHU mr$TH!NUxFUe3tOMeb~G+ٗ mA"iiŷm#;cOJ:]&149Ķ069M1vICI1S =6`T:np7(7mF' W'#E9,I+??F3P3G7d&7t]} /?hRSV5Eҁ@̲JK=$Y=!a٥b|`EcL @%PE5fL]2?#T* ! 2S#AC8UiUVN z5>% 79gVܖomF)d_B,$﷕clx+JV0i}`M5c?)te(#Ҁ焴W5(;% BNWݠܴEON,\[ Vf7|+/o%?gCa4WKb0b9Jlދΰ!vCX%r-`~!~ eΰq]j/X"vPƨ3TQͿSm̏pHU mr$TH!NUxFUe3tOMeb~G+ٗ m0ʴpULZ4 [mUliLjzm #BgW5(;% BNWݠܴEON,\[ V}WQ?P ۷ 2Ca#XKb0b9JlޣXXf2ݬƈr{8|)Xz-`~h0O<4;z]""z3t"vPƨ3TQͿSm̏pHU mr$TH!NUxFUe3tOMeb~G+ٗ m0ʴpULZ4 [mUliLjzm #BgW5(;% BNWݠܴEON,\[ V{P>G')wGW0`% 79gVܖomF)d_B,$﷕clx+JV0i}`8lXp$vˋmeal=MC8 B٘ v0*g 6TЉQz ʴ|Q?3/?po{n€%1x4ZZe,aMm7Z6s`)@N@|E吝#I삗OòK~ǘ9J6Fj͘jd~3GGUhkMCd40 !Fq0, NW?k|JnrΤ-[ی>R^ɾXHo+ ƀV"`qFo?MI,2dؤ=z0f1v3cepD1S =6`T:np7(7mF' W'#E9,i{zp|v#/uW`r@0`_%1x4ZZe,?0h]$e'R8d|(X䯰0?./Pm~:!֒4N*aw]* V4<$ȩ?T1 UToT%#8@>B[k A 904?tSQedw ]SpCp&mmfJ%B~[i0̆2-.iQ$et^MI,2:V&у4+;# Niyv ߯AiC58X:)`L~7>ʻ"_'ٳSxX0`M%x7Ca=W^yk1løafXl*]$e7%8 >{E,bP0?hc+6x_J^aaمBFH i}p6a4Vc4B-7c풹 cTׂTH!NUxFUe3tOMeb~G+aޏ '0pBd666}(c8`Lba' b[Y`a>,bF%%U\'cpBE3X:)`LU~+_WGʓ w37\IŰU6v cM+$AgEl*ښm"qtlņPi+4,P¨!Nf0jWfL]2U7␔AcL Z  :ĩ OèvL;]u)Q!8Ll}o3Hy%2%OJ[骓Y9]`8lٝI2X duy,MڣanrR5b?TA(3NWݠܴEON,\[ Viy=;~?snllos!LOnL idXܱLB;j| >{$E,£0?c(bհ VZ& 9 .+c\ԎP*u*7cpv U5]XQ  :ĩ OèvL;]u)Q!8Ll}o3H*<]P A>dXQ{ވcTNg%ئqFu(C5e&\wy,MaFanrRe ?TA(37QΣUi6`tX~E&] (ztbd(Gނe2mo/~ޞOe {{>Mu_A1TM uv4d&/6t]}E,b\ kk~Ax`ӱJ/BIvHӰB! V4>p8]01^1v\VCRUB1]*kAFr*`$h~*< *2RtƧD&Lڊ2m#T^`%FOC WJUWGATҠ4(.<(]֔N @~y,MڣanrR5b?TA(3RN՚6A`F6 ߯AiC58X:)`LUnO˟_ڗ@ ~Q nsM#]œM3 yu5="] kk1VAFH1jX+-Kfe 1 .rj(l:C1vg8D;P֚.,(h`@BNTaTYe;Y&@Ꝯ:@(IqaDCs9 ^`%FOC+`X)gDݦqFq]A$vˋmeal= sT$ Brִ 0*Y~ M;U?&أLHQȜ JjbϾm=f|ϕ?Xla\RpXgnj q3ۋF!vCX%r-`~!OVl+&!Nò (Z.҈tmhhzZnT%sYu/IT A0t  ʩC4*l'HUg3j3 R:9՘^`%FOC WJUWGATҠ4(.<(]֔N @~y,MڣanrR5b?TA(3RN՚6A`F6 ߯Ai J!h@JX51k)ڧ>b.]f~uq}+eΉcW0k,̚Qf 8p;7Vh+Gdt sesHNd'; .+c\ԎP*u*7cpv U5]XQ  :ĩ OèvL;]uPlqP?+j_ò E,χCF=M5BׄV: b$vM㰍»2V6'%V&iYmTc\"%U=SM؄Qa+nPnکR d(G!ejRowi #R~ Dr -Q43s-TtsIg䢈E sޖc8Jo-Iva٥|`EcL @%PE5fL]2?#T* ! 2S#AC8UiUVN zNECΊdƲACs9 ^`%FOC\-# Vn4(ؓgjpզ$vˋmeal= sT$ Brִ 0*Y~ M;UP A{d<jThZٳ2 "-Q43k,Z8HPc}ϖ!Y kk~9wAFH>ƽ VAJ7a٥|`EcL @%PE5fL]2?#T* ! 2S#AC8UiUVN z-gEm5Kϩ`(+1zBqWGA\iQwԔMI,2dؤ=z0f!'U#vHCI2s/Ti6aTk v88)ylQը)~'?~M`ۿ8?w̓go[Ύ`"K6^[ 2}<2ܲ[kҁL`%Q%M? .+c\ԎP*u*7cpv U5]XQ  :ĩ OèvL;]ugT@ѐ6uzЍ ͥTc0CpXx=}suJ9#66 fj24#zSf ; }Dؤ s*<#BPR\E9UkM,|妝*(=2NFrDp[vT5*OO:.C˿ZVd+!QQT.43ۋ!v"|<2ܲ[kҁL`%Q%M? .+c\ԎP*u*7cpv U5]XQ  :ĩ OèVXFRt|Z$qeL s1!8,J>W^: bua寎hS{3l&̛KX^l+ czaoT (%UmSM؄Qa+nPnکR d(G!elGUng^{/a_?X( &dl/>%e !#-չ:GSM9y2y&I%NòKE~ǘ9J6Fj͘jd~3GGUhkMCd40 !1xF͐Ѹ6DVwj!4&ѐ6=$ ͥTc0CpXx=}s: buaŅweۚ2X d//I{`6BNFR*e^ʩZ&l¨0fp7(7TA)qq2R#2'^ h*V;~?s02; 27ffaYˆ)qw̵oْ2}yde:W'ázӑψ$]/ThxIpS;~@dcߌKG8q$ځ*|Tta9DAFr*`lP/D`)F홅Ѹ6DVwj!4&ѐ6=$ ͥTc0CpXx=}s: buaŅweۚ2X d//I{`6BNFR*e^ʩZ&l¨0f!"(7TA)w2R#RelGU#o><eoc1'j?'9; 2rm5fJ@vV(ǁ@1׾gKs|\ OG>#J!> K.+e\ԎP*u*7cpv U5]X>d40S )AgbԞYakCduBc 9+jCl\zN5C9XP<ʽ# VQiQ\xgP)3@b[YGf,j. (Pf5m& cm(rNB0x'#E9"5n[vT5N/z #v7op{x<COn5fJ@vV(ǁ@1׾gKs|\ OG>#J"0XhxIpS;~@dcߌKG8q$ځ*|TtaJɩſeCRXD3k^ >4䬨5X6a3<&(+1z^y=6rFmm+_j)PlMI,2$]^l+ c|QoTC8%UMSM؄QaC2EPnکRFCd(G&m؎~{o?3{:ȿxxG/{~_*M?t 3 ffaY)qw̵oْ2}yde:W'ázӑψ$=ģ +g\Ԇ ,t&TK\!v\7cOT A-L]R7`UPNL-,(2"Y dM0!gEIJ 19p@9XP[x> 4z}f ئٮXcaƖ=cMDR&-Q!G[vsu2^?Il+*M C<`Ѿ_y&ENm`CLToT%sYg?"`b^il*0R&Bb Kaͬ 2z&АtHbvj8 s({# VQiQ|(C5e&\wy,MaFanrRe ?TA(37QN՚6A`F6@Ai J! U[g%Zh5y㵇?߷1~{{>ry4,p9=Qam>UP")Nr | ɮe#x!'ázӑψ$=ģ +g\Ԇ 6TI5fI\/IXId!Vil*,R&Bb Kaͬ 2z&АtHbvj8 s({# VQiQ|(C5e&\wy,MaFanrRe ?TA(37QN՚6A`F6@Ai J! e|whb %r!HQ'^w^r-7~wuBd7̶؊ LD d` -!!WG[vsu2^?Il+*M C<`Ѿ_y&ENm`CLTolW^: buaʗZ2![Sf ; Ʌx$ff!'UƐNCI2sTi6aTk3 DvP~&*P2?+7>XZW?_k}{kݟ_S.O\7̶؎ LD d` -!!WG[vsu2^?Il+*M C<`Ѿ_y&ENmӰ!t&T،~?yW 3ġqIbr*ebm/D`)Fِ #n 9+jM$ ng ɩ!8,J>W^: buaʗZ2![Sf ; Ʌx$ff!'UƐNCI2sTi6aTk3 DT2PAoCE OʤɪQ?~W-kO~p;O߼_]؂©6̶ؒ LD d` -!2"ᔱ p}tǭޒ擋ʎa5ģ O6 .rjß 3qZfIͼbH$K4 6XS)k!K1ʆ̥fHut}hYQk:$lv;fxLN5PaVb9Ͻzl+匨4(VDRٚ2X dH.ĻV&007 92pJ (jM X?'շ C,kAߡʚJߣjw'__E?zsxkAcg/% mm *BNe8 >[B.?dE)cS='@sP=̨7BN!*e&ʩZ6ÀMF*.\T7rҎa(qPRFf?^|wNn(fz"HTl@:@%CX$2=Փ|>8ճΓ[|rQQ4ND qdjdaC^e-c3j$fi{q4L0dK. V:!9P XQs$ӊ2z&%NC`3lT^`%FOC+gRΈM㰍bKT-E)3@Bˋmeal3 s*cH'H`lF\<=ejQ MU$c~VzT#WeRΈM㰍;2mMI,2dؤ=z0f!'U#vHCI2s/Timaӓ[!DQM9gGL5v{7?ӟA+OcX)&_ Xh;)]f|$_~ۅSk~6'á}Z8l+mIvK#hz;i&WY،~?yY^948.ْ 3NHNeBbԜ9'"^ h!s41 ̗ΰ>"(+1z^y=6rFmm+_j)PlMI,2$]^l+ c|QoTC8w%DRhT$`3*dBғ[!fDewhaYZOW~^soX]/ݞ—? *4x)(tJJ@vu@~oْ|!slNGGEh k=༲A&ٹ,9@?CqdjdaC^e-c3j$fi{q4L0dK. V:!9P XQs$ӊ2z&%NPeAΰPj8 s(|f# V2Y4(V_e0p.MI,24/ՑV& anrR rߕI3RʩZhmP^ǴR{J(`~&@ a~VUc۾v,}?RpWKAUbPBVV.gḴ]8Eaaq2۷?}3u$!)^r}DӅdjdaC^e-c3j$fi{q4L0dK. V:!9P XQs$ӊ2z&%Nq30_v;fj8 s({# VQiQ|(C5e&\wy,MaFanzO$2\ TMC@9U mlF=\YHzR!fDewhaYZÇov7˿ჿv}k{Ǽ/Ҁx(N%$Af؎2GVj9TN ` fDŽkc=5DC$;^%gV4=L^4lӫelFm<,m/МfIFlJ'$!K1jΜ`ZQAF@/ĉѐo7Gxi3lT< Ĩ?SvX~iQ\>gaimQ 0܎TcI#If1vïW0N'd"DlͨԽ+ IOw!fDewhaYz?ypݖWޔvxoy|c[7xJ (!U+'.fX̑t=|؂21aXO Q퐹45`aM`'W## *kQ'1O4Kۋ34f%[w~ ɩ_R3'VDTwK7-qb4d[q 9:.(u>+1T+,쀝lVjomOYX&a[T c'7؆bHY ;H;(j`3*uBғrQ(ig~Vb|?_vXt5O<(g ۻz$T a!sL=w` ):k(jC)& xaM`'W## *kQ/> n X Sc!K1jΜ`ZQAF@/ĉѐo7Gxi3lT< Ĩ?SvX~iQ\>gaim ߀u4y1V6c_l P"ÕI4$SжfTÕ'mlFT~&h晟X;<}G+~>Hwwo?PuJūWR'T!A\ClͰ9;MM[0S{%D*ʯZ:Hi/9p0 qdjdaC^e-c3Ň~ d+\s*`lP/D`)F͙|L+";襛818vМJ:gvN+76˧,,RR-"&/ʆbrL Jd"r606،J{-C͈DQM<kK۳?vwŖ@?w˿[J~W_S5\{%G(PBƀD\|϶9&;MM}d\*jҮIvdN3+N&FF6U26^|7@5BbԜ9'"^ h!{:ong ͩqy@XQZaal;8homO3%;6/ʆb C$UӐPNBQ{WTrQ(ig~Vb>-?_zOտh߶/ooÓ|A.½\_+ɳdP%p>RCx4-6!St s9J&UZ0a%"jgV4= L^4lӫelFo6 be7kN *,Ũ9sOiEDut'FCuΰSR3LvpVѾ4(6OCy72mm3+ISo M"ÕI4$SжfTÕ'mlFT~&h晟@f"yKg?> 4_PЯ_;[[*E9w93*mT_-9>dNb`2?E"8ʯZk2Qes]"6~`Eӣ HÆ09Zfԋf VvTؠ_R3'VDTwK7-qb4d[q 9:.(u>+1T+,쀝mgMb4wc+Ia9s ʋ:YO$2\ TMC@9U mlF=\YHzR!fDewhaYٴ?'n}D7>aЗ~h2;aһInJ2a./ٖ2si he/?EpWmh?d*4nle䉙#|@62 E2906"I>dM@K A~sGv;fxhNJJ3 ;`gYEdmd2S.re6i3|+CeIu^ $q Id"r606،J{-C͈DQM<ʁ~O9z?|g?v XnƗ//-o/-o/.}S{Lw! ӮvкTs|;MM>J V WiXvp;cb& AF;'E2906"I>dM@K A~sGv;fxhNJJ3 ;`gYEdmd2S.re6i3|+CeIu^f S"ÕI4$SжfTÕ'mlFT~&`/r?{{c?y?ooX_1~8[">IENDB`ic11 PNG  IHDR szz iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`oPIDATX WYlTU23]h)t UR@и‹(&Fh&^xL11W%@""qbBC"m {23n~ܡܞ{|=c_[g]y% |^2^}Ea}_|۟O-a$z^p Ԋ{A8ڦiߡ=#R/'}; t-p=ðu q|x y$HҶ 56<~ [obl+94_E[&룭;uϢq\x|v2(׎bb> (g%zRBr ̧"UdI:X(W<٘@`H\\7BP,b дH'3(+x8n؍7_لrKy؈=z :w=a$2wg,WnVAp|R0(?g{6ƞnB%ꔎ9X#M hI3y|ވv;L4 .2a1Gg2%Ğ_ۍN8Wfwh0}۰fa. . iTrpt#0'HZQU~Ѷ=?=F,Ldqh0N`~eym;֬ZXđ#GC@kE\[eqh G-R)ZX% ʘJHt 1Vay+]`-Hؒ+  A"wսeyfJ D8U<.O*yNӊ,ܢJWWG>$@9B1a}u{>bn{ E8Jg~0dfZ,m˲P(`ffFԲe˰UJ[]N=ؤjryo: Ee.1=$ 7mSB@ڱA>BF/&p-Bb`"/+fl˹ku2T} @.r)0mG)W'Hytpo1D;ǎsg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`oIDATx[idU>WTWwt{Yܙ-^g8`gƎ%"( GOAI2!1 g&a3Ktm|߹uEUݳ~nW~ԺGRnґN%>,H[%\-I[-뭧;Ɠ>{I)~ʄr%=iKUgډT*ni5:N G͕@crh]H:SD Y9-'L?2h S6.jV0swZw?sqn5L?8n:qo`[+X3we-.ZA#uҬwfe萵Yُf~%xL"@bf 1+Etg]њDZ-1xFzaSbǯ0e/a ,HLZݚ,$}aٵ ~AkA GfNmМueBۧj+uYTH:uOubRNmrR_AgM0t:+goLjIA 4f!~XukJ=_B@(`[0Ùr&E9P/}ll>V L,{oƴk ؝'-zߣ X qRYVj`ˉ ɡ[R(yz uA؍ `ȱ I)ZiWk{ ){Yi3d4crcG:YCGSrG0JR{" [-Ǐu7.bc zJ{7"(ō&8́JK? | 'fg2V9c{N&A_YcL  BA!r-9x*G~t҃'aqЀ|ft]{wkD0 l6]_[7".-n-a xxьaPA3q@\ɺʻ1+7/jrKrP+c;*zmsuٽ#/ J]զT* pVnBM^G8PUo\ڨ\%Tk([CY,0f?X9RIeq sDeTqӁtb)#-:M'"1lƧXHK!( W^8Eh כ27##8tnÊ8+ }j׈|nm[p3@ȥK4xlcxK W埏 H k .ۯNL-QWu:Yfdnw~%rOaHzp|8R\;9-LkN2ǟ>B%A`;v쐉M *O<{\Z[T~7!QUfؐjŦnN>Cҿje~l(_ f,.\x̚hW2TeƷ4VM]viA&)lAc G!N^ ?$jZ+WmBlM@1ƈFbFf>-g}%yvC`m&txGQzPns?Γ|M{)i7E"ݨ)@-l\`6`v횜|25t@mNEvp*TUya#i wQ s"NŃcJ`=J8v{Ȟ%OrF'zU* rܻw ɹs\.GMYk3s4{p%cxqZ[Ov|u+FjB@92^~eYZZRs n ;+0An MJ?2"c=R=bg?"S^g{=a???/'OnIۧ:뮻NΜ9ccc=z6>Φ+ɺ:'Fen"MK_g>z]~3" 43י\?8i5s8ƍ5-p7ku MnꄂO5]Â@)1ic-tNan!Ǿ9'WKmy[2&˕ކ4Ǿ#W*~Ԑn+'hEJwrrRw{\  `籅AFEF\ cH(#aOK?=t;S ԞMz{~ܾ}{eXeǾ9}"&!P[R93wb q$x{-E۩~݇=@6H޾ @ḏRtۘlC!!;;]?3Un0FNA>FJC;>#… r!MT\+"*nd5$& }F/ῂmo/+O=43 @-玤${'r3񱃁V>.FFp@`'Yâ%&:L!0Jyx+zT}̕|q\GI;K-{A$Ko|:̤'i_fJ3`O`}|h5sӟڙG>&<=ă6MZ ܝҳ |2róØ<}d ?i`+>qu!3B4rg!gp%i=^arN04 K+ 灚lRTaAxF 8sbW,mxxX(VXKqE; x&-I99iƋuM Wc?<.eT减('0%P}a̞g*P29۾Oy#VyZl~ɀ1jӿWcܛp(s}e| u}5oמqr5ÒURȡ#w:yf*I_2H\wЛl 9øߵQ\m˝ӶjFxڦyC>w#8rXT}Va| Ph\%H~Ǘ/df1G1$G\N'Eڌe!WsQC-xl90ƨrC>Sɏ㘏9dq~ɟ=!6i(5^NImst G*9>0Ξ서8y-SbK8ޮe>|[,q* ||Ō <<|Sóޟ%\Ipwi uP~t6<敶?dsʢ_ל U'm&˜[]Z6BkޘEYrbu: *%_2<~Az<77KY!Ѧ~GEv^8sUW:3Jݴ1͏G?$СdsRދw|Ә 2r0S(O<"0 b<OfWKwLj3+!) {FI+W9jX@V'Bq *JxGhE,nةCאV_F{ 4uTnF^#4D" T*y<ގ x+O]Љ!cVt\DP%  xt|?Z>`FGO[{1Is7 JmP9&"9nZIVkO+t B S1Ct`-FGGKtwUDiir\4bteUKmvZbX:.ah>=ع\Ǐ3΃fIsq [D$VO~4㛗 iq{ ;qIsg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx eY{ݳ4Q-HB2`,0ab'*Sr" SUI)A2hi4ϻ}ϻswOwO9}y;=s?>~v{Iz[Ȣ ZK. d0\r*X4g$xbY`Hzɖ!@f y1*|F&jJ/=[ΒMGyșᒀ]1DE00 SSx#u{l["rmss= mՅ>xIz5k% g^9~a{{ͶEmIS!j_'%8A)eŊO|F'Fao:UАY+*$hL}՚)(&UB|1OEɦsyșᒀy)p} SSx#uK} (rA\o[kd|~_}o`֟>şo[X\j[[ M]=9<.yB9Ϙz 9%O̩!f!e ;6)y_ z}2O93,^In("Y sF:6ZqotsNm[Oλߍ$?Cɗ77h{ži7X >T N~ iML8j/29Q숫 Pa&9`i1'41 'Ӵ)STq"gK>R3"}IrHð%Aɡz;~RK'/,Zō͵w|^uc:}//,~:_vhnbX!^GMspSҪ Ñٙy۪V-~TqRqA)1,$A ez5;S\ʢ'Ha5tHG*n D=5L+tŁ޳#ݻ?i=|O}mccU KaFx15 NȎx1'&Mޣ#tXGkY$.(GJI|f-+dPT)!mX2Ze> 4b_T`|>Fq3Y HPIuK' cA#&F1Bo ߿ֿ{L?} nn^l<̉Te2I-Aɑ"z*{`54ڣ-P3[Hj -%KJ-33C9qRsR7)Jnij0 15z%: lܿ=?odE___x&~n]K[s GH'&#)Y,?0%-X<8PHfZeL(R)^;]TQØ y.[ZIe ?#2 0oR$(9@}G'%`RO]G NGmkҭssx*NCz䐗 UAozU>PI7u T  )C.k{7!T 8L@(PZM\+J垄92#|j= BjJ`|zY5,) a7QtbW|0<@2sf}Vlrl4E7$%R,}¶ +aNXf@.["cP&b_<(Vx#uKe>`Qc\ U.1@9sS'DAMKܪ#Ezr(X4PFܮЋzґPTǖ`z3 x<hJ-;hJO1rik9 'P0P3Е喎} CIG JiۢOGIQkc ϓrP']V !4eŊO edM[aJ"qw> Z4#-/f50 @SSY<1w,wRPN5р, }\q B1 Y(_X,hM#T9s@*ij̦UFx18#&&U Kj:Vz P0CLuK܇!$g 9=όT TOkB.nSP_ZwՎEQ! `xMuKaޔ axE'89D[ڨI Ԕyʐ $/ހ52N'p`d μ&9yCRuEn>`eE^''0^[=2Ɛt#e=A s'pE@A񻯽xzZrq{j֕T;nȐq0tVQDM|aX=^Q N;NI Ԕyʐ $/APb0 ub))tA CcXQبpȂ3l41 LC%A %:zB(;$htRk/ \h&jJ/z02 TɼA*FȂ8&%_1hd3YI`w_-ÐS#㷅xj͓&#M`53i\5'5&`A]:S ]DrȌt YPwR@lj5ANhfT J+e>V#|4iM"WFʔ54PzRSڢ 9 ĨRJ]HtbOJ0$PZ TM;bAi~Ԋ}ۆg0Kxƻah#FC9Ϩy|p>\ڏߓ,hFM>zO:8i\dZ!` IY=)mф^SbT c)%x[:1'%`ZzJPritm^L& 4? jžmË3ryA$\3j!\)p.kJh =ۘQS){`tcu yNf|䉡\ x s_-5(@fUjͤ %8tͰ8{Y ZHSF{d>.{ U**129]C;DϜOV(akfTc$yD>A󎝂ZK%!c <["f| @OafH)F*H 20 )=%(9?f$xbYqxIG%FE0,)qK'} )Aɑ ;j2rijԃ4 {Tu*p7рax%ҩ[^jDd@!F zC.ZKrjlA2'6h1'0P3w1K10 I*=%(92ƌ& 9BaAz wDdYŴV!̔T0$n&F:[>;b&XB]e<2155 1dKkECzQA1l_t@3Rr1i>;+3hXLoY`fnt-m} {JPrhudF\ӁAh-_yԊ} xΪ C^i~RyՄ=-~H }a%nFY YTD&갑 aUaD~8K gIuݙk+mvl=^(jgasdե,#9gİ\z-֐xmqDkTmXqxx<;B>/!9̓D!$ЙTGSLqF0M<7]+bpUy_yW>t-~Yc5=!Isu3?#S}G[CT[+)|I1RgnkE{1tؘoϽQ:~; Թf>:]uo3xU<2<1w,0,IjE *sr_vۧ@׍4:DyZ!Qս.6@Z "UUHP cp9,C~l?}?8gx{K/c*F-Mg {Âtױ$f bY`XY 5GzY'(Ee )t<qn'F Kϙ+w71I W-h=@? pB`W;o뼲'(9čOWS5+g崋Rz sǔh|6*#0ҩhǍdCykF3'mnbXi-ꀶ>wKؑN?dI' W;o\ڷZt(͟ŦsJ+ H}尬b7Dkǀ{vO;)OKgcr :]y= y%@@39MX7Q$C'KʘpػMGZkkmSɅzIIYhGZ]w3iB;v`g[yMOv{ .;nglX"X6N蒈f6ۻw-/ʄw'~%o$FM$GL2<1w,;bH'8ȣ ,fF=߿G?i4U!Aa*t9,Ӹ?4Xy&' OL2a/ҭ11-jN*zd_4M'h_=^rJ۷2'*n1WkOMťe~h틍oZߤ DqtKW ;j^5IP+bE MUB#|Ϊe>`Fy4Mp4RUA%9M2`cS[k?coYj/fs$oX?ٽu`RޛSpA6PX3:xpnyْ?T*> > X~e5M MΪe>`$^!{Yhr,s"РY4XH J,KO{>ږ/.$_# s3-"D3fؿ]/u;6ӅJ]ۆJUzІHTQ]}gS 0t\gAϮY O 3&A#i$PƤj BK:f_om5 a3xh`s-VQ|[ v5nW7#jpzW]N',_I97kEi 6F9ͫ!Mw& juZ6#TKqg;1HX/Ú+"9_c+ԓk)*~nC٠3"W_{?~A~%?@W,80F÷QyF=o~Zh8E=ăYJ.%:ȪB4U!XGBxð$41F Too+؁5Sũ*QlЈ|oez)pGھzY@0G'XZNl9ӶqTk=fS Ja~ZhT.5=K̆t.'SjuZ6#TKȨ2l͜Šbb k27G)sP% zf7sF/XsyeFUڧ>!WןJTXA@|iB„.=!?q`R$a^4Қ-%rQLQB|F! XZ9{>*)9G ̓Bx2f/r3,c~NjM,tsV~O$Yat ,B_N[&I.5w?}<(h\5!4B_b> 'yd6CZqlc(Gi՗/BJ >ma$7wdb"f%)*~ϫFEntm+; ,` 0lX;d %Ra 8هb,ȐR.5=tʉf dĚh ޳TF{ ZI5a!d1I8vKS_kO;֔Bak(QYS%I&+ڡC%/~F;1GyZh 90 {>2[ӪhF9hhA'$AgS6r *O& d!g(!Yہ÷7ҝ'N-YCjzf97ox7ciHt|m'×3I~:8 |G'Oΐ, ̰eLc ]k012 BGeaA1%N9;+o MBaY' S/-=I0P3kk[@|ȩK-逼"LS! Z6y.e郟 F٬YUԜ*t9,.FONq#9T}z^SB|=YΒMI8"93R|g}TAh@>5Qog7!z^Y\GFyla|db0s71܄~ɭ/zx!7y/K_ʌN[j"gs:ꖖ%?@  CSRPg߿o_]u7%t'2 \tUD*t9,Ӹ<1`7sPm{VCd|6(2 v$5v=QSB|z:f"g5 r-%s f 0 K NKnˎη7h/{~jC_ xa1l` B,L|*ϽT4sn򅖓{a*"邴*$hPDKq8,d/ZFT||j5-[3hZe>D DoW~zς15 r-lѳ32 J'gxFo~Wrt%_?u']^96O?Dž4RDPZcUy.Q/G1J11`I~wr]N`^>/\0!cum=ua [[z,af_*ەW-#ڢlCS, C2  a7޵v㲜hֳZy֬A}Ov1ƶc 06~"1> fb3+cGr&Ds;u@ZL| >U֬сwх7j}աvK|j"S6"u:D?y~\ $` ðQܗ< xb{kxN}n9`ʍjeQ6ݧQ3=cWv%MtqV$ 5ʓl=BO"1;5>WPb:H;M@A H\kԼ>_~v#]/IiZ<6k?eGp% ׾=_?lĒ WUH5+VjR T, 0|`ĝ |7Joi[78>[>Ǿ~v.9'4\r,/'== i:B3 V#|q"\n1 ;"IyR@3&F|3kE_}҃\xx i<7]q9Ki;'j;= > t-'Ҳ'0ǣڼXJǀpE`ǍSs G'C@64 8O hh#Pr0=of($;r}:Tn RsgI:a$3 >ԏ90Ppl~N`IjE!P@kA-;K}'w>//4U~Yvۉ9B'o.m}it`_ Wƪ3bK82eFzۀ!hڻ;/lCQM{~1~j]+S`{|vA':1Ou;Q\cmbnϨ/^Ao {sj::G@ \}qӘ˖ anXH*qv8Vg'o~TσGh[=p;gۓ~=Gq_hZLto}.//kp.K~V> ЧA?&|loŪ]A~ \n#|JE-uy7620v _xЈ9\z+_r5mʋu5OM>E\OΗ919_ }; }>o߾K*n_ؕ@cEk+QĐq>@"㋐En03As$(N_)cebO|աh'dx}'۟~|ݙuǗor%{# |+A&m.{3̦5ow{r611|pM7.cc|;m;FG1CE!w#9$'S"e/;T /KP/-4  25U>|K~D%v`3XZowk?=ub. cJc6X6ک+?rƶ"7ѨY3=:W%Mcǎ{~?Z0cn$SO{キ?o??[T\ū-,.l~=W/CO5ͧNiﴸ$?A@q<∹7u:QR;B%X R𬫠 <_=tp;Ws@_͡]X?%G(OKfwxZLcguu=ޏ=>tb/lSshV;x=t]w?ұBC݂|W-i};$1ȱ@W*n0J9G;q˽O'Ol'׾5G ^fy:r|_G.W,fnaa}!: ; 'ikþe8=98ZdB8%):@a>V#|% @IDAT$KKZ%>'_?3vcCW9tNKO^Gy~sn}p;~G^Л|"-yr:_<nNC"NYz.rS1[+죓HH(+KaX+—؟, 7KꑠDaR %, 1y YN*~3l֪q%drwFSabzDPT/WӾd [de(;o_~xk=> G$Ooo7|s{'Oeʰ_|^7lM v\ON$ޅwm:/lǸso/--+RgN).Cn{xr%F NDe[6HW/0^ Āje]:c5'H@bMë)BLهrH9-Qub@cHl(=Y/Q5-iYcdf`JO&ܿ}O@of;'|~)p)MLoО<+:ɾز4o _aO3 bz9$,a,aaO!ǠC#A*e%*jה}8gњ]pAAhC% ӕSt%`']_Dr71g͹ϡezss~ tߏqÔ)Z6Sсrຶj:1%e H`B'*f)yY9pm[wE.~ 1;t]5bS%dtg1?urGN6{gҹ&8/ʕ@Gz<2&qEZY!ϱ6\>jO: bNV:JfY͢9б;$"z=^E\5+X1è}-I3CWBEqt8d~RTt/W9v%pgDM_KNxE])nl3D xR~ EJQc5'H@F%#)Me>*j 7-×8t+=Tӳ% Nsx'o7t)<+2>a8\AOHGp;vf 'PL}\E[WsMLƴ鏃*1L* -G@kӏslnRr*4Gg~]RKgt_Xo%z x6igzsJNځ#weʷvԓtã}8|g?2}A]N|$M\wRMTIkUӆ?008@[>d<,N=3KEp\;BBj xb{?bBA#j/W4T?;z4B`rfDO|}|A-w;2atؤ+O`?'0?f ZhM{?DJX_Duy>ZEcZr7tӠfPHBˏt/aonQ]h>K-Pzb=,ʘ9$1a/' *N7W@_>|hSboo5cx:G`RJNPrqKs_-ðᑟH͌lWZ M;z ME36!߸<a=bpP$=g &ª uy4. DgI^Hdq0uB y)* c$'*Br"AaA#jԔQe͏]K_(Әѹ>ӝU+3<%0 m\y@QaUO:FӸL93QD/6^aÒ'0\㰠} CjJO JԌ}w~sDp{Z+85_?N"G`Y}ߍ+yMCLU "?Aj v5l^$`/SVVެh8+V*`ca9{xY`ØYxqsmW^{c@P7Y<4esLbbmp|7|?2 EV|S%w~?>jG`6`=o3nifp(z;N`lLN'f F 0 ~>zdۮ)%Kywup6.hR6m] e5FLÍ@ ;M%uxtŁ* a<;ĸ`QJj {stC!8z{eQ—)$P> sEW줴YtR%"`]MY*RlAC).ۡ㗕(g\߬?񏷇zH?я>'P#){$S$_r8n|=$ Bk)KZR.@є A8J>O[x& hO] ԛNegĔ=||///{bM?J Oj ;麾bS찐 Pkix%s1}dd,}Aag 6l'rf$_YNä2Cɓ[|g[{/~ǂ$pWk:Աwo9yמ da(Aq1|U7g_? oG>"S'9tP{ы^4z LQ%|3#r :*>L d>Lz6 IRh3MD ל?R0eyN{;޳ogLۓÇgvl~2몰ל46kiz7j=T%*|9)X"cw5M0!_F#KD'>Jf|R/|౓z&| GS| _h?'kYQuKF؇  Iu@PJ~̒gD r +|4 #&F_yH> ?8?3T=僟oჰ? ; :9ב#GJǓ׉~9zڧc Ғ܏g*C= dG KPex0F0,+}didH_O r[bî 6!MyW} ׅr*y) ѱ& M)2_ )2Y:493\pBg) ^6|W?`߾yKcog~PZ^N 1qID@|9W ?S{\9>^a=RZ4O@d.,Vf@A~e%_zd[#C(5ß8ѾWMG _SvY]^iE hd|6kg=ܠCT 僟mǛ|4>r{۩Sٚ$o>8gO0~91r,@4u/;x`&aYzq  >z41NRNZC" A#fEea9#;g81 j,ӕ׀K?u5+tEym^4JGj:@DR|(@$h b?c Q0l=y=\g-ӌH{y'NHT0dP\u7 yp -7ko~ӝ%:H xc0ˁYW|F/$ $ҟDpz&^BQ^`/fEc>`RR1h8ӥ @a+aL<s-zcֆ  i S?ͥ́־E;teeUQ Gӳ+@鐧%_x/'s״vy[s?烟_{G9 惗+ ~vgرcy9_:&h7 2}B 3c0Q5 VGo;V Vf/@አ  uK{q_-q?P 7\in>ܖ*{-<Ĕ!Ok_짓ڇ>P; D>y$0!ZYY~feqy?^e10lH?y# >b iE`whk2VjB#HWU0$W+׫6| 'kDv"5y},h CYΰ ,~]ɵ3:PBfgr~%E?)<f}||7Liih'_ kd38`/'?| FpE[ %_oH] 'f ܹ$U K;z>ge,p̨98Kկ˜!m+.?.} ?_gc_:>\s?N\) s%_ B^-u2` \\1*([A#fEjaNj.e 9Ӄ_C--_F'} ;}0De\=@ EpQ"}B$!y I_ ?|wL7qާwin_ڧ޷]W<%8 譔y/%N|PW)()q8lD wF לyQa` WA+坁!O-|◞hgh~)ױ%g}?ނ/wXvuYMоr_o)CSU%FmuQCvy# DYlGZ=A 90 &F/&/8ysHP;hi ҪWe@鐧pU0W7g/ ަ.u|mwCy>j7A(@9}h^3Z# d #FG`#a`"Aɡw-e?M/:p}98(t;S'\ .{S9;OZ|~!2%eëqݚgߦ?=)UߵBH=M IPror_.*F2`$0 djhUA=Ie!@u磗7SOOw>)-H9{G'y{%W $ ]d$SDPmپԾM7a`m6y?iz$ɠͳ*|1OaqxMCIwT+6y(pziGzHdhBz'I ﴒ.qKu?)ɡ!×=WNЕzq6N6IPrd[ A*`J9 ٳY 05!0y?C9NGwǼy0}lчճeQШJ5kEL\t%ķ:g I,~dl$Kn%]TE## hxhDհ)QT|O1(aX$(9S}Gݪ2mJY!ϰfaXHOEW_W;o v,06_ıd|%6= J{5G \h!~PwG-c Ln>!Tb:њpa Ѹ#`.Ҫ ^꯮nWHQfǾg<p >eJ“%[Z}>ǸoDA􇵊8&!_d9D܌ ''uC561#7 k:-]#F ĩa 4I'~l}]-oۮX{34<l /`oB=ȖKpr(]{{ʃTc 0,\Nj.!7c 0 c}c7钋<{sPH"`pАdyWR$/zӼ>ah  Ho!*f =`ڦ}ávˍO{?~}' 0hi}|DŽ䃇c:1wڱr!2APJ~C3 8mqL`Y;[nW],'5{J{ӛs2$[.)= ^Wz>%W~ë/.Fy?7IRD,p-q|S MD?E'$a2R$(9t_VD?{+yV@?76U}Q.j!|׎YÍs<̀a7w ! OD;r_-ZVuKs_-+o )i ^}7m;j9@|m{WI;˗mzAQ990`?Mخ?٤Qnq4/z aR$(96Kgrx*GcKQ&{u7۾\vR]Rɲ$ǔȶ[AbAb0 R AbER`(NbHb!6qQ#HQPJI",6;sg}KY;r̝f];/lgoX7:t֩.CMrL50J#%`R#̳O6GV.ƣ ^m͗pګo|zegSD\XN^xv׎Fi5i1~W u؟k~a{TS88i-;ks("S?hgo~K6yJR{`zb/5u Ɇp`SM ?^ry?o|FZO|ʭö@IHkKDtgz7C<+Ooǃ!6%#GyKVI]jU ꕐMrL5ͫ5*mTr)x/pnFjyvVa0H= 5znۺ8lb[ږ6sri@xtw/ r! 6x_>\wu?<|_{??oFnotW^m^i U%b`SMA5%^* _`,jM ͍]H•:8 (8IX+踑40F_lȮ{n袋m><*,Gvqc,ʞ\p:o޽q`Ғn~BǛјǑ |s)pYzP }E IH&9hE$4PB_`6A4JͳOvsK"sTŝpG{AW=[`{=2۷_Vczvaaðn®?,40. \c>ynJ엟pa0?SđKE~ydž-ۯ6otws&.g S޾ޘC6nZ tC<8e/ 8*9ٷ1+\_ztl_`6u4J͒*&Rh):CQq0|Oܾgtٓ8ۼe>k]0۝XSO>A7q)[A ?%Zc#Ac 8O_Wu#N{08'?ӝjԘƉ$:v(*iQ[7vjt$ GBEż-j]~<4,qLNNx%㻟/}B*<(S/w7-򊰏n{Rw̘.g@uS۔e;p6c$$68(|ÓO>#x+_f(l5f-j^~TDe^eWd^0Hvv;5c9RXёhv76$17~=?ʝzO90{>({R.)殉^dr#9_}H[.Ѝ@m~\ʂ\ݧW}Yo3C;"n˅=?W~] deڄT lc)hs4ZQP;2d'cYfIv2̅& QJg?6}c/=+[E}t#Y'́q/~c'5*fpcz⽿p~r!o {S|v&=*j =tV.6 ǩNuHc Q1I` |كTL1'b2KrHA Rm{gwchC"g7- >p`x9`xx ۇ;t㞁>C5$5Cavy%Ց/}vjpeų=Wm. 2o<7eÊo' 8),*bf/XA_9ű£"0j>[d Iu;I"nJ HmwMY(~nuyV#xlude}CC6]EGeO$;+s%Q^y) \IDw ĪIM$1C<?eQ;o/^W 7\wdN~)͛7k~O}q<#ā>G1=å^:XmpCE`;],m_a71<*@lyI7vjfIvbI1aX/ou˵<M牧?yH9m)jh,P˹D&a\&J{viJTkֲ:N2ؓՋFrsMo~ګϗ#c=l=< l@q7}OvBvx?5A#E8uДO˭"[./X5$TSԺvUĜ5aJCrdRFJMHSXv76>YNw x 0NJ!jPcv F@թH_,29'iқږUVH9 Ç ;~7rn0F zDC7{plp(x';!GwV Ըg=0:=v`;1O7 _dݨD8@Ť曰KXńW )3!9v(hƕ;{T#EY< `e{O`B &N(X14 3aA4faFAhMEUgq k{5, 1p0«Й`_ޫqJ;;A.4TN-mEOV8pd1W1k¯pq?dRvq>SQKpWm}lu_چ]+tyvvc:n4\%1.0PFMIULX9B,uX mH@)L(xEUSoXڮw8F| ߤe]?ƶtpfiñ#IAuӡ 15eNMmEoH ;_~|vQ8B9(2<1K=pKW\E>0 qkzUȅ&N"t%m ~oC:K+qBSM3a"^7md8(gcvPcݴbݻd`[ƽHN>>&Pӱ 2kfd dՐ>v?`Lumσ)l +hSÆ/Y.`tʱ&SZV/r)$mz~P**&>?yKd|3'KKRexQVy%|t]EeҲJ!͎{5PlHw?OxBڗT&6: ?t-F9s8 IJ"rL5}z~++a97 tr)Q/.nR_iTta5s׍6ydÑOJ|E@J1^U7yPN%CNW> އ]WgoD]2EUex t`2)aj H#ẞv76$slc)xFEU&,W, w;{pX~sSv4Yˢo|߲ 9XZdc#aȁGuV69BkdVNjzfڵIo'GsW58 |J'X`c"6BE- Ur)t9VMB+@d!a)TS u1!Y1MrL5uȲW޺z~?., W ptu{ 0۴b N~uv kqqT1M*-.]#rW\~Q-Z,?fMovײ@\ ErMdN+t~ORRSM2T9RPM0.u+u&j!8 p;f\".0ʙNIu{ܞ V[zG @"' p{r# iYk:&c\2n.*@/Gd]{նoP}hRWc{xvRw=޽wg /Q FL GS'հѺ*W 6ˊ-ˈqlhǎKy;d`.%?k {AkKr)IDAT2< N/Qbk6.]w&C^l0aLEݔq\BpҘ4 >́GCTa $B$ kIN]=͗' ~mRZz=μI&iuj/ĄCxǍkG쮉Ef׆xoYWW^ug?ys1b}MTx?3胦^~+~z8CL\Q@\\]vmϲ7T$9pUTmAߺ?Owuco4g,'8I?~7oÙv 8p1k_<qd~[\W>1lTUϦXBْVvKWb^|_>_8||'ל;ʇk5s냏uvH*X6ƋD$큵Gog{1@q5sG0o)'9yò ^Y-/ De/]%ηDa`//_E>w|ߥ~>#%Ā󠜲gJr`t])qG/wA]OZY'iCSheK 5XVpڐЙj EU6Y_ ϊgwtKq3W6`u}ʋ&_zκYw}6 =$_':4O+c# 8M{~a. Z6 .^ܧ!6H;-BM,Kdp\|BQY\Vn5C)e71SѰkm-CL?8t"Of+_~*MٽX&o@5YR16C4XZp rK7}nez MiP1a:6졘pX>`5mHA ١<52m3}t׭emSw^-8ceM|z;]3%q§Li@SmalTPpPyP ]j8XMC%IbG5 ~O?}dx~Q9ND:tL~*o={8|p"i@ Fۆp𔼐ta7l<:}׃{?cOBń5cM l n*6?|VІd.tTMEqO=sx뾸[zFf8{=}1޿K^}ӆbd8!M5Dco9q?|yfg;}tkǏ ®asw- íݭgקRsç>qi@},`t| ~K?h qC-)FlI!9v2sFj0FyGr?ŬCrRYo[tY)ѽþ?';|RqJ9 X'~aQNZ:<'l]sÆb82SeX`0$E]W;߻qB|rl'' 589G6Ǐ?}ṽwVpϷAccc*&-uMy'OZNf{JqCd # 7oZ{ԏ ~zksZF _2zh𝯽`8gN979+xz_p=_w>,gq>hM_%D^Jc뱯\5(P*9υ[~v qCd 3芺1ͷek3= s;we&Y2β  ~ |t?Uaǹ7Go_axk.7ͪa^}go-j}^H }AoO׆z礿ka^1I^B$/8(4LecO2,Ϣ.LOhkD 8-9"xkK_s|y{0p6;˰犚 x{9(o}|w }GDv ]VUuuIAQx ~$2y6nңRZMfYVb΋'Xn],y^}95*_/3 P>\hFni⹰(c~lˌvhB(Ԕ:NU-S"$TS0wѫ&bV#*M lTP0$ĞktT{=fu!gK>.4ZLǸ zAI3)ţB=QMJԽ<<+'7r?rS޺ ,k)cx 5SV/|1*$^ ѱzE&]#/(hѤQfRruKTr i`I2R{)UDC`Yv(Q1tLۑP-bnx Dmu5f5y!,1f*F1*ȷ놫Ŭuz8Xsp3mr($c[؆ۓb64F 5PSD6 P>3 % /=?_ug67JfI*(|e9Dv6(x8IQU2܁~&ɩQԔn_~9X࠰( P5iQ,R|ږq4),uaay\mlr&bw I1rM_9_iGM댜 CA*8(A׉ryȰU~j$&Nr"cڅs6((OTA2cFi;be^ǀxԂ7RhjdS/.mk=BЧipXWLTN|${ bm:ĠZ^zKIl|fB&D 0ѯ4w 6 VQ*&:꒜U~j^PH543+'iUOEj˘ag`ZƊ\9qSԙLa9R roӹ#Q̨=Ov6^\"ۘiĠwl3L7iм&q&uy?PPZU1IЉad,rW m`?i.|@4Iv5EHCypnN.1e``cQ(/!Ј&T.向*ZІd+ͨ>ffI(_?5$8r^=:bZ$1+,H )Z aKpCV5D2%viڊ̞\L WN5*d7)M~uXBBLfޖUhCs(X4iܨcM l|*(x-n+ԗ,Lb0mOQ U10PfJQ.I - j{ WjN864@՜6ZlJYLY 5tLؓjQE"Xj jL mHؤ]-}Q$ І,L(DMUUkxL D!J@FU}zl|S̒*&ގ5[ub3ڐ/paRMAsRUZi*HT@~Oթ,O"kJHɚ TXPIMa9Q9;*uD 3) G<0L6])xQF׵-]g3اhfTN|jhT&l]2jIXOTS`TuphCs&i ^GȺS\Ndk'feb'DJIMծFudb_a,?"8T- ٤Bc'j a P5j$-#mHu&5l9s?FU$5QM!d7)8)د iFPu$;U΅^DvbfN$') PbE HjۗFudb_a,?"8T- ٤Bc'j a P5j$-3;FqZlCF"3A3 JZz'jE5IT ؜쎩KtE] 6>gaF0u8f'QbaS̗JDmC0s}njƓ0 TPpP4;]Pw:Edt]gOfdW1aȥB5眢.@C-I5ͩ5UV 2L !@&\۽PA9K)f \NDR q;i&^BtL$fsRW1JTBǠ+Xa)4Zbk7QDYc; ɒ6)pDc f#(7au4!FaeU 6$Kؤ]ZiԎ3?e-ɳCNJbl~S1 g% ,?J5hD-C|4^9TtLY"o% +phcbF0x_aձq_ѷĞbjuL5, %=1!i(W'qd -LD_ƺtSRqaiyG)/ps)R7i^յ87s\`q4 ?av/oo/ͪ#*M ;@4l6i#ōRiI(&-'UdVHPmHm;%L {R| k=rQp` LoRgW1aЭ]HNP)j (@\Ujca^*j᜘If!\ΉWZ8!xNN364Jn/9q;AWDJxu_D뽤jKpL5tL`]pXIMah)$uLA*614F+&N|OƐɃ0 >v>jjNdc|(-bp*^pLrL +:D5*330^yDHaT ; :9t9v(F+>#&i(kuiȰ8qEvjP(d+'s y; ;7#mt"•:fqZM;GG|,uL'|j#Y&IU!~lj5яs_`z< |)̜}T4DAݴ*&FD [VŎƞ'1UǙLY5hja,uPފWRE!E I(ͶqǨ)n_tLq:1יp*>J#Y&IUi$K5DȧA\7)&)qebꕐMrL5 ъ>c[QHϐ%Lb!luf"ҎhgFN8?]43GɃ*mX^ǏZlP&:FHqRc!Da"W^\H ֨i;8ki3C%~vO- w_ {c//(}ĉ|go{8Cxx*M8;GjZ@+## "ȄAo#j BltF9noآ{yab5u|CjmߢF6-G+4mo,~魾r_z4?w/,z۱~Qh~5󣣧;;֋ l6ێ-ݙ Eڐb%UL8D5K! 6+!74 J^(Z#1*F;}BU::WFPjz1As6aW &\,8GxJ+N[o[Jo_ذqR^m m~F眅a&2=)Ytb/.ca!L%@L8Iɥ(dTĠlS< 3RMk]!GR)5MpQi54LDP6Ae 5fe) bNG`&2x}9~{ر :G~6oU޲|b&y

-@ Vut(3rz1soZ4~Y^*xw&+EzC_z|8Ņ ϊ/fQExB<|۾/?=sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx_U>^C!C@ݤ.'Q)RxH%%}>>TЪ*ERn+56JIBb$@µ={w1Ɵlgkgsn. |ߋronnZ.+[^ֽM K:3\Ys  B)#~:Z[+~*OO,(n#t I w5H Bg$I8&%&/TPPhD{yglr%k/e:?WE@7(}JD hRq%_s"3nt;) i:S? /˳gy{S?_/x-.wԯ}69nݹ DܯL" ,m6Z([j0*OO,(n#t I w5W .a@(D[8hrqV9J) =F&APn6e%KM:DzJ@ Jp4w !Cξ sWg^1$BuاI'zT[e3W49|sQ}7$rss{{M-__q?sou9O޿˟w7\ˍ~3*|k +/lV90ڀI@R-zq\jn*q2S:3\UDR bAkxbTa@(hMDQѢ1D`>>˥bώwm1p] ~Is3= yALj+V5=6` >H"źf⽒/9n[49衼F%[QƷ|0_.//^ç~wy[_}_I^O8N~U Ћ~IE\!Q HSݕŕr ӠB! W"H",=ԛń*`޾BAC ݩtUzvwu|?|o%_gob(۹Ћ>yBxE]+a6TsuZ[+.*OO,оpFIA(h܍P+ބ; ^@S4Ijj[<H7O);)J4Ym v% gw%r'k/ՙtrA@plzm(}JD hRq%_s"3ܶirCyuKB)JS]S|}}g./^7_kn{w/>(aЋfGDW5>b&cq, B! W[ Eb T tb|_ߍON3xeWh&m-ytWkkB靅BڗW9{ BQJśu)DS4Ijj[<Hk4:P: Af QHr&kdTL j6ֺdO 2RVTvc \C3Nҕ@> ,r%6dzpfZ -,e6@.}JDNg<ŝD,K4%衬k`TyATi꽫P|7ůN/ؿW>|矿?/?/"xeWh&m%0]+yw QTq }{U *DS4X#ۤbQCG-$}V(e)sg2qI`.i!Cξ3 tղdm=`(kwQS$bu8A.E'reY(,AeXkRλJS]B_ u'"_|3OGrGUϟ_O7EKb4KDxeWh&m%0]+yw QTq }{U *DRZC PpTk'}V(e)sg2qI`.i!Cξ3 tղdm=`(kwQS$bu8A.!eFDSa`n6X;uD޻ (*tasU7/}gn_{ϻ=?OG _3}Z_plҺ=<# 'L P}>tvh=B! ]3rQxzxJQHZJk3R1ajZo~mFJ=e d0/f+|XK;m/,],Av[jX m]D*v,\g<ŝ#hh*=XLЭOGATi꽫PBƌ\+ N@;]Ra1Ҷc:xhsA <# 'L P}>t]Zavq; (*/3rQxzxJQHZJk3R1ajZo~mF' \)S(e%KeScX̀xtW$q4wې!g_X jY2@5ɻ("TXNx;'G扙ٯ$Ä-%K5`=GXwJ@q4w!g_X jY2@eH-HQh$'Pc:=8A.!'fFDSa`n&}:JS]B͊V~.E|?3?'w< _&h(¡L`*An#ulZ[+Ё\{BEbJI>m=* c DM"g)! Hńu 8>NJ9LbiQT\su\0~wIs rY%3TԂ$e6@.};Ӄ3ybf4O4,P&aۧ 4U(DQѬyvwg~֬l?|_޾ߘ݄-/ph),m6JC9%u =t ^aEEpRxzxJXBE>QHYJk3R1ajjdORXZ,׀a +r~0,]!2W-*!92zQ r(BŎsr`M uh;*Mw QT4+v]"x>?)ܢ߼={ϛvEֱim0;F,о̸AJI>m=* c DM"g)! Hńu xk'}$r+ ZEp] T4JK;u tCuu@We=$gYTB/ jawQEرp wN3y`rv0A>mQB!fྜྷ{/ zg?&\f$?w߿?sM=]>!qeA T-FشVX (*Ĕ }{U*DRZC PpTk'}$rւÜԊ(*\nàrBGH`;aݐDla].UYY! Zm]D*v,\?6Wz`vgF@4>@9;[ l{WEEbqI_{qy? |y/ȟ!<}ڛiDشV} w QTh_f\ n$C6D共|&h볔֐fb:<>NJ9Lbkb}aNjE.a]9#v$]swIs΂n^L"N説,ːJD-6@.> ;Eرp\ٝ]T`n&}:JS]B͊A܎'˟?L>v?!?ph)L`*An#ulZ[+Ё\{BEbJI>m=* c DM"g)! Hńu 8>NJ9Lbkb}aNjE.a]9#v$]swIs΂n^L"N説,ːJD-6@.>%{"v]?6Wz`vgFM ,P&aۧ 4U(DQѬydRݠC|^~kfcZaq/Y(DQ}q }{U*DRZC P4zO쓄RZXrZEmtWNIWl]!2-*!92zQ Oɞu͕!ݙDSak O+9cۧ 4U(DQQR[8]sKkGw{ٳJ=a ./ߵ؊ m PNoI6r]ǦjȵWX(DQQ$ d#&ރ0PO$m}TLXgڀZm>O) &Z CsR+p #*K;utCbuu@We=$gYTB/ jaw):=#;3:h*lm`'BN4vUz*hV<;og+g?`2\;mD2R;kC34NqeY T-FkZ[+Ё\{BEbJI>m=*ihCE>QHtTLX XpTk?Qc4ւRgԊ(*3tWND K;utCZuu@We=$gYTB/ jaw):=#;3:h*lm`'BN4vUz*(V{/?(&Y ͕e6`S z($wʱ9Zġ (*Ĕ }{U*DXCk3R1"3=cU,hbkb3 NjE +'te"v%: !{-º:],C*0ɻHQw\ٝN46@!~[lB!bNww1P-¢o}oC|_?pwo1_n Xz=KÒ=pe6`S z($wʱ9Z"c,е1*q%>n>_@WwY4' RZCx]{g&AX@hK=ZXꌂZEb aswIsn^K"N説,ːJD-6@.>%{"v]?6Wz`vgF=n]u0ɶOfw QTwt&XA((:>^g/ Ap1׃ tvKog]KphFkNqGG'C9%Zɱ9Z"c,е1*q%[VA%{ya,"O j RZCO$MHɖ3PP=QiG[ KQpR+@P]9#,x.iQ kI]eR HEڧdOĎJp 5Mx"ŽZ[+LO,(Vtvj J~/oksx GaXeu*AehfY*suk` (*t03rpi2Gfې .(=ZC9?qG?pRΏbkb}aNjE.a]9#v$]swIs΂n^L"N説,ːJD-6@.>%{"vt]>=#;3:ham{ev(ImZ[+*OO,wt&XA(8J!O|?|vWU[IJvֵh=pf0i PNoI6rcsEY{{5rU&JRcoYm共bw|_|Wߍ0Ga+[L`*An#Wyy-Gv,o0*q%T䱷6Jm$"% rK֐bO$DZCF נʡ':-hbkb3d+6 +'tĎx.iY ؋I]eR HEڧdOĎ+³ Ό6C*9=uq ; (*;AZ+Gbq|?ۻw/? 0>~&,Rv* F0i=s[(#5O`0*1$U< Hm21G3Q@{.bY,G?pR/geX9me< +'tv%:!{!º:],C*^1wu^WH|(+ؤVٝm]d(@ HO ZaTY(DQQ܆|:a}?x~+[yoo:`6P^٥urlRQЁ'3fUbJI>`P^^KH֐bs;ϚdA=r~9S,-Jkue݅ݕ:v%:!{º:],C*^1ɀI}JD`8e8$rЯ"# <03rSJ6JXBE(@@,㞋y& ܧ˙biQT\s,.,,U`;aݐab)^G13ܡ e&y pk[ACY!3Ύ=Bw̨b+- `穵lB!bNww1P-ZDW?0t[MFKe`NdFm.\Ce8$rx% y2SQ,bJI<y-0P' 5b)! ĸ"v5ɂh){$)rXZ, ˺ t3@rxU9؎CX'@7d`Xk3]!zct# &ps>%{"Vg%{"Vt^-Xj82G:%]7G9 (*]q6l Vmv1k×`4znR-2'26;KCyeV w% y2SQ,bJI{js0hIټPXJk`OL, DXDz&ecYwaNwpxfH˷Eg`^t-?l×wcJ3F>%{"x{b락evIq4ފ>ӎ~|XM4`!R.=ahM2衴{Hi?4 LyP*OE{js0hIX "7F<1a@hQ2.5QTjsg`f .U-`; i) *ֺeyLzX&݈cI\Oɞ-].Wf ֹѭNIA@9l.B!bW n$d6tHǰwYWo9]L8.QJ9?(J:dXĔ\7.`AwVy rKW#0ןrTBGl鋚ehV/tC Mዳ]!zct# &ps>%{"Vt^-Xj82G:%]7o?=n%{"Vt^-Xj82G:%]7c(ЭTt1$y#Ƃyx ϹjFѼS z(O}$r[׃tLE1bJI<y-&aM(Ȼw~xw~㛐a4urFhX&݈#nݔi=Ab락*33s{).2 Gt/{+>nj_EJikvy_lW6mE .'23X&X PVw~_uhtZL# S$TF .n@s|tיlř;şvo 6j%{"|\;{EUvgfRVP.%Ƚ~EB  QTqR*։G"\E^(gDrh 3%衼rV wU<(t1$e.VtkYjȉRދ3z-?@! &JJ4Ym ^%X *`yf/l.PCo(zav5ɀI}JD˥w6 4.衜]Jr{h9d((t1$U6ŭ9ɉEJ)w oT,2'2-6;;KCy4>J9? y3БɢJRME{1s94ưD}jg{3fSCrUҢdEX3p8P;ÂW `;am3urZ85tP@;aW AڧdO zҸ-u@j=>-Gw QTmNw"5b븚F"5yG"s"3hS z(KM<< ǵfAgdaXuCg\~dqumt%xRʎb kdD< 8iWNjKz =\wVS47GͺFȃ)䬟ۇҢd1ycyI 7x,}!SNKt=QMZw6v;IA_O)=: п4s8'2Í6~ Ĺ񒪢-G6*T(tq+%}'r{(R:OSI]Er(mG1rR؈)gÚC},td0EBen@v"cv-򉪻@H$$PF)?u)Q%@țـL%Wo*[n[r݃fj!H &H+uV@Kom\%K%Α7g`4䏖+IJ(Bt̲eyAyC rP頯@کID5f{!!36@qۃKuhUDQ_6kNwR'<nTPUtv̮E>Q}3"W6{ KC$MD= of:2Y\Ijco9wDquANL[qNyd6v"ާ~nJ s}|@hK=6wuӖ5'2М7vĮ*eL;"LYX凅=\),Z 9CxIrϮB% ܾ\$F\ 79"|]_L6 TFvH9o:*Az@,td(~SrD%:qFg7M^Y'@p6@{^NjEAw儎ؑx=Z&ȴ/,Q Ez91x@J}NH"¨J^ep5eFaZX y{xIrϮB%bW n$OD6uEJ <QE_p+ʛdQL%ܮ6bmEʙ&yư2G7 ,3LP#ުC3&^ἲh7=]2aom\ר}Aw儎ؑt=y&ciu;Q Ez91x@J}NH"¨J^ep5eFaZX y{xIrϮB%bW n$OD6uEJ <QM 8M2@(PnW "`-Q\2Pp;A"14X@} YEGTpey0 =JbR="9fTk 8)%T䱷 .) aBEP&u;:8i4{<ڧy>Aڣ!wu|q :X ʍBA}qԊ(*8U]9#ڮ8urEJg^14O9oztm&[ej2݌6@CqۃKD-QF!bW n$]k Z ֠ЉQnKu+MYJC]b 7D= of:2Y\Ijco9wu݃f%:G&ӿh{3{%} 9 .MUS3*;rBGiڮ8urEJg^14O9oztmLҰh*ԐSfWÞmb s<^'h9*Mw5 Ttq+%^[Т3ܕWk6!͜Q] ڻ4b*Ae;:H938*A'#<2YQHYJk3R1ajj')0aDkb}Th.Б1GATk9 %b 1kp h m]ӔQw\ٝN46@:OGATi꽫P ϲ}LIWQp#E&蕦I`/r)P^uh4%< QThW9xD@AT^^`P5D[,0#6|?pRM,ևIe62:O Z~IsǰLn^P"N説,ːJD-6@..%{"v]?6Wz`vgFM ,PD}:JS]B͊A'@ BуHI$ʗ&gV4v=[\ױimCr QT ;)% d//%T5D[,0#y^[){$)RZXr8'1ڀD<%tdj-r'2!{Aº:],C*0ɻHQw\ٝN46@!~[h;*Mw QT+vj  j[ ^@|ib?fn0rzKr(J:kPH8AL)GpN*cyJZO;etCuu@We=$gYTB/ jawv):=#;3:h*lm`'BN4vUz*(VtqXA(8AAnʽDW4v=[\-GQЁ\{BEbJI>m=* c yPQ5dzOc͂ʱD*z4M`EN*cDr:2e!r'ókNK8/zM wcLn,(lwv):=#;3:h*lm`}jm0<=P ; BQFu P$:[ Z]:rKd'vڀW.; (*t>3r -G<|*` ! HDЋx{lP$Vԣi(rR Б);я>i\sXyԣ^njcrc Da Kɞu͕!ݙDSak T?mN4SkkD靅Bm8A@V ju5zЭ[r/ 1XkMu*A47ζwY(DQqXhI>`P^^KH֐fb" E`P) aD$DDn)@{.bY,G?pR/g!sR LSBGƨ֢-ܧ ܐJ]eR HEڥdOĎJp 5Mx"eKom2=Pbȭ:Z+G:=-xBo ,p53bmJҖ9:]v,f\ Z*OE{js0d//%TBD XJk1gMn {}J9),ևIe62:O Z~\٧cv Dla].UYY! Zm]]JDB pN*cyJZ>i!{Aº:],C*0ɻHuxBx!ݙѠR{(@!9.C]zkkM靅B{EnNwXA(8AAnʽD|k`d X(ge9Zġ3Ef*4̌Ĕt}D-0P' 5b)! ĸ"v5ɂh){$)rZXr8'1ڀD<%tdj-re4{j͐Dla].UYY! Zm]]JDB 6&*hiSyzzgEEqNw!] ^Oë_r/ ѶK`0%6M -96GQ}}eTW9h4X&`P^^>p' +\Jk1gMn {}J9)%K5:Dz :O ˆ~\'͞@3d`/([XW tUCreH+w6&0ɃKɞ g(#= Of*bJI<y-0P' 5b)! ĸ"v5ɂh){$)rXZ, ˺ /<%td #r'ó\)E(Wh \֜ 7LG pU]JD>qƃ¬C=3m` ^- `Za6QyzzgEEqNww1P-ZDt< Ht̷ZM™  uID!((J:S<=\^qSJRcoYmt7 kD@F5d\γ&7Y=ed!bRqERq αh9=D40h*ȏbyj 6c&݈cI\KɞA,& At̨b+z9N4SkkD靅Bm8A@V ju5zЭ[r/ 1Xj4&}{ ;v]^fU,$u+"em&fB@b)F<1a@#{frChK=XZ,7%˺ +8+я>i^CǜL^T._ZˤqL0ɀ v)˥wl:WÑ?)z8}(l0B!btѮvA(8AAnʽD|k`DLZ ~0vȵAWzFlDSQq؍tE-&aM(Ąh͂qӉU,5Ar ,hrz*,f<(J@ <h95hzi`T9<|ɺcJ3FML2Wv),].W^ !<"P&?}|3IB~;A V [J)u('j^Ki ,p5I> eLDh0fh;,dWcĔTy*[VAMšQh ,p5 zѳ2?y<)&t^Jۄn&\9mU!'͠8(H%2w)&CbcN@%,_J0Mh*.7ϰOlc"Vt^-Xj82G:%]7Sۦ8=P;W8V ju5zл|L"ѶMɤu _>6l_h0'0(Z"i2]z0hkB' XjBb€^Z\^q/r&8k!; (*p"5زDtR艚DmKi ,p5I>A6ԑ ̸6J¨57Ɗ $U<`Aw&AADnZ \xb€^Ѭ// bo|[?˻~^)쾸uHpoCA#Lof֛{~3L^\~?˗?~ OKxgcKnAQK'334Pӑ>>Z5X (*pR* d72&p)}PO$mXj4&}>3x ⾝*[vy,s5)tP5kCZc=1qp~/{vط]Wgf|#\]vyߊ7ˬzoߛɜwc#˭|g֗ɹ8-c_Kc8&oÊ5EEvTٔ\ 97HտYQFc<0C&3,i+;)%钢`Aw&AAݎu;BCn?xa(~B+/O7;w~=zǷ/Q?C v.+:&;33"Crn@NG|ѭOP(DQQ܆ĐTN<2'' {SvmC1'0(VtWڴcc׮r?4XX#DgCJ fO/|3e5xO;9܁! ~#h 8eEʥ?^Wٝ趺rv)=@M(J:_PXĔTN<2''՜OZJk`>1sX{gC ?fd1bJI<y-M'Z,so"s̪|˟^_|Vuu|O;8?,0~wKK}vgfJ]=KIG(J:_PXĔTN<2''՜OZJk`>1sX{#JZC k/fAgda^A\ fX<6<mʗ.G;wO7yQ=Y_| Ȍ1@j?rJ=}|tqO*hkNwnHln[9H_fQڥzcdJ:~xԡX5gd݈i2xW RejH,p5h;<4Sa}u{3?ӯ|ځq燀_Կg"nn+Xʉhh),=xD?Z"wB!b9NwR*fFihEk-,p%ޫ8A?[!&i<D!67 ,J$T䱷;C-j{R56(BOn9/#|~87N}xewOG- Ȍ6qۃKK(J:PR 3bJIl['rӓj'-5h~z=f=|X[}nAT :G@U3БɢJҍ;C-j{R56(BO%#oo{;0^7!#;|ѳݿ4Ow9aj s<^OGQꝅBE;[)IM sn'-5h܃ڃhO_^ڒh0ȞIu;Éi31BO%ϻ]| WGج'y;0)?,k?ה|t7Ȍ0 9o/ ~+֮,@mpPǔo@DۍDzh6gXjO̾w=h>~%L;to\JR /zuSԘXBkhcDD.?iPv>pp#)V2CT+I ]fۯLa"sJ<#H5L M"u '&W@Qt}XǦi[.zEeU9= BҵWO$TLXgڀ1C{^wz=؁q~D~&_?<^Jgwf_Nޝ{WEE}et)k1FG >DP$WI8s@r4u+ܐfd//"I$,5d 67w/}T>;8}/_ZPy[8^Jewf_NػvUz*ocu3%鬢זc^EuDWXj/dm 8én0;F,о̸AJI>m=* c DM"g)! Hńu4Roz凨0|ځϻW&@>|w2aG?3ډh*lX5tKػvUz*(ncV3 P;cxE'{Nqpd"{`Ϻ^iirFQT\f\Z` B,5IMMTLP/OQO|8]C L6@kniڻvUz*(n{Z+G:=-xB*_j5 gZ(S7en+M"{gEW94 FevJځp-D -[C04lK~_˗a/q~W˯dc T^l;!⒦޻ (*iNwo BQFu P$:懡41jΜ6Po` ЁL]PB3̸AL)IGүqЬ6P{! HDЋ8kg6OKo/xy\T>؇N8zSm Ɍ'T6_TN4;VMTY(DQQ܆] T `VQݺO(V![WeQ_Gb q읅BښW9hk8i2GfCB TLfS1aS_;0δ~'!@~)+$32ڀ Pvo;VTY(DQQ܆۩PpTуn݂'{W72ދkݖ7" Y;kgg3r-0i2J!C4g'|A#[ҨP !4.k5}e$Q y&( A!ZJ8Nv\0]%lV t]~Nwj=;5b"*S AUZm$H G zba68Pd(W)U[Ỳ.:5}e$GW@B$R6 :Zi6+EdUmۍOT';ݱ}Q k0$(oP@^޼IAD"e0]' E[9Fm$bkGik;/?m9G:~Iz~JX9F]ħ1;2LR%+@& zx.OC$N'YR4i퀃7 ,aڅj@DZ?i-PQ+L\,OKx&~I"SXQˢs65a '%(GO}f܎XMGx_`yԕu\+@& fC*B$R6 :Zi6+EdUm;ڶ=PtԈMB#nH\Ч4ś68Ht0Zm k8ʁGӓOwMss2u==IՓ?˲Hˀ]YՈΚ2` d#[ūP A!Z)d\4i퀃[W` "C6]xqr:Kcֵ,+D2!Htfo_R D%͚(HLH x jYsnF"־&vW>s>3srݏnWJ:ˤ36IPG*~`/)ep0 D4O x jYpIe4|u:zȩc<wIEeLc|$R+wʺD.Atc xyPd"HdKFӤ so XfX=r(hx65SwDZvZͰBT( љ}I)3}O$K`f,97j#k_pr AvүWљ7v&Wz5LmH*1 AyʺDAtc }xLE"Ihp 8wă\=`>5T=$oūT'OxI"nt4b ߤ82^gg>{.՝<I~8ɯp Ae%]Y5Κ2` d+W!1!(DX_S8 8{S6"x ~|zt!ӝ%5bxJ$:3/:$1"T7$f]%ltH@k_5KVlC|[On$YXs&z{MI.^ h< !#D[P2$KYWL̆U3AAL $:Wjjweܛtă\=`?MA*\t'0d&B) |_R d%D"DF-6CC|"b hzlgw_JG 5mh~>1``]]x>H;D HY2`MaK AX " @d\U6m2@ Q A3Rf W-I 2I$2j1t4X\pi |m+/LO<=m #!'ߥl0`\?fJsIdT.sw XfXr9ļix6UF֜4l$m=p(^(o⁀^+6/V#ZX |9,y]X\y>V,|Yrk]Hy1}|0;<}nx{$"8+?7ΦɼSX!H3W-3ɍW0t6"x ļix6U@N6)ūPz |_R d%D"DZPjESЖG,yk[XWPths)P39R|X͓'O^z މ`oLMϞ<?%2Xtw,]xTԕ̀S6•%5bOVS)OtX$p(^Pۗ䙐b6*0$Bhgd[g! Q}@6ǩEwtx5̀?Lkm=|j:ql9 aIDcwWG^xF}팟;;#!ݽv@?# <'ȸ1!+:wP"J02IūPZ AQXI] 8= ,a3(29ؐsQbye5q*ۍMox+ySYjHj8U h#d[ge}\BƲ85~(![k[Wұ#:+`Νy*^ܒTd.lY9>!LK"\P,,TxJ:zt=|`# iee Ͼ EuGY<c. }r52eMQ ^͌tJ :/ 8+͠|t[D-pK+HgAUwc kUXketJ r+ߏcFtwHkR]bThL_{&$Be%C]J#g!XW83>F0^L ߕ !*B`fzC7-p=VZo\ZQ4cB:&;}2\t;~ @N~ qJ7-G1e/=>Oy{}|4|͟\K7FKn oGxrY},r$ڥxJK!hu@vG 8g0]%ltW ֈ5F>=NX LO>_b,"ls $SP+:Lq) pf:<1h4.mULTCq-\dM 9 '*H1JLbXXG"D-/}+ŗΥ_GO?>9 Ozi2Mt{aAʾ9` dvO5BYj5S5p")#d̾tGlȹuF0^L ߕ !*B`ȴ"νSIs6D5Fi^ HȴE )7>~~4IQw9}qtKN@&[z5T*AjSQg +jxS"#"8l}}9}k/J~:)09^LIP€5I=Q ku ] =!p#uPAt!\YX#֘b'1d(N<5D<ɍ؆s7Uǀ,bQ땩)+DZPjE;N&K8s~ Kx+B"UiE{CN01Yjqh>u}8!4ehG"D h Y]]JW^@h5s]ā{+e1^,k U(A[g8l;HMU)sj a?Rj5ʌʉZCY&C X!*Wۗn@jjq5LYI$90Xdua;GLWƲ85>E(!0d~X V$`#Y@[C5@8'ᠢ< S<Hta ]:Nkk񗾨vpU5O/whW? 'J"ku d`P !hu@>ImASUj[!X{ ! sxk.mjiᇲL3cNX!*~Q,Ϫjf7LH[R mZilEʠP*b6ǩII!$6w4dZ6~ hrrH8Q6爃;1(;%2bCפ5Wv?@m<'Ɠfշc,+$X{Bi#m(߮Ov>8hJ18_܆q6Lޥ иy0q % 'rG(o諲Y6`J"Qm UVudWQ`nù>m4.ma2!$REhCFT\XLbpc'wH2\Ts"=@͔aDW s`蚸q9 ӫ(y N~zs,+$XBq:{a#PޭOx>*`|!K ;D\l |ji0r㳍 ϚBM  o]F)0$qPjXGqs݆U7K@9K'jJ2u| puxZ^لRVjLO!͛W$@ ~($X5Z2oR#dYbdN wEv!M 1O(S4ޚ6KxZl0f\ܖQ[*F)0$qPjXGqs݆U7K@9K'jJ2wxj8xl̓p}Sm3=ePG"D, s`蚸qj5\9U8p@û\en*AKP"6$UQWP(ED>NBQx!{\i\U) Zx4$DNoN dU H'Iph햙 F8H SU sXخ6~:i! e[ӆwiTO ?y1 ͠ͼ|C K陊ȇ31,0I$RJMZuѕ !<:)3IT6Js񣀃 Gj_`:'jD5#BCNx(ّ,&nj) Ht~G_T Qj>H^ےиܘjM㜃ʒ)ꃝGC٠ f@r5S!KL#h7 6 H BU( :d+ 6k jQ-<JgBPb)[3Aʉz5"K q?nmjˆwi4n^C`74@Vկj܊<|_05$يCb.^b8\)hzqjBH9 0PN ԫYfPlruꁵjûXe& XZVl_Hs}kDo0,nhRGQ 3dv鋕# = b ܙ^(G4['e2IiMO^9T2|=p%tSҥ[;3jATsAX5 Sզـq:2>xl0~E[i)/YA! kT[\@]%l&qA=X#]mx6KTl0k:"߾57 7 4BUp2ʑsdt?OT1^Li/L#2' *@L @~Cߗ-m]x1lJm+kyϔs ^MyL~S8ifiR_Q\vfZa:޷6w 7^B$IoAy7PN zRخ6b8X[6K@%*P6m Ў[K9"߾57 7 4BUp2ʑsdt?OT1^Li/L#2'WTn@m4N|wu`8|GZ?t;w/^HwB <ܙN>g9ȼQU(5iIE]"N!&V $5`ʘdM"jf8XjtG||KI,/ݰI%* <>~@+͠^Y]mTk\ûXe& X[.%8"߾57 7 4BUp2ʑsdt?OT1^Li/L#2'WTn@m4lpᗖ`~LXv|\>Zbgq<+!g_ :XNzaբȼ[ByRDdu'9F~W (Dt:حOJ7PN ԫYfPL6A*k5R]b,QỲL.kЙD}`{ikLV3$7ʸ=$P EAإ/V#3g|z)rgJ{drl$5=Q rj+Ǡ8e= 9@z b4^C; wH3A-jn_jW̏l.߳u\Jv} K"I:= 'W%(',a3(֮ nmjy#_xĘ)3$'Hq4  ׀+)DBQx!v鋕# = b ܙ^(G4['e2IiMOB܀J1o fn#7J7IEc'J5U$[`ծnb(js)wZz-ũ:M9 P %$;U ʉz5"K E+i9F4K@%*|W1em:3IH7C|ӫ@"Pt,}92 pf:[, ʉL ŨHd'&WBu6kF0^L ߕebLwҚ)3ILH#ɀ$jzq p0F1*d:'+MbLT19oS!$L0T3*^Cūٜ6`p70S;s L=A؉L#p@+''w)|D;Hz]zM9}MF /?< x)}?"حO@91IG'믬s.S5 ߕebLwp>Im,&YDC݀$jz(\d]V fdDAb^J4|8H%*07Hg] x!6IPG*DaNU(@6) Ԟq =u]3@1؉R !I;$ڙ5S7NN WKi '>>g75wy9pWd Pn} 5n@ZzC#iW2Ab~ԇKN(|W1e$^<P7  ׀+sB2dDQ)|-2SFW>eJ7=OM"Ǒ0 d ? ( ԔqΩh !;dCb8Kɼ ق?pF p6U-1e Wr>XpryP@91; fMh\ƋIỲL.[ZkV&듃bT4dlm9G0Aqi21li$uYUs0(RZ(v:(=5B-@ֵ|zN:om%}˔'& P%aHo`2oTDfb0^C@UO)qrrh;ąI?wםk=xx`SbN0#2@'[ SYKr0Q 6쐭3U>h4.mW]Y&t-Y2.+&@ 5~eg5Ed4ȺR; 1(L4!Z5NXw;Cw<}fkUfⴶJ cPF _*U.WO;,nܻܩ2|PɳLQy' (;QZSP5T\5NX`B}nq9mk[+on,MMlphk:*Z00#48b?^yu7=Nz;{6ߪZ;]#j#J)bj6kp0e%Vҍ7,V3cIA=N/ _IϞI_څœ#}6='NH1؉Қ /;޲>FnݾJS<F7^zcñ^CA.?zsIαv@.4i^h̔AVi IeAb^PiTX5{b F&t-ݰ bB";( x"Y+K _ib$?^^%I?#pP&B N[q={⹒nwܱhK篽4A~ %k'm'Jd3đ}8x"rKe |=<{xM2W<|'N[4)@`v)FLS` aqX{uMҨ64'/kdbLw Sf Ԯ0O \vP\uatdĂWߤI%Ϻ]?^=ʏK' O )-;(n ]',@ֵ|j@1̿7\/:.R|i Ϡ*p>%v~aؠhDI ǖoHӽ_ Fq}EW𥶮2Oy (FljTd\%9["JªޓS521l)3IjTa@pwaEue$mľ*N@fjTXJtz+r-/qC:yC@{Oĭ{?rHP7$!_4vq <㍙9}ӽG>}wJB~ڮq@);dk9[̳=% SGv!j:eKSf ԆTaW _W'Y(0]',ib+MoR쀃eKt7_OoT9=\{x3Qx7OVKQ m?9tA}_d7>o |GÂ=񖁳ͰC]MѤ4*xsL.S`X3c3Paj\ ~j("ʀtdĂWߤI% ;ٹ7|w'iugiK^V;_1a`p]+" o+uf%lbŶ_iTXޓSاLL2Dj0T`i> ~j("ʀtdĂ[ioojЛ SG3Sepo#'7X99@+u )6z;;~hwo2OKxݰ|)#섻IVOvȝt҉-X7)xs;]Fٌ 9$WCŇEM M EPAp07V\YX+8aOr?'6{9`?x.ǵԺPBGj-}ZO?gڨy:1$ x%}tL2 !(^w=޵se3 |2)@&Q*> X-L EPAp07V\YXzOx_7SᮥeRQB Rf#zD~?gp/kn<W<t7╀zPی A ڬd,6.uRô\we]Fd5$WCŇMC,`sc r88Wb;j!P!#1=s8m ՜#pݏ$>xbFgnS6+ )t//  (<2ѧjI4q2(_  7MuN"ʀtd9@2ȺR;H~#ڀ~8_Ww|o?u-P<"˫-XqD8<=#: N<}lVHvȝA ]ߍ0TV4J(VyjY(0]',lRֵaM׃?~66x#uG`dнl8rTV|bC7 nίm4.·Jne6+}rsk>R t 5' >a4-LW @fS}Dʺ6,=ee 濔< m[bep C;Wbi\VN13Fx,u=܂9ݍW9un-LmWUgk>ALS*y+@Rk I$0]',j2퓲5H7)vA,zmFoğTބ]p#g WGX, 5_]xNCxO=p ܀Bb^ô\we]F$8@ICV#+'k I$j[)LW @f>)Z_5|bdzQ؁h ~TV\z~6#u=YJFj!|տ2o|,+d@ @'w+.Rô\"QҕeOwՒh8e&Q*> X-n a8XI"QJaOX2 4 ZiV;+p9z~F#C]#hlz#+Phw&:GAp6XTIOÂI@]N[b^J4|6KPɮD:* n8@IƑPitduڗ1H"Qx 65unԚP'}M64|(9ȶ1UaEίfph_^ĝlT N&lČ^[#U<ͯ?+p!|b ~Epre#i0m@1$]tU@q*#Ň2cDOdr04.־&I-eT:qvxb\Ο?tޅK`Lj wbBe$sr.:o_ۼFsnT|H[eesyQ7|6KPɮD:* n8@IƑP`4&qvK 9 ԒH$ErTL61}n'߃?5mrjy>5d3;pv 6/+oÅ^yuw>t(z-G7] ~ M'G1Vl9m@1dtU@TDl)> ?d $H7qaOdr04.־&Iqd[ Id|f쪍|b:s߾?~^dcƯ'Nx=lDt#F0ݵڏqCD鬔48@I;'Cn?d VlD QZaOdl,Cą8v:ڙo?E_WwWӷ|>=~x?VzZ^٤Ͽwղ>? ~a~qFO677ӑ#G/L렧 Փ}f æ^rΉh0m@W$]tU΀urLQ|*0pS$S@}_M+A.s0ub5 ߤ8X2U ;e2A߾NChJɾ'+/=6\[,~"nܔV֎@'׾u!> 鱹zM7ݔ^zuZXq<,|/Inگ^<{h'I"e lq8@I2Ň\y*HԶVԭTԒ7|^?.k ɝů 7C=gn*V/Vbt̓ɇӫo‹=.j >Sx/ɀ.[o5͏]">6JyزP]uzYElُ,`` X+HH)%miY򍯦F]AɆoROٖ2FR*u >*~3f~]X]]J_ѯڃu8p@v6_=tG? O_MwOÉU\zGO:D'}|-^ Np 'ntûSB eJ#%MdqbwN T Ԕ@=/-LW` rU{ SCQ&C ¬w?2>ֲ(L.7^8tz/ q |gSp3|37DrwУ;'١wĺdz/Bm,]ep6OP$d>K Xf8ifϋIqdzQخL-Ǣg3MɞG`uu9}Ϧ\ Wgz%- f?W^ņē8 z۷ftivA3e+wfq2(> m?djJ B XfP2S.VWU˰t?C&Hg>烈]PWx~|:sfK7^j-K;ˏGx> Ʃ%ڻδ˵_>Y[XO95% .mp A3e+\O3S& z0T`IWotEdJ` 2S.VjyD#D q/y~[Rm/w>\-_, %2jt > _H'fܮx0+}:}?"H~P)c+Xg(4 ;%P|*PL*~ Ԕy* :e&׆BȒ4|fz)۶J&1(?Գcu^~_qyߑgJȢV . qo.\CLY݁Φ2O?^=>! njѾOBV\ >k??>̙3+ 7jkZ{-o/O 歴|`'=A: E%dM٦.˽C$Zʺl? lj|}:P"Jg<sgs'IQ6 SL77#DU E}5"X_[Ns ; p653!QE >0hm7ΧK/L'szxAxfІ)|S?A/3?N@stcƍZI{ΥSp%{B_3G^x8x-,+< ,-kvO\Ô.[˫꡿fad8NKcp? z:d>!#CYJ?wNysx࿰u1/e߁U 2S͵(3Z`$vҿ v)tpWzqG7|gJ?Bܫq_1-6vZ߸}+9; u87cu[q_j ^ 8y$}=p> x=Hn,+ vebLwҬ)3I6|I-j3_dx#̍*~?|Sq^9y<@鼔5o !XU46kG?q,9Vk?jY֓+翸CŃ|=lgӗy._{4OMۙ wHj8nY 1R~2 .\OӇܤ+ ʌ|}]Xdgѵ0ݫ=\SgrQ {mg/d3<y'E_>=_k=j𽶵z!A?v(“u2c˖j@&q=ʖ."$fԚ70^'! Bs,4|(9H%*PTu]$ |6߻߱ne5mn=2vklFpX0!Hc[Cv{g7xlַ_I_g~_?w+n7S4sv~.⃇Hف֛VHwUn\O'Fy38CvgzK\Vl ?JxqNFw^[= ^ƒ>կ~!I@=|RD|=ﵭ+O⣇g'v۝L'd05z读 YXc iOOŶJp8DĹHżTh&Ab.QQt]WL5?w4tqPfG+o9cwH'nؠ1޾mR-Ñ ϕsΥ{,=쳰X.:vcn|=Wz쳞 %+tc eڙ]t$5|I-j3_t ̬mT_"YKK d]73!FVI&t)/Âeҕ+mxAi іxAc:J'mwg 7K7z7StÉy9){PI@ZeW`=SyDtRbyzYXc 70scMbQW|@f.-A,IwN̄UTL[%]ꢪV1z _JpR#`fLAW: ~={?zTckqų_mAo|#=sUDzQ<Xõ7[P? n8c^/YDtY-ry.qc? ڦ,%_F(GjP2^8L} -"8T~2oT&%l0s7\@SG~F1cVx)]8,\U.˯o,aV\HS*|ܳގYܶ j#Fe[2Φ S#6s%@F%݊_9~o:SCI ^7t~{z,{xWX F^;~C^z[S:~V8|hu豼X޷%?Iy2 =@E`*m1Ƽh&N:H%*0}]rICV&xX^hFf8Rd_ 2P'2LnqI:Nz%do*/@`mjCI\# ( ~%ԫϤ:co <n{xo8iz[־|+ںZVN~+V:t~h]k+ L?.@ևQE Af*m1Ƽh&8H%PɮD:*g; 1I:CnA%û2rG| EJr$0T`d-`k0Fpchȧ̃6$ S< $I4\|8M[#p•r_m(,7n'd4ʣhy:m߆'4N'f|5xu& ZW;+,wbyq0T {4o{ s ɼQ ZQ_&3ii 4|P۰|.dW"&{'!6Paj1;<$si=ؿtIs4(!Dmy ֨Aɶ ,ff4Q< 8SN2$0T`h2"?fdިL"pk}M޷MhMpw2ٕHgkfꝄ$SŇ7Qғ5^z ҕ _\ՙ7*T]oxM H" |15j"Mc=y=c2r^MX[T S90|Jjd"Sҧ~U5s g=hخД$AŇw@370'Fe9' kE&wbT@bb(dW"wb`4~L J`౴k'*^"kU{jvˀSQ&$iKbpkԴ6;.u1ƍpbOl_ϥ9 McHTz($^-8 U<W}p+#^ gjmRf gN \c s']|3i3oT&At+Ï7Kcb^4|b'$b(dW"u3$AŇ7P"w<|6M;,IŽ)p@"D5AFWjI~̲~#v`46Rj8t; ,f0Q#QXM%īѯW#Tsܙ@IDAT8Uf},'=ԮPf =U|*0pg4YZIy2 P[(QTaB|\Lv%͎X;s을0'NPa;Nmf%y0*S" MX"p`G iK'|; pm6o6\6As1j$*iwoȨp:>$[矁_Im,& z0T`h2u706Fe9@"<p\Dm1/-5|b'$b(dW"u30AǏ҄*%8=\Jz8Kn4cc \D<K!QHM[3w](tyGFg6Ar`Fi(@?~!]_5;x,ȍ$Aw @OffިL"V/ @\Dm1/-5|b'$IP EJ$IEJ t^JD_)>vwf56IAŇw@ӑ!O:feިLB;W ."ØI'Bf:]tVT을0́҄[+%UxC ~ p{a?-s$Oh:sHԀ/7RӖ)QK0踱bJ،EL'ͣ,B.[dޯid@* yx|@#p V-1Zq)> MGY]3Tq%NGExR7)w(]tT; aHiP΋C87m{%ɰM-xC ,Qzn~iKVT>~7!>GqcŔhx'wIsP7oU+hRpxN zYEqq ,5͠8vBty$YOP7)έv(dW"5%NB@,.R0T"P21N5<F:ʣi{~JCz`-" MX"|1ئ >c𭊷^E(;V\vjٞ7kJR`[Yafp}^k+%;Z`VA%b2!Q 8^&xk)Jۨ]>8e+j9P6T0T`kdb)GO5Դqw+k7CK8<":fD[P*0 ֎'W <,{Lm[s)!Df5q4:㳶~xd^Ea1P s -펵ζ..]ܢۓ)Q#ݮsmMx|Pe#k066  ̆/h: wHn~SkAgq@$Vf4<ilIiEJ$IEJ t^J&Ɖ CG&ߞv^J^.?vwN{3%1Sm\)/mR鿥5xh >(}rBry.q xwڸse Si>{'esfP0-|,˵~Eܳf+^9&)IsQ0+D"l#tdXOMUM' X);㩈%m&Ŧd v(dW"5%NB@,.R0T"P21Nm_l^06>unv;K[˼-mĶVmZ47 ߡH%"j,%c1 t |Ǐ>Gm{Ǿ45ү`}P 9ЕU˕)Wj^7?qW ߤشLEJ$IEJ t^J&Ɖ mX/iv^`'W B"{ dT( @0 w,Mi0*0]p:Y0\a-ZY9'On+# 7} .1:&#%جI~ '%Q`.h: wHt5੅ʔL&Q> }LivU|JRߕHgII(O*M*RP21NV2oTh64b^P_hY4j3hy:0oJd}U^:\5Jc:2 T(OVH;S49GF^I4 @LKj%XPo=;Z 4ӆoRlABaLv%YSY$a" C:/%Da6e,&ɼ*(=bKļ&D bp#,h|jl3Ӵ ) 90A%"ABƯwd\T^n5ͪ%~v&Ms0YAMxIb `dm"Xf.T1%11;%(LFY4j3 *UyWXM_?ZJc9G5)$UB1퀠@`lU"< Ijʹh`c+=k9tjRT ΧeJv>I f2 C_MJ&ƉP捪ԎP!EUJd|5ɀh@6tZ;jFׅ 9 M!M8 eUlo_Jwn<Wq=ūPw(-d(A0k7@9P6T0T`kdb;,dިIK%.B#PgYK"ÄO`MStv40=B@KoRǛ*9y-VӏȉcGGm(MIūQ'pccL_ZIV401p+ 7ASILE %jye. eUoWWZRwkn- !v-5x3a1fl<Ą`&fƒ'  e# EH-z_k眓gs2_U]s;7}TA/CD?v K#8 SEJfjk-ɥt&U;M>z时ĸDŲ"j#жr.Y\ܑw^nn9b`~==0)\v.CS^ҰNn[P)B"a!szN33R("*ǭJ\L Mb>Z̓:THSRTί &F$IN2iim#n 59̆BbY]7x r'|_߲nH4>ٜ{`z@\]{`rN q:M/(bvd?dj(\`f2EtBQ R_|@w[mメtSm\=pz@}Y) Ԑ "fQN|x@q9QN ) Vq%.Ω z1DRI!Plh}fW"SEg=%3iX"҄䠟Xw}>BjaꐨXVW ^ê/[^ڑ?:};Li՟z3fs(sՍ"w985$b$4pqcbIҸX q htH`H`ZN 3W`;5**<)&:98̆4VON&CݎNS;^#6mq'H@t+ AxW@6kҩ6=fey B]G+!hSC,FbR?E̎'n9 pjK@!Ғ֨Wt12LjUK030NͮJE.zJ(N fCr*+%#1RVSNS(2e#l!=0iD,Rr?ui] g 8sZ& B,/^I_tϞ5_q0~xk7+sM@)Y81;rfqQ#vW. H5 ޥ VBIiI3̱sXbe|HRIfߩ S"SEg=%s; iei!-oJ1Ā?aKe9`^iψA/uc6Ěܾ};=Zr/ spyǞ_۷p.>h> M8d1tZ8P8:Ēf4^wiUPR0DZsVbYmPFjNm**;ٳXaPvU_vEz[Åb+i7<pImfH3¶[ ǩS­_s\=]m:w:)(qTX gF4ĪZ()"-}f{9`b+WLz(~"@\(fa GD[)187Û_IWJ_A8TqQp?O&=eGzk=Q]/);kr؜gg)ef$o,XV!SP)1.QXg8$sQFj+.D XLA)V 2' \Ifa GD[)1%_};*oGmslI'ۇ~8;v&:1O5N֚*ycXxk_KSePՐZDʉ0ʸa/(qTX)/Kn.RQ0DZsVbYmPDVQ*0̾*1LRbFqNjܘ)%~Cwz]<揓^;sJ!jaq-U8S8ʄaXQŚjDǬKn.Ī@IiI3̱sXbe|@ZE0Pd0W(>"J9Ł dސՇF0}̥s={?SKKKu{Mxdb~], Mx/'X<ōL ̢5 F䒤Kj-PR0DZsVbYmPDVQ*0̾*1LRbN7oƫҷ|ݕpȿF]90%7n:-mxyc`xa:8wd J ESyBD ?Siq )X*C%%kӫ&)s9tkprl\=\ꫯ'  q6H@Vb(2J'ʄM1_l3k4-n!K)"-}/Y^5I1\?lNo|utC`~=0zN}]Fq}8NAJq;AcL)ne8*ͷ; B\n$P0DuoR,U2P&wkjPd0W(>"ZBQD -߳o#ٻ{bnUW]5Oê8MFC#w2; 72a?>p (^J7?!e8s)#f;A2ESyG?,Ehh HcipfS5І; b@pبr R`*2 %VMq~wUiEzOs{߬oxؿNBxE~7i83kjNLTQ&l-tZ4R,n"aTдL8uҗp!@L--6&@Jp0FPDK8G_LvM4>s={Nyοkx*ĉx^c\ &2nX}GƖ"SE GXFXFGZnП1{`=}nSw NS"rNG罐!C'Zoo5 k /B:cxwlG/;dP8*mVe=r o\l9,BjS F)a6v'4UP|H'?`K>&=I@ssLZ<ʰ#'f mţ8?~w34'诂C3$BKӱcgz=2W{n]/Åatz}:s~c!w3t8 ELL$&2nX}[&"SE8"N5x*W~KTZ^Y3dLo? BS ?^?5~~&Ӳi~@a̎ǸDbM5b"\frNRlؐ˚efC:KqBS)*+0"SEd2e$J8 ~ry?#{,*,pA0_z|\=خ]jɿnF뀵E@}M/_43N #ev8%*k2# ޸se8 .k3x\*@ q M>ZCX0NͮJE.N"֨23''255ۮpI0V]{ǟgoy2X "HGqŚj0FEH7n圤lY;_.kq -Rv+WLP)Vդ 3s Tb𬧄bDAD)0'''q@w 'O!I_Co;na' t.MDv6tLji=iFfʺX~ϰ-]7v]`=hCۯ'&~]w}mMlǃ>He{C1@j/9=r.l/3ɦ)1.QXS بiUQ8B({S,#e!%ԪtCqafa]\LPhD6N!9P6Ͻx4WNytv+-с;ls%Q2ʋQlMUuO׋{Jްz9{q ruX8R:3p H\]N/z'={ Mma=x |ff.[ni?Mu}}taw $l0pDr̭9iqGn1{Mٶ?nCa0hoc)=CϤ?GH"ٟ쭷wW_M䥾o?NͿfuֺ{Pp[EfmɓyNEJ53Ć˟o6tۨN\9S8 J0I ;CT^h+%HT,0')'q񩝦 495MMB%2ԑ7AbJ2VՐ6a EBlbǎK{N_W|ҍ7sJ՗Oox}ZÇ ^"c;q9j_^ݚ_})((IqTXAxm8[Y5/KL,UՔ \1j|@ 4ICc}Ub(2+mef=$. r2?XGlcdaNeXSOC@҈)}D;B`&.رS~o!^R@8>Yo_{E>J> ܗϫLɅE̎^J,T6ƭ֗%Uh7)SF\\歶)H73;s J ESy##Ql:O1RfAgKh*b6NLhdza~uY>(]AST :/v\L?! =/C^ep]vmkZ'Nl[;~ o Q-$ݺ-Br@a̎ǸDbM5hiʪi}Y8]ez`Cިdb+WLqqP)MҐn fffa GD[)1Gb-0pu~rRf< ވmHrY٪$8ʰ2:+6  _Mb x{$=xb/O{7h[6]/)6NPT^턚1$X㒠Č6ƭ,댒*1!بlp)V#..J8D+/R#3P8+mef H1*Ue;_XX U@4<bCԅ[?l3#t\mb Z7n)yp:fqUG z?op.DMchQ8 P3Ub\4цӸyQ8]%vxclTUS6r%j" ĕuUb(LRbD2Zad$Ʃ̜ ܉г1r.-,G hn_eXc&18S$d=VH,qTUqA̿p?xrg 5Y9 N;EX~L7rz. n8Asn'y$X.Am8[Y.?J.@rȘbU%J8+/R#3P8+-Z(9h9̆ʚ2s:MDFٱc n'7ja(M;fr? ipI;Hw{(Xd0I7%O< 7 _~`pg͞}n[M3nj 5c\eI.*%Am8[Y%Uba0AjqESZ-Px"nP̚\J ESy#E+Eb-0pu~2TYSfNgDF6MZ˩ 4 $"Y=Ld`Ty)M]NH+/WA9$q?DC*_m> ZU$0NFNNdq:[K\I:GV%5t,M9z үt W*utˋ12ǪEsRLaV& pyAw9 MÄpIU>e K)S)|c 04(Sjs;?[؋z·tƚnE,\s oGQqP52;r(t, ^sm>!Mi"Ed1)*R>b5Y1QU1. zI\F;Mʄ`˱#T7ـ9x^ZDj:9OOL :Y~n2<%l]'ebj*cZfjdw%Z[\{j¡nRܔJ2H;LV߱/:#o9A3+EU1. zI\Fƛ]q&[0Pfŋw>w ȧz='*<=܅l4,I .ߟy:ok6E/ז7VI-Lna`t2[YŃ5鈱$iTe¡&{uRܔV$02nX}'O"#&LX'^2i*p'$au>B00}{oBBMً m/B*=M?Ntf>P89r&r({#f\{$*KJ7t˳?c.mLBSm>)f!03nX}'O"#&LX'^2i*p'C>"ۈVK4[{ WA jm_D;08[cN grGP$VIT,!6:ogAj]R%9*S,!MiFϸabqQq EzI\*㝦wBGdъq%s//=Ν?iy.f]e7ڽkwkl>MD=[Oh#ojkYż{g|5F)kjD rew% 3t@IDATLC`ب $ 4EaJql~^L)|1Tg긨8 Br@SUt \WHi~Ѓ'm'+K^z:>4d\'xMh9hYf7ǫL$x?̱I؈yq26d@ܕv`D`VwN{Nzo# 9j..jTv)g 4x'X8nd8)RR?ϰp逼 dezC ]zo6ِ`IT,"4f.. w1=s,9sN `,N Ǭa[ݟބC5c GӂfUd)2߸?s\>LN%sx#=u˳|=oqַM8ClH7tK A|S,H,-!R7UulA8% I\@7wx#7}p DŲJopTBKex9 ,NZRmg[[!X~ 8jB$,#Ӏ;Nma7)KZt=7`!<5sNcdig +# ޸sPdq:PbB%b> ֱ!iq;FE.zAәX'^H1hx7&lH^$*W3M@9!|[)&њ;r}kXZ&Q& K`#QDXzS銞{bzݝ2;sl{sA!6\4xόsdq:&1cfi)25Ŧweb(r1UhQQT$h+''qw } )/mqeuE>FQO5i:YĄZʔvA")z (E3cB&CAJqttmә}I1;sliL!6\3~L4i$X3ֲ lpP!tCqɓ9wTTbWU &FE)URgrONF;M i4 F fC%Ƀ<.U {T2ЛPvLd("{ MW6- 2dW.JHbBlLQH,ɫ=H7$c}VJE.zJ(f01"(JD A?9 :4U77}f!QU9O3:}UU &2 g L_W=z@a:8beH7J9P8@kz&= &jP!tCqɓ9wTTbWU &FE)UW29tȂNSZ} f䖨XDh6VtuYĄ4GʔVA")z (E3cB&CAw!|뷾t5+_s{@ac: dH7n8& @kz&A YD;M*r1U⁚BĈq#Ia6 ,NON"'+㝦cBGl757xNrʟHPb peJ+àxL=B{ҢT`B1! 4=׾*Yf==0?N>'# ޸#}t_ @kz&`$c].jrc}G骩T6s2 y2Ba`q~r9Y4@>bw DNGP @2gu6N+SZUc=J'+ЛPvLd(HCz9 ~07}6;5f3FG;Y Ǫ>T0K(!lJR+Dsa}htT6y'$X"ҩ;8~r`u62inL+n 5zD%ۼˬYM, q,D*\Җ0SJX`6VĤ͎ U7veJCl_,NW] XUepK|@9[fa GD[)1Gb-08AO9 sxMW%*jri l~ AĄCFt&Qef I\KNS3WB4¨ {,=w[xQgӋΦPK/Y,4daHt8iĄwۈ۹oB >.neRd~Oz;xL)M`XU-QFh\dqq0X;+@ҹV!mBv.H/tƏqOʿIM`u{MÊzq[fTf+1s\b=0;PLi[Ǫet2`UXQ ǸDŢK u&A)30QfGT^M"Jl:a?q)3?O個`÷{톴z ~ަs{%_tEz$ ]oƸOCm=}S+_s5m/_s=r8 i qAIS8]uL X8bU=qT1.Q(kblkb4H a G`*oz/"Œz0NOFb*kOS9䏇ݖ9qwݹ7?|Gx˩m~҆`:fgt}'f˭:cG'n})2|hd|8ɥt5ACfCVB:!AlT T Syo EI.L aVXq4Kٙ*o{,L_%[`LsG~Uup!#k{`[2SJ. X5AԄBE`㪆_ Tqez2`&(TevCAK,l:|A'gv?7oM_Cԩ K @Ξ=I߽'98/#?P1֐7o,NWI] XUe5!PbظY A L\1gJt R`*2 %Ia6 \$PR91\5:&Ҁ Z@t/[+nMEr? ctq3]X/cs=0@0iRF`,TqIbj  ,kaCpب$@)<(\bfIM*XI&o@zϷáeiz]<~ Y7oۺmWw֬{e!E@5dGK]t] X8b*YІT ޸!Ru J gJs R`*2 %Ia6 \$PR9)&r!w-7Sͻ| |ɕ]oWk~n$(߄暫V{ㆴt0Ժr^=!rN`u`ሥk&dJT,W5GH*Jb\T8xn("HreRp W'5 Tc}&eN"ȡx/}A:/l"[ @@i)Q_ c:{``H-ʹܸ sT\ f0CR4+qX(Ql,-md#3jgUϗJ GlPDQefIM*XIց}]Kly\|*ؗ+(mjS$(xpoOچ&=0CJ`i q+K]8]euL X8bU=AԄBE`.hWĆlQ.PR*LyQf7$2)8̆'m{]ߺ/η?X͇9t~؜A_q7+nop35sddQܰfҥu`ሥ &dJT,n #ϫkBv%+[lT T Syo EI.L al[G|7<~.o8tz%пae4o{Az`lv{`5Y4Fҥu"G\8bj X+EI8?(TevCAK,l:u=Qoe? ċ^y`e>읿o95evd)WV`$%K֫UQ2Nwjt[tde&@)<(\bfΓ?N{9g= ~5A,$\hzN-k&:t'_D{K/ڋc`nHf!>=z4---:W߮]C^ms8nTn湀²3o1;qQ6bncV& V @Jp0FPDˤ0iTӼWk6B_#D|/~n6{x{|񭠍?~P6gjl.X<ϰ8ɾ/׼5 'T|By8` K08(yQ-&Ox #p-G0^N|8tMXZl䏧>Oi̅yp;Uش>pC ƌ1z9NA"@qc ŝxk` UO ߬?r }=|9T6x %>D#I퟇}?Ο;ީtԑtXyeZ}=-  ޻E·;WnU\z'3'_P98VOork>5?qw:"bKC8( T'#9؇ Zv8AS1F/;< DGplQ@9%; ^1/7'ܿxHz蔀lűȋ!m 8\wK/ݟN |~y@ƭXtϯ6pr Uhm?}uѯ0v{￟.ĉ}]PUn'.&.+o iʘ1F|&@}bP1Tk(TPrbذwL9Y|} xwr ;~> 64l6AspQs/B8zxG?". p;<^$Dk"`jDž Jk ^m Ǜ0h3EM1wHe'H{ [7SMw@!6Tbn=GHa6(o̳-Vo NHnG]=b:ҩPr+D:~ 2[s7evDIu05ㄏ? ׁ2mx"q9L8 Q2%, @*WآbޡpD-ClPϦNmrmhW #lp1:\Ǐ7!V!ͅOG>Mੂޏ~O[ { ^Xꩧ̷ޞʇ JAbօ=﬈7S%p80!PlDy̭jG 8 ReIb ETB,;e U_pqy!o>7}\{B/~C?+]f^7lx x,Wl_Bz]+nޕNs7}2x|;i^'QJN[ѵuXjڀ<΂a>\<_I3\6nH,yIb nUL ,ZE;;ŝQA*#dpQGa66NptU05.8ݟ~]= ;pڿOϦ\؈Y:NdgC=&Mx͇j;VB&|DWֻ$m'`]d\myq<ctGoơۈ)nHq<{$PA%CZP)Qb"&P}$#.nxmοK?yß|&~)Nv2@8q`϶v? |2akM:3)_C6<]w74Q}Mozxv;c0wt'ї=bKgBDgסpbC+i[ ? ?{.ON| ) ~IXB|6uOAEj rKqF# ;e xn3<#l;M? bՆxGϦG?zM8ao '`}HO=} ~&\j#ǴaqUΜ|> |<#<>{k/>Z3p]@kЙ|ÇӃ>&ȩNM8ã|+_z{[!nV.$ӫv l L8ֳ? v|7 @8 筆Z$<( "!Tl2Ć`m@P}5gHզtp3r |? ?x:o1p}V'n46J#ho8`<ԋa}/]!6 szK`&`!=C?/2}7ݐ^w>/<6z˺p; <o~u"]EkͿ^~84r1#ڄa=x*àڂi 7kr~4VSB#'*!6%jյ JkK4bJa6Lneu.z^>|G^/W`8 5ŋuCy**x(}g[-Z}uj귥ŭp Ϝ> 2'|> Wwb ߟ5ûڋ6u$'᷾Ϣ>^:OK[H+xcWYz"ϝ9vT@hKΥ?g}tU7߰ $Qp<}yH?PӪF5FΡF^ n~mv/ !ڽ{%:;]yhw:'7^<\ w_oӌKM>=h|A 7@Oěl׾tmğak]v`|f.,.+~'j3t&lوY[)f`= @8KP>+|J(jf8 4Ue?nN鱭c=GfDŲIJhp޼? 8"{_A'%<(%<`~bwH_6D\kЦ3ginkN X.x>!>h@\ EӋK!TgT1k G^L2 XӻHmd)J'/p-WqmzǗN{ui,N>K J@]}ꅋ M^N,n=)}RN`+ǼI*ϝ= >-<7LH˜If`azLi %V·}\rozפwk|88 GUnv]k['jGp0V۳Ü< -֛c#:ܫ1ڎ>OGoơ6Jm}ԩS+l]o8ßms|\\D(&_j6.f}c&?W 6FL00wC$p^kR#d@2Y(!h 'q)=r5lPXVWX6  26[?ZLh0d`0hݲy/n}yu`f$Kp\>rD)Voƍ48qA?y?{R$nQ~G%rQ1G~s#gPw9p{9.v;'vUGoơͳ)S! 2ݐ6 ,^Hk5DX㒠^n3ldO!K# 9̆iDŲ:Mo8iHe~Bg9a0H[y/n 'Q}Q͘If`&HۀG( ɊIIse1}?>O _6Mq:́Cx:rć8b_޵M'vUGoơvۈ)nHqk@oB0FĊL#4kTw Lb9ÆF%+ޜ?79/`uUk !s3`pYOV -Mu^X\ENsq$ ZS0ª ޸F0/|xӟ=ָ|2MPNj́:ga4^LW 6FL00,C$p^kzN9`u:SŚj4 m@3i*"7(We0eup46Ӑ2fc^2 Cm}qi/8J1HoE6o[b1uSYz7Q&Lz^mJτ? dQck}7x|(_?Iz^9.ok{`kjþ8 xs1dơ)Sz> 26  }=ʍtH⭫Af8 8"Rx`˱|q SDŲ:Mo8w&.X_;H$XUTYZ,Ԓ0 JMv?Y'\\A4\ovETʎU[p Y)U5 cOǾ΅ooئ믿m[_w.q6rb7'h>ڣ|x=<]omk7vŀ[R;cTjBO9=FLiNdx8rF2N+ә" VюJNMU4z-zXP#8eue)CoYx'@wwMzMXap'Ý=Ϳ, XA-Red끼Jy ޸F =>aq_/m-p{=n NPx`e\OR(G6~!wP;ʇ> ˫l<ksxo(OLavEr5A2)NgT,Us6 E6I\*㝦wBOXj٠p#Q2l8"!e"T:‹VvM=ܪЍ &;VΫA*CyDp+iS٭\:r6x6- (|,Ȱ]B8 }eo2\? |>@]Xl~Zo;L~:;s'\Sf嶟RgƧPWCrK8wq3Pnt4YړOp3,V\/NMU4z-zXP#8eue)Y8s#M¶Vm=ˌ+$ 9B-8JmhƉ3g̓.;7"D-8Xv)oIdYR*+NʎˉˑlJLdy%ђP A8:^Nwxvs}ܷGr6*[7/pZ_BJzg[nQ{Q8c J^>gÀz3n}+[ZY࿫v[}\;sٹ+i8z( SPsVsB;.&SjeKogl>Woh1E]#*#ρ[϶* ";|z u$7 ,.>z?@2,F$y{x\>x31 _瞥+^5`IBu|J!^Aj@5"3P_X}\ٽ+asNq3PͰj*C`h7=. 9N(A%E4^!slѨZ^fvJ5Jrw,'^,(Б :Ӡ]O>ކГL?q7 1>x ndj$xΪ^FiRK Z ~\=+~6 X/w@;<9“r[}\bcٹqsP͵\Ҙ 9:u-ދ*U($h7AFP~_9r1BMHX כFN67PzQW* _vvoZ{*-W2ti.B?!sCtT&z r DGJR1YMh<[cNKHe`J_e9'J{nRpE_YJW{-(7o?v\f{+tRG=lWwo/Ot]p:viҠ];r<|'pۇWɳL1pm݊1 fM.䜤t' oA\hgpW'^OCBj"R,U@IDAT$֨Z^f{M,ݱt$ Rt_\=n88O9zE?`C^t(;q:sgfZ(В}Ϊ+^ FiR@ Ukm W{'3^ [pgM/}Nh T3L];%9\HKzh(Y.-A83YkRM}< y7XlF.6[)mx*v=7>p LIOm#If;rw+ ^<|, ]۷frl? 4!ڸKɎ;o} Ū!qlr*En䏑&(u p*_;8Vxvcڏ॑bo-}vFK=kv5H+I1P ^;%9\Hl'&jL jY)MF P4$D1cD+5בW;kr K>A_)Jw>CsBogax |ۀ9[9]H+R9!9s>ڳ˹3@F}NZ:=;61ЃC1PCA\M_s^] ~,0.Z{=tUG8c{K f)"p AJ)u\RQ꫅4 "HB}"i^r\jy]ʀLMi kVs.:wwo+'Ǘ_/WvIWgxB_r=pG DB츐ˀ%pzܽ 2ؾDcp/wj<($+8\Jr7I@4]\Kwu%y_+z嗩p"/ㅍf'>W/"{:G5)f 6x푗p!uB@Mp%ږXOQ#}ИH%O@YI1xW ݹs'sgO" T8Wh)E?rwg}{ tpqF2d\RBCzx{v9$$x=1Br"O$Q*plʎV8e1P9;X7"n_mOV%k?g ^k?ū= > }IM\<8ߚ",#&i׎rIRG-[DipP "hSdA^(9vڊ>Q9$,F/J[5?. ;4SS$U .TMcx ߚ)옢9\Zu*9lXaIT*끡 8 &ȡ*רZ^fKw6 bf ;g>OFA{(p̠SSmᵇŦGIgB;GSr@ #fxu$Vޤ|࢏/ M7=:^ ۽`N|Oz+5:+gL fM.Z:CJ`<L $M8o*BרZ^f{-fhyXeK]2Ha !2TqO2p;HIcK.ݺ< @{gGwvvGyd{:.xoE{9 vruk:JϘ$c:Mu=h('ĞtZJu5)İ "HBd\\pGua&zbDž\DZ<= Ǩ8VJvK5\"^I^g9 /NGn\?\1vhv-?` /7n'xK~{:=43& 0[SzI bTF9M01TP$!2bV0*bDLjeE|p+}m?NC8%{rJr;.2Ę2h>7wLe!c 0J6^[ p\BKW! SO=<裴- ~'\n8V^.x,|;uP}>?o8gȻIJcx~a=[q!uԲ52 U5lX,qDZr8s$F2- / UYm׺sZ_ތc8~lcx著:uJyi!+yW3E{W#Y8|+geSX-5? :5x '<wMԘHy @NIc=rXz}"|b{71GƽD1MD'`6^zJܺ츐ZrI^ CClM! U@ H+?|C??,h8-kە₌lşc@{×m\D_GZa yXQv Ec\W4tk & AOXy'PG-1]$$0*'q#$D1⇖PEjMnn^k1ҠRL]:xrI0E CClM! U ܓ%[^O?c̖|PM/UMo9r<ߵv=ctcO>ǷE{9@ pO ׽R-.Ӡ^S7 b|s8:jqD4;}eeN!ohd1- u̿Iħ ,Q g"IY4⚕ .|f.c]gSa6(٣zO[rM|(PJh|cNxM$ +_52Ҡ?'@r'KVkUNqd呰!"jY MͬNq!#FlwJØ'ӐdR-K̜ .䜤iQtR& I'd8Q_<=Ll]r\ xp )͚lC˿ߺ?t||e]C >S?q~9)"j~7_EnP/OsgK{1>:T'_e8~]ɛ߿gL' XT HFw!42| 1BS& 3$5Ճ#ĕ=lQk&\j&в^7qd@$C!,R<`=b<|f c ]5vPxM`t)L-Yn=q}#'u|{O;F'||x@z>HmNY̓ ѥOۋ[*SAC cL@ޅ?~{S=z*x> Z9$l465F(pĉa]oVD- Ťy2)c FqgU[,w~p-{9t*xviC5Vl)<5~-/zfyk'rI8^'NpFR38’!%|óv;o?4G%nwawOngҪ5D̆cͶ!m :ݏ箃g!_Wsc|Jm\qǓ>=}!! xiP9iA#%H.<&AGGWN0 \0]74 iOli)ئ9ⳬp0-{Z>c ~ ~SZMdXHUq,5b]Ϛ_ym^\~׾{5mcJ%vc"(]ضޅq<ԉF&AiU3kkz{Ȉwa̞je߀ e?Ec߻yB޿-;gX?~z9ϸL~q %ICJ^Kؾ㡛Sҳ]x?܆ƞ\<,NE BB=uqf"u)| {6ǂ'AAS%.xsV@\9>ۓ 0o%Ҡ6at,8b=>Cmp$"KX MF|p-3$hKMD5~;3<W7Bw%.o]~1de?8r4$(g ?{m27/"? &0^&#/|EMj^lνq /h!\̢OP{=P+}k:}lgrhzRj"Zq" ;Ik8ljfP2D/Ӊw?>_|!Wpp ݆Pw߶'v˩3~7 ~13(m<"FH,a14gej|jǡdzp?Օ3[·~?{}sl!L=BXmHIYxOۗ@󃿽f_>.l<@bSrpqoq,||oo6~9l9Ӈqjܘ?Z)X.J@8*bCsQ(Sg?<l({ù9ſno~38d+qR+@_x" >G=.sJ@uUtccE}R\qx"ǞzcZu2G;{"^q d"hw@Dܮ9ޛHF"ppYM.)(cD,?Ybo"r?'X<':jҐ|]w~7~~,;N] ;r Vi'E"6vpkMo2hgOX?>53P !М`iT-c!2yŧ<?|"ܡ?=Wg!%I޻7EmT;%,.}ѷ/=wޱc7 Or^|T;8GLX9\f [zL!)FH*a1zF%S ?Bov[n>~C~mBsE~7doǿh1WQ&*N3afXI29.Me\~|{A.~M/Z,~6x]wvg'OYWǪ1cAs|"Ͳ,'}vg C ǂeP, BVؐ[z^=I:o0mc?}!(F(p4YohmfUm[[>8R[=̯}XC6 \o3ljgyysp,[a+'qN61}pzAni;^T `V9I]kR39&D1Q0,J /}$cf@-p X+|*!{@7;+r JUކ3bvv/'GN ~80^$˹y!1o5.bq^=A,:kM`)={B_!aQ3>,J-P[c|{l ߗڰ|ſv3pؿpTT@.(?O5Q~tVzx1B $,u\Aw=?H3Oiºja D(DW [d;g` ڿ~by1~?>Wpg8y14.CYQoH4\K!`$p5tTp_k˯fWN>(Kx`'_~?K ǂ03b BI7o5.R4ck-.vGYr0) 1RרZ^G^K4p:ԶWc^SϜٺ}3Ϟ {½o>)SJspgog`wwg<|w][|5gw2&:S`2""cqz/?dڤ$s'$ߠ G ⏭cw_y5zº*`kw'>yr9 ?? Ni@_~vry9`N[k;fg?.gs9GSaI+vW8z՜9 nLt\*-أ9p-KDרZ^f{5l,hUpqIg>__1ݳXeNvgǖCIQGJOX޷Wqv?\ ;WW8rC^=lD雎$^pHg=G{}ŵ1&MFEP yҨZ^f{leg~r_1m>+O>k{ @GgܙS'\^}k<W9E ?g^^gv8{^_o+k+H'Q-:DQFEuuM/8$z?ג#i2*bCҢQZXrnۚ8xe^>^xw>'+Ϝz~9疓'l2x5_t`[c;g ,_yۿJhG Ǭ<"vu&&jLp8j?y!3@b,OX$Mƶb~ f)f\K? W_?<tKzė/}J zNk_[^}j ~qܣX \3/_We86A±7$ tAs˜Ȏ-ZN Aciqual ץ~)g/rj?'=xd {kj˙׏dkW瞢ݶ~mU|"k7?kW8sO2 >'h +[ߤ]?ÕzL37k/O_{|pM$e˘1-cHHۥ?GʤVF:π^ˋ?B_/zYa)L4g?>A5o}n}b+ ^}|9we_"Ǘ:};`=ǖ \co.|!5+٧Z 5edkۥ\?'In%,x>Dm^O=F,O.kddNh,'_2"}8z >xN66ɴlg`;Wj!X?sO7p_.4p~r"LԘ~""8v7[Q?\CT;Gl&_L ըZDJo]/yv|xS ɓg?_rpdsx_X9 8x=K;c6 exK//Wz*ϣ9pӲsX2$s1'FEV>Rƙ!/ɐX5X@ja&aE7>=+Emb7 _g?_߹ OtѠ~>{g|3>Ӎmw~ē\~\7: u}lx^&_b`桔oL%A_5YWUVj$xՋ?y/q2rT Q MF"ZK`ro?/,ek?[G"p9L<}?=Zw\pLx ']OX[;o? lg.DO,}j/-c~4y $F1OB(uAː4h;Lsr =d-Y,GrN˂M- evŸ>}Anw;zlwy/idK ?wwo{?O?y%"< ƒ/#';xY毜o|NJvGy9|^ ^}|^'ms; q`g}g~yr4^W(H ' !;/nƔfeذ\ĔJ.Xp_nĐňVjQL)97?,|@ ;;`aWƃ^rWn{g,xl'Stbt<uj|bX}+j}|޶|N[o=@o <#VjMzAJz6/y GT Usd `#'!G֨#9GkT-Wq#.}>s^"۷]ǖ]n=^NhI8ß}S pǿr|W~ǿA'/xZ}`}"L`;OQAQ, |X ޻/zp9ztrylpOJNZB9{,x/p$?2$504a4%cIBrGKsHgj90 #. L?ZgBMl'qN]Qp s3R:H8ل&u2w΃Y9 k5)"K+a;`JEd;a4g9 Y ??PQWq-c=F \ BkG&:w%u!$ 0`t8 bf% 9 O#M4QsJDY85p[rWc- {G:hҠS@' Au>WfTk,C֫IeLͣp$M4h RiEl'qavs[MuSpPֻ,Sk8i5@Sny"k#'a4^{i"lp5L齈l',&,G!,V4]#x5 cAD|{-R\j T@lau@~Mr5&{@ʋ?6sr#ihIbRw)vCl'qN!NLMtkIqrWvtHn8iCk >z- ݴ EFNZi  ENk{NXMYB1o?#k[:R0<Mtq5qt>\c󔄉әjP Pj"pI%MX<=>04ZS9hWs&J#\9/="5zD]ߟRFANJNѐ4^­Bk >z5 ݴ EFNZi  ENk{NXMYB{.{'d_$EWq H 3ChA׋(O4LtV@ N@#Y BΡH :T 90Zco1BFBGQ1E=~2`a@E}#8'ܘ qD+$D$ L%*Wc@M;^dm$՜p/Z )E(dyGpCdhzL|VŒ0<Mth !윮4J3p; bf% 9 O#,{T*XP g6H xZM*iK%CKXKeqo1G]X.i&hma.9p7͝v$"3!NB h4I28+Q;ICx &՘2w΃Y9 k5)"K+a;`JEd;a4g9 YQa{ }q)H[Afڣ Q[|н84w# /  Ѧ8R {cq"A\9w hҠSڨ&`dvp՟ k,CmƫͥQK2c,e铽n2a' IrΩ_&jNT)NJNŐ4^­Bk >z5 ݴ EFNZi  ENk{NXMYBM݃w/}+pcck"3RLZ{6A" X;]iG'$`0D~HH쑎uq!P5R&bLm) 9 Ң]'9Ѥf.I"sbJn8LiO&* C9+%P.9kD-‰6ߟd ])IvZSM k1e Y/6rjNSEVX-wZދv"hrpV6r ܘ qD;ݟ&YDL,CxX7T52wDr!m_TY)\n( t? j=C\r%$0 טK-s ݡWE|Zj8Axewv|k p2(Q,S N}yI$A&XNvGI RҚ`3kĐL5%l%$!̙mXaq7NmAiKw!v\HhhIR@9E])9id6ۤcq"A\9E51vM.sJK1E:dO'ײdITZ-FYF !7UOÇ"˰]@IDATK}ˡ޶k sQapc*‰V{J9ܕ$!i[T^*LȃiA&j(p[@r5f ׈!j6J6%X˂IB=la:v:qlUs+H$ B: -I e@jtpfJ@Ag&)cq"A\9E51vM.sJK1E:P,oz&L|Rzv8h.֘% >waj_2P%Ҫ5I׃g^dΑ.01D+MRNJN4^­Bk/c ݴ b5BW8I RҚ`3kĐL5%eI$s琍k: |nI{&<("t-A;.dВZ&IrEc6k䵦γlұN .3cj/i<O8e_>Mj'|6ɟShcQZ9{7`?sg.S8:pv8xq7) Baym)prWv nZSx0m foMɕ֔sF TQ)ZOB=w(oyNJ⏭u|ZMtZw*ï<*K@ & A׬q!iq8j9Q1nN@Nb,$TS'-Ƈ-A!ba>Ő0}E襖.S^98߻"ԉ'~z9~̡ä K-5\"r:(o?'νmljä<%Ndt)d28KUhMUEx´"x7剫8ezߘsN9(|"%GP:BREuJ =l'i#4Dwv40&AR%XŷU|j~ uX09s"5ϲUyrv'!gYt& A׬qa$!SMsM~N ;.dNj;~B/p4<;g`A:O4 VƪAE2BcܘL?b>&7'M5Ca/&!J*=Wjy |[Cxs!WU2 PP̱jgPu `.J׊W~҅t%\ pb7q>AH8oS'E1>DOx8ZN~,uza;pB.CCKjcD*@)L6iS$U .3ޮQ;Qm]vw\Mv\B) mx9Ϋ}՝&MLil9G ܘ/G{1,€Q͡g(xw}o\y9ϵM-`3+ L ،UoT˨YJiP2bE[ѣ)1B%<+Jܭ?":! -9}u׷v6.Rߑk!ej$xTg&Q@vu(o4 gL@YΉðOmA<^.%;.d)pr#; VvRvP P-ǣ<9c}|BI7h$769;JHd`\[-$@t=Da3󨐻ޖ۫gL%٠O) 0 z'Cy&7+3s9x"c%GI :C:1"7*- 6hS$U .dX7# <MQnN 츐Φ r4AAT㾙!510KU8y.СؑF*unP&b)2dqG%8BCUj9#F|pmP-KY$Hq6 Y +2a1 ߚkRH;O6+)Be1}n-,kLA<]ZFM 3# 44)mx*vaSǒMsMvw\Mv\TgS PYȘ1p;B4p3Cl'qN0ܗUދIP&b)6dqG%8BCUj9#F|pmP-KY$Hq6 Y +2a1 ߚkRD7O#/+ Z$MbTF4YDYˁ8K )z 1i23*) @$ihS$U .gS7% <MQnN 츐%ރd{|`,dt3p;B4pj'υ:tUދIP&b)6dqG%8BCUj9#F|pmP-KY$Hq6 Y +2a1 ߚkRH'YFIRG-# 4hU0ƜUj59<ǝoV]'*V`:FF%EZ/hn2& cJ1^ ątmpcҠxNɎ N_<=Huwm4H6A5%x<rkO(y"Dlj8s4164JBq$rG9 jZ HjmVd ÆcLA5פlN.ZFnL iЪ`9G8Kks$xM wZCy54#"  a`ɝZ*vN5,nS[iL'MRd$! J6G%IJ-tC 1:"[F|p%IYj\qR d1. Aߚ HV Q&jLeA{5=Sq :Q:1"7*)"a6gLf0D񬈰L:Ex1kDA:y6hN P/O[QAN\Yb8NNKsD[5B Jit^ e?n""C Qp<*IMUj22[u.IȊV-$䊫!aEf|pY bM@NZ=5Qc*# wd#Ԝ=1!HǝA׉ 1خQI ?` 4HHTS$ 9RO#M4QsJlݝpiq!H4 bLG`N'VYzZw-GKsD['υGހ7G>LtI&gQI;PZH5\[-Ԡ@t]L X9ju=ns\jQJi)ut, 8:jQ평SiW-5 w*nV]'*V`:FFy<}yH:Es(4AФApT$NjDlWLjܨ'mg;1`uq!Pi$I& xNi ZC!,Sjc L{ib'C(lxIc`hq\9*$O(tI&gQI;PZ .yjXP*UrmZVGu"r\jQJi)1>a9@{kTFwdK$4!4s6Dk{%8y.޺xs}{F=L(y"DLw, )Zoh93Zw *AH\ B hUkQ"DFբ˕Ҡ^S7 b|s8:jqD4;Ȗ&ρI)pi"ChLMtju iЌ/O ʔ7f% 9F"N&R*XP g6hBJ4bc L{ibcNx-``hk乐szfkRM's6Dk{%8y.޺<5MШ m:O$o@˙yT#!4T#iK3ZAJ\a-UQרZupR4tk & AOXy'PG-1}90 #.M$xM Q@mx1M.sJ̯Ij;8R  xD * ;l ǘ<%Z@ HoC 9i4. [Lhy"DLw, ).^JJPMX BB*QqEY+ACopuSiw^ߑ-M0RD&Y e=̶` kVs̴ ǘH&9"p(ˀno e1I@&NPY)fk8F&J.l=<rui]ЦDj1XAR ]6o gE;ت^b8}w8ku߶,AY%V6j85e\ RHi1}b[ˁI8{^cwM4i FReifĀ7w8HFΝ3^ąOzz& 9e'P@x:kJ^= {) ,5р.Wt5\",5ZNS$l3Mt1bܱlΊ^wU p0:pV:mYJ +Gmu!Pj8,HxM$ +_52Ҡ)*H$ BgIR@-x\)#U S0tU%[1^ąSucLm' \甖bt¡,>AĘ'5 4!/?NbB.jv 1P%bz\9FM vIgB&bŸc9I!0tp1}J-ԴU\̕Pe (&Iԅ#BE :5x '<wMԘHss"]K.Ď n%ILqTu^ Q#1^ąSucR]'L@N8:=aRuSZw4-`l׎ħZ5\"҃' ɜct4xj`-p&r#,`b=B)[1fOWJK5E:P@g=$^Kw4t r7^V乐szcfk4A&p&⏳5 Axۄn9ꎓ.ɯQ\ACoxJҀZ}CJHss)RDZ!]m7ZPka[﬍Y*n\65>R5=[DrNq1%ߪ Dg$=X H8™>%Z7ӤY0!hl6dY#VbqikȩiT|4 T#$! ݱp1}q_J-K72gpM2WB%:@rz&Qr5,Q $ BP!%A99)"-pɐ.Ď  -I յ-WHU,9 M|9ExF,rNq1J4)-$&B8ӧ$^F  StvX%m%62k@%2 &(NApb&8JI M6o gE_R$5R\̕Pe (&Iԅ#BE%KGAt4I:E*zH itxN1E H \2 8.Ď  -I յX/Zal,gßSRp&_A .?ϔ1)vL*=}VCcL|{-%k9^:CPjxI[ɲ:F,㭩acȩiR]4 & hS$Kgr>#;F\@x8k~۲]ghQqD]9"ԨZn_tDAgH3t,S$Hi@MS %Àno EFCKjʠq4Q q4ڷ,'[_0 NL0gmh,u H .B8ӧ$^Kw4t r7^Vhrxo 95M  Mpd 0I!Ȏ /{Yс3Y6GǓV-$^&QrNc`0%N@Y BP!%A3,s)RDZa7¢G%ILzgmTәli 'ʨIr412r@`} CKe@ Tw@sOM쒰NApb&8JI ]6693J S] 8[u+ Um&i`FRKɛ$m no G}IZ7ӤY> tv𒶒euXG%{W~w-fU[ţU11>#0i"1IL0с@L|1Q#v F0t4 tWskZ[kWnpRm[ksu˙KH <['"7e%E zf`')@%n:'gi["5}*DahCKCI"VN 0DkAW& qeOwIK_Q`*3c ـQDOS 8gДqJ{Hgք,m QZJ# c4G&1 41htkG86h3d!W9LÞ~=9]} W7?cPl(Ʊ0Vf'd %GE\ E蘟P<㇦lC 6ҥ$}ihټG^P3Б13y&FOD΋e"0t]jwZGb')@%ʷI [zV (tLgi["V*2, D&$|yF'6=c=A.-+^.feI^iVPv"p oYHi,2OYi$24!;4:ѵ)aS5ϠS \2Y=d`G]WۉYl_~H[aSb%dBBז @c~j@^<4U1S=V=!, H_XeXn.uk:h)CaPH"F_oXHi,2=C>\&Ԡѭ7(]dM04tSX,뮽Cq62t_{KUOӿ CqNwR 2-D.tm 0 $:p%k+2uMȗc q44qj{"!AwVp_XeXhms kY-2J+y IĈ7vsQFr»g`w{hB‡˄4U00`;F Ǖ9rJX$!qmm ,6^ǿշ>v7W0c[:Z0 i!Jtk[ql 1?5S/CIqJ{H_Z6Zl0\\'l! ֿ4i˰eѢ"0@]Z6y-,eH3 VҧYH"F_oXH9bZԫ9A$24!aQEF',7qN);1:2GN $<t:ҟ>8'?y:~mh BYZ0 c)w]],H_Z8fGabR'l! ֿ4i˰eѢ"0@]Z6y-,eH3 Vҧ^B6ZG]@ d3n#צүKB)v C>X͑IdhB¢X4:ѵ)aDiSa6E298h]6>9yӓ/ڿ `K c"1ւlL Q ]ۂ'czNZl{P"ط>,ZB!}8f1&uRF4i%t"}aiҖaJE D`p{{RkV& i @CzJ{I YLSThkw"&nhɯN,Di0"(# :hL"C5T~甲c&]3sJ2 m ziwpLo?p{]qpk-Kʴ%е-x86蘟PKC#I-SB4}"ĩ&Q{6`(v:HעlEH }&ղka)Caǐ>͖8:= EMMM_(R:moŃ|c4G& Z]xdsJٱ] sJ9% rTq6Rst{x+߽vpA4/w9A_:g)8rj!JtƱFc~j@~j=kn%AOc ,y&N-Y!&uR^|cБEZ&mY@rzd5eR4@i !}-%9*d1qLmSu{  ҋ(IJ.m Qt@ V/hkephtY77qN)6ai H5QZEr@ w?]Q}8ٕu±]}k>J_٥7\D)#"(х. F$Zo+S 8q 1j=kn%AOc ,y&N-Y!&uR^|cБEzhEH ],Z CK=ղka)C@NiXZ:=f73E*ȸATYii["i"(@E݃VUsD+MH,ÅхGڂF^#Qej4+ ϜS:)aV8,1S g^HkNOpg_.T1 ֧h!Jt敭cԕxUb2i6سVV4‚gikbubR';vz Y^^te͢"0s kY-2Y)W6z=8%3,l!Mzӭ4[Y|Sk*aw݃֬O NZ]xdݠѝۥ=r6FiIyd[9Om$/7]qwgwbn_/wo>ΟP.TR 6D:d)҈Dk+S 8q 1j=kn%AOc ,y&N-Y!&uR^|cБEzhEH ],Z CK=ղka)C@%Ckz;*%|gڦBݙ 0dٟ~$DҶEJ y`{24! Fi zy +)g9l8QZA*<@jn2@?tx~{odN`}ͧ :]u RևEyi[H_ԀN#&ZAmuk zaAȳ5qjͺN1;,ҿ4i˰e͢"0s kY-2TR0de"o֛8p066]Pq@0yUn, C.p!jtᑶH좫K\9% ,8ȶ6/-,r;?*՝}d&?Crũdǥ`!ԀN^b@hO}ND7c&\\'l! ֿ4i˰eѢ"0@]Z6y-,eH3Ŀ=yJDvw-STh;3,3Vd۟]_H鴵" wZ:Y@&$\B#mA#(EW& 9ÜFi98rH_ZOƿ;7|Grv_7u 6=j =QHuej@'*ټG^(duֺ `ha \UZZbL'L꤀0hҀKH 9DҤ-B6 C]aZXʐfiٮڟʔĖHh;u4zv_%-^q( j݃h e5H[N;=HDCNqڨe \iv=d)r~/}qw?{'~>˿RDv =j =QHuej@'*ټG^(duֺ ` Y*u%tI[.k-vt֬Q AM ]zFRW?); S=fwf$4 Yv'}]^*KN[\â(&uZ"Y@&$\B#mA#(EW& 9ÜFi98rH_Z͟^{owC_p}x%oz*Z~ l[{\%Y =QH_ԀNK6=i>ط>,ZB5Y*u.3Б!F9iNAX\pfnrޚU=8J1!0iKOWJJNa 13#AaȲ;1SUME9P0LVE MH,ÅхGڂF^#Q.M&r%sJ9% rTc SK OߣGߧw_{ nWo3"G+=.F$/xj@'%F 4[B!gXZ,WN l  ЙKȐl# 4\a,Pt83A79oͪXԴХ'a+~%i0Lfݙ 0dٝc[S^*KN[\â(&uZ"Y@&$\B#mA#(EW& 9ÜFi9*L1D~w<7?.q/_2=KW^|<lFir $y }ò7~N~[_O ny<ᅬo\܅B =QHn+S 8Q nZSsd[-LJK*P)PͰWc dI_X9}p͇Ù ykV (R=`KDZw#u*%Plj- 0dwd| i;nb'-zP KtZ>e(8ͺ~WeكF0ԄNhqe0J+c ! \t}ӓw\͟Wpx〷7_B]Z܅BBOT#=67Nrj@'*|`x-ZbjN}â)rUHIDATiiT\:e2jl3 4^/Pt83A79oͪXPmTqY ֩ĖHh;u4zv_%-^q( j݃h e5H[N;=HM>FCuңe ,(H@"o8Gz?M/C?/m[_EfBǂzn+B(D{R1J+k&!KŭNLj-XC1f哠9}p͇Ù ykV gR4@i `$bDݕN{i[Wqe2# C}Gא&An1+@0Y{к\4! D6OikApSgn$S("^<O?tާO~M c헾_~7h􍂻oRcq``!En+F1 8z~uaURc z1bz"d ՝Df3PLe$hNAD_\pfnrޚU䙳!0PZchjÈ+ҶiKeF64 Y:_Cx=Tfix诒8\Ct 24!(ewOi)zƆgn$˅QZE yHw+}C>D?w:j}Ko}ijK_~9:vEotC/ x+LWʍEPZV%!;Ơ1#׮/mM,-B_ug25m&}aY> SP/|(:f0y,eH3 V<00bJ-E+8R C}Gא&\7+zeO%ًp;A:0rB$i^BG[z Z{ci H5QZE yX?ܾ%z=u??Ş+_?y8=%K;^Iڋ@B$/D80(P&pi]F@^-Ir0N2P3k(g!}aYܡ- dѦk>tfnrޚU䙳!0PZchjÈ+ҶiKeF64 Y:_Cx*sP蕖]>d/ taT4M ;l{ an達kq-?>vw=q/ޟ?i2_?G$~N_ h;8թ  -=Z(i dyҶ2lk՝AwT5O*,f5]:3A79oͪaYʐf(145x`a•N{i[Wq2#,l!M nX=|*=0+:v0,OF;lHp ܧhhyw:o/gΏՇz8,Gt(IENDB`il32 NQQNMMLKJIHGFEEDCBA@?>=<<;::84OQRNMMLKJIHGFEEDCBA@?>=<<;:98974ORNMMLKJIHGFEDDCBA@?>=<<;:987674QNMLLKJIHGFEDDCBA@?>=<<;:9876545NMLLKJIHGB?>=<<;:98<=<;:98765433MLLKJIHGDh}|zyxvtH9;:987654332LLKJIHGDWB79876543321LKJIHGFDMܧA5765433210eKJIHGFEDBDB98772mܣ@45433210/JIHGFC?@?>87412-\ݣ>243210/.IHGFCVƮIaܣ<0210/.-HGFE>^ަ:.0/.-,GFEDBK-Fʤ<..-,+FEDCCA9,%""(//!Fa&-,+*EDCCBA@>?[]\[ZXT^-+**DCCBA@?8_⺠^,**)CCBA@?>7⹭Ÿ]+*)(BBA@?>=<>VMLKJIDڿ˘\*)('BA@?>=<446)"!H̸\)('&A@?>==<7cCż['&%$?>=<;9C$oͻ[&%$#>=<;::61%%,- WOȾZ%$#"=<;::6=CB431;:95nZ$#"!<;:95Y6ȺX#"!!;:992p6ٶ,"!! ::9868jqoJa`_^]YUVUSMIFDD?iݣNCDCBA?>=`_]\Yj̶\qܣKABA?>=<_]\[TmަH>@>=<;]\[ZX`:SʤJ==<;:\[ZYXVO=4116@B0(Rh5<;:8[ZYXVUTSSklkkigbd*;:87ZYXVUTSL⺠d':876YXVUTSRK⹭Ÿc'9765XVUTSRQOQeXVUTSNڿ˘c&7654VUTSRQOHGH:2210+S̸b%6543UTSRQORGnb$5431TSRQOJrSża#4310SRQONLU1yºͻa"310/RQONMLIC41<=,**%WȾ`!20/.QONMLHOTRED:BIHGBw` 0/.-ONMLGhFȺ]/.-+NMLKD|Cٷ4".-+*MLKJHIvxvS(Iwmljihfecba`\4&-+*)LKJHGF@4,/4;6*('&%%$#"! "&,,*)(LIHGFEDCBA@>=<;:97654320/.-,*)((IKGFEDCA@V?>=<:987654210/.-+*)(('FIHEDCA@?>=<:987654210/.-+*)((&#DFECA@?>=<:987654210/.-+*)((&$Ļܭܡ¿۴ܣμܣܣަɻؾʤԩ•̠Ҕ⺠є⹭Ÿѓ~Ûڿ˘Ӓ|ç̸ґzѐyżяwÅͻӏu}|xȾԎtþӍrȺҋpÿrqʯvrbw}|zywvtsqqxļis32QQPLJIGECA?><;96OQLKHECB@?><:884PLJHJTRPND::8644LJIA}اE56421JIFBJUNJŇC131/IFL֎۾>//-GDFwvMRjڹ4,+ECA6v4*)CA?7y¶3('A?;WYWph̽1&%?=6vĺ׵0$$=;:PJ7HC۰0"";9Ak"! ;86?8,:6320/" 7841//+)'&$"! 6540/-+)'%#" kiidb_][XVTQONJQijdb_\YWUSQOLJJDidb_`hfc`WMLJHEEdb_XګUHHEC@b`]X^c[WĊRCC@>_\aۙܽL?><]Z[Y`sڹB;9ZXVK?87XVTL¶=54VSOghdzs̾;32SQJƽ׵910QNM`XFTNڰ9.-NKRt+,+NJGND:G@><:8+(+)JJEA?>:8530.-+)&CED@>;9742/-+)'(¼Ĺýýѽ佒׻غȏȾ¶ϾÈֵɴװűxyøit32OPONMLKJIHGFEDCBA@?>=<;:987885PRQSRPONMLKJIHGFEDCBA@?>=<;:989:889OSTQPPONMLKJIHGFEDCBA@?>=<;:9878986PRRPONMLKJIHGFEDCBA@?>=<;:98786SRRPONMLKJIHGFEDCBA@?>=<;:98766765QTPONMLKJIHGFEDCBA@?>=<;:987686RPQPPONMLKJIHGFEDCBA@?>=<;:987657QSPPONMLKJIHGFEDCBA@?>=<;:98765573ORPONMLKJIHGFEDCBA@?>=<;:9876564PPONMLKJIHGFEDCBA@?>=<;:9876543PONMLKJIHGFEDCBA@?>=<;:987654ONMLKJIHGFEDCBA@?>=<;:987654ONMLKJIHGFEDCBA@?>=<;:987654ONMLKJIHGFEDCBA@?>=<;:9876543OONMLKJIHGFEDCBA@?>=<;:98765433ONMLKJIHGFEDCBA@?>=<;:9876543NMLKJIHGFEDCBA@?>=<;:9876543NMLKJIHGFEDCBA@?>=<;:98765432NNMLKJIHGFEDCBA@?>=<;:987654322NMLKJIHGFEDCBA@?>=<;:98765432MLKJIHGFD@>=<<=<;:98779=<;:98765432MLKJIHGF@Im~~}|{{zyyxwvvuuwb9<;:98765432MLKJIHGF-?oa8<<;:987654321MMLKJIHGF-@jšd8;:9876543211MLKJIHGF/ECc7;:987654321LKJIHGF1EBYc7:987654321LKJIHGF.EEAcc6:987654321LKJIHGFE3CSb6:9876543210LKJIHGFE2?b69876543210KJIHGFE.DDASa599876543210KJIHGFED+@Pa5876543210KJIHGFEDCA=LYRFEDCFea48876543210/KKJIHGFEDCA@7*+*)(($*a476543210//KJIHGFEDCBC>.-,+ (-`476543210/JIHGFEDCB4-.-,+**!z`476543210/JIHGFEDCB?2-,,-,+,+*+*))#[`36543210/JIHGFEDCBA@93210110/. 'g`36543210/.JIHGFEDCBA@?>=< 3`2543210/..IHGFEDCBA@?>=< 3] _25543210/.IHGFEDD@<<;:9;?@@?>=96656548g_143210/.IHGFEB@hpA=@?>;>k^143210/.-IIHGFEDBGQ;?>;M^13210/.--IHGFE;DD?C>?>=@^03210/.-HGFE>8v^03210/.-HGFED:=7>>7Ù]/210/.-HGFED:<8>?7͚ ].2210/.-,HHGFED:>r(165ۤ].10/.-,,GFED;CBED(+*,MῘ\.110/.-,GFEDC?_6k%,++*+oܬ\-0/.-,GFEDC=R׀ЦP&,+*3Ýث\,0/.-,+GGFEDCB?;AG:32.%',+*)695=@1.-/G۽Y-0/.-,++GFEDCBA@A3,+,+**)6<;::+(' #}ހȾY*/.-,+FEDCBAB8,-,+*+9;<0()('&$݁j!0/.-,+FEDCBA?0,-,+*)1<;:7*))('+ph)/.-,+FEDCBA@@=0+*)*0;::;5*'&. Xh (/..-,+*FFEDCBA@>84432137;:82//.-&lh (.-,+**FEDCBA@??@@?>=<;:99::9+871h '-,+*EDCBA@?>=<;:98+5-nh +-,+*EDCBA@?>=76876543+@h +-,+*)EEDCBA@?> ;?|ہh *,+*))EDCBA@?>);Iתh *,+*)DCBA@?>):֝ـh *,,+*)DCBA@?>*9_֟h *,+*)DCBA@?>+=5֟ĘҀh*+*)(DDCBA@?>,==6֟پh)+*)((DCBA@?>=*9_֟ٽҪg)++*)(CBA@?>=*<9$ןÙٽҬg)+*)(CBA@?>=*<:Gםؼϭg)+*)('CCBA@?>=<9={Ӧؼg(*)(''CBA@?>=<;:447*#$$#"! !/v׻ g(*)('BA@?>=<;0()('&%2#aհg(**)('BA@?>=<;:;4('&%ڀծg(*)('BA@?>=<;:.'('&%)fԭg()('&BBA@?>=<;:8.('&%$V؀ g')('&&BA@?>=<;:96210/.*-%rf')('&A@?>=<;:98765+430f'))('&A@?>=<;:9:9876543,2/+f')('&%AA@?>=<;65=;36:98419<;:;;:998-79Pf'('&%%A@?>=<;5jժV3982[Ҁ8f&('&%@?>=<; 5k398C2wf&(('&%@?>=<;X6gH6884Qf&('&%@?>=<;@41880ـf&('&%$@@?>=<;1::0881ϾԽϼf%'&%$$@?>=<;>:8,671ǟǡf%'&%$?>=<;[::2f (+,ԤȣҰf%''&%$?>=<;:=7K(&'&&9ۿȣf%'&%$#??>=<;:4\ր8#'.&%L֡ȣʕf%&%$##?>=<;:94@v^'$'&/8k0դȣe$&%$#>=<;:9710-"&'&15/-,,U޿ȣe$&&%$#>=<;:986('&154'%$#"Iݰ̦ȣe$&%$#>=<;:9877,&'&%(545,$%$#" ހդȣe$&%$#">>=<;:9875)%&%%16543(#$#")^úȣe#%$#"">=<;:9874*'&&(16542)%$#"!Uۧȣe#%$#"=<;:98764225431/.-,1+#zդȣe#%%$#"=<;:9876543210 /-2ĹѸȣ̖e#%$#"=<;:986432455430/.-,(-ޱ̫śȣe#$#"!=<;:9833EOONMLH6.43.0BIIHGF.EDHgդȤe"$#"!<;:982]z13443-aټջȣȤd"$$#"!<;:982i/43-s$ӡˠɩd"$#"!<;:98X6?c.330F["$#"! <<;:98<1q,33+~܀π>!#"! <;:98X7/032*ҫ!#"! ;:98X77/}'.1*Z!##"! ;:987V4Mq#$'V׊!#"! ;;:987 /D'"##"%ԉ!"! ;:9876531#"!05̧U!"! :98765.Fn}vrqjF #""!*+Djtlihhgfeeddcbba``_^^]\[[R8"! :9876553.-"#"!*1-*)""! :98765+$%%$#"+10& !! !"! ::98765441%$#"$/00//+!!  "! :9876545,#$#"!",00/'  "! 9876543.'%$#&-00/..)#! ""! 987654334310/0/../.,+*)('&%&%$#"!"! 9876543210/.-,+*)('&%$#"! 99876543210/.-,+*)('&%$#"! 9876543210/.-,+*)('&%$#"! 876543210/.-,+*)('&%$#"! 876543210/.-,+*)('&%$#"! 7876543210/.-,+*)('&%$#"! 8876543210/.-,+*)('&%$#"! 6976543210/.-,+*)('&%$#"! 77876543210/.-,+*)('&%$#"! 796543210/.-,+*)('&%$#"! 57766543210/.-,+*)('&%$#"! 7543210/.-,+*)('&%$#"! 66765543210/.-,+*)('&%$#"! 3667543210/.-,+*)('&%$#"! 564343210/.-,+*)('&%$#"! jkihgfedcba`_^]\[ZYXWVUTSRQPONMLKJFmlkmkihgfedcba`_^]\[ZYXWVUTSRQPONMLKJLLJJGhlnjiihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIIJLJHillihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHHJIFiklihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHIHDjnihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIH GGJFmijiihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFIimiihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFHEhkihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFFHEiihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEEihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEhgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDhgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDDhhgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDhgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCggfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCCgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBBffedcba`_^]\ZWUUTSRQPONMNMLKLPPONMLKJIHGFEDCBfedcba`_^]\W^~pMONMLKJIHGFEDCBedcba`_^]\-VlLOONMLKJIHGFEDCBAeedcba`_^]\-W{šoLNMLKJIHGFEDCBAAedcba`_^]\0[YnKNMLKJIHGFEDCBAdcba`_^]\1[YmnJMLKJIHGFEDCBAdcba`_^]\.[[XumJMLKJIHGFEDCBA@ddcba`_^]\[2YfmILKJIHGFEDCBA@@dcba`_^]\[4ZZUlIKJIHGFEDCBA@cba`_^]\[Z,XflHKKJIHGFEDCBA@?ccba`_^]\[Z,YUdkHJIHGFEDCBA@??cba`_^]\[ZYVS`lbSTSRQPONRokGJJIHGFEDCBA@?ba`_^]\[ZYXWVJ9:987617kGIHGFEDCBA@?>ba`_^]\[ZYXWXQ<;:9886:jFIHGFEDCBA@?>>bba`_^]\[ZYXWD;<;:98 7/jEHGFEDCBA@?>ba`_^]\[ZYXWVSA;:987 61ejEGFEDCBA@?>a`_^]\[ZYXWVTKDBA@?>= 7qiDGGFEDCBA@?>=aa`_^]\[ZYXWVUUVUTSRQPO GiDFEDCBA@?>==a`_^]\[ZYXWVUTSRQPONGl hCFFEDCBA@?>=`_^]\[ZVRQPONNPTSRQMJIHKuhCEDCBA@?>=`_^]\[ ZZYVy UQTSRORygBEDCBA@?>=<``_^]\[ZZY\ cPSRO_gADCBA@?>=<`_^]\[Z9UVRSRQTgACBA@?>=<_^]\[Z:VsMRRMg@CCBA@?>=<;__^]\[Z:RLRRKÙf@BA@?>=<;;_^]\[Z;YRLRRK͚ f?BBA@?>=<;^]\[Z=<;:^]\[ZY:XZP588<]Ῐe>A@?>=<;::^]\[ZYXXTq6t39987:}ܬe=@?>=<;:]\[ZYXSfـҬ[3998Fi΀ثd=?>=<;:]\[ZYXWWUPV\J@A@?<359986FMIOR?;:=<;:9]]\[ZYXWVC:;:98 76GPNMM94554 1-ހȾc;>=<;:99]\[ZYXWVJ;:98778KN?565 31݁q/?>>=<;:9\[ZYXWVUUT?:;;:98 776AONNMMI745-44,wo%6>=<;:98\\[ZYXWVUP@:987667@NMF743--ao%+5>=<;:988\[ZYXWVUTTUSJEDCBAACINMLMJB>=.<5uo%++5=<;:98[ZYXWVUTSTTSRQPOONNMLKLK1JJDn$++*4<<;:98[ZYXWVUTSRQPONMLKJ-IG@zn$+*9;:987[[ZYXWVUTSRPKKLKJJIJIHGF-EEQn$*8;;:9877[ZYXWVUTSR NSہn$*)8;:987ZYXWVUTSR)O\$תn#**))8;:9876ZZYXWVUTSR*QN֝ـn#*)7:98766ZYXWVUTSR+QMp֟n#)7::9876YXWVUTSR,QQJ֟ĘҀm")7:9876YXWVUTSRQ*J֟پm")798765YYXWVUTSRQ+PLn֟ٽҪm")69987655YXWVUTSRQP)M$ןÙٽҬm"))((698765XWVUTSRQP)MY$םؼϭm"))((6987654XXWVUTSRQP OOLPĿ Ӧؼm")(5876544XWVUTSRQPONMHHJ90/.- ,-:|׻ m"('5887654WVUTSRQPONMN?4543211.&hհm!((''587654WVUTSRQPONMMNE4543210)ڀծl!('576543WWVUTSRQPONMLL<4543210))nԭl!'47765433WVUTSRQPONMLK=5443210/)_؀ l '476543VUTSRQPONMLKKGBA@?>=<)4{l '4765432VUTSRQPONMLKJIHGF,EEDAl '3654322VVUTSRQPONLLMLKJHGFED-CC@FICǟǡk%1443210SRQPONMREo-49>ԤȣҰk%143210SRQPONM>LI\42322Iۿȣk%13210/SSRQPONMLGkـC/3.23[֡ȣʕk%033210//SRQPONMLKFQh3032?Jw-դȣk%$03210/RQPONMLKIDB?.2321@FA?=*('&1*^޿ȣk%%$$03210/.RRQPONMLKJG532AGFFE40/.--$Qݰ̦ȣk%$0210/..RQPONMLKJIJ:2325EFFEF:/0/.- ,#ހդȣk$#/2210/.QPONMLKJ IIJF5232212@GFEC4/0/.-1,,%fúȣj$$##/210/.QPONMLKJI HIE93226@GFEDB70//.-,&^ۧȣ j$#/10/.-QQPONMLKJIHGDBCFGFEDA>=<;:)1դȣj#.110/.--QPONMLKJIHGFEDCBA@ ?=<7=ޱ̫śȣj##"".10/.-,PPONMLKJEEV^^]\WG@EDC>AQWWVUTS.RUrդȤj#".0/.-,,PONMLKJDk҄BDC>nټջȣȤj".00/.-,ONMLKJDv@DC=}ӡˠɩi".0/.-,ONMLKJWHPo?DCAT`"-/.-,+OONMLKJ"""! #-,+*)MMLKJIHGFD@?/'('(+.,7A=98)$%$#"! !""! '.--,+*))MLKJIHGF9/0/. -,:A??@2+,,+*)('&%$#"! ",--,+*)LKJIHGFEF@0/.--/?8+,+*)('&%$#"! !*--,+*)(LLKJIHGFE:/.-,-:@??>4*++*)('&%$#"! #*--,+*)((LKJIHGFEDDE=300/..3<@??>=6/,+*)('&%$#"!!$(-,+*)(KJIHGFEDCA@?>>?@?>==><:9876543210/.-,,-..--,+*)(KJIHGFEDCBA@?>=<;:9876543210/.-,+*)('KKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(''KJIHGFEDCBA@?>=<;:9876543210/.-,+*)('JIHGFEDCBA@?>=<;:9876543210/.-,+*)('&JIHGFEDCBA@?>=<;:9876543210/.-,+*)('&&JIHGFEDCBA@?>=<;:9876543210/.-,+*)('&HKIIHGFEDCBA@?>=<;:9876543210/.-,+*)('&&'&HLIHGFEDCBA@?>=<;:9876543210/.-,+*)('&'$IHIHHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$GKHGFEDCBA@?>=<;:9876543210/.-,+*)('&'%DHIGFEDCBA@?>=<;:9876543210/.-,+*)('&%&GHIGFEDCBA@?>=<;:9876543210/.-,+*)('&'FHIGFFEDCBA@?>=<;:9876543210/.-,+*)('&'&&BGGHGEEDCBA@?>=<;:9876543210/.-,+*)('&&''&&$FEDCBA@?>=<;:9876543210/.-,+*)('&&%&'#$'ЀĂ%'Ѐ'%Ѐ!½!%Ѐ#!+π'!+πNŻ)πN)πN+΀L+΀#)+̀!)%̀N+ˀL+ˀL+ˀN)ˀ'%+ɀN߀݀%ɀN߀+ȀL߀+Ȁkπ̀ȀM*ȀM*Ȁ}ǀ"{{{S*&$)O(.ơ'#܀۾(#ۀʗ"#ۀـ!#ۀ У #ۀ Ӻ#ۀр #ڀ,T1Ӏ ހՀ[Z9Ù\͚ ]ěۤT뜎Ῐ 6ﰎܬ;ͣހث%٭Ș۽ &׫!ǖހȾ &غɣ݁ӝրП ȼ4Мt$ɟ ȵ2Мux#ՀлkǿМvxwՀ̀hМuxwvԀҀLЛuw'xԀQЛtw%Ԁ 'ہ>ЛswwvwԀ1תЛswv#Ԁ2֝ـ9ЛsvvuvӀՀ%֟AЛsvuuvӀ2֟ĘҀКru'vр/֟پКru%рԀ%I֟ٽҪЙquutuЀ/ןÙٽҬЙqut%Ѐ GםؼϭЙqttstЀπˀ ӦؼЙqts#Ѐȟ ׻0ЙpssrsЀɣS|հЙpsrrsπȳ}ڀծИorsрŜ,}ԭИorр~{؀0ИnrrqrЀĸQИnrrqqЀÀ`ИnrqpqЀ{ИnqqpqЀ#PЗmqppqҀЀ]Зmpqπ\ЗlpрπwЖlppopрπLـЖlpò9ϾԽ3ϼЖloonòE۴ǟ2ǡЖkonnò[ԤȣҰЖknòG؇ۿȣЖjn΀̀ ֐$֡+ȣʕЕjnnmn΀ˀ٧ 5դȣЕjnm̀ˀ |}||{zzyGxwz޿ȣЕjmmlm̀ˀ» ~}}|wݰ̦ȣЕiml̀ ~~}}| {vހ3դȣЕillkl̀ }~}}|D{{vúȣДhlkkl̀,~~}||{ zzyvۧȣДhkl̀ .դȣДgk̀*4ĹѸȣ̖єgkkjk̀Pޱ̫śȣєgkjʀ/CդȤєgjjijʀπ 7ټջȣȤѓfjiijɀ#׶ӡˠɩѓfijɀDƵЍeiǀŀBܴ܀1}giihhǀŀ_巷кkiihƀĀwؠЍeihhggƀĀ[y}Өhgƀ^Ձ~~}|Ѩkghhggfgƀ ʆ|~<}θghggffhƀӻ}}~}}zƬ ziehggffeoŀ{||{}~ }}|ztuttsrrqppoonmmlkkjiihhg ffegiihhggfd}ŀ~ }}||zwyxxwvvuttssrqqpoonmmllkjjihhggfdlŀ~}}||{xyyxxwvvuttssrqqpoonmmllkjjihhggfdhŀ~~~ }}|{z}vwvvuttssrqqpoonmmllkjjihhggffeedm  zyzyxxwvuutssrqqpoonnmllkHjjiihhjq :m+L+%#'N%''+!'+Lƿ+N)NǾ%N+'!''ý''%+%l8mk11-,,,11name icons8mkt8mk@VV#$QQPP""UUUT""OOPQ##UUandroid-file-transfer-linux-4.5/osx/android-file-transfer.png000066400000000000000000002655621501120463400244100ustar00rootroot00000000000000PNG  IHDR+sBIT|d pHYsuu}n|tEXtSoftwarewww.inkscape.org< IDATxyw]l>M%l4$2(nxaEQxNjq}<|3;Sak y=Sgxݓ(Y;bsiFQ;Ƈ>_zpS1.5Y/E{)eѰGʎW%A/>U<<0Z俛;>_(o:\W@*锤$MVJ$IrݫӳWscVɨn}c;O"I+4yN"]Jjx=ǀXEo&`(ߍ'~ /S$ɝZsҸֹI0v+MXE= 3\ƀzvw>!?Ǹ)Q}.>J[|W?;57n}OiUJu]%iV?Ȝy߫ .c!-_]ǀzvw>qr;;H;?>C_܌7xiӱN;*ы$ߛm3%2VHQ{_{ UK;Nϼ'] [xyVST ߛm3%2m3 Q; Nh,hVί;C_cr MnyӉ:ҿVe{O/u NG4O,/ f8ap?S=/)j nK[[DoW>Cr~c?j,Hߘy࿞On08a\ߗl&?ۯyWo4+tIo?!;dg ok3' _0{&g3ѧN;owSJ^}wS!}So$=ߡRy}S; N0ׇ;ִИ7%]NH־?6[b -G>qV7Z?q{7n~Ӊ[$EJW> qCv6z"N^mCw_O ϶%z|=/\#7<'% #'1 wp1 {PK&4TQv?GnriKo??5##n㇥$qCv6z"N3Gn08azQ}xkӅOSJ~p*6Q5z"N3('_W~;dj%'x[ܯRUߍ3m[u/= wwl^T;o?x:A7؋߂8!;fԋԥ`xڶ~ m8Jߍ3?  kc^F^;ߒ I;tŭ`:"ox❒.2)]!]ό0/52--1o z{8o9 5W}L9%7˕^ M/ǘnyok[-1%g`[CZc+i_nkW7n~c+jFJw-<AAo{ ߖ ĿRI:d魼7xh"̓N0-7G!0e'n[ @$mGC6Pf8u^C y_T?-`[ ǿ$)t~NVjҋ|D?dl p'_gP#UǀS] IR>;,Wr MDoo$S1{5EokzޡI{);6trꧤ-'^4}F:doʃzvwNo=$=Ɓ`zɃWϾX/~@G?V7?  k-Kc; %??NJf57|^2#(wl,IӇ0u Caoio{?>y4d|Og}@ACv?  k-Kc; %? 4m`M9_|tEc)KR{=;A%Tww; C9vJt}_p}J3KjWnh]-u^%)_ ׳ޡI{t {;v{Zm&>C_=S̭'Z }0?wȃ ;i87yid[/ {H? bi/G١H_KtP#UǀeLun} MoMίC࿞OcA*_ {PK$=Ɓf?Q uɹc;>?&{C'y߫[ZDo˃<eƔ_NpiX}׳?- P=1ON m@o+oK#l{3N4IC7_d* 5 _߯fی,~# =2w+I/h1~\;Zv~6f /ʏc#oϝ}GId4B"SPf;Zv~6f (?~g 0sg?UD[!k|n Ƃ/mo2׳Caom[ oO]3%IEہR>̀ߔA;v|S`Ο% mE?iDQc?j,H.c=;;dav HGZ ^{E/k{>~>;h;d#0e g῞F @o+omP[% R"Ea_?omm3-RjaFJ缗c?j,H.cB_w`m[ oT|kI2^^ _v"0e s7 1|oIq^{߫ .[fwuہV6"x} O}a-elf}J); {<mm3-WcA]7pGmm3-+߫ .FF_ok<3G5; >Ɓ(hK^^ _t#0e g}S; N}y?j,Hߘq^h> oN{08!/ZMXE1c=;;8apH+% "7&O?wX#eG=;'ˌkRg>7WcA]λ} ww 2b1/a[ [gK [tD3Tϲ`(ߍ'L_˹ϧ )s ^ _t::׳)ߍ' oĿs ^ _tqyλbXw>q5y|Kw1{5$EoL_?ߧ7}wX;fdFO]|w;v  Qv?GnȄ_Zg?Ȁ ok3'_8{?!ROT!;dg Sd0'&0/cW]Gzv ZQV6G!K;fԯC_}K)S6[b -G:qC6myo JM3=;[u/pGy>zv~!<ņrg6z"N3Gn̓[bĿT_9׳Cc}w;J;_8{s{ao ?=;ƒ_*|G5z"N38ߍ?xXNK3=;|؄?::㚇wGw~>;;dc¿4_9׳7po˃_8{C<C66JK}3yE){_2B_ {=Sw>% /W3::Po-o̓[ R̀Ì!~?(m3:!*#}KrXD_qb(c|tC/1^_*)=<AAo{ ߖ p-[55 RO1 o=8ጡ#FԽ-P3?hl pSt IDATo˃_8{Cة?O`~їo=['C-}Q^};x_r}!?(m3::8o9 k:%?hl po˃Po+o˃X/Y̓NmЧQR+w~` mmyoeW/j~!?(m3::aL8#J{C-m&⿬v?  k3Q;6tluwy6é-my_Po+o˃[l/ev?  k|,ϠG;zv^; %mqǿ?hl po˃[8o9 Kwy6éC}\K$=Ɓr7 -yo5qO-C6Pf8u^my_?a0 -yoM`σN *y4rgߒX>i`σNm'[8wLKbߖ?(m3:&0 v"I{_1v~ O8w[ po;$=Ɓ`[d+-x@4G|1QXE~/a}:ߒ~>}viƋ1/mS5ۉߖ%y1ߒ4_ʼ}KRp'[8wLOC_Wش_Zo˃^'[8wL oM3<AFῦgCa/̓<P&}Gt_Zߖ?([O(c8wLKnwI4Eo 1??هu; %m1ok4d*>)_tq|-Kc; 'ɫg[ ۚS׳i,Ic8wL o[sL?j,IڞO࿞4Kc; C|pB_m>r&WcI ʏE!oK$?V|0c]@%;6 R"Ea_?هmmm3-mPK%pk5 o12??ҟ%#oϝ}IſT)_v"0e g}6 @,K9h"^π<C6 3\ƀzv6#cg m@o+omo ĿyϋƂ/mo2׳Caom[ o[ 6uy֮/6??7padw{yo; f[u_:)_v"0e g}7 @] _v"0e Cn^@o+omo ԉ{Xe- 3\ƀ;:w~@o+omo ԍi{Xhc| k.;d#0ec/VHQ{`@o+omo OZ _v"0e g}Sz iO;w;ǚ("¨ {Mߖ`5?8_xqɫĿ=\7?1omOn|EQ &H_> oNفapBk9_9{0}6xs?ߧ7}w-3-mQ_ʾGߡ?2VHQ{_{ 2c:[Ŀ??1om4o f8apF}675fPe Q; N";s;!Ogbyb/I3࿆c ^޴1)lHUte!yx 2o%y'G!R׿_}(#?'UGf||Rϧ\fsU xN5k6C66;~4dIߘsuMzjC&(uޞo%>7穓wO [n9KGcyb #"7&oͺ9%)swՋDy'7v 'oσ[ Mǿv;wyïݪK|?EQ9=?G:O?;P +??R ?I%˗nҫfZREMlZ)}_ߧ$;e6ΨKsx_tߺEQyuήoQd?@࿞OcI5cw:~]Ƅ'Wvf93)-Zk7࿞OcI5cw z[bSIgݒEQ@-]{%I'pR敀Op9#_![8_yoM0Cg8uvm{)\]w C~+ߖ Q?~0Imm (sVy>3_}Xy mĿ|  ~}wO =6&-jeGQRWk_N3C6mqǿtqSx?I~6(Q춾EQXy t %I¿$}ǡyngREQ[7ό&?<-0)*o) o Lrތ-ngREQ >3Q࿴G١H{wɃK [$=43KQE]Gu&o5cw%-1o L%7:((ڿ{Em}l )wɃK [[Z7;((ڿrT/9=?.OM' uyN?#_]$_na6mEQì|YIکn+oʃ;{x}4cw$KN?(ʭ6ml5FrvpӁsv=k/W-^𿑚j6 ;]aյ_-1ui#)FUݛG7?ٔ09-Ϳ[-Q^};8&|rϧ/.yok`R$ҍlREQ]y?"p}|j?_KG7lCnwdKZ--1o L*%Ϝ"OQՄڳQނ2_k?'CݦtmSGOÿ$]{BP([{n8(_pM`['_?x!_Kzv(1jqv99g?}>pEQUwy; '@wxo?5O;%*g\k1Mo=ܙyEQ0j*~D; ';f-c›?( cCH];ښ)ƥ<뫾'a0h߷]97S 1`bǿneqK+ {0sgnw1CNW{MӁt`_xh\M8w/SREQKŗr #uZӀ'Ty`o6Ƅ30?/-r(j\kc{^O@c89\r0 /n;Yy(jkc:t^Ҿ_x"ϛ}Eon?N(0;^0/I@Q5g:ty7G߷]_ʽ0j; %;;LRiwPEMD;pdG[ o[W_5]?A] RX?REQTcju{ mm3-mo:5j;k5cǹ@Q5IrN_n/ ?( `˓Ǫ^ REQV{v<(7[f[rZc-;Ƃ/m?oEQY{ס {eE-].w_ߧ .[d/o<\c(;Wܻ;[f[&ߧ .[)v:|(ɵg7|H37^1s4Eo4 N@QE57+w!x Xe-4KhG=7(:=* C"7v%ߧ .[}PEQP{w>.Z*/ {/YoƂ/m&_>9nPEMKы/M { /YnƂ/m_>O'<(Ʊt C"7v)ߧ .[d/I}? (jj߮JiƋ=n?5/Qj0bߥf;9DQE{u{'4Eon$݄;h;d#^}c]EQվ]?EG37^߭oƾ]8_i=|3@p IDATi+o XR( ="7vj} "Ew(&Ё}9@q(hƋ؝ 7OcA/5}kM[|#o(&tte7Q{4 |j=;6$W&-LREQPe7Q{4ſuO % bjZ~@O;_>~1OL(ƽtK>wEG37^{>I bO\5࿼a?Ti*(q}gn(hƋM^v)'.cbcdWG8?A(n?;ϝ}''"a{/en/h/?~L}oHQ5twZU9!߷{RuO}"7&Ù_?GtDdEQ8nt_b"~yF E1HV>{uS;| EQ5{G/ZIa࿞b_Z'.c/yeWWOn<4(jk.w}'CˌQ5])ÿMMC[P;7Ck|߫~ףzG >(ڽ#ݝƀEN=7߯P3 ƿ}sTSEQ_vttݥ9_W)Q|B_?%rL7߯r W~WEQ$it|c@5yI(]0١!*o_n߄2/HɣPEQ_vtt 0 | }SE3)^Wgbu7ØR~7ŏ(kGG./NkL xM o4wv(OLa? _?_| >{|((jkJ'NJ1m1o TOrrn40/cW3)/|Cuz5EQ5ѡƀE9Y࿴aZ\qWA0<}1om#^87wO (ڳ3ՍW|A;5m1o 8JK}7߯ #Fok#_yX_8?EQ$rG׭}O<-_Z0 7n.cWFt]? 8?(=;C[Mjfr^v::b݋O }Ҏ9Aި}g](ƴn z'ՙUo̓[ 6)䥯- aA& Eό.~.h^Ϻp^[7[ΰ2< 7̳0j~wq;6b0{!/>y:Mo̓[ 6I\O$ii$mҾ3ڷ{V+mmٔhBKs|O=]WO=U7P"6Dv]5Om:*jY-,ham47[^{vJ.mõ[ 6i,7qS?p?1F#G;zIO؎ɜUz]qgcm E7|b]CxAyL5IRڹIl[t/Zю qv!V&O^K;yTU7qS5Dr ?3|Ro<} 鯯]ٷg\=jRTgg/|O_P}$ ~U|Gg8u^CTG mLe#^(;b7poWٴө;aEg"N+zޫ_|'#gQke9աVU\'. 5c7hUvqS5[ڦNmQ+˅5_N'?zD?C\R}'@M(hn_/*AAo{ ߖ_R7ڢW 1 ?>w_}}>E VS]97iC?/T6U6é-oſ{g#h_Y.8WG}CQC;puvM%m wNmyG+X+0m̰߫';~[u:ST^!]n}KGQv>,#r~   kVOEu++Z|A?KwwϤ/}rg0¯vLt_L()X WFdn po_V>W'п'=EQCee|c>%?uqĺ?(m3:$g{ߴgſS!?I];ܣ PRߧg>C0d+]6é-Ijw>Jp6=>ӝZ]Rͬew9i,نh'̪>[?lo{ ߖſ$g706Ofnc^ϥOcI764OEC{d?KK|W=;(; BIjlrϏTHcw Q;Ԍ^{]vH7ߧg?^_2o=17lygN)o8N(˿7CQue_~OX Ӌ@Md_-1o zbLjvnі7  ?_;iʯ˩]yʹN=Ca@GߡJnK [rXn{;A%D8q̘/>/IQu֮ iúa4/ ].7 ίnK^o=ս3:80S^{=ȱ)Zّ_LG= F߫&TGS7%o9b|_ҴCaGMl53[ϠG"HfL)%'O7ϧkyQ}_{t;t/enK [rĤ?Fތ <ǃv)Q8o_V;~.xF-/JfgnGGkv;vu_:s[b#"?Z  *y$1;da QԐjyQ =:r6iÿ$%o9[O6{a9oP#qa$3@>>t>5ZYV l;*1/-0Og ;Iۮٹ7w'#G|蠨!tMN Ӂo=q]ڰ< $}k~Eu7*j<&h\NK7i#Q_[X\{D!;>/?!5ZZLK' ^?xn_9cn/-P?#0u̔W=MJZMC/o8N8(jȵsI/V{}"w3Mÿ$f6hw-o8N%Wշ7X/Qw<[e/_[3M W{vsB ӟFܡ5 0x447bOs/[f$Ie3f"[?pߝ0\/>R`R--:|e"^;_Z {PP~՞٠M۞a_c !ҶT'swk?S8yCaG߫ ayK97w'&o0;]5 i^gCM0u-kWq6n@ .X?pߝ0ZKWVW;DQCmn⋚msKo[^;Ur<-uQzw~'+{i۷knNץ F)j'<=(j(ct{5=-mr9fm& {L߯[Zqp~ZN  5Y-ՍW~&E-].G_Z࿆JMSk'JyBmrCR?pߝ,KғN Pj--j&E-].S!ԔX?࿞lmr6oHJ^F?pߝ<dҢt/~9w N]1o:Ga~.-|ڳxw¨ܜ(j kRk'e_u+ymlfQwhlԶia~<5w'4ŏl0[5Z^g w[2:927퐍 o=qZM[ŝjvnќf^F^7?%].^f0N6Fm]~l3e_ S$ƍZ^n_`;g)7퐍 1;[ةŝhҳ53m c 5Scyfc|LڱU_ `;gi7퐍 1C 31__sKڶ\m_9[M6 =sz/IR*Zڦ7=;lǁlp/o2f_eQ6lޯŕ˴m ikn~YGL%i?7)m+\#7w5eLU}}w; ;FSb{љ͚ݬM$IZ=dSYU*MOGؿC#E.z}v~qdž+TS.Y.ZZnz/s%߼vGT3amlfyjϫ՞c/̻ 2KDy>  y˧={%\Zr~8ww;؎ϧ_*1Eo 1࿞jձ9$--{HQwfw;؎]7padOC+y옚,3mظ_u|59(pŗ}Oo`;n omo2׳HZ ƿˏ3%ϧɪ_%ϜpʁEon,C ]0.[fwXG wos br=ޯZv^;^u|-Wy >Z v(h3װqǿ$]yɂf?񒢦o;=*~D ]\o:M})߽{M6^J); {04^1pھM/in6x?SC/h* w/^ͨ oN\s$iy1MWYvq|z)}:3bL/mj__kg03G5%BQS[^uoEr^zv^;^ |-7p.} k<3G53u.| OQ.E/ivM^rjLKv yJ); {Kgۉ}(j:ky^-nL_Yuug_Zw.c!{MǿѢF}}SI:|&6p'Z"Z=}`uψ1ſt]D?wwJ); {+g,-n ܉-n^͵{NH ࿞W#kEoL4]fx}e^?;cEQm20(:3b/y5+ofγ]λ0A/7BW$$7z655ھ-MW}cL q)^SG@; $uug ÿr{C;(;dz㆖~uKzkkfoOQMm[Wц/.タ ]VZ IDAT#GN5o~[u/p~@_ sx(_pf n qeo9 $w }Q5z"N3;{VfzQ^RUW-n_un9:~j 6e?s[Nm ࿴{PK&c\>[dztpnxfOQcT۶J7+c؉o2noS/w;v p7O§k6oP)jkf+u\om6 R _ w(% 1nt^xFi@/w}c?v _-rj*/w;v p7^iKw/ճ6g>}Aˋ)jbmۚU=;GOn6n TK7aw2Z;ڲD -mP4jKZn'AdqcNNudǟX}vJOV>KfډVv44?hc^Gsj럦mXhiÙWZڻqN&_8)m[tWr{nﶌ9_K}7a7g?I9-O_йfo׬ff^0+3̳outϗO3;}\ǎ3Jt9z-蜽ڹLM6}?7\ -_Z GND]-9m+oZֶun>UI'_>x_nL;s]Q{mX(ʭ_ ->1:3Hs7m՗nk)\۶H7]}'ٛ-ߒ}6é-}-C[nMbMYR;}ljɯ.8T3mKK%]{&%M73g5}a;25g>?ON1 6n R o9^'t[\bq v>|?xDz4=$Zk5X)R۶$#gnWy!#6é-;Ol%tM zo|S޿ށ)z\EQUwmےUG `](;T B,/INP'y/+I ڲ5KZV_|тOEQ ~mI?G}x6éM?^9| EY*Iqh~wi󙷺i!궷Ҷ埢(ut Qv>_(`ҫcp:-G^QTh=i zG;~d/n'Iv}+7PEOu -?}G١HhM>6éHoe;G7Qs?G{weށ2oyƭAs(͉n~-=Y T9 ;CDKE7mS5/u^q(*F\n]{'?IRPEQt[O8rRvf }6éwܰM'PTڹ4mPǿoޮ_7ۤ(ںYz w}_w(:7mS5ԉ.٤|r UGN?C+j_2?tf%(jj&+׆'<ҏ>yW'6éʎ_kF-5/_{g^7I[A\}7qq04) #V,ZCuXW EKZ{kW^(ie֮dQRlEIH鹏r<^'B]]]_ee>~Z_lew΃ZD]>UʿHhX0/q 7 UQ}l'8*eN!$ 5 | hS+nʿPʿ|&_0G{C-xުrB4O64w`' ߳{wBh>MEuÔYʿH[G'#Xez]BHjx)+^gPera@?|h>?LʿX_a|2 &~E4 |oG!fxēw8_&G(R=ɛdmKfE=| L}4JcbNB!q!_&G(R=ɉсٗ q?|^@t*u*}IO!PuP@!HP׏fBU+;1@M"!i#rYM→(+%%&tʿTGr&@D]`$C<*t?|} żBPS^({Kx$P׏*RBjh^/ <^!lǦnhCKx$ bSE$#Q]%ʄ?\G޵`tBHiԁ'=z6xʿTGr, +򯺋ooHB;M_i|%%7T9!)ai/w@ʿP<_#9 p*SB(HFiMܽqvK!Q ͆'f(BPtȿtVݔhzHp>Hv9|wT4pnBtQ(c6$;4G=xʿTG(x (G)HZ`|frL`J%~)yM-|9}0)A)(i]|QD͞T?QTETnIh |ikPUP׏R̳gcΓJ__JBbvMfmwYSMi g7G(zpPr,.wd2P,,7}%###I@H4|"E,"hzU=m=8CbI`?,.R@HY\IѤ[ $vԆ"ٕ9k ۳m#dٹ6!q@H\ej $vu{"5A+B~V<kB¥LB6fU].^!q266t $B<%4.wWI,?`(zpPrls^y7<ӨOz C>wO?ʿ“sgB\Ŝj˟ySoW0sW_Hql g'#!k|>@??R(E2.L~?k~sU)!I5;{CBoi/a7~:x!qr0 $k>O?j@*^HU|,,,vbBe !a:e|V}vkBݻnbC??R,ߍh~y+g+6~Ǚ+)qu)ՕX;>Wq?CH\ g灑{{Au=~s Zzc=/RNH-[ҥKtxGB( ܹ{.q !W/Cʿ_:GxʿR;,`jzWV H;yɧ13FŠJ-0 e S+ouRZ+W$ !T4pǮyTr{/ՃQ@tr (9Szܱiey| RBLi̮PiΣ\y2Ν_{6P(i4h۸}vҭjJ%wDžK%, (R=D'+/#L<_.[7 Ԫw$D<7Ogi}!q2 UϚ[81mpGH lݺKBRML+e߽0?`?]D,.&ʿX߃x}G{v?VE;ji;^A÷楫xks8*m-*&m6`qq1VI5gx_/V&:]Po&ȿҲg2 k. 3gz\ ?tKklbϽ0Zā{.!boߎ[nayy98S*cB֠UN_}^ys_yiwQLjG6~{oȘo`\ ,x}/{`l  $*LquZ!$ՔJkw1UN[<ȁ+\7sv \[U^(M%ؕK{U-7Sc|64GC7Պ|`l-twq{g'nkRB,'W--ndsOx=dPxBd 4H)arxQ,|'>8#߻|@s4n^3ϝƟc8}f@5U]CX@ E\2Ï=RZj8k?ٸvz /_AcdǠ ⒅i;n5e2TL ʿ2*@_(ԃ}k~t{WQt f}XK/_{4 X]u9Ϳ/Az*!?f >]E|7` IDAT}Cxp︳,tAz,VVSeYN8K S+Y؟l\]Ilí0gPEr(@MMp͔9V"&E$Sx*&}U [qĤ2Ν3S1yVȂ*KmF=BF;΃5Y]el+ XZi/ZiOGK.رchRԊ2F,'WPEr(@MM`@AwGE``lkC ՊZc@g5|7)MW)>t}{i[_laaܼ[3KQ!}0CأJm)Y/Z5P(ͱ0s+ЋH6h6˨VU:Zž=#ػ{۷5 F3cnnN0%ƥߕ:xKP/0PKÿZC*߹Ng7/}Ex8E.Ar]+P@c䀖_J<~iկI G$~adڮTޭx.9؆Ǟ\ F3:N:)>Ԋ"F,'WZ[\y\/Cʿl )B9(=Ȁ@{u1~1(zSxYv{QK/uhp߿?,/ׯׯ>;ݍC}AF28{8qVVVnTRxPEr(@M&*~ʿx,?oLҏ(ʿx4Y6jJ=8ԥ[h-ߐ ҃ߍVo_8=v{ETXfݻw`vv6vI%"p\\JJ=Pe \|wdH`e*VWnKw'ِ߬]0 QN|p-=mMVWOjk+eiöm077["$uƦ!@HʿlP?.//݃?6hFB⏗KY_ZDHX]7J/v ? $JvB\ldw dPqr Ke xʿl?MʿPʿt;2(xy])vNrmf! VyQ37..1TwA0 c(˘ MP)ɤ~\kʿPʿt;2*+h.(dHדoFl?Tj0 _F+KW.R(ףּ!ruW'Ѩqmڻ!ٳjhZIEH*( ܵg 4SeI,O|gx/^1/ i_+9ߎB\݆B&ԃ(5xyJ_)cco\ĩ37ym c&g4Mm,-=#$K {pj K~C`<_6PEJPE+IgPu%wJTWE{Us 3xoE.yX2(F;v`Ν(hZ} 5BgP)ɔPEJPE+Ma@וߍV $\kʸ}{},ud7yX,btt{;PVaYaYA $u3xʿl2e|ъy+ a@וߍ^sdV[ښkh=,./Gval@HV(J]o>V>Z$ =Sei0s!_rR ?,Nbq`t=fl͏0> x sHB?.dK+v\U*T_ࣔ_xM~-ʿH ʿh%/ ?@Zm76:Nlw&Z/8\E_?06 _IʿW_P (S??,ˣzͯWos>Eh*%YP(4M^&ORZ/8T*a~5ԇɃZNZ_hʿH ʿhE_Wr7:^&_ï*%ia~8:@?(Um{VDj{S0 S`ʿY'Gņg$^m—6TBKd@w >&_D/MJQ =ɳ"j$%P,HZm6 ˿J*~W^R2(oʑ,R6}5 =guJJNfHNBi#O[,3(!$tz=PUE.oo?STٲ)'9忛!9RyK_4?g(@/$N{=P*;{_,/FCtV? 9¥c޳[R(ru({x5|i٤ ,+D[@ϕ[,X(׶0 R(2i(ʵW)(W77::Ͼ&B2@9Rm@AW\OB_b_:C70 (CHJ\R*t*=gP?e6@#ۑP{rP{0)(bx#ּ;"cKC=$#\ʿru b} ߦ+_KOlBPܟ# (=9(Zz0) xʿX[hPEYұdPҥGLYv? ^*_:啶l1B0^ (=9(z `D)2i(J@ן#Cb _^ Re !x= @,τ^xlAB Ğ#=( (}E( ,D+P(6Pm,Mט#Szq,l`&j{[P(w ԓ";Iɿ͗:%K!AȿkCvߓ@z@+[c>ߦ>tLW1G_ǯ@PI˵Gmj;zR{_$B,ch.FS=-忯H_|?н"ycd&_[1 UTZ+@6q?BoZ٩[y!$X*fg8x~_S}זB(}EoQ{r$6vTjȔm9(p19ؤHߦ\CC`;_G+_E!ƲV7 8bM$MK!"ɿ*1˿]1RC6FE]PҥO-{P,^OP=(>)=NeglM7kdAx@uQ!'a`h~fQ-ע.(})v ˕^vVCA>xQ`Œ|NB! S'r˻=Q=]^ (}(#Pcx! J⵨ ʿt|_t@W'B 8@J?{SI˿)\ ʿhEʿX.)'KN5GwȱX\x^, 9r09(.a7n.byyU.?!itkP?9x> [()bߦ\Cs>٢˦h˃O\)U1m B.,,r@!n8JNw[*=\%/#lʿR(68zhz{R, adPln1Po%1xb#(IW$_VG.B2HۋgLKFMPrMCɿi0j;8 _v;߭YbhaT{ʧSmLQmޱ~]ʿ0h4D>^B!GTkwLKV{G?;1!#))}E n E4>^|> o@<I˿{> Ԫ"$lVW @.ln^/#lʿZ7⋥&FƎ1rgRe >=>/?(Yl`h!40ik"ט "+GKH6aP߼RD6奫X;vKB0zH$_"(j;Q(V~rmVaq<:Ka\*^_T}ڙ{Q(6sPeA0ʿXi({ȠcvRU,/^Bk喦Ǒwk0 (WZ߉B!P&q۱t ˋWں-{v7aFFB }ڙ(9(ɲ"/#lʿ,=*qTj贗thfWtɿiP\݆ry) 'vi`e*Z7ښ¾=#B@[#Ww(b9¦!7ߋY؇Zc/,ՕhB{u%_:/]|oE U!ʣ/()Lj}=߿ VWn-ӖL6₊nȞ]6V;WPrMCCNy0L*[P깇N{N ڰ6,yvYsn0" ?qa_ǣ%0Lʣ(G7mY`Wɿ3Fă)! Qzku!_:GXaP@a,T`*)颕.z˥xsdyeȖ  䇸]Cʿt2ޚzp.ʿ˔tʿ|%;+}B+k n V;_zG\nʿXi({kLM[}9()(9t|g_hB}IWv!1 LK:p(b9¦2_4]1vױ͐MG=:!`鹽hRyՅ ˿T_o%lPrMC_{eʿh4ȿ7-vzSȲ→}.XI!!-h:?Xwk0=8PrMC_{eʿh:?O?Cv>>3u%zȽI6 _=8)钖3_25N #<Do:#D)&Y=YLʿ@i({kLMwPRP]sxF !y na L=GU-@E#a _"_,O/V/FZ1- B4ڙak"'s#`} US})=_ʿb֏bx:3!dz\'@5ɿ@Kq?`{ @ʿQkFd1"۷:3!d5zEΥ.WP忻ʿ˃!reslVBHuy(zp.*k(eB,=ƤЃsٿ(Zz[˥JywM4Bg'g=_  Q\JB?7/1փ~d!zp.˿(Zz[˥Jo- !d LLRRE,JMx@4p?hdĪPxʿXAʿyBďD BH8B=8 D=$&:ko/#*|G5tB7(8OكnC #_8Bʿ(=gȗ߷o7?>!ʿRqdSu//V/F~X o(JݓQ8Oʿ(r(b)bM(~n ]B _{2ʿ?_g/y 茧'7 {u+?!T=_=Pe'/ ?8?O!+wOF ?;/$CMW/C?Uʿt#{v݉UBW |CW*/u (zk/N?C>0?:mVCEBr_{2ʿM]>鳉NAA Y5=QB=;NBr_{2ʿVEC(xʿ(2P3d_G'P0Bg'BW*/e(Ԉ@ UWʿ!ʿWʿpZhzKПATHͣxFQ)tBT=_tjQGX)>l4=P@ П!_,xP|d>DB([}V}nٳAKp5æI-/sH@MW/?(=gȖ_#aYB([V*){k/N?nbY]eUe{wwʿRqdѭP)r,9=d]w,]e]eVBjZ6Qw?{,{V.TG+_ߋnL83?^*ey>^͟BtCW*/Up@w7#G> L?!BH. IDAT&nU![ъ zHػЃ jcB! _6_tb>ʿhEʿht=fTX1Mmwnɳ !$^~e̸+ 0MS%PLӄi(( 0֮knS,aYo-E8pb]gQ//UCτъ zHuOӽx`]C_BDt:X]]u=fyմ޾X,Ô!(Q\GU_lHH͐'ߌ+~v,kBы-ü (zp.eYa ()$㛑Pqc=5럸 }B }J&&&l6nMP@ъ zHgg@*wq)B!zeLLLh$ !Ȃ&߃s9/X0))~AmKx?!TB'كE+Ae(=X_k!hɺp@DH l {0TnK0s(9(2|VP{p<B0n!4iNUm8 ~P7)O߻?,?0_&JPʿtq$db%CiO~/j{w-Dj9?MߋǏk{,QCur ?8$Y *CG`a>'!"hٻw/J.;77ǻu(G,{|˽{|D>+Ae(=?,,%BƱcb}ZfH"g4Zy-=$,/g% _%@!$:.^ǏRL7nIxT2(9(2|VP{z=/H!Gއy0ko xʿd (9(2|VP{!p~z%| B!$$kp5[,k ^ ? /JN?0_&JPʿt!..C!H!@> pu#xʿD*+8Gak_7JPʿt.p@!$>>5^~盧3(T[,D>+Ae(=H?=9 BH|}Տ (\/vg% _NkoχK!(!@W\SR×TO/vg% _Ey !dکdcp%;v瘵ǭ@|(x-oʿ˔`^|yN)BC04N8ʟ_PC˿* `M9({kLcjz o\TC!Ȑ!^pzz'O e QM{,QäF&Y *CAۏgBHvT*ŋqĉc8:__%T?r@=P\WnqB!j5<8x`C .~ +z}`E=8/\1ʿ]e,tkBDRѣj{8@EN%'_m`O"_,?p/WV9Ǧx++]SG!$ ȿ Pӂ D*~<ȿ4g%L/3G?wSBM0o`zz:CÇgn`Y&'')MAWac{OV¿t:{E!$d߆C Μ9Xj?(9(=-=ot'-uX%Syyy^QBNl !@ĹsKk0wSPU{QIȿɼ3=I\%Seʬb/Bt\WUe !\Cp 忛ڃoR#!1]TȿFKCc?s |1CIbSm8q"* ԩSP)r Bo(Hh7^]UI WffקqZ:CL!yFTj5o;J!Spy-t:8nȿ`&y6q _{dڍ~'13*!QCE"68 N>0P)(=8-( (==PE%6SKO<BBL7MsxMBIS9r`q&''cynG^cwSPU{p[AUQrP#z$c%6>ŻBHhѣʿ ?!@W?(Va>ﺻ{K̵/~ ߙj;}/!$ȊTo!@zq?7n ʿjx?vʿoʿGʿzeU:H@/` ! 3 64qСDOUܡwSPU{pƻ? 4S@!yy?;_i,."gG.qfV~9NAW/"2j <!ʿWʿpZ9/t77{G>pŞ!$0::  k.iD_L0#忛ڃ3^T/ʿ +Π/s[ž=]]eo+^5ѨP0E.UjyWLjyJM\KZ^1j5SI0::Ç1x",R4YMAWF`ʿ@_ȿLN-arj p?V8/_3?k"¿7u<2*"na3;{a=, SdzR:$&(aXP){ː]i/RCeI`n~s+~c'N]96&vx;lBqdw7Ϲa׮]( x79`wSPU{p?`HwL%zkeګz{G_qyyʿ<}}ڇB!]BD6p{˖-{ڿ!@iG忛g)+Exw(9(Pv@j_>/~ v7=݃'Zu҆ [СCqm^0CC@裂I:yw(9(Pbi/M qɿKW|?ßg?a6u̽d׮]x"}ŋTp;GbC裂M_4Cʚ{ʿ_U[M>(yn\į~k7.[xb066ÇD<ii?~C忛"iTٓc@͐N&r@R/{sP26<(TȸonO%~>4V ccc8tP.߆C忛" /OpPE3P=s\灔:/OmĂeO$?yj ɿ8<ii8q"p0W-ʿ9t?PE3P=s +@ WM i\<bmlPs)65 [C?t=((9((pGaS)7juKe|^ f!ڈLHkl6u֤ @Tٗ`m@@AH'O`}M $?| ǧvS?Q P.cǎ099˗/'!r^癙gLʿhʿgʿDɿ7<&ΡRW`!QNʿRAq< Յ2&&&h4V̙38w\m(«(a{x/./B`ezrU#H6g=_헴CHt.gϞM i H;w '`zPato)%B&POs7__D%4?|ו!D]C?߹[xU<_/N-B!`ICדS7_ī_RCCr#GPnUD\EQ=sN-=("EeOfŒ#(TK%k;_ qkQ1!rRѣt;w 'Pa5(bEeOQ@-򯞊'"~bNB {wZM#e1355E(«(:z4w(9b ==dYm(,׈ɿ^³ϟVM<H;SSS8}GOϳPäZPa4Y(mP/]PFW>U/k"=|R#GR?Wwk._g`R0ʿ{TtO`x=Q{xӑZf?(o78>L;w.?(~I({T"&/SEQE|<FVwǓ/̗[~cA!j8i)LNN&Q*GQ@_m忿y71))n9.ru\)_oD%Xi?y]&!CjZ?s U$)g+ e/! >xʿD:ʿH(JQfI*_oD%?c]VM:xߝJ?}4 (~I2* sH =wі*N2"!ay.ݡsPMDW"-s6zr8O@c7.)&dp9}4埬WޙoKuyk?;+ G@?54![Q_xQۼub~BAwLukr =H\c@W,OP򯿇ru;ʵN{2ʿB*xlSn+yS# IDAT d𘜜ĹsnʿT|_k?:+ G@ÀɿMm(Ս=/]\ʿP6H8{,%eʿT|_@|k9@Z_g<_8BʿR!^0 FվFGt:y'.\3g$IY@hzҦ_g<_8BʿR!ٜ0hPєQ6_$_Xi1RIH|2N+Ae(=dN]2&"-r}(dPV-*AHX\\īU~PU{o/P 8L G)'ʿ\ A)z+KW0{~rʿXʿh#ّh4j2nܸ;wmY,<`uuvNӳ6۰'P%RQ?y@IT/݃ƔY (G4?+,1e("9(dK^-)f $]ٳkɕjg2 Ð3P%RQ?`(2|VPk}awE f[76f {PmE,/^e//HhU~aS%RQ?)R|VPUH-ߌiPkމjcZ+7x[(b9(dO`ߞQ Y!$P%RQ?k+~_R${0L+c(W鬠|Bڔ//H6pǞ!$P%RQ?2&Y *򯫇ʿ\߉iQ@^]DtڋhN ꄸR)C5X[y#7 ]SŔJG`iwݹ5t&B$(aRQ?dF5 c_M=PзBKQ(`(V(Sc;~3Ps\0垏'ڋ)qwBʿr|R !dIE+=l߭_)b)bʿfʿA?,}UBl(=8$Ų=Ŝkzko//vP0t 0BN?r0':ko//v0 L!ϣg;ϽWkI͵U޷nbfkh&hf'0@'BL F=IfBHaI ` nlR.\(\%J{{%}TbI%=lY@5)ʿp({KP//H x>@!rP)Z 6P!ĺh "_*_ _ ʿXʿXH#?<CzBJ@wC~|#?`§IEWCCȿ\<_ _ ʿXʿXH? !;N5J@d( 34Fʿ_&c$>|$w&ocP|ʿ#AÄJ@N0_$_,)lnݝU !$O^ /A_(Jz/_,G忂ϧj˿2B0I(PvUBi,(6 ɿsxY %{@_PO!??1`ΈXPm ˅#TJ+!Wʿlʿ{Y=UEmUS:G]B ʿjǢˇ Q_x+A qϧi?}k5-7[ɿahxŋ;WptB!򟟃 \B"9*4^m9]oó??/~eltP&̻{{|nB!1MPKK*G;w zzhć'1>ɋ(>Aoc/k !R~ʿpAS) {(R)l#䰧3{wF~g~v{ >]xK(B@B){o[KK[ > =^/CG?ۛ\CV 7pmLB! Tց>_Pa+*t77?m-~]z~2ԡ?vOBCn_VWR کV.Q[ʕK@LEw3~C{= ᐎ_Xg,F՟eB!򟟣!_AJ?i a({0hGnƯh;b/#'/{?6?!BJAosޒ_}_#(rxEGB(T׊s0_xb)?V;sB!hwZؙ +]Gw#ҦVxo|gGB!VPsP=4 dp704c[[m/'^ލ>(忹ه_zߘeB!P+_h`O=4777ި5|(z7k O`[{cLB! P+_x` ʿE bo{j_<i2>Uދox{p!B6o7[/˿ߧjؕv wu6$!B*'/?5};QüMhXy`@!*CL ?R@W҃忧+7y'd]o/9P V܄B!9(/?@W_I˿Y_Ճ`m@o1/?5!B(/C;BW_I29x.h w7 BHcYheWʿTѷR@WC-ȿ >>{]MBq2=P7 /uz+V/l_jAz`| B!ezۮoHDOWC?gG \ ڨ7B!D W0S@WC-? }=Ayz7H|lyB!(9 0G( DҷX(tP)"x_#MY:iQujB!U:;(_t4@߈+Ϩ7_W_{jB!(/V/FxʿjQr o3zoG!wvso)RxP F<_I*p]\5^COՕO]B!1Tf[)<* F<_I,~ѡ ž"uaC\]B!5?o;HalMP7)Jze ^/@o<!BQ+?xǕ/Chk)%=ԺP?QRgJ'@!BCn/z/խG:OWC=?`b(JOW~w9;B!DޑLjCxʿE9Eu H&c'BB0MttLvHLӄ)w15S()v{(iGP7)Jz'0'60;7"M| !i~:_-rс455͕d011i9jJLJ455{… 8tP/Rkx$144TLĵkpU5MCWWFFF 6(ʿ({ٻw)b(b+!9XB %ɿudҫȦאaw>X=n`a(|fFj/bH2c߾}ױ!U! |%t``h/^B͈,4M\rؽ{wŷf8y$fggrMkUFu(esԸ;~^p Rǎd3 d3 >S_7Zu}s/K hoow{/4٬DL4133)NZHGbddo&Lɓ|ʿ(=leG򯤇[PG/рJɿLɵ)$צAB:˜Px+z7C!uX,܌NĉXYY.]<ك@ ,wNqLMM߇t<_j?yFĩ-S o_(~@u6@|*gd,gQE,߂DRa}ݍuxÓO>%%rg ÓgP)TSLUʿU|#挽| Z _V ewJ-,9dI5M$Y|_njBH;q=xl&=j IDAT6'Nؖ\u>M_;%ئ;?|۷Jem X]͓q& o(bz?3'r!ظ|ȓfqi\|Y:ĉ7 C*ΓPGf/pD8G#LQʿIRFo7TR"|KsO! 8yfW'B"!F0>>n{{H---ұOY]NP{F; ='RWs| %&Eg'ܹZLjsǑ_}yaoBMcׯB*_XOY} &(=477gp__ſG>߻ wB2X]:Kx7ڷfpsB%@!*VWWn.P%Y({vzF(PkK\H6o?\Tr+ a[*קqa& !x-58wܖTࠣv=w~)zW\,m_|tj+ g<6WdL^q13ÉJ399+WۅoMߌ(.K@_]_]_/,$-tj+gaww2PI!^dii Ǐۭ4 㘜ۂo?88/#/򏚗_ >ԂMLO(VnJbuyh/c(B wd2<$2KT`g8'1D$kuKdW-d|ʿ Gyԍ&L??݃rm-+e&nu3ϟ8?/p)BH1??'NTE5@j F /Kȋ?R_#n7gO?ME٪#9֖/"Y+:Ug*B;o 0ىڵ ###anVQ2-({(!yԥ8~j {؉Z؝Ve+`.G`ėz=sBjU,,,QǡC0<477 okkÞ={*ܕsͿ:iQʿGz_ ʿtx?Js|ħƑUnͮ 8~bRA6L3˖鴉_sR1A!kyz{{j+]]]H ʿ}()2 LPsSѐK_9RY9ݟsfR[ȤW-YO._T1A!!Dy=>rLl.򏆖,~C'qm2n/o[s>kWH^/Uo "x FvaʿM()"JL@WC4N_hLw})|[\2o7_;,.@2>l&Qvw?w)>"YB*iu ␦&W2<<֬u(),&jO FK)sX]?<~03=P Lo 9i&HnAt5jڿM()v2Mw2=ԍۭ)6߹7!Vbʸ5W'>T}T|D'SY8_geBrvލ# J9O&C:DSk _RS fm/N Tcc-5|xcx]lЩٹ5>C,-'Yl6tr@p[I'{bx+{4ن!D F<G</+J"%'q-LO,dYTk׮g4f&()26MPslSpc*q|Kxc=xwpPs$3ybN^‡s ;ǚí8C!"x}zzz'N (fKL&1??o{@A&S݅jS]uJ@wN/mgNH=ު౬A|R,Ie|/.=xCx}ϨOXW~O\i ,N.8p*>h xvyppO !)X s9Ty'T[q}lRf1>>^J011A:'Wʿ?`GwcK4_(_4ߟϧa(wapZ[͚1+_\L#eLa+ͩW27O!q׾f{0B8I"C(Μ9v+caa/_Ȉp̹s簶VJNq R߫PT;,nLN_v*ͿQ%S/f?tSԩCC!6h  Dk ֲ]H8&& .=tj EoN#`t9L}5n-3@o=]!:B!hKFMg)fZgRx5kɿF*'7  @_3?<q^M._l6ђgdY9sSSSU쮐 :k;FϏw'(("E)b+8yf',D&juH/tj@)bf6$,6!cli,hr(y)~+΅ťބ6Յ{]{WoBT:(a^Y c)T Ӹz*]9݊PTǫ`@ɿ_(_ ;龜l& ,M ``6#~ ͸lCv@#?̮bfvO_0tݍ>^4_(/Y5UOԩS4 MMM|H$[rr'(NQU?h8w_(_dI-dM]YLHo&Ȥ8~rON_!eC ) zW47vvvTa&g;FϏW)kLWMpMu˦[Co)l YENdο;6܍G +zH$=)iAOOۭTʿjxz!or9daVYu UփL|֬"{E##|)"/\}x{c'&Bz._v Ă7oBա;FϏ'GA+JZD ZeM_-F K-7pkbQB\ƍ~m L&nxAej (N/oNCAMѸhDdD'r((rMWve^g=0,4M,,,lHU%6 P3D*˗q5[!%0Msssvr? d=PrPDfU\`/?_${ p{==_(_ /?`\Ѿ={I@ ?@3s8Dʿ*9~r15>9d2$fggؕ$Fi4VWv(e)aӰ" 1"n ={MX_ʿ S)(N<ܗp:Oɮ7np}+WX;<,n(e)!o^l=PrPB'&M˿RgrPPU05`nnNjJ~&7\(Pd^&L{i(b9()(04#T+:4ݿV?R,r|I$k_~RwLM/?X\JThÐJn߉1|k; ^D[%+/CaA ޫzG W&rPѿ_VY,~cK׫#^ي/ }kXO ʿRa?`B2|SxW͒-gw4@WY'Nu 4n`0pp8D 4txez(X:\E.FLA/%Mf={]5_>/>{?r=E}&EobFj[2 X:/oZBo7WZ0͞FA9ܑd_QzfM~+^_Cם!GGG4rr|k+@jAh^|G/oZ^(%`}@/b+?*>5{0_?ǭU駿_v6{hD#'V⒱~tuuU/R$@(l0| w:@Vb)Jzp]^d(bԒQӊ<([}ѵ&""/_G`玎ʴ@477xd2Yu]G8F{{a߾}bG:V$QP׌;L5GEmNPU@A 'kC:ykƭ&.U #_\>l|[j@4MCkk+Z[[cd& h!cqq֊2% `yyv,qƘp_⑔rmLPU@aTړP ƭrN~?؎Dzy1(9vbNL=Z"Du[֭[8}4#8ou4AkPU@A 5pzZk@4R՗`}"q1(s|g۶m8t Ct7PK1^bv}h#U=PrP94=AoߡBvrGuA\ӎ;"$H$QW{(H"C6dI>)n>Rfʿ\ʿU Y3l[_ Y3Gb|tE Z\Yoe)Kh0_Qz󏧜4D%mRkW;MAuJNPU@A S#`auwt׸(jwN L0Qx@WUYz?|#6E%a @tqȺǴa*IEKAQ XNPU@A S'9BgTP+3XX _ސrgPeLBBIjM'O;hu_U0V|5ٸ~ѡPXZ?_ ܔ(b9dRD"v DltʿAe) ˛/oB G~G9j]"{OsÀ&r-,_ʿX#G$&$dnApsסT&_`)zO7{|]>#{n.@Q*| N Ē+WxiI-iZ@3L4APʿ(r9WQ vf;G?#π(Ri Y-H/C$c2E)ʭ[p,ʿAe)e7S@ˡ^M=fh, mAC9AJ{Լ(V_(N."d lW\SO=v+D@ v է-򟟣|5=PrTFѨ_ iمJoUo"גccX]!eŠo$H}ŏK=P{(@!|Z?~p7A IDATkHMDis 0t#`P4MdF;hptpHglf S#mΝ;[!( ([~;l=D_qʿU<_2hZFB~6 "F ԁ-g#j'^saC6܏$R[0r>P)`"tD0p}ٳqR@KK two$2 |xʿd "ot?‘!#H' ˓H5!u@7¶rR-nA#GJ#A*gwP)[hN|طoFGG1;;D"lVlBd㘛.5B{{-x(N2[HB<ʿU<_2hzhZo2o"^A:TjZ SkР!|fuS)E%4mD:tj"25YߔEq ̀a!&wIicc41s,8SX o8;{Q{w٭BHcpphǎsbASSag##)VKGKx_|0|`jomy,EWQ4  ø|ۭ"tvvTx\+/v[ʿt("9ܐ)-QןO'0F[v!tbAWW- O'GJ|.?@W_ ʿX_S ?ܽ{|>n3A(N2[\+1Lʿ(=fPjCXRgr10R-T@ X,@ t:nE([~;l=R8A@ ʿ(=fP()]oiIepsKjB0::Vh~<˗qMwF0 Oo$HL(0E-H//B z>9+)EjPEu⶯L&%ۨ8ؾ} {ntvv駟F&rwaկ -^KoKMψTX^_(=fP()`#p+]7ޕ;v찔ʹcBc+isv@W@ F/_4_v4A_< Ly:CH! w sN[1mmmPGiooG(rvM@WÖu*E>يϣ@ȿ)=fԏ۩'V:a.kxm Qiıc!Md2<<\5_}E>ץ;/BPm/:ʿu*N0 RGSˤP$gw7TV-1335㺮# .X\\4VVVl_?Ԅn[[e2!#͖o&fffH$lXhfq҂9]Ym6DѪի)V_9K:@L 3PErPB(bÔU|x"L&q a@$ y&S055G41>>!ʕ+/:A hʿ(*[~Z4==f$A%QSA Y'Nȑ#EneeG)Mi8~cߜ8w\qOƅ (ew.|>%(顎MP+_ *i(_bpY_,/z_uX/}Q .`mmM*6L񉉉|C<11n޼I5T*(>L*`Nz8_"s8(_4/A/6&_5"u"ϼzbhooǎ;JngEOOOIbؽ{7Μ9zq0p033Y%x<ӧOٳD"H&X[[vɝ p]w!ݎ +tȦ|F1/:?C\hDPXm~>U[`%8}>;e4ſ|L ? in-&(Ο?/[0ʎC8mE;v七ٳ~tF&bii KKKHR8v?  =/CaʿHʿX_,/z_5~.?q@ ]van#c``ccce2GkkpI$ A^_*ҝ#wb=/Caڗ`@@}av bX2 t{,W23'XXLTW5$6ʿX[B 9G(#)6T" %Xǩ[p,C0#2GKQBF6Ғm`@4b NT_A;G$M( 3Ԧwl ྻZqݭ8}=ahψU/jcYKL6q;'ZE?REʥ tی{Ё7+#,_Zgrrl&f۷ϑ @4\pAagT_A;G<\?(z(P[#u%waώ|mR]0@_/}%7oOcqG~߆GCmx7!Y[[åKnƮ]'R!vVemAϏ;L>./Caڑ{ YS\d߮=a>ԹS:)bŎvn CRBWO?|aؾ};z{{教m~?(z(+Պa"_(ЃЃs'dx>| ፯ߧTBHiL f) K&X^^-v_LD<ǭ[0??o+l%b޽hjjH$ˎ سgOEp~ oy ~!ZYf|o{s3ڎ_ARqG+[177#G{A$cǎ!(TX,]v|\UӃf\v sssH&uAGG4.ST+_俻3_^,_J8d'l=3Ylƒ_ǻ:WnBHPRN<Çt:'NPkߏaAӴyԄ]vU;P)NG(*/Xm7Km?_'ß|bDc@owQ i0\Rq[YYtc:ξi!(/y_nM={]_~և9VOוKWi,߷+?1RS9 !`nn*unݺev 4MCww7>۷z52=Ppnj(l֧W2i{17fP4GEoH s{p= BL&S[%'0 ===D0`\_({S{Yu v%w 5k_ևP@sH_N|艝0 IH#zծ渾Vl}Bo2=P૶R.SԀpʿ?( JKԑ?v?! ihii|k57_?O4 A[[P+_Yep);^wqox| RUOo|m:{џC'Dz+Zގ0O@Le=*ZW6}#uoAjz(^_w0~CWGO{ GAww7,WWBPchø|rIf---\äY[e=(2@_/=S 4 şݍwqUw7 n4v Bٳʿuccc, KzbR]uaDQb1DQDQ^_CRQ[e=lʿL/ʿ9ޣ/&A #ߍ}8i^?M8uEuٳy&8٭@ڑS0F)KKKHRekL&Sl6[>o#ոbu+Ao}>:::Ǻo B9[d2c[e= nq_&*=ohC@r`o39Z[`O3^~! D$Xc2vcBRI\5L$RjQʿHʿCLt2PeSPɿY$Po"Av_Z[oy'!y.?ۊ+!?$S%{W /|b~][AOs7b%aHnB!R ʿHʿC6? /o*&jvU{6s(?!E!skyPmER(:[ڏ=N9"y붮b] ~i\ؒBnN-mo+򯬇b ʿLʿU?x;# ýwn_+ xKz"BK陕oyԴ#ҝ#ʿYCr/o*.6~OlW ԌEK3"Bj[H$Ҡی+!?KP{?ơ-5Iho ؈pOG[B!.r(6#ZoAlOPerPB? ]ш Cbl+B!YXʿX|mɿݚ=/[:_&*Ծk#/薯K =o*+@Gi !B\[Rqx{o$}YG_sq/x,Q%<`Rᰤ]vXjDsCW-`ݠky-)"mQ\Q$$A$  0`03w<ߑUYU@w'+*L ϳÿ$euEs/ۮ ϟ-~m;B!T}Cÿٹx_2]}zyV?7hb\-/E]@!PzȩM[o,{=_*/q?*fIz#7B!4vz{' CKo'KRd{ῑHoakpKt Bht}̵q?|kJ῰ 6{ϳ$]tᬶ-\BD?xBh uJ3Q3dq=)_2:H]2!B#֞ǞsKX3[B_pϮ.$ (\])#I:Y:|*+B!Foݶ-it/myߧgtɅ| ץ^ٿBR꛷=^X%Kw>=<[\m-#QٿBqCԑsׁ"oĂ)Uÿ$m|60wf?K7B!4N+{r׀"oĄ=?K"haY;BNZmw\m o ?%L:33 IDAT"ԫvj#BozXkہ"oTҙ7}[Y~nťuu?KR\Biuu]_Ef %UGh/I_E@!P7>cǶo[m*_1MOAw(?Tt4PN.ÿ$-L@!ШmmYM}OAtRs+W VGt\!ny?{r{߶,À|/9`Iڷ?sV9+Ib!Bkuu]~"o .~3iZ_ܟnT¿$-.٣A=B!T:x$ߺ,Ä$u4/IOr jGY:zߘ}q!BgN3n/gY.xIfa¿$8}O'#$IyVgK>(IRT44glΩќl-'^wN`?BG>zWba/_Xзs c=o,}1. oqZY:|;IfcNNfvڮ$q )<~_!B;鎻 /gX࿴_M{y:B|ygRj}}Q+Kt=^:=,V*08;ՎcBUťU}/n /gYn ;,hڴKQZO{C!:Too< V3?U;`cc>-kqiUݙkUVOsjmx:,C!P|}>/gpaoѻz؊P>u;7ut-/fE︿LF!E/gpao鑵~RKY*>;{t)-zpܝ9.Bh/?uW`࿰% wGgOGSm:5ohyO+?|Q.!P}Si?_X ;`ңl'}R{u4с ohe-:BSuBЭjn%;`ò?u:Am4itR?A++g?n/u3-->gz~phiBi}E!R V3¿d{g[zO<>Hht׽gD𿡥Sr\YIֵ=U!4MO߭& _*{g[zOɽ; Ф{ҟ7?}1u:+rfZG݇G͆BM;ݿxN~~k_R-WKǎ'K KX9}:qr-MN^{9s~I ݭ-~*JG7q!*ғ}>{"|bd¿$%Ic 6 /^3 v-=b{~wѡü'$Snkq+Ϋ+Ǵz"wko{64B!4qZ^^'?}#qr_he0ϴ |4/3 v-= =w|U|y/Gz# Ίff_u~V;w!(}>e}'#/M:Z3јkoT_񫻃sg:UFSkۛjζ^.fTc}ᆱu)Ii,= w;n<j?!T=O~>Z\{=_ffWFoAKRᆳoo1,;~B7_;z"YwQ]α gŽz\zѼ/^{0g=0g=0g=0g=0g=0g5{?_yJ4StD ;^,in C@T,3;şL$'Qf}1zlIc (юs_2 MW^];=_aΪ{aΪ{aΪ{aΪ{aΪ{aΪ[>~>~羳>I m1 %5rC<"K[Q^]z?{^J? (g #CԿ{3淽H3s 9b]uE9guߗ999999N\׾ny7,._fkWa/\mڄVaEE7x~}5xˬ*{~}@ܦ:t=_2ʹn+IPbTIU|ÜU|ÜU|ÜU|Ü5|շاzdmԞP7RɄ[^oH/.uz['aΊtWݧ'7;oɈ;vlʟ@ ʗ}b@:x&uV sV sV sV s0=)v^:+UNc 4%j4+N.K}[q}VC?vstE5qq}_W7}'%iaK՞=/o9Ý_pe {#_K;o'{V sV sV sV sV?{RyF=|P=|P=#@N)`߷[q,]|#+te 9ZSv؞?;'8}R>ѭxVyI}淿4gfz+w^C]2]/]Ў͏vg=0g=0g=0g=0g;'ui={>sBS}Gu{_4;;Kjin5F ҙ MSeC23жھ3:y{Rg=c2=\: t:NZѩSZZZRgE=8Q,Cham -5;A-/mSTiwshjؠ_UfG{drjUz3wh'YyUM*Iqѽw>wzᚡ/I-&c_VVֵck;=@Ƽc`ôÿ݇κYa}-Չk:qrua<8GaǸӏx2M ϐ}˹aԣ_fs{v%_M$59jiiJ o1g;GƆv}/Lt :q #:#u O-5F/^/{Y[= bh_J^mGIСcaDgNl 5Ij9t:8onAFWn89|@%KwNgyk?jMKPC'C©Z]/ew;M[2m5KR-tK.࿂֮7.߫03R 1FswSE V͂aÿ4p0 p-?y+h# 7,:gˆH&%5s<1K[:iG !CAߦt@_\]3Q3o]ZWaDgN Rr࿚ 5Ij9ȄZ_~?o6[T5CAߦÿɢn/sɁ8 7 '4D=vK+*ib_jw*IUcgNca&/~v{% z6%%nA1_oXtп wi^:M0KR{|࿪ cR gYG}_'C2ł7,: ˆH&j$CԷx< _MߧQ3%)17,: ˆH&%>HzV'  N3m5Kj$.QΠ/Y _M?~ߧ03R)fs͹!LYewނÿ{&SX/L[[}`_@6 B?  #:#u"];_ڸWaL?5M.gg?ߤfk=Ð5cKOo;9L)\âsMG+*i_j^P_{?k+0FݼB:/IITk y"uu^RKIRi)U{> |S1dJ֮7.߫03R)fs3U< --d~ ),-w3s_-qs{G 馫q \4_({caDgNSIʿ7K pߧz nQ.@Eil#?xA#^%jƽ \Dv߱03R))eJf8IFҖ pߧz2_$ Ǚ%if| ^j]eou~s:?t٦j4*4jq K OaA_,5ʖd G}f.,R#Fc6{V7=#JA~ #V?)*[GTOK[#vCL1!¿ drou~s:{FtF4lEM:st Ko2 '74` _n>[v>KIC[^Fs.st*iΌCoe \_rY‚ɆIj4f47oK4Cj \?9 #:#u(OMf/a_2^caAFE7im$o453f4ښ](7&7 /m WR/WMWHI Sg J%~ws7K+/#g̝fsmL7o _wld<17[4p?,Ô¿Trϳ]55pYf?مKloݭD;^ZY ?USTWgU/ocdb .yV߱z:C 5kKoӤ;Ow{43 ?UVg/\ާD5Cbsϳ Z25sQXgјFYPk|@w ?U˔45;3/ _zN _ʸ+Ec F߱zoq22Im;_%m{h]/PJ?rCs ={ t-y-T֎ISwmI9*o(ijۮW+IZAx]/RWќ0𿡹mW>w"*??N7࿴ǴWinK4f&WN  mj3Àym*%(Iڶj#̬7wWo n:O5L'ɌfE{J࿿`Z_:sϳM8e]_PMy{Če-#/Phka+l}Loff732O5L 'g^+iIjyI4 &Coќ])9ŲZж]W7$vf/)gUoy6@I{ ǴW55嚙HIR))4*?: ^Fb߸X-ihSZ^AP,k1}f.‹?lm'IWgUcF쪤1 l6)/kHsw{Bg}ޡœO(Ak9J_̹C76;m׫xjV;ɠ IDAT 7ito>*3tpo4gnf{}R~%_0/m^o5蓨=sZNKkeG#؞=Os /Rh bPhka˵r\KTwo_ahΩ=sI4ÿ5 VY!'BI+43sN?뫥_?ޡZoQkf_ŃZY>4]w; 7i|eaTkg;PT;BI$QSslnL;K$<o,6[ ڶZ_;Okuo@?V>ڻ4pqߛ8e~{kvR_gt(BGINic?~J_jکf{17e3_plmZC/jeY.quVh={ffS9_\< c45;f.sZ]>:Qi::U "~Ous꘩*9% 5[lnWCIY12Hd!\evK^Y&,5"mLk'Vi9E_Fs^fQCozjǸ,%ISԞ=OβVimNn3`hj1n^'P}^z:%ih̫ZPpE$yӈ^z9dԯ9^Go6ߪDN;%J;kZ[;ڢY_Rn9J3ư9d[w8>E~1 53wTkvo/MK; ,CnߣߵE;}%Vh(it 6o4g󟯂z*Y75q[z6t!i97sjRq,17ZJHcy C3Xnˬ>=[#dG;)ik^!#dp8XBOG}ǧaI5?\.yy2ʒFICw? PVɺ[ Yas5I$-%Y3D8߶jM3'C*4gOz2iiY?X!'o$7>ٙ< ~2O?иx~D1P:lw_W̲࿰ |$e}7Y~\&! ;ª)ӉP  #'C0Mf?3LG8ف N,qɍ= #࿰ |$e}7Y~\&! ;*?~hӄf2\?,>D<+He/R2 (*Sw3j2^Y-] (ˁ, 5 N,ۖSm࿰ aj%? Wg{eveᜓEߡ/mV3AskdgC 5 N,ۖ{_RP{?O'~7  v?!9"`bʹ6~V >Вw,, j GGп wi~Yfm߶(| %XXP W?3!=Tk{؆ Q@5C&ߥevcc9l<6 o3GߧlK{mgv5C/[mfmEOaA^c7?~+C _s^;uL'_MO\} ij࿴o  5nlY-[;22%s-<&C0S1 M]2dCgf qC¿Խ` XS g-i߶53d2dEfm߶($(NdEߡ/6fu]  Gx"o6o[v|i@Nߣ7.i߶5#p{-H%_;T7KJ+ZFyfm2_6wq~tÿP9=ӀH%_0\o~2,5$qwK{: =?/*'TG[J#J{!%o)]B27i?~?j~ o3AbCGiy*C5_mm̀I?ϭ{!P9I=j3Y;&[ g-Fyx1 +=_yPPsxJ Gom:ƣq*80)mNE?🝡t/p~ߣߵ"o6okQ;O4$00)mNE?🝡t/p:v5C/[mfmcZH:--[nj2^JW'KwW!1_M߻ 4yį%e ڭ|4eMm3okL?Uz^w6@&\z6 o3.wg69cP~?: |`WUyyk_o^H;ɗs8s,,eSmx w࿚ s|'_,uϧ!S a? 5IJ;_o^hϴnR,g\0N_ M.o3voп wiwk|'_,uhC5/ieu6y OPo}[nj2^ W*4l?C3I?o1_pfWhtΐUK>}cj#gqZPW`Ko-&! -6 _wm|Nn1]/I%~g%3.V *j2l~h?-G+k]3;Ef,8g(YKZ_M-n)׭ lKb] /q=VfwE2y:f]Jgz5- Ղ ࿚ p8k/w[L%G࿒ _dÿ eȭ*_o6ג+g\0Td3?5C&ߥUIqX~?: @{g7\3?H7/7jA_M?8mܨ?& ϐ Ȑ =ρ[65\>ӻOoI>\>6~Ս"?Wm[Kceg( l.#Y3/w?)*7CS {OSZÿ@v7s}Y C:WiՂ ࿚ pqCx, {;۝p?-\ɏ^u^k,`ڟ࿚ o3Z+?!9"x"W?3Ar<}Rο@I*u:o6o]A; 2;=CX.<_*@Gu=J7)g-;drځ7./Cs4_99o+WtS^ wHR&IzM Fb߸rO ?~?jMK+ː c"Tk_*nAi1A#O(>a .j 2d;CIgWe 4Ǯ:_HZSĺ9B1o\ߧ?5C&ߥeve~fmu>婴^t/Js{I z89xgf' ΁j22l.mj2$}dW?`q.HR#]}%:4=fmߺ\w ?~?jMK+! ץD3:nUG;JKBT =Zq9 𯌽;,;2ץoXNO>Moث^uyA "o>-7.x:ӿ_d#L:+c.|}ak;6 ϠHzomߺ\w ?~?jMKI3C/6 _õհ? z[+ovqp zkϠH$?WP GI N,IVOwTJe];9_ʿOSVmC#o/gf '!~ Wl'_m࿤k+?~吏)u@|o"}k`a1X }Z3Q3ovF' kIDAT]Z ŀkl'_m࿤k+?~c\ }߼SIlngO2o GPO2{ KHi=e߮$TC"i ô!5f4VZ 0 𯌽;,_M|c)$}w"R*ߧ?5CԯǖV?! / !.Jԣ댂/H_DQ\3Q2AP C&/6wqe~fKzg t́=Wo^%1t&o+ɯgwQL]nÿSu  Wl'_m࿤k+?~^R#d;J;7&u;oGP>29;3 Wl'_m࿤k+?~ub$%JJ9%CH?wGGwi~Yÿ>s9K/HkW8%J?m/OF?'C?J࿷ET'\nٽ9O_iHT(ߵŴy3oޟ4࿿`h%[1qgo~C^4yU7]4ho+ߵ_MCU1~?-G Wci_sb*[y?I}L\o+ߵ_M?jK+ːpmW/wܟ}C Gwݷv@~}J6Vrk ? V?! / !"Jכ'}]]StuW8\4TO(ߵEm?. W{eve~f JrlÄiwW4MBJ/ c5_wmw&^p'Hco 82?o+؟"rpW~/y .H[/G~\?p/pi %! >wK{lؓD_XYmgxd! _~K'_ms?J gsuxǸZcw͐R`0HIs_Ŋ@]_j%믓I?{g(\ wV-?o]3d;2?o6o[)KP{5}U_zo|zC$Ƀh'B C'A q~v5C/6Ksrc?(Cf%_#MtsCEU.l$7 纆}2ghFw9[[V7ڇ~4JT=_yrO+?~c\"QźGw_@Wiw&k? gv5C/6 o3}J{+Q/՛~O6Re zAff_4j2^?heȬ3ֺ?|3ohl.lis)%Ir [V2\?,fve~fm߶\?&Dt>\/]Л/JGI?-;.e*ou~Yw͐0nmw7H7w}<=O>`$ w~U5~^JA~R o+]~7  v?!9  ŋ my"u~pk".?sڕJҫ%J^N[JW7ځ NlY- SIK$_^]w X+M~ʛK?IcIa) cf{e(]G v?! %] iD$$-Yu&@w%W&iN$WtR]HIyJt^j6|hgȬK{oWʐ L'_MܙeIGG4=4RO$icZ֦1~8)IENDB`android-file-transfer-linux-4.5/osx/android-file-transfer.svg000066400000000000000000000342771501120463400244200ustar00rootroot00000000000000 image/svg+xml android-file-transfer-linux-4.5/osx/icns.sh000066400000000000000000000010331501120463400207700ustar00rootroot00000000000000export PROJECT=android-file-transfer export ICONDIR=./$PROJECT.iconset export ORIGICON=./android-file-transfer.png mkdir $ICONDIR # Normal screen icons for SIZE in 16 32 64 128 256 512; do sips -z $SIZE $SIZE $ORIGICON --out $ICONDIR/icon_${SIZE}x${SIZE}.png ; done # Retina display icons for SIZE in 32 64 256 512 1024; do sips -z $SIZE $SIZE $ORIGICON --out $ICONDIR/icon_$(expr $SIZE / 2)x$(expr $SIZE / 2)@2x.png ; done # Make a multi-resolution Icon iconutil -c icns -o ./$PROJECT.icns $ICONDIR rm -rf $ICONDIR #it is useless now android-file-transfer-linux-4.5/python/000077500000000000000000000000001501120463400202135ustar00rootroot00000000000000android-file-transfer-linux-4.5/python/CMakeLists.txt000066400000000000000000000003371501120463400227560ustar00rootroot00000000000000message(STATUS ${CMAKE_CURRENT_SOURCE_DIR}) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}) include(pybind11Tools) pybind11_add_module(aftl aftl.cpp) target_link_libraries(aftl PRIVATE ${LIB_NAME}) android-file-transfer-linux-4.5/python/FindPythonLibsNew.cmake000066400000000000000000000256661501120463400246020ustar00rootroot00000000000000# - Find python libraries # This module finds the libraries corresponding to the Python interpreter # FindPythonInterp provides. # This code sets the following variables: # # PYTHONLIBS_FOUND - have the Python libs been found # PYTHON_PREFIX - path to the Python installation # PYTHON_LIBRARIES - path to the python library # PYTHON_INCLUDE_DIRS - path to where Python.h is found # PYTHON_MODULE_EXTENSION - lib extension, e.g. '.so' or '.pyd' # PYTHON_MODULE_PREFIX - lib name prefix: usually an empty string # PYTHON_SITE_PACKAGES - path to installation site-packages # PYTHON_IS_DEBUG - whether the Python interpreter is a debug build # # Thanks to talljimbo for the patch adding the 'LDVERSION' config # variable usage. #============================================================================= # Copyright 2001-2009 Kitware, Inc. # Copyright 2012 Continuum Analytics, Inc. # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # * Neither the names of Kitware, Inc., the Insight Software Consortium, # nor the names of their contributors may be used to endorse or promote # products derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #============================================================================= # Checking for the extension makes sure that `LibsNew` was found and not just `Libs`. if(PYTHONLIBS_FOUND AND PYTHON_MODULE_EXTENSION) return() endif() if(PythonLibsNew_FIND_QUIETLY) set(_pythonlibs_quiet QUIET) else() set(_pythonlibs_quiet "") endif() if(PythonLibsNew_FIND_REQUIRED) set(_pythonlibs_required REQUIRED) endif() # Check to see if the `python` command is present and from a virtual # environment, conda, or GHA activation - if it is, try to use that. if(NOT DEFINED PYTHON_EXECUTABLE) if(DEFINED ENV{VIRTUAL_ENV}) find_program( PYTHON_EXECUTABLE python PATHS "$ENV{VIRTUAL_ENV}" "$ENV{VIRTUAL_ENV}/bin" NO_DEFAULT_PATH) elseif(DEFINED ENV{CONDA_PREFIX}) find_program( PYTHON_EXECUTABLE python PATHS "$ENV{CONDA_PREFIX}" "$ENV{CONDA_PREFIX}/bin" NO_DEFAULT_PATH) elseif(DEFINED ENV{pythonLocation}) find_program( PYTHON_EXECUTABLE python PATHS "$ENV{pythonLocation}" "$ENV{pythonLocation}/bin" NO_DEFAULT_PATH) endif() if(NOT PYTHON_EXECUTABLE) unset(PYTHON_EXECUTABLE) endif() endif() # Use the Python interpreter to find the libs. if(NOT PythonLibsNew_FIND_VERSION) set(PythonLibsNew_FIND_VERSION "3.6") endif() find_package(PythonInterp ${PythonLibsNew_FIND_VERSION} ${_pythonlibs_required} ${_pythonlibs_quiet}) if(NOT PYTHONINTERP_FOUND) set(PYTHONLIBS_FOUND FALSE) set(PythonLibsNew_FOUND FALSE) return() endif() # According to https://stackoverflow.com/questions/646518/python-how-to-detect-debug-interpreter # testing whether sys has the gettotalrefcount function is a reliable, cross-platform # way to detect a CPython debug interpreter. # # The library suffix is from the config var LDVERSION sometimes, otherwise # VERSION. VERSION will typically be like "2.7" on unix, and "27" on windows. execute_process( COMMAND "${PYTHON_EXECUTABLE}" "-c" " import sys;import struct; import sysconfig as s USE_SYSCONFIG = sys.version_info >= (3, 10) if not USE_SYSCONFIG: from distutils import sysconfig as ds print('.'.join(str(v) for v in sys.version_info)); print(sys.prefix); if USE_SYSCONFIG: scheme = s.get_default_scheme() if scheme == 'posix_local': # Debian's default scheme installs to /usr/local/ but we want to find headers in /usr/ scheme = 'posix_prefix' print(s.get_path('platinclude', scheme)) print(s.get_path('platlib')) print(s.get_config_var('EXT_SUFFIX') or s.get_config_var('SO')) else: print(ds.get_python_inc(plat_specific=True)); print(ds.get_python_lib(plat_specific=True)); print(ds.get_config_var('EXT_SUFFIX') or ds.get_config_var('SO')); print(hasattr(sys, 'gettotalrefcount')+0); print(struct.calcsize('@P')); print(s.get_config_var('LDVERSION') or s.get_config_var('VERSION')); print(s.get_config_var('LIBDIR') or ''); print(s.get_config_var('MULTIARCH') or ''); " RESULT_VARIABLE _PYTHON_SUCCESS OUTPUT_VARIABLE _PYTHON_VALUES ERROR_VARIABLE _PYTHON_ERROR_VALUE) if(NOT _PYTHON_SUCCESS MATCHES 0) if(PythonLibsNew_FIND_REQUIRED) message(FATAL_ERROR "Python config failure:\n${_PYTHON_ERROR_VALUE}") endif() set(PYTHONLIBS_FOUND FALSE) set(PythonLibsNew_FOUND FALSE) return() endif() option( PYBIND11_PYTHONLIBS_OVERWRITE "Overwrite cached values read from Python library (classic search). Turn off if cross-compiling and manually setting these values." ON) # Can manually set values when cross-compiling macro(_PYBIND11_GET_IF_UNDEF lst index name) if(PYBIND11_PYTHONLIBS_OVERWRITE OR NOT DEFINED "${name}") list(GET "${lst}" "${index}" "${name}") endif() endmacro() # Convert the process output into a list if(WIN32) string(REGEX REPLACE "\\\\" "/" _PYTHON_VALUES ${_PYTHON_VALUES}) endif() string(REGEX REPLACE ";" "\\\\;" _PYTHON_VALUES ${_PYTHON_VALUES}) string(REGEX REPLACE "\n" ";" _PYTHON_VALUES ${_PYTHON_VALUES}) _pybind11_get_if_undef(_PYTHON_VALUES 0 _PYTHON_VERSION_LIST) _pybind11_get_if_undef(_PYTHON_VALUES 1 PYTHON_PREFIX) _pybind11_get_if_undef(_PYTHON_VALUES 2 PYTHON_INCLUDE_DIR) _pybind11_get_if_undef(_PYTHON_VALUES 3 PYTHON_SITE_PACKAGES) _pybind11_get_if_undef(_PYTHON_VALUES 4 PYTHON_MODULE_EXTENSION) _pybind11_get_if_undef(_PYTHON_VALUES 5 PYTHON_IS_DEBUG) _pybind11_get_if_undef(_PYTHON_VALUES 6 PYTHON_SIZEOF_VOID_P) _pybind11_get_if_undef(_PYTHON_VALUES 7 PYTHON_LIBRARY_SUFFIX) _pybind11_get_if_undef(_PYTHON_VALUES 8 PYTHON_LIBDIR) _pybind11_get_if_undef(_PYTHON_VALUES 9 PYTHON_MULTIARCH) # Make sure the Python has the same pointer-size as the chosen compiler # Skip if CMAKE_SIZEOF_VOID_P is not defined # This should be skipped for (non-Apple) cross-compiles (like EMSCRIPTEN) if(NOT CMAKE_CROSSCOMPILING AND CMAKE_SIZEOF_VOID_P AND (NOT "${PYTHON_SIZEOF_VOID_P}" STREQUAL "${CMAKE_SIZEOF_VOID_P}")) if(PythonLibsNew_FIND_REQUIRED) math(EXPR _PYTHON_BITS "${PYTHON_SIZEOF_VOID_P} * 8") math(EXPR _CMAKE_BITS "${CMAKE_SIZEOF_VOID_P} * 8") message(FATAL_ERROR "Python config failure: Python is ${_PYTHON_BITS}-bit, " "chosen compiler is ${_CMAKE_BITS}-bit") endif() set(PYTHONLIBS_FOUND FALSE) set(PythonLibsNew_FOUND FALSE) return() endif() # The built-in FindPython didn't always give the version numbers string(REGEX REPLACE "\\." ";" _PYTHON_VERSION_LIST ${_PYTHON_VERSION_LIST}) list(GET _PYTHON_VERSION_LIST 0 PYTHON_VERSION_MAJOR) list(GET _PYTHON_VERSION_LIST 1 PYTHON_VERSION_MINOR) list(GET _PYTHON_VERSION_LIST 2 PYTHON_VERSION_PATCH) set(PYTHON_VERSION "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}.${PYTHON_VERSION_PATCH}") # Make sure all directory separators are '/' string(REGEX REPLACE "\\\\" "/" PYTHON_PREFIX "${PYTHON_PREFIX}") string(REGEX REPLACE "\\\\" "/" PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}") string(REGEX REPLACE "\\\\" "/" PYTHON_SITE_PACKAGES "${PYTHON_SITE_PACKAGES}") if(DEFINED PYTHON_LIBRARY) # Don't write to PYTHON_LIBRARY if it's already set elseif(CMAKE_HOST_WIN32) set(PYTHON_LIBRARY "${PYTHON_PREFIX}/libs/python${PYTHON_LIBRARY_SUFFIX}.lib") # when run in a venv, PYTHON_PREFIX points to it. But the libraries remain in the # original python installation. They may be found relative to PYTHON_INCLUDE_DIR. if(NOT EXISTS "${PYTHON_LIBRARY}") get_filename_component(_PYTHON_ROOT ${PYTHON_INCLUDE_DIR} DIRECTORY) set(PYTHON_LIBRARY "${_PYTHON_ROOT}/libs/python${PYTHON_LIBRARY_SUFFIX}.lib") endif() # if we are in MSYS & MINGW, and we didn't find windows python lib, look for system python lib if(DEFINED ENV{MSYSTEM} AND MINGW AND NOT EXISTS "${PYTHON_LIBRARY}") if(PYTHON_MULTIARCH) set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}/${PYTHON_MULTIARCH}" "${PYTHON_LIBDIR}") else() set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}") endif() unset(PYTHON_LIBRARY) find_library( PYTHON_LIBRARY NAMES "python${PYTHON_LIBRARY_SUFFIX}" PATHS ${_PYTHON_LIBS_SEARCH} NO_DEFAULT_PATH) endif() # raise an error if the python libs are still not found. if(NOT EXISTS "${PYTHON_LIBRARY}") message(FATAL_ERROR "Python libraries not found") endif() else() if(PYTHON_MULTIARCH) set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}/${PYTHON_MULTIARCH}" "${PYTHON_LIBDIR}") else() set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}") endif() #message(STATUS "Searching for Python libs in ${_PYTHON_LIBS_SEARCH}") # Probably this needs to be more involved. It would be nice if the config # information the python interpreter itself gave us were more complete. find_library( PYTHON_LIBRARY NAMES "python${PYTHON_LIBRARY_SUFFIX}" PATHS ${_PYTHON_LIBS_SEARCH} NO_DEFAULT_PATH) # If all else fails, just set the name/version and let the linker figure out the path. if(NOT PYTHON_LIBRARY) set(PYTHON_LIBRARY python${PYTHON_LIBRARY_SUFFIX}) endif() endif() mark_as_advanced(PYTHON_LIBRARY PYTHON_INCLUDE_DIR) # We use PYTHON_INCLUDE_DIR, PYTHON_LIBRARY and PYTHON_DEBUG_LIBRARY for the # cache entries because they are meant to specify the location of a single # library. We now set the variables listed by the documentation for this # module. set(PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIR}") set(PYTHON_LIBRARIES "${PYTHON_LIBRARY}") if(NOT PYTHON_DEBUG_LIBRARY) set(PYTHON_DEBUG_LIBRARY "") endif() set(PYTHON_DEBUG_LIBRARIES "${PYTHON_DEBUG_LIBRARY}") find_package_message(PYTHON "Found PythonLibs: ${PYTHON_LIBRARIES}" "${PYTHON_EXECUTABLE}${PYTHON_VERSION_STRING}") set(PYTHONLIBS_FOUND TRUE) set(PythonLibsNew_FOUND TRUE) if(NOT PYTHON_MODULE_PREFIX) set(PYTHON_MODULE_PREFIX "") endif() android-file-transfer-linux-4.5/python/aftl.cpp000066400000000000000000000303011501120463400216420ustar00rootroot00000000000000#include #include #include #include #include #include #include #include namespace py = pybind11; using namespace mtp; using system_clock = std::chrono::system_clock; namespace pybind11 { namespace detail { template <> struct type_caster { public: PYBIND11_TYPE_CASTER(ByteArray, _("ByteArray")); bool load(handle src, bool) { PyObject *source = src.ptr(); if (!PyByteArray_Check(source)) return false; auto size = PyByteArray_Size(source); u8 * ptr = reinterpret_cast(PyByteArray_AsString(source)); if (ptr && size) value.assign(ptr, ptr + size); else value.clear(); return !PyErr_Occurred(); } static handle cast(const ByteArray & src, return_value_policy /* policy */, handle /* parent */) { return PyByteArray_FromStringAndSize(reinterpret_cast(src.data()), src.size()); } }; }} // namespace pybind11::detail namespace { struct PythonInputStream final : public IObjectInputStream { py::object ReadMethod; u64 Size; bool Cancelled = false; PythonInputStream(py::object object, u64 size): ReadMethod(object.attr("read")), Size(size) { } void Cancel() override { Cancelled = true; } u64 GetSize() const override { return Size; } size_t Read(u8 *data, size_t maxSize) override { if (Cancelled) return 0; py::bytes value = ReadMethod(maxSize); char *buffer; ssize_t size; if (PYBIND11_BYTES_AS_STRING_AND_SIZE(value.ptr(), &buffer, &size) || !buffer || size > maxSize) py::pybind11_fail("Unable to extract bytes contents!"); memcpy(data, buffer, size); return PyErr_Occurred()? 0: size; } }; struct PythonOutputStream final : public IObjectOutputStream { py::object WriteMethod; bool Cancelled = false; PythonOutputStream(py::object object): WriteMethod(object.attr("write")) { } void Cancel() override { Cancelled = true; } size_t Write(const u8* data, size_t size) override { if (Cancelled) return 0; py::bytes payload(reinterpret_cast(data), size); WriteMethod(payload).cast(); return PyErr_Occurred()? 0: size; } }; } static void EnableDebug(bool enable) { g_debug = enable; } PYBIND11_MODULE(aftl, m) { m.doc() = "Android File Transfer for Linux python bindings"; m.def("debug", &EnableDebug, "Enables logs from MTP library"); py::class_ session(m, "Session"); py::class_ objectId(m, "ObjectId"); #define VALUE(TYPE, NAME) .value(#NAME, TYPE :: NAME) #define DEF_READONLY(TYPE, NAME) .def_readonly(#NAME, & TYPE :: NAME ) py::enum_(m, "ObjectFormat", "MTP Object format for querying specific types of media, or Any") #define ENUM_VALUE(NAME, _) VALUE(ObjectFormat, NAME) # include #undef ENUM_VALUE ; py::enum_(m, "ObjectProperty", "MTP object property") #define ENUM_VALUE(NAME, _) VALUE(ObjectProperty, NAME) # include #undef ENUM_VALUE ; py::enum_(m, "DeviceProperty", "MTP device property") #define ENUM_VALUE(NAME, _) VALUE(DeviceProperty, NAME) # include #undef ENUM_VALUE ; py::enum_(m, "AssociationType", "MTP Association Type") VALUE(AssociationType, GenericFolder) VALUE(AssociationType, Album) VALUE(AssociationType, TimeSequence) VALUE(AssociationType, HorizontalPanoramic) VALUE(AssociationType, VerticalPanoramic) VALUE(AssociationType, Panoramic2D) VALUE(AssociationType, AncillaryData) ; py::enum_(m, "OperationCode", "MTP Operation Code") #define ENUM_VALUE(NAME, _) VALUE(OperationCode, NAME) # include #undef ENUM_VALUE ; py::class_(m, "DeviceDescriptor"). def_property_readonly("vendor_id", &usb::DeviceDescriptor::GetVendorId). def_property_readonly("product_id", &usb::DeviceDescriptor::GetProductId). def("__repr__", [](const usb::DeviceDescriptor f) -> std::string { return "DeviceDescriptor(" + std::to_string(f.GetVendorId()) + ":" + std::to_string(f.GetProductId()) + ")"; }) ; ; py::class_(m, "UsbContext"). def(py::init<>()). def("get_device_descriptors", &usb::Context::GetDevices) ; py::class_(m, "Device"). def_static("find_first", static_cast(&Device::FindFirst), py::arg("filter_device") = std::string(), py::arg("claim_interface") = true, py::arg("reset_device") = false). def_static("open", &Device::Open, py::arg("context"), py::arg("device_descriptor"), py::arg("claim_interface") = true, py::arg("reset_device") = false). def("open_session", &Device::OpenSession, py::arg("session_id") = 1, py::arg("timeout") = static_cast(Session::DefaultTimeout)) ; py::class_(m, "StorageId"). def("__repr__", [](const StorageId &id) { return "StorageId(" + std::to_string(id.Id) + ")"; }); objectId. def("__repr__", [](const ObjectId &id) { return "ObjectId(" + std::to_string(id.Id) + ")"; }); py::class_(m, "NewObjectInfo", "information about newly created object"). def_readonly("storage_id", &msg::NewObjectInfo::StorageId). def_readonly("parent_object_id", &msg::NewObjectInfo::ParentObjectId). def_readonly("object_id", &msg::NewObjectInfo::ObjectId) ; py::class_(m, "ObjectInfo"). def(py::init<>()) DEF_READONLY(msg::ObjectInfo, ObjectFormat) DEF_READONLY(msg::ObjectInfo, ProtectionStatus) DEF_READONLY(msg::ObjectInfo, ObjectCompressedSize) DEF_READONLY(msg::ObjectInfo, ThumbFormat) DEF_READONLY(msg::ObjectInfo, ThumbCompressedSize) DEF_READONLY(msg::ObjectInfo, ThumbPixWidth) DEF_READONLY(msg::ObjectInfo, ThumbPixHeight) DEF_READONLY(msg::ObjectInfo, ImagePixWidth) DEF_READONLY(msg::ObjectInfo, ImagePixHeight) DEF_READONLY(msg::ObjectInfo, ImageBitDepth) DEF_READONLY(msg::ObjectInfo, ParentObject) DEF_READONLY(msg::ObjectInfo, AssociationType) DEF_READONLY(msg::ObjectInfo, AssociationDesc) DEF_READONLY(msg::ObjectInfo, SequenceNumber) DEF_READONLY(msg::ObjectInfo, Filename) DEF_READONLY(msg::ObjectInfo, CaptureDate) DEF_READONLY(msg::ObjectInfo, ModificationDate) DEF_READONLY(msg::ObjectInfo, Keywords) ; py::class_(m, "StorageInfo") DEF_READONLY(msg::StorageInfo, StorageType) DEF_READONLY(msg::StorageInfo, FilesystemType) DEF_READONLY(msg::StorageInfo, AccessCapability) DEF_READONLY(msg::StorageInfo, MaxCapacity) DEF_READONLY(msg::StorageInfo, FreeSpaceInBytes) DEF_READONLY(msg::StorageInfo, FreeSpaceInImages) DEF_READONLY(msg::StorageInfo, StorageDescription) DEF_READONLY(msg::StorageInfo, VolumeLabel) ; py::class_(m, "DeviceInfo") DEF_READONLY(msg::DeviceInfo, StandardVersion) DEF_READONLY(msg::DeviceInfo, VendorExtensionId) DEF_READONLY(msg::DeviceInfo, VendorExtensionVersion) DEF_READONLY(msg::DeviceInfo, VendorExtensionDesc) DEF_READONLY(msg::DeviceInfo, FunctionalMode) DEF_READONLY(msg::DeviceInfo, OperationsSupported) DEF_READONLY(msg::DeviceInfo, EventsSupported) DEF_READONLY(msg::DeviceInfo, DevicePropertiesSupported) DEF_READONLY(msg::DeviceInfo, CaptureFormats) DEF_READONLY(msg::DeviceInfo, ImageFormats) DEF_READONLY(msg::DeviceInfo, Manufacturer) DEF_READONLY(msg::DeviceInfo, Model) DEF_READONLY(msg::DeviceInfo, DeviceVersion) DEF_READONLY(msg::DeviceInfo, SerialNumber) ; py::class_(m, "ObjectEditSession"). def("truncate", &Session::ObjectEditSession::Truncate). def("send", &Session::ObjectEditSession::Send) ; session. // def_readonly_static("DefaultTimeout", &Session::DefaultTimeout). // def_readonly_static("LongTimeout", &Session::LongTimeout). def_readonly_static("AllStorages", &Session::AllStorages). def_readonly_static("AnyStorage", &Session::AnyStorage). def_readonly_static("Device", &Session::Device). def_readonly_static("Root", &Session::Root). def("get_storage_ids", [](Session * self) -> std::vector { std::vector result; auto sids = self->GetStorageIDs(); result.reserve(sids.StorageIDs.size()); for(auto & sid: sids.StorageIDs) { result.push_back(sid); } return result; }). def("get_storage_info", &Session::GetStorageInfo). def("get_object_handles", [](Session *self, StorageId storageId, ObjectFormat objectFormat, ObjectId parent, int timeout) -> std::vector { std::vector result; auto objects = self->GetObjectHandles(storageId, objectFormat, parent, timeout); result.reserve(objects.ObjectHandles.size()); for(auto & oid: objects.ObjectHandles) { result.push_back(oid); } return result; }, py::arg("storageId"), py::arg("objectFormat") = ObjectFormat::Any, py::arg("parent") = Session::Root, py::arg("timeout") = static_cast(Session::LongTimeout)). def("get_object_info", &Session::GetObjectInfo). def("get_object_properties_supported", [](Session *self, ObjectFormat format) -> std::vector { std::vector result; auto props = self->GetObjectPropertiesSupported(format); result.reserve(props.ObjectPropertyCodes.size()); for(auto prop: props.ObjectPropertyCodes) { result.push_back(prop); } return result; }). def("get_object", [](Session * self, ObjectId object, py::object stream) { self->GetObject(object, std::make_shared(stream)); }). def("get_thumb", [](Session * self, ObjectId object, py::object stream) { self->GetThumb(object, std::make_shared(stream)); }). def("send_object", [](Session * self, py::object stream, long size, int timeout) { self->SendObject(std::make_shared(stream, size), timeout); }, py::arg("stream"), py::arg("size"), py::arg("timeout") = static_cast(Session::LongTimeout)). def("get_object_property", &Session::GetObjectProperty). def("get_object_string_property", &Session::GetObjectStringProperty). def("get_object_integer_property", &Session::GetObjectIntegerProperty). def("set_object_property", (void (Session::*)(ObjectId, ObjectProperty, u64)) &Session::SetObjectProperty). def("set_object_property", (void (Session::*)(ObjectId, ObjectProperty, const std::string &)) &Session::SetObjectProperty). def("set_object_property", (void (Session::*)(ObjectId, ObjectProperty, const ByteArray &)) &Session::SetObjectProperty). def("get_object_storage", &Session::GetObjectStorage). def("get_object_parent", &Session::GetObjectParent). def("delete_object", &Session::DeleteObject). def("edit_object_supported", &Session::EditObjectSupported). def("get_object_property_list_supported", &Session::GetObjectPropertyListSupported). def("get_object_property_list", &Session::GetObjectPropertyList). def("get_object_modification_time", [](Session * self, ObjectId objectId) -> std::chrono::system_clock::time_point { time_t t = self->GetObjectModificationTime(objectId); return system_clock::from_time_t(t); }). def("get_partial_object", &Session::GetPartialObject). def("edit_object", &Session::EditObject). def("create_directory", &Session::CreateDirectory, py::arg("name"), py::arg("parent"), py::arg("storage") = Session::AnyStorage, py::arg("association_type") = AssociationType::GenericFolder). def("send_object_info", &Session::SendObjectInfo, py::arg("object_info"), py::arg("storage"), py::arg("parent")). def("get_device_info", static_cast(&Session::GetDeviceInfo)). def("get_device_property", &Session::GetDeviceProperty). def("get_device_integer_property", &Session::GetDeviceIntegerProperty). def("get_device_string_property", &Session::GetDeviceStringProperty). def("set_device_property", (void (Session::*)(DeviceProperty, const std::string &)) &Session::SetDeviceProperty). def("set_device_property", (void (Session::*)(DeviceProperty, const ByteArray &)) &Session::SetDeviceProperty). def("abort_current_transaction", &Session::AbortCurrentTransaction, py::arg("timeout") = static_cast(Session::DefaultTimeout)) ; ; } android-file-transfer-linux-4.5/python/example/000077500000000000000000000000001501120463400216465ustar00rootroot00000000000000android-file-transfer-linux-4.5/python/example/menu.py000077500000000000000000000044761501120463400232020ustar00rootroot00000000000000#!/usr/bin/python3 # -*- coding: utf-8 -*- """ hierarchical prompt usage example """ from __future__ import print_function, unicode_literals from PyInquirer import style_from_dict, Token, prompt, Separator import aftl def get_storage_prompt(session, s): info = session.get_storage_info(s) name = info.StorageDescription if not name: name = repr(s) return { 'name': name, 'value': s } def get_storage(session): storage_prompt = { 'type': 'list', 'name': 'storage', 'message': 'Select storage', 'choices': list(map(lambda s: get_storage_prompt(session, s), session.get_storage_ids())) + [{'name': 'Quit', 'value': None}] } answers = prompt(storage_prompt) return answers.get('storage') def get_object_prompt(session, object): info = session.get_object_info(object) name = info.Filename if info.ObjectFormat == aftl.ObjectFormat.Association: name += '/' return {'name': name, 'value': (object, info.ObjectFormat) } def get_object(session, storage, parent): objects = session.get_object_handles(storage, aftl.ObjectFormat.Any, parent) object_prompt = { 'type': 'list', 'name': 'object', 'message': 'Browse objects', 'choices': [{'name': '..', 'value': (None, None) }] + \ list(map(lambda o: get_object_prompt(session, o), objects)) } answers = prompt(object_prompt) return answers.get('object') def get_object_action(session): action_prompt = { 'type': 'expand', 'name': 'action', 'message': 'Object Action (press h for help)', 'choices': [ { 'key': 'b', 'name': 'Back', 'value': 'back' }, Separator(), { 'key': 'd', 'name': 'Download', 'value': 'download' }, ] } return prompt(action_prompt).get('action') def main(): session = aftl.Device.find_first().open_session() while True: storage = get_storage(session) if storage is None: break path = [aftl.Session.Root] while path: object, type = get_object(session, storage, path[-1]) if type == aftl.ObjectFormat.Association: path.append(object) continue if object is None: path.pop() continue while True: action = get_object_action(session) if action == 'back': break if action == 'download': name = session.get_object_string_property(object, aftl.ObjectProperty.ObjectFilename) with open(name, "wb") as f: session.get_object(object, f) if __name__ == '__main__': main() android-file-transfer-linux-4.5/python/example/requirements.txt000066400000000000000000000000131501120463400251240ustar00rootroot00000000000000pyinquirer android-file-transfer-linux-4.5/python/pybind11Tools.cmake000066400000000000000000000202511501120463400236650ustar00rootroot00000000000000# tools/pybind11Tools.cmake -- Build system for the pybind11 modules # # Copyright (c) 2020 Wenzel Jakob # # All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. # include_guard(global) (pre-CMake 3.10) if(TARGET pybind11::python_headers) return() endif() # Built-in in CMake 3.5+ include(CMakeParseArguments) if(pybind11_FIND_QUIETLY) set(_pybind11_quiet QUIET) else() set(_pybind11_quiet "") endif() # If this is the first run, PYTHON_VERSION can stand in for PYBIND11_PYTHON_VERSION if(NOT DEFINED PYBIND11_PYTHON_VERSION AND DEFINED PYTHON_VERSION) message(WARNING "Set PYBIND11_PYTHON_VERSION to search for a specific version, not " "PYTHON_VERSION (which is an output). Assuming that is what you " "meant to do and continuing anyway.") set(PYBIND11_PYTHON_VERSION "${PYTHON_VERSION}" CACHE STRING "Python version to use for compiling modules") unset(PYTHON_VERSION) unset(PYTHON_VERSION CACHE) elseif(DEFINED PYBIND11_PYTHON_VERSION) # If this is set as a normal variable, promote it set(PYBIND11_PYTHON_VERSION "${PYBIND11_PYTHON_VERSION}" CACHE STRING "Python version to use for compiling modules") else() # Make an empty cache variable. set(PYBIND11_PYTHON_VERSION "" CACHE STRING "Python version to use for compiling modules") endif() # A user can set versions manually too set(Python_ADDITIONAL_VERSIONS "3.11;3.10;3.9;3.8;3.7;3.6" CACHE INTERNAL "") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} MODULE REQUIRED ${_pybind11_quiet}) list(REMOVE_AT CMAKE_MODULE_PATH -1) # Makes a normal variable a cached variable macro(_PYBIND11_PROMOTE_TO_CACHE NAME) set(_tmp_ptc "${${NAME}}") # CMake 3.21 complains if a cached variable is shadowed by a normal one unset(${NAME}) set(${NAME} "${_tmp_ptc}" CACHE INTERNAL "") endmacro() # Cache variables so pybind11_add_module can be used in parent projects _pybind11_promote_to_cache(PYTHON_INCLUDE_DIRS) _pybind11_promote_to_cache(PYTHON_LIBRARIES) _pybind11_promote_to_cache(PYTHON_MODULE_PREFIX) _pybind11_promote_to_cache(PYTHON_MODULE_EXTENSION) _pybind11_promote_to_cache(PYTHON_VERSION_MAJOR) _pybind11_promote_to_cache(PYTHON_VERSION_MINOR) _pybind11_promote_to_cache(PYTHON_VERSION) _pybind11_promote_to_cache(PYTHON_IS_DEBUG) if(PYBIND11_MASTER_PROJECT) if(PYTHON_MODULE_EXTENSION MATCHES "pypy") if(NOT DEFINED PYPY_VERSION) execute_process( COMMAND ${PYTHON_EXECUTABLE} -c [=[import sys; sys.stdout.write(".".join(map(str, sys.pypy_version_info[:3])))]=] OUTPUT_VARIABLE pypy_version) set(PYPY_VERSION ${pypy_version} CACHE INTERNAL "") endif() message(STATUS "PYPY ${PYPY_VERSION} (Py ${PYTHON_VERSION})") else() message(STATUS "PYTHON ${PYTHON_VERSION}") endif() endif() # Only add Python for build - must be added during the import for config since # it has to be re-discovered. # # This needs to be an target to it is included after the local pybind11 # directory, just in case there are multiple versions of pybind11, we want the # one we expect. add_library(pybind11::python_headers INTERFACE IMPORTED) set_property(TARGET pybind11::python_headers PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$") set_property( TARGET pybind11::pybind11 APPEND PROPERTY INTERFACE_LINK_LIBRARIES pybind11::python_headers) set(pybind11_INCLUDE_DIRS "${pybind11_INCLUDE_DIR}" "${PYTHON_INCLUDE_DIRS}" CACHE INTERNAL "Directories where pybind11 and possibly Python headers are located") # Python debug libraries expose slightly different objects before 3.8 # https://docs.python.org/3.6/c-api/intro.html#debugging-builds # https://stackoverflow.com/questions/39161202/how-to-work-around-missing-pymodule-create2-in-amd64-win-python35-d-lib if(PYTHON_IS_DEBUG) set_property( TARGET pybind11::pybind11 APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS Py_DEBUG) endif() # The <3.11 code here does not support release/debug builds at the same time, like on vcpkg if(CMAKE_VERSION VERSION_LESS 3.11) set_property( TARGET pybind11::module APPEND PROPERTY INTERFACE_LINK_LIBRARIES pybind11::python_link_helper "$<$,$>:$>" ) set_property( TARGET pybind11::embed APPEND PROPERTY INTERFACE_LINK_LIBRARIES pybind11::pybind11 $) else() # The IMPORTED INTERFACE library here is to ensure that "debug" and "release" get processed outside # of a generator expression - https://gitlab.kitware.com/cmake/cmake/-/issues/18424, as they are # target_link_library keywords rather than real libraries. add_library(pybind11::_ClassicPythonLibraries IMPORTED INTERFACE) target_link_libraries(pybind11::_ClassicPythonLibraries INTERFACE ${PYTHON_LIBRARIES}) target_link_libraries( pybind11::module INTERFACE pybind11::python_link_helper "$<$,$>:pybind11::_ClassicPythonLibraries>") target_link_libraries(pybind11::embed INTERFACE pybind11::pybind11 pybind11::_ClassicPythonLibraries) endif() function(pybind11_extension name) # The prefix and extension are provided by FindPythonLibsNew.cmake set_target_properties(${name} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" SUFFIX "${PYTHON_MODULE_EXTENSION}") endfunction() # Build a Python extension module: # pybind11_add_module( [MODULE | SHARED] [EXCLUDE_FROM_ALL] # [NO_EXTRAS] [THIN_LTO] [OPT_SIZE] source1 [source2 ...]) # function(pybind11_add_module target_name) set(options "MODULE;SHARED;EXCLUDE_FROM_ALL;NO_EXTRAS;SYSTEM;THIN_LTO;OPT_SIZE") cmake_parse_arguments(ARG "${options}" "" "" ${ARGN}) if(ARG_MODULE AND ARG_SHARED) message(FATAL_ERROR "Can't be both MODULE and SHARED") elseif(ARG_SHARED) set(lib_type SHARED) else() set(lib_type MODULE) endif() if(ARG_EXCLUDE_FROM_ALL) set(exclude_from_all EXCLUDE_FROM_ALL) else() set(exclude_from_all "") endif() add_library(${target_name} ${lib_type} ${exclude_from_all} ${ARG_UNPARSED_ARGUMENTS}) target_link_libraries(${target_name} PRIVATE pybind11::module) if(ARG_SYSTEM) message( STATUS "Warning: this does not have an effect - use NO_SYSTEM_FROM_IMPORTED if using imported targets" ) endif() pybind11_extension(${target_name}) # -fvisibility=hidden is required to allow multiple modules compiled against # different pybind versions to work properly, and for some features (e.g. # py::module_local). We force it on everything inside the `pybind11` # namespace; also turning it on for a pybind module compilation here avoids # potential warnings or issues from having mixed hidden/non-hidden types. if(NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET) set_target_properties(${target_name} PROPERTIES CXX_VISIBILITY_PRESET "hidden") endif() if(NOT DEFINED CMAKE_CUDA_VISIBILITY_PRESET) set_target_properties(${target_name} PROPERTIES CUDA_VISIBILITY_PRESET "hidden") endif() if(ARG_NO_EXTRAS) return() endif() if(NOT DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION) if(ARG_THIN_LTO) target_link_libraries(${target_name} PRIVATE pybind11::thin_lto) else() target_link_libraries(${target_name} PRIVATE pybind11::lto) endif() endif() # Use case-insensitive comparison to match the result of $ string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) if(NOT MSVC AND NOT "${uppercase_CMAKE_BUILD_TYPE}" MATCHES DEBUG|RELWITHDEBINFO) pybind11_strip(${target_name}) endif() if(MSVC) target_link_libraries(${target_name} PRIVATE pybind11::windows_extras) endif() if(ARG_OPT_SIZE) target_link_libraries(${target_name} PRIVATE pybind11::opt_size) endif() endfunction() # Provide general way to call common Python commands in "common" file. set(_Python PYTHON CACHE INTERNAL "" FORCE) android-file-transfer-linux-4.5/qt/000077500000000000000000000000001501120463400173165ustar00rootroot00000000000000android-file-transfer-linux-4.5/qt/.gitignore000066400000000000000000000000211501120463400212770ustar00rootroot00000000000000*.cxx_parameters android-file-transfer-linux-4.5/qt/CMakeLists.txt000066400000000000000000000162031501120463400220600ustar00rootroot00000000000000if(DESIRED_QT_VERSION) if(DESIRED_QT_VERSION MATCHES 4) find_package(Qt4 QUIET) elseif(DESIRED_QT_VERSION MATCHES 5) find_package(Qt5Widgets QUIET) find_package(Qt5Network QUIET) find_package(Qt5LinguistTools QUIET) else() find_package(Qt6Widgets QUIET) find_package(Qt6Network QUIET) find_package(Qt6LinguistTools QUIET) endif() else() find_package(Qt6Widgets QUIET) find_package(Qt6Network QUIET) find_package(Qt6LinguistTools QUIET) find_package(Qt5Widgets QUIET) find_package(Qt5Network QUIET) find_package(Qt5LinguistTools QUIET) find_package(Qt4 QUIET) endif() set(SOURCES main.cpp mainwindow.cpp fileuploader.cpp commandqueue.cpp mtpobjectsmodel.cpp mtpstoragesmodel.cpp progressdialog.cpp createdirectorydialog.cpp renamedialog.cpp devicesdialog.cpp qtobjectstream.cpp utils.cpp ) set(HEADERS mainwindow.h fileuploader.h commandqueue.h mtpobjectsmodel.h progressdialog.h createdirectorydialog.h renamedialog.h devicesdialog.h qtobjectstream.h ) set(FORMS mainwindow.ui progressdialog.ui createdirectorydialog.ui renamedialog.ui devicesdialog.ui ) set(TS_FILES translations/android-file-transfer-linux_cs.ts translations/android-file-transfer-linux_it.ts translations/android-file-transfer-linux_nl.ts translations/android-file-transfer-linux_ru.ts translations/android-file-transfer-linux_zh-CN.ts ) if (Qt6Widgets_FOUND) message(STATUS "Using Qt6") qt6_wrap_cpp(HEADERS_MOC ${HEADERS}) qt6_wrap_ui(FORMS_HEADERS ${FORMS}) qt6_add_translation(QM_FILES ${TS_FILES}) set(QM_FILES_XML "") foreach(_QM ${QM_FILES}) get_filename_component(_QM ${_QM} NAME) set(QM_FILES_XML "${QM_FILES_XML}${_QM}") endforeach() configure_file(translations/translations.qrc.in translations.qrc) qt6_add_resources(RESOURCES android-file-transfer.qrc ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc) include_directories(${Qt6Widgets_INCLUDE_DIRS} ${Qt6Network_INCLUDE_DIRS}) set(EXTRA_QT_LINK ${Qt6Widgets_LIBRARIES} ${Qt6Network_LIBRARIES}) elseif (Qt5Widgets_FOUND) message(STATUS "Using Qt5") qt5_wrap_cpp(HEADERS_MOC ${HEADERS}) qt5_wrap_ui(FORMS_HEADERS ${FORMS}) qt5_add_translation(QM_FILES ${TS_FILES}) set(QM_FILES_XML "") foreach(_QM ${QM_FILES}) get_filename_component(_QM ${_QM} NAME) set(QM_FILES_XML "${QM_FILES_XML}${_QM}") endforeach() configure_file(translations/translations.qrc.in translations.qrc) qt5_add_resources(RESOURCES android-file-transfer.qrc ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc) include_directories(${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS}) set(EXTRA_QT_LINK ${Qt5Widgets_LIBRARIES} ${Qt5Network_LIBRARIES}) elseif(Qt4_FOUND OR QT_FOUND OR QT4_FOUND) message(STATUS "Using Qt4") qt4_wrap_cpp(HEADERS_MOC ${HEADERS}) qt4_wrap_ui(FORMS_HEADERS ${FORMS}) qt4_add_translation(QM_FILES ${TS_FILES}) set(QM_FILES_XML "") foreach(_QM ${QM_FILES}) get_filename_component(_QM ${_QM} NAME) set(QM_FILES_XML "${QM_FILES_XML}${_QM}") endforeach() configure_file(translations/translations.qrc.in translations.qrc) qt4_add_resources(RESOURCES android-file-transfer.qrc ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc) include(${QT_USE_FILE}) set(EXTRA_QT_LINK Qt4::QtNetwork ${QT_LIBRARIES}) else() message(WARNING "No Qt found") set(BUILD_QT_UI FALSE) endif() if (BUILD_MTPZ) set(MTPZ_DATA_SOURCE "\"https://raw.githubusercontent.com/kbhomes/libmtp-zune/master/src/.mtpz-data\"") add_definitions(-DMTPZ_DATA_SOURCE=${MTPZ_DATA_SOURCE}) endif() if (BUILD_QT_UI) if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") string(TIMESTAMP COPYRIGHT_YEAR "%Y") set(MACOSX_BUNDLE_BUNDLE_VERSION "${AFT_BASE_VERSION}") set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${AFT_VERSION}") set(MACOSX_BUNDLE_COPYRIGHT "2015-${COPYRIGHT_YEAR} Vladimir Menshakov") set(MACOSX_BUNDLE_ICON_FILE "android-file-transfer.icns") set(MACOSX_BUNDLE_GUI_IDENTIFIER "io.github.whoozle.android-file-transfer-linux") set(MACOSX_BUNDLE_BUNDLE_NAME "${CMAKE_PROJECT_NAME}") set(MACOSX_BUNDLE_EXECUTABLE_NAME "${CMAKE_PROJECT_NAME}") set(MACOSX_BUNDLE_RESOURCES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.app/Contents/Resources") execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${MACOSX_BUNDLE_RESOURCES}) set(MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_BINARY_DIR}/Info.plist) message(STATUS "generated bundle info is in ${MACOSX_BUNDLE_INFO_PLIST}") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in ${MACOSX_BUNDLE_INFO_PLIST}) set(APPLICATION_ICON ${CMAKE_SOURCE_DIR}/osx/android-file-transfer.icns) set_source_files_properties(${APPLICATION_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") else() set(APPLICATION_ICON "") endif() add_definitions(${QT_DEFINITIONS}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_executable("${CMAKE_PROJECT_NAME}" MACOSX_BUNDLE ${APPLICATION_ICON} ${SOURCES} ${HEADERS_MOC} ${FORMS_HEADERS} ${RESOURCES}) set_property(TARGET ${CMAKE_PROJECT_NAME} PROPERTY MACOSX_BUNDLE_INFO_PLIST "${MACOSX_BUNDLE_INFO_PLIST}") target_link_libraries("${CMAKE_PROJECT_NAME}" ${EXTRA_QT_LINK} ${MTP_LIBRARIES}) install(TARGETS ${CMAKE_PROJECT_NAME} RUNTIME DESTINATION bin BUNDLE DESTINATION .) if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(MACOSX_BUNDLE_APP_PATH "${MACOSX_BUNDLE_ROOT_DIR}/Contents/MacOS/${CMAKE_PROJECT_NAME}") add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${OTOOL_BIN} -L "$" ) if (Qt5Widgets_FOUND) set(MACDEPLOYQT_TOOL ${Qt5Widgets_DIR}/../../../bin/macdeployqt) else() get_filename_component(QT_BIN_DIR ${QT_QMAKE_EXECUTABLE} PATH) set(MACDEPLOYQT_TOOL ${QT_BIN_DIR}) endif() message(STATUS "Macdeployqt path: ${MACDEPLOYQT_TOOL}") if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/" $) set(MACOSX_BUNDLE_LIBS_PRE_INSTALL) foreach(DYNLIB ${MACOSX_BUNDLE_LIBS}) message(STATUS "additional bundle lib ${DYNLIB}") get_filename_component(DYNLIB_NAME "${DYNLIB}" NAME) set(MACOSX_BUNDLE_LIBS_PRE_INSTALL " ${MACOSX_BUNDLE_LIBS_PRE_INSTALL} file(INSTALL \"${DYNLIB}\" DESTINATION \"${MACOSX_BUNDLE_ROOT_DIR}/Contents/Frameworks\" FOLLOW_SYMLINK_CHAIN) message(STATUS \"qt: fixing ${DYNLIB_NAME}\") execute_process(COMMAND \"${CMAKE_INSTALL_NAME_TOOL}\" -change \"${DYNLIB_NAME}\" \"@executable_path/../Frameworks/${DYNLIB_NAME}\" \"${MACOSX_BUNDLE_APP_PATH}\") ") endforeach() endif() install (CODE " message(STATUS \"Running deploy Qt tool: ${MACDEPLOYQT_TOOL}\") execute_process(COMMAND \"${MACDEPLOYQT_TOOL}\" ${MACOSX_BUNDLE_ROOT_DIR}) ${MACOSX_BUNDLE_LIBS_PRE_INSTALL} ${MACOSX_BUNDLE_LIBS_INSTALL} execute_process(COMMAND \"${OTOOL_BIN}\" -L \"${MACOSX_BUNDLE_APP_PATH}\") " COMPONENT Runtime) else() install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/android-file-transfer.desktop DESTINATION share/applications) install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/android-file-transfer.appdata.xml DESTINATION share/metainfo) install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/android-file-transfer.png DESTINATION share/icons/hicolor/512x512/apps) endif() endif() android-file-transfer-linux-4.5/qt/Info.plist.in000066400000000000000000000026171501120463400217010ustar00rootroot00000000000000 CFBundleDevelopmentRegion English CFBundleDisplayName Android File Transfer for Linux and macOS CFBundleExecutable ${MACOSX_BUNDLE_EXECUTABLE_NAME} CFBundleIconFile ${MACOSX_BUNDLE_ICON_FILE} CFBundleIdentifier ${MACOSX_BUNDLE_GUI_IDENTIFIER} CFBundleInfoDictionaryVersion 6.0 CFBundleLocalizations en it ru CFBundleName ${MACOSX_BUNDLE_BUNDLE_NAME} CFBundlePackageType APPL CFBundleShortVersionString ${MACOSX_BUNDLE_SHORT_VERSION_STRING} CFBundleVersion ${MACOSX_BUNDLE_BUNDLE_VERSION} NSHumanReadableCopyright ${MACOSX_BUNDLE_COPYRIGHT} CSResourcesFileMapped NSPrincipalClass NSApplication NSHighResolutionCapable True LSMinimumSystemVersion 10.12.0 CFBundleSignature ???? android-file-transfer-linux-4.5/qt/android-file-transfer.appdata.xml000066400000000000000000000022171501120463400256320ustar00rootroot00000000000000 android-file-transfer CC0-1.0 LGPL-2.0+ Android File Transfer

Interactive MTP client with Qt4/Qt5 GUI
  • Simple Qt UI with progress dialogs.
  • FUSE wrapper (If you'd prefer mounting your device), supporting partial read/writes, allowing instant access to your files.
  • No file size limits.
  • Automatically renames album cover to make it visible from media player.
  • No extra dependencies (e.g. libptp/libmtp).
  • Available as static/shared library.
  • Simple CLI tool.
android-file-transfer.desktop https://whoozle.github.io/android-file-transfer-linux vladimir.menshakov@gmail.com android-file-transfer.desktop android-file-transfer-linux-4.5/qt/android-file-transfer.desktop000066400000000000000000000003711501120463400250710ustar00rootroot00000000000000[Desktop Entry] Name=Android File Transfer (MTP) Comment=Transfer files from/to MTP devices Exec=android-file-transfer Icon=android-file-transfer StartupNotify=false Terminal=false Type=Application Categories=Utility;System;FileTools;Filesystem;Qt; android-file-transfer-linux-4.5/qt/android-file-transfer.png000066400000000000000000001110471501120463400242070ustar00rootroot00000000000000PNG  IHDRxzTXtRaw profile type exifxڭW$9mY8Y`?=L:~OuBc^]ys~sV=g?ocfyUo8p/|ܯW'[Ds5k3E\ws߾=VC/w|!0#&Q v7]qwq?|}ntR9[?sŸra(rY䕖*2_yc`\ynsOnyFcpen`\ 6\vx_c+܇p)!6J(z=k@ HH!41?%VrI)TRM-r)\0PbI%RjiPcM5Rkm7ZnŴZ띇vnݹsFÏ0H#2hOgƙfef_~EYuշۤҎ;ˮfNLCj>WXƘS!L#o#&JU;@#ܸ^AbDW?W`{n <яVM}ר5`<`ɰ22 2k99|G%Q m Ar쐓R.0͞g{"YF.O1*nG/D2_iLUںk7n\>ǻJnA&d4'O!J>'e<Ϡ$bf8hTRCʚ[R5(wA c%Gp:)KC8ܯ<l!8=[< ,Z-SSPv"+ #> TN#Ö́j`L;!$vo4߅1M`RGlǙmw|\ o26KaSpM VSlFP߿N̥xx𣓫yGŽtB;t_i) 9S#2É{ r'<}F*l iiLD0FTIy2b{v]5xIĈ̹W8ȥO[te3' %"޺yQ:- Sx2"^'}=+9¹5?vDsjjiu%_F7a@_2#"f7>8XaÆ#cU #.BA&I?r58LHC hpGy 'B? [I|F_%O4Op)rfſΝC]:{)_SxJdמ8Xt4[o9.PWDT^”Ĺ(~:CY0تcCbP4ʞ"YMDj/ B"?gȣ8B~e@j%fDP)g< G7Ō{BkTPTXn{` *vEז {&dM(C4O&1a NLE,Ĺ+2<| cq«ſ "۩SHi7)#.cDG>c½`2 :'U)*^Xrfq5llQ$B.{{ .찆"~ ~BYԛ w#m, ]N[#'o$o_aWe_npJT!HWȕfjQs:\@C*d/N&5+n(9Ѣ s'%|ytO[,FS r!|@1[PuW4-8 ݟ@]rjy}>C0$pBt0;Ɖ"ɯ獥"stk4(iG#׶ШsrHL_UJc 8؃@(,\Bé ՈZnPet0Hܚ7y71؈p r ?idL#hǥ%u1;2]°[0 *@$x;P0d{V̪,|9#v0,p4Zn 1H8~4"An3jA5O=2lOx&HE$hkZQSWzR2k#G#2Ss#L|Jeݒ9[,hM% >ԅZ)s]7EPZJtnTI:⚲ *JB$8眿_SJeTXSmWs0X``ڨ"4IܙI|8a_ּQ"_$CT#Rܝhj8NhW2#πE 6YP1^PA0187 }!)"%FqJyP3׊ ܠ.7n#Q! {6J 9Dlt5@t;xZ2#`Zr枙wx4(Wo̬65)Ub]'!L[Azh,/A1/%sp2)au|LQAL0w&1BV-ТTKx{W15]d겦^FṢ8jM&B| )n;&0K# jObW30XV0 upEC+WK}B0Ǖ9!fa3[=H7W*U8BSRmZk%~,N'qH188 6m0*ji&KjW #3> WpqL#MxT;O*!TM+?[BتK떡Jpɏ[\O7ULw #NPnt:u&:u˽9Zx8BՖ6B`ET|*Waڅۊ ]+R)bF#f5,LaDų_>u1`)d]4٢Uač(վ* h갟3EbْIVw "DDHvl;fGk5w[ @& ME$ô!6ad~J4y` b"=ke#Z& ;:VrВL oSF:.S]b;Wѭ#s;_~bd7 2"&Ly0e&`HWg?h߷ )؊PU \!F-d&$<*jVEN1-1gkYh.2{H _L(:Bܚ d=`-pZXǕSm-=ڎBTGnX((Su[ wFZ:`ˁMpС ydo;ߴ&N%eE'*I5D[Q3L ԣڈpC& Ųg%wkQYZj [l9[9Xu{m IbԀ,QS#Z M(8l6/0|Š(m~*LŌIFM 0!<6/h+ y Gꩣ`x/QBtImFJT41J嬒D 5JF׻b~EZ?إvr L+Zٌ>9mnEJ_TӋ}6M0|>K~ O:oբmPW%9^;G1'=5`4DbPx9a١z9WvZ#uW.A-OCl~c9B>mS@a^.+Hբ1&9wwmCL/oè ֞UQ‹O ؁G.!R0 $:Nw12Ϯ݅FD{3: 5A&jMjif+}sжHA{%{B( Z0aƻ2dI$O6N,j] fN̨!]y4o&S6])?ap0l+iѻA჌@Wk>u+ ~m++huS>#P FRh\.Px;ЛH_(]Sj׻hF^ssÐM׮hqmKDkЩET%mh ѹW+L2nF#fOI43X2@Gz>KFpw}\ap/*,kg`@n4Z;Z!%DC T&wZMa䮅7qB M_&nF}"=,tͳ CL@x8p 5xh$U7s?ߨPs/PE%BߌiE@1[fqp93tSxV:';@Y/BҊ`;Հ\Wh3k!S@ W>Hv``8A`K+6mw+5 ws}j+eҳC^S] t8v) H ;7#x倻D飆6DQ5zz3ښ vCZR/UUݵɨתӧSΑPه] R;D'B,(̷}PwB ;3(JcrڶQȖr[[ejQ LjޯPy\GQ񯺜c`9 mTɟE6rG6h9inDChGrḪ--' x Bs~KX2 hΗp;!YO!ߪ 1'Ŀ-6U@|o? [&>Z+0ğ U'ğW ig}-<\NB|PQatobW';/tC_?'>rwd۰O铀Ψ C?'ğgg凖H}0 FVO?'ğܨ/ =ȭ5K}(v =_ C+_Z ho?h\@GoJU޿G ? Q*Ou*Y o<-1}()A| ğЈ'0E koi }vIzO?'ğGܣ4<Bc:XNğ_C?wQ:?'S??& 3g##Bn#{&>$&[$Eo4{ ʥ$MO?'__Sіo$}NRA?'6_ׯH]v@Fd>]j^[|E9SfCts߻+w Ǡj?O?'ğ[n]$ğsğsI§TdW{O_E9q;3-߻=%}?'_Y7'I'R_K?'N_6o+/QN]P@O_Omt?~;{]-l$Ŀ6sO}O=x/'_O?_=+zhΗ2g_&ğ=hgrZ|2$r n O?'2q; oA5JV_8ğ_l%ȝ߮ cA\5/%IH?'M U;ſL O?'2E$ZW_a Q#ğj@p`{-OlSO?6?'ğ! 6*: C?75ON@3'ğ;D{@Ua?'U_hD!;* '*'ĿOFޒğO?w< )ğr"/TXJ]F7`i-O݃ 'ߢm~@?׳ˉO-? V$ğ"ğ;d2O킿PJZO?74mQa?'ğ}$ˡ_D嫌P'/4WğW?'ğsBqE-Fk?Ě[O?w/z?7 '_e9m?{H!UO?w"V?Ou n_Mm!Z6W^a?'ğ>VΙ7l'ğ bs?'rO?'¿p'߽ UG'M?'_;_hD_SdO?'ğ O0 O5s 'ğ/4FrR?'ZOb'ğoUUbqjğg$ğoK=bAO?BĿ⯡?'ğPBR'ğRUNgֹTS ]FaT)UU夲x4XJ/SNB6V4OF6Ta^҇6W*1a_)/4$Ն j/TTOpTS}T?'ğJ88#oO}TVO?'^ko㜎H?'O?7w4؃P5*!ğOuOMTۉkK 9$ğv 'ğğUm^/4F?'_ o3O?' k_%ğU q+AOğ_mO?/me?'+oT!ğW5OM_ թ0ğk_Vğ⯯S?'ğ$WZ[V"M'?@~P__?'_oG5죵_N?)ʉO/&ğCl^F A 짎:%ʚ'[RWl;DFsOK3%^|q>U/.}F-y[غُu]zօ|?8:M^QV0__9wy7sY>anl\wo*Z[!`wkt|1X _{ Pw0! $voU?Wh@KG}d0LD~+ŔBm|$f:?7c?:2 b7'ĿH[ᷨwW~0,t_ ԋ=%tG/T[ +x/6œOS"h#Ŀ\&_+᠄FϰC3_=_* Ŀ~{{Z°UOWA"rAfkQb}^oHBs/amy^ M#^q蜋Ш#IQjPQa0<21aش'g7]*ߠ6f6Oo~9߈1FSeSW~PaFr2-cԿoSTa$UIٷi<3{,E][n^egFȉ26{۶Q i?W?p@u1_]_YC_ _eٱO9]c8tP m"Ῐ/,$|TᬞBk;!_" \4ho } &]euO#Ŀa!Ld8n ݜ p mo8*>*0 MQa?/CW">O _05$`bi` o D7`!jt O@ɻ 'g2$`y` '%O`*0\6㏢=ğ4 p]0CV$)N?/ iܼb@C__5`3 [N?w+U%ݧb$}^]6 5 g W'mO-퀿W.9,D9W/'ğ,' Z+[4,`'`O5_WwH#U?/|c ' IĿr׀? {oDѴ2O$&Tá}!I2q_>B)⯺^82Ɵ㘛x˓yrځ_rIu?M>uS `;z沗/͂[ 0{╳)/sLI@WA)c2>C~_*!K;2Ƈ>>/{ $`` &f/^`0Z Ta3ߘïz32\Յ& "_m;jQ~\UΛMm~Dc<{8o}SI d0-fgg166fiK~/5?tϻ7¸RFs 6%x䕟_xBd:nZLb2.l+18^EF] "Atw79dvQϻx"N8Qw\/Ad$O-?(ğ?LL1jހ@L&,uN66KT|ɍ} طw:؝``u-" !Deȿ@ݍ }8Gl053/}ӓ3i@}VZ@~4%R]9OL-_$+cxo|_Z? `TvV XAb@ Dk_3?yǸqIB6 Jo{? Iok ??^8:1847v]i yI_-Ok?HB-..O}gx ohO@$kj˅?%QU_8a.Mc 3٨X9[g $/U_TT? .D5IR {Ue-/w..\\<8I@n26 =V-DyZEϽ;ŗ)I30g_B=Iϝ<=ed+$R% %.D IV $q-H2^oasof$ <ğR'5?@ [WNO㵁)mBtw,k "XMwy 7Au E~ 0x5wdPu+6go<%)7B-?<6; 8p'r/CEq;d%@-?;K' c, v?`wUn-J){Q߲#.. Pj$.@u1Ѕv' '}LLk XBW# -Ŀ>Prf+h삿 ) PhhZŘ_MsLrBj'ry?׸bm@+082TYA}n]5W\f/d?jğ[:'!BS6%_>A @Mzn?0@NBg/rHz4D]n B`s_$IȵJ(2uB]?L@7в46 I BdR"yEB> MﯠihDٮbe7RZ&I>ۋ?׌? o+ , |o:Q5,/۽ ['`1 P/T<]9@?k7⑰e[& X}w'/o_m#׏?·HXIbc ׍_ ._uTbB3_H߈ c$N_1*|mzN̨Fteo36_⿔8#*ğW"7s{bdd4ǿ\$f2.AUBZ8;f{JuW #ğgn "&׭N@[ hC݇-x}TՆ=w\7vOK`k7HWA-=Bx/@T&ׯE;e+_ Ck?,p1Y¯ 9m[ _ jߒ(4p?2PuomBwz:) nf2B&kOezo!Ijʞa$΃x'IWNU'MÁض%6W0;h]3 22w1b9 VS_Ǧ>A^ ^E^e)Ұmj?'J,{.H$238 8z*L~Kw~vy y(TQ%4g&f^"K|> u4{T3Y=qy {f2=._,o޻p0Zv>w{(~b,+!nMnvu@Um߹ɋw\AK ؿ7{ø0c?Wl6bǖ Ϣ6Mbz{%oR<.}=>{;pyg199^/k؈GY4њKzVW.F(6Z$ lwwݸ~uz7,`8% X' 7 ƾm+ĝ7j-^;ťpNL1de@MÿCBk3o$> 6rmoZlN:8Ҍ8yS o>?H'U$۱iR~ v45;# e/mlh2>`A4S2p!H$Ҷ\ r|olQLu>[Muv_pep Զ墼5_?m-yiqF4ɦ/x6<8fL@i˽/F&@7؊~p!x\&&/j?J%n_p+^]Y68tS &w.CG &n\WaBAwrT!ckPŸx2f'_^1M9J΢%k Cq 2@͓8:8œv%oOO 6\ I 1 !&q6 I҂Ӽ+ZI@t1 Ŀg>ţ?D2!Uo=+8pU]At{Ii Ggq$^91TU)3FJ~'Bط;:Ze?&=zss{f`OI_}6_XA8$!Zs?+w5x<ףD"$ dsT94zڳe$o #U_$Hb K>[6dO܎m[]P(B(BWW #"#J-5 lj 4^5}L+nǗP _X<0'UdOd?T} A[~?~Ÿm~J?smV'_}sdoD[g(a/ $3?D:.pFm"q4_ Sc]?x`yq! _e?y2᫔Q$$28,_a d"h%I7$xmɿ싘6>al0MҚ=/[+' C՝@%⥀ZX [u9C͐_mQ7݆B?_Id8QF/K57v$m0[Q ?"  Lİ-)jo| :G$$m4#`{?k]G KD*9_gO#iYb #X'`/ܰuqA?~aX' Hd \m_|@q''!u 3 "\ ':p$aX, dn$\kJĿhAa[$kOoMU?ߌPf[c1$i0V [{j0zߌl1D]S?؅HlGqǚ)N.$aX?Rقcj?,^@.|6Է[qHIߚ= >K=97_ Y a9szL/<&Z&?7ƶF/Me_ծ;=JWU1n RJI܉ sK BlD&pr.pyW0$>B- #ȤL_O {(aI{`ݖlRMWs,ۈPd=SH\s('[^_)€}/'I!C0܋db id em?l@mygH#_y ^=$H)+tGBϯGmm$H';쬎J5!1H;€q*İe˩ 2n Q#$JbaUd.OUo~_ :? "Ety)u\?w&,KFBİ%+rYs얬 EO-C>bİJ&$`HOX<? Eՙ;FE0wNC; BE]&/߮K_D8A Gt}gC!v_[ф'xVl‘ $zs ] ğx[ph ;=_]ğ;YVzg* ߦ mgĿgt*{;y#_r'b9BEO+_Q$M3 ğ[ hĿуQ4ɛ-"]-+Am? ֮~Ox$6yeW w]7A݂|ƍB> _hiɋPO*yX9->OX%m+# yy 9 _%Wliud㐪KcKQ ]*o_uȭIT "F_y[ vF̰W\t b/$A+/I ,d2k>3oIId3w:U '+0lΝC,Sy՘+$aĿZK_!ĿP9E/va۰9[[[ym] JًCl Beٳ, o ETXYGMa# _\&N(0w+o?_L+@S)o- x[;َ0 _%MZ׉u Z3L*+Dcc#_[KnğrQ2F3.9pz*c5V7\z =?z[4=a0,s033Â%7/I !) Fݻ0Ϝ9^{ Ga@M_,&_>4{ф8s tI7@@=T `F?CG?.3@?ɳCFv,o4%9aQ~X7_$ n_!c"AZ.˲ Ш۾}; )9 zI9c .bM@݈\ 8{,Ѩ!DgsX' J!L̿ȧXQW @f0x/aI@KKmd2&Nǿ ?/ n?y6@{nK'dG<$pS) FV=պ; r${ =?֜HO$ ɘH$߿j`ULlg@&Od-9,& 0r@"ѣG1?oj_#?Pl*}zS `߻w/m$ ȑ#vyk NK%J q G'qmoؾX_WNe _q\OP k31 P=zڦk $b6@<"`2Դ9=IÇk~b8r5w8 +᯽T\|fSc(Gj T4U¿(W+0]ST5R۽'@Mk'._OF< NફR[7p)о(T f҆Zs~^ad2e׳=+[o} h$:kuG?~W/"`T*Uݟ7 XXXL=ğ|s_C=#/~  ;' w/4_O ^<>_R`K<gzD"a$m<_/dzd@'k8?Z}<d`u 2[%M|+]:cS>E!vµVW]u S4\uU%ǎSufN^OJK?/??-B19JU͇ˆmh8E*PέMU'z׵bvxwyej3T%سg<!>|ذބBQ 0@5~h Q95 (u9V/᯲L,AG UA B>$ffJ܀=Y _]go yo::%%ml"%J^aO@~`FI`lkeo>ٝj.>oqix/ז_(:xkq2BO${1h4jRg$K"*ǿD` ={ſ$bq/$S0b},5k-&2Q,o4]P1C\`7WZQ"&5(Q+V7N 'q%Ps]X F<IYd2SB{DЛk= *a:i>+.7LJS9Pyh!!T7} >€$;}f5JwNT:ڿ׋[DߪH&|CF_?䧗p ?r7 _-_ sl^去Sg')q "Hy;aߒ?Gu7WtMk?'q `5#L9r_YL^='&@iSĿ"MR*E:ѣG1??oEG'"._TۄpVH e'5od2x13c j:P?/ε/ ky$bQNo 4fZFHRX~= XwsÜdtA{O~/@8QA1 5_/@V߂@xc$0-|\(>>Yw)<쉿p%=ğ<FJ"E*5tj"@ -fyf;.*$ZlJn\|li`pșeI]_J܅K⭇7̿P񰆱N_h=_>^w[9 7]<=Tݓ*)o9`C_"1l2=_0/K?76/6o06 /4ğ 0`[OCA7\ ow/;(;:PQ  'Q#¿G]=JgyOkj0 7 H?we|#KG{0Ɩܒk?=_0='_c9V/vn5 z쇿tN>6{𻿵[7sA?_MjCk믍]M>Z +W$7j>hOk_sؿ Qa0j?ׂ; * wݷ] x2Q@SW<@}DA((#O#8 WB$A((Cַv QpNՖU$G /\?D.t[#nCgGm>x'a`p?:o+mCذ>Z|+_*h:c0p⿦r2  IDATH>Ԁ7݉["1̏F/صwօQ1|Kg _:UW$Yohm7a0c00_r)+oO¯l #u|[Co xס=`[W$_(+%|=x흆-XŻqn8Ќ?sOO^ 7|c]wmŖV9G|2>л.TC-V_ߌUÿ΃?-hm3 âi,[a^_w>~l:l@W' 7 /]ajnkġjruw3k=l @;*?aL٢Q}2}5=n vLÿA?F`TwLe_ m&%mݦ/;8-Tb@-$A|ŮFl0_M`0Ŀ:g{U]_Vk$࿽P-w4!? 0Qr_sΛ-u h~nb`88r #8rZ9(Z,u wu3 I6UFaʥn(+zip"Rъ?vQ apEo`?s)vl j\`85Wנּ?0@{גzm^0fD<$<DVb%oU+\osKOLL`aaw@UJ*ߚ@}cOQ<ԅ=`0y HRKI]vdpQ?u$F}ގ;V$x(z455ߠor"y ׺ +Aš?$S q$MٴE!\ 177Wx<@ GA".\z#/\?[ ?0:u{ޥkP5133cǎ矷m7 {´;pa(D2cةrfȤȤ" 8y&JǦM󭜯B$ܹsiR- _ѣG;8^{5:u ###dF#v mH HΞǎ֙ (~t3SSHck~GYAVBQ"SS3) ]2`0믿H&eMMMK*cddh$dz.,,,]ZLLL͛ե)8r$`xx/_ƞ={j@+ ԋЈ\a^=3cQ_* LD vh$w ( :::ʾG7垛DϒG&S055m۶h??FCCCUz1? " aϿpFSuI=񗐈mǨÑ؈uYzGGGqa"k3㿘,|Ĵ%h4Q ]^YfX9Åjpd9KXXXЌ$IB/%79+/~%~xc1G2>~S+|`82vvlv9rdEP`qe0,ƯH+? _JgbO=7'k8LOGO"s17R |q=?G-… D"~qPT/b$5?y'nc14)MHȞ㿻 ~L9<==f`уc C=zD$7no% Kq|gUAxǧ?j=IϜBq$I /mR Sbttǎ<*>uuuH$մƿk_/J'*&qoz33_|#hSpzACWF:A<\g_⻏=.i*]V8/4_/\Bkoέ!{kL:5vDߣsꏁR8M6ϋƆ 1%===w8q{E rgVtg>a/ODmR9e_2=7?o~E-tj` VcI|cKU0=1 U*8S~^Ӆa۶uhkv~_,?.D"$ `n6033Sy/Gv?X̯&_gR1_╣c gG@$yy/vDߙNzzi@P}!ct b:'x ǟ.v '?00̇X@ 征?Lr/{N=ypo݈;nmT *8_!$IB8FookxhTg"a``6-ۙ h1Ǎk&n?YņxգD5gGi??wMz=J~LNN|MZ?~y`V~?L]$3 T??+'I{L;55Uѽd###KVIʘ`)Pq;w y|_wT$W˵j*{79}i6!6W.9[FeSRH3ycƍ%<::W^yqw 1v/Ե._.g/J7TcEfCWW.*ԳB?$9$YV EP77L/3iqz)/P9\W&E/8% _˲P) .>}Z`vTz18hߑ A5EEhD1[5?G_*"LVu)`W$@!_e9U$/[17J; 2Vie?u0sW?<0}bH$%yhϟ=慢(_h:? @݃aۀP&,̞^_{kB ԋ_I! Y| !N8K.z @݇_*߈P\!7l/xMLsu#T| ⯪ƬZ/dYF0$&/wbߌHӕPVO[! Tƪ~hCa73^/::,U*[e *_.>O=dK?==ÇzommݻW d;y$4;6lƍFe(w馳:)Tu@&ww܄jǎ+z}ll K?ϟ߯Vt:]29;ahhSSSAKc?7rV;;߱ Vx<^vAWקRԙLE/rmV M_qq$OU|GC?u֟ο-NZ{< _8N>]gҥK$`bbz=?_3 _SAkK{;BelٲdӳHe7MPLCN|<_NBZzZ"x!Ar!,AB!od7nL?KwOLW<xrl訅0$).f1}$F]ᛗ P8#@UU dJ&''목GCC:::'ocow wovem z9pG3ԿXs\5CxCwwwb=SS6lؐ6>)K#/n EQގVS[t)TUEOO(jkkny_:'Z7^6\NQ]⣷ йXIvU> 8Μ9ʙ"v~ue dY~>MӰi&^h%K%m`ӦM8~8FGGu}Fw4/ 4 pPpUUUif?˭Hf,D?qAy(q}ß;$_X0EEE6nhLA_59@[[\do/sGhl݆N}U5 (Dfۋ*5KrDuORGe 7l[Bf0q`z"'or%*["̡qP5V}8=Qqꏣfs$ĿMlZÍXd+p,o\c?gIoXSeUsĕ7ڊP`ab 8x[\<_(p戶j͵h2 (=1c瞽F-?+WjU[MlGM sOmbʐ-@S#/c05x{7'0FUVly>`  r q)]ay^7blY>I㓘a\8A^`2淟/[N@<.ؚ3DS#}ywڂx71pf0iO1L\ąn~O`0>| HeDZo?Gg%F,zc#'3o / s}?zdMAw!xCxI&y;?Y`x):vMۂ?g 04t_]>GOO&㇏Y(r/_.O%N0 z;X,o|I ؆]$&fǟSس2.}wo |z@m?Uqo?ˆ.|vSŦ!yk;W2.* $)6 o$/DZQe~OD<Bӿ2.:ۅ?(v2 Z"EIeEǭwOEtJǓfem˫ʾLz ԸuH UK.~%tB4 Fɢ_GTpKwj(jU GI>~wԃ3ϟAtrkV V˲?ЋO@qOa$& =k#QY]j=1Q=ڍo2Mb/Zd%;@{)EBFmچXꪅ0\];5~ı,9SQYjv5U­^ AϕWDz`Y\}xbA-4@b%2Qa3Z@G\kkȞO}/_ /̱lRTUhۯl:TUˢb Tofg@q⟧P cğ_- d%[$_?u/*tO"_?I}wwkoONVki5@V)?%(4ğ@#2-'XE3l?oKp+<=܋0% 咤@*VKqA'@h1T3#YgE P $ 'ğ{E,@1 ğIR/!"k+F⟱h$h:ɽ2HCo^&H ޞ;h9Wہjӣ.ğ/gg+ n: ^d%4;/,(!nT3 O=5@ 4T!D!OEbYڂ7No8-i=mzFo$ğ,V@gs2,J{_0JbO0+W"yG'}h7rG9{٣Ҧ xq$#\>;E#OMU,KO?5…K#l1.?9 * /f W.bO@BvԻfAW/ 0'tq$ y _W-'kP_PQjhX` OqWEUuNF:b?`ʚ@trPR" AD'NJ}3Y@ x"`)ϳ/Tm#TEAd>Uk*QS5B*?D|a]/+M'f<+ Zj[09d?+ U%PJ8{1S(J5E!釞O_d(Z-T0^@H-qY(;ƾ%IF0܊`ĢÈCxTqA 9'EŤ$hF<6:/;Kݶ}6kWvlE8Ү,o/0՗ۊ$APJJi?T#bh{.XEm='+aM8m1{yEt$z B"QpVlZR)9/)%a]c[$Z̊z"=@" eD]kFj0XEݰyo80!I$O*$Y,fɶ{unG1}25A z\?]anzq>/!rB3 _8y7r)ӛ&oG60 N=Yx_. Fm?N'Ip> WDgG?O  ML?'ğ?iO?'1?w93 ğOob_OgV_>O~#'ğ-s&%_.?'߭?!>t @v?'ğO 1 oaaOYM'>^ 3ğO?:sO?񷊿0pI'MO'ğ0'ğ*'ߋ MOoC%ğğ_oOϑOa5O}`k%(_#Oߨg* 'ĿD[mĿ/vr^@H?'IOq7CKD@ ;SaT9O_R'?[_O?2@U a{* />`G\L:G?8_X_.:"ğ7]@$ğo?w0%(.+ '0rlZl- Op-0!&ğ(-Z`X6?w?75BBy2 z[0ğ?_{?/_O?7\N>w>}U? 'ğ|^!ğA?'E˅$:ΝW&ğD0 o/fSGw yHN OO-B/?'G_&Z|sğAԋo,\j'0tٙSOO<k`]S#/gM_$B#o#뜐p_3 @k^ooJAɓ'޹oA kN'ğ[p >{Z߼es{l%vK s uğ&AS0O=Oos:lsmBY0< ğ['4sRQoh#TOK@?!IW:z@W>=-$\O?'P{$z@ /} O?/w:vQ#K0[=VHi+o.'ğ[i`c;5]|cB` NOݍ 'ğj }ӊ&] {?w/>Os!x{秺v \Bqnh ԅpğJ5 ?р__K@_A{Om>7O I#smlݘYJ8Ual6A{/:cnۖev;\GZ on_[Ŕ䱯촓mٸlۯH!兿0зO?'~&$wpڱ Wlu :K@la#&ğ 5_ 3 u@חcNx`dO ` KIZ T CɆE?'$u{ p!#i{OeIENDB`android-file-transfer-linux-4.5/qt/android-file-transfer.qrc000066400000000000000000000007441501120463400242110ustar00rootroot00000000000000 android-file-transfer.png icons/dark/folder-new.svg icons/dark/go-next.svg icons/dark/go-previous.svg icons/dark/view-refresh.svg icons/light/folder-new.svg icons/light/go-next.svg icons/light/go-previous.svg icons/light/view-refresh.svg android-file-transfer-linux-4.5/qt/commandqueue.cpp000066400000000000000000000234461501120463400225160ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "commandqueue.h" #include "mtpobjectsmodel.h" #include "utils.h" #include #include #include #include #include #include #include #include void FinishQueue::execute(CommandQueue &queue) { queue.finish(DirectoryId); } void UploadFile::execute(CommandQueue &queue) { queue.uploadFile(Filename, Format); } void MakeDirectory::execute(CommandQueue &queue) { queue.createDirectory(Filename); } void DownloadFile::execute(CommandQueue &queue) { queue.downloadFile(Filename, ObjectId); } void ImportFile::execute(CommandQueue &queue) { queue.importFile(Filename); } void LoadLibrary::execute(CommandQueue &queue) { queue.loadLibrary(); } void CommandQueue::loadLibrary() { using namespace mtp; auto session = _model->session(); qDebug() << "loading media library..."; _library.reset(); auto reporter = [&](Library::State state , u64 c , u64 t) { qDebug() << "progress " << static_cast(state) << ", " << c << " of "<< t; switch(state) { case Library::State::Initialising: start(tr("Loading media library…")); break; case Library::State::QueryingArtists: start(tr("Querying artists…")); break; case Library::State::LoadingArtists: start(tr("Loading artists…")); break; case Library::State::QueryingAlbums: start(tr("Querying albums…")); break; case Library::State::LoadingAlbums: start(tr("Loading albums…")); break; case Library::State::Loaded: start(tr("Done")); break; } if (t) emit total(t); if (c) emit progress(c); }; try { _library = std::make_shared(session, reporter); } catch (const std::exception & ex) { qWarning() << "loading media library failed: " << ex.what(); } } void CommandQueue::downloadFile(const QString &filename, mtp::ObjectId objectId) { if (_aborted) return; qDebug() << "downloading " << objectId << "to" << filename; QFileInfo fi(filename); QDir().mkpath(fi.dir().path()); start(fi.fileName()); try { model()->downloadFile(filename, objectId); } catch(const std::exception &ex) { qDebug() << "downloading file " << filename << " failed: " << fromUtf8(ex.what()); } addProgress(fi.size()); } void CommandQueue::uploadFile(const QString &filename, mtp::ObjectFormat format) { if (_aborted) return; QFileInfo fi(filename); QString parentPath = fi.dir().path(); qDebug() << "uploading file " << filename << ", parent: " << parentPath << ", format: " << fromUtf8(mtp::ToString(format)); if (_directories.empty()) { qDebug() << "adding first parent path"; _directories[parentPath] = _model->parentObjectId(); qDebug() << "directories[0]: " << parentPath << " -> " << _model->parentObjectId().Id; } start(fi.fileName()); auto parent = _directories.find(parentPath); if (parent == _directories.end()) { qWarning() << "invalid parent " << parentPath; return; } try { if (_model->parentObjectId() != parent.value()) //needed for overwrite protection _model->setParent(parent.value()); _model->uploadFile(parent.value(), filename, {}, format); } catch(const std::exception &ex) { qDebug() << "uploading file " << filename << " failed: " << fromUtf8(ex.what()); } addProgress(fi.size()); } void CommandQueue::importFile(const QString &filename) { if (_aborted || !_library) return; QFileInfo fi(filename); mtp::scope_guard r([this, &fi]() { addProgress(fi.size()); }); std::string utfFilename = toUtf8(filename); mtp::ObjectFormat format = mtp::ObjectFormatFromFilename(utfFilename); if (mtp::IsImageFormat(format)) { qDebug() << "image: " << filename; int score = GetCoverScore(fi.baseName()); qDebug() << "image cover score: " << score << " for " << fi.baseName(); auto dir = fi.dir().path(); auto it = _covers.find(dir); if (it == _covers.end()) { Cover cover; cover.Score = score; cover.Path = filename; _covers.insert(std::make_pair(dir, std::move(cover))); } else if (score > it->second.Score) { it->second.Score = score; it->second.Path = filename; } return; } if (!mtp::IsAudioFormat(format)) return; auto metadata = mtp::Metadata::Read(utfFilename); if (!metadata) return; qDebug() << "import: " << filename << ", format: " << fromUtf8(mtp::ToString(format)) << ", artist: " << fromUtf8(metadata->Artist) << ", album: " << fromUtf8(metadata->Album) << ", title: " << fromUtf8(metadata->Title) << ", year: " << metadata->Year << ", track: " << metadata->Track << ", genre: " << fromUtf8(metadata->Genre) << ", size: " << fi.size(); auto artist = _library->GetArtist(metadata->Artist); if (!artist) artist = _library->CreateArtist(metadata->Artist); if (!artist) { qDebug() << "can't create artist"; return; } auto album = _library->GetAlbum(artist, metadata->Album); if (!album) album = _library->CreateAlbum(artist, metadata->Album, metadata->Year); if (!album) { qDebug() << "can't create album"; return; } auto dir = fi.dir().path(); if (_albums.find(dir)== _albums.end()) { qDebug() << "registering " << dir << " as a path to album"; _albums.insert(std::make_pair(dir, album)); } if (!metadata->Picture.Data.empty()) _library->AddCover(album, metadata->Picture.Data); if (_library->HasTrack(album, metadata->Title, metadata->Track)) { qDebug() << "skipping" << filename << ", already uploaded"; return; } auto songId = _library->CreateTrack( artist, album, format, metadata->Title, metadata->Genre, metadata->Track, toUtf8(fi.fileName()), fi.size()); start(fi.fileName()); _model->sendFile(filename); _library->AddTrack(album, songId); } void CommandQueue::createDirectory(const QString &srcPath) { if (_aborted) return; QFileInfo fi(srcPath); QString parentPath = fi.dir().path(); qDebug() << "making directory" << srcPath << ", parent: " << parentPath << ", dir: " << fi.fileName(); if (_directories.empty()) { qDebug() << "adding first parent path"; _directories[parentPath] = _model->parentObjectId(); qDebug() << "directories[0]: " << parentPath << " -> " << _model->parentObjectId().Id; } auto parent = _directories.find(parentPath); if (parent == _directories.end()) { qWarning() << "invalid parent " << parentPath; return; } try { mtp::ObjectId dirId = _model->createDirectory(parent.value(), fi.fileName()); _directories[srcPath] = dirId; qDebug() << "directories[]: " << srcPath << " -> " << dirId.Id; } catch(const std::exception &ex) { qDebug() << "creating directory" << srcPath << "failed: " << fromUtf8(ex.what()); return; } } CommandQueue::CommandQueue(MtpObjectsModel *model): _model(model), _completedFilesSize(0), _aborted(false) { connect(_model, SIGNAL(filePositionChanged(qint64,qint64)), this, SLOT(onFileProgress(qint64,qint64))); qDebug() << "upload worker started"; } CommandQueue::~CommandQueue() { qDebug() << "upload worker stopped"; } mtp::LibraryPtr CommandQueue::library() const { return _library; } void CommandQueue::execute(Command *ptr) { std::unique_ptr cmd(ptr); try { cmd->execute(*this); } catch(const std::exception & ex) { qWarning() << "exception in command queue: " << ex.what(); } } void CommandQueue::start(const QString &filename) { emit started(filename); } void CommandQueue::finish(mtp::ObjectId directoryId) { qDebug() << "finishing queue"; try { model()->setParent(directoryId); } catch(const std::exception &ex) { qDebug() << "finalizing commands failed: " << fromUtf8(ex.what()); } for(auto & akv : _albums) { auto & albumPath = akv.first; auto & album = akv.second; if (_aborted) break; QString albumName = fromUtf8(akv.second->Name); qDebug() << "looking for a cover for album " << albumName; QString bestPath; for(auto & ckv : _covers) { auto & cover = ckv.second; if (cover.Path.startsWith(albumPath) && albumPath.size() > bestPath.size()) { bestPath = cover.Path; } } if (bestPath.isEmpty()) continue; emit started(tr("Setting cover for album %1").arg(albumName)); QFile file(bestPath); qDebug() << "setting cover from " << bestPath; if (!file.open(QIODevice::ReadOnly)) continue; auto buffer = file.readAll(); mtp::ByteArray value(buffer.begin(), buffer.end()); try { _model->session()->SetObjectPropertyAsArray(album->Id, mtp::ObjectProperty::RepresentativeSampleData, value); } catch(const std::exception & ex) { qWarning() << "setting cover failed: " << ex.what(); } } _covers.clear(); _model->moveToThread(QApplication::instance()->thread()); _completedFilesSize = 0; _directories.clear(); _albums.clear(); _aborted = false; emit finished(); } void CommandQueue::abort() { qDebug() << "aborting..."; _aborted = true; _model->session()->AbortCurrentTransaction(6000); qDebug() << "sent abort request"; } void CommandQueue::addProgress(qint64 fileSize) { _completedFilesSize += fileSize; emit progress(_completedFilesSize); } void CommandQueue::onFileProgress(qint64 pos, qint64) { //qDebug() << "on file progress " << _completedFilesSize << " " << pos; emit progress(_completedFilesSize + pos); } android-file-transfer-linux-4.5/qt/commandqueue.h000066400000000000000000000066701501120463400221630ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_QT_COMMANDQUEUE_H #define AFTL_QT_COMMANDQUEUE_H #include #include #include #include #include #include #include class MtpObjectsModel; class CommandQueue; namespace mtp { class Library; DECLARE_PTR(Library); } struct Command { virtual ~Command() = default; virtual void execute(CommandQueue &queue) = 0; }; struct FinishQueue : public Command { mtp::ObjectId DirectoryId; //return id FinishQueue(mtp::ObjectId id): DirectoryId(id) { } virtual void execute(CommandQueue &queue); }; struct FileCommand : public Command { QString Filename; FileCommand(const QString &filename) : Filename(filename) { } }; struct MakeDirectory : public FileCommand { MakeDirectory(const QString &filename) : FileCommand(filename) { } void execute(CommandQueue &queue); }; struct UploadFile : public FileCommand { mtp::ObjectFormat Format; UploadFile(const QString &filename, mtp::ObjectFormat format) : FileCommand(filename), Format(format) { } void execute(CommandQueue &queue); }; struct ImportFile : public FileCommand { ImportFile(const QString &filename) : FileCommand(filename) { } void execute(CommandQueue &queue); }; struct DownloadFile : public FileCommand { mtp::ObjectId ObjectId; DownloadFile(const QString &filename, mtp::ObjectId objectId) : FileCommand(filename), ObjectId(objectId) { } void execute(CommandQueue &queue); }; struct LoadLibrary : public Command { void execute(CommandQueue &queue); }; class CommandQueue: public QObject { Q_OBJECT private: MtpObjectsModel * _model; qint64 _completedFilesSize; QMap _directories; std::map _albums; struct Cover { QString Path; int Score; }; std::map _covers; mtp::LibraryPtr _library; volatile bool _aborted; public: CommandQueue(MtpObjectsModel *model); ~CommandQueue(); MtpObjectsModel *model() const { return _model; } mtp::LibraryPtr library() const; void loadLibrary(); void createDirectory(const QString &path); void uploadFile(const QString &file, mtp::ObjectFormat format); void downloadFile(const QString &filename, mtp::ObjectId objectId); void importFile(const QString &file); public slots: void onFileProgress(qint64, qint64); void execute(Command *cmd); void start(const QString &filename); void finish(mtp::ObjectId directoryId); void addProgress(qint64); void abort(); signals: void started(QString); void progress(qint64 bytes); void total(qint64 bytes); void finished(); }; #endif // COMMANDQUEUE_H android-file-transfer-linux-4.5/qt/createdirectorydialog.cpp000066400000000000000000000022751501120463400244000ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "createdirectorydialog.h" #include "ui_createdirectorydialog.h" CreateDirectoryDialog::CreateDirectoryDialog(QWidget *parent) : QDialog(parent), ui(new Ui::CreateDirectoryDialog) { ui->setupUi(this); } CreateDirectoryDialog::~CreateDirectoryDialog() { delete ui; } QString CreateDirectoryDialog::name() const { return ui->lineEdit->text(); } android-file-transfer-linux-4.5/qt/createdirectorydialog.h000066400000000000000000000023451501120463400240430ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_QT_CREATEDIRECTORYDIALOG_H #define AFTL_QT_CREATEDIRECTORYDIALOG_H #include namespace Ui { class CreateDirectoryDialog; } class CreateDirectoryDialog : public QDialog { Q_OBJECT public: explicit CreateDirectoryDialog(QWidget *parent = 0); ~CreateDirectoryDialog(); QString name() const; private: Ui::CreateDirectoryDialog *ui; }; #endif // CREATEDIRECTORYDIALOG_H android-file-transfer-linux-4.5/qt/createdirectorydialog.ui000066400000000000000000000033701501120463400242300ustar00rootroot00000000000000 CreateDirectoryDialog 0 0 401 99 Dialog Create New Directory Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() CreateDirectoryDialog accept() 248 254 157 274 buttonBox rejected() CreateDirectoryDialog reject() 316 260 286 274 android-file-transfer-linux-4.5/qt/devicesdialog.cpp000066400000000000000000000107011501120463400226230ustar00rootroot00000000000000#include "devicesdialog.h" #include "ui_devicesdialog.h" #include "utils.h" #include #include #include #include #include #include #include DevicesDialog::DevicesDialog(bool resetDevice, QWidget *parent) : QDialog(parent), _resetDevice(resetDevice), ui(new Ui::DevicesDialog) { ui->setupUi(this); _buttonScan = ui->buttonBox->addButton(tr("Rescan Devices"), QDialogButtonBox::ActionRole); _buttonKill = ui->buttonBox->addButton(tr("Kill Users"), QDialogButtonBox::ActionRole); connect(ui->listWidget, SIGNAL(itemClicked(QListWidgetItem *)), SLOT(itemClicked(QListWidgetItem *))); connect(ui->listWidget, SIGNAL(itemDoubleClicked(QListWidgetItem *)), SLOT(itemDoubleClicked(QListWidgetItem *))); connect(_buttonScan, SIGNAL(clicked()), SLOT(scan())); connect(_buttonKill, SIGNAL(clicked()), SLOT(kill())); } mtp::DevicePtr DevicesDialog::getDevice() { int row = ui->listWidget->currentRow(); if (row >= 0 && row < static_cast(_devices.size())) return _devices[row].Device; else return nullptr; } void DevicesDialog::kill() { qDebug("kill"); int index = ui->listWidget->currentRow(); if (index < 0 && index >= static_cast(_devices.size())) return; auto & row = _devices[index]; bool canKill = !row.Processes.empty(); QString processList; for (auto & desc : row.Processes) { processList += QString("%1 (pid: %2)\n").arg(fromUtf8(desc.Name)).arg(desc.Id); } if (canKill) processList = tr("The following processes are keeping file descriptors for your device:\n") + processList; QMessageBox dialog( QMessageBox::Warning, tr("Device is busy"), tr("Device is busy, maybe another process is using it.\n\n") + processList + tr("Close other MTP applications and restart Android File Transfer.\n" "\nPress Abort to kill them or Ignore to try next device."), (canKill? QMessageBox::Ok: QMessageBox::StandardButton(0)) | QMessageBox::Cancel, this ); dialog.setDefaultButton(QMessageBox::Ignore); dialog.setEscapeButton(QMessageBox::Ignore); auto r = dialog.exec(); if ((r & QMessageBox::Ok) == QMessageBox::Ok) { qDebug("kill'em all"); mtp::usb::DeviceBusyException::Kill(row.Processes); scan(); } } void DevicesDialog::scan() { qDebug("scan"); bool claimInterface = true; bool resetDevice = _resetDevice; _devices.clear(); mtp::usb::ContextPtr ctx(new mtp::usb::Context); auto devices = ctx->GetDevices(); for (auto desc = devices.begin(); desc != devices.end(); ++desc) { qDebug("probing device..."); try { auto device = mtp::Device::Open(ctx, *desc, claimInterface, resetDevice); _devices.push_back({ *desc, device, {} }); } catch(const mtp::usb::DeviceBusyException &ex) { if (!ex.Processes.empty()) _devices.push_back(Row { *desc, nullptr, ex.Processes }); } catch(const std::exception &ex) { qWarning("Device::Find failed: %s", ex.what()); } } std::stable_sort(_devices.begin(), _devices.end(), [](const Row & a, const Row & b) { return a.Device > b.Device; }); auto it = std::remove_if(_devices.begin(), _devices.end(), [](const Row & row) { return !row.Device && row.Processes.empty(); }); _devices.erase(it, _devices.end()); ui->listWidget->clear(); for(auto & row : _devices) { QString name; if (row.Device) { auto info = row.Device->GetInfo(); name = QString("%1 %2 %3") .arg(fromUtf8(info.Manufacturer)) .arg(fromUtf8(info.Model)) .arg(fromUtf8(info.SerialNumber)); } else { auto & desc = row.Descriptor; name = QString(tr("USB Device %1:%2")) .arg(static_cast(desc->GetVendorId()), 4, 16, QChar('0')) .arg(static_cast(desc->GetProductId()), 4, 16, QChar('0')); } ui->listWidget->addItem(name); } if (!_devices.empty()) ui->listWidget->setCurrentRow(0); updateButtons(); } int DevicesDialog::exec() { scan(); if (_devices.empty()) return QDialog::Rejected; if (_devices.size() == 1 && _devices.front().Device) { return QDialog::Accepted; } return QDialog::exec(); } void DevicesDialog::updateButtons() { _buttonKill->setEnabled(false); int row = ui->listWidget->currentRow(); if (row >= 0 && row < static_cast(_devices.size())) { _buttonKill->setEnabled(!_devices[row].Processes.empty()); } } void DevicesDialog::itemClicked(QListWidgetItem *) { updateButtons(); } void DevicesDialog::itemDoubleClicked(QListWidgetItem *) { updateButtons(); accept(); } DevicesDialog::~DevicesDialog() { delete ui; } android-file-transfer-linux-4.5/qt/devicesdialog.h000066400000000000000000000016231501120463400222730ustar00rootroot00000000000000#ifndef DEVICESDIALOG_H #define DEVICESDIALOG_H #include #include #include #include namespace Ui { class DevicesDialog; } class QListWidgetItem; class DevicesDialog : public QDialog { Q_OBJECT private: struct Row { mtp::usb::DeviceDescriptorPtr Descriptor; mtp::DevicePtr Device; std::vector Processes; }; bool _resetDevice; std::vector _devices; public: explicit DevicesDialog(bool resetDevice, QWidget *parent = nullptr); ~DevicesDialog(); mtp::DevicePtr getDevice(); int exec(); private slots: void scan(); void kill(); void itemClicked(QListWidgetItem *); void itemDoubleClicked(QListWidgetItem *); void updateButtons(); private: Ui::DevicesDialog * ui; QPushButton * _buttonScan; QPushButton * _buttonKill; }; #endif // DEVICESDIALOG_H android-file-transfer-linux-4.5/qt/devicesdialog.ui000066400000000000000000000027371501120463400224700ustar00rootroot00000000000000 DevicesDialog 0 0 615 447 Dialog Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DevicesDialog accept() 248 254 157 274 buttonBox rejected() DevicesDialog reject() 316 260 286 274 android-file-transfer-linux-4.5/qt/fileuploader.cpp000066400000000000000000000153131501120463400225000ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "fileuploader.h" #include "commandqueue.h" #include "mtpobjectsmodel.h" #include #include #include #include #include FileUploader::FileUploader(MtpObjectsModel * model, QObject *parent) : QObject(parent), _model(model), _aborted(false) { _worker = new CommandQueue(_model); _worker->moveToThread(&_workerThread); connect(&_workerThread, SIGNAL(finished()), SLOT(deleteLater())); connect(this, SIGNAL(executeCommand(Command*)), _worker, SLOT(execute(Command*))); connect(_worker, SIGNAL(progress(qint64)), SLOT(onProgress(qint64))); connect(_worker, SIGNAL(total(qint64)), SLOT(onTotal(qint64))); connect(_worker, SIGNAL(started(QString)), SLOT(onStarted(QString))); connect(_worker, SIGNAL(finished()), SLOT(onFinished())); _workerThread.start(); } FileUploader::~FileUploader() { _workerThread.quit(); _workerThread.wait(); } void FileUploader::tryCreateLibrary() { mtp::ObjectId currentParentId = _model->parentObjectId(); emit executeCommand(new LoadLibrary()); emit executeCommand(new FinishQueue(currentParentId)); } mtp::LibraryPtr FileUploader::library() const { return _worker? _worker->library(): nullptr; } void FileUploader::onTotal(qint64 total) { _total = total; } void FileUploader::onProgress(qint64 current) { //qDebug() << "progress " << current << " of " << _total; qint64 secs = _startedAt.secsTo(QDateTime::currentDateTime()); if (secs > 0) emit uploadSpeed(current / secs); if (_total > 0) emit uploadProgress(1.0 * current / _total); } void FileUploader::onStarted(const QString &file) { emit uploadStarted(file); } void FileUploader::onFinished() { qDebug() << "finished"; emit finished(); } void FileUploader::upload(QStringList files, mtp::ObjectFormat format) { _model->moveToThread(&_workerThread); _total = 0; mtp::ObjectId currentParentId = _model->parentObjectId(); QList commands; while(!files.empty()) { QString currentFile = files.front(); files.pop_front(); QFileInfo currentFileInfo(currentFile); if (currentFileInfo.isDir()) { qDebug() << "adding subdirectory" << currentFile; commands.push_back(new MakeDirectory(currentFile)); QDirIterator it(currentFile, QDirIterator::Subdirectories); while(it.hasNext()) { QString next = it.next(); QFileInfo fi(next); QString filename = fi.fileName(); if (filename == "." || filename == "..") continue; if (fi.isFile()) { commands.push_back(new UploadFile(next, format)); _total += fi.size(); } else if (fi.isDir()) { commands.push_back(new MakeDirectory(next)); files.push_back(next); } } } else if (currentFileInfo.isFile()) { commands.push_back(new UploadFile(currentFile, format)); _total += currentFileInfo.size(); } } qDebug() << "uploading" << _total << "bytes"; if (_total < 1) _total = 1; _startedAt = QDateTime::currentDateTime(); _aborted = false; for(auto command: commands) { if (_aborted) break; emit executeCommand(command); } emit executeCommand(new FinishQueue(currentParentId)); } void FileUploader::download(const QString &rootPath, const QVector &objectIds) { _model->moveToThread(&_workerThread); _total = 0; mtp::ObjectId currentParentId = _model->parentObjectId(); QVector > input; for(auto id : objectIds) input.push_back(qMakePair(rootPath, id)); QVector > files; while(!input.empty()) { QString prefix = input.front().first; mtp::ObjectId id = input.front().second; input.pop_front(); MtpObjectsModel::ObjectInfo oi = _model->getInfoById(id); if (oi.Format == mtp::ObjectFormat::Association) { //enumerate here QString dirPath = prefix + "/" + oi.Filename; mtp::SessionPtr session = _model->session(); mtp::msg::ObjectHandles handles = session->GetObjectHandles(mtp::Session::AllStorages, mtp::ObjectFormat::Any, id); qDebug() << "found " << handles.ObjectHandles.size() << " objects in " << dirPath; for(mtp::ObjectId id : handles.ObjectHandles) input.push_back(qMakePair(dirPath, id)); } else { _total += oi.Size; files.push_back(qMakePair(prefix + "/" + oi.Filename, id)); } } qDebug() << "downloading " << files.size() << " file(s), " << _total << " bytes"; _startedAt = QDateTime::currentDateTime(); _aborted = false; if (_total < 1) _total = 1; for(const auto & file : files) { if (_aborted) break; emit executeCommand(new DownloadFile(file.first, file.second)); } emit executeCommand(new FinishQueue(currentParentId)); } void FileUploader::importMusic(const QString & path) { qDebug() << "importMusic " << path; _model->moveToThread(&_workerThread); _total = 0; QStringList files; files.push_back(path); QList commands; while(!files.empty()) { QString currentFile = files.front(); files.pop_front(); QFileInfo currentFileInfo(currentFile); if (currentFileInfo.isDir()) { qDebug() << "going into subdirectory" << currentFile; QDirIterator it(currentFile, QDirIterator::Subdirectories); QStringList dirs; while(it.hasNext()) { QString next = it.next(); QFileInfo fi(next); QString filename = fi.fileName(); if (filename == "." || filename == "..") continue; if (fi.isFile()) files.push_back(next); else if (fi.isDir()) dirs.push_back(next); } files += dirs; } else if (currentFileInfo.isFile()) { commands.push_back(new ImportFile(currentFile)); _total += currentFileInfo.size(); } } qDebug() << "uploading" << _total << "bytes"; if (_total < 1) _total = 1; _startedAt = QDateTime::currentDateTime(); _aborted = false; for(auto command: commands) { if (_aborted) break; emit executeCommand(command); } emit executeCommand(new FinishQueue(mtp::Session::Root)); } void FileUploader::abort() { qDebug() << "abort request"; _aborted = true; _worker->abort(); } android-file-transfer-linux-4.5/qt/fileuploader.h000066400000000000000000000041011501120463400221360ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_QT_FILEUPLOADER_H #define AFTL_QT_FILEUPLOADER_H #include #include #include #include #include #include class MtpObjectsModel; struct Command; class CommandQueue; namespace mtp { struct ObjectId; class Library; DECLARE_PTR(Library); } class FileUploader : public QObject { Q_OBJECT private: MtpObjectsModel * _model; QThread _workerThread; CommandQueue * _worker; qint64 _total; QDateTime _startedAt; bool _aborted; private slots: void onTotal(qint64 total); void onProgress(qint64 current); void onStarted(const QString &file); void onFinished(); public: explicit FileUploader(MtpObjectsModel * model, QObject *parent = 0); ~FileUploader(); void tryCreateLibrary(); mtp::LibraryPtr library() const; void upload(QStringList files, mtp::ObjectFormat format); void importMusic(const QString & path); void download(const QString &path, const QVector & objectIds); public slots: void abort(); signals: void executeCommand(Command *cmd); //incoming signals (from worker) void uploadStarted(QString file); void uploadProgress(float); void uploadSpeed(qint64); void finished(); }; #endif // FILEUPLOADER_H android-file-transfer-linux-4.5/qt/icons/000077500000000000000000000000001501120463400204315ustar00rootroot00000000000000android-file-transfer-linux-4.5/qt/icons/dark/000077500000000000000000000000001501120463400213525ustar00rootroot00000000000000android-file-transfer-linux-4.5/qt/icons/dark/folder-new.svg000066400000000000000000000011251501120463400241340ustar00rootroot00000000000000 android-file-transfer-linux-4.5/qt/icons/dark/go-next.svg000066400000000000000000000005141501120463400234540ustar00rootroot00000000000000 android-file-transfer-linux-4.5/qt/icons/dark/go-previous.svg000066400000000000000000000005131501120463400243510ustar00rootroot00000000000000 android-file-transfer-linux-4.5/qt/icons/dark/view-refresh.svg000066400000000000000000000012441501120463400245020ustar00rootroot00000000000000 android-file-transfer-linux-4.5/qt/icons/light/000077500000000000000000000000001501120463400215405ustar00rootroot00000000000000android-file-transfer-linux-4.5/qt/icons/light/folder-new.svg000066400000000000000000000011251501120463400243220ustar00rootroot00000000000000 android-file-transfer-linux-4.5/qt/icons/light/go-next.svg000066400000000000000000000005141501120463400236420ustar00rootroot00000000000000 android-file-transfer-linux-4.5/qt/icons/light/go-previous.svg000066400000000000000000000005131501120463400245370ustar00rootroot00000000000000 android-file-transfer-linux-4.5/qt/icons/light/view-refresh.svg000066400000000000000000000012441501120463400246700ustar00rootroot00000000000000 android-file-transfer-linux-4.5/qt/main.cpp000066400000000000000000000046551501120463400207600ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mainwindow.h" #include "utils.h" #include #include #include #include #include #include #if QT_VERSION >= 0x050000 # include #endif namespace { class Application : public QApplication { public: Application(int &argc, char **argv, int flags = ApplicationFlags): QApplication(argc, argv, flags) { } virtual bool notify ( QObject * receiver, QEvent * e ) { try { return QApplication::notify( receiver, e ); } catch ( const std::exception& e ) { QMessageBox::warning(0, "Error", fromUtf8(e.what())); return false; } } }; } int main(int argc, char *argv[]) { QApplication app(argc, argv); Q_INIT_RESOURCE(android_file_transfer); QCoreApplication::setApplicationName("aft-linux-qt"); QCoreApplication::setOrganizationDomain("whoozle.github.io"); QCoreApplication::setOrganizationName("whoozle.github.io"); #if QT_VERSION >= 0x050000 QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif QTranslator qtTranslator; qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); app.installTranslator(&qtTranslator); QTranslator aTranslator; aTranslator.load(":/android-file-transfer-linux_" + QLocale::system().name()); app.installTranslator(&aTranslator); MainWindow w; for (int i = 0; i < argc; ++i) { if (strcmp(argv[i], "-R") == 0) w.enableDeviceReset(true); else if (strcmp(argv[i], "-v") == 0) mtp::g_debug = true; } w.show(); if (!w.started()) return 1; return app.exec(); } android-file-transfer-linux-4.5/qt/mainwindow.cpp000066400000000000000000000751611501120463400222100ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mainwindow.h" #include "ui_mainwindow.h" #include "createdirectorydialog.h" #include "progressdialog.h" #include "renamedialog.h" #include "mtpobjectsmodel.h" #include "mtpstoragesmodel.h" #include "fileuploader.h" #include "devicesdialog.h" #include "utils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= 0x050000 # include #else # include #endif #include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), _ui(new Ui::MainWindow), _nam(), _clipboard(QApplication::clipboard()), _proxyModel(new QSortFilterProxyModel), _storageModel(), _objectModel(new MtpObjectsModel()), _uploader(new FileUploader(_objectModel, this)), _resetDevice(false), _networkReply() { _ui->setupUi(this); setWindowIcon(QIcon(":/android-file-transfer.png")); QString theme; { auto value = palette().text().color().value(); //trying to guess if it's dark or light theme theme = value > 128? "dark": "light"; qDebug() << "current text color value: " << value << ", guessed theme: " << theme; } _ui->actionBack->setIcon(QIcon(":/icons/" + theme + "/go-previous.svg")); _ui->actionGoDown->setIcon(QIcon(":/icons/" + theme + "/go-next.svg")); _ui->actionCreateDirectory->setIcon(QIcon(":/icons/" + theme + "/folder-new.svg")); _ui->actionRefresh->setIcon(QIcon(":/icons/" + theme + "/view-refresh.svg")); _ui->listView->setModel(_proxyModel); _proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); _proxyModel->sort(0); _proxyModel->setDynamicSortFilter(true); _objectModel->moveToThread(QApplication::instance()->thread()); connect(_ui->listView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), SLOT(updateActionsState())); connect(_ui->listView, SIGNAL(doubleClicked(QModelIndex)), SLOT(onActivated(QModelIndex))); connect(_ui->listView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(showContextMenu(QPoint))); connect(_ui->actionBack, SIGNAL(triggered()), SLOT(back())); connect(_ui->actionGoDown, SIGNAL(triggered()), SLOT(down())); connect(_ui->actionCreateDirectory, SIGNAL(triggered()), SLOT(createDirectory())); connect(_ui->actionUploadDirectory, SIGNAL(triggered()), SLOT(uploadDirectories())); connect(_ui->actionUploadAlbum, SIGNAL(triggered()), SLOT(uploadAlbum())); connect(_ui->actionImportMusic, SIGNAL(triggered()), SLOT(importMusic())); connect(_ui->actionImportMusicFiles, SIGNAL(triggered()), SLOT(importMusicFiles())); connect(_ui->actionUpload, SIGNAL(triggered()), SLOT(uploadFiles())); connect(_ui->actionRename, SIGNAL(triggered()), SLOT(renameFile())); connect(_ui->actionDownload, SIGNAL(triggered()), SLOT(downloadFiles())); connect(_ui->actionDelete, SIGNAL(triggered()), SLOT(deleteFiles())); connect(_ui->storageList, SIGNAL(activated(int)), SLOT(onStorageChanged(int))); connect(_ui->actionRefresh, SIGNAL(triggered()), SLOT(refresh())); connect(_ui->actionPaste, SIGNAL(triggered()), SLOT(pasteFromClipboard())); connect(_ui->actionShowThumbnails, SIGNAL(triggered(bool)), SLOT(showThumbnails(bool))); connect(_ui->actionRemoveCover, SIGNAL(triggered(bool)), SLOT(removeCover())); connect(_ui->actionAttachCover, SIGNAL(triggered(bool)), SLOT(attachCover())); connect(_ui->actionUploadFirmware, SIGNAL(triggered()), SLOT(uploadFirmware())); connect(_ui->actionRebootDevice, SIGNAL(triggered()), SLOT(rebootDevice())); connect(_objectModel, SIGNAL(onFilesDropped(QStringList)), SLOT(onFilesDropped(QStringList))); connect(_objectModel, SIGNAL(existingFileOverwrite(QString)), SLOT(confirmOverwrite(QString)), Qt::BlockingQueuedConnection); connect(_clipboard, SIGNAL(dataChanged()), SLOT(validateClipboard())); validateClipboard(); QToolButton * importMusic = dynamic_cast(_ui->mainToolBar->widgetForAction(_ui->actionImportMusic)); importMusic->setMenu(new QMenu(tr("Import Music"))); importMusic->menu()->addAction(_ui->actionImportMusicFiles); //fixme: find out how to specify alternative in designer _ui->actionBack->setShortcuts(_ui->actionBack->shortcuts() << QKeySequence("Alt+Up") << QKeySequence("Esc")); _ui->actionGoDown->setShortcuts(_ui->actionGoDown->shortcuts() << QKeySequence("Alt+Down") << QKeySequence("Enter")); _ui->actionCreateDirectory->setShortcuts(_ui->actionCreateDirectory->shortcuts() << QKeySequence("F7")); _ui->actionRefresh->setShortcuts(_ui->actionRefresh->shortcuts() << QKeySequence("Ctrl+R")); _ui->listView->setFocus(); } MainWindow::~MainWindow() { _proxyModel->setSourceModel(NULL); delete _objectModel; delete _ui; } void MainWindow::saveGeometry(const QString &name, const QWidget &widget) { QSettings settings; settings.setValue("geometry/" + name, widget.saveGeometry()); } void MainWindow::restoreGeometry(const QString &name, QWidget &widget) { QSettings settings; QVariant geometry = settings.value("geometry/" + name); if (geometry.isValid()) widget.restoreGeometry(geometry.toByteArray()); } void MainWindow::closeEvent(QCloseEvent *event) { QSettings settings; saveGeometry("main-window", *this); settings.setValue("state/main-window", saveState()); QMainWindow::closeEvent(event); } void MainWindow::replyReadyRead() { if (!_networkReply) { qWarning() << "replyReadyRead called without network reply object"; return; } auto data = _networkReply->read(128 * 1024); qDebug() << "read: " << data.size() << " bytes..."; _networkReplyBody.append(data); } void MainWindow::replyFinished(QNetworkReply * reply) { _networkReply = NULL; qDebug() << "got reply " << reply << " with status " << reply->error(); auto title = tr("MTPZ Keys Download"); if (reply->error() != 0) { QMessageBox::warning(this, title, tr("Could not download keys, please find the error below:\n\n%1\n\nPlease look for .mtpz-data file on the internet and manually install it to your home directory.").arg(reply->errorString())); return; } reply->open(QIODevice::ReadOnly); auto mtpzDataPath = getMtpzDataPath(); QFile destination(mtpzDataPath); qDebug() << "writing to " << destination.fileName(); if (!destination.open(QIODevice::WriteOnly)) { QMessageBox::warning(this, title, tr("Could not write keys to %1").arg(mtpzDataPath)); return; } if (destination.write(_networkReplyBody) == -1) QMessageBox::warning(this, title, tr("Could not write keys, please find the error below:\n\n%1\n\nPlease look for .mtpz-data file on the internet and manually install it to your home directory.").arg(destination.errorString())); destination.close(); reply->close(); reply->deleteLater(); try { _trustedApp = mtp::TrustedApp::Create(_session, toUtf8(mtpzDataPath)); if (!_trustedApp->KeysLoaded()) throw std::runtime_error("failed to load new keys"); qDebug() << "new keys loaded, authenticating..."; _trustedApp->Authenticate(); QMessageBox::information(this, title, tr("MTPZ keys have been installed to your system.")); tryCreateLibrary(); } catch (const std::exception & ex) { qWarning() << "failed to recreate session: " << ex.what(); QMessageBox::warning(this, title, tr("Your MTPZ keys failed to install or load.\n\nPlease restart the application to try again.\n\nException: %1").arg(ex.what())); } } QString MainWindow::getMtpzDataPath() { #if QT_VERSION >= 0x050000 auto path = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); #else auto path = QDesktopServices::storageLocation(QDesktopServices::HomeLocation); #endif path += "/.mtpz-data"; return path; } bool MainWindow::reconnectToDevice() { _session.reset(); _device.reset(); { DevicesDialog dialog(_resetDevice, this); int r = dialog.exec(); if (r == QDialog::Accepted) _device = dialog.getDevice(); } if (!_device) { QMessageBox::critical(this, tr("No MTP device found"), tr("No MTP device found")); return false; } qDebug() << "device found, opening session..."; static const int MaxAttempts = 3; QString error; for(int attempt = 0; attempt < MaxAttempts; ++attempt) { try { _session = _device->OpenSession(1); mtp::msg::DeviceInfo di = _session->GetDeviceInfo(); qDebug() << "device info" << fromUtf8(di.Manufacturer) << " " << fromUtf8(di.Model); auto path = getMtpzDataPath(); qDebug() << "mtpz-data path: " << path; _trustedApp = mtp::TrustedApp::Create(_session, toUtf8(path)); if (_trustedApp && !_trustedApp->KeysLoaded()) { QString title = tr("MTPZ Keys are Missing"); QString header = tr( "It seems your computer is missing an important bit for talking with MTPZ device: " "private keys and certificate material from Microsoft.\n" "This means that you can't download and upload files from/to such devices.\n\n" "Microsoft could have released that key material and documentation for MTPZ devices " "as they are not interested in those anymore.\n\n" "Because of the legal risks we can't bundle those keys, even though in some countries it's lawful to modify things to make them working again, " "just because you own it.\n\n" ); #ifdef MTPZ_DATA_SOURCE QMessageBox downloadKeys(QMessageBox::Question, title, header + tr( "Alternatively I (as an app) can offer you to download keys from the Internet.\n" "Can I download keys for you?\n\n" "(Please press Yes only if all of the above is legal in your country or you just don't care)." ), QMessageBox::Yes | QMessageBox::No ); int r = downloadKeys.exec(); if (r & QMessageBox::Yes) { qDebug() << "downloading keys"; if (!_nam) { _nam = new QNetworkAccessManager(this); connect(_nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); } _networkReply = _nam->get(QNetworkRequest(QUrl(MTPZ_DATA_SOURCE))); connect(_networkReply, SIGNAL(readyRead()), this, SLOT(replyReadyRead())); } #else QMessageBox downloadKeys(QMessageBox::Warning, title, header + tr( "You can look for .mtpz-data file on the internet, download it and place it in your home directory." )); downloadKeys.exec(); #endif } break; } catch(const mtp::usb::TimeoutException &ex) { qDebug() << "timed out getting device info: " << fromUtf8(ex.what()) << ", retrying..."; if (attempt + 1 == MaxAttempts) { QMessageBox::critical(this, tr("MTP"), tr("MTP device does not respond")); _device.reset(); return false; } } catch(const mtp::usb::DeviceNotFoundException &ex) { qDebug() << "device disconnected, retrying..."; } catch(const std::exception &ex) { error = fromUtf8(ex.what()); qWarning() << "open session/device info failed: " << error; } } if (!_session) { _device.reset(); QMessageBox::critical(this, tr("MTP"), tr("Could not open MTP session: %1").arg(error)); return false; } return true; } void MainWindow::showEvent(QShowEvent *) { if (!_device) { if (!reconnectToDevice()) return; QSettings settings; restoreGeometry("main-window", *this); restoreState(settings.value("state/main-window").toByteArray()); _storageModel = new MtpStoragesModel(this); while (true) { try { if (_storageModel->update(_session)) break; } catch(const mtp::usb::DeviceNotFoundException &ex) { qDebug() << "device disconnected, retrying..."; if (!reconnectToDevice()) return; } int r = QMessageBox::warning(this, tr("No MTP Storages"), tr("No MTP storage found, your device might be locked.\nPlease unlock and press Retry to continue or Abort to exit."), QMessageBox::Retry | QMessageBox::Abort); if (r & QMessageBox::Abort) { _device.reset(); return; } if (!reconnectToDevice()) return; } _ui->storageList->setModel(_storageModel); _objectModel->setSession(_session); onStorageChanged(_ui->storageList->currentIndex()); qDebug() << "session opened, starting"; _proxyModel->setSourceModel(_objectModel); if (_trustedApp && _trustedApp->KeysLoaded()) { qDebug() << "keys loaded, authenticated"; _trustedApp->Authenticate(); } tryCreateLibrary(); bool canUpload = _session->GetDeviceInfo().Supports(mtp::OperationCode::SendObjectInfo); _ui->actionCreateDirectory->setVisible(canUpload); _ui->actionUploadAlbum->setVisible(canUpload); _ui->actionUploadDirectory->setVisible(canUpload); _ui->actionUpload->setVisible(canUpload); } } void MainWindow::tryCreateLibrary() { if (_uploader->library()) return; _ui->actionUploadAlbum->setVisible(true); _ui->actionImportMusic->setVisible(false); if (mtp::Library::Supported(_session)) { ProgressDialog progressDialog(this, false); progressDialog.setWindowTitle(tr("Loading Media Library")); progressDialog.setModal(true); progressDialog.setValue(0); connect(_uploader, SIGNAL(uploadProgress(float)), &progressDialog, SLOT(setValue(float))); connect(_uploader, SIGNAL(uploadSpeed(qint64)), &progressDialog, SLOT(setSpeed(qint64))); connect(_uploader, SIGNAL(uploadStarted(QString)), &progressDialog, SLOT(setFilename(QString))); connect(_uploader, SIGNAL(finished()), &progressDialog, SLOT(accept())); connect(&progressDialog, SIGNAL(abort()), &progressDialog, SLOT(reject())); try { _uploader->tryCreateLibrary(); _ui->actionUploadAlbum->setVisible(false); _ui->actionImportMusic->setVisible(true); } catch (const std::exception & ex) { qWarning() << "importing music disabled: " << ex.what(); } progressDialog.exec(); } } QModelIndex MainWindow::mapIndex(const QModelIndex &index) { return _proxyModel->mapToSource(index); } void MainWindow::onStorageChanged(int idx) { if (!_storageModel) return; mtp::StorageId storageId = _storageModel->getStorageId(idx); qDebug() << "switching to storage id " << storageId.Id; _objectModel->setStorageId(storageId); _history.clear(); updateActionsState(); } void MainWindow::down() { QModelIndex index = mapIndex(_ui->listView->currentIndex()); activate(index); } void MainWindow::onActivated ( const QModelIndex & proxyIndex ) { QModelIndex index = mapIndex(proxyIndex); activate(index); } void MainWindow::activate(const QModelIndex & index) { QString name = _objectModel->data(index).toString(); if (_objectModel->enter(index.row())) _history.push_back(qMakePair(name, _objectModel->parentObjectId())); updateActionsState(); } void MainWindow::updateActionsState() { QModelIndexList rows = _ui->listView->selectionModel()->selectedRows(); _ui->actionDelete->setEnabled(!rows.empty()); _ui->actionDownload->setEnabled(!rows.empty()); _ui->actionRename->setEnabled(rows.size() == 1); _ui->actionGoDown->setEnabled(rows.size() == 1); _ui->actionBack->setEnabled(!_history.empty()); _ui->actionUploadFirmware->setEnabled(_session? _session->GetDeviceInfo().Supports(mtp::ObjectFormat::UndefinedFirmware): false); _ui->actionRebootDevice->setEnabled(_session? _session->GetDeviceInfo().Supports(mtp::OperationCode::RebootDevice): false); QStringList statusList; for(const auto & h : _history) statusList.push_back(h.first); _ui->statusBar->showMessage(statusList.join(QString::fromUtf8(" » "))); } void MainWindow::refresh() { qDebug() << "refreshing object list"; _objectModel->refresh(); } void MainWindow::downloadFiles() { QItemSelectionModel *selection =_ui->listView->selectionModel(); QModelIndexList rows = selection->selectedRows(); QVector objects; for(QModelIndex row : rows) { row = mapIndex(row); objects.push_back(_objectModel->objectIdAt(row.row())); } downloadFiles(objects); } void MainWindow::renameFile() { QItemSelectionModel *selection =_ui->listView->selectionModel(); QModelIndexList rows = selection->selectedRows(); if (rows.empty()) return; QModelIndex row = mapIndex(rows.at(0)); RenameDialog d(_objectModel->data(row).toString(), this); restoreGeometry("rename-dialog", d); if (d.exec()) _objectModel->rename(row.row(), d.name()); saveGeometry("rename-dialog", d); } void MainWindow::deleteFiles() { QItemSelectionModel *selection =_ui->listView->selectionModel(); QModelIndexList rows = selection->selectedRows(); int r = QMessageBox::question(this, tr("Deleting file(s)"), tr("Are you sure?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ); if (r != QMessageBox::Yes) return; MtpObjectList objects; for(QModelIndex row : rows) { row = mapIndex(row); objects.push_back(_objectModel->objectIdAt(row.row())); } _objectModel->deleteObjects(objects); } void MainWindow::showContextMenu ( const QPoint & pos ) { QItemSelectionModel *selection =_ui->listView->selectionModel(); QModelIndexList rows = selection->selectedRows(); QMenu menu(this); menu.addAction(_ui->actionCreateDirectory); menu.addAction(_ui->actionRename); menu.addAction(_ui->actionDownload); menu.addAction(_ui->actionDelete); bool showRSMenu = false; std::unordered_set visited; for(QModelIndex row : rows) { row = mapIndex(row); auto id = _objectModel->objectIdAt(row.row()); try { mtp::ObjectFormat format = static_cast(_session->GetObjectIntegerProperty(id, mtp::ObjectProperty::ObjectFormat)); if (visited.find(format) != visited.end()) continue; visited.insert(format); auto supportedProperties = _session->GetObjectPropertiesSupported(format); showRSMenu = supportedProperties.Supports(mtp::ObjectProperty::RepresentativeSampleData); } catch (const std::exception & ex) { qWarning() << "checking representative sample failed"; } } if (showRSMenu) { menu.addSeparator(); if (rows.size() == 1) menu.addAction(_ui->actionAttachCover); menu.addAction(_ui->actionRemoveCover); } menu.exec(_ui->listView->mapToGlobal(pos)); } void MainWindow::back() { if (_history.empty()) return; mtp::ObjectId oldParent = _objectModel->parentObjectId(); _history.pop_back(); mtp::ObjectId oid = _history.empty()? mtp::Session::Root: _history.back().second; _objectModel->setParent(oid); QModelIndex prevIndex = _objectModel->findObject(oldParent); if (prevIndex.isValid()) _ui->listView->setCurrentIndex(_proxyModel->mapFromSource(prevIndex)); updateActionsState(); } void MainWindow::createDirectory() { CreateDirectoryDialog d(this); restoreGeometry("create-directory-dialog", d); if (d.exec() && !d.name().isEmpty()) { try { _objectModel->createDirectory(d.name(), mtp::AssociationType::GenericFolder); } catch(const std::exception &ex) { QMessageBox::warning(this, tr("Error"), tr("Failed to create new directory:\n") + fromUtf8(ex.what()) ); } } saveGeometry("create-directory-dialog", d); } void MainWindow::uploadFiles(const QStringList &files, mtp::ObjectFormat format) { if (files.isEmpty()) return; qDebug() << "uploadFiles " << files << ", format: " << fromUtf8(mtp::ToString(format)); _uploadAnswer = 0; _proxyModel->setSourceModel(NULL); ProgressDialog progressDialog(this); progressDialog.setModal(true); progressDialog.setValue(0); connect(_uploader, SIGNAL(uploadProgress(float)), &progressDialog, SLOT(setValue(float))); connect(_uploader, SIGNAL(uploadSpeed(qint64)), &progressDialog, SLOT(setSpeed(qint64))); connect(_uploader, SIGNAL(uploadStarted(QString)), &progressDialog, SLOT(setFilename(QString))); connect(_uploader, SIGNAL(finished()), &progressDialog, SLOT(accept())); connect(&progressDialog, SIGNAL(abort()), _uploader, SLOT(abort())); _uploader->upload(files, format); progressDialog.exec(); _objectModel->moveToThread(QApplication::instance()->thread()); _proxyModel->setSourceModel(_objectModel); refresh(); } void MainWindow::downloadFiles(const QVector &objects) { if (objects.isEmpty()) return; QFileDialog d(this); QSettings settings; { QVariant ld = settings.value("the-latest-download-directory"); if (ld.isValid()) d.setDirectory(ld.toString()); } d.setAcceptMode(QFileDialog::AcceptSave); d.setFileMode(QFileDialog::Directory); d.setOption(QFileDialog::ShowDirsOnly, true); restoreGeometry("download-files", d); if (!d.exec()) return; QStringList selected = d.selectedFiles(); if (selected.isEmpty()) return; QString path = selected.at(0); saveGeometry("download-files", d); settings.setValue("the-latest-download-directory", path); downloadFiles(path, objects); } void MainWindow::downloadFiles(const QString & path, const QVector &objects) { qDebug() << "downloading to " << path; ProgressDialog progressDialog(this); progressDialog.setWindowTitle(tr("Download Progress")); progressDialog.show(); connect(_uploader, SIGNAL(uploadProgress(float)), &progressDialog, SLOT(setValue(float))); connect(_uploader, SIGNAL(uploadSpeed(qint64)), &progressDialog, SLOT(setSpeed(qint64))); connect(_uploader, SIGNAL(uploadStarted(QString)), &progressDialog, SLOT(setFilename(QString))); connect(_uploader, SIGNAL(finished()), &progressDialog, SLOT(accept())); connect(&progressDialog, SIGNAL(abort()), _uploader, SLOT(abort())); _uploader->download(path, objects); progressDialog.exec(); } void MainWindow::uploadFiles() { QFileDialog d(this); QSettings settings; { QVariant ld = settings.value("the-latest-directory"); if (ld.isValid()) d.setDirectory(ld.toString()); } d.setAcceptMode(QFileDialog::AcceptOpen); d.setFileMode(QFileDialog::ExistingFiles); d.setOption(QFileDialog::ShowDirsOnly, false); d.setOption(QFileDialog::ReadOnly, true); restoreGeometry("upload-files", d); if (!d.exec()) return; saveGeometry("upload-files", d); settings.setValue("the-latest-directory", d.directory().absolutePath()); uploadFiles(d.selectedFiles()); } void MainWindow::uploadDirectories() { QSettings settings; QFileDialog d(this); { QVariant ld = settings.value("the-latest-directory"); if (ld.isValid()) d.setDirectory(ld.toString()); } d.setAcceptMode(QFileDialog::AcceptOpen); d.setFileMode(QFileDialog::Directory); d.setOption(QFileDialog::ShowDirsOnly, true); d.setOption(QFileDialog::ReadOnly, true); restoreGeometry("upload-directories", d); if (!d.exec()) return; saveGeometry("upload-directories", d); settings.setValue("the-latest-directory", d.directory().absolutePath()); QStringList dirs(d.selectedFiles()); if (dirs.isEmpty()) return; uploadFiles(dirs); } void MainWindow::uploadAlbum() { QFileDialog d(this); QSettings settings; { QVariant ld = settings.value("the-latest-directory"); if (ld.isValid()) d.setDirectory(ld.toString()); } d.setAcceptMode(QFileDialog::AcceptOpen); d.setFileMode(QFileDialog::Directory); d.setOption(QFileDialog::ShowDirsOnly, true); d.setOption(QFileDialog::ReadOnly, true); restoreGeometry("upload-albums", d); if (!d.exec()) return; saveGeometry("upload-albums", d); settings.setValue("the-latest-directory", d.directory().absolutePath()); QStringList selected = d.selectedFiles(); if (selected.isEmpty()) return; for(const auto &path : selected) { uploadAlbum(path); back(); } } void MainWindow::importMusic() { importMusic(true); } void MainWindow::importMusicFiles() { importMusic(false); } void MainWindow::importMusic(bool directoryMode) { qDebug() << "import music, directory mode: " << directoryMode; QFileDialog d(this); QSettings settings; { QVariant ld = settings.value("the-latest-directory"); if (ld.isValid()) d.setDirectory(ld.toString()); } d.setAcceptMode(QFileDialog::AcceptOpen); d.setOption(QFileDialog::ReadOnly, true); if (directoryMode) { d.setFileMode(QFileDialog::Directory); d.setOption(QFileDialog::ShowDirsOnly, true); } else { d.setFileMode(QFileDialog::ExistingFiles); } restoreGeometry("import-music", d); if (!d.exec()) return; saveGeometry("import-music", d); settings.setValue("the-latest-directory", d.directory().absolutePath()); QStringList selected = d.selectedFiles(); if (selected.isEmpty()) return; for(const auto &path : selected) { importMusic(path); } } void MainWindow::onFilesDropped(const QStringList &files) { if (_uploader->library()) { for(auto & file : files) { importMusic(file); } } else uploadFiles(files); } namespace { bool HeuristicLess(const QString &s1, const QString &s2) { return GetCoverScore(s1) > GetCoverScore(s2); } } void MainWindow::uploadAlbum(QString dirPath) { QDir dir(dirPath); qDebug() << "adding directory " << dir.dirName(); mtp::ObjectId dirId = _objectModel->createDirectory(dir.dirName(), mtp::AssociationType::Album); _objectModel->setParent(dirId); _history.push_back(qMakePair(dir.dirName(), dirId)); QString cover, coverTarget; QStringList covers; { QStringList ext({"*.png", "*.jpg", "*.jpeg"}); covers = dir.entryList(ext, QDir::Files); std::sort(covers.begin(), covers.end(), &HeuristicLess); qDebug() << "covers" << covers; if (!covers.isEmpty()) cover = covers.front(); if (cover.endsWith(".png")) coverTarget = "albumart.png"; else coverTarget = "albumart.jpg"; qDebug() << "use " << cover << " as album art -> " << coverTarget; } _objectModel->uploadFile(dir.canonicalPath() + "/" + cover, coverTarget); QStringList files; for(QString file : dir.entryList(QDir::Dirs | QDir::Files | QDir::NoDot | QDir::NoDotDot)) { if (!covers.contains(file)) files.push_back(dir.canonicalPath() + "/" + file); } uploadFiles(files); } void MainWindow::importMusic(QString path) { qDebug() << "import music from " << path; ProgressDialog progressDialog(this); progressDialog.setWindowTitle(tr("Importing Progress")); progressDialog.show(); connect(_uploader, SIGNAL(uploadProgress(float)), &progressDialog, SLOT(setValue(float))); connect(_uploader, SIGNAL(uploadSpeed(qint64)), &progressDialog, SLOT(setSpeed(qint64))); connect(_uploader, SIGNAL(uploadStarted(QString)), &progressDialog, SLOT(setFilename(QString))); connect(_uploader, SIGNAL(finished()), &progressDialog, SLOT(accept())); connect(&progressDialog, SIGNAL(abort()), _uploader, SLOT(abort())); _uploader->importMusic(path); progressDialog.exec(); } void MainWindow::validateClipboard() { QStringList files = _objectModel->extractMimeData(_clipboard->mimeData()); _ui->actionPaste->setEnabled(!files.isEmpty()); } void MainWindow::pasteFromClipboard() { //fixme: CHECK THAT THE MODEL IS NOT IN UPLOADER NOW uploadFiles(_objectModel->extractMimeData(_clipboard->mimeData())); } bool MainWindow::confirmOverwrite(const QString &file) { int b = _uploadAnswer? _uploadAnswer: QMessageBox::question(this, tr("Overwrite confirmation"), tr("You are about to overwrite file ") + file, QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll); switch(b) { case QMessageBox::YesToAll: _uploadAnswer = QMessageBox::Yes; case QMessageBox::Yes: return true; case QMessageBox::NoToAll: _uploadAnswer = QMessageBox::No; case QMessageBox::No: return false; default: return false; } } void MainWindow::showThumbnails(bool enable) { QSize maxSize; if (enable) maxSize = QSize(192, 160); _ui->listView->setGridSize(maxSize); _ui->listView->setWrapping(enable); _ui->listView->setFlow(enable? QListView::LeftToRight: QListView::TopToBottom); _ui->listView->setViewMode(enable? QListView::IconMode: QListView::ListMode); _objectModel->enableThumbnail(enable, maxSize); //resets model/size hints, etc } void MainWindow::attachCover() { QItemSelectionModel *selection =_ui->listView->selectionModel(); QModelIndexList rows = selection->selectedRows(); if (rows.empty()) return; auto row = mapIndex(rows.at(0)); auto targetObjectId = _objectModel->objectIdAt(row.row()); qDebug() << "attaching cover to object " << targetObjectId.Id; QFileDialog d(this); QSettings settings; { QVariant ld = settings.value("the-latest-directory"); if (ld.isValid()) d.setDirectory(ld.toString()); } d.setAcceptMode(QFileDialog::AcceptOpen); d.setFileMode(QFileDialog::ExistingFile); d.setOption(QFileDialog::ShowDirsOnly, false); d.setOption(QFileDialog::ReadOnly, true); restoreGeometry("upload-files", d); if (!d.exec()) return; saveGeometry("upload-files", d); settings.setValue("the-latest-directory", d.directory().absolutePath()); auto files = d.selectedFiles(); if (files.empty()) return; QFile file(files.at(0)); if (!file.open(QIODevice::ReadOnly)) { QMessageBox::warning(this, tr("Attach Cover"), tr("Could not open selected file")); return; } auto buffer = file.readAll(); qDebug() << "read " << buffer.size() << " bytes of cover file"; file.close(); mtp::ByteArray value(buffer.begin(), buffer.end()); try { _session->SetObjectPropertyAsArray(targetObjectId, mtp::ObjectProperty::RepresentativeSampleData, value); } catch (const std::exception & ex) { QMessageBox::warning(this, tr("Attach Cover"), tr("Could not attach cover: %1").arg(ex.what())); } } void MainWindow::removeCover() { QItemSelectionModel *selection =_ui->listView->selectionModel(); QModelIndexList rows = selection->selectedRows(); for(QModelIndex row : rows) { row = mapIndex(row); auto id = _objectModel->objectIdAt(row.row()); try { _session->SetObjectPropertyAsArray(id, mtp::ObjectProperty::RepresentativeSampleData, {}); } catch(const std::exception & ex) { qWarning() << "failed to remove cover:" << ex.what(); } } } void MainWindow::uploadFirmware() { QFileDialog d(this); QSettings settings; { QVariant ld = settings.value("the-latest-firmware-directory"); if (ld.isValid()) d.setDirectory(ld.toString()); } d.setAcceptMode(QFileDialog::AcceptOpen); d.setFileMode(QFileDialog::ExistingFiles); d.setOption(QFileDialog::ShowDirsOnly, false); d.setOption(QFileDialog::ReadOnly, true); restoreGeometry("upload-firmware", d); if (!d.exec()) return; saveGeometry("upload-firmware", d); settings.setValue("the-latest-firmware-directory", d.directory().absolutePath()); uploadFiles(d.selectedFiles(), mtp::ObjectFormat::UndefinedFirmware); } void MainWindow::rebootDevice() { _session->RebootDevice(); } android-file-transfer-linux-4.5/qt/mainwindow.h000066400000000000000000000073221501120463400216470ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_QT_MAINWINDOW_H #define AFTL_QT_MAINWINDOW_H #include #include #include #include #include #include #include namespace Ui { class MainWindow; } namespace mtp { class Device; DECLARE_PTR(Device); class TrustedApp; DECLARE_PTR(TrustedApp); class Library; DECLARE_PTR(Library); class Session; DECLARE_PTR(Session); } class MtpObjectsModel; class MtpStoragesModel; class FileUploader; class QSortFilterProxyModel; class QClipboard; class QNetworkAccessManager; class QNetworkReply; class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); bool started() const { return _device != 0; } void enableDeviceReset(bool enable) { _resetDevice = enable; } private: void showEvent(QShowEvent *e); void closeEvent(QCloseEvent *event); QModelIndex mapIndex(const QModelIndex &index); void saveGeometry(const QString &name, const QWidget &widget); void restoreGeometry(const QString &name, QWidget &widget); static QString getMtpzDataPath(); void tryCreateLibrary(); void importMusic(bool directoryMode); private slots: bool reconnectToDevice(); void back(); void down(); void onActivated ( const QModelIndex & index ); void activate(const QModelIndex & index); void updateActionsState(); void showContextMenu ( const QPoint & pos ); void createDirectory(); void refresh(); void uploadFiles(); void uploadDirectories(); void uploadAlbum(); void uploadAlbum(QString path); void importMusic(); void importMusicFiles(); void importMusic(QString path); void downloadFiles(); void renameFile(); void deleteFiles(); void downloadFiles(const QVector &objects); void uploadFiles(const QStringList &files, mtp::ObjectFormat format = mtp::ObjectFormat::Any); void onFilesDropped(const QStringList &files); void onStorageChanged(int idx); void validateClipboard(); void pasteFromClipboard(); bool confirmOverwrite(const QString &file); void showThumbnails(bool enable); void attachCover(); void removeCover(); void uploadFirmware(); void rebootDevice(); public slots: void downloadFiles(const QString & path, const QVector &objects); void replyReadyRead(); void replyFinished(QNetworkReply*); private: Ui::MainWindow * _ui; QNetworkAccessManager * _nam; QClipboard * _clipboard; QSortFilterProxyModel * _proxyModel; MtpStoragesModel * _storageModel; MtpObjectsModel * _objectModel; FileUploader * _uploader; typedef QVector> History; History _history; int _uploadAnswer; mtp::DevicePtr _device; mtp::SessionPtr _session; mtp::TrustedAppPtr _trustedApp; mtp::LibraryPtr _mediaLibrary; bool _resetDevice; QNetworkReply * _networkReply; QByteArray _networkReplyBody; }; #endif // MAINWINDOW_H android-file-transfer-linux-4.5/qt/mainwindow.ui000066400000000000000000000156351501120463400220430ustar00rootroot00000000000000 MainWindow 0 0 839 621 Android File Transfer for Linux 50 false Qt::CustomContextMenu true false QAbstractItemView::DropOnly Qt::CopyAction QAbstractItemView::ExtendedSelection true 0 0 839 39 &File &Navigate false false TopToolBarArea false Upload Ctrl+U &CreateDirectory... Ctrl+N UploadDirectory &Back Backspace &Go Down Return Upload Album &Rename F2 D&elete Del &Download Ctrl+D E&xit Ctrl+Q Re&fresh F5 &Paste Ctrl+V true Show Thumbnails Show Thumbnails of images where available Import Music Remove Cover Attach Cover Import Music Files Import individual music files Upload Firmware... Reboot Device actionExit triggered() MainWindow close() -1 -1 335 293 android-file-transfer-linux-4.5/qt/mtpmimedata.cpp000066400000000000000000000016211501120463400223240ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mtpmimedata.h" MtpMimeData::MtpMimeData() { } android-file-transfer-linux-4.5/qt/mtpmimedata.h000066400000000000000000000017571501120463400220030ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_QT_MTPMIMEDATA_H #define AFTL_QT_MTPMIMEDATA_H class MtpMimeData : public QMimeData { public: MtpMimeData(); }; #endif // MTPMIMEDATA_H android-file-transfer-linux-4.5/qt/mtpobjectsmodel.cpp000066400000000000000000000265501501120463400232250ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mtpobjectsmodel.h" #include "qtobjectstream.h" #include "utils.h" #include #include #include #include #include #include #include #include #include #include #include //for mtime MtpObjectsModel::MtpObjectsModel(QObject *parent): QAbstractListModel(parent), _storageId(mtp::Session::AllStorages), _parentObjectId(mtp::Session::Root), _enableThumbnails(false) { } MtpObjectsModel::~MtpObjectsModel() { } void MtpObjectsModel::setStorageId(mtp::StorageId storageId) { _storageId = storageId; setParent(mtp::Session::Root); } void MtpObjectsModel::setParent(mtp::ObjectId parentObjectId) { beginResetModel(); _parentObjectId = parentObjectId; mtp::msg::ObjectHandles handles; try { handles = _session->GetObjectHandles(_storageId, mtp::ObjectFormat::Any, parentObjectId); } catch(const std::exception & ex) { qWarning() << "setParent failed:" << fromUtf8(ex.what()); } _rows.clear(); _rows.reserve(handles.ObjectHandles.size()); for(size_t i = 0; i < handles.ObjectHandles.size(); ++i) { mtp::ObjectId oid = handles.ObjectHandles[i]; _rows.append(Row(oid)); } endResetModel(); } bool MtpObjectsModel::enter(int idx) { if (idx < 0 || idx >= _rows.size()) return false; Row &row = _rows[idx]; if (row.IsAssociation(_session)) { setParent(row.ObjectId); return true; } else return false; } QModelIndex MtpObjectsModel::findObject(mtp::ObjectId objectId) const { auto idx = std::find_if(_rows.begin(), _rows.end(), [objectId](const Row & row) { return row.ObjectId == objectId; } ); return idx != _rows.end()? createIndex(std::distance(_rows.begin(), idx), 0): QModelIndex(); } QModelIndex MtpObjectsModel::findObject(const QString &filename) const { auto idx = std::find_if(_rows.begin(), _rows.end(), [filename, this](Row & row) { return fromUtf8(row.GetInfo(_session)->Filename) == filename; } ); return idx != _rows.end()? createIndex(std::distance(_rows.begin(), idx), 0): QModelIndex(); } void MtpObjectsModel::setSession(mtp::SessionPtr session) { beginResetModel(); _session = session; setParent(mtp::Session::Root); endResetModel(); } int MtpObjectsModel::rowCount(const QModelIndex &) const { return _rows.size(); } mtp::msg::ObjectInfoPtr MtpObjectsModel::Row::GetInfo(mtp::SessionPtr session) { if (!_info) { _info = std::make_shared(); try { *_info = session->GetObjectInfo(ObjectId); //qDebug() << fromUtf8(row.Info->Filename); } catch(const std::exception &ex) { qWarning() << "failed to get object info " << fromUtf8(ex.what()); } } return _info; } MtpObjectsModel::ThumbnailPtr MtpObjectsModel::Row::GetThumbnail(mtp::SessionPtr session, QSize maxSize) { if (!_thumbnail) { auto stream = std::make_shared(); _thumbnail = std::make_shared(); try { auto format = GetInfo(session)->ThumbFormat; if (format == 0) throw std::runtime_error("invalid thumbnail format"); qDebug() << "requesting thumbnail for " << ObjectId.Id << ", format: " << format; session->GetThumb(ObjectId, stream); auto & data = stream->GetData(); QPixmap pixmap; if (!pixmap.loadFromData(data.data(), data.size())) throw std::runtime_error("couldn't load pixmap"); *_thumbnail = pixmap.scaled(maxSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); qDebug() << "loaded " << data.size() << " bytes of thumbnail data"; } catch(const std::exception &ex) { qWarning() << "failed to get thumbnail" << fromUtf8(ex.what()); *_thumbnail = QIcon::fromTheme("image-missing").pixmap(maxSize); } } return _thumbnail; } bool MtpObjectsModel::Row::IsAssociation(mtp::SessionPtr session) { mtp::ObjectFormat format = GetInfo(session)->ObjectFormat; return format == mtp::ObjectFormat::Association; } void MtpObjectsModel::rename(int idx, const QString &fileName) { qDebug() << "renaming row " << idx << " to " << fileName; try { _session->SetObjectProperty(objectIdAt(idx), mtp::ObjectProperty::ObjectFilename, toUtf8(fileName)); } catch(const std::exception &ex) { qWarning() << "failed to rename object" << fromUtf8(ex.what()); } _rows[idx].ResetInfo(); emit dataChanged(createIndex(idx, 0), createIndex(idx, 0)); } void MtpObjectsModel::deleteObjects(const MtpObjectList &objects) { for(mtp::ObjectId objectId: objects) { try { qDebug() << "deleting object " << objectId; _session->DeleteObject(objectId); } catch(const std::exception &ex) { qWarning() << "failed to delete object " << fromUtf8(ex.what()); } } refresh(); } mtp::ObjectId MtpObjectsModel::objectIdAt(int idx) { return (idx >= 0 && idx < _rows.size())? _rows[idx].ObjectId: mtp::ObjectId(); } QVariant MtpObjectsModel::data(const QModelIndex &index, int role) const { int row_idx = index.row(); if (row_idx < 0 || row_idx > _rows.size()) return QVariant(); Row &row = _rows[row_idx]; switch(role) { case Qt::ToolTipRole: case Qt::DisplayRole: return fromUtf8(row.GetInfo(_session)->Filename); case Qt::FontRole: { QFont font; if (row.IsAssociation(_session)) font.setBold(true); return font; } case Qt::DecorationRole: if (_enableThumbnails) return *row.GetThumbnail(_session, QSize(_maxThumbnailSize.width(), _maxThumbnailSize.height() / 1.5f)); else return QVariant(); case Qt::SizeHintRole: if (_enableThumbnails) return _maxThumbnailSize; else return QVariant(); default: return QVariant(); } } mtp::ObjectId MtpObjectsModel::createDirectory(mtp::ObjectId parentObjectId, const QString &name, mtp::AssociationType type) { QModelIndex existingDir = findObject(name); if (existingDir.isValid()) return _rows.at(existingDir.row()).ObjectId; mtp::StorageId storageId = _storageId != mtp::Session::AllStorages? _storageId: mtp::Session::AnyStorage; auto noi = _session->CreateDirectory(toUtf8(name), parentObjectId, storageId, type); if (parentObjectId == _parentObjectId) { beginInsertRows(QModelIndex(), _rows.size(), _rows.size()); _rows.push_back(Row(noi.ObjectId)); endInsertRows(); } return noi.ObjectId; } bool MtpObjectsModel::uploadFile(mtp::ObjectId parentObjectId, const QString &filePath, QString filename, mtp::ObjectFormat format) { QFileInfo fileInfo(filePath); mtp::ObjectFormat objectFormat = format == mtp::ObjectFormat::Any? mtp::ObjectFormatFromFilename(toUtf8(filePath)): format; if (filename.isEmpty()) filename = fileInfo.fileName(); qDebug() << "uploadFile " << fileInfo.fileName() << " as " << filename; bool needReset = false; QModelIndex existingObject = findObject(filename); if (existingObject.isValid()) { if (!emit existingFileOverwrite(filename)) { qDebug() << "skipping, overwrite not confirmed"; return false; } try { _session->DeleteObject(_rows.at(existingObject.row()).ObjectId); } catch(const std::exception &ex) { qWarning() << "failed to delete object" << fromUtf8(ex.what()); return false; } needReset = parentObjectId == _parentObjectId; } std::shared_ptr object(new QtObjectInputStream(filePath)); if (!object->Valid()) { qWarning() << "file " << filePath << " could not be opened"; return false; } qDebug() << "sending " << fileInfo.size() << " bytes"; connect(object.get(), SIGNAL(positionChanged(qint64,qint64)), this, SIGNAL(filePositionChanged(qint64,qint64))); mtp::msg::ObjectInfo oi; oi.Filename = toUtf8(filename); oi.ObjectFormat = objectFormat; oi.ObjectCompressedSize = fileInfo.size(); mtp::msg::NewObjectInfo noi; try { noi = _session->SendObjectInfo(oi, _storageId != mtp::Session::AllStorages? _storageId: mtp::Session::AnyStorage, parentObjectId); qDebug() << "new object id: " << noi.ObjectId << ", sending..."; _session->SendObject(object); qDebug() << "ok"; } catch(const std::exception &ex) { qWarning() << "failed to send new object info " << fromUtf8(ex.what()); return false; } if (parentObjectId == _parentObjectId) { beginInsertRows(QModelIndex(), _rows.size(), _rows.size()); _rows.push_back(Row(noi.ObjectId)); endInsertRows(); } if (needReset) refresh(); return true; } bool MtpObjectsModel::sendFile(const QString &filePath) { try { std::shared_ptr object(new QtObjectInputStream(filePath)); connect(object.get(), SIGNAL(positionChanged(qint64,qint64)), this, SIGNAL(filePositionChanged(qint64,qint64))); _session->SendObject(object); return true; } catch(const std::exception &ex) { qWarning() << "failed to send file " << fromUtf8(ex.what()); return false; } } bool MtpObjectsModel::downloadFile(const QString &filePath, mtp::ObjectId objectId) { auto object = std::make_shared(filePath); if (!object->Valid()) { qWarning() << "cannot open file " << filePath; return false; } connect(object.get(), SIGNAL(positionChanged(qint64,qint64)), this, SIGNAL(filePositionChanged(qint64,qint64))); _session->GetObject(objectId, object); object.reset(); cli::ObjectOutputStream::SetModificationTime(filePath.toStdString(), _session->GetObjectModificationTime(objectId)); return true; } MtpObjectsModel::ObjectInfo MtpObjectsModel::getInfoById(mtp::ObjectId objectId) const { mtp::msg::ObjectInfo oi(_session->GetObjectInfo(objectId)); qint64 size = oi.ObjectCompressedSize; if (size == mtp::MaxObjectSize) size = _session->GetObjectIntegerProperty(objectId, mtp::ObjectProperty::ObjectSize); return ObjectInfo(fromUtf8(oi.Filename), oi.ObjectFormat, size); } QStringList MtpObjectsModel::extractMimeData(const QMimeData *data) { QStringList files; QList urls = data->urls(); for (auto url : urls) { //qDebug() << "url " << url; if (url.isLocalFile()) files.push_back(url.toLocalFile()); else qWarning() << "skipping non-local url" << url; } return files; } bool MtpObjectsModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { qDebug() << "data: " << data << action << row << column; if (action != Qt::CopyAction || !data) return false; QStringList files = extractMimeData(data); qDebug() << "files dropped: " << files; emit onFilesDropped(files); return true; } QStringList MtpObjectsModel::mimeTypes () const { return QStringList("text/uri-list"); } Qt::ItemFlags MtpObjectsModel::flags(const QModelIndex &index) const { Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); return defaultFlags | Qt::ItemIsDropEnabled; } void MtpObjectsModel::enableThumbnail(bool enable, QSize maxSize) { beginResetModel(); _enableThumbnails = enable; _maxThumbnailSize = maxSize; endResetModel(); } android-file-transfer-linux-4.5/qt/mtpobjectsmodel.h000066400000000000000000000101531501120463400226620ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_QT_MTPOBJECTSMODEL_H #define AFTL_QT_MTPOBJECTSMODEL_H #include #include #include #include #include typedef QVector MtpObjectList; class MtpObjectsModel : public QAbstractListModel { Q_OBJECT private: mtp::SessionPtr _session; mtp::StorageId _storageId; mtp::ObjectId _parentObjectId; bool _enableThumbnails; QSize _maxThumbnailSize; using ThumbnailPtr = std::shared_ptr; class Row { mtp::msg::ObjectInfoPtr _info; ThumbnailPtr _thumbnail; public: mtp::ObjectId ObjectId; Row(mtp::ObjectId id = mtp::ObjectId()): ObjectId(id) { } void ResetInfo() { _info.reset(); } mtp::msg::ObjectInfoPtr GetInfo(mtp::SessionPtr session); ThumbnailPtr GetThumbnail(mtp::SessionPtr session, QSize maxSize); bool IsAssociation(mtp::SessionPtr); }; mutable QVector _rows; signals: void filePositionChanged(qint64, qint64); void onFilesDropped(QStringList); bool existingFileOverwrite(QString); public: struct ObjectInfo { QString Filename; mtp::ObjectFormat Format; qint64 Size; ObjectInfo(): Filename(), Format(), Size(0) { } ObjectInfo(const QString &fname, mtp::ObjectFormat format, qint64 size): Filename(fname), Format(format), Size(size) { } }; MtpObjectsModel(QObject *parent = 0); ~MtpObjectsModel(); void enableThumbnail(bool enable, QSize maxSize); void setSession(mtp::SessionPtr session); mtp::SessionPtr session() const { return _session; } void setStorageId(mtp::StorageId storageId); void setParent(mtp::ObjectId parentObjectId); void refresh() { setParent(_parentObjectId); } bool enter(int idx); mtp::ObjectId objectIdAt(int idx); QModelIndex findObject(mtp::ObjectId objectId) const; QModelIndex findObject(const QString &filename) const; mtp::ObjectId parentObjectId() const { return _parentObjectId; } mtp::ObjectId createDirectory(mtp::ObjectId parentObjectId, const QString &name, mtp::AssociationType type = mtp::AssociationType::GenericFolder); mtp::ObjectId createDirectory(const QString &name, mtp::AssociationType type = mtp::AssociationType::GenericFolder) { return createDirectory(_parentObjectId, name, type); } bool uploadFile(mtp::ObjectId parentObjectId, const QString &filePath, QString filename = QString(), mtp::ObjectFormat format = mtp::ObjectFormat::Any); bool uploadFile(const QString &filePath, QString filename = QString(), mtp::ObjectFormat format = mtp::ObjectFormat::Any) { return uploadFile(_parentObjectId, filePath, filename, format); } bool sendFile(const QString &filePath); bool downloadFile(const QString &filePath, mtp::ObjectId objectId); void rename(int idx, const QString &fileName); ObjectInfo getInfoById(mtp::ObjectId objectId) const; void deleteObjects(const MtpObjectList &objects); QStringList extractMimeData(const QMimeData *data); virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; protected: virtual QStringList mimeTypes () const; virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); }; #endif // MTPOBJECTSMODEL_H android-file-transfer-linux-4.5/qt/mtpstoragesmodel.cpp000066400000000000000000000043161501120463400234170ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mtpstoragesmodel.h" #include "utils.h" #include MtpStoragesModel::MtpStoragesModel(QObject *parent): QAbstractListModel(parent) { } bool MtpStoragesModel::update(const mtp::SessionPtr &session) { beginResetModel(); _storages.clear(); mtp::msg::StorageIDs storages = session->GetStorageIDs(); for(auto id : storages.StorageIDs) { mtp::msg::StorageInfo info; try { info = session->GetStorageInfo(id); } catch (const mtp::InvalidResponseException &ex) { if (ex.Type == mtp::ResponseType::InvalidStorageID) return false; else throw; } _storages.append(qMakePair(id, info)); } mtp::msg::StorageInfo anyStorage; anyStorage.StorageDescription = toUtf8(tr("All storages (BUGS, BEWARE)")); _storages.append(qMakePair(mtp::Session::AllStorages, anyStorage)); endResetModel(); return !storages.StorageIDs.empty(); } mtp::StorageId MtpStoragesModel::getStorageId(int idx) const { return idx >= 0 && idx < _storages.size()? _storages[idx].first: mtp::Session::AllStorages; } int MtpStoragesModel::rowCount(const QModelIndex &parent) const { return _storages.size(); } QVariant MtpStoragesModel::data(const QModelIndex &index, int role) const { const mtp::msg::StorageInfo & si = _storages[index.row()].second; switch(role) { case Qt::DisplayRole: { auto name = si.GetName(); return fromUtf8(name); } default: return QVariant(); } } android-file-transfer-linux-4.5/qt/mtpstoragesmodel.h000066400000000000000000000026771501120463400230740ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_QT_MTPSTORAGESMODEL_H #define AFTL_QT_MTPSTORAGESMODEL_H #include #include #include #include class MtpStoragesModel : public QAbstractListModel { QVector > _storages; public: MtpStoragesModel(QObject *parent = 0); bool update(const mtp::SessionPtr &session); mtp::StorageId getStorageId(int idx) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; }; #endif // MTPSTORAGESMODEL_H android-file-transfer-linux-4.5/qt/progressdialog.cpp000066400000000000000000000063331501120463400230530ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "progressdialog.h" #include "ui_progressdialog.h" #include #include #include #include ProgressDialog::ProgressDialog(QWidget *parent, bool showAbort) : QDialog(parent), ui(new Ui::ProgressDialog), _progress(0), _duration(0) { ui->setupUi(this); ui->progressBar->setMaximum(10000); _animation = new QPropertyAnimation(this, "progress", this); _animation->setDuration(30000); _animation->setStartValue(0); _animation->setEndValue(1); _animation->start(); if (showAbort) { QPushButton *b = ui->buttonBox->button(QDialogButtonBox::Abort); Q_ASSERT(b); connect(b, SIGNAL(clicked(bool)), SLOT(onAbortButtonPressed())); } else ui->buttonBox->clear(); } ProgressDialog::~ProgressDialog() { delete ui; } void ProgressDialog::reject() { } void ProgressDialog::onAbortButtonPressed() { QPushButton *b = ui->buttonBox->button(QDialogButtonBox::Abort); Q_ASSERT(b); b->setEnabled(false); emit abort(); } void ProgressDialog::setProgress(float current) { if (current < 0) current = 0; if (current > 1) current = 1; ui->progressBar->setValue(current * 10000); } void ProgressDialog::setValue(float current) { //qDebug() << "setValue " << current; _duration += _animation->currentTime(); float currentAnimated = _animation->currentValue().toFloat(); if (_duration <= 0) return; float currentSpeed = current * 1000 / _duration; if (currentSpeed <= 0) return; int estimate = 1000 / currentSpeed; int duration = estimate - _duration; if (duration < 100) duration = 100; //qDebug() << current << currentSpeed << estimate; _animation->stop(); _animation->setStartValue(currentAnimated); _animation->setDuration(duration); _animation->start(); } void ProgressDialog::setSpeed(qint64 speed) { static constexpr double kB = 1000; static constexpr double MB = 1000 * kB; // k, M, G are metric prefixes static constexpr double GB = 1000 * MB; // 1024 is for binary prefixes if (speed < 2 * MB) ui->speedLabel->setText(tr("Speed: %1 kB/s").arg(speed / kB, 0, 'f', 1)); else if (speed < 2 * GB) ui->speedLabel->setText(tr("Speed: %1 MB/s").arg(speed / MB, 0, 'f', 1)); else ui->speedLabel->setText(tr("Speed: %1 GB/s").arg(speed / GB, 0, 'f', 1)); } void ProgressDialog::setFilename(const QString &filename) { ui->fileLabel->setText(filename); } void ProgressDialog::closeEvent(QCloseEvent *event) { event->ignore(); } android-file-transfer-linux-4.5/qt/progressdialog.h000066400000000000000000000032461501120463400225200ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_QT_PROGRESSDIALOG_H #define AFTL_QT_PROGRESSDIALOG_H #include namespace Ui { class ProgressDialog; } class QPropertyAnimation; class ProgressDialog : public QDialog { Q_OBJECT Q_PROPERTY(float progress READ progress WRITE setProgress); public: explicit ProgressDialog(QWidget *parent = 0, bool showAbort = true); ~ProgressDialog(); float progress() const { return _progress; } void setProgress(float value); signals: void abort(); public slots: void setSpeed(qint64 speed); void setFilename(const QString &filename); void setValue(float current); virtual void reject(); private slots: void onAbortButtonPressed(); private: void closeEvent(QCloseEvent *event); private: Ui::ProgressDialog *ui; QPropertyAnimation *_animation; float _progress; int _duration; }; #endif // PROGRESSDIALOG_H android-file-transfer-linux-4.5/qt/progressdialog.ui000066400000000000000000000045151501120463400227060ustar00rootroot00000000000000 ProgressDialog 0 0 334 138 0 0 Upload Progress true 24 Qt::Horizontal QDialogButtonBox::Abort buttonBox accepted() ProgressDialog accept() 248 254 157 274 buttonBox rejected() ProgressDialog reject() 316 260 286 274 android-file-transfer-linux-4.5/qt/qtobjectstream.cpp000066400000000000000000000015631501120463400230560ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "qtobjectstream.h" android-file-transfer-linux-4.5/qt/qtobjectstream.h000066400000000000000000000050671501120463400225260ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_QT_QTOBJECTSTREAM_H #define AFTL_QT_QTOBJECTSTREAM_H #include #include #include class QtObjectInputStream : public QObject, public mtp::IObjectInputStream, public mtp::CancellableStream { Q_OBJECT signals: void positionChanged(qint64, qint64); private: QFile _file; qint64 _size; int _progress; public: QtObjectInputStream(const QString &file) : _file(file), _size(_file.size()), _progress(-1) { _file.open(QFile::ReadOnly); } bool Valid() const { return _file.isOpen(); } virtual mtp::u64 GetSize() const { return _size; } virtual size_t Read(mtp::u8 *data, size_t size) { CheckCancelled(); qint64 r = _file.read(static_cast(static_cast(data)), size); if (r < 0) throw std::runtime_error(_file.errorString().toStdString()); int progress = _size > 0? _file.pos() * 1000L / _size: 0; if (progress != _progress) //throttle events a bit { _progress = progress; emit positionChanged(_file.pos(), _size); } return r; } }; class QtObjectOutputStream : public QObject, public mtp::IObjectOutputStream, public mtp::CancellableStream { Q_OBJECT signals: void positionChanged(qint64, qint64); private: QFile _file; qint64 _size; public: QtObjectOutputStream(const QString &file): _file(file) { _file.open(QFile::WriteOnly | QFile::Truncate); } bool Valid() const { return _file.isOpen(); } virtual size_t Write(const mtp::u8 *data, size_t size) { CheckCancelled(); qint64 r = _file.write(static_cast(static_cast(data)), size); if (r < 0) throw std::runtime_error(_file.errorString().toStdString()); emit positionChanged(_file.pos(), _size); return r; } }; #endif // QTOBJECTSTREAM_H android-file-transfer-linux-4.5/qt/renamedialog.cpp000066400000000000000000000022401501120463400224470ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "renamedialog.h" #include "ui_renamedialog.h" RenameDialog::RenameDialog(QString name, QWidget *parent) : QDialog(parent), ui(new Ui::RenameDialog) { ui->setupUi(this); ui->lineEdit->setText(name); } QString RenameDialog::name() const { return ui->lineEdit->text(); } RenameDialog::~RenameDialog() { delete ui; } android-file-transfer-linux-4.5/qt/renamedialog.h000066400000000000000000000022531501120463400221200ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_QT_RENAMEDIALOG_H #define AFTL_QT_RENAMEDIALOG_H #include namespace Ui { class RenameDialog; } class RenameDialog : public QDialog { Q_OBJECT public: explicit RenameDialog(QString name, QWidget *parent = 0); ~RenameDialog(); QString name() const; private: Ui::RenameDialog *ui; }; #endif // RENAMEDIALOG_H android-file-transfer-linux-4.5/qt/renamedialog.ui000066400000000000000000000033151501120463400223060ustar00rootroot00000000000000 RenameDialog 0 0 433 99 Dialog Rename Object Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() RenameDialog accept() 248 254 157 274 buttonBox rejected() RenameDialog reject() 316 260 286 274 android-file-transfer-linux-4.5/qt/translations/000077500000000000000000000000001501120463400220375ustar00rootroot00000000000000android-file-transfer-linux-4.5/qt/translations/android-file-transfer-linux_cs.ts000066400000000000000000000501761501120463400304210ustar00rootroot00000000000000 CommandQueue Loading media library… Načítání knihovny médií... Querying artists… Zjišťuji umělce... Loading artists… Načítám umělce... Querying albums… Zjišťuji alba... Loading albums… Načítám alba... Done Hotovo Setting cover for album %1 Nastavuji obal pro album %1 CreateDirectoryDialog Dialog Dialog Create New Directory Vytvořit Novou Složku DevicesDialog Dialog Dialog Rescan Devices Kill Users The following processes are keeping file descriptors for your device: Následující procesy drží popisovače souboru pro vaše zařízení: Device is busy Zařízení je zaneprázdněno Device is busy, maybe another process is using it. Zařízení je zaneprázdněno, možná jej používá jiný proces. Close other MTP applications and restart Android File Transfer. Press Abort to kill them or Ignore to try next device. Zavřete ostatní MTP aplikace a restartujte program. Zmáčkněte Zrušit pro jejich ukončení nebo Ignorovat pro použití dalšího zařízení. USB Device %1:%2 MainWindow Android File Transfer for Linux Android File Transfer pro Linux &File &Soubor &Navigate &Navigovat Upload Nahrát Ctrl+U Ctrl+U &CreateDirectory &Vytvořit Složku Ctrl+N Ctrl+N UploadDirectory Nahrrát Složku &Back &Zpět Backspace Backspace &Go Down &Jít Dolů Return Zpět Upload Album Nahrát Album &Rename &Přejmenovat F2 F2 D&elete &Smazat Del Del &Download &Stáhnout Ctrl+D Ctrl+D E&xit U&končit Ctrl+Q Ctrl+Q Re&fresh &Obnovit F5 F5 &Paste &Vložit Ctrl+V Ctrl+V Show Thumbnails Zobrazit Náhledy Show Thumbnails of images where available Zobrazit Náhledy obrázků Import Music Importovat Hudbu Remove Cover Odstranit Obal Attach Cover Vložit Obal Import Music Files Importovat Hudební Soubory Import individual music files Importovat jednotlivé hudební soubory MTPZ Keys Download Stáhnout MTPZ klíče Could not download keys, please find the error below: %1 Please look for .mtpz-data file on the internet and manually install it to your home directory. Nemohu stáhnout klíče, prosím chybu najdete tady: %1 Prosím hledejte .mtpz-data soubor na internetu a vložte jej do své domovské složky. Could not write keys to %1 Nemohu zapsat klíče do %1 Could not write keys, please find the error below: %1 Please look for .mtpz-data file on the internet and manually install it to your home directory. Nemohu zapsat klíče, prosím chybu najdete tady: %1 Prosím hledejte .mtpz-data soubor na internetu a vložte jej do své domovské složky. MTPZ keys have been installed to your system. MTPZ klíče byly nainstalovány do vašeho systému. Your MTPZ keys failed to install or load. Please restart the application to try again. Exception: %1 Instalace nebo načtení Vašich MTPZ klíčů selhala Prosím restartujte aplikaci pro nový pokus Vyjímka: %1 The following processes are keeping file descriptors for your device: Následující procesy drží popisovače souboru pro vaše zařízení: Device is busy Zařízení je zaneprázdněno Device is busy, maybe another process is using it. Zařízení je zaneprázdněno, možná jej používá jiný proces. Close other MTP applications and restart Android File Transfer. Press Abort to kill them or Ignore to try next device. Zavřete ostatní MTP aplikace a restartujte program. Zmáčkněte Zrušit pro jejich ukončení nebo Ignorovat pro použití dalšího zařízení. Device::Find failed Zařízení::Hledání selhalo MTP device could not be opened at the moment Failure: %1 Press Reset to reset them or Ignore to try next device. MTP zařízení nemohlo být nyní otevřeno Problém: %1 Zmáčkněte Reset pro obnovu nebo Ignorovat pro použití dalšího zařízení. No MTP device found Nenalezeno žádné MTP zařízení MTPZ Keys are Missing MTPZ Klíče Nenalezeny It seems your computer is missing an important bit for talking with MTPZ device: private keys and certificate material from Microsoft. This means that you can't download and upload files from/to such devices. Microsoft could have released that key material and documentation for MTPZ devices as they are not interested in those anymore. Because of the legal risks we can't bundle those keys, even though in some countries it's lawful to modify things to make them working again, just because you own it. Vypadá to, že váš počítač postrádá důležitou součást pro komunikaci s MTPZ zařízením: privítní klíče a certifikát od Microsoftu To znamená, že nemůžete stáhovat nebo nahrávat soubory z/do tohoto zařízení. Microsoft mohl vydat tyto klíčové soubory a dokumentaci pro MTPZ zařízení protože už o to nemají zájem. Protože je monžost překročení legálních restrikcí, nemůžeme přiložit tyto klíče, přestože v některých zemích je to legální - spravit věci tak, aby byly opět funkční, protože je vlastníte. Alternatively I (as an app) can offer you to download keys from the Internet. Can I download keys for you? (Please press Yes only if all of the above is legal in your country or you just don't care). Jako alternativa pro vás mohu stáhnout klíče z Internetu automaticky. Stáhnout klíče? (Zmáčkněte Ano jen v případě, že je řečené u vás legální nebo je vám to jedno). You can look for .mtpz-data file on the internet, download it and place it in your home directory. Můžete najít .mtpz-data soubor na internetu a stáhnou jej do vaší domovské složky. MTP MTP MTP device does not respond MTP zařízení neodpovídá Could not open MTP session: %1 Nemohu otevřít MTP sezení: %1 No MTP Storages Žádné MTP Uložiště No MTP storage found, your device might be locked. Please unlock and press Retry to continue or Abort to exit. Nenalezeno žádné MTP uložiště, vaše zařízení může být zamčeno. Prosím odemčete jej a zmáčknětě Zkust znovu pro pokračování nebo Zrušit pro ukončení. Loading Media Library Načítání Knihovny Médií Deleting file(s) Mazání souboru(ů) Are you sure? Opravdu? Error Chyba Failed to create new directory: Vytvoření nové složky selhalo: Download Progress Postup Stahování Importing Progress Postup Importu Overwrite confirmation Potvrzení přepsání You are about to overwrite file Chystáte se přepsat soubor Could not open selected file Nemohu otevřít vybraný soubor Could not attach cover: %1 Nemohu vložit obal: %1 MtpStoragesModel All storages (BUGS, BEWARE) Všechna uložiště (POZOR, S CHYBAMI) ProgressDialog Upload Progress Postup Nahrávání Speed: %1 kB/s Rychlost: %1 kB/s Speed: %1 MB/s Rychlost: %1 MB/s Speed: %1 GB/s Rychlost: %1 GB/s RenameDialog Dialog Dialog Rename Object Přejmenovat Objekt android-file-transfer-linux-4.5/qt/translations/android-file-transfer-linux_it.ts000066400000000000000000000442421501120463400304250ustar00rootroot00000000000000 CommandQueue Loading media library… Querying artists… Loading artists… Querying albums… Loading albums… Done Setting cover for album %1 CreateDirectoryDialog Dialog Dialogo Create New Directory Crea Nuova Cartella DevicesDialog Dialog Dialogo Rescan Devices Kill Users The following processes are keeping file descriptors for your device: Device is busy Dispositivo è occupato Device is busy, maybe another process is using it. Close other MTP applications and restart Android File Transfer. Press Abort to kill them or Ignore to try next device. USB Device %1:%2 MainWindow Android File Transfer for Linux Android File Transfer per Linux &File &File &Navigate &Navigare Upload Caricare Ctrl+U Ctrl+U &CreateDirectory &CreaDirectory Ctrl+N Ctrl+N UploadDirectory Carica Directory &Back &Indietro Backspace BACKSPACE &Go Down &Scendere Return Ritornare Upload Album Carica Album &Rename &Rinomina F2 F2 D&elete &Cancella Del Del &Download &Scarica Ctrl+D Ctrl+D E&xit U&scita Ctrl+Q Ctrl+Q Re&fresh &Aggiorna F5 F5 &Paste &Incolla Show Thumbnails Show Thumbnails of images where available Import Music Remove Cover Attach Cover Import Music Files Import individual music files Paste Incolla Ctrl+V Ctrl+V Device is busy Dispositivo è occupato Device is busy, maybe another process is using it. Close other MTP applications and restart Android File Transfer. Dispositivo è occupato, forse un altro processo lo sta usando. Chiudere altre applicazioni MTP e riavviare Android File Transfer. MTPZ Keys Download Could not download keys, please find the error below: %1 Please look for .mtpz-data file on the internet and manually install it to your home directory. Could not write keys to %1 Could not write keys, please find the error below: %1 Please look for .mtpz-data file on the internet and manually install it to your home directory. MTPZ keys have been installed to your system. Your MTPZ keys failed to install or load. Please restart the application to try again. Exception: %1 No MTP device found Nessun dispositivo MTP trovato MTPZ Keys are Missing It seems your computer is missing an important bit for talking with MTPZ device: private keys and certificate material from Microsoft. This means that you can't download and upload files from/to such devices. Microsoft could have released that key material and documentation for MTPZ devices as they are not interested in those anymore. Because of the legal risks we can't bundle those keys, even though in some countries it's lawful to modify things to make them working again, just because you own it. Alternatively I (as an app) can offer you to download keys from the Internet. Can I download keys for you? (Please press Yes only if all of the above is legal in your country or you just don't care). You can look for .mtpz-data file on the internet, download it and place it in your home directory. MTP MTP MTP device does not respond Il dispositivo MTP non risponde Could not open MTP session: %1 No MTP Storages Nessun Dispositivo MTP No MTP storage found, your device might be locked. Please unlock and press Retry to continue or Abort to exit. Nessun dispositivo MTP trovato, il dispositivo potrebbe essere bloccato. Si prega di sbloccare e premere Ritenta per continuare o Interrompere per uscire. Loading Media Library Deleting file(s) Eliminazione di file Are you sure? Sie sicuri? Error Errore Failed to create new directory: Impossibile creare una nuova directory: Download Progress Importing Progress Overwrite confirmation Confermare la Sovrascrittura You are about to overwrite file Si sta per sovrascrivere file Could not open selected file Could not attach cover: %1 MtpStoragesModel All storages (BUGS, BEWARE) Tutti percorsi di stoccaggio (BUG, ATTENZIONE) ProgressDialog Upload Progress Avanzamento Caricamento Speed: Velocità: kB/s kB/s MB/s MB/s GB/s GB/s Speed: %1 kB/s Speed: %1 MB/s Speed: %1 GB/s RenameDialog Dialog Dialogo Rename Object Rinomina Oggetto android-file-transfer-linux-4.5/qt/translations/android-file-transfer-linux_nl.ts000066400000000000000000000521101501120463400304130ustar00rootroot00000000000000 CommandQueue Loading media library… Bezig met laden van mediaverzameling… Querying artists… Bezig met zoeken naar artiesten… Loading artists… Bezig met laden van artiesten… Querying albums… Bezig met zoeken naar albums… Loading albums… Bezig met laden van albums… Loading media library… Querying artists… Loading artists… Querying albums… Loading albums… Done Voltooid Setting cover for album %1 Bezig met instellen van hoes bij %1 CreateDirectoryDialog Dialog Venster Create New Directory Nieuwe map DevicesDialog Dialog Venster Rescan Devices Kill Users The following processes are keeping file descriptors for your device: De volgende processen bevatten bestandsomschrijvingen van je apparaat: Device is busy Apparaat is bezig Device is busy, maybe another process is using it. Het apparaat is bezig. Mogelijk is het in gebruik door een ander proces. Close other MTP applications and restart Android File Transfer. Press Abort to kill them or Ignore to try next device. Sluit andere mtp-programma's en herstart Android-bestandsoverdracht. Druk op 'Afbreken' om ze af te sluiten of 'Negeren' om een ander apparaat te proberen. USB Device %1:%2 MainWindow Android File Transfer for Linux Android-bestandsoverdracht voor Linux &File &Bestand &Navigate &Ga Upload Uploaden Ctrl+U Ctrl+U &CreateDirectory Nieuwe &map Ctrl+N Ctrl+N UploadDirectory Map uploaden &Back &Terug Backspace Backspace &Go Down &Ga omlaag Return Enter Upload Album Album uploaden &Rename Naam &wijzigen F2 F2 D&elete Verwijd&eren Del Del &Download &Downloaden Ctrl+D Ctrl+D E&xit Af&sluiten Ctrl+Q Ctrl+Q Re&fresh Her&laden F5 F5 &Paste &Plakken Ctrl+V Ctrl+V Show Thumbnails Miniaturen tonen Show Thumbnails of images where available Toon miniatuurvoorbeelden (indien beschikbaar) Import Music Muziek importeren Remove Cover Hoes verwijderen Attach Cover Hoes bijsluiten Import Music Files Muziekbestanden importeren Import individual music files Importeer losse muziekbestanden MTPZ Keys Download MTPZ-sleutels downloaden Could not download keys, please find the error below: %1 Please look for .mtpz-data file on the internet and manually install it to your home directory. De sleutels kunnen niet worden opgehaald vanwege de volgende fout: %1 Zoek handmatig naar .mtpz-gegevensbestanden op het internet en plaats ze in je persoonlijke map. Could not write keys to %1 De sleutels kunnen niet worden weggeschreven naar %1 Could not write keys, please find the error below: %1 Please look for .mtpz-data file on the internet and manually install it to your home directory. De sleutels kunnen niet worden weggeschreven vanwege de volgende fout: %1 Zoek handmatig naar .mtpz-gegevensbestanden op het internet en plaats ze in je persoonlijke map. MTPZ keys have been installed to your system. De mtpz-sleutels zijn op je systeem geplaatst. Your MTPZ keys failed to install or load. Please restart the application to try again. Exception: %1 Je mtpz-sleutels kunnen niet worden geplaatst of geladen. Herstart het programma en probeer het opnieuw. Uitzonderingsfout: %1 The following processes are keeping file descriptors for your device: De volgende processen bevatten bestandsomschrijvingen van je apparaat: Device is busy Apparaat is bezig Device is busy, maybe another process is using it. Het apparaat is bezig. Mogelijk is het in gebruik door een ander proces. Close other MTP applications and restart Android File Transfer. Press Abort to kill them or Ignore to try next device. Sluit andere mtp-programma's en herstart Android-bestandsoverdracht. Druk op 'Afbreken' om ze af te sluiten of 'Negeren' om een ander apparaat te proberen. Device::Find failed Apparaat niet aangetroffen MTP device could not be opened at the moment Failure: %1 Press Reset to reset them or Ignore to try next device. Het mtp-apparaat kan momenteel niet worden benaderd. Foutmelding: %1 Druk op 'Herstellen' om te herstellen of 'Negeren' om een ander apparaat te proberen. No MTP device found Geen mtp-apparaat aangetroffen MTPZ Keys are Missing De mtpz-sleutels ontbreken It seems your computer is missing an important bit for talking with MTPZ device: private keys and certificate material from Microsoft. This means that you can't download and upload files from/to such devices. Microsoft could have released that key material and documentation for MTPZ devices as they are not interested in those anymore. Because of the legal risks we can't bundle those keys, even though in some countries it's lawful to modify things to make them working again, just because you own it. Het lijkt er op dat je computer een belangrijk onderdeel mist voor het communiceren met je mtpz-apparaat: privésleutels en certificaatmateriaal van Microsoft. Dit houdt in dat je geen bestanden kunt downloaden en uploaden van/naar zulke apparaten. Microsoft had dit materiaal beschikbaar kunnen stellen aangezien ze niet meer geïnteresseerd zijn in deze apparaten. Vanwege juridische conflicten kunnen deze sleutels niet meeleveren, zelfs al is dat in jouw land mogelijk legaal. Alternatively I (as an app) can offer you to download keys from the Internet. Can I download keys for you? (Please press Yes only if all of the above is legal in your country or you just don't care). Het enige wat ik je kan bieden is de mogelijkheid deze sleutels te downloaden van het internet. Zal ik dat voor je doen? (klik alleen op 'Ja' als dit legaal is in je land of als de wet je niet interesseert) You can look for .mtpz-data file on the internet, download it and place it in your home directory. Je kunt .mtpz-gegevens vinden op het internet en deze in je persoonlijke map plaatsen. MTP MTP MTP device does not respond MTP-apparaat reageert niet Could not open MTP session: %1 De mtp-sessie kan niet worden geopend: %1 No MTP Storages Geen mtp-apparaten No MTP storage found, your device might be locked. Please unlock and press Retry to continue or Abort to exit. Geen mtp-apparaat aangetroffen - wellicht is je apparaat vergrendeld. Ontgrendel je apparaat en druk op 'Opnieuw' of 'Afbreken'. Loading Media Library Bezig met laden van mediaverzameling Deleting file(s) Bezig met verwijderen van bestand(en) Are you sure? Weet je het zeker? Error Fout Failed to create new directory: De map kan niet worden aangemaakt: Download Progress Downloadvoortgang Importing Progress Importvoortgang Overwrite confirmation Overschrijven bevestigen You are about to overwrite file Je staat op het punt om het volgende te overschrijven: Could not open selected file Kan het geselecteerde bestand niet openen Could not attach cover: %1 De hoes kan niet worden bijgesloten: %1 MtpStoragesModel All storages (BUGS, BEWARE) Alle opslagapparaten (LET OP: MOGELIJK ONBETROUWBAAR) ProgressDialog Upload Progress Uploadvoortgang Speed: %1 kB/s Snelheid: %1 kB/s Speed: %1 MB/s Snelheid: %1 MB/s Speed: %1 GB/s Snelheid: %1 GB/s RenameDialog Dialog Venster Rename Object Itemnaam wijzigen android-file-transfer-linux-4.5/qt/translations/android-file-transfer-linux_ru.ts000066400000000000000000000605721501120463400304430ustar00rootroot00000000000000 CommandQueue Loading media library… Загрузка библиотеки… Querying artists… Поиск исполнителей… Loading artists… Загрузка исполнителей… Querying albums… Поиск альбомов… Loading albums… Загрузка альбомов… Done Загрузка завершена Setting cover for album %1 Устанавливаем обложку для альбома %1 CreateDirectoryDialog Dialog Новый каталог Create New Directory Создать новый каталог DevicesDialog Dialog Диалог Rescan Devices Сканировать Устройства Kill Users Убить использующие процессы The following processes are keeping file descriptors for your device: Следующие процессы удерживают файловые дескрипторы вашего устройства: Device is busy Устройство занято Device is busy, maybe another process is using it. Устройство занято, может быть какой-нибудь другой процесс его использует. Close other MTP applications and restart Android File Transfer. Press Abort to kill them or Ignore to try next device. Закройте другие MTP приложения и перезапустите Android File Transfer Нажмите "Прервать" чтобы убить сторонний процесс, или "Игнорировать" чтобы попробовать следующее устройство. USB Device %1:%2 Усб Устройство %1:%2 MainWindow Android File Transfer for Linux Android File Transfer для Linux &File &Файл &Navigate &Навигация Upload Закачать Ctrl+U &CreateDirectory Создать &каталог Ctrl+N UploadDirectory Закачать каталог &Back &Назад Backspace &Go Down &Зайти Return Upload Album Закачать альбом &Rename &Переименовать F2 D&elete &Удалить Del &Download &Скачать Ctrl+D E&xit В&ыход Ctrl+Q Re&fresh &Обновить F5 &Paste &Вставить Show Thumbnails Показывать эскизы Show Thumbnails of images where available Показывай эскизы где можно Import Music Импортировать музыку Remove Cover Убрать обложку Attach Cover Установить обложку Import Music Files Импортировать файлы с музыкой Import individual music files Импортировать отдельные файлы с музыкой Paste Вставить Ctrl+V Device is busy Устройство занято Device is busy, maybe another process is using it. Close other MTP applications and restart Android File Transfer. Устройство занято, вероятно, какой-то другой процесс в данный момент его использует. Закройте другие MTP приложения и перезапустите Android File Transfer for Linux. MTPZ Keys Download Загрузить ключи MTPZ Could not download keys, please find the error below: %1 Please look for .mtpz-data file on the internet and manually install it to your home directory. Не получилось загрузить ключи, ошибка: %1 Вы можете самостоятельно найти файл .mtpz-data в интернете и положить его в вашу домашную директорию. Could not write keys to %1 Не получилось записать ключи в %1 Could not write keys, please find the error below: %1 Please look for .mtpz-data file on the internet and manually install it to your home directory. Не получилось загрузить ключи, ошибка: %1 Вы можете самостоятельно найти файл .mtpz-data в интернете и положить его в вашу домашную директорию. MTPZ keys have been installed to your system. MTPZ ключи были успешно установлены. Your MTPZ keys failed to install or load. Please restart the application to try again. Exception: %1 Не удалось установить ваши MTPZ ключи. Перезапустите приложение чтобы попробовать ещё раз. Ошибка: %1 The following processes are keeping file descriptors for your device: Следующие процессы удерживают файловые дескрипторы вашего устройства: Device is busy, maybe another process is using it. Устройство занято, может быть какой-нибудь другой процесс его использует. Close other MTP applications and restart Android File Transfer. Press Abort to kill them or Ignore to try next device. Закройте другие MTP приложения и перезапустите Android File Transfer Нажмите "Прервать" чтобы убить сторонний процесс, или "Игнорировать" чтобы попробовать следующее устройство. Device::Find failed Device::Find взорвалось MTP device could not be opened at the moment Failure: %1 Press Reset to reset them or Ignore to try next device. MTP устройство не может быть открыто в данный момент Ошибка: %1 Нажмите "Сброс" чтобы попытаться сбросить устройство или "Игнорировать" чтобы попробовать другие устройства. No MTP device found Не найдено ни одного устройства MTP MTPZ Keys are Missing MTPZ ключи не найдены It seems your computer is missing an important bit for talking with MTPZ device: private keys and certificate material from Microsoft. This means that you can't download and upload files from/to such devices. Microsoft could have released that key material and documentation for MTPZ devices as they are not interested in those anymore. Because of the legal risks we can't bundle those keys, even though in some countries it's lawful to modify things to make them working again, just because you own it. Похоже, что ваш компьютер не содержит важного файла для общения с MTPZ устройствами: ключи и сертификаты от Microsoft. Это означает, вы не может скачивать и закачивать файлы на MTPZ устройства. Майкрософт мог бы выложить ключи и документацию о MTPZ, так как они не очень заинтересованы в развитии MTPZ или производстве MTPZ устройств. Так как поставлять чужие ключи может оказаться юридически рискованным делом, мы напоминаем, что в некоторых странах (включая Россию) — вы можете легально модифицировать ваше неработающее устройство, чтобы оно начало выполнять свои функции. Alternatively I (as an app) can offer you to download keys from the Internet. Can I download keys for you? (Please press Yes only if all of the above is legal in your country or you just don't care). В качестве альтернативы, я (как приложение) могу предложить вам скачать ключи из сети интернет. Скачать ключи? Пожалуйста нажмите "Да" только если всё вышеуказанное легально в вашей стране. (Или вам всё равно) You can look for .mtpz-data file on the internet, download it and place it in your home directory. Вы можете найти файл ".mtpz-data" в интернете и просто положить его в домашнюю директорию. MTP MTP device does not respond MTP устройство не отвечает Could not open MTP session: %1 Не получается открыть MTP сессию: %1 No MTP Storages Нет хранилищ MTP No MTP storage found, your device might be locked. Please unlock and press Retry to continue or Abort to exit. Нет ни одного хранилища, возможно ваше устройство заблокировано Разблокируйте устройство и нажмите Повторить чтобы попробовать ещё раз или Прервать, чтобы выйти. Loading Media Library Загружаем библиотеку Deleting file(s) Удаление файла(ов) Are you sure? Вы уверены? Error Ошибка Failed to create new directory: Не удалось создать новый каталог: Download Progress Загрузка Importing Progress Импорт Overwrite confirmation Подтверждение перезаписи You are about to overwrite file Вы собираетесь перезаписать файл Could not open selected file Не получилось открыть выбранный файл Could not attach cover: %1 Не удалось установить обложку: %1 MtpStoragesModel All storages (BUGS, BEWARE) Все хранилища (РАБОТАЕТ НЕ ВЕЗДЕ, БАГИ) ProgressDialog Upload Progress Прогресс Speed: Скорость: kB/s кБ/с MB/s МБ/с GB/s ГБ/с Speed: %1 Kb/s Скорость: %1 Кб/c Speed: %1 Mb/s Скорость: %1 Мб/c Speed: %1 Gb/s Скорость: %1 Гб/c Speed: %1 kB/s Скорость: %1 КБайт/c Speed: %1 MB/s Скорость: %1 Мбайт/c Speed: %1 GB/s Скорость: %1 Гбайт/c RenameDialog Dialog Переименовать Rename Object Переименовать android-file-transfer-linux-4.5/qt/translations/android-file-transfer-linux_zh-CN.ts000066400000000000000000000442651501120463400307350ustar00rootroot00000000000000 CommandQueue Loading media library… 正在加载媒体库…… Querying artists… 正在查询艺术家…… Loading artists… 正在加载艺术家…… Querying albums… 正在查询相册…… Loading albums… 正在加载相册…… Done 已完成 Setting cover for album %1 为相册 %1 设定封面 CreateDirectoryDialog Dialog 新目录 Create New Directory 创建新目录 DevicesDialog Dialog 设备行为 Rescan Devices 重新扫描设备 Kill Users 杀死用户 The following processes are keeping file descriptors for your device: 以下进程正在为您的设备保存文件描述符: Device is busy 设备正忙 Device is busy, maybe another process is using it. 设备正忙,可能有另外的进程正在使用。 Close other MTP applications and restart Android File Transfer. Press Abort to kill them or Ignore to try next device. 请关闭其他的 MTP 应用并重新启动 Android 文件传输器。 点击“放弃”以终止它们,或“忽略”以尝试下一台设备。 USB Device %1:%2 USB 设备 %1:%2 MainWindow Android File Transfer for Linux Android 文件传输器(Linux 版) &File 文件(&F) &Navigate 导航(&N) Upload 上传 Ctrl+U Ctrl+U &CreateDirectory 创建目录(&C) Ctrl+N Ctrl+N UploadDirectory 上传目录 &Back 后退(&B) Backspace Backspace &Go Down 转入(&G) Return Return Upload Album 上传相册 &Rename 重命名(&R) F2 F2 D&elete 删除(&E) Del Del &Download 下载(&D) Ctrl+D Ctrl+D E&xit 退出(&X) Ctrl+Q Ctrl+Q Re&fresh 刷新(&F) F5 F5 &Paste 粘贴(&P) Ctrl+V Ctrl+V Show Thumbnails 显示缩略图 Show Thumbnails of images where available 显示图像的缩略图(如果可用) Import Music 导入音乐 Remove Cover 移除封面 Attach Cover 附加封面 Import Music Files 导入音乐文件 Import individual music files 导入个人音乐文件 MTPZ Keys Download MTPZ 密钥下载 Could not download keys, please find the error below: %1 Please look for .mtpz-data file on the internet and manually install it to your home directory. 无法下载密钥,请在下方找出错误: %1 请在互联网上寻找 .mtpz-data 文件并手动将其安装到家目录中。 Could not write keys to %1 无法将密钥写入 %1 Could not write keys, please find the error below: %1 Please look for .mtpz-data file on the internet and manually install it to your home directory. 无法写入密钥,请在下方找出错误: %1 请在互联网上寻找 .mtpz-data 文件并手动将其安装到家目录中。 MTPZ keys have been installed to your system. MTPZ 密钥已安装到系统。 Your MTPZ keys failed to install or load. Please restart the application to try again. Exception: %1 MTPZ 密钥安装或加载失败。 请重启应用再试一次。 异常:%1 No MTP device found 找不到 MTP 设备 MTPZ Keys are Missing MTPZ 密钥缺失 It seems your computer is missing an important bit for talking with MTPZ device: private keys and certificate material from Microsoft. This means that you can't download and upload files from/to such devices. Microsoft could have released that key material and documentation for MTPZ devices as they are not interested in those anymore. Because of the legal risks we can't bundle those keys, even though in some countries it's lawful to modify things to make them working again, just because you own it. 计算机似乎缺少了与MTPZ设备对话的重要部分:来自微软(Microsoft)的私钥和证书材料。 这意味着无法从这些设备下载和上传文件。 微软可能已经发布了 MTPZ 设备的密钥材料和文档,因为他们对这些不再感兴趣了。 尽管在一些国家,因个人持有文件而对其进行修改、使其重新工作的行为是合法的,但由于各种法律风险,我们无法捆绑这些密钥。 Alternatively I (as an app) can offer you to download keys from the Internet. Can I download keys for you? (Please press Yes only if all of the above is legal in your country or you just don't care). 此外,本应用可为您从互联网上下载密钥。 可否下载? (仅当上述行为在您所在国家合法,或者您不在意时,才点击“是”)。 You can look for .mtpz-data file on the internet, download it and place it in your home directory. 可在互联网上寻找 .mtpz-data 文件,下载并将其放入家目录中。 MTP MTP MTP device does not respond MTP 设备没有回应 Could not open MTP session: %1 无法打开 MTP 会话:%1 No MTP Storages 没有 MTP 存储空间 No MTP storage found, your device might be locked. Please unlock and press Retry to continue or Abort to exit. 找不到 MTP 存储空间,您的设备可能已被锁定。 请解锁设备并点击“重试”以继续或“放弃”以退出。 Loading Media Library 正在加载媒体库 Deleting file(s) 正在删除文件 Are you sure? 确定吗? Error 错误 Failed to create new directory: 创建新目录失败: Download Progress 下载进程 Importing Progress 导入进程 Overwrite confirmation 覆盖确认 You are about to overwrite file 即将覆盖文件 Could not open selected file 无法打开选定文件 Could not attach cover: %1 无法附加封面:%1 MtpStoragesModel All storages (BUGS, BEWARE) 所有存储空间 (有 Bug,请留意) ProgressDialog Upload Progress 上传进程 Speed: %1 kB/s 速率:%1 kB/s Speed: %1 MB/s 速率:%1 MB/s Speed: %1 GB/s 速率:%1 GB/s RenameDialog Dialog 重命名 Rename Object 重命名对象 android-file-transfer-linux-4.5/qt/translations/translations.qrc.in000066400000000000000000000001211501120463400256660ustar00rootroot00000000000000 ${QM_FILES_XML} android-file-transfer-linux-4.5/qt/utils.cpp000066400000000000000000000013221501120463400211600ustar00rootroot00000000000000#include "utils.h" int GetCoverScore(const QString & str_) { QString str = str_.toLower(); int score = 0; if (str.contains("0001")) score += 1001; else if (str.contains("0000")) score += 1000; else if (str.contains("001")) score += 101; else if (str.contains("000")) score += 100; else if (str.contains("01")) score += 11; else if (str.contains("00")) score += 10; else if (str.contains("1")) score += 1; if (str.contains("art")) score += 10000; if (str.contains("album")) score += 10000; if (str.contains("cover")) score += 20000; if (str.contains("large")) score += 20000; if (str.contains("small")) score += 10000; if (str.contains("folder")) score += 10000; return score; } android-file-transfer-linux-4.5/qt/utils.h000066400000000000000000000023221501120463400206260ustar00rootroot00000000000000/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AFTL_QT_UTILS_H #define AFTL_QT_UTILS_H #include #include inline QString fromUtf8(const std::string &str) { return QString::fromUtf8(str.c_str(), str.size()); } inline std::string toUtf8(const QString &str) { QByteArray utf8(str.toUtf8()); return std::string(utf8.data(), utf8.size()); } int GetCoverScore(const QString &str_); #endif // UTILS android-file-transfer-linux-4.5/replace-guards.py000077500000000000000000000020251501120463400221440ustar00rootroot00000000000000#!/usr/bin/python3 #!/usr/bin/env python import os import os.path import re guard_re = re.compile(r'#ifndef\s+(\w+)\n#define\s+(\w+)', re.M | re.S) base_dir = os.path.dirname(os.path.realpath(__file__)) clean_re = re.compile(r'[^\w]') print(base_dir) for root, dirs, files in os.walk('.'): root = os.path.relpath(root, base_dir) if (root[0] == '.' and len(root) > 1) or root.startswith("build"): dirs[:] = [] continue if root == "mtp/backend/linux/usb/linux": continue for file in files: name, ext = os.path.splitext(file) if ext != '.h' or name == "arg_lexer.l" or name.endswith(".values"): continue fname = os.path.join(root, file) with open(fname) as f: data = f.read() guard = 'AFTL_' + clean_re.sub('_', os.path.relpath(os.path.join(root, file), base_dir)).upper() def replace_guard(m): return "#ifndef %s\n#define %s" %(guard, guard) data, n = guard_re.subn(replace_guard, data, count=1) if n == 0: print("missing guard in %s" %file) else: with open(fname, 'w') as f: f.write(data) android-file-transfer-linux-4.5/replace-license.py000077500000000000000000000026661501120463400223140ustar00rootroot00000000000000#!/usr/bin/env python import os import os.path import re license = """/* This file is part of Android File Transfer For Linux. Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ """ license_re = re.compile(r'^/\*.*?\*/\s+', re.M | re.S) for root, dirs, files in os.walk('.'): if root == "./mtp/backend/linux/usb/linux": continue for file in files: name, ext = os.path.splitext(file) if ext != '.h' and ext != '.cpp': continue if name == "arg_lexer.l": continue fname = os.path.join(root, file) with open(fname) as f: data = f.read() if license_re.match(data): data = license_re.sub(license, data, 1) else: data = license + data with open(fname, 'w') as f: f.write(data) android-file-transfer-linux-4.5/screenshot.png000066400000000000000000007372631501120463400215770ustar00rootroot00000000000000PNG  IHDRykͦ pHYs++tEXtwindowTitleAndroid File Transfer for Linux  IDATx^wU?9efnٞMF HR# "**X(QQU@t!!dw)n67mR+/{Ιwfgc11caY1cΚ1clpd1c#&c1)5c1Hc1FeMk8c1ػdMqd﷜n7x)c1pB֔۟y]a v:?wCp۟y&juKV/?~kxH1cJ?;^5e%O\w~ ) ~f輶3yJg=vۡ[z1czCȚ@^އ =i j4U?y=wlK^z52=+}.kI;~|S^Fg>@7Ln _:Iav̶E LR`v{9@ccwf?_/RU%[:GII[k 87jg埸KO{MTU:kO]cYy{O^sO Dt^~=O['a)lΗ>[;?;Fnx7YU3N']𛳎yO?oG{EӬsy<1|y1c`Yp "RaϽ^Z #~0K?=ξgeJngo_k]έ #ԓ?~=jN{.߿#; ZLMSrɿ}-߾јi~&v/;yU<}g{5μpS _OUgrcwg+.V+aĄ|֛f\5S݆#/U8P[yGPV =kgw꣏~連l[`T0!;-qZV$8NK㨃W?)@?>ٰSc1!G<57S儉˿Kiz.㡫֫_۠r9z:&SaOHѩ@u>L3;`w5WX&9f|(ڒ s7?A3&5ԛ䥓rY4Mc>{[\Tc1醚57u;_[ڨ$=#?2 .Eӌ]Zk~t# (͚D|3k?7c1ػ҆o.*7NQnw>ܽ _mmղNoJ~d Sw$˗{6 L_-]g<];⚊~ӿ\z7.o/` eˊr?[k}.On}fɋś\y3&n=nڞ6vV5c1pkR#wKk?k.iʳuRF?{ߞfop?|L=} [:iCWY%p+ݶ/zgL[~q_'}嬟t.Z^u1H{}s{W/8_&L?/}t1cw{-ƴwlSkKoWod4m\YVKb? .?AeeL2=d|um˸&ұlUa-um˸;WX3c k|S<ūʢa~>n×k|c1P 1wi9gm{1c[5c1Hዣc1Hc1F gMc16R8k2cY1cΚ1clpd1c#&c1)5c1Hc1F gMc16R8k2cY1cΚ1cl6Rɨ1cÍȪr$)m:c Κ#elM>EIX&pMERLlK+1je26岵p[92:G ===DcavGQarzy3X+c1F$kN?nҤa.mmm;vLu[[D,7{f3RcmϚƏ_ oaz)o|ܔR665jϫyq|pi_W2cl AiмWWm՝?8׷ܲrtQi${z\.`_ۊ+[oB)c16p.R#44651q|&qܱ-mʀemMMss W3Iެ*zUoR]3,FrQ盚ҪՕ͛"ͯ`1G4h@Ryy+?V,ς WXYuIrG6L<裞x򿛼`\.gX4;>tjs}xd^j8 Q}YV&9~ڙ>׾yR]1:s }f >nmu sS<ϼBD|83w]ϦOY̬.}K܌C=jK|Jc864S#7:p;]|KZZ~WNۻom΍6}l~XP4?b~'5ixx׾ nGƞ=7c7f72hF"n}ӦNB~_FD?c꺡v۹<Mxlתύa͵jެWW(U7yNۍs^xeesc& w)ږm¥K"'VVOG3uhY#8ܟPè,VtFǍo4m WsI-^10n憬J+̚*Mw~|,L@dԖ\ұpˋ{s_Q7mڶ- Yw-=sn !Y7mf4^G=+͛햤cʚo|L {i}/|ꓛΙO./Eu'O. +Fe\+֝&It3ezM1#߸cUx~_?n̤R1*t^u3}^=z=@Nk#ry[q;F_*z#v n=W)}/\'W '}˧'K?O3>Ѐn/mgLD 2;e@M1k{qֲ9Bo_?wgN8s{O9vMg|҉9}/Sw(si'SN?W=/'> N;3'z~r_:cQ y''x܉|oeɘr :ɧtyg o_ذ'F?zq|ߺo_b3 Ś|f}v~/~4ns'^sO9>MWϚ5z:{ LKkp֑nEE2ad_(6eN^]9=3QuřO{e"du_FPT(r")?X_^u/ѭTN0M;~v/n-G&K!亓VtpJe2G}?Ϸ`^{G?Ы_]yg{5!ݼ$KΨs~zE/0g3o}KrL[؝|_=>MȖ;>1clxn{S]Eހ[ PL !z_3on477W*AdnT0իVt*ijGmP!O4dqLSNJoggOTڦ憼*z.׏n.,tu60z '"jnjiʤбߦRAڼ1:Ö5a͔7eʶ_3ғ{xh)6h(Yupd1FpfM--c?cC!"Z1cÁ.]L* ~Ȝaͭc1688klX.e1{5c1Hc1F gMc16R8k2cY1cΚ1clpd1c#Ee1cmٯQ_S]c1pPQX.c1cl81clpd1c#&c1)5c1Hc1F gMc16R8k2cY1cΚ1clpd1c#&c1)`L6Mc1֌5r% C͈٘M8Ik] C\u5c1{BbhQWWoPԩ;{zc1ػC]]ԩΝIu |I'utc1nѱzI0) 24c1XWww 3g0$ڬ3Ac1;SXLu )k*)/b1cRVϐ&c1c[&c1)#5UyZL^ c1ػeM1s?-gSo'1c~o;{1cm}?ɹܤ#;3cC ep)i LZ'MnN]^$̷n7Zpu"5qm'mgg-xveY\:WVc1ƶ"nfڤ͚z#١Ϲ>#?󟮝Olt֋^J bQpo|ٿbmt)sbsf(WFe3 o*.>un3c18pN[nuR+~d~Br+g>{糕y`Îyo=O^QvsG\l\~ءz'\lv]J>?˲qG~ԄMσ1cmsqO?$! VMM߾ٯ\>t[Ӱ~ߏP;Zh=/4nM3׺_2{P^}dJupߣK` [;6c1{ŷi|ɧoO[| }(QoCĘ{#Ot|ߏq0X ne2emPC&Y*H~ 1cmXU [uؑZֻl?[P}Оk XG JtšaSQc1ХqK 2~\O>A&k)6>~D r~ '{T^Ȃ+tv99n87Pc1Io޻A"kMܕ{~Et9/fX):o̿0zQM1coϝ|zE[4a˳[gnwZ/­??p7|;z%^EǟU#.H zO/n=\9Ng`ZD771cMQ -]v.[GC}Ccc˫+6 umsi,ylSʫ茪76)ww|41c-j\kkWwgWgWu:t\s(mEݫu\?)/.W2c\c1ؖc1FP&a镌1c]jpHY3"Rc1O6 IDATQu )kV²yJm/c1+ru )kʕ+nj_b1c8v+W FCʚr 0aB6'c1ػd&N+Wހ_S9wQGТ,c1{G@0 ;VI5 9v ZѾ 1cCBʁb1{JW Pd1clsqd1c#&c1)5c1Hc1F\}=q':2U1ck,~!: =k;?z)̱Şjc1N'~Վ*dѬR֔N7u:c1npI'W_1KT׭{u:ξå6(}s L>N%Ɍ>"d=fLvRKS%}=aoK;.^8܅o" PYK+GKl.V{M55Z)U*PL / rRbx|P,VJ(\\*ӵu$=}ŅXĢuޮ!% 9%otпn&SI{KdYuRT*JX C󹼵[ 4g2eZƈSIL6 b<P(ql]\Zĥ w%ƆC K](l&!x,HK)Ӓ(ҩU )R0K 뾾5ܽiaUPx RړIu5wv`лpJzcﮟd!*ae`SlVk]T8\.T*Jep9d2uU(٬sX,.\MN T'Oݻ wmue\lgg/_h$! $ Vv,p/x{W$>ܻF3f4kHxjvPgTWcH u]7v*&"DLwuT5DtΥks փ(݊rbI$B@ HviEo[6=YDk̋|VckJO7ʁ*m~\ 2#B.k+6T6S(yLL/m)u59)N7d]^6]l_XOI} @NgiGqR*iI&&'Ib-t+#L1[&C0)$D (L@f3Xh_JE<:IAWŞ-@7_tC$}A7߈u'zSTQ򥒾D' ( A H6L[26NBD  RW'LDЁ'0AxJi"}_0cg8t B6'@tơdm & (uY q1$|(1@%8I9Zc@HD,I "t1HqE49{(Ӓu9KKo5J 5j-Ou@n޲*zBbz# Q"qI+ȆLNO' [xBV_Q/.\9wIG{O\Ka;.L "@Bh3έ5sRJk]ZiyUUZ봪|PꟽJ[]'hvy\]>CʚHk;U6z`M#p9ToD@(@IQ{R t5``%NabQ9LJe|Zw֡R)4ʋÊ@!0 3<(76+"YLWM̪Y|0nѦ2fǼ)ey^FD!ĂPZie@ϠP4%"&uec=I23 % ʼnKU9r>Bi ͔QR!B 1 6j؅Ҝo%$@)Y r P*`,$q)+qhT)tPHHC~ӞƾHRxsJc;%s.IDTc"@k8QB:k3Bi9 yR )𬓐C#(&-cd,(C.2VZR 򵧥B B!|3PP8"V"sq$BKhM?<:\:leӌLl%+*cz۫waL ٌȾ1#z5FP*9$"h= `8fQP$mAy al4|}^e<}/@*%s׾@APJ;kJ\ 7@!b:f) d=X%e-YCFyVZA/&a. Yj4U"+(P(X& cT'2 -RXQ 1VRs.ܚr8 綅kҽJ4ʱheT-BqM\Y4X{TUw&Qq],qgox9 ߶@DC!sNom0B$H $XdDWR(p=n@9%]%6"B@!뼯Ӯ)&nW8H IzBc1QNҷ&q`$;6PJ8[Vp*،~giޕJގleʐ :d#F0NZ)d#`IJ֊$Q dl~AN"qŹљ8%kcɁ5R+m(16&c$)\*{MuP|aN#T\\Wii^η ?vA,KWIbM#X*)kкZ<_h/HxrkE>P%Q" |LTW*UVV&9@AxR"!mo*NBιò/me2\)$%FH?IH4QVY)!8'4BX*ze.H(a@@jM@ }J#r$*=#87@Dh MYB{Jj8!@<_#΁sRF "H;'PRHL d$P>h-q_tH@VzρID9e-" "iGXWJrőgI\u۫w RP$B !BAD dPӊSfHCpteAoQG3"J+Q ]] l8cK/yC0P qM 8@"v2v(Gҕ#GH4`!% %BAʃ D6; 3@tI Y}$mзl7w t]@FN) @@p)HC;Z yayo8φ%I}q@](Z| ( pp9u'1H)hIRiZջwkz0dtpN5P{ĉ]^,Eyt"{K@}i]Q(QDq1Q V+ @XB;Hkk  H)[JG9Y+IZŚutдRcuFԏ m4)^(’2cr&e:nH+)IhgB8 g#THJa5^ hTnm!!zґʑ-T8 Ґ -%:  q+ =ES,4\m"Jq}iO6iSmABD (,!")IJJHI i.˕u詜 4hEH `g!F  RȬ&}ॣxC! @о )@cTazrZׁ͚@)j`Dpx  TZ81dA"il": EB|Xrbzhᤒ9oQi!ZI&!@z (N !Lm:!o%ҹqJDHVBZMG皖 \FWAKb'4@|ӵX[ӞkMWk" E^a֚0Π@$t )e: &җl_?Pl.댫*:)st1" uPǏ]! (IkaV(V q! @җmIucF(~P%KV>DUWk:/wyO$ 5qhN)Z^ꤔRJ4R ~H(q΀#VbXgZi O <eXt Pz1v& @@1Y&Ɓ#RqX ˕$J9$ßo%Z(ҡ]^*%5~ad[g 3$4A7+IF[/oC&*>'g2~]mƶt/~>>#G;Yiq"l7pn9έp-lp쵶B j |]:- uy3٣OwbRbwۑUѫQ 5+b)Hb,w~}$>џ]Of9! {uVD$Tl_ @֖e:nfZB.RKЋRޤc)'vSrڴlp6~T3.\?Ybpvن)9`nsa 7 {kP К""9νp׭w]q{Za}ѿ~} ?⏎yucy07?{ uA !HYgY] v<n)pjs6")Hzg.يDᙦtڬC`$}w7|Yln^[K su<"{xD(Eh~R /WϮwf%_]Ey%dǛ??0D@7W{T{Yri=unC/)E-꽅_Z{;2ZoHjAX룛ya7W<6Η!n~6'};(ZuzbRbt8ԺxX͕[i* IDAT |88|< %BY",RW7kQm '?:8 h~r5Doo݊ގ]|" js(r^w6?bH%KJaAB!%4%L%ʺpRCx|X lm€-:[]l6k=Ҏ́ʈ!W`SA!YHsz0G78#̬ã73~uB)Y%)J?gLΞٍl/5/KӛݸٌyI?L9 T~ITR)2/ "N^d .4LO?I^>Ta$.<'2$D*\Uo{Ǘu:6|?껂5XDܙК[o`lt8mF/|:vobnFE>Η>7mf[)eHVz3"hIߟYxDƈuClwZ8ܴnucMe@1l00,^W7?/=Ko/!ԛve$w?*R^ISލg fQrss}|SN2R ̝I"#$eS}v}})LhKJ~?n0|nD 9nQOK D$v)~fкH= T@މ qXCnk/D-ݔ9 S]5h\rݎRTu_^Jn @u]0KL{xc}2D,ցHI2z 8\!daa|ʾ3o 7#@~ꂄ{GOǣ]&.(on"V5 Vv4&Gݤpj)%{Ki@ ,zۥ 78.3rfOgErH{rٟ\0ηEftU(7G} ^k^z[6F^;+eI3#.qw&tbǔB% YcX :tF,=[k^#<ۍE#Rĸݶ_6›.g=[W[Zj!C/nઃ@7gO$Lׇi)n/n_}l6!,4& E0W.̡z0_Ri.kkf"y\O~=<+g& gg>a,¼cHMkkRIF]Uqnz4( >)0'ýGgxӱ>}~ӭy:|ñd۝y̚WUWV8ԷʮH@;G=6ܚ+s̘pv2uM9I @m%Y4"N!HP0IRME5L [="D%kVQ ֺl6^] t3Os[p#JGKQ(8ID` ʐD\^-!aSd#!Hna.s-@I:[m\=גw] tBj8n&~q5qyԒѢI۔{[s=il:42$ܼޛ͇yݵ-qH9881}],kN9S*r|AcJ8sC ^JIsrs*L<,0{y} Ł`0H]E+)!n'u|Y]w9x]=@2⮇pu܃G8Ew<ݿo&;\O$7=v=>8~,v;SGd6_]q7xu][&|v?qwW"@mp}?Ycnv3MlNKSwHH[:M.M*^+H99y܍DJiYfl}S`fS~~3MTTR)7?{/>Z_JVufzDZDT`LU{}KuښH޻hX,}y|˃6eŋv/.C GHpw]pWvg$IiXuOoo]aphxIPqB !X1U4 4s)3 H8=92ӷiK*dff)%v) 88&D]7 s8#,\H {fM'qXgǛU kБ!JU\ , ;FCj4M`ȶpmOm?ES%yx Eva S8XL­&\>Ƴv~Iٖi n̴"[6>Xͼ͸_kqf$ƯZHC?:א:/CSO!g|G#m$^^ϟ;<rzٸݻ , !ADS < AסּtiR2H pC$듙'~rYM ,4C{z/ˣBFM^2^-/4L}c4LٺOmyܝ8h3vrRD  q^"5|~e^ؾVUkM_/g,c^%7XgCkf&ub5X 2e7q9̈́APx+$0oKJVLٸ <TuSkHUke-nP"D!ڲ^ tsY祃қEdf4 H䄒t Bգ0{w]9s>rdxHwЛO\>x#h-!5)Ň 3DJ`8*S*كqt7&`X''1k9חZTHԡmdMw"Z}#ܻe.xYk @DrιuSy}5If /-! 亜%( "nޏ3:HB)gK$IDcPt/w]$5박2h1 fo#lE$Ͱ=߽}q>'?^fC=@:[T||;///~+c̀b0W4{3g4ŭ +czܒjU7a|c0sxX? VIy̓'ߏ_BM#ֆugkwokX("u?~b=oFɯt ts Pre(y+fieit# "*ڼ֍gpn].ڼsS|)uPw6|[_VB?ߎBF^ f܍㋏z});ݭ߻7XUXkړV_o]$suJT:U$$:pI$GA""l\ ʌ.]vCa5n7Z{r-ǔwfSDt]rF~^xM3zwûp胉ޛnff;/ʰJh;@@ 1@eq_*Gi18Ɯj'&GS,Ia yEj]]; Dɀ3:4J|H[RR0@*=nw}楌<'ZZ^!X} 4Bѻ2ۇ˟XV}&N `Ltpn]cC)TH6j/rϮz[6gܗ-F'Ieh!Xu!LxF]Ny#د]wR'%~!`UVF tg'NŠxf ۧ'ǟ` ibFFfZN|ULwZ_zIIo~|OsóD's^%`]C#-Z%;sMT35̭0 en"zDP=nOץ;O XVK7YQ`]{y3{o {-{w*0kujJp\]ߩ{{ؘ0n}i4//NXrxٟǧ/MwWᾮ}Ŝ^?𭲋 HYBTa3b]; g/IPl֮o.KDe9j DEqz"7^]Dd;q;j))% F{irh:7BT WZDݕlX?q5 *HQ1#0n}0sc(SwLbDkv D݁hw}|g "CĘ=!vI~YR"ΛHup A!(A:_]?t^!p2 >UN:zGDRylǝpkFZUᮜW8>ʽR6"ҧ]unH"jZ݇aLպ۱եFك,:B" `@DS.ǩNIzettASٌyHؔtpauLͱkG ^9Mn \ɒ"tb1":-iOȥ!I"<6 tMZx po"(/_*Mi?2ueD"= t3,,G'_'k>(u PB"NrnD puV4q@$ LKSXT0 BAsA,i%뜹Ʈ;@*Md]J4+S#eSrp!5g ݏeWBc7=r5_M :/ }m0"ͧOk~0!\+_$ HܝrɇמUm4`_^ŐJz1~\ !ٕ\j %8)+U ZM̠V+uVصK- \1Gs9g3#(,]Ⱦw}8l8?]춛f{ '9slHe#*oRJ6:ggϻ5q0rCzowIOS3773"(a}S[jIR Ed X NDV@RQSpMBP,<@MEE%iN90-9A98X8edds7K `suMERMTWjr?=>Ha]"`JU Cا%# R#lʢ ySU_/D܎ǡ|`m.rOBc/-\%XtH=nf~ nsZȚveC$KÑbމf*m_|[xVPX@.D6:+Ow.hQ1[Ch(aݢ#!xxw{~pTKKN@Q`B_^<3`"e@dy^뛜F<"mz7gc7'v~~΃y'D[}`{~|EM S/YFXKRdlb(rsI~ckz_]+7^{5p:yvϮ>{:s}|?>܏GQzofޑuAN$fBZgrM ""U[܅"N n!9AlNf]Q0U ph  ~'"Д@xVã/KJ#Z: y+YN?||>dfMuZ5<ee:w]b3XW#Q"ܭiݐ^簠 5pa.Bt@ S@5",c*<ð3;$@e#@GÃ> gø ]8漆HWܽ]OV4!UA e+ df}]*% ̼0Y]DSKCP ,KA_`:WY"TQ s '|Sʄ)1CI`{yκõNS*cA*x0VG>o%@ Pp/Bpgx\kI&ffn1|1@$LM8BklᒘD{LKRMn!aBaOf.;ARywu jJ^rRU* ^k}EhZkBr.=?3c 21x|ۀ{O2kmY?:)k[5ekbYyJ_f==n'{"܇!@Lǩ8ӣu7{ (pafw'":ȥ9)gEX7R=L Zc4k IDAT: #{uIU"ZzdwUJ#[͍6@jk]<\UDUja}ff1lw łB%, 3Dua)e$>\z7 ED6!k 7v?ofW ކV@,y5$M}4$͒ 6D)Rׇf]t#X"qjx %~ _n2T) !eI7I)I?Uhny P"V Nw%)=/"tH])=:T"UETśS҃uVpIp "2x/Lį+9dŃ@zOς$`qi6 IY#Jo˫g! 7&`*D< c!RdWF!lk^9xF]*k,B w)%EBAEֆfpCA!DZAM\ЗaO"m}ƭGmYNa Zk }ZM567~޶?ٳ>ǹ<G fjպp RI*_uWoV杅Xk=C_UI՜UaBCAl3c<\(cV D(` < ̴X#1erIYGU}m#^(L ͙OȄDGDRՔ=@;A"!0l!b D 8(rN"rq))PQpw! )%sі9Vj , cw?8߆۷*_U'V)}HXo^m+ 5vWC<%^_ )B!׵ E*2֦V0?R)`:%dİͦ@ECH  g9nQ; :&X=Tc\ ׊!L$]|6ۑ#"Dykp0"uD8)Dxn˂5nƺ= %EPfSBh*xm@}UU^"V]@[&aG1>#!D@!v7 rpbl|qɡ_ xs

~ml֖~ZCG܅IB" Ie=Խ aUTɀЬ"0 R6$T[of5SM8$aպvqP(B "y-BFN3G{oe&Æ"Rcm%zn 0("H$"ES`ȚՇgN|̂Diǫo653N Υs^W+`׷ 'DpĔgv(e7nF8!C)k\UVV֝~TDM7ŋd@!૱ X_{d'2CnG54Vj [:(p=@W#ILЬ#~L ;T쎺nVCYG_"D N|م*`` /-J9ۢ>b@vl(t2dpmLtDN-T&/)p ǪFv*1W9ĉ 0;OC!F.lmYsV!y*)H "g9ps Ҩ xuww#eiޏ٥/RX4d%{J"J:lV EdaJAY=0`L1 !#AO>UJ2qxS}c ^Epz|Mܳ뷭lCK)0~/nLif9G=^R=4s7SkmHJ!/X\[kBSV'B2laA,Kɚt/K)i,7rVkm-\JTi]pY朆!4Mn6r g]DCvf֡\ծW.՜S %a4Mp]USfu,B" q]V> ٛq 1@ƘL;r1gVZL#L(ݯ+/,AQ`f]H P6 qlu(f pm-dO]1ʋ FR>HXe3`$F<#Tc7N:vdIȲkH,Ì` PW~2C('cȬT&*u|ϴȃkfɚŌrjt9P"X%\//^+/߹+ QsYRb';b&ynNbYl8V5&RUqsb$l1͚~{`fbN .u?M !W;3K1i6;^a5: NqLUB&80@*f-9-)ey>cv])0^ٖbWaW@L|gIDD.f A fD"JOqS[IҜ4gS%eM&vo42i2ve8A&lQur2ʄńYѤ)J,9!Q55USbbbڌfi23\& %+&dLhƱID2*N-"Ô̈JD0c|ad3!1 Ryi6CLbHƀȆŕc0Y)SbHO>bW9YMDTGG}wBF5!Xf4 Tmr{F &#g(7ګ3_&(5*sY)dLgv#(;QXR2@ʐq̜Zv,9ȤU"2rw7^wD5ќQ"82 ]M[ap,N(! ^懇b J}2[5.9yB.q=mv yz><TmjjSu(lb]s48q0Oã?Ooc^:lhz!wGzG?7ywwz(>)LܝVTbZ`* &yW/b/ũ>|_1>xﱠ_ek`f+WsZŽaf9ifl~U7,fN}x8*jf*&#bR 5ʢ4NLy6!f@ۦYaH4MMnr5m;'{3%"_{`̅bqH",;)Qg9X6{_9'\Mc1 Dm6+X7)fM5*3 @FL 3e"ʦP09ͩ~)YkW͛znJLleO6@q<#Jvєr?$Ju+&]fBp/V*j_{/,gYT3rEē'p'sz>52pt rŤ 39lVy ;sP#0G|݁뾛r2 FÉd%3ss&lFyAa޷Ji1ec1&D}DtK0_{ZFMMSB) 0C .O&ZZyVv( *] z.ub1f)Y wlS;u4MPfkj׳UxR͟0 fϭ7|?ܽ{3Y(0i ]Սt!ԹY Z!cЬ-vn'fsirN dNigC@n]A$c+?|oܻ5*W #6 1t ꡨRb6kkapoa113DDb f&NX(s6$8 "rsRsL@f R SY.3'С_;X^>_OɃ͚|>3w} Tc\$*W ]AEv S"T⨑` bbWU69&04fu]mC1f by{R{53c"0EUv{>_̨i3!Ӡ]k,Tu6)!)FeN)BQSW;=@(]\fZ!c" v>](.gRkbEϨmƷJU*(Oq\ _])3~&k,W%ifC6UM9vMAsdyffwV[/ )B8nbLdɥiSI}m1 SF"n^CBm1ah쥭kf8NjMAs>4MS׳PCFr7UqS@ĚE+ 0 eJٛ,G əW>l!@mU "#R%+,<)^2 P6Gxs8Nq~pw5f`~3.$~ٚ @bJCGCogrZ'!P]eb9HPN`JQ{ Jv%FEdbjn_;^:xv2A}e(&LlؗNZ FDqҴl! ].lR*mɥ6c""^{s6ὍͯK\n5<|},%V˂o Aq]&J )3 Onՠ>L=ܬ&ɵB,+ZjB[|2Q"bOq:9A|rne[ y-#C'RI4tRߚ]w_yso6Λ[)_|3 [2=ۘ$k*dJ8(qB&ZO4#dBL0U (:ouMD~><˟+u5q[C! #`|oYz'oܿoǣ{F2(W^[_ѻ]w\^ya}bщJK xcxFvUOlM".ϫejUǧrp嗮Mcsaf;i1b󵧀5yRDM(8ƘR/`Gj?7̀yLy: @sN)pc '"dS6R站"F =MhgS7Yfd1Dzu'L\MD9'3a^ScSUbBۦYjkfqc-b R)y/WHr?oMԤl &u~z5Ъܯ ccZz'hRPQl|.+d y{N6ccFL]3PeCzW9* )2wg՝j?fL}qs齍ԺygFf /fmrFq)L,` @eju[+y+-Elώ9[߿vu_}pmQLfer'R?}31lJ6‡,c- +̒JZIQUkjg &k.eesj03f; :6.aؠcW=TusF{Xͻ￯Mח;o/UQ+ ((3&"M,Iglu ZRc :KVDdDUʏ~w^OU}]2IM@В|p޳&vSPL,1̀u\¿ pŤrz>d FNvyni]k0|xQsW/^]EF|IL]ĕFNΏOOm&`lE'.[ k/h`31\Z| 36]̤ R.V"#֋DD/ƒaWa˳k5qfvUC>MI4LM-[y[V>dS6]lݭ61K1Anj,+1XԒ1kUTzm3w (欕T㔦Z&1k|SΣMδqU bHYk/PgG6}LFSzni Ԭmf !*皦Esy!*kwg0:pCL@Ȅ9+xV#435AkgLc~͸" x)r4` Vw^i pd=]nlДǬjZ 0I;7ѨDͨzrzs~24Nya.%Ye!NO1hy `kj>*Y/n].[~$jk@Oy͏=;USjK t%9#*{-TaG(I8(*GȚ-~Į!Œl+-vbaMgE;7Nrz^1}6JAHV]iGȋߞd0琾zؚzǬw^8vܢx,v=`&bLp|_qsoo<_/NUW&PoK.'D<{W4'o/~˿[bDXjDʣ=,-m}5"QՍ8' sR5f'Y|si@&]mbxvv],駅ښO`Wꪃi;S!Y*qPKS4eUόVG8M!vƬJfو3r>=l)&" 9qL9WƤvnRCֶ][!^G;s'G'g* tݍ5%MDT33;T-T^GU90u9%SCsuJBV%f,F3D̄,$dc?5-vl=V7FV$.)͔ŶϞyzHxv )ƬS /<8VN`vaBR9_{AfcKU1؇Ϣ ynqmqڬHH8LqUYUW"eQ#2~0!"̽gd1Qx\6Ǵ;k[tT.捝nY8(i*y\kL,U {tAfceJfD͑iXڍ~> d 4.Bb]D'SS^vˇU$C2M.6Lϫf8t&o|qÏ~WW"w}}o&4 )bջo?;[ vEH+c2 dF 6b*SVg0ݺo_ \?:W/r?NeǣƊ& 2K3+xC1,s:$ˤhL10kd;l_Jٟ>_їgs:2, y#}so~_2ULU?<*dD@Uwb&cSqSWc]"Mb6 o&%hTT12lt}>>[vGƖ*x.[ }jQqe#^eg?94q~X*gƐn/jQӼjrQNc9U9PL%d>^.NZ \^OiӋLLÃcgVe` \hYEAbRgK[7_Y?8#M/|㭃ݝYe`e>I+pCN\_;677_e8e%BfVcgHTv1e8;F^UۙQR,`0boa٘C#dbHS6@*pBBę-l`. S)|Yv} %ck[Aƅt jJX>x/%LvH "EX+@vf8v)M@Rj,JT$|_ɀ=*[\ .u5c]Awɐ E6 [<.Jx.[+Uym*PgDцb32y<ʁzJΪYrʉLUP*$jot`vp|4 9؛uČ_:ݷ)gpX4%ڃAՈ}:?){:3QFn޾^7iLiwvgzq3R؍۷?A`AUAUU3\EsGGmQTi7SHJm۪jFoEǮ_:v8e5դdL)j41F &( gĥ"cd Mְ;ܙlxPv­k{u*h0ԞV]5Ĝi>FP`J9^mۃݽ?BnV$v1M:d/t&*Pyw^) OeCMtI,+&D"D)d`K+k(.Mq| GU&S.@΍13Jb1Z ^|;O~n٢w|sw9FT1 1 0$ca|__ptcr76/v/txtz</*i1#>NppmS7}WoJUo>;eIh`+Q1хe@d`j&ePQ켃 !CAm cwîBxBπR+G/ox]3Mjqdust v{|E.mǬ#(Ҹi#`"ʗLmL\ЗW"r&^v֏޳2l&A4_ή'x.[iaE*[BSP",%U۳8M@h fU&ΆizJd!WyDUT93QѦ7㬩Nv?|;?_߹Ʒn:=#['0رqv^w ʹNgrc*׵jȄ jS|t`y#ު7}ƭ0|M-޸ɮNJ+RDʰۈ 3SA_ CFΈE6Θf` 004 <3}!+:VǞ]RvC六 FQU˖J g f~}gvɔhkܼRLX3bE[q*D;?jt-[7v@fbTwΉX73mV<+/]}z4N8*tT צs.3T Y`Ԉq"39ɖ_]AAXMpREOP9vKp!\j@fD0|{w^}՛7>{=[f:=kh DFv1hR7~ڦ^ja*c2u3;J~sj R7Hyƅ`:'3DUlrs@5 .!JTB?@}yrP?[v96hb IdzPfxnT *āzW !z65ܻsvVɗˏ~|kމ`qO\|.PΊbe $(H9i7lOf.7M[wc w'{ rpu&<9rb@F'AQtwܖ|>fٟ?eOnIԘ)Pc͑t؏n7s5B\f IA/]ȼET9(:$ J@F\!,i+li߽#O(u-sSSXo& ڴ"@7E '?u`rsrPWx+_&^*npk@4)O~t6qИ(Yd).ꢍӌMPV5(P073# %<|4@pv[Scβ1>g K^̺#allbbaƤn6˓rY.esZ7MET_;mnt7oإ829* kYMɣAP_ " E (ܩ IDATSl]= ڤfnަal65 v].ư\MvSA\|4 ִ"@ba*"b'2 WE{i%8jl4)Z[4NA A.<,UK0HBZ{i!$ ٳa|$KڨLl, $]T A\gsY?!\D;OLuu6m GR{`Q&Zp;S}~,>@\=ڮxyYrަbC ?Y,m'bB~]Sy' -mH0T+hpU*@MU2 oԒY%K z#vBH]/&$O?Y6}|9q&& F.@X2rFigM_ 6/ɽ~o~Hԅ ]ӻ u&Λj5쪇*;okV!+B:E:|3er& Vr5nX?1TU'nN3q>{r5m3y= 44y֏.V'M5:̅t"( rUy{yiD[)Ą @JfYx;z Xvk&_ǣũ2}z~Mﶷ|&zK&~H@k۷%s^PΖYbG˺Dx9d؊BR):X{-) +5n šg/N7R 1 M;zLώ46Õ) ]q|*)BS(g}nw3rVo P5QGδ;ߎ(?Hs_I.^W7"Ԡ}C)#Û>Hi_:ۦ;velc`طmĔ]v7ƧyV㳣f_^-dCSaf*GGKWw߹'IWxf8FڤbT܁z8anFxqMەG>66,&)8!$q)i2B@`&h D KA] ` !FprG9xmImb<c̏gi1E:. PDwLxzsZoKANfE+R|u?O>]'ame,}OϿL׿ݿo[<^Μ~^^Sq1 AhT׿/tPi@IaTUɶO0j2|609.7X!DJCfQh; te9}~:+y%^ !Ig _qJiiMIspyxsvp/I T>x'/`&/泮]tM61(D~V,FYVQ!t`7,htϓLBm hE7![جJ)ZJri4E"%IP83j PMjj Q@(,SՆ)2jd:N:uaظ .fmҼI7swH43qQUp9<٬kL)I|b; q y;9oۼi`/[͟_7Wx+v!Q\UZNbaIlQ4B 1i_!ov'::g+YED!dhin/]!0A) O2;/2zhѶwO) )1zt A!or!Z_ȳt'7Cݙ{BSOҊ;R QmR&r_ \R/#U@bgwz=[(TyiW1D#"I) ($7\.z% 8`pW "90f9dqt6=i,tF(2F3vtzezvˆ 2ÅN2n2~$I]2ޏQBJ׆Y>srR]>&X( \AdRld ]iEA@*f]6j( !@uz8Ǐ݄TaM " &ylvV HѢɗn\x|躳< %EE R.x|DqZ,fyB%{'8/7wChKœfևSPdQ#q e!v+!pv}Mr8{9=_DfIR4rlfw]$BT Bw,hh4E Ql@)Zh %eZHP]n#MYCNJU薑{CI jz1U!rSy1ٟvG?]o>BRd`JwG|q/ tI!"`y N'BT A0h4x6'lj4bgiU;WWB8$n~VSO<ܫ&^yDDS"LBNaF)A"DQbPr!nTJ%@ĥ޻AҼ_oZLऻ{4޻;/?}zh"s|1 |hGhgw)"* îb$ڒJ82$i;ʈi!͓F ƻx.8X6& @1S$Fh&/b)EA(A $E4GW"LP6:}Hg 6 9CE(^&@DHu1)( ArFP@ȝA89j j@*($ܨҘ&] & .nd:&Ko긏h {6*pLe*fڎ@ I1MD xLxBz@4r~=8h.`Azӛe^&0҇w%MbI^rifjQb{A]dpo˟.rOqQ@:#7vXma5FUkإثokl$TZ)<{O˴Y.]v*Mb기vUn]NMGG,,NnK-L׏o/S9?V 5"Tw(\UPBi` GHQ%`%bٛ.=cn }\.O Q!ꎛ"`D|\'G^Lu~4E/'aq޲IEUCL*tΝܝ|eV0mR2Y)&4nBlӶLB:T@0{\v>n{ĮɨN˴}kj? ˆM7欴6EWL$kBLƃKUd X.wbR"E!jGQ@\:'RlᓋIw\'B@hPȎ^8mpݏ>yr<^A2dO^rY=4Թr.f£e]W' 9qU ((&P:wWv#%j(:v}TP%?=T ͢y=#b;t)   ߘֺ8H??rk>uB&@] o®Cbx+_q*ÀߛCT7xʟGjݻ4ls% n7y؞/aK `АTF+<a"!H!`G'wxK8NRZmO}뇏{CӜ PW7ѭX&)QZbUU ۇwIex==Cn?IOoS枝EDUC"BsY69-X7SN<~&Ab(&޾8=;f75RXgQ]>ܡL͗=]]+61;iHQ%vmmn4Qa.18}L7wA *!8CJ R^HY%Лz|:o?bm?ZW]jo2{׶9<)yt34܎jɒ39ڪhB;_z6;| ]+Ml#ʳIn))(TnCa:TLyfi* Pz~kWձhQ6Iߺ}b"2__ѺH); .,AʋE[g#k^ G!D$PRSl,O<= ʤC:@ٝvwOVbGǭJb`G)$BOy3J0Pԅ'dsYIٌeS4 B{4eoVHvqpzȷe Ƹ/58:Վ1IC#X]he^4d/fm;KEH&$ ͘7a[_8tS/2sQSޑڵ1RA{P­{=_ 4ۣe׾}jhZmrU_2BCQh5yDJX"PlL1ȟ}1{G/De7߻ן~ i6A* 1 64$F*pZתLlƝyx=] W]&$u M11. ΫNc^w~/՟D1Jʒ"4AyOßCcB%UȠ){;Jլ~J?/V_녀z PƲތvGo/8*|ֶّ7ɜ$&%VqgW]{nW8ꐨ_îk IDATAuB 8 ]#D#Ѻ܉݇-qqWV ݫvjoRC~AMӤ;jm*Y˴m[aᔊiioB7bW֒|͗o&*jL2B

azuѧ4mؼCRD%!$K)$"8zތ2% pĴ$O>f,gEH+vx1K*HRJr$!"`qycY;331%>l8^jc8 !΀>kw4`w4v㞛fb5Ne@"CDϔTG3Q lO7ߖ,e0B= {RU[*89:yh52 D%AAuY;8(U\l޵Ҥ˔%tM\6{1!9"Q#9y|uRL,"py.L[hR.7&.Ӈ?tm"DmwٙIpr?NG .8`Uf N=Bn Sxvw:1X/끺(B!B)C?9j Qש@%0;z՞Gn_h6S( !sf>2HC!&7n___:!G-vA@!jL'(~ksyQ t$T U5ϝ]@ T fze<0;i~ԪƯzڃҵ^_u˨ >%P?%Gk7?{z4%Hn+&p(%Y%CGlw~Opkx7A M A4:ar^\.R 69 AU>QfMuf6c@.< _uecL_FJFO41'"pƩ?^͚xCv[7M|@Mӣ6,4g+x>A|v٢KG&R"[wlLIAa lSur3A2(1ĠA4T!rKf$DVBD E)D@,"τjh@e!QffP8,&,T@Na5vb.?rtlLG7@8:.pK^M]ןxT^6z!_]E@Y܋yBr,LR%쯳>-Ah4!TdT54y.chޝt8@2hR9E'[Q_X8NԤDRAګZ!>/*g?U LmyW@ VlGyӛ{\vi1c tpC]KDTixNKvs._fl8<`5Z]lR+ԁK bN^$ 7i^j;ma>/j& EbŪ֐;Ha%")zfa J i4f='%RTB` v{#E'埒 ̺b%X&G&Ct~]߄6/Z~D{OÎ$K,qAC`1h,ELR(1Mcɖ]t,{1uma>3O$JmH)MJjs@DZ\Ȱax;D Ul^m/HncqQ"Χ}Q,PWTsƗv8$bd%RҔ5Z_YH*A/'tb'R@W_N;trűK&)5y<=;?]La5~amZ'J2HD[ l7lھveMESQߘ{}<`[u IDQJe?vg؈Cb=c@]dYX! ѥz^\ $;"ЬP{@|J eZ~Mȋ#|j1:xVX2]4@쾨s촹L.獰;g7CI@_7 /ݠ(}v9ioh *DAvc D$ 4FM }W`g."v]g5q0TUUD^d-t.򮐳|iuu0;].7&+6n ~GoOfA=duK~Q>I2JIىpmU +1_>$Q^X<"T5b T QRRn+{=֐e v:tN꼄C %HQG|>vrÌT6QnS// DJR/f~5" CP}r3_A<gULr0: @@RWaO=(՛zyi@!x! هrM2"&6/it.2 vW㜹rx:9z%xv*R*RI$@2QHH7K,t9zyF 4)(o/]Pug ,!M%ĠU> 9].>9}$Ryo2:],Qx6}V7k^֑M!4 jK`@QQ)z8;~ELvvxm*@vWƎҊ2g-Z1ҕ,ɺޕw~/s:|%23%p~Wn~yH2z٭ѕ~/_ }睷LNgǟ|p}OF{̓w1O}yrz\]}~j\ ?Ou%ydyOAv季Oww? ~lQOA+D_>xF_<8~O $$ѠGfE& uTGd/ǣ2/z,B.&vM)uSRJoĜ~JD ݟ PDZV6DO#@)&Mu2)ҘLS@RYMd`4mi#E3>ƺUj̍ ef.sÛMCn+N â`϶>$Jm s9k]۶{f}[%mDu^e} nꯂS[xvX"%Bh|A(hFŰ:0$Bb23%M#Ay?*vmA BnmcV{1*!ΖY({#jBIHUI"M썞>>K? T ]ԸMbC{AY7|=[T^Zxhw;>^Jf 39_ H 8$Z,7X7lb=U֌rcV)''RAeطaXF2X؀5ԠUCj0M*\mc(QM.^"8a7®o[ޒa&nZ{?Si_7}p'3Vm|Í˷!cH&Ԧ6B 'FpGy`6UId10/Pl YKlsk CLZg8cm10crc wb׎;椀f6]:SLiCL)4>& ~{+! 2!RL5+aћ+AQy$!zeΉ~T GFǓY] Y:Y]:]妽r+#}~/~dǟ=y Ҭ?{{͛Ϟ>6ZJ=^l ֯&+{Ѩˬ,z'f {fk&#D2HچT h08"(Νst_m.;^.C;3Wv]SE`%ZKFa1X "YXHU QXk8WeQѨ@S e&CdD@TY y1u[AHYΓp36 E7.-Pb[Yh"0m Μ[ԉvk[9i7+bf0k$aAR(b062:qni'x "B u^P6D(E؀̜5s]AߎTrk8&l:աAguJP&vF Ǟi{]3YP )%UlM7c`B}*mM{7T,JGH19% 1٘bL"2HU~:;qwkY3.Q U%R_9ʲe 3I諷]UO♿"|*EtPR$M ˬmRIk`RÃִfor9I^ҤCR F&! h@:,rkX#2W&{{ ɣy=}$Hy2 ?zg3>yx <˄^2EJ gE"g IDATOj6ɥ+  |3o6e@5TqxֺZ'R4]A<dœƣjtF Jm&Bf08\R W %Rq2c2C!_da"0YH6g˂I@*)eԈ4 ~y## ,wwoe7AL"l !D "M;f&!A(?Fzb,b/OUUճU]͆cVI)Q""z/ Pkf}{%b^QbhT= z)vP6+"/l Eg)BtΪN;k=ytB9wPU}:rJBqepsoUIE"5wAϩ GTNMUoۊ<km[kC$Mj\o6(Lv&֡pmTTOy߂LvrxIUgV|*'kIqƻyόѥӏ8Dl_;cl:kbgmXY}DHz>nQL̪֬['*K_̐Zf۩LK%bR1Y Yk 3@ @$Ed(έɈU4U LfȬRQη ˬ U xٵWG~(ΕS5EardžH 2#U*óΝgTL3/hZeYMFw%_Rm 7f2YB(pfK+vm6ݓue)4:f3 TB "&Ybu"Ā<˵lI̚4"ۍ VhQ*V]a׀?BL "!p"*4ƣp*CeVU]5KcRhxI&^f@ qt5J{ ?mv=.t JHlu)ats9laqYg?7vMi9gPկ>S2A&}/v麜]kuP 1&'PlFLƲW=mief2WX3̞5# M2k 9"`✼)ŤȆib}gh|F`vy>^3B++J*Y`]:$o hts'γL Aa˩PKeͳⰱNdg7ra, bH6+Tղi}]5UCƹD#dEw4^RLgu/eS A7.ʐ3>qeg92ܔy"4j"5V@Tcim D(۳z°gQqxsV!d *T;1Eknu'-6yY75LD)Ɣ"DՔRׁr\l6b-{{A?3aV&mM U2\6Q k(IE8vXQ4I·^ vNy"7iA/p/+/ORQc ܝ LEY.Woޜ<|\M ~21AMk9gW..mZZ/㶐,cw &U(H OϦ^zh{\[Eau @Cb)`4ɺD K>hRfX+DH t[vF1i/Ʈ]fWHhhSBbUE0\Pds*aX:Ψ!zatD*Y~P/k*(e![Jp?|^E0,gˍ߹qɻW~oɝZ3oķ}ڑj5w򺘳;^.^J& 2_-l-[ILPorֵ$ѪzQ]N0ٻ??nu(AAyf ԁLqVǟXѕwҪZr>eslB6z`dZ#(l@d1AI;; TU;r#b Ugjo\}NΧ MYb1i`]ZIb]a(yQXXWiL a""TW>$VlV5!:OMZLHE+%^ɷ wBPhn2/-6'nQ@IE%Q#*,&USUunoFzs^M+1*zpX1A1D*)ef$ҴMITIjoR̜ȋ(2H E$)uŬqN$$IY!uUBa9nQ4jS5u+I;"UծfayQBYƦU pu>TʻhK%J!!Fy9~E lvvBHķ `m. )+(Z àoAt1._چ>Mcl>}8l`uN3w2 Y_dAXq^cO>by6ڏuʥtv6[!$VK  QFbk=uꛈaiNn4@$X "XP%Aj[KRn+ߔ]sqw"^]@TDcTF˘F$0'igm]!U(VLLnK&)< eHŖ{rrWWoEɳǿyo-S =6M? r_etE^ߕ]ϫ/~C'D9b;v9o7-mT &tWX֡W&BƠ_ Ug+>Oy SUjǽ{$KhmϚ\ZgOTew}e1ɪ}dc8LMƇM<&fa|9Wd<v)l{Y-R]ڿa, ;3A2[sppyYmNﲶe"Hq}oSI7?8E~_{r<]Tm⋧ /zĶ?TE6*6mL ڽ=/㢑\_˿>{+[eٸ,ynp#MT, &$I InB[G(~p\f9/9zXq-{EP7MBFkkUUpvn`k1 wMVae=| @Jh4;ô7?,bΗmvۀ+$  ZQiڤ Q2JlZHW!"@p$$f^9إ[QQLR , JJ櫸^RiA]!Y[Qw<$n}Qt}g .BO><KViѶvOϓ@wf2ۜ:*B7[fm>|o׿}AӄNr3B 5E!"JӺ6U'lQŋD玝!ڮv4Exϒn.?]߂$ibu!($ݬ=ti!m3.O>xOEe-(eKW(BI>,N̐ ! `{! ԬWI"w]jM*c{bl$A1wj @D4` |͝Y^N]]D؉J 7opzkt4VU* wt0^7~ӘWo_rpHl3L.z7+uM㯼V*G#҆u6i ט6VcţoM>V׮eXM[tcrG7ҕ舋QyΗY?OM/i:zk/WuZ5?vDY-{G:/i/>>N`e=LLFʢjS1!&AGE y>8lX tAcNw2w9]S Co]o] $$aKO>33 VM1ϘII_@7iTETcKcLV]gdz6AYdI!6ޫ$)z$J $umj!2SzԽ S%TsúV+&1@D P72ƴ\ӳf1!zg~ifl*9C HUV ITP@%VHm2HU8 @vѰKФƦۦqܸ4(T!%,±c%1;[[Wns`º;w>-::.cnͮb C;qwrEAඑ?L|ͦj{Y?_}Jܽg6^jb@M[7 xI\>"Ԧi l<c%(=ӿZu.\EuƮv @ (%Z29_O?{f}tOvRADY!Bf+2q܄gGBY}|]-|d4dVd^1,G4@5*evrĎ!%|Af'lCWى5ۧ'&ژ *;;[x2cɲ5d`T̝ =;%YΦN6&ѦZ,֋SUb:~% Ù5d\_4,{,]>Rj~!MwlyJ^wz:Kʆ<#f{e؜>ygrXDlqY}e%e b;_,0:0ج,M ʷd,^>@Q6 ULMHD igb^.v7.ReQ !Tm ~B$%%Hd gae6L2$67dG㴜ib&Kgho R R`BSLI9GD)h1UU1MUŘ2笵Xì*1F6vf1IJI%1lV%t~z^,EHd44Aݴgg|DňDFIJtc\)Q@UL.iM32%(4|maz2C>v9H ہϯŏ]I˱ը2knLكAVdT, etc8E/c mشflǟۏ]V n *0u/GmNv3ٿ ǧO+r^: tWQ`ȾANDڪZ.|ލ=~*^3SdnԠı; lr8]Og4v % P1m{K|]?= _W۽l$?^4>!lW  V):R`DXt?wzr訸r,GIKhl=}t:>>ك_1!8UyH*m|8ӿ{>zy<>lzNgbq:L硞&6AaWكKWfvr(;>y0?TU6)Q>6΄v5?֛}v?<ˇG!IyoM=Z郦(e WZrd2\.WhDH1P[Rnh6T/ oe!"QrLjOUʬD?ެ;jh rHLfzI&ӃL/LQ lbiKDC{U Hk"$"sH{AA8YW{]+g*RD]}tX8M8D K,6kZAZLD2 4nt% &w~,.Dc\rp@t~q+k_)w~ِfICj)07 ;޻QAabH(bNLٸ7%fK ʎBlκZς əD Pk'@RI ; xqoljgtx]d[B쎝+BU;_ IDATCD{1Z+Wޣ_~D q},ҖBԛu:~"!z\rp8yKa.z:'P^{wm9ˍDy͗`>k0^ƖĔEcrhv #هy ^,uq*MWE;OPj)QǃۿI;:B U"E&7W\*F\ߍ*?<(ƛ_LΤRߞv'; **}u$;>|Xq&t尾d?+*RfA [{ciPraq:>yodU5Χ},ȏFuQ9_ybA__'<쪕;mmj ⵲""FQzJ2%gpk!BDD 4KN!ɭ.pGz4pp8z#| ]L&(7]? 1:O`=[/ \,_.aG7]r"V}hb4NHR(+~]Rj%Mg׿Gko_Oj}po\i// OI'_=< oi |p||z: dCa(KgOHP\*F*7:% MhEAv{XV27٨fvl˲ka=_ N: Y/צ( @d>%r!d,^7y*@X@Lij@rFhTEÂ?0(P!Y4 صwkn,umMn`S{71-XWa\דɸ./Vm p~ޙHW{'~\ u>;\Jmz\+xsjoN^is!E1l"EԳw8cqȫ9Ws~2Y,P|qzT sėR a*<kǃڻK gQ- &2,f!".v>#e0~s?*#R^Iƿ4A"ԶPɾb;V-Ef:(aPHnY.f *eP6ϵeY?.,$/7)Z@d,b=vk7>OFo.O[えAw^3AlU~>8_ջVoo^>K ݘY#з|6I !Bycýe؟h[z2C[ ŧdh5oo>]I?> ;eꮻTǣ1 }wq񇫦ܠkaYUmwB[ H2(ಹu[4y8  g{tه{sc8^?Xtr: NCWY](IlssuNc? ̔D.C+[oLltbP*8Q" 9RI9P@ >. TUjNՓk ܟ-OvJidwm,#kaU]oܘ޽SKLyMg>8r7#|9o_?7w?~isiӣv*:( B,EcZMiv}wz6ܺs_~շn U (%$I =;]o.0@`HB5u1؅rnV]#tu ?a")փ/>]~ݳOnש;Ս1:^>XGp}7owt$LޚuD R0w#uNrL6R^Rz-*4b^L|+iCvn٬ھIB } pT{ƔzWty?܏)~q7Ճb4o䤹8Md\B'"cێC~ (Ͳ͖Y3\k7>ʮg˘M.Ws’q)}Im/ ** 00!,հU`N;PS0% 7]W+…b44]̖TZiR@"ھw>㱑Z՞6%G'}n}0ws(֧決{c&P ;=޽sŭ۟ݫK?w߽vm0->s]='v4b~8b=r)( j*VUL{CITWD P* B Q<$~ S˲k}"" HH 9Y/̶o9?lBf oh_DU] RzrkTg`PI>?ߢ"Q"Ij{_4(f@X uk̀}) Ʒk~B0&$0+w߾^]AR,\ ޸ywiߺAJ{?`d{uέwo_?wEb~qwpUxgӏ޸~~??EɰBrXKOOJNn_<we19@cf[_'dy};4K4wadqB;G#$Ǹ' 'V=}%<]"(RORb*gf}&t .6"*pZ Ur?$3)vq|}zצ_|g|4,jw߽^p4ó卛pn=Z! `g˽ޛo_Cxp8!O΋w8(K`ف LB妃Y  𾪼8% =%f",J7.R۸q 3OEaA@E+ĩ:I:C@$l9bl߻f>/pW^~_MrGdm>7.Ͼf\>?~𫢪e~k!(I8YW5[TudXdԢqPEPKv\'=<,B2@z6hdXJE^ (D(@T贬'{YUU{)&@( ;ʓgO[U4aqHmxM/EYHYx\)*kˮmwkfNJjCrk6h?_ NRxep:zT&J9&%bzU7v}YER)Z_e\ݿVӾ )7y7'G!PJ$a\:y:>]vmu(BK?M?}H'sW_IdžKJ7޻ |=)VW}}{_fa`Ft<!jPhwWd;z;a:K@J;>᷊8~.[qT!v˦fwG>Wk 0(l_d.OxUY&09TB$t?8nyH@nތݲꖭ!(&K+R׼ Vj]O^]_4O)f>,xUIH@US2#Dئ95mDK ̭\e'TTr4-bB@DS3M`0(m$E\yO/h((7`糓y &!phhdyx>vJf]6i[ʧr)E2LBs$Y?[ԃ|)?g'ίwӾheLrX DN[N,o߾>@jU/>+o`yv9v*^ץvүbu[2.#-7]vxxzj|4iPא7ki!5򞪴mwxp\|>5N{rӕBS2@"$'0)SvUYz8ERP "(31g9HJ*Lo`W9_34HTeq8I]{ kO/6.d\L:YԵJ`4 Ξg~yˇǟ|~tjg I8}7?-|qy1wypuoDl9WQA%Ul=@)Drr|QGoaڤ& Q$4PAQxZ6a^1]9®: !632LN nÏnR.RRAa҆_y"D kww$:H)49 if"pءYfaDdfH_^޻M5eX;\RRBJR7<];W5w%Il󩭎"(0*HiѴӷ}ޫr~SCˢ6OPW8tSs&=ʔf&]tp;ŭ/d|8@P4ZwuN1bK',g" 80kJLPǸzpqæoBM~Z =\:t'ô#FIUGn-N-E|TEl_9ׯ jF"~a*ȷx%v8pкjjDoa_W"\BYB.BPjߴVtZs8+ʊ\UUQUYZA',|2ΗmOcJ(}NzTD]~ (VΕe- 1j8$QTd2u " RUTED )랒'o$Ee eEg&'z%';tj~7]I"b`'9Y4OSߺ_j!Ě>2Z3`S~ BW @|9֮ /K/?_,L>&V$ W-G߽{O׫Mq{zK3`ē>b|u]vޗўs&y^ cLmk:A" "ܔӵ*9H3/|Q(RN&$ūrb& IDATd˾wMHI^U $XZv]3"@ `׼/'̍4j_mX֕/Z|<ǞD8mل_L//ghxuĉi ޿w:էwf 85b$˩ p*$,d)Y2gp`im]?u1.KW{B}@djHBQ &3B/',$9粢RKݘ|mEK1`0!^]]ZDXUT(uX,4}%gwFv{JNbֺNۮU9-m_+'4)5aVKr&Ov1bL)RHXZ\9|p=FK]ЇT1%$7 d)&ºrXqP9Qw  '1~ŔLy,']5 @@żqzt6 ?wuNy..}gmV8O&WYWx]_(:L5BRXm y#cސ}=pdU6Nܦ`J-K;<-c׋9 N,qw&>ֻɝA#OlӈPC-烡rsYWO ^ԋv$+L !:Ywm Q)oB$_/Ǯ%h«[ 13sxn[@l (!n7Wx" [{앿 ^)M|! ulўPhq*f5ͨ*ι WC&sS5-&$w* QywњΎO.AL%i{b@ k&B ȪI\HLC2; !/ltWC,Lx&7y9ӼyƐs>:U!^$v} d/=]ջy,\sIJʼne5(jiuhPףVO^U0c&e AA@ޗUEAd4x Eũz7F $'&<&+0Hx;icUiZ $]Ae ˢ@kk|c LV"#{?528+ ̓WϧXͧC%N)BJTE tIUр@IVQ늏j ! !*Wkۢr[MS)7|i|wK ],$T+uEQ8C]g_e}ND9WO0Rs.vOjQ 0B&7̢0 M` _)uMǓZR"biN=O|)Qdm CHG9>'vGLՠ8g+޺6rΊBG|UB0ZUm>@ _FjڐV9"R) $b\*Hv.E*1F ɦ rP+bM_ºe8x?uڄ'DgZ2 `ӗPBţ-Zxk? MxrNH BCN}J}3[U2P5]߭>F&B`D^qhJQ `ԲbeuAbBI6TKZ}F6El}J:3FVʕfqp}܃@(æUZ`2v6w֥{V}\'4[7 dmDd'rMvq"^fP@E Մ /Fbf99~·dy:+@뻿Ht lvJRE(U +s^rsrm[&4B $zr3 >} )R@ Ux9v^;W5Ur`Kܯl7$(p|D49^v5i׷OM_2(읫u 1%v{Ez>kNc!tPQɹ0 T XJ"BFQ351YJɕ8,G˙g),ku+?;v죞_6moBUAʥ%֎>dnYۑ]sz:xZ{@ ^Ve/iaaٮQGeXjB52CPc.Drѧy\!(=GWm3XE56)-s@KRbD oG=!1پ6z"TU > xo|PPjrPKI}%D T *_ί5՛« ڹ/Dݻ bo3SBD7erHmWׅ8P 3Ĩ)̣ϖo]9=hm6&ir9z.暟@l5&_[V:;o}SYmN(bD FRY sܘ{>(yLqH@bJ -.EgT9i?p7ӗ yG H{[vS07r \rt2^]y6<^2 ٍ ?3WqM֜~DG,Ӥ*^;s`O.6WAS[7MYbE `$֓uD(RD$;킒1.&/`qQ l:d8hX+?=7M1J1Oݭ iek 9c  fؚЫkd$HB@ Q@xCV[j6CP5'16z""0ʺ*R5+nA@r@gYO^J86Yk#?z浇Fh66=T\Y ?GCk{s^@JDKɹ~%B'o*5? vq&Tp), fP&zu- iͣhI1V=A " HFq"Nx_R.υ AR!Ecl׾p:Ӛ.yErQ:ф7^,|ɉQP0MMfR䉾D/î/Br7 b8 kE0{^yc`lmID2J\1DDd6KPWN*jX%P=='&!vk>-zbfuDRh;w2&lyk` vkf~19A^Lvc@'%Ayulx1o]jU_,E#ERxv).JLKf8!\$"HMf˶`霯Q¨)(]M糾 zeTVV.r+t7 FE({2xZr8+tm_|+7SJ)DK0sP]ߴf=;.Q`I@QQ'h3R,| i)sa):'cFdy Q\X/5mzu%Mթ@s9fF1nd @HÃseUX|FW: SQEq FVlĔk,͌SuzF]|n1_6@׋+bnhD=m _…`b)i=ЪD]P@׭0p'Jeev2|%v=@AwqgvM7[սi{/&B$jB-@ iC $@ WcƶdIt꺶myө %|>73{rr<$HnUT#B:wDB]s!)J'D':/ܭH`jIWpBqnod(…g9ȷ,d mȇD0tt\ c#|ͭPװXa302OƘB! ͐Nn"oZ@_ "&2m:W /_PMY,P4 pɱHA'4UtÇh, i @r;;uPQJ,$[?Mk-!gtuy./ FED[f"XqtUQ%l׶i-1RiBpG C$@`Ȉw]DzUE _p!rv*!p (~R!s!ஐ( 2DD "S.!k,]V4Uէ!(W.w 7]eXOiZ<* h>pP#MNw@Pe!tM0Bc(HQH$DG*7.ȐG_J]B+zS'MC<(%K-C ld5K;҉O]3ʇxo_$@K 4*by>ei | Dr@@$_nMo;@!뀐L&d)͗Uחa9`Z2ZJ6W@Lcy'=vH"0Z?`RȯĊܼ`E!fM]$ t-"&/,*-WAaLDD[Z[hT2DBEF^qqńh4 $@bi7(Q̢Ā0d5ٹzQ~٪Éef}T)1ARaN: a+{1&S%Q|u]+0c5T\ҁ| *Iׁ C*S9p*Ȉq:-4U3tR"t)*B fI7m猈 Dd !`n M' 쩡ՖX@}Ti `$1Hqrvp {rI.tb /P}*=T0` 1à]7.tuAQI,]!n Pa.pm%f.uM1sQP~Ca S &Eyk[,> !܄*Ccvoޗp3N_sTU ]UALS@7P;Zla%AF\vtx9J_rKHdge熀8(`3.耄DT 2?;"d" ̄X@DHL @#xW_( ,H7Hf2h -! "RU@B3yo@ܵ9e%6{w,ue~Ҿ&ZPc yY&>ݎMI K *uI$2&-#$l=Qe@~ VПTA w ,]a&\˲k=s 6AIMEhD.p8kjwqM0%7uK],[ި麔7tTBo|"@kKR%HVLZ8ؔM^"hp`0H>DJ=2[ZdGFO5@ҩ q0a.1!UA\b1]e ÅpBɿùaLUi! Hp$8\Qrm.0LVJ$P 18}ҥH)i@@et ݟ*ԘJf-RF!m[ܱ E(@Ov GDt>hL>=FD(P+L=Sy@Bu1 n n.Uƈ1`BW0?='tEtDιmb!Cu)2 *@8dEm1|>E(әl(K6 PNjL3J]uȳ0lˊ X8)@dw 1倖$40I@0y$B7#.qGAKԃ>5LE C)˃t3c!u9&_A OdXdݖYd )PQ{=5$`0D70'G@! Xo4Ŭ@g_[`D br~#4$ =G:뱊`q3äsPT@T周?XnjdM[ 8 L=LTW<}M\3K~zؔ-< 08ݧ\Lr.a$0dBC9 Sk&f~  kBRܲc!#lX!קDL޸pDvkh́u6DZס#6)[>9p./\TU=kZJ ljZpS#Emޘph@g@Q8ACaqc'KpG:3cY4X=Yq" "!fȒ@r }R&E8!9-^ȯE<K򼜲eil\G0` @E!o( dM: D߰H\(@O $UUUUvI5,\_qkkqAUGvNT?TAUST&e*$\`LUuU74MEJ @"ˢrHw+IFu_p,,[cjQ׈!1$J%kFڈ%E55]]7Ri*4!q X)!s]´喪h:BGT $)9J "  LV>tW`[+CunFAC([tLη%̄  #7qD,&\mAQxe6Swov20PBZ@('p1xZNrY%t,CUUu." 0 LLC&HO}츤e8R]Cq+'2DTUEU8&AD(sE HO(O} ;#Bd$@ c@D:"ׯ.#'a #]1b Cʄuq*>T D= " ,%A&HPq A )&HcLe}ʚ-QTMau]WjjUUTU%A b'_jaNVFvRz "DA$ Cr2 @rD F2m.HIyLsʈQˍuk.2T"Q#m2Y@tg0BDPKV4COaJ )I%AIkȘ8s#" *-qAtj3 D R+cFEG@T2ʌQyqhոCEq]L"1fYRl$K]=PҒ:(u]/--L$MMMX~Bp9>u]UU ia㘦u]jH"Ky@rWXUUes8fb|@BSD"LB 3n ]C̸8!8  wp &VV\Z_W6m޸-aڌ1 &TUWb֭ w꺡kF1mȒ]uy8q)z"M%FDI+aYO" !lVEUU!ze9_Qf*MrB!TU|iڶ-MqE$m9FIYIY3hnlE#>@Q 9~߶LMS)L0&ڎiȘn8D!%1/J|~"eȘcPw]Tc+ ,˱]* !\SI  U' ` M6mڸ1a&1! L8<7[5M3|Ft B)5BD]ӉtCw\WUD".e!GD<+Y0D!#!P#}\mm#ugH]Hە_ QR\RVQ0uMMh):`}81d 2 DԵ܅n'T\ "Bz9G$n4HuqUU@ a&L."`+E$12"i\A~Q~F!Juafr/_Ltƒ gqƂ 6lp-,]tΜ9s{{{'Oe"''uݵk&W__9ojju=۶MRt GsGy7VpmSs^Qi[k˼S꾾h57k()*.**v؎+/h{9C fsQVVRĒ%޾3+Ѻ3>\wsoYIĚw8N `*eYie]YYlnhhO18eY0000uTXzuOOlgι BBqR[[YXX@nn뺹Dj<bhTUlƘeYreee'Nlllܸq);6Tm@D'Vvƙ` yt]oh'!zz&O1|~"vvtt4[,\~0˴tCw-N` kQ ;803??௯,|͚ś6o7w^,eLƢ~i]]]~^^sssGGGqqqaQQ$28:vrM 쬼:Ï(--]d x}} pA=O,Yr=T,=4"*jY~,F. pgA~ފOWCm˱-D'L1]pqmWO$S1vbq3++d[JHK[Z] O?lشᖛo^s6 #??=LB54L wv}~!xnn8k}pgcZhhhpmu=iYܸ` 9fqo3G uueYxsysb1EahlŊ wE"nDZ]ĞTFI.<#(.)~'n}Q[[[0L$)uOsϽ%%%5-[fYV0DDM,RUM UqEEe4u;3ܑ+dvXR]ӦM=@D c`V0\!8|rmmǖHGWw믾JJJ:]%%5Wo\~BUX__W~]]EUqqY2| D"˗ؔVeM1Ŵ=}]ujkj'aQ7`pr&Ok(([zu{{{kKk,ޟ*+(΋@  :300(qUMMrJ֫dN6n@t ꓧ5777~5WL1eMaEA!(7 &M}Wƞ訫[~(iD"5Εwo)pxmmmk׮u]CP,SUU^x<㺮9Q6MĢΎiZ3Tw+rŇ#D˵#h_ni!"ƘM7_oIw`Fj @q\ݴqSw+444PӴH4U5ci]ٰaC 󻻻[[[~MMMoo/D͛7'999&L ÐC% KJJ@<ÝBx<.p`vv6iYVsssYYYWWWQQQwwwggg0lllv'&oS#UUP(VZ]@'ڻ;zjkkiu v8=n9{f[[ۚ1bqUQrsr],ɓ]aZN_DQ?8N{J]fqG X<9rf~UQ?5׆;:#XuyԵy 9IJJK2q7oJK+,(4m!@]]WTXӛ1w WpE_w .JDc[FTU PL ]~L;E(;::f͚(|h4(JzeelooD4UbXGG;쐗vڎ"bgYyc`/H$"BfҒVWsssssHJQ"*))a9߼ysWWW44ii ^XX]ZZ^D[[|ZX,2VUUB!˲c>PPOήήq%⮮P(ؘ/LJ$Apڬyύ8cWUS`n0n5'Ol<G]O5MSUղ,qFUW bq.p G~wvT] i5w=㋟{wVHI}~~[w7m>F5m]^Z?zc>|鏧1az{%W;';i?V jEKs՞9#l#MJKkOLvR˕w=Γw䣯qŃ:iy`S5'?|?K_||g+kiw[.}i{/y`tl+^򛟼S7B]_>o(ʹuy_}+/}||վ?k_y㽏١5_+/ktǼ)0Ƿ`3Y#)OYG}GO^7ػ_;ы#MjW?և7- 6i]N8'gpTM!+9;~|'Gv9ҋ{6_yOvAgt_s??Ĕ rM7vXm~@ZʱL[2K~egn  uspb fwއ^{>Gv3RS.r7k~qOZߜ}p>* fa0)]{._NjoXJ7(Ug B漳Oئ^Zt_B 7mS>n]rA{x/:0AѾ]/]Cj>'.G1ܻ諯N=҇Z|´1t˴uy)Zœg=sr55M`;h-7_Ə5t(Gܰg;feaHE[>{Osюit!IpL[`(_qp]X]tY :,cv+X,?9uǛO9z>xpgM9껿%Yow5dHfc./VwoZޖk>xо35x/ ƚ]vy 8Cy^jϻ>_6>Nd9ZYvYÔ5CYY3dgxT_Oc-W;?)߸ǔP0T1}yhi^jWMpUCp&L-/ճU!SjfL^Th{<n8rߔ_롋v?U}  ?[t/WNS-o]sߕ2Ǐ?(d{&x׏;bHfc./e{;^pk \gTR'Mt?])E@?Au `wKN9rEޔ,Ҧ';M{hYY^|s%U8pm/:w?Iɳc\%D/Nr #wӐdBu|OuC*1h6(iSj7}Pun,foٸ-5I3=OvbSB2ƷD]eܳۮVyS>^ljJkwp9llpɻo^vo ^}<ᇻ}KԽ(?{˷+||l'8M>ԛ~3c M"mM-p㦛kMRY'xs$z]vQwzZ~ؐv-arҼb?ݐS3oʐ۱e}I0QuU!-RmNMF"}SƧ>(Gg;s+6H:$#3яvޙ~}c2/+fRFJ0O$G? |{jÌy5 M?y{,=5뉶>3uGe-\˓ax&%++[DzcqV={z?3Uc`j=|v-[Fqqァ_x!B9tѢ]_*SNzj1ՌۦS|) {g^mXq_|{/J`@ܝzswgo/YC{<4 ߟs7Xٿ4m]^WSWZbyua('#P}W$յ7˾wCo02Z7QrrE_Od+jnaώk h&fזE}sgG65}u'.^rm1Ii睇/XnHXuwwKtY?ΪaNX`uZ~%,3ꎺn񯏪[ј0BO9/{R&{ţ85~KVWfCAq DO;xGM!w-<67h\߱V6n7ON՗>rG8iEYo|/Mdv{׌% 2kɚ;я_mpʃ/_>Ov߇UO+)3k߲9]aϯ,L;ڻ|SڄO5g09)ƾajL[^;vGQ7q3?tGK-R* W"|n.ma@pU?.|ƍklE|eCz⅕aWN-q,++d@r‰siu{sA'W Zq%|,n??C=Ku 1L zX۲%eUel<sO:9>6g=< ASgO1k=O^\n>6ۯ9\[zсO=oڌ#ሞ5 M|8!GB[.}ɳb.fV9aD7ZLL %"x橇^K|=לsC~cx+Qbf}IKox_L=SκGoƴ3ru ~pígMM1~7 oA;o{x#4 e9Ӳ qjA^MwɏH6/~_L{ Գ /7.ܵ| dv:+۴|G`o0>j]O5cn'ϞW>C2ȱlg q1XV=!wFEp.zgN^@בo҉w~wM!Yf=IgvZyuw^4qE>Jy[EKo_}]|3u4q6X}=wJu ,$RY1aÏ8tUҏi9kW_|ݒ/#/Y8any':W4gefH2-|8i,\P x,:ʇ#\%Z^Eva hutC׵:#ڭS5i Ƒ8 ,G`*k@iS ӴbSW;_=srymh1L*u']v޻r=нg@Bd~ʘleGA8`u;/I>SzȂX8.O=ɪ|u9~ozɞv*3~7}M_M]gZmY>@n X^}\x/VMr9'mw?}ӬC/{is\|e'޹ޒl -fh:{^>qL鋋tU^} g 9ge jFeӸq)Ⅷ^Fꎎe1y#TV4G;x gǟIz7W8=>遖I\ py;w+#cL 2* :EFO>qO~Ǯ=mTWGoCUqaw_O][qMc21ηN]u;k~|/zkL >FQdi4w'sAv/^]s6e9b-]k\tmg.:]r-7)iol]&Pu^x.>fnwB}J= Cd@+>l\oNuXw_0>&`IqA*(][KO {:߽V{:y5qپC%'.mϥ^/MߑfEwRgsep?}/7;KcJŖwv9nvbKG7`~IP>E0i,(Gw|v?|?` "-c0KNゅ\uovl[|iw|<6Yiy Ʒ}~qO{5w]/V3?>=?{>] Iw7ŷ8~t?[2նKӿQ*bK[.P*\?ȇ>y[w=̟?Qu΁U_ʚMWܶqG8,0oimҔπY%%!.`XWoW:ǾCQ$l+뫨 01ŷK]ꄣp7kUszN3B"((DrP 2h. D"2#^gQ{ Bh2vN>3kQ9N$SkW_Z'W߻~u\DŽ۽ONP~U۽7/x '}O>wK_}|~s-!={;ھۿON^ٛG%nzs:DK Ր)g=_ص啗}+򪓖T3BP޳"bG;xY^b3rlв;U<@qwox\񺧜eMꏼlUKT+^X.ퟲ9|կ]|'>m82xřl7:c8z.|ॿ]B/_ܷ%4dML{ڙ!9'Y*i)dvk?w_r9K9c{˶ma?<ʟ.__uQWknғvݖ ?;7w|k7~Kxc*+[65pܻgY{(! 9Myw|M﹯nu=W럷\_{#-.l_A_gϞ1_}'[ؿǿ􆯌@? /Z}]6f|XuLeMzE\/uGz~˾;k.x+NۆϾAޞm7ӵϯ}/}{So^w @˟gֽ6k*o;9/;ivL'o:O~_G`~“4̨{oiHo>ˋ/o|s?W'?z\wM;'yӊ Z3y/,4Y߾}jWvɿ=^o̿z7n=?~ ,0յdYBBK$??L_8o58z]Ӷs-ko?k˾95;`)ˮ+_xb~k3WMa\GM*:S;ݲp$c/9:~t돾?~_GNgw-놜v7 ˿M{3cɩ/z;~n`a X5{?+[&oK>˾Fj[䲫:lݘu=k!Su?_ZtAsle}r{?}և@&n%./pҐ[]Lr_SпO>;k;>o S~1u,W*eznp\f̝KePIKz/~+$-_~_TF6r͝ -5#g~CLyej\w]wg7Wcēx?WW|O_v+Oo+/!+}cof o8yK7]Ὼh!S>~:?{ǻ67?/(K>Snu+~] O?տK%_q1Y8to{0 ܎o|yϟʥ^3=W['w,D'?W2dֿ`% Z&nw} <ӷ9q潁Io ޞſq׫s^~8kˊt׾F/19NյDR,!%&ue[nt_||k?U-\K:&t~J[߿c|櫯%(_/M=6KĖS;ox߿ ~ttitٻvQл#۽z[FXC5swdO_IGn=ٷkї!t ]޻wt67|¦M{}ѵ7&a=16_ɻvNt%UNX@o`4ps!~Û//Y?t#ܻwlqiG杧_٣~Dza|Ml{?oa=SYيnZc;E7zhwㇷk޴y݈Os=6nٸ<ܽshړyَIGݼyu݃;wD՗ѫqtt#iݿk*b IDATG7;7o~xoܴޱn3m"]{dRأ.w [z^+Yy$ȺSNezqq*Df߽mGwhohd9O<;18ow7>m_ԑ+qĞ'@#?ʗ۹k|j9y/#ǃrr͓#TV1c}MGz=x-u }۾űLSԭ#յ+qۻc.; :)^un]oxw.[.x/]/GpL=~SY?Z"iiK0'=VԵ#sVy {߿☤{4;FK X,x^yp)k˩=ۧ7%Lyߘ'v.'o׬?Ux#9W<7Ht{~}fJgkly۷/}9okN<(o](_7y'w#oI MOtTag\xV6]ЎpǶQOGwEh^t?359/if zMMMMMMCsĚG׬9^ԾfMMMMMMMM5kjjjjjjjjYSSSSSSSSs}͚Ek/j_xQ555555555Nj׬9^ԾfMMMMMMMM5kjjjjjjjjYSSSSSSSSs}͚Ek/∣[USHҗ,yL|{>Q55R-yL|jߝjjjjjjjjjjC5#,#PUU "1P8M۲V$8bT`M0ܕ%whfU@D̤jfFDDdfj01+LD$2BDfzx3#dq1lff3 OfȀD0D fP,`l&ōXI1 S5 ( @U[z6 Y&A4a)3y GZY2%X6O%b5bBs.Df[%[ܻy冇5+,%xDWDja"@t&}^1͇-l17EƳԦoKw昅hIDma"y^[,ޤ#/Md^Baި2cs=׮ne'"AePyip8b')E݈61ӼhKNj2~X|D3#L-JH Cf@hfHM  מ 0?ro"f&3mb2 gFH9pruOX3%c29Gff*λ"נEcLżs *9JjLNƎy Ca#L HPq1sx*XX @"3b@ IL`*E2Uc034Α!X0<ȉLjf rD ffPg'gu=xȾ摘 E0QLZ$1jqҦxʩ'n5*"KPanږ7tժrq #|o3EĤfj;' v\=:^sp[ 1aj jJDL3` #4c4IҢ8;ёzsW!c'@X Bm3H h $>" l7Ȋe"["O{uyjjnzذqN?YC955#Αe$ 5ӿ~ukoAGu[٠1U㟇_Uus}5+3f!$I,T1l D(0 xSU< b@UVι( 9G3Tr9̬(<Pe2obf<`[UUEع(Q!^sqԌ}g "2"9N~D{PUAri̮6ЧS#15h ,8p˿_{wQ%ңJ]?u͋u+IUU11UUѱ!XT{ʲ̲ eQzٱ;̧"0;eCTW9(@D$Ø%(j;qh`puEA2Ѣ,VfTAͼs윈*1;vj5zO0U៮/_&Buy[^g߰$%l fEdEYt:Fv+It:D#BUfn6 EYYYav4=3 PU$YUUzgU6-}2;3~Yn!~@yzDHz^Ns9Mʀ;竪,%@;fE?Fބa4T%yޗUc)jWT~;TVY̡eēwAWHY/A]㊫Q#kI?yw݃O&DfK6\Mȹ$I҄ *͏[s|oxS$".*sUh`l~'h3[22HTI G02"$G K,1@JIb52"v41Y*eCTFKT9QS44nN쿳_lh&Vh{8jrzh#N:pٙ|zzROQRedcLFjyCX=5ǬfL$"d"Q9DfPUFnL(zz8 * CT)o3I糨*qkam7[b," 7SLE)PVA)s|h梼K@Q2?111`u @|GJ>ƛa~ml0j|ӘD #213QYp16 ϫ #1ŕ3@]k0h،q̢ "Uf`T B+o(x+U5b\0kJ+oP4kRykn#e0+m^*v3kpB?U <ע /\[#@ B$5<=HIcs`í޺U-kˢPRwKؑt]jZm"*'$IyV(^K !l4r3 !"N#MӡwlsUU)ADHaJf[2XerJB39UM'24G b&)eUfI 38KI)8CHE;OW{DDU#fx VyH$0231 RfgdQ*FLDi0"R L i=0 u>`/2`~t84'"Yt(>L;U)}_cѢ.|[kOZ/z4i暱̓ԧNDm6>MA2S󜘓~_EXUyCe)"萙9mSh`v޻q4M(zD-Qd5ݪ뎨c@ ﳌtX7CƄ~Bܘ 5m8"zO|vL"0`byv]E O331M*uLUa #"fjhŽA!$05p|<1řO$" GD `crӁ 3 (*@8ǞL4.% /"L=S"8boѸLfYE mHD*DL51{'Ld0S˼'"c(цTRad`5IYlAR"f6:bXG5S&!D`L]l501ȀZ"6g&@kLDj"O`@lР F08 G tcyfdA0~heLH:5@$bJ޳wLWJl {KD)(*RN9QPs{!DAA8q.a g!(PcN2v 1%i(@$JHؓ^9T[{fvب#QcfO>lʮ2&#%G>uIbj*1 `6R%#IPI AL JD =Y،<NI QIdHC]Iy8%/|'&w9wFO=߸;w+VF>;7]z&`6IU-jP2R^/CzEΑOH@A1{r ##&rL *i 86Sg0z L#y2S!fNޥL(<8QnIT@4sFd!,8G'&sL\8$IO&!h)ܷs.Oɬ*{hzZ,#nA+j<11m7rʙ[7nʾ K78~FBp5 L@̙?_03$fߑ'"3 VŹьDADĬP3@ŵAHpQ#b2Uh,Ȍ, $H<j2sD&Bq݈P P 6bX 3S@(qj`Vr8:rTqD xc8ɔSe bb+=9*(`}jj08pHf !0&qAO@ "SD1Nj8)]TLSr 8 ^$ƃ#Hlb#@3Al@ bVh-ɫD8U1j0JPb&&ʀh0^<KuNP jC٢D;s4O[[TTʢ_jJUC}03h 5ڙKMO=$TVnVU95=[iJNC)㹑~znbfJF"P3I$$keUUėb6;@1N s<ʪ&wPfy:<lnYT5Ur4IU;$D\V5ԧΘ8ICZj(3K~TzeYZ/sL~obr|llଚ:>z }Mu}i=;Jwr Dgm+vػv{@FÎ@Y $RU5s)dܯHA0)Mcȹ,Mw{ݢ_hS0*e9ۙDG"0P`( *` U+`"aa&v授D f$;D(Kګ h IeexϱMyR5kS'5(wg`9o~q-[^qmYAMvN|wIl&fnӶl*&`&)ܐ03fo!pzuxMd LA+~&i샔LN CA"Bf_;D'jT`}H$$JAԜiBUOѓ02c 3T;!vȇ̛Y`aBA0AH BbF0 J fP+DT\P@f0#jP!j,)jRQTD1,½"qT]9S*"&$w:xJV y53gDAH`B %VHR*eL`42țHLcQ]A9 $H`C$#Se 2_#beBdjfvj RCÔZ"S0 Ι\V(W'q]{JJU #QU+,Ki.$ʠy7DoJ-ucX( UX̪lF XBDA21`L*%lDĂ#fާIQvutʲ$MDO\Ud W_ՙIMx^ bq;jRI*KD4ڤ4͢*Jٌ$ԂI;<=AfL+-IeH$K O8T*~(KZMjUU V s}&z*D_f?<@B<Tel5 4I*P̈,kv,E:ObM(xi.|+j׭ ّrx3g9盍f6LXt9xX-)J $E聩NRde5CPQȥbR:GĄB@SvP4I^Y{ >lxϘu{ݲ֮_gi#СI\lnTYd$!j<De?̬5v$@.n7K}?;t˖`qތaT<腢*;^YV0@DNP£T] \ujETad j֝ 򜰵S?@-h#YMHNM e%Fa*{e5W4VbQRJUy3߯UP3j lO*P5KJ bREw>vdkZeS4v DU*KbdyfrF鲪ڭP~2@YaLz}oD>]Α y;&1i ! IDGBBFdꙑgv·Es㳡㡮!y;jHo5H#Kl2c/4 9xݦIªU7)3j:7nS̴=s3H峽w]f**bj"Wp`q3^gbղnP(7;Wv'gΥa뗥խܯ\)ĕȾ{n>δ5z,IgeEgo#ފs011AV=43!Je0}ÏMv^OXss@{w7Y&%|dhDՊ/0;ÌZ؛?"F׬θt99(c#`^i* ;z&{/~) KGIMM5[f!Eyhigjdmv;m&Y&Uu:y;k'4%̡V-WU)(3݃a]:alUNd&9ʕ33 YHtfz&l4>YhTf3V:k뙞-g:y Hec:4<::;9e3 hLREٝ۷<;TU~R.C`YH0Q KAAggu=yȾ2PS?r;SOڴvxfNU艒Z0;37$!) *U~nF9g3X1H,`nrT&=3KC֬\te4:$S0èA5۲";e4whsOי9'3k^ AJGdKC9<hoG34cy,RFڷ=3X42M/**V<7O.A4vH#B A3$q}o Ag9+j0"(J2]6+T &d&`6ADB/l^''$ɫ~w{8lɴNf^'+^`NNNjqzX"-(%mgЎe"bcax|z+k9_F|{f@Bl, I]ʿ/7n޻wl:y8iC" ă~ RlFȅG!OIq$F"=)I@/ʟءw쮟8kp6qN-bZ-.mj$ :v"'ɟI}iyw=Ʃ:N=5'Ԛw@HȺZ^ XT wⴗ3[Kde;k PzY.'&T=}wB!T U1 Q3#TBeV||ֽ=דXI4I;@K^/J@yVjAoX/ ǫl;q $MG]D<4bC zAxr~_#=~woP:LMJ\_T~/v}{N$ab\?2/XK*ML[!PjZUCoQn:Zvh^ҭbWI?͏V?RQ MhyZ "Եos-hA"" ^РK .jڀLlE7>?\r]NvO)K@4 Զ;Bn\)Ije :Y``bPD3a܋0$FGmlJKRMuc.'=5B;ߍt+*FǁBD)plU R^,($ ;4D zט$fZl A g'O.nr RfjRpQ7(0$+uX4)xhq*2T\Ơ .'1^Bʅ?3hF%BX(Q@|y>=rN׾ρU(vs[ЃtaO j!P$H/tl {AFYAGP蹭+@dvxl@PAnzZT`^\0p`.V̎ήQ2%cZ< yG)!B噉 0^M0Rܮ\af+ErʑV^*lO|{[7;/b4{[YH+Bj6fУ4rgggnɋ*'.c|KPUVevcm< uQR "d(O8y/r,S tm4@g|o3ͧЛ aG̚YO/U&U ݞh.2`.mcIAU/>tү+"ҡ2EnrqBLY!Gv5[PD D\xp%f(m Hy^ngw. J7e&HU PhLG}OO󳲬k>Wٲd^OeʪI&GeQ'yQLO^<]'Y%pgN%RVw8hS/ ]g0^JP<c_e EtU ً$h1]|ɪ*9_, !q=_Ugrq0`)$g-#E*f[_@ZH`|pC\$HPAkmumN,Rs6;G c.|m6 XyQISHM5W}suf {)Ehs: Q:]M#[ie$'2:PVAܿ}S(8^BnU* @Qewq#7eU֪N̤L!xhOу[&1dJjpe.VӸ Tvx1%pbU !ahI i.l"y[[6PU-3aM [co~m_S3xDzVx3,>l"",ȂW wBR1Vq2" Z~d=w@OB([b޽/~~ L7zGnl }3Fú//NhЊ׸GQwn6/ziA!JϜ'֮'Q|pn6 e3s绨Y CNuz'kbǶFW`4F@h( EƘ^B&H(k׷:Y,WFޓK*mYxD<@;Nh6󅥾WxeWz]\P@XތJ=[ro7HLU/'GFhc1$MfAG[+WZ&@*7)gydyVkl0!b@$DaB e!+)|`h"~O@vv֜>;x~:DD&;߿T'v/nl |:JIR^j ֝{揷?W鳣%P*^feq޸/V5=ãZ`eߺn`Dl]VdZdAXES1)vNj*&z꒰Ӄ'`cc}X Gin wB԰ӈ(FU{` f\ϋ4H; XmQ2+N`zD 1 0yqo~ű°RXduv4!aT*e݋gˢ. D)Z;U]Ai` <+rTcnv'ygd<UYJ\(:Coy'kc6(4 VT]6s6A3{5ڒ8)bc8nWe:+f蜏Mutm(ml$Q5SAT{Y*yChUbuvn^b^;)co~m_@y$+5ót^{/Wz ׄt`nG^ &ÇCyW/~lXEQHPAϞ>9w&"@:;k~\5m ֭N7oݸۓ_g?YG&wuzzRg~1nm%/Va''~/>{Ka`x|{lQ i 1mMtf#br_-q~鷶0ٙ xM;g+kt`rABbkrE^V%*Jgm b De4Altұl̈́q_W[xWD@@@}ځn??\XGy6xwOgOV~Ze1;D~~6Mz.GU`2݃f+ɳwnL;7dv2}4Isv`3o}vO<¤ۘpzhq|(74x=]>{w崔&_[zfziPV+`VfŹJa|mą:=.DzkUn9w]\9^N1M>N =q #`"+3:f QEWD "֙N'w7ZXp,қp؞=Ѡ:<}v|R7;YN~|~,H4Vݛ2>, )ꭸ..Nnmw\x֍8<;8${{M9?dtsw"Gձ I7Խ[m_|b8S0^utvZ.-G v{yt??;#~Z]^+F'.PIw;))]./wR8xv~t('qiW?|yVb^$Q=wGo!SHGB0khC'{ٸqR*SyjKYP!H#^wwx}B͸{a*[R|FTrsT#cja:[:L<.ͽO> Jѭpya|뺝Wttb燝\yQ܋Gmغ?Tgԕ^÷®7)rg?O'kjcնq,NaAo? v70tQkͼI" v1qZ\{=nkxcg͛*bP Zvo'tUXfa{FHͽ[we]޼MS'Ho=^ۺm]<*1RAev({v'xhR}ƽjfSGðccԎNNxty72vs1xgV< 5ƶh;DDjgF}YI+]W?o]aafDD<ϽoORjllFؒf+JvX!zn6˿sv>13vx>xa]:Yw+ׇduYЉ'`\ J (MT?NX'uX42 Y{{ud,&QJEuj_NBMI0'!5Mz2ot&ܭnRZ<~Q]A|޽u9Ϟp:v9痗ƞw#Ĥ&ڂ jXK , -QTU4k6ۭ(MBdĦnbYUMɮUwwwo<T^(2!)%WxjEn}X5+K8*iitBpNA5!Ы>iV?YO&ERB`Ju0Oɱ/Ed-zg`CA{ 4s--\OwopMG[3|ycV$5_JD@xs%,:ZWX ^1lKVq IDATI9"!|U9geU׬lxx~S>m\ejytfݽo_6a8znthr@1lTg;hc{wtzxrfi<(Bm?Hvk)Xn:c+SKQA 0=)rA MQ!RTFXtmvOr=YͲĽZisg7͢~vnG,EM)Ѥ71a{Kn2qhͫjF"~&kɅ嶷00 \d ?9~ k ¯RV_,}q_k_˿ʹʒD&@Aa@!P  DO?xth1}7}ggsc:9a%nz/֕ÇϞQ<;_͏OdA FI[No{&U "`0PAY.裏jHx8h hDX|ኈ5 WQb@pW͛dbܹY;oq|R ]@Ic7v$|z}}#, ˃oE@+f+6vDʉ(5\-Zc~Я_J+|Kn#Ơxf@fh("sصgok㎑3X`~ fGnd:[/N&Xať`oq=v)cGNQcW2&4U<[8 mnblRs*TƬ1EI"NO{]RL'lԼ5eUxY4MfgϾ}ksn--3zFķ:Ւ.s^9/3# G]fe'6H+JX 0ՄM@`iō`vH+un$No*pպb3bJw}cIN3Y.0[~y;Oi|Y~xDZXZx=;_d}ZT؀Ud |U=xrZvi9 NQْ"~z&r{7=O*;ۼ KN>dpKGɎ&a`ud'r6ǣnB`=|:Dw$/CS5ֿN7] d}kR=ט(DD yESX Hw};YQ䧫aE,\7L&Prqz,1}@-â/&Op < P:/8)GO/ORTtuLn> 넣D+坘nueIcC^֤z5V݁I>^\vl>zwEtwBIؤm{]Uȧu{PFVO:cފp-\{ nO:p$ zgXNq/6~ k( W:!f߶2mA/| Av[q4a%I#VDMeKDT;l/;"HgOA mq/O=RZ]γYvgjks%?Ν=3aΑTE0v{SDr=xm(;qJ,{!T$ R"3[FZtUND[=v)jO$#$lu 1]|_~?C+|L g ϙϭ D?۪r2Dlg !GԘO>ktۊ2Q*@|y6K}Qx6"=y:̳:ԵBų  IL/e'NdtӰ_վ~\UM z[Ӌ5_ƃӳ꙯mՒla~WWglMtjmqyyVձ][fyuT1ZC7G@$$$T "TyzCم@J)Ba@B!u \N(S5 b*'^kkDz?~z| |鍒za8lO;*ng>aJjy,8,?8>xA5UA,rMϤH8/^ ӃΘD$_tv4?YVjpϽ=Ǣә.lZL7 ˼n;KP͵m6%/4!h彍ÎGgN\zxm8O+: WvM)hm!v 2w.o.9"nn^&s 48Ў,%B:DƬVjzpq哭P]&g/~`~u/6Yggz_Lkqy-"J-D*-W_%,#!" zdLN4/8CZ۠4@((AXm`'-.ObmTȣƞ"g+5|: n"84%qbK N7j*l _41(LDF|JSk3IT5e@@.1!xQGk,Eʪ0t(@ 9i) glH/I*C DP6*dɩGv6ЙY).Y^OT%&",3 2/'x%윫ϒ:A ޳g; `PxbV:+'僋@}A/q9d 6e8#'F)FD|GB$Ҋ&PA836څ. H& &.:rVV&AF܌,dAJXVYq#bb靓: E|.2yd BTb>8^(̢QSG蜯uXck'D1zUȹ4]E2{.l\Zj-h' ao]o>~}_x%KW.BO"R!/) j."\ZEU'x rf3X"%̎,mn8`@aQiDZ[kf{nv?X}K֖:T3x+Ad"T 8Ķ)EʿW{rZ~ݸ_\]`5rWrNș,\\YfD!lyB-ˬ+EdHCy^MT# Jf945^iyr*}qmq8yOgqMV!h 0+,/-UGvQۼkKQRaݘiԚcf6t&r8~W @啫GsS5^eQ[_o'j |BƲK%,"""Eye2'.k.DT( qEUTeHaG"XrDCRkp6JljEiDc*1OgV~4Ti\gYma#0)4)kX/ iQ嫢.s˺:A@Ȟ)_V6JkX4zd OZe-P)&sM6]S]AedF"` I"LyDL!xE.'$a | 0;iga>$@E]g+[@}t_[q!Q0(eDA\ }j2Py#V K٪b"PBVwAkvۗ f/ F¢ioVJqp.IQFyˮjڮ:+*^Jtƨ;l۔ oK"&IG[kIl0WUD Txnj!*0JLB"a[KE@!DQ@ *`v7hjgBYn7R󬙊!ijX^u퀱͌ B 8;P47΅& XϚ{Nt6FHbTMS.AM(F(D#bJӸiU'Ϧd#aٽ|o+´OŮ7z^VhF2! r [GsAfUNx6ժu6M`_k'0fM@dAZ 38V)"FNb@jCg5Mg "gsH}wZ0+ԤQ!2@q vDAM@kaa_G +.o|oߜ]Dr@k-(@2"o W-ְ߽vtZpz/0EYT\XOXN1euTV'$U:NNizLո5y*bA'YMY9wU4&*ΚiO%tztQi8Ey*rQ vdWnlS=B }bN(&`  :9[,m>^Q-+ V~p=~GqN/G=B-{lS/] X. DЈ%4H! +GOcQeDR_, !K!If6MS)a`<)34 >gLeِE M)&8sYurqձp`_hkV-lpKZB77KKrI+6.8:d 1f7_ϻOVks͕NNyxBԧ}&b8i歏we5>r!b"a}Fse9gROs2/~t>$`W[ }u6hC̓Nv0uxFmqY7]j<Η|ӝo'@f~/ojͣd,l6mo~>솜R8t5t*%wH\d>Z! V7Q*ӜGƺuOj/LP-hOG\7ᢤHKmxyA$~ˏn蓭4|=\.^ 6c_=_l.ӡlE}'۝S"o])NgN G O_`0my:%0; 9]3>P Sa& iQ٤/K͌{^ѐЉ}2n1 ".k-fD@3pӧfmSܸ7>bW3IJ2WܑkPV?y?7Ҍ~*iu/B=dbB-fIlP1`cΗ) C8}!ѭTf1Bu!__N5ppT?a]㐺wjis-8K<][T mΣ 3Y XKtS͘H0U275VkK[=#!BLKZGA@$as|7T͖q;/go |:)_ͮυ xFS<;˟=Wã(J΋_95IʜM!ur٫fM+@'ի Z0$VrfX'0!D ],#8߯~>]>ҝ!9_n貑 cg;~zMlOFIZjZ뢯~R#l8) VlZ@ j1db$q0 j>9Y/lA%Z͆璍h5K)45֖Rr)%y=bdɥZK,Z4e*GEلy#XM+KZ%V(s<beWC*~Û2!t;=,JR z*uq&)yHgIa^i_ HȁZP(:N0TUםS =awj٥pSH Z4cx_{v!X03y$Sc}ƐŀU9c n .mNq7ٳ1I1n7j'߯ \wOً'dڃw2.vC]~i!~X~m C޷BJ]ɞLp̘†eC0^!0jч~^<|>Us|`OK~Oͮ_*TY|զ]-&AZiB[\ VDP (Q$Z/ rZ Ű*OIH TكPNד)4]F$iRi&waTb%:$x0 vh!p)lo@Oݯ`&Ѣc+Mˠ=Y_]tw-rGaKb!+٨>$ A 9S9ia)+ frSv޳Y[@@'\BK cUvdl˪.$H `"D)(ܘ E jnRP>qH,@"!]2d,HQ(Ā3#سT T\JPkӜC`ϚŽ@ J)6-:쨞Kf .!D8$fC:.3& kcpHVN/Φ_RJqELPm  $-54o˔d;ְM&ўvKKЌB3, !#q{dGwU8 Ҋlrk[H) 2C&Eɍ2СR)HzV J 0JdNTH40ScJ:"8 QNH BkѢ,ҢPL 8*RS@0 ^Xv*u_Bm]dc,r M7SEYEjJt&TM#d,i:Re4iޖitKٚro>H_$DQb.R.W v,\h" 8v&/rfJ9kB>,[3;{qkRf}d,{R;~Qi\ut+zc^ Q)OC ֱ~SvZ۞ZKH=X,D,D3%}K*,bĭV՝,k:ގTrjk%D&\ c(F4AJZu fze0T o\A\Z[4v֭0Ę\vw0đZ4Bp\v"8htwd;Ky]61VG|)F@A*q!\C PP6k!jM(b3ޤ4! @j 9)cQ0Ixw,B̞a٭}h<k.ud>钉),2.tCjTbӐwԟ_,Oǥnu]Ýn:rjlJ s}~~?Ƽ[VMoc9~=#K~ )l)b;7w!xl8? yʶd 8Bg4չg'oFu]GnQ c),X`]~H+Rj\GqW9WV#`cx%b3y6g^Or7Z.}R}$@B~y[kJ_8;^Q M|aP#X#Ȍl&)JFc.W4bjr iv~s&͎G Wf c~zn:Z:͞5aո~ǏOE jhm}/ |oyI74}5jEaf|RYBй77ף=<{* ٪[*/r=r(_0,Ua4O~~vU TĵQU Z DZƛw݅XDzU~/>TK0T3hC fGB\w,0wD0'izdeCT8ʋ%M־H :h0bdTZKfFYZhKtw?3oNΏT3L8 hAJʍZXܽo-}Ua"9}o|4 TUt|&yueV#sJٽWm C =y/ pb T<gqxqg ԍhŃ1C[ !]x}epJ.#zvɫ\Mwϓ˹_;[÷/V%. A!0$BL$dnIwRsޔ⭓^*XМ(~,קzVs^Ɠ鸿JnwuY s)ϟ_u'l9 ݲU^j8.|A gÆ&]h4 Il *m/9X`Lt Z!BL!Pc4-tfj;r7=DڪWͮLnkȍTK-5/=??=zCc[6]1#)oA!Ar/$}]NpSC YܜA %~nG,M)sEjnmwyݡ ^n~Ai|#OiE۲$fִ f nXKrK0#Lo9o?X /*[duVe:#>U"0E#gyKHkV??{vBO!՛9`Z (tvo/oyxVgX;>?ݎ %w]4]n<^G2Us-9mOT9 5(}&HRR5Rx}uhK ĉi 1~O^ `=}p5o+IUI4nD(7_YWg;_}Ox8P H`ATwUkL ޽a8;};ޓ WYSG~0\:rU $( 2J<6Ps]xmed"4 c]M?_~_@$Iᣟ};V;ߌWI]nWf:svR3Я*T'EK![m@do>*t9qزʧi8̼=m??eb?9ሩ~u2SW;8jc8:9 4MU0X SUlICҤq*)Ui-y,>;*PiJR-eۑbے5/٭M491^G|ef7+RJAqGŇm%qoIU]Ul{?R)KIRo? )U%39\澟{ YQ3_D+n8ͥ/] "/Vj+Rnnk%x~?o]Y0ZoqMƳIH0dha"gk.i^bz  CF/PBU@4h5ԅS8cx~~N)A* hZ@@yh}L1FfC@Ju=tky36%oTA!Zo^י]j)]vE*i?_?<\~{c7~ggP"Yb3iSNC, taXNN#./?TҰzE!vmyHuWO kiB-NOn<^GyQ7 Zi ^*^ Lf$Bh+BuZ71 Q!e1))ݟak~*Ujy? &'_I68:Oz./ŧ~4n!Sk^ Kk%+2Wy;k]yyw?{~d(_Tr\զQiڹSJ^5:G'9;Sn\~nQ0y ƑaPa*Өa9tK.ϹVujsׅnuė5Kkkkk^O_0 UHIZ¿%! A qf4nۆa)B(@S}Žw._nR-háYydh|sHD'DR[o`] wc47sxM$6Mh$pvS% rh AL 0O`Ytȇ5#P*eC h& "*ne秧r^ 6M%ώ|ɽѨP!)Z<@QCCQ ֝ (fő%'чUo.MSd w'."%} D5_P'c7Wd^9YW7R@msp9MHg6@#gHvўM[y7XG:<0O`;gw4v-~LȲHNԱ) )lQYZa<PJB-"R!!bal>ЧƜ_nâci:Kٚ5+QZUͼ4c Z`Sx֋2TxLmBFN{;x)Xd,PygyB}Ƿ3@P6>%6qjjA lrc&{߈fr{XVn=.o-7SbpJ)E#V} |ؗj}8Wqq8"'ƅs6 0.2_>I c][?| ٻ|@p,b45%Z&XI?,$\,H"?.̬3 TM8:|u-{-:/$ nVy:"o^w~>}%wz:7|OJR7+}=p"SvlL_ IDAT/庖2^_<. r:SLc.mmjYΆ:mR+g'yw{K?kX4\$plJՒIjHz$E,Zd7x``ˡF7T.q<13Af+g!YnE0F:Cn+$]&'Mh~BdZNeeD~qj{8ß=m/aX]L;!rqz=-PwQ'Am8'9\)uݻ8=D h.SU!CR0 UBX8GeNGҽ ^S|)[6nMdӷVR&<$R'^t0V{k$^k˾_-9qDO;=~l8;c\PQ]G[.ǏNsv٦[XM<\fYF#%)X:rvz $)Zny;ҍoo]jK5e ] ,3U"TH-vNed湸+ 7`!iƐ"ˮ߈GsU_n.{C% g(7ٽ? cYZף3#k-nRjܽyGXRdo_;[-'AffJfV9RgX!t1X0Ѫ T%t+ @T9\g٘[v5 iۄJQ탖5 DxQ8;~f ܰKٲasXvo(zzv(o.1Z'%Mi|,yIح{?Wo_C^NqxZ(ee4驖p9姛o}7|Oe3[.?|[zOOEpz`g7$fSu 2U vzd+n(]&M3!@ Uu>u|˝rrg[kd[{yc/U͏n~סs͓_ժ};ѦI$%%aHNfB$QPDa͓8>8y2eQfT#\e>y? w)1_1~zlM4Dզ%n?]%`u)Jcjцa!ťW!J^]PwOm^lr|9)9ѡvVѣia)@jfyXk@B Hy.Uwo/ I$hMb>{gRr'}!RRd!-CHS2jO i”' 6GӺ1yy%b}t dl4jr8H CꓫO0{n?|`^jLnR^Vjxxy,sChд; Xj8}6TW孯ܽ+ }9x1ђoOpw8?zݻ痏g?tk+>[/bz}ri5G/vyH1CE*.5l;[98yI&_Q:I\*J]X.d `gG ,>KgvI*Z$-P2c4=cr)֦u}@{NU4^Ue.eG4mէE.K&"8,wzu8D"/rywu9ò=){ag* e.f(Tuևp۬ Lf4 mIhKɜaVwصY7C}ߥqfMFqp#ba93nI2d&3/փ$ѨVӤkɬ̌=bMOWbɌ0 pYծ\ pQh 26M 6z*Af4[#\]Et7'gdHNUQQ:HQ!o24umsgM.--x͇jK"RsT>q|VF$́V*{_5t mBU^Y$B0[ 80 8صرWKJC#iCЇ෻O~өĠ6^nP(Xrq3,]<i8H'ET:Q˾TqH\h҅!di}YL@|v&dO.!AgQ'>@DD@h2vEuW;^Ufpuxwvhpl ٬۔/~CUw` sqN>fݮ4?҈l{*_͞*1Lէ~]F E J/}u^}&`uq jRH% [S1iC%D " HD&R&jΟ#m|Iw )&24ɼ٬@׶lĘ,YXbvK:hpb)Ei|1nL,/֫~ Q;E2wWA{>Y=\.nO..aLTI)űJR!7SwNC)(źߎqf̌tpjp_A@,ILyu )`!3vw]rU WWB@ĝP f%0º&-ك!=X谢īt;EVfSz75 .zgI B}hQxԞew1_m<>M adV #^=>|0ոAibnp{J, %8 iF\Gn&Xr[TTT!u .jArӔ]8K.]T!rSRUDZa˔JviqͰSE]DP9XeLv(œEwt3Cض1~Fy[D ,BXJlX1"b !A:~ޤ LkMRClSf!B- T1B"OfHB*A$vkzlMsεV}=LakhId d%*% \ ]p߆d WHO*iRy9j  NNPݠHQfmJusRb}޵QU dkJd @VMQ ;ZetPj !ݵV#BE^uaNM_US _+Hՙ%bxIQI@)co~ANtB~Ik4o> LΗGGji85pެM+gݼY;<߬R{ZܓٯomY uێ*,n (bQPRd'Pc%,IEPT"ވbEH8gQ QZ NU A$hPhS,2: .StE B Qb(E /Wn4͐~ !Tc$DZO6vv&veY:[{wiX<퇟٫!4Pjy٤vֵm/ǜXMg .:HO9`dNhh!".*w+`vSƩrvCSb(nfҤ`FWa>IX8o&{.i3³ƶjBS)` հ18]0ʱH\4C^fv~͚OSvjE淾??Y6~Gys fzi_.i0|,GowvK-@%M@(d4JQ$#0efM3(A>Ѳ;Jơr,1Lx]vSW5˽֮ f]*RUYDsjpY[@Y o7m1k& |vHX:XX<,糟[IIƳ4 `"Qhn#q쏏[Jٸy5*3Wk%AdVf2Cq%P ~M#"j:z;ϋ]$<[6y' Rܝ"88\m"Mj|ޒ27!FI{lGϺ2ṣ}\!(J4O9HCmb;pCKNH1|~f밹_O~xxxSK8Uu4(V5ƐH*uK00';0mb*ƶ;qL,8ZdBZ&YvS!IuKCT|[{<9_](aw*7sHC3!9'>}xBηs|x<_Si\]Wa{[Ajq*Ai mcJ0X)yqy;P̋4B)Nn!UQOQӽ-J\U~*Q3P "){43#??TCj0 0qϻ×K48 "*A zK짜)SA!bġFf;ގ !*`1㓳"xxt`=v&H,.tf r3E'_i >Y¿ݹX.?9!4-zsK1$%7AoQ*Ip )'J)qKLk*od_kCG?v 4QRRW \X B]ATJ)%' "ľz^գחѠ*bb8>9_x)%Ձ(<¶h? gsy\iKz*xP.'O&]3}yֈA5V.OS:8X *be3(4޻sŠBˋirwtR4ʞ__bŢ]̤$h$<5Lr if!]GLޤ41.n1K'C7uӵi-]Cb﬩{wlttz^{.zui? d(x+{?9>~ソZǏ?`pD@P4hUhWZ*] a."ρ&iSMir\.(^L%mS':oSx CpW )uv4fW_?ē{.ˮw_|jO7QQД__ g?φW'OrO~_b*/h @G@LIDW9C4Sk0W9A4N@eVC T}8fTD@swt2F-؝! DHL!!Lh ;ƻ;ՠo}ע.pwQ t)xc,"FIՠ!YEl 7$#@hTUc,EE=yRW)E HoGx~{N]]fl~ ?wLp*PLkX9ڶѱ[ ARVF~톏4E4c t9w+ TGHQMVbW-C . uwReBSQuTLSMr?Q] B_hUErOc4]zRab}v8j..`*`qxHi{Wy>zwz[z$HҼg߮n5 h."!D@mv-XPB kAjd`Q`,6߃4 8sq'RۍUn^g/aLٳq7:ُ OͷY˒Jlv2".u.R)㸅`KrY.oUSa q?Uo.ZY?x%WͣGmX_ 4mY aqq' R$ 04>{5 2i[IbL6PBX13Fˑ_6۟_l< }-{UM z~6؆ャa_\|O./wN\b nB"jo~wtt%6ӳW91%")'sVN%boQUk@8="wq u/Pջ.z>tt QA 'Q1̜Hx<  IDATPG;TD,;r??X.îd;={D|#U{w̏.a,#`{O .ϻ̛t\͔Vv9L:"|цGGZNNV!wDph¾Kѽ.Tj#|G0PūFV;.3LdtrdxLT0r⵻a[Kve ITD)$]Il& ZblTUQ)L4AIL׷4v-D3sC֛^솫^xؓob_AyٍͶhHːin{=f٬V:CB}_IVU7X{MBJm{v~tDs = + J<ۨ;Q.3XinDuf;gs1 Û714!(qj]/^]J͆J O<;I&,[wXh+n>t);OV7cpf=\o`Ӧk;[4H6&@E7U%0{T DBac; ht~('7W,usƒH{1Z=Oܓ>% 1nBEjBG )ijZlKtg m%h|6MJ~kMZ 00.9O~ϋzwC+6a;\<-&Ƽn;f V2(-\޺k[jhu{ܾNNv2x_W.>ڟy~nVQgS'/Ef%3[ο v0y ðje.ͬn!> E<Đ4qvn -bYobhD<@C.(*PnX,hw#*|D:lvJԐsq/?  cbwt,S9g*:fc׍{kP0F7(&ADSA0H]jʘ!dĜ”amKTJ1Y]o@zkVSw])sj{ˑn "%' Nsz \!v:>|axCxH/m|"AP1D"PR*1urV׳y I)HCARX BATz{*QD]HBv94iK~e}tgq9V݇swyNjsţĻ7'3Q '<;9j-ݞGդCP<՛|w/L,إAUj5Ԡ !D0N>-;iE4z4Pn9 ѓV"iodNb"4+1P.zD9l?l<~u[qaE{vfޜyuRoX!D(?>GS]7!$I'}&FQI %[o?v>iUqUOi5Gi\opqp~sϻ)*U5#CLMaJjZ#Јv[tNlY~G^wOzGl7p/5ޙ!"o$\f/~R`ݖRhl6kKj_LP(ulqSqP'uBFeGmڇ2^l)ਭ,AWgZg((g,n ED41öaɥ \lw6_p*IJG08L./t/kEv'O)E `NF^ƘbJPE0MY6iFASNSS. hц[Fw}vǿIOկ^>ak6_˓;uËuf{Z !$TvTPMqibR@mvn]![$4Q gVKyФ7+cX40Σ1}C_ƾm n3weCfs]NŧۡMz*/t>^]A8vWwm{ጶ/fӸɖ㚾{pߌ7SţSu@zQb.ۮy(%ᣆ1__[+]=;;nS٬]ǻn)2^/jțA2v;mŏQc{wl#CJ?Ni!6anf7כMlYrX}壿g}?zWem?r?*̇95 |a ~yyÏ)7wBڦ9>=fKb끷):D3EQR"ѱ(1; F[ɞ"6-o/_I Bo'~NbfP) L4'˃5uF0KF4&9Ɣ*"Bdwm}^5zIOԜzI%xH7tky*H A7XhmۅPLd\ (ڈ6 }AYDjmӺRߘ":20@P,oW%FxhϦ1L?^ѓ} 3٭G :p[j@Ѫ(/QFxx0@h7D*C4Z徻UU{1]׃B*[slEJ!K^\]K>6M420xb ,He"@۴#Q᪚ d"H0YgϞ~Ξ>7z/o~:bفV@>gQ^/08EKG*)p: 4-U P?kN *RhjtLM3nUU>L(7j\`6q0Oy:9>VZ8:o0SfzCvm(. Պ8@ݮzxWҴ/׽rtG{vpz;8C"NR'[?xIxZm PALP'&`zuUuO #m&20~($ba6o0OHL )PBGԻmV"5  ! t͊Bb#h ]˝y#~~$gB" IP IUBP Ai\$"1[q'NÕگᾆf3T+2!T5E/'YJyo{Iud@UUŰBPP*ji)쇜s8J1gΦ0ߊ1Uqн@ͧ\$j ;BJ}1ltWҀU]Yohff|ς VGͿ?{ʹesׯ P*BjS1:71U%%I*(TBb fc1-Ňa " Vv@Ak%c.KT?OvhqJQV2˅κE \ۃ?~xl8YM[70nWe~tmpc_loU"a`?Sp \`ey}1o틃;ܡ_ڽ@FeǾᒇ8D`jw؛RK'|.&j!"P o [x$kOΌRTsdP!`VX7xW%=vwʊ"fvQBhHT_ MSc2d@TLSS`S =B$$P) !5TV' $PC Bؤ;w15 bR@ hzB $D," 4j1 ɠLE XN Ĩ",V'TUTԨXtO}&PwtUX]t)OI  "1ȤRB.pARV $hľwv<BD5].K <}}爻[3'g{|}U?O>nۘ>WCOr:}""tw *4QDBLR#!wo_KwRTJu$52DU+@-:=mMs £zRFzʔ5@PrNFeJ$R\PySmST%Dt>77P`yxpcMФs'o)ROwM30Aow/>w?uhB\DLAy7~Իu5MӃtm#93=fìtlq6_Pws;= ĠIcɦ6$ʄ/V&OjEY q?Qˎu` ED  s=DDTCwodɑescyK. 4=JFII?X?AAfL&Cq43= 43m=/_eez8z)TyxD{=74M^%VTwPz~U'mzQ[鮺&Ge 5 2'@,X-Yi yj4ٕs⤦": R:GDTN TAu,X--M %7 9{'W= 脺n k8ȚT({(RW9}h(zIՑU!K`ɚ/ ;\VMp $Yz3$"I3yӤ* \u")=,cy.P7&!QlpR$]ۏ@9PP+HWU~v!m=O/6(k6㗯^8n$$8B.4T=ȚxDE4ҥ#5 "Gο!:cw O} {wk|N,vK{$ uW|JHefxtj՛8>mfVYivi}8u":^V `\HSK]ȥ5@8 Qc WB@tk ӬhM1!fX^G2u}wo2˞tV (կ8Ȝ# )]˟S`U5G?@⩐0@m3kImhv4vt,ݱfdԪ7zQ}P;0SaӦ˳n3|o{>'v1}YeJAzfnBD] 9UbP۶$]fV //{ptQ1P݈49û9Vy޻[}peogt4g ,/b(9 Y)̌tzܥߖ $\fk>N?IwVbeN?:,  f.rSa?9-0Vh>x` (y.& A K7U\>!mmJ. IDAT8 ҸyX. 4N%.[F>ȴ\,b\Ls^$B T "#3hb /# 8M ,pTBdD8|x'8sݟ VQɿ4aڏԶ,dek'ŒXwNv4%pX)ܸZZ )2ӏ&_\w_n(lge~8Y^6{"|ViXO3Snd=! &v|M p`$:J4) is86oހcI'Ջ{?+>k/Cڈv-`C sj|Z*]c]q/Vݴd#5k _К@C ,xߵ?.iy'X~#ΙD([lm aSc Ž8U c~?"Q5kOm?~嫛o~ˮ0LM _<{:_] .kN@ՕSH@瀨=`GBbHصD0( 17K歂Ij=]TZ=r/.s.=w)uʥqJHJ%K ,Xy=} @XoOsBkFyHgNU%͚\f,op"zVQ9hV6F}EIr+.6;TPH}P3qF5ѿ|r?{wO~9\wi>|$dFYG+\9x$xbf1I`@~\#yfr^g8mTiUg׏jj&(e\!ak]j,\vE71]<"Wq2ns #Ð6ZCBLa<7͐<ci!4]!n7)2w<3dEڏE׎%=HPAi4_vzpo%2kY&ڰ -4t$Tu0#c-kTH@>mGfׇkZw̻!r/ny26g]_2ZPq`AW7{~54O5,p@,& ÅVD 6\YHF3bE&q;Z@YNHzgCry]}􁥣lf.0N!5]{{Ggm7T(ZnPP4Xp3o:Y qA6C+`$,ԅVBa{ݯ>[ aΓBg,]{X ٽkNaKS׷oZMyh탩MW~jHs/I[S-1͹oIhvѴ_q(Iif ߜF@ Kݥ wG~Rx] ώ]*G/@0G*1.@B8ʖVhPŽxQJp8T9n>< WgSOf9Ͷ<_= x!Zٲ__͏]$k7YcJ*O_yJՅ)ϐJid06V֛UA sD`F`H? ϸ2 pIG>\vկ 0&P%(jB`4.O~>2rFMR4 |].s\&JȮgMreA\pb!wlvڏ\hŁ`*yˀypq1. _ޔZ_,\]Z i|z4ֱ9E8Ae!^Nyʇ]Ŝڍ,.rSg(9M44i&Ja\v_jj~T]ECoqB?](~yRXF㝑?D(p82Ll}OW_}0%@$3d^06HF܏]Z8o{^_%H(lHO‒J)H]޷Q5{r.QU.\: 8s(xhJ*tևr,. ˩޻D,Ey ~b}awhJ|Xa}}8EW=7!s|ݭEηϞ׈EZr;/>f믾^fؗ߼f/>z\qo6;u5)Z(qԋg*I*xUe"0Y6dsW!rRf< ws 7C%Įj}Cf/E, Z @ľoC4Fv]Xt7"h]K?og*LXp<~(Vnyk `.6n$׫%,4gKMŗQlk?l77MużJEPǁmJ4v4}$YTr(hn>xޣxaj Э-.v/bhdd4MۮM ffhDЄ$Myosj\v}Xq|wqNը~\h)0h8N rrLɴɁX}/jPt 3'7E rDD}KI,nYV)BH;ѷ J)f ѫ D#AU/2wPHX稒48N{dz0ϰ:E H|첚$+ AugwIǁo\/n@"\ى'PF 8;k>ꟽx/~}?ŋףio]wvi ? oy4ib(~ Dulyyo˃L&h۔|die6@!X3~KWfx[@G8pD8cocX,"m&͋Ųo2ܗ?(Cvm^1e)M͂ ٶ>зyf^2(e^+51,׫G9U ,Wً]ڐv^Vsx!mFL))Kcr{+bY!e71U:9 $rNV,.TKں[cڦmX,B<;C03=5m0K.!2Phe)ÕLj>N7iFoSOή?g _dş^!.={߄Q$xd{ F0ΰŃ]HNŔzZTyop2uonMB;$JЭ\&[../*1@ /m6@ D=?Iۏͮ> sr:jdcf!XɈ:&`Fr*&F]1vH}bk/Ik}̣˥֟\߽Y~7_gyp&Ʈۆ1X oz+o ! r(Kdqdx Lg(U׮ B{2='bk");k62K f 8|2C2 RJuPx#ɚ6rƷN1GRyPnvSlf=? m, SϬ"oV36k Kۘ+O 2,f-Vr.$J9EOȥ\rBnB`0@4ɡRқb[޷jbr[0.oyCSzBi*hOSEHr|v0yN6 g G=%T2·ܱٿ\t)o UB ?l>Yb6\}V+А3)۠T|e #fep&AP<}@0B"Hq9wD )Vu9I8~Լſ  .yR.`FPF2@␋!)BzWSU$O)!u\{r_.v; &5bu]W)Qż p- $Q״Ms^퟿^vKF}.0ʅj$DZ$PRrc0VTT)@41,}ʘS^@Z ( kwQTB<٥c$I& &B ݲoHx6/<{B&+fEv/Rnq x43)SֈK2>x^,JE;LDԛTK (%Y @^9 (*nr )GFBEӊu4ʎ-j-%FRj~QmaHvPQl"rpDT̋c !҂||uJs: EEXK:ܘё 87K|B}Qa]K'e62Lٗ]=0 )s28Ҭrsp!' <\ƈn=i֒I.1}P^T9אܺu-r^c=KOV 0tԋ h,ԩQ)3LFn{{eHBv$JR !:*M<VJW4v  Ծpr;Y=;w,63fG%@wUWOU0jb :0znJɏ\ٳD 0CY"kkD*qZ\6ʄ@-BS5]B)ֆHD ! 44 yA:4FT28x]A-ƇŮ'?g/ǩejs1r? bѯV+^fha\m@eկԵ!6 hpNS,b>RjDmS[fq+E`l۸X=n)yqў)8|( XܦE]~oaW? 0nY׬|e.2ZΰNH35yʱHx2k5-au|!D>/0qc0 m.j5녙vբ_؄vH!7@[w՜NҿOgi;A0g@GȄ<_aMwyή_KHDtL7_DqL, Cwr @J.3PtCm 5bX#T'!v̼TԐ;C{>%^v&B!D f9mrhD@T$RR)pB[ѫFa 1lSqwa}qH;,}q&>6m>i o_ /_ay2HmW < A 0a^x] I@  $c_CcC43:Zf&)U (fj-HB, T.#X肅cP0~wI5nH=QM> qUoW闶mъFwguyl N MRfv,F}Ue(j_>V Z$7ܢh0*`oNyo]flۦ{ל J BMX)>1:⢦ }"ftNg zyXטj8irci&q]ۧy_b>HW%ͅq]іxN/vc6wy6 +:blڶ1ʯ-wW (M*nȟ3"$ C5R}3i6"99^} Cl`Hzpr+O(טajN_  O9(h b`#,nZqdž<zHi"(d'%7m p]h%뼒hMl.Ɔ j;R*Im(wyTR*HSJLR|+vC sJ^Ba7mra<4X+\P`v\G1q_E>~O=W÷_m,"><͇זe曜y7m1k TkIS$u]o]2J ).Dz?m xq=S IDATitG?kګWFfAᶇ HsZ/WU.qS@ǡK ./jdIEbX lIYQ@l=͘]܏k1NUQfch "n(:8'TD.RNT= Y%or^z\,7_\OgFCtu? }'^t/>oWG  &o 9Ŋnʞz!.&G)V'  A!X'pNA*glُދ]aÜckmߡ5<9朼xi^LKH>fqo[A}$ !7WMl?{ IV|Qt }T\'Xdg_f7RyWLvT4owl}qxad`ԙ!+c{d&i !{d<z3BC2kb4h6:Rӵ(3DK.X@l`t!:JU.}5ZInq1wf "h^@'c16xE2/}^v)Ek;ɫ{)h16. d4ΚĜB @axΥibܖP]>OƮlWK뺳 ʥ$ ej B8X֍@0u]F]q`ݜ/W ھø]+\9ܮ=0(@M7267!˘eE!vbџ-VM/iTc] ~_Ȫ wCĸj/@+b3 !D ql'0MVi"Q3v (ybњ&{ @wa<(h T !h2:4Ҍ*ۯFp0٥2ǛO>>~˛y?C!|˂!Zg&:`{vIJs̼yo-b#Pr85>=I^k'9\&@wWu-ẅp7;zȬјn@]cu+3cK\H+G/|Zn>]{J\@؃DKͬтl_u^52%{DuQkG6 CuW]}S[%qJcq^״CKdjUaw}DXL RLfo.Z al-20wCNUސެ-R5JNC2jMwΙжPBݻ^wӱh/'BZ_8^Q%#t)mb}*PPF[:r tZ040u>K U6~o4@)zn;_R@icuu'}7mY?>]sA<\&FՄ9gwo5_?كrX]哩8>Kdc8MrJmI 賊g0a^2Ƞ9HZPZoLh#ұ'!P;AҙsR4Ђ&D)z";UR"aJ`L Z=+GN hX_]\~nztEO`{ wDf.v6Uz?>'nz:.<yab7Wy1| sȀ͍sԚS Ix{7%۩~ųgIQ}|pRBu!x%bw8(< jw{xvkw v43[`/&:Q}z>N7ü) _R|-c#/V4gǻ벙Xq1/ Ml]/Ц#nWҔnn[vzcg)pf"4|wQ TCH֍Qo^\u,nvK"ԳVzښr"7*ջDX koKnh@J9ZH}_js2kSeSN))ˆ*5_:rDMGax-;/+S+E$m1YE_.n:-˲kٚ4j Ma0u{ssWr7>zoz;?|ZGx. lET}z:W?/'j>˃9\.@zʒǨ~XZWI^ۢ zy4޽$M|`i-W+$ F@j^'ê_]\+}n ]!4klY2H ;\=xws}_/7?~yʵ 1jv??/}|>dG?:dtǓW&8I; 6lجըw&(4R0kw]/?? ɳ'wb=tsE)GO~x` Qf̺s?gOs^X8uZgfhf=jn5(`@)͎臇eĭ™DrZKbTاi>L9wRrd V[yZ OMs?y;kק?ElͦB7DZ91w$eR>Q@( ./W7)UR!i.733,CPjηש%U%OdN^jR3j /dK$sd02r6lwwu!8+[_rN"- Ijݍj/"oI@Wks*&S-0" h;/O~r~_x?}ˏC_XWf{{?g?t.㏟_?B㜦JDsаT4ȖV]׼'RFOktY=J1rH4&c!.+YtHRJ$Z}0k _z-ng7MvκMM v`,5%|eʔ4B9 )byv?5ߏ9[飛_yJÏ~/WՇտ|'/:~=}$MsEYHз͋y}Vn2>f&zBvR/>{"oׄCHsID) C.΢K?Gx*>t špiV&e9cr޽n\߿{\wcS=B5zQ:U\Jl m*6{?(SY=ZGG)X'|3ţ U{'V AIXӢ?}~2>π2 (o> ZHܝ RGu _Bj^R2d"3ɔ딻c!Ow?Ujr+pN3Zb~N%d-&v&#5 p[b%f#iABq9-;.%3 V:ֻ=" `c_G9؜hsS0": 9a&% `D 7#ʊITgF2%pNVD YRԪW]B@"]5A, R`hd$"U zI>rrFNR%Ynhs t]?xS$2Oc)W_}^M a3x7o/wwq|6qֽN}u_/ݘ 2]y۪| %ovl`Y  h8~*J,Ո}oum+.Ƙ H0Tw)\J1ARqc)En7(sɻ| 둇~Ϛ6- `N}qe'x}S nf Ms,;CHhyj3[)Z.!T1rNaA9aJlaz?b6D)XT9+r٦@psb?w8ؔdq[ }WH $)b6*]eTَT@X#!o]9pV)%e)N0+N;RʜóB2"6ieaPJ"M2H5"H#Bf̹+/I1ihM)5TEXidae5a4$5ѪʚYYkQ'5j% $@J̼uID1m.6 #Vzs?ٿ|r}w{~HOG޸͝ZCկ:uoLj hk4eV]=I r_۪߸o<5 J$@]k<;.b--n ۷$4"+jZў9S%\oۑF$P4* Ԙ ir2Vf>/zе]Uvi6"?y|p?xqR! [sddfʢ ;x4tKaYun4Әu:Xq:KSb9&*#( P MXn==Q"ل{p52  ATZ 4H(p0}?q,sr2NCV77y{Y$4Ӝ21PRia7vOlV Rz8zGٚ8ꌤW| 9p~q!Riwڮ#n7v8n%Yz TCl6nwsYm?QǏv;XaU<{'{ZY)49I333gIIcujFCAUh8LxxDN':)s%Ԕ]4B^סo]zM}Yf)M߭! }tZe)G& PS㎙WּF {$/zC1ӌfw1it[4b|뛿HA/f3?.I}t +X8hΊ3ŀUSĊ=d PЯV  iTDURTVy CXv5JW&UiiUQb 7k;^:}IjQA5ٺd+x>ac"xlw}{LϞϞNȻ#_"=xO>$ ȣ0?y懑yncQY"_+v慨|$ـ.&02@Rr߯0昄3%{$ƽS"P<>}|vo&tqquÙ bP763Э=qwz:<])nN8quz+,9Hc%{6ýyv|% $}9)BlMNHpt)߿ۡ;Ta9o(煉"% ^gpn;X?;wvP$K݆!uuq㑀.Uw膮 K)"IHd2ԥfb@<xtVm:$^~+ծ8Fz -eu|U{{RGz Z-wZkur{HgC}զYZ~`Y@Y즌$\0tf]4M8{嫍D39jdGn#HsdB f\u]wyyym DPBs Y\/I]mffMЯ[$mKVp R)ռf9Զ$@!"N ^Ek+節R W J 4 G|FBB{Кgx1 en{RJN[j2(0 fFK-$_# qD#iArl/dk `CQʸڋmq z|Wj;nt2j})bC(5w ]R[v 16&A"1`@/]2ሂ(Qq  $a)后̢Xe I F-|#\J&]цgZb rlĆ.BQzИaoP"f@%i|yHab[-""fdB@)w]A<qÌuy "3_hVkƩc $Hu.mj&iqrɸLÉ:SxиE#S l4)*Q}<4ZR\LeSKK"%@"AV#Q<F5s΂Z%n!JH9YswؗRRfl.s?1Y`-U|&ܼXpGF׻H|Hnez.,m@"R ,S-b}lXQYsn2?XHZ3#@1\ƛnZ)kzjgSZnNȻMٍ d|O@Дz=\\]^_}AO /MզVkxlf/kt1 xcW B'u߅%$HmcHXfq1R\jdPmenE҉9-#&F(L)|c1\DH][.u租@\EBdϵxqv!+VE5Nk r5Ĕ!r5~^CZft-ሮSJ/~]RΖ,TkuGPh5:@j2P[F-H\ЎFEËş/z<!0P [MjDy.s33+b9U޻RMdC]&YFge=H9_}ÕS9'.pvWNFiƠχá&Ðy<&SXtr #ImͥӌSk#pgO+IנH2K)u)MIrr$W.ڭ&xE8=uEB`Nuffˢ/Uq3mTouf0CD5Y")V yH,&Ҍf\j[/gx1t)ɭcY^AL\ 8XL#-ωd3P&zDRm4)Y3_73/Y2ae:ZXt$H _ΠK4~S晄wXV,Hɨa;wn.>X+G[xoC]Co9ݎr[2eHI񤭜s))'y yqAԈ_+wi<]/1%Rή*I@[Xrp@ate Y߅ %NecL=R`GiުSݬY"4M TRz=|ƻrycۏ߻xإO^9]"}S[j|lo,«Ќt ?v.\8|%l`i~"`480a@`PJ ""C_VPgXzo4R"},GW+)cϔ:+. F .zH1wi?שa ΰA/ZZY ωNuR77Dm#q0 9uxD*UƞsRksBR\Y<\iΉo:Ӆ/dp"M}xi(HmYK)s=hvvx3 i`Z@1͵.~_ nK -dOswЎ,J٭ GhiNmo̠v%a-kQߖ t 0~.tJ[(z6oj$N 0BGt)$(ڱg6NSR q3HyLV/csXSW!zF`ZBO$ *5qݡ,@ntHF$\jx("ԊpicW=`EMw_\Oz"X!^ hn;zMrSԣalU7l{="FuDƮ}(CoJ9^%Yѧx40b3 U|tٚMg HoV7S3Sa(]ݮwD?z{}S1QwY|9v-%М#M- 4UY+}$ BR$b9s%xfD"@$=AC;E"at20< A L*pS:5RF@SH̔L{J})vf.+w}6\oއn˔968wIrEms>yK&E4d j8`o7ĶXW<CSR;ve * vˣGRN4y^yt@)s, r9ҲE۷HҺܭԷUaJ(5K `8D[,u5jrV1y:unOvjVSxEXodkLD_Hj:shׯ|=(,T!K6%orUgĩDGxdzFW>4 ʗR<=}lOZ'HCJ`@P$ i.UwlR, "1[QD~єr6aK#H KflqGA.'5gՠBR, haRF4m~@3w?8޻t=ytS{H$N lYBPvt5?ps`aDHIs$)hY łV %.قtv&;)"4qljunIL)5Puoջ. dB[rJPV`=L8M";"pg3O))̖d2Z 2@A j B-hNaÏj'_ܔ}"vj{ Z"UyiWuw~{{b7779a*DUFTy,1j/Nc> =d6l6:U81C?V~{˾Wu2~6 ^ʴ٬2s0 (Gd(T Pyy LZJlZx 9B2ֱF>?0QF4s:+",lJ)b4;t|79(ؚq4{Mxx7劘1(p^ka]fXWErrd[Oh7]xRs¿Zױz9]P0 B"j@"%!#\v "f;9S>5.;SW`첦z[3}UF@x,  F@ Q'.Q&W<@9#sϟ#M>qΐEc U/}sJ&az#u?CPi0><~ᔱ;ꚵ(; i`Hr}C )0Iz==~첻JY]A:~'piwɌ(Eyy +k-P˼mns]9֊ֵ-fMS aơ2=l!"m68J8v PJ*:΋r Bx{MjZ@Ӱ1Odi.SΧes48m&œi2))J)k2jڗa7|".RV2-j|Xb-Xa]ֹܣnMq'>P$_| xL'C>ŏSQ_oϮ.ZϮ_oD HN#0VQ:"!)?X<벼Lk?[Ԡ2~NOcHӓ1x:O`dfh-HssU>yctir&jD)KPD'^!Ièc6c1[7!c8 Ot609`'LlpR2 T$*R14$2snRCַSzĀ9g 5h)PT|HIE-U&Ɣ|pw-vBk)>hvI0@k-h>Yg6N3OkFVj'}6$۲h{T\`/+Ah){LJF#cEn *y'j/$zDrS.AAB "!EDRFM98zQkA:$N}|LW./_N >߮뺝qbZ*?wrn6@~9noÐ6Z+iq-KCl7oޮ24˲ޚC(v]2]eۍivƻáe.uifNî@rmUֱx}zsJ)ۻvF;lk]yOCN-ǵ@S'FNiFb |9h1 S4ӛrsڌ~G~z3>N=CO5ZHS9{Oa!Z˻OU)o7쮾Y| Oؼ>XQV[.֢YPEo[U[7 I-y9lXHOؽںɛnu4;vq=%5`9ߤ͸= oggonֻ|9_](~e^ߧA~!֕Yhu,ZQMkOr`eq4@; 0HF,If$xטzl%=G)U܍wGwi?~7H`b6I@`  DZ T8ݏɝ0k6L#tec)!yI-՘%XmsˡZb][s|wdá)y;L۱AT嘓 k*`N3i$rYijwkZZzooMvlo)\nç%<,01hրH5LZJD*H†jYC>ץwXZ*kkwP)kMf-T*ca08BJ=g:FW](~ g)O;)PG(H +xbn9-J􆬿B^aFtgs$kCkQ/u@HKMyAY*4<}-[ZwNZ#+?[vE }9$RhD遑!EyqkT NnO: hPM)5EYfD# ,spww!rn_s\EU Bls8@2A ݶk)J)|XVR:-99 Tu=Tg @(:12Olw poY9.'dfn0y.Og@@Vb9gxl CbYCWz<@M)<ܼbOZ0DcV-M`+PMZj%R:k')py95Bi3@]~.c0$ .o].P4#-өP+rZuvoAku^cTa.ۺy[VZZ0eT70 dE>8ki|ՙ(ptK1I)%ƖY򫷯W1dnXeжrP-_m+ I5{K"HR ۥζE|ء)Ynqð'NxWZk LIZ_G}(Pm7b!eݦ伺"skkg}JL\7a e3||3_Ϸg5񋫫~&˫gum*u]nK)뺦al5LV MY Jmu7w^<;mJz7G<{qyv铣`<nnގiZKnw˺"at\$0q)R?gwŋor;naWTYv<, ɯίBԂmDN7ypK{|ۻ͸?K%n۷!ovĮ?{G'w 釤 J6o$S&W,dm!K*BIɒ_^rZͫZ˳ni3 E R2A}O,?jjHe@'("nn^yS@ Zݯo!ZVox HD$N"A-'/B r;MqknPT-GE ȶ]?k j53PR(ٿaH@D4c}:$*IT+x׷oonNt|n[-_76نoo//Zx3lgy3$9">秛Kf >M4ib^nj/G%'ۅ`zQ$=FFS=yylf)PmQ$ֺn7Phђj(.]! K -" Jr#aSi/S.t\[+|J׋uC mAR뷉k-7!}myy/2|\<ݛ2E?|6\fJ?d f@L7KKrƳ,Q<ߗ/|[o/^ݙٱ(/˘c~}k~mAZ ^ׯ(Y :xnK\ziRX }6yEP@KW|h|ng|w˵c[ʾ]mm9fZW6YK.eSkk-mccՖgo?*w??fq:$wU'|(BI&Iifws{gA800Pb*%(ª(7|WѾ~59|<yum&`fSszS+Hw))^R~[v K"<"zIТ4NX0cCks 5NrIcFi P4u$CV &X֘ݰL7n.gUqWЯo^F  N@H!"""R EC[Q18 .߈=JDKR!U $30y{nIQͭQgU NJTpR#ed@8 tm2B6' f\Ȱ[sOëv|:Ek= ;YDLȐEZ`0) #vbqQ"}VCݐzԢI0U*@S@ՈOɟѺ63O0^L0yweFKlRbCi/YZk`6c`I~QB+d ,Ze vVK1GjV@W ,ݷ l [VTf!f2/[kA|mhu ch:g+c*s+~nnv 6`Øvs7Y!X>QU!'$ 1S,CN#"<4oh~W9 &gj${hPSP`JUJJb2 ddtF!HښS6Ʒ7 f&y9=]dJ*ݍû& h,k)C Gs@y 4!j#(`` CB P@8u`Mʶ~<ԳU gw[#$+POA $#$“,t}ۛSՃ;ϗ--4^k])Kx"`nْCNE]dNCO@!-ڲ.u-\U„to}S #W5p%f giɌDU\Һ.]wc~fMmǗqmsq+6W^,%F@ 5rZW ŔhP$eLxԸ`(Z)˼Ot ͼ{N;IË_~GԆy(1^!dq8!I6am"){Ji&62߼ng?o~7>ydK]}2 ~}|q|wy]}.mk⣏#e9~ao^ۋi8޾L\~CË?֮oZU>~+o_ i7χ~lK]onnqsų~w/?p?߼.6Roni^@i~wy~vPuMڝmBJC A0 M1%;iv3&ݛo^؏WXFȁ֕~/Ǯ?|DZIF'ԏAAUcU`6?ׯnūO~7_Dz8iSwhCQQ<6?DH9pB@"@EA+ Z%|FxЏ،;? v9!BFB ɾz0P)UBv}If"ڔ^Uꅡ5& !"basPNCIiL[lv F̠M]",=S< r|8:}x! 1eA/LXzeZu<# s ɞLTAYBIdLY1Z'= SI!tPWk4ji tnh?7^V+%Mr&FSz'9Ɂb]M 6zO=@UJO]R[PR-zFRvhƈ5J E5n`)% }hD5 b;Sx\@gSMˮ _>-5oݏQ H/޽|s\Z-CI7i@ `f FkPl5d_$@@f)yYg4ɇ!o<9:Gu_=}>}mlT)eu.E/ 6w"?ryz`vH@H6==d?t+Y(@@I1.~n|xj~ gzm@ K(ЋwtCл: Lvf]س ]=G{? ,-3 %Hd]7e<#-?|w\\my>ږCQ4X2#O} +/;B}@_'6GzO{ݛ{M%Ԁ %ԲtT IDAT0K$i*%O| ]$BGw<[eZ'eHVt }6iz>-%xh[m$0VSmQwvǠ;x(]l @ke4W:Mvŋ;xٽq7N* PLbCL>t4n,[oR!%ӫ >1HC8BiJӴQmNYC\5 hF 9CѦj?!P0E 8S5_>AIƑyRi!`Rml H f($AgSm3B"ȘA2sHF0i8n68@11_O(5.ζ.a6rR)3eۍ0qw?\]]4zf4OfLqI6ۍ';.yp2Ԏ,ij9n[2f3L=xp) -jmWfǩ5w鸿lwZIqJmբ?"qFx@0EDnI46mֵ6heFgmy{dȖ.'?G+z.no}їD? PjߞP*}vn3UXnsK:_)'Hjx E<ΙN:<G cT㪨UEdP߸!{f>`ѺMI'ٙI߆a`s9@֥k9ZNKLSlqler@Iu5ZrG Q"<*ژ. kXDeXKß?k7g4^ rֹVl-,$iV)yJ"BBrEQ$u-AV-e C+R@~cDYK]kຮ;n' FCX' ¬Wp'6-|`삙NGND(z8,jޗnբV6[Yk94 H9Ec-4IJ QnƵLws@-)fJkI ZmRhKѴzO۳*/;_*zYG %dRfﰝLDiH)"!er&Z@2kf2R<59ma@j&ce a]".5 @MHQECb͐)Hy1ܸf4d. Sw'6}pҘ}д%!3@ ͘|ۥ i0#v#sEggdmǔhii0L&Tk?n|~HrciG4Ncru]4iZkffi 40>L0$O˲ZRN}8Zvo@CL$ZNIA0l)!_j>_3~S$=w@@ 3Խz*i P?`K'NA;7tS|q:T|ʻ+Nz`{z/I@w]?>]zH|{:=p;L0aCPJP 8,mir['෢XWw76Ɗݛ`h;MZ44PVua- ritjC  .AH!d81ߒBDV+gMfp2I Q[]04 F9sV +!,ݠcP܊s) Cͬ( b.Fh\fWͫPb1-7hp\a퍽EZW??vI<@DfUݏΐ](jz5D3}*3#?U]}"w\uJ@D n{E˃|pyDgF!]qS<ZBZcatHQo=>.c]e喧nr1z˸Xo}DԒɑrЀ=A1y#⒑g8aQF? ]iR#("9ٻELl* DEY6T ,yIs%` Y-َ̍hǴúfH \T˧~{#/cmfeq v_h1FÈV0RQ ;B S8KpTW貺ܼ군8e[ JuȦ'W,t yHI *mXM @ŭ&CUj0Fk #in| =;- ʃř]K.>nN>cn*/K(Kǡ8+ &:B4R$A}5XAO15!VnMRTlXe]K,0d)'$-2C ~8F!,`ڱ\Zݼh@qxxX{/<ܗRk&Ӊ4woޚiyޖù"NnRR|]w&Z3epﭵ4enf\~84Pz1j콗򩴦1 @~su%1K{/֪q]W@uRxxps,[_/e.; yT$/ܟUie9#7Cw#5S,=po3=CY\t}ٚ/陃xKy٥gys[) P1JvZոL":>Yw'rnal>NR)eҮY34E(iwtnd$s'3gfR^"iF/)ͦbDe1v0%A;&wbbV|v/Drt4IҬ<dz|?W>_}٥iK4Bug0[ i"S=ǻ%%>ҢSHAq2S"?uOd (4:oz,2xtzտ+OQiCr:oJ}^賅qЙcې.H-bnvb+a =ϱ}?I3LIp::ħ4tpD,==F<- .NF`a0A`;!d~d/5"a79]a1ѥE4oc{TlaN[b @r~O1tA(OTc /&[23Cȁ" Kv&ֻb mIKv\Nn $(HWF{A39?AS.SQEm{td#ܘtz<:ϷaT^̥w3;Hqc۷8 umm2޻}ǭo{j}&¢O|J|J_]_AdKIVgEF9 Y*E9`232n4IҦ!Ps̑;miTfN')sD{"sd)F22GbR `$ymA{Kf!K)fkC)eFH9Ěhȡ0w)P,0 e2|rs2Ay٤sa@Dj ij3|ُGPyD0'pvHz+r:N.^6If愌AIZFhdI#@!}rz0rStzri84Y/CkKbxl.,Ot8f$foP#@Br!bL bn½B9X⊾ D&m |QU] ! @ ľH6u)[j^2$`-e4su 33A7ּLkjuE8{l`#z۷.wxS-TǹR"X H) 'tXKG'R0U3(Hbc~(4z{XP3ܜB) IfC6pwP44җbzI?B?˔ǏL@[hL*@JJ@j)f̌@VRv RF(()'RJ(@Og@1 ]6 LR D9hM!FSY*1F uoG"`1a~H Vb8$ :Np(HXQJATH("a?!0B5LS[\O:MXDٌfLeZ0NtKipK30یRedhFc( Mӗtha33nFy)|J!s O6%|0l)49ђ BD14,,L]]_7^@"9NwkVsQ,Yٙciѵ.ϕ Tr=|Ӱk fiPa׷Q!3)iV:U]A)ztEZ@Bs9>=b)n;Gd[aI#Ro1ҫ"bnE8D ]jW(Kz({hqD I *!(I30כ~D_@Wt`l4dփԛW͈%W3S>2%m C2͐H 2sݮ'tyTB(5R*Բެڡnᴉt 2.[z0CihG$ut&N"gҪ+S PW^B&Y]"- E"4oF.O CW*ȭk1bNwr?_j""cȥ{f)#bAr`dv[k豍40gØ#"zFG1ʀ=c$=9"#RcއEFѣo=]#="riDK)IOhl=#uI}E0#|csIFmУcFb\F`!Ar$.x1F1|"_ _닠?e$~κȈ.ѼFf*Kpz $bt(`df&!P$)I4 Ytu)nRX D@(Bf.)B?lO?*9$8A3(^ZTnCB2"FeiMB09Sp.Ȍݺ} ^|r#LFJT! Eq9,LׯL7M>}VQfd&ֵ,z(!df-c<&l벍=ƃ]WQV/!H@5lҘIK23>ח. ;x4=ynEx=BcXZ0.2=KWx8nJlARyrH!Xu Ubqx};]{n`q=q Wqyu{C7Ű\d}AͅvH2jF^n)0g]p, vQ o%-:T,,v,',RK4t+ (Qʔ4yfu\FTѷ>@%#R},uDpч1ƶCC, )EF2n}b2zٖF)G}FVk c~7I7hm{EmIܶct@4BD}-+]gvfptEQI}9!QqA##K1'ܻ1v#qm_п2,Cm͟TRpx'Kmk_kԶxmf y@`kJBPD>^jm03c $K)s1Hcﳀ^k5711$Vk-ōT*"}{ȈhRH##{mK%GP܋R35+K$eOo1ZJ~\oUeĆJ2$Dml~9~,HǏ|~Ϳ~:$,ijԞՃcgA!~w$S4P Q0l=m_$)OM"D2Iro:,5Z6OqnE[>>b @fiݩ MY 4I fVݫjRhfC$fq;D|Ƙ] B"t>CfY׵VVy!'X|bE ɔ"eIOjf澘yۆ@@8.3h Jׇit@o޼9/}:HZ9j^<~x{w>9 "d &Xb0WoȈ7ҷ1k3 向>+Yz>o!MuflKt>ILbv^OMcGdt`s0/[aY%R,lӓ)S+uYZ彴kkHFpZme Qh,hYz0˲RJD5SIwz\a%dkNӔG)u%9o~IWK$j.K478nk lTJ).1PlmHCaP1Jwˌ=RKFcI20+Gٴ)F-F+<$ 8"E:PI1Ic JY@1cp 0(8j5pT)<^oԥ9$& #`n^Q嫛ڲjO|e˄žA"~ycbzI/]=M~ IDAT4:  w>on+%+%5BJ5(Pk#G\ђ^.BQSևPHҗ%ԥRX=?~zw{JHX {iWy<>qz~{sc4 R()12MRfR56uhULЌ$aɴȩk!wOv>ܼ>..I&I+N'}[w @0sȈxx8wi^D]aPK P^޼vFSO{~2<*fp9Tf$Ji~ۣGC#Gzi#:~V@$q8x^?lJ!$ a Z9 ff] xxwyܬ7uvڋ1s3I2Z䳰^"KAb:&: dhvk;:f7(c@H3aJ#fn1uV4R2rp >?4dƌ.SyuU*bFmJmtS1{D"UJɎPC JTqn\=Gsy-^FW*# UjT]9HiNf 92J^+Sct ^%BEu?~m:YӶ=4=UYi4~Wy{HJe&L1#wHu@N țma>"7(Pa}tqyS0ŋk]wCYkw7|<=XJR0ow^~gwywp8 Yw7CxIqsswO3Q،H#Á; AF¨D{Y)Ui۶}AHB!Jȧy"2I=4o]DJ d2BrBftts\nZ惸Ĺ֥]@ Ic?޼11+c)3#3"*%9 mNWo"&]xMOJYá-qn4JqRq]$[ax}?"mY8F6dawJܶm_.RI"O7FmHeu32qDҹ5/fT*Vv7`<#_Pqwp_[; iGsʺ!x53j9CK13;FћU hJ-s7ˡ-&崵%vhqIZr ).K)sS7Z)ٺ,ʼG)v)aU7ۉK[4ƠS8k^`vh _]_"lNK]z~KXh(^F͉Mf%ic $C@Do@p?N ljfZHHpH 9nnܕ:+7{{589 R @dJ̨ kN|z`+cip(û__o}ccQ9D'㥈`&RJL)Tj _gmߙ(rQK]2ϯ(e}ӣ2 3cK#e +nnnzc,y{p<^Pn!A?ooz{s{.%Ly53bz2r+gI,콁ĕ@ڲ4?)w9rVw֊9m$=rB'Brօ D*MdY C-ň,V̋zu;2'Zr @[qrSJT詄4[ME773b{R޾ǖ=]?C}7o7Wt͂=dfx.0ux\OoA%b]W)˲le[yw{  "3@}3!\#Տ77-iv{[ZӾqode-Ziw~N'xkm]Mf>?|.L]ZJW~ŭR3zhǛCp8J\3~㥵F5#nn<bt>c߿kK@N'(anگ.8=<>ƌ}>~<o%@Og]OUb|ugt9/z O_»w9sxp>}ka}Jo?=ܴ,//P[sLMOԙ~r}ݛ~)&bD2.e]4$rY w\[T.$rq,Ց) NJY>9c<3 $'xe4^su.s^Eh+\f@5Gw_]-.bε6F Hq}xF*nRꧏǯ~_eq{6RȌ!Y{$Scx\NOF͛3B^ 1`9Nv>^]W4zmw߾Nk^ںHn?KBz06;$ٖ:x.<K}uz5~us<6jI/NZȌ0#W. I7UL+eáF /)ڇ=Tڐsͩfx,}tEBKG޽?'q+bˡ \ypXr /"tj/اtkN }ToC}؈i-$gA<yf$گ#N$E?EM-zJn B1}y"Rn޾Hܣ4_K[׵{kP@<om1GۖVҸV/ƌmr<ސ$Zݽ-ЊZkmA W%-].uPr\K}R+[eXYՎ77YrzL8Q+j-8.Y }=9>౺Yv;[I{MVܗeY%JVw_u!ap<?6*,$wt-=ӿDl4%%=O,g70""GnrB$F%3 Kì-b.&8 kʈ0# ziG=#Jju#~uYk۶I΢Z[k$/K\׊#cV1ꀻO+{ZRJkgzf;AJZ*vf%.ߴwxkkBCBKuph/󺬵xޟO?__ž>~*4{XKtu=ѳlOU ΃g??, 1(%)2M2Ahyi.B\HXE);?cy)8Cr`>\ȗ|l_7r3'afeDnh뷇՛c|OSu^S?Dעi:$'I(mRmTtYQ,̎$A 先,/]$e tfNY!N3:zi/ +p"dS|!UJn[?\~_}fUn U#r]~޿c5d+銗k}^TG,̟פ6hd8p{w0B6O-#j OBWPFJ݆.(-R; $>2:ƾ%`Έ˨$bڷThm]BA;o/{J^4sS!l0'$ACab2@R9F綱83v)%ySFf\ƨT {ww/Np!'#z>Ln=wm^xkt3߷0zF$Fw (KO𯅮/ٶTgz''z,ZJ$4)`)4zA;YizF"e9 HyKO.CZ}\b?ś М0+,21iVX:u]mkYqe]m룻yuYf, 2Xjjf{hzm"Qk??=?{[[ըP+'K PsaúJ۲g?1Fww_Otv*L|_g<'q-`fO'_f&xWov"x/SW?ݧ|W7廏.~_5kWi %31$NI)l T,ih`/|Y H ;@K2oUn̝Af@ 9HR$E]?EAd"(.@JA`yn:NUG9sn:\ [?U]tua( t\g䈀:@=VVK,5 !xNΩTDHJ!Zx*G$7$ ))gRR-فa2",IJ+*=wtέ !Rԃ ZT` b]R+ "Zp򑗙Ծ~sfhE(Q p8x6?sr6&D#\P(WP( A*,`Y H8 %Bd +l" ѨLug̶-|>#J &RF#xC2K,s. iY&a|%,rE:i۶ @Pz,=7J&qD" 9;6: hs|h2䆡^~x<m7 }B1F¡ c,N[8~i:4G4ϗO;[QLbRyui9 ܁S9~%#d~js|~4fh iHāK[[9 W"j V+[_G+F]TP6e2fP7vE1OJ#fL!Q4c4%&>44XD8  dhf&'Œ'qFd}illBA 2DƑ3WD5դ>RJmP]+I0D ) -lj8%:.*tEJ)"$u78RՂ%'` #"A?6 UxBB²wD@^bB$(EF& ,/\" =XTʭY>JD@$ RmȮs8A hpT5Y 1E !OU'Sf s% L/wR5"QOa$9!z/'шޫ)u]θ:˲J8rkS]K]<$ N% bE;/ ̐|.=ګ`8r/䅔#'"Q1&P݈DGd•R I)q R`LU0!3x0d2hC9X,!""h$"8cpDJ!+ =G@RW*^TRiRtC@ "rQ{bT& RHby'^ۦ(Ϋ"d' 2oR=[Dxc'1@^T"X:kN&3M#.q ` 'th+5{SF TO8mX$Rz@@ &v {%.@$Vl4)m[+VvW҈. BPʅTV#"zQl/72J${9o[)%/KwKٮMb@1I\bBr+.`RXk, aEtpL&k #I{k͢,,W0} GH)>?c~>J۶1؎:p4Jr1FB Cq]D B8RX,8p۶#HQ!dr$u]O"vmu}C,W$sis%J`C`Ẹ#dsӱ>aV߆@%ql2Q iNGH)md(\$C,ˎD((p$E36X hHJG>cѱLG"Ftc5Isݝ||(BDSje!Je o> Ha {2$Qulzw;mpuR @"I_̙Ra0!1PϸH_"@UHD$B )wϧeBݰe7QCT!ɻ 1@ sjeʺT$@ >HoP.2㡧8B "5IU' BRMcD8cs:mTrQ!0Y+%=gzRGHe1dqUD="DeTۋ~Z OJxR*%C@jr,P* 9aC" jVLTRQ?K+D_ڍ*$IRW@LƱ\ UԮv;uyt>PRH`93TRC!Ыj=DV@>\^P{AdyTI *"(2\d3F&o5lmAX#Cv-BٴId$HUtQbF=10\)H Kq$yr{̐0KwzzD`yy/ XI"2dR )T9RJLHD$a߆>$ %d.!! ߐ#$o#C,*0RG$ÔQz-`L=1$UHuR1])K-HB g5>"G4$8eӜN{ͱ1Ɇl IDATO0A˔S-1!<)dQH?CD bWccR@J2 3f9)H"CA@p.@HuQkjk8cd+"j$3#)o] fDf?rP@`qO'+: èIXEstt*10 uD.jd"dLU2IYFػ !HA=.#M `{ UtRr#blQ Tnۮ85B9S(Z)D@‘P&pRqʟH) nPICf=x ư\:`0H$T*],*u*~: ATݨ_R%f=7]6J)>xj7_p~PFu݋.U5CPcSK&Tu]~_MMM"0Mstt4M\( yvɅC!׻2R3uhJDH H= & !E*6 gD"e[qvՏH08$cA$"*oRJ) /TVikjj9TP(`B@)$ @s@d8rPiiDd(KmʕBмFO%өp]$DO$/Z~ÛӚ{k8R0Xwu b)R2 &Hsw;5՗U(IhT @Kkiv҉'׿~iժKpx2)d]d:"p8:NOVknm m 03M )H*I!@0<N9d?}HuݺX,644ҥmq-Zn}[[k4stq\۱ f3BY`;w۶m tvu}<'S}]s}C]Sk˚իm F q؎z\Rojj,qֶt:_52 ñmulYŎnqT: 0LHa? uq5%<묮Եr%K _,M&ps\ggg2 0ZZ[z{z@GG}0MR>  rJ x\שi}>4ׯ_Ҫ?>6V[[[[[/븶岮p+$s9'tR}]o߶mpCg}G'?熺֖_zv@0qnJ]cp9MͶm;֖L&Yr0X 6mLޠRZ.54%\ՕL'jB~x."jii9N<޾_/⅋$#L#N!dD\1|֖Ѿ^PKr b4VY1UPP]O< Gۄ+nŶm۶tϥi\>~X,36:Z___[[ٌ8mqd2]p(|·>xI'}a?`9vWWGJ&ﺻe5A>ñmDRr΄&۲]mmmMRhtÆ ]UiVG{gww7ܼys*]]LaV00 `Bt:ّѷA]vkOեV2si*Hc|I£?&QWk^#5dFZZvƦ#oooif 9bxq5kD__y\.W[W766 C>6t-[$t&+ZZ[[[[3l<e0mZ~C___]RSSӱٽpa 06n܈MMMBd29669s=CP>@ f;;;{{{Zy6d2^]LanEEqaD⨣߻fx"Դf͚D"1<<9hp[kk+ Ը=|[kaMaDM¶kPbY|W_Yjk? ~cmmK\eT*N%p[[ZZZ[әl<O$iR)ۆ֯__qxܱ6wvv,Z؝Ǣp{G@mmm[[K$6 ~!bFSs3tvuf1{횵==TE'W,=ue5|f||ܯmM]T*I --fxW׾\SmhҼzDfxhkAWm6;?08nl%mf miqu׮Y+'_:^yeM. Hw?::tҭ[R)6 D[{Kssl6+I  mXb|6Էwtt/^OԄ"ښX$+YSOCve7ˑP<WeD{{{]9dsK=QPese]r… /YdYV]}-CdY)`@]t믽2dܜN{ͷ Id;DE4BJ2#auuR ΙX,؎\2RJ!E(jooB!&шYv$!xDB)DDTq>>>ҙL.Og2eIIRR$8b1npƘiYj2N(G#x"&)(ύrDMp$RW_  54@c Y&900 Yp!_od2T:eY|qc6n"eY~ ñ|.0o^:J35>_ht21 ð, 2˩{h48N<Br`0hv4 sDT<9!rWJ łiZRqd0z!$g\"u뺹\4M)" u }PM".0Gd@ \!_m;^ALR1ߟI|:I'-RRFX\Ax,9EH8RHvH|6WA@,AD BMJX f3Xql&Fg|X4!l6ɤ@[k[6}16n -guB.0/NәDI]EAخR]Oq8Bb1UʛES jkoBG~D<pd3~! `{8ϥR)ؘ?OgiPY:H$Iԕs\MM ""H8RWW 1 557}~.dXuL&˸.n\>gp8/gT: ڶ+mܸ.^R㸹\a޼T*rfQ }wWDJHIHP4-$$Q,GD7j0΅Xα.)t`{GOK$h49WFDP]\0>I$RkB*b &stj<9nZ+\O]8Xs-{H&Q !rc\&@ G}W00YʘeՕ1/sE |}}]6MSP-"B\nӦMP)[][Z5өW]RvkVzp@h㦍wyG@#wGfe2̃< xn9w]P(maBAv-[=c`YV6m&I&yg9/b*I']_^.[ld2e\4@ 8CCC1u,aϗL&ա!D"eR)y}}iB[ |D"Tc\.ͮYf㦍|>G"`08888>>7mamۣ`qT*ΫX,V,~P(lٲett4P(422bJhZBulܸ;(ܼyfc3'︺ #ϯ|?~{c ~?z졇~ѽ#^ o{C“>/q6͑%Z]Ż>Vy ^0+匡=Ͽ]W_{~}왕euWEBUBBkVy `Reƣ*6yi՚VԡUPЬ!/9 +{o?77!L{I"PMWU U ͊-uf%^3sN:ۧ5 &4x'sgoM.;, `tsQCW~~^rǍU LL&ocx?]9y_wб{cw+YGɟm&( 혶x,T;H1_}XOğ9g}?a%;;GirhNꚠ.͋>Kկ}_%=<, doĵ9{gn|W}N%A_pԁ{zεϯ<JJhhu"]C]%]rR~lظՂ%Z]3Eu]:賾t붽>oRq1g$U^^&g U˫fv3&?v^}Ʊu `Uv-ӸkQ+?GɏSs_Źu%^{੿9w_n@ثos>6!Cˋwy"/"@?ފeM;\ذ{KN8_fb{?={^n?x?U]u̖]OGB+w_{>߾xm[V U`,W_Zq̞7 C GtAk]Vv8L5>h҃[׽Z_Jꚠ.cr==ی!/?sOVy9廥ǂɣ=6ʛ'~cf iuU$T%4vu)GBK7|W]wͮūHJJ^$6תնGeZz֭Ù/IfiJh漪HJ|S5յkt DS+0sQG⧏Mu]^T(`arl^SS1F!p<gxYIN>zk^'k,՗-~K;2/z߇Ve:_lYg@g|YW ̈́_<^O\xOo:&k,/`}Oo]s#ƒ =]_G%;VWx&.SiUBD{{{]ȼƘ7Dyy k leWPPռ*[NAow8% `fM`B/TMUB0c^U$T%4v+"o` mm~9?Hk[|}“xʥ֎Nke^r};7.9>nx4?a୧~C)p[[=m/b-m _lONfz^Z5hNJ`-*zSjZS IDATp0--Zh?|tEz1~zS">}c>zEG3]qxk}θu8iwvhua0G|=q߆wO{7^ѥFe{{Fw_zGf@ f2]F]U.LJ?{\sJ4U$T%4Z]0s >|K/4=|ſkęv3^&0Ixzjdb^U$T%4vfY\r~A?mP24M`{W{ׯr6/{ޛ:~齶˻7M8Xa#oKLXI 5qg;M]hF0 e 2-X-4bc8޸uP.~e߻&*l5Ե}Why1r/Czq,Z``}}ʛDh鏾o_GշWrjP+<}|ןu?BnϞ5: dϦG:kP]8?s{-UPhuUw}̓[vyWn7i  UdbڼHJhzvuu6IQc} nxJYSKcڄq rӏNoǯxbl/;, O0t&gzߵEw갃{~>=DZojmrp`Hx\Gt7}} [7|q{Z .Ts`Ե4ƚ4?$fBn{ۧy~;KO<,S'$I"4F+ J(SYѴlYfQa=?}OxoAaCy?O}7m6'ETPLhuUcyځ+tqG&~ho HJh&y5bpHlo o{*"*ؕյ;xMl8;WaOte|ٗe{Vx5`K|z4= -*/ 64D(̔QDc>k.{]>s_B݊,U/-y^{ne7]8>8/^9Pe_980o cz^Q-T ^L?otguUOf&oegstu؁s^zok~u<:yF.G}qK ?{XG{nG=@b}'_1u+x%]]{8ᜳ;o.;*.]B]U{<ּ>N*TɋyցK+׍䍚}e3;J/TEUB3UEBUBӳk7-|Qr/ 4o]<})=ĵ_־hA<[(oGwKeVyue]?k=G؋۶J`.\?ǔ7?k׺P|_Ko~"$w|_OU_|[<"ڟ.zjhZKO{7f{.#??3&}፧}3$KPU^fR"&j>sV1_&s.zĩ%oJrr1-mFL߫/MN}uk'&琅)oʡ^9XHn`K.0v/qc݄7c Û^H;3SX׳-mGW|S'^tUϒYdɞUC0I o'K!7ˬ5k&K!7 y3[̨̽I, &ޙڥxf׀}l3~'W&'{\6?~yL#d&L`ڮO^qRcM[V[zܦ٭Z]Ckyk4FhvgS|Fh4FFh4f^Sh4F3Whh4F+h4Fkj4Fh 55Fh4sFh4B{MFh4\Fh4f^Sh4F3Whh4F+h4Fkj4Fh 55Fh4sFh4B{MFh4\Fh4f0&'ဏJRJDt]W !*CjM!D:*1fp' P)_,-hr=$"R!UD8j2|>)%W- ,Gh4N{JGtR*ELR ~ÈE>ϳ5*bk1e[&Q&-Eo5"DTM1+uTZRh4F75cB9T^({*?@^\Մ bD\P)aPaO+h4F75`L"R*F.RVy@F{ͷI^S5" ɕ_n;X5)sո*E;NFh^H*$&QWSEsX*&IHDHaVNteHw"YH=7r/3=CBzPy-蘒䖼Z)<sge#MoFrj%(﫢"Ԛ}.U'3e+4Fd&6759QPHخ]kjơ2(`ڶ#i gC@YR (Vv*d<ȳT@@UU=X>TckjO=JRZRY;hbz* ө+ j4Fy^P"4B=ߵg<"-mv̱d5RS;2<8@D;NDTrʗ|ڪs^ekHHTI"@ M 0z"vP,c*Qrj +9({6]E)7}Cɰz.ovm*db3te@h4N{M`Hu󌆆ֽ7G1R#ISkbm2%}ȸSԜQ δn%10ȕd(Q"JFd@0@ Xl[$$dHr )"C @9NљM+|E( z ?@ szJ5^WWDcuuD^{md|, ϛ7\5  RiQP='?*3]*CFh/^E˚QZ9Yڻ%b1'|RJHV}[ B [Z @6GtCqD"@F0M `LB@@=OٻqёAiI$dHHmk6}M}mZ(BryPK]8;Rِ$r U-8/x@ V2$$Bp.>G hH$ɴmؼPN8 L:744`@Tũ+PU*]2G>QWLSh4;2>l= ٴ Q0hf\a $) !2if;zC7!@0=wuu "dSVS{̯ rIBp$ <`ꗆSL0.9ޒf9Db@ % F{^K@o=x;eLIdRin ۰!ۀ?`o3䇆mZr*(UE2y3żNdɺنsw'Y{ ̏pg LȾ ce^*@ Oݗ$g2020BT<<;?[J2"3ϓ$!VU)MQ83x~vK& $w pM{D@nRc`````` "b!\?74;;7Ӄÿ?ͪ~[oq臛޺u_ƃ2022b`z__;oc:@Ay_qץҞk_wO>#ؙ@qw: N7Cg8Mۮ1?wr8m$ũGyQV[u5:d~KYGMg LǛu$^?Xm?moܯ:}rmA7^)k``&"{{-R  {Ѽt30GBU|ۯq!,\MJ+۹qhG*ux~r鼳B)Ooo]H!y#NF7oO|>8%%!$x>o^~; ?nL-Xk׎ͮ3[?gO,̒@b`r[o]^Rmo~ w޹nh;:(wz7e{>c~|j;oyhyӟQƵ$ftR~Nܲ[g^@9000000])>'!YlapZwF Uqtmv^^Ȫi6?]4uE~~k_{YW?b7ιHDς,}+zXLm"{_FXc*[7k (#(1$+Gh‡O,an]꓏޽͞7?oWy{?({{;Nh'ڝ$Y??sx<*?󽛣Tdw(&43'O 7gx#}6?3_5ӽ0@/((B/`+`!D} )m${łѰ@f%A?郸#1#3=[ZA$!޸?_{+]TO1itm6\ڴI|k q m5Vş$oi@de<?#|돾ײ.޹?=3Y[ol2~7*?>;o=Io~ ?.OǿWL}aSzn|xv*;heyo~W?׫շ;VNN(ЏnNx6qi/Un/6>K_Ks!amKgjSDħB1Vπ̙#GFOk7-|D߼}+K0]=w'qtk׵T,WEE׎'o1d컢ۿɣNjdk{72X\ޣ׏^;4k/kG|׳#]~|:7wnƙYƭ螜<<6'a]6}g™7n%oz0O-/,oCpC3s B>y٭\v|G/n{4f}^ׅȼuGi_b[΁;e~gm%  ƣIzGX$Wg+eI?_$d]ꎄt_u˒t]ulW;9Ϋ$ưI>tD7nNF)08n99{.n` IDAT1S/ml $` |Yj]lDEˬC`%@QE{qmBLJfz8?:?8<8-+I\] f<<;Zțރ\wbm E#gqiOhژ@YϟXvkkYJ5?wz|tuO(O:^ؔ|8hHDĈB |ޚ"}6w* WrK˾'<оAD/2_ߒ2V%B(Iկ뷎SP\wrRVw(0zZgDU铓C "RilYnbյFrXݦY=T1Q֞-ʳb: BZ#ʢt;[n֒$V Z~rYzLRXu֙ڵ S/6ϊm&bFZ~N@Mn#@DG}Ʒ}^2JDn#gu\{VJN``T$& 'OQybRJȌl:""# m H" $ µkg PCqB[dp%F%#dAu8B|:YlZ+cP8"`)Z^yX6Ͳ҉8 븞w;9[[^yw磁W+s|w3aÏ7N$|$fg=ZV[ӦRKڶiB*CӅu%%P1[4 )I u뛖lD d$$"tt< RfTQc(+@Qq@R]cH'Rmәqh ) QwO~2s/DcckK:000000*p%쭂w }iǶgych 6`2>OԔ AJ4GRIS)`ژ .YZ0ˣkͦnL,K)y\]Y&(VHi2Mkʦ3NB3\3zʺ ƣDk!yZVͦmq4Pi$n\ϋDa:ε$|7c(Hz4Ug7X |Z|\ʾ̹/T>$k |IkOը>m$ADH'A9W* 5ʂ$lJ)ų_X dHO_ HFBiTm;礔SM,!t&EY0s"li᝷!ٸ3kk(V(UUBI4]Gqڦdr*c͉Tpܮ18.7j4,jSxՌi1]׭ 46Id6͜'k7.JKi\G8cG{/2?/1gs7L@wz]q%| w=wm )SH(%!B d$b |lb@,TU4q>%:Dljqi\8)DOY,IWk:;:n*XQ؛UquZ!{pWl6c0tOUlpM+γBuMm4Q6]\T6Yո6 :sXT[&j4N[Uh#JE:m{ɞw,6ϴρ/1/5{dw^I4SpU[7G%0S< K@ FUTQi Z44YunSꜱuec)x_N )7F,(\o(٬.;ͦڰ\TrtaAH ʶQk47Ɲ.7P㍱]+ %BT:RsNV[Eʆ,Yu۶I(RJ -Ay޷-YC{ɦ5%³Ysu%vj&P/7Wu/}G.Ì(Dۙ_>:w. ƀ KK$G&Tǵ0 5uMLHEʹ:/N'`\֤Nr)"齿X\ő x\Y;dbFr~xl&1iVeyhư*$ޯ^cqH>t($%Zc+iAh,%Φt/udU]Xж^^1w#[|q\^CO}"Mw-(?P,I2UJ>>;7ņUE.;@M֛k j<eֶʳUWTDMg];֑+MzIpiΑ筳AA+umUZ`ukCSvmJUjʦ0Qdd<]TZ׸qptq^18ѨBxo2vm_2Ғ @FV]]PnH]xk"boJd}dBv/K~ D 7UR΃P[hWD@Yʚmy1)md&R0$M,4B0mӦZMF\YթgchZ=Yn"{7IY6A&t84MejZ(1rje:RYնDtY:C{B@ ܶ3,qtqA=S3w#Kc=9000000*p%nptoo#/2ҿzB:a,tY0@mOÃxgw. -YҺ5e,DN;obq,xlV%dyn!CH1 i}dRٚ..Q>7]'%dZ`,؋(bgr$[ɲSxӃIy,k_mB<g͗R|KeDڠ;v+&^;zvK~auٝjvY2J#L#Y[du/Kz5Sf>(6$/ *y5xU& Mu,'IڴU\p.[yRn:/٧q")p\RX_6uM٪,r֣`I,eJ)<cGuՁeK  b~Ok؎1%A^䚈B^2.ƶB/ ^ N y$b(J{xQfowVEaR -"[U@Dx}G:N ѣM4>1c(SdZCR iyONXkc4x̲us=^ۋe#Kb&<8YM/NVuZ,IDRj< ČbsE[6` Wr>q϶pcWSj_zBU35_.,>OF ^8OwnmɪMVZE*- A^ִI(Gq\ a%qAQ'B.+Cc$Պ@g aa[CfO%(:/Z ):FAe* ;+Nawl/\bDʰ7e#}^DәE%?UKF__ \5{kb6i~QDyբ(jf<Av(SJou>PeXK8V D2. `"$(Dav`<r֟A0dܺ)'Np jPZ֝fplZ+&YgUY 475hʎEJ@H.dOóuKW|_dds})`!9000000p%ׄegxا("2͢Yv8U盳EigP'\yzݮ7] bcZW\i$IhͲ DwR`K\[Ųj[ "Â4b˒ZeF`Yr! yJ S ey-xG 1]*8YyX@\׶Ypv^8:@$i:-Mh/>}݈R_&ꚻl%Memc%W~<M@j5Ux:e DQضѦ5 ץ[7eX.pA02- Va?2l枞VyWtas xyJV 8IZR%\$q7D"жɦ^<,B)|)%9p2oZ8_ˊ RκUih4iq\14m@gےBnE/26 !S6_ 4[\_hWQ,cMD*-4P4lYK|%{4d6`>4TkdۺڸԆ]bH ! 0e/Sv6+ȕ\GƮP\|my3~閹j6EYRSf<ȴ>UtECI1jZTZRֻ RyL]/`wVl2RxHL"qAg%Qݹ`eBIHthAcGcaZ(,r+H .P|!EPE.t,R,ko,s4>VNuOw_/W8ͣ1]//Wr^&Zb|HHܶ pt@ @ãM PR0Zkbz|ZlJsGUQ 1uXNjj9Tj$9̺ի6\Jz(M&Z}uyR<UB1'B&QzF"CL\:i6 wK򞚦9[N3Ǧ,LuJ)TB"2@Fa. Sͦ E*;_Ӻ/]QvMOV?UJmͰƥ |kŽ)=:k|j}# f>8 $E]?DR8,brm  xH$h2 Isc&mS]*az~G˒͙ N=ejw~SEH {'@#pP4etF"I,jg}QFd P@c,1 @enOkbK~H?~e=000000*p%߇Hx r_99V2}dA 3%*o,2=۴UTޓ%BOt8(6 ?Ԓ(-XD(i"{^$¸"8`"]J!ZKcw'JF\Rg,mb@bݱXֱƹ.5#>}c͗F}~\^:s%Vmf7zݼ4BW b$3E߽9],n 'g@x¶9묧X*jt8Xsȫ2ԝw]NL:<1&Q)mD2RʦCE׹"b!&t&ZB(0`t{͝bW.TmQ9H+ŕ\W;^2]RG cQPۘg!@7l:y$MY'Y%qKMg5X] Akq͹]šiA- 4ŸB_MGX{.8Mh מeYf$ u] J$tYlIgY;u(T5X{^G/5Q7wsRd``````UJKoRli=5_~#6&rLkq֝1^=t3o 8JfWM8i&O=8vBl)iszz_ IDATݝ2X|RiMrb61Цœ)P1o@g90Wzխ̤4묫[ d47UkM4M4I,fP-5P"c_??35wJ}<{^ \5:<+hmb_ZR`mN[5 L $lLo /iӣ>}Z{['knnnA4^5[mlt vꚻ2zeA`BY,{~Y7XഐuYD&gc긪HPqQ$ڪ[W@"m%H3F:G+Ųf_ WNr雳h_x%)!xb Z:1KwP+7h-/6Mj[q} Bכκ$<m[E!Z@25mt۹ޢ6w_uƋ^//`0`DD4ӳiz09Jbm'֝Ϛ7nSZq|},Ƚ4RHPjPq'Cyk7D}2ZpgprwvF{빪iG4pu߿],}g6\5^zV"MYhiT3l<[>-Dv 4zxk^{:eؖއRAF`Bh2<ɪ$ubSEiUeͲ,I&ۇغO:# )}õG%ҥ<ޛo}?+=7AZ\,jFofG{NEwRuH f:촀d$),i)6r.M i IēcMUSXO-%23|>O7ug&x5kJ7ϭlW+/W45@d@@@r^}hP*j:VG$=$3e%,"ZmZ߹!z6Z+6PM:B(ȬB,O = qC:ч%62Z: F6 ̊ċX>z aMc%iս?ǂM>ٌ-;dc]B`Ɠ'?yMvI(';ӃHEШ,i X P.雃屛^~J=}/9p%Uc{K;nixꗅY "gss#[vu*t.B;O]u&q*Xx\u~4IMmn%7&vYəwOJ )Az@, h0BrQC uG<F!iEe1FditINHKf]'K# fc|oJ"&gĭ_~yH&q̾g(j R\5?lmrCg@@Bf;)<}ObSZ/hĦ{'ɒNwW22A@@!m(3 iYi+j? m Ar6]UGFGg.TQ_W8#^X|y!|/.r,B<]ݫ5e3j:8&i0aIIIkIJ bत u cY#$HUڑGZ[7wŪpE ]:xw` AlZ\_g嶟}T|fqVQQ0jțI"˓Mj698wз v Rv77s}7$ a?Cm?ɴ$?ciMm*ix}EVe%Pֻ!L6[%Î`e  (Y.:I}!m.I4Vf3oLt6z~r0v?QW=h[\^.gI=I2L)t쭷G<98Ga=;GFFFFFk/ty %u<H%j\o$Z;DeL5/M]\Hݡ2 myxzK[&Bd#u>]H*S“lVL*.M->}8b>qUbCLP>P*At "  !4lL daÎYBۡ4$!PM )$皢(w2y4M! Ef?<~ y+#=}C#######_5OIg __.U5‡2 &%(Pߧ/F4 aP""V%aB _yQغ B  !%դ 0JHEe?\S>3/|q%$EMdrdIm.&*T|QrBd:ߋ(Xo܌ 9G$*?{av DPB/@н8f0}u/sˑ/=oYNJqZ~~ZzDkԕ?"߬I8}MK[VɈ%"tT o}CUlҔְGFa|<T!R2#@ 1BHTz<3hgJ '$DpR I BaxR21M^nͶ߷õ.'[n٨IaO(s_E =ͤgaoIOyG;222222U\o$_)H~I AA8z :Kj^PcXf1+KG,-bVY4EcLrVI>h'(w{N&b|5y#|T%\T՜=#:?>ȎB@|K@dN0doJҼ{S=}RNc-DòBȤ.cY<K(wH!8? ۢhm-X@ϓPBY>JVoQ-MX"'ղA ~ӇYUzJs)Pe> ]w &Xn2od{X:d 1Da*XI!҇k̞2O<֧@-OF?<9222222\&Oy/&\TU(C i w&%™\=F @T )00kRJ06M ʘB|` Z20b,5f8Y:P{=Yj)dJU8A̶rE,y,lzQ֙&,İea:I׋ЁȨp7r7ϋO^1UJ(+H4@3)Gˑ/7od rjpz%#T)y?loBI HB; 鮛?<AQ6EiYVO.M>e6>.cJT|MǴ> v@T) ?ԖM++ ˖Dǂ&@DJH)1 7#(&BdH!*ȖΤC!iUXL]fQ > U96i=(?x:!QE3CD )8D\$9~T r4uۜ7r~ddddddf9K~|u7R>dZ4S_?_$u^Q$ѪpI"TߙזLn_v5]=A4<=[QIv]xǿf9g; pr<2'F3e<~Gf yB(-J֓wSOwpNxc:qU$9v EU 7MUX[>z5@yU58"Q$6ʪڟO+}0*J P؀@Q0)bBZ:\Ofˠf{8lg6~mHߘ޸R]EB"$lPDҼ}A;0QRG+__ᎧI@s2g5yiG参|qSR $*Дp 5` '"w17aH*DN)VY'VH”OrQ}7K%I-mBYZ$S 1SӮ&J"IIUݳ`}H1p:\f:m-CY3T\5fLF+`X2&ފ1%(ЇJ f8u_oGFFFFFF45פ9R98L@_h & !BR+\|݋(ؕ6XZ+i o=[um[o岞Nd߅h'.ScbwmՔ+7~Ҳ‹Qy('7Cl):丽&!%LjkzO*x{q>$fSn%p9ybyQ䋂JT(XOuGmR#TF>06DžMP9v&k8^fff&i7`b.y@+˩NC]/u܅D/o!fk@&Mqs3_^L_nl֬[ SDyX[%p,p~Qп|fnd>)#######_j5*?&lGThMeK;DixzכlyClq*[4T%┽0[;Y}J`ʻ˫?r".1!1yM!˩[NcXI7f~k Ljb(k*ߘ|9x|ldddddd+fV U}f)ȇN:%![o!! סMX T﷝Xί0lʒ߻U|lMK^ܲ16B\W79 s%/LM"*RHUT\ijאbsH uWSޖbgbvmJگ;]Edc@oDSUStgiQч|y#ę)f8II.sU>/!(ABP4ݶiÏWNFfє/mjdy.TpIUXc]CX 2Pb@ /=gH!bbk~D !z!h.fLb^;#UAY=C@P!,v컠kJx5R.&Ln$yȗ7rMzXW1UWHofY6םOUvM{nEi9;kaXS$C. U z,~*2F cc"@L6);}r9UE b쮽W!wela@DF":}"N!Ǔ)<|: oBDꈘ*=#Χ崶(~?6}B4-mݡ| M}8/fB̷yxʜ +=͡8q#######_5;2#2~!1TZyr32Be]SÙ84_Oﻺ,&}ۮ8Ĵ9H48a;$QY6oste摍1" 3SU:Cp,5Ko#0iŬ*lL5Es& aj STETorFUO/мGR]wY|W~w? ,{ +8%>ԇTFiCڶ:iJ% ͏bEÐVJ16JOuS>gN]&ܤl,a RRQ(UU.nm;L$O]HX#? vt٪wۮߋZQ5:Gm>-#######_J5md:S9yszcPPDDl: ,ыuWz](76IR6^, J!6 }И0βR["ЧW+yVp  a]ِO+Ym.EUv틻e5mwFRoE]Ub^_]Ό1!!23BN38o8\$[m̩ou~k] IDAT*Qdv]TrURR2:g g4QJyHH 4% J%Cd +(Kn/TBS4@`) $&bpWf%!2"%$d9Pk93(A1IIb `Xy 9E=14j-2#######_n5Oxn"o)Hy#R aH۶. K]7\^i1kLfڦ&}?m,{xּ|YADG$}bP9Dc!* K1z&RR٤tEwh!r1.M՘qCNSb=q-oAN6_LnsnNGFFFFFFk>q>-\Mr-B :.Qx:4nQEi,4ֶ·R$S Cݭ2 !$B{6I POE{܃ * -!)ӅK&O)!&c6Lm~׵,ɕ1)i7rbUU4 1Ec,;=imv{!>}v\JS&<ϜQ7A>{؁XDav:ˢTvʔU h1&f. W:S0n$d  t1IJ:f!9džcJ)F"2֊JZ*JDSQXqa@T$jL.䣔UQ91tar1~SlfuYu>%.!~8 Q*P%S^7W֡|5y#|O*^.b"Qwm,,i9o.糔BDR ~->CG3gƨ######_59oY)Qƻuѳ}Ra*KZ\4E:o}uaK1TZ0Tec= f}t# +jE)$ !D,`U %hawlSЮP-!Efr#m䓰awS\Ld|Vw]mI]b **1)nl};=ϜϥӗW7rl/sSJ9gOGoy 6̑m}ҖAcu06&lTS׵cY]Iܷf7&!Gk]RtMOrafM84kwC ! B#Pr`$2mj*Va.e۵K1kCwPbuif1#eizsn@;aP1Hci1)b6iC>o$ Oҙ2ǯ֣ut|x#|쑧;2OZy*n䵌3%A5+2ۿ/w}*h75!Z^\L擗}Ma=lb֔1EI0dZ\Vͪ]f}lV4HL̘J(CHjua"iU5$ih:)}L:`m3ч͖پK-['7K1&UYu4pӀng?rdddddU<< biyy;y./OGU}~AAA(H d͓s]㊢, 2laPVے@B!8@H| N{^"3%I 7yWwJDPC$"C+ "&Ue1TPaMPY9'"%$( HDEY HT۔RnJcjS{&d$NkyC~y,x[qGFFFFFF~ 0M~&M]Ue P @ ( s5mH ai_l_v}LI !=Z̧uw6$_\Hysۮ|ŋs|uuIۍݧO™ͦ уtK0!b\̚`DJuS7abq$m6^__{?>Y'PC ȔʢX-HkEO 3g* j QCP" !V$(^_6Mm~|)!n6=x_n`EU19ӛ+|>1=P50/_\_r6xz~ݺtw&E޻rw󋋫놹1в"lS+X@l*mkzߓ+^Vb*Zm sSׅWӛy>@D 8>,"3D>u_吟r<ӡNd8l_~M&;feTGPs4qtMBBL :NRqH;KM S\YTmw>p}3O߬b5!o M&uH%\dj-L@*t o#ɤ^lVΘj>_znOjO}HɤMEA۽Dvι4&&DG#oxoOz_U=͡?rG*d?kb*ސ7u<[ |Z̶z_Ϫ Ly )R|rsHJr+v^̰ eɄ}cMC(+bT]e "n=(WUrb|bJ!Dwm^1VŪ][kƘ~k ήUDtz{1U]Nz6CY5=TB0īMDGTD"_ZVɷllAzn0o?*q7ܟknKYö9s27ӿGɷj*´GlgX߮[TDWagXҙiriw0l>` ,*[ͦ &UvkjT ӦNɤ\̧LEq}`v?uzVnmej jV%M ES]t2]e''Wsh_A `5 Im>x?j = 9\̖o| >ӷ`dddddd+5O̙Sp矹օ#A %jZ[ckgRbc/g m/1-fe]u^]B`c1䈇!ޢ(G^ͪJHY("Cgq9iY;k(^^Non/>Y&T)Y%5L*ax qMs<98er0>22222U]98I._ r&Ş@>Ng poC RXƤvyU ƊjWn:gtb]eSԬ ∮6Z6mʢ-Li!̚겾y2%਩] K* 0)&^?a( TĚ\Ϋd2MiY$uQ)414\;v)|Z$j ِ"({NT>t1sa lRFSrg|^43ȗvM<)b*x07·Nyd1cNU.k @VH RUb8.DIEA1ovl6+;.JSt{۩` ӉcN9jsbC͚ }O0 /ww{1,f/Sfo~a1k4x1j{q0崶΁T]_ޮW{"{%>H"kkT: ((Ky8W$S%=H_S{"zu/rڮ9 I%p)·D$Of9kid{R$7$"(Q(F4X;> cTURJ)Ŕ@D$Un?t]RUʒZ[wvIS1fC"JeiRaEׇ;OI t& ~x8thHUbcݰ? !Ia) 6LU};xrS#&q΅1CHYfrHnvX 4) ?Nz\t{ Nㅇ'"cL,")\<<@6######_Z^5sdydvSp:SA$!w(iTd)`Ξ?%棍"p$" : ÇئUPlP|zFI LҤ*-%Q keAZY~k|6hZ dnlv)01K "ͿL "_oU50ΆUiG^)ώ觝yOcʲl6m˪$rN$bV+k2^(dhB:???vVefݽ,rپˢ !<{lXTuZt2ia}ץSlL1  et6w?zJ'Oooot\~"CD&DD"abcH"E۶uQ=<뜨B!ˋ'OT˗yY.}u= nsUUR{B1zZKD)%1Xk0...Vv=)]sdddddKksQ_- }_7lvss(|;u{h'"}\~{߽]Q~R裏RLY?>=t/?D{߾跾~;?wٳ??|~l<~juyylU9o?zZt*^]^/*W{gvrIDLSnooCVOo~+b{o{^?}n{'OD{~NUg5M0lzm׾uΩ͋/fO>O~mS|ym "WWW!{wrXcnnnh4a٬ 1nwޙdRdBb>~6m֛~lx]un jy6ۭa`Z7_f|r{o'Uϛ׾\aM ?L&/^~0 zNi>je].]m۾kW/Ƙ/ϟi>_~oXk/>nonO)`6n[⣏(RJ]ש1axY۶S|]}~wy?g}lv3Ƽm۶?'O/^K.,oW?eYn{EYnK"/_l?d2:DDιfRl6?OWrzZ{"_{ˀD6aoP@EPPd8+V\YʼnQ8ѺZ=ֽ+ZBR !~0Z}/{Tx,P** @P^^^XXT*⌍KJJTR499Y( .P(I!B}`\kN8X\\|}\MHH`$I\bgIMMU ԩPpT AZ eee׮]rl6[,WTTTFՓ]J%0 zsy4Md2UY nݺeff{QQQYYjp1??ѣGEEE4MgeebDZգ ++KPBPݭ&IRPPz\.'&&REQTjj\.///g&--媞 E^^IJbU+11㩾8Œ媕T+W=TAx꿹\P(H$UB!AyWh"B!mi6鰥K;5[k?tB!ꂵ&B!j.Xk"B悵&B!j.Xk"B悵&B!j.Xk"B悵&B!j.Xk"B悵&B!j.Xk"B悵&B!j.Xk"B悵&B!j.Xk"B悵&B!j.5If B!/c5>lTYeIDATi6!B03{&mUkMy!B?Iޝmf٪Mj͊_&m"ux!B?-4 ":١ A64 il#),\!B}f<_g dT5q&ps݃YVP!B}i) ~CDM5B!jF=B!&B!j.Xk"B悵&B!j.Xk"B悵&B!j.Xk"B悵&B!j.Xk"B悵&B!j.l7W6B!D!Bͅx6B!D!BkMB!\H+]f>$le Bѷ55Z<#[+Cf3B!5Y|S[G+#^}KHQN Vՙ],Λۉ?ck1ކ]EՅt܁9͎q";'; &m=s4 s|>olmook&hBH SBLی\ssN9s[҄r~ʅCmk|6Qggg>lPY9g>[IZϚ_nUu1"iu²/div4RS"qlAVY^#QW⽏,Q}/}zǹ5 I;_߈ ͻ7|JG)`RϤgo\rSgnݾzz]F&˱QclɂF߹ϼm~y]uzVSgzVw~APMsdsM&eR5?:hy"V_a0E֌2)|^XYM}Fo@]]׳ךʌW/T )ޅ5;)9쳟+^5{JG=3#4;TK٨nUSUOqӏ/;?Lu5axSXk)[KmdBQ/vJ˪ygoZYt-PSlr%;;ژ $Gk. RT1\N]LHqʓҪ洰tr67೩1ejggy v6&)㲪QSxZ3sr0 *xEch*mBW,Qjijr \\,'!Yhɷl`ϑ<~!Qͦ%rCXCKGks#>[YiL\oĉǫ|] @p mL[HEaӘ9^DvssU9IoĊ:cd,"߼|Rߺ'?u t-}Zs+ _&UnMPp钌g1\<YiOƩyEyrϹg$O]R+g6i6S[Hm{vĕQ`8v5וdĉ5Y^>zqf4Ϻ=x=8z3?1gWSwƩN(ﰜmax V۩~cGRr%.ݻ`ڮ'F83GW8 XinBĂ_x#riJӛRV.uq)+ VZw=nH lI^ZK`ϐ1Rha؆'qง挏'v+-*&]HkY2l9a\ӌL%}F -݁ e Zc !>|3zPbɎCۿQm-H_4_`')n?uAgz~=CC{|~a><e+m%?0o#}mzf5qU(bM q6cg *@%ݹ_h[OX5߿ؤ~}~+t9T/SM?T)m;i ҳ%dtt;N[=ϻn=SG~uu@Hh~}*gȣu'zQUS2a];wHAB'tҡ&:\`~~ߐԊ@ٍA{Գ]Æy~$UxoDlڣ_%GOa@(nإ[`[ uSqu_ޝN9Є:Tνb_ #~\>ȓ! llĠnݻ$2&-X9$&rHFrPq')\þ߿5^};CHv>,jkvuۈ~RO;88/d :~~Z"BZ6<~F?yCZPPR_R!ЇNNy9. ^ ǶO\H+ZI@K^_۲{mu@z#NϨB˵WRj ^ ]J$k_˨`>˨Yt}XJѷ[~'zK5qfϜ9]m^>1L C+dr\.+i m{' y+H3;;>2-yDj9 4k7F6))(-|$5KqыT G!M (d]H!a--trx"~VzV)). O@1@.OҤ (((OM,}pf~߀?&<;yrɦiBpwz|zZRcATVʋx)8xDZWGLskVx7bϞi_\٩ջԆtyde cZc',lXؤt-ȶu%T19D{[6y,ꎅWw 8so\E3Fx'*stH6:7:z]zeǾ؂N.G+yю#}*U!-k|%r@ToU?=;!WWB'G[49W4,6hIhiRmv`C}UkBAm+6vjt0׿BֻSSLG A0Rw)%.F5 _i(~7ZJKr\&}Vlph{ĝd10cEQ[% 1.տ4EW'gԿ ;i4|rλOGYyh@_ʫzVR@٦3~FP$Xec5kNN1y܉ORadZ wg(X݌Q*f86]9ګ w nrsO\AF}|K٫S9'Mҫ([mCsCefgjENOH`%E7~ 2\dzjz_ʫd1JҺc{xB:)o- y~Fư^8J3o8pDjfZOJԽل/UEx_]}eIǍd<[2@MɆz !>|,As hhabllѺٟ9chPݾWb2mfVv:ȷhg[+>_>&_?9~"cnv"3O@O5)}}]/Mkڪ~6N]:rGbVTdmuR nv /'sSaqY+tr9 דC{߾cࠀz?Lz- hcߦsGg>} qq lefjײߣ,ԯ_PkS!_Oh_=W}c @S9ڵuqϽ,:2 r%B-(/05偢L<7h8Qg,b-23o9qCD- xT!2I]z 1ztQzwOPzc}fKF;=1 -CKRnn  r/@I0s cgk4Stg]׺kvԏλMM0ѦqJ=xޢ-n4`蒻+=zS('wn,f,9S *y/ zjI0򜇇/Z -̏ mUsj JozmAӳ0iԶ".pg/ QSZ"p;