pax_global_header00006660000000000000000000000064142170315720014514gustar00rootroot0000000000000052 comment=c4fd1b8986c6d6d4ae5cd51e65a8bbeb495dfa4e userland/000077500000000000000000000000001421703157200126755ustar00rootroot00000000000000userland/.github/000077500000000000000000000000001421703157200142355ustar00rootroot00000000000000userland/.github/ISSUE_TEMPLATE/000077500000000000000000000000001421703157200164205ustar00rootroot00000000000000userland/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000032651421703157200211200ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- --- name: Bug report about: Create a report to help us fix your issue --- **Is this the right place for my bug report?** This repository contains the Raspberry Pi userland code. If you believe that the issue you are seeing is in the userland libraries, this is the right place. If not, we have other repositories for the GPU firmware at [github.com/raspberrypi/firmware](https://github.com/raspberrypi/firmware) and linux kernel at [github.com/raspberrypi/linux](https://github.com/raspberrypi/linux). If you have problems with the Raspbian distribution packages, report them in the [github.com/RPi-Distro/repo](https://github.com/RPi-Distro/repo). If you simply have a question, then [the Raspberry Pi forums](https://www.raspberrypi.org/forums) are the best place to ask it. **Describe the bug** Add a clear and concise description of what you think the bug is. **To reproduce** List the steps required to reproduce the issue. **Expected behaviour** Add a clear and concise description of what you expected to happen. **Actual behaviour** Add a clear and concise description of what actually happened. **System** Copy and paste the results of the raspinfo command in to this section. Alternatively, copy and paste a pastebin link, or add answers to the following questions: * Which model of Raspberry Pi? e.g. Pi3B+, PiZeroW * Which OS and version (`cat /etc/rpi-issue`)? * Which firmware version (`vcgencmd version`)? * Which kernel version (`uname -a`)? **Logs** If applicable, add the relevant output from `dmesg` or similar. **Additional context** Add any other relevant context for the problem. userland/.gitignore000066400000000000000000000003671421703157200146730ustar00rootroot00000000000000# Build directory build/ # Compiled Object files *.slo *.lo *.o # Compiled Dynamic libraries *.so # Compiled Static libraries *.lai *.la *.a *.raw *.rgb *.bgr *.h264 *.264 *.yuyv *.uyvy *.yvyu *.vyuy *.i420 *.yuv *.jpg *.avi *.pts *.ppm *.mkv userland/CMakeLists.txt000066400000000000000000000075321421703157200154440ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(vmcs_host_apps) SET(PROJECT_VER_MAJOR 1) SET(PROJECT_VER_MINOR 0) SET(PROJECT_VER_PATCH 0) SET(PROJECT_VER "${PROJECT_VER_MAJOR}.${PROJECT_VER_MINOR}.${PROJECT_VER_PATCH}") SET(PROJECT_APIVER "${PROJECT_VER}") if(ARM64) set(BUILD_MMAL FALSE) set(BUILD_MMAL_APPS FALSE) else() set(BUILD_MMAL TRUE) set(BUILD_MMAL_APPS TRUE) endif() set(vmcs_root ${PROJECT_SOURCE_DIR}) get_filename_component(VIDEOCORE_ROOT . ABSOLUTE) set(VCOS_PTHREADS_BUILD_SHARED TRUE) include(makefiles/cmake/global_settings.cmake) include(makefiles/cmake/arm-linux.cmake) include(makefiles/cmake/vmcs.cmake) enable_language(ASM) # Global include paths include_directories(host_applications/framework) include_directories(${PROJECT_SOURCE_DIR}) include_directories(interface/vcos/pthreads) include_directories(interface/vmcs_host/linux) include_directories(interface/vmcs_host) include_directories(interface/vmcs_host/khronos) include_directories(interface/khronos/include) include_directories(${PROJECT_BINARY_DIR}) include_directories(interface/vchiq_arm) #include_directories(tools/inet_transport) include_directories(host_support/include) # Global compiler flags if(CMAKE_COMPILER_IS_GNUCC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-multichar -Wall -Wno-unused-but-set-variable -fPIC") endif() add_definitions(-D_REENTRANT) add_definitions(-DUSE_VCHIQ_ARM -DVCHI_BULK_ALIGN=1 -DVCHI_BULK_GRANULARITY=1) add_definitions(-DOMX_SKIP64BIT) add_definitions(-DEGL_SERVER_DISPMANX) add_definitions(-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64) add_definitions(-D_GNU_SOURCE) # do we actually need this? add_definitions(-D__VIDEOCORE4__) add_definitions(-DTV_SUPPORTED_MODE_NO_DEPRECATED) # add_definitions(-DKHRONOS_CLIENT_LOGGING) # Check for OpenWF-C value set via command line if(KHRONOS_EGL_PLATFORM MATCHES "openwfc") add_definitions(-DKHRONOS_EGL_PLATFORM_OPENWFC) endif() # List of subsidiary CMakeLists add_subdirectory(interface/vcos) add_subdirectory(interface/vmcs_host) add_subdirectory(interface/vchiq_arm) if(NOT ARM64) add_subdirectory(interface/khronos) endif() #add_subdirectory(opensrc/tools/lua) if(BUILD_MMAL) include_directories(interface/mmal) add_subdirectory(interface/mmal) add_subdirectory(containers) endif() # VidTex supports Android and Linux if(BUILD_MMAL_APPS) add_subdirectory(host_applications/android/apps/vidtex) endif(BUILD_MMAL_APPS) if(NOT ARM64) add_subdirectory(middleware/openmaxil) endif() # 3d demo code #if(NOT ANDROID) # add_subdirectory(thirdparty/applications/demos) # add_subdirectory(opensrc/applications/demos) #endif() #if(ENABLE_3D_TESTS) # add_subdirectory(thirdparty/applications/test) #endif() # FIXME: we should use a pre-packaged version of freetype # rather than the one included in the repo. #add_subdirectory(opensrc/helpers/freetype) #add_subdirectory(${PROJECT_SOURCE_DIR}/opensrc/helpers/fonts/ttf-bitstream-vera) # VMCS Host Applications #add_subdirectory(host_applications/framework) # add_subdirectory(interface/vchiq/test/win32) # Apps and libraries supporting Camera Tuning Tool #add_subdirectory(tools/inet_transport/linux) #add_subdirectory(host_support/vcstandalone) # add linux apps add_subdirectory(host_applications/linux) add_subdirectory(opensrc/helpers/libfdt) add_subdirectory(helpers/dtoverlay) set(vmcs_host_apps_VERSION_MAJOR 1) set(vmcs_host_apps_VERSION_MINOR 0) include_directories("${PROJECT_BINARY_DIR}") include(FindPkgConfig QUIET) if(PKG_CONFIG_FOUND) # Produce a pkg-config file foreach(PCFILE bcm_host.pc brcmegl.pc brcmglesv2.pc brcmvg.pc vcsm.pc mmal.pc) configure_file("pkgconfig/${PCFILE}.in" "${PCFILE}" @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PCFILE}" DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig") endforeach() endif() # Remove cache entry, if one added by command line unset(KHRONOS_EGL_PLATFORM CACHE) userland/LICENCE000066400000000000000000000030461421703157200136650ustar00rootroot00000000000000Copyright (c) 2012, Broadcom Europe Ltd Copyright (c) 2015, Raspberry Pi (Trading) Ltd 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 name of the copyright holder nor the names of its 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. userland/README.md000066400000000000000000000016021421703157200141530ustar00rootroot00000000000000This repository contains the source code for the ARM side libraries used on Raspberry Pi. These typically are installed in /opt/vc/lib and includes source for the ARM side code to interface to: EGL, mmal, GLESv2, vcos, openmaxil, vchiq_arm, bcm_host, WFC, OpenVG. Use buildme to build. It requires cmake to be installed and an ARM cross compiler. For 32-bit cross compilation it is set up to use this one: https://github.com/raspberrypi/tools/tree/master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian Whilst 64-bit userspace is not officially supported, some of the libraries will work for it. To cross compile, install gcc-aarch64-linux-gnu and g++-aarch64-linux-gnu first. For both native and cross compiles, add the option ```--aarch64``` to the buildme command. Note that this repository does not contain the source for the edidparser and vcdbg binaries due to licensing restrictions. userland/buildme000077500000000000000000000024311421703157200142440ustar00rootroot00000000000000#!/bin/bash BUILDTYPE=Release ARCH=$(uname -m) ARM64=OFF CMAKE_TOOLCHAIN_FILE=../../../makefiles/cmake/toolchains/arm-linux-gnueabihf.cmake if [ "$1" = "--debug" ]; then BUILDTYPE=Debug shift fi if [ "$1" = "--aarch64" ]; then ARM64=ON CMAKE_TOOLCHAIN_FILE=../../../makefiles/cmake/toolchains/aarch64-linux-gnu.cmake shift fi BUILDSUBDIR=`echo $BUILDTYPE | tr '[A-Z]' '[a-z]'`; if [ $ARCH = "armv6l" ] || [ $ARCH = "armv7l" ] || [ $ARCH = "aarch64" ]; then # Native compile on the Raspberry Pi mkdir -p build/raspberry/$BUILDSUBDIR pushd build/raspberry/$BUILDSUBDIR cmake -DCMAKE_BUILD_TYPE=$BUILDTYPE -DARM64=$ARM64 ../../.. if [ $ARCH = "armv6l" ]; then make else make -j4 fi if [ "$1" != "" ]; then sudo make install DESTDIR=$1 else sudo make install fi elif [ "$1" = "--native" ]; then # Build natively on the host mkdir -p build/native/$BUILDSUBDIR pushd build/native/$BUILDSUBDIR cmake -DCMAKE_BUILD_TYPE=$BUILDTYPE ../../.. shift make -j `nproc` $* else # Cross compile on a more capable machine mkdir -p build/arm-linux/$BUILDSUBDIR pushd build/arm-linux/$BUILDSUBDIR cmake -DCMAKE_TOOLCHAIN_FILE=$CMAKE_TOOLCHAIN_FILE -DCMAKE_BUILD_TYPE=$BUILDTYPE -DARM64=$ARM64 ../../.. make -j `nproc` if [ "$1" != "" ]; then sudo make install DESTDIR=$1 fi fi popd userland/containers/000077500000000000000000000000001421703157200150425ustar00rootroot00000000000000userland/containers/CMakeLists.txt000066400000000000000000000124301421703157200176020ustar00rootroot00000000000000SET( SOURCE_DIR . ) # We support building both static and shared libraries if (NOT DEFINED LIBRARY_TYPE) set(LIBRARY_TYPE SHARED) endif (NOT DEFINED LIBRARY_TYPE) # Make sure the compiler can find the necessary include files include_directories (${SOURCE_DIR}/.. ${SOURCE_DIR}/../interface/vcos) # Needed for the container loader add_definitions(-DDL_PATH_PREFIX="${VMCS_PLUGIN_DIR}/") SET( GCC_COMPILER_FLAGS -Wall -g -O2 -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wcast-qual -Wwrite-strings -Wundef ) SET( GCC_COMPILER_FLAGS ${GCC_COMPILER_FLAGS} -Wextra )#-Wno-missing-field-initializers ) SET( GCC_COMPILER_FLAGS ${GCC_COMPILER_FLAGS} -std=c99 -D_POSIX_C_SOURCE=200112L ) SET( GCC_COMPILER_FLAGS ${GCC_COMPILER_FLAGS} -Wno-missing-field-initializers ) SET( GCC_COMPILER_FLAGS ${GCC_COMPILER_FLAGS} -Wno-unused-value ) add_definitions( ${GCC_COMPILER_FLAGS} ) # Containers core library set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers.c) set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_io.c) set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_io_helpers.c) set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_codecs.c) set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_utils.c) set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_writer_utils.c) set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_loader.c) set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_filters.c) set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_logging.c) set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_uri.c) set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_bits.c) set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_list.c) set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_index.c) # Containers io library set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_file.c) set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_null.c) set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_net.c) set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_pktfile.c) set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_http.c) add_definitions( -DENABLE_CONTAINER_IO_HTTP ) # Containers net library if (DEFINED MSVC) set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_common.c) set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_win32.c) elseif (DEFINED LINUX OR DEFINED UNIX) set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_common.c) set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_bsd.c) else (DEFINED MSVC) set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_null.c) endif (DEFINED MSVC) set(extra_net_SRCS net_sockets_win32.c net_sockets_win32.h net_sockets_null.c) add_custom_target(containers_net_extra ALL COMMAND touch ${extra_net_SRCS} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/containers/net) # Packetizers library set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/core/packetizers.c) set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/mpga/mpga_packetizer.c) set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/mpgv/mpgv_packetizer.c) set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/pcm/pcm_packetizer.c) set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/h264/avc1_packetizer.c) add_library(containers ${LIBRARY_TYPE} ${core_SRCS} ${io_SRCS} ${net_SRCS} ${packetizers_SRCS}) target_link_libraries(containers vcos) install(TARGETS containers DESTINATION lib) set(container_readers) set(container_writers) # Container modules add_subdirectory(mp4) set(container_readers ${container_readers} reader_mp4) set(container_writers ${container_writers} writer_mp4) add_subdirectory(mpeg) set(container_readers ${container_readers} reader_ps) add_subdirectory(mpga) set(container_readers ${container_readers} reader_mpga) add_subdirectory(binary) set(container_readers ${container_readers} reader_binary) set(container_writers ${container_writers} writer_binary) add_subdirectory(mkv) set(container_readers ${container_readers} reader_mkv) add_subdirectory(wav) set(container_readers ${container_readers} reader_wav) add_subdirectory(asf) set(container_readers ${container_readers} reader_asf) set(container_writers ${container_writers} writer_asf) add_subdirectory(flash) set(container_readers ${container_readers} reader_flv) add_subdirectory(avi) set(container_readers ${container_readers} reader_avi) set(container_writers ${container_writers} writer_avi) add_subdirectory(rtp) set(container_readers ${container_readers} reader_rtp) add_subdirectory(rtsp) set(container_readers ${container_readers} reader_rtsp) add_subdirectory(rcv) set(container_readers ${container_readers} reader_rcv) add_subdirectory(rv9) set(container_readers ${container_readers} reader_rv9) add_subdirectory(qsynth) set(container_readers ${container_readers} reader_qsynth) add_subdirectory(simple) set(container_readers ${container_readers} reader_simple) set(container_writers ${container_writers} writer_simple) add_subdirectory(raw) set(container_readers ${container_readers} reader_raw_video) set(container_writers ${container_writers} writer_raw_video) add_subdirectory(dummy) set(container_writers ${container_writers} writer_dummy) add_subdirectory(metadata/id3) set(container_readers ${container_readers} reader_metadata_id3) if (${LIBRARY_TYPE} STREQUAL STATIC) target_link_libraries(containers ${container_readers} ${container_writers}) endif (${LIBRARY_TYPE} STREQUAL STATIC) # Test apps add_subdirectory(test) userland/containers/asf/000077500000000000000000000000001421703157200156135ustar00rootroot00000000000000userland/containers/asf/CMakeLists.txt000066400000000000000000000010261421703157200203520ustar00rootroot00000000000000# Container module needs to go in as a plugins so different prefix # and install path set(CMAKE_SHARED_LIBRARY_PREFIX "") # Make sure the compiler can find the necessary include files include_directories (../..) add_library(reader_asf ${LIBRARY_TYPE} asf_reader.c) target_link_libraries(reader_asf containers) install(TARGETS reader_asf DESTINATION ${VMCS_PLUGIN_DIR}) add_library(writer_asf ${LIBRARY_TYPE} asf_writer.c) target_link_libraries(writer_asf containers) install(TARGETS writer_asf DESTINATION ${VMCS_PLUGIN_DIR}) userland/containers/asf/asf_reader.c000066400000000000000000002605121421703157200200600ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include /* prevent double-defines when it is defined on the command line - as in the test app */ #ifndef ENABLE_CONTAINERS_LOG_FORMAT //#define ENABLE_CONTAINERS_LOG_FORMAT #endif #ifndef ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE //#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE #endif #define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->object_level /* For the sanity of the Visual Studio debugger make local names for structures */ #define VC_CONTAINER_TRACK_MODULE_T ASF_VC_CONTAINER_TRACK_MODULE_T #define VC_CONTAINER_MODULE_T ASF_VC_CONTAINER_MODULE_T #define VC_CONTAINER_T ASF_VC_CONTAINER_T #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_logging.h" /****************************************************************************** Defines. ******************************************************************************/ #define ASF_TRACKS_MAX 2 #define ASF_EXTRADATA_MAX 256 #define ASF_MAX_OBJECT_LEVEL 4 #define ASF_MAX_CONSECUTIVE_UNKNOWN_OBJECTS 5 #define ASF_MAX_OBJECT_SIZE (1<<29) /* Does not apply to the data object */ #define ASF_OBJECT_HEADER_SIZE (16+8) #define ASF_UNKNOWN_PTS ((uint32_t)(-1)) #define ASF_MAX_CONSECUTIVE_CORRUPTED_PACKETS 100 #define ASF_MAX_SEARCH_PACKETS 1000 #define ASF_SKIP_GUID(ctx, size, n) (size -= 16, SKIP_GUID(ctx,n)) #define ASF_SKIP_U8(ctx, size, n) (size -= 1, SKIP_U8(ctx,n)) #define ASF_SKIP_U16(ctx, size, n) (size -= 2, SKIP_U16(ctx,n)) #define ASF_SKIP_U24(ctx, size, n) (size -= 3, SKIP_U24(ctx,n)) #define ASF_SKIP_U32(ctx, size, n) (size -= 4, SKIP_U32(ctx,n)) #define ASF_SKIP_U64(ctx, size, n) (size -= 8, SKIP_U64(ctx,n)) #define ASF_READ_GUID(ctx, size, buffer, n) (size -= 16, READ_GUID(ctx,(uint8_t *)buffer,n)) #define ASF_READ_U8(ctx, size, n) (size -= 1, READ_U8(ctx,n)) #define ASF_READ_U16(ctx, size, n) (size -= 2, READ_U16(ctx,n)) #define ASF_READ_U24(ctx, size, n) (size -= 3, READ_U24(ctx,n)) #define ASF_READ_U32(ctx, size, n) (size -= 4, READ_U32(ctx,n)) #define ASF_READ_U64(ctx, size, n) (size -= 8, READ_U64(ctx,n)) #define ASF_READ_STRING(ctx, size, buffer, to_read, n) (size -= to_read, READ_STRING_UTF16(ctx,buffer,to_read,n)) #define ASF_SKIP_STRING(ctx, size, to_read, n) (size -= to_read, SKIP_STRING_UTF16(ctx,to_read,n)) #define ASF_READ_BYTES(ctx, size, buffer, to_read) (size -= to_read, READ_BYTES(ctx,buffer,to_read)) #define ASF_SKIP_BYTES(ctx, size, to_read) (size -= to_read, SKIP_BYTES(ctx,to_read)) /* Read variable length field from p_context. */ #define READ_VLC(p_context, length, value_if_missing, txt) \ (length) == 1 ? READ_U8(p_context, txt) : \ (length) == 2 ? READ_U16(p_context, txt) : \ (length) == 3 ? READ_U32(p_context, txt) : value_if_missing #define CHECK_POINT(p_context, amount_to_read) do { \ if(amount_to_read < 0) return VC_CONTAINER_ERROR_CORRUPTED; \ if(STREAM_STATUS(p_context)) return STREAM_STATUS(p_context); } while(0) /****************************************************************************** Type definitions. ******************************************************************************/ /** Context for our reader */ typedef struct { uint64_t start; /* The byte offset start of the current packet in the file */ uint32_t size; uint32_t padding_size; uint64_t send_time; /* read in mS, stored in uS */ bool eos; bool corrupted; uint16_t bad_packets; /* All the different Length Types for the VLC codes */ unsigned int replicated_data_lt; unsigned int offset_into_media_object_lt; unsigned int media_object_number_lt; unsigned int payload_lt; unsigned int multiple_payloads; unsigned int compressed_payloads; uint8_t num_payloads; uint8_t current_payload; uint32_t current_offset; /* The offset in the current packet for the next byte to be read */ /* Info already read */ uint32_t stream_num; /* Stream number and key-frame flag */ uint32_t media_object_num; uint32_t media_object_off; uint32_t payload_size; uint32_t subpayload_size; /* Info read from the replicated data */ uint32_t media_object_size; uint64_t media_object_pts; /**< Presentation timestamp in microseconds */ uint64_t media_object_pts_delta; /**< Presentation timestamp delta in microseconds */ } ASF_PACKET_STATE; typedef struct VC_CONTAINER_TRACK_MODULE_T { /* The ID of the stream (the index in the containing array need not be the ID) */ unsigned int stream_id; bool b_valid; uint8_t extradata[ASF_EXTRADATA_MAX]; ASF_PACKET_STATE *p_packet_state; ASF_PACKET_STATE local_packet_state; /* Simple index structure. Corresponds to the simple index in 6.1 of the spec * This index has locations in packets, not in bytes, and relies on the * file having fixed-length packets - as is required */ struct { uint64_t offset; /**< Offset to the start of the simple index data */ uint32_t num_entries; int64_t time_interval; /* in uS */ bool incomplete; /* The index does not go to the end of the file */ } simple_index; } VC_CONTAINER_TRACK_MODULE_T; typedef struct VC_CONTAINER_MODULE_T { int object_level; uint32_t packet_size; /**< Size of a data packet */ uint64_t packets_num; /**< Number of packets contained in the data object */ bool broadcast; /**< Specifies if we are dealing with a broadcast stream */ int64_t duration; /**< Duration of the stream in microseconds */ int64_t preroll; /**< Duration of the preroll in microseconds. */ /* This is the PTS of the first packet; all are offset by this amount. */ uint64_t time_offset; /**< Offset added to timestamps in microseconds */ uint64_t data_offset; /**< Offset to the start of the data packets */ int64_t data_size; /**< Size of the data contained in the data object */ /* The track objects. There's a count of these in VC_CONTAINER_T::tracks_num */ VC_CONTAINER_TRACK_T *tracks[ASF_TRACKS_MAX]; /* Translation table from stream_number to index in the tracks array */ unsigned char stream_number_to_index[128]; /* Data for a top-level index structure as defined in 6.2 of the spec */ struct { uint64_t entry_time_interval; /* The time interval between specifiers, scaled to uS */ uint32_t specifiers_count; /* The number of specifiers in the file, 0 if no index */ uint64_t active_specifiers[ASF_TRACKS_MAX]; /* the specifier in use for each track, * or >=specifiers_count if none */ uint64_t specifiers_offset; /* The file address of the first specifier. */ uint32_t block_count; /* The number of index blocks */ uint64_t blocks_offset; /* The file address of the first block */ } top_level_index; /* A pointer to the track (in the tracks array) which is to be used with a simple index. * null if there is no such track */ ASF_VC_CONTAINER_TRACK_MODULE_T *simple_index_track; /* Shared packet state. This is used when the tracks are in sync, and for the track at the earliest position in the file when they are not in sync */ ASF_PACKET_STATE packet_state; } VC_CONTAINER_MODULE_T; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T asf_reader_open( VC_CONTAINER_T * ); /****************************************************************************** Prototypes for local functions ******************************************************************************/ static VC_CONTAINER_STATUS_T asf_read_object( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T asf_read_object_header( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T asf_read_object_header_ext( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T asf_read_object_file_properties( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T asf_read_object_stream_properties( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T asf_read_object_ext_stream_properties( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T asf_read_object_simple_index( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T asf_read_object_index( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T asf_read_object_index_parameters( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T asf_read_object_data( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T asf_read_object_codec_list( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T asf_read_object_content_description( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T asf_read_object_stream_bitrate_props( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T asf_read_object_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T asf_read_object_ext_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T asf_read_object_adv_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T asf_skip_unprocessed_object( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T seek_to_positions(VC_CONTAINER_T *p_ctx, uint64_t track_positions[ASF_TRACKS_MAX], int64_t *p_time, VC_CONTAINER_SEEK_FLAGS_T flags, unsigned int start_track, bool seek_on_start_track); /****************************************************************************** GUID list for the different ASF objects ******************************************************************************/ static const GUID_T asf_guid_header = {0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; static const GUID_T asf_guid_file_props = {0x8CABDCA1, 0xA947, 0x11CF, {0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; static const GUID_T asf_guid_stream_props = {0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; static const GUID_T asf_guid_ext_stream_props = {0x14E6A5CB, 0xC672, 0x4332, {0x83, 0x99, 0xA9, 0x69, 0x52, 0x06, 0x5B, 0x5A}}; static const GUID_T asf_guid_data = {0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; static const GUID_T asf_guid_simple_index = {0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}}; static const GUID_T asf_guid_index = {0xD6E229D3, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}; static const GUID_T asf_guid_index_parameters = {0xD6E229DF, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}; static const GUID_T asf_guid_header_ext = {0x5FBF03B5, 0xA92E, 0x11CF, {0x8E, 0xE3, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; static const GUID_T asf_guid_codec_list = {0x86D15240, 0x311D, 0x11D0, {0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}}; static const GUID_T asf_guid_content_description = {0x75B22633, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; static const GUID_T asf_guid_ext_content_description = {0xD2D0A440, 0xE307, 0x11D2, {0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50}}; static const GUID_T asf_guid_stream_bitrate_props = {0x7BF875CE, 0x468D, 0x11D1, {0x8D, 0x82, 0x00, 0x60, 0x97, 0xC9, 0xA2, 0xB2}}; static const GUID_T asf_guid_language_list = {0x7C4346A9, 0xEFE0, 0x4BFC, {0xB2, 0x29, 0x39, 0x3E, 0xDE, 0x41, 0x5C, 0x85}}; static const GUID_T asf_guid_metadata = {0xC5F8CBEA, 0x5BAF, 0x4877, {0x84, 0x67, 0xAA, 0x8C, 0x44, 0xFA, 0x4C, 0xCA}}; static const GUID_T asf_guid_padding = {0x1806D474, 0xCADF, 0x4509, {0xA4, 0xBA, 0x9A, 0xAB, 0xCB, 0x96, 0xAA, 0xE8}}; static const GUID_T asf_guid_content_encryption = {0x2211B3FB, 0xBD23, 0x11D2, {0xB4, 0xB7, 0x00, 0xA0, 0xC9, 0x55, 0xFC, 0x6E}}; static const GUID_T asf_guid_ext_content_encryption = {0x298AE614, 0x2622, 0x4C17, {0xB9, 0x35, 0xDA, 0xE0, 0x7E, 0xE9, 0x28, 0x9C}}; static const GUID_T asf_guid_adv_content_encryption = {0x43058533, 0x6981, 0x49E6, {0x9B, 0x74, 0xAD, 0x12, 0xCB, 0x86, 0xD5, 0x8C}}; static const GUID_T asf_guid_compatibility = {0x26F18B5D, 0x4584, 0x47EC, {0x9F, 0x5F, 0x0E, 0x65, 0x1F, 0x04, 0x52, 0xC9}}; static const GUID_T asf_guid_script_command = {0x1EFB1A30, 0x0B62, 0x11D0, {0xA3, 0x9B, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}}; static const GUID_T asf_guid_mutual_exclusion = {0xD6E229DC, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}; static const GUID_T asf_guid_stream_type_video = {0xBC19EFC0, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; static const GUID_T asf_guid_stream_type_audio = {0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; /****************************************************************************** List of GUIDs and their associated processing functions ******************************************************************************/ static struct { const GUID_T *guid; const char *psz_name; VC_CONTAINER_STATUS_T (*pf_func)( VC_CONTAINER_T *, int64_t ); } asf_object_list[] = { {&asf_guid_header, "header", asf_read_object_header}, {&asf_guid_file_props, "file properties", asf_read_object_file_properties}, {&asf_guid_stream_props, "stream properties", asf_read_object_stream_properties}, {&asf_guid_ext_stream_props, "extended stream properties", asf_read_object_ext_stream_properties}, {&asf_guid_data, "data", asf_read_object_data}, {&asf_guid_simple_index, "simple index", asf_read_object_simple_index}, {&asf_guid_index, "index", asf_read_object_index}, {&asf_guid_index_parameters, "index parameters", asf_read_object_index_parameters}, {&asf_guid_header_ext, "header extension", asf_read_object_header_ext}, {&asf_guid_codec_list, "codec list", asf_read_object_codec_list}, {&asf_guid_content_description, "content description", asf_read_object_content_description}, {&asf_guid_ext_content_description, "extended content description", asf_skip_unprocessed_object}, {&asf_guid_stream_bitrate_props, "stream bitrate properties", asf_read_object_stream_bitrate_props}, {&asf_guid_language_list, "language list", asf_skip_unprocessed_object}, {&asf_guid_metadata, "metadata", asf_skip_unprocessed_object}, {&asf_guid_padding, "padding", asf_skip_unprocessed_object}, {&asf_guid_compatibility, "compatibility", asf_skip_unprocessed_object}, {&asf_guid_script_command, "script command", asf_skip_unprocessed_object}, {&asf_guid_mutual_exclusion, "mutual exclusion", asf_skip_unprocessed_object}, {&asf_guid_content_encryption, "content encryption", &asf_read_object_content_encryption}, {&asf_guid_ext_content_encryption, "extended content encryption", &asf_read_object_ext_content_encryption}, {&asf_guid_adv_content_encryption, "advanced content encryption", &asf_read_object_adv_content_encryption}, {0, "unknown", asf_skip_unprocessed_object} }; /****************************************************************************** Local Functions ******************************************************************************/ /** Find the track associated with an ASF stream id */ static VC_CONTAINER_TRACK_T *asf_reader_find_track( VC_CONTAINER_T *p_ctx, unsigned int stream_id, bool b_create) { VC_CONTAINER_TRACK_T *p_track = 0; VC_CONTAINER_MODULE_T * module = p_ctx->priv->module; unsigned int i; /* discard the key-frame flag */ stream_id &= 0x7f; /* look to see if we have already allocated the stream */ i = module->stream_number_to_index[stream_id]; if(i < p_ctx->tracks_num) /* We found it */ p_track = p_ctx->tracks[i]; if(!p_track && b_create && p_ctx->tracks_num < ASF_TRACKS_MAX) { /* Allocate and initialise a new track */ p_ctx->tracks[p_ctx->tracks_num] = p_track = vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); if(p_track) { /* store the stream ID */ p_track->priv->module->stream_id = stream_id; /* Store the translation table value */ module->stream_number_to_index[stream_id] = p_ctx->tracks_num; /* count the track */ p_ctx->tracks_num++; } } if(!p_track && b_create) LOG_DEBUG(p_ctx, "could not create track for stream id: %i", stream_id); return p_track; } /** Base function used to read an ASF object from the ASF header. * This will read the object header do lots of sanity checking and pass on the rest * of the reading to the object specific reading function */ static VC_CONTAINER_STATUS_T asf_read_object( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; int64_t object_size, offset = STREAM_POSITION(p_ctx); unsigned int i, unknown_objects = 0, is_data_object; GUID_T guid; /* Sanity check the size of the data */ if(size && size < ASF_OBJECT_HEADER_SIZE) { LOG_DEBUG(p_ctx, "invalid object header (too small)"); return VC_CONTAINER_ERROR_CORRUPTED; } if(READ_GUID(p_ctx, &guid, "Object ID") != sizeof(guid)) return STREAM_STATUS(p_ctx); /* Find out which GUID we are dealing with */ for( i = 0; asf_object_list[i].guid; i++ ) { if(guid.word0 != asf_object_list[i].guid->word0) continue; if(!memcmp(&guid, asf_object_list[i].guid, sizeof(guid))) break; } LOG_FORMAT(p_ctx, "Object Name: %s", asf_object_list[i].psz_name); /* Bail out if we find too many consecutive unknown objects */ if(!asf_object_list[i].guid) unknown_objects++; else unknown_objects = 0; if(unknown_objects >= ASF_MAX_CONSECUTIVE_UNKNOWN_OBJECTS) { LOG_DEBUG(p_ctx, "too many unknown objects"); return VC_CONTAINER_ERROR_CORRUPTED; } is_data_object = asf_object_list[i].pf_func == asf_read_object_data; object_size = READ_U64(p_ctx, "Object Size"); /* Sanity check the object size */ if(object_size < 0 /* Shouldn't ever get that big */ || /* Minimum size check (data object can have a size == 0) */ (object_size < ASF_OBJECT_HEADER_SIZE && !(is_data_object && !object_size)) || /* Only the data object can really be massive */ (!is_data_object && object_size > ASF_MAX_OBJECT_SIZE)) { LOG_DEBUG(p_ctx, "object %s has an invalid size (%"PRIi64")", asf_object_list[i].psz_name, object_size); return VC_CONTAINER_ERROR_CORRUPTED; } if(size && object_size > size) { LOG_DEBUG(p_ctx, "object %s is bigger than it should (%"PRIi64" > %"PRIi64")", asf_object_list[i].psz_name, object_size, size); return VC_CONTAINER_ERROR_CORRUPTED; } size = object_size; if(module->object_level >= 2 * ASF_MAX_OBJECT_LEVEL) { LOG_DEBUG(p_ctx, "object %s is too deep. skipping", asf_object_list[i].psz_name); status = asf_skip_unprocessed_object(p_ctx, size - ASF_OBJECT_HEADER_SIZE); /* Just bail out, hoping we have enough data */ } else { module->object_level++; /* Call the object specific parsing function */ status = asf_object_list[i].pf_func(p_ctx, size - ASF_OBJECT_HEADER_SIZE); module->object_level--; if(status != VC_CONTAINER_SUCCESS) LOG_DEBUG(p_ctx, "object %s appears to be corrupted (%i)", asf_object_list[i].psz_name, status); } /* The stream position should be exactly at the end of the object */ { int64_t bytes_processed = STREAM_POSITION(p_ctx) - offset; /* fail with overruns */ if (bytes_processed > size) { /* Things have gone really bad here and we ended up reading past the end of the * object. We could maybe try to be clever and recover by seeking back to the end * of the object. However if we get there, the file is clearly corrupted so there's * no guarantee it would work anyway. */ LOG_DEBUG(p_ctx, "%"PRIi64" bytes overrun past the end of object %s", bytes_processed-size, asf_object_list[i].psz_name); return VC_CONTAINER_ERROR_CORRUPTED; } /* Handle underruns by throwing away the data (this should never happen, but we don't really care if it does) */ if (bytes_processed < size) { size -= bytes_processed; LOG_DEBUG(p_ctx, "%"PRIi64" bytes left unread in object %s", size, asf_object_list[i].psz_name); if(size < ASF_MAX_OBJECT_SIZE) SKIP_BYTES(p_ctx, size); /* read a small amount */ else SEEK(p_ctx, STREAM_POSITION(p_ctx) + size); /* seek a large distance */ } } return STREAM_STATUS(p_ctx); } /** Reads an ASF header object */ static VC_CONTAINER_STATUS_T asf_read_object_header( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; int64_t offset = STREAM_POSITION(p_ctx); /* Sanity check the size of the data */ if((size -= 6) < 0) return VC_CONTAINER_ERROR_CORRUPTED; SKIP_U32(p_ctx, "Number of Header Objects"); /* FIXME: could use that */ SKIP_U8(p_ctx, "Reserved1"); SKIP_U8(p_ctx, "Reserved2"); /* Read contained objects */ module->object_level++; while(status == VC_CONTAINER_SUCCESS && size >= ASF_OBJECT_HEADER_SIZE) { offset = STREAM_POSITION(p_ctx); status = asf_read_object(p_ctx, size); size -= (STREAM_POSITION(p_ctx) - offset); } module->object_level--; return status; } /** Reads an ASF extended header object */ static VC_CONTAINER_STATUS_T asf_read_object_header_ext( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; int64_t data_size, offset; ASF_SKIP_GUID(p_ctx, size, "Reserved Field 1"); ASF_SKIP_U16(p_ctx, size, "Reserved Field 2"); data_size = ASF_READ_U32(p_ctx, size, "Header Extension Data Size"); if(data_size != size) LOG_DEBUG(p_ctx, "invalid header extension data size (%"PRIi64",%"PRIi64")", data_size, size); CHECK_POINT(p_ctx, size); /* Read contained objects */ module->object_level++; while(status == VC_CONTAINER_SUCCESS && size >= ASF_OBJECT_HEADER_SIZE) { offset = STREAM_POSITION(p_ctx); status = asf_read_object(p_ctx, size); size -= (STREAM_POSITION(p_ctx) - offset); } module->object_level--; return status; } /** Reads an ASF file properties object */ static VC_CONTAINER_STATUS_T asf_read_object_file_properties( VC_CONTAINER_T *p_ctx, int64_t size ) { uint32_t max_packet_size; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; ASF_SKIP_GUID(p_ctx, size, "File ID"); ASF_SKIP_U64(p_ctx, size, "File Size"); ASF_SKIP_U64(p_ctx, size, "Creation Date"); ASF_SKIP_U64(p_ctx, size, "Data Packets Count"); module->duration = ASF_READ_U64(p_ctx, size, "Play Duration") / UINT64_C(10); /* read in 100nS units, stored in uS */ ASF_SKIP_U64(p_ctx, size, "Send Duration"); module->preroll = ASF_READ_U64(p_ctx, size, "Preroll") * UINT64_C(1000); /* read in mS, storedin uS */ module->broadcast = ASF_READ_U32(p_ctx, size, "Flags") & 0x1; module->packet_size = ASF_READ_U32(p_ctx, size, "Minimum Data Packet Size"); max_packet_size = ASF_READ_U32(p_ctx, size, "Maximum Data Packet Size"); ASF_SKIP_U32(p_ctx, size, "Maximum Bitrate"); if(module->preroll < module->duration) module->duration -= module->preroll; else module->duration = 0; /* Sanity check the packet size */ if(!module->packet_size) { LOG_DEBUG(p_ctx, "packet size cannot be 0"); return VC_CONTAINER_ERROR_FORMAT_FEATURE_NOT_SUPPORTED; } if(max_packet_size != module->packet_size) { LOG_DEBUG(p_ctx, "asf stream not supported (min packet size: %i != max packet size: %i)", module->packet_size, max_packet_size); return VC_CONTAINER_ERROR_FORMAT_FEATURE_NOT_SUPPORTED; } return STREAM_STATUS(p_ctx); } /** Reads the bitmapinfoheader structure contained in a stream properties object */ static VC_CONTAINER_STATUS_T asf_read_bitmapinfoheader( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *p_track, int64_t size ) { uint32_t bmih_size, formatdata_size; uint32_t fourcc; /* Sanity check the size of the data */ if(size < 40 + 11) return VC_CONTAINER_ERROR_CORRUPTED; /* Read the preamble to the BITMAPINFOHEADER */ ASF_SKIP_U32(p_ctx, size, "Encoded Image Width"); ASF_SKIP_U32(p_ctx, size, "Encoded Image Height"); ASF_SKIP_U8(p_ctx, size, "Reserved Flags"); formatdata_size = ASF_READ_U16(p_ctx, size, "Format Data Size"); /* Sanity check the size of the data */ if(formatdata_size < 40 || size < formatdata_size) return VC_CONTAINER_ERROR_CORRUPTED; bmih_size = ASF_READ_U32(p_ctx, size, "Format Data Size"); if(bmih_size < 40 || bmih_size > formatdata_size) return VC_CONTAINER_ERROR_CORRUPTED; /* Read BITMAPINFOHEADER structure */ p_track->format->type->video.width = ASF_READ_U32(p_ctx, size, "Image Width"); p_track->format->type->video.height = ASF_READ_U32(p_ctx, size, "Image Height"); /* Signed */ ASF_SKIP_U16(p_ctx, size, "Reserved"); ASF_SKIP_U16(p_ctx, size, "Bits Per Pixel Count"); ASF_READ_BYTES(p_ctx, size, (char *)&fourcc, 4); /* Compression ID */ LOG_FORMAT(p_ctx, "Compression ID: %4.4s", (char *)&fourcc); p_track->format->codec = vfw_fourcc_to_codec(fourcc); if(p_track->format->codec == VC_CONTAINER_CODEC_UNKNOWN) p_track->format->codec = fourcc; ASF_SKIP_U32(p_ctx, size, "Image Size"); ASF_SKIP_U32(p_ctx, size, "Horizontal Pixels Per Meter"); ASF_SKIP_U32(p_ctx, size, "Vertical Pixels Per Meter"); ASF_SKIP_U32(p_ctx, size, "Colors Used Count"); ASF_SKIP_U32(p_ctx, size, "Important Colors Count"); if(!(bmih_size -= 40))return VC_CONTAINER_SUCCESS; if(bmih_size > ASF_EXTRADATA_MAX) { LOG_DEBUG(p_ctx, "extradata truncated"); bmih_size = ASF_EXTRADATA_MAX; } p_track->format->extradata = p_track->priv->module->extradata; p_track->format->extradata_size = ASF_READ_BYTES(p_ctx, size, p_track->format->extradata, bmih_size); return STREAM_STATUS(p_ctx); } /** Reads the waveformatex structure contained in a stream properties object */ static VC_CONTAINER_STATUS_T asf_read_waveformatex( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *p_track, int64_t size) { uint16_t extradata_size; /* Read WAVEFORMATEX structure */ p_track->format->codec = waveformat_to_codec(ASF_READ_U16(p_ctx, size, "Codec ID")); p_track->format->type->audio.channels = ASF_READ_U16(p_ctx, size, "Number of Channels"); p_track->format->type->audio.sample_rate = ASF_READ_U32(p_ctx, size, "Samples per Second"); p_track->format->bitrate = ASF_READ_U32(p_ctx, size, "Average Number of Bytes Per Second") * 8; p_track->format->type->audio.block_align = ASF_READ_U16(p_ctx, size, "Block Alignment"); p_track->format->type->audio.bits_per_sample = ASF_READ_U16(p_ctx, size, "Bits Per Sample"); extradata_size = ASF_READ_U16(p_ctx, size, "Codec Specific Data Size"); CHECK_POINT(p_ctx, size); if(!extradata_size) return VC_CONTAINER_SUCCESS; /* Sanity check the size of the data */ if(extradata_size > size) return VC_CONTAINER_ERROR_CORRUPTED; if(extradata_size > ASF_EXTRADATA_MAX) { LOG_DEBUG(p_ctx, "extradata truncated"); extradata_size = ASF_EXTRADATA_MAX; } p_track->format->extradata = p_track->priv->module->extradata; p_track->format->extradata_size = ASF_READ_BYTES(p_ctx, size, p_track->format->extradata, extradata_size); return STREAM_STATUS(p_ctx); } /** Reads an ASF stream properties object */ static VC_CONTAINER_STATUS_T asf_read_object_stream_properties( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *p_track; unsigned int ts_length, flags; VC_CONTAINER_ES_TYPE_T type = VC_CONTAINER_ES_TYPE_UNKNOWN; GUID_T stream_type; int64_t offset; ASF_READ_GUID(p_ctx, size, &stream_type, "Stream Type"); ASF_SKIP_GUID(p_ctx, size, "Error Correction Type"); /* The time_offset field is in 100nS units. Scale back to uS */ module->time_offset = ASF_READ_U64(p_ctx, size, "Time Offset") / UINT64_C(10); ts_length = ASF_READ_U32(p_ctx, size, "Type-Specific Data Length"); ASF_SKIP_U32(p_ctx, size, "Error Correction Data Length"); flags = ASF_READ_U16(p_ctx, size, "Flags"); ASF_SKIP_U32(p_ctx, size, "Reserved"); CHECK_POINT(p_ctx, size); /* Zero is not a valid stream id */ if(!(flags & 0x7F)) goto skip; if(!memcmp(&stream_type, &asf_guid_stream_type_video, sizeof(GUID_T))) type = VC_CONTAINER_ES_TYPE_VIDEO; else if(!memcmp(&stream_type, &asf_guid_stream_type_audio, sizeof(GUID_T))) type = VC_CONTAINER_ES_TYPE_AUDIO; /* Check we know what to do with this track */ if(type == VC_CONTAINER_ES_TYPE_UNKNOWN) goto skip; /* Sanity check sizes */ if(ts_length > size) return VC_CONTAINER_ERROR_CORRUPTED; p_track = asf_reader_find_track( p_ctx, flags, true); if(!p_track) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; p_track->format->es_type = type; offset = STREAM_POSITION(p_ctx); if(type == VC_CONTAINER_ES_TYPE_AUDIO) status = asf_read_waveformatex(p_ctx, p_track, (int64_t)ts_length); else if(type == VC_CONTAINER_ES_TYPE_VIDEO) status = asf_read_bitmapinfoheader(p_ctx, p_track, (int64_t)ts_length); size -= STREAM_POSITION(p_ctx) - offset; if(status) return status; p_track->priv->module->b_valid = true; p_track->is_enabled = true; p_track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; /* Codec specific work-arounds */ switch(p_track->format->codec) { case VC_CONTAINER_CODEC_MPGA: /* Can't guarantee that the data is framed */ p_track->format->flags &= ~VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; break; default: break; } skip: if(size) SKIP_BYTES(p_ctx, size); return STREAM_STATUS(p_ctx); } /** Reads an ASF extended stream properties object */ static VC_CONTAINER_STATUS_T asf_read_object_ext_stream_properties( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_TRACK_T *p_track; unsigned int i, name_count, pes_count, length, stream_id; ASF_SKIP_U64(p_ctx, size, "Start Time"); ASF_SKIP_U64(p_ctx, size, "End Time"); ASF_SKIP_U32(p_ctx, size, "Data Bitrate"); ASF_SKIP_U32(p_ctx, size, "Buffer Size"); ASF_SKIP_U32(p_ctx, size, "Initial Buffer Fullness"); ASF_SKIP_U32(p_ctx, size, "Alternate Data Bitrate"); ASF_SKIP_U32(p_ctx, size, "Alternate Buffer Size"); ASF_SKIP_U32(p_ctx, size, "Alternate Initial Buffer Fullness"); ASF_SKIP_U32(p_ctx, size, "Maximum Object Size"); ASF_SKIP_U32(p_ctx, size, "Flags"); stream_id = ASF_READ_U16(p_ctx, size, "Stream Number"); ASF_SKIP_U16(p_ctx, size, "Stream Language ID Index"); ASF_SKIP_U64(p_ctx, size, "Average Time Per Frame"); name_count = ASF_READ_U16(p_ctx, size, "Stream Name Count"); pes_count = ASF_READ_U16(p_ctx, size, "Payload Extension System Count"); CHECK_POINT(p_ctx, size); p_track = asf_reader_find_track( p_ctx, stream_id, true); if(!p_track) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; /* Stream Names */ for(i = 0; i < name_count; i++) { if(size < 4) return VC_CONTAINER_ERROR_CORRUPTED; ASF_SKIP_U16(p_ctx, size, "Language ID Index"); length = ASF_READ_U16(p_ctx, size, "Stream Name Length"); if(size < length) return VC_CONTAINER_ERROR_CORRUPTED; ASF_SKIP_BYTES(p_ctx, size, length); /* Stream Name */ } CHECK_POINT(p_ctx, size); /* Payload Extension Systems */ for(i = 0; i < pes_count; i++) { if(size < 22) return VC_CONTAINER_ERROR_CORRUPTED; ASF_SKIP_GUID(p_ctx, size, "Extension System ID"); ASF_SKIP_U16(p_ctx, size, "Extension Data Size"); length = ASF_READ_U32(p_ctx, size, "Extension System Info Length"); if(size < length) return VC_CONTAINER_ERROR_CORRUPTED; ASF_SKIP_BYTES(p_ctx, size, length); /* Extension System Info */ } CHECK_POINT(p_ctx, size); /* Optional Stream Properties Object */ if(size >= ASF_OBJECT_HEADER_SIZE) status = asf_read_object(p_ctx, size); return status; } /** Reads an ASF simple index object */ static VC_CONTAINER_STATUS_T asf_read_object_simple_index( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = 0; uint64_t time_interval, index_duration; uint32_t count; unsigned int i; ASF_SKIP_GUID(p_ctx, size, "File ID"); /* time in 100nS units, converted to uS */ time_interval = ASF_READ_U64(p_ctx, size, "Index Entry Time Interval") / UINT64_C(10); ASF_SKIP_U32(p_ctx, size, "Maximum Packet Count"); count = ASF_READ_U32(p_ctx, size, "Index Entries Count"); CHECK_POINT(p_ctx, size); if(count > size / 6) { LOG_DEBUG(p_ctx, "invalid number of entries in the index (%i, %"PRIi64")", count, size / 6); count = (uint32_t)(size / 6); } /* Find the track corresponding to this index */ for(i = 0; i < p_ctx->tracks_num; i++) { if(p_ctx->tracks[i]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) continue; if(p_ctx->tracks[i]->priv->module->simple_index.offset) continue; break; } /* Skip the index if we can't find the associated track */ if(i == p_ctx->tracks_num || !count || !time_interval) return VC_CONTAINER_SUCCESS; track_module = p_ctx->tracks[i]->priv->module; track_module->simple_index.offset = STREAM_POSITION(p_ctx); track_module->simple_index.time_interval = time_interval; track_module->simple_index.num_entries = count; /* Check that the index covers the whole duration of the stream */ index_duration = (count * time_interval); if(module->preroll + module->time_offset < index_duration) index_duration -= module->preroll + module->time_offset; else index_duration = 0; if((uint64_t)module->duration > index_duration + time_interval) { track_module->simple_index.incomplete = true; } LOG_DEBUG(p_ctx, "index covers %fS on %fS", (float)index_duration / 1E6, (float)module->duration / 1E6); #if defined(ENABLE_CONTAINERS_LOG_FORMAT) && defined(ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE) for(i = 0; i < count; i++) { LOG_FORMAT(p_ctx, "Entry: %u", i); ASF_SKIP_U32(p_ctx, size, "Packet Number"); ASF_SKIP_U16(p_ctx, size, "Packet Count"); if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) break; } size = i * 6; #else size = CACHE_BYTES(p_ctx, count * 6); #endif /* Check that the index is complete */ if(size / 6 != count ) { LOG_DEBUG(p_ctx, "index is incomplete (%i entries on %i)", (int)size / 6, count); track_module->simple_index.num_entries = (uint32_t)(size / 6); track_module->simple_index.incomplete = true; } /* If we haven't had an index before, or this track is enabled, we'll store this one. * (Usually there will only be one video track, and it will be enabled, so both tests * will pass. This check is an attempt to handle content not structured as it should be) */ if ((!module->simple_index_track) || (p_ctx->tracks[i]->is_enabled)) { /* Save the track so we don't have to look for it in when seeking */ module->simple_index_track = track_module; } return STREAM_STATUS(p_ctx); } /** Reads an ASF index object */ static VC_CONTAINER_STATUS_T asf_read_object_index( VC_CONTAINER_T *p_ctx, int64_t size ) { uint32_t i, specifiers_count, blocks_count; uint32_t best_specifier_type[ASF_TRACKS_MAX] = {0}; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; /* Read the time interval and scale to microseconds */ module->top_level_index.entry_time_interval = (uint64_t)ASF_READ_U32(p_ctx, size, "Index Entry Time Interval") * INT64_C(1000); module->top_level_index.specifiers_count = specifiers_count = (uint32_t)ASF_READ_U16(p_ctx, size, "Index Specifiers Count"); module->top_level_index.block_count = blocks_count = ASF_READ_U32(p_ctx, size, "Index Blocks Count"); CHECK_POINT(p_ctx, size); /* Index specifiers. Search for the one we like best */ if(size < specifiers_count * 4) return VC_CONTAINER_ERROR_CORRUPTED; for(i = 0; i < specifiers_count; i++) { uint32_t stream_id = (uint32_t)ASF_READ_U16(p_ctx, size, "Stream Number"); uint32_t index_type = (uint32_t)ASF_READ_U16(p_ctx, size, "Index Type"); /* Find the track index for this stream */ unsigned track = module->stream_number_to_index[stream_id]; if ((track < ASF_TRACKS_MAX) && (index_type > best_specifier_type[track])) { /* We like this better than any we have seen before. Note - if we don't like any * the file must be subtly corrupt - best we say nothing, and attempt a seek with * the data for the first specifier, it will be better than nothing. At worst it * will play until a seek is attempted */ module->top_level_index.active_specifiers[track] = i; best_specifier_type[track] = index_type; } } for (i = 0; i < p_ctx->tracks_num; i++) { LOG_DEBUG(p_ctx, "indexing track %"PRIu32" with specifier %"PRIu32, i, module->top_level_index.active_specifiers[i]); } CHECK_POINT(p_ctx, size); /* The blocks start here */ module->top_level_index.blocks_offset = STREAM_POSITION(p_ctx); /* Index blocks */ #if !(defined(ENABLE_CONTAINERS_LOG_FORMAT) && defined(ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE)) blocks_count = 0; /* Don't log the index. Note we'll get a warning on unprocessed data */ #endif /* coverity[dead_error_condition] Code needs to stay there for debugging purposes */ for(i = 0; i < blocks_count; i++) { uint32_t j, k, count = ASF_READ_U32(p_ctx, size, "Index Entry Count"); for(j = 0; j < specifiers_count; j++) { ASF_SKIP_U64(p_ctx, size, "Block Positions"); } for(j = 0; j < count; j++) { for(k = 0; k < specifiers_count; k++) { ASF_SKIP_U32(p_ctx, size, "Offsets"); } } } return STREAM_STATUS(p_ctx); } /** Reads an ASF index parameters object */ static VC_CONTAINER_STATUS_T asf_read_object_index_parameters( VC_CONTAINER_T *p_ctx, int64_t size ) { #ifdef ENABLE_CONTAINERS_LOG_FORMAT /* This is added for debugging only. The spec (section 4.9) states that this is also enclosed in * the index object (see above) and that they are identical. I think they aren't always... */ uint32_t i, specifiers_count; /* Read the time interval in milliseconds */ ASF_SKIP_U32(p_ctx, size, "Index Entry Time Interval"); specifiers_count = (uint32_t)ASF_READ_U16(p_ctx, size, "Index Specifiers Count"); CHECK_POINT(p_ctx, size); /* Index specifiers. Search for the one we like best */ if(size < specifiers_count * 4) return VC_CONTAINER_ERROR_CORRUPTED; for(i = 0; i < specifiers_count; i++) { ASF_SKIP_U16(p_ctx, size, "Stream Number"); ASF_SKIP_U16(p_ctx, size, "Index Type"); } #endif CHECK_POINT(p_ctx, size); return STREAM_STATUS(p_ctx); } /** Reads an ASF codec list object */ static VC_CONTAINER_STATUS_T asf_read_object_codec_list( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; uint32_t i, count, length; ASF_SKIP_GUID(p_ctx, size, "Reserved"); count = ASF_READ_U32(p_ctx, size, "Codec Entries Count"); CHECK_POINT(p_ctx, size); /* Codec entries */ for(i = 0; i < count; i++) { ASF_SKIP_U16(p_ctx, size, "Type"); length = ASF_READ_U16(p_ctx, size, "Codec Name Length"); if(size < length) return VC_CONTAINER_ERROR_CORRUPTED; ASF_SKIP_STRING(p_ctx, size, length * 2, "Codec Name"); length = ASF_READ_U16(p_ctx, size, "Codec Description Length"); if(size < length) return VC_CONTAINER_ERROR_CORRUPTED; ASF_SKIP_STRING(p_ctx, size, length * 2, "Codec Description"); length = ASF_READ_U16(p_ctx, size, "Codec Information Length"); if(size < length) return VC_CONTAINER_ERROR_CORRUPTED; ASF_SKIP_BYTES(p_ctx, size, length); CHECK_POINT(p_ctx, size); } return status; } /** Reads an ASF content description object */ static VC_CONTAINER_STATUS_T asf_read_object_content_description( VC_CONTAINER_T *p_ctx, int64_t size ) { uint16_t t_length, a_length, c_length, d_length, r_length; t_length = ASF_READ_U16(p_ctx, size, "Title Length"); a_length = ASF_READ_U16(p_ctx, size, "Author Length"); c_length = ASF_READ_U16(p_ctx, size, "Copyright Length"); d_length = ASF_READ_U16(p_ctx, size, "Description Length"); r_length = ASF_READ_U16(p_ctx, size, "Rating Length"); CHECK_POINT(p_ctx, size); if(size < t_length) return VC_CONTAINER_ERROR_CORRUPTED; ASF_SKIP_STRING(p_ctx, size, t_length, "Title"); if(size < a_length) return VC_CONTAINER_ERROR_CORRUPTED; ASF_SKIP_STRING(p_ctx, size, a_length, "Author"); if(size < c_length) return VC_CONTAINER_ERROR_CORRUPTED; ASF_SKIP_STRING(p_ctx, size, c_length, "Copyright"); if(size < d_length) return VC_CONTAINER_ERROR_CORRUPTED; ASF_SKIP_STRING(p_ctx, size, d_length, "Description"); if(size < r_length) return VC_CONTAINER_ERROR_CORRUPTED; ASF_SKIP_STRING(p_ctx, size, r_length, "Rating"); return STREAM_STATUS(p_ctx); } /** Reads an ASF stream bitrate properties object */ static VC_CONTAINER_STATUS_T asf_read_object_stream_bitrate_props( VC_CONTAINER_T *p_ctx, int64_t size ) { uint16_t i, count; count = ASF_READ_U16(p_ctx, size, "Bitrate Records Count"); /* Bitrate records */ if(size < count * 6) return VC_CONTAINER_ERROR_CORRUPTED; for(i = 0; i < count; i++) { ASF_SKIP_U16(p_ctx, size, "Flags"); ASF_SKIP_U32(p_ctx, size, "Average Bitrate"); } return STREAM_STATUS(p_ctx); } /** Reads an ASF content encryption object */ static VC_CONTAINER_STATUS_T asf_read_object_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size ) { uint32_t length; length = ASF_READ_U32(p_ctx, size, "Secret Data Length"); ASF_SKIP_BYTES(p_ctx, size, length); length = ASF_READ_U32(p_ctx, size, "Protection Type Length"); ASF_SKIP_BYTES(p_ctx, size, length); length = ASF_READ_U32(p_ctx, size, "Key ID Length"); ASF_SKIP_BYTES(p_ctx, size, length); length = ASF_READ_U32(p_ctx, size, "License URL Length"); ASF_SKIP_BYTES(p_ctx, size, length); /* null-terminated ASCII string */ return STREAM_STATUS(p_ctx); } /** Reads an ASF extended content encryption object */ static VC_CONTAINER_STATUS_T asf_read_object_ext_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size ) { uint32_t length; length = ASF_READ_U32(p_ctx, size, "Data Size"); ASF_SKIP_BYTES(p_ctx, size, length); return STREAM_STATUS(p_ctx); } /** Reads an ASF advanced content encryption object */ static VC_CONTAINER_STATUS_T asf_read_object_adv_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size ) { uint32_t i, count; count = ASF_READ_U16(p_ctx, size, "Content Encryption Records Count"); for(i = 0; i < count; i++) { uint32_t j, rec_count, data_size, length; ASF_SKIP_GUID(p_ctx, size, "System ID"); ASF_SKIP_U32(p_ctx, size, "System Version"); rec_count = ASF_READ_U16(p_ctx, size, "Encrypted Object Record Count"); CHECK_POINT(p_ctx, size); for(j = 0; j < rec_count; j++) { ASF_SKIP_U16(p_ctx, size, "Encrypted Object ID Type"); length = ASF_READ_U16(p_ctx, size, "Encrypted Object ID Length"); if(length > size) return VC_CONTAINER_ERROR_CORRUPTED; ASF_SKIP_BYTES(p_ctx, size, length); CHECK_POINT(p_ctx, size); } data_size = ASF_READ_U32(p_ctx, size, "Data Size"); if(data_size > size) return VC_CONTAINER_ERROR_CORRUPTED; ASF_SKIP_BYTES(p_ctx, size, data_size); CHECK_POINT(p_ctx, size); } return STREAM_STATUS(p_ctx); } /** Skips over an object that is if a type we don't handle, or is nested too deep */ static VC_CONTAINER_STATUS_T asf_skip_unprocessed_object( VC_CONTAINER_T *p_ctx, int64_t size ) { LOG_DEBUG(p_ctx, "%"PRIi64" bytes ignored in unhandled object", size); if(size < ASF_MAX_OBJECT_SIZE) SKIP_BYTES(p_ctx, size); /* read a small amount */ else SEEK(p_ctx, STREAM_POSITION(p_ctx) + size); /* seek a large distance */ return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T asf_find_packet_header( VC_CONTAINER_T *p_ctx, ASF_PACKET_STATE *p_state ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; unsigned int search_size = 64*1024; /* should be max packet size according to spec */ #ifdef ENABLE_CONTAINER_LOG_DEBUG uint64_t offset = STREAM_POSITION(p_ctx); #endif uint8_t h[3]; VC_CONTAINER_PARAM_UNUSED(p_state); /* Limit the search up to what should theoretically be the packet boundary */ if(module->packet_size) search_size = module->packet_size - (STREAM_POSITION(p_ctx) - module->data_offset) % module->packet_size; for(; search_size > sizeof(h); search_size--) { if(PEEK_BYTES(p_ctx, h, sizeof(h)) != sizeof(h)) return STREAM_STATUS(p_ctx); if(!h[0] && !h[1] && h[2] == 0x82) { search_size = 2; break; /* Got it */ } SKIP_BYTES(p_ctx, 1); } /* If we failed, we just skip to the theoretical packet boundary */ SKIP_BYTES(p_ctx, search_size); LOG_DEBUG(p_ctx, "found potential sync, discarded %"PRIu64" bytes)", STREAM_POSITION(p_ctx) - offset); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T asf_read_packet_header( VC_CONTAINER_T *p_ctx, ASF_PACKET_STATE *p_state, uint64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; uint64_t offset = STREAM_POSITION(p_ctx); uint8_t flags, property_flags, length; VC_CONTAINER_PARAM_UNUSED(size); p_state->start = offset; LOG_FORMAT(p_ctx, "Packet Offset: %"PRIu64, offset); if ((module->data_size > 0) && (offset >= (module->data_size + module->data_offset))) { return VC_CONTAINER_ERROR_EOS; } /* Find out whether we are dealing with error correction data or payload parsing info */ if( PEEK_U8(p_ctx) >> 7 ) { /* We have error correction data */ flags = READ_U8(p_ctx, "Error Correction Flags"); length = flags & 0xF; SKIP_BYTES(p_ctx, length); /* Error correction data */ } /* Payload parsing information */ flags = READ_U8(p_ctx, "Length Type Flags"); p_state->multiple_payloads = flags & 1; property_flags = READ_U8(p_ctx, "Property Flags"); p_state->replicated_data_lt = (property_flags >> 0) & 0x3; p_state->offset_into_media_object_lt = (property_flags >> 2) & 0x3; p_state->media_object_number_lt = (property_flags >> 4) & 0x3; /* Sanity check stream number length type */ if(((property_flags >> 6) & 0x3) != 1) goto error; /* If there's no packet size field we default to the size in the file header. */ p_state->size = READ_VLC(p_ctx, (flags >> 5) & 0x3 /* Packet length type */, module->packet_size, "Packet Length"); READ_VLC(p_ctx, (flags>>1)&0x3 /* Sequence type */, 0, "Sequence"); p_state->padding_size = READ_VLC(p_ctx, (flags>>3)&0x3 /* Padding length type */, 0, "Padding Length"); p_state->send_time = READ_U32(p_ctx, "Send Time") * UINT64_C(1000); /* Read in millisecond units, stored in uS */ SKIP_U16(p_ctx, "Duration"); /* in milliseconds */ p_state->num_payloads = 1; p_state->current_payload = 0; if(p_state->multiple_payloads) { LOG_FORMAT(p_ctx, "Multiple Payloads"); flags = READ_U8(p_ctx, "Payload Flags"); p_state->num_payloads = flags & 0x3F; LOG_FORMAT(p_ctx, "Number of Payloads: %i", p_state->num_payloads); p_state->payload_lt = (flags >> 6) & 3; /* Sanity check */ if(!p_state->num_payloads) goto error; } /* Update the current offset in the packet. */ p_state->current_offset = STREAM_POSITION(p_ctx) - offset; /* Sanity check offset */ if(p_state->current_offset > p_state->size) goto error; /* Sanity check padding size */ if(p_state->padding_size + p_state->current_offset > p_state->size) goto error; /* Sanity check packet size */ if(!module->broadcast && (p_state->size != module->packet_size)) goto error; return STREAM_STATUS(p_ctx); error: LOG_FORMAT(p_ctx, "Invalid payload parsing information (offset %"PRIu64")", STREAM_POSITION(p_ctx)); return STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS ? VC_CONTAINER_ERROR_CORRUPTED : STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T asf_read_payload_header( VC_CONTAINER_T *p_ctx, ASF_PACKET_STATE *p_state /* uint64_t size */ ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; uint32_t rep_data_length; if(p_state->current_payload >= p_state->num_payloads) return VC_CONTAINER_ERROR_CORRUPTED; p_state->stream_num = READ_U8(p_ctx, "Stream Number"); if(!(p_state->stream_num & 0x7F)) return VC_CONTAINER_ERROR_CORRUPTED; p_state->media_object_num = READ_VLC(p_ctx, p_state->media_object_number_lt, 0, "Media Object Number"); /* For a compressed packet this field is a timestamp, and is moved to p_state->media_object_pts later */ p_state->media_object_off = READ_VLC(p_ctx, p_state->offset_into_media_object_lt, 0, "Offset Into Media Object"); rep_data_length = READ_VLC(p_ctx, p_state->replicated_data_lt, 0, "Replicated Data Length"); /* Sanity check the replicated data length */ if(rep_data_length && rep_data_length != 1 && (rep_data_length < 8 || STREAM_POSITION(p_ctx) - p_state->start + p_state->padding_size + rep_data_length > p_state->size)) { LOG_FORMAT(p_ctx, "invalid replicated data length"); return VC_CONTAINER_ERROR_CORRUPTED; } /* Read what we need from the replicated data */ if(rep_data_length > 1) { p_state->media_object_size = READ_U32(p_ctx, "Media Object Size"); p_state->media_object_pts = READ_U32(p_ctx, "Presentation Time") * UINT64_C(1000); p_state->compressed_payloads = 0; SKIP_BYTES(p_ctx, rep_data_length - 8); /* Rest of replicated data */ } else if(rep_data_length == 1) { LOG_FORMAT(p_ctx, "Compressed Payload Data"); p_state->media_object_pts_delta = READ_U8(p_ctx, "Presentation Time Delta") * UINT64_C(1000); p_state->compressed_payloads = 1; /* Move the pts from media_object_off where it was read, and adjust it */ p_state->media_object_off *= UINT64_C(1000); p_state->media_object_pts = p_state->media_object_off - p_state->media_object_pts_delta; p_state->media_object_off = 0; p_state->media_object_size = 0; } else { p_state->media_object_size = 0; p_state->media_object_pts = p_state->send_time; p_state->compressed_payloads = 0; } if(p_state->media_object_pts > module->preroll + module->time_offset) p_state->media_object_pts -= (module->preroll + module->time_offset); else p_state->media_object_pts = 0; p_state->payload_size = p_state->size - p_state->padding_size - (STREAM_POSITION(p_ctx) - p_state->start); if(p_state->multiple_payloads) { p_state->payload_size = READ_VLC(p_ctx, p_state->payload_lt, 0, "Payload Length"); if(!p_state->payload_size) return VC_CONTAINER_ERROR_CORRUPTED; } else LOG_FORMAT(p_ctx, "Payload Length: %i", p_state->payload_size); if(p_state->payload_size >= p_state->size) return VC_CONTAINER_ERROR_CORRUPTED; p_state->subpayload_size = p_state->payload_size; /* Update current_offset to reflect the variable number of bytes we just read */ p_state->current_offset = STREAM_POSITION(p_ctx) - p_state->start; /* Sanity check offset */ if(p_state->current_offset > p_state->size) return VC_CONTAINER_ERROR_CORRUPTED; return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T asf_read_object_data( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; unsigned int i; VC_CONTAINER_PARAM_UNUSED(size); SKIP_GUID(p_ctx, "File ID"); SKIP_U64(p_ctx, "Total Data Packets"); SKIP_U16(p_ctx, "Reserved"); module->data_offset = STREAM_POSITION(p_ctx); /* Initialise state for all tracks */ module->packet_state.start = module->data_offset; for(i = 0; i < p_ctx->tracks_num; i++) { VC_CONTAINER_TRACK_T *p_track = p_ctx->tracks[i]; p_track->priv->module->p_packet_state = &module->packet_state; } return STREAM_STATUS(p_ctx); } /*****************************************************************************/ /* Read the next sub-payload or next payload */ static VC_CONTAINER_STATUS_T asf_read_next_payload_header( VC_CONTAINER_T *p_ctx, ASF_PACKET_STATE *p_state, uint32_t *pi_track, uint32_t *pi_length) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status; if(p_state->subpayload_size) { /* We still haven't read the current subpayload, return the info we already have */ goto end; } /* Check if we're done reading a packet */ if(p_state->current_payload >= p_state->num_payloads) { /* Skip the padding at the end */ if(p_state->size) { int32_t pad_length = p_state->size - (STREAM_POSITION(p_ctx) - p_state->start); if(pad_length < 0) return VC_CONTAINER_ERROR_CORRUPTED; SKIP_BYTES(p_ctx, pad_length); /* Padding */ } /* Read the header for the next packet */ module->object_level = 0; /* For debugging */ status = asf_read_packet_header( p_ctx, p_state, (uint64_t)0/*size???*/ ); module->object_level = 1; /* For debugging */ if(status != VC_CONTAINER_SUCCESS) return status; } /* Check if we're done reading a payload */ if(!p_state->payload_size) { /* Read the payload header */ status = asf_read_payload_header( p_ctx, p_state ); if(status != VC_CONTAINER_SUCCESS) return status; } /* For compressed payloads, payload_size != subpayload_size */ if(p_state->compressed_payloads && p_state->payload_size) { p_state->payload_size--; p_state->subpayload_size = READ_U8(p_ctx, "Sub-Payload Data Length"); if(p_state->subpayload_size > p_state->payload_size) { /* TODO: do something ? */ LOG_DEBUG(p_ctx, "subpayload is too big"); p_state->subpayload_size = p_state->payload_size; } p_state->media_object_off = 0; p_state->media_object_size = p_state->subpayload_size; p_state->media_object_pts += p_state->media_object_pts_delta; } end: /* We've read the payload header, return the requested info */ if(pi_track) *pi_track = module->stream_number_to_index[p_state->stream_num & 0x7F]; if(pi_length) *pi_length = p_state->subpayload_size; p_state->current_offset = STREAM_POSITION(p_ctx) - p_state->start; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ /* read the next payload (not sub-payload) */ static VC_CONTAINER_STATUS_T asf_read_next_payload( VC_CONTAINER_T *p_ctx, ASF_PACKET_STATE *p_state, uint8_t *p_data, uint32_t *pi_size ) { uint32_t subpayload_size = p_state->subpayload_size; if(p_data && *pi_size < subpayload_size) subpayload_size = *pi_size; if(!p_state->subpayload_size) return VC_CONTAINER_SUCCESS; p_state->payload_size -= subpayload_size; if(!p_state->payload_size) p_state->current_payload++; p_state->subpayload_size -= subpayload_size; p_state->media_object_off += subpayload_size; if(p_data) *pi_size = READ_BYTES(p_ctx, p_data, subpayload_size); else *pi_size = SKIP_BYTES(p_ctx, subpayload_size); p_state->current_offset += subpayload_size; if(*pi_size!= subpayload_size) return STREAM_STATUS(p_ctx); return VC_CONTAINER_SUCCESS; } /****************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ /*****************************************************************************/ /** Read data from the ASF file @param p_ctx Context for the file being read from @param p_packet Packet information. Includes data buffer and stream ID as aprropriate. @param flags Flags controlling the read. May request reading only, skipping a packet or force access to a set track. ******************************************************************************/ static VC_CONTAINER_STATUS_T asf_reader_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) { VC_CONTAINER_MODULE_T *global_module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; ASF_PACKET_STATE *p_state; uint32_t buffer_size = 0, track, data_size; uint64_t state_pos; LOG_DEBUG(p_ctx, "asf_reader_read track %"PRIu32" flags %u", p_packet->track, flags); /* If a specific track has been selected, we need to use the track packet state */ if(flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) { vc_container_assert(p_packet->track < p_ctx->tracks_num); /* The state to use is the one referred to by the track we selected */ p_state = p_ctx->tracks[p_packet->track]->priv->module->p_packet_state; } else { /* No specific track was selected. Read the next data from the global position. */ p_state = &global_module->packet_state; } /* Stop if the stream can't be read */ if(p_state->eos) return VC_CONTAINER_ERROR_EOS; if(p_state->corrupted) return VC_CONTAINER_ERROR_CORRUPTED; /* If we aren't in the right position in the file go there now. */ state_pos = p_state->start + p_state->current_offset; if ((uint64_t)STREAM_POSITION(p_ctx) != state_pos) { LOG_DEBUG(p_ctx, "seeking from %"PRIu64" to %"PRIu64, STREAM_POSITION(p_ctx), state_pos); SEEK(p_ctx, state_pos); } /* Look at the next payload header */ status = asf_read_next_payload_header( p_ctx, p_state, &track, &data_size ); if((status == VC_CONTAINER_ERROR_CORRUPTED) && (p_state->bad_packets < ASF_MAX_CONSECUTIVE_CORRUPTED_PACKETS)) { /* If the current packet is corrupted we will try to search for the next packet */ uint32_t corrupted = p_state->bad_packets; LOG_DEBUG(p_ctx, "packet offset %"PRIi64" is corrupted", p_state->start); memset(p_state, 0, sizeof(*p_state)); p_state->bad_packets = corrupted + 1; /* TODO: flag discontinuity */ if(asf_find_packet_header(p_ctx, p_state) == VC_CONTAINER_SUCCESS) { p_state->start = STREAM_POSITION(p_ctx); return VC_CONTAINER_ERROR_CONTINUE; } } if(status == VC_CONTAINER_ERROR_EOS) p_state->eos = true; if(status == VC_CONTAINER_ERROR_CORRUPTED) p_state->corrupted = true; if(status != VC_CONTAINER_SUCCESS) { return status; } p_state->bad_packets = 0; /* bad track number or track is disabled */ if(track >= p_ctx->tracks_num || !p_ctx->tracks[track]->is_enabled) { LOG_DEBUG(p_ctx, "skipping packet because track %u is invalid or disabled", track); /* Skip payload by reading with a null buffer */ status = asf_read_next_payload(p_ctx, p_state, 0, &data_size ); if(status != VC_CONTAINER_SUCCESS) return status; return VC_CONTAINER_ERROR_CONTINUE; } track_module = p_ctx->tracks[track]->priv->module; /* If we are reading from the global state, and the track we found is not on the global state, * either skip the data or reconnect it to the global state */ if ((p_state == &global_module->packet_state) && (track_module->p_packet_state != &global_module->packet_state)) { uint64_t track_pos = track_module->p_packet_state->start + track_module->p_packet_state->current_offset; /* Check if the end of the current packet is beyond the track's position */ if (track_pos > state_pos + track_module->p_packet_state->size) { LOG_DEBUG(p_ctx, "skipping packet from track %u as it has already been read", track); status = asf_read_next_payload(p_ctx, p_state, 0, &data_size); if(status != VC_CONTAINER_SUCCESS) return status; return VC_CONTAINER_ERROR_CONTINUE; } else { LOG_DEBUG(p_ctx, "switching track index %u location %"PRIu64" back to global state", track, track_pos); track_module->p_packet_state = &global_module->packet_state; /* Update the global state to the precise position */ global_module->packet_state = track_module->local_packet_state; return VC_CONTAINER_ERROR_CONTINUE; } } /* If we are forcing, and the data is from a different track, skip it. * We may need to move the track we want onto a local state. */ if ((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) && (track != p_packet->track)) { track_module = p_ctx->tracks[p_packet->track]->priv->module; /* If the track we found is on the same state as the track we want they must both be on the global state */ if (p_ctx->tracks[track]->priv->module->p_packet_state == p_state) { LOG_DEBUG(p_ctx, "switching track index %u location %"PRIu64" away from global state", p_packet->track, state_pos); /* Change the track we want onto a local state */ track_module->p_packet_state = &track_module->local_packet_state; /* Copy the global state into the local state for the track we are forcing */ track_module->local_packet_state = global_module->packet_state; } LOG_DEBUG(p_ctx, "skipping packet from track %u while forcing %u", track, p_packet->track); status = asf_read_next_payload(p_ctx, track_module->p_packet_state, 0, &data_size ); return VC_CONTAINER_ERROR_CONTINUE; } /* If we arrive here either the data is from the track we are forcing, or we are not forcing * and we haven't already read the data while forcing that track */ /* If skip, and no info required, skip over it and return now. */ if((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO)) return asf_read_next_payload(p_ctx, p_state, 0, &data_size ); /* Fill-in the packet information */ if(p_state->media_object_pts == ASF_UNKNOWN_PTS || p_state->media_object_off) p_packet->dts = p_packet->pts = VC_CONTAINER_TIME_UNKNOWN; else p_packet->dts = p_packet->pts = p_state->media_object_pts; p_packet->flags = 0; if(p_state->stream_num >> 7) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; if(!p_state->media_object_off) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; if(p_state->media_object_size && p_state->media_object_off + data_size >= p_state->media_object_size) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; if(!p_state->media_object_size) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; p_packet->track = track; p_packet->frame_size = p_state->media_object_size; p_packet->size = data_size; /* If the skip flag is set (Info must have been too) skip the data and return */ if(flags & VC_CONTAINER_READ_FLAG_SKIP) { /* Skip payload by reading with a null buffer */ return asf_read_next_payload(p_ctx, p_state, 0, &data_size ); } else if(flags & VC_CONTAINER_READ_FLAG_INFO) { return VC_CONTAINER_SUCCESS; } /* Read the payload data */ buffer_size = p_packet->buffer_size; status = asf_read_next_payload(p_ctx, p_state, p_packet->data, &buffer_size ); if(status != VC_CONTAINER_SUCCESS) { /* FIXME */ return status; } p_packet->size = buffer_size; if(buffer_size != data_size) p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; LOG_DEBUG(p_ctx, "asf_reader_read exit %u PTS %"PRIi64" track %"PRIu32, status, p_packet->pts, track); return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T asf_reader_index_find_time( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_MODULE_T* track_module, int64_t time, uint32_t *packet_num, bool forward ) { VC_CONTAINER_STATUS_T status; uint32_t entry, previous_packet_num; bool eos = false; /* Default to beginning of file in case of error */ *packet_num = 0; /* Special case - time zero is beginning of file */ if(time == 0) {return VC_CONTAINER_SUCCESS;} /* Sanity checking */ if(!track_module->simple_index.num_entries) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; if(!track_module->simple_index.time_interval) return VC_CONTAINER_ERROR_CORRUPTED; entry = time / track_module->simple_index.time_interval; LOG_DEBUG(p_ctx, "entry: %i, offset: %"PRIi64", interv: %"PRIi64, entry, track_module->simple_index.offset, track_module->simple_index.time_interval); if(entry >= track_module->simple_index.num_entries) { entry = track_module->simple_index.num_entries - 1; eos = true; } /* Fetch the entry from the index */ status = SEEK(p_ctx, track_module->simple_index.offset + 6 * entry); if(status != VC_CONTAINER_SUCCESS) return status; *packet_num = READ_U32(p_ctx, "Packet Number"); if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) return STREAM_STATUS(p_ctx); /* When asking for the following keyframe we need to find the next entry with a greater * packet number */ previous_packet_num = *packet_num; while(!eos && forward && previous_packet_num == *packet_num) { if(++entry == track_module->simple_index.num_entries) {eos = true; break;} status = SEEK(p_ctx, track_module->simple_index.offset + 6 * entry); if(status != VC_CONTAINER_SUCCESS) break; *packet_num = READ_U32(p_ctx, "Packet Number"); if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) break; } if(eos && track_module->simple_index.incomplete) return VC_CONTAINER_ERROR_INCOMPLETE_DATA; else if(eos) return VC_CONTAINER_ERROR_EOS; else return STREAM_STATUS(p_ctx); } #if 0 /*****************************************************************************/ static VC_CONTAINER_STATUS_T asf_reader_index_find_packet( VC_CONTAINER_T *p_ctx, unsigned int track, uint32_t *packet_num, bool forward ) { VC_CONTAINER_STATUS_T status; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = 0; uint32_t i, prev_packet_num = 0, next_packet_num; bool eos = false; /* Sanity checking */ if(track >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_FAILED; track_module = p_ctx->tracks[track]->priv->module; if(!track_module->num_index_entries) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; if(!track_module->index_time_interval) return VC_CONTAINER_ERROR_CORRUPTED; status = SEEK(p_ctx, track_module->index_offset); if(status != VC_CONTAINER_SUCCESS) return status; /* Loop through all the entries in the index */ for(i = 0; i < track_module->num_index_entries; i++) { next_packet_num = READ_U32(p_ctx, "Packet Number"); SKIP_U16(p_ctx, "Packet Count"); if(next_packet_num > *packet_num) break; if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) break; prev_packet_num = next_packet_num; } if(i == track_module->num_index_entries ) eos = true; if(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS && !eos) { if(forward) *packet_num = next_packet_num; else *packet_num = prev_packet_num; } if(eos && track_module->index_incomplete) return VC_CONTAINER_ERROR_INCOMPLETE_DATA; else if(eos) return VC_CONTAINER_ERROR_EOS; else return STREAM_STATUS(p_ctx); } #endif /*****************************************************************************/ static VC_CONTAINER_STATUS_T asf_reader_find_next_frame( VC_CONTAINER_T *p_ctx, unsigned int track, ASF_PACKET_STATE *p_state, bool keyframe, bool timeout ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; uint32_t data_track, data_size; unsigned int packets = 0; if(p_ctx->tracks[track]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) keyframe = false; /* We still need to go to the right payload */ while(status == VC_CONTAINER_SUCCESS && (!timeout || packets++ < ASF_MAX_SEARCH_PACKETS)) { status = asf_read_next_payload_header( p_ctx, p_state, &data_track, &data_size ); if(status != VC_CONTAINER_SUCCESS) break; if(data_track == track && ((p_state->stream_num >> 7) || !keyframe) && !p_state->media_object_off) break; /* Skip payload */ status = asf_read_next_payload(p_ctx, p_state, 0, &data_size ); } return status; } /*****************************************************************************/ /* Helper for asf_reader_seek - seek when there is a top-level index (spec section 6.2) */ static VC_CONTAINER_STATUS_T seek_by_top_level_index( VC_CONTAINER_T *p_ctx, int64_t *p_time, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; unsigned index; uint64_t time = 0; uint64_t block_address = module->top_level_index.blocks_offset; uint64_t track_positions[ASF_TRACKS_MAX]; VC_CONTAINER_PARAM_UNUSED(mode); LOG_DEBUG(p_ctx, "seek_by_top_level_index"); for (index = 0; index < ASF_TRACKS_MAX; ++index) { /* Set all to a stupid value */ track_positions[index] = UINT64_MAX; } /* Loop through the index blocks to find the one(s) that deal with the time(s) in question. * Note that most ASF files only have one index block. */ for (index = 0; index < module->top_level_index.block_count; ++index) { uint64_t block_duration, block_position; uint32_t index_entry_count, stream; LOG_DEBUG(p_ctx, "looking for index blocks at offset %"PRIu64, block_address); status = SEEK(p_ctx, block_address); if(status != VC_CONTAINER_SUCCESS) return status; /* Read the number of entries for this index block. */ index_entry_count = READ_U32(p_ctx, "Index Entry Count"); /* Turn into a duration */ block_duration = (uint64_t)index_entry_count * module->top_level_index.entry_time_interval; /* Go through each stream */ for (stream = 0; stream < p_ctx->tracks_num; ++stream) { /* Work out the track's target time */ uint64_t track_time = *p_time + module->preroll + module->time_offset; /* Have we the correct index block for the seek time? */ if ((time <= track_time) && (track_time < time + block_duration)) { /* We have the correct index block for the seek time. Work out where in it. */ uint32_t block_index = (track_time - time) / module->top_level_index.entry_time_interval; uint64_t active_specifier = module->top_level_index.active_specifiers[stream]; uint64_t new_position; /* Read the Block Positions value for the correct specifier */ status = SEEK(p_ctx, block_address + INT64_C(4) + active_specifier * INT64_C(8)); if (status != VC_CONTAINER_SUCCESS) { return status; } block_position = READ_U32(p_ctx, "Block Position"); /* Read the target address for the stream */ status = SEEK(p_ctx, block_address + 4 /* skip index entry count */ + (UINT64_C(8) * module->top_level_index.specifiers_count) /* block positions */ + (UINT64_C(4) * module->top_level_index.specifiers_count * block_index) /* prior index entries */ + (UINT64_C(4) * active_specifier)); /* correct specifier */ LOG_DEBUG(p_ctx, "reading at %"PRIu64, STREAM_POSITION(p_ctx)); new_position = module->data_offset + block_position + (uint64_t)READ_U32(p_ctx, "Offset"); LOG_DEBUG(p_ctx, "actual address for stream %"PRIu32" = %"PRIu64, stream, new_position); track_positions[stream] = new_position; } } /* Work out where the next block is */ block_address += (UINT64_C(8) * module->top_level_index.specifiers_count) + (UINT64_C(4) * module->top_level_index.specifiers_count * index_entry_count); } return seek_to_positions(p_ctx, track_positions, p_time, flags, 0, 0); } /* Helper for asf_reader_seek - * Given a set of positions seek the tracks. The status is the result of physically seeking each one. * It is expected that the positions will be before *p_time; if the flags require it search * for the next keyframe that is at or above *p_time. */ static VC_CONTAINER_STATUS_T seek_to_positions(VC_CONTAINER_T *p_ctx, uint64_t track_positions[ASF_TRACKS_MAX], int64_t *p_time, VC_CONTAINER_SEEK_FLAGS_T flags, unsigned int start_track, bool seek_on_start_track) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; uint64_t global_position = UINT64_MAX; unsigned int lowest_track, index, tracks; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; int64_t track_best_pts[ASF_TRACKS_MAX]; if (*p_time == 0) { // Special case: Time 0 means beginning of file. Don't search for the matching packet(s). memset(&module->packet_state, 0, sizeof(module->packet_state)); module->packet_state.start = track_positions[0]; status = SEEK(p_ctx, module->packet_state.start); // Set each track to using the global state for(index = 0; index < p_ctx->tracks_num; index++) { p_ctx->tracks[index]->priv->module->p_packet_state = &module->packet_state; } return status; } for(tracks = 0, index = start_track; tracks < p_ctx->tracks_num; tracks++, index = (index+1) % p_ctx->tracks_num) { uint32_t data_size; /* Use an on-stack packet state. We can't use the global state, as we must leave it at * the lowest position. We can't use any track's private state, as we will move it past * the desired location. */ ASF_PACKET_STATE private_state; memset(&private_state, 0, sizeof(private_state)); track_best_pts[index] = INT64_MAX; status = SEEK(p_ctx, track_positions[index]); /* loop until we find the packet we're looking for. * stop when we've seen a big enough PTS, and are on a key frame */ while(status == VC_CONTAINER_SUCCESS) { /* Get the next key-frame */ status = asf_reader_find_next_frame(p_ctx, index, &private_state, true, true); if(status == VC_CONTAINER_SUCCESS) { /* Get the PTS, if any */ int64_t pts = (int64_t)private_state.media_object_pts; if(pts != ASF_UNKNOWN_PTS) { if ((track_best_pts[index] == INT64_MAX) /* we don't have a time yet */ || (pts <= *p_time) /* it's before our target */ || (flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) /* we want after target */ { /* Store this time. It's the best yet. */ track_best_pts[index] = pts; /* Update the desired position */ track_positions[index] = private_state.start + private_state.current_offset; /* Copy the local state into this track's private state */ p_ctx->tracks[index]->priv->module->local_packet_state = private_state; LOG_DEBUG(p_ctx, "seek forward track %u to pts %"PRIu64, index, track_best_pts[index]); } /* If we've got to our target time we can stop. */ if (pts >= *p_time) { /* Then stop. */ break; } } status = asf_read_next_payload(p_ctx, &private_state, 0, &data_size ); } } /* If we are seeking using a specific track, usually this is the video track * and we want all the other tracks to start at the same time or later */ if (seek_on_start_track && start_track == index) { flags |= VC_CONTAINER_SEEK_FLAG_FORWARD; *p_time = track_best_pts[index]; } { ASF_PACKET_STATE *p_state = &p_ctx->tracks[index]->priv->module->local_packet_state; LOG_DEBUG(p_ctx, "seek track %u to pts %"PRIu64" (key:%i,moo:%i)", index, track_best_pts[index], p_state->stream_num >> 7, p_state->media_object_off); } } /* Find the smallest track address in track_positions. This will be the global position */ /* Also the lowest PTS in track_best_pts, this will be the new global PTS */ for (index = 0, lowest_track = 0; index < p_ctx->tracks_num; ++index) { /* If it is smaller, remember it */ if (track_positions[index] < global_position) { global_position = track_positions[index]; lowest_track = index; } /* Put the lowest PTS into entry 0 of the array */ if ((track_best_pts[index] != INT64_MAX) && (track_best_pts[index] < track_best_pts[0])) { track_best_pts[0] = track_best_pts[index]; } } /* Update the caller with the lowest real PTS, if any. (we may have already done this above) */ if (track_best_pts[0] != INT64_MAX) { *p_time = track_best_pts[0]; } else { LOG_DEBUG(p_ctx, "no PTS suitable to update the caller"); } /* As we did an extra read on the index track past the desired location seek back to it */ status = SEEK(p_ctx, global_position); /* Copy the packet state for the stream with the lowest address into the global state */ module->packet_state = p_ctx->tracks[lowest_track]->priv->module->local_packet_state; for(index = 0; index < p_ctx->tracks_num; index++) { VC_CONTAINER_TRACK_MODULE_T* track_mod = p_ctx->tracks[index]->priv->module; /* If the track position is the global position, or it is invalid, use the global state */ if ((track_positions[index] <= global_position) || (track_positions[index] == UINT64_MAX)) { track_mod->p_packet_state = &module->packet_state; } else { /* Track is not at the global position. Use the local state. */ LOG_DEBUG(p_ctx, "track %u local position %"PRIu64, index, track_positions[index]); track_mod->p_packet_state = &track_mod->local_packet_state; } } return status; } /*****************************************************************************/ /* Seek to a location in the file, using whatever indices are available * If flags bit VC_CONTAINER_SEEK_FLAG_FORWARD is set the position is guaranteed to * be a keyframe at or after the requested location. Conversely if it is not set * the position is guaranteed to be at or before the request. */ static VC_CONTAINER_STATUS_T asf_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *p_time, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_EOS; /* initialised to known fail state */ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; unsigned int stream; VC_CONTAINER_PARAM_UNUSED(mode); LOG_DEBUG(p_ctx, "asf_reader_seek"); /* Prefer the top-level index to the simple index - it has byte offsets not packet offsets, * and is likely to have separate tables for every track */ if (module->top_level_index.block_count) { status = seek_by_top_level_index(p_ctx, p_time, mode, flags); } else { uint64_t track_positions[ASF_TRACKS_MAX]; int seek_track = -1; uint32_t packet_num; uint64_t new_position; if (*p_time == 0) { // Special optimisation - for time zero just go to the beginning. packet_num = 0; } /* If there is a simple index use the packet number from it */ else if (module->simple_index_track) { /* Correct time desired */ uint64_t track_time = *p_time + module->preroll + module->time_offset; LOG_DEBUG(p_ctx, "using simple index"); /* Search the index for the correct packet */ status = asf_reader_index_find_time(p_ctx, module->simple_index_track, track_time, &packet_num, flags & VC_CONTAINER_SEEK_FLAG_FORWARD); } else { /* No index at all. Use arithmetic to guess the packet number. */ LOG_DEBUG(p_ctx, "index not usable %u", (unsigned)status); if (module->packets_num == 0) { /* This is a broadcast stream, and we can't do the arithmetic. * Set it to a value that will guarantee a seek fail. */ LOG_DEBUG(p_ctx, "no packets in file"); packet_num = UINT32_MAX; } else { packet_num = *p_time * module->packets_num / module->duration; } } /* calculate the byte address of the packet, relative to the start of data */ new_position = (uint64_t)packet_num * (uint64_t)module->packet_size; LOG_DEBUG(p_ctx, "packet number %"PRIu32" approx byte offset %"PRIu64 , packet_num, new_position + module->data_offset); if (new_position > (uint64_t)module->data_size) { new_position = module->data_size; LOG_DEBUG(p_ctx, "arithmetic error, seeking to end of file %" PRIu64 , new_position + module->data_offset); } new_position += module->data_offset; for(stream = 0; stream < p_ctx->tracks_num; stream++) { /* Use the 1st enabled video track as the seek stream */ if(p_ctx->tracks[stream]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO && p_ctx->tracks[stream]->is_enabled && seek_track < 0) seek_track = stream; track_positions[stream] = new_position; } /* repeat for all tracks */ /* Work out if we actually got anywhere. If so, save the positions for the subsequent reads */ status = seek_to_positions(p_ctx, track_positions, p_time, flags, seek_track < 0 ? 0 : seek_track, seek_track >= 0); } return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T asf_reader_close( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; unsigned int i; /* FIXME: metadata is currently shared across all readers so freeing it is left to the common layer but this isn't necessarily the best solution. for(i = 0; i meta_num; i++) free(p_ctx->meta[i]); if(p_ctx->meta_num) free(p_ctx->meta); p_ctx->meta_num = 0; */ for(i = 0; i < p_ctx->tracks_num; i++) vc_container_free_track(p_ctx, p_ctx->tracks[i]); p_ctx->tracks_num = 0; free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T asf_reader_open( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = 0; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; unsigned int i; GUID_T guid; /* Check for an ASF top-level header object */ if(PEEK_BYTES(p_ctx, (uint8_t *)&guid, sizeof(guid)) < sizeof(guid) || memcmp(&guid, &asf_guid_header, sizeof(guid))) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* * We are dealing with an ASF file */ /* Allocate our context */ module = malloc(sizeof(*module)); if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); /* Set the translation table to all error values */ memset(&module->stream_number_to_index, 0xff, sizeof(module->stream_number_to_index)); p_ctx->priv->module = module; p_ctx->tracks = module->tracks; /* Read the top level header object */ status = asf_read_object(p_ctx, INT64_C(0)); if(status != VC_CONTAINER_SUCCESS) goto error; /* Bail out if we didn't find a track */ if(!p_ctx->tracks_num) {status = VC_CONTAINER_ERROR_NO_TRACK_AVAILABLE; goto error;} /* * The top level data object must come next */ if(READ_GUID(p_ctx, &guid, "Object ID") != sizeof(guid) || memcmp(&guid, &asf_guid_data, sizeof(guid))) goto error; LOG_FORMAT(p_ctx, "Object Name: data"); module->data_size = READ_U64(p_ctx, "Object Size"); /* If the data size was supplied remove the size of the common object header and the local header for this object */ if(module->data_size) module->data_size -= ASF_OBJECT_HEADER_SIZE + 16 + 8 + 2; /* Sanity check the data object size */ if(module->data_size < 0) goto error; module->object_level++; SKIP_GUID(p_ctx, "File ID"); module->packets_num = READ_U64(p_ctx, "Total Data Packets"); if(module->broadcast) module->packets_num = 0; SKIP_U16(p_ctx, "Reserved"); if (module->packet_size) { LOG_DEBUG(p_ctx, "object size %"PRIu64" means %f packets", module->data_size, (float)(module->data_size) / (float)(module->packet_size)); } module->data_offset = STREAM_POSITION(p_ctx); LOG_DEBUG(p_ctx, "expect end of data at address %"PRIu64, module->data_size + module->data_offset); module->object_level--; /* * We now have all the information we really need to start playing the stream */ /* Initialise state for all tracks */ module->packet_state.start = module->data_offset; for(i = 0; i < p_ctx->tracks_num; i++) { VC_CONTAINER_TRACK_T *p_track = p_ctx->tracks[i]; p_track->priv->module->p_packet_state = &module->packet_state; } p_ctx->priv->pf_close = asf_reader_close; p_ctx->priv->pf_read = asf_reader_read; p_ctx->priv->pf_seek = asf_reader_seek; if(STREAM_SEEKABLE(p_ctx)) { p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; p_ctx->capabilities |= VC_CONTAINER_CAPS_FORCE_TRACK; } p_ctx->duration = module->duration; /* Check if we're done */ if(!module->data_size || !STREAM_SEEKABLE(p_ctx)) return VC_CONTAINER_SUCCESS; /* If the stream is seekable and not a broadcast stream, we want to use any index there * might be at the end of the stream */ /* Seek back to the end of the data object */ if( SEEK(p_ctx, module->data_offset + module->data_size) == VC_CONTAINER_SUCCESS) { /* This will catch the simple index object if it is there */ do { status = asf_read_object(p_ctx, INT64_C(0)); } while(status == VC_CONTAINER_SUCCESS); } for(i = 0; i < p_ctx->tracks_num; i++) { if(p_ctx->tracks[i]->priv->module->simple_index.offset) LOG_DEBUG(p_ctx, "track %i has an index", i); } /* Seek back to the start of the data */ return SEEK(p_ctx, module->data_offset); error: if(status == VC_CONTAINER_SUCCESS) status = VC_CONTAINER_ERROR_FORMAT_INVALID; LOG_DEBUG(p_ctx, "asf: error opening stream (%i)", status); if(module) asf_reader_close(p_ctx); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak reader_open asf_reader_open #endif userland/containers/asf/asf_writer.c000066400000000000000000000562421421703157200201350ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include //#define ENABLE_CONTAINERS_LOG_FORMAT #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_writer_utils.h" #include "containers/core/containers_logging.h" #undef CONTAINER_HELPER_LOG_INDENT #define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->object_level VC_CONTAINER_STATUS_T asf_writer_open( VC_CONTAINER_T *p_ctx ); /****************************************************************************** Defines. ******************************************************************************/ #define ASF_TRACKS_MAX 16 #define ASF_OBJECT_HEADER_SIZE (16+8) /****************************************************************************** Type definitions. ******************************************************************************/ typedef enum { ASF_OBJECT_TYPE_UNKNOWN = 0, ASF_OBJECT_TYPE_HEADER, ASF_OBJECT_TYPE_FILE_PROPS, ASF_OBJECT_TYPE_STREAM_PROPS, ASF_OBJECT_TYPE_EXT_STREAM_PROPS, ASF_OBJECT_TYPE_DATA, ASF_OBJECT_TYPE_SIMPLE_INDEX, ASF_OBJECT_TYPE_INDEX, ASF_OBJECT_TYPE_HEADER_EXT, ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL, ASF_OBJECT_TYPE_CODEC_LIST, ASF_OBJECT_TYPE_CONTENT_DESCRIPTION, ASF_OBJECT_TYPE_EXT_CONTENT_DESCRIPTION, ASF_OBJECT_TYPE_STREAM_BITRATE_PROPS, ASF_OBJECT_TYPE_LANGUAGE_LIST, ASF_OBJECT_TYPE_METADATA, ASF_OBJECT_TYPE_PADDING, } ASF_OBJECT_TYPE_T; typedef struct VC_CONTAINER_TRACK_MODULE_T { unsigned int stream_id; uint64_t time_offset; bool b_valid; uint64_t index_offset; uint32_t num_index_entries; int64_t index_time_interval; } VC_CONTAINER_TRACK_MODULE_T; typedef struct VC_CONTAINER_MODULE_T { int object_level; uint32_t packet_size; VC_CONTAINER_TRACK_T *tracks[ASF_TRACKS_MAX]; VC_CONTAINER_WRITER_EXTRAIO_T null; bool b_header_done; unsigned int current_track; } VC_CONTAINER_MODULE_T; /****************************************************************************** Static functions within this file. ******************************************************************************/ static VC_CONTAINER_STATUS_T asf_write_object( VC_CONTAINER_T *p_ctx, ASF_OBJECT_TYPE_T object_type ); static VC_CONTAINER_STATUS_T asf_write_object_header( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T asf_write_object_header_ext( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T asf_write_object_header_ext_internal( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T asf_write_object_file_properties( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T asf_write_object_stream_properties( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T asf_write_object_ext_stream_properties( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T asf_write_object_simple_index( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T asf_write_object_index( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T asf_write_object_data( VC_CONTAINER_T *p_ctx ); #if 0 static VC_CONTAINER_STATUS_T asf_write_object_codec_list( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T asf_write_object_content_description( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T asf_write_object_stream_bitrate_props( VC_CONTAINER_T *p_ctx ); #endif static const GUID_T asf_guid_header = {0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; static const GUID_T asf_guid_file_props = {0x8CABDCA1, 0xA947, 0x11CF, {0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; static const GUID_T asf_guid_stream_props = {0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; static const GUID_T asf_guid_ext_stream_props = {0x14E6A5CB, 0xC672, 0x4332, {0x83, 0x99, 0xA9, 0x69, 0x52, 0x06, 0x5B, 0x5A}}; static const GUID_T asf_guid_data = {0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; static const GUID_T asf_guid_simple_index = {0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}}; static const GUID_T asf_guid_index = {0xD6E229D3, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}; static const GUID_T asf_guid_header_ext = {0x5FBF03B5, 0xA92E, 0x11CF, {0x8E, 0xE3, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; static const GUID_T asf_guid_codec_list = {0x86D15240, 0x311D, 0x11D0, {0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}}; static const GUID_T asf_guid_content_description = {0x75B22633, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; static const GUID_T asf_guid_ext_content_description = {0xD2D0A440, 0xE307, 0x11D2, {0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50}}; static const GUID_T asf_guid_stream_bitrate_props = {0x7BF875CE, 0x468D, 0x11D1, {0x8D, 0x82, 0x00, 0x60, 0x97, 0xC9, 0xA2, 0xB2}}; static const GUID_T asf_guid_language_list = {0x7C4346A9, 0xEFE0, 0x4BFC, {0xB2, 0x29, 0x39, 0x3E, 0xDE, 0x41, 0x5C, 0x85}}; static const GUID_T asf_guid_metadata = {0xC5F8CBEA, 0x5BAF, 0x4877, {0x84, 0x67, 0xAA, 0x8C, 0x44, 0xFA, 0x4C, 0xCA}}; static const GUID_T asf_guid_padding = {0x1806D474, 0xCADF, 0x4509, {0xA4, 0xBA, 0x9A, 0xAB, 0xCB, 0x96, 0xAA, 0xE8}}; static const GUID_T asf_guid_stream_type_video = {0xBC19EFC0, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; static const GUID_T asf_guid_stream_type_audio = {0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; static const GUID_T asf_guid_error_correction = {0x20FB5700, 0x5B55, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; static struct { const ASF_OBJECT_TYPE_T type; const GUID_T *guid; const char *psz_name; VC_CONTAINER_STATUS_T (*pf_func)( VC_CONTAINER_T * ); } asf_object_list[] = { {ASF_OBJECT_TYPE_HEADER, &asf_guid_header, "header", asf_write_object_header}, {ASF_OBJECT_TYPE_FILE_PROPS, &asf_guid_file_props, "file properties", asf_write_object_file_properties}, {ASF_OBJECT_TYPE_STREAM_PROPS, &asf_guid_stream_props, "stream properties", asf_write_object_stream_properties}, {ASF_OBJECT_TYPE_EXT_STREAM_PROPS, &asf_guid_ext_stream_props, "extended stream properties", asf_write_object_ext_stream_properties}, {ASF_OBJECT_TYPE_DATA, &asf_guid_data, "data", asf_write_object_data}, {ASF_OBJECT_TYPE_SIMPLE_INDEX, &asf_guid_simple_index, "simple index", asf_write_object_simple_index}, {ASF_OBJECT_TYPE_INDEX, &asf_guid_index, "index", asf_write_object_index}, {ASF_OBJECT_TYPE_HEADER_EXT, &asf_guid_header_ext, "header extension", asf_write_object_header_ext}, {ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL, &asf_guid_header_ext, "header extension", asf_write_object_header_ext_internal}, #if 0 {ASF_OBJECT_TYPE_CODEC_LIST, &asf_guid_codec_list, "codec list", asf_write_object_codec_list}, {ASF_OBJECT_TYPE_CONTENT_DESCRIPTION, &asf_guid_content_description, "content description", asf_write_object_content_description}, {ASF_OBJECT_TYPE_EXT_CONTENT_DESCRIPTION, &asf_guid_ext_content_description, "extended content description", 0}, {ASF_OBJECT_TYPE_STREAM_BITRATE_PROPS, &asf_guid_stream_bitrate_props, "stream bitrate properties", asf_write_object_stream_bitrate_props}, #endif {ASF_OBJECT_TYPE_UNKNOWN, 0, "unknown", 0} }; static GUID_T guid_null; /*****************************************************************************/ static VC_CONTAINER_STATUS_T asf_write_object( VC_CONTAINER_T *p_ctx, ASF_OBJECT_TYPE_T type ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; int64_t object_size = 0; unsigned int i; /* Find out which object we want to write */ for( i = 0; asf_object_list[i].type && asf_object_list[i].type != type; i++ ); /* Check we found the requested type */ if(!asf_object_list[i].type) { vc_container_assert(0); return VC_CONTAINER_ERROR_CORRUPTED; } /* We need to find out the size of the object we're going to write. * Because we want to be streamable, we can't just come back later to update the size field. * The easiest way to find out the size of the data we're going to write is to write a dummy * version of it and get the size from that. It is a bit wasteful but is so much easier and * shouldn't really impact performance as there's no actual i/o involved. */ if(!vc_container_writer_extraio_enable(p_ctx, &module->null)) { asf_object_list[i].pf_func(p_ctx); object_size = STREAM_POSITION(p_ctx); } vc_container_writer_extraio_disable(p_ctx, &module->null); /* Special case for header extension internal function */ if(type == ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL) { WRITE_U32(p_ctx, object_size, "Header Extension Data Size"); /* Call the object specific writing function */ status = asf_object_list[i].pf_func(p_ctx); return status; } /* Write the object header */ if(WRITE_GUID(p_ctx, asf_object_list[i].guid, "Object ID") != sizeof(GUID_T)) return VC_CONTAINER_ERROR_EOS; LOG_FORMAT(p_ctx, "Object Name: %s", asf_object_list[i].psz_name); WRITE_U64(p_ctx, object_size + ASF_OBJECT_HEADER_SIZE, "Object Size"); module->object_level++; /* Call the object specific writing function */ status = asf_object_list[i].pf_func(p_ctx); module->object_level--; if(status != VC_CONTAINER_SUCCESS) LOG_DEBUG(p_ctx, "object %s appears to be corrupted", asf_object_list[i].psz_name); return status; } static VC_CONTAINER_STATUS_T asf_write_object_header( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; WRITE_U32(p_ctx, 0, "Number of Header Objects"); /* FIXME: could use that */ WRITE_U8(p_ctx, 0, "Reserved1"); WRITE_U8(p_ctx, 0, "Reserved2"); status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_FILE_PROPS); status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_HEADER_EXT); for(module->current_track = 0; module->current_track < p_ctx->tracks_num; module->current_track++) { status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_STREAM_PROPS); } /* Codec List */ /* Content Description */ /* Stream Bitrate Properties */ return status; } static VC_CONTAINER_STATUS_T asf_write_object_header_ext( VC_CONTAINER_T *p_ctx ) { WRITE_GUID(p_ctx, &guid_null, "Reserved Field 1"); WRITE_U16(p_ctx, 0, "Reserved Field 2"); return asf_write_object(p_ctx, ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL); } static VC_CONTAINER_STATUS_T asf_write_object_header_ext_internal( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; for(module->current_track = 0; module->current_track < p_ctx->tracks_num; module->current_track++) { status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_EXT_STREAM_PROPS); } return status; } static VC_CONTAINER_STATUS_T asf_write_object_file_properties( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; WRITE_GUID(p_ctx, &guid_null, "File ID"); WRITE_U64(p_ctx, 0, "File Size"); WRITE_U64(p_ctx, 0, "Creation Date"); WRITE_U64(p_ctx, 0, "Data Packets Count"); WRITE_U64(p_ctx, 0, "Play Duration"); WRITE_U64(p_ctx, 0, "Send Duration"); WRITE_U64(p_ctx, 0, "Preroll"); WRITE_U32(p_ctx, 0, "Flags"); WRITE_U32(p_ctx, module->packet_size, "Minimum Data Packet Size"); WRITE_U32(p_ctx, module->packet_size, "Maximum Data Packet Size"); WRITE_U32(p_ctx, 0, "Maximum Bitrate"); return VC_CONTAINER_SUCCESS; } static VC_CONTAINER_STATUS_T asf_write_bitmapinfoheader( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *p_track ) { uint32_t fourcc; /* Write the preamble to the BITMAPINFOHEADER */ WRITE_U32(p_ctx, p_track->format->type->video.width, "Encoded Image Width"); WRITE_U32(p_ctx, p_track->format->type->video.height, "Encoded Image Height"); WRITE_U8(p_ctx, 0, "Reserved Flags"); WRITE_U16(p_ctx, 40 + p_track->format->extradata_size, "Format Data Size"); /* Write BITMAPINFOHEADER structure */ WRITE_U32(p_ctx, 40 + p_track->format->extradata_size, "Format Data Size"); WRITE_U32(p_ctx, p_track->format->type->video.width, "Image Width"); WRITE_U32(p_ctx, p_track->format->type->video.height, "Image Height"); WRITE_U16(p_ctx, 0, "Reserved"); WRITE_U16(p_ctx, 0, "Bits Per Pixel Count"); fourcc = codec_to_fourcc(p_track->format->codec); WRITE_BYTES(p_ctx, (char *)&fourcc, 4); /* Compression ID */ LOG_FORMAT(p_ctx, "Compression ID: %4.4s", (char *)&fourcc); WRITE_U32(p_ctx, 0, "Image Size"); WRITE_U32(p_ctx, 0, "Horizontal Pixels Per Meter"); WRITE_U32(p_ctx, 0, "Vertical Pixels Per Meter"); WRITE_U32(p_ctx, 0, "Colors Used Count"); WRITE_U32(p_ctx, 0, "Important Colors Count"); WRITE_BYTES(p_ctx, p_track->format->extradata, p_track->format->extradata_size); return VC_CONTAINER_SUCCESS; } static VC_CONTAINER_STATUS_T asf_write_waveformatex( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *p_track) { /* Write WAVEFORMATEX structure */ WRITE_U16(p_ctx, codec_to_waveformat(p_track->format->codec), "Codec ID"); WRITE_U16(p_ctx, p_track->format->type->audio.channels, "Number of Channels"); WRITE_U32(p_ctx, p_track->format->type->audio.sample_rate, "Samples per Second"); WRITE_U32(p_ctx, p_track->format->bitrate, "Average Number of Bytes Per Second"); WRITE_U16(p_ctx, p_track->format->type->audio.block_align, "Block Alignment"); WRITE_U16(p_ctx, p_track->format->type->audio.bits_per_sample, "Bits Per Sample"); WRITE_U16(p_ctx, p_track->format->extradata_size, "Codec Specific Data Size"); WRITE_BYTES(p_ctx, p_track->format->extradata, p_track->format->extradata_size); return VC_CONTAINER_SUCCESS; } static VC_CONTAINER_STATUS_T asf_write_object_stream_properties( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; unsigned int track = module->current_track, ts_size = 0; const GUID_T *p_guid = &guid_null; if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) { p_guid = &asf_guid_stream_type_video; ts_size = 11 + 40 + p_ctx->tracks[track]->format->extradata_size; } else if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) { p_guid = &asf_guid_stream_type_audio; ts_size = 18 + p_ctx->tracks[track]->format->extradata_size; } WRITE_GUID(p_ctx, p_guid, "Stream Type"); WRITE_GUID(p_ctx, &asf_guid_error_correction, "Error Correction Type"); WRITE_U64(p_ctx, 0, "Time Offset"); WRITE_U32(p_ctx, ts_size, "Type-Specific Data Length"); WRITE_U32(p_ctx, 0, "Error Correction Data Length"); WRITE_U16(p_ctx, track + 1, "Flags"); WRITE_U32(p_ctx, 0, "Reserved"); /* Type-Specific Data */ if(ts_size) { if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) status = asf_write_bitmapinfoheader( p_ctx, p_ctx->tracks[track]); else if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) status = asf_write_waveformatex( p_ctx, p_ctx->tracks[track]); } /* Error Correction Data */ return status; } static VC_CONTAINER_STATUS_T asf_write_object_ext_stream_properties( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; WRITE_U64(p_ctx, 0, "Start Time"); WRITE_U64(p_ctx, 0, "End Time"); WRITE_U32(p_ctx, 0, "Data Bitrate"); WRITE_U32(p_ctx, 0, "Buffer Size"); WRITE_U32(p_ctx, 0, "Initial Buffer Fullness"); WRITE_U32(p_ctx, 0, "Alternate Data Bitrate"); WRITE_U32(p_ctx, 0, "Alternate Buffer Size"); WRITE_U32(p_ctx, 0, "Alternate Initial Buffer Fullness"); WRITE_U32(p_ctx, 0, "Maximum Object Size"); WRITE_U32(p_ctx, 0, "Flags"); WRITE_U16(p_ctx, module->current_track + 1, "Stream Number"); WRITE_U16(p_ctx, 0, "Stream Language ID Index"); WRITE_U64(p_ctx, 0, "Average Time Per Frame"); WRITE_U16(p_ctx, 0, "Stream Name Count"); WRITE_U16(p_ctx, 0, "Payload Extension System Count"); /* Stream Names */ /* Payload Extension Systems */ /* Stream Properties Object */ return VC_CONTAINER_SUCCESS; } static VC_CONTAINER_STATUS_T asf_write_object_index( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_PARAM_UNUSED(p_ctx); return VC_CONTAINER_SUCCESS; } static VC_CONTAINER_STATUS_T asf_write_object_simple_index( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_PARAM_UNUSED(p_ctx); return VC_CONTAINER_SUCCESS; } static VC_CONTAINER_STATUS_T asf_write_object_data( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_PARAM_UNUSED(p_ctx); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T asf_write_header( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_STATUS_T status; /* TODO Sanity check the tracks */ status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_HEADER); status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_DATA); return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T asf_writer_write( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_PARAM_UNUSED(p_packet); if(!module->b_header_done) { module->b_header_done = true; status = asf_write_header(p_ctx); } return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T asf_writer_close( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); vc_container_writer_extraio_delete(p_ctx, &module->null); free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T asf_writer_add_track( VC_CONTAINER_T *p_ctx, VC_CONTAINER_ES_FORMAT_T *format ) { VC_CONTAINER_STATUS_T status; VC_CONTAINER_TRACK_T *track; /* TODO check we support this format */ if(!(format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; /* Allocate and initialise track data */ if(p_ctx->tracks_num >= ASF_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; p_ctx->tracks[p_ctx->tracks_num] = track = vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; if(format->extradata_size) { status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size ); if(status) goto error; } vc_container_format_copy(track->format, format, format->extradata_size); p_ctx->tracks_num++; return VC_CONTAINER_SUCCESS; error: vc_container_free_track(p_ctx, track); return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T asf_writer_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status; switch(operation) { case VC_CONTAINER_CONTROL_TRACK_ADD: { VC_CONTAINER_ES_FORMAT_T *p_format = (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * ); return asf_writer_add_track(p_ctx, p_format); } case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: if(!module->b_header_done) { status = asf_write_header(p_ctx); if(status != VC_CONTAINER_SUCCESS) return status; module->b_header_done = true; } return VC_CONTAINER_SUCCESS; default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; } } /****************************************************************************** Global function definitions. ******************************************************************************/ VC_CONTAINER_STATUS_T asf_writer_open( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; const char *extension = vc_uri_path_extension(p_ctx->priv->uri); VC_CONTAINER_MODULE_T *module = 0; unsigned int i; /* Check if the user has specified a container */ vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); /* Check we're the right writer for this */ if(!extension) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if(strcasecmp(extension, "asf") && strcasecmp(extension, "wmv") && strcasecmp(extension, "wma")) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Allocate our context */ module = malloc(sizeof(*module)); if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); p_ctx->priv->module = module; p_ctx->tracks = module->tracks; /* Create a null i/o writer to help us out in writing our data */ status = vc_container_writer_extraio_create_null(p_ctx, &module->null); if(status != VC_CONTAINER_SUCCESS) goto error; /* We'll only write the header once we've got all our tracks */ p_ctx->priv->pf_close = asf_writer_close; p_ctx->priv->pf_write = asf_writer_write; p_ctx->priv->pf_control = asf_writer_control; return VC_CONTAINER_SUCCESS; error: LOG_DEBUG(p_ctx, "asf: error opening stream"); for(i = 0; i < ASF_TRACKS_MAX && p_ctx->tracks && p_ctx->tracks[i]; i++) vc_container_free_track(p_ctx, p_ctx->tracks[i]); free(module); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak writer_open asf_writer_open #endif userland/containers/avi/000077500000000000000000000000001421703157200156215ustar00rootroot00000000000000userland/containers/avi/CMakeLists.txt000066400000000000000000000010261421703157200203600ustar00rootroot00000000000000# Container module needs to go in as a plugins so different prefix # and install path set(CMAKE_SHARED_LIBRARY_PREFIX "") # Make sure the compiler can find the necessary include files include_directories (../..) add_library(reader_avi ${LIBRARY_TYPE} avi_reader.c) target_link_libraries(reader_avi containers) install(TARGETS reader_avi DESTINATION ${VMCS_PLUGIN_DIR}) add_library(writer_avi ${LIBRARY_TYPE} avi_writer.c) target_link_libraries(writer_avi containers) install(TARGETS writer_avi DESTINATION ${VMCS_PLUGIN_DIR}) userland/containers/avi/avi_reader.c000066400000000000000000001620321421703157200200720ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #define CONTAINER_IS_LITTLE_ENDIAN //#define ENABLE_CONTAINERS_LOG_FORMAT //#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE #define CONTAINER_HELPER_LOG_INDENT(a) 0 #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_waveformat.h" /****************************************************************************** Defines. ******************************************************************************/ #define AVISF_DISABLED 0x00000001 /*< If set stream should not be enabled by default. */ #define AVIF_MUSTUSEINDEX 0x00000020 #define AVIF_TRUSTCKTYPE 0x00000800 /*< (OpenDML) keyframe information reliable. */ #define AVIIF_LIST 0x00000001 #define AVIIF_KEYFRAME 0x00000010 #define AVIIF_NOTIME 0x00000100 #define AVI_INDEX_OF_INDEXES 0x00 #define AVI_INDEX_OF_CHUNKS 0x01 #define AVI_INDEX_2FIELD 0x01 #define AVI_INDEX_DELTAFRAME 0x80000000 #define AVI_TRACKS_MAX 16 /*< We won't try to handle streams with more tracks than this */ #define AVI_TWOCC(a,b) ((a) | (b << 8)) #define AVI_SYNC_CHUNK(ctx) \ while(STREAM_POSITION(ctx) & 1) \ { \ if (SKIP_BYTES(ctx, 1) != 1) break; \ } #define AVI_SKIP_CHUNK(ctx, size) \ do { \ SKIP_BYTES(ctx, size); \ AVI_SYNC_CHUNK(ctx); \ } while(0) /****************************************************************************** Type definitions ******************************************************************************/ typedef struct AVI_TRACK_STREAM_STATE_T { unsigned current_track_num; /**< Number of track currently being read */ int64_t data_offset; /**< Offset within the stream to find the track data */ uint32_t chunk_size; /**< Size of the current chunk being read */ uint32_t chunk_data_left; /**< Data left from the current chunk being read */ unsigned extra_chunk_track_num; /**< Temporary storage for in-band data e.g. 'dd' chunks */ uint32_t extra_chunk_data[4]; uint32_t extra_chunk_data_offs; uint32_t extra_chunk_data_len; } AVI_TRACK_STREAM_STATE_T; typedef struct AVI_TRACK_CHUNK_STATE_T { uint64_t index; uint64_t offs; /**< offset into bytestream consisting of all chunks for this track */ int64_t time_pos; /**< pts of chunk (if known) */ uint32_t flags; /**< flags associated with chunk */ AVI_TRACK_STREAM_STATE_T local_state; AVI_TRACK_STREAM_STATE_T *state; } AVI_TRACK_CHUNK_STATE_T; typedef struct VC_CONTAINER_TRACK_MODULE_T { int64_t time_start; /**< i.e. 'dwStart' in 'strh' (converted to microseconds) */ int64_t duration; /**< i.e. 'dwLength' in 'strh' (converted to microseconds) */ uint32_t time_num; /**< i.e. 'dwScale' in 'strh' */ uint32_t time_den; /**< i.e. 'dwRate' in 'strh', time_num / time_den = samples (or frames) / second for audio (or video) */ uint32_t sample_size; /**< i.e. 'dwSampleSize' in 'strh' */ uint64_t index_offset; /**< Offset to the start of an OpenDML index i.e. 'indx' (if available) */ uint32_t index_size; /**< Size of the OpenDML index chunk */ AVI_TRACK_CHUNK_STATE_T chunk; } VC_CONTAINER_TRACK_MODULE_T; typedef struct VC_CONTAINER_MODULE_T { VC_CONTAINER_TRACK_T *tracks[AVI_TRACKS_MAX]; uint64_t data_offset; /**< Offset to the start of data packets i.e. the data in the 'movi' list */ uint64_t data_size; /**< Size of the chunk containing data packets */ uint64_t index_offset; /**< Offset to the start of index data e.g. the data in a 'idx1' list */ uint32_t index_size; /**< Size of the chunk containing index data */ AVI_TRACK_STREAM_STATE_T state; } VC_CONTAINER_MODULE_T; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T avi_reader_open( VC_CONTAINER_T * ); /****************************************************************************** Local Functions ******************************************************************************/ static VC_CONTAINER_STATUS_T avi_find_chunk(VC_CONTAINER_T *p_ctx, VC_CONTAINER_FOURCC_T id, uint32_t *size) { VC_CONTAINER_STATUS_T status; VC_CONTAINER_FOURCC_T chunk_id; uint32_t chunk_size; do { chunk_id = READ_FOURCC(p_ctx, "Chunk ID"); chunk_size = READ_U32(p_ctx, "Chunk size"); if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; if(chunk_id == id) { *size = chunk_size; return VC_CONTAINER_SUCCESS; } /* Not interested in this chunk, skip it. */ AVI_SKIP_CHUNK(p_ctx, chunk_size); } while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS); return status; /* chunk not found */ } static VC_CONTAINER_STATUS_T avi_find_list(VC_CONTAINER_T *p_ctx, VC_CONTAINER_FOURCC_T fourcc, uint32_t *size) { VC_CONTAINER_STATUS_T status; VC_CONTAINER_FOURCC_T chunk_id; uint32_t chunk_size; uint32_t peek_buf[1]; do { chunk_id = READ_FOURCC(p_ctx, "Chunk ID"); chunk_size = READ_U32(p_ctx, "Chunk size"); if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; if(chunk_id == VC_FOURCC('L','I','S','T')) { if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if (peek_buf[0] == fourcc) { *size = chunk_size; return VC_CONTAINER_SUCCESS; } } /* Not interested in this chunk, skip it. */ AVI_SKIP_CHUNK(p_ctx, chunk_size); } while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS); return status; /* list not found */ } static int64_t avi_stream_ticks_to_us(VC_CONTAINER_TRACK_MODULE_T *track_module, uint64_t ticks) { int64_t time; vc_container_assert(track_module->time_den != 0); time = INT64_C(1000000) * track_module->time_num * ticks / track_module->time_den; return time; } static int64_t avi_calculate_chunk_time(VC_CONTAINER_TRACK_MODULE_T *track_module) { if (track_module->sample_size == 0) return track_module->time_start + avi_stream_ticks_to_us(track_module, track_module->chunk.index); else return track_module->time_start + avi_stream_ticks_to_us(track_module, ((track_module->chunk.offs + (track_module->sample_size >> 1)) / track_module->sample_size)); } static VC_CONTAINER_STATUS_T avi_read_stream_header_list(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, VC_CONTAINER_TRACK_MODULE_T *track_module) { VC_CONTAINER_STATUS_T status; int64_t list_offset; uint32_t list_size, chunk_id, chunk_size; int stream_header_chunk_read = 0, stream_format_chunk_read = 0; /* Look for a 'strl' LIST (sub)chunk */ status = avi_find_list(p_ctx, VC_FOURCC('s','t','r','l'), &chunk_size); if (status != VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "'strl' LIST not found for stream"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } list_offset = STREAM_POSITION(p_ctx); list_size = chunk_size; SKIP_FOURCC(p_ctx, "strl"); while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS && STREAM_POSITION(p_ctx) < list_offset + list_size) { int64_t offset = STREAM_POSITION(p_ctx); chunk_id = READ_FOURCC(p_ctx, "Chunk ID"); chunk_size = READ_U32(p_ctx, "Chunk size"); LOG_FORMAT(p_ctx, "chunk %4.4s, offset: %"PRIi64", size: %i", (char *)&chunk_id, offset, chunk_size); if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; if(chunk_id == VC_FOURCC('s','t','r','h')) { VC_CONTAINER_FOURCC_T fourcc_type, fourcc_handler; uint32_t flags, scale, rate, div, start, length, sample_size; /* We won't accept more than one 'strh' per stream */ if (stream_header_chunk_read) { LOG_DEBUG(p_ctx, "rejecting invalid 'strl', found more than one 'strh'"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } fourcc_type = READ_FOURCC(p_ctx, "fccType"); fourcc_handler = READ_FOURCC(p_ctx, "fccHandler"); flags = READ_U32(p_ctx, "dwFlags"); SKIP_U16(p_ctx, "wPriority"); SKIP_U16(p_ctx, "wLanguage"); SKIP_U32(p_ctx, "dwInitialFrames"); scale = READ_U32(p_ctx, "dwScale"); rate = READ_U32(p_ctx, "dwRate"); start = READ_U32(p_ctx, "dwStart"); length = READ_U32(p_ctx, "dwLength"); SKIP_U32(p_ctx, "dwSuggestedBufferSize"); SKIP_U32(p_ctx, "dwQuality"); sample_size = READ_U32(p_ctx, "dwSampleSize"); SKIP_U16(p_ctx, "rcFrame.left"); SKIP_U16(p_ctx, "rcFrame.top"); SKIP_U16(p_ctx, "rcFrame.right"); SKIP_U16(p_ctx, "rcFrame.bottom"); /* In AVI, sec/frame = scale/rate and frames/sec = rate/scale */ if (rate == 0) { LOG_DEBUG(p_ctx, "invalid dwRate: 0, using 1 as a guess"); LOG_DEBUG(p_ctx, "timestamps will almost certainly be wrong"); rate = 1; } div = vc_container_maths_gcd((int64_t)scale, (int64_t)rate); scale = scale / div; rate = rate / div; track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; if(fourcc_type == VC_FOURCC('v','i','d','s')) { track->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; track->format->type->video.frame_rate_num = rate; track->format->type->video.frame_rate_den = scale; if (sample_size != 0) { LOG_DEBUG(p_ctx, "ignoring dwSampleSize (%d) for video stream", sample_size); sample_size = 0; } } else if(fourcc_type == VC_FOURCC('a','u','d','s')) { track->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; /* VBR audio is going to be non-framed */ track->format->flags &= ~VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; } else if(fourcc_type == VC_FOURCC('t','x','t','s')) track->format->es_type = VC_CONTAINER_ES_TYPE_SUBPICTURE; /* Don't overwrite any existing value (i.e. in the unlikely case where we see 'strf' before 'strh') */ if(!track->format->codec) track->format->codec = vfw_fourcc_to_codec(fourcc_handler); /* FIXME: enable this once read_media does the right thing */ if (!(flags & AVISF_DISABLED) || 1) track->is_enabled = 1; track_module->time_num = scale; track_module->time_den = rate; track_module->time_start = avi_stream_ticks_to_us(track_module, (uint64_t)start); track_module->duration = avi_stream_ticks_to_us(track_module, (uint64_t)length); track_module->sample_size = sample_size; p_ctx->duration = MAX(p_ctx->duration, track_module->duration); stream_header_chunk_read = 1; } else if(chunk_id == VC_FOURCC('s','t','r','f')) { uint8_t *buffer; unsigned extra_offset = 0, extra_size = 0; /* We won't accept more than one 'strf' per stream */ if (stream_format_chunk_read) { LOG_DEBUG(p_ctx, "rejecting invalid 'strl', found more than one 'strf'"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } /* Use the extradata buffer for reading in the entire 'strf' (should not be a large chunk) */ if ((status = vc_container_track_allocate_extradata(p_ctx, track, chunk_size)) != VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "failed to allocate memory for 'strf' (%d bytes)", chunk_size); return status; } buffer = track->priv->extradata; if(READ_BYTES(p_ctx, buffer, chunk_size) != chunk_size) return VC_CONTAINER_ERROR_FORMAT_INVALID; AVI_SYNC_CHUNK(p_ctx); if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) { status = vc_container_bitmapinfoheader_to_es_format(buffer, chunk_size, &extra_offset, &extra_size, track->format); } else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) { status = vc_container_waveformatex_to_es_format(buffer, chunk_size, &extra_offset, &extra_size, track->format); if (track_module->sample_size != 0 && track_module->sample_size != track->format->type->audio.block_align) { LOG_DEBUG(p_ctx, "invalid dwSampleSize (%d), should match nBlockAlign (%d) for audio streams.", track_module->sample_size, track->format->type->audio.block_align); /* Note that if nBlockAlign really is 0, strf is seriously broken... */ if (track->format->type->audio.block_align != 0) track_module->sample_size = track->format->type->audio.block_align; } else { /* Flawed muxers might only set nBlockAlign (i.e. not set dwSampleSize correctly). */ if (track->format->type->audio.block_align == 1) track_module->sample_size = 1; } } if (status != VC_CONTAINER_SUCCESS) return status; if (extra_size) { track->format->extradata = buffer + extra_offset; track->format->extradata_size = extra_size; } /* Codec specific fix-up */ if (track->format->codec == VC_CONTAINER_CODEC_MP4A && track->format->extradata_size) { /* This is going to be raw AAC so it will be framed */ track_module->sample_size = 0; track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; } /* WMA specific fix-up */ if ((track->format->codec == VC_CONTAINER_CODEC_WMA1 || track->format->codec == VC_CONTAINER_CODEC_WMA2 || track->format->codec == VC_CONTAINER_CODEC_WMAP || track->format->codec == VC_CONTAINER_CODEC_WMAL || track->format->codec == VC_CONTAINER_CODEC_WMAV) && track->format->extradata_size) { track_module->sample_size = 0; track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; } stream_format_chunk_read = 1; } else if(chunk_id == VC_FOURCC('s','t','r','d')) { /* The data in a 'strd' chunk is either codec configuration data or DRM information, we can safely assume it might be either as long as we don't overwrite any config data read previously from a 'strf' chunk */ if ((status = vc_container_track_allocate_drmdata(p_ctx, track, chunk_size)) != VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "failed to allocate memory for 'strd' (%d bytes)", chunk_size); return status; } if(READ_BYTES(p_ctx, track->priv->drmdata, chunk_size) != chunk_size) return VC_CONTAINER_ERROR_FORMAT_INVALID; AVI_SYNC_CHUNK(p_ctx); if (!track->format->extradata) { if (vc_container_track_allocate_extradata(p_ctx, track, chunk_size) == VC_CONTAINER_SUCCESS) { memcpy(track->format->extradata, track->priv->drmdata, chunk_size); track->format->extradata = track->priv->extradata; track->format->extradata_size = chunk_size; } else { LOG_DEBUG(p_ctx, "failed to allocate memory for 'strd' (%d bytes)", chunk_size); LOG_DEBUG(p_ctx, "no codec configuration data set"); } } } else if(chunk_id == VC_FOURCC('i','n','d','x')) { track_module->index_offset = STREAM_POSITION(p_ctx); track_module->index_size = chunk_size; } else { /* Not interested in this chunk, skip it. */ } /* Skip any left-over data */ AVI_SKIP_CHUNK(p_ctx, offset + chunk_size + 8 - STREAM_POSITION(p_ctx) ); } if (!stream_header_chunk_read || !stream_format_chunk_read) { LOG_DEBUG(p_ctx, "invalid 'strl', 'strh' and 'strf' are both required"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } return status; } static VC_CONTAINER_STATUS_T avi_find_next_data_chunk(VC_CONTAINER_T *p_ctx, uint32_t *id, uint32_t *size) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_FOURCC_T chunk_id; uint32_t chunk_size = 0; uint32_t peek_buf[1]; do { chunk_id = READ_FOURCC(p_ctx, "Chunk ID"); chunk_size = READ_U32(p_ctx, "Chunk size"); if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) break; /* Check if this is a 'rec ' or a 'movi' LIST instead of a plain data chunk */ if(chunk_id == VC_FOURCC('L','I','S','T')) { if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4) return VC_CONTAINER_ERROR_EOS; if (peek_buf[0] == VC_FOURCC('r','e','c',' ')) SKIP_FOURCC(p_ctx, "rec "); else if (peek_buf[0] == VC_FOURCC('m','o','v','i')) SKIP_FOURCC(p_ctx, "movi"); else AVI_SKIP_CHUNK(p_ctx, chunk_size); /* Not interested in this LIST chunk, skip it. */ continue; } /* Check if this is a 'AVIX' RIFF header instead of a data chunk */ if(chunk_id == VC_FOURCC('R','I','F','F')) { if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4) return VC_CONTAINER_ERROR_EOS; if (peek_buf[0] == VC_FOURCC('A','V','I','X')) SKIP_FOURCC(p_ctx, "AVIX"); else AVI_SKIP_CHUNK(p_ctx, chunk_size); /* Not interested in this RIFF header, skip it. */ continue; } /* We treat only db/dc/dd or wb chunks as data */ if((uint32_t)chunk_id >> 16 == AVI_TWOCC('d','c') || (uint32_t)chunk_id >> 16 == AVI_TWOCC('d','b') || (uint32_t)chunk_id >> 16 == AVI_TWOCC('d','d') || (uint32_t)chunk_id >> 16 == AVI_TWOCC('w','b')) { *id = chunk_id; *size = chunk_size; break; } /* Need to exit if a zero sized chunk encountered so we don't loop forever. */ if(chunk_size == 0 && chunk_id == 0) return VC_CONTAINER_ERROR_EOS; /* Not interested in this chunk, skip it */ AVI_SKIP_CHUNK(p_ctx, chunk_size); } while ((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS); return status; } static void avi_track_from_chunk_id(VC_CONTAINER_FOURCC_T chunk_id, uint16_t *data_type, uint16_t *track_num) { *track_num = (((uint8_t*)&chunk_id)[0] - 48) * 10 + ((uint8_t*)&chunk_id)[1] - 48; *data_type = (uint32_t)chunk_id >> 16; } static VC_CONTAINER_STATUS_T avi_check_track(VC_CONTAINER_T *p_ctx, uint16_t data_type, uint16_t track_num) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; if (track_num < p_ctx->tracks_num) { if (data_type == AVI_TWOCC('w','b') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_AUDIO) { LOG_DEBUG(p_ctx, "suspicious track type ('wb'), track %d is not an audio track", track_num); status = VC_CONTAINER_ERROR_FAILED; } if (data_type == AVI_TWOCC('d','b') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) { LOG_DEBUG(p_ctx, "suspicious track type ('db'), track %d is not a video track", track_num); status = VC_CONTAINER_ERROR_FAILED; } if (data_type == AVI_TWOCC('d','c') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) { LOG_DEBUG(p_ctx, "suspicious track type ('dc'), track %d is not a video track", track_num); status = VC_CONTAINER_ERROR_FAILED; } if (data_type == AVI_TWOCC('d','d') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) { LOG_DEBUG(p_ctx, "suspicious track type ('dd'), track %d is not a video track", track_num); status = VC_CONTAINER_ERROR_FAILED; } } else { LOG_DEBUG(p_ctx, "invalid track number %d (no more than %d tracks expected)", track_num, p_ctx->tracks_num); status = VC_CONTAINER_ERROR_FAILED; } return status; } static int avi_compare_seek_time(int64_t chunk_time, int64_t seek_time, int chunk_is_keyframe, VC_CONTAINER_SEEK_FLAGS_T seek_flags) { if (chunk_time == seek_time && chunk_is_keyframe && !(seek_flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) return 0; if (chunk_time > seek_time && chunk_is_keyframe && (seek_flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) return 0; if (chunk_time > seek_time && !(seek_flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) return 1; /* Chunk time is past seek time, caller should use the previous keyframe */ return -1; } static VC_CONTAINER_STATUS_T avi_scan_legacy_index_chunk(VC_CONTAINER_T *p_ctx, int seek_track_num, int64_t *time, VC_CONTAINER_SEEK_FLAGS_T flags, uint64_t *pos) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status; VC_CONTAINER_TRACK_MODULE_T *track_module; AVI_TRACK_CHUNK_STATE_T selected_chunk; int64_t base_offset = module->data_offset; int64_t selected_chunk_offset = base_offset + 4; int32_t extra_offset = 0; int first_chunk_offset = 1; uint64_t position; SEEK(p_ctx, module->index_offset); memset(&selected_chunk, 0, sizeof(selected_chunk)); while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS && (uint64_t)STREAM_POSITION(p_ctx) < module->index_offset + module->index_size) { VC_CONTAINER_FOURCC_T chunk_id; uint16_t data_type, track_num; uint32_t chunk_flags, offset, size; chunk_id = READ_FOURCC(p_ctx, "Chunk ID"); chunk_flags = READ_U32(p_ctx, "dwFlags"); offset = READ_U32(p_ctx, "dwOffset"); size = READ_U32(p_ctx, "dwSize"); if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) break; /* Although it's rare, the offsets might be given from the start of the file instead of the data chunk, we have to handle both cases. */ if (first_chunk_offset) { if (offset > module->data_offset) base_offset = INT64_C(0); selected_chunk_offset = base_offset + 4; first_chunk_offset = 0; } avi_track_from_chunk_id(chunk_id, &data_type, &track_num); LOG_DEBUG(p_ctx, "reading track %"PRIu16, track_num); if (avi_check_track(p_ctx, data_type, track_num) != VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "skipping index entry for track %d/%d", track_num, p_ctx->tracks_num); continue; } track_module = p_ctx->tracks[track_num]->priv->module; if (data_type == AVI_TWOCC('d','d')) { if (track_num == seek_track_num) track_module->chunk.flags |= VC_CONTAINER_PACKET_FLAG_ENCRYPTED; extra_offset = -(size + 8); } /* If this entry does not affect timing, skip it */ if ((chunk_flags & (AVIIF_LIST | AVIIF_NOTIME)) || data_type == AVI_TWOCC('d','d')) continue; position = base_offset + offset + extra_offset; extra_offset = INT64_C(0); /* Check validity of position */ if (position <= module->data_offset /* || (*pos > module->data_offset + module->data_size*/) return VC_CONTAINER_ERROR_FORMAT_INVALID; if (track_num == seek_track_num) { bool is_keyframe = true; int res; if (p_ctx->tracks[track_num]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) is_keyframe = chunk_flags & AVIIF_KEYFRAME; if (is_keyframe) track_module->chunk.flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; else track_module->chunk.flags &= ~(VC_CONTAINER_PACKET_FLAG_KEYFRAME); res = avi_compare_seek_time(track_module->chunk.time_pos, *time, is_keyframe, flags); if (res > 0) break; /* We've found the keyframe we wanted */ if (is_keyframe) { selected_chunk_offset = position; selected_chunk = track_module->chunk; } if (res == 0) break; /* We've found the keyframe we wanted */ track_module->chunk.index++; track_module->chunk.offs += size; track_module->chunk.time_pos = avi_calculate_chunk_time(track_module); LOG_DEBUG(p_ctx, "index %"PRIu64", offs %"PRIu64", time %"PRIi64"us", track_module->chunk.index, track_module->chunk.offs, track_module->chunk.time_pos); } } if (status == VC_CONTAINER_SUCCESS || /* When seeking backwards, always return the last good position */ !(flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) { *pos = selected_chunk_offset; track_module = p_ctx->tracks[seek_track_num]->priv->module; track_module->chunk.index = selected_chunk.index; track_module->chunk.offs = selected_chunk.offs; track_module->chunk.flags = selected_chunk.flags; track_module->chunk.time_pos = selected_chunk.time_pos; *time = track_module->chunk.time_pos; return VC_CONTAINER_SUCCESS; } return VC_CONTAINER_ERROR_NOT_FOUND; } static VC_CONTAINER_STATUS_T avi_scan_standard_index_chunk(VC_CONTAINER_T *p_ctx, uint64_t index_offset, unsigned seek_track_num, int64_t *time, VC_CONTAINER_SEEK_FLAGS_T flags, uint64_t *pos) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND; VC_CONTAINER_TRACK_MODULE_T *track_module = NULL; VC_CONTAINER_FOURCC_T chunk_id; uint32_t chunk_size; uint16_t data_type, track_num; uint8_t index_type, index_sub_type; uint32_t entry, entry_count = 0; uint16_t entry_size; uint64_t base_offset = UINT64_C(0); uint64_t position = UINT64_C(0); uint64_t prev_keyframe_offs = INT64_C(0); AVI_TRACK_CHUNK_STATE_T prev_keyframe_chunk = { 0 }; SEEK(p_ctx, index_offset); chunk_id = READ_FOURCC(p_ctx, "Chunk ID"); chunk_size = READ_U32(p_ctx, "Chunk Size"); entry_size = READ_U16(p_ctx, "wLongsPerEntry"); index_sub_type = READ_U8(p_ctx, "bIndexSubType"); index_type = READ_U8(p_ctx, "bIndexType"); entry_count = READ_U32(p_ctx, "nEntriesInUse"); chunk_id = READ_FOURCC(p_ctx, "dwChunkId"); base_offset = READ_U64(p_ctx, "qwBaseOffset"); SKIP_U32(p_ctx, "dwReserved"); if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; avi_track_from_chunk_id(chunk_id, &data_type, &track_num); status = avi_check_track(p_ctx, data_type, track_num); if (status || chunk_size < 24 || track_num != seek_track_num) return VC_CONTAINER_ERROR_FORMAT_INVALID; if (entry_size != 2 || index_sub_type != 0 || index_type != AVI_INDEX_OF_CHUNKS) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; entry_count = MIN(entry_count, (chunk_size - 24) / (entry_size * 4)); track_module = p_ctx->tracks[seek_track_num]->priv->module; for (entry = 0; entry < entry_count; ++entry) { uint32_t chunk_offset; int key_frame = 0; chunk_offset = READ_U32(p_ctx, "dwOffset"); chunk_size = READ_U32(p_ctx, "dwSize"); if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) break; status = VC_CONTAINER_ERROR_NOT_FOUND; if (!(chunk_size & AVI_INDEX_DELTAFRAME)) key_frame = 1; chunk_size &= ~AVI_INDEX_DELTAFRAME; position = base_offset + chunk_offset - 8; if (key_frame) track_module->chunk.flags = VC_CONTAINER_PACKET_FLAG_KEYFRAME; else track_module->chunk.flags = 0; if (time != NULL) { int res; status = VC_CONTAINER_ERROR_NOT_FOUND; res = avi_compare_seek_time(track_module->chunk.time_pos, *time, key_frame, flags); if (res == 0) { *pos = position; *time = track_module->chunk.time_pos; status = VC_CONTAINER_SUCCESS; break; } else if (res > 0) { if (prev_keyframe_offs) { *pos = prev_keyframe_offs; track_module->chunk = prev_keyframe_chunk; *time = track_module->chunk.time_pos; status = VC_CONTAINER_SUCCESS; } break; } if (key_frame) { prev_keyframe_offs = position; prev_keyframe_chunk = track_module->chunk; } } else { /* Not seeking to a time position, but scanning track chunk state up to a certain file position instead */ if (position >= *pos) { status = VC_CONTAINER_SUCCESS; break; } } track_module->chunk.index++; track_module->chunk.offs += chunk_size; track_module->chunk.time_pos = avi_calculate_chunk_time(track_module); } return status; } static VC_CONTAINER_STATUS_T avi_scan_super_index_chunk(VC_CONTAINER_T *p_ctx, unsigned index_track_num, int64_t *time, VC_CONTAINER_SEEK_FLAGS_T flags, uint64_t *pos) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND; VC_CONTAINER_FOURCC_T chunk_id; uint64_t index_offset; uint32_t index_size; uint16_t data_type, track_num; uint32_t entry, entry_count; uint16_t entry_size; uint8_t index_sub_type, index_type; index_offset = p_ctx->tracks[index_track_num]->priv->module->index_offset; index_size = p_ctx->tracks[index_track_num]->priv->module->index_size; SEEK(p_ctx, index_offset); entry_size = READ_U16(p_ctx, "wLongsPerEntry"); index_sub_type = READ_U8(p_ctx, "bIndexSubType"); index_type = READ_U8(p_ctx, "bIndexType"); entry_count = READ_U32(p_ctx, "nEntriesInUse"); chunk_id = READ_FOURCC(p_ctx, "dwChunkId"); SKIP_U32(p_ctx, "dwReserved0"); SKIP_U32(p_ctx, "dwReserved1"); SKIP_U32(p_ctx, "dwReserved2"); if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; if (index_type == AVI_INDEX_OF_INDEXES) { avi_track_from_chunk_id(chunk_id, &data_type, &track_num); status = avi_check_track(p_ctx, data_type, track_num); if (status || index_size < 24 || track_num != index_track_num) return VC_CONTAINER_ERROR_FORMAT_INVALID; /* FIXME: We should probably support AVI_INDEX_2FIELD as well */ if (entry_size != 4 || index_sub_type != 0) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; entry_count = MIN(entry_count, (index_size - 24) / entry_size); for (entry = 0; entry < entry_count; ++entry) { uint64_t entry_offset, standard_index_offset; standard_index_offset = READ_U64(p_ctx, "qwOffset"); SKIP_U32(p_ctx, "dwSize"); SKIP_U32(p_ctx, "dwDuration"); if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) break; if (standard_index_offset == UINT64_C(0)) { status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Not plausible */ break; } entry_offset = STREAM_POSITION(p_ctx); status = avi_scan_standard_index_chunk(p_ctx, standard_index_offset, index_track_num, time, flags, pos); if (status != VC_CONTAINER_ERROR_NOT_FOUND) break; SEEK(p_ctx, entry_offset); /* Move to next entry ('ix' chunk); */ } } else if (index_type == AVI_INDEX_OF_CHUNKS) { /* It seems we are dealing with a standard index instead... */ status = avi_scan_standard_index_chunk(p_ctx, index_offset, index_track_num, time, flags, pos); } else { status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; } return status; } static VC_CONTAINER_STATUS_T avi_read_dd_chunk( VC_CONTAINER_T *p_ctx, AVI_TRACK_STREAM_STATE_T *p_state, uint16_t data_type, uint32_t chunk_size, uint16_t track_num ) { if (data_type == AVI_TWOCC('d','d')) { if (p_state->extra_chunk_data_len || chunk_size > sizeof(p_state->extra_chunk_data)) { LOG_DEBUG(p_ctx, "cannot handle multiple consecutive 'dd' chunks"); return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; } if(READ_BYTES(p_ctx, p_state->extra_chunk_data, chunk_size) != chunk_size) return VC_CONTAINER_ERROR_FORMAT_INVALID; AVI_SYNC_CHUNK(p_ctx); p_state->extra_chunk_track_num = track_num; p_state->extra_chunk_data_len = chunk_size; p_state->extra_chunk_data_offs = 0; return VC_CONTAINER_ERROR_CONTINUE; } else if (p_state->extra_chunk_data_len && p_state->extra_chunk_track_num != track_num) { LOG_DEBUG(p_ctx, "dropping data from '%02ddd' chunk, not for this track (%d)", p_state->extra_chunk_track_num, track_num); p_state->extra_chunk_data_len = 0; } return VC_CONTAINER_SUCCESS; } /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ static VC_CONTAINER_STATUS_T avi_reader_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = NULL; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; AVI_TRACK_STREAM_STATE_T *p_state = &module->state; if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) { p_state = p_ctx->tracks[p_packet->track]->priv->module->chunk.state; } LOG_DEBUG(p_ctx, "seeking to %"PRIi64, p_state->data_offset); SEEK(p_ctx, p_state->data_offset); if (p_state->chunk_data_left == 0) { VC_CONTAINER_FOURCC_T chunk_id; uint32_t chunk_size; uint16_t data_type, track_num; if ((status = avi_find_next_data_chunk(p_ctx, &chunk_id, &chunk_size)) != VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "unable to find the next data chunk %d", status); p_state->data_offset = STREAM_POSITION(p_ctx); return status; } avi_track_from_chunk_id(chunk_id, &data_type, &track_num); if (avi_check_track(p_ctx, data_type, track_num) != VC_CONTAINER_SUCCESS) { AVI_SKIP_CHUNK(p_ctx, chunk_size); LOG_DEBUG(p_ctx, "skipping data for track %d/%d", track_num, p_ctx->tracks_num); p_state->data_offset = STREAM_POSITION(p_ctx); return VC_CONTAINER_ERROR_CONTINUE; } /* If we are reading from the global state (i.e. normal read or forced read from the track on the global state), and the track we found is not on the global state, connect the two */ if (p_state == &module->state && p_ctx->tracks[track_num]->priv->module->chunk.state != &module->state) { int64_t next_chunk; /* The track's offset is past the current position, skip it as we are not interested in track data from before the track's offset. If we were to read it we would return the same data multiple times. */ next_chunk = (STREAM_POSITION(p_ctx) + chunk_size + 1) & ~1; if (p_ctx->tracks[track_num]->priv->module->chunk.state->data_offset > next_chunk) { AVI_SKIP_CHUNK(p_ctx, chunk_size); LOG_DEBUG(p_ctx, "skipping track %d/%d as we have already read it", track_num, p_ctx->tracks_num); p_state->data_offset = STREAM_POSITION(p_ctx); return VC_CONTAINER_ERROR_CONTINUE; } /* The track state must be pointing to the current chunk. We need to reconnect the track to the global state. */ LOG_DEBUG(p_ctx, "reconnect track %u to the global state", track_num); p_ctx->tracks[track_num]->priv->module->chunk.state = &module->state; module->state = p_ctx->tracks[track_num]->priv->module->chunk.local_state; vc_container_assert(chunk_size >= p_state->chunk_data_left); vc_container_assert(!p_state->chunk_data_left || ((p_state->data_offset + p_state->chunk_data_left + 1) & ~1) == next_chunk); vc_container_assert(p_state->current_track_num == track_num); return VC_CONTAINER_ERROR_CONTINUE; } /* If we are not forcing, or if we are and found the track we are interested in, check for dd data and set the track module for the later code */ if (!(flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) || (track_num == p_packet->track)) { if ((status = avi_read_dd_chunk(p_ctx, p_state, data_type, chunk_size, track_num)) != VC_CONTAINER_SUCCESS) { p_state->data_offset = STREAM_POSITION(p_ctx); return status; } } p_state->chunk_size = p_state->chunk_data_left = chunk_size; p_state->current_track_num = track_num; } /* If there is data from another track skip past it */ if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK && p_state->current_track_num != p_packet->track) { p_state->data_offset = STREAM_POSITION(p_ctx); AVI_SKIP_CHUNK(p_ctx, p_state->chunk_data_left); LOG_DEBUG(p_ctx, "skipping track %d/%d as we are ignoring it", p_state->current_track_num, p_ctx->tracks_num); track_module = p_ctx->tracks[p_packet->track]->priv->module; /* Handle disconnection from global state */ if (p_state == &module->state && p_ctx->tracks[p_state->current_track_num]->priv->module->chunk.state == &module->state) { /* Make a copy of the global state */ LOG_DEBUG(p_ctx, "using local state on track %d", p_packet->track); track_module->chunk.local_state = module->state; track_module->chunk.state = &track_module->chunk.local_state; } track_module->chunk.state->data_offset = STREAM_POSITION(p_ctx); track_module->chunk.state->chunk_data_left = 0; return VC_CONTAINER_ERROR_CONTINUE; } track_module = p_ctx->tracks[p_state->current_track_num]->priv->module; if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) { vc_container_assert(p_state->current_track_num == p_packet->track); } LOG_DEBUG(p_ctx, "reading track %u chunk at time %"PRIi64"us, offset %"PRIu64, p_state->current_track_num, track_module->chunk.time_pos, track_module->chunk.offs); if (p_state->extra_chunk_data_len) track_module->chunk.flags |= VC_CONTAINER_PACKET_FLAG_ENCRYPTED; else track_module->chunk.flags &= ~VC_CONTAINER_PACKET_FLAG_ENCRYPTED; if (p_packet) { p_packet->track = p_state->current_track_num; p_packet->size = p_state->chunk_data_left + p_state->extra_chunk_data_len; p_packet->flags = track_module->chunk.flags; if (p_state->chunk_data_left == p_state->chunk_size) { p_packet->pts = track_module->chunk.time_pos; if (track_module->sample_size == 0) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME; } else { p_packet->pts = VC_CONTAINER_TIME_UNKNOWN; if (track_module->sample_size == 0) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; } p_packet->dts = VC_CONTAINER_TIME_UNKNOWN; } if (flags & VC_CONTAINER_READ_FLAG_SKIP) { SKIP_BYTES(p_ctx, p_state->chunk_data_left); AVI_SYNC_CHUNK(p_ctx); p_state->chunk_data_left = 0; p_state->extra_chunk_data_len = 0; } if (flags & VC_CONTAINER_READ_FLAG_INFO) { p_state->data_offset = STREAM_POSITION(p_ctx); LOG_DEBUG(p_ctx, "data position %"PRIi64, p_state->data_offset); return VC_CONTAINER_SUCCESS; } if (p_packet) { uint8_t *data = p_packet->data; uint32_t buffer_size = p_packet->buffer_size; uint32_t size = 0; uint32_t len; /* See if we need to insert extra data */ if (p_state->extra_chunk_data_len) { len = MIN(buffer_size, p_state->extra_chunk_data_len); memcpy(data, p_state->extra_chunk_data + p_state->extra_chunk_data_offs, len); data += len; buffer_size -= len; size = len; p_state->extra_chunk_data_len -= len; p_state->extra_chunk_data_offs += len; } /* Now try to read data into buffer */ len = MIN(buffer_size, p_state->chunk_data_left); READ_BYTES(p_ctx, data, len); size += len; p_state->chunk_data_left -= len; p_packet->size = size; if (p_state->chunk_data_left) p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; } if (p_state->chunk_data_left == 0) { AVI_SYNC_CHUNK(p_ctx); track_module->chunk.index++; track_module->chunk.offs += p_state->chunk_size; track_module->chunk.flags = 0; track_module->chunk.time_pos = avi_calculate_chunk_time(track_module); } /* Update the track's position */ p_state->data_offset = STREAM_POSITION(p_ctx); LOG_DEBUG(p_ctx, "data position %"PRIi64, p_state->data_offset); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *p_offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status; uint64_t position, pos; AVI_TRACK_CHUNK_STATE_T chunk_state[AVI_TRACKS_MAX]; AVI_TRACK_STREAM_STATE_T global_state; unsigned seek_track_num, i; if (mode != VC_CONTAINER_SEEK_MODE_TIME || !STREAM_SEEKABLE(p_ctx)) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; LOG_DEBUG(p_ctx, "AVI seeking to %"PRIi64"us", *p_offset); /* Save current position and chunk state so we can restore it if we hit an error whilst scanning index data */ position = STREAM_POSITION(p_ctx); for(i = 0; i < p_ctx->tracks_num; i++) chunk_state[i] = p_ctx->tracks[i]->priv->module->chunk; global_state = p_ctx->priv->module->state; /* Clear track state affected by a seek operation of any kind */ for(i = 0; i < p_ctx->tracks_num; i++) { p_ctx->tracks[i]->priv->module->chunk.index = INT64_C(0); p_ctx->tracks[i]->priv->module->chunk.offs = INT64_C(0); p_ctx->tracks[i]->priv->module->chunk.flags = 0; p_ctx->tracks[i]->priv->module->chunk.time_pos = p_ctx->tracks[i]->priv->module->time_start; p_ctx->tracks[i]->priv->module->chunk.state = &p_ctx->tracks[i]->priv->module->chunk.local_state; p_ctx->tracks[i]->priv->module->chunk.local_state.chunk_data_left = UINT64_C(0); p_ctx->tracks[i]->priv->module->chunk.local_state.chunk_size = UINT64_C(0); p_ctx->tracks[i]->priv->module->chunk.local_state.extra_chunk_data_len = 0; p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset = module->data_offset + 4; } /* Clear the global state */ p_ctx->priv->module->state.chunk_data_left = UINT64_C(0); p_ctx->priv->module->state.chunk_size = UINT64_C(0); p_ctx->priv->module->state.extra_chunk_data_len = 0; p_ctx->priv->module->state.data_offset = module->data_offset + 4; /* Choose track to use for seeking, favor video tracks and tracks that are enabled */ for(i = 0; i < p_ctx->tracks_num; i++) if(p_ctx->tracks[i]->is_enabled && p_ctx->tracks[i]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) break; if(i == p_ctx->tracks_num) for(i = 0; i < p_ctx->tracks_num; i++) if(p_ctx->tracks[i]->is_enabled) break; if(i == p_ctx->tracks_num) i = 0; LOG_DEBUG(p_ctx, "seek on track %d/%d", i, p_ctx->tracks_num); seek_track_num = i; if (p_ctx->tracks[seek_track_num]->priv->module->index_offset) { LOG_DEBUG(p_ctx, "seeking using the super index"); status = avi_scan_super_index_chunk(p_ctx, seek_track_num, p_offset, flags, &pos); if (status != VC_CONTAINER_SUCCESS) goto error; /* As AVI chunks don't convey timestamp information, we need to scan all tracks to the seek file position */ for(i = 0; i < p_ctx->tracks_num; i++) { if (p_ctx->tracks[i]->priv->module->index_offset && i != seek_track_num) { uint64_t track_pos; int64_t track_time = *p_offset; status = avi_scan_super_index_chunk(p_ctx, i, &track_time, flags, &track_pos); if (status != VC_CONTAINER_SUCCESS) goto error; p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset = track_pos; } } } else { LOG_DEBUG(p_ctx, "seeking using the legacy index"); /* The legacy index comes after data so it might not have been available at the time the container was opened; if this is the case, see if we can find an index now, if we can't, then there's no way we can proceed with the seek. */ if(!module->index_offset) { uint32_t chunk_size; LOG_DEBUG(p_ctx, "no index offset, searching for one"); /* Locate data chunk and skip it */ SEEK(p_ctx, module->data_offset); AVI_SKIP_CHUNK(p_ctx, module->data_size); /* Now search for the index */ status = avi_find_chunk(p_ctx, VC_FOURCC('i','d','x','1'), &chunk_size); if (status == VC_CONTAINER_SUCCESS) { /* Store offset to index data */ module->index_offset = STREAM_POSITION(p_ctx); module->index_size = chunk_size; p_ctx->capabilities |= VC_CONTAINER_CAPS_HAS_INDEX; p_ctx->capabilities |= VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG; } } /* Check again, we may or may not have an index */ if (!module->index_offset) { /* If there is no index and we are seeking to 0 we can assume the correct location is the start of the data. Otherwise we are unable to seek to a specified non-zero location without an index */ if (*p_offset != INT64_C(0)) { LOG_DEBUG(p_ctx, "failed to find the legacy index, unable to seek"); status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; goto error; } pos = module->data_offset; } else { LOG_DEBUG(p_ctx, "scanning the legacy index chunk"); status = avi_scan_legacy_index_chunk(p_ctx, seek_track_num, p_offset, flags, &pos); if (status != VC_CONTAINER_SUCCESS) goto error; for (i = 0; i < p_ctx->tracks_num; i++) { if (i != seek_track_num) { uint64_t track_pos = pos; int64_t track_time = *p_offset; status = avi_scan_legacy_index_chunk(p_ctx, i, &track_time, flags, &track_pos); if (status != VC_CONTAINER_SUCCESS) goto error; p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset = track_pos; p_ctx->tracks[i]->priv->module->chunk.local_state.current_track_num = i; } } } } position = pos; /* Set the seek track's data offset */ p_ctx->tracks[seek_track_num]->priv->module->chunk.local_state.data_offset = position; p_ctx->tracks[seek_track_num]->priv->module->chunk.local_state.current_track_num = seek_track_num; /* Connect the earlier track(s) to the global state. Needs 2 passes */ module->state.data_offset = INT64_MAX; for(i = 0; i < p_ctx->tracks_num; i++) { if(p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset < module->state.data_offset) module->state = p_ctx->tracks[i]->priv->module->chunk.local_state; } for(i = 0; i < p_ctx->tracks_num; i++) { if(p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset == module->state.data_offset) p_ctx->tracks[i]->priv->module->chunk.state = &module->state; } LOG_DEBUG(p_ctx, "seek to %"PRIi64", position %"PRIu64, *p_offset, pos); return SEEK(p_ctx, position); error: p_ctx->priv->module->state = global_state; for(i = 0; i < p_ctx->tracks_num; i++) p_ctx->tracks[i]->priv->module->chunk = chunk_state[i]; SEEK(p_ctx, position); return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_reader_close( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; unsigned int i; for(i = 0; i < p_ctx->tracks_num; i++) vc_container_free_track(p_ctx, p_ctx->tracks[i]); p_ctx->tracks = NULL; p_ctx->tracks_num = 0; free(module); p_ctx->priv->module = 0; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T avi_reader_open( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = 0; VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; uint32_t chunk_size; uint32_t peek_buf[3]; unsigned int i; uint32_t flags, num_streams; int64_t offset; /* Check the RIFF chunk descriptor */ if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 12) != 12) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if( peek_buf[0] != VC_FOURCC('R','I','F','F') ) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if( peek_buf[2] != VC_FOURCC('A','V','I',' ') ) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* * We now know we are dealing with an AVI file */ SKIP_FOURCC(p_ctx, "RIFF ID"); SKIP_U32(p_ctx, "fileSize"); SKIP_FOURCC(p_ctx, "fileType"); /* Look for the 'hdrl' LIST (sub)chunk */ status = avi_find_list(p_ctx, VC_FOURCC('h','d','r','l'), &chunk_size); if (status != VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "'hdrl' LIST not found"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } SKIP_FOURCC(p_ctx, "hdrl"); /* Now look for the 'avih' sub-chunk */ status = avi_find_chunk(p_ctx, VC_FOURCC('a','v','i','h'), &chunk_size); if (status != VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "'avih' not found"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } /* Parse the 'avih' sub-chunk */ SKIP_U32(p_ctx, "dwMicroSecPerFrame"); SKIP_U32(p_ctx, "dwMaxBytesPerSec"); SKIP_U32(p_ctx, "dwPaddingGranularity"); flags = READ_U32(p_ctx, "dwFlags"); SKIP_U32(p_ctx, "dwTotalFrames"); SKIP_U32(p_ctx, "dwInitialFrames"); num_streams = READ_U32(p_ctx, "dwStreams"); SKIP_U32(p_ctx, "dwSuggestedBufferSize"); SKIP_U32(p_ctx, "dwWidth"); SKIP_U32(p_ctx, "dwHeight"); SKIP_U32(p_ctx, "dwReserved0"); SKIP_U32(p_ctx, "dwReserved1"); SKIP_U32(p_ctx, "dwReserved2"); SKIP_U32(p_ctx, "dwReserved3"); if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) goto error; /* Allocate our context and tracks */ if ((module = malloc(sizeof(*module))) == NULL) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; memset(module, 0, sizeof(*module)); p_ctx->priv->module = module; p_ctx->tracks = module->tracks; if (num_streams > AVI_TRACKS_MAX) { LOG_DEBUG(p_ctx, "cannot handle %u tracks, restricted to %d", num_streams, AVI_TRACKS_MAX); num_streams = AVI_TRACKS_MAX; } for (p_ctx->tracks_num = 0; p_ctx->tracks_num != num_streams; p_ctx->tracks_num++) { p_ctx->tracks[p_ctx->tracks_num] = vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); if(!p_ctx->tracks[p_ctx->tracks_num]) break; } if(p_ctx->tracks_num != num_streams) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } /* Try to read stream header list chunks ('strl') */ for (i = 0; i != num_streams; ++i) { status = avi_read_stream_header_list(p_ctx, p_ctx->tracks[i], p_ctx->tracks[i]->priv->module); if(status != VC_CONTAINER_SUCCESS) goto error; } /* Look for the 'movi' LIST (sub)chunk */ status = avi_find_list(p_ctx, VC_FOURCC('m','o','v','i'), &chunk_size); if (status != VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "'movi' LIST not found"); status = VC_CONTAINER_ERROR_FORMAT_INVALID; goto error; } /* Store offset to the start and size of data (the 'movi' LIST) */ module->data_offset = STREAM_POSITION(p_ctx); module->data_size = chunk_size; p_ctx->priv->pf_close = avi_reader_close; p_ctx->priv->pf_read = avi_reader_read; p_ctx->priv->pf_seek = avi_reader_seek; if (flags & AVIF_MUSTUSEINDEX) { LOG_DEBUG(p_ctx, "AVIF_MUSTUSEINDEX not supported, playback might not work properly"); } /* If the stream is seekable, see if we can find an index (for at least one of the tracks); even if we cannot find an index now, one might become available later (e.g. when the stream grows run-time), in that case we might want to report that we can seek and re-search for the index again if or when we're requested to seek. */ if(STREAM_SEEKABLE(p_ctx)) { p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; p_ctx->capabilities |= VC_CONTAINER_CAPS_FORCE_TRACK; for(i = 0; i < p_ctx->tracks_num; i++) if(p_ctx->tracks[i]->priv->module->index_offset) break; if (i < p_ctx->tracks_num) { p_ctx->capabilities |= VC_CONTAINER_CAPS_HAS_INDEX; if (flags & AVIF_TRUSTCKTYPE) p_ctx->capabilities |= VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG; } else { /* Skip data first */ AVI_SKIP_CHUNK(p_ctx, module->data_size); /* Now search for the index */ status = avi_find_chunk(p_ctx, VC_FOURCC('i','d','x','1'), &chunk_size); if (status == VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "'idx1' found"); /* Store offset to index data */ module->index_offset = STREAM_POSITION(p_ctx); module->index_size = chunk_size; p_ctx->capabilities |= VC_CONTAINER_CAPS_HAS_INDEX; p_ctx->capabilities |= VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG; } /* Seek back to the start of the data */ SEEK(p_ctx, module->data_offset); } } SKIP_FOURCC(p_ctx, "movi"); for (i = 0; i != num_streams; i++) { p_ctx->tracks[i]->priv->module->chunk.state = &p_ctx->priv->module->state; } p_ctx->priv->module->state.data_offset = STREAM_POSITION(p_ctx); /* Update the tracks to set their data offsets. This help with bad interleaving, for example when there is all the video tracks followed by all the audio tracks. It means we don't have to read through the tracks we are not interested in when forcing a read from a given track, as could be the case in the above example. If this fails we will fall back to skipping track data. */ offset = INT64_C(0); avi_reader_seek(p_ctx, &offset, VC_CONTAINER_SEEK_MODE_TIME, VC_CONTAINER_SEEK_FLAG_PRECISE); if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) goto error; return VC_CONTAINER_SUCCESS; error: LOG_DEBUG(p_ctx, "error opening stream (%i)", status); for(i = 0; i < p_ctx->tracks_num; i++) vc_container_free_track(p_ctx, p_ctx->tracks[i]); p_ctx->tracks = NULL; p_ctx->tracks_num = 0; if (module) free(module); p_ctx->priv->module = NULL; return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak reader_open avi_reader_open #endif userland/containers/avi/avi_writer.c000066400000000000000000001303501421703157200201420ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #define CONTAINER_IS_LITTLE_ENDIAN //#define ENABLE_CONTAINERS_LOG_FORMAT //#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE #define CONTAINER_HELPER_LOG_INDENT(a) 0 #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_writer_utils.h" #include "containers/core/containers_logging.h" /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T *p_ctx ); /****************************************************************************** Defines. ******************************************************************************/ #define AVISF_DISABLED 0x00000001 /*< If set stream should not be enabled by default. */ #define AVIF_HASINDEX 0x00000010 #define AVIF_TRUSTCKTYPE 0x00000800 #define AVIIF_KEYFRAME 0x00000010 #define AVI_INDEX_OF_INDEXES 0x00 #define AVI_INDEX_OF_CHUNKS 0x01 #define AVI_INDEX_DELTAFRAME 0x80000000 #define AVI_INDEX_ENTRY_SIZE 16 #define AVI_SUPER_INDEX_ENTRY_SIZE 16 #define AVI_STD_INDEX_ENTRY_SIZE 8 #define AVI_FRAME_BUFFER_SIZE 100000 #define AVI_TRACKS_MAX 3 #define AVI_AUDIO_CHUNK_SIZE_LIMIT 16384 /*< Watermark limit for data chunks when 'dwSampleSize' is non-zero */ #define AVI_END_CHUNK(ctx) \ do { \ if(STREAM_POSITION(ctx) & 1) WRITE_U8(ctx, 0, "AVI_END_CHUNK"); \ } while(0) #define AVI_PACKET_KEYFRAME (VC_CONTAINER_PACKET_FLAG_KEYFRAME | VC_CONTAINER_PACKET_FLAG_FRAME_END) #define AVI_PACKET_IS_KEYFRAME(flags) (((flags) & AVI_PACKET_KEYFRAME) == AVI_PACKET_KEYFRAME) /****************************************************************************** Type definitions. ******************************************************************************/ typedef struct VC_CONTAINER_TRACK_MODULE_T { uint32_t chunk_index; /**< index of current chunk */ uint32_t chunk_offs; /**< current offset into bytestream consisting of all chunks for this track */ uint32_t sample_size; /**< i.e. 'dwSampleSize' in 'strh' */ uint32_t max_chunk_size; /**< largest chunk written so far */ uint64_t index_offset; /**< Offset to the start of an OpenDML index for this track i.e. 'indx' */ uint32_t index_size; /**< Size of the OpenDML index for this track i.e. 'indx' */ } VC_CONTAINER_TRACK_MODULE_T; typedef struct VC_CONTAINER_MODULE_T { VC_CONTAINER_TRACK_T *tracks[AVI_TRACKS_MAX]; VC_CONTAINER_WRITER_EXTRAIO_T null_io; /**< Null I/O for calculating chunk sizes, etc. */ VC_CONTAINER_WRITER_EXTRAIO_T temp_io; /**< I/O for temporary storage of index data */ int headers_written; uint32_t header_list_offset; /**< Offset to the header list chunk ('hdrl') */ uint32_t header_list_size; /**< Size of the header list chunk ('hdrl') */ uint32_t data_offset; /**< Offset to the start of data packets i.e. the data in the AVI RIFF 'movi' list */ uint64_t data_size; /**< Size of the chunk containing data packets */ uint32_t index_offset; /**< Offset to the start of index data e.g. the data in an 'idx1' list */ unsigned current_track_num; /**< Number of track currently being written */ uint32_t chunk_size; /**< Final size of the current chunk being written (if known) */ uint32_t chunk_data_written; /**< Data written to the current chunk so far */ uint8_t *avi_frame_buffer; /**< For accumulating whole frames when seeking isn't available. */ VC_CONTAINER_PACKET_T frame_packet; /**< Packet header for whole frame. */ VC_CONTAINER_STATUS_T index_status; } VC_CONTAINER_MODULE_T; /****************************************************************************** Local Functions ******************************************************************************/ static void avi_chunk_id_from_track_num( VC_CONTAINER_T *p_ctx, VC_CONTAINER_FOURCC_T *p_chunk_id, unsigned int track_num ) { VC_CONTAINER_TRACK_T *track = p_ctx->tracks[track_num]; VC_CONTAINER_FOURCC_T chunk_id = 0; char track_num_buf[3]; if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) chunk_id = VC_FOURCC('0','0','d','c'); else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) chunk_id = VC_FOURCC('0','0','w','b'); else { /* Note that avi_writer_add_track should ensure this can't happen */ *p_chunk_id = VC_FOURCC('J','U','N','K'); return; } snprintf(track_num_buf, sizeof(track_num_buf), "%02d", track_num); memcpy(&chunk_id, track_num_buf, 2); *p_chunk_id = chunk_id; } /*****************************************************************************/ static void avi_index_chunk_id_from_track_num(VC_CONTAINER_FOURCC_T *p_chunk_id, unsigned int track_num ) { VC_CONTAINER_FOURCC_T chunk_id = 0; char track_num_buf[3]; chunk_id = VC_FOURCC('i','x','0','0'); snprintf(track_num_buf, sizeof(track_num_buf), "%02d", track_num); memcpy(((uint8_t*)&chunk_id) + 2, track_num_buf, 2); *p_chunk_id = chunk_id; } /*****************************************************************************/ static uint32_t avi_num_chunks( VC_CONTAINER_T *p_ctx ) { unsigned int i; uint32_t num_chunks = 0; for (i = 0; i < p_ctx->tracks_num; i++) num_chunks += p_ctx->tracks[i]->priv->module->chunk_index; return num_chunks; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_finish_data_chunk( VC_CONTAINER_T *p_ctx, uint32_t chunk_size ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; if (chunk_size) { /* Rewrite the chunk size, this won't be efficient if it happens often */ if (STREAM_SEEKABLE(p_ctx)) { SEEK(p_ctx, STREAM_POSITION(p_ctx) - chunk_size - 4); WRITE_U32(p_ctx, chunk_size, "Chunk Size"); SKIP_BYTES(p_ctx, chunk_size); } else { LOG_DEBUG(p_ctx, "warning, can't rewrite chunk size, data will be malformed"); status = VC_CONTAINER_ERROR_FAILED; } } AVI_END_CHUNK(p_ctx); if (status != VC_CONTAINER_SUCCESS) status = STREAM_STATUS(p_ctx); return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_write_index_entry( VC_CONTAINER_T *p_ctx, uint8_t track_num, uint32_t chunk_size, int keyframe ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; uint32_t deltaframe = keyframe ? 0 : AVI_INDEX_DELTAFRAME; vc_container_io_write_uint8(module->temp_io.io, track_num); vc_container_io_write_be_uint32(module->temp_io.io, chunk_size | deltaframe); if (module->temp_io.io->status != VC_CONTAINER_SUCCESS) { module->index_status = module->temp_io.io->status; LOG_DEBUG(p_ctx, "warning, couldn't store index data, index data will be incorrect"); } return module->temp_io.io->status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_read_index_entry( VC_CONTAINER_T *p_ctx, unsigned int *p_track_num, uint32_t *p_chunk_size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; uint32_t chunk_size; uint8_t track_num; track_num = vc_container_io_read_uint8(module->temp_io.io); chunk_size = vc_container_io_read_be_uint32(module->temp_io.io); /* This shouldn't really happen if the temporary I/O is reliable */ if (track_num >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_FAILED; *p_track_num = track_num; *p_chunk_size = chunk_size; return module->temp_io.io->status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_write_stream_format_chunk(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, uint32_t chunk_size) { VC_CONTAINER_STATUS_T status; WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','f'), "Chunk ID"); WRITE_U32(p_ctx, chunk_size, "Chunk Size"); if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) status = vc_container_write_bitmapinfoheader(p_ctx, track->format); else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) status = vc_container_write_waveformatex(p_ctx, track->format); if (status != VC_CONTAINER_SUCCESS) return status; AVI_END_CHUNK(p_ctx); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_write_stream_header_chunk(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track) { VC_CONTAINER_FOURCC_T fourcc_type = 0, fourcc_handler = 0; uint32_t flags, scale = 0, rate = 0, div, start = 0, sample_size = 0; uint16_t left = 0, right = 0, top = 0, bottom = 0; uint32_t max_chunk_size, length = 0; WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','h'), "Chunk ID"); WRITE_U32(p_ctx, 56, "Chunk Size"); if (!track->is_enabled) flags = 0; /* AVISF_DISABLED; FIXME: write_media should set this correctly! */ else flags = 0; if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) { fourcc_type = VC_FOURCC('v','i','d','s'); sample_size = 0; scale = track->format->type->video.frame_rate_den; rate = track->format->type->video.frame_rate_num; if (rate == 0 || scale == 0) { LOG_DEBUG(p_ctx, "invalid video framerate (%d/%d)", rate, scale); LOG_DEBUG(p_ctx, "using 30/1 (playback timing will almost certainly be incorrect)"); scale = 1; rate = 30; } top = track->format->type->video.y_offset; left = track->format->type->video.x_offset; bottom = track->format->type->video.y_offset + track->format->type->video.visible_height; right = track->format->type->video.x_offset + track->format->type->video.visible_width; } else if (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) { fourcc_type = VC_FOURCC('a','u','d','s'); sample_size = track->format->type->audio.block_align; scale = 1; if (track->format->type->audio.block_align) rate = (track->format->bitrate / track->format->type->audio.block_align) >> 3; if (rate == 0) { rate = track->format->type->audio.sample_rate ? track->format->type->audio.sample_rate : 32000; LOG_DEBUG(p_ctx, "invalid audio rate, using %d (playback timing will almost certainly be incorrect)", rate); } } else { /* avi_writer_add_track should ensure this can't happen */ vc_container_assert(0); } fourcc_handler = codec_to_vfw_fourcc(track->format->codec); div = vc_container_maths_gcd((int64_t)scale, (int64_t)rate); scale /= div; rate /= div; length = sample_size ? track->priv->module->chunk_offs : track->priv->module->chunk_index; max_chunk_size = track->priv->module->max_chunk_size; track->priv->module->sample_size = sample_size; WRITE_FOURCC(p_ctx, fourcc_type, "fccType"); WRITE_FOURCC(p_ctx, fourcc_handler, "fccHandler"); WRITE_U32(p_ctx, flags, "dwFlags"); WRITE_U16(p_ctx, 0, "wPriority"); WRITE_U16(p_ctx, 0, "wLanguage"); WRITE_U32(p_ctx, 0, "dwInitialFrames"); WRITE_U32(p_ctx, scale, "dwScale"); WRITE_U32(p_ctx, rate, "dwRate"); WRITE_U32(p_ctx, start, "dwStart"); WRITE_U32(p_ctx, length, "dwLength"); WRITE_U32(p_ctx, max_chunk_size, "dwSuggestedBufferSize"); WRITE_U32(p_ctx, 0, "dwQuality"); WRITE_U32(p_ctx, sample_size, "dwSampleSize"); WRITE_U16(p_ctx, left, "rcFrame.left"); WRITE_U16(p_ctx, top, "rcFrame.top"); WRITE_U16(p_ctx, right, "rcFrame.right"); WRITE_U16(p_ctx, bottom, "rcFrame.bottom"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_write_super_index_chunk(VC_CONTAINER_T *p_ctx, unsigned int index_track_num, uint32_t index_size) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[index_track_num]->priv->module; VC_CONTAINER_FOURCC_T chunk_id; uint32_t num_indices = 1; /* FIXME: support for multiple RIFF chunks (AVIX) */ unsigned int i; if(module->null_io.refcount) { /* Assume that we're not actually writing the data, just want know the index chunk size */ WRITE_BYTES(p_ctx, NULL, 8 + 24 + num_indices * (int64_t)AVI_SUPER_INDEX_ENTRY_SIZE); return STREAM_STATUS(p_ctx); } if (track_module->index_offset) WRITE_FOURCC(p_ctx, VC_FOURCC('i','n','d','x'), "Chunk ID"); else WRITE_FOURCC(p_ctx, VC_FOURCC('J','U','N','K'), "Chunk ID"); WRITE_U32(p_ctx, index_size, "Chunk Size"); avi_chunk_id_from_track_num(p_ctx, &chunk_id, index_track_num); WRITE_U16(p_ctx, 4, "wLongsPerEntry"); WRITE_U8(p_ctx, 0, "bIndexSubType"); WRITE_U8(p_ctx, AVI_INDEX_OF_INDEXES, "bIndexType"); WRITE_U32(p_ctx, num_indices, "nEntriesInUse"); WRITE_FOURCC(p_ctx, chunk_id, "dwChunkId"); WRITE_U32(p_ctx, 0, "dwReserved0"); WRITE_U32(p_ctx, 0, "dwReserved1"); WRITE_U32(p_ctx, 0, "dwReserved2"); for (i = 0; i < num_indices; ++i) { uint64_t index_offset = track_module->index_offset; uint32_t chunk_size = track_module->index_size; uint32_t length = track_module->sample_size ? track_module->chunk_offs : track_module->chunk_index; WRITE_U64(p_ctx, index_offset, "qwOffset"); WRITE_U32(p_ctx, chunk_size, "dwSize"); WRITE_U32(p_ctx, length, "dwDuration"); } AVI_END_CHUNK(p_ctx); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_write_stream_header_list(VC_CONTAINER_T *p_ctx, unsigned int track_num, uint32_t list_size) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[track_num]; VC_CONTAINER_STATUS_T status; uint32_t chunk_size = 0; WRITE_FOURCC(p_ctx, VC_FOURCC('L','I','S','T'), "Chunk ID"); WRITE_U32(p_ctx, list_size, "LIST Size"); WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','l'), "Chunk ID"); if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; /* Write the stream header chunk ('strh') */ status = avi_write_stream_header_chunk(p_ctx, track); if (status != VC_CONTAINER_SUCCESS) return status; /* Write the stream format chunk ('strf') */ if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io)) { status = avi_write_stream_format_chunk(p_ctx, track, 0); chunk_size = STREAM_POSITION(p_ctx) - 8; } vc_container_writer_extraio_disable(p_ctx, &module->null_io); status = avi_write_stream_format_chunk(p_ctx, track, chunk_size); if (status != VC_CONTAINER_SUCCESS) return status; /* If the track has DRM data, write it into the 'strd' chunk (we don't write write codec configuration data into 'strd') */ if (track->priv->drmdata && track->priv->drmdata_size) { WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','d'), "Chunk ID"); WRITE_U32(p_ctx, track->priv->drmdata_size, "Chunk Size"); WRITE_BYTES(p_ctx, track->priv->drmdata, track->priv->drmdata_size); AVI_END_CHUNK(p_ctx); if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; } /* Write the super index chunk ('indx') */ if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io)) { status = avi_write_super_index_chunk(p_ctx, track_num, 0); chunk_size = STREAM_POSITION(p_ctx) - 8; } vc_container_writer_extraio_disable(p_ctx, &module->null_io); status = avi_write_super_index_chunk(p_ctx, track_num, chunk_size); if (status != VC_CONTAINER_SUCCESS) return status; AVI_END_CHUNK(p_ctx); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_write_avi_header_chunk(VC_CONTAINER_T *p_ctx) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; uint32_t bitrate = 0, width = 0, height = 0, frame_interval = 0; uint32_t flags, num_chunks = 0, max_video_chunk_size = 0; uint32_t num_streams = p_ctx->tracks_num; unsigned int i; for (i = 0; i < p_ctx->tracks_num; i++) { VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i]; VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[i]->priv->module; if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) { width = track->format->type->video.width; height = track->format->type->video.height; if (track->format->type->video.frame_rate_num) frame_interval = track->format->type->video.frame_rate_den * UINT64_C(1000000) / track->format->type->video.frame_rate_num; num_chunks = track_module->chunk_index; max_video_chunk_size = track_module->max_chunk_size; break; } } flags = (module->index_offset && module->index_status == VC_CONTAINER_SUCCESS) ? (AVIF_HASINDEX | AVIF_TRUSTCKTYPE) : 0; WRITE_FOURCC(p_ctx, VC_FOURCC('a','v','i','h'), "Chunk ID"); WRITE_U32(p_ctx, 56, "Chunk Size"); WRITE_U32(p_ctx, frame_interval, "dwMicroSecPerFrame"); WRITE_U32(p_ctx, bitrate >> 3, "dwMaxBytesPerSec"); WRITE_U32(p_ctx, 0, "dwPaddingGranularity"); WRITE_U32(p_ctx, flags, "dwFlags"); WRITE_U32(p_ctx, num_chunks, "dwTotalFrames"); WRITE_U32(p_ctx, 0, "dwInitialFrames"); WRITE_U32(p_ctx, num_streams, "dwStreams"); WRITE_U32(p_ctx, max_video_chunk_size, "dwSuggestedBufferSize"); WRITE_U32(p_ctx, width, "dwWidth"); WRITE_U32(p_ctx, height, "dwHeight"); WRITE_U32(p_ctx, 0, "dwReserved0"); WRITE_U32(p_ctx, 0, "dwReserved1"); WRITE_U32(p_ctx, 0, "dwReserved2"); WRITE_U32(p_ctx, 0, "dwReserved3"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_write_header_list( VC_CONTAINER_T *p_ctx, uint32_t header_list_size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status; unsigned int i; WRITE_FOURCC(p_ctx, VC_FOURCC('L','I','S','T'), "Chunk ID"); WRITE_U32(p_ctx, header_list_size, "LIST Size"); WRITE_FOURCC(p_ctx, VC_FOURCC('h','d','r','l'), "Chunk ID"); if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; /* Write the main AVI header chunk ('avih') */ if ((status = avi_write_avi_header_chunk(p_ctx)) != VC_CONTAINER_SUCCESS) return status; for (i = 0; i < p_ctx->tracks_num; i++) { uint32_t list_size = 0; /* Write a stream header list chunk ('strl') */ if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io)) { status = avi_write_stream_header_list(p_ctx, i, 0); if (status != VC_CONTAINER_SUCCESS) return status; list_size = STREAM_POSITION(p_ctx) - 8; } vc_container_writer_extraio_disable(p_ctx, &module->null_io); status = avi_write_stream_header_list(p_ctx, i, list_size); if (status != VC_CONTAINER_SUCCESS) return status; } return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_write_headers( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status; uint32_t header_list_offset, header_list_size = 0; /* Write the header list chunk ('hdrl') */ if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io)) { status = avi_write_header_list(p_ctx, 0); if (status != VC_CONTAINER_SUCCESS) return status; header_list_size = STREAM_POSITION(p_ctx) - 8; } vc_container_writer_extraio_disable(p_ctx, &module->null_io); header_list_offset = STREAM_POSITION(p_ctx); status = avi_write_header_list(p_ctx, header_list_size); if (status == VC_CONTAINER_SUCCESS && !module->header_list_offset) { module->header_list_offset = header_list_offset; module->header_list_size = header_list_size; } return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_write_legacy_index_chunk( VC_CONTAINER_T *p_ctx, uint32_t index_size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status; uint32_t chunk_offset = 4; unsigned int track_num; vc_container_assert(8 + avi_num_chunks(p_ctx) * INT64_C(16) <= (int64_t)ULONG_MAX); if(module->null_io.refcount) { /* Assume that we're not actually writing the data, just want know the index size */ WRITE_BYTES(p_ctx, NULL, 8 + avi_num_chunks(p_ctx) * (int64_t)AVI_INDEX_ENTRY_SIZE); return STREAM_STATUS(p_ctx); } module->index_offset = STREAM_POSITION(p_ctx); WRITE_FOURCC(p_ctx, VC_FOURCC('i','d','x','1'), "Chunk ID"); WRITE_U32(p_ctx, index_size, "Chunk Size"); /* Scan through all written entries, convert to appropriate index format */ vc_container_io_seek(module->temp_io.io, INT64_C(0)); while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS) { VC_CONTAINER_FOURCC_T chunk_id; uint32_t chunk_size, flags; status = avi_read_index_entry(p_ctx, &track_num, &chunk_size); if (status != VC_CONTAINER_SUCCESS) break; avi_chunk_id_from_track_num(p_ctx, &chunk_id, track_num); flags = (chunk_size & AVI_INDEX_DELTAFRAME) ? 0 : AVIIF_KEYFRAME; chunk_size &= ~AVI_INDEX_DELTAFRAME; WRITE_FOURCC(p_ctx, chunk_id, "Chunk ID"); WRITE_U32(p_ctx, flags, "dwFlags"); WRITE_U32(p_ctx, chunk_offset, "dwOffset"); WRITE_U32(p_ctx, chunk_size, "dwSize"); chunk_offset += ((chunk_size + 1) & ~1) + 8; } AVI_END_CHUNK(p_ctx); /* Note that currently, we might write a partial index but still set AVIF_HASINDEX */ /* if ( STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS ) module->index_offset = 0 */ return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_write_standard_index_chunk( VC_CONTAINER_T *p_ctx, unsigned int index_track_num, uint32_t index_size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[index_track_num]->priv->module; VC_CONTAINER_STATUS_T status; VC_CONTAINER_FOURCC_T chunk_id; int64_t base_offset = module->data_offset + 12; uint32_t num_chunks = track_module->chunk_index; uint32_t chunk_offset = 4; vc_container_assert(32 + num_chunks * (int64_t)AVI_STD_INDEX_ENTRY_SIZE <= (int64_t)ULONG_MAX); if(module->null_io.refcount) { /* Assume that we're not actually writing the data, just want know the index chunk size */ WRITE_BYTES(p_ctx, NULL, 8 + 24 + num_chunks * INT64_C(8)); return STREAM_STATUS(p_ctx); } track_module->index_offset = STREAM_POSITION(p_ctx); track_module->index_size = index_size ? (index_size - 8) : 0; avi_index_chunk_id_from_track_num(&chunk_id, index_track_num); WRITE_FOURCC(p_ctx, chunk_id, "Chunk ID"); WRITE_U32(p_ctx, index_size, "Chunk Size"); avi_chunk_id_from_track_num(p_ctx, &chunk_id, index_track_num); WRITE_U16(p_ctx, 2, "wLongsPerEntry"); WRITE_U8(p_ctx, 0, "bIndexSubType"); WRITE_U8(p_ctx, AVI_INDEX_OF_CHUNKS, "bIndexType"); WRITE_U32(p_ctx, num_chunks, "nEntriesInUse"); WRITE_FOURCC(p_ctx, chunk_id, "dwChunkId"); WRITE_U64(p_ctx, base_offset, "qwBaseOffset"); WRITE_U32(p_ctx, 0, "dwReserved"); /* Scan through all written entries, convert to appropriate index format */ vc_container_io_seek(module->temp_io.io, INT64_C(0)); while(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS) { uint32_t chunk_size; unsigned int track_num; status = avi_read_index_entry(p_ctx, &track_num, &chunk_size); if (status != VC_CONTAINER_SUCCESS) break; if(track_num != index_track_num) continue; WRITE_U32(p_ctx, chunk_offset, "dwOffset"); WRITE_U32(p_ctx, chunk_size, "dwSize"); chunk_offset += ((chunk_size + 1) & ~(1 | AVI_INDEX_DELTAFRAME)) + 12; } AVI_END_CHUNK(p_ctx); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_write_legacy_index_data( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; uint32_t chunk_size = 0; /* Write the legacy index chunk ('idx1') */ if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io)) { status = avi_write_legacy_index_chunk(p_ctx, 0); if (status != VC_CONTAINER_SUCCESS) return status; chunk_size = STREAM_POSITION(p_ctx) - 8; } vc_container_writer_extraio_disable(p_ctx, &module->null_io); status = avi_write_legacy_index_chunk(p_ctx, chunk_size); return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_write_standard_index_data( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; uint32_t chunk_size = 0; unsigned int i; /* Write the standard index chunks ('ix00') */ for (i = 0; i < p_ctx->tracks_num; i++) { if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io)) { status = avi_write_standard_index_chunk(p_ctx, i, 0); if (status != VC_CONTAINER_SUCCESS) return status; chunk_size = STREAM_POSITION(p_ctx) - 8; } vc_container_writer_extraio_disable(p_ctx, &module->null_io); status = avi_write_standard_index_chunk(p_ctx, i, chunk_size); if (status != VC_CONTAINER_SUCCESS) return status; } return status; } /*****************************************************************************/ static int64_t avi_calculate_file_size( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; int64_t filesize = 0; int refcount; /* Start from current file position */ filesize = STREAM_POSITION(p_ctx); refcount = vc_container_writer_extraio_enable(p_ctx, &module->null_io); vc_container_assert(refcount == 0); /* Although perfectly harmless, we should not be called with the null i/o enabled. */ VC_CONTAINER_PARAM_UNUSED(refcount); do { /* If we know what the final size of the chunk is going to be, we can use that here to avoid writing a partial final packet */ WRITE_BYTES(p_ctx, NULL, p_packet->frame_size ? p_packet->frame_size : p_packet->size); AVI_END_CHUNK(p_ctx); /* Index entries for the chunk */ WRITE_BYTES(p_ctx, NULL, AVI_INDEX_ENTRY_SIZE + AVI_STD_INDEX_ENTRY_SIZE); /* Current standard index data */ if (avi_write_standard_index_data(p_ctx) != VC_CONTAINER_SUCCESS) break; /* Current legacy index data */ status = avi_write_legacy_index_data(p_ctx); if (status != VC_CONTAINER_SUCCESS) break; } while(0); filesize += STREAM_POSITION(p_ctx); vc_container_writer_extraio_disable(p_ctx, &module->null_io); return filesize; } /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ static VC_CONTAINER_STATUS_T avi_writer_write( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_TRACK_T *track = NULL; VC_CONTAINER_TRACK_MODULE_T *track_module = NULL; /* Check we have written headers before any data */ if(!module->headers_written) { if ((status = avi_write_headers(p_ctx)) != VC_CONTAINER_SUCCESS) return status; module->headers_written = 1; } /* Check that we have started the 'movi' list */ if (!module->data_offset) { module->data_offset = STREAM_POSITION(p_ctx); vc_container_assert(module->data_offset != INT64_C(0)); WRITE_FOURCC(p_ctx, VC_FOURCC('L','I','S','T'), "Chunk ID"); WRITE_U32(p_ctx, 0, "LIST Size"); WRITE_FOURCC(p_ctx, VC_FOURCC('m','o','v','i'), "Chunk ID"); if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; } /* If the container is passing in a frame from a new track but we arent't finished with a chunk from another track we need to finish that chunk first */ if (module->chunk_data_written && p_packet->track != module->current_track_num) { track_module = p_ctx->tracks[module->current_track_num]->priv->module; status = avi_finish_data_chunk(p_ctx, module->chunk_data_written); avi_write_index_entry(p_ctx, module->current_track_num, module->chunk_data_written, 0); track_module->chunk_index++; track_module->chunk_offs += module->chunk_data_written; track_module->max_chunk_size = MAX(track_module->max_chunk_size, module->chunk_data_written); module->chunk_data_written = 0; if (status != VC_CONTAINER_SUCCESS) return status; } /* Check we are not about to go over the limit of total number of chunks */ if (avi_num_chunks(p_ctx) == (uint32_t)ULONG_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; if(STREAM_SEEKABLE(p_ctx)) { /* Check we are not about to go over the maximum file size */ if (avi_calculate_file_size(p_ctx, p_packet) >= (int64_t)ULONG_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; } /* FIXME: are we expected to handle this case or should it be picked up by the above layer? */ vc_container_assert(!(module->chunk_data_written && (p_packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START))); track = p_ctx->tracks[p_packet->track]; track_module = p_ctx->tracks[p_packet->track]->priv->module; module->current_track_num = p_packet->track; if (module->chunk_data_written == 0) { /* This is the first fragment of the chunk */ VC_CONTAINER_FOURCC_T chunk_id; uint32_t chunk_size; avi_chunk_id_from_track_num(p_ctx, &chunk_id, p_packet->track); if (p_packet->frame_size) { /* We know what the final size of the chunk is going to be */ chunk_size = module->chunk_size = p_packet->frame_size; } else { chunk_size = p_packet->size; module->chunk_size = 0; } WRITE_FOURCC(p_ctx, chunk_id, "Chunk ID"); if(STREAM_SEEKABLE(p_ctx) || p_packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END) { /* If the output stream can seek we can fix up the frame size later, and if the * packet holds the whole frame we won't need to, so write data straight out. */ WRITE_U32(p_ctx, chunk_size, "Chunk Size"); WRITE_BYTES(p_ctx, p_packet->data, p_packet->size); } else { vc_container_assert(module->avi_frame_buffer); if(p_packet->size > AVI_FRAME_BUFFER_SIZE) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; module->frame_packet = *p_packet; module->frame_packet.data = module->avi_frame_buffer; memcpy(module->frame_packet.data, p_packet->data, module->frame_packet.size); } module->chunk_data_written = p_packet->size; } else { if(module->frame_packet.size > 0 && module->avi_frame_buffer) { if(module->frame_packet.size > 0) { if(module->frame_packet.size + p_packet->size > AVI_FRAME_BUFFER_SIZE) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; memcpy(module->frame_packet.data + module->frame_packet.size, p_packet->data, p_packet->size); module->frame_packet.size += p_packet->size; } } else { WRITE_BYTES(p_ctx, p_packet->data, p_packet->size); } module->chunk_data_written += p_packet->size; } if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; if ((p_packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END) || (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO && track->format->type->audio.block_align && module->chunk_data_written > AVI_AUDIO_CHUNK_SIZE_LIMIT)) { if(module->frame_packet.size > 0) { WRITE_U32(p_ctx, module->frame_packet.size, "Chunk Size"); WRITE_BYTES(p_ctx, module->frame_packet.data, module->frame_packet.size); p_packet->size = module->frame_packet.size; module->frame_packet.size = 0; } if (!module->chunk_size && module->chunk_data_written > p_packet->size) { /* The chunk size needs to be rewritten */ status = avi_finish_data_chunk(p_ctx, module->chunk_data_written); } else { status = avi_finish_data_chunk(p_ctx, 0); } if(!STREAM_SEEKABLE(p_ctx)) { /* If we are streaming then flush to avoid delaying data transport. */ vc_container_control(p_ctx, VC_CONTAINER_CONTROL_IO_FLUSH); } if(STREAM_SEEKABLE(p_ctx)) { /* Keep track of data written so we can check we don't exceed file size and also for doing * index fix-ups, but only do this if we are writing to a seekable IO. */ avi_write_index_entry(p_ctx, p_packet->track, module->chunk_data_written, AVI_PACKET_IS_KEYFRAME(p_packet->flags)); } track_module->chunk_index++; track_module->chunk_offs += module->chunk_data_written; track_module->max_chunk_size = MAX(track_module->max_chunk_size, module->chunk_data_written); module->chunk_data_written = 0; if (status != VC_CONTAINER_SUCCESS) return status; } return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_writer_close( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; unsigned int i; /* If we arent't finished with a chunk we need to finish it first */ if (module->chunk_data_written) { VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track_num]->priv->module; status = avi_finish_data_chunk(p_ctx, module->chunk_data_written); if (status != VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "warning, writing failed, last chunk truncated"); } avi_write_index_entry(p_ctx, module->current_track_num, module->chunk_data_written, 0); track_module->chunk_index++; track_module->chunk_offs += module->chunk_data_written; track_module->max_chunk_size = MAX(track_module->max_chunk_size, module->chunk_data_written); module->chunk_data_written = 0; } if(STREAM_SEEKABLE(p_ctx)) { uint32_t filesize; /* Write standard index data before finalising the size of the 'movi' list */ status = avi_write_standard_index_data(p_ctx); if (status != VC_CONTAINER_SUCCESS) { module->index_status = status; LOG_DEBUG(p_ctx, "warning, writing standard index data failed, file will be malformed"); } /* FIXME: support for multiple RIFF chunks (AVIX) */ module->data_size = STREAM_POSITION(p_ctx) - module->data_offset - 8; /* Now write the legacy index */ status = avi_write_legacy_index_data(p_ctx); if (status != VC_CONTAINER_SUCCESS) { module->index_status = status; LOG_DEBUG(p_ctx, "warning, writing legacy index data failed, file will be malformed"); } /* If we can, do the necessary fixups for values not know at the time of writing chunk headers */ /* Rewrite the AVI RIFF chunk size */ filesize = (uint32_t)STREAM_POSITION(p_ctx); SEEK(p_ctx, 4); WRITE_U32(p_ctx, filesize, "fileSize"); if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "warning, rewriting 'fileSize' failed, file will be malformed"); } /* Rewrite the header list chunk ('hdrl') */ SEEK(p_ctx, module->header_list_offset); status = avi_write_header_list(p_ctx, module->header_list_size); if (status != VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "warning, rewriting 'hdrl' failed, file will be malformed"); } /* Rewrite the AVI RIFF 'movi' list size */ SEEK(p_ctx, module->data_offset + 4); WRITE_U32(p_ctx, module->data_size, "Chunk Size"); if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "warning, rewriting 'movi' list size failed, file will be malformed"); } } vc_container_writer_extraio_delete(p_ctx, &module->null_io); if(module->temp_io.io) vc_container_writer_extraio_delete(p_ctx, &module->temp_io); for(i = 0; i < p_ctx->tracks_num; i++) vc_container_free_track(p_ctx, p_ctx->tracks[i]); p_ctx->tracks_num = 0; p_ctx->tracks = NULL; if(module->avi_frame_buffer) free(module->avi_frame_buffer); free(module); return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_writer_add_track( VC_CONTAINER_T *p_ctx, VC_CONTAINER_ES_FORMAT_T *format ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_TRACK_T *track = NULL; if (module->headers_written) return VC_CONTAINER_ERROR_FAILED; /* FIXME: should we check the format in more detail? */ if((format->es_type != VC_CONTAINER_ES_TYPE_VIDEO && format->es_type != VC_CONTAINER_ES_TYPE_AUDIO) || format->codec == VC_CONTAINER_CODEC_UNKNOWN) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; if(!(format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; /* Allocate new track */ if(p_ctx->tracks_num >= AVI_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; p_ctx->tracks[p_ctx->tracks_num] = track = vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; if(format->extradata_size) { status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size ); if(status) goto error; } status = vc_container_format_copy(track->format, format, format->extradata_size); if(status) goto error; p_ctx->tracks_num++; return VC_CONTAINER_SUCCESS; error: vc_container_free_track(p_ctx, track); return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avi_writer_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status; switch(operation) { case VC_CONTAINER_CONTROL_TRACK_ADD: { VC_CONTAINER_ES_FORMAT_T *format = (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * ); return avi_writer_add_track(p_ctx, format); } case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: { if(!module->headers_written) { if ((status = avi_write_headers(p_ctx)) != VC_CONTAINER_SUCCESS) return status; module->headers_written = 1; return VC_CONTAINER_SUCCESS; } else return VC_CONTAINER_ERROR_FAILED; } default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; } } /****************************************************************************** Global function definitions. ******************************************************************************/ VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T *p_ctx ) { const char *extension = vc_uri_path_extension(p_ctx->priv->uri); VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = 0; /* Check if the user has specified a container */ vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); /* Check we're the right writer for this */ if(!extension) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if(strcasecmp(extension, "avi") && strcasecmp(extension, "divx")) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Allocate our context */ module = malloc(sizeof(*module)); if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); p_ctx->priv->module = module; /* Create a null i/o writer to help us out in writing our data */ status = vc_container_writer_extraio_create_null(p_ctx, &module->null_io); if(status != VC_CONTAINER_SUCCESS) goto error; if(STREAM_SEEKABLE(p_ctx)) { /* Create a temporary i/o writer for storage of index data while we are writing */ status = vc_container_writer_extraio_create_temp(p_ctx, &module->temp_io); if(status != VC_CONTAINER_SUCCESS) goto error; } else { module->avi_frame_buffer = malloc(AVI_FRAME_BUFFER_SIZE); if(!module->avi_frame_buffer) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } } module->frame_packet.size = 0; p_ctx->tracks = module->tracks; /* Write the RIFF chunk descriptor */ WRITE_FOURCC(p_ctx, VC_FOURCC('R','I','F','F'), "RIFF ID"); WRITE_U32(p_ctx, 0, "fileSize"); WRITE_FOURCC(p_ctx, VC_FOURCC('A','V','I',' '), "fileType"); if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) goto error; p_ctx->priv->pf_close = avi_writer_close; p_ctx->priv->pf_write = avi_writer_write; p_ctx->priv->pf_control = avi_writer_control; return VC_CONTAINER_SUCCESS; error: LOG_DEBUG(p_ctx, "error opening stream"); p_ctx->tracks_num = 0; p_ctx->tracks = NULL; if(module) { if(module->avi_frame_buffer) free(module->avi_frame_buffer); free(module); } return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak writer_open avi_writer_open #endif userland/containers/binary/000077500000000000000000000000001421703157200163265ustar00rootroot00000000000000userland/containers/binary/CMakeLists.txt000066400000000000000000000010561421703157200210700ustar00rootroot00000000000000# Container module needs to go in as a plugins so different prefix # and install path set(CMAKE_SHARED_LIBRARY_PREFIX "") # Make sure the compiler can find the necessary include files include_directories (../..) add_library(reader_binary ${LIBRARY_TYPE} binary_reader.c) target_link_libraries(reader_binary containers) install(TARGETS reader_binary DESTINATION ${VMCS_PLUGIN_DIR}) add_library(writer_binary ${LIBRARY_TYPE} binary_writer.c) target_link_libraries(writer_binary containers) install(TARGETS writer_binary DESTINATION ${VMCS_PLUGIN_DIR}) userland/containers/binary/binary_reader.c000066400000000000000000000243641421703157200213110ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_logging.h" /****************************************************************************** Defines. ******************************************************************************/ #define DEFAULT_BLOCK_SIZE (1024*16) /* Work-around for JPEG because our decoder expects that much at the start */ #define DEFAULT_JPEG_BLOCK_SIZE (1024*80) /****************************************************************************** Type definitions ******************************************************************************/ typedef struct VC_CONTAINER_MODULE_T { VC_CONTAINER_TRACK_T *track; unsigned int default_block_size; unsigned int block_size; bool init; VC_CONTAINER_STATUS_T status; } VC_CONTAINER_MODULE_T; static struct { const char *ext; VC_CONTAINER_ES_TYPE_T type; VC_CONTAINER_FOURCC_T codec; } extension_to_format_table[] = { /* Audio */ {"mp3", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_MPGA}, {"aac", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_MP4A}, {"adts", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_MP4A}, {"ac3", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_AC3}, {"ec3", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_EAC3}, {"amr", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_AMRNB}, {"awb", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_AMRWB}, {"evrc", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_EVRC}, {"dts", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_DTS}, /* Video */ {"m1v", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP1V}, {"m2v", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP2V}, {"m4v", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP4V}, {"mp4v", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP4V}, {"h263", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H263}, {"263", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H263}, {"h264", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H264}, {"264", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H264}, {"mvc", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MVC}, {"vc1l", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_WVC1}, /* Image */ {"gif", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_GIF}, {"jpg", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_JPEG}, {"jpeg", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_JPEG}, {"png", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_PNG}, {"ppm", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_PPM}, {"tga", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_TGA}, {"bmp", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_BMP}, {"bin", 0, 0}, {0, 0, 0} }; static struct { const char *ext; VC_CONTAINER_ES_TYPE_T type; VC_CONTAINER_FOURCC_T codec; } bin_extension_to_format_table[] = { {"m4v.bin", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP4V}, {"263.bin", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H263}, {"264.bin", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H264}, {0, 0, 0} }; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T binary_reader_open( VC_CONTAINER_T * ); /****************************************************************************** Local Functions ******************************************************************************/ /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ static VC_CONTAINER_STATUS_T binary_reader_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *packet, uint32_t flags ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; unsigned int size; if(module->status != VC_CONTAINER_SUCCESS) return module->status; if(!module->block_size) { module->block_size = module->default_block_size; module->init = 0; } packet->size = module->block_size; packet->dts = packet->pts = VC_CONTAINER_TIME_UNKNOWN; if(module->init) packet->dts = packet->pts = 0; packet->track = 0; packet->flags = 0; if(flags & VC_CONTAINER_READ_FLAG_SKIP) { size = SKIP_BYTES(p_ctx, module->block_size); module->block_size -= size; module->status = STREAM_STATUS(p_ctx); return module->status; } if(flags & VC_CONTAINER_READ_FLAG_INFO) return VC_CONTAINER_SUCCESS; size = MIN(module->block_size, packet->buffer_size); size = READ_BYTES(p_ctx, packet->data, size); module->block_size -= size; packet->size = size; module->status = size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(p_ctx); return module->status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T binary_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_PARAM_UNUSED(module); VC_CONTAINER_PARAM_UNUSED(offset); VC_CONTAINER_PARAM_UNUSED(mode); VC_CONTAINER_PARAM_UNUSED(flags); return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T binary_reader_close( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T binary_reader_open( VC_CONTAINER_T *p_ctx ) { const char *extension = vc_uri_path_extension(p_ctx->priv->uri); VC_CONTAINER_MODULE_T *module = 0; VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; VC_CONTAINER_ES_TYPE_T es_type = 0; VC_CONTAINER_FOURCC_T codec = 0; unsigned int i; /* Check if the user has specified a container */ vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); /* Check if the extension is supported */ if(!extension || !vc_uri_path(p_ctx->priv->uri)) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; for(i = 0; extension_to_format_table[i].ext; i++) { if(strcasecmp(extension, extension_to_format_table[i].ext)) continue; es_type = extension_to_format_table[i].type; codec = extension_to_format_table[i].codec; break; } if(!extension_to_format_table[i].ext) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* If this a .bin file we look in our bin list */ for(i = 0; !codec && bin_extension_to_format_table[i].ext; i++) { if(!strstr(vc_uri_path(p_ctx->priv->uri), bin_extension_to_format_table[i].ext) && !strstr(extension, bin_extension_to_format_table[i].ext)) continue; es_type = bin_extension_to_format_table[i].type; codec = bin_extension_to_format_table[i].codec; break; } if(!codec) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Allocate our context */ module = malloc(sizeof(*module)); if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); p_ctx->priv->module = module; p_ctx->tracks_num = 1; p_ctx->tracks = &module->track; p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0); if(!p_ctx->tracks[0]) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; p_ctx->tracks[0]->format->es_type = es_type; p_ctx->tracks[0]->format->codec = codec; p_ctx->tracks[0]->is_enabled = true; module->default_block_size = DEFAULT_BLOCK_SIZE; if(codec == VC_CONTAINER_CODEC_JPEG) module->default_block_size = DEFAULT_JPEG_BLOCK_SIZE; module->block_size = module->default_block_size; module->init = 1; /* * We now have all the information we really need to start playing the stream */ p_ctx->priv->pf_close = binary_reader_close; p_ctx->priv->pf_read = binary_reader_read; p_ctx->priv->pf_seek = binary_reader_seek; return VC_CONTAINER_SUCCESS; error: LOG_DEBUG(p_ctx, "binary: error opening stream (%i)", status); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak reader_open binary_reader_open #endif userland/containers/binary/binary_writer.c000066400000000000000000000144171421703157200213610ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_logging.h" /****************************************************************************** Supported extensions ******************************************************************************/ static const char *extensions[] = { "mp3", "aac", "adts", "ac3", "ec3", "amr", "awb", "evrc", "dts", "m1v", "m2v", "mp4v", "h263", "263", "h264", "264", "mvc", "bin", 0 }; /****************************************************************************** Type definitions ******************************************************************************/ typedef struct VC_CONTAINER_MODULE_T { VC_CONTAINER_TRACK_T *track; } VC_CONTAINER_MODULE_T; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T binary_writer_open( VC_CONTAINER_T * ); /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ static VC_CONTAINER_STATUS_T binary_writer_close( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T binary_writer_write( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *packet ) { WRITE_BYTES(p_ctx, packet->data, packet->size); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T binary_writer_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args ) { VC_CONTAINER_ES_FORMAT_T *format; VC_CONTAINER_TRACK_T *track; VC_CONTAINER_STATUS_T status; switch(operation) { case VC_CONTAINER_CONTROL_TRACK_ADD: format = (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * ); /* Allocate and initialise track data */ if(p_ctx->tracks_num >= 1) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; p_ctx->tracks[p_ctx->tracks_num] = track = vc_container_allocate_track(p_ctx, 0); if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; if(format->extradata_size) { status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size ); if(status != VC_CONTAINER_SUCCESS) { vc_container_free_track(p_ctx, track); return status; } WRITE_BYTES(p_ctx, format->extradata, format->extradata_size); } vc_container_format_copy(track->format, format, format->extradata_size); p_ctx->tracks_num++; return VC_CONTAINER_SUCCESS; case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: return VC_CONTAINER_SUCCESS; default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; } } /*****************************************************************************/ VC_CONTAINER_STATUS_T binary_writer_open( VC_CONTAINER_T *p_ctx ) { const char *extension = vc_uri_path_extension(p_ctx->priv->uri); VC_CONTAINER_MODULE_T *module = 0; VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; unsigned int i; /* Check if the user has specified a container */ vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); /* Check we're the right writer for this */ if(!extension) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; for(i = 0; extensions[i]; i++) if(!strcasecmp(extension, extensions[i])) break; if(!extensions[i]) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Allocate our context */ module = malloc(sizeof(*module)); if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); p_ctx->priv->module = module; p_ctx->tracks = &module->track; p_ctx->priv->pf_close = binary_writer_close; p_ctx->priv->pf_write = binary_writer_write; p_ctx->priv->pf_control = binary_writer_control; return VC_CONTAINER_SUCCESS; error: LOG_DEBUG(p_ctx, "binary: error opening stream (%i)", status); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak writer_open binary_writer_open #endif userland/containers/containers.h000066400000000000000000001046151421703157200173670ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_CONTAINERS_H #define VC_CONTAINERS_H /** \file containers.h * Public API for container readers and writers */ #ifdef __cplusplus extern "C" { #endif #include "containers/containers_types.h" /** \defgroup VcContainerApi Container API * API for container readers and writers */ /* @{ */ /** Status codes returned by the container API */ typedef enum { VC_CONTAINER_SUCCESS = 0, /**< No error */ VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED, /**< Format of container is not supported */ VC_CONTAINER_ERROR_FORMAT_FEATURE_NOT_SUPPORTED, /**< Format of container uses unsupported features */ VC_CONTAINER_ERROR_FORMAT_INVALID, /**< Format of container is invalid */ VC_CONTAINER_ERROR_CORRUPTED, /**< Container is corrupted */ VC_CONTAINER_ERROR_URI_NOT_FOUND, /**< URI could not be found */ VC_CONTAINER_ERROR_URI_OPEN_FAILED, /**< URI could not be opened */ VC_CONTAINER_ERROR_OUT_OF_MEMORY, /**< Out of memory */ VC_CONTAINER_ERROR_OUT_OF_SPACE, /**< Out of disk space (used when writing) */ VC_CONTAINER_ERROR_OUT_OF_RESOURCES, /**< Out of resources (other than memory) */ VC_CONTAINER_ERROR_EOS, /**< End of stream reached */ VC_CONTAINER_ERROR_LIMIT_REACHED, /**< User defined limit reached (used when writing) */ VC_CONTAINER_ERROR_BUFFER_TOO_SMALL, /**< Given buffer is too small for data to be copied */ VC_CONTAINER_ERROR_INCOMPLETE_DATA, /**< Requested data is incomplete */ VC_CONTAINER_ERROR_NO_TRACK_AVAILABLE, /**< Container doesn't have any track */ VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED, /**< Format of the track is not supported */ VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION, /**< The requested operation is not supported */ VC_CONTAINER_ERROR_INVALID_ARGUMENT, /**< The argument provided is invalid */ VC_CONTAINER_ERROR_CONTINUE, /**< The requested operation was interrupted and needs to be tried again */ VC_CONTAINER_ERROR_ABORTED, /**< The requested operation was aborted */ VC_CONTAINER_ERROR_NOT_FOUND, /**< The requested data was not found */ VC_CONTAINER_ERROR_DRM_NOT_AUTHORIZED, /**< The DRM was not authorized */ VC_CONTAINER_ERROR_DRM_EXPIRED, /**< The DRM has expired */ VC_CONTAINER_ERROR_DRM_FAILED, /**< Generic DRM error */ VC_CONTAINER_ERROR_FAILED, /**< Generic error */ VC_CONTAINER_ERROR_NOT_READY /**< The container was not yet able to carry out the operation. */ } VC_CONTAINER_STATUS_T; /** Four Character Code type used to identify codecs, etc. */ typedef uint32_t VC_CONTAINER_FOURCC_T; /** Type definition for language codes. * Language are defined as ISO639 Alpha-3 codes (http://en.wikipedia.org/wiki/List_of_ISO_639-2_codes) */ typedef uint8_t VC_CONTAINER_LANGUAGE_T[3]; /** Enumeration of the character encodings supported. */ typedef enum { VC_CONTAINER_CHAR_ENCODING_UNKNOWN = 0, /**< Encoding is unknown */ VC_CONTAINER_CHAR_ENCODING_UTF8 /**< UTF8 encoding */ } VC_CONTAINER_CHAR_ENCODING_T; /** \name Container Capabilities * The following flags are exported by containers to describe their capabilities */ /* @{ */ /** Type definition for container capabilities */ typedef uint32_t VC_CONTAINER_CAPABILITIES_T; /** The container can seek */ #define VC_CONTAINER_CAPS_CAN_SEEK 0x1 /** Seeking is fast. The absence of this flag can be used as a hint to avoid seeking as much as possible */ #define VC_CONTAINER_CAPS_SEEK_IS_FAST 0x2 /** The container controls the pace at which it is reading the data */ #define VC_CONTAINER_CAPS_CAN_CONTROL_PACE 0x4 /** The container provides an index. This basically means that seeking will be precise and fast */ #define VC_CONTAINER_CAPS_HAS_INDEX 0x8 /** The container provides keyframe information */ #define VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG 0x10 /** The container supports adding tracks after TRACK_ADD_DONE control message has been sent */ #define VC_CONTAINER_CAPS_DYNAMIC_TRACK_ADD 0x20 /** The container supports forcing reading of a given track */ #define VC_CONTAINER_CAPS_FORCE_TRACK 0x40 /* @} */ /** \defgroup VcContainerMetadata Container Metadata * Container metadata contains descriptive information which is associated with the multimedia data */ /* @{ */ /** Enumeration of the different metadata keys available. */ typedef enum { /* Metadata of global scope */ VC_CONTAINER_METADATA_KEY_TITLE = VC_FOURCC('t','i','t','l'), VC_CONTAINER_METADATA_KEY_ARTIST = VC_FOURCC('a','r','t','i'), VC_CONTAINER_METADATA_KEY_ALBUM = VC_FOURCC('a','l','b','m'), VC_CONTAINER_METADATA_KEY_DESCRIPTION = VC_FOURCC('d','e','s','c'), VC_CONTAINER_METADATA_KEY_YEAR = VC_FOURCC('y','e','a','r'), VC_CONTAINER_METADATA_KEY_GENRE = VC_FOURCC('g','e','n','r'), VC_CONTAINER_METADATA_KEY_TRACK = VC_FOURCC('t','r','a','k'), VC_CONTAINER_METADATA_KEY_LYRICS = VC_FOURCC('l','y','r','x'), VC_CONTAINER_METADATA_KEY_UNKNOWN = 0 } VC_CONTAINER_METADATA_KEY_T; /** Definition of the metadata type. * This type is used to store one element of metadata */ typedef struct VC_CONTAINER_METADATA_T { /** Identifier for the type of metadata the value refers to. * Using an enum for the id will mean that a list of possible values will have to be * defined and maintained. This might limit extensibility and customisation.\n * Maybe it would be better to use a FOURCC or even a string here. */ VC_CONTAINER_METADATA_KEY_T key; VC_CONTAINER_LANGUAGE_T language; /**< Language code for the metadata */ VC_CONTAINER_CHAR_ENCODING_T encoding; /**< Encoding of the metadata */ /** Metadata value. This value is defined as null-terminated UTF-8 string.\n * Do we want to support other types than strings (e.g. integer) ?\n * We need an encoding conversion library! */ char *value; /** Size of the memory area reserved for metadata value (including any * terminating characters). */ unsigned int size; } VC_CONTAINER_METADATA_T; /* @} */ /** \defgroup VcContainerESFormat Container Elementary Stream Format * This describes the format of an elementary stream associated with a track */ /* @{ */ /** Enumeration of the different types of elementary streams. * This divides elementary streams into 4 big categories. */ typedef enum { VC_CONTAINER_ES_TYPE_UNKNOWN, /**< Unknown elementary stream type */ VC_CONTAINER_ES_TYPE_AUDIO, /**< Audio elementary stream */ VC_CONTAINER_ES_TYPE_VIDEO, /**< Video elementary stream */ VC_CONTAINER_ES_TYPE_SUBPICTURE /**< Sub-picture elementary stream (e.g. subtitles, overlays) */ } VC_CONTAINER_ES_TYPE_T; /** Definition of a video format. * This describes the properties specific to a video stream */ typedef struct VC_CONTAINER_VIDEO_FORMAT_T { uint32_t width; /**< Width of the frame */ uint32_t height; /**< Height of the frame */ uint32_t visible_width; /**< Width of the visible area of the frame */ uint32_t visible_height; /**< Height of the visible area of the frame */ uint32_t x_offset; /**< Offset to the start of the visible width */ uint32_t y_offset; /**< Offset to the start of the visible height */ uint32_t frame_rate_num; /**< Frame rate numerator */ uint32_t frame_rate_den; /**< Frame rate denominator */ uint32_t par_num; /**< Pixel aspect ratio numerator */ uint32_t par_den; /**< Pixel aspect ratio denominator */ } VC_CONTAINER_VIDEO_FORMAT_T; /** Enumeration for the different channel locations */ typedef enum { VC_CONTAINER_AUDIO_CHANNEL_LEFT = 0, /**< Left channel */ VC_CONTAINER_AUDIO_CHANNEL_RIGHT, /**< Right channel */ VC_CONTAINER_AUDIO_CHANNEL_CENTER, /**< Center channel */ VC_CONTAINER_AUDIO_CHANNEL_LOW_FREQUENCY, /**< Low frequency channel */ VC_CONTAINER_AUDIO_CHANNEL_BACK_LEFT, /**< Back left channel */ VC_CONTAINER_AUDIO_CHANNEL_BACK_RIGHT, /**< Back right channel */ VC_CONTAINER_AUDIO_CHANNEL_BACK_CENTER, /**< Back center channel */ VC_CONTAINER_AUDIO_CHANNEL_SIDE_LEFT, /**< Side left channel */ VC_CONTAINER_AUDIO_CHANNEL_SIDE_RIGHT, /**< Side right channel */ VC_CONTAINER_AUDIO_CHANNELS_MAX = 32 /**< Maximum number of channels supported */ } VC_CONTAINER_AUDIO_CHANNEL_T; /** \name Audio format flags * \anchor audioformatflags * The following flags describe properties of an audio stream */ /* @{ */ #define VC_CONTAINER_AUDIO_FORMAT_FLAG_CHANNEL_MAPPING 0x1 /**< Channel mapping available */ /* @} */ /** Definition of an audio format. * This describes the properties specific to an audio stream */ typedef struct VC_CONTAINER_AUDIO_FORMAT_T { uint32_t channels; /**< Number of audio channels */ uint32_t sample_rate; /**< Sample rate */ uint32_t bits_per_sample; /**< Bits per sample */ uint32_t block_align; /**< Size of a block of data */ uint32_t flags; /**< Flags describing the audio format. * See \ref audioformatflags "Audio format flags". */ /** Mapping of the channels in order of appearance */ VC_CONTAINER_AUDIO_CHANNEL_T channel_mapping[VC_CONTAINER_AUDIO_CHANNELS_MAX]; uint16_t gap_delay; /**< Delay introduced by the encoder. Used for gapless playback */ uint16_t gap_padding; /**< Padding introduced by the encoder. Used for gapless playback */ /** Replay gain information. First element is the track information and the second * is the album information. */ struct { float peak; /**< Peak value (full range is 1.0) */ float gain; /**< Gain value in dB */ } replay_gain[2]; } VC_CONTAINER_AUDIO_FORMAT_T; /** Definition of a subpicture format. * This describes the properties specific to a subpicture stream */ typedef struct VC_CONTAINER_SUBPICTURE_FORMAT_T { VC_CONTAINER_CHAR_ENCODING_T encoding; /**< Encoding for text based subpicture formats */ uint32_t x_offset; /**< Width offset to the start of the subpicture */ uint32_t y_offset; /**< Height offset to the start of the subpicture */ } VC_CONTAINER_SUBPICTURE_FORMAT_T; /** \name Elementary stream format flags * \anchor esformatflags * The following flags describe properties of an elementary stream */ /* @{ */ #define VC_CONTAINER_ES_FORMAT_FLAG_FRAMED 0x1 /**< Elementary stream is framed */ /* @} */ /** Definition of the type specific format. * This describes the type specific information of the elementary stream. */ typedef union { VC_CONTAINER_AUDIO_FORMAT_T audio; /**< Audio specific information */ VC_CONTAINER_VIDEO_FORMAT_T video; /**< Video specific information */ VC_CONTAINER_SUBPICTURE_FORMAT_T subpicture; /**< Subpicture specific information */ } VC_CONTAINER_ES_SPECIFIC_FORMAT_T; /** Definition of an elementary stream format */ typedef struct VC_CONTAINER_ES_FORMAT_T { VC_CONTAINER_ES_TYPE_T es_type; /**< Type of the elementary stream */ VC_CONTAINER_FOURCC_T codec; /**< Coding of the elementary stream */ VC_CONTAINER_FOURCC_T codec_variant; /**< If set, indicates a variant of the coding */ VC_CONTAINER_ES_SPECIFIC_FORMAT_T *type; /**< Type specific information for the elementary stream */ uint32_t bitrate; /**< Bitrate */ VC_CONTAINER_LANGUAGE_T language; /**< Language code for the elementary stream */ uint32_t group_id; /**< ID of the group this elementary stream belongs to */ uint32_t flags; /**< Flags describing the properties of an elementary stream. * See \ref esformatflags "Elementary stream format flags". */ unsigned int extradata_size; /**< Size of the codec specific data */ uint8_t *extradata; /**< Codec specific data */ } VC_CONTAINER_ES_FORMAT_T; /* @} */ /** \defgroup VcContainerPacket Container Packet * A container packet is the unit of data that is being read from or written to a container */ /* @{ */ /** Structure describing a data packet */ typedef struct VC_CONTAINER_PACKET_T { struct VC_CONTAINER_PACKET_T *next; /**< Used to build lists of packets */ uint8_t *data; /**< Pointer to the buffer containing the actual data for the packet */ unsigned int buffer_size; /**< Size of the p_data buffer. This is used to indicate how much data can be read in p_data */ unsigned int size; /**< Size of the data contained in p_data */ unsigned int frame_size; /**< If set, indicates the size of the frame this packet belongs to */ int64_t pts; /**< Presentation Timestamp of the packet */ int64_t dts; /**< Decoding Timestamp of the packet */ uint64_t num; /**< Number of this packet */ uint32_t track; /**< Track associated with this packet */ uint32_t flags; /**< Flags associated with this packet */ void *user_data; /**< Field reserved for use by the client */ void *framework_data; /**< Field reserved for use by the framework */ } VC_CONTAINER_PACKET_T; /** \name Container Packet Flags * The following flags describe properties of the data packet */ /* @{ */ #define VC_CONTAINER_PACKET_FLAG_KEYFRAME 0x01 /**< Packet is a keyframe */ #define VC_CONTAINER_PACKET_FLAG_FRAME_START 0x02 /**< Packet starts a frame */ #define VC_CONTAINER_PACKET_FLAG_FRAME_END 0x04 /**< Packet ends a frame */ #define VC_CONTAINER_PACKET_FLAG_FRAME 0x06 /**< Packet contains only complete frames */ #define VC_CONTAINER_PACKET_FLAG_DISCONTINUITY 0x08 /**< Packet comes after a discontinuity in the stream. Decoders might have to be flushed */ #define VC_CONTAINER_PACKET_FLAG_ENCRYPTED 0x10 /**< Packet contains DRM encrypted data */ #define VC_CONTAINER_PACKET_FLAG_CONFIG 0x20 /**< Packet contains stream specific config data */ /* @} */ /** \name Special Unknown Time Value * This is the special value used to signal that a timestamp is not known */ /* @{ */ #define VC_CONTAINER_TIME_UNKNOWN (INT64_C(1)<<63) /**< Special value for signalling that time is not known */ /* @} */ /* @} */ /** \name Track flags * \anchor trackflags * The following flags describe properties of a track */ /* @{ */ #define VC_CONTAINER_TRACK_FLAG_CHANGED 0x1 /**< Track definition has changed */ /* @} */ /** Definition of the track type */ typedef struct VC_CONTAINER_TRACK_T { struct VC_CONTAINER_TRACK_PRIVATE_T *priv; /**< Private member used by the implementation */ uint32_t is_enabled; /**< Flag to specify if the track is enabled */ uint32_t flags; /**< Flags describing the properties of a track. * See \ref trackflags "Track flags". */ VC_CONTAINER_ES_FORMAT_T *format; /**< Format of the elementary stream contained in the track */ unsigned int meta_num; /**< Number of metadata elements associated with the track */ VC_CONTAINER_METADATA_T **meta; /**< Array of metadata elements associated with the track */ } VC_CONTAINER_TRACK_T; /** Definition of the DRM type */ typedef struct VC_CONTAINER_DRM_T { VC_CONTAINER_FOURCC_T format; /**< Four character code describing the format of the DRM in use */ unsigned int views_max; /**< Maximum number of views allowed */ unsigned int views_current; /**< Current number of views */ } VC_CONTAINER_DRM_T; /** Type definition for the progress reporting function. This function will be called regularly * by the container during a call which blocks for too long and will report the progress of the * operation as an estimated total length in microseconds and a percentage done. * Returning anything else than VC_CONTAINER_SUCCESS in this function will abort the current * operation. */ typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_PROGRESS_REPORT_FUNC_T)(void *userdata, int64_t length, unsigned int percentage_done); /** \name Container Events * The following flags are exported by containers to notify the application of events */ /* @{ */ /** Type definition for container events */ typedef uint32_t VC_CONTAINER_EVENTS_T; #define VC_CONTAINER_EVENT_TRACKS_CHANGE 1 /**< Track information has changed */ #define VC_CONTAINER_EVENT_METADATA_CHANGE 2 /**< Metadata has changed */ /* @} */ /** Definition of the container context */ typedef struct VC_CONTAINER_T { struct VC_CONTAINER_PRIVATE_T *priv; /**< Private member used by the implementation */ VC_CONTAINER_EVENTS_T events; /**< Events generated by the container */ VC_CONTAINER_CAPABILITIES_T capabilities; /**< Capabilities exported by the container */ VC_CONTAINER_PROGRESS_REPORT_FUNC_T pf_progress; /**< Progress report function pointer */ void *progress_userdata; /**< Progress report user data */ int64_t duration; /**< Duration of the media in microseconds */ int64_t position; /**< Current time position into the media */ int64_t size; /**< Size of the media in bytes */ unsigned int tracks_num; /**< Number of tracks available */ /** Pointer to an array of pointers to track elements. * The reasoning for using a pointer to pointers here is to allow us to extend * VC_CONTAINER_TRACK_T without losing binary backward compatibility. */ VC_CONTAINER_TRACK_T **tracks; unsigned int meta_num; /**< Number of metadata elements associated with the container */ VC_CONTAINER_METADATA_T **meta; /**< Array of metadata elements associated with the container */ VC_CONTAINER_DRM_T *drm; /**< Description used for DRM protected content */ } VC_CONTAINER_T; /** Forward declaration of a container input / output context. * This structure defines the context for a container io instance */ typedef struct VC_CONTAINER_IO_T VC_CONTAINER_IO_T; /** Opens the media container pointed to by the URI for reading. * This will create an an instance of a container reader and its associated context. * The context returned will also be filled with the information retrieved from the media. * * If the media isn't accessible or recognized, this will return a null pointer as well as * an error code indicating why this failed. * * \param psz_uri Unified Resource Identifier pointing to the media container * \param status Returns the status of the operation * \param pf_progress User provided function pointer to a progress report function. Can be set to * null if no progress report is wanted. This function will be used during * the whole lifetime of the instance (i.e. it will be used during * open / seek / close) * \param progress_userdata User provided pointer that will be passed during the progress report * function call. * \return A pointer to the context of the new instance of the * container reader. Returns NULL on failure. */ VC_CONTAINER_T *vc_container_open_reader( const char *psz_uri, VC_CONTAINER_STATUS_T *status, VC_CONTAINER_PROGRESS_REPORT_FUNC_T pf_progress, void *progress_userdata); /** Opens for reading the media container pointed to by the container i/o. * This will create an an instance of a container reader and its associated context. * The context returned will also be filled with the information retrieved from the media. * * If the media isn't accessible or recognized, this will return a null pointer as well as * an error code indicating why this failed. * * \param p_io Instance of the container i/o to use * \param psz_uri Unified Resource Identifier pointing to the media container (optional) * \param status Returns the status of the operation * \param pf_progress User provided function pointer to a progress report function. Can be set to * null if no progress report is wanted. This function will be used during * the whole lifetime of the instance (i.e. it will be used during * open / seek / close) * \param progress_userdata User provided pointer that will be passed during the progress report * function call. * \return A pointer to the context of the new instance of the * container reader. Returns NULL on failure. */ VC_CONTAINER_T *vc_container_open_reader_with_io( VC_CONTAINER_IO_T *p_io, const char *psz_uri, VC_CONTAINER_STATUS_T *status, VC_CONTAINER_PROGRESS_REPORT_FUNC_T pf_progress, void *progress_userdata); /** Opens the media container pointed to by the URI for writing. * This will create an an instance of a container writer and its associated context. * The context returned will be initialised to sensible values. * * The application will need to add all the media tracks using \ref vc_container_control before * it starts writing data using \ref vc_container_write. * * If the media isn't accessible or recognized, this will return a null pointer as well as * an error code indicating why this failed. * * \param psz_uri Unified Resource Identifier pointing to the media container * \param status Returns the status of the operation * \param pf_progress User provided function pointer to a progess report function. Can be set to * null if no progress report is wanted. * \param progress_userdata User provided pointer that will be passed during the progress report * function call. * \return A pointer to the context of the new instance of the * container writer. Returns NULL on failure. */ VC_CONTAINER_T *vc_container_open_writer( const char *psz_uri, VC_CONTAINER_STATUS_T *status, VC_CONTAINER_PROGRESS_REPORT_FUNC_T pf_progress, void *progress_userdata); /** Closes an instance of a container reader / writer. * This will free all the resources associated with the context. * * \param context Pointer to the context of the instance to close * \return the status of the operation */ VC_CONTAINER_STATUS_T vc_container_close( VC_CONTAINER_T *context ); /** \name Container read flags * The following flags can be passed during a read call */ /* @{ */ /** Type definition for the read flags */ typedef uint32_t VC_CONTAINER_READ_FLAGS_T; /** Ask the container to only return information on the next packet without reading it */ #define VC_CONTAINER_READ_FLAG_INFO 1 /** Ask the container to skip the next packet */ #define VC_CONTAINER_READ_FLAG_SKIP 2 /** Force the container to read data from the specified track */ #define VC_CONTAINER_READ_FLAG_FORCE_TRACK 4 /* @} */ /** Reads a data packet from a container reader. * By default, the reader will read whatever packet comes next in the container and update the * given \ref VC_CONTAINER_PACKET_T structure with this packet's information. * This behaviour can be changed using the \ref VC_CONTAINER_READ_FLAGS_T.\n * \ref VC_CONTAINER_READ_FLAG_INFO will instruct the reader to only return information on the * following packet but not its actual data. The data can be retreived later by issuing another * read request.\n * \ref VC_CONTAINER_READ_FLAG_FORCE_TRACK will force the reader to read the next packet for the * selected track (as present in the \ref VC_CONTAINER_PACKET_T structure) instead of defaulting * to reading the packet which comes next in the container.\n * \ref VC_CONTAINER_READ_FLAG_SKIP will instruct the reader to skip the next packet. In this case * it isn't necessary for the caller to pass a pointer to a \ref VC_CONTAINER_PACKET_T structure * unless the \ref VC_CONTAINER_READ_FLAG_INFO is also given.\n * A combination of all these flags can be used. * * \param context Pointer to the context of the reader to use * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet * This needs to be partially filled before the call (buffer, buffer_size) * \param flags Flags controlling the read operation * \return the status of the operation */ VC_CONTAINER_STATUS_T vc_container_read( VC_CONTAINER_T *context, VC_CONTAINER_PACKET_T *packet, VC_CONTAINER_READ_FLAGS_T flags ); /** Writes a data packet to a container writer. * * \param context Pointer to the context of the writer to use * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet * \return the status of the operation */ VC_CONTAINER_STATUS_T vc_container_write( VC_CONTAINER_T *context, VC_CONTAINER_PACKET_T *packet ); /** Definition of the different seek modes */ typedef enum { /** The offset provided for seeking is an absolute time offset in microseconds */ VC_CONTAINER_SEEK_MODE_TIME = 0, /** The offset provided for seeking is a percentage (Q32 ?) */ VC_CONTAINER_SEEK_MODE_PERCENT } VC_CONTAINER_SEEK_MODE_T; /** \name Container Seek Flags * The following flags control seek operations */ /* @{ */ /** Type definition for the seek flags */ typedef uint32_t VC_CONTAINER_SEEK_FLAGS_T; /** Choose precise seeking even if slower */ #define VC_CONTAINER_SEEK_FLAG_PRECISE 0x1 /** By default a seek will always seek to the keyframe which comes just before the requested * position. This flag allows the caller to force the container to seek to the keyframe which * comes just after the requested position. */ #define VC_CONTAINER_SEEK_FLAG_FORWARD 0x2 /* @} */ /** Seek into a container reader. * * \param context Pointer to the context of the reader to use * \param offset Offset to seek to. Used as an input as well as output value. * \param mode Seeking mode requested. * \param flags Flags affecting the seeking operation. * \return the status of the operation */ VC_CONTAINER_STATUS_T vc_container_seek( VC_CONTAINER_T *context, int64_t *offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags); /** Performance statistics. */ /** The maximum number of bins a statistics value is held in */ #define VC_CONTAINER_STATS_BINS 10 /** This type is used to represent multiple values of a statistic. */ typedef struct VC_CONTAINER_STATS_T { /** The number of places to right shift count before using. Resulting values * of zero are rounded to 1. */ uint32_t shift; /** We store VC_CONTAINER_STATS_BINS+1 records, in sorted order of numpc. * At least one will be invalid and all zero. We combine adjacent records * as necessary. */ struct { /** Sum of count. For single value statistics this is the freqency, for paired statistics * this is the number of bytes written. */ uint32_t count; /** Number of count. For single value statistics this is the total value, for paired statistics * this is the total length of time. */ uint32_t num; /** Number>>shift per count. Stored to save recalculation. */ uint32_t numpc; } record[VC_CONTAINER_STATS_BINS+1]; } VC_CONTAINER_STATS_T; /** This type represents the statistics saved by the io layer. */ typedef struct VC_CONTAINER_WRITE_STATS_T { /** This logs the number of bytes written in count, and the microseconds taken to write * in num. */ VC_CONTAINER_STATS_T write; /** This logs the length of time the write function has to wait for the asynchronous task. */ VC_CONTAINER_STATS_T wait; /** This logs the length of time that we wait for a flush command to complete. */ VC_CONTAINER_STATS_T flush; } VC_CONTAINER_WRITE_STATS_T; /** Control operations which can be done on containers. */ typedef enum { /** Adds a new track to the list of tracks. This should be used by writers to create * their list of tracks.\n * Arguments:\n * arg1= VC_CONTAINER_ES_FORMAT_T *: format of the track to add\n * return= VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED if the format is not supported */ VC_CONTAINER_CONTROL_TRACK_ADD = 0, /** Specifies that we're done adding new tracks. This is optional but can be used by writers * to trigger the writing of the container header early. If this isn't used, the header will be * written when the first data packet is received.\n * No arguments.\n * return= VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED if the format is not supported */ VC_CONTAINER_CONTROL_TRACK_ADD_DONE, /** Change the format of a track in the list of tracks. This should be used by writers to modify * the format of a track at run-time.\n * Arguments:\n * arg1= unsigned int: index of track to change\n * arg2= VC_CONTAINER_ES_FORMAT_T *: format of the track to add\n * return= VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED if the format is not supported */ VC_CONTAINER_CONTROL_TRACK_CHANGE, /** Deletes a track from the list of tracks. This should be used by writers to delete tracks * during run-time. Note that vc_container_close will automatically delete all track so it * isn't necessary to call this before closing a writer.\n * Arguments:\n * arg1= index of the track to delete */ VC_CONTAINER_CONTROL_TRACK_DEL, /** Activate the playback of DRM protected content.\n * No arguments.\n * return= one of the VC_CONTAINER_ERROR_DRM error codes if content can't be played */ VC_CONTAINER_CONTROL_DRM_PLAY, /** TBD */ VC_CONTAINER_CONTROL_METADATA_ADD, /** TBD */ VC_CONTAINER_CONTROL_METADATA_CHANGE, /** TBD */ VC_CONTAINER_CONTROL_METADATA_DEL, /** TBD */ VC_CONTAINER_CONTROL_CHAPTER_ADD, /** TBD */ VC_CONTAINER_CONTROL_CHAPTER_DEL, /** Set a maximum size for files generated by writers.\n * Arguments:\n * arg1= uint64_t: maximum size */ VC_CONTAINER_CONTROL_SET_MAXIMUM_SIZE, /** Enables/disabled performance statistic gathering.\n * Arguments:\n * arg1= bool: enable or disable */ VC_CONTAINER_CONTROL_SET_IO_PERF_STATS, /** Collects performance statistics.\n * Arguments:\n * arg1= VC_CONTAINER_WRITE_STATS_T *: */ VC_CONTAINER_CONTROL_GET_IO_PERF_STATS, /** HACK.\n * Arguments:\n * arg1= void (*)(void *): callback function * arg1= void *: opaque pointer to pass during the callback */ VC_CONTAINER_CONTROL_SET_IO_BUFFER_FULL_CALLBACK, /** Set the I/O read buffer size to be used.\n * Arguments:\n * arg1= uint32_t: New buffer size in bytes*/ VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE, /** Set the timeout on I/O read operations, if applicable.\n * Arguments:\n * arg1= uint32_t: New timeout in milliseconds, or VC_CONTAINER_READ_TIMEOUT_BLOCK */ VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, /** Set the timestamp base.\n * The timestamp passed equates to time zero for the stream.\n * Arguments:\n * arg1= uint32_t: Timestamp base in stream clock units. */ VC_CONTAINER_CONTROL_SET_TIMESTAMP_BASE, /** Set the next expected sequence number for the stream.\n * Arguments:\n * arg1= uint32_t: Next expected sequence number. */ VC_CONTAINER_CONTROL_SET_NEXT_SEQUENCE_NUMBER, /** Set the source ID for the container.\n * Arguments:\n * arg1= uint32_t: Source identifier. */ VC_CONTAINER_CONTROL_SET_SOURCE_ID, /** Arguments:\n * arg1= void *: metadata buffer * arg2= unsigned long: length of metadata in bytes */ VC_CONTAINER_CONTROL_GET_DRM_METADATA, /** Arguments:\n * arg1= unsigned long: track number * arg2= VC_CONTAINER_FOURCC_T : drm type * arg3= void *: encryption configuration parameters. * arg4= unsigned long: configuration data length */ VC_CONTAINER_CONTROL_ENCRYPT_TRACK, /** Causes the io to be flushed.\n * Arguments: none */ VC_CONTAINER_CONTROL_IO_FLUSH, /** Request the container reader to packetize data for the specified track. * Arguments:\n * arg1= unsigned long: track number * arg2= VC_CONTAINER_FOURCC_T: codec variant to output */ VC_CONTAINER_CONTROL_TRACK_PACKETIZE, /** Private user extensions must be above this number */ VC_CONTAINER_CONTROL_USER_EXTENSIONS = 0x1000 } VC_CONTAINER_CONTROL_T; /** Used with the VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS control to indicate the read shall * block until either data is available, or an error occurs. */ #define VC_CONTAINER_READ_TIMEOUT_BLOCK (uint32_t)(-1) /** Extensible control function for container readers and writers. * This function takes a variable number of arguments which will depend on the specific operation. * * \param context Pointer to the VC_CONTAINER_T context to use * \param operation The requested operation * \return the status of the operation. Can be \ref VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION * if the operation is not supported or implemented by the container. */ VC_CONTAINER_STATUS_T vc_container_control( VC_CONTAINER_T *context, VC_CONTAINER_CONTROL_T operation, ... ); /* @} */ #ifdef __cplusplus } #endif #endif /* VC_CONTAINERS_H */ userland/containers/containers_codecs.h000066400000000000000000000251431421703157200207050ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_CONTAINERS_CODECS_H #define VC_CONTAINERS_CODECS_H /** \file containers_codecs.h * Codec helpers */ #ifdef __cplusplus extern "C" { #endif #include "containers/containers_types.h" /* Video */ #define VC_CONTAINER_CODEC_MP1V VC_FOURCC('m','p','1','v') #define VC_CONTAINER_CODEC_MP2V VC_FOURCC('m','p','2','v') #define VC_CONTAINER_CODEC_MP4V VC_FOURCC('m','p','4','v') #define VC_CONTAINER_CODEC_DIV3 VC_FOURCC('d','i','v','3') #define VC_CONTAINER_CODEC_DIV4 VC_FOURCC('d','i','v','4') #define VC_CONTAINER_CODEC_H263 VC_FOURCC('h','2','6','3') #define VC_CONTAINER_CODEC_H264 VC_FOURCC('h','2','6','4') #define VC_CONTAINER_CODEC_MVC VC_FOURCC('m','v','c',' ') #define VC_CONTAINER_CODEC_WMV1 VC_FOURCC('w','m','v','1') #define VC_CONTAINER_CODEC_WMV2 VC_FOURCC('w','m','v','2') #define VC_CONTAINER_CODEC_WMV3 VC_FOURCC('w','m','v','3') #define VC_CONTAINER_CODEC_WVC1 VC_FOURCC('w','v','c','1') #define VC_CONTAINER_CODEC_WMVA VC_FOURCC('w','m','v','a') #define VC_CONTAINER_CODEC_MJPEG VC_FOURCC('m','j','p','g') #define VC_CONTAINER_CODEC_MJPEGA VC_FOURCC('m','j','p','a') #define VC_CONTAINER_CODEC_MJPEGB VC_FOURCC('m','j','p','b') #define VC_CONTAINER_CODEC_THEORA VC_FOURCC('t','h','e','o') #define VC_CONTAINER_CODEC_VP3 VC_FOURCC('v','p','3',' ') #define VC_CONTAINER_CODEC_VP6 VC_FOURCC('v','p','6',' ') #define VC_CONTAINER_CODEC_VP7 VC_FOURCC('v','p','7',' ') #define VC_CONTAINER_CODEC_VP8 VC_FOURCC('v','p','8',' ') #define VC_CONTAINER_CODEC_RV10 VC_FOURCC('r','v','1','0') #define VC_CONTAINER_CODEC_RV20 VC_FOURCC('r','v','2','0') #define VC_CONTAINER_CODEC_RV30 VC_FOURCC('r','v','3','0') #define VC_CONTAINER_CODEC_RV40 VC_FOURCC('r','v','4','0') #define VC_CONTAINER_CODEC_AVS VC_FOURCC('a','v','s',' ') #define VC_CONTAINER_CODEC_SPARK VC_FOURCC('s','p','r','k') #define VC_CONTAINER_CODEC_DIRAC VC_FOURCC('d','r','a','c') #define VC_CONTAINER_CODEC_YUV VC_FOURCC('y','u','v',' ') #define VC_CONTAINER_CODEC_I420 VC_FOURCC('I','4','2','0') #define VC_CONTAINER_CODEC_YV12 VC_FOURCC('Y','V','1','2') #define VC_CONTAINER_CODEC_I422 VC_FOURCC('I','4','2','2') #define VC_CONTAINER_CODEC_YUYV VC_FOURCC('Y','U','Y','V') #define VC_CONTAINER_CODEC_YVYU VC_FOURCC('Y','V','Y','U') #define VC_CONTAINER_CODEC_UYVY VC_FOURCC('U','Y','V','Y') #define VC_CONTAINER_CODEC_VYUY VC_FOURCC('V','Y','U','Y') #define VC_CONTAINER_CODEC_NV12 VC_FOURCC('N','V','1','2') #define VC_CONTAINER_CODEC_NV21 VC_FOURCC('N','V','2','1') #define VC_CONTAINER_CODEC_ARGB VC_FOURCC('A','R','G','B') #define VC_CONTAINER_CODEC_RGBA VC_FOURCC('R','G','B','A') #define VC_CONTAINER_CODEC_ABGR VC_FOURCC('A','B','G','R') #define VC_CONTAINER_CODEC_BGRA VC_FOURCC('B','G','R','A') #define VC_CONTAINER_CODEC_RGB16 VC_FOURCC('R','G','B','2') #define VC_CONTAINER_CODEC_RGB24 VC_FOURCC('R','G','B','3') #define VC_CONTAINER_CODEC_RGB32 VC_FOURCC('R','G','B','4') #define VC_CONTAINER_CODEC_BGR16 VC_FOURCC('B','G','R','2') #define VC_CONTAINER_CODEC_BGR24 VC_FOURCC('B','G','R','3') #define VC_CONTAINER_CODEC_BGR32 VC_FOURCC('B','G','R','4') #define VC_CONTAINER_CODEC_YUVUV128 VC_FOURCC('S','A','N','D') #define VC_CONTAINER_CODEC_JPEG VC_FOURCC('j','p','e','g') #define VC_CONTAINER_CODEC_PNG VC_FOURCC('p','n','g',' ') #define VC_CONTAINER_CODEC_GIF VC_FOURCC('g','i','f',' ') #define VC_CONTAINER_CODEC_PPM VC_FOURCC('p','p','m',' ') #define VC_CONTAINER_CODEC_TGA VC_FOURCC('t','g','a',' ') #define VC_CONTAINER_CODEC_BMP VC_FOURCC('b','m','p',' ') /* Audio */ #define VC_CONTAINER_CODEC_PCM_UNSIGNED_BE VC_FOURCC('P','C','M','U') #define VC_CONTAINER_CODEC_PCM_UNSIGNED_LE VC_FOURCC('p','c','m','u') #define VC_CONTAINER_CODEC_PCM_SIGNED_BE VC_FOURCC('P','C','M','S') #define VC_CONTAINER_CODEC_PCM_SIGNED_LE VC_FOURCC('p','c','m','s') #define VC_CONTAINER_CODEC_PCM_FLOAT_BE VC_FOURCC('P','C','M','F') #define VC_CONTAINER_CODEC_PCM_FLOAT_LE VC_FOURCC('p','c','m','f') /* Defines for native endianness */ #ifdef VC_CONTAINER_IS_BIG_ENDIAN #define VC_CONTAINER_CODEC_PCM_UNSIGNED VC_CONTAINER_CODEC_PCM_UNSIGNED_BE #define VC_CONTAINER_CODEC_PCM_SIGNED VC_CONTAINER_CODEC_PCM_SIGNED_BE #define VC_CONTAINER_CODEC_PCM_FLOAT VC_CONTAINER_CODEC_PCM_FLOAT_BE #else #define VC_CONTAINER_CODEC_PCM_UNSIGNED VC_CONTAINER_CODEC_PCM_UNSIGNED_LE #define VC_CONTAINER_CODEC_PCM_SIGNED VC_CONTAINER_CODEC_PCM_SIGNED_LE #define VC_CONTAINER_CODEC_PCM_FLOAT VC_CONTAINER_CODEC_PCM_FLOAT_LE #endif #define VC_CONTAINER_CODEC_MPGA VC_FOURCC('m','p','g','a') #define VC_CONTAINER_CODEC_MP4A VC_FOURCC('m','p','4','a') #define VC_CONTAINER_CODEC_ALAW VC_FOURCC('a','l','a','w') #define VC_CONTAINER_CODEC_MULAW VC_FOURCC('u','l','a','w') #define VC_CONTAINER_CODEC_ADPCM_MS VC_FOURCC('m','s',0x0,0x2) #define VC_CONTAINER_CODEC_ADPCM_IMA_MS VC_FOURCC('m','s',0x0,0x1) #define VC_CONTAINER_CODEC_ADPCM_SWF VC_FOURCC('a','s','w','f') #define VC_CONTAINER_CODEC_WMA1 VC_FOURCC('w','m','a','1') #define VC_CONTAINER_CODEC_WMA2 VC_FOURCC('w','m','a','2') #define VC_CONTAINER_CODEC_WMAP VC_FOURCC('w','m','a','p') #define VC_CONTAINER_CODEC_WMAL VC_FOURCC('w','m','a','l') #define VC_CONTAINER_CODEC_WMAV VC_FOURCC('w','m','a','v') #define VC_CONTAINER_CODEC_AMRNB VC_FOURCC('a','m','r','n') #define VC_CONTAINER_CODEC_AMRWB VC_FOURCC('a','m','r','w') #define VC_CONTAINER_CODEC_AMRWBP VC_FOURCC('a','m','r','p') #define VC_CONTAINER_CODEC_AC3 VC_FOURCC('a','c','3',' ') #define VC_CONTAINER_CODEC_EAC3 VC_FOURCC('e','a','c','3') #define VC_CONTAINER_CODEC_DTS VC_FOURCC('d','t','s',' ') #define VC_CONTAINER_CODEC_MLP VC_FOURCC('m','l','p',' ') #define VC_CONTAINER_CODEC_FLAC VC_FOURCC('f','l','a','c') #define VC_CONTAINER_CODEC_VORBIS VC_FOURCC('v','o','r','b') #define VC_CONTAINER_CODEC_SPEEX VC_FOURCC('s','p','x',' ') #define VC_CONTAINER_CODEC_ATRAC3 VC_FOURCC('a','t','r','3') #define VC_CONTAINER_CODEC_ATRACX VC_FOURCC('a','t','r','x') #define VC_CONTAINER_CODEC_ATRACL VC_FOURCC('a','t','r','l') #define VC_CONTAINER_CODEC_MIDI VC_FOURCC('m','i','d','i') #define VC_CONTAINER_CODEC_EVRC VC_FOURCC('e','v','r','c') #define VC_CONTAINER_CODEC_NELLYMOSER VC_FOURCC('n','e','l','y') #define VC_CONTAINER_CODEC_QCELP VC_FOURCC('q','c','e','l') /* Text */ #define VC_CONTAINER_CODEC_TEXT VC_FOURCC('t','e','x','t') #define VC_CONTAINER_CODEC_SSA VC_FOURCC('s','s','a',' ') #define VC_CONTAINER_CODEC_USF VC_FOURCC('u','s','f',' ') #define VC_CONTAINER_CODEC_VOBSUB VC_FOURCC('v','s','u','b') #define VC_CONTAINER_CODEC_UNKNOWN VC_FOURCC('u','n','k','n') /* Codec variants */ /** ISO 14496-10 Annex B byte stream format */ #define VC_CONTAINER_VARIANT_H264_DEFAULT 0 /** ISO 14496-15 AVC format (used in mp4/mkv and other containers) */ #define VC_CONTAINER_VARIANT_H264_AVC1 VC_FOURCC('a','v','c','C') /** Implicitly delineated NAL units without emulation prevention */ #define VC_CONTAINER_VARIANT_H264_RAW VC_FOURCC('r','a','w',' ') /** MPEG 1/2 Audio - Layer unknown */ #define VC_CONTAINER_VARIANT_MPGA_DEFAULT 0 /** MPEG 1/2 Audio - Layer 1 */ #define VC_CONTAINER_VARIANT_MPGA_L1 VC_FOURCC('l','1',' ',' ') /** MPEG 1/2 Audio - Layer 2 */ #define VC_CONTAINER_VARIANT_MPGA_L2 VC_FOURCC('l','2',' ',' ') /** MPEG 1/2 Audio - Layer 3 */ #define VC_CONTAINER_VARIANT_MPGA_L3 VC_FOURCC('l','3',' ',' ') /** Converts a WaveFormat ID into a VC_CONTAINER_FOURCC_T. * * \param waveformat_id WaveFormat ID to convert * \return a valid VC_CONTAINER_FOURCC_T or VC_CONTAINER_CODEC_UNKNOWN if no mapping was found. */ VC_CONTAINER_FOURCC_T waveformat_to_codec(uint16_t waveformat_id); /** Converts a VC_CONTAINER_FOURCC_T into a WaveFormat ID. * * \param codec VC_CONTAINER_FOURCC_T to convert * \return a valid WaveFormat ID of 0 if no mapping was found. */ uint16_t codec_to_waveformat(VC_CONTAINER_FOURCC_T codec); /** Tries to convert a generic fourcc into a VC_CONTAINER_FOURCC_T. * * \param fourcc fourcc to convert * \return a valid VC_CONTAINER_FOURCC_T or VC_CONTAINER_CODEC_UNKNOWN if no mapping was found. */ VC_CONTAINER_FOURCC_T fourcc_to_codec(uint32_t fourcc); uint32_t codec_to_fourcc(VC_CONTAINER_FOURCC_T codec); /** Tries to convert VideoForWindows fourcc into a VC_CONTAINER_FOURCC_T. * * \param fourcc vfw fourcc to convert * \return a valid VC_CONTAINER_FOURCC_T or VC_CONTAINER_CODEC_UNKNOWN if no mapping was found. */ VC_CONTAINER_FOURCC_T vfw_fourcc_to_codec(uint32_t fourcc); /** Tries to convert a VC_CONTAINER_FOURCC_T into a VideoForWindows fourcc. * * \param codec VC_CONTAINER_FOURCC_T to convert * \return a valid vfw fourcc or 0 if no mapping was found. */ uint32_t codec_to_vfw_fourcc(VC_CONTAINER_FOURCC_T codec); #ifdef __cplusplus } #endif #endif /* VC_CONTAINERS_CODECS_H */ userland/containers/containers_types.h000066400000000000000000000062231421703157200206070ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_CONTAINERS_TYPES_H #define VC_CONTAINERS_TYPES_H /** \file containers_types.h * Definition of types used by the containers API */ #include #include #include #include #if defined( __STDC__ ) && defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #include #include #elif defined( __GNUC__ ) #include #include #elif defined(_MSC_VER) #include #if (_MSC_VER < 1700) typedef __int8 int8_t; typedef unsigned __int8 uint8_t; typedef __int16 int16_t; typedef unsigned __int16 uint16_t; typedef __int32 int32_t; typedef unsigned __int32 uint32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; #endif #define PRIu16 "u" #define PRIu32 "u" #define PRId64 "I64d" #define PRIi64 "I64i" #define PRIo64 "I64o" #define PRIu64 "I64u" #define PRIx64 "I64x" #else typedef signed char int8_t; typedef unsigned char uint8_t; typedef signed short int16_t; typedef unsigned short uint16_t; typedef signed long int32_t; typedef unsigned long uint32_t; typedef signed long long int64_t; typedef unsigned long long uint64_t; #endif /* C99 64bits integers */ #ifndef INT64_C # define INT64_C(value) value##LL # define UINT64_C(value) value##ULL #endif /* C99 boolean */ #ifndef __cplusplus #ifndef bool # define bool int #endif #ifndef true # define true 1 #endif #ifndef false # define false 0 #endif #endif /* __cplusplus */ /* FIXME: should be different for big endian */ #define VC_FOURCC(a,b,c,d) ((a) | (b << 8) | (c << 16) | (d << 24)) #endif /* VC_CONTAINERS_TYPES_H */ userland/containers/core/000077500000000000000000000000001421703157200157725ustar00rootroot00000000000000userland/containers/core/containers.c000066400000000000000000000570601421703157200203130ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include "containers/core/containers_private.h" #include "containers/core/containers_io.h" #include "containers/core/containers_filters.h" #include "containers/core/containers_loader.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_utils.h" #define WRITER_SPACE_SAFETY_MARGIN (10*1024) #define PACKETIZER_BUFFER_SIZE (32*1024) /*****************************************************************************/ VC_CONTAINER_T *vc_container_open_reader_with_io( struct VC_CONTAINER_IO_T *io, const char *uri, VC_CONTAINER_STATUS_T *p_status, VC_CONTAINER_PROGRESS_REPORT_FUNC_T pfn_progress, void *progress_userdata) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_T *p_ctx = 0; const char *extension; VC_CONTAINER_PARAM_UNUSED(pfn_progress); VC_CONTAINER_PARAM_UNUSED(progress_userdata); VC_CONTAINER_PARAM_UNUSED(uri); /* Sanity check the i/o */ if (!io || !io->pf_read || !io->pf_seek) { LOG_ERROR(0, "invalid i/o instance: %p", io); status = VC_CONTAINER_ERROR_INVALID_ARGUMENT; goto error; } /* Allocate our context before trying out the different readers / writers */ p_ctx = malloc( sizeof(*p_ctx) + sizeof(*p_ctx->priv) + sizeof(*p_ctx->drm)); if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*p_ctx->priv) + sizeof(*p_ctx->drm)); p_ctx->priv = (VC_CONTAINER_PRIVATE_T *)(p_ctx + 1); p_ctx->priv->verbosity = vc_container_log_get_default_verbosity(); p_ctx->drm = (VC_CONTAINER_DRM_T *)(p_ctx->priv + 1); p_ctx->size = io->size; p_ctx->priv->io = io; p_ctx->priv->uri = io->uri_parts; /* If the uri has an extension, use it as a hint when loading the container */ extension = vc_uri_path_extension(p_ctx->priv->uri); /* If the user has specified a container, then use that instead */ vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); status = vc_container_load_reader(p_ctx, extension); if (status != VC_CONTAINER_SUCCESS) goto error; p_ctx->priv->drm_filter = vc_container_filter_open(VC_FOURCC('d','r','m',' '), VC_FOURCC('u','n','k','n'), p_ctx, &status); if (status != VC_CONTAINER_SUCCESS) { /* Some other problem occurred aside from the filter not being appropriate or the stream not needing it. */ if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; /* Report no DRM and continue as normal */ p_ctx->drm = NULL; status = VC_CONTAINER_SUCCESS; } end: if(p_status) *p_status = status; return p_ctx; error: if (p_ctx) { p_ctx->priv->io = NULL; /* The i/o doesn't belong to us */ vc_container_close(p_ctx); p_ctx = NULL; } goto end; } /*****************************************************************************/ VC_CONTAINER_T *vc_container_open_reader( const char *uri, VC_CONTAINER_STATUS_T *p_status, VC_CONTAINER_PROGRESS_REPORT_FUNC_T pfn_progress, void *progress_userdata) { VC_CONTAINER_IO_T *io; VC_CONTAINER_T *ctx; /* Start by opening the URI */ io = vc_container_io_open( uri, VC_CONTAINER_IO_MODE_READ, p_status ); if (!io) return 0; ctx = vc_container_open_reader_with_io( io, uri, p_status, pfn_progress, progress_userdata); if (!ctx) vc_container_io_close(io); return ctx; } /*****************************************************************************/ VC_CONTAINER_T *vc_container_open_writer( const char *uri, VC_CONTAINER_STATUS_T *p_status, VC_CONTAINER_PROGRESS_REPORT_FUNC_T pfn_progress, void *progress_userdata) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_T *p_ctx = 0; VC_CONTAINER_IO_T *io; const char *extension; VC_CONTAINER_PARAM_UNUSED(pfn_progress); VC_CONTAINER_PARAM_UNUSED(progress_userdata); /* Start by opening the URI */ io = vc_container_io_open( uri, VC_CONTAINER_IO_MODE_WRITE, &status ); if(!io) goto error; /* Make sure we have enough space available to start writing */ if(io->max_size && io->max_size < WRITER_SPACE_SAFETY_MARGIN) { LOG_DEBUG(p_ctx, "not enough space available to open a writer"); status = VC_CONTAINER_ERROR_OUT_OF_RESOURCES; goto error; } /* Allocate our context before trying out the different readers / writers */ p_ctx = malloc( sizeof(*p_ctx) + sizeof(*p_ctx->priv)); if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*p_ctx->priv)); p_ctx->priv = (VC_CONTAINER_PRIVATE_T *)(p_ctx + 1); p_ctx->priv->verbosity = vc_container_log_get_default_verbosity(); p_ctx->priv->io = io; p_ctx->priv->uri = io->uri_parts; io = NULL; /* io now owned by the context */ /* If the uri has an extension, use it as a hint when loading the container */ extension = vc_uri_path_extension(p_ctx->priv->uri); /* If the user has specified a container, then use that instead */ vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); status = vc_container_load_writer(p_ctx, extension); if(status != VC_CONTAINER_SUCCESS) goto error; end: if(p_status) *p_status = status; return p_ctx; error: if(io) vc_container_io_close(io); if (p_ctx) vc_container_close(p_ctx); p_ctx = NULL; goto end; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_close( VC_CONTAINER_T *p_ctx ) { unsigned int i; if(!p_ctx) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; for(i = 0; i < p_ctx->tracks_num; i++) if(p_ctx->tracks[i]->priv->packetizer) vc_packetizer_close(p_ctx->tracks[i]->priv->packetizer); if(p_ctx->priv->packetizer_buffer) free(p_ctx->priv->packetizer_buffer); if(p_ctx->priv->drm_filter) vc_container_filter_close(p_ctx->priv->drm_filter); if(p_ctx->priv->pf_close) p_ctx->priv->pf_close(p_ctx); if(p_ctx->priv->io) vc_container_io_close(p_ctx->priv->io); if(p_ctx->priv->module_handle) vc_container_unload(p_ctx); for(i = 0; i < p_ctx->meta_num; i++) free(p_ctx->meta[i]); if(p_ctx->meta_num) free(p_ctx->meta); p_ctx->meta_num = 0; free(p_ctx); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T container_read_packet( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) { VC_CONTAINER_STATUS_T status; while(1) { status = p_ctx->priv->pf_read(p_ctx, p_packet, flags); if(status == VC_CONTAINER_ERROR_CONTINUE) continue; if(!p_packet || (flags & VC_CONTAINER_READ_FLAG_SKIP)) return status; /* We've just been requested to skip the data */ if(status != VC_CONTAINER_SUCCESS) return status; /* Skip data from out of bounds tracks, disabled tracks or packets that are encrypted and cannot be decrypted */ if(p_packet->track >= p_ctx->tracks_num || !p_ctx->tracks[p_packet->track]->is_enabled || ((p_packet->flags & VC_CONTAINER_PACKET_FLAG_ENCRYPTED) && !p_ctx->priv->drm_filter)) { if(flags & VC_CONTAINER_READ_FLAG_INFO) status = p_ctx->priv->pf_read(p_ctx, p_packet, VC_CONTAINER_READ_FLAG_SKIP); if(status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_CONTINUE) continue; } if(status != VC_CONTAINER_SUCCESS) return status; if(p_ctx->priv->drm_filter) status = vc_container_filter_process(p_ctx->priv->drm_filter, p_packet); break; } return status; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_CONTINUE; VC_PACKETIZER_FLAGS_T packetizer_flags = 0; VC_PACKETIZER_T *packetizer; uint32_t force = flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK; unsigned int i; if(!p_packet && !(flags & VC_CONTAINER_READ_FLAG_SKIP)) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; if(!p_packet && (flags & VC_CONTAINER_READ_FLAG_INFO)) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; if(p_packet && !p_packet->data && !(flags & (VC_CONTAINER_READ_FLAG_INFO | VC_CONTAINER_READ_FLAG_SKIP))) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; if((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) && (!p_packet || p_packet->track >= p_ctx->tracks_num || !p_ctx->tracks[p_packet->track]->is_enabled)) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; /* Always having a packet structure to work with simplifies things */ if(!p_packet) p_packet = &p_ctx->priv->packetizer_packet; /* Simple/Fast case first */ if(!p_ctx->priv->packetizing) { status = container_read_packet( p_ctx, p_packet, flags ); goto end; } if(flags & VC_CONTAINER_READ_FLAG_INFO) packetizer_flags |= VC_PACKETIZER_FLAG_INFO; if(flags & VC_CONTAINER_READ_FLAG_SKIP) packetizer_flags |= VC_PACKETIZER_FLAG_SKIP; /* Loop through all the packetized tracks first to see if we've got any * data to consume there */ for(i = 0; i < p_ctx->tracks_num; i++) { VC_PACKETIZER_T *packetizer = p_ctx->tracks[i]->priv->packetizer; if(!p_ctx->tracks[i]->is_enabled || !packetizer || (force && i != p_packet->track)) continue; status = vc_packetizer_read(packetizer, p_packet, packetizer_flags); p_packet->track = i; if(status == VC_CONTAINER_SUCCESS) break; } if(i < p_ctx->tracks_num) /* We've got some data */ goto end; /* Let's go and read some data from the actual container */ while(1) { VC_CONTAINER_PACKET_T *tmp = &p_ctx->priv->packetizer_packet; tmp->track = p_packet->track; /* Let's check what the container has to offer */ status = container_read_packet( p_ctx, tmp, force|VC_PACKETIZER_FLAG_INFO ); if(status != VC_CONTAINER_SUCCESS) return status; if(!p_ctx->tracks[tmp->track]->priv->packetizer) { status = container_read_packet( p_ctx, p_packet, flags ); break; } /* Feed data from the container onto the packetizer */ packetizer = p_ctx->tracks[tmp->track]->priv->packetizer; tmp->data = p_ctx->priv->packetizer_buffer; tmp->buffer_size = PACKETIZER_BUFFER_SIZE; tmp->size = 0; status = container_read_packet( p_ctx, tmp, force ); if(status != VC_CONTAINER_SUCCESS) return status; p_packet->track = tmp->track; vc_packetizer_push(packetizer, tmp); vc_packetizer_pop(packetizer, &tmp, VC_PACKETIZER_FLAG_FORCE_RELEASE_INPUT); status = vc_packetizer_read(packetizer, p_packet, packetizer_flags); if(status == VC_CONTAINER_SUCCESS) break; } end: if(status != VC_CONTAINER_SUCCESS) return status; if(p_packet && p_packet->dts > p_ctx->position) p_ctx->position = p_packet->dts; if(p_packet && p_packet->pts > p_ctx->position) p_ctx->position = p_packet->pts; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_write( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet ) { VC_CONTAINER_STATUS_T status; void * p_metadata_buffer = NULL; uint32_t metadata_length = 0; /* TODO: check other similar argument errors and non-stateless errors */ if (!p_packet || !p_packet->data || p_packet->track >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; /* Check for a previous error */ if(p_ctx->priv->status != VC_CONTAINER_SUCCESS && p_ctx->priv->status != VC_CONTAINER_ERROR_NOT_READY) return p_ctx->priv->status; /* Check we have enough space to write the data */ if(p_ctx->priv->max_size && p_ctx->size + p_packet->size + WRITER_SPACE_SAFETY_MARGIN > p_ctx->priv->max_size) {status = VC_CONTAINER_ERROR_LIMIT_REACHED; goto end;} if(p_ctx->priv->io->max_size && p_ctx->size + p_packet->size + WRITER_SPACE_SAFETY_MARGIN + (p_ctx->priv->tmp_io ? p_ctx->priv->tmp_io->offset : 0) > p_ctx->priv->io->max_size) {status = VC_CONTAINER_ERROR_OUT_OF_SPACE; goto end;} /* If a filter is created, then send the packet to the filter for encryption. */ if(p_ctx->priv->drm_filter) { status = vc_container_filter_process(p_ctx->priv->drm_filter, p_packet); if(status == VC_CONTAINER_SUCCESS) { /* Get the encryption metadata and send it to the output first. */ if(vc_container_control(p_ctx, VC_CONTAINER_CONTROL_GET_DRM_METADATA, &p_metadata_buffer, &metadata_length) == VC_CONTAINER_SUCCESS && metadata_length > 0) { /* Make a packet up with the metadata in the payload and write it. */ VC_CONTAINER_PACKET_T metadata_packet; metadata_packet.data = p_metadata_buffer; metadata_packet.buffer_size = metadata_length; metadata_packet.size = metadata_length; metadata_packet.frame_size = p_packet->frame_size + metadata_length; metadata_packet.pts = p_packet->pts; metadata_packet.dts = p_packet->dts; metadata_packet.num = p_packet->num; metadata_packet.track = p_packet->track; /* As this packet is written first, we must transfer any frame start flag from the following packet. Also, this packet can never have the end flag set. */ metadata_packet.flags = p_packet->flags & ~VC_CONTAINER_PACKET_FLAG_FRAME_END; p_packet->pts = p_packet->dts = VC_CONTAINER_TIME_UNKNOWN; p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_START; if(p_ctx->priv->pf_write(p_ctx, &metadata_packet) != VC_CONTAINER_SUCCESS) goto end; } } else if (status != VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION) { /* Encryption was appropriate but a problem has occurred. Skip the write of data to the io and return the status to the caller. */ goto end; } } status = p_ctx->priv->pf_write(p_ctx, p_packet); end: p_ctx->priv->status = status; return status; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_seek( VC_CONTAINER_T *p_ctx, int64_t *p_offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) { VC_CONTAINER_STATUS_T status; int64_t offset = *p_offset; unsigned int i; /* Reset all packetizers */ for(i = 0; i < p_ctx->tracks_num; i++) if(p_ctx->tracks[i]->priv->packetizer) vc_packetizer_reset(p_ctx->tracks[i]->priv->packetizer); status = p_ctx->priv->pf_seek(p_ctx, p_offset, mode, flags); /* */ if(status == VC_CONTAINER_SUCCESS && (flags & VC_CONTAINER_SEEK_FLAG_FORWARD) && *p_offset < offset) { LOG_DEBUG(p_ctx, "container didn't seek forward as requested (%"PRIi64"/%"PRIi64")", *p_offset, offset); for(i = 1; i <= 5 && *p_offset < offset; i++) { *p_offset = offset + i * i * 100000; status = p_ctx->priv->pf_seek(p_ctx, p_offset, mode, flags); if(status != VC_CONTAINER_SUCCESS) break; } } return status; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, ... ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; va_list args; va_start( args, operation ); if(operation == VC_CONTAINER_CONTROL_ENCRYPT_TRACK) { VC_CONTAINER_FOURCC_T type = va_arg(args, VC_CONTAINER_FOURCC_T); uint32_t track_num = va_arg(args, uint32_t); void *p_drm_data = va_arg(args, void *); int drm_data_size = va_arg(args, uint32_t); if(!p_ctx->priv->drm_filter && track_num < p_ctx->tracks_num) { VC_CONTAINER_TRACK_T * p_track = p_ctx->tracks[track_num]; if ((status = vc_container_track_allocate_drmdata(p_ctx, p_track, drm_data_size)) != VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "failed to allocate memory for 'drm data' (%d bytes)", drm_data_size); goto end; } memcpy(p_track->priv->drmdata, p_drm_data, drm_data_size); /* Track encryption enabled and no filter - create one now. */ p_ctx->priv->drm_filter = vc_container_filter_open(VC_FOURCC('d','r','m',' '), type, p_ctx, &status); if(status != VC_CONTAINER_SUCCESS) goto end; status = vc_container_filter_control(p_ctx->priv->drm_filter, operation, track_num); } } else if(operation == VC_CONTAINER_CONTROL_GET_DRM_METADATA) { void *p_drm_data = va_arg(args, void *); int *drm_data_size = va_arg(args, int *); status = vc_container_filter_control(p_ctx->priv->drm_filter, operation, p_drm_data, drm_data_size); } if(status == VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION && p_ctx->priv->pf_control) status = p_ctx->priv->pf_control(p_ctx, operation, args); /* If the request has already been handled then we're done */ if(status != VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION) goto end; switch(operation) { case VC_CONTAINER_CONTROL_DRM_PLAY: if (p_ctx->priv->drm_filter) status = vc_container_filter_control(p_ctx->priv->drm_filter, operation, args); break; case VC_CONTAINER_CONTROL_SET_MAXIMUM_SIZE: p_ctx->priv->max_size = (uint64_t)va_arg( args, uint64_t ); if(p_ctx->priv->io->max_size && p_ctx->priv->max_size > p_ctx->priv->io->max_size) p_ctx->priv->max_size = p_ctx->priv->io->max_size; status = VC_CONTAINER_SUCCESS; break; case VC_CONTAINER_CONTROL_TRACK_PACKETIZE: { unsigned int track_num = va_arg(args, unsigned int); VC_CONTAINER_FOURCC_T fourcc = va_arg(args, VC_CONTAINER_FOURCC_T); VC_CONTAINER_TRACK_T *p_track; if(track_num >= p_ctx->tracks_num) { status = VC_CONTAINER_ERROR_INVALID_ARGUMENT; break; } if(p_ctx->tracks[track_num]->priv->packetizer) { status = VC_CONTAINER_SUCCESS; break; } p_track = p_ctx->tracks[track_num]; p_track->priv->packetizer = vc_packetizer_open( p_track->format, fourcc, &status ); if(!p_track->priv->packetizer) break; if(!p_ctx->priv->packetizer_buffer) { p_ctx->priv->packetizer_buffer = malloc(PACKETIZER_BUFFER_SIZE); if(!p_ctx->priv->packetizer_buffer) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; vc_packetizer_close(p_track->priv->packetizer); p_track->priv->packetizer = NULL; break; } } vc_container_format_copy(p_track->format, p_track->priv->packetizer->out, p_track->format->extradata_size); p_track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; p_ctx->priv->packetizing = true; } break; default: break; } if (status == VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION) status = vc_container_io_control_list(p_ctx->priv->io, operation, args); end: va_end( args ); return status; } /*****************************************************************************/ VC_CONTAINER_TRACK_T *vc_container_allocate_track( VC_CONTAINER_T *context, unsigned int extra_size ) { VC_CONTAINER_TRACK_T *p_ctx; unsigned int size; VC_CONTAINER_PARAM_UNUSED(context); size = sizeof(*p_ctx) + sizeof(*p_ctx->priv) + sizeof(*p_ctx->format) + sizeof(*p_ctx->format->type) + extra_size; p_ctx = malloc(size); if(!p_ctx) return 0; memset(p_ctx, 0, size); p_ctx->priv = (VC_CONTAINER_TRACK_PRIVATE_T *)(p_ctx + 1); p_ctx->format = (VC_CONTAINER_ES_FORMAT_T *)(p_ctx->priv + 1); p_ctx->format->type = (void *)(p_ctx->format + 1); p_ctx->priv->module = (struct VC_CONTAINER_TRACK_MODULE_T *)(p_ctx->format->type + 1); return p_ctx; } /*****************************************************************************/ void vc_container_free_track( VC_CONTAINER_T *context, VC_CONTAINER_TRACK_T *p_track ) { VC_CONTAINER_PARAM_UNUSED(context); if(p_track) { if(p_track->priv->extradata) free(p_track->priv->extradata); if(p_track->priv->drmdata) free(p_track->priv->drmdata); free(p_track); } } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_track_allocate_extradata( VC_CONTAINER_T *context, VC_CONTAINER_TRACK_T *p_track, unsigned int extra_size ) { VC_CONTAINER_PARAM_UNUSED(context); /* Sanity check the size of the extra data */ if(extra_size > 100*1024) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; /* Check if we need to allocate a buffer */ if(extra_size > p_track->priv->extradata_size) { p_track->priv->extradata_size = 0; if(p_track->priv->extradata) free(p_track->priv->extradata); p_track->priv->extradata = malloc(extra_size); p_track->format->extradata = p_track->priv->extradata; if(!p_track->priv->extradata) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; p_track->priv->extradata_size = extra_size; } p_track->format->extradata = p_track->priv->extradata; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_track_allocate_drmdata( VC_CONTAINER_T *context, VC_CONTAINER_TRACK_T *p_track, unsigned int size ) { VC_CONTAINER_PARAM_UNUSED(context); /* Sanity check the size of the drm data */ if(size > 200*1024) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; /* Check if we need to allocate a buffer */ if(size > p_track->priv->drmdata_size) { p_track->priv->drmdata_size = 0; if(p_track->priv->drmdata) free(p_track->priv->drmdata); p_track->priv->drmdata = malloc(size); if(!p_track->priv->drmdata) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; p_track->priv->drmdata_size = size; } return VC_CONTAINER_SUCCESS; } userland/containers/core/containers_bits.c000066400000000000000000000410161421703157200213260ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include "containers/core/containers_bits.h" #include "containers/core/containers_common.h" #ifdef ENABLE_CONTAINERS_LOG_FORMAT #include "containers/core/containers_logging.h" #endif /****************************************************************************** Defines and constants. ******************************************************************************/ #ifdef ENABLE_CONTAINERS_LOG_FORMAT /** String used for indentation. If more spaces are needed, just add them. */ #define INDENT_SPACES_STRING "> " #define INDENT_SPACES_LENGTH (sizeof(INDENT_SPACES_STRING) - 1) #endif /* ENABLE_CONTAINERS_LOG_FORMAT */ /****************************************************************************** Type definitions ******************************************************************************/ /****************************************************************************** Function prototypes ******************************************************************************/ /****************************************************************************** Local Functions ******************************************************************************/ #ifdef ENABLE_CONTAINERS_LOG_FORMAT /**************************************************************************//** * Returns a string that indicates whether the bit stream is valid or not. * * \pre bit_stream is not NULL. * * \param bit_stream The bit stream object. * \return A string indicating the validity of the stream. */ static const char * vc_container_bits_valid_str( VC_CONTAINER_BITS_T *bit_stream ) { return vc_container_bits_valid(bit_stream) ? "" : " - stream invalid"; } /**************************************************************************//** * Returns a string of spaces the length of which is determined by the * parameter. * The length is limited to a certain size, above which a greater than symbol * prefixes the maximum number of spaces. * * \param length The required length of the string. * \return A string indicating the validity of the stream. */ static const char * vc_container_bits_indent_str(uint32_t length) { uint32_t str_length = length; if (str_length > INDENT_SPACES_LENGTH) str_length = INDENT_SPACES_LENGTH; return INDENT_SPACES_STRING + (INDENT_SPACES_LENGTH - str_length); } #endif /* ENABLE_CONTAINERS_LOG_FORMAT */ /**************************************************************************//** * Returns the number of consecutive zero bits in the stream. * the zero bits are terminated either by a one bit, or the end of the stream. * In the former case, the zero bits and the terminating one bit are removed * from the stream. * In the latter case, the stream becomes invalid. The stream also becomes * invalid if there are not as many bits after the one bit as zero bits before * it. * If the stream is already or becomes invalid, zero is returned. * * \pre bit_stream is not NULL. * * \param bit_stream The bit stream object. * \return The number of consecutive zero bits, or zero if the stream is * invalid. */ static uint32_t vc_container_bits_get_leading_zero_bits( VC_CONTAINER_BITS_T *bit_stream ) { uint32_t leading_zero_bits; uint32_t bits_left = vc_container_bits_available(bit_stream); uint32_t bits; uint8_t mask, current_byte; if (!bits_left) return vc_container_bits_invalidate(bit_stream); /* Cache 'bits' field to avoid repeated pointer access */ bits = bit_stream->bits; if (bits) { current_byte = *bit_stream->buffer; mask = 1 << (bits - 1); } else { /* Initialize variables to placate the compiler */ current_byte = 0; mask = 0; } /* Scan for the first one bit, counting the number of zeroes. This gives the * number of further bits after the one that are part of the value. See * section 9.1 of ITU-T REC H.264 201003 for more details. */ for (leading_zero_bits = 0; leading_zero_bits < bits_left; leading_zero_bits++) { if (!bits) { if (!bit_stream->bytes) return vc_container_bits_invalidate(bit_stream); bit_stream->bytes--; current_byte = *(++bit_stream->buffer); bits = 8; mask = 0x80; } bits--; bits_left--; if (current_byte & mask) break; /* Found the marker bit */ mask >>= 1; } /* Check enough bits are left in the stream for the value. */ if (leading_zero_bits > bits_left) return vc_container_bits_invalidate(bit_stream); /* Return cached value of bits to the stream */ bit_stream->bits = bits; return leading_zero_bits; } /***************************************************************************** Functions exported as part of the bit stream API *****************************************************************************/ /*****************************************************************************/ void vc_container_bits_init(VC_CONTAINER_BITS_T *bit_stream, const uint8_t *buffer, uint32_t available) { vc_container_assert(buffer && (buffer != (const uint8_t *)1)); /* Start with buffer pointing at the previous byte with no bits available * to make the mathematics easier */ bit_stream->buffer = buffer - 1; bit_stream->bytes = available; bit_stream->bits = 0; } /*****************************************************************************/ uint32_t vc_container_bits_invalidate( VC_CONTAINER_BITS_T *bit_stream ) { bit_stream->buffer = NULL; return 0; } /*****************************************************************************/ bool vc_container_bits_valid(VC_CONTAINER_BITS_T *bit_stream) { return (bit_stream->buffer != NULL); } /*****************************************************************************/ void vc_container_bits_reset(VC_CONTAINER_BITS_T *bit_stream) { bit_stream->bytes = 0; bit_stream->bits = 0; } /*****************************************************************************/ const uint8_t *vc_container_bits_current_pointer(const VC_CONTAINER_BITS_T *bit_stream) { const uint8_t *buffer = bit_stream->buffer; /* Only valid on byte boundaries, where buffer pointer has not been moved yet */ vc_container_assert(!bit_stream->bits); return buffer ? (buffer + 1) : NULL; } /*****************************************************************************/ void vc_container_bits_copy_stream(VC_CONTAINER_BITS_T *dst, const VC_CONTAINER_BITS_T *src) { memcpy(dst, src, sizeof(VC_CONTAINER_BITS_T)); } /*****************************************************************************/ uint32_t vc_container_bits_available(const VC_CONTAINER_BITS_T *bit_stream) { if (!bit_stream->buffer) return 0; return (bit_stream->bytes << 3) + bit_stream->bits; } /*****************************************************************************/ uint32_t vc_container_bits_bytes_available(const VC_CONTAINER_BITS_T *bit_stream) { if (!bit_stream->buffer) return 0; vc_container_assert(!bit_stream->bits); return vc_container_bits_available(bit_stream) >> 3; } /*****************************************************************************/ void vc_container_bits_skip(VC_CONTAINER_BITS_T *bit_stream, uint32_t bits_to_skip) { uint32_t have_bits; uint32_t new_bytes; have_bits = vc_container_bits_available(bit_stream); if (have_bits < bits_to_skip) { vc_container_bits_invalidate(bit_stream); return; } have_bits -= bits_to_skip; new_bytes = have_bits >> 3; bit_stream->bits = have_bits & 7; bit_stream->buffer += (bit_stream->bytes - new_bytes); bit_stream->bytes = new_bytes; } /*****************************************************************************/ void vc_container_bits_skip_bytes(VC_CONTAINER_BITS_T *bit_stream, uint32_t bytes_to_skip) { /* Only valid on byte boundaries */ vc_container_assert(!bit_stream->bits); vc_container_bits_skip(bit_stream, bytes_to_skip << 3); } /*****************************************************************************/ void vc_container_bits_reduce_bytes(VC_CONTAINER_BITS_T *bit_stream, uint32_t bytes_to_reduce) { if (bit_stream->bytes >= bytes_to_reduce) bit_stream->bytes -= bytes_to_reduce; else vc_container_bits_invalidate(bit_stream); } /*****************************************************************************/ void vc_container_bits_copy_bytes(VC_CONTAINER_BITS_T *bit_stream, uint32_t bytes_to_copy, uint8_t *dst) { vc_container_assert(!bit_stream->bits); if (bit_stream->bytes < bytes_to_copy) { /* Not enough data */ vc_container_bits_invalidate(bit_stream); return; } /* When the number of bits is zero, the next byte to take is at buffer + 1 */ memcpy(dst, bit_stream->buffer + 1, bytes_to_copy); bit_stream->buffer += bytes_to_copy; bit_stream->bytes -= bytes_to_copy; } /*****************************************************************************/ uint32_t vc_container_bits_read_u32(VC_CONTAINER_BITS_T *bit_stream, uint32_t value_bits) { uint32_t value = 0; uint32_t needed = value_bits; uint32_t bits; vc_container_assert(value_bits <= 32); if (needed > vc_container_bits_available(bit_stream)) return vc_container_bits_invalidate(bit_stream); bits = bit_stream->bits; while (needed) { uint32_t take; if (!bits) { bit_stream->bytes--; bit_stream->buffer++; bits = 8; } take = bits; if (needed < take) take = needed; bits -= take; needed -= take; value <<= take; if (take == 8) value |= *bit_stream->buffer; /* optimize whole byte case */ else value |= (*bit_stream->buffer >> bits) & ((1 << take) - 1); } bit_stream->bits = bits; return value; } /*****************************************************************************/ void vc_container_bits_skip_exp_golomb(VC_CONTAINER_BITS_T *bit_stream) { vc_container_bits_skip(bit_stream, vc_container_bits_get_leading_zero_bits(bit_stream)); } /*****************************************************************************/ uint32_t vc_container_bits_read_u32_exp_golomb(VC_CONTAINER_BITS_T *bit_stream) { uint32_t leading_zero_bits; uint32_t codeNum; leading_zero_bits = vc_container_bits_get_leading_zero_bits(bit_stream); /* Anything bigger than 32 bits is definitely overflow */ if (leading_zero_bits > 32) return vc_container_bits_invalidate(bit_stream); codeNum = vc_container_bits_read_u32(bit_stream, leading_zero_bits); if (leading_zero_bits == 32) { /* If codeNum is non-zero, it would need 33 bits, so is also overflow */ if (codeNum) return vc_container_bits_invalidate(bit_stream); return 0xFFFFFFFF; } return codeNum + (1 << leading_zero_bits) - 1; } /*****************************************************************************/ int32_t vc_container_bits_read_s32_exp_golomb(VC_CONTAINER_BITS_T *bit_stream) { uint32_t uval; uval = vc_container_bits_read_u32_exp_golomb(bit_stream); /* The signed Exp-Golomb code 0xFFFFFFFF cannot be represented as a signed 32-bit * integer, because it should be one larger than the largest positive value. */ if (uval == 0xFFFFFFFF) return vc_container_bits_invalidate(bit_stream); /* Definition of conversion is * s = ((-1)^(u + 1)) * Ceil(u / 2) * where '^' is power, but this should be equivalent */ return ((int32_t)((uval & 1) << 1) - 1) * (int32_t)((uval >> 1) + (uval & 1)); } #ifdef ENABLE_CONTAINERS_LOG_FORMAT /*****************************************************************************/ void vc_container_bits_log(VC_CONTAINER_T *p_ctx, uint32_t indent, const char *txt, VC_CONTAINER_BITS_T *bit_stream, VC_CONTAINER_BITS_LOG_OP_T op, uint32_t length) { const char *valid_str = vc_container_bits_valid_str(bit_stream); const char *indent_str = vc_container_bits_indent_str(indent); switch (op) { case VC_CONTAINER_BITS_LOG_SKIP: vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bits skipped%s", indent_str, txt, length, valid_str); break; case VC_CONTAINER_BITS_LOG_SKIP_BYTES: vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bytes skipped%s", indent_str, txt, length, valid_str); break; case VC_CONTAINER_BITS_LOG_COPY_BYTES: vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bytes copied%s", indent_str, txt, length, valid_str); break; case VC_CONTAINER_BITS_LOG_REDUCE_BYTES: vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bytes reduced%s", indent_str, txt, length, valid_str); break; case VC_CONTAINER_BITS_LOG_EG_SKIP: vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: Exp-Golomb value skipped%s", indent_str, txt, valid_str); break; default: /* Unexpected operation. Check bit stream logging macros */ vc_container_assert(0); } } /*****************************************************************************/ uint32_t vc_container_bits_log_u32(VC_CONTAINER_T *p_ctx, uint32_t indent, const char *txt, VC_CONTAINER_BITS_T *bit_stream, VC_CONTAINER_BITS_LOG_OP_T op, uint32_t length, uint32_t value) { const char *valid_str = vc_container_bits_valid_str(bit_stream); const char *indent_str = vc_container_bits_indent_str(indent); switch (op) { case VC_CONTAINER_BITS_LOG_U8: vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%02x (%u) in %u bits%s", indent_str, txt, value, value, length, valid_str); break; case VC_CONTAINER_BITS_LOG_U16: vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%04x (%u) in %u bits%s", indent_str, txt, value, value, length, valid_str); break; case VC_CONTAINER_BITS_LOG_U32: vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%08x (%u) in %u bits%s", indent_str, txt, value, value, length, valid_str); break; case VC_CONTAINER_BITS_LOG_EG_U32: vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%08x (%u) unsigned Exp-Golomb%s", indent_str, txt, value, value, valid_str); break; default: /* Unexpected operation. Check bit stream logging macros */ vc_container_assert(0); } return value; } /*****************************************************************************/ int32_t vc_container_bits_log_s32(VC_CONTAINER_T *p_ctx, uint32_t indent, const char *txt, VC_CONTAINER_BITS_T *bit_stream, VC_CONTAINER_BITS_LOG_OP_T op, uint32_t length, int32_t value) { const char *valid_str = vc_container_bits_valid_str(bit_stream); const char *indent_str = vc_container_bits_indent_str(indent); VC_CONTAINER_PARAM_UNUSED(length); switch (op) { case VC_CONTAINER_BITS_LOG_EG_S32: vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%08x (%d) signed Exp-Golomb%s", indent_str, txt, value, value, valid_str); break; default: /* Unexpected operation. Check bit stream logging macros */ vc_container_assert(0); } return value; } #endif /* ENABLE_CONTAINERS_LOG_FORMAT */ userland/containers/core/containers_bits.h000066400000000000000000000404011421703157200213300ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_CONTAINERS_BITS_H #define VC_CONTAINERS_BITS_H #include "containers/containers.h" /** Bit stream structure * Value are read from the buffer, taking bits from MSB to LSB in sequential * bytes until the number of bit and the number of bytes runs out. */ typedef struct vc_container_bits_tag { const uint8_t *buffer; /**< Buffer from which to take bits */ uint32_t bytes; /**< Number of bytes available from buffer */ uint32_t bits; /**< Number of bits available at current pointer */ } VC_CONTAINER_BITS_T; /** Initialise a bit stream object. * * \pre bit_stream is not NULL. * * \param bit_stream The bit stream object to initialise. * \param buffer Pointer to the start of the byte buffer. * \param available Number of bytes in the bit stream. */ void vc_container_bits_init(VC_CONTAINER_BITS_T *bit_stream, const uint8_t *buffer, uint32_t available); /** Invalidates the bit stream. * Also returns zero, because it allows callers that need to invalidate and * immediately return zero to do so in a single statement. * * \pre bit_stream is not NULL. * * \param bit_stream The bit stream object. * \return Zero, always. */ uint32_t vc_container_bits_invalidate( VC_CONTAINER_BITS_T *bit_stream ); /** Returns true if the bit stream is currently valid. * The stream becomes invalid when a read or skip operation goe beyond the end * of the stream. * * \pre bit_stream is not NULL. * * \param bit_stream The bit stream object. * \return True if the stream is valid, false if it is invalid. */ bool vc_container_bits_valid(VC_CONTAINER_BITS_T *bit_stream); /** Reset a valid bit stream object to appear empty. * Once a stream has become invalid, reset has no effect. * * \pre bit_stream is not NULL. * * \param bit_stream The bit stream object. */ void vc_container_bits_reset(VC_CONTAINER_BITS_T *bit_stream); /** Return the current byte pointer for the bit stream. * * \pre bit_stream is not NULL. * \pre The stream is on a byte boundary. * * \param bit_stream The bit stream object. * \return The current byte pointer, or NULL if the stream is invalid. */ const uint8_t *vc_container_bits_current_pointer(const VC_CONTAINER_BITS_T *bit_stream); /** Copy one bit stream to another. * If the source stream is invalid, the destination one will become so as well. * * \pre Neither bit stream is NULL. * * \param dst The destination bit stream object. * \param src The source bit stream object. */ void vc_container_bits_copy_stream(VC_CONTAINER_BITS_T *dst, const VC_CONTAINER_BITS_T *src); /** Return the number of bits left to take from the stream. * If the stream is invalid, zero is returned. * * \pre bit_stream is not NULL. * * \param bit_stream The bit stream object. * \return The number of bits left to take. */ uint32_t vc_container_bits_available(const VC_CONTAINER_BITS_T *bit_stream); /** Return the number of bytes left to take from the stream. * If the stream is invalid, zero is returned. * * \pre bit_stream is not NULL. * * \param bit_stream The bit stream object. * \return The number of bytes left to take. */ uint32_t vc_container_bits_bytes_available(const VC_CONTAINER_BITS_T *bit_stream); /** Skip past a number of bits in the stream. * If bits_to_skip is greater than the number of bits available in the stream, * the stream becomes invalid. * If the stream is already invalid, this has no effect. * * \pre bit_stream is not NULL. * * \param bit_stream The bit stream object. * \param bits_to_skip The number of bits to skip. */ void vc_container_bits_skip(VC_CONTAINER_BITS_T *bit_stream, uint32_t bits_to_skip); /** Skip past a number of bytes in the stream. * If bytes_to_skip is greater than the number of bytes available in the stream, * the stream becomes invalid. * If the stream is already invalid, this has no effect. * * \pre bit_stream is not NULL. * \pre The stream is on a byte boundary. * * \param bit_stream The bit stream object. * \param bytes_to_skip The number of bytes to skip. */ void vc_container_bits_skip_bytes(VC_CONTAINER_BITS_T *bit_stream, uint32_t bytes_to_skip); /** Reduce the length of the bit stream by a number of bytes. * This reduces the number of bits/bytes available without changing the current * position in the stream. If bytes_to_reduce is greater than the number of * bytes available in the stream, the stream becomes invalid. * If the stream is already invalid, this has no effect. * * \pre bit_stream is not NULL. * * \param bit_stream The bit stream object. * \param bytes_to_reduce The number of bytes by which to reduce the stream. */ void vc_container_bits_reduce_bytes(VC_CONTAINER_BITS_T *bit_stream, uint32_t bytes_to_reduce); /** Copies a number of bytes from the stream to a byte buffer. * If the stream is or becomes invalid, no data is copied. * * \pre bit_stream is not NULL. * \pre The stream is on a byte boundary. * * \param bit_stream The bit stream object. * \param bytes_to_copy The number of bytes to copy. * \param dst The byte buffer destination. */ void vc_container_bits_copy_bytes(VC_CONTAINER_BITS_T *bit_stream, uint32_t bytes_to_copy, uint8_t *dst); /** Returns the next value_bits from the stream. The last bit will be the least * significant bit in the returned value. * If value_bits is greater than the number of bits available in the stream, * the stream becomes invalid. * If the stream is invalid, or becomes invalid while reading the value, zero * is returned. * * \pre bit_stream is not NULL. * \pre value_bits is not larger than 32. * * \param bit_stream The bit stream object. * \param value_bits The number of bits to retrieve. * \return The value read from the stream, or zero if the stream is invalid. */ uint32_t vc_container_bits_read_u32(VC_CONTAINER_BITS_T *bit_stream, uint32_t value_bits); /** Skips the next Exp-Golomb value in the stream. * See section 9.1 of ITU-T REC H.264 201003 for details. * If there are not enough bits in the stream to complete an Exp-Golomb value, * the stream becomes invalid. * If the stream is already invalid, this has no effect. * * \pre bit_stream is not NULL. * * \param bit_stream The bit stream object. */ void vc_container_bits_skip_exp_golomb(VC_CONTAINER_BITS_T *bit_stream); /** Returns the next unsigned Exp-Golomb value from the stream. * See section 9.1 of ITU-T REC H.264 201003 for details. * If there are not enough bits in the stream to complete an Exp-Golomb value, * the stream becomes invalid. * If the next unsigned Exp-Golomb value in the stream is larger than 32 bits, * or the stream is or becomes invalid, zero is returned. * * \pre bit_stream is not NULL. * * \param bit_stream The bit stream object. * \return The next unsigned value from the stream, or zero on error. */ uint32_t vc_container_bits_read_u32_exp_golomb(VC_CONTAINER_BITS_T *bit_stream); /** Returns the next signed Exp-Golomb value from the stream. * See section 9.1.1 of ITU-T REC H.264 201003 for details. * If there are not enough bits in the stream to complete an Exp-Golomb value, * the stream becomes invalid. * If the next signed Exp-Golomb value in the stream is larger than 32 bits, * or the stream is or becomes invalid, zero is returned. * * \pre bit_stream is not NULL. * * \param bit_stream The bit stream object. * \return The next signed value from the stream, or zero on error. */ int32_t vc_container_bits_read_s32_exp_golomb(VC_CONTAINER_BITS_T *bit_stream); /****************************************************************************** * Macros reduce function name length and enable logging of some operations * ******************************************************************************/ #define BITS_INIT(ctx, bits, buffer, available) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_init(bits, buffer, available)) #define BITS_INVALIDATE(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_invalidate(bits)) #define BITS_VALID(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_valid(bits)) #define BITS_RESET(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_reset(bits)) #define BITS_AVAILABLE(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_available(bits)) #define BITS_BYTES_AVAILABLE(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_bytes_available(bits)) #define BITS_CURRENT_POINTER(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_current_pointer(bits)) #define BITS_COPY_STREAM(ctx, dst, src) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_copy_stream(dst, src)) #ifdef ENABLE_CONTAINERS_LOG_FORMAT typedef enum { VC_CONTAINER_BITS_LOG_SKIP, VC_CONTAINER_BITS_LOG_SKIP_BYTES, VC_CONTAINER_BITS_LOG_U8, VC_CONTAINER_BITS_LOG_U16, VC_CONTAINER_BITS_LOG_U32, VC_CONTAINER_BITS_LOG_COPY_BYTES, VC_CONTAINER_BITS_LOG_REDUCE_BYTES, VC_CONTAINER_BITS_LOG_EG_SKIP, VC_CONTAINER_BITS_LOG_EG_U32, VC_CONTAINER_BITS_LOG_EG_S32, } VC_CONTAINER_BITS_LOG_OP_T; /** Logs an operation with void return. * * \pre None of p_ctx, txt or bit_stream are NULL. * * \param p_ctx Container context. * \param indent Indent level. * \param txt Description of what is being read. * \param bit_stream The bit stream object. * \param op The operation just performed. * \param length The length of the operation. */ void vc_container_bits_log(VC_CONTAINER_T *p_ctx, uint32_t indent, const char *txt, VC_CONTAINER_BITS_T *bit_stream, VC_CONTAINER_BITS_LOG_OP_T op, uint32_t length); /** Logs an operation with unsigned 32-bit integer return. * * \pre None of p_ctx, txt or bit_stream are NULL. * * \param p_ctx Container context. * \param indent Indent level. * \param txt Description of what is being read. * \param bit_stream The bit stream object. * \param op The operation just performed. * \param length The length of the operation. * \param value The value returned by the operation. * \return The unsigned 32-bit integer value passed in. */ uint32_t vc_container_bits_log_u32(VC_CONTAINER_T *p_ctx, uint32_t indent, const char *txt, VC_CONTAINER_BITS_T *bit_stream, VC_CONTAINER_BITS_LOG_OP_T op, uint32_t length, uint32_t value); /** Logs an operation with signed 32-bit integer return. * * \pre None of p_ctx, txt or bit_stream are NULL. * * \param p_ctx Container context. * \param indent Indent level. * \param txt Description of what is being read. * \param bit_stream The bit stream object. * \param op The operation just performed. * \param length The length of the operation. * \param value The value returned by the operation. * \return The signed 32-bit integer value passed in. */ int32_t vc_container_bits_log_s32(VC_CONTAINER_T *p_ctx, uint32_t indent, const char *txt, VC_CONTAINER_BITS_T *bit_stream, VC_CONTAINER_BITS_LOG_OP_T op, uint32_t length, int32_t value); #ifndef BITS_LOG_INDENT # ifndef CONTAINER_HELPER_LOG_INDENT # define BITS_LOG_INDENT(ctx) 0 # else # define BITS_LOG_INDENT(ctx) ((ctx)->priv->io->module ? CONTAINER_HELPER_LOG_INDENT(a) : 0) # endif #endif #define BITS_SKIP(ctx, bits, length, txt) (vc_container_bits_skip(bits, length), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_SKIP, length)) #define BITS_SKIP_BYTES(ctx, bits, length, txt) (vc_container_bits_skip_bytes(bits, length), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_SKIP_BYTES, length)) #define BITS_READ_U8(ctx, bits, length, txt) (uint8_t)vc_container_bits_log_u32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_U8, length, vc_container_bits_read_u32(bits, length)) #define BITS_READ_U16(ctx, bits, length, txt) (uint16_t)vc_container_bits_log_u32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_U16, length, vc_container_bits_read_u32(bits, length)) #define BITS_READ_U32(ctx, bits, length, txt) vc_container_bits_log_u32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_U32, length, vc_container_bits_read_u32(bits, length)) #define BITS_COPY_BYTES(ctx, bits, length, dst, txt) (vc_container_bits_copy_bytes(bits, length, dst), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_COPY_BYTES, length)) #define BITS_REDUCE_BYTES(ctx, bits, length, txt) (vc_container_bits_reduce_bytes(bits, length), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_REDUCE_BYTES, length)) #define BITS_SKIP_EXP(ctx, bits, txt) (vc_container_bits_skip_exp_golomb(bits), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_EG_SKIP, 0)) #define BITS_READ_S32_EXP(ctx, bits, txt) vc_container_bits_log_s32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_EG_S32, 0, vc_container_bits_read_s32_exp_golomb(bits)) #define BITS_READ_U32_EXP(ctx, bits, txt) vc_container_bits_log_u32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_EG_U32, 0, vc_container_bits_read_u32_exp_golomb(bits)) #else /* ENABLE_CONTAINERS_LOG_FORMAT */ #define BITS_SKIP(ctx, bits, length, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_skip(bits, length)) #define BITS_SKIP_BYTES(ctx, bits, length, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_skip_bytes(bits, length)) #define BITS_READ_U8(ctx, bits, length, txt) (uint8_t)(VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_u32(bits, length)) #define BITS_READ_U16(ctx, bits, length, txt) (uint16_t)(VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_u32(bits, length)) #define BITS_READ_U32(ctx, bits, length, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_u32(bits, length)) #define BITS_COPY_BYTES(ctx, bits, length, dst, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_copy_bytes(bits, length, dst)) #define BITS_REDUCE_BYTES(ctx, bits, length, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_reduce_bytes(bits, length)) #define BITS_SKIP_EXP(ctx, bits, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_skip_exp_golomb(bits)) #define BITS_READ_S32_EXP(ctx, bits, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_s32_exp_golomb(bits)) #define BITS_READ_U32_EXP(ctx, bits, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_u32_exp_golomb(bits)) #endif /* ENABLE_CONTAINERS_LOG_FORMAT */ #endif /* VC_CONTAINERS_BITS_H */ userland/containers/core/containers_bytestream.h000066400000000000000000000271131421703157200225530ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_CONTAINERS_BYTESTREAM_H #define VC_CONTAINERS_BYTESTREAM_H /** \file * Utility functions to provide a byte stream out of a list of container packets */ typedef struct VC_CONTAINER_BYTESTREAM_T { VC_CONTAINER_PACKET_T *first; /**< first packet in the chain */ VC_CONTAINER_PACKET_T **last; /**< last packet in the chain */ VC_CONTAINER_PACKET_T *current; /**< packet containing the current read pointer */ size_t current_offset; /**< position of current packet (in bytes) */ size_t offset; /**< position within the current packet */ size_t bytes; /**< Number of bytes available in the bytestream */ } VC_CONTAINER_BYTESTREAM_T; /*****************************************************************************/ STATIC_INLINE void bytestream_init( VC_CONTAINER_BYTESTREAM_T *stream ) { stream->first = stream->current = NULL; stream->last = &stream->first; stream->offset = stream->current_offset = stream->bytes = 0; } STATIC_INLINE void bytestream_push( VC_CONTAINER_BYTESTREAM_T *stream, VC_CONTAINER_PACKET_T *packet ) { *stream->last = packet; stream->last = &packet->next; packet->next = NULL; if( !stream->current ) stream->current = packet; stream->bytes += packet->size; } STATIC_INLINE VC_CONTAINER_PACKET_T *bytestream_pop( VC_CONTAINER_BYTESTREAM_T *stream ) { VC_CONTAINER_PACKET_T *packet = stream->first; if( stream->current == packet ) return NULL; vc_container_assert(packet); stream->bytes -= packet->size; stream->current_offset -= packet->size; stream->first = packet->next; if( !stream->first ) stream->last = &stream->first; return packet; } STATIC_INLINE VC_CONTAINER_PACKET_T *bytestream_get_packet( VC_CONTAINER_BYTESTREAM_T *stream, size_t *offset ) { VC_CONTAINER_PACKET_T *packet = stream->current; size_t off = stream->offset; while(packet && packet->size == off) { packet = packet->next; off = 0; } if (offset) *offset = off; return packet; } STATIC_INLINE bool bytestream_skip_packet( VC_CONTAINER_BYTESTREAM_T *stream ) { VC_CONTAINER_PACKET_T *packet = stream->current; if( packet ) { stream->current = packet->next; stream->current_offset += (packet->size - stream->offset); stream->offset = 0; } return !!packet; } STATIC_INLINE void bytestream_get_timestamps( VC_CONTAINER_BYTESTREAM_T *stream, int64_t *pts, int64_t *dts, bool b_same ) { VC_CONTAINER_PACKET_T *packet = bytestream_get_packet( stream, 0 ); if(packet) { if(b_same && packet->pts == VC_CONTAINER_TIME_UNKNOWN) packet->pts = packet->dts; if(pts) *pts = packet->pts; if(dts) *dts = packet->dts; packet->pts = packet->dts = VC_CONTAINER_TIME_UNKNOWN; return; } if(pts) *pts = VC_CONTAINER_TIME_UNKNOWN; if(dts) *dts = VC_CONTAINER_TIME_UNKNOWN; } STATIC_INLINE void bytestream_get_timestamps_and_offset( VC_CONTAINER_BYTESTREAM_T *stream, int64_t *pts, int64_t *dts, size_t *offset, bool b_same ) { VC_CONTAINER_PACKET_T *packet = bytestream_get_packet( stream, offset ); if(packet) { if(b_same && packet->pts == VC_CONTAINER_TIME_UNKNOWN) packet->pts = packet->dts; if(pts) *pts = packet->pts; if(dts) *dts = packet->dts; packet->pts = packet->dts = VC_CONTAINER_TIME_UNKNOWN; return; } if(pts) *pts = VC_CONTAINER_TIME_UNKNOWN; if(dts) *dts = VC_CONTAINER_TIME_UNKNOWN; } STATIC_INLINE size_t bytestream_size( VC_CONTAINER_BYTESTREAM_T *stream ) { return stream->bytes - stream->current_offset - stream->offset; } STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_skip( VC_CONTAINER_BYTESTREAM_T *stream, size_t size ) { VC_CONTAINER_PACKET_T *packet; size_t offset, bytes = 0, skip; if( !size ) return VC_CONTAINER_SUCCESS; /* Nothing to do */ if( stream->bytes - stream->current_offset - stream->offset < size ) return VC_CONTAINER_ERROR_EOS; /* Not enough data */ for( packet = stream->current, offset = stream->offset; ; packet = packet->next, offset = 0 ) { if( packet->size - offset >= size) break; skip = packet->size - offset; bytes += skip; size -= skip; } stream->current = packet; stream->current_offset += stream->offset - offset + bytes; stream->offset = offset + size; return VC_CONTAINER_SUCCESS; } STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_get( VC_CONTAINER_BYTESTREAM_T *stream, uint8_t *data, size_t size ) { VC_CONTAINER_PACKET_T *packet; size_t offset, bytes = 0, copy; if( !size ) return VC_CONTAINER_SUCCESS; /* Nothing to do */ if( stream->bytes - stream->current_offset - stream->offset < size ) return VC_CONTAINER_ERROR_EOS; /* Not enough data */ for( packet = stream->current, offset = stream->offset; ; packet = packet->next, offset = 0 ) { if( packet->size - offset >= size) break; copy = packet->size - offset; memcpy( data, packet->data + offset, copy ); bytes += copy; data += copy; size -= copy; } memcpy( data, packet->data + offset, size ); stream->current = packet; stream->current_offset += stream->offset - offset + bytes; stream->offset = offset + size; return VC_CONTAINER_SUCCESS; } STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_peek( VC_CONTAINER_BYTESTREAM_T *stream, uint8_t *data, size_t size ) { VC_CONTAINER_PACKET_T *packet; size_t offset, copy; if( !size ) return VC_CONTAINER_SUCCESS; /* Nothing to do */ if( stream->bytes - stream->current_offset - stream->offset < size ) return VC_CONTAINER_ERROR_EOS; /* Not enough data */ for( packet = stream->current, offset = stream->offset; ; packet = packet->next, offset = 0 ) { if( packet->size - offset >= size) break; copy = packet->size - offset; memcpy( data, packet->data + offset, copy ); data += copy; size -= copy; } memcpy( data, packet->data + offset, size ); return VC_CONTAINER_SUCCESS; } STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_peek_at( VC_CONTAINER_BYTESTREAM_T *stream, size_t peek_offset, uint8_t *data, size_t size ) { VC_CONTAINER_PACKET_T *packet; size_t copy; if( !size ) return VC_CONTAINER_SUCCESS; /* Nothing to do */ if( stream->bytes - stream->current_offset - stream->offset < peek_offset + size ) return VC_CONTAINER_ERROR_EOS; /* Not enough data */ peek_offset += stream->offset; /* Find the right place */ for( packet = stream->current; ; packet = packet->next ) { if( packet->size > peek_offset ) break; peek_offset -= packet->size; } /* Copy the data */ for( ; ; packet = packet->next, peek_offset = 0 ) { if( packet->size - peek_offset >= size) break; copy = packet->size - peek_offset; memcpy( data, packet->data + peek_offset, copy ); data += copy; size -= copy; } memcpy( data, packet->data + peek_offset, size ); return VC_CONTAINER_SUCCESS; } STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_skip_byte( VC_CONTAINER_BYTESTREAM_T *stream ) { VC_CONTAINER_PACKET_T *packet = stream->current; if( !packet ) return VC_CONTAINER_ERROR_EOS; /* Fast path first */ if( packet->size - stream->offset ) { stream->offset++; return VC_CONTAINER_SUCCESS; } return bytestream_skip( stream, 1 ); } STATIC_INLINE VC_CONTAINER_STATUS_T packet_peek_byte( VC_CONTAINER_BYTESTREAM_T *stream, uint8_t *data ) { VC_CONTAINER_PACKET_T *packet = stream->current; if( !packet ) return VC_CONTAINER_ERROR_EOS; /* Fast path first */ if( packet->size - stream->offset ) { *data = packet->data[stream->offset]; return VC_CONTAINER_SUCCESS; } return bytestream_peek( stream, data, 1 ); } STATIC_INLINE VC_CONTAINER_STATUS_T packet_get_byte( VC_CONTAINER_BYTESTREAM_T *stream, uint8_t *data ) { VC_CONTAINER_PACKET_T *packet = stream->current; if( !packet ) return VC_CONTAINER_ERROR_EOS; /* Fast path first */ if( packet->size - stream->offset ) { *data = packet->data[stream->offset]; stream->offset++; return VC_CONTAINER_SUCCESS; } return bytestream_get( stream, data, 1 ); } STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_find_startcode( VC_CONTAINER_BYTESTREAM_T *stream, size_t *search_offset, const uint8_t *startcode, unsigned int length ) { VC_CONTAINER_PACKET_T *packet, *backup_packet = NULL; size_t position, start_offset = position = *search_offset; size_t offset, backup_offset = 0; unsigned int match = 0; if( stream->bytes - stream->current_offset - stream->offset < start_offset + length ) return VC_CONTAINER_ERROR_EOS; /* Not enough data */ /* Find the right place */ for( packet = stream->current, offset = stream->offset; packet != NULL; packet = packet->next, offset = 0 ) { if( packet->size - offset > start_offset) break; start_offset -= (packet->size - offset); } /* Start the search for the start code. * To make things simple we try to find a match one byte at a time. */ for( offset += start_offset; packet != NULL; packet = packet->next, offset = 0 ) { for( ; offset < packet->size; offset++ ) { if( packet->data[offset] != startcode[match] ) { if ( match ) /* False positive */ { packet = backup_packet; offset = backup_offset; match = 0; } position++; continue; } /* We've got a match */ if( !match++ ) { backup_packet = packet; backup_offset = offset; } if( match == length ) { /* We have the full start code it */ *search_offset = position; return VC_CONTAINER_SUCCESS; } } } *search_offset = position; return VC_CONTAINER_ERROR_EOS; /* No luck in finding the start code */ } #endif /* VC_CONTAINERS_BYTESTREAM_H */ userland/containers/core/containers_codecs.c000066400000000000000000000234631421703157200216330ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include "containers/containers.h" #include "containers/containers_codecs.h" #include "containers/core/containers_utils.h" /*****************************************************************************/ static struct { VC_CONTAINER_FOURCC_T codec; uint16_t id; } codec_to_wf_table[] = { {VC_CONTAINER_CODEC_PCM_SIGNED_LE, WAVE_FORMAT_PCM}, {VC_CONTAINER_CODEC_ALAW, WAVE_FORMAT_ALAW}, {VC_CONTAINER_CODEC_MULAW, WAVE_FORMAT_MULAW}, {VC_CONTAINER_CODEC_ADPCM_MS, WAVE_FORMAT_ADPCM}, {VC_CONTAINER_CODEC_MPGA, WAVE_FORMAT_MPEG}, {VC_CONTAINER_CODEC_MPGA, WAVE_FORMAT_MPEGLAYER3}, {VC_CONTAINER_CODEC_WMA1, WAVE_FORMAT_WMAUDIO1}, {VC_CONTAINER_CODEC_WMA2, WAVE_FORMAT_WMAUDIO2}, {VC_CONTAINER_CODEC_WMAP, WAVE_FORMAT_WMAUDIOPRO}, {VC_CONTAINER_CODEC_WMAL, WAVE_FORMAT_WMAUDIO_LOSSLESS}, {VC_CONTAINER_CODEC_WMAV, WAVE_FORMAT_WMAUDIO_VOICE}, {VC_CONTAINER_CODEC_AC3, WAVE_FORMAT_DVM}, {VC_CONTAINER_CODEC_AC3, WAVE_FORMAT_DOLBY_AC3_SPDIF}, /**< AC-3 padded for S/PDIF */ {VC_CONTAINER_CODEC_AC3, WAVE_FORMAT_RAW_SPORT}, /**< AC-3 padded for S/PDIF */ {VC_CONTAINER_CODEC_AC3, WAVE_FORMAT_ESST_AC3}, /**< AC-3 padded for S/PDIF */ {VC_CONTAINER_CODEC_EAC3, WAVE_FORMAT_DVM}, {VC_CONTAINER_CODEC_DTS, WAVE_FORMAT_DTS}, #if 0 {CODEC_G726, WAVE_FORMAT_G726_ADPCM}, {CODEC_G726, WAVE_FORMAT_DF_G726}, {CODEC_G726, WAVE_FORMAT_G726ADPCM}, {CODEC_G726, WAVE_FORMAT_PANASONIC_G726}, #endif {VC_CONTAINER_CODEC_MP4A, WAVE_FORMAT_AAC}, {VC_CONTAINER_CODEC_MP4A, WAVE_FORMAT_MP4A}, {VC_CONTAINER_CODEC_ATRAC3, WAVE_FORMAT_SONY_SCX}, {VC_CONTAINER_CODEC_UNKNOWN, WAVE_FORMAT_UNKNOWN} }; VC_CONTAINER_FOURCC_T waveformat_to_codec(uint16_t waveformat_id) { unsigned int i; for(i = 0; codec_to_wf_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) if(codec_to_wf_table[i].id == waveformat_id) break; return codec_to_wf_table[i].codec; } uint16_t codec_to_waveformat(VC_CONTAINER_FOURCC_T codec) { unsigned int i; for(i = 0; codec_to_wf_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) if(codec_to_wf_table[i].codec == codec) break; return codec_to_wf_table[i].id; } static struct { VC_CONTAINER_FOURCC_T codec; uint32_t fourcc; } codec_to_vfw_table[] = { #if defined(ENABLE_CONTAINERS_STANDALONE) || !defined(NDEBUG) /* We are legally required to not play DivX in RELEASE mode. See Jira SW-3138 */ {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('D','I','V','3')}, {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('d','i','v','3')}, {VC_CONTAINER_CODEC_DIV4, VC_FOURCC('D','I','V','4')}, {VC_CONTAINER_CODEC_DIV4, VC_FOURCC('d','i','v','4')}, {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('D','X','5','0')}, {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('D','I','V','X')}, {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('d','i','v','x')}, #endif /* ENABLE_CONTAINERS_STANDALONE || !NDEBUG */ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','P','4','V')}, {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','p','4','v')}, {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','P','4','S')}, {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','p','4','s')}, {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','4','S','2')}, {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','4','s','2')}, {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('F','M','P','4')}, {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('X','V','I','D')}, {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('x','v','i','d')}, {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('M','P','4','3')}, {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('m','p','4','3')}, {VC_CONTAINER_CODEC_MP1V, VC_FOURCC('m','p','g','1')}, {VC_CONTAINER_CODEC_MP1V, VC_FOURCC('M','P','G','1')}, {VC_CONTAINER_CODEC_MP2V, VC_FOURCC('m','p','g','2')}, {VC_CONTAINER_CODEC_MP2V, VC_FOURCC('M','P','G','2')}, {VC_CONTAINER_CODEC_MJPEG, VC_FOURCC('M','J','P','G')}, {VC_CONTAINER_CODEC_MJPEG, VC_FOURCC('m','j','p','g')}, {VC_CONTAINER_CODEC_WMV1, VC_FOURCC('W','M','V','1')}, {VC_CONTAINER_CODEC_WMV1, VC_FOURCC('w','m','v','1')}, {VC_CONTAINER_CODEC_WMV2, VC_FOURCC('W','M','V','2')}, {VC_CONTAINER_CODEC_WMV2, VC_FOURCC('w','m','v','2')}, {VC_CONTAINER_CODEC_WMV3, VC_FOURCC('W','M','V','3')}, {VC_CONTAINER_CODEC_WMV3, VC_FOURCC('w','m','v','3')}, {VC_CONTAINER_CODEC_WVC1, VC_FOURCC('W','V','C','1')}, {VC_CONTAINER_CODEC_WVC1, VC_FOURCC('w','v','c','1')}, {VC_CONTAINER_CODEC_WMVA, VC_FOURCC('w','m','v','a')}, {VC_CONTAINER_CODEC_WMVA, VC_FOURCC('W','M','V','A')}, {VC_CONTAINER_CODEC_VP6, VC_FOURCC('V','P','6','F')}, {VC_CONTAINER_CODEC_VP6, VC_FOURCC('v','p','6','f')}, {VC_CONTAINER_CODEC_VP7, VC_FOURCC('V','P','7','0')}, {VC_CONTAINER_CODEC_VP7, VC_FOURCC('v','p','7','0')}, {VC_CONTAINER_CODEC_H263, VC_FOURCC('H','2','6','3')}, {VC_CONTAINER_CODEC_H263, VC_FOURCC('h','2','6','3')}, {VC_CONTAINER_CODEC_H264, VC_FOURCC('H','2','6','4')}, {VC_CONTAINER_CODEC_H264, VC_FOURCC('h','2','6','4')}, {VC_CONTAINER_CODEC_H264, VC_FOURCC('A','V','C','1')}, {VC_CONTAINER_CODEC_H264, VC_FOURCC('a','v','c','1')}, {VC_CONTAINER_CODEC_SPARK, VC_FOURCC('F','L','V','1')}, {VC_CONTAINER_CODEC_SPARK, VC_FOURCC('f','l','v','1')}, {VC_CONTAINER_CODEC_UNKNOWN, 0} }; VC_CONTAINER_FOURCC_T vfw_fourcc_to_codec(uint32_t fourcc) { unsigned int i; for(i = 0; codec_to_vfw_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) if(codec_to_vfw_table[i].fourcc == fourcc) break; if(codec_to_vfw_table[i].codec == VC_CONTAINER_CODEC_UNKNOWN) return fourcc; return codec_to_vfw_table[i].codec; } uint32_t codec_to_vfw_fourcc(VC_CONTAINER_FOURCC_T codec) { unsigned int i; for(i = 0; codec_to_vfw_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) if(codec_to_vfw_table[i].codec == codec) break; return codec_to_vfw_table[i].fourcc; } static struct { VC_CONTAINER_FOURCC_T codec; uint32_t fourcc; } codec_to_fourcc_table[] = { {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','P','4','S')}, {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','4','S','2')}, {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','p','4','s')}, {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','4','s','2')}, {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','P','4','V')}, {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','p','4','v')}, {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('F','M','P','4')}, {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('M','P','4','3')}, {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('m','p','4','3')}, {VC_CONTAINER_CODEC_WMV1, VC_FOURCC('W','M','V','1')}, {VC_CONTAINER_CODEC_WMV1, VC_FOURCC('w','m','v','1')}, {VC_CONTAINER_CODEC_WMV2, VC_FOURCC('W','M','V','2')}, {VC_CONTAINER_CODEC_WMV2, VC_FOURCC('w','m','v','2')}, {VC_CONTAINER_CODEC_WMV3, VC_FOURCC('W','M','V','3')}, {VC_CONTAINER_CODEC_WMV3, VC_FOURCC('w','m','v','3')}, {VC_CONTAINER_CODEC_MP1V, VC_FOURCC('m','p','g','1')}, {VC_CONTAINER_CODEC_MP1V, VC_FOURCC('M','P','G','1')}, {VC_CONTAINER_CODEC_MP2V, VC_FOURCC('m','p','g','2')}, {VC_CONTAINER_CODEC_MP2V, VC_FOURCC('M','P','G','2')}, {VC_CONTAINER_CODEC_MJPEG, VC_FOURCC('M','J','P','G')}, {VC_CONTAINER_CODEC_MJPEG, VC_FOURCC('m','j','p','g')}, {VC_CONTAINER_CODEC_UNKNOWN, 0} }; VC_CONTAINER_FOURCC_T fourcc_to_codec(uint32_t fourcc) { unsigned int i; for(i = 0; codec_to_fourcc_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) if(codec_to_fourcc_table[i].fourcc == fourcc) break; return codec_to_fourcc_table[i].codec; } uint32_t codec_to_fourcc(VC_CONTAINER_FOURCC_T codec) { unsigned int i; for(i = 0; codec_to_fourcc_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) if(codec_to_fourcc_table[i].codec == codec) break; return codec_to_fourcc_table[i].fourcc; } userland/containers/core/containers_common.h000066400000000000000000000052561421703157200216700ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_CONTAINERS_COMMON_H #define VC_CONTAINERS_COMMON_H /** \file containers_common.h * Common definitions for containers infrastructure */ #ifndef ENABLE_CONTAINERS_STANDALONE # include "vcos.h" # define vc_container_assert(a) vcos_assert(a) #else # include "assert.h" # define vc_container_assert(a) assert(a) #endif /* ENABLE_CONTAINERS_STANDALONE */ #ifndef countof # define countof(a) (sizeof(a) / sizeof(a[0])) #endif #ifndef MIN # define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif #ifndef MAX # define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif #ifdef _MSC_VER # define strcasecmp stricmp # define strncasecmp strnicmp #endif #define STATIC_INLINE static __inline #define VC_CONTAINER_PARAM_UNUSED(a) (void)(a) #if defined(__HIGHC__) && !defined(strcasecmp) # define strcasecmp(a,b) _stricmp(a,b) #endif #if defined(__GNUC__) && (__GNUC__ > 2) # define VC_CONTAINER_CONSTRUCTOR(func) void __attribute__((constructor,used)) func(void) # define VC_CONTAINER_DESTRUCTOR(func) void __attribute__((destructor,used)) func(void) #else # define VC_CONTAINER_CONSTRUCTOR(func) void func(void) # define VC_CONTAINER_DESTRUCTOR(func) void func(void) #endif #endif /* VC_CONTAINERS_COMMON_H */ userland/containers/core/containers_filters.c000066400000000000000000000170351421703157200220410ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include "containers/containers.h" #include "containers/core/containers_private.h" #include "containers/core/containers_filters.h" #if !defined(ENABLE_CONTAINERS_STANDALONE) #include "vcos_dlfcn.h" #define DL_SUFFIX VCOS_SO_EXT #ifndef DL_PATH_PREFIX #define DL_PATH_PREFIX "" #endif #endif typedef struct VC_CONTAINER_FILTER_PRIVATE_T { /** Pointer to the container filter code and symbols */ void *handle; } VC_CONTAINER_FILTER_PRIVATE_T; typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_FILTER_OPEN_FUNC_T)(VC_CONTAINER_FILTER_T*, VC_CONTAINER_FOURCC_T); static VC_CONTAINER_FILTER_OPEN_FUNC_T load_library(void **handle, VC_CONTAINER_FOURCC_T filter, const char *name); static void unload_library(void *handle); static struct { VC_CONTAINER_FOURCC_T filter; const char *name; } filter_to_name_table[] = { {VC_FOURCC('d','r','m',' '), "divx"}, {VC_FOURCC('d','r','m',' '), "hdcp2"}, {0, NULL} }; static VC_CONTAINER_STATUS_T vc_container_filter_load(VC_CONTAINER_FILTER_T *p_ctx, VC_CONTAINER_FOURCC_T filter, VC_CONTAINER_FOURCC_T type) { void *handle = NULL; VC_CONTAINER_FILTER_OPEN_FUNC_T func; VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; unsigned int i; for(i = 0; filter_to_name_table[i].filter; ++i) { if (filter_to_name_table[i].filter == filter) { if ((func = load_library(&handle, filter, filter_to_name_table[i].name)) != NULL) { status = (*func)(p_ctx, type); if(status == VC_CONTAINER_SUCCESS) break; unload_library(handle); if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) break; } } } p_ctx->priv->handle = handle; return status; } static void vc_container_filter_unload(VC_CONTAINER_FILTER_T *p_ctx) { unload_library(p_ctx->priv->handle); p_ctx->priv->handle = NULL; } /*****************************************************************************/ VC_CONTAINER_FILTER_T *vc_container_filter_open(VC_CONTAINER_FOURCC_T filter, VC_CONTAINER_FOURCC_T type, VC_CONTAINER_T *p_container, VC_CONTAINER_STATUS_T *p_status ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND; VC_CONTAINER_FILTER_T *p_ctx = 0; VC_CONTAINER_FILTER_PRIVATE_T *priv = 0; /* Allocate our context before trying out the different filter modules */ p_ctx = malloc(sizeof(*p_ctx) + sizeof(*priv)); if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*priv)); p_ctx->priv = priv = (VC_CONTAINER_FILTER_PRIVATE_T *)&p_ctx[1]; p_ctx->container = p_container; status = vc_container_filter_load(p_ctx, filter, type); if(status != VC_CONTAINER_SUCCESS) goto error; end: if(p_status) *p_status = status; return p_ctx; error: if(p_ctx) free(p_ctx); p_ctx = 0; goto end; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_filter_close( VC_CONTAINER_FILTER_T *p_ctx ) { if (p_ctx) { if(p_ctx->pf_close) p_ctx->pf_close(p_ctx); if(p_ctx->priv && p_ctx->priv->handle) vc_container_filter_unload(p_ctx); free(p_ctx); } return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_filter_process( VC_CONTAINER_FILTER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet ) { VC_CONTAINER_STATUS_T status; status = p_ctx->pf_process(p_ctx, p_packet); return status; } VC_CONTAINER_STATUS_T vc_container_filter_control(VC_CONTAINER_FILTER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, ... ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; va_list args; va_start( args, operation ); if(p_ctx->pf_control) status = p_ctx->pf_control(p_ctx, operation, args); va_end( args ); return status; } static VC_CONTAINER_FILTER_OPEN_FUNC_T load_library(void **handle, VC_CONTAINER_FOURCC_T filter, const char *name) { VC_CONTAINER_FILTER_OPEN_FUNC_T func = NULL; #ifdef ENABLE_CONTAINERS_STANDALONE VC_CONTAINER_PARAM_UNUSED(handle); VC_CONTAINER_PARAM_UNUSED(filter); VC_CONTAINER_PARAM_UNUSED(name); #else char *dl_name, *entrypt_name; const char *entrypt_name_short = "filter_open"; char filter_[6], *ptr; void *dl_handle; int dl_name_len; int entrypt_name_len; snprintf(filter_, sizeof(filter_), "%4.4s", (const char*)&filter); ptr = strchr(filter_, '\0'); while (ptr > filter_ && isspace(*--ptr)) *ptr = '\0'; strncat(filter_, "_", 1); dl_name_len = strlen(DL_PATH_PREFIX) + strlen(filter_) + strlen(name) + strlen(DL_SUFFIX) + 1; dl_name = malloc(dl_name_len); if (!dl_name) return NULL; entrypt_name_len = strlen(name) + 1 + strlen(filter_) + strlen(entrypt_name_short) + 1; entrypt_name = malloc(entrypt_name_len); if (!entrypt_name) { free(dl_name); return NULL; } snprintf(dl_name, dl_name_len, "%s%s%s%s", DL_PATH_PREFIX, filter_, name, DL_SUFFIX); snprintf(entrypt_name, entrypt_name_len, "%s_%s%s", name, filter_, entrypt_name_short); if ((dl_handle = vcos_dlopen(dl_name, VCOS_DL_NOW)) != NULL) { /* Try generic entrypoint name before the mangled, full name */ func = (VC_CONTAINER_FILTER_OPEN_FUNC_T)vcos_dlsym(dl_handle, entrypt_name_short); if (!func) func = (VC_CONTAINER_FILTER_OPEN_FUNC_T)vcos_dlsym(dl_handle, entrypt_name); /* Only return handle if symbol found */ if (func) *handle = dl_handle; else vcos_dlclose(dl_handle); } free(dl_name); free(entrypt_name); #endif return func; } static void unload_library(void *handle) { #ifdef ENABLE_CONTAINERS_STANDALONE VC_CONTAINER_PARAM_UNUSED(handle); #else vcos_dlclose(handle); #endif } userland/containers/core/containers_filters.h000066400000000000000000000123201421703157200220360ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_CONTAINERS_FILTERS_H #define VC_CONTAINERS_FILTERS_H /** \file containers_filters.h * Interface definition for the filter abstraction used by the container * common layer */ #include #include "containers/containers.h" /** \defgroup VcContainerFilterApi Container Filter API */ /* @{ */ /** Container Filter Context. * This structure defines the context for a container filter instance */ typedef struct VC_CONTAINER_FILTER_T { /** Pointer to container instance */ struct VC_CONTAINER_T *container; /** Pointer to information private to the container filter instance */ struct VC_CONTAINER_FILTER_PRIVATE_T *priv; /** Pointer to information private to the container filter module */ struct VC_CONTAINER_FILTER_MODULE_T *module; /** \note the following list of function pointers should not be used directly. * They defines the interface for implementing container filter modules and are * filled in by the container filter modules themselves. */ /** \private * Function pointer to close and free all resources allocated by a * container filter module */ VC_CONTAINER_STATUS_T (*pf_close)(struct VC_CONTAINER_FILTER_T *filter); /** \private * Function pointer to filter a data packet using a container filter module */ VC_CONTAINER_STATUS_T (*pf_process)(struct VC_CONTAINER_FILTER_T *filter, VC_CONTAINER_PACKET_T *p_packet); /** \private * Function pointer to control container filter module */ VC_CONTAINER_STATUS_T (*pf_control)( struct VC_CONTAINER_FILTER_T *filter, VC_CONTAINER_CONTROL_T operation, va_list args ); } VC_CONTAINER_FILTER_T; /** Opens a container filter using a four character code describing the filter. * This will create an instance of the container filter. * * \param filter Four Character Code describing the filter * \param type Four Character Code describing the subtype - indicated whether filter is encrypt or decrypt * \param container Pointer to the container instance * \param status Returns the status of the operation * \return If successful, this returns a pointer to the new instance * of the container filter. Returns NULL on failure. */ VC_CONTAINER_FILTER_T *vc_container_filter_open(VC_CONTAINER_FOURCC_T filter, VC_CONTAINER_FOURCC_T type, VC_CONTAINER_T *container, VC_CONTAINER_STATUS_T *status ); /** Closes an instance of a container filter. * \param context Pointer to the VC_CONTAINER_FILTER_T context of the instance to close * \return VC_CONTAINER_SUCCESS on success. */ VC_CONTAINER_STATUS_T vc_container_filter_close( VC_CONTAINER_FILTER_T *context ); /** Filter a data packet using a container filter module. * \param context Pointer to the VC_CONTAINER_FILTER_T instance to use * \param packet Pointer to the VC_CONTAINER_PACKET_T structure to process * \return the status of the operation */ VC_CONTAINER_STATUS_T vc_container_filter_process(VC_CONTAINER_FILTER_T *context, VC_CONTAINER_PACKET_T *p_packet); /** Extensible control function for container filter modules. * This function takes a variable number of arguments which will depend on the specific operation. * * \param context Pointer to the VC_CONTAINER_FILTER_T instance to use * \param operation The requested operation * \param args Arguments for the operation * \return the status of the operation */ VC_CONTAINER_STATUS_T vc_container_filter_control(VC_CONTAINER_FILTER_T *context, VC_CONTAINER_CONTROL_T operation, ... ); /* @} */ #endif /* VC_CONTAINERS_FILTERS_H */ userland/containers/core/containers_index.c000066400000000000000000000145621421703157200215020ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include "containers/containers.h" #include "containers/core/containers_index.h" typedef struct { int64_t file_offset; int64_t time; } VC_CONTAINER_INDEX_POS_T; struct VC_CONTAINER_INDEX_T { int len; // log2 of length of entry array int next; // next array entry to write into int gap; // log2 of the passes through entry array to build the full list int mgap; // len-gap, stored for convenience int count; // number of calls to index_add since last entry added int max_count; // log2 of the number of calls to discard between each entry added int64_t max_time; // time of the latest entry VC_CONTAINER_INDEX_POS_T entry[0]; // array of position/time pairs }; // We have a fixed length list, and when it is full we want to discard half the entries. // This is done without coping data by mapping the entry number to the index to the array, // in the following way: // Length is a power of two, so the entry number is a fixed constant bit width. The highest gap // bits are used as a direct offset into the array (o), the lowest mgap bits are right shifted by // gap to increment this (S). Each time we double the number of passes through the actual array. // So if len=3, we start off with mgap=3, gap=0, we have a single pass with the trivial mapping: // |S|S|S| [0 1 2 3 4 5 6 7] // when this is full we change to mgap=2, gap=1, so we iterate this way: // |o|S|S| [0 2 4 6] [1 3 5 7] // when this is full we change to mgap=1, gap=2 // |o|o|S| [0 4] [1 5] [2 6] [3 7] // when this is full we change to this, which is equivalent to where we started // |o|o|o| [0] [1] [2] [3] [4] [5] [6] [7] #define ENTRY(x, i) ((x)->gap == 0 ? (i) : ((i)>>(x)->mgap) + (((i) & ((1<<(x)->mgap)-1)) << (x)->gap)) VC_CONTAINER_STATUS_T vc_container_index_create( VC_CONTAINER_INDEX_T **index, int length ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; VC_CONTAINER_INDEX_T *id = NULL; int len = 0; if(length < 16) length = 16; if(length > 4096) length = 4096; while((length >>= 1) != 0) len++; id = malloc(sizeof(VC_CONTAINER_INDEX_T) + (sizeof(VC_CONTAINER_INDEX_POS_T)<len = id->mgap = len; *index = id; return VC_CONTAINER_SUCCESS; error: return status; } VC_CONTAINER_STATUS_T vc_container_index_free( VC_CONTAINER_INDEX_T *index ) { if(index == NULL) return VC_CONTAINER_ERROR_FAILED; free(index); return VC_CONTAINER_SUCCESS; } VC_CONTAINER_STATUS_T vc_container_index_add( VC_CONTAINER_INDEX_T *index, int64_t time, int64_t file_offset ) { if(index == NULL) return VC_CONTAINER_ERROR_FAILED; // reject entries if they are in part of the time covered if(index->next != 0 && time <= index->max_time) return VC_CONTAINER_SUCCESS; index->count++; if(index->count == (1<max_count)) { int entry; if(index->next == (1<len)) { // New entry doesn't fit, we discard every other index record // by changing how we map index positions to array entry indexes. index->next >>= 1; index->gap++; index->mgap--; index->max_count++; if(index->gap == index->len) { index->gap = 0; index->mgap = index->len; } } entry = ENTRY(index, index->next); index->entry[entry].file_offset = file_offset; index->entry[entry].time = time; index->count = 0; index->next++; index->max_time = time; } return VC_CONTAINER_SUCCESS; } VC_CONTAINER_STATUS_T vc_container_index_get( VC_CONTAINER_INDEX_T *index, int later, int64_t *time, int64_t *file_offset, int *past ) { int guess, start, end, entry; if(index == NULL || index->next == 0) return VC_CONTAINER_ERROR_FAILED; guess = start = 0; end = index->next-1; *past = *time > index->max_time; while(end-start > 1) { int64_t gtime; guess = (start+end)>>1; gtime = index->entry[ENTRY(index, guess)].time; if(*time < gtime) end = guess; else if(*time > gtime) start = guess; else break; } if (*time != index->entry[ENTRY(index, guess)].time) { if(later) { if(*time <= index->entry[ENTRY(index, start)].time) guess = start; else guess = end; } else { if(*time >= index->entry[ENTRY(index, end)].time) guess = end; else guess = start; } } entry = ENTRY(index, guess); *time = index->entry[entry].time; *file_offset = index->entry[entry].file_offset; return VC_CONTAINER_SUCCESS; } userland/containers/core/containers_index.h000066400000000000000000000072711421703157200215060ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_CONTAINERS_INDEX_H #define VC_CONTAINERS_INDEX_H /** \file containers_index.h * Definition of index utilitie for containers. Creates and maintains an * index of file offsets and times, and is able to suggest a file position * to seek to achieve a given time target. Useful for container formats * that don't include an index. */ #include "containers/containers.h" struct VC_CONTAINER_INDEX_T; typedef struct VC_CONTAINER_INDEX_T VC_CONTAINER_INDEX_T; /** * Creates an index with a suggested number of entries. * @param index Pointer to created index will be filled here on success. * @param length Suggested length of index. * @return Status code */ VC_CONTAINER_STATUS_T vc_container_index_create( VC_CONTAINER_INDEX_T **index, int length ); /** * Frees an index. * @param index Pointer to valid index. * @return Status code. */ VC_CONTAINER_STATUS_T vc_container_index_free( VC_CONTAINER_INDEX_T *index ); /** * Adds an entry to the index. If the index is full then some stored records will be * discarded. * @param index Pointer to a valid index. * @param time Timestamp of new index entry. * @param file_offset File offset for new index entry. * @return Status code */ VC_CONTAINER_STATUS_T vc_container_index_add( VC_CONTAINER_INDEX_T *index, int64_t time, int64_t file_offset ); /** * Retrieves the best entry for the supplied time offset. * @param index Pointer to valid index. * @param later If true, the selected entry is the earliest retained entry with a greater or equal timestamp. * If false, the selected entry is the latest retained entry with an earlier or equal timestamp. * @param time The requested time. On success, this is filled in with the time of the selected entry. * @param file_offset On success, this is filled in with the file offset of the selected entry. * @param past Set if the requested time is after the last entry in the index. * @return Status code. */ VC_CONTAINER_STATUS_T vc_container_index_get( VC_CONTAINER_INDEX_T *index, int later, int64_t *time, int64_t *file_offset, int *past ); #endif /* VC_CONTAINERS_WRITER_UTILS_H */ userland/containers/core/containers_io.c000066400000000000000000001074561421703157200210070ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include "containers/containers.h" #include "containers/core/containers_io.h" #include "containers/core/containers_common.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_uri.h" #define MAX_NUM_CACHED_AREAS 16 #define MAX_NUM_MEMORY_AREAS 4 #define NUM_TMP_MEMORY_AREAS 2 #define MEM_CACHE_READ_MAX_SIZE (32*1024) /* Needs to be a power of 2 */ #define MEM_CACHE_WRITE_MAX_SIZE (128*1024) /* Needs to be a power of 2 */ #define MEM_CACHE_TMP_MAX_SIZE (32*1024) /* Needs to be a power of 2 */ #define MEM_CACHE_ALIGNMENT (1*1024) /* Needs to be a power of 2 */ #define MEM_CACHE_AREA_READ_MAX_SIZE (4*1024*1024) /* Needs to be a power of 2 */ typedef struct VC_CONTAINER_IO_PRIVATE_CACHE_T { int64_t start; /**< Offset to the start of the cached area in the stream */ int64_t end; /**< Offset to the end of the cached area in the stream */ int64_t offset; /**< Offset of the currently cached data in the stream */ size_t size; /**< Size of the cached area */ bool dirty; /**< Whether the cache is dirty and needs to be written back */ size_t position; /**< Current position in the cache */ uint8_t *buffer; /**< Pointer to the start of the valid cache area */ uint8_t *buffer_end; /**< Pointer to the end of the cache */ unsigned int mem_max_size; /**< Maximum size of the memory cache */ unsigned int mem_size; /**< Size of the memory cache */ uint8_t *mem; /**< Pointer to the memory cache */ VC_CONTAINER_IO_T *io; } VC_CONTAINER_IO_PRIVATE_CACHE_T; typedef struct VC_CONTAINER_IO_PRIVATE_T { VC_CONTAINER_IO_PRIVATE_CACHE_T *cache; /**< Current cache */ unsigned int caches_num; VC_CONTAINER_IO_PRIVATE_CACHE_T caches; unsigned int cached_areas_num; VC_CONTAINER_IO_PRIVATE_CACHE_T cached_areas[MAX_NUM_CACHED_AREAS]; int64_t actual_offset; struct VC_CONTAINER_IO_ASYNC_T *async_io; } VC_CONTAINER_IO_PRIVATE_T; /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_io_file_open( VC_CONTAINER_IO_T *p_ctx, const char *uri, VC_CONTAINER_IO_MODE_T mode ); VC_CONTAINER_STATUS_T vc_container_io_null_open( VC_CONTAINER_IO_T *p_ctx, const char *uri, VC_CONTAINER_IO_MODE_T mode ); VC_CONTAINER_STATUS_T vc_container_io_net_open( VC_CONTAINER_IO_T *p_ctx, const char *uri, VC_CONTAINER_IO_MODE_T mode ); VC_CONTAINER_STATUS_T vc_container_io_pktfile_open( VC_CONTAINER_IO_T *p_ctx, const char *uri, VC_CONTAINER_IO_MODE_T mode ); VC_CONTAINER_STATUS_T vc_container_io_http_open( VC_CONTAINER_IO_T *p_ctx, const char *uri, VC_CONTAINER_IO_MODE_T mode ); static VC_CONTAINER_STATUS_T io_seek_not_seekable(VC_CONTAINER_IO_T *p_ctx, int64_t offset); static size_t vc_container_io_cache_read( VC_CONTAINER_IO_T *p_ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, uint8_t *data, size_t size ); static int32_t vc_container_io_cache_write( VC_CONTAINER_IO_T *p_ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, const uint8_t *data, size_t size ); static VC_CONTAINER_STATUS_T vc_container_io_cache_seek( VC_CONTAINER_IO_T *p_ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int64_t offset ); static size_t vc_container_io_cache_refill( VC_CONTAINER_IO_T *p_ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache ); static size_t vc_container_io_cache_flush( VC_CONTAINER_IO_T *p_ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete ); static struct VC_CONTAINER_IO_ASYNC_T *async_io_start( VC_CONTAINER_IO_T *io, int num_areas, VC_CONTAINER_STATUS_T * ); static VC_CONTAINER_STATUS_T async_io_stop( struct VC_CONTAINER_IO_ASYNC_T *ctx ); static int async_io_write( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache ); static VC_CONTAINER_STATUS_T async_io_wait_complete( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete ); static void async_io_stats_initialise( struct VC_CONTAINER_IO_ASYNC_T *ctx, int enable ); static void async_io_stats_get( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_WRITE_STATS_T *stats ); /*****************************************************************************/ static VC_CONTAINER_IO_T *vc_container_io_open_core( const char *uri, VC_CONTAINER_IO_MODE_T mode, VC_CONTAINER_IO_CAPABILITIES_T capabilities, bool b_open, VC_CONTAINER_STATUS_T *p_status ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_IO_T *p_ctx = 0; VC_CONTAINER_IO_PRIVATE_T *private = 0; unsigned int uri_length, caches = 0, cache_max_size, num_areas = MAX_NUM_MEMORY_AREAS; /* XXX */ uri_length = strlen(uri) + 1; /* Allocate our context before trying out the different io modules */ p_ctx = malloc( sizeof(*p_ctx) + sizeof(*private) + uri_length); if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*private) + uri_length ); p_ctx->priv = private = (VC_CONTAINER_IO_PRIVATE_T *)&p_ctx[1]; p_ctx->uri = (char *)&private[1]; memcpy((char *)p_ctx->uri, uri, uri_length); p_ctx->uri_parts = vc_uri_create(); if(!p_ctx->uri_parts) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } vc_uri_parse(p_ctx->uri_parts, uri); if (b_open) { /* Open the actual i/o module */ status = vc_container_io_null_open(p_ctx, uri, mode); if(status) status = vc_container_io_net_open(p_ctx, uri, mode); if(status) status = vc_container_io_pktfile_open(p_ctx, uri, mode); #ifdef ENABLE_CONTAINER_IO_HTTP if(status) status = vc_container_io_http_open(p_ctx, uri, mode); #endif if(status) status = vc_container_io_file_open(p_ctx, uri, mode); if(status != VC_CONTAINER_SUCCESS) goto error; if(!p_ctx->pf_seek || (p_ctx->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK)) { p_ctx->capabilities |= VC_CONTAINER_IO_CAPS_CANT_SEEK; p_ctx->pf_seek = io_seek_not_seekable; } } else { /* We're only creating an empty container i/o */ p_ctx->capabilities = capabilities; } if(p_ctx->capabilities & VC_CONTAINER_IO_CAPS_NO_CACHING) caches = 1; if(mode == VC_CONTAINER_IO_MODE_WRITE) cache_max_size = MEM_CACHE_WRITE_MAX_SIZE; else cache_max_size = MEM_CACHE_READ_MAX_SIZE; if(mode == VC_CONTAINER_IO_MODE_WRITE && vc_uri_path_extension(p_ctx->uri_parts) && !strcasecmp(vc_uri_path_extension(p_ctx->uri_parts), "tmp")) { caches = 1; cache_max_size = MEM_CACHE_TMP_MAX_SIZE; num_areas = NUM_TMP_MEMORY_AREAS; } /* Check if the I/O needs caching */ if(caches) { VC_CONTAINER_IO_PRIVATE_CACHE_T *cache = &p_ctx->priv->caches; cache->mem_max_size = cache_max_size; cache->mem_size = cache->mem_max_size; cache->io = p_ctx; cache->mem = malloc(p_ctx->priv->caches.mem_size); if(cache->mem) { cache->buffer = cache->mem; cache->buffer_end = cache->mem + cache->mem_size; p_ctx->priv->caches_num = 1; } } if(p_ctx->priv->caches_num) p_ctx->priv->cache = &p_ctx->priv->caches; /* Try to start an asynchronous io if we're in write mode and we've got at least 2 cache memory areas */ if(mode == VC_CONTAINER_IO_MODE_WRITE && p_ctx->priv->cache && num_areas >= 2) p_ctx->priv->async_io = async_io_start( p_ctx, num_areas, 0 ); end: if(p_status) *p_status = status; return p_ctx; error: if(p_ctx) vc_uri_release(p_ctx->uri_parts); if(p_ctx) free(p_ctx); p_ctx = 0; goto end; } /*****************************************************************************/ VC_CONTAINER_IO_T *vc_container_io_open( const char *uri, VC_CONTAINER_IO_MODE_T mode, VC_CONTAINER_STATUS_T *p_status ) { return vc_container_io_open_core( uri, mode, 0, true, p_status ); } /*****************************************************************************/ VC_CONTAINER_IO_T *vc_container_io_create( const char *uri, VC_CONTAINER_IO_MODE_T mode, VC_CONTAINER_IO_CAPABILITIES_T capabilities, VC_CONTAINER_STATUS_T *p_status ) { return vc_container_io_open_core( uri, mode, capabilities, false, p_status ); } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_io_close( VC_CONTAINER_IO_T *p_ctx ) { unsigned int i; if(p_ctx) { if(p_ctx->priv) { if(p_ctx->priv->caches_num) { if(p_ctx->priv->caches.dirty) vc_container_io_cache_flush( p_ctx, &p_ctx->priv->caches, 1 ); } if(p_ctx->priv->async_io) async_io_stop( p_ctx->priv->async_io ); else if(p_ctx->priv->caches_num) free(p_ctx->priv->caches.mem); for(i = 0; i < p_ctx->priv->cached_areas_num; i++) free(p_ctx->priv->cached_areas[i].mem); if(p_ctx->pf_close) p_ctx->pf_close(p_ctx); } vc_uri_release(p_ctx->uri_parts); free(p_ctx); } return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ size_t vc_container_io_peek(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) { size_t ret; if(p_ctx->priv->cache) { /* FIXME: do something a bit more clever than this */ int64_t offset = p_ctx->offset; ret = vc_container_io_read(p_ctx, buffer, size); vc_container_io_seek(p_ctx, offset); return ret; } if (p_ctx->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK) return 0; ret = p_ctx->pf_read(p_ctx, buffer, size); p_ctx->pf_seek(p_ctx, p_ctx->offset); return ret; } /*****************************************************************************/ size_t vc_container_io_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) { size_t ret; if(p_ctx->priv->cache) ret = vc_container_io_cache_read( p_ctx, p_ctx->priv->cache, (uint8_t*)buffer, size ); else { ret = p_ctx->pf_read(p_ctx, buffer, size); p_ctx->priv->actual_offset += ret; } p_ctx->offset += ret; return ret; } /*****************************************************************************/ size_t vc_container_io_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size) { int32_t ret; if(p_ctx->priv->cache) ret = vc_container_io_cache_write( p_ctx, p_ctx->priv->cache, (const uint8_t*)buffer, size ); else { ret = p_ctx->pf_write(p_ctx, buffer, size); p_ctx->priv->actual_offset += ret; } p_ctx->offset += ret; return ret < 0 ? 0 : ret; } /*****************************************************************************/ size_t vc_container_io_skip(VC_CONTAINER_IO_T *p_ctx, size_t size) { if(!size) return 0; if(size < 8) { uint8_t value[8]; return vc_container_io_read(p_ctx, value, size); } if(p_ctx->priv->cache) { if(vc_container_io_cache_seek(p_ctx, p_ctx->priv->cache, p_ctx->offset + size)) return 0; p_ctx->offset += size; return size; } if(vc_container_io_seek(p_ctx, p_ctx->offset + size)) return 0; return size; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_io_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset) { VC_CONTAINER_STATUS_T status; unsigned int i; /* Check if the requested position is in one of the cached areas */ for(i = 0; i < p_ctx->priv->cached_areas_num; i++) { VC_CONTAINER_IO_PRIVATE_CACHE_T *cache = &p_ctx->priv->cached_areas[i]; if(offset >= cache->start && offset < cache->end) { p_ctx->priv->cache = cache; break; } } if(i == p_ctx->priv->cached_areas_num) p_ctx->priv->cache = p_ctx->priv->caches_num ? &p_ctx->priv->caches : 0; if(p_ctx->priv->cache) { status = vc_container_io_cache_seek( p_ctx, p_ctx->priv->cache, offset ); if(status == VC_CONTAINER_SUCCESS) p_ctx->offset = offset; return status; } if(p_ctx->status == VC_CONTAINER_SUCCESS && offset == p_ctx->offset) return VC_CONTAINER_SUCCESS; status = p_ctx->pf_seek(p_ctx, offset); if(status == VC_CONTAINER_SUCCESS) p_ctx->offset = offset; p_ctx->priv->actual_offset = p_ctx->offset; return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T io_seek_not_seekable(VC_CONTAINER_IO_T *p_ctx, int64_t offset) { VC_CONTAINER_IO_PRIVATE_T *private = p_ctx->priv; vc_container_assert(offset >= private->actual_offset); if(offset == private->actual_offset) return VC_CONTAINER_SUCCESS; if(offset < private->actual_offset) { p_ctx->status = VC_CONTAINER_ERROR_EOS; return p_ctx->status; } offset -= private->actual_offset; while(offset && !p_ctx->status) { uint8_t value[64]; unsigned int ret, size = MIN(offset, 64); ret = p_ctx->pf_read(p_ctx, value, size); if(ret != size) p_ctx->status = VC_CONTAINER_ERROR_EOS; offset -= ret; } return p_ctx->status; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_io_control_list(VC_CONTAINER_IO_T *context, VC_CONTAINER_CONTROL_T operation, va_list args) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; if (context->pf_control) status = context->pf_control(context, operation, args); /* Option to add generic I/O control here */ if(operation == VC_CONTAINER_CONTROL_IO_FLUSH && context->priv->cache) { status = VC_CONTAINER_SUCCESS; (void)vc_container_io_cache_flush( context, context->priv->cache, 1 ); } if(operation == VC_CONTAINER_CONTROL_SET_IO_PERF_STATS && context->priv->async_io) { status = VC_CONTAINER_SUCCESS; async_io_stats_initialise(context->priv->async_io, va_arg(args, int)); } if(operation == VC_CONTAINER_CONTROL_GET_IO_PERF_STATS && context->priv->async_io) { status = VC_CONTAINER_SUCCESS; async_io_stats_get(context->priv->async_io, va_arg(args, VC_CONTAINER_WRITE_STATS_T *)); } return status; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_io_control(VC_CONTAINER_IO_T *context, VC_CONTAINER_CONTROL_T operation, ...) { VC_CONTAINER_STATUS_T result; va_list args; va_start(args, operation); result = vc_container_io_control_list(context, operation, args); va_end(args); return result; } /*****************************************************************************/ size_t vc_container_io_cache(VC_CONTAINER_IO_T *p_ctx, size_t size) { VC_CONTAINER_IO_PRIVATE_T *private = p_ctx->priv; VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, *main_cache; VC_CONTAINER_STATUS_T status; /* Sanity checking */ if(private->cached_areas_num >= MAX_NUM_CACHED_AREAS) return 0; cache = &private->cached_areas[private->cached_areas_num]; cache->start = p_ctx->offset; cache->end = cache->start + size; cache->offset = p_ctx->offset; cache->position = 0; cache->size = 0; cache->io = p_ctx; /* Set the size of the cache area depending on the capabilities of the i/o */ if(p_ctx->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK) cache->mem_max_size = MEM_CACHE_AREA_READ_MAX_SIZE; else if((p_ctx->capabilities & VC_CONTAINER_IO_CAPS_SEEK_SLOW) && size <= MEM_CACHE_AREA_READ_MAX_SIZE) cache->mem_max_size = MEM_CACHE_AREA_READ_MAX_SIZE; else cache->mem_max_size = MEM_CACHE_READ_MAX_SIZE; cache->mem_size = size; if(cache->mem_size > cache->mem_max_size) cache->mem_size = cache->mem_max_size; cache->mem = malloc(cache->mem_size); cache->buffer = cache->mem; cache->buffer_end = cache->mem + cache->mem_size; if(!cache->mem) return 0; private->cached_areas_num++; /* Copy any data we've got in the current cache into the new cache */ main_cache = p_ctx->priv->cache; if(main_cache && main_cache->position < main_cache->size) { cache->size = main_cache->size - main_cache->position; if(cache->size > cache->mem_size) cache->size = cache->mem_size; memcpy(cache->buffer, main_cache->buffer + main_cache->position, cache->size); main_cache->position += cache->size; } /* Read the rest of the cache directly from the stream */ if(cache->mem_size > cache->size) { size_t ret = cache->io->pf_read(cache->io, cache->buffer + cache->size, cache->mem_size - cache->size); cache->size += ret; cache->io->priv->actual_offset = cache->offset + cache->size; } status = vc_container_io_seek(p_ctx, cache->end); if(status != VC_CONTAINER_SUCCESS) return 0; if(p_ctx->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK) return cache->size; else return size; } /*****************************************************************************/ static size_t vc_container_io_cache_refill( VC_CONTAINER_IO_T *p_ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache ) { size_t ret = vc_container_io_cache_flush( p_ctx, cache, 1 ); if(ret) return 0; /* TODO what should we do there ? */ if(p_ctx->priv->actual_offset != cache->offset) { if(cache->io->pf_seek(cache->io, cache->offset) != VC_CONTAINER_SUCCESS) return 0; } ret = cache->io->pf_read(cache->io, cache->buffer, cache->buffer_end - cache->buffer); cache->size = ret; cache->position = 0; cache->io->priv->actual_offset = cache->offset + ret; return ret; } /*****************************************************************************/ static size_t vc_container_io_cache_refill_bypass( VC_CONTAINER_IO_T *p_ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, uint8_t *buffer, size_t size ) { size_t ret = vc_container_io_cache_flush( p_ctx, cache, 1 ); if(ret) return 0; /* TODO what should we do there ? */ if(p_ctx->priv->actual_offset != cache->offset) { if(cache->io->pf_seek(cache->io, cache->offset) != VC_CONTAINER_SUCCESS) return 0; } ret = cache->io->pf_read(cache->io, buffer, size); cache->size = cache->position = 0; cache->offset += ret; cache->io->priv->actual_offset = cache->offset; return ret; } /*****************************************************************************/ static size_t vc_container_io_cache_read( VC_CONTAINER_IO_T *p_ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, uint8_t *data, size_t size ) { size_t read = 0, bytes, ret; while(size) { bytes = cache->size - cache->position; /* Bytes left in cache */ #if 1 // FIXME Only if stream is seekable /* Try to read directly from the stream if the cache just gets in the way */ if(!bytes && size > cache->mem_size) { bytes = cache->mem_size; ret = vc_container_io_cache_refill_bypass( p_ctx, cache, data + read, bytes); read += ret; if(ret != bytes) /* We didn't read as many bytes as we had hoped */ goto end; size -= bytes; continue; } #endif /* Refill the cache if it is empty */ if(!bytes) bytes = vc_container_io_cache_refill( p_ctx, cache ); if(!bytes) goto end; /* We do have some data in the cache so override the status */ p_ctx->status = VC_CONTAINER_SUCCESS; /* Read data directly from the cache */ if(bytes > size) bytes = size; memcpy(data + read, cache->buffer + cache->position, bytes); cache->position += bytes; read += bytes; size -= bytes; } end: vc_container_assert(cache->offset + cache->position == p_ctx->offset + read); return read; } /*****************************************************************************/ static int32_t vc_container_io_cache_write( VC_CONTAINER_IO_T *p_ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, const uint8_t *data, size_t size ) { int32_t written = 0; size_t bytes, ret; /* If we do not have a write cache then we need to flush it */ if(cache->size && !cache->dirty) { ret = vc_container_io_cache_flush( p_ctx, cache, 1 ); if(ret) return -(int32_t)ret; } while(size) { bytes = (cache->buffer_end - cache->buffer) - cache->position; /* Space left in cache */ /* Flush the cache if it is full */ if(!bytes) { /* Cache full, flush it */ ret = vc_container_io_cache_flush( p_ctx, cache, 0 ); if(ret) { written -= ret; return written; } continue; } if(bytes > size) bytes = size; if(!p_ctx->priv->async_io && bytes == cache->mem_size) { /* Write directly from the buffer */ ret = cache->io->pf_write(cache->io, data + written, bytes); cache->offset += ret; cache->io->priv->actual_offset += ret; } else { /* Write in the cache */ memcpy(cache->buffer + cache->position, data + written, bytes); cache->position += bytes; cache->dirty = 1; ret = bytes; } written += ret; if(ret != bytes) goto end; size -= bytes; } end: vc_container_assert(cache->offset + (int64_t)cache->position == p_ctx->offset + written); if(cache->position > cache->size) cache->size = cache->position; return written; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T vc_container_io_cache_seek(VC_CONTAINER_IO_T *p_ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int64_t offset) { VC_CONTAINER_STATUS_T status; size_t shift, ret; /* Check if the seek position is within our cache */ if(offset >= cache->offset && offset < cache->offset + (int64_t)cache->size) { cache->position = offset - cache->offset; return VC_CONTAINER_SUCCESS; } shift = cache->buffer - cache->mem; if(!cache->dirty && shift && cache->size && offset >= cache->offset - (int64_t)shift && offset < cache->offset) { /* We need to refill the partial bit of the cache that we didn't take care of last time */ status = cache->io->pf_seek(cache->io, cache->offset - shift); if(status != VC_CONTAINER_SUCCESS) return status; cache->offset -= shift; cache->buffer -= shift; ret = cache->io->pf_read(cache->io, cache->buffer, shift); vc_container_assert(ret == shift); /* FIXME: ret must = shift */ cache->size += shift; cache->position = offset - cache->offset; cache->io->priv->actual_offset = cache->offset + ret; return VC_CONTAINER_SUCCESS; } if(cache->dirty) vc_container_io_cache_flush( p_ctx, cache, 1 ); // FIXME: what if all the data couldn't be flushed ? if(p_ctx->priv->async_io) async_io_wait_complete( p_ctx->priv->async_io, cache, 1 ); status = cache->io->pf_seek(cache->io, offset); if(status != VC_CONTAINER_SUCCESS) return status; vc_container_io_cache_flush( p_ctx, cache, 1 ); cache->offset = offset; cache->io->priv->actual_offset = offset; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static size_t vc_container_io_cache_flush( VC_CONTAINER_IO_T *p_ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete ) { size_t ret = 0, shift; if(cache->position > cache->size) cache->size = cache->position; if(cache->dirty && cache->size) { if(p_ctx->priv->actual_offset != cache->offset) { if(p_ctx->priv->async_io) async_io_wait_complete( p_ctx->priv->async_io, cache, complete ); if(cache->io->pf_seek(cache->io, cache->offset) != VC_CONTAINER_SUCCESS) return 0; } if(p_ctx->priv->async_io) { ret = async_io_write( p_ctx->priv->async_io, cache ); if(async_io_wait_complete( p_ctx->priv->async_io, cache, complete ) != VC_CONTAINER_SUCCESS) ret = 0; } else ret = cache->io->pf_write(cache->io, cache->buffer, cache->size); cache->io->priv->actual_offset = cache->offset + ret; ret = cache->position - ret; } cache->dirty = 0; cache->offset += cache->size; if(cache->mem_size == cache->mem_max_size) { shift = cache->offset &(MEM_CACHE_ALIGNMENT-1); cache->buffer = cache->mem + shift; } cache->position = cache->size = 0; return ret; } /***************************************************************************** * Asynchronous I/O. * This is here to keep the I/O as busy as possible by allowing the writer * to continue its work while the I/O is taking place in the background. *****************************************************************************/ #ifdef ENABLE_CONTAINERS_ASYNC_IO #include "vcos.h" #define NUMPC(c,n,s) ((c) < (1<<(s)) ? (n) : ((n) / (c >> (s)))) static void stats_initialise(VC_CONTAINER_STATS_T *st, uint32_t shift) { memset(st, 0, sizeof(VC_CONTAINER_STATS_T)); st->shift = shift; } static void stats_add_value(VC_CONTAINER_STATS_T *st, uint32_t count, uint32_t num) { uint32_t numpc; int i, j; if(count == 0) return; numpc = NUMPC(count, num, st->shift); // insert in the right place i=0; while(i < VC_CONTAINER_STATS_BINS && st->record[i].count != 0 && st->record[i].numpc > numpc) i++; if(st->record[i].count != 0 && st->record[i].numpc == numpc) { // equal numpc, can merge now st->record[i].count += count; st->record[i].num += num; } else { // shift higher records up for(j=VC_CONTAINER_STATS_BINS; j>i; j--) st->record[j] = st->record[j-1]; // write record in st->record[i].count = count; st->record[i].num = num; st->record[i].numpc = numpc; // if full, join the two closest records if(st->record[VC_CONTAINER_STATS_BINS].count) { uint32_t min_diff = 0; j = -1; // find closest, based on difference between numpc for(i=0; irecord[i].numpc - st->record[i+1].numpc; if(j == -1 || diff < min_diff) { j = i; min_diff = diff; } } // merge these records st->record[j].count += st->record[j+1].count; st->record[j].num += st->record[j+1].num; st->record[j].numpc = NUMPC(st->record[j].count, st->record[j].num, st->shift); // shift down higher records while(++j < VC_CONTAINER_STATS_BINS) st->record[j] = st->record[j+1]; // zero the free top record st->record[VC_CONTAINER_STATS_BINS].count = 0; st->record[VC_CONTAINER_STATS_BINS].num = 0; st->record[VC_CONTAINER_STATS_BINS].numpc = 0; } } } typedef struct VC_CONTAINER_IO_ASYNC_T { VC_CONTAINER_IO_T *io; VCOS_THREAD_T thread; VCOS_SEMAPHORE_T spare_sema; VCOS_SEMAPHORE_T queue_sema; VCOS_EVENT_T wake_event; int quit; unsigned int num_area; uint8_t *mem[MAX_NUM_MEMORY_AREAS]; /**< Base address of memory areas */ uint8_t *buffer[MAX_NUM_MEMORY_AREAS]; /**< When queued for writing, pointer to start of valid cache area */ size_t size[MAX_NUM_MEMORY_AREAS]; /**< When queued for writing, size of valid area to write */ unsigned int cur_area; unsigned char stack[3000]; int error; int stats_enable; VC_CONTAINER_WRITE_STATS_T stats; } VC_CONTAINER_IO_ASYNC_T; /*****************************************************************************/ static void async_io_stats_initialise( struct VC_CONTAINER_IO_ASYNC_T *ctx, int enable ) { ctx->stats_enable = enable; stats_initialise(&ctx->stats.write, 8); stats_initialise(&ctx->stats.wait, 0); stats_initialise(&ctx->stats.flush, 0); } static void async_io_stats_get( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_WRITE_STATS_T *stats ) { *stats = ctx->stats; } static void *async_io_thread(VOID *argv) { VC_CONTAINER_IO_ASYNC_T *ctx = argv; unsigned int write_area = 0; while (1) { unsigned long time = 0; vcos_event_wait(&ctx->wake_event); if(ctx->quit) break; while(vcos_semaphore_trywait(&ctx->queue_sema) == VCOS_SUCCESS) { uint8_t *buffer = ctx->buffer[write_area]; size_t size = ctx->size[write_area]; if(ctx->stats_enable) time = vcos_getmicrosecs(); if(ctx->io->pf_write(ctx->io, buffer, size) != size) ctx->error = 1; if(ctx->stats_enable) stats_add_value(&ctx->stats.write, size, vcos_getmicrosecs() - time); /* Signal that the write is done */ vcos_semaphore_post(&ctx->spare_sema); if(++write_area == ctx->num_area) write_area = 0; } } return NULL; } static int async_io_write( VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache ) { unsigned long time = 0; unsigned int offset; if(ctx->stats_enable) time = vcos_getmicrosecs(); /* post the current area */ ctx->buffer[ctx->cur_area] = cache->buffer; ctx->size[ctx->cur_area] = cache->size; vcos_semaphore_post(&ctx->queue_sema); vcos_event_signal(&ctx->wake_event); /* now we need to grab another area */ vcos_semaphore_wait(&ctx->spare_sema); if(++ctx->cur_area == ctx->num_area) ctx->cur_area = 0; if(ctx->stats_enable) stats_add_value(&ctx->stats.wait, 1, vcos_getmicrosecs() - time); /* alter cache mem to point to the new cur_area */ offset = cache->buffer - cache->mem; cache->mem = ctx->mem[ctx->cur_area]; cache->buffer = cache->mem + offset; cache->buffer_end = cache->mem + cache->mem_size; return ctx->error ? 0 : cache->size; } static VC_CONTAINER_STATUS_T async_io_wait_complete( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete ) { unsigned int time = 0; if(ctx->stats_enable) time = vcos_getmicrosecs(); if(complete) { int num; /* Need to make sure that all memory areas have been written out, so should have num-1 spare */ for(num=0; numnum_area-1; num++) vcos_semaphore_wait(&ctx->spare_sema); for(num=0; numnum_area-1; num++) vcos_semaphore_post(&ctx->spare_sema); } else { /* Need to make sure we can acquire one memory area */ vcos_semaphore_wait(&ctx->spare_sema); vcos_semaphore_post(&ctx->spare_sema); } if(ctx->stats_enable) stats_add_value(&ctx->stats.flush, 1, vcos_getmicrosecs() - time); return ctx->error ? VC_CONTAINER_ERROR_FAILED : VC_CONTAINER_SUCCESS; } static VC_CONTAINER_IO_ASYNC_T *async_io_start( VC_CONTAINER_IO_T *io, int num_areas, VC_CONTAINER_STATUS_T *status ) { VC_CONTAINER_IO_ASYNC_T *ctx = 0; VCOS_UNSIGNED pri = 0; /* Allocate our context */ ctx = malloc(sizeof(*ctx)); if(!ctx) goto error_spare_sema; memset(ctx, 0, sizeof(*ctx)); ctx->io = io; ctx->mem[0] = io->priv->cache->mem; for(ctx->num_area = 1; ctx->num_area < num_areas; ctx->num_area++) { ctx->mem[ctx->num_area] = malloc(io->priv->cache->mem_size); if(!ctx->mem[ctx->num_area]) break; } if(ctx->num_area == 1) // no real benefit in asynchronous writes goto error_spare_sema; async_io_stats_initialise(ctx, 0); if(vcos_semaphore_create(&ctx->spare_sema, "async_spare_sem", ctx->num_area-1) != VCOS_SUCCESS) goto error_spare_sema; if(vcos_semaphore_create(&ctx->queue_sema, "async_queue_sem", 0) != VCOS_SUCCESS) goto error_queue_sema; if (vcos_event_create(&ctx->wake_event, "async_wake_event") != VCOS_SUCCESS) goto error_event; // run this thread at a slightly higher priority than the calling thread - that means that // we prefer to write to the SD card rather than filling the memory buffer. pri = vcos_thread_get_priority(vcos_thread_current()); if(vcos_thread_create_classic(&ctx->thread, "async_io", async_io_thread, ctx, ctx->stack, sizeof(ctx->stack), pri-1, 10, VCOS_START) != VCOS_SUCCESS) goto error_thread; if(status) *status = VC_CONTAINER_SUCCESS; return ctx; error_thread: vcos_event_delete(&ctx->wake_event); error_event: vcos_semaphore_delete(&ctx->queue_sema); error_queue_sema: vcos_semaphore_delete(&ctx->spare_sema); error_spare_sema: if(ctx) free(ctx); if(status) *status = VC_CONTAINER_ERROR_FAILED; return 0; } static VC_CONTAINER_STATUS_T async_io_stop( VC_CONTAINER_IO_ASYNC_T *ctx ) { /* Block if a write operation is already in progress */ //vcos_semaphore_wait(&ctx->sema); // XXX block until all done ctx->quit = 1; vcos_event_signal(&ctx->wake_event); vcos_thread_join(&ctx->thread,NULL); vcos_event_delete(&ctx->wake_event); vcos_semaphore_delete(&ctx->queue_sema); vcos_semaphore_delete(&ctx->spare_sema); while(ctx->num_area > 0) free(ctx->mem[--ctx->num_area]); free(ctx); return VC_CONTAINER_SUCCESS; } #else static struct VC_CONTAINER_IO_ASYNC_T *async_io_start( VC_CONTAINER_IO_T *io, int num_areas, VC_CONTAINER_STATUS_T *status ) { VC_CONTAINER_PARAM_UNUSED(io); VC_CONTAINER_PARAM_UNUSED(num_areas); if(status) *status = VC_CONTAINER_ERROR_FAILED; return 0; } static int async_io_write( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache ) { VC_CONTAINER_PARAM_UNUSED(ctx); VC_CONTAINER_PARAM_UNUSED(cache); return 0; } static VC_CONTAINER_STATUS_T async_io_wait_complete( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete ) { VC_CONTAINER_PARAM_UNUSED(ctx); VC_CONTAINER_PARAM_UNUSED(cache); VC_CONTAINER_PARAM_UNUSED(complete); return 0; } static VC_CONTAINER_STATUS_T async_io_stop( struct VC_CONTAINER_IO_ASYNC_T *ctx ) { VC_CONTAINER_PARAM_UNUSED(ctx); return VC_CONTAINER_SUCCESS; } static void async_io_stats_initialise( struct VC_CONTAINER_IO_ASYNC_T *ctx, int enable ) { VC_CONTAINER_PARAM_UNUSED(ctx); VC_CONTAINER_PARAM_UNUSED(enable); } static void async_io_stats_get( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_WRITE_STATS_T *stats ) { VC_CONTAINER_PARAM_UNUSED(ctx); VC_CONTAINER_PARAM_UNUSED(stats); } #endif userland/containers/core/containers_io.h000066400000000000000000000235321421703157200210040ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_CONTAINERS_IO_H #define VC_CONTAINERS_IO_H /** \file containers_io.h * Interface definition for the input / output abstraction layer used by container * readers and writers */ #include "containers/containers.h" #ifdef __cplusplus extern "C" { #endif /** \defgroup VcContainerIoApi Container I/O API */ /* @{ */ /** Container io opening mode. * This is used to specify whether a reader or writer is requested. */ typedef enum { VC_CONTAINER_IO_MODE_READ = 0, /**< Container io opened in reading mode */ VC_CONTAINER_IO_MODE_WRITE = 1 /**< Container io opened in writing mode */ } VC_CONTAINER_IO_MODE_T; /** \name Container I/O Capabilities * The following flags are exported by container i/o modules to describe their capabilities */ /* @{ */ /** Type definition for container I/O capabilities */ typedef uint32_t VC_CONTAINER_IO_CAPABILITIES_T; /** Seeking is not supported */ #define VC_CONTAINER_IO_CAPS_CANT_SEEK 0x1 /** Seeking is slow and should be avoided */ #define VC_CONTAINER_IO_CAPS_SEEK_SLOW 0x2 /** The I/O doesn't provide any caching of the data */ #define VC_CONTAINER_IO_CAPS_NO_CACHING 0x4 /* @} */ /** Container Input / Output Context. * This structure defines the context for a container io instance */ struct VC_CONTAINER_IO_T { /** Pointer to information private to the container io instance */ struct VC_CONTAINER_IO_PRIVATE_T *priv; /** Pointer to information private to the container io module */ struct VC_CONTAINER_IO_MODULE_T *module; /** Uniform Resource Identifier for the stream to open. * This is a string encoded in UTF-8 which follows the syntax defined in * RFC2396 (http://tools.ietf.org/html/rfc2396). */ char *uri; /** Pre-parsed URI */ struct VC_URI_PARTS_T *uri_parts; /** Current offset into the i/o stream */ int64_t offset; /** Current size of the i/o stream (0 if unknown). This size might grow during the * lifetime of the i/o instance (for instance when doing progressive download). */ int64_t size; /** Capabilities of the i/o stream */ VC_CONTAINER_IO_CAPABILITIES_T capabilities; /** Status of the i/o stream */ VC_CONTAINER_STATUS_T status; /** Maximum size allowed for this i/o stream (0 if unknown). This is used during writing * to limit the size of the stream to below this value. */ int64_t max_size; /** \note the following list of function pointers should not be used directly. * They defines the interface for implementing container io modules and are filled in * by the container modules themselves. */ /** \private * Function pointer to close and free all resources allocated by a * container io module */ VC_CONTAINER_STATUS_T (*pf_close)(struct VC_CONTAINER_IO_T *io); /** \private * Function pointer to read or skip data from container io module */ size_t (*pf_read)(struct VC_CONTAINER_IO_T *io, void *buffer, size_t size); /** \private * Function pointer to write data to a container io module */ size_t (*pf_write)(struct VC_CONTAINER_IO_T *io, const void *buffer, size_t size); /** \private * Function pointer to seek into a container io module */ VC_CONTAINER_STATUS_T (*pf_seek)(struct VC_CONTAINER_IO_T *io, int64_t offset); /** \private * Function pointer to perform a control operation on a container io module */ VC_CONTAINER_STATUS_T (*pf_control)(struct VC_CONTAINER_IO_T *io, VC_CONTAINER_CONTROL_T operation, va_list args); }; /** Opens an i/o stream pointed to by a URI. * This will create an instance of the container i/o module. * * \param uri Uniform Resource Identifier pointing to the multimedia container * \param mode Mode in which the i/o stream will be opened * \param status Returns the status of the operation * \return If successful, this returns a pointer to the new instance * of the i/o module. Returns NULL on failure. */ VC_CONTAINER_IO_T *vc_container_io_open( const char *uri, VC_CONTAINER_IO_MODE_T mode, VC_CONTAINER_STATUS_T *status ); /** Creates an empty i/o stream. The i/o function pointers will have to be set * by the caller before the i/o gets used. * This will create an instance of the container i/o module. * * \param uri Uniform Resource Identifier pointing to the multimedia container * \param mode Mode in which the i/o stream will be opened * \param capabilities Flags indicating the capabilities of the i/o * \param status Returns the status of the operation * \return If successful, this returns a pointer to the new instance * of the i/o module. Returns NULL on failure. */ VC_CONTAINER_IO_T *vc_container_io_create( const char *uri, VC_CONTAINER_IO_MODE_T mode, VC_CONTAINER_IO_CAPABILITIES_T capabilities, VC_CONTAINER_STATUS_T *p_status ); /** Closes an instance of a container i/o module. * \param context Pointer to the VC_CONTAINER_IO_T context of the instance to close * \return VC_CONTAINER_SUCCESS on success. */ VC_CONTAINER_STATUS_T vc_container_io_close( VC_CONTAINER_IO_T *context ); /** Read data from an i/o stream without advancing the read position within the stream. * \param context Pointer to the VC_CONTAINER_IO_T instance to use * \param buffer Pointer to the buffer where the data will be read * \param size Number of bytes to read * \return The size of the data actually read. */ size_t vc_container_io_peek(VC_CONTAINER_IO_T *context, void *buffer, size_t size); /** Read data from an i/o stream. * \param context Pointer to the VC_CONTAINER_IO_T instance to use * \param buffer Pointer to the buffer where the data will be read * \param size Number of bytes to read * \return The size of the data actually read. */ size_t vc_container_io_read(VC_CONTAINER_IO_T *context, void *buffer, size_t size); /** Skip data in an i/o stream without reading it. * \param context Pointer to the VC_CONTAINER_IO_T instance to use * \param size Number of bytes to skip * \return The size of the data actually skipped. */ size_t vc_container_io_skip(VC_CONTAINER_IO_T *context, size_t size); /** Write data to an i/o stream. * \param context Pointer to the VC_CONTAINER_IO_T instance to use * \param buffer Pointer to the buffer containing the data to write * \param size Number of bytes to write * \return The size of the data actually written. */ size_t vc_container_io_write(VC_CONTAINER_IO_T *context, const void *buffer, size_t size); /** Seek into an i/o stream. * \param context Pointer to the VC_CONTAINER_IO_T instance to use * \param offset Absolute file offset to seek to * \return Status of the operation */ VC_CONTAINER_STATUS_T vc_container_io_seek(VC_CONTAINER_IO_T *context, int64_t offset); /** Perform control operation on an i/o stream (va_list). * \param context Pointer to the VC_CONTAINER_IO_T instance to use * \param operation Control operation to be performed * \param args Additional arguments for the operation * \return Status of the operation */ VC_CONTAINER_STATUS_T vc_container_io_control_list(VC_CONTAINER_IO_T *context, VC_CONTAINER_CONTROL_T operation, va_list args); /** Perform control operation on an i/o stream (varargs). * \param context Pointer to the VC_CONTAINER_IO_T instance to use * \param operation Control operation to be performed * \param ... Additional arguments for the operation * \return Status of the operation */ VC_CONTAINER_STATUS_T vc_container_io_control(VC_CONTAINER_IO_T *context, VC_CONTAINER_CONTROL_T operation, ...); /** Cache the pointed region of the i/o stream (from current position). * This will allow future seeking into the specified region even on non-seekable streams. * \param context Pointer to the VC_CONTAINER_IO_T instance to use * \param size Size of the region to cache * \return Status of the operation */ size_t vc_container_io_cache(VC_CONTAINER_IO_T *context, size_t size); /* @} */ #ifdef __cplusplus } #endif #endif /* VC_CONTAINERS_HELPERS_H */ userland/containers/core/containers_io_helpers.c000066400000000000000000000216121421703157200225160ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_logging.h" void vc_container_helper_format_debug(VC_CONTAINER_T *ctx, int indent, const char *format, ...) { char debug_string[512]; va_list args; int result; if(indent >= (int)sizeof(debug_string)) return; memset(debug_string, ' ', indent); va_start( args, format ); result = vsnprintf(debug_string + indent, sizeof(debug_string) - indent, format, args); va_end( args ); if(result <= 0) return; vc_container_log(ctx, VC_CONTAINER_LOG_FORMAT, debug_string); fflush(0); } uint64_t vc_container_helper_int_debug(VC_CONTAINER_T *ctx, int type, uint64_t value, const char *name, int indent) { VC_CONTAINER_PARAM_UNUSED(ctx); if(type == LOG_FORMAT_TYPE_HEX) vc_container_helper_format_debug(ctx, indent, "%s: 0x%"PRIx64, name, value); else vc_container_helper_format_debug(ctx, indent, "%s: %"PRIi64, name, value); return value; } uint64_t vc_container_helper_read_debug(VC_CONTAINER_T *ctx, int type, int size, const char *name, uint8_t *buffer, int indent, int b_skip) { int64_t offset = STREAM_POSITION(ctx); uint64_t value = 0; GUID_T guid; if(type == LOG_FORMAT_TYPE_STRING || type == LOG_FORMAT_TYPE_STRING_UTF16_LE || type == LOG_FORMAT_TYPE_STRING_UTF16_BE) { uint8_t stringbuf[256]; char utf8buf[256]; int stringsize = sizeof(stringbuf) - 2; if(!buffer) { buffer = stringbuf; if(size < stringsize) stringsize = size; } else stringsize = size; value = vc_container_io_read(ctx->priv->io, buffer, stringsize); if(!utf8_from_charset(type == LOG_FORMAT_TYPE_STRING ? "UTF8" : "UTF16-LE", utf8buf, sizeof(utf8buf), buffer, stringsize)) vc_container_helper_format_debug(ctx, indent, "%s: \"%s\"", name, utf8buf); else vc_container_helper_format_debug(ctx, indent, "%s: (could not read)", name); if(size - stringsize) value += vc_container_io_skip(ctx->priv->io, size - stringsize); return value; } if(type == LOG_FORMAT_TYPE_UINT_LE) { switch(size) { case 1: value = vc_container_io_read_uint8(ctx->priv->io); break; case 2: value = vc_container_io_read_le_uint16(ctx->priv->io); break; case 3: value = vc_container_io_read_le_uint24(ctx->priv->io); break; case 4: value = vc_container_io_read_le_uint32(ctx->priv->io); break; case 5: value = vc_container_io_read_le_uint40(ctx->priv->io); break; case 6: value = vc_container_io_read_le_uint48(ctx->priv->io); break; case 7: value = vc_container_io_read_le_uint56(ctx->priv->io); break; case 8: value = vc_container_io_read_le_uint64(ctx->priv->io); break; } } else if(type == LOG_FORMAT_TYPE_UINT_BE) { switch(size) { case 1: value = vc_container_io_read_uint8(ctx->priv->io); break; case 2: value = vc_container_io_read_be_uint16(ctx->priv->io); break; case 3: value = vc_container_io_read_be_uint24(ctx->priv->io); break; case 4: value = vc_container_io_read_be_uint32(ctx->priv->io); break; case 5: value = vc_container_io_read_be_uint40(ctx->priv->io); break; case 6: value = vc_container_io_read_be_uint48(ctx->priv->io); break; case 7: value = vc_container_io_read_be_uint56(ctx->priv->io); break; case 8: value = vc_container_io_read_be_uint64(ctx->priv->io); break; } } else if(type == LOG_FORMAT_TYPE_FOURCC) { value = vc_container_io_read_fourcc(ctx->priv->io); } else if(type == LOG_FORMAT_TYPE_GUID) { value = vc_container_io_read(ctx->priv->io, &guid, 16); } else { vc_container_assert(0); return 0; } if(type == LOG_FORMAT_TYPE_GUID) { if(value == 16) { vc_container_helper_format_debug(ctx, indent, "%s: 0x%x-0x%x-0x%x-0x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x", name, guid.word0, guid.short0, guid.short1, guid.bytes[0], guid.bytes[1], guid.bytes[2], guid.bytes[3], guid.bytes[4], guid.bytes[5], guid.bytes[6], guid.bytes[7]); if(buffer) memcpy(buffer, &guid, sizeof(guid)); } } else if(type == LOG_FORMAT_TYPE_FOURCC) { uint32_t val = value; vc_container_helper_format_debug(ctx, indent, "%s: %4.4s", name, (char *)&val); } else { vc_container_helper_format_debug(ctx, indent, "%s: %"PRIi64, name, value); } if(b_skip) value = (STREAM_POSITION(ctx) - offset) != size; return value; } VC_CONTAINER_STATUS_T vc_container_helper_write_debug(VC_CONTAINER_T *ctx, int type, int size, const char *name, uint64_t value, const uint8_t *buffer, int indent, int silent) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; if(type == LOG_FORMAT_TYPE_STRING) { value = vc_container_io_write(ctx->priv->io, buffer, size); if(!silent) vc_container_helper_format_debug(ctx, indent, "%s: \"%ls\"", name, buffer); return value == (uint64_t)size ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; } if(type == LOG_FORMAT_TYPE_UINT_LE) { switch(size) { case 1: status = vc_container_io_write_uint8(ctx->priv->io, (uint8_t)value); break; case 2: status = vc_container_io_write_le_uint16(ctx->priv->io, (uint16_t)value); break; case 3: status = vc_container_io_write_le_uint24(ctx->priv->io, (uint32_t)value); break; case 4: status = vc_container_io_write_le_uint32(ctx->priv->io, (uint32_t)value); break; case 8: status = vc_container_io_write_le_uint64(ctx->priv->io, value); break; } } else if(type == LOG_FORMAT_TYPE_UINT_BE) { switch(size) { case 1: status = vc_container_io_write_uint8(ctx->priv->io, (uint8_t)value); break; case 2: status = vc_container_io_write_be_uint16(ctx->priv->io, (uint16_t)value); break; case 3: status = vc_container_io_write_be_uint24(ctx->priv->io, (uint32_t)value); break; case 4: status = vc_container_io_write_be_uint32(ctx->priv->io, (uint32_t)value); break; case 8: status = vc_container_io_write_be_uint64(ctx->priv->io, value); break; } } else if(type == LOG_FORMAT_TYPE_FOURCC) { status = vc_container_io_write_fourcc(ctx->priv->io, (uint32_t)value); } else if(type == LOG_FORMAT_TYPE_GUID) { value = vc_container_io_write(ctx->priv->io, buffer, 16); } else { vc_container_assert(0); return 0; } if(status) { vc_container_helper_format_debug(ctx, indent, "write failed for %s", name); return status; } if(!silent) { if (type == LOG_FORMAT_TYPE_FOURCC) { vc_container_helper_format_debug(ctx, indent, "%s: %4.4s", name, (char *)&value); } else if(type == LOG_FORMAT_TYPE_GUID) { GUID_T guid; memcpy(&guid, buffer, sizeof(guid)); vc_container_helper_format_debug(ctx, indent, "%s: 0x%x-0x%x-0x%x-0x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x", name, guid.word0, guid.short0, guid.short1, guid.bytes[0], guid.bytes[1], guid.bytes[2], guid.bytes[3], guid.bytes[4], guid.bytes[5], guid.bytes[6], guid.bytes[7]); } else { vc_container_helper_format_debug(ctx, indent, "%s: %"PRIi64, name, value); } } return status; } userland/containers/core/containers_io_helpers.h000066400000000000000000001034321421703157200225240ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_CONTAINERS_IO_HELPERS_H #define VC_CONTAINERS_IO_HELPERS_H /** \file containers_io_helpers.h * Helper functions and macros which provide functionality which is often used by containers */ #include "containers/core/containers_io.h" #include "containers/core/containers_utils.h" /***************************************************************************** * Helper inline functions to read integers from an i/o stream *****************************************************************************/ /** Reads an unsigned 8 bits integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint8_t vc_container_io_read_uint8(VC_CONTAINER_IO_T *io) { uint8_t value; size_t ret = vc_container_io_read(io, &value, 1); return ret == 1 ? value : 0; } /** Reads a FOURCC from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The FOURCC to read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE VC_CONTAINER_FOURCC_T vc_container_io_read_fourcc(VC_CONTAINER_IO_T *io) { VC_CONTAINER_FOURCC_T value; size_t ret = vc_container_io_read(io, (int8_t *)&value, 4); return ret == 4 ? value : 0; } /** Reads an unsigned 16 bits big endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint16_t vc_container_io_read_be_uint16(VC_CONTAINER_IO_T *io) { uint8_t value[2]; size_t ret = vc_container_io_read(io, value, 2); return ret == 2 ? (value[0] << 8) | value[1] : 0; } /** Reads an unsigned 24 bits big endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint32_t vc_container_io_read_be_uint24(VC_CONTAINER_IO_T *io) { uint8_t value[3]; size_t ret = vc_container_io_read(io, value, 3); return ret == 3 ? (value[0] << 16) | (value[1] << 8) | value[2] : 0; } /** Reads an unsigned 32 bits big endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint32_t vc_container_io_read_be_uint32(VC_CONTAINER_IO_T *io) { uint8_t value[4]; size_t ret = vc_container_io_read(io, value, 4); return ret == 4 ? (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3] : 0; } /** Reads an unsigned 40 bits big endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint64_t vc_container_io_read_be_uint40(VC_CONTAINER_IO_T *io) { uint8_t value[5]; uint32_t value1, value2; size_t ret = vc_container_io_read(io, value, 5); value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]; value2 = value[4]; return ret == 5 ? (((uint64_t)value1) << 8)|value2 : 0; } /** Reads an unsigned 48 bits big endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint64_t vc_container_io_read_be_uint48(VC_CONTAINER_IO_T *io) { uint8_t value[6]; uint32_t value1, value2; size_t ret = vc_container_io_read(io, value, 6); value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]; value2 = (value[4] << 8) | value[5]; return ret == 6 ? (((uint64_t)value1) << 16)|value2 : 0; } /** Reads an unsigned 56 bits big endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint64_t vc_container_io_read_be_uint56(VC_CONTAINER_IO_T *io) { uint8_t value[7]; uint32_t value1, value2; size_t ret = vc_container_io_read(io, value, 7); value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]; value2 = (value[4] << 16) | (value[5] << 8) | value[6]; return ret == 7 ? (((uint64_t)value1) << 24)|value2 : 0; } /** Reads an unsigned 64 bits big endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint64_t vc_container_io_read_be_uint64(VC_CONTAINER_IO_T *io) { uint8_t value[8]; uint32_t value1, value2; size_t ret = vc_container_io_read(io, value, 8); value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]; value2 = (value[4] << 24) | (value[5] << 16) | (value[6] << 8) | value[7]; return ret == 8 ? (((uint64_t)value1) << 32)|value2 : 0; } /** Reads an unsigned 16 bits little endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint16_t vc_container_io_read_le_uint16(VC_CONTAINER_IO_T *io) { uint8_t value[2]; size_t ret = vc_container_io_read(io, value, 2); return ret == 2 ? (value[1] << 8) | value[0] : 0; } /** Reads an unsigned 24 bits little endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint32_t vc_container_io_read_le_uint24(VC_CONTAINER_IO_T *io) { uint8_t value[3]; size_t ret = vc_container_io_read(io, value, 3); return ret == 3 ? (value[2] << 16) | (value[1] << 8) | value[0] : 0; } /** Reads an unsigned 32 bits little endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint32_t vc_container_io_read_le_uint32(VC_CONTAINER_IO_T *io) { uint8_t value[4]; size_t ret = vc_container_io_read(io, value, 4); return ret == 4 ? (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0] : 0; } /** Reads an unsigned 40 bits little endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint64_t vc_container_io_read_le_uint40(VC_CONTAINER_IO_T *io) { uint8_t value[5]; uint32_t value1, value2; size_t ret = vc_container_io_read(io, value, 5); value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0]; value2 = value[4]; return ret == 5 ? (((uint64_t)value2) << 32)|value1 : 0; } /** Reads an unsigned 48 bits little endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint64_t vc_container_io_read_le_uint48(VC_CONTAINER_IO_T *io) { uint8_t value[6]; uint32_t value1, value2; size_t ret = vc_container_io_read(io, value, 6); value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0]; value2 = (value[5] << 8) | value[4]; return ret == 6 ? (((uint64_t)value2) << 32)|value1 : 0; } /** Reads an unsigned 56 bits little endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint64_t vc_container_io_read_le_uint56(VC_CONTAINER_IO_T *io) { uint8_t value[7]; uint32_t value1, value2; size_t ret = vc_container_io_read(io, value, 7); value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0]; value2 = (value[6] << 16) | (value[5] << 8) | value[4]; return ret == 7 ? (((uint64_t)value2) << 32)|value1 : 0; } /** Reads an unsigned 64 bits little endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint64_t vc_container_io_read_le_uint64(VC_CONTAINER_IO_T *io) { uint8_t value[8]; uint32_t value1, value2; size_t ret = vc_container_io_read(io, value, 8); value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0]; value2 = (value[7] << 24) | (value[6] << 16) | (value[5] << 8) | value[4]; return ret == 8 ? (((uint64_t)value2) << 32)|value1 : 0; } /***************************************************************************** * Helper inline functions to peek integers from an i/o stream *****************************************************************************/ /** Peeks an unsigned 8 bits integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint8_t vc_container_io_peek_uint8(VC_CONTAINER_IO_T *io) { uint8_t value; size_t ret = vc_container_io_peek(io, &value, 1); return ret == 1 ? value : 0; } /** Peeks an unsigned 16 bits big endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint16_t vc_container_io_peek_be_uint16(VC_CONTAINER_IO_T *io) { uint8_t value[2]; size_t ret = vc_container_io_peek(io, value, 2); return ret == 2 ? (value[0] << 8) | value[1] : 0; } /** Peeks an unsigned 24 bits big endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint32_t vc_container_io_peek_be_uint24(VC_CONTAINER_IO_T *io) { uint8_t value[3]; size_t ret = vc_container_io_peek(io, value, 3); return ret == 3 ? (value[0] << 16) | (value[1] << 8) | value[2] : 0; } /** Peeks an unsigned 32 bits big endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint32_t vc_container_io_peek_be_uint32(VC_CONTAINER_IO_T *io) { uint8_t value[4]; size_t ret = vc_container_io_peek(io, value, 4); return ret == 4 ? (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3] : 0; } /** Peeks an unsigned 64 bits big endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint64_t vc_container_io_peek_be_uint64(VC_CONTAINER_IO_T *io) { uint8_t value[8]; uint32_t value1, value2; size_t ret = vc_container_io_peek(io, value, 8); value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]; value2 = (value[4] << 24) | (value[5] << 16) | (value[6] << 8) | value[7]; return ret == 8 ? (((uint64_t)value1) << 32)|value2 : 0; } /** Peeks an unsigned 16 bits little endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint16_t vc_container_io_peek_le_uint16(VC_CONTAINER_IO_T *io) { uint8_t value[2]; size_t ret = vc_container_io_peek(io, value, 2); return ret == 2 ? (value[1] << 8) | value[0] : 0; } /** Peeks an unsigned 24 bits little endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint32_t vc_container_io_peek_le_uint24(VC_CONTAINER_IO_T *io) { uint8_t value[3]; size_t ret = vc_container_io_peek(io, value, 3); return ret == 3 ? (value[2] << 16) | (value[1] << 8) | value[0] : 0; } /** Peeks an unsigned 32 bits little endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint32_t vc_container_io_peek_le_uint32(VC_CONTAINER_IO_T *io) { uint8_t value[4]; size_t ret = vc_container_io_peek(io, value, 4); return ret == 4 ? (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0] : 0; } /** Peeks an unsigned 64 bits little endian integer from an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \return The integer read. In case of failure during the read, * this will return a value of 0. */ STATIC_INLINE uint64_t vc_container_io_peek_le_uint64(VC_CONTAINER_IO_T *io) { uint8_t value[8]; uint32_t value1, value2; size_t ret = vc_container_io_peek(io, value, 8); value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0]; value2 = (value[7] << 24) | (value[6] << 16) | (value[5] << 8) | value[4]; return ret == 8 ? (((uint64_t)value2) << 32)|value1 : 0; } /***************************************************************************** * Helper inline functions to write integers to an i/o stream *****************************************************************************/ /** Writes an unsigned 8 bits integer to an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \param value The integer to write. * \return The status of the operation. */ STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_uint8(VC_CONTAINER_IO_T *io, uint8_t value) { size_t ret = vc_container_io_write(io, &value, 1); return ret == 1 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; } /** Writes a FOURCC to an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \param value The FOURCC to write. * \return The status of the operation. */ STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_fourcc(VC_CONTAINER_IO_T *io, VC_CONTAINER_FOURCC_T value) { size_t ret = vc_container_io_write(io, (uint8_t *)&value, 4); return ret == 4 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; } /** Writes an unsigned 16 bits big endian integer to an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \param value The integer to write. * \return The status of the operation. */ STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_be_uint16(VC_CONTAINER_IO_T *io, uint16_t value) { uint8_t bytes[2] = {(uint8_t)(value >> 8), (uint8_t)value}; size_t ret = vc_container_io_write(io, bytes, 2); return ret == 2 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; } /** Writes an unsigned 24 bits big endian integer to an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \param value The integer to write. * \return The status of the operation. */ STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_be_uint24(VC_CONTAINER_IO_T *io, uint32_t value) { uint8_t bytes[3] = {(uint8_t)(value >> 16), (uint8_t)(value >> 8), (uint8_t)value}; size_t ret = vc_container_io_write(io, bytes, 3); return ret == 3 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; } /** Writes an unsigned 32 bits big endian integer to an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \param value The integer to write. * \return The status of the operation. */ STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_be_uint32(VC_CONTAINER_IO_T *io, uint32_t value) { uint8_t bytes[4] = {value >> 24, value >> 16, value >> 8, value}; size_t ret = vc_container_io_write(io, bytes, 4); return ret == 4 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; } /** Writes an unsigned 64 bits big endian integer to an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \param value The integer to write. * \return The status of the operation. */ STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_be_uint64(VC_CONTAINER_IO_T *io, uint64_t value) { uint8_t bytes[8] = { (uint8_t)(value >> 56), (uint8_t)(value >> 48), (uint8_t)(value >> 40), (uint8_t)(value >> 32), (uint8_t)(value >> 24), (uint8_t)(value >> 16), (uint8_t)(value >> 8), (uint8_t) value }; size_t ret = vc_container_io_write(io, bytes, 8); return ret == 8 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; } /** Writes an unsigned 16 bits little endian integer to an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \param value The integer to write. * \return The status of the operation. */ STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_le_uint16(VC_CONTAINER_IO_T *io, uint16_t value) { uint8_t bytes[2] = {(uint8_t)value, (uint8_t)(value >> 8)}; size_t ret = vc_container_io_write(io, bytes, 2); return ret == 2 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; } /** Writes an unsigned 24 bits little endian integer to an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \param value The integer to write. * \return The status of the operation. */ STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_le_uint24(VC_CONTAINER_IO_T *io, uint32_t value) { uint8_t bytes[3] = {value, value >> 8, value >> 16}; size_t ret = vc_container_io_write(io, bytes, 3); return ret == 3 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; } /** Writes an unsigned 32 bits little endian integer to an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \param value The integer to write. * \return The status of the operation. */ STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_le_uint32(VC_CONTAINER_IO_T *io, uint32_t value) { uint8_t bytes[4] = {value, value >> 8, value >> 16, value >> 24}; size_t ret = vc_container_io_write(io, bytes, 4); return ret == 4 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; } /** Writes an unsigned 64 bits little endian integer to an i/o stream. * \param io Pointer to the VC_CONTAINER_IO_T instance to use * \param value The integer to write. * \return The status of the operation. */ STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_le_uint64(VC_CONTAINER_IO_T *io, uint64_t value) { uint8_t bytes[8] = { (uint8_t) value, (uint8_t)(value >> 8), (uint8_t)(value >> 16), (uint8_t)(value >> 24), (uint8_t)(value >> 32), (uint8_t)(value >> 40), (uint8_t)(value >> 48), (uint8_t)(value >> 56) }; size_t ret = vc_container_io_write(io, bytes, 8); return ret == 8 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; } /***************************************************************************** * Helper macros for accessing the i/o stream. These will also call the right * functions depending on the endianness defined. *****************************************************************************/ /** Macro which returns the current position within the stream */ #define STREAM_POSITION(ctx) (ctx)->priv->io->offset /** Macro which returns true if the end of stream has been reached */ #define STREAM_EOS(ctx) ((ctx)->priv->io->status == VC_CONTAINER_ERROR_EOS) /** Macro which returns the status of the stream */ #define STREAM_STATUS(ctx) (ctx)->priv->io->status /** Macro which returns true if an error other than end of stream has occurred */ #define STREAM_ERROR(ctx) ((ctx)->priv->io->status && (ctx)->priv->io->status != VC_CONTAINER_ERROR_EOS) /** Macro which returns true if we can seek into the stream */ #define STREAM_SEEKABLE(ctx) (!((ctx)->priv->io->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK)) #define PEEK_BYTES(ctx, buffer, size) vc_container_io_peek((ctx)->priv->io, buffer, (size_t)(size)) #define READ_BYTES(ctx, buffer, size) vc_container_io_read((ctx)->priv->io, buffer, (size_t)(size)) #define SKIP_BYTES(ctx, size) vc_container_io_skip((ctx)->priv->io, (size_t)(size)) #define SEEK(ctx, off) vc_container_io_seek((ctx)->priv->io, (int64_t)(off)) #define CACHE_BYTES(ctx, size) vc_container_io_cache((ctx)->priv->io, (size_t)(size)) #define _SKIP_GUID(ctx) vc_container_io_skip((ctx)->priv->io, 16) #define _SKIP_U8(ctx) (vc_container_io_skip((ctx)->priv->io, 1) != 1) #define _SKIP_U16(ctx) (vc_container_io_skip((ctx)->priv->io, 2) != 2) #define _SKIP_U24(ctx) (vc_container_io_skip((ctx)->priv->io, 3) != 3) #define _SKIP_U32(ctx) (vc_container_io_skip((ctx)->priv->io, 4) != 4) #define _SKIP_U64(ctx) (vc_container_io_skip((ctx)->priv->io, 8) != 8) #define _SKIP_FOURCC(ctx) (vc_container_io_skip((ctx)->priv->io, 4) != 4) #define _READ_GUID(ctx, buffer) vc_container_io_read((ctx)->priv->io, buffer, 16) #define _READ_U8(ctx) vc_container_io_read_uint8((ctx)->priv->io) #define _READ_FOURCC(ctx) vc_container_io_read_fourcc((ctx)->priv->io) #define PEEK_GUID(ctx, buffer) vc_container_io_peek((ctx)->priv->io, buffer, 16) #define PEEK_U8(ctx) vc_container_io_peek_uint8((ctx)->priv->io) #ifdef CONTAINER_IS_BIG_ENDIAN # define _READ_U16(ctx) vc_container_io_read_be_uint16((ctx)->priv->io) # define _READ_U24(ctx) vc_container_io_read_be_uint24((ctx)->priv->io) # define _READ_U32(ctx) vc_container_io_read_be_uint32((ctx)->priv->io) # define _READ_U40(ctx) vc_container_io_read_be_uint40((ctx)->priv->io) # define _READ_U48(ctx) vc_container_io_read_be_uint48((ctx)->priv->io) # define _READ_U56(ctx) vc_container_io_read_be_uint56((ctx)->priv->io) # define _READ_U64(ctx) vc_container_io_read_be_uint64((ctx)->priv->io) # define PEEK_U16(ctx) vc_container_io_peek_be_uint16((ctx)->priv->io) # define PEEK_U24(ctx) vc_container_io_peek_be_uint24((ctx)->priv->io) # define PEEK_U32(ctx) vc_container_io_peek_be_uint32((ctx)->priv->io) # define PEEK_U64(ctx) vc_container_io_peek_be_uint64((ctx)->priv->io) #else # define _READ_U16(ctx) vc_container_io_read_le_uint16((ctx)->priv->io) # define _READ_U24(ctx) vc_container_io_read_le_uint24((ctx)->priv->io) # define _READ_U32(ctx) vc_container_io_read_le_uint32((ctx)->priv->io) # define _READ_U40(ctx) vc_container_io_read_le_uint40((ctx)->priv->io) # define _READ_U48(ctx) vc_container_io_read_le_uint48((ctx)->priv->io) # define _READ_U56(ctx) vc_container_io_read_le_uint56((ctx)->priv->io) # define _READ_U64(ctx) vc_container_io_read_le_uint64((ctx)->priv->io) # define PEEK_U16(ctx) vc_container_io_peek_le_uint16((ctx)->priv->io) # define PEEK_U24(ctx) vc_container_io_peek_le_uint24((ctx)->priv->io) # define PEEK_U32(ctx) vc_container_io_peek_le_uint32((ctx)->priv->io) # define PEEK_U64(ctx) vc_container_io_peek_le_uint64((ctx)->priv->io) #endif #define WRITE_BYTES(ctx, buffer, size) vc_container_io_write((ctx)->priv->io, buffer, (size_t)(size)) #define _WRITE_GUID(ctx, buffer) vc_container_io_write((ctx)->priv->io, buffer, 16) #define _WRITE_U8(ctx, v) vc_container_io_write_uint8((ctx)->priv->io, v) #define _WRITE_FOURCC(ctx, v) vc_container_io_write_fourcc((ctx)->priv->io, v) #ifdef CONTAINER_IS_BIG_ENDIAN # define _WRITE_U16(ctx, v) vc_container_io_write_be_uint16((ctx)->priv->io, v) # define _WRITE_U24(ctx, v) vc_container_io_write_be_uint24((ctx)->priv->io, v) # define _WRITE_U32(ctx, v) vc_container_io_write_be_uint32((ctx)->priv->io, v) # define _WRITE_U64(ctx, v) vc_container_io_write_be_uint64((ctx)->priv->io, v) #else # define _WRITE_U16(ctx, v) vc_container_io_write_le_uint16((ctx)->priv->io, v) # define _WRITE_U24(ctx, v) vc_container_io_write_le_uint24((ctx)->priv->io, v) # define _WRITE_U32(ctx, v) vc_container_io_write_le_uint32((ctx)->priv->io, v) # define _WRITE_U64(ctx, v) vc_container_io_write_le_uint64((ctx)->priv->io, v) #endif #ifndef CONTAINER_HELPER_LOG_INDENT # define CONTAINER_HELPER_LOG_INDENT(a) 0 #endif #ifdef CONTAINER_IS_BIG_ENDIAN # define LOG_FORMAT_TYPE_UINT LOG_FORMAT_TYPE_UINT_BE # define LOG_FORMAT_TYPE_STRING_UTF16 LOG_FORMAT_TYPE_STRING_UTF16_BE #else # define LOG_FORMAT_TYPE_UINT LOG_FORMAT_TYPE_UINT_LE # define LOG_FORMAT_TYPE_STRING_UTF16 LOG_FORMAT_TYPE_STRING_UTF16_LE #endif #ifndef ENABLE_CONTAINERS_LOG_FORMAT #define SKIP_GUID(ctx,n) _SKIP_GUID(ctx) #define SKIP_U8(ctx,n) _SKIP_U8(ctx) #define SKIP_U16(ctx,n) _SKIP_U16(ctx) #define SKIP_U24(ctx,n) _SKIP_U24(ctx) #define SKIP_U32(ctx,n) _SKIP_U32(ctx) #define SKIP_U64(ctx,n) _SKIP_U64(ctx) #define SKIP_FOURCC(ctx,n) _SKIP_FOURCC(ctx) #define READ_GUID(ctx,buffer,n) _READ_GUID(ctx,(uint8_t *)buffer) #define READ_U8(ctx,n) _READ_U8(ctx) #define READ_U16(ctx,n) _READ_U16(ctx) #define READ_U24(ctx,n) _READ_U24(ctx) #define READ_U32(ctx,n) _READ_U32(ctx) #define READ_U40(ctx,n) _READ_U40(ctx) #define READ_U48(ctx,n) _READ_U48(ctx) #define READ_U56(ctx,n) _READ_U56(ctx) #define READ_U64(ctx,n) _READ_U64(ctx) #define READ_FOURCC(ctx,n) _READ_FOURCC(ctx) #define READ_STRING(ctx,buffer,sz,n) READ_BYTES(ctx,buffer,sz) #define READ_STRING_UTF16(ctx,buffer,sz,n) READ_BYTES(ctx,buffer,sz) #define SKIP_STRING(ctx,sz,n) SKIP_BYTES(ctx,sz) #define SKIP_STRING_UTF16(ctx,sz,n) SKIP_BYTES(ctx,sz) #else #define SKIP_GUID(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_GUID, 16, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) #define SKIP_U8(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 1, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) #define SKIP_U16(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 2, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) #define SKIP_U24(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 3, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) #define SKIP_U32(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 4, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) #define SKIP_U64(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 8, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) #define SKIP_FOURCC(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_FOURCC, 4, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) #define READ_GUID(ctx,buffer,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_GUID, 16, n, (uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), 0) #define READ_U8(ctx,n) (uint8_t)vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 1, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) #define READ_U16(ctx,n) (uint16_t)vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 2, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) #define READ_U24(ctx,n) (uint32_t)vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 3, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) #define READ_U32(ctx,n) (uint32_t)vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 4, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) #define READ_U40(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 5, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) #define READ_U48(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 6, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) #define READ_U56(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 7, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) #define READ_U64(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 8, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) #define READ_FOURCC(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_FOURCC, 4, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) #define READ_STRING_UTF16(ctx,buffer,sz,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_STRING_UTF16, sz, n, (uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), 0) #define READ_STRING(ctx,buffer,sz,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_STRING, sz, n, (uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), 0) #define SKIP_STRING_UTF16(ctx,sz,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_STRING_UTF16, sz, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) #define SKIP_STRING(ctx,sz,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_STRING, sz, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) #endif #ifndef ENABLE_CONTAINERS_LOG_FORMAT #define WRITE_GUID(ctx,buffer,n) _WRITE_GUID(ctx,(const uint8_t *)buffer) #define WRITE_U8(ctx,v,n) _WRITE_U8(ctx,(uint8_t)(v)) #define WRITE_FOURCC(ctx,v,n) _WRITE_FOURCC(ctx,(uint32_t)(v)) #define WRITE_U16(ctx,v,n) _WRITE_U16(ctx,(uint16_t)(v)) #define WRITE_U24(ctx,v,n) _WRITE_U24(ctx,(uint32_t)(v)) #define WRITE_U32(ctx,v,n) _WRITE_U32(ctx,(uint32_t)(v)) #define WRITE_U64(ctx,v,n) _WRITE_U64(ctx,(uint64_t)(v)) #define WRITE_STRING(ctx,buffer,size,n) WRITE_BYTES(ctx, buffer, size) #else #define WRITE_GUID(ctx,buffer,n) (vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_GUID, 16, n, UINT64_C(0), (const uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) ? 0 : 16) #define WRITE_U8(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 1, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) #define WRITE_FOURCC(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_FOURCC, 4, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) #define WRITE_U16(ctx,v,n) (uint16_t)vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 2, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) #define WRITE_U24(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 3, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) #define WRITE_U32(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 4, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) #define WRITE_U64(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 8, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) #define WRITE_STRING(ctx,buffer,size,n) (vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_STRING, size, n, UINT64_C(0), (const uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) ? 0 : size) #endif #ifdef ENABLE_CONTAINERS_LOG_FORMAT #define LOG_FORMAT(ctx, ...) do { if((ctx)->priv->io->module) vc_container_helper_format_debug(ctx, CONTAINER_HELPER_LOG_INDENT(ctx), __VA_ARGS__); } while(0) #else #define LOG_FORMAT(ctx, ...) do {} while (0) #endif #define LOG_FORMAT_TYPE_UINT_LE 0 #define LOG_FORMAT_TYPE_UINT_BE 1 #define LOG_FORMAT_TYPE_STRING 2 #define LOG_FORMAT_TYPE_STRING_UTF16_LE 3 #define LOG_FORMAT_TYPE_STRING_UTF16_BE 4 #define LOG_FORMAT_TYPE_FOURCC 5 #define LOG_FORMAT_TYPE_GUID 6 #define LOG_FORMAT_TYPE_HEX 0x100 uint64_t vc_container_helper_int_debug(VC_CONTAINER_T *ctx, int type, uint64_t value, const char *name, int indent); uint64_t vc_container_helper_read_debug(VC_CONTAINER_T *ctx, int type, int size, const char *name, uint8_t *buffer, int indent, int b_skip); VC_CONTAINER_STATUS_T vc_container_helper_write_debug(VC_CONTAINER_T *ctx, int type, int size, const char *name, uint64_t value, const uint8_t *buffer, int indent, int silent); void vc_container_helper_format_debug(VC_CONTAINER_T *ctx, int indent, const char *format, ...); #endif /* VC_CONTAINERS_IO_HELPERS_H */ /* End of file */ /*-----------------------------------------------------------------------------*/ userland/containers/core/containers_list.c000066400000000000000000000164141421703157200213440ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include "containers/core/containers_common.h" #include "containers/core/containers_list.h" /****************************************************************************** Defines and constants. ******************************************************************************/ /****************************************************************************** Type definitions ******************************************************************************/ /****************************************************************************** Function prototypes ******************************************************************************/ /****************************************************************************** Local Functions ******************************************************************************/ /** Find an entry in the list, or the insertion point. * Uses binary sub-division to find the search item. If index is not NULL, the * index of the matching entry, or the point at which to insert if not found, is * written to that address. * * \param list The list to be searched. * \param entry The entry for which to search. * \param index Set to index of match, or insertion point if not found. May be NULL. * \return True if a match was found, false if not. */ static bool vc_containers_list_find_index(const VC_CONTAINERS_LIST_T *list, const void *entry, uint32_t *index) { const char *entries = (const char *)list->entries; size_t entry_size = list->entry_size; VC_CONTAINERS_LIST_COMPARATOR_T comparator = list->comparator; uint32_t start = 0, end = list->size; uint32_t mid = end >> 1; bool match = false; while (mid < end) { int comparison = comparator(entry, entries + mid * entry_size); if (comparison < 0) end = mid; else if (comparison > 0) start = mid + 1; else { match = true; break; } mid = (start + end) >> 1; } if (index) *index = mid; return match; } /****************************************************************************** Functions exported as part of the API ******************************************************************************/ /*****************************************************************************/ VC_CONTAINERS_LIST_T *vc_containers_list_create(uint32_t capacity, size_t entry_size, VC_CONTAINERS_LIST_COMPARATOR_T comparator) { VC_CONTAINERS_LIST_T *list; list = (VC_CONTAINERS_LIST_T *)malloc(sizeof(VC_CONTAINERS_LIST_T)); if (!list) return NULL; /* Ensure non-zero capacity, as that signifies a read-only list */ if (!capacity) capacity = 1; list->entries = malloc(capacity * entry_size); if (!list->entries) { free(list); return NULL; } list->size = 0; list->capacity = capacity; list->entry_size = entry_size; list->comparator = comparator; return list; } /*****************************************************************************/ void vc_containers_list_destroy(VC_CONTAINERS_LIST_T *list) { /* Avoid trying to destroy read-only lists */ if (list && list->capacity) { if (list->entries) free(list->entries); free(list); } } /*****************************************************************************/ void vc_containers_list_reset(VC_CONTAINERS_LIST_T *list) { /* Avoid trying to reset read-only lists */ if (list && list->capacity) list->size = 0; } /*****************************************************************************/ bool vc_containers_list_insert(VC_CONTAINERS_LIST_T *list, void *new_entry, bool allow_duplicates) { uint32_t insert_idx; char *insert_ptr; size_t entry_size; bool match; if (!list || !list->capacity) return false; entry_size = list->entry_size; match = vc_containers_list_find_index(list, new_entry, &insert_idx); insert_ptr = (char *)list->entries + entry_size * insert_idx; if (!match || allow_duplicates) { /* Ensure there is space for the new entry */ if (list->size == list->capacity) { void *new_entries = realloc(list->entries, (list->size + 1) * entry_size); if (!new_entries) return false; list->entries = new_entries; list->capacity++; } /* Move up anything above the insertion point */ if (insert_idx < list->size) memmove(insert_ptr + entry_size, insert_ptr, (list->size - insert_idx) * entry_size); list->size++; } /* Copy in the new entry (overwriting the old one if necessary) */ memcpy(insert_ptr, new_entry, list->entry_size); return true; } /*****************************************************************************/ bool vc_containers_list_find_entry(const VC_CONTAINERS_LIST_T *list, void *entry) { uint32_t index; size_t entry_size; if (!vc_containers_list_find_index(list, entry, &index)) return false; entry_size = list->entry_size; memcpy(entry, (const char *)list->entries + entry_size * index, entry_size); return true; } /*****************************************************************************/ void vc_containers_list_validate(const VC_CONTAINERS_LIST_T *list) { uint32_t ii, entry_size; const uint8_t *entry_ptr; vc_container_assert(list); vc_container_assert(!list->capacity || list->size <= list->capacity); vc_container_assert(list->entry_size); vc_container_assert(list->comparator); vc_container_assert(list->entries); /* Check all entries are in sorted order */ entry_ptr = (const uint8_t *)list->entries; entry_size = list->entry_size; for (ii = 1; ii < list->size; ii++) { vc_container_assert(list->comparator(entry_ptr, entry_ptr + entry_size) <= 0); entry_ptr += entry_size; } } userland/containers/core/containers_list.h000066400000000000000000000112341421703157200213440ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef _VC_CONTAINERS_LIST_H_ #define _VC_CONTAINERS_LIST_H_ #include "containers/containers.h" /** List entry comparison prototype. * Returns zero if items at a and b match, positive if a is "bigger" than b and * negative if a is "smaller" than b. */ typedef int (*VC_CONTAINERS_LIST_COMPARATOR_T)(const void *a, const void *b); /** Sorted list type. * Storage type providing efficient insertion and search via binary sub-division. */ typedef struct vc_containers_list_tag { uint32_t size; /**< Number of defined entries in list */ uint32_t capacity; /**< Capacity of list, in entries, or zero for read-only */ size_t entry_size; /**< Size of one entry, in bytes */ VC_CONTAINERS_LIST_COMPARATOR_T comparator; /**< Entry comparison function */ void *entries; /**< Pointer to array of entries */ } VC_CONTAINERS_LIST_T; /** Macro to generate a static, read-only list from an array and comparator */ #define VC_CONTAINERS_STATIC_LIST(L, A, C) static VC_CONTAINERS_LIST_T L = { countof(A), 0, sizeof(*(A)), (VC_CONTAINERS_LIST_COMPARATOR_T)(C), A } /** Create an empty list. * The list is created based on the details provided, minimum capacity one entry. * * \param The initial capacity in entries. * \param entry_size The size of each entry, in bytes. * \param comparator A function for comparing two entries. * \return The new list or NULL. */ VC_CONTAINERS_LIST_T *vc_containers_list_create(uint32_t capacity, size_t entry_size, VC_CONTAINERS_LIST_COMPARATOR_T comparator); /** Destroy a list. * Has no effect on a static list. * * \param list The list to be destroyed. */ void vc_containers_list_destroy(VC_CONTAINERS_LIST_T *list); /** Reset a list to be empty. * Has no effect on a static list. * * \param list The list to be reset. */ void vc_containers_list_reset(VC_CONTAINERS_LIST_T *list); /** Insert an entry into the list. * * \param list The list. * \param new_entry The new entry to be inserted. * \param allow_duplicates Determines whether to insert or overwrite if there * is an existing matching entry. * \return True if the entry has successfully been inserted, false if the list * needed to be enlarged and the memory allocation failed. */ bool vc_containers_list_insert(VC_CONTAINERS_LIST_T *list, void *new_entry, bool allow_duplicates); /** Find an entry in the list and fill in the result. * Searches for an entry in the list using the comparator and if found * overwrites the one passed in with the one found. * * \param list The list to search. * \param entry An entry with enough defined to find it in the list, filled in * with the rest if found. * \return True if found, false if not. */ bool vc_containers_list_find_entry(const VC_CONTAINERS_LIST_T *list, void *entry); /** Validates a list pointer. * Fields and contents of a list are checked and asserted to be correct. With a * large list this may be slow, so it is recommended only to call this in debug * builds. * * \param list The list to be validated. */ void vc_containers_list_validate(const VC_CONTAINERS_LIST_T *list); #endif /* _VC_CONTAINERS_LIST_H_ */ userland/containers/core/containers_loader.c000066400000000000000000000364041421703157200216400ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include "containers/core/containers_private.h" #include "containers/core/containers_loader.h" #if !defined(ENABLE_CONTAINERS_STANDALONE) #include "vcos_dlfcn.h" #define DL_SUFFIX VCOS_SO_EXT #ifndef DL_PATH_PREFIX #define DL_PATH_PREFIX "" #endif #endif /****************************************************************************** Type definitions. ******************************************************************************/ typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_READER_OPEN_FUNC_T)(VC_CONTAINER_T *); typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_WRITER_OPEN_FUNC_T)(VC_CONTAINER_T *); /****************************************************************************** Prototypes for local functions ******************************************************************************/ static void reset_context(VC_CONTAINER_T *p_ctx); static VC_CONTAINER_READER_OPEN_FUNC_T load_library(void **handle, const char *name, const char *ext, int read); static void unload_library(void *handle); static VC_CONTAINER_READER_OPEN_FUNC_T load_reader(void **handle, const char *name); static VC_CONTAINER_READER_OPEN_FUNC_T load_writer(void **handle, const char *name); static VC_CONTAINER_READER_OPEN_FUNC_T load_metadata_reader(void **handle, const char *name); static const char* container_for_fileext(const char *fileext); /******************************************************************************** List of supported containers ********************************************************************************/ static const char *readers[] = {"mp4", "asf", "avi", "mkv", "wav", "flv", "simple", "rawvideo", "mpga", "ps", "rtp", "rtsp", "rcv", "rv9", "qsynth", "binary", 0}; static const char *writers[] = {"mp4", "asf", "avi", "binary", "simple", "rawvideo", 0}; static const char *metadata_readers[] = {"id3", 0}; #if defined(ENABLE_CONTAINERS_STANDALONE) VC_CONTAINER_STATUS_T asf_reader_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T avi_reader_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T mp4_reader_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T mp4_writer_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T mpga_reader_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T mkv_reader_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T wav_reader_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T flv_reader_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T ps_reader_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T rtp_reader_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T rtsp_reader_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T binary_reader_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T binary_writer_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T rv9_reader_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T qsynth_reader_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T simple_reader_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T simple_writer_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T rawvideo_reader_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T rawvideo_writer_open( VC_CONTAINER_T * ); VC_CONTAINER_STATUS_T id3_metadata_reader_open( VC_CONTAINER_T * ); static struct { const char *name; VC_CONTAINER_READER_OPEN_FUNC_T func; } reader_entry_points[] = { {"asf", &asf_reader_open}, {"avi", &avi_reader_open}, {"mpga", &mpga_reader_open}, {"mkv", &mkv_reader_open}, {"wav", &wav_reader_open}, {"mp4", &mp4_reader_open}, {"flv", &flv_reader_open}, {"ps", &ps_reader_open}, {"binary", &binary_reader_open}, {"rtp", &rtp_reader_open}, {"rtsp", &rtsp_reader_open}, {"rcv", &rcv_reader_open}, {"rv9", &rv9_reader_open}, {"qsynth", &qsynth_reader_open}, {"simple", &simple_reader_open}, {"rawvideo", &rawvideo_reader_open}, {0, 0} }; static struct { const char *name; VC_CONTAINER_READER_OPEN_FUNC_T func; } metadata_reader_entry_points[] = { {"id3", &id3_metadata_reader_open}, {0, 0} }; static struct { const char *name; VC_CONTAINER_WRITER_OPEN_FUNC_T func; } writer_entry_points[] = { {"avi", &avi_writer_open}, {"mp4", &mp4_writer_open}, {"binary", &binary_writer_open}, {"simple", &simple_writer_open}, {"rawvideo", &rawvideo_writer_open}, {0, 0} }; #endif /* defined(ENABLE_CONTAINERS_STANDALONE) */ /** Table describing the mapping between file extensions and container name. This is only used as optimisation to decide which container to try first. Entries where the file extension and container have the same name can be omitted. */ static const struct { const char *extension; const char *container; } extension_container_mapping[] = { { "wma", "asf" }, { "wmv", "asf" }, { "mov", "mp4" }, { "3gp", "mp4" }, { "mp2", "mpga" }, { "mp3", "mpga" }, { "webm", "mkv" }, { "mid", "qsynth" }, { "mld", "qsynth" }, { "mmf", "qsynth" }, { 0, 0 } }; /******************************************************************************** Public functions ********************************************************************************/ VC_CONTAINER_STATUS_T vc_container_load_reader(VC_CONTAINER_T *p_ctx, const char *fileext) { const char *name; void *handle = NULL; VC_CONTAINER_READER_OPEN_FUNC_T func; VC_CONTAINER_STATUS_T status; unsigned int i; int64_t offset; vc_container_assert(p_ctx && !p_ctx->priv->module_handle); /* FIXME: the missing part here is code that reads a configuration or searches the filesystem for container libraries. Instead, we currently rely on static arrays i.e. 'readers', 'writers', etc. */ /* Before trying proper container readers, iterate through metadata readers to parse tags concatenated to start/end of stream */ for(i = 0; metadata_readers[i]; i++) { if ((func = load_metadata_reader(&handle, metadata_readers[i])) != NULL) { status = (*func)(p_ctx); if(!status && p_ctx->priv->pf_close) p_ctx->priv->pf_close(p_ctx); reset_context(p_ctx); unload_library(handle); if(status == VC_CONTAINER_SUCCESS) break; if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; } } /* Store the current position, in case any containers don't leave the stream at the start, and the IO layer can cope with the seek */ offset = p_ctx->priv->io->offset; /* Now move to containers, try to find a readers using the file extension to name mapping first */ if (fileext && (name = container_for_fileext(fileext)) != NULL && (func = load_reader(&handle, name)) != NULL) { status = (*func)(p_ctx); if(status == VC_CONTAINER_SUCCESS) goto success; unload_library(handle); if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; } /* If there was no suitable mapping, iterate through all readers. */ for(i = 0; readers[i]; i++) { if ((func = load_reader(&handle, readers[i])) != NULL) { if(vc_container_io_seek(p_ctx->priv->io, offset) != VC_CONTAINER_SUCCESS) { unload_library(handle); goto error; } status = (*func)(p_ctx); if(status == VC_CONTAINER_SUCCESS) goto success; reset_context(p_ctx); unload_library(handle); if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; } } error: return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; success: p_ctx->priv->module_handle = handle; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_load_writer(VC_CONTAINER_T *p_ctx, const char *fileext) { const char *name; void *handle = NULL; VC_CONTAINER_WRITER_OPEN_FUNC_T func; VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FAILED; unsigned int i; vc_container_assert(p_ctx && !p_ctx->priv->module_handle); /* Do we have a container mapping for this file extension? */ if ((name = container_for_fileext(fileext)) != NULL && (func = load_writer(&handle, name)) != NULL) { status = (*func)(p_ctx); if(status == VC_CONTAINER_SUCCESS) goto success; unload_library(handle); if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; } /* If there was no suitable mapping, iterate through all writers. */ for(i = 0; writers[i]; i++) { if ((func = load_writer(&handle, writers[i])) != NULL) { status = (*func)(p_ctx); if(status == VC_CONTAINER_SUCCESS) goto success; unload_library(handle); if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; } } error: return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; success: p_ctx->priv->module_handle = handle; return status; } /*****************************************************************************/ void vc_container_unload(VC_CONTAINER_T *p_ctx) { if (p_ctx->priv->module_handle) { unload_library(p_ctx->priv->module_handle); p_ctx->priv->module_handle = NULL; } } /****************************************************************************** Local Functions ******************************************************************************/ static void reset_context(VC_CONTAINER_T *p_ctx) { vc_container_assert(p_ctx); p_ctx->capabilities = 0; p_ctx->tracks = NULL; p_ctx->tracks_num = 0; p_ctx->drm = NULL; p_ctx->priv->module = NULL; p_ctx->priv->pf_close = NULL; p_ctx->priv->pf_read = NULL; p_ctx->priv->pf_write = NULL; p_ctx->priv->pf_seek = NULL; p_ctx->priv->pf_control = NULL; p_ctx->priv->tmp_io = NULL; } /*****************************************************************************/ static VC_CONTAINER_READER_OPEN_FUNC_T load_reader(void **handle, const char *name) { return load_library(handle, name, NULL, 1); } /*****************************************************************************/ static VC_CONTAINER_READER_OPEN_FUNC_T load_writer(void **handle, const char *name) { return load_library(handle, name, NULL, 0); } /*****************************************************************************/ static VC_CONTAINER_READER_OPEN_FUNC_T load_metadata_reader(void **handle, const char *name) { #define DL_PREFIX_METADATA "metadata_" return load_library(handle, name, DL_PREFIX_METADATA, 1); } #if !defined(ENABLE_CONTAINERS_STANDALONE) /*****************************************************************************/ static VC_CONTAINER_READER_OPEN_FUNC_T load_library(void **handle, const char *name, const char *ext, int read) { #define DL_PREFIX_RD "reader_" #define DL_PREFIX_WR "writer_" const char *entrypt_read = {"reader_open"}; const char *entrypt_write = {"writer_open"}; char *dl_name, *entrypt_name; void *dl_handle; VC_CONTAINER_READER_OPEN_FUNC_T func = NULL; unsigned dl_size, ep_size, name_len = strlen(name) + (ext ? strlen(ext) : 0); vc_container_assert(read == 0 || read == 1); dl_size = strlen(DL_PATH_PREFIX) + MAX(strlen(DL_PREFIX_RD), strlen(DL_PREFIX_WR)) + name_len + strlen(DL_SUFFIX) + 1; if ((dl_name = malloc(dl_size)) == NULL) return NULL; ep_size = name_len + 1 + MAX(strlen(entrypt_read), strlen(entrypt_write)) + 1; if ((entrypt_name = malloc(ep_size)) == NULL) { free(dl_name); return NULL; } snprintf(dl_name, dl_size, "%s%s%s%s%s", DL_PATH_PREFIX, read ? DL_PREFIX_RD : DL_PREFIX_WR, ext ? ext : "", name, DL_SUFFIX); snprintf(entrypt_name, ep_size, "%s_%s%s", name, ext ? ext : "", read ? entrypt_read : entrypt_write); if ( (dl_handle = vcos_dlopen(dl_name, VCOS_DL_NOW)) != NULL ) { /* Try generic entrypoint name before the mangled, full name */ func = (VC_CONTAINER_READER_OPEN_FUNC_T)vcos_dlsym(dl_handle, read ? entrypt_read : entrypt_write); #if !defined(__VIDEOCORE__) /* The following would be pointless on MW/VideoCore */ if (!func) func = (VC_CONTAINER_READER_OPEN_FUNC_T)vcos_dlsym(dl_handle, entrypt_name); #endif /* Only return handle if symbol found */ if (func) *handle = dl_handle; else vcos_dlclose(dl_handle); } free(entrypt_name); free(dl_name); return func; } /*****************************************************************************/ static void unload_library(void *handle) { vcos_dlclose(handle); } #else /* !defined(ENABLE_CONTAINERS_STANDALONE) */ /*****************************************************************************/ static VC_CONTAINER_READER_OPEN_FUNC_T load_library(void **handle, const char *name, const char *ext, int read) { int i; VC_CONTAINER_PARAM_UNUSED(handle); VC_CONTAINER_PARAM_UNUSED(ext); if (read) { for (i = 0; reader_entry_points[i].name; i++) if (!strcasecmp(reader_entry_points[i].name, name)) return reader_entry_points[i].func; for (i = 0; metadata_reader_entry_points[i].name; i++) if (!strcasecmp(metadata_reader_entry_points[i].name, name)) return metadata_reader_entry_points[i].func; } else { for (i = 0; writer_entry_points[i].name; i++) if (!strcasecmp(writer_entry_points[i].name, name)) return writer_entry_points[i].func; } return NULL; } /*****************************************************************************/ static void unload_library(void *handle) { (void)handle; } #endif /* !defined(ENABLE_CONTAINERS_STANDALONE) */ /*****************************************************************************/ static const char* container_for_fileext(const char *fileext) { int i; for( i = 0; fileext && extension_container_mapping[i].extension; i++ ) { if (!strcasecmp( fileext, extension_container_mapping[i].extension )) return extension_container_mapping[i].container; } return fileext; } userland/containers/core/containers_loader.h000066400000000000000000000040761421703157200216450ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_CONTAINERS_LOADER_H #define VC_CONTAINERS_LOADER_H /** Find and attempt to load & open reader, 'fileext' is a hint that can be used to speed up loading. */ VC_CONTAINER_STATUS_T vc_container_load_reader(VC_CONTAINER_T *p_ctx, const char *fileext); /** Find and attempt to load & open writer, 'fileext' is a hint used to help in selecting the appropriate container format. */ VC_CONTAINER_STATUS_T vc_container_load_writer(VC_CONTAINER_T *p_ctx, const char *fileext); void vc_container_unload(VC_CONTAINER_T *p_ctx); #endif /* VC_CONTAINERS_LOADER_H */ userland/containers/core/containers_logging.c000066400000000000000000000076611421703157200220230ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include "containers/containers.h" #include "containers/core/containers_private.h" #include "containers/core/containers_logging.h" #ifndef ENABLE_CONTAINERS_STANDALONE # include "vcos.h" #endif #ifdef __ANDROID__ #define LOG_TAG "ContainersCore" #include #endif /* Default verbosity that will be inherited by containers */ static uint32_t default_verbosity_mask = VC_CONTAINER_LOG_ALL; /* By default log everything that's not associated with a container context */ static uint32_t verbosity_mask = VC_CONTAINER_LOG_ALL; void vc_container_log_set_default_verbosity(uint32_t mask) { default_verbosity_mask = mask; } uint32_t vc_container_log_get_default_verbosity(void) { return default_verbosity_mask; } void vc_container_log_set_verbosity(VC_CONTAINER_T *ctx, uint32_t mask) { if(!ctx) verbosity_mask = mask; else ctx->priv->verbosity = mask; } void vc_container_log(VC_CONTAINER_T *ctx, VC_CONTAINER_LOG_TYPE_T type, const char *format, ...) { uint32_t verbosity = ctx ? ctx->priv->verbosity : verbosity_mask; va_list args; // Optimise out the call to vc_container_log_vargs etc. when it won't do anything. if(!(type & verbosity)) return; va_start( args, format ); vc_container_log_vargs(ctx, type, format, args); va_end( args ); } void vc_container_log_vargs(VC_CONTAINER_T *ctx, VC_CONTAINER_LOG_TYPE_T type, const char *format, va_list args) { uint32_t verbosity = ctx ? ctx->priv->verbosity : verbosity_mask; // If the verbosity is such that the type doesn't need logging quit now. if(!(type & verbosity)) return; #ifdef __ANDROID__ { // Default to Android's "verbose" level (doesn't usually come out) android_LogPriority logLevel = ANDROID_LOG_VERBOSE; // Where type suggest a higher level is required update logLevel. // (Usually type contains only 1 bit as set by the LOG_DEBUG, LOG_ERROR or LOG_INFO macros) if (type & VC_CONTAINER_LOG_ERROR) logLevel = ANDROID_LOG_ERROR; else if (type & VC_CONTAINER_LOG_INFO) logLevel = ANDROID_LOG_INFO; else if (type & VC_CONTAINER_LOG_DEBUG) logLevel = ANDROID_LOG_DEBUG; // Actually put the message out. LOG_PRI_VA(logLevel, LOG_TAG, format, args); } #else #ifndef ENABLE_CONTAINERS_STANDALONE vcos_vlog(format, args); #else vprintf(format, args); printf("\n"); fflush(0); #endif #endif } userland/containers/core/containers_logging.h000066400000000000000000000057661421703157200220340ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_CONTAINERS_LOGGING_H #define VC_CONTAINERS_LOGGING_H #ifdef __cplusplus extern "C" { #endif /** \file containers_logging.h * Logging API used by container readers and writers */ typedef enum { VC_CONTAINER_LOG_ERROR = 0x01, VC_CONTAINER_LOG_INFO = 0x02, VC_CONTAINER_LOG_DEBUG = 0x04, VC_CONTAINER_LOG_FORMAT = 0x08, VC_CONTAINER_LOG_ALL = 0xFF } VC_CONTAINER_LOG_TYPE_T; void vc_container_log(VC_CONTAINER_T *ctx, VC_CONTAINER_LOG_TYPE_T type, const char *format, ...); void vc_container_log_vargs(VC_CONTAINER_T *ctx, VC_CONTAINER_LOG_TYPE_T type, const char *format, va_list args); void vc_container_log_set_verbosity(VC_CONTAINER_T *ctx, uint32_t mask); void vc_container_log_set_default_verbosity(uint32_t mask); uint32_t vc_container_log_get_default_verbosity(void); #define ENABLE_CONTAINER_LOG_ERROR #define ENABLE_CONTAINER_LOG_INFO #ifdef ENABLE_CONTAINER_LOG_DEBUG # define LOG_DEBUG(ctx, ...) vc_container_log(ctx, VC_CONTAINER_LOG_DEBUG, __VA_ARGS__) #else # define LOG_DEBUG(ctx, ...) VC_CONTAINER_PARAM_UNUSED(ctx) #endif #ifdef ENABLE_CONTAINER_LOG_ERROR # define LOG_ERROR(ctx, ...) vc_container_log(ctx, VC_CONTAINER_LOG_ERROR, __VA_ARGS__) #else # define LOG_ERROR(ctx, ...) VC_CONTAINER_PARAM_UNUSED(ctx) #endif #ifdef ENABLE_CONTAINER_LOG_INFO # define LOG_INFO(ctx, ...) vc_container_log(ctx, VC_CONTAINER_LOG_INFO, __VA_ARGS__) #else # define LOG_INFO(ctx, ...) VC_CONTAINER_PARAM_UNUSED(ctx) #endif #ifdef __cplusplus } #endif #endif /* VC_CONTAINERS_LOGGING_H */ userland/containers/core/containers_private.h000066400000000000000000000200451421703157200220430ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_CONTAINERS_PRIVATE_H #define VC_CONTAINERS_PRIVATE_H /** \file containers_private.h * Private interface for container readers and writers */ #include #include "containers/containers.h" #include "containers/core/containers_common.h" #include "containers/core/containers_io.h" #include "containers/core/containers_filters.h" #include "containers/packetizers.h" #include "containers/core/containers_uri.h" #define URI_MAX_LEN 256 /** \defgroup VcContainerModuleApi Container Module API * Private interface for modules implementing container readers and writers */ /* @{ */ /** Track context private to the container reader / writer instance. This private context is used to * store data which shouldn't be exported by the public API. */ typedef struct VC_CONTAINER_TRACK_PRIVATE_T { /** Pointer to the private data of the container module in use */ struct VC_CONTAINER_TRACK_MODULE_T *module; /** Pointer to the allocated buffer for the track extradata */ uint8_t *extradata; /** Size of the allocated buffer for the track extradata */ uint32_t extradata_size; /** Pointer to the allocated buffer for the track DRM data*/ uint8_t *drmdata; /** Size of the allocated buffer for the track DRM data */ uint32_t drmdata_size; /** Packetizer used by this track */ VC_PACKETIZER_T *packetizer; } VC_CONTAINER_TRACK_PRIVATE_T; /** Context private to the container reader / writer instance. This private context is used to * store data which shouldn't be exported by the public API. */ typedef struct VC_CONTAINER_PRIVATE_T { /** Pointer to the container i/o instance used to read / write to the container */ struct VC_CONTAINER_IO_T *io; /** Pointer to the private data of the container module in use */ struct VC_CONTAINER_MODULE_T *module; /** Reads a data packet from a container reader. * By default, the reader will read whatever packet comes next in the container and update the * given \ref VC_CONTAINER_PACKET_T structure with this packet's information. * This behaviour can be changed using the \ref VC_CONTAINER_READ_FLAGS_T.\n * \ref VC_CONTAINER_READ_FLAG_INFO will instruct the reader to only return information on the * following packet but not its actual data. The data can be retreived later by issuing another * read request. * \ref VC_CONTAINER_READ_FLAG_FORCE_TRACK will force the reader to read the next packet for the * selected track (as present in the \ref VC_CONTAINER_PACKET_T structure) instead of defaulting * to reading the packet which comes next in the container. * \ref VC_CONTAINER_READ_FLAG_SKIP will instruct the reader to skip the next packet. In this case * it isn't necessary for the caller to pass a pointer to a \ref VC_CONTAINER_PACKET_T structure * unless the \ref VC_CONTAINER_READ_FLAG_INFO is also given. * A combination of all these flags can be used. * * \param context Pointer to the context of the reader to use * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet * This needs to be partially filled before the call (buffer, buffer_size) * \param flags Flags controlling the read operation * \return the status of the operation */ VC_CONTAINER_STATUS_T (*pf_read)( VC_CONTAINER_T *context, VC_CONTAINER_PACKET_T *packet, VC_CONTAINER_READ_FLAGS_T flags ); /** Writes a data packet to a container writer. * * \param context Pointer to the context of the writer to use * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet * \return the status of the operation */ VC_CONTAINER_STATUS_T (*pf_write)( struct VC_CONTAINER_T *context, VC_CONTAINER_PACKET_T *packet ); /** Seek into a container reader. * * \param context Pointer to the context of the reader to use * \param offset Offset to seek to. Used as an input as well as output value. * \param mode Seeking mode requested. * \param flags Flags affecting the seeking operation. * \return the status of the operation */ VC_CONTAINER_STATUS_T (*pf_seek)( VC_CONTAINER_T *context, int64_t *offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags); /** Extensible control function for container readers and writers. * This function takes a variable number of arguments which will depend on the specific operation. * * \param context Pointer to the VC_CONTAINER_T context to use * \param operation The requested operation * \return the status of the operation */ VC_CONTAINER_STATUS_T (*pf_control)( VC_CONTAINER_T *context, VC_CONTAINER_CONTROL_T operation, va_list args ); /** Closes a container reader / writer module. * * \param context Pointer to the context of the instance to close * \return the status of the operation */ VC_CONTAINER_STATUS_T (*pf_close)( struct VC_CONTAINER_T *context ); /** Pointer to container filter instance used for DRM */ struct VC_CONTAINER_FILTER_T *drm_filter; /** Pointer to the container module code and symbols*/ void *module_handle; /** Maximum size of a stream that is being written. * This is set by the client using the control mechanism */ int64_t max_size; /** Pointer to the temp i/o instance used to write temporary data */ struct VC_CONTAINER_IO_T *tmp_io; /** Current status of the container (only used for writers to prevent trying to write * more data if one of the writes failed) */ VC_CONTAINER_STATUS_T status; /** Logging verbosity */ uint32_t verbosity; /** Uniform Resource Identifier */ struct VC_URI_PARTS_T *uri; /** Flag specifying whether one of the tracks is being packetized */ bool packetizing; /** Temporary packet structure used to feed data to the packetizer */ VC_CONTAINER_PACKET_T packetizer_packet; /** Temporary buffer used by the packetizer */ uint8_t *packetizer_buffer; } VC_CONTAINER_PRIVATE_T; /* Internal functions */ VC_CONTAINER_TRACK_T *vc_container_allocate_track( VC_CONTAINER_T *context, unsigned int extra_size ); void vc_container_free_track( VC_CONTAINER_T *context, VC_CONTAINER_TRACK_T *track ); VC_CONTAINER_STATUS_T vc_container_track_allocate_extradata( VC_CONTAINER_T *context, VC_CONTAINER_TRACK_T *p_track, unsigned int extra_size ); VC_CONTAINER_STATUS_T vc_container_track_allocate_drmdata( VC_CONTAINER_T *context, VC_CONTAINER_TRACK_T *p_track, unsigned int size ); /* @} */ #endif /* VC_CONTAINERS_PRIVATE_H */ userland/containers/core/containers_time.h000066400000000000000000000100151421703157200213230ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_CONTAINERS_TIME_H #define VC_CONTAINERS_TIME_H /** \file * Utility functions to help with timestamping of elementary stream frames */ typedef struct VC_CONTAINER_TIME_T { uint32_t samplerate_num; uint32_t samplerate_den; uint32_t time_base; uint32_t remainder; int64_t time; } VC_CONTAINER_TIME_T; /*****************************************************************************/ STATIC_INLINE void vc_container_time_init( VC_CONTAINER_TIME_T *time, uint32_t time_base ) { time->samplerate_num = 0; time->samplerate_den = 0; time->remainder = 0; time->time_base = time_base; time->time = VC_CONTAINER_TIME_UNKNOWN; } /*****************************************************************************/ STATIC_INLINE int64_t vc_container_time_get( VC_CONTAINER_TIME_T *time ) { if (time->time == VC_CONTAINER_TIME_UNKNOWN || !time->samplerate_num || !time->samplerate_den) return VC_CONTAINER_TIME_UNKNOWN; return time->time + time->remainder * (int64_t)time->time_base * time->samplerate_den / time->samplerate_num; } /*****************************************************************************/ STATIC_INLINE void vc_container_time_set_samplerate( VC_CONTAINER_TIME_T *time, uint32_t samplerate_num, uint32_t samplerate_den ) { if(time->samplerate_num == samplerate_num && time->samplerate_den == samplerate_den) return; /* We're changing samplerate, we need to reset our remainder */ if(time->remainder) time->time = vc_container_time_get( time ); time->remainder = 0; time->samplerate_num = samplerate_num; time->samplerate_den = samplerate_den; } /*****************************************************************************/ STATIC_INLINE void vc_container_time_set( VC_CONTAINER_TIME_T *time, int64_t new_time ) { if (new_time == VC_CONTAINER_TIME_UNKNOWN) return; time->remainder = 0; time->time = new_time; } /*****************************************************************************/ STATIC_INLINE int64_t vc_container_time_add( VC_CONTAINER_TIME_T *time, uint32_t samples ) { uint32_t increment; if (time->time == VC_CONTAINER_TIME_UNKNOWN || !time->samplerate_num || !time->samplerate_den) return VC_CONTAINER_TIME_UNKNOWN; samples += time->remainder; increment = samples * time->samplerate_den / time->samplerate_num; time->time += increment * time->time_base; time->remainder = samples - increment * time->samplerate_num / time->samplerate_den; return vc_container_time_get(time); } #endif /* VC_CONTAINERS_TIME_H */ userland/containers/core/containers_uri.c000066400000000000000000000757461421703157200212050ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include "containers/core/containers_uri.h" /*****************************************************************************/ /* Internal types and definitions */ /*****************************************************************************/ typedef struct VC_URI_QUERY_T { char *name; char *value; } VC_URI_QUERY_T; struct VC_URI_PARTS_T { char *scheme; /**< Unescaped scheme */ char *userinfo; /**< Unescaped userinfo */ char *host; /**< Unescaped host name/IP address */ char *port; /**< Unescaped port */ char *path; /**< Unescaped path */ char *path_extension; /**< Unescaped path extension */ char *fragment; /**< Unescaped fragment */ VC_URI_QUERY_T *queries; /**< Array of queries */ uint32_t num_queries; /**< Number of queries in array */ }; typedef const uint32_t *RESERVED_CHARS_TABLE_T; /** Reserved character table for scheme component * Controls, space, !"#$%&'()*,/:;<=>?@[\]^`{|} and 0x7F and above reserved. */ static uint32_t scheme_reserved_chars[8] = { 0xFFFFFFFF, 0xFC0097FF, 0x78000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; /** Reserved character table for userinfo component * Controls, space, "#%/<>?@[\]^`{|} and 0x7F and above reserved. */ static uint32_t userinfo_reserved_chars[8] = { 0xFFFFFFFF, 0xD000802D, 0x78000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; /** Reserved character table for host component * Controls, space, "#%/<>?@\^`{|} and 0x7F and above reserved. */ static uint32_t host_reserved_chars[8] = { 0xFFFFFFFF, 0xD000802D, 0x50000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; /** Reserved character table for port component * Controls, space, !"#$%&'()*+,/:;<=>?@[\]^`{|} and 0x7F and above reserved. */ static uint32_t port_reserved_chars[8] = { 0xFFFFFFFF, 0xFC009FFF, 0x78000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; /** Reserved character table for path component * Controls, space, "#%<>?[\]^`{|} and 0x7F and above reserved. */ static uint32_t path_reserved_chars[8] = { 0xFFFFFFFF, 0xD000002D, 0x78000000, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; /** Reserved character table for query component * Controls, space, "#%<>[\]^`{|} and 0x7F and above reserved. */ static uint32_t query_reserved_chars[8] = { 0xFFFFFFFF, 0x5000002D, 0x78000000, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; /** Reserved character table for fragment component * Controls, space, "#%<>[\]^`{|} and 0x7F and above reserved. */ static uint32_t fragment_reserved_chars[8] = { 0xFFFFFFFF, 0x5000002D, 0x78000000, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; #define URI_RESERVED(C, TABLE) (!!((TABLE)[(unsigned char)(C) >> 5] & (1 << ((C) & 0x1F)))) #define SCHEME_DELIMITERS ":/?#" #define NETWORK_DELIMITERS "@/?#" #define HOST_PORT_DELIMITERS "/?#" #define PATH_DELIMITERS "?#" #define QUERY_DELIMITERS "#" /*****************************************************************************/ /* Internal functions */ /*****************************************************************************/ static char to_hex(int v) { if (v > 9) return 'A' + v - 10; return '0' + v; } /*****************************************************************************/ static uint32_t from_hex(const char *str, uint32_t str_len) { uint32_t val = 0; while (str_len--) { char c = *str++; if (c >= '0' && c <= '9') c -= '0'; else if (c >= 'A' && c <= 'F') c -= 'A' - 10; else if (c >= 'a' && c <= 'f') c -= 'a' - 10; else c = 0; /* Illegal character (not hex) */ val = (val << 4) + c; } return val; } /*****************************************************************************/ static uint32_t escaped_length( const char *str, RESERVED_CHARS_TABLE_T reserved ) { uint32_t ii; uint32_t esclen = 0; char c; for (ii = strlen(str); ii > 0; ii--) { c = *str++; if (URI_RESERVED(c, reserved)) { /* Reserved character needs escaping as %xx */ esclen += 3; } else { esclen++; } } return esclen; } /*****************************************************************************/ static uint32_t escape_string( const char *str, char *escaped, RESERVED_CHARS_TABLE_T reserved ) { uint32_t ii; uint32_t esclen = 0; if (!str) return 0; for (ii = strlen(str); ii > 0; ii--) { char c = *str++; if (URI_RESERVED(c, reserved)) { escaped[esclen++] = '%'; escaped[esclen++] = to_hex((c >> 4) & 0xF); escaped[esclen++] = to_hex(c & 0xF); } else { escaped[esclen++] = c; } } return esclen; } /*****************************************************************************/ static uint32_t unescaped_length( const char *str, uint32_t str_len ) { uint32_t ii; uint32_t unesclen = 0; for (ii = 0; ii < str_len; ii++) { if (*str++ == '%' && (ii + 2) < str_len) { str += 2; /* Should be two hex values next */ ii += 2; } unesclen++; } return unesclen; } /*****************************************************************************/ static void unescape_string( const char *str, uint32_t str_len, char *unescaped ) { uint32_t ii; for (ii = 0; ii < str_len; ii++) { char c = *str++; if (c == '%' && (ii + 2) < str_len ) { c = (char)(from_hex(str, 2) & 0xFF); str += 2; ii += 2; } *unescaped++ = c; } *unescaped = '\0'; } /*****************************************************************************/ static char *create_unescaped_string( const char *escstr, uint32_t esclen ) { char *unescstr; unescstr = (char *)malloc(unescaped_length(escstr, esclen) + 1); /* Allow for NUL */ if (unescstr) unescape_string(escstr, esclen, unescstr); return unescstr; } /*****************************************************************************/ static bool duplicate_string( const char *src, char **p_dst ) { if (*p_dst) free(*p_dst); if (src) { size_t str_size = strlen(src) + 1; *p_dst = (char *)malloc(str_size); if (!*p_dst) return false; memcpy(*p_dst, src, str_size); } else *p_dst = NULL; return true; } /*****************************************************************************/ static void release_string( char **str ) { if (*str) { free(*str); *str = NULL; } } /*****************************************************************************/ static void to_lower_string( char *str ) { char c; while ((c = *str) != '\0') { if (c >= 'A' && c <= 'Z') *str = c - 'A' + 'a'; str++; } } /*****************************************************************************/ static const char *vc_uri_find_delimiter(const char *str, const char *delimiters) { const char *ptr = str; char c; while ((c = *ptr) != 0) { if (strchr(delimiters, c) != 0) break; ptr++; } return ptr; } /*****************************************************************************/ static void vc_uri_set_path_extension(VC_URI_PARTS_T *p_uri) { char *end; if (!p_uri) return; p_uri->path_extension = NULL; if (!p_uri->path) return; /* Look for the magic dot */ for (end = p_uri->path + strlen(p_uri->path); *end != '.'; end--) if (end == p_uri->path || *end == '/' || *end == '\\') return; p_uri->path_extension = end + 1; } /*****************************************************************************/ static bool parse_authority( VC_URI_PARTS_T *p_uri, const char *str, uint32_t str_len, const char *userinfo_end ) { const char *marker = userinfo_end; const char *str_end = str + str_len; char c; if (marker) { p_uri->userinfo = create_unescaped_string(str, marker - str); if (!p_uri->userinfo) return false; str = marker + 1; /* Past '@' character */ } if (*str == '[') /* IPvFuture / IPv6 address */ { /* Find end of address marker */ for (marker = str; marker < str_end; marker++) { c = *marker; if (c == ']') break; } if (marker < str_end) marker++; /* Found marker, move to next character */ } else { /* Find port value marker*/ for (marker = str; marker < str_end; marker++) { c = *marker; if (c == ':') break; } } /* Always store the host, even if empty, to trigger the "://" form of URI */ p_uri->host = create_unescaped_string(str, marker - str); if (!p_uri->host) return false; to_lower_string(p_uri->host); /* Host names are case-insensitive */ if (*marker == ':') { str = marker + 1; p_uri->port = create_unescaped_string(str, str_end - str); if (!p_uri->port) return false; } return true; } /*****************************************************************************/ static bool store_query( VC_URI_PARTS_T *p_uri, const char *name_start, const char *equals_ptr, const char *query_end) { uint32_t name_len, value_len; if (equals_ptr) { name_len = equals_ptr - name_start; value_len = query_end - equals_ptr - 1; /* Don't include '=' itself */ } else { name_len = query_end - name_start; value_len = 0; } /* Only store something if there is a name */ if (name_len) { char *name, *value = NULL; VC_URI_QUERY_T *p_query; if (equals_ptr) { value = create_unescaped_string(equals_ptr + 1, value_len); if (!value) return false; equals_ptr = query_end; } name = create_unescaped_string(name_start, name_len); if (!name) { if (value) free(value); return false; } /* Store query data in URI structure */ p_query = &p_uri->queries[ p_uri->num_queries++ ]; p_query->name = name; p_query->value = value; } return true; } /*****************************************************************************/ static bool parse_query( VC_URI_PARTS_T *p_uri, const char *str, uint32_t str_len ) { uint32_t ii; uint32_t query_count; VC_URI_QUERY_T *queries; const char *name_start = str; const char *equals_ptr = NULL; char c; if (!str_len) return true; /* Scan for the number of query items, so array can be allocated the right size */ query_count = 1; /* At least */ for (ii = 0; ii < str_len; ii++) { c = str[ii]; if (c == '&' || c ==';') query_count++; } queries = (VC_URI_QUERY_T *)malloc(query_count * sizeof(VC_URI_QUERY_T)); if (!queries) return false; p_uri->queries = queries; /* Go back and parse the string for each query item and store in array */ for (ii = 0; ii < str_len; ii++) { c = *str; /* Take first '=' as break between name and value */ if (c == '=' && !equals_ptr) equals_ptr = str; /* If at the end of the name or name/value pair */ if (c == '&' || c ==';') { if (!store_query(p_uri, name_start, equals_ptr, str)) return false; equals_ptr = NULL; name_start = str + 1; } str++; } return store_query(p_uri, name_start, equals_ptr, str); } /*****************************************************************************/ static uint32_t calculate_uri_length(const VC_URI_PARTS_T *p_uri) { uint32_t length = 0; uint32_t count; /* With no scheme, assume this is a plain path (without escaping) */ if (!p_uri->scheme) return p_uri->path ? strlen(p_uri->path) : 0; length += escaped_length(p_uri->scheme, scheme_reserved_chars); length++; /* for the colon */ if (p_uri->host) { length += escaped_length(p_uri->host, host_reserved_chars) + 2; /* for the double slash */ if (p_uri->userinfo) length += escaped_length(p_uri->userinfo, userinfo_reserved_chars) + 1; /* for the '@' */ if (p_uri->port) length += escaped_length(p_uri->port, port_reserved_chars) + 1; /* for the ':' */ } if (p_uri->path) length += escaped_length(p_uri->path, path_reserved_chars); count = p_uri->num_queries; if (count) { VC_URI_QUERY_T * queries = p_uri->queries; while (count--) { /* The name is preceded by either the '?' or the '&' */ length += escaped_length(queries->name, query_reserved_chars) + 1; /* The value is optional, but if present will require an '=' */ if (queries->value) length += escaped_length(queries->value, query_reserved_chars) + 1; queries++; } } if (p_uri->fragment) length += escaped_length(p_uri->fragment, fragment_reserved_chars) + 1; /* for the '#' */ return length; } /*****************************************************************************/ static void build_uri(const VC_URI_PARTS_T *p_uri, char *buffer, size_t buffer_size) { uint32_t count; /* With no scheme, assume this is a plain path (without escaping) */ if (!p_uri->scheme) { if (p_uri->path) strncpy(buffer, p_uri->path, buffer_size); else buffer[0] = '\0'; return; } buffer += escape_string(p_uri->scheme, buffer, scheme_reserved_chars); *buffer++ = ':'; if (p_uri->host) { *buffer++ = '/'; *buffer++ = '/'; if (p_uri->userinfo) { buffer += escape_string(p_uri->userinfo, buffer, userinfo_reserved_chars); *buffer++ = '@'; } buffer += escape_string(p_uri->host, buffer, host_reserved_chars); if (p_uri->port) { *buffer++ = ':'; buffer += escape_string(p_uri->port, buffer, port_reserved_chars); } } if (p_uri->path) buffer += escape_string(p_uri->path, buffer, path_reserved_chars); count = p_uri->num_queries; if (count) { VC_URI_QUERY_T * queries = p_uri->queries; *buffer++ = '?'; while (count--) { buffer += escape_string(queries->name, buffer, query_reserved_chars); if (queries->value) { *buffer++ = '='; buffer += escape_string(queries->value, buffer, query_reserved_chars); } /* Add separator if there is another item to add */ if (count) *buffer++ = '&'; queries++; } } if (p_uri->fragment) { *buffer++ = '#'; buffer += escape_string(p_uri->fragment, buffer, fragment_reserved_chars); } *buffer = '\0'; } /*****************************************************************************/ static bool vc_uri_copy_base_path( const VC_URI_PARTS_T *base_uri, VC_URI_PARTS_T *relative_uri ) { const char *base_path = vc_uri_path(base_uri); /* No path set (or empty), copy from base */ if (!vc_uri_set_path(relative_uri, base_path)) return false; /* If relative path has no queries, copy base queries across */ if (!vc_uri_num_queries(relative_uri)) { uint32_t base_queries = vc_uri_num_queries(base_uri); const char *name, *value; uint32_t ii; for (ii = 0; ii < base_queries; ii++) { vc_uri_query(base_uri, ii, &name, &value); if (!vc_uri_add_query(relative_uri, name, value)) return false; } } return true; } /*****************************************************************************/ static void vc_uri_remove_single_dot_segments( char *path_str ) { char *slash = path_str - 1; while (slash++) { if (*slash == '.') { switch (slash[1]) { case '/': /* Single dot segment, remove it */ memmove(slash, slash + 2, strlen(slash + 2) + 1); break; case '\0': /* Trailing single dot, remove it */ *slash = '\0'; break; default: /* Something else (e.g. ".." or ".foo") */ ; /* Do nothing */ } } slash = strchr(slash, '/'); } } /*****************************************************************************/ static void vc_uri_remove_double_dot_segments( char *path_str ) { char *previous_segment = path_str; char *slash; if (previous_segment[0] == '/') previous_segment++; /* Remove strings of the form "/../" (or "/.." at the end of the path) * as long as is not itself ".." */ slash = strchr(previous_segment, '/'); while (slash) { if (previous_segment[0] != '.' || previous_segment[1] != '.' || previous_segment[2] != '/') { if (slash[1] == '.' && slash[2] == '.') { bool previous_segment_removed = true; switch (slash[3]) { case '/': /* "/../" inside path, snip it and last segment out */ memmove(previous_segment, slash + 4, strlen(slash + 4) + 1); break; case '\0': /* Trailing "/.." on path, just terminate path at last segment */ *previous_segment = '\0'; break; default: /* Not a simple ".." segment, so skip over it */ previous_segment_removed = false; } if (previous_segment_removed) { /* The segment just removed was the first one in the path (optionally * prefixed by a slash), so no more can be removed: stop. */ if (previous_segment < path_str + 2) break; /* Move back to slash before previous segment, or the start of the path */ slash = previous_segment - 1; while (--slash >= path_str && *slash != '/') ; /* Everything done in the while */ } } } previous_segment = slash + 1; slash = strchr(previous_segment, '/'); } } /*****************************************************************************/ /* API functions */ /*****************************************************************************/ VC_URI_PARTS_T *vc_uri_create( void ) { VC_URI_PARTS_T *p_uri; p_uri = (VC_URI_PARTS_T *)malloc(sizeof(VC_URI_PARTS_T)); if (p_uri) { memset(p_uri, 0, sizeof(VC_URI_PARTS_T)); } return p_uri; } /*****************************************************************************/ void vc_uri_clear( VC_URI_PARTS_T *p_uri ) { if (!p_uri) return; release_string(&p_uri->scheme); release_string(&p_uri->userinfo); release_string(&p_uri->host); release_string(&p_uri->port); release_string(&p_uri->path); release_string(&p_uri->fragment); if (p_uri->queries) { VC_URI_QUERY_T *queries = p_uri->queries; uint32_t count = p_uri->num_queries; while (count--) { release_string(&queries[count].name); release_string(&queries[count].value); } free(queries); p_uri->queries = NULL; p_uri->num_queries = 0; } } /*****************************************************************************/ void vc_uri_release( VC_URI_PARTS_T *p_uri ) { if (!p_uri) return; vc_uri_clear(p_uri); free(p_uri); } /*****************************************************************************/ bool vc_uri_parse( VC_URI_PARTS_T *p_uri, const char *uri ) { const char *marker; uint32_t len; if (!p_uri || !uri) return false; vc_uri_clear(p_uri); /* URI = scheme ":" hier_part [ "?" query ] [ "#" fragment ] */ /* Find end of scheme, or another separator */ marker = vc_uri_find_delimiter(uri, SCHEME_DELIMITERS); if (*marker == ':') { len = (marker - uri); if (isalpha((int)*uri) && len == 1 && marker[1] == '\\') { /* Looks like a bare, absolute DOS/Windows filename with a drive letter */ /* coverity[double_free] Pointer freed and set to NULL */ bool ret = duplicate_string(uri, &p_uri->path); vc_uri_set_path_extension(p_uri); return ret; } p_uri->scheme = create_unescaped_string(uri, len); if (!p_uri->scheme) goto error; to_lower_string(p_uri->scheme); /* Schemes should be handled case-insensitively */ uri = marker + 1; } if (uri[0] == '/' && uri[1] == '/') /* hier-part includes authority */ { const char *userinfo_end = NULL; /* authority = [ userinfo "@" ] host [ ":" port ] */ uri += 2; marker = vc_uri_find_delimiter(uri, NETWORK_DELIMITERS); if (*marker == '@') { userinfo_end = marker; marker = vc_uri_find_delimiter(marker + 1, HOST_PORT_DELIMITERS); } if (!parse_authority(p_uri, uri, marker - uri, userinfo_end)) goto error; uri = marker; } /* path */ marker = vc_uri_find_delimiter(uri, PATH_DELIMITERS); len = marker - uri; if (len) { p_uri->path = create_unescaped_string(uri, len); vc_uri_set_path_extension(p_uri); if (!p_uri->path) goto error; } /* query */ if (*marker == '?') { uri = marker + 1; marker = vc_uri_find_delimiter(uri, QUERY_DELIMITERS); if (!parse_query(p_uri, uri, marker - uri)) goto error; } /* fragment */ if (*marker == '#') { uri = marker + 1; p_uri->fragment = create_unescaped_string(uri, strlen(uri)); if (!p_uri->fragment) goto error; } return true; error: vc_uri_clear(p_uri); return false; } /*****************************************************************************/ uint32_t vc_uri_build( const VC_URI_PARTS_T *p_uri, char *buffer, size_t buffer_size ) { uint32_t required_length; if (!p_uri) return 0; required_length = calculate_uri_length(p_uri); if (buffer && required_length < buffer_size) /* Allow for NUL */ build_uri(p_uri, buffer, buffer_size); return required_length; } /*****************************************************************************/ const char *vc_uri_scheme( const VC_URI_PARTS_T *p_uri ) { return p_uri ? p_uri->scheme : NULL; } /*****************************************************************************/ const char *vc_uri_userinfo( const VC_URI_PARTS_T *p_uri ) { return p_uri ? p_uri->userinfo : NULL; } /*****************************************************************************/ const char *vc_uri_host( const VC_URI_PARTS_T *p_uri ) { return p_uri ? p_uri->host : NULL; } /*****************************************************************************/ const char *vc_uri_port( const VC_URI_PARTS_T *p_uri ) { return p_uri ? p_uri->port : NULL; } /*****************************************************************************/ const char *vc_uri_path( const VC_URI_PARTS_T *p_uri ) { return p_uri ? p_uri->path : NULL; } /*****************************************************************************/ const char *vc_uri_path_extension( const VC_URI_PARTS_T *p_uri ) { return p_uri ? p_uri->path_extension : NULL; } /*****************************************************************************/ const char *vc_uri_fragment( const VC_URI_PARTS_T *p_uri ) { return p_uri ? p_uri->fragment : NULL; } /*****************************************************************************/ uint32_t vc_uri_num_queries( const VC_URI_PARTS_T *p_uri ) { return p_uri ? p_uri->num_queries : 0; } /*****************************************************************************/ void vc_uri_query( const VC_URI_PARTS_T *p_uri, uint32_t index, const char **p_name, const char **p_value ) { const char *name = NULL; const char *value = NULL; if (p_uri) { if (index < p_uri->num_queries) { name = p_uri->queries[index].name; value = p_uri->queries[index].value; } } if (p_name) *p_name = name; if (p_value) *p_value = value; } /*****************************************************************************/ bool vc_uri_find_query( VC_URI_PARTS_T *p_uri, uint32_t *p_index, const char *name, const char **p_value ) { unsigned int i = p_index ? *p_index : 0; if (!p_uri) return false; for (; name && i < p_uri->num_queries; i++) { if (!strcmp(name, p_uri->queries[i].name)) { if (p_value) *p_value = p_uri->queries[i].value; if (p_index) *p_index = i; return true; } } return false; } /*****************************************************************************/ bool vc_uri_set_scheme( VC_URI_PARTS_T *p_uri, const char *scheme ) { return p_uri ? duplicate_string(scheme, &p_uri->scheme) : false; } /*****************************************************************************/ bool vc_uri_set_userinfo( VC_URI_PARTS_T *p_uri, const char *userinfo ) { return p_uri ? duplicate_string(userinfo, &p_uri->userinfo) : false; } /*****************************************************************************/ bool vc_uri_set_host( VC_URI_PARTS_T *p_uri, const char *host ) { return p_uri ? duplicate_string(host, &p_uri->host) : false; } /*****************************************************************************/ bool vc_uri_set_port( VC_URI_PARTS_T *p_uri, const char *port ) { return p_uri ? duplicate_string(port, &p_uri->port) : false; } /*****************************************************************************/ bool vc_uri_set_path( VC_URI_PARTS_T *p_uri, const char *path ) { bool ret = p_uri ? duplicate_string(path, &p_uri->path) : false; vc_uri_set_path_extension(p_uri); return ret; } /*****************************************************************************/ bool vc_uri_set_fragment( VC_URI_PARTS_T *p_uri, const char *fragment ) { return p_uri ? duplicate_string(fragment, &p_uri->fragment) : false; } /*****************************************************************************/ bool vc_uri_add_query( VC_URI_PARTS_T *p_uri, const char *name, const char *value ) { VC_URI_QUERY_T *queries; uint32_t count; if (!p_uri || !name) return false; count = p_uri->num_queries; if (p_uri->queries) queries = (VC_URI_QUERY_T *)realloc(p_uri->queries, (count + 1) * sizeof(VC_URI_QUERY_T)); else queries = (VC_URI_QUERY_T *)malloc(sizeof(VC_URI_QUERY_T)); if (!queries) return false; /* Always store the pointer, in case it has changed, and even if we fail to copy name/value */ p_uri->queries = queries; queries[count].name = NULL; queries[count].value = NULL; if (duplicate_string(name, &queries[count].name)) { if (duplicate_string(value, &queries[count].value)) { /* Successful exit path */ p_uri->num_queries++; return true; } release_string(&queries[count].name); } return false; } /*****************************************************************************/ bool vc_uri_merge( const VC_URI_PARTS_T *base_uri, VC_URI_PARTS_T *relative_uri ) { bool success = true; const char *relative_path; /* If scheme is already set, the URI is already absolute */ if (relative_uri->scheme) return true; /* Otherwise, copy the base scheme */ if (!duplicate_string(base_uri->scheme, &relative_uri->scheme)) return false; /* If any of the network info is set, use the rest of the relative URI as-is */ if (relative_uri->host || relative_uri->port || relative_uri->userinfo) return true; /* Otherwise, copy the base network info */ if (!duplicate_string(base_uri->host, &relative_uri->host) || !duplicate_string(base_uri->port, &relative_uri->port) || !duplicate_string(base_uri->userinfo, &relative_uri->userinfo)) return false; relative_path = relative_uri->path; if (!relative_path || !*relative_path) { /* No relative path (could be queries and/or fragment), so take base path */ success = vc_uri_copy_base_path(base_uri, relative_uri); } else if (*relative_path != '/') { const char *base_path = base_uri->path; char *merged_path; char *slash; size_t len; /* Path is relative, merge in with base path */ if (!base_path || !*base_path) { if (relative_uri->host || relative_uri->port || relative_uri->userinfo) base_path = "/"; /* Need a separator to split network info from path */ else base_path = ""; } len = strlen(base_path) + strlen(relative_path) + 1; /* Allocate space for largest possible combined path */ merged_path = (char *)malloc(len); if (!merged_path) return false; strncpy(merged_path, base_path, len); slash = strrchr(merged_path, '/'); /* Note: reverse search */ if (*relative_path == ';') { char *semi; /* Relative path is just parameters, so remove any base parameters in final segment */ if (!slash) slash = merged_path; semi = strchr(slash, ';'); if (semi) semi[0] = '\0'; } else { /* Remove final segment */ if (slash) slash[1] = '\0'; else merged_path[0] = '\0'; } strncat(merged_path, relative_path, len - strlen(merged_path) - 1); vc_uri_remove_single_dot_segments(merged_path); vc_uri_remove_double_dot_segments(merged_path); success = duplicate_string(merged_path, &relative_uri->path); free(merged_path); } /* Otherwise path is absolute, which can be left as-is */ return success; } userland/containers/core/containers_uri.h000066400000000000000000000236541421703157200212010ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_CONTAINERS_URI_H #define VC_CONTAINERS_URI_H /** \file containers_uri.h * API for parsing and building URI strings as described in RFC3986. */ #ifdef __cplusplus extern "C" { #endif #include "containers/containers.h" typedef struct VC_URI_PARTS_T VC_URI_PARTS_T; /** Create an empty URI structure. * * \return The new URI structure. */ VC_URI_PARTS_T *vc_uri_create( void ); /** Destroy a URI structure. * * \param p_uri Pointer to a URI parts structure. */ void vc_uri_release( VC_URI_PARTS_T *p_uri ); /** Clear a URI structure. * Any URI component strings held are released, but the structure itself is not. * * \param p_uri Pointer to a URI parts structure. */ void vc_uri_clear( VC_URI_PARTS_T *p_uri ); /** Parses and unescapes a URI into the component parts. * * \param p_uri Pointer to a URI parts structure. * \param uri Pointer to a URI string to be parsed. * \return True if successful, false if not. */ bool vc_uri_parse( VC_URI_PARTS_T *p_uri, const char *uri ); /** Builds the URI component parts into a URI string. * If buffer is NULL, or buffer_size is too small, nothing is written to the * buffer but the required string length is still returned. buffer_size must be * at least one more than the value returned. * * \param p_uri Pointer to a URI parts structure. * \param buffer Pointer to where the URI string is to be built, or NULL. * \param buffer_size Number of bytes available in the buffer. * \return The length of the URI string. */ uint32_t vc_uri_build( const VC_URI_PARTS_T *p_uri, char *buffer, size_t buffer_size ); /** Retrieves the scheme of the URI. * The string is valid until either the scheme is changed or the URI is released. * * \param p_uri The parsed URI. * \return Pointer to the scheme string. */ const char *vc_uri_scheme( const VC_URI_PARTS_T *p_uri ); /** Retrieves the userinfo of the URI. * The string is valid until either the userinfo is changed or the URI is released. * * \param p_uri The parsed URI. * \return Pointer to the userinfo string. */ const char *vc_uri_userinfo( const VC_URI_PARTS_T *p_uri ); /** Retrieves the host of the URI. * The string is valid until either the host is changed or the URI is released. * * \param p_uri The parsed URI. * \return Pointer to the host string. */ const char *vc_uri_host( const VC_URI_PARTS_T *p_uri ); /** Retrieves the port of the URI. * The string is valid until either the port is changed or the URI is released. * * \param p_uri The parsed URI. * \return Pointer to the port string. */ const char *vc_uri_port( const VC_URI_PARTS_T *p_uri ); /** Retrieves the path of the URI. * The string is valid until either the path is changed or the URI is released. * * \param p_uri The parsed URI. * \return Pointer to the path string. */ const char *vc_uri_path( const VC_URI_PARTS_T *p_uri ); /** Retrieves the extension part of the path of the URI. * The string is valid until either the path is changed or the URI is released. * * \param p_uri The parsed URI. * \return Pointer to the extension string. */ const char *vc_uri_path_extension( const VC_URI_PARTS_T *p_uri ); /** Retrieves the fragment of the URI. * The string is valid until either the fragment is changed or the URI is released. * * \param p_uri The parsed URI. * \return Pointer to the fragment string. */ const char *vc_uri_fragment( const VC_URI_PARTS_T *p_uri ); /** Returns the number of query name/value pairs stored. * * \param p_uri The parsed URI. * \return Number of queries stored. */ uint32_t vc_uri_num_queries( const VC_URI_PARTS_T *p_uri ); /** Retrieves a given query's name and value * If either p_name or p_value are NULL, that part of the query is not returned, * otherwise it is set to the address of the string (which may itself be NULL). * * \param p_uri The parsed URI. * \param index Selects the query to get. * \param p_name Address of a string pointer to receive query name, or NULL. * \param p_value Address of a string pointer to receive query value, or NULL. */ void vc_uri_query( const VC_URI_PARTS_T *p_uri, uint32_t index, const char **p_name, const char **p_value ); /** Finds a specific query in the array. * If p_index is NULL, then it is assumed the search should start at index 0, * otherwise the search will start at the specified index and the index will * be updated on return to point to the query which has been found. * If p_value is NULL, that part of the query is not returned, * otherwise it is set to the address of the value string (which may itself be NULL). * * \param p_uri Pointer to a URI parts structure. * \param p_index Index from which to start the search. May be NULL. * \param name Unescaped query name. * \param value Unescaped query value. May be NULL. * \return True if successful, false if not. */ bool vc_uri_find_query( VC_URI_PARTS_T *p_uri, uint32_t *p_index, const char *name, const char **p_value ); /** Sets the scheme of the URI. * The string will be copied and stored in the URI, releasing and replacing * any existing string. If NULL is passed, any existing string shall simply be * released. * * \param p_uri The parsed URI. * \param scheme Pointer to the new scheme string, or NULL. * \return True if successful, false on memory allocation failure. */ bool vc_uri_set_scheme( VC_URI_PARTS_T *p_uri, const char *scheme ); /** Sets the userinfo of the URI. * The string will be copied and stored in the URI, releasing and replacing * any existing string. If NULL is passed, any existing string shall simply be * released. * * \param p_uri The parsed URI. * \param userinfo Pointer to the new userinfo string, or NULL. * \return True if successful, false on memory allocation failure. */ bool vc_uri_set_userinfo( VC_URI_PARTS_T *p_uri, const char *userinfo ); /** Sets the host of the URI. * The string will be copied and stored in the URI, releasing and replacing * any existing string. If NULL is passed, any existing string shall simply be * released. * * \param p_uri The parsed URI. * \param host Pointer to the new host string, or NULL. * \return True if successful, false on memory allocation failure. */ bool vc_uri_set_host( VC_URI_PARTS_T *p_uri, const char *host ); /** Sets the port of the URI. * The string will be copied and stored in the URI, releasing and replacing * any existing string. If NULL is passed, any existing string shall simply be * released. * * \param p_uri The parsed URI. * \param port Pointer to the new port string, or NULL. * \return True if successful, false on memory allocation failure. */ bool vc_uri_set_port( VC_URI_PARTS_T *p_uri, const char *port ); /** Sets the path of the URI. * The string will be copied and stored in the URI, releasing and replacing * any existing string. If NULL is passed, any existing string shall simply be * released. * * \param p_uri The parsed URI. * \param path Pointer to the new path string, or NULL. * \return True if successful, false on memory allocation failure. */ bool vc_uri_set_path( VC_URI_PARTS_T *p_uri, const char *path ); /** Sets the fragment of the URI. * The string will be copied and stored in the URI, releasing and replacing * any existing string. If NULL is passed, any existing string shall simply be * released. * * \param p_uri The parsed URI. * \param fragment Pointer to the new fragment string, or NULL. * \return True if successful, false on memory allocation failure. */ bool vc_uri_set_fragment( VC_URI_PARTS_T *p_uri, const char *fragment ); /** Adds an query to the array. * Note that the queries pointer may change after this function is called. * May fail due to memory allocation failure or invalid parameters. * * \param p_uri Pointer to a URI parts structure. * \param name Unescaped query name. * \param value Unescaped query value. May be NULL. * \return True if successful, false if not. */ bool vc_uri_add_query( VC_URI_PARTS_T *p_uri, const char *name, const char *value ); /** Merge a base URI and a relative URI. * In general, where the relative URI does not have a given element, the * corresponding element from the base URI is used. See RFC1808. * The combined URI is left in relative_uri. If the function is unsuccessful, * the relative_uri may have been partially modified. * * \param base_uri Pointer to the base URI parts structure. * \param relative_uri Pointer to the relative URI parts structure. * \return True if successful, false if not. */ bool vc_uri_merge( const VC_URI_PARTS_T *base_uri, VC_URI_PARTS_T *relative_uri ); #ifdef __cplusplus } #endif #endif /* VC_CONTAINERS_URI_H */ userland/containers/core/containers_utils.c000066400000000000000000000323021421703157200215230ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include "containers/containers.h" #include "containers/core/containers_common.h" #include "containers/core/containers_utils.h" /****************************************************************************** Defines. ******************************************************************************/ #define BITMAPINFOHEADER_SIZE_MAX 40 #define MAX_EXTENSION_SIZE 4 #define VC_CONTAINER_ES_FORMAT_MAGIC ((uint32_t)VC_FOURCC('m','a','g','f')) #define EXTRADATA_SIZE_DEFAULT 32 #define EXTRADATA_SIZE_MAX (10*1024) /*****************************************************************************/ typedef struct VC_CONTAINER_ES_FORMAT_PRIVATE_T { VC_CONTAINER_ES_FORMAT_T format; VC_CONTAINER_ES_SPECIFIC_FORMAT_T type; uint32_t magic; unsigned int extradata_size; uint8_t *extradata; uint8_t buffer[EXTRADATA_SIZE_DEFAULT]; } VC_CONTAINER_ES_FORMAT_PRIVATE_T; /*****************************************************************************/ VC_CONTAINER_ES_FORMAT_T *vc_container_format_create(unsigned int extradata_size) { VC_CONTAINER_ES_FORMAT_PRIVATE_T *private; VC_CONTAINER_STATUS_T status; private = malloc(sizeof(*private)); if(!private) return 0; memset(private, 0, sizeof(*private)); private->magic = VC_CONTAINER_ES_FORMAT_MAGIC; private->format.type = (void *)&private->type; private->extradata_size = EXTRADATA_SIZE_DEFAULT; status = vc_container_format_extradata_alloc(&private->format, extradata_size); if(status != VC_CONTAINER_SUCCESS) { free(private); return NULL; } return &private->format; } /*****************************************************************************/ void vc_container_format_delete(VC_CONTAINER_ES_FORMAT_T *format) { VC_CONTAINER_ES_FORMAT_PRIVATE_T *private = (VC_CONTAINER_ES_FORMAT_PRIVATE_T *)format; vc_container_assert(private->magic == VC_CONTAINER_ES_FORMAT_MAGIC); if(private->extradata) free(private->extradata); free(private); } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_format_extradata_alloc( VC_CONTAINER_ES_FORMAT_T *format, unsigned int size) { VC_CONTAINER_ES_FORMAT_PRIVATE_T *private = (VC_CONTAINER_ES_FORMAT_PRIVATE_T *)format; vc_container_assert(private->magic == VC_CONTAINER_ES_FORMAT_MAGIC); /* Sanity check the size requested */ if(size > EXTRADATA_SIZE_MAX) return VC_CONTAINER_ERROR_CORRUPTED; /* Allocate memory if needed */ if(private->extradata_size < size) { if(private->extradata) free(private->extradata); private->extradata = malloc(size); if(!private->extradata) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; private->extradata_size = size; } /* Set the fields in the actual format structure */ if(private->extradata) private->format.extradata = private->extradata; else private->format.extradata = private->buffer; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_format_copy( VC_CONTAINER_ES_FORMAT_T *p_out, VC_CONTAINER_ES_FORMAT_T *p_in, unsigned int extra_buffer_size) { void *type = p_out->type; uint8_t *extradata = p_out->extradata; /* Check we have a sufficient buffer to copy the extra data */ if(p_in->extradata_size > extra_buffer_size || (p_in->extradata_size && !p_out->extradata)) return VC_CONTAINER_ERROR_BUFFER_TOO_SMALL; *p_out->type = *p_in->type; *p_out = *p_in; p_out->type = type; p_out->extradata = extradata; if(p_in->extradata_size) memcpy(p_out->extradata, p_in->extradata, p_in->extradata_size); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ int utf8_from_charset(const char *charset, char *out, unsigned int out_size, const void *in, unsigned int in_size) { unsigned int i; const uint16_t *in16 = (const uint16_t *)in; const uint8_t *in8 = (const uint8_t *)in; if(out_size < 1) return 1; if(!strcmp(charset, "UTF16-LE")) goto utf16le; if(!strcmp(charset, "UTF8")) goto utf8; else return 1; utf16le: for(i = 0; i < in_size / 2 && in16[i] && i < out_size - 1; i++) { out[i] = in16[i]; } out[i] = 0; return 0; utf8: for(i = 0; i < in_size && in8[i] && i < out_size - 1; i++) { out[i] = in8[i]; } out[i] = 0; return 0; } /*****************************************************************************/ unsigned int vc_container_es_format_to_waveformatex(VC_CONTAINER_ES_FORMAT_T *format, uint8_t *buffer, unsigned int buffer_size) { uint16_t waveformat = codec_to_waveformat(format->codec); if(format->es_type != VC_CONTAINER_ES_TYPE_AUDIO || waveformat == WAVE_FORMAT_UNKNOWN) return 0; if(!buffer) return format->extradata_size + 18; if(buffer_size < format->extradata_size + 18) return 0; /* Build a waveformatex header */ buffer[0] = waveformat; buffer[1] = waveformat >> 8; buffer[2] = format->type->audio.channels; buffer[3] = 0; buffer[4] = (format->type->audio.sample_rate >> 0) & 0xFF; buffer[5] = (format->type->audio.sample_rate >> 8) & 0xFF; buffer[6] = (format->type->audio.sample_rate >> 16) & 0xFF; buffer[7] = (format->type->audio.sample_rate >> 24) & 0xFF; buffer[8] = (format->bitrate >> 3) & 0xFF; buffer[9] = (format->bitrate >> 11) & 0xFF; buffer[10] = (format->bitrate >> 19) & 0xFF; buffer[11] = (format->bitrate >> 27) & 0xFF; buffer[12] = (format->type->audio.block_align >> 0) & 0xFF; buffer[13] = (format->type->audio.block_align >> 8) & 0xFF; buffer[14] = (format->type->audio.bits_per_sample >> 0) & 0xFF; buffer[15] = (format->type->audio.bits_per_sample >> 8) & 0xFF; buffer[16] = (format->extradata_size >> 0) & 0xFF; buffer[17] = (format->extradata_size >> 8) & 0xFF; memcpy(buffer+18, format->extradata, format->extradata_size); return format->extradata_size + 18; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_waveformatex_to_es_format(uint8_t *p, unsigned int buffer_size, unsigned int *extra_offset, unsigned int *extra_size, VC_CONTAINER_ES_FORMAT_T *format) { VC_CONTAINER_FOURCC_T fourcc; uint32_t waveformat_id; if(!p || buffer_size < 16) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; waveformat_id = (p[1] << 8) | p[0]; fourcc = waveformat_to_codec(waveformat_id); /* Read the waveformatex header */ if(extra_offset) *extra_offset = 16; if(extra_size) *extra_size = 0; format->type->audio.channels = p[2]; format->type->audio.sample_rate = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4]; format->bitrate = ((p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8]) * 8; format->type->audio.block_align = (p[13] << 8) | p[12]; format->type->audio.bits_per_sample = (p[15] << 8) | p[14]; if(waveformat_id == WAVE_FORMAT_PCM && format->type->audio.bits_per_sample == 8) fourcc = VC_CONTAINER_CODEC_PCM_UNSIGNED_LE; if(buffer_size >= 18) { if(extra_size) { *extra_size = (p[17] << 8) | p[16]; if(*extra_size + 18 > buffer_size) *extra_size = buffer_size - 18; } if(extra_offset) *extra_offset = 18; } /* Skip the MPEGLAYER3WAVEFORMAT structure */ if(waveformat_id == WAVE_FORMAT_MPEGLAYER3 && extra_size) { if(extra_offset) *extra_offset += *extra_size; *extra_size = 0; } format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; format->codec = fourcc; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ unsigned int vc_container_es_format_to_bitmapinfoheader(VC_CONTAINER_ES_FORMAT_T *format, uint8_t *buffer, unsigned int buffer_size) { uint32_t fourcc = codec_to_vfw_fourcc(format->codec); uint32_t size = BITMAPINFOHEADER_SIZE_MAX + format->extradata_size; if(format->es_type != VC_CONTAINER_ES_TYPE_VIDEO || fourcc == VC_CONTAINER_CODEC_UNKNOWN) return 0; if(!buffer) return size; if(buffer_size < size) return 0; /* Build a bitmapinfoheader header */ memset(buffer, 0, BITMAPINFOHEADER_SIZE_MAX); buffer[0] = (size >> 0) & 0xFF; buffer[1] = (size >> 8) & 0xFF; buffer[2] = (size >> 16) & 0xFF; buffer[3] = (size >> 24) & 0xFF; buffer[4] = (format->type->video.width >> 0) & 0xFF; buffer[5] = (format->type->video.width >> 8) & 0xFF; buffer[6] = (format->type->video.width >> 16) & 0xFF; buffer[7] = (format->type->video.width >> 24) & 0xFF; buffer[8] = (format->type->video.height >> 0) & 0xFF; buffer[9] = (format->type->video.height >> 8) & 0xFF; buffer[10] = (format->type->video.height >> 16) & 0xFF; buffer[11] = (format->type->video.height >> 24) & 0xFF; memcpy(buffer + 16, &fourcc, 4); memcpy(buffer + BITMAPINFOHEADER_SIZE_MAX, format->extradata, format->extradata_size); return size; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_bitmapinfoheader_to_es_format(uint8_t *p, unsigned int buffer_size, unsigned int *extra_offset, unsigned int *extra_size, VC_CONTAINER_ES_FORMAT_T *format) { VC_CONTAINER_FOURCC_T fourcc; if(!p || buffer_size < BITMAPINFOHEADER_SIZE_MAX) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; /* size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]; */ format->type->video.width = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4]; format->type->video.height = (p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8]; memcpy(&fourcc, p + 16, 4); format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; format->codec = vfw_fourcc_to_codec(fourcc); /* If no mapping is found from vfw, try a more generic one */ if (format->codec == fourcc && (fourcc = fourcc_to_codec(fourcc)) != VC_CONTAINER_CODEC_UNKNOWN) format->codec = fourcc; if(extra_offset) *extra_offset = BITMAPINFOHEADER_SIZE_MAX; if(extra_size) { if (buffer_size > BITMAPINFOHEADER_SIZE_MAX) *extra_size = buffer_size - BITMAPINFOHEADER_SIZE_MAX; else *extra_size = 0; } return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static struct { VC_CONTAINER_METADATA_KEY_T key; const char *name; } meta_key_conv[] = { {VC_CONTAINER_METADATA_KEY_TITLE, "title"}, {VC_CONTAINER_METADATA_KEY_ARTIST, "artist"}, {VC_CONTAINER_METADATA_KEY_ALBUM, "album"}, {VC_CONTAINER_METADATA_KEY_DESCRIPTION, "description"}, {VC_CONTAINER_METADATA_KEY_YEAR, "year"}, {VC_CONTAINER_METADATA_KEY_GENRE, "genre"}, {VC_CONTAINER_METADATA_KEY_TRACK, "track"}, {VC_CONTAINER_METADATA_KEY_LYRICS, "lyrics"}, {VC_CONTAINER_METADATA_KEY_UNKNOWN, 0} }; /*****************************************************************************/ const char *vc_container_metadata_id_to_string(VC_CONTAINER_METADATA_KEY_T key) { int i; for(i = 0; meta_key_conv[i].key != VC_CONTAINER_METADATA_KEY_UNKNOWN; i++ ) if(meta_key_conv[i].key == key) break; return meta_key_conv[i].name; } /*****************************************************************************/ int64_t vc_container_maths_gcd(int64_t a, int64_t b) { while(b != 0) { int64_t t = b; b = a % b; a = t; } return a; } /*****************************************************************************/ void vc_container_maths_rational_simplify(uint32_t *num, uint32_t *den) { int64_t div = vc_container_maths_gcd((int64_t)*num, (int64_t)*den); if(div) { *num /= div; *den /= div; } } userland/containers/core/containers_utils.h000066400000000000000000000100401421703157200215230ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_CONTAINERS_UTILS_H #define VC_CONTAINERS_UTILS_H #include "containers/containers.h" #include "containers/containers_codecs.h" #include "containers/core/containers_waveformat.h" /***************************************************************************** * Type definitions *****************************************************************************/ /** Definition of the Global Unique Identifier type as used by some containers */ typedef struct GUID_T { uint32_t word0; uint16_t short0; uint16_t short1; uint8_t bytes[8]; } GUID_T; VC_CONTAINER_ES_FORMAT_T *vc_container_format_create(unsigned int extradata_size); void vc_container_format_delete(VC_CONTAINER_ES_FORMAT_T *); VC_CONTAINER_STATUS_T vc_container_format_extradata_alloc( VC_CONTAINER_ES_FORMAT_T *format, unsigned int size); VC_CONTAINER_STATUS_T vc_container_format_copy( VC_CONTAINER_ES_FORMAT_T *p_out, VC_CONTAINER_ES_FORMAT_T *p_in, unsigned int extra_buffer_size ); int utf8_from_charset(const char *charset, char *out, unsigned int out_size, const void *in, unsigned int in_size); const char *vc_container_metadata_id_to_string(VC_CONTAINER_METADATA_KEY_T key); unsigned int vc_container_es_format_to_waveformatex(VC_CONTAINER_ES_FORMAT_T *format, uint8_t *buffer, unsigned int buffer_size); unsigned int vc_container_es_format_to_bitmapinfoheader(VC_CONTAINER_ES_FORMAT_T *format, uint8_t *buffer, unsigned int buffer_size); VC_CONTAINER_STATUS_T vc_container_waveformatex_to_es_format(uint8_t *p, unsigned int buffer_size, unsigned int *extra_offset, unsigned int *extra_size, VC_CONTAINER_ES_FORMAT_T *format); VC_CONTAINER_STATUS_T vc_container_bitmapinfoheader_to_es_format(uint8_t *p, unsigned int buffer_size, unsigned int *extra_offset, unsigned int *extra_size, VC_CONTAINER_ES_FORMAT_T *format); /** Find the greatest common denominator of 2 numbers. * * @param a first number * @param b second number * * @return greatest common denominator of a and b */ int64_t vc_container_maths_gcd(int64_t a, int64_t b); /** Reduce a rational number to it's simplest form. * * @param num Pointer to the numerator of the rational number to simplify * @param den Pointer to the denominator of the rational number to simplify */ void vc_container_maths_rational_simplify(uint32_t *num, uint32_t *den); #endif /* VC_CONTAINERS_UTILS_H */ userland/containers/core/containers_waveformat.h000066400000000000000000000317651421703157200225570ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_CONTAINERS_WAVEFORMAT_H #define VC_CONTAINERS_WAVEFORMAT_H /* WAVE form wFormatTag IDs */ #define WAVE_FORMAT_UNKNOWN 0x0000 /* Microsoft Corporation */ #define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft Corporation */ #define WAVE_FORMAT_IEEE_FLOAT 0x0003 /* Microsoft Corporation */ #define WAVE_FORMAT_VSELP 0x0004 /* Compaq Computer Corp. */ #define WAVE_FORMAT_IBM_CVSD 0x0005 /* IBM Corporation */ #define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */ #define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */ #define WAVE_FORMAT_DTS 0x0008 /* Microsoft Corporation */ #define WAVE_FORMAT_DRM 0x0009 /* Microsoft Corporation */ #define WAVE_FORMAT_WMAUDIO_VOICE 0x000A /* Microsoft Corporation */ #define WAVE_FORMAT_OKI_ADPCM 0x0010 /* OKI */ #define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel Corporation */ #define WAVE_FORMAT_IMA_ADPCM (WAVE_FORMAT_DVI_ADPCM) /* Intel Corporation */ #define WAVE_FORMAT_MEDIASPACE_ADPCM 0x0012 /* Videologic */ #define WAVE_FORMAT_SIERRA_ADPCM 0x0013 /* Sierra Semiconductor Corp */ #define WAVE_FORMAT_G723_ADPCM 0x0014 /* Antex Electronics Corporation */ #define WAVE_FORMAT_DIGISTD 0x0015 /* DSP Solutions, Inc. */ #define WAVE_FORMAT_DIGIFIX 0x0016 /* DSP Solutions, Inc. */ #define WAVE_FORMAT_DIALOGIC_OKI_ADPCM 0x0017 /* Dialogic Corporation */ #define WAVE_FORMAT_MEDIAVISION_ADPCM 0x0018 /* Media Vision, Inc. */ #define WAVE_FORMAT_CU_CODEC 0x0019 /* Hewlett-Packard Company */ #define WAVE_FORMAT_YAMAHA_ADPCM 0x0020 /* Yamaha Corporation of America */ #define WAVE_FORMAT_SONARC 0x0021 /* Speech Compression */ #define WAVE_FORMAT_DSPGROUP_TRUESPEECH 0x0022 /* DSP Group, Inc */ #define WAVE_FORMAT_ECHOSC1 0x0023 /* Echo Speech Corporation */ #define WAVE_FORMAT_AUDIOFILE_AF36 0x0024 /* Virtual Music, Inc. */ #define WAVE_FORMAT_APTX 0x0025 /* Audio Processing Technology */ #define WAVE_FORMAT_AUDIOFILE_AF10 0x0026 /* Virtual Music, Inc. */ #define WAVE_FORMAT_PROSODY_1612 0x0027 /* Aculab plc */ #define WAVE_FORMAT_LRC 0x0028 /* Merging Technologies S.A. */ #define WAVE_FORMAT_DOLBY_AC2 0x0030 /* Dolby Laboratories */ #define WAVE_FORMAT_GSM610 0x0031 /* Microsoft Corporation */ #define WAVE_FORMAT_MSNAUDIO 0x0032 /* Microsoft Corporation */ #define WAVE_FORMAT_ANTEX_ADPCME 0x0033 /* Antex Electronics Corporation */ #define WAVE_FORMAT_CONTROL_RES_VQLPC 0x0034 /* Control Resources Limited */ #define WAVE_FORMAT_DIGIREAL 0x0035 /* DSP Solutions, Inc. */ #define WAVE_FORMAT_DIGIADPCM 0x0036 /* DSP Solutions, Inc. */ #define WAVE_FORMAT_CONTROL_RES_CR10 0x0037 /* Control Resources Limited */ #define WAVE_FORMAT_NMS_VBXADPCM 0x0038 /* Natural MicroSystems */ #define WAVE_FORMAT_CS_IMAADPCM 0x0039 /* Crystal Semiconductor IMA ADPCM */ #define WAVE_FORMAT_ECHOSC3 0x003A /* Echo Speech Corporation */ #define WAVE_FORMAT_ROCKWELL_ADPCM 0x003B /* Rockwell International */ #define WAVE_FORMAT_ROCKWELL_DIGITALK 0x003C /* Rockwell International */ #define WAVE_FORMAT_XEBEC 0x003D /* Xebec Multimedia Solutions Limited */ #define WAVE_FORMAT_G721_ADPCM 0x0040 /* Antex Electronics Corporation */ #define WAVE_FORMAT_G728_CELP 0x0041 /* Antex Electronics Corporation */ #define WAVE_FORMAT_MSG723 0x0042 /* Microsoft Corporation */ #define WAVE_FORMAT_PANASONIC_G726 0x0045 /* Not official Panasonic G.726 codec */ #define WAVE_FORMAT_MPEG 0x0050 /* Microsoft Corporation */ #define WAVE_FORMAT_RT24 0x0052 /* InSoft, Inc. */ #define WAVE_FORMAT_PAC 0x0053 /* InSoft, Inc. */ #define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ #define WAVE_FORMAT_LUCENT_G723 0x0059 /* Lucent Technologies */ #define WAVE_FORMAT_CIRRUS 0x0060 /* Cirrus Logic */ #define WAVE_FORMAT_ESPCM 0x0061 /* ESS Technology */ #define WAVE_FORMAT_VOXWARE 0x0062 /* Voxware Inc */ #define WAVE_FORMAT_CANOPUS_ATRAC 0x0063 /* Canopus, co., Ltd. */ #define WAVE_FORMAT_G726_ADPCM 0x0064 /* APICOM */ #define WAVE_FORMAT_G722_ADPCM 0x0065 /* APICOM */ #define WAVE_FORMAT_DSAT_DISPLAY 0x0067 /* Microsoft Corporation */ #define WAVE_FORMAT_VOXWARE_BYTE_ALIGNED 0x0069 /* Voxware Inc */ #define WAVE_FORMAT_VOXWARE_AC8 0x0070 /* Voxware Inc */ #define WAVE_FORMAT_VOXWARE_AC10 0x0071 /* Voxware Inc */ #define WAVE_FORMAT_VOXWARE_AC16 0x0072 /* Voxware Inc */ #define WAVE_FORMAT_VOXWARE_AC20 0x0073 /* Voxware Inc */ #define WAVE_FORMAT_VOXWARE_RT24 0x0074 /* Voxware Inc */ #define WAVE_FORMAT_VOXWARE_RT29 0x0075 /* Voxware Inc */ #define WAVE_FORMAT_VOXWARE_RT29HW 0x0076 /* Voxware Inc */ #define WAVE_FORMAT_VOXWARE_VR12 0x0077 /* Voxware Inc */ #define WAVE_FORMAT_VOXWARE_VR18 0x0078 /* Voxware Inc */ #define WAVE_FORMAT_VOXWARE_TQ40 0x0079 /* Voxware Inc */ #define WAVE_FORMAT_SOFTSOUND 0x0080 /* Softsound, Ltd. */ #define WAVE_FORMAT_VOXWARE_TQ60 0x0081 /* Voxware Inc */ #define WAVE_FORMAT_MSRT24 0x0082 /* Microsoft Corporation */ #define WAVE_FORMAT_G729A 0x0083 /* AT&T Labs, Inc. */ #define WAVE_FORMAT_MVI_MVI2 0x0084 /* Motion Pixels */ #define WAVE_FORMAT_DF_G726 0x0085 /* DataFusion Systems (Pty) (Ltd) */ #define WAVE_FORMAT_DF_GSM610 0x0086 /* DataFusion Systems (Pty) (Ltd) */ #define WAVE_FORMAT_ISIAUDIO 0x0088 /* Iterated Systems, Inc. */ #define WAVE_FORMAT_ONLIVE 0x0089 /* OnLive! Technologies, Inc. */ #define WAVE_FORMAT_SBC24 0x0091 /* Siemens Business Communications Sys */ #define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092 /* Sonic Foundry */ #define WAVE_FORMAT_MEDIASONIC_G723 0x0093 /* MediaSonic */ #define WAVE_FORMAT_PROSODY_8KBPS 0x0094 /* Aculab plc */ #define WAVE_FORMAT_ZYXEL_ADPCM 0x0097 /* ZyXEL Communications, Inc. */ #define WAVE_FORMAT_PHILIPS_LPCBB 0x0098 /* Philips Speech Processing */ #define WAVE_FORMAT_PACKED 0x0099 /* Studer Professional Audio AG */ #define WAVE_FORMAT_MALDEN_PHONYTALK 0x00A0 /* Malden Electronics Ltd. */ #define WAVE_FORMAT_MP4A 0x00FF /* AAC */ #define WAVE_FORMAT_RHETOREX_ADPCM 0x0100 /* Rhetorex Inc. */ #define WAVE_FORMAT_IRAT 0x0101 /* BeCubed Software Inc. */ #define WAVE_FORMAT_VIVO_G723 0x0111 /* Vivo Software */ #define WAVE_FORMAT_VIVO_SIREN 0x0112 /* Vivo Software */ #define WAVE_FORMAT_DIGITAL_G723 0x0123 /* Digital Equipment Corporation */ #define WAVE_FORMAT_SANYO_LD_ADPCM 0x0125 /* Sanyo Electric Co., Ltd. */ #define WAVE_FORMAT_SIPROLAB_ACEPLNET 0x0130 /* Sipro Lab Telecom Inc. */ #define WAVE_FORMAT_SIPROLAB_ACELP4800 0x0131 /* Sipro Lab Telecom Inc. */ #define WAVE_FORMAT_SIPROLAB_ACELP8V3 0x0132 /* Sipro Lab Telecom Inc. */ #define WAVE_FORMAT_SIPROLAB_G729 0x0133 /* Sipro Lab Telecom Inc. */ #define WAVE_FORMAT_SIPROLAB_G729A 0x0134 /* Sipro Lab Telecom Inc. */ #define WAVE_FORMAT_SIPROLAB_KELVIN 0x0135 /* Sipro Lab Telecom Inc. */ #define WAVE_FORMAT_G726ADPCM 0x0140 /* Dictaphone Corporation */ #define WAVE_FORMAT_QUALCOMM_PUREVOICE 0x0150 /* Qualcomm, Inc. */ #define WAVE_FORMAT_QUALCOMM_HALFRATE 0x0151 /* Qualcomm, Inc. */ #define WAVE_FORMAT_TUBGSM 0x0155 /* Ring Zero Systems, Inc. */ #define WAVE_FORMAT_WMAUDIO1 0x0160 /* Microsoft Corporation */ #define WAVE_FORMAT_WMAUDIO2 0x0161 /* Microsoft Corporation */ #define WAVE_FORMAT_WMAUDIOPRO 0x0162 /* Microsoft Corporation */ #define WAVE_FORMAT_WMAUDIO_LOSSLESS 0x0163 /* Microsoft Corporation */ #define WAVE_FORMAT_UNISYS_NAP_ADPCM 0x0170 /* Unisys Corp. */ #define WAVE_FORMAT_UNISYS_NAP_ULAW 0x0171 /* Unisys Corp. */ #define WAVE_FORMAT_UNISYS_NAP_ALAW 0x0172 /* Unisys Corp. */ #define WAVE_FORMAT_UNISYS_NAP_16K 0x0173 /* Unisys Corp. */ #define WAVE_FORMAT_CREATIVE_ADPCM 0x0200 /* Creative Labs, Inc */ #define WAVE_FORMAT_CREATIVE_FASTSPEECH8 0x0202 /* Creative Labs, Inc */ #define WAVE_FORMAT_CREATIVE_FASTSPEECH10 0x0203 /* Creative Labs, Inc */ #define WAVE_FORMAT_UHER_ADPCM 0x0210 /* UHER informatic GmbH */ #define WAVE_FORMAT_QUARTERDECK 0x0220 /* Quarterdeck Corporation */ #define WAVE_FORMAT_ILINK_VC 0x0230 /* I-link Worldwide */ #define WAVE_FORMAT_RAW_SPORT 0x0240 /* Aureal Semiconductor */ #define WAVE_FORMAT_ESST_AC3 0x0241 /* ESS Technology, Inc. */ #define WAVE_FORMAT_IPI_HSX 0x0250 /* Interactive Products, Inc. */ #define WAVE_FORMAT_IPI_RPELP 0x0251 /* Interactive Products, Inc. */ #define WAVE_FORMAT_CS2 0x0260 /* Consistent Software */ #define WAVE_FORMAT_SONY_SCX 0x0270 /* Sony Corp. */ #define WAVE_FORMAT_FM_TOWNS_SND 0x0300 /* Fujitsu Corp. */ #define WAVE_FORMAT_BTV_DIGITAL 0x0400 /* Brooktree Corporation */ #define WAVE_FORMAT_QDESIGN_MUSIC 0x0450 /* QDesign Corporation */ #define WAVE_FORMAT_VME_VMPCM 0x0680 /* AT&T Labs, Inc. */ #define WAVE_FORMAT_TPC 0x0681 /* AT&T Labs, Inc. */ #define WAVE_FORMAT_OLIGSM 0x1000 /* Ing C. Olivetti & C., S.p.A. */ #define WAVE_FORMAT_OLIADPCM 0x1001 /* Ing C. Olivetti & C., S.p.A. */ #define WAVE_FORMAT_OLICELP 0x1002 /* Ing C. Olivetti & C., S.p.A. */ #define WAVE_FORMAT_OLISBC 0x1003 /* Ing C. Olivetti & C., S.p.A. */ #define WAVE_FORMAT_OLIOPR 0x1004 /* Ing C. Olivetti & C., S.p.A. */ #define WAVE_FORMAT_LH_CODEC 0x1100 /* Lernout & Hauspie */ #define WAVE_FORMAT_NORRIS 0x1400 /* Norris Communications, Inc. */ #define WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS 0x1500 /* AT&T Labs, Inc. */ #define WAVE_FORMAT_DVM 0x2000 /* FAST Multimedia AG */ #define WAVE_FORMAT_AAC 0x706D /* AAC */ #if !defined(WAVE_FORMAT_EXTENSIBLE) #define WAVE_FORMAT_EXTENSIBLE 0xFFFE /* Microsoft */ #endif // !defined(WAVE_FORMAT_EXTENSIBLE) #if !defined(WAVE_FORMAT_PCM) #define WAVE_FORMAT_PCM 0x0001 #endif // !defined(WAVE_FORMAT_PCM) #endif /* VC_CONTAINERS_WAVEFORMAT_H */ userland/containers/core/containers_writer_utils.c000066400000000000000000000113611421703157200231210ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include "containers/containers.h" #include "containers/core/containers_private.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_writer_utils.h" #include "vcos.h" #include /*****************************************************************************/ static VC_CONTAINER_STATUS_T vc_container_writer_extraio_create(VC_CONTAINER_T *context, const char *uri, VC_CONTAINER_WRITER_EXTRAIO_T *extraio) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_PARAM_UNUSED(context); extraio->io = vc_container_io_open( uri, VC_CONTAINER_IO_MODE_WRITE, &status ); extraio->refcount = 0; extraio->temp = 0; return status; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_writer_extraio_create_null(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio) { return vc_container_writer_extraio_create(context, "null://", extraio); } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_writer_extraio_create_temp(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; unsigned int length = strlen(context->priv->io->uri) + 5; char *uri = malloc(length); if(!uri) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; snprintf(uri, length, "%s.tmp", context->priv->io->uri); status = vc_container_writer_extraio_create(context, uri, extraio); free(uri); extraio->temp = true; if(status == VC_CONTAINER_SUCCESS && !context->priv->tmp_io) context->priv->tmp_io = extraio->io; return status; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_writer_extraio_delete(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio) { VC_CONTAINER_STATUS_T status; char *uri = extraio->temp ? vcos_strdup(extraio->io->uri) : 0; while(extraio->refcount) vc_container_writer_extraio_disable(context, extraio); status = vc_container_io_close( extraio->io ); /* coverity[check_return] On failure the worst case is a file or directory is not removed */ if(uri) remove(uri); if(uri) free(uri); if(context->priv->tmp_io == extraio->io) context->priv->tmp_io = 0; return status; } /*****************************************************************************/ int64_t vc_container_writer_extraio_enable(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio) { VC_CONTAINER_IO_T *tmp; if(!extraio->refcount) { vc_container_io_seek(extraio->io, INT64_C(0)); tmp = context->priv->io; context->priv->io = extraio->io; extraio->io = tmp; } return extraio->refcount++; } /*****************************************************************************/ int64_t vc_container_writer_extraio_disable(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio) { VC_CONTAINER_IO_T *tmp; if(extraio->refcount) { extraio->refcount--; if(!extraio->refcount) { tmp = context->priv->io; context->priv->io = extraio->io; extraio->io = tmp; } } return extraio->refcount; } userland/containers/core/containers_writer_utils.h000066400000000000000000000111021421703157200231170ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_CONTAINERS_WRITER_UTILS_H #define VC_CONTAINERS_WRITER_UTILS_H /** \file containers_writer_utils.h * Helper functions and macros for container writers */ #include "containers/containers.h" #include "containers/containers_codecs.h" #include "containers/core/containers_io_helpers.h" /***************************************************************************** * Helper inline functions to write format specific structus to an i/o stream *****************************************************************************/ STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_write_waveformatex( VC_CONTAINER_T *p_ctx, VC_CONTAINER_ES_FORMAT_T *format) { /* Write waveformatex structure */ WRITE_U16(p_ctx, codec_to_waveformat(format->codec), "Codec ID"); WRITE_U16(p_ctx, format->type->audio.channels, "Number of Channels"); WRITE_U32(p_ctx, format->type->audio.sample_rate, "Samples per Second"); WRITE_U32(p_ctx, format->bitrate >> 3, "Average Number of Bytes Per Second"); WRITE_U16(p_ctx, format->type->audio.block_align, "Block Alignment"); WRITE_U16(p_ctx, format->type->audio.bits_per_sample, "Bits Per Sample"); WRITE_U16(p_ctx, format->extradata_size, "Codec Specific Data Size"); WRITE_BYTES(p_ctx, format->extradata, format->extradata_size); return STREAM_STATUS(p_ctx); } STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_write_bitmapinfoheader( VC_CONTAINER_T *p_ctx, VC_CONTAINER_ES_FORMAT_T *format) { VC_CONTAINER_FOURCC_T fourcc; /* Write bitmapinfoheader structure */ WRITE_U32(p_ctx, 40, "Format Data Size"); WRITE_U32(p_ctx, format->type->video.width, "Image Width"); WRITE_U32(p_ctx, format->type->video.height, "Image Height"); WRITE_U16(p_ctx, 0, "Reserved"); WRITE_U16(p_ctx, 0, "Bits Per Pixel Count"); fourcc = codec_to_vfw_fourcc(format->codec); WRITE_BYTES(p_ctx, (char *)&fourcc, 4); /* Compression ID */ LOG_FORMAT(p_ctx, "Compression ID: %4.4s", (char *)&fourcc); WRITE_U32(p_ctx, 0, "Image Size"); WRITE_U32(p_ctx, 0, "Horizontal Pixels Per Meter"); WRITE_U32(p_ctx, 0, "Vertical Pixels Per Meter"); WRITE_U32(p_ctx, 0, "Colors Used Count"); WRITE_U32(p_ctx, 0, "Important Colors Count"); WRITE_BYTES(p_ctx, format->extradata, format->extradata_size); return STREAM_STATUS(p_ctx); } /* Helper functions to create and use extra i/o */ typedef struct VC_CONTAINER_WRITER_EXTRAIO_T { VC_CONTAINER_IO_T *io; unsigned int refcount; bool temp; } VC_CONTAINER_WRITER_EXTRAIO_T; VC_CONTAINER_STATUS_T vc_container_writer_extraio_create_null(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null); VC_CONTAINER_STATUS_T vc_container_writer_extraio_create_temp(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null); VC_CONTAINER_STATUS_T vc_container_writer_extraio_delete(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null); int64_t vc_container_writer_extraio_enable(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null); int64_t vc_container_writer_extraio_disable(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null); #endif /* VC_CONTAINERS_WRITER_UTILS_H */ userland/containers/core/packetizers.c000066400000000000000000000203531421703157200204650ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include "containers/packetizers.h" #include "containers/core/packetizers_private.h" #include "containers/core/containers_common.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_utils.h" /** List of registered packetizers. */ static VC_PACKETIZER_REGISTRY_ENTRY_T *registry; /*****************************************************************************/ void vc_packetizer_register(VC_PACKETIZER_REGISTRY_ENTRY_T *entry) { LOG_DEBUG(0, "registering packetizer %s", entry->name); entry->next = registry; registry = entry; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T vc_packetizer_load(VC_PACKETIZER_T *p_ctx) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; VC_PACKETIZER_REGISTRY_ENTRY_T *entry; /* Try all the packetizers until we find the right one */ for (entry = registry; entry; entry = entry->next) { status = entry->open(p_ctx); if(status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) break; } return status; } /*****************************************************************************/ static void vc_packetizer_unload(VC_PACKETIZER_T *p_ctx) { VC_CONTAINER_PARAM_UNUSED(p_ctx); } /*****************************************************************************/ VC_PACKETIZER_T *vc_packetizer_open( VC_CONTAINER_ES_FORMAT_T *in, VC_CONTAINER_FOURCC_T out_variant, VC_CONTAINER_STATUS_T *p_status ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_PACKETIZER_T *p_ctx = 0; /* Allocate our context before trying out the different packetizers */ p_ctx = malloc( sizeof(*p_ctx) + sizeof(*p_ctx->priv)); if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*p_ctx->priv)); p_ctx->priv = (VC_PACKETIZER_PRIVATE_T *)(p_ctx + 1); bytestream_init( &p_ctx->priv->stream ); p_ctx->in = vc_container_format_create(in->extradata_size); if(!p_ctx->in) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } p_ctx->out = vc_container_format_create(in->extradata_size); if(!p_ctx->out) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } vc_container_format_copy( p_ctx->in, in, in->extradata_size ); p_ctx->in->extradata_size = 0; vc_container_format_copy( p_ctx->out, p_ctx->in, in->extradata_size ); p_ctx->in->extradata_size = in->extradata_size; p_ctx->out->extradata = p_ctx->in->extradata; p_ctx->out->extradata_size = p_ctx->in->extradata_size; p_ctx->out->codec_variant = out_variant; vc_container_time_init(&p_ctx->priv->time, 1000000); status = vc_packetizer_load(p_ctx); if(status != VC_CONTAINER_SUCCESS) goto error; end: if(p_status) *p_status = status; return p_ctx; error: if(p_ctx) vc_packetizer_close(p_ctx); p_ctx = NULL; goto end; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_packetizer_close( VC_PACKETIZER_T *p_ctx ) { VC_CONTAINER_BYTESTREAM_T *stream; VC_CONTAINER_PACKET_T *packet, *next; if(!p_ctx) return VC_CONTAINER_SUCCESS; stream = &p_ctx->priv->stream; if(p_ctx->in) vc_container_format_delete(p_ctx->in); if(p_ctx->out) vc_container_format_delete(p_ctx->out); if(p_ctx->priv->pf_close) p_ctx->priv->pf_close(p_ctx); if(p_ctx->priv->module_handle) vc_packetizer_unload(p_ctx); /* Free the bytestream */ for(packet = stream->first; packet; packet = next) { next = packet->next; if(packet->framework_data) free(packet); } free(p_ctx); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_packetizer_push( VC_PACKETIZER_T *p_ctx, VC_CONTAINER_PACKET_T *in) { /* Do some sanity checking on packet ? */ in->framework_data = 0; bytestream_push(&p_ctx->priv->stream, in); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_packetizer_pop( VC_PACKETIZER_T *p_ctx, VC_CONTAINER_PACKET_T **in, VC_PACKETIZER_FLAGS_T flags) { VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream; VC_CONTAINER_PACKET_T *packet, *new, **prev; /* Release the packets which have been read */ while((*in = bytestream_pop(stream)) != NULL) { if(*in && (*in)->framework_data) { free(*in); continue; } if(*in) return VC_CONTAINER_SUCCESS; } if(!(flags & VC_PACKETIZER_FLAG_FORCE_RELEASE_INPUT)) return VC_CONTAINER_ERROR_INCOMPLETE_DATA; /* Look for the 1st non-framework packet */ for (packet = stream->first, prev = &stream->first; packet && packet->framework_data; prev = &packet->next, packet = packet->next); if (!packet || (packet && packet->framework_data)) return VC_CONTAINER_ERROR_INCOMPLETE_DATA; /* We'll currently alloc an internal packet for each packet the client forcefully releases. * We could probably do something a bit more clever than that though. */ /* Replace the packet with a newly allocated one */ new = malloc(sizeof(*packet) + packet->size); if(!new) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; *new = *packet; new->framework_data = new; if(!new->next) stream->last = &new->next; if(stream->current == packet) stream->current = new; *prev = new; new->data = (uint8_t *)&new[1]; memcpy(new->data, packet->data, packet->size); *in = packet; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_packetizer_read( VC_PACKETIZER_T *p_ctx, VC_CONTAINER_PACKET_T *packet, VC_PACKETIZER_FLAGS_T flags) { if(!packet && !(flags & VC_CONTAINER_READ_FLAG_SKIP)) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; if(!packet && (flags & VC_CONTAINER_READ_FLAG_INFO)) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; if(packet && !packet->data && (!(flags & VC_CONTAINER_READ_FLAG_INFO) && !(flags & VC_CONTAINER_READ_FLAG_SKIP))) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; /* Always having a packet structure to work with simplifies things */ if(!packet) packet = &p_ctx->priv->packet; return p_ctx->priv->pf_packetize(p_ctx, packet, flags); } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_packetizer_reset( VC_PACKETIZER_T *p_ctx ) { VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream; bytestream_skip( stream, stream->bytes - stream->current_offset - stream->offset ); if (p_ctx->priv->pf_reset) return p_ctx->priv->pf_reset(p_ctx); else return VC_CONTAINER_SUCCESS; } userland/containers/core/packetizers_private.h000066400000000000000000000106311421703157200222220ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_PACKETIZERS_PRIVATE_H #define VC_PACKETIZERS_PRIVATE_H /** \file * Private interface for packetizers */ #include #include "containers/packetizers.h" #include "containers/core/containers_common.h" #include "containers/core/containers_bytestream.h" #include "containers/core/containers_time.h" /** \defgroup VcPacketizerModuleApi Packetizer Module API * Private interface for modules implementing packetizers */ /* @{ */ /** Context private to the packetizer instance. This private context is used to * store data which shouldn't be exported by the public API. */ typedef struct VC_PACKETIZER_PRIVATE_T { /** Pointer to the private data of the packetizer module in use */ struct VC_PACKETIZER_MODULE_T *module; /** Bytestream abstraction layer */ struct VC_CONTAINER_BYTESTREAM_T stream; /** Current stream time */ VC_CONTAINER_TIME_T time; /** Packetize the bytestream. * * \param context Pointer to the context of the instance of the packetizer * \param out Pointer to the output packet structure which needs to be filled * \param flags Miscellaneous flags controlling the packetizing * \return the status of the operation */ VC_CONTAINER_STATUS_T (*pf_packetize)( VC_PACKETIZER_T *context, VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags ); /** Reset packetizer state. * * \param context Pointer to the context of the instance of the packetizer * \return the status of the operation */ VC_CONTAINER_STATUS_T (*pf_reset)( VC_PACKETIZER_T *context ); /** Closes a packetizer module. * * \param context Pointer to the context of the instance to close * \return the status of the operation */ VC_CONTAINER_STATUS_T (*pf_close)( struct VC_PACKETIZER_T *context ); /** Pointer to the packetizer module code and symbols*/ void *module_handle; /** Temporary packet structure used when the caller does not provide one */ VC_CONTAINER_PACKET_T packet; } VC_PACKETIZER_PRIVATE_T; /** Structure used by packetizers to register themselves with the core. */ typedef struct VC_PACKETIZER_REGISTRY_ENTRY_T { struct VC_PACKETIZER_REGISTRY_ENTRY_T *next; /**< To link entries together */ const char *name; /**< Name of the packetizer */ VC_CONTAINER_STATUS_T (*open)( VC_PACKETIZER_T * ); /**< Called to open packetizer */ } VC_PACKETIZER_REGISTRY_ENTRY_T; /** Register a packetizer with the core. * * \param entry Entry to register with the core */ void vc_packetizer_register(VC_PACKETIZER_REGISTRY_ENTRY_T *entry); /** Utility macro used to register a packetizer with the core */ #define VC_PACKETIZER_REGISTER(func, name) \ VC_CONTAINER_CONSTRUCTOR(func##_register); \ static VC_PACKETIZER_REGISTRY_ENTRY_T registry_entry = {0, name, func}; \ void func##_register(void) { vc_packetizer_register(®istry_entry); } /* @} */ #endif /* VC_PACKETIZERS_PRIVATE_H */ userland/containers/dummy/000077500000000000000000000000001421703157200161755ustar00rootroot00000000000000userland/containers/dummy/CMakeLists.txt000066400000000000000000000005751421703157200207440ustar00rootroot00000000000000# Container module needs to go in as a plugins so different prefix # and install path set(CMAKE_SHARED_LIBRARY_PREFIX "") # Make sure the compiler can find the necessary include files include_directories (../..) add_library(writer_dummy ${LIBRARY_TYPE} dummy_writer.c) target_link_libraries(writer_dummy containers) install(TARGETS writer_dummy DESTINATION ${VMCS_PLUGIN_DIR}) userland/containers/dummy/dummy_writer.c000066400000000000000000000125521421703157200210750ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_logging.h" /****************************************************************************** Type definitions ******************************************************************************/ typedef struct VC_CONTAINER_MODULE_T { VC_CONTAINER_TRACK_T *track[2]; } VC_CONTAINER_MODULE_T; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T dummy_writer_open( VC_CONTAINER_T * ); /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ static VC_CONTAINER_STATUS_T dummy_writer_close( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T dummy_writer_write( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *packet ) { VC_CONTAINER_PARAM_UNUSED(p_ctx); VC_CONTAINER_PARAM_UNUSED(packet); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T dummy_writer_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args ) { VC_CONTAINER_TRACK_T *track; VC_CONTAINER_PARAM_UNUSED(args); switch(operation) { case VC_CONTAINER_CONTROL_TRACK_ADD: if(p_ctx->tracks_num >= 2) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; /* Allocate and initialise track data */ p_ctx->tracks[p_ctx->tracks_num] = track = vc_container_allocate_track(p_ctx, 0); if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; p_ctx->tracks_num++; return VC_CONTAINER_SUCCESS; case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: return VC_CONTAINER_SUCCESS; default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; } } /*****************************************************************************/ VC_CONTAINER_STATUS_T dummy_writer_open( VC_CONTAINER_T *p_ctx ) { const char *extension = vc_uri_path_extension(p_ctx->priv->uri); VC_CONTAINER_MODULE_T *module = 0; VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; /* Check if the user has specified a container */ vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); /* Check we're the right writer for this */ if(!extension) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if(strcasecmp(extension, "dummy")) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Allocate our context */ module = malloc(sizeof(*module)); if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); p_ctx->priv->module = module; p_ctx->tracks = module->track; p_ctx->capabilities |= VC_CONTAINER_CAPS_DYNAMIC_TRACK_ADD; p_ctx->priv->pf_close = dummy_writer_close; p_ctx->priv->pf_write = dummy_writer_write; p_ctx->priv->pf_control = dummy_writer_control; return VC_CONTAINER_SUCCESS; error: LOG_DEBUG(p_ctx, "dummy: error opening stream (%i)", status); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak writer_open dummy_writer_open #endif userland/containers/flash/000077500000000000000000000000001421703157200161375ustar00rootroot00000000000000userland/containers/flash/CMakeLists.txt000066400000000000000000000005661421703157200207060ustar00rootroot00000000000000# Container module needs to go in as a plugins so different prefix # and install path set(CMAKE_SHARED_LIBRARY_PREFIX "") # Make sure the compiler can find the necessary include files include_directories (../..) add_library(reader_flv ${LIBRARY_TYPE} flv_reader.c) target_link_libraries(reader_flv containers) install(TARGETS reader_flv DESTINATION ${VMCS_PLUGIN_DIR}) userland/containers/flash/flv_reader.c000066400000000000000000001265061421703157200204260ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include /* Work-around for MSVC debugger issue */ #define VC_CONTAINER_MODULE_T VC_CONTAINER_MODULE_FLV_READER_T #define VC_CONTAINER_TRACK_MODULE_T VC_CONTAINER_TRACK_MODULE_FLV_READER_T //#define ENABLE_FLV_EXTRA_LOGGING #define CONTAINER_IS_BIG_ENDIAN #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_index.h" #include "containers/core/containers_logging.h" #undef CONTAINER_HELPER_LOG_INDENT #define CONTAINER_HELPER_LOG_INDENT(a) 0 VC_CONTAINER_STATUS_T flv_reader_open( VC_CONTAINER_T *p_ctx ); /****************************************************************************** Defines. ******************************************************************************/ #define FLV_TRACKS_MAX 2 #define FLV_TAG_TYPE_AUDIO 8 #define FLV_TAG_TYPE_VIDEO 9 #define FLV_TAG_TYPE_METADATA 18 #define FLV_TAG_HEADER_SIZE 15 #define FLV_SCRIPT_DATA_TYPE_NUMBER 0 #define FLV_SCRIPT_DATA_TYPE_BOOL 1 #define FLV_SCRIPT_DATA_TYPE_STRING 2 #define FLV_SCRIPT_DATA_TYPE_ECMA 8 #define FLV_SCRIPT_DATA_TYPE_LONGSTRING 12 #define FLV_FLAG_DISCARD 1 #define FLV_FLAG_KEYFRAME 2 #define FLV_FLAG_INTERFRAME 4 /****************************************************************************** Type definitions. ******************************************************************************/ typedef struct { VC_CONTAINER_STATUS_T status; int64_t tag_position; /* position of the current tag we're reading */ int64_t data_position; /* position to the start of the data within the tag */ int data_offset; /* current position inside the tag's data */ int data_size; /* size of the data from the current tag */ int tag_prev_size; /* size of the previous tag in the stream */ int flags; /* flags for the current tag */ uint32_t timestamp; /* timestamp for the current tag */ uint32_t track; /* track the current tag belongs to */ VC_CONTAINER_INDEX_T *index; /* index of key frames */ } FLV_READER_STATE_T; typedef struct VC_CONTAINER_TRACK_MODULE_T { FLV_READER_STATE_T *state; FLV_READER_STATE_T track_state; } VC_CONTAINER_TRACK_MODULE_T; typedef struct VC_CONTAINER_MODULE_T { VC_CONTAINER_TRACK_T *tracks[FLV_TRACKS_MAX]; int64_t data_offset; /*< offset to the first FLV tag in the stream */ FLV_READER_STATE_T state; /*< global state of the reader */ int audio_track; int video_track; uint32_t meta_videodatarate; uint32_t meta_audiodatarate; float meta_framerate; uint32_t meta_width; uint32_t meta_height; } VC_CONTAINER_MODULE_T; /****************************************************************************** Static functions within this file. ******************************************************************************/ /** Reads an FLV tag header * * @param p_ctx pointer to our context * @param[out] p_prev_size size of the previous tag * @param[out] p_type type of the tag * @param[out] p_type size of the tag * @param[out] p_type timestamp for the tag * @return VC_CONTAINER_SUCCESS on success */ static VC_CONTAINER_STATUS_T flv_read_tag_header(VC_CONTAINER_T *p_ctx, int *p_prev_size, int *p_type, int *p_size, uint32_t *p_timestamp) { int prev_size, type, size; uint32_t timestamp; prev_size = READ_U32(p_ctx, "PreviousTagSize"); type = READ_U8(p_ctx, "TagType"); size = READ_U24(p_ctx, "DataSize"); timestamp = READ_U24(p_ctx, "Timestamp"); timestamp |= (READ_U8(p_ctx, "TimestampExtended") << 24); SKIP_U24(p_ctx, "StreamID"); if(p_prev_size) *p_prev_size = prev_size + 4; if(p_type) *p_type = type; if(p_size) *p_size = size; if(p_timestamp) *p_timestamp = timestamp; return STREAM_STATUS(p_ctx); } /** Reads an FLV video data header. * This contains the codec id and the current frame type. * * @param p_ctx pointer to our context * @param[out] codec video codec * @param[out] frame_type type of the current frame * @return VC_CONTAINER_SUCCESS on success */ static VC_CONTAINER_STATUS_T flv_read_videodata_header(VC_CONTAINER_T *p_ctx, VC_CONTAINER_FOURCC_T *codec, int *frame_type) { uint8_t header = READ_U8(p_ctx, "FrameType/CodecID"); if(frame_type) *frame_type = (header >> 4) == 1 ? FLV_FLAG_KEYFRAME : (header >> 4) == 3 ? FLV_FLAG_INTERFRAME : 0; switch(header &0xF) { case 2: *codec = VC_CONTAINER_CODEC_SPARK; break; case 3: *codec = VC_FOURCC('s','c','r','1'); break; /* screen video */ case 4: *codec = VC_CONTAINER_CODEC_VP6; break; case 5: *codec = VC_FOURCC('v','p','6','a'); break; /* vp6 alpha */ case 6: *codec = VC_FOURCC('s','c','r','2'); break; /* screen video 2 */ case 7: *codec = VC_CONTAINER_CODEC_H264; break; default: *codec = 0; break; } return STREAM_STATUS(p_ctx); } /** Get the properties of a video frame * This is only really useful at setup time when trying to detect * the type of content we are dealing with. * This will try to get some of the properties of the video stream * as well as codec configuration data if there is any. * * @param p_ctx pointer to our context * @param track track number this data/tag belongs to * @param size size of the data we are parsing * @return VC_CONTAINER_SUCCESS on success */ static VC_CONTAINER_STATUS_T flv_read_videodata_properties(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, int size) { VC_CONTAINER_STATUS_T status; int width = 0, height = 0; if(track->format->codec == VC_CONTAINER_CODEC_VP6 || track->format->codec == VC_FOURCC('v','p','6','a')) { /* Just extract the width / height */ uint8_t data = _READ_U8(p_ctx); _SKIP_U16(p_ctx); height = _READ_U8(p_ctx) * 16; width = _READ_U8(p_ctx) * 16; width -= data >> 4; height -= data & 0xf; } else if(track->format->codec == VC_CONTAINER_CODEC_H264) { uint8_t type = _READ_U8(p_ctx); size--; if(type || size <= 8) return VC_CONTAINER_ERROR_CORRUPTED; _SKIP_U24(p_ctx); size-=3; track->format->codec_variant = VC_FOURCC('a','v','c','C'); status = vc_container_track_allocate_extradata(p_ctx, track, size); if(status != VC_CONTAINER_SUCCESS) return status; track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size); } track->format->type->video.width = width; track->format->type->video.height = height; return STREAM_STATUS(p_ctx); } /** Reads an FLV audio data header. * This contains the codec id, samplerate, number of channels and bitrate. * * @param p_ctx pointer to our context * @param[out] codec audio codec * @param[out] p_samplerate audio sampling rate * @param[out] p_channels number of audio channels * @param[out] p_bps bits per sample * @return VC_CONTAINER_SUCCESS on success */ static VC_CONTAINER_STATUS_T flv_read_audiodata_header(VC_CONTAINER_T *p_ctx, VC_CONTAINER_FOURCC_T *codec, int *p_samplerate, int *p_channels, int *p_bps) { int samplerate, channels, bps; uint8_t header = _READ_U8(p_ctx); switch((header >> 2) & 0x3) { case 0: samplerate = 5512; break; case 1: samplerate = 11025; break; case 2: samplerate = 22050; break; default: case 3: samplerate = 44100; break; } channels = 1 << (header & 1); bps = 8 << ((header >> 1) & 1); switch(header >> 4) { case 0: *codec = bps == 8 ? VC_CONTAINER_CODEC_PCM_UNSIGNED : VC_CONTAINER_CODEC_PCM_SIGNED; break; case 1: *codec = VC_CONTAINER_CODEC_ADPCM_SWF; break; case 2: *codec = VC_CONTAINER_CODEC_MPGA; break; case 3: *codec = bps == 8 ? VC_CONTAINER_CODEC_PCM_UNSIGNED : VC_CONTAINER_CODEC_PCM_SIGNED_LE; break; case 4: *codec = VC_CONTAINER_CODEC_NELLYMOSER; samplerate = 8000; channels = 1; break; case 5: *codec = VC_CONTAINER_CODEC_NELLYMOSER; samplerate = 16000; channels = 1; break; case 6: *codec = VC_CONTAINER_CODEC_NELLYMOSER; channels = 1; break; case 7: *codec = VC_CONTAINER_CODEC_ALAW; break; case 8: *codec = VC_CONTAINER_CODEC_MULAW; break; case 10: *codec = VC_CONTAINER_CODEC_MP4A; samplerate = 44100; channels = 2; break; case 11: *codec = VC_CONTAINER_CODEC_SPEEX; break; case 14: *codec = VC_CONTAINER_CODEC_MPGA; samplerate = 8000; break; default: *codec = 0; break; } if(p_samplerate) *p_samplerate = samplerate; if(p_channels) *p_channels = channels; if(p_bps) *p_bps = bps; return STREAM_STATUS(p_ctx); } /** Get the properties of an audio frame * This is only really useful at setup time when trying to detect * the type of content we are dealing with. * This will try to get some of the properties of the audio stream * as well as codec configuration data if there is any. * * @param p_ctx pointer to our context * @param track track number this data/tag belongs to * @param size size of the data we are parsing * @param samplerate sampling rate of the audio data * @param channels number of channels of the audio data * @param bps bits per sample * @return VC_CONTAINER_SUCCESS on success */ static VC_CONTAINER_STATUS_T flv_read_audiodata_properties(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, int size, int samplerate, int channels, int bps) { static const int aac_freq[16] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350}; VC_CONTAINER_STATUS_T status; if(track->format->codec == VC_CONTAINER_CODEC_MP4A) { uint8_t *p_data, data = _READ_U8(p_ctx); size--; if(data || size <= 0) return VC_CONTAINER_ERROR_FAILED; /* Read the AudioSpecificConfig */ status = vc_container_track_allocate_extradata(p_ctx, track, size); if(status != VC_CONTAINER_SUCCESS) return status; track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size); if(track->format->extradata_size >= 2) { p_data = track->format->extradata; data = ((p_data[0] & 0x7) << 1) | (p_data[1] >> 7); if(data >= countof(aac_freq)) return VC_CONTAINER_ERROR_FAILED; samplerate = aac_freq[data]; channels = (p_data[1] >> 3) & 0xf; if(track->format->extradata_size >= 5 && data == 0xf) { samplerate = ((p_data[1] & 0x7f) << 17) | (p_data[2] << 9) | (p_data[3] << 1) | (p_data[4] >> 7); channels = (p_data[4] >> 3) & 0xf; } } } track->format->type->audio.sample_rate = samplerate; track->format->type->audio.channels = channels; track->format->type->audio.bits_per_sample = bps; return STREAM_STATUS(p_ctx); } /** Reads an FLV metadata tag. * This contains metadata information about the stream. * All the data we extract from this will be placed directly in the context. * * @param p_ctx pointer to our context * @param size size of the tag * @return FLV_FILE_NO_ERROR on success */ static int flv_read_metadata(VC_CONTAINER_T *p_ctx, int size) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; #define MAX_METADATA_STRING_SIZE 25 char psz_string[MAX_METADATA_STRING_SIZE+1]; uint16_t length, num_values; double f_value; uint64_t u_value; uint8_t type; /* We're looking for an onMetaData script */ type = READ_U8(p_ctx, "Type"); size--; if(type != FLV_SCRIPT_DATA_TYPE_STRING) return VC_CONTAINER_SUCCESS; length = READ_U16(p_ctx, "StringLength"); size -= 2; if(!length || length > size || length > MAX_METADATA_STRING_SIZE) return VC_CONTAINER_SUCCESS; if(READ_BYTES(p_ctx, psz_string, length) != length) return VC_CONTAINER_SUCCESS; psz_string[length] = 0; size -= length; if(strcmp(psz_string, "onMetaData")) return VC_CONTAINER_SUCCESS; if(size < 5) return VC_CONTAINER_SUCCESS; type = READ_U8(p_ctx, "Type"); size--; if(type != FLV_SCRIPT_DATA_TYPE_ECMA) return VC_CONTAINER_SUCCESS; num_values = READ_U32(p_ctx, "ECMAArrayLength"); size -= 4; /* We found our script, now extract the metadata values */ while(num_values-- && size >= 2) { uint16_t length = _READ_U16(p_ctx); size -= 2; if(!length || length >= size || length > MAX_METADATA_STRING_SIZE) break; if(READ_BYTES(p_ctx, psz_string, length) != length) break; psz_string[length] = 0; size -= length; type = _READ_U8(p_ctx); size--; switch(type) { case FLV_SCRIPT_DATA_TYPE_NUMBER: /* We only cope with DOUBLE types*/ if(size < 8) return VC_CONTAINER_SUCCESS; u_value = _READ_U64(p_ctx); size -= 8; /* Convert value into a double */ { int64_t value = ((u_value & ((UINT64_C(1)<<52)-1)) + (UINT64_C(1)<<52)) * ((((int64_t)u_value)>>63)|1); int exp = ((u_value>>52)&0x7FF)-1075 + 16; if(exp >= 0) value <<= exp; else value >>= -exp; f_value = ((double)value) / (1 << 16); } LOG_DEBUG(p_ctx, "metadata (%s=%i.%i)", psz_string, ((int)(f_value*100))/100, ((int)(f_value*100))%100); if(!strcmp(psz_string, "duration")) p_ctx->duration = (int64_t)(f_value*INT64_C(1000000)); if(!strcmp(psz_string, "videodatarate")) module->meta_videodatarate = (uint32_t)f_value; if(!strcmp(psz_string, "width")) module->meta_width = (uint32_t)f_value; if(!strcmp(psz_string, "height")) module->meta_height = (uint32_t)f_value; if(!strcmp(psz_string, "framerate")) module->meta_framerate = f_value; if(!strcmp(psz_string, "audiodatarate")) module->meta_audiodatarate = (uint32_t)f_value; continue; /* We skip these */ case FLV_SCRIPT_DATA_TYPE_BOOL: if(size < 1) return VC_CONTAINER_SUCCESS; u_value = _READ_U8(p_ctx); size -= 1; LOG_DEBUG(p_ctx, "metadata (%s=%i)", psz_string, (int)u_value); continue; case FLV_SCRIPT_DATA_TYPE_STRING: if(size < 2) return VC_CONTAINER_SUCCESS; length = _READ_U16(p_ctx); size -= 2; if(length > size) return VC_CONTAINER_SUCCESS; SKIP_BYTES(p_ctx, length); size -= length; LOG_DEBUG(p_ctx, "metadata skipping (%s)", psz_string); continue; /* We can't cope with anything else */ default: LOG_DEBUG(p_ctx, "unknown amf type (%s,%i)", psz_string, type); return VC_CONTAINER_SUCCESS; } } return STREAM_STATUS(p_ctx); } /** Reads an FLV frame header. * This reads the current tag header and matches the contained frame * with one of the tracks we have. If no match can be found, the frame is marked * for discarding. The current read position will be updated to the start * of the data (i.e. the frame) contained within the FLV tag. * * @param p_ctx pointer to our context * @param[out] p_track track this frame belongs to * @param[out] p_size size of the frame * @param[out] p_timestamp timestamp of the frame * @param[out] p_flags flags associated with the frame * @param b_extra_check whether to perform extra sanity checking on the tag * @return VC_CONTAINER_SUCCESS on success */ static int flv_read_frame_header(VC_CONTAINER_T *p_ctx, int *p_prev_size, int *p_track, int *p_size, uint32_t *p_timestamp, int *p_flags, int b_extra_check) { int64_t position = STREAM_POSITION(p_ctx); int type, size, flags = 0, frametype = 0; VC_CONTAINER_STATUS_T status; VC_CONTAINER_ES_TYPE_T es_type = VC_CONTAINER_ES_TYPE_UNKNOWN; unsigned int track = p_ctx->tracks_num; uint32_t codec = 0; status = flv_read_tag_header(p_ctx, p_prev_size, &type, &size, p_timestamp); if(status != VC_CONTAINER_SUCCESS) return status; if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) return status; if(position == STREAM_POSITION(p_ctx)) return VC_CONTAINER_ERROR_EOS; if(type == 0) return VC_CONTAINER_ERROR_CORRUPTED; /* Sanity checking */ if(b_extra_check && type != FLV_TAG_TYPE_AUDIO && type != FLV_TAG_TYPE_VIDEO && type != FLV_TAG_TYPE_METADATA) return VC_CONTAINER_ERROR_CORRUPTED; /* We're only interested in audio / video */ if((type != FLV_TAG_TYPE_AUDIO && type != FLV_TAG_TYPE_VIDEO) || !size) { flags |= FLV_FLAG_DISCARD; goto end; } if(type == FLV_TAG_TYPE_AUDIO) { flv_read_audiodata_header(p_ctx, &codec, 0, 0, 0); es_type = VC_CONTAINER_ES_TYPE_AUDIO; } else if(type == FLV_TAG_TYPE_VIDEO) { flv_read_videodata_header(p_ctx, &codec, &frametype); es_type = VC_CONTAINER_ES_TYPE_VIDEO; } size--; /* Find which track this belongs to */ for(track = 0; track < p_ctx->tracks_num; track++) if(es_type == p_ctx->tracks[track]->format->es_type) break; if(track == p_ctx->tracks_num) flags |= FLV_FLAG_DISCARD; /* Sanity checking */ if(b_extra_check && codec != p_ctx->tracks[track]->format->codec) return VC_CONTAINER_ERROR_CORRUPTED; end: // add to the index if we have one, and we're not discarding this frame. // also require that we either have no video track or this is a video frame marked as a key frame. if(p_ctx->priv->module->state.index && !(flags & FLV_FLAG_DISCARD) && (p_ctx->priv->module->video_track < 0 || (es_type == VC_CONTAINER_ES_TYPE_VIDEO && (frametype & FLV_FLAG_KEYFRAME)))) vc_container_index_add(p_ctx->priv->module->state.index, (int64_t) (*p_timestamp) * 1000LL, position); *p_flags = flags | frametype; *p_size = size; *p_track = track; return VC_CONTAINER_SUCCESS; } /** Validate the data contained within the frame and update the read * position to the start of the frame data that we want to feed to the codec. * * Each codec is packed slightly differently so this function is necessary * to prepare for reading the actual codec data. * * @param p_ctx pointer to our context * @param track track this frame belongs to * @param[in] p_size size of the frame * @param[out] p_size updated size of the frame * @param[in] p_timestamp timestamp for the frame * @param[out] p_timestamp updated timestamp for the frame * @return VC_CONTAINER_SUCCESS on success */ static VC_CONTAINER_STATUS_T flv_validate_frame_data(VC_CONTAINER_T *p_ctx, int track, int *p_size, uint32_t *p_timestamp) { int32_t time_offset; if(track >= (int)p_ctx->tracks_num) return *p_size ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_CONTINUE; switch(p_ctx->tracks[track]->format->codec) { case VC_CONTAINER_CODEC_VP6: if(*p_size < 1) return VC_CONTAINER_ERROR_CORRUPTED; _READ_U8(p_ctx); *p_size -= 1; break; case VC_CONTAINER_CODEC_MP4A: if(*p_size < 1) return VC_CONTAINER_ERROR_CORRUPTED; *p_size -= 1; if(_READ_U8(p_ctx)!=1) return VC_CONTAINER_ERROR_CONTINUE; /* empty frame*/ break; case VC_CONTAINER_CODEC_H264: if(*p_size < 4) return VC_CONTAINER_ERROR_CORRUPTED; *p_size -= 1; if(_READ_U8(p_ctx)!=1) return VC_CONTAINER_ERROR_CONTINUE; /* empty frame*/ time_offset = _READ_U24(p_ctx); time_offset <<= 8; time_offset >>= 8; /* change to signed */ *p_timestamp += time_offset; *p_size -= 3; break; default: break; } return *p_size ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_CONTINUE; } /** Small utility function to update the reading position of a track */ static void flv_update_track_position(VC_CONTAINER_T *p_ctx, int track, int64_t tag_position, int tag_prev_size, int64_t data_position, int data_size, uint32_t timestamp, int flags) { VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[track]->priv->module; track_module->state->tag_position = tag_position; track_module->state->tag_prev_size = tag_prev_size; track_module->state->data_position = data_position; track_module->state->data_size = data_size; track_module->state->data_offset = 0; track_module->state->timestamp = timestamp; track_module->state->flags = flags; track_module->state->track = track; } /** Utility function to find the next frame of a given track in the stream. * * This will basically walk through all the tags in the file until it * finds a tag/frame which belongs to the given track. * * @param p_ctx pointer to our context * @param track track wanted * @param[out] p_size size of the frame * @param[out] p_timestamp timestamp of the frame * @param[out] p_flags flags associated with the frame * @param b_keyframe whether we specifically want a keyframe or not * @param b_extra_check whether to perform extra sanity checking on the tag * @return VC_CONTAINER_SUCCESS on success */ static VC_CONTAINER_STATUS_T flv_find_next_frame(VC_CONTAINER_T *p_ctx, int track, int *p_size, uint32_t *p_timestamp, int *p_flags, int b_keyframe, int b_extra_check) { int frame_track, prev_size, size, flags; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; FLV_READER_STATE_T *state = p_ctx->tracks[track]->priv->module->state; uint32_t timestamp; int64_t position; VC_CONTAINER_PARAM_UNUSED(b_extra_check); /* Seek to the next tag in the stream or the current position * if none of its data has been consumed */ position = state->tag_position; if(state->data_offset) position = state->data_position + state->data_size; status = SEEK(p_ctx, position); if(status != VC_CONTAINER_SUCCESS) return status; /* Look for the next frame we want */ while (status == VC_CONTAINER_SUCCESS) { position = STREAM_POSITION(p_ctx); status = flv_read_frame_header(p_ctx, &prev_size, &frame_track, &size, ×tamp, &flags, 0); if(status != VC_CONTAINER_SUCCESS) break; if(flags & FLV_FLAG_DISCARD) goto skip; if(frame_track != track) goto skip; if(b_keyframe && p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO && !(flags & FLV_FLAG_KEYFRAME)) goto skip; if(flv_validate_frame_data(p_ctx, track, &size, ×tamp) != VC_CONTAINER_SUCCESS) goto skip; /* We have what we need */ flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx), size, timestamp, flags); break; skip: flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx), size, timestamp, 0); state->data_offset = size; /* consume data */ if(SKIP_BYTES(p_ctx, size) != (size_t)size) status = STREAM_STATUS(p_ctx); } if(!status) { if(p_size) *p_size = size; if(p_timestamp) *p_timestamp = timestamp; if(p_flags) *p_flags = flags; } return status; } /** Utility function to find the previous frame of a given track in the stream. * * This will basically walk back through all the tags in the file until it * finds a tag/frame which belongs to the given track. * * @param p_ctx pointer to our context * @param track track wanted * @param[out] p_size size of the frame * @param[out] p_timestamp timestamp of the frame * @param[out] p_flags flags associated with the frame * @param b_keyframe whether we specifically want a keyframe or not * @param b_extra_check whether to perform extra sanity checking on the tag * @return VC_CONTAINER_SUCCESS on success */ static VC_CONTAINER_STATUS_T flv_find_previous_frame(VC_CONTAINER_T *p_ctx, int track, int *p_size, uint32_t *p_timestamp, int *p_flags, int b_keyframe, int b_extra_check) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; FLV_READER_STATE_T *state = p_ctx->tracks[track]->priv->module->state; int frame_track, prev_size, size, flags; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; uint32_t timestamp; int64_t position; /* Look for the previous frame we want */ while (status == VC_CONTAINER_SUCCESS) { /* Seek to the previous tag in the stream */ position = state->tag_position - state->tag_prev_size; if(position < module->data_offset) position = module->data_offset; status = SEEK(p_ctx, position); if(status != VC_CONTAINER_SUCCESS) return status; status = flv_read_frame_header(p_ctx, &prev_size, &frame_track, &size, ×tamp, &flags, 0); if(status) break; if(flags & FLV_FLAG_DISCARD) goto skip; if(frame_track != track) goto skip; if(b_keyframe && p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO && !(flags & FLV_FLAG_KEYFRAME)) goto skip; if(flv_validate_frame_data(p_ctx, track, &size, ×tamp) != VC_CONTAINER_SUCCESS) goto skip; /* We have what we need */ flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx), size, timestamp, flags); break; skip: if(position <= module->data_offset) { /* We're back at the beginning but we still want to return something */ flv_update_track_position(p_ctx, track, (int64_t)module->data_offset, 0, (int64_t)module->data_offset, 0, 0, 0); return flv_find_next_frame(p_ctx, track, p_size, p_timestamp, p_flags, b_keyframe, b_extra_check); } flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx), size, timestamp, 0); state->data_offset = size; /* consume data */ } if(!status) { if(p_size) *p_size = size; if(p_timestamp) *p_timestamp = timestamp; if(p_flags) *p_flags = flags; } return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T flv_reader_close( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; unsigned int i; for(i = 0; i < p_ctx->tracks_num; i++) vc_container_free_track(p_ctx, p_ctx->tracks[i]); if(module->state.index) vc_container_index_free(module->state.index); free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T flv_read_sample_header( VC_CONTAINER_T *p_ctx, FLV_READER_STATE_T *state) { int track, prev_size, size, flags; uint32_t timestamp; int64_t position; /* Check if we still have some data left to read from the current frame */ if(state->data_offset < state->data_size) return state->status; /* Read the next tag header */ position = STREAM_POSITION(p_ctx); state->status = flv_read_frame_header(p_ctx, &prev_size, &track, &size, ×tamp, &flags, 0); if(state->status != VC_CONTAINER_SUCCESS) return state->status; state->status = flv_validate_frame_data(p_ctx, track, &size, ×tamp); if(state->status == VC_CONTAINER_ERROR_CONTINUE) { /* Skip it */ state->status = VC_CONTAINER_SUCCESS; track = p_ctx->tracks_num; } if(state->status != VC_CONTAINER_SUCCESS) return state->status; state->tag_position = position; state->data_position = STREAM_POSITION(p_ctx); state->data_size = size; state->data_offset = 0; state->flags = flags; state->tag_prev_size = prev_size; state->timestamp = timestamp; state->track = track; return state->status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T flv_read_sample_data( VC_CONTAINER_T *p_ctx, FLV_READER_STATE_T *state, uint8_t *data, unsigned int *data_size ) { unsigned int size = state->data_size - state->data_offset; if(state->status != VC_CONTAINER_SUCCESS) return state->status; if(data_size && *data_size < size) size = *data_size; if(!data) size = SKIP_BYTES(p_ctx, size); else size = READ_BYTES(p_ctx, data, size); state->data_offset += size; if(data_size) *data_size = size; state->status = STREAM_STATUS(p_ctx); return state->status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T flv_reader_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *packet, uint32_t flags ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; FLV_READER_STATE_T *state = &module->state; unsigned int data_size; /* TODO: select right state */ status = flv_read_sample_header(p_ctx, state); if(status != VC_CONTAINER_SUCCESS) return status; #ifdef ENABLE_FLV_EXTRA_LOGGING LOG_DEBUG(p_ctx, "read_sample_header (%i,%i,%i/%i/%i/%i)", state->timestamp, state->flags, (int)state->tag_position, (int)(state->data_position-state->tag_position), state->data_offset, state->data_size); #endif if(state->track >= p_ctx->tracks_num || !p_ctx->tracks[state->track]->is_enabled) { /* Skip packet */ status = flv_read_sample_data(p_ctx, state, 0, 0); if(status != VC_CONTAINER_SUCCESS) return status; return VC_CONTAINER_ERROR_CONTINUE; } if((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO)) /* Skip packet */ return flv_read_sample_data(p_ctx, state, 0, 0); packet->dts = packet->pts = state->timestamp * (int64_t)1000; packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END; if(state->flags & FLV_FLAG_KEYFRAME) packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; if(!state->data_offset) packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; packet->track = state->track; // The frame size is all the data packet->frame_size = state->data_size; // the size is what's left packet->size = state->data_size - state->data_offset; if(flags & VC_CONTAINER_READ_FLAG_SKIP) return flv_read_sample_data(p_ctx, state, 0, 0); else if(flags & VC_CONTAINER_READ_FLAG_INFO) return VC_CONTAINER_SUCCESS; data_size = packet->buffer_size; status = flv_read_sample_data(p_ctx, state, packet->data, &data_size); if(status != VC_CONTAINER_SUCCESS) { /* FIXME */ return status; } packet->size = data_size; if(state->data_offset != state->data_size) packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T flv_reader_seek(VC_CONTAINER_T *p_ctx, int64_t *offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; FLV_READER_STATE_T last_state = {0}; FLV_READER_STATE_T *state; uint32_t time = (*offset / 1000), timestamp, previous_time; unsigned int i, track; int size, past = 0; int64_t position; VC_CONTAINER_PARAM_UNUSED(mode); /* If we have a video track, then we want to find the keyframe closest to * the requested time, otherwise we just look for the tag with the * closest timestamp */ /* Select the track on which we'll do our seeking */ for(i = 0, track = 0; i < p_ctx->tracks_num; i++) { if(p_ctx->tracks[i]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) continue; track = i; break; } if(track >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_CORRUPTED; state = p_ctx->tracks[track]->priv->module->state; previous_time = state->timestamp; LOG_DEBUG(p_ctx, "seek (%i, prev %i)", time, previous_time); if(state->index && vc_container_index_get(state->index, flags & VC_CONTAINER_SEEK_FLAG_FORWARD, offset, &position, &past) == VC_CONTAINER_SUCCESS) { flv_update_track_position(p_ctx, track, position, 0, position, 0, (uint32_t) (*offset / 1000LL), 0); } else { if(time < state->timestamp / 2) flv_update_track_position(p_ctx, track, (int64_t)module->data_offset, 0, (int64_t)module->data_offset, 0, 0, 0); past = 1; } /* If past it clear then we're done, otherwise we need to find our point from here */ if(past == 0) { status = flv_find_next_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0); } else { if(time > previous_time) { while(!status) { status = flv_find_next_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0); if(status) break; /* Check if we have our frame */ if(time <= timestamp) break; last_state = *state; state->data_offset = size; /* consume data */ } } else { while(!status) { status = flv_find_previous_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0); if(status) break; /* Check if we have our frame */ if(time >= timestamp) break; /* Detect when we've reached the 1st keyframe to avoid an infinite loop */ if(state->timestamp == last_state.timestamp) break; last_state = *state; state->data_offset = size; /* consume data */ } } } if(status != VC_CONTAINER_SUCCESS && (flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) { LOG_DEBUG(p_ctx, "seek failed (%i)", status); return status; } else if(status != VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "seek failed (%i), look for previous frame", status); if(last_state.tag_position) *state = last_state; else status = flv_find_previous_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0); } LOG_DEBUG(p_ctx, "seek done (%i)", timestamp); state->status = VC_CONTAINER_SUCCESS; last_state.status = VC_CONTAINER_SUCCESS; if(past == 1) { /* Make adjustment based on seek mode */ if((flags & VC_CONTAINER_SEEK_FLAG_FORWARD) && timestamp < time && timestamp < previous_time) { if(last_state.tag_position) *state = last_state; else status = flv_find_next_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0); } else if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD) && timestamp > time) { if(last_state.tag_position) *state = last_state; else status = flv_find_previous_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0); } LOG_DEBUG(p_ctx, "seek adjustment (%i)", timestamp); } if(state->data_position == last_state.data_position) status = SEEK(p_ctx, state->data_position); *offset = timestamp * INT64_C(1000); return VC_CONTAINER_SUCCESS; } /****************************************************************************** Global function definitions. ******************************************************************************/ VC_CONTAINER_STATUS_T flv_reader_open( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = 0; uint8_t buffer[4], type_flags; unsigned int i, frames, audio_present, video_present; uint32_t data_offset; /* Check the FLV marker */ if( PEEK_BYTES(p_ctx, buffer, 4) < 4 ) goto error; if( buffer[0] != 'F' || buffer[1] != 'L' || buffer[2] != 'V' ) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Check FLV version */ if( buffer[3] > 4 ) { LOG_DEBUG(p_ctx, "Version too high: %d", buffer[3]); return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; } SKIP_BYTES(p_ctx, 4); /* FLV marker and version */ /* Find out which tracks should be available. * FLV can only have up to 1 audio track and 1 video track. */ type_flags = READ_U8(p_ctx, "TypeFlags"); audio_present = !!(type_flags & 0x04); video_present = !!(type_flags & 0x01); /* Sanity check DataOffset */ data_offset = READ_U32(p_ctx, "DataOffset"); if(data_offset < 9) goto error; /* * We are dealing with an FLV file */ LOG_DEBUG(p_ctx, "using flv reader"); /* Allocate our context */ module = malloc(sizeof(*module)); if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); p_ctx->priv->module = module; p_ctx->tracks = module->tracks; module->data_offset = data_offset; module->audio_track = -1; module->video_track = -1; /* Skip to the start of the actual data */ SKIP_BYTES(p_ctx, data_offset - 9); /* We'll start parsing a few of the FLV tags to find out the * metadata / audio / video properties. * The first tag we should see is the metadata one which will give us all the * properties of the stream. However we do not rely on that being there and we * actually look at the first audio / video tags as well. */ for(frames = 0; frames < 20; frames++) { VC_CONTAINER_TRACK_T *track; int64_t offset, skip; int prev_size, type, size, channels, samplerate, bps; uint32_t codec, timestamp; /* Stop there if we have everything we want */ if(audio_present == (module->audio_track >= 0) && video_present == (module->video_track >= 0)) break; if(module->audio_track >= 0 && module->video_track >= 0) break; /* Start reading the next tag */ if(flv_read_tag_header(p_ctx, &prev_size, &type, &size, ×tamp)) break; if(!size) continue; offset = STREAM_POSITION(p_ctx); /* to keep track of how much data we read */ switch(type) { case FLV_TAG_TYPE_AUDIO: if(module->audio_track >= 0) break; /* We already have our audio track */ flv_read_audiodata_header(p_ctx, &codec, &samplerate, &channels, &bps); p_ctx->tracks[p_ctx->tracks_num] = track = vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; track->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; track->format->codec = codec; flv_read_audiodata_properties(p_ctx, track, size - 1, samplerate, channels, bps); module->audio_track = p_ctx->tracks_num++; track->is_enabled = 1; track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; break; case FLV_TAG_TYPE_VIDEO: if(module->video_track >= 0) break; /* We already have our video track */ flv_read_videodata_header(p_ctx, &codec, 0); p_ctx->tracks[p_ctx->tracks_num] = track = vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; track->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; track->format->codec = codec; status = flv_read_videodata_properties(p_ctx, track, size - 1); if(status != VC_CONTAINER_SUCCESS) { vc_container_free_track(p_ctx, track); break; } module->video_track = p_ctx->tracks_num++; track->is_enabled = 1; track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; break; case FLV_TAG_TYPE_METADATA: flv_read_metadata(p_ctx, size); break; default: break; } /* Skip any data that's left unparsed from the current tag */ skip = size - (STREAM_POSITION(p_ctx) - offset); if(skip < 0) break; SKIP_BYTES(p_ctx, (size_t)skip); } /* Make sure we found something we can play */ if(!p_ctx->tracks_num) {LOG_DEBUG(p_ctx, "didn't find any track"); goto error;} /* Try and create an index. All times are signed, so adding a base timestamp * of zero means that we will always seek back to the start of the file, even if * the actual frame timestamps start at some higher number. */ if(vc_container_index_create(&module->state.index, 512) == VC_CONTAINER_SUCCESS) vc_container_index_add(module->state.index, 0LL, (int64_t) data_offset); /* Use the metadata we read */ if(module->audio_track >= 0) { VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->audio_track]; track->format->bitrate = module->meta_audiodatarate; } if(module->video_track >= 0) { VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->video_track]; track->format->bitrate = module->meta_videodatarate; if(module->meta_framerate) { track->format->type->video.frame_rate_num = (uint32_t)(100 * module->meta_framerate); track->format->type->video.frame_rate_den = 100; } if(module->meta_width && module->meta_width > track->format->type->video.width) track->format->type->video.width = module->meta_width; if(module->meta_height && module->meta_height > track->format->type->video.height) track->format->type->video.height = module->meta_height; } status = SEEK(p_ctx, data_offset); if(status != VC_CONTAINER_SUCCESS) goto error; /* Some initialisation */ module->state.tag_position = data_offset; module->state.data_position = data_offset; for(i = 0; i < p_ctx->tracks_num; i++) { VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[i]->priv->module; track_module->state = &module->state; } if(STREAM_SEEKABLE(p_ctx)) p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; p_ctx->priv->pf_close = flv_reader_close; p_ctx->priv->pf_read = flv_reader_read; p_ctx->priv->pf_seek = flv_reader_seek; return VC_CONTAINER_SUCCESS; error: if(status == VC_CONTAINER_SUCCESS) status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; LOG_DEBUG(p_ctx, "flv: error opening stream"); if(module) flv_reader_close(p_ctx); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak reader_open flv_reader_open #endif userland/containers/h264/000077500000000000000000000000001421703157200155255ustar00rootroot00000000000000userland/containers/h264/avc1_packetizer.c000066400000000000000000000275341421703157200207570ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ /** \file * Implementation of an ISO 14496-15 to Annexe-B AVC video packetizer. */ #include #include #include "containers/packetizers.h" #include "containers/core/packetizers_private.h" #include "containers/core/containers_common.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_bytestream.h" #ifndef ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE //#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE #endif /** Arbitrary number which should be sufficiently high so that no sane frame will * be bigger than that. */ #define MAX_FRAME_SIZE (1920*1088*2) VC_CONTAINER_STATUS_T avc1_packetizer_open( VC_PACKETIZER_T * ); /*****************************************************************************/ typedef struct VC_PACKETIZER_MODULE_T { enum { STATE_FRAME_WAIT = 0, STATE_BUFFER_INIT, STATE_NAL_START, STATE_NAL_DATA, } state; unsigned int length_size; unsigned int frame_size; unsigned int bytes_read; unsigned int start_code_bytes_left; unsigned int nal_bytes_left; } VC_PACKETIZER_MODULE_T; static const uint8_t h264_start_code[] = {0, 0, 0, 1}; /*****************************************************************************/ static VC_CONTAINER_STATUS_T avc1_packetizer_close( VC_PACKETIZER_T *p_ctx ) { free(p_ctx->priv->module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avc1_packetizer_reset( VC_PACKETIZER_T *p_ctx ) { VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; module->state = STATE_FRAME_WAIT; module->frame_size = 0; module->bytes_read = 0; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avc1_packetizer_packetize( VC_PACKETIZER_T *p_ctx, VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags) { VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream; VC_CONTAINER_PACKET_T *packet; unsigned int offset, size, nal_num; uint8_t data[4]; VC_CONTAINER_PARAM_UNUSED(nal_num); while(1) switch (module->state) { case STATE_FRAME_WAIT: for (packet = stream->current, size = 0; packet && !(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END); packet = packet->next) size += packet->size; if (!packet) return VC_CONTAINER_ERROR_INCOMPLETE_DATA; /* We need more data */ size += packet->size; /* We now have a complete frame available */ module->nal_bytes_left = 0; module->start_code_bytes_left = 0; /* Find out the number of NAL units and size of the frame */ for (offset = nal_num = 0; offset + module->length_size < size; nal_num++) { unsigned int nal_size; bytestream_peek_at(stream, offset, data, module->length_size); offset += module->length_size; nal_size = data[0]; if (module->length_size > 1) nal_size = (nal_size << 8)|data[1]; if (module->length_size > 2) nal_size = (nal_size << 8)|data[2]; if (module->length_size > 3) nal_size = (nal_size << 8)|data[3]; if (offset + nal_size > size) nal_size = size - offset; offset += nal_size; module->frame_size += nal_size + sizeof(h264_start_code); #ifdef ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE LOG_DEBUG(0, "nal unit size %u", nal_size); #endif } LOG_DEBUG(0, "frame size: %u(%u/%u), pts: %"PRIi64, module->frame_size, size, nal_num, stream->current->pts); /* fall through to the next state */ module->state = STATE_BUFFER_INIT; case STATE_BUFFER_INIT: packet = stream->current; out->size = module->frame_size - module->bytes_read; out->pts = out->dts = VC_CONTAINER_TIME_UNKNOWN; out->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END; if (!module->bytes_read) { out->pts = packet->pts; out->dts = packet->dts; out->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; } if (flags & VC_PACKETIZER_FLAG_INFO) return VC_CONTAINER_SUCCESS; if (flags & VC_PACKETIZER_FLAG_SKIP) { /* The easiest is to just drop all the packets belonging to the frame */ while (!(stream->current->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END)) bytestream_skip_packet(stream); bytestream_skip_packet(stream); module->frame_size = 0; module->bytes_read = 0; return VC_CONTAINER_SUCCESS; } /* We now know that we'll have to read some data so reset the output size */ out->size = 0; /* Go to the next relevant state */ module->state = STATE_NAL_START; if (module->nal_bytes_left || module->bytes_read == module->frame_size) module->state = STATE_NAL_DATA; break; case STATE_NAL_START: /* Extract the size of the current NAL */ bytestream_get(stream, data, module->length_size); module->nal_bytes_left = data[0]; if (module->length_size > 1) module->nal_bytes_left = (module->nal_bytes_left << 8)|data[1]; if (module->length_size > 2) module->nal_bytes_left = (module->nal_bytes_left << 8)|data[2]; if (module->length_size > 3) module->nal_bytes_left = (module->nal_bytes_left << 8)|data[3]; if (module->bytes_read + module->nal_bytes_left + sizeof(h264_start_code) > module->frame_size) { LOG_ERROR(0, "truncating nal (%u/%u)", module->nal_bytes_left, module->frame_size - module->bytes_read - sizeof(h264_start_code)); module->nal_bytes_left = module->frame_size - sizeof(h264_start_code); } #ifdef ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE LOG_DEBUG(0, "nal unit size %u", module->nal_bytes_left); #endif module->start_code_bytes_left = sizeof(h264_start_code); /* fall through to the next state */ module->state = STATE_NAL_DATA; case STATE_NAL_DATA: /* Start by adding the start code */ if (module->start_code_bytes_left) { size = MIN(out->buffer_size - out->size, module->start_code_bytes_left); memcpy(out->data + out->size, h264_start_code + sizeof(h264_start_code) - module->start_code_bytes_left, size); module->start_code_bytes_left -= size; module->bytes_read += size; out->size += size; } /* Then append the NAL unit itself */ if (module->nal_bytes_left) { size = MIN(out->buffer_size - out->size, module->nal_bytes_left); bytestream_get( stream, out->data + out->size, size ); module->nal_bytes_left -= size; module->bytes_read += size; out->size += size; } /* Check whether we're done */ if (module->bytes_read == module->frame_size) { bytestream_skip_packet(stream); module->state = STATE_FRAME_WAIT; module->frame_size = 0; module->bytes_read = 0; return VC_CONTAINER_SUCCESS; } else if (out->buffer_size == out->size) { out->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; module->state = STATE_BUFFER_INIT; return VC_CONTAINER_SUCCESS; } /* We're not done, go to the next relevant state */ module->state = STATE_NAL_START; break; default: break; }; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T avc1_packetizer_codecconfig( VC_PACKETIZER_T *p_ctx ) { VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status; uint8_t *out, *extra = p_ctx->in->extradata + 5; uint8_t *extra_end = extra + p_ctx->in->extradata_size - 5; unsigned int i, j, nal_size, out_size = 0; if (p_ctx->in->extradata_size <= 5 || p_ctx->in->extradata[0] != 1 /* configurationVersion */) return VC_CONTAINER_ERROR_FORMAT_INVALID; status = vc_container_format_extradata_alloc(p_ctx->out, p_ctx->in->extradata_size); if (status != VC_CONTAINER_SUCCESS) return status; out = p_ctx->out->extradata; module->length_size = (*(p_ctx->in->extradata + 4) & 0x3) + 1; for (i = 0; i < 2 && extra < extra_end - 1; i++) { j = *(extra++) & (!i ? 0x1F : 0xFF); for (; j > 0 && extra < extra_end - 2; j--) { nal_size = (extra[0] << 8) | extra[1]; extra += 2; if (extra + nal_size > extra_end) { extra = extra_end; break; } out[0] = out[1] = out[2] = 0; out[3] = 1; memcpy(out + 4, extra, nal_size); out += nal_size + 4; extra += nal_size; out_size += nal_size + 4; } } p_ctx->out->extradata_size = out_size; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T avc1_packetizer_open( VC_PACKETIZER_T *p_ctx ) { VC_PACKETIZER_MODULE_T *module; VC_CONTAINER_STATUS_T status; if(p_ctx->in->codec != VC_CONTAINER_CODEC_H264 && p_ctx->out->codec != VC_CONTAINER_CODEC_H264) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if(p_ctx->in->codec_variant != VC_CONTAINER_VARIANT_H264_AVC1 && p_ctx->out->codec_variant != VC_CONTAINER_VARIANT_H264_DEFAULT) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if(!(p_ctx->in->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; p_ctx->priv->module = module = malloc(sizeof(*module)); if(!module) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; memset(module, 0, sizeof(*module)); vc_container_format_copy(p_ctx->out, p_ctx->in, 0); status = avc1_packetizer_codecconfig(p_ctx); if (status != VC_CONTAINER_SUCCESS) { free(module); return status; } p_ctx->out->codec_variant = VC_CONTAINER_VARIANT_H264_DEFAULT; p_ctx->max_frame_size = MAX_FRAME_SIZE; p_ctx->priv->pf_close = avc1_packetizer_close; p_ctx->priv->pf_packetize = avc1_packetizer_packetize; p_ctx->priv->pf_reset = avc1_packetizer_reset; LOG_DEBUG(0, "using avc1 video packetizer"); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_PACKETIZER_REGISTER(avc1_packetizer_open, "avc1"); userland/containers/io/000077500000000000000000000000001421703157200154515ustar00rootroot00000000000000userland/containers/io/io_file.c000066400000000000000000000125151421703157200172270ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include "containers/containers.h" #include "containers/core/containers_common.h" #include "containers/core/containers_io.h" #include "containers/core/containers_uri.h" typedef struct VC_CONTAINER_IO_MODULE_T { FILE *stream; } VC_CONTAINER_IO_MODULE_T; VC_CONTAINER_STATUS_T vc_container_io_file_open( VC_CONTAINER_IO_T *, const char *, VC_CONTAINER_IO_MODE_T ); /*****************************************************************************/ static VC_CONTAINER_STATUS_T io_file_close( VC_CONTAINER_IO_T *p_ctx ) { VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; fclose(module->stream); free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static size_t io_file_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) { size_t ret = fread(buffer, 1, size, p_ctx->module->stream); if(ret != size) { /* Sanity check return value. Some platforms (e.g. Android) can return -1 */ if( ((int)ret) < 0 ) ret = 0; if( feof(p_ctx->module->stream) ) p_ctx->status = VC_CONTAINER_ERROR_EOS; else p_ctx->status = VC_CONTAINER_ERROR_FAILED; } return ret; } /*****************************************************************************/ static size_t io_file_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size) { return fwrite(buffer, 1, size, p_ctx->module->stream); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T io_file_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; int ret; //FIXME: large file support #ifdef _VIDEOCORE extern int fseek64(FILE *fp, int64_t offset, int whence); ret = fseek64(p_ctx->module->stream, offset, SEEK_SET); #else if (offset > (int64_t)UINT_MAX) { p_ctx->status = VC_CONTAINER_ERROR_EOS; return VC_CONTAINER_ERROR_EOS; } ret = fseek(p_ctx->module->stream, (long)offset, SEEK_SET); #endif if(ret) { if( feof(p_ctx->module->stream) ) status = VC_CONTAINER_ERROR_EOS; else status = VC_CONTAINER_ERROR_FAILED; } p_ctx->status = status; return status; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_io_file_open( VC_CONTAINER_IO_T *p_ctx, const char *unused, VC_CONTAINER_IO_MODE_T mode ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_IO_MODULE_T *module = 0; const char *psz_mode = mode == VC_CONTAINER_IO_MODE_WRITE ? "wb+" : "rb"; const char *uri = p_ctx->uri; FILE *stream = 0; VC_CONTAINER_PARAM_UNUSED(unused); if(vc_uri_path(p_ctx->uri_parts)) uri = vc_uri_path(p_ctx->uri_parts); stream = fopen(uri, psz_mode); if(!stream) { status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; } /* Turn off buffering. The container layer will provide its own cache */ setvbuf(stream, NULL, _IONBF, 0); module = malloc( sizeof(*module) ); if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); p_ctx->module = module; module->stream = stream; p_ctx->pf_close = io_file_close; p_ctx->pf_read = io_file_read; p_ctx->pf_write = io_file_write; p_ctx->pf_seek = io_file_seek; if(mode == VC_CONTAINER_IO_MODE_WRITE) { p_ctx->max_size = (1UL<<31)-1; /* For now limit to 2GB */ } else { //FIXME: large file support, platform-specific file size fseek(p_ctx->module->stream, 0, SEEK_END); p_ctx->size = ftell(p_ctx->module->stream); fseek(p_ctx->module->stream, 0, SEEK_SET); } p_ctx->capabilities = VC_CONTAINER_IO_CAPS_NO_CACHING; return VC_CONTAINER_SUCCESS; error: if(stream) fclose(stream); return status; } userland/containers/io/io_http.c000066400000000000000000000704611421703157200172730ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include "containers/containers.h" #include "containers/core/containers_common.h" #include "containers/core/containers_io.h" #include "containers/core/containers_uri.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_list.h" #include "containers/core/containers_utils.h" #include "containers/net/net_sockets.h" /* Set to 1 if you want to log all HTTP requests */ #define ENABLE_HTTP_EXTRA_LOGGING 0 /****************************************************************************** Defines and constants. ******************************************************************************/ #define IO_HTTP_DEFAULT_PORT "80" /** Space for sending requests and receiving responses */ #define COMMS_BUFFER_SIZE 4000 /** Largest allowed HTTP URI. Must be substantially smaller than COMMS_BUFFER_SIZE * to allow for the headers that may be sent. */ #define HTTP_URI_LENGTH_MAX 1024 /** Initial capacity of header list */ #define HEADER_LIST_INITIAL_CAPACITY 16 /** Format of the first line of an HTTP request */ #define HTTP_REQUEST_LINE_FORMAT "%s %s HTTP/1.1\r\nHost: %s\r\n" /** Format of a range request */ #define HTTP_RANGE_REQUEST "Range: bytes=%"PRId64"-%"PRId64"\r\n" /** Format string for common headers used with all request methods. * Note: includes double new line to terminate headers */ #define TRAILING_HEADERS_FORMAT "User-Agent: Broadcom/1.0\r\n\r\n" /** \name HTTP methods, used as the first item in the request line * @{ */ #define GET_METHOD "GET" #define HEAD_METHOD "HEAD" /* @} */ /** \name Names of headers used by the code * @{ */ #define CONTENT_LENGTH_NAME "Content-Length" #define CONTENT_BASE_NAME "Content-Base" #define CONTENT_LOCATION_NAME "Content-Location" #define ACCEPT_RANGES_NAME "Accept-Ranges" #define CONNECTION_NAME "Connection" /* @} */ /** Supported HTTP major version number */ #define HTTP_MAJOR_VERSION 1 /** Supported HTTP minor version number */ #define HTTP_MINOR_VERSION 1 /** Lowest successful status code value */ #define HTTP_STATUS_OK 200 #define HTTP_STATUS_PARTIAL_CONTENT 206 typedef struct http_header_tag { const char *name; char *value; } HTTP_HEADER_T; /****************************************************************************** Type definitions ******************************************************************************/ typedef struct VC_CONTAINER_IO_MODULE_T { VC_CONTAINER_NET_T *sock; VC_CONTAINERS_LIST_T *header_list; /**< Parsed response headers, pointing into comms buffer */ bool persistent; int64_t cur_offset; bool reconnecting; /* Buffer used for sending and receiving HTTP messages */ char comms_buffer[COMMS_BUFFER_SIZE]; } VC_CONTAINER_IO_MODULE_T; /****************************************************************************** Function prototypes ******************************************************************************/ static int io_http_header_comparator(const HTTP_HEADER_T *first, const HTTP_HEADER_T *second); static VC_CONTAINER_STATUS_T io_http_send(VC_CONTAINER_IO_T *p_ctx); VC_CONTAINER_STATUS_T vc_container_io_http_open(VC_CONTAINER_IO_T *, const char *, VC_CONTAINER_IO_MODE_T); /****************************************************************************** Local Functions ******************************************************************************/ /**************************************************************************//** * Trim whitespace from the end and start of the string * * \param str String to be trimmed * \return Trimmed string */ static char *io_http_trim(char *str) { char *s = str + strlen(str); /* Search backwards for first non-whitespace */ while (--s >= str &&(*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')) ; /* Everything done in the while */ s[1] = '\0'; /* Now move start of string forwards to first non-whitespace */ s = str; while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r') s++; return s; } /**************************************************************************//** * Header comparison function. * Compare two header structures and return whether the first is less than, * equal to or greater than the second. * * @param first The first structure to be compared. * @param second The second structure to be compared. * @return Negative if first is less than second, positive if first is greater * and zero if they are equal. */ static int io_http_header_comparator(const HTTP_HEADER_T *first, const HTTP_HEADER_T *second) { return strcasecmp(first->name, second->name); } /**************************************************************************//** * Check a response status line to see if the response is usable or not. * Reasons for invalidity include: * - Incorrectly formatted * - Unsupported version * - Status code is not in the 2xx range * * @param status_line The response status line. * @return The resulting status of the function. */ static bool io_http_successful_response_status(const char *status_line) { unsigned int major_version, minor_version, status_code; /* coverity[secure_coding] String is null-terminated */ if (sscanf(status_line, "HTTP/%u.%u %u", &major_version, &minor_version, &status_code) != 3) { LOG_ERROR(NULL, "HTTP: Invalid response status line:\n%s", status_line); return false; } if (major_version != HTTP_MAJOR_VERSION || minor_version != HTTP_MINOR_VERSION) { LOG_ERROR(NULL, "HTTP: Unexpected response HTTP version: %u.%u", major_version, minor_version); return false; } if (status_code != HTTP_STATUS_OK && status_code != HTTP_STATUS_PARTIAL_CONTENT) { LOG_ERROR(NULL, "HTTP: Response status unsuccessful:\n%s", status_line); return false; } return true; } /**************************************************************************//** * Get the content length header from the response headers as an unsigned * 64-bit integer. * If the content length header is not found or badly formatted, zero is * returned. * * @param header_list The response headers. * @return The content length. */ static uint64_t io_http_get_content_length(VC_CONTAINERS_LIST_T *header_list) { uint64_t content_length = 0; HTTP_HEADER_T header; header.name = CONTENT_LENGTH_NAME; if (header_list && vc_containers_list_find_entry(header_list, &header)) /* coverity[secure_coding] String is null-terminated */ sscanf(header.value, "%"PRIu64, &content_length); return content_length; } /**************************************************************************//** * Get the accept ranges header from the response headers and verify that * the server accepts byte ranges.. * If the accept ranges header is not found false is returned. * * @param header_list The response headers. * @return The resulting status of the function. */ static bool io_http_check_accept_range(VC_CONTAINERS_LIST_T *header_list) { HTTP_HEADER_T header; header.name = ACCEPT_RANGES_NAME; if (header_list && vc_containers_list_find_entry(header_list, &header)) { /* coverity[secure_coding] String is null-terminated */ if (!strcasecmp(header.value, "bytes")) return true; } return false; } /**************************************************************************//** * Check whether the server supports persistent connections. * * @param header_list The response headers. * @return The resulting status of the function. */ static bool io_http_check_persistent_connection(VC_CONTAINERS_LIST_T *header_list) { HTTP_HEADER_T header; header.name = CONNECTION_NAME; if (header_list && vc_containers_list_find_entry(header_list, &header)) { /* coverity[secure_coding] String is null-terminated */ if (!strcasecmp(header.value, "close")) return false; } return true; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T translate_net_status_to_container_status(vc_container_net_status_t net_status) { switch (net_status) { case VC_CONTAINER_NET_SUCCESS: return VC_CONTAINER_SUCCESS; case VC_CONTAINER_NET_ERROR_INVALID_SOCKET: return VC_CONTAINER_ERROR_INVALID_ARGUMENT; case VC_CONTAINER_NET_ERROR_NOT_ALLOWED: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; case VC_CONTAINER_NET_ERROR_INVALID_PARAMETER: return VC_CONTAINER_ERROR_INVALID_ARGUMENT; case VC_CONTAINER_NET_ERROR_NO_MEMORY: return VC_CONTAINER_ERROR_OUT_OF_MEMORY; case VC_CONTAINER_NET_ERROR_IN_USE: return VC_CONTAINER_ERROR_URI_OPEN_FAILED; case VC_CONTAINER_NET_ERROR_NETWORK: return VC_CONTAINER_ERROR_EOS; case VC_CONTAINER_NET_ERROR_CONNECTION_LOST: return VC_CONTAINER_ERROR_EOS; case VC_CONTAINER_NET_ERROR_NOT_CONNECTED: return VC_CONTAINER_ERROR_INVALID_ARGUMENT; case VC_CONTAINER_NET_ERROR_TIMED_OUT: return VC_CONTAINER_ERROR_ABORTED; case VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED: return VC_CONTAINER_ERROR_NOT_FOUND; case VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND: return VC_CONTAINER_ERROR_NOT_FOUND; case VC_CONTAINER_NET_ERROR_TRY_AGAIN: return VC_CONTAINER_ERROR_CONTINUE; default: return VC_CONTAINER_ERROR_FAILED; } } /*****************************************************************************/ static VC_CONTAINER_STATUS_T io_http_open_socket(VC_CONTAINER_IO_T *ctx) { VC_CONTAINER_IO_MODULE_T *module = ctx->module; VC_CONTAINER_STATUS_T status; const char *host, *port; /* Treat empty host or port strings as not defined */ port = vc_uri_port(ctx->uri_parts); if (port && !*port) port = NULL; /* Require the port to be defined */ if (!port) { status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; goto error; } host = vc_uri_host(ctx->uri_parts); if (host && !*host) host = NULL; if (!host) { status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; goto error; } module->sock = vc_container_net_open(host, port, VC_CONTAINER_NET_OPEN_FLAG_STREAM, NULL); if (!module->sock) { status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; } return VC_CONTAINER_SUCCESS; error: return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T io_http_close_socket(VC_CONTAINER_IO_MODULE_T *module) { if (module->sock) { vc_container_net_close(module->sock); module->sock = NULL; } return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static size_t io_http_read_from_net(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) { size_t ret; vc_container_net_status_t net_status; ret = vc_container_net_read(p_ctx->module->sock, buffer, size); net_status = vc_container_net_status(p_ctx->module->sock); p_ctx->status = translate_net_status_to_container_status(net_status); return ret; } /**************************************************************************//** * Reads an HTTP response and parses it into headers and content. * The headers and content remain stored in the comms buffer, but referenced * by the module's header list. Content uses a special header name that cannot * occur in the real headers. * * @param p_ctx The HTTP reader context. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T io_http_read_response(VC_CONTAINER_IO_T *p_ctx) { VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; char *next_read = module->comms_buffer; size_t space_available = sizeof(module->comms_buffer) - 1; /* Allow for a NUL */ char *ptr = next_read; bool end_response = false; HTTP_HEADER_T header; const char endstr[] = "\r\n\r\n"; int endcount = sizeof(endstr) - 1; int endchk = 0; vc_containers_list_reset(module->header_list); /* Response status line doesn't need to be stored, just checked */ header.name = NULL; header.value = next_read; /* * We need to read just a byte at a time to make sure that we just read the HTTP response and * no more. For example, if a GET operation was requested the file being fetched will also * be waiting to be read on the socket. */ while (space_available) { if (io_http_read_from_net(p_ctx, next_read, 1) != 1) break; next_read++; space_available--; if (next_read[-1] == endstr[endchk]) { if (++endchk == endcount) break; } else endchk = 0; } if (!space_available) { LOG_ERROR(NULL, "comms buffer too small for complete HTTP message (%d)", sizeof(module->comms_buffer)); return VC_CONTAINER_ERROR_CORRUPTED; } *next_read = '\0'; if (endchk == endcount) { if (ENABLE_HTTP_EXTRA_LOGGING) LOG_DEBUG(NULL, "READ FROM SERVER: %d bytes\n%s\n-----------------------------------------", sizeof(module->comms_buffer) - 1 - space_available, module->comms_buffer); while (!end_response && ptr < next_read) { switch (*ptr) { case ':': if (header.value) { /* Just another character in the value */ ptr++; } else { /* End of name, expect value next */ *ptr++ = '\0'; header.value = ptr; } break; case '\n': if (header.value) { /* End of line while parsing the value part of the header, add name/value pair to list */ *ptr++ = '\0'; header.value = io_http_trim(header.value); if (header.name) { if (!vc_containers_list_insert(module->header_list, &header, false)) { LOG_ERROR(NULL, "HTTP: Failed to add <%s> header to list", header.name); return VC_CONTAINER_ERROR_OUT_OF_MEMORY; } } else { /* Check response status line */ if (!io_http_successful_response_status(header.value)) return VC_CONTAINER_ERROR_FORMAT_INVALID; } /* Ready for next header */ header.name = ptr; header.value = NULL; } else { /* End of line while parsing the name of a header */ *ptr++ = '\0'; if (*header.name && *header.name != '\r') { /* A non-empty name is invalid, so fail */ LOG_ERROR(NULL, "HTTP: Invalid name in header - no colon:\n%s", header.name); return VC_CONTAINER_ERROR_FORMAT_INVALID; } /* An empty name signifies the end of the HTTP response */ end_response = true; } break; default: /* Just another character in either the name or the value */ ptr++; } } } if (!space_available && !end_response) { /* Ran out of buffer space */ LOG_ERROR(NULL, "HTTP: Response header section too big"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } return p_ctx->status; } /**************************************************************************//** * Send a GET request to the HTTP server. * * @param p_ctx The reader context. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T io_http_send_get_request(VC_CONTAINER_IO_T *p_ctx, size_t size) { VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; char *ptr = module->comms_buffer, *end = ptr + sizeof(module->comms_buffer); int64_t end_offset; ptr += snprintf(ptr, end - ptr, HTTP_REQUEST_LINE_FORMAT, GET_METHOD, vc_uri_path(p_ctx->uri_parts), vc_uri_host(p_ctx->uri_parts)); end_offset = module->cur_offset + size - 1; if (end_offset >= p_ctx->size) end_offset = p_ctx->size - 1; if (ptr < end) ptr += snprintf(ptr, end - ptr, HTTP_RANGE_REQUEST, module->cur_offset, end_offset); if (ptr < end) ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT); if (ptr >= end) { LOG_ERROR(0, "comms buffer too small (%i/%u)", (int)(end - ptr), sizeof(module->comms_buffer)); return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; } if (ENABLE_HTTP_EXTRA_LOGGING) LOG_DEBUG(NULL, "Sending server read request:\n%s\n---------------------\n", module->comms_buffer); return io_http_send(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T io_http_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset) { VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; /* * No seeking past the end of the file. */ if (offset < 0 || offset > p_ctx->size) { p_ctx->status = VC_CONTAINER_ERROR_EOS; return VC_CONTAINER_ERROR_EOS; } module->cur_offset = offset; p_ctx->status = VC_CONTAINER_SUCCESS; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T io_http_close(VC_CONTAINER_IO_T *p_ctx) { VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; if (!module) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; io_http_close_socket(module); if (module->header_list) vc_containers_list_destroy(module->header_list); free(module); p_ctx->module = NULL; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static size_t io_http_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; size_t content_length; size_t bytes_read; size_t ret = 0; char *ptr = buffer; /* * Are we at the end of the file? */ if (module->cur_offset >= p_ctx->size) { p_ctx->status = VC_CONTAINER_ERROR_EOS; return 0; } if (!module->persistent) { status = io_http_open_socket(p_ctx); if (status != VC_CONTAINER_SUCCESS) { LOG_ERROR(NULL, "Error opening socket for GET request"); return status; } } /* Send GET request and get response */ status = io_http_send_get_request(p_ctx, size); if (status != VC_CONTAINER_SUCCESS) { LOG_ERROR(NULL, "Error sending GET request"); goto error; } status = io_http_read_response(p_ctx); if (status == VC_CONTAINER_ERROR_EOS && !module->reconnecting) { LOG_DEBUG(NULL, "reconnecting"); io_http_close_socket(module); status = io_http_open_socket(p_ctx); if (status == VC_CONTAINER_SUCCESS) { module->reconnecting = true; status = io_http_read(p_ctx, buffer, size); module->reconnecting = false; return status; } } if (status != VC_CONTAINER_SUCCESS) { LOG_ERROR(NULL, "Error reading GET response"); goto error; } /* * How much data is the server offering us? */ content_length = (size_t)io_http_get_content_length(module->header_list); if (content_length > size) { LOG_ERROR(NULL, "received too much data (%i/%i)", (int)content_length, (int)size); status = VC_CONTAINER_ERROR_CORRUPTED; goto error; } bytes_read = 0; while (bytes_read < content_length && p_ctx->status == VC_CONTAINER_SUCCESS) { ret = io_http_read_from_net(p_ctx, ptr, content_length - bytes_read); if (p_ctx->status == VC_CONTAINER_SUCCESS) { bytes_read += ret; ptr += ret; } } if (p_ctx->status == VC_CONTAINER_SUCCESS) { module->cur_offset += bytes_read; ret = bytes_read; } if (!module->persistent) io_http_close_socket(module); return ret; error: if (!module->persistent) io_http_close_socket(module); return status; } /*****************************************************************************/ static size_t io_http_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size) { size_t ret = vc_container_net_write(p_ctx->module->sock, buffer, size); vc_container_net_status_t net_status; net_status = vc_container_net_status(p_ctx->module->sock); p_ctx->status = translate_net_status_to_container_status(net_status); return ret; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T io_http_control(struct VC_CONTAINER_IO_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args) { vc_container_net_status_t net_status; VC_CONTAINER_STATUS_T status; switch (operation) { case VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE: net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE, args); break; case VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS: net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS, args); break; default: net_status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; } status = translate_net_status_to_container_status(net_status); p_ctx->status = status; return status; } /**************************************************************************//** * Send out the data in the comms buffer. * * @param p_ctx The reader context. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T io_http_send(VC_CONTAINER_IO_T *p_ctx) { VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; size_t to_write; size_t written; const char *buffer = module->comms_buffer; to_write = strlen(buffer); while (to_write) { written = io_http_write(p_ctx, buffer, to_write); if (p_ctx->status != VC_CONTAINER_SUCCESS) break; to_write -= written; buffer += written; } return p_ctx->status; } /**************************************************************************//** * Send a HEAD request to the HTTP server. * * @param p_ctx The reader context. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T io_http_send_head_request(VC_CONTAINER_IO_T *p_ctx) { VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; char *ptr = module->comms_buffer, *end = ptr + sizeof(module->comms_buffer); ptr += snprintf(ptr, end - ptr, HTTP_REQUEST_LINE_FORMAT, HEAD_METHOD, vc_uri_path(p_ctx->uri_parts), vc_uri_host(p_ctx->uri_parts)); if (ptr < end) ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT); if (ptr >= end) { LOG_ERROR(0, "comms buffer too small (%i/%u)", (int)(end - ptr), sizeof(module->comms_buffer)); return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; } return io_http_send(p_ctx); } static VC_CONTAINER_STATUS_T io_http_head(VC_CONTAINER_IO_T *p_ctx) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; uint64_t content_length; /* Send HEAD request and get response */ status = io_http_send_head_request(p_ctx); if (status != VC_CONTAINER_SUCCESS) return status; status = io_http_read_response(p_ctx); if (status != VC_CONTAINER_SUCCESS) return status; /* * Save the content length since that's our file size. */ content_length = io_http_get_content_length(module->header_list); if (content_length) { p_ctx->size = content_length; LOG_DEBUG(NULL, "File size is %"PRId64, p_ctx->size); } /* * Now make sure that the server supports byte range requests. */ if (!io_http_check_accept_range(module->header_list)) { LOG_ERROR(NULL, "Server doesn't support byte range requests"); return VC_CONTAINER_ERROR_FAILED; } /* * Does it support persistent connections? */ if (io_http_check_persistent_connection(module->header_list)) { module->persistent = true; } else { LOG_DEBUG(NULL, "Server does not support persistent connections"); io_http_close_socket(module); } module->cur_offset = 0; return status; } /***************************************************************************** Functions exported as part of the I/O Module API *****************************************************************************/ /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_io_http_open(VC_CONTAINER_IO_T *p_ctx, const char *unused, VC_CONTAINER_IO_MODE_T mode) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_IO_MODULE_T *module = 0; VC_CONTAINER_PARAM_UNUSED(unused); /* Check the URI to see if we're dealing with an http stream */ if (!vc_uri_scheme(p_ctx->uri_parts) || strcasecmp(vc_uri_scheme(p_ctx->uri_parts), "http")) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* * Some basic error checking. */ if (mode == VC_CONTAINER_IO_MODE_WRITE) { status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; goto error; } if (strlen(p_ctx->uri) > HTTP_URI_LENGTH_MAX) { status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; goto error; } module = calloc(1, sizeof(*module)); if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } p_ctx->module = module; /* header_list will contain pointers into the response_buffer, so take care in re-use */ module->header_list = vc_containers_list_create(HEADER_LIST_INITIAL_CAPACITY, sizeof(HTTP_HEADER_T), (VC_CONTAINERS_LIST_COMPARATOR_T)io_http_header_comparator); if (!module->header_list) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } /* * Make sure that we have a port number. */ if (vc_uri_port(p_ctx->uri_parts) == NULL) vc_uri_set_port(p_ctx->uri_parts, IO_HTTP_DEFAULT_PORT); status = io_http_open_socket(p_ctx); if (status != VC_CONTAINER_SUCCESS) goto error; /* * Whoo hoo! Our socket is open. Now let's send a HEAD request. */ status = io_http_head(p_ctx); if (status != VC_CONTAINER_SUCCESS) goto error; p_ctx->pf_close = io_http_close; p_ctx->pf_read = io_http_read; p_ctx->pf_write = NULL; p_ctx->pf_control = io_http_control; p_ctx->pf_seek = io_http_seek; p_ctx->capabilities = VC_CONTAINER_IO_CAPS_NO_CACHING; p_ctx->capabilities |= VC_CONTAINER_IO_CAPS_SEEK_SLOW; return VC_CONTAINER_SUCCESS; error: io_http_close(p_ctx); return status; } userland/containers/io/io_net.c000066400000000000000000000321201421703157200170700ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include "containers/containers.h" #include "containers/core/containers_common.h" #include "containers/core/containers_io.h" #include "containers/core/containers_uri.h" #include "containers/net/net_sockets.h" /* Uncomment this macro definition to capture data read and written through this interface */ /* #define IO_NET_CAPTURE_PACKETS */ #ifdef IO_NET_CAPTURE_PACKETS #include #ifdef ENABLE_CONTAINERS_STANDALONE #ifdef _MSC_VER #define IO_NET_CAPTURE_PREFIX "C:\\" #else /* !_MSC_VER */ #define IO_NET_CAPTURE_PREFIX "~/" #endif #else /* !ENABLE_CONTAINERS_STANDALONE */ #define IO_NET_CAPTURE_PREFIX "/mfs/sd/" #endif #define IO_NET_CAPTURE_READ_FILE "capture_read_%s_%s%c.pkt" #define IO_NET_CAPTURE_WRITE_FILE "capture_write_%s_%s%c.pkt" #define IO_NET_CAPTURE_READ_FORMAT IO_NET_CAPTURE_PREFIX IO_NET_CAPTURE_READ_FILE #define IO_NET_CAPTURE_WRITE_FORMAT IO_NET_CAPTURE_PREFIX IO_NET_CAPTURE_WRITE_FILE #define CAPTURE_FILENAME_BUFFER_SIZE 300 #define CAPTURE_BUFFER_SIZE 65536 /** Native byte order word */ #define NATIVE_BYTE_ORDER 0x50415753 #endif /****************************************************************************** Defines and constants. ******************************************************************************/ /****************************************************************************** Type definitions ******************************************************************************/ typedef struct VC_CONTAINER_IO_MODULE_T { VC_CONTAINER_NET_T *sock; #ifdef IO_NET_CAPTURE_PACKETS FILE *read_capture_file; FILE *write_capture_file; #endif } VC_CONTAINER_IO_MODULE_T; /** List of recognised network URI schemes (TCP or UDP). * Note: always use lower case for the scheme name. */ static struct { const char *scheme; bool is_udp; } recognised_schemes[] = { { "rtp:", true }, { "rtsp:", false }, }; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T vc_container_io_net_open( VC_CONTAINER_IO_T *, const char *, VC_CONTAINER_IO_MODE_T ); /****************************************************************************** Local Functions ******************************************************************************/ #ifdef IO_NET_CAPTURE_PACKETS /*****************************************************************************/ static FILE *io_net_open_capture_file(const char *host_str, const char *port_str, bool is_udp, VC_CONTAINER_IO_MODE_T mode) { char filename[CAPTURE_FILENAME_BUFFER_SIZE]; const char *format; FILE *stream = NULL; uint32_t byte_order = NATIVE_BYTE_ORDER; switch (mode) { case VC_CONTAINER_IO_MODE_WRITE: format = IO_NET_CAPTURE_WRITE_FORMAT; break; case VC_CONTAINER_IO_MODE_READ: format = IO_NET_CAPTURE_READ_FORMAT; break; default: /* Invalid mode */ return NULL; } if (!host_str) host_str = ""; if (!port_str) port_str = ""; /* Check filename will fit in buffer */ if (strlen(format) + strlen(host_str) + strlen(port_str) - 4 > CAPTURE_FILENAME_BUFFER_SIZE) return NULL; /* Create the file */ sprintf(filename, format, host_str, port_str, is_udp ? 'u' : 't'); stream = fopen(filename, "wb"); if (!stream) return NULL; /* Buffer plenty of data at a time, if possible */ setvbuf(stream, NULL, _IOFBF, CAPTURE_BUFFER_SIZE); /* Start file with a byte order marker */ if (fwrite(&byte_order, 1, sizeof(byte_order), stream) != sizeof(byte_order)) { /* Failed to write even just the byte order mark - abort */ fclose(stream); stream = NULL; remove(filename); } return stream; } /*****************************************************************************/ static void io_net_capture_write_packet( FILE *stream, const char *buffer, uint32_t buffer_size ) { if (stream && buffer && buffer_size) { fwrite(&buffer_size, 1, sizeof(buffer_size), stream); fwrite(buffer, 1, buffer_size, stream); } } #endif /*****************************************************************************/ static bool io_net_recognise_scheme(const char *uri, bool *is_udp) { size_t ii; const char *scheme; if (!uri) return false; for (ii = 0; ii < countof(recognised_schemes); ii++) { scheme = recognised_schemes[ii].scheme; if (strncmp(scheme, uri, strlen(scheme)) == 0) { *is_udp = recognised_schemes[ii].is_udp; return true; } } return false; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T translate_net_status_to_container_status(vc_container_net_status_t net_status) { switch (net_status) { case VC_CONTAINER_NET_SUCCESS: return VC_CONTAINER_SUCCESS; case VC_CONTAINER_NET_ERROR_INVALID_SOCKET: return VC_CONTAINER_ERROR_INVALID_ARGUMENT; case VC_CONTAINER_NET_ERROR_NOT_ALLOWED: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; case VC_CONTAINER_NET_ERROR_INVALID_PARAMETER: return VC_CONTAINER_ERROR_INVALID_ARGUMENT; case VC_CONTAINER_NET_ERROR_NO_MEMORY: return VC_CONTAINER_ERROR_OUT_OF_MEMORY; case VC_CONTAINER_NET_ERROR_IN_USE: return VC_CONTAINER_ERROR_URI_OPEN_FAILED; case VC_CONTAINER_NET_ERROR_NETWORK: return VC_CONTAINER_ERROR_EOS; case VC_CONTAINER_NET_ERROR_CONNECTION_LOST: return VC_CONTAINER_ERROR_EOS; case VC_CONTAINER_NET_ERROR_NOT_CONNECTED: return VC_CONTAINER_ERROR_INVALID_ARGUMENT; case VC_CONTAINER_NET_ERROR_TIMED_OUT: return VC_CONTAINER_ERROR_ABORTED; case VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED: return VC_CONTAINER_ERROR_NOT_FOUND; case VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND: return VC_CONTAINER_ERROR_NOT_FOUND; case VC_CONTAINER_NET_ERROR_TRY_AGAIN: return VC_CONTAINER_ERROR_CONTINUE; default: return VC_CONTAINER_ERROR_FAILED; } } /*****************************************************************************/ static VC_CONTAINER_STATUS_T io_net_close( VC_CONTAINER_IO_T *p_ctx ) { VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; if (!module) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; if (module->sock) vc_container_net_close(module->sock); #ifdef IO_NET_CAPTURE_PACKETS if (module->read_capture_file) fclose(module->read_capture_file); if (module->write_capture_file) fclose(module->write_capture_file); #endif free(module); p_ctx->module = NULL; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static size_t io_net_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) { size_t ret = vc_container_net_read(p_ctx->module->sock, buffer, size); vc_container_net_status_t net_status; net_status = vc_container_net_status(p_ctx->module->sock); p_ctx->status = translate_net_status_to_container_status(net_status); #ifdef IO_NET_CAPTURE_PACKETS if (p_ctx->status == VC_CONTAINER_SUCCESS) io_net_capture_write_packet(p_ctx->module->read_capture_file, (const char *)buffer, ret); #endif return ret; } /*****************************************************************************/ static size_t io_net_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size) { size_t ret = vc_container_net_write(p_ctx->module->sock, buffer, size); vc_container_net_status_t net_status; net_status = vc_container_net_status(p_ctx->module->sock); p_ctx->status = translate_net_status_to_container_status(net_status); #ifdef IO_NET_CAPTURE_PACKETS if (p_ctx->status == VC_CONTAINER_SUCCESS) io_net_capture_write_packet(p_ctx->module->write_capture_file, (const char *)buffer, ret); #endif return ret; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T io_net_control(struct VC_CONTAINER_IO_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args) { vc_container_net_status_t net_status; VC_CONTAINER_STATUS_T status; switch (operation) { case VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE: net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE, args); break; case VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS: net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS, args); break; default: net_status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; } status = translate_net_status_to_container_status(net_status); p_ctx->status = status; return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T io_net_open_socket(VC_CONTAINER_IO_T *ctx, VC_CONTAINER_IO_MODE_T mode, bool is_udp) { VC_CONTAINER_IO_MODULE_T *module = ctx->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; const char *host, *port; /* Treat empty host or port strings as not defined */ port = vc_uri_port(ctx->uri_parts); if (port && !*port) port = NULL; /* Require the port to be defined */ if (!port) { status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; goto error; } host = vc_uri_host(ctx->uri_parts); if (host && !*host) host = NULL; if (!host) { /* TCP servers cannot be handled by this interface and UDP senders need a target */ if (!is_udp || mode == VC_CONTAINER_IO_MODE_WRITE) { status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; goto error; } } module->sock = vc_container_net_open(host, port, is_udp ? 0 : VC_CONTAINER_NET_OPEN_FLAG_STREAM, NULL); if (!module->sock) { status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; } #ifdef IO_NET_CAPTURE_PACKETS if (!is_udp || mode == VC_CONTAINER_IO_MODE_READ) module->read_capture_file = io_net_open_capture_file(host, port, is_udp, VC_CONTAINER_IO_MODE_READ); if (!is_udp || mode == VC_CONTAINER_IO_MODE_WRITE) module->write_capture_file = io_net_open_capture_file(host, port, is_udp, VC_CONTAINER_IO_MODE_WRITE); #endif error: return status; } /***************************************************************************** Functions exported as part of the I/O Module API *****************************************************************************/ /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_io_net_open( VC_CONTAINER_IO_T *p_ctx, const char *unused, VC_CONTAINER_IO_MODE_T mode ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_IO_MODULE_T *module = 0; bool is_udp; VC_CONTAINER_PARAM_UNUSED(unused); if (!io_net_recognise_scheme(p_ctx->uri, &is_udp)) { status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; } module = (VC_CONTAINER_IO_MODULE_T *)malloc( sizeof(*module) ); if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); p_ctx->module = module; status = io_net_open_socket(p_ctx, mode, is_udp); if (status != VC_CONTAINER_SUCCESS) goto error; p_ctx->pf_close = io_net_close; p_ctx->pf_read = io_net_read; p_ctx->pf_write = io_net_write; p_ctx->pf_control = io_net_control; /* Disable caching, as this will block waiting for enough data to fill the cache or an error */ p_ctx->capabilities = VC_CONTAINER_IO_CAPS_CANT_SEEK; return VC_CONTAINER_SUCCESS; error: io_net_close(p_ctx); return status; } userland/containers/io/io_null.c000066400000000000000000000071321421703157200172610ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include "containers/containers.h" #include "containers/core/containers_common.h" #include "containers/core/containers_io.h" #include "containers/core/containers_uri.h" VC_CONTAINER_STATUS_T vc_container_io_null_open( VC_CONTAINER_IO_T *, const char *, VC_CONTAINER_IO_MODE_T ); /*****************************************************************************/ static VC_CONTAINER_STATUS_T io_null_close( VC_CONTAINER_IO_T *p_ctx ) { VC_CONTAINER_PARAM_UNUSED(p_ctx); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static size_t io_null_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) { VC_CONTAINER_PARAM_UNUSED(p_ctx); VC_CONTAINER_PARAM_UNUSED(buffer); VC_CONTAINER_PARAM_UNUSED(size); return size; } /*****************************************************************************/ static size_t io_null_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size) { VC_CONTAINER_PARAM_UNUSED(p_ctx); VC_CONTAINER_PARAM_UNUSED(buffer); VC_CONTAINER_PARAM_UNUSED(size); return size; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T io_null_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset) { VC_CONTAINER_PARAM_UNUSED(p_ctx); VC_CONTAINER_PARAM_UNUSED(offset); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_io_null_open( VC_CONTAINER_IO_T *p_ctx, const char *unused, VC_CONTAINER_IO_MODE_T mode ) { VC_CONTAINER_PARAM_UNUSED(unused); VC_CONTAINER_PARAM_UNUSED(mode); /* Check the URI */ if (!vc_uri_scheme(p_ctx->uri_parts) || (strcasecmp(vc_uri_scheme(p_ctx->uri_parts), "null") && strcasecmp(vc_uri_scheme(p_ctx->uri_parts), "null"))) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; p_ctx->pf_close = io_null_close; p_ctx->pf_read = io_null_read; p_ctx->pf_write = io_null_write; p_ctx->pf_seek = io_null_seek; return VC_CONTAINER_SUCCESS; } userland/containers/io/io_pktfile.c000066400000000000000000000203261421703157200177450ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include "containers/containers.h" #include "containers/core/containers_common.h" #include "containers/core/containers_io.h" #include "containers/core/containers_uri.h" /** Native byte order word */ #define NATIVE_BYTE_ORDER 0x50415753 /** Reverse of native byte order - need to swap bytes around */ #define SWAP_BYTE_ORDER 0x53574150 typedef struct VC_CONTAINER_IO_MODULE_T { FILE *stream; bool is_native_order; } VC_CONTAINER_IO_MODULE_T; /** List of recognised schemes. * Note: always use lower case for the scheme name. */ static const char * recognised_schemes[] = { "rtp", "rtppkt", "rtsp", "rtsppkt", "pktfile", }; VC_CONTAINER_STATUS_T vc_container_io_pktfile_open( VC_CONTAINER_IO_T *, const char *, VC_CONTAINER_IO_MODE_T ); /*****************************************************************************/ static bool recognise_scheme(const char *scheme) { size_t ii; if (!scheme) return false; for (ii = 0; ii < countof(recognised_schemes); ii++) { if (strcmp(recognised_schemes[ii], scheme) == 0) return true; } return false; } /*****************************************************************************/ static uint32_t swap_byte_order( uint32_t value ) { /* Reverse the order of the bytes in the word */ return ((value << 24) | ((value & 0xFF00) << 8) | ((value >> 8) & 0xFF00) | (value >> 24)); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T io_pktfile_close( VC_CONTAINER_IO_T *p_ctx ) { VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; fclose(module->stream); free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static size_t io_pktfile_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) { VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; uint32_t length = 0; size_t ret; ret = fread(&length, 1, sizeof(length), module->stream); if (ret != sizeof(length)) { if( feof(module->stream) ) p_ctx->status = VC_CONTAINER_ERROR_EOS; else p_ctx->status = VC_CONTAINER_ERROR_FAILED; return 0; } if (!module->is_native_order) length = swap_byte_order(length); if (length > 1<<20) { p_ctx->status = VC_CONTAINER_ERROR_FAILED; return 0; } if (size > length) size = length; ret = fread(buffer, 1, size, module->stream); if(ret != size) { if( feof(module->stream) ) p_ctx->status = VC_CONTAINER_ERROR_EOS; else p_ctx->status = VC_CONTAINER_ERROR_FAILED; } else if (length > size) { /* Not enough space to read all the packet, so skip to the next one. */ length -= size; vc_container_assert((long)length > 0); fseek(module->stream, (long)length, SEEK_CUR); } return ret; } /*****************************************************************************/ static size_t io_pktfile_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size) { uint32_t size_word; size_t ret; if (size >= 0xFFFFFFFFUL) size_word = 0xFFFFFFFFUL; else size_word = (uint32_t)size; ret = fwrite(&size_word, 1, sizeof(size_word), p_ctx->module->stream); if (ret != sizeof(size_word)) { p_ctx->status = VC_CONTAINER_ERROR_FAILED; return 0; } ret = fwrite(buffer, 1, size_word, p_ctx->module->stream); if (ret != size_word) p_ctx->status = VC_CONTAINER_ERROR_FAILED; if (fflush(p_ctx->module->stream) != 0) p_ctx->status = VC_CONTAINER_ERROR_FAILED; return ret; } /*****************************************************************************/ static FILE *open_file(VC_CONTAINER_IO_T *ctx, VC_CONTAINER_IO_MODE_T mode, VC_CONTAINER_STATUS_T *p_status) { const char *psz_mode = mode == VC_CONTAINER_IO_MODE_WRITE ? "wb+" : "rb"; FILE *stream = 0; const char *port, *path; /* Treat empty port or path strings as not defined */ port = vc_uri_port(ctx->uri_parts); if (port && !*port) port = NULL; path = vc_uri_path(ctx->uri_parts); if (path && !*path) path = NULL; /* Require the port to be undefined and the path to be defined */ if (port || !path) { *p_status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; goto error; } if (!recognise_scheme(vc_uri_scheme(ctx->uri_parts))) { *p_status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; } stream = fopen(path, psz_mode); if(!stream) { *p_status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; } *p_status = VC_CONTAINER_SUCCESS; return stream; error: return NULL; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T write_byte_order(FILE *stream) { /* Simple byte order header word */ uint32_t value = NATIVE_BYTE_ORDER; if (fwrite(&value, 1, sizeof(value), stream) != sizeof(value)) return VC_CONTAINER_ERROR_OUT_OF_SPACE; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T read_byte_order(FILE *stream, bool *is_native) { uint32_t value; if (fread(&value, 1, sizeof(value), stream) != sizeof(value)) return VC_CONTAINER_ERROR_EOS; switch (value) { case NATIVE_BYTE_ORDER: *is_native = true; break; case SWAP_BYTE_ORDER: *is_native = false; break; default: return VC_CONTAINER_ERROR_CORRUPTED; } return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T vc_container_io_pktfile_open( VC_CONTAINER_IO_T *p_ctx, const char *unused, VC_CONTAINER_IO_MODE_T mode ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_IO_MODULE_T *module = 0; FILE *stream = 0; bool is_native_order = true; VC_CONTAINER_PARAM_UNUSED(unused); stream = open_file(p_ctx, mode, &status); if (status != VC_CONTAINER_SUCCESS) goto error; if (mode == VC_CONTAINER_IO_MODE_WRITE) status = write_byte_order(stream); else status = read_byte_order(stream, &is_native_order); if (status != VC_CONTAINER_SUCCESS) goto error; module = malloc( sizeof(*module) ); if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); p_ctx->module = module; module->stream = stream; module->is_native_order = is_native_order; p_ctx->pf_close = io_pktfile_close; p_ctx->pf_read = io_pktfile_read; p_ctx->pf_write = io_pktfile_write; /* Do not allow caching by I/O core, as this will merge packets in the cache. */ p_ctx->capabilities = VC_CONTAINER_IO_CAPS_CANT_SEEK; return VC_CONTAINER_SUCCESS; error: if(stream) fclose(stream); return status; } userland/containers/metadata/000077500000000000000000000000001421703157200166225ustar00rootroot00000000000000userland/containers/metadata/id3/000077500000000000000000000000001421703157200173015ustar00rootroot00000000000000userland/containers/metadata/id3/CMakeLists.txt000066400000000000000000000006321421703157200220420ustar00rootroot00000000000000# Container module needs to go in as a plugins so different prefix # and install path set(CMAKE_SHARED_LIBRARY_PREFIX "") # Make sure the compiler can find the necessary include files include_directories (../..) add_library(reader_metadata_id3 ${LIBRARY_TYPE} id3_metadata_reader.c) target_link_libraries(reader_metadata_id3 containers) install(TARGETS reader_metadata_id3 DESTINATION ${VMCS_PLUGIN_DIR}) userland/containers/metadata/id3/id3_metadata_reader.c000066400000000000000000000361201421703157200233100ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #define CONTAINER_IS_BIG_ENDIAN //#define ENABLE_CONTAINERS_LOG_FORMAT //#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE #define CONTAINER_HELPER_LOG_INDENT(a) 0 #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_logging.h" #include "id3_metadata_strings.h" /****************************************************************************** Defines ******************************************************************************/ #define ID3_SYNC_SAFE(x) ((((x >> 24) & 0x7f) << 21) | (((x >> 16) & 0x7f) << 14) | \ (((x >> 8) & 0x7f) << 7) | (((x >> 0) & 0x7f) << 0)) /****************************************************************************** Type definitions ******************************************************************************/ /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T id3_metadata_reader_open( VC_CONTAINER_T * ); /****************************************************************************** Local Functions ******************************************************************************/ static VC_CONTAINER_METADATA_T *id3_metadata_append( VC_CONTAINER_T *p_ctx, VC_CONTAINER_METADATA_KEY_T key, unsigned int size ) { VC_CONTAINER_METADATA_T *meta, **p_meta; unsigned int i; for (i = 0; i != p_ctx->meta_num; ++i) { if (key == p_ctx->meta[i]->key) break; } /* Avoid duplicate entries for now */ if (i < p_ctx->meta_num) return NULL; /* Sanity check size, truncate if necessary */ size = MIN(size, 512); /* Allocate a new metadata entry */ if((meta = malloc(sizeof(VC_CONTAINER_METADATA_T) + size)) == NULL) return NULL; /* We need to grow the array holding the metadata entries somehow, ideally, we'd like to use a linked structure of some sort but realloc is probably okay in this case */ if((p_meta = realloc(p_ctx->meta, sizeof(VC_CONTAINER_METADATA_T *) * (p_ctx->meta_num + 1))) == NULL) { free(meta); return NULL; } p_ctx->meta = p_meta; memset(meta, 0, sizeof(VC_CONTAINER_METADATA_T) + size); p_ctx->meta[p_ctx->meta_num] = meta; meta->key = key; meta->value = (char *)&meta[1]; meta->size = size; p_ctx->meta_num++; return meta; } /*****************************************************************************/ static VC_CONTAINER_METADATA_T *id3_read_metadata_entry( VC_CONTAINER_T *p_ctx, VC_CONTAINER_METADATA_KEY_T key, unsigned int len ) { VC_CONTAINER_METADATA_T *meta; if ((meta = id3_metadata_append(p_ctx, key, len + 1)) != NULL) { unsigned int size = meta->size - 1; READ_BYTES(p_ctx, meta->value, size); if (len > size) { LOG_DEBUG(p_ctx, "metadata value truncated (%d characters lost)", len - size); SKIP_BYTES(p_ctx, len - size); } } else { SKIP_BYTES(p_ctx, len); } return meta; } /*****************************************************************************/ static VC_CONTAINER_METADATA_T *id3_read_metadata_entry_ex( VC_CONTAINER_T *p_ctx, VC_CONTAINER_METADATA_KEY_T key, unsigned int len, const char *encoding ) { VC_CONTAINER_METADATA_T *meta; if ((meta = id3_metadata_append(p_ctx, key, encoding ? len + 2 : len + 1)) != NULL) { unsigned int size; if (encoding) { size = meta->size - 2; READ_STRING_UTF16(p_ctx, meta->value, size, "ID3v2 data"); } else { size = meta->size - 1; READ_STRING(p_ctx, meta->value, size, "ID3v2 data"); } if (len > size) { LOG_DEBUG(p_ctx, "metadata value truncated (%d characters lost)", len - size); SKIP_BYTES(p_ctx, len - size); } } return meta; } /*****************************************************************************/ static VC_CONTAINER_METADATA_T *id3_add_metadata_entry( VC_CONTAINER_T *p_ctx, VC_CONTAINER_METADATA_KEY_T key, const char *value ) { VC_CONTAINER_METADATA_T *meta; unsigned int len = strlen(value); if ((meta = id3_metadata_append(p_ctx, key, len + 1)) != NULL) { unsigned int size = meta->size - 1; if (len > size) { LOG_DEBUG(p_ctx, "metadata value truncated (%d characters lost)", len - size); } strncpy(meta->value, value, size); } return meta; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T id3_read_id3v2_frame( VC_CONTAINER_T *p_ctx, VC_CONTAINER_FOURCC_T frame_id, uint32_t frame_size ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_METADATA_KEY_T key; VC_CONTAINER_METADATA_T *meta = NULL; uint8_t encoding; const char *charset = NULL; if(frame_size < 1) return VC_CONTAINER_ERROR_CORRUPTED; switch (frame_id) { case VC_FOURCC('T','A','L','B'): key = VC_CONTAINER_METADATA_KEY_ALBUM; break; case VC_FOURCC('T','I','T','2'): key = VC_CONTAINER_METADATA_KEY_TITLE; break; case VC_FOURCC('T','R','C','K'): key = VC_CONTAINER_METADATA_KEY_TRACK; break; case VC_FOURCC('T','P','E','1'): key = VC_CONTAINER_METADATA_KEY_ARTIST; break; case VC_FOURCC('T','C','O','N'): key = VC_CONTAINER_METADATA_KEY_GENRE; break; default: key = VC_CONTAINER_METADATA_KEY_UNKNOWN; break; } if (key == VC_CONTAINER_METADATA_KEY_UNKNOWN) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; encoding = READ_U8(p_ctx, "ID3v2 text encoding byte"); frame_size -= 1; switch(encoding) { case 0: /* ISO-8859-1 */ case 3: /* UTF-8 */ break; case 1: /* UTF-16 with BOM */ if(frame_size < 2) return VC_CONTAINER_ERROR_CORRUPTED; SKIP_U16(p_ctx, "ID3v2 text encoding BOM"); /* FIXME: Check BOM, 0xFFFE vs 0xFEFFF */ frame_size -= 2; charset = "UTF16-LE"; break; case 2: /* UTF-16BE */ charset = "UTF16-BE"; break; default: LOG_DEBUG(p_ctx, "skipping frame, text encoding %x not supported", encoding); SKIP_BYTES(p_ctx, frame_size); return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; } if ((meta = id3_read_metadata_entry_ex(p_ctx, key, frame_size, charset)) != NULL) { if (charset) { utf8_from_charset(charset, meta->value, meta->size, meta->value, meta->size); } meta->encoding = VC_CONTAINER_CHAR_ENCODING_UTF8; /* Okay for ISO-8859-1 as well? */ status = VC_CONTAINER_SUCCESS; } else { SKIP_BYTES(p_ctx, frame_size); } return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T id3_read_id3v2_tag( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; uint8_t maj_version, flags; uint32_t tag_size, size = 0; uint8_t peek_buf[10]; SKIP_STRING(p_ctx, 3, "ID3v2 identifier"); maj_version = READ_U8(p_ctx, "ID3v2 version (major)"); SKIP_U8(p_ctx, "ID3v2 version (minor)"); flags = READ_U8(p_ctx, "ID3v2 flags"); tag_size = READ_U32(p_ctx, "ID3v2 syncsafe tag size"); tag_size = ID3_SYNC_SAFE(tag_size); LOG_DEBUG(p_ctx, "ID3v2 tag size: %d", tag_size); /* Check that we support this major version */ if (!(maj_version == 4 || maj_version == 3 || maj_version == 2)) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* We can't currently handle unsynchronisation */ if ((flags >> 7) & 1) { LOG_DEBUG(p_ctx, "skipping unsynchronised tag, not supported"); return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; } /* FIXME: check for version 2.2 and extract iTunes gapless playback information */ if (maj_version == 2) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if ((flags >> 6) & 1) { /* Skip extended header, we don't support it */ uint32_t ext_hdr_size; LOG_DEBUG(p_ctx, "skipping ID3v2 extended header, not supported"); ext_hdr_size = READ_U32(p_ctx, "ID3v2 syncsafe extended header size"); ext_hdr_size = ID3_SYNC_SAFE(ext_hdr_size); LOG_DEBUG(p_ctx, "ID3v2 extended header size: %d", ext_hdr_size); SKIP_BYTES(p_ctx, MIN(tag_size, ext_hdr_size)); size += ext_hdr_size; } while (PEEK_BYTES(p_ctx, peek_buf, 10) == 10 && size < tag_size) { VC_CONTAINER_FOURCC_T frame_id; uint32_t frame_size; uint8_t format_flags; frame_id = READ_FOURCC(p_ctx, "Frame ID"); frame_size = READ_U32(p_ctx, "Frame Size"); if (maj_version >= 4) { frame_size = ID3_SYNC_SAFE(frame_size); LOG_DEBUG(p_ctx, "ID3v2 actual frame size: %d", frame_size); } SKIP_U8(p_ctx, "ID3v2 status message flags"); format_flags = READ_U8(p_ctx, "ID3v2 format description flags"); size += 10; if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS || !frame_id) break; /* Early exit if we detect an invalid tag size */ if (size + frame_size > tag_size) { status = VC_CONTAINER_ERROR_FORMAT_INVALID; break; } /* We can't currently handle unsynchronised frames */ if ((format_flags >> 1) & 1) { LOG_DEBUG(p_ctx, "skipping unsynchronised frame, not supported"); SKIP_BYTES(p_ctx, frame_size); continue; } if ((status = id3_read_id3v2_frame(p_ctx, frame_id, frame_size)) != VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "skipping unsupported frame"); SKIP_BYTES(p_ctx, frame_size); } size += frame_size; } /* Try to skip to end of tag in case we bailed out early */ if (size < tag_size) SKIP_BYTES(p_ctx, tag_size - size); return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T id3_read_id3v1_tag( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; uint8_t track, genre; char track_num[4] = {0}; SKIP_STRING(p_ctx, 3, "ID3v1 identifier"); /* ID3v1 title */ id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_TITLE, 30); /* ID3v1 artist */ id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_ARTIST, 30); /* ID3v1 album */ id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_ALBUM, 30); /* ID3v1 year */ id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_YEAR, 4); SKIP_STRING(p_ctx, 28, "ID3v1 comment"); if (READ_U8(p_ctx, "ID3v1 zero-byte") == 0) { track = READ_U8(p_ctx, "ID3v1 track"); snprintf(track_num, sizeof(track_num) - 1, "%02d", track); id3_add_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_TRACK, track_num); } else { SKIP_BYTES(p_ctx, 1); } genre = READ_U8(p_ctx, "ID3v1 genre"); if (genre < countof(id3_genres)) { id3_add_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_GENRE, id3_genres[genre]); } status = STREAM_STATUS(p_ctx); return status; } /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ /*****************************************************************************/ static VC_CONTAINER_STATUS_T id3_metadata_reader_close( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_PARAM_UNUSED(p_ctx); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T id3_metadata_reader_open( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; uint8_t peek_buf[10]; int64_t data_offset; if (PEEK_BYTES(p_ctx, peek_buf, 10) != 10) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Initial ID3v2 tag(s), variable size */ while ((peek_buf[0] == 'I') && (peek_buf[1] == 'D') && (peek_buf[2] == '3')) { if ((status = id3_read_id3v2_tag(p_ctx)) != VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "error reading ID3v2 tag (%i)", status); } if (PEEK_BYTES(p_ctx, peek_buf, 10) != 10) break; } data_offset = STREAM_POSITION(p_ctx); /* ID3v1 tag, 128 bytes at the end of a file */ if (p_ctx->priv->io->size >= INT64_C(128) && STREAM_SEEKABLE(p_ctx)) { SEEK(p_ctx, p_ctx->priv->io->size - INT64_C(128)); if (PEEK_BYTES(p_ctx, peek_buf, 3) != 3) goto end; if ((peek_buf[0] == 'T') && (peek_buf[1] == 'A') && (peek_buf[2] == 'G')) { if ((status = id3_read_id3v1_tag(p_ctx)) != VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "error reading ID3v1 tag (%i)", status); } } } end: /* Restore position to start of data */ if (STREAM_POSITION(p_ctx) != data_offset) SEEK(p_ctx, data_offset); p_ctx->priv->pf_close = id3_metadata_reader_close; if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) goto error; return VC_CONTAINER_SUCCESS; error: LOG_DEBUG(p_ctx, "error opening stream (%i)", status); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak reader_open id3_metadata_reader_open #endif userland/containers/metadata/id3/id3_metadata_strings.h000066400000000000000000000073741421703157200235550ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ /* ID3 genre byte translation table */ static const char* id3_genres[] = { "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "Alternative Rock", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap", "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian", "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop", "SynthPop" }; userland/containers/mkv/000077500000000000000000000000001421703157200156375ustar00rootroot00000000000000userland/containers/mkv/CMakeLists.txt000066400000000000000000000005731421703157200204040ustar00rootroot00000000000000# Container module needs to go in as a plugins so different prefix # and install path set(CMAKE_SHARED_LIBRARY_PREFIX "") # Make sure the compiler can find the necessary include files include_directories (../..) add_library(reader_mkv ${LIBRARY_TYPE} matroska_reader.c) target_link_libraries(reader_mkv containers) install(TARGETS reader_mkv DESTINATION ${VMCS_PLUGIN_DIR}) userland/containers/mkv/matroska_reader.c000066400000000000000000002725711421703157200211640ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include //#define ENABLE_MKV_EXTRA_LOGGING #define CONTAINER_IS_BIG_ENDIAN #define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->element_level #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_logging.h" /****************************************************************************** Defines. ******************************************************************************/ #define MKV_TRACKS_MAX 16 #define MKV_CODECID_MAX 32 #define MKV_MAX_LACING_NUM 64 #define MKV_MAX_ENCODINGS 1 #define MKV_MAX_ENCODING_DATA 256 #define MKV_MAX_ELEMENT_LEVEL 8 #define MKV_MAX_CONSECUTIVE_UNKNOWN_ELEMENTS 5 #define MKV_MAX_ELEMENT_SIZE (1<<29) /* Does not apply to the data element */ #define MKV_MAX_STRING_SIZE 256 #define MKV_ELEMENT_MIN_HEADER_SIZE 2 #define MKV_MAX_READER_STATE_LEVEL 4 #define MKV_SKIP_U8(ctx,n) (size -= 1, SKIP_U8(ctx,n)) #define MKV_SKIP_U16(ctx,n) (size -= 2, SKIP_U16(ctx,n)) #define MKV_SKIP_U24(ctx,n) (size -= 3, SKIP_U24(ctx,n)) #define MKV_SKIP_U32(ctx,n) (size -= 4, SKIP_U32(ctx,n)) #define MKV_SKIP_U64(ctx,n) (size -= 8, SKIP_U64(ctx,n)) #define MKV_READ_U8(ctx,n) (size -= 1, READ_U8(ctx,n)) #define MKV_READ_U16(ctx,n) (size -= 2, READ_U16(ctx,n)) #define MKV_READ_U24(ctx,n) (size -= 3, READ_U24(ctx,n)) #define MKV_READ_U32(ctx,n) (size -= 4, READ_U32(ctx,n)) #define MKV_READ_U64(ctx,n) (size -= 8, READ_U64(ctx,n)) #define MKV_READ_BYTES(ctx,buffer,sz) (size -= sz, READ_BYTES(ctx,buffer,sz)) #define MKV_SKIP_BYTES(ctx,sz) (size -= sz, SKIP_BYTES(ctx,sz)) #define CHECK_POINT(a) do { \ /*if(size < 0 && size != INT64_C(-1)) return VC_CONTAINER_ERROR_CORRUPTED;*/ \ if(STREAM_STATUS(p_ctx)) return STREAM_STATUS(p_ctx); } while(0) static uint32_t mkv_io_read_id(VC_CONTAINER_IO_T *io, int64_t *size) { uint32_t value, mask; value = vc_container_io_read_uint8(io); (*size)--; for(mask = 0x80; mask; mask <<= 7) { if(value & mask) return value; value = (value << 8) | vc_container_io_read_uint8(io); (*size)--; } return 0; } static int64_t mkv_io_read_uint(VC_CONTAINER_IO_T *io, int64_t *size) { uint64_t value, mask; value = vc_container_io_read_uint8(io); (*size)--; if(value == 0xFF) return -1; for(mask = 0x80; mask; mask <<= 7) { if(value & mask) return value & ~mask; value = (value << 8) | vc_container_io_read_uint8(io); (*size)--; } return 0; } static int64_t mkv_io_read_sint(VC_CONTAINER_IO_T *io, int64_t *size) { int64_t value, count = io->offset; value = mkv_io_read_uint(io, size); count = io->offset - count; switch(count) { case 1: value -= 0x3F; break; case 2: value -= 0x1FFF; break; case 3: value -= 0xFFFFF; break; case 4: value -= 0x7FFFFFF; break; default: break; } return value; } #define MKV_READ_ID(ctx, n) mkv_io_read_id((ctx)->priv->io, &size) #define MKV_READ_UINT(ctx, n) mkv_io_read_uint((ctx)->priv->io, &size) #define MKV_READ_SINT(ctx, n) mkv_io_read_sint((ctx)->priv->io, &size) /****************************************************************************** Type definitions. ******************************************************************************/ typedef enum { MKV_ELEMENT_ID_UNKNOWN = 0, /* EBML Basics */ MKV_ELEMENT_ID_EBML = 0x1A45DFA3, MKV_ELEMENT_ID_EBML_VERSION = 0x4286, MKV_ELEMENT_ID_EBML_READ_VERSION = 0x42F7, MKV_ELEMENT_ID_EBML_MAX_ID_LENGTH = 0x42F2, MKV_ELEMENT_ID_EBML_MAX_SIZE_LENGTH = 0x42F3, MKV_ELEMENT_ID_DOCTYPE = 0x4282, MKV_ELEMENT_ID_DOCTYPE_VERSION = 0x4287, MKV_ELEMENT_ID_DOCTYPE_READ_VERSION = 0x4285, /* Global Elements */ MKV_ELEMENT_ID_CRC32 = 0xBF, MKV_ELEMENT_ID_VOID = 0xEC, /* Segment */ MKV_ELEMENT_ID_SEGMENT = 0x18538067, /* Meta Seek Information */ MKV_ELEMENT_ID_SEEK_HEAD = 0x114D9B74, MKV_ELEMENT_ID_SEEK = 0x4DBB, MKV_ELEMENT_ID_SEEK_ID = 0x53AB, MKV_ELEMENT_ID_SEEK_POSITION = 0x53AC, /* Segment Information */ MKV_ELEMENT_ID_INFO = 0x1549A966, MKV_ELEMENT_ID_SEGMENT_UID = 0x73A4, MKV_ELEMENT_ID_SEGMENT_FILENAME = 0x7384, MKV_ELEMENT_ID_PREV_UID = 0x3CB923, MKV_ELEMENT_ID_PREV_FILENAME = 0x3C83AB, MKV_ELEMENT_ID_NEXT_UID = 0x3EB923, MKV_ELEMENT_ID_NEXT_FILENAME = 0x3E83BB, MKV_ELEMENT_ID_SEGMENT_FAMILY = 0x4444, MKV_ELEMENT_ID_CHAPTER_TRANSLATE = 0x6924, MKV_ELEMENT_ID_CHAPTER_TRANSLATE_EDITION_UID = 0x69FC, MKV_ELEMENT_ID_CHAPTER_TRANSLATE_CODEC = 0x69BF, MKV_ELEMENT_ID_CHAPTER_TRANSLATE_ID = 0x69A5, MKV_ELEMENT_ID_TIMECODE_SCALE = 0x2AD7B1, MKV_ELEMENT_ID_DURATION = 0x4489, MKV_ELEMENT_ID_DATE_UTC = 0x4461, MKV_ELEMENT_ID_TITLE = 0x7BA9, MKV_ELEMENT_ID_MUXING_APP = 0x4D80, MKV_ELEMENT_ID_WRITING_APP = 0x5741, /* Cluster */ MKV_ELEMENT_ID_CLUSTER = 0x1F43B675, MKV_ELEMENT_ID_TIMECODE = 0xE7, MKV_ELEMENT_ID_SILENT_TRACKS = 0x5854, MKV_ELEMENT_ID_SILENT_TRACK_NUMBER = 0x58D7, MKV_ELEMENT_ID_POSITION = 0xA7, MKV_ELEMENT_ID_PREV_SIZE = 0xAB, MKV_ELEMENT_ID_BLOCKGROUP = 0xA0, MKV_ELEMENT_ID_BLOCK = 0xA1, MKV_ELEMENT_ID_BLOCK_ADDITIONS = 0x75A1, MKV_ELEMENT_ID_BLOCK_MORE = 0xA6, MKV_ELEMENT_ID_BLOCK_ADD_ID = 0xEE, MKV_ELEMENT_ID_BLOCK_ADDITIONAL = 0xA5, MKV_ELEMENT_ID_BLOCK_DURATION = 0x9B, MKV_ELEMENT_ID_REFERENCE_PRIORITY = 0xFA, MKV_ELEMENT_ID_REFERENCE_BLOCK = 0xFB, MKV_ELEMENT_ID_CODEC_STATE = 0xA4, MKV_ELEMENT_ID_SLICES = 0x8E, MKV_ELEMENT_ID_TIME_SLICE = 0xE8, MKV_ELEMENT_ID_LACE_NUMBER = 0xCC, MKV_ELEMENT_ID_SIMPLE_BLOCK = 0xA3, /* Track */ MKV_ELEMENT_ID_TRACKS = 0x1654AE6B, MKV_ELEMENT_ID_TRACK_ENTRY = 0xAE, MKV_ELEMENT_ID_TRACK_NUMBER = 0xD7, MKV_ELEMENT_ID_TRACK_UID = 0x73C5, MKV_ELEMENT_ID_TRACK_TYPE = 0x83, MKV_ELEMENT_ID_FLAG_ENABLED = 0xB9, MKV_ELEMENT_ID_FLAG_DEFAULT = 0x88, MKV_ELEMENT_ID_FLAG_FORCED = 0x55AA, MKV_ELEMENT_ID_FLAG_LACING = 0x9C, MKV_ELEMENT_ID_MIN_CACHE = 0x6DE7, MKV_ELEMENT_ID_MAX_CACHE = 0x6DF8, MKV_ELEMENT_ID_DEFAULT_DURATION = 0x23E383, MKV_ELEMENT_ID_TRACK_TIMECODE_SCALE = 0x23314F, MKV_ELEMENT_ID_MAX_BLOCK_ADDITION_ID = 0x55EE, MKV_ELEMENT_ID_NAME = 0x536E, MKV_ELEMENT_ID_LANGUAGE = 0x22B59C, MKV_ELEMENT_ID_TRACK_CODEC_ID = 0x86, MKV_ELEMENT_ID_TRACK_CODEC_PRIVATE = 0x63A2, MKV_ELEMENT_ID_TRACK_CODEC_NAME = 0x258688, MKV_ELEMENT_ID_ATTACHMENT_LINK = 0x7446, MKV_ELEMENT_ID_CODEC_DECODE_ALL = 0xAA, MKV_ELEMENT_ID_TRACK_OVERLAY = 0x6FAB, MKV_ELEMENT_ID_TRACK_TRANSLATE = 0x6624, MKV_ELEMENT_ID_TRACK_TRANSLATE_EDITION_UID = 0x66FC, MKV_ELEMENT_ID_TRACK_TRANSLATE_CODEC = 0x66BF, MKV_ELEMENT_ID_TRACK_TRANSLATE_TRACK_ID = 0x66A5, /* Video */ MKV_ELEMENT_ID_VIDEO = 0xE0, MKV_ELEMENT_ID_FLAG_INTERLACED = 0x9A, MKV_ELEMENT_ID_STEREO_MODE = 0x53B8, MKV_ELEMENT_ID_PIXEL_WIDTH = 0xB0, MKV_ELEMENT_ID_PIXEL_HEIGHT = 0xBA, MKV_ELEMENT_ID_PIXEL_CROP_BOTTOM = 0x54AA, MKV_ELEMENT_ID_PIXEL_CROP_TOP = 0x54BB, MKV_ELEMENT_ID_PIXEL_CROP_LEFT = 0x54CC, MKV_ELEMENT_ID_PIXEL_CROP_RIGHT = 0x54DD, MKV_ELEMENT_ID_DISPLAY_WIDTH = 0x54B0, MKV_ELEMENT_ID_DISPLAY_HEIGHT = 0x54BA, MKV_ELEMENT_ID_DISPLAY_UNIT = 0x54B2, MKV_ELEMENT_ID_ASPECT_RATIO_TYPE = 0x54B3, MKV_ELEMENT_ID_COLOUR_SPACE = 0x2EB524, MKV_ELEMENT_ID_FRAME_RATE = 0x2383E3, /* Audio */ MKV_ELEMENT_ID_AUDIO = 0xE1, MKV_ELEMENT_ID_SAMPLING_FREQUENCY = 0xB5, MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY = 0x78B5, MKV_ELEMENT_ID_CHANNELS = 0x9F, MKV_ELEMENT_ID_BIT_DEPTH = 0x6264, /* Content Encoding */ MKV_ELEMENT_ID_CONTENT_ENCODINGS = 0x6D80, MKV_ELEMENT_ID_CONTENT_ENCODING = 0x6240, MKV_ELEMENT_ID_CONTENT_ENCODING_ORDER = 0x5031, MKV_ELEMENT_ID_CONTENT_ENCODING_SCOPE = 0x5032, MKV_ELEMENT_ID_CONTENT_ENCODING_TYPE = 0x5033, MKV_ELEMENT_ID_CONTENT_COMPRESSION = 0x5034, MKV_ELEMENT_ID_CONTENT_COMPRESSION_ALGO = 0x4254, MKV_ELEMENT_ID_CONTENT_COMPRESSION_SETTINGS = 0x4255, MKV_ELEMENT_ID_CONTENT_ENCRYPTION = 0x5035, MKV_ELEMENT_ID_CONTENT_ENCRYPTION_ALGO = 0x47E1, MKV_ELEMENT_ID_CONTENT_ENCRYPTION_KEY_ID = 0x47E2, MKV_ELEMENT_ID_CONTENT_SIGNATURE = 0x47E3, MKV_ELEMENT_ID_CONTENT_SIGNATURE_KEY_ID = 0x47E4, MKV_ELEMENT_ID_CONTENT_SIGNATURE_ALGO = 0x47E5, MKV_ELEMENT_ID_CONTENT_SIGNATURE_HASH_ALGO = 0x47E6, /* Cueing Data */ MKV_ELEMENT_ID_CUES = 0x1C53BB6B, MKV_ELEMENT_ID_CUE_POINT = 0xBB, MKV_ELEMENT_ID_CUE_TIME = 0xB3, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS = 0xB7, MKV_ELEMENT_ID_CUE_TRACK = 0xF7, MKV_ELEMENT_ID_CUE_CLUSTER_POSITION = 0xF1, MKV_ELEMENT_ID_CUE_BLOCK_NUMBER = 0x5378, /* Attachments */ MKV_ELEMENT_ID_ATTACHMENTS = 0x1941A469, /* Chapters */ MKV_ELEMENT_ID_CHAPTERS = 0x1043A770, /* Tagging */ MKV_ELEMENT_ID_TAGS = 0x1254C367, MKV_ELEMENT_ID_TAG = 0x7373, MKV_ELEMENT_ID_TAG_TARGETS = 0x63C0, MKV_ELEMENT_ID_TAG_TARGET_TYPE_VALUE = 0x68CA, MKV_ELEMENT_ID_TAG_TARGET_TYPE = 0x63CA, MKV_ELEMENT_ID_TAG_TRACK_UID = 0x63C5, MKV_ELEMENT_ID_TAG_EDITION_UID = 0x63C9, MKV_ELEMENT_ID_TAG_CHAPTER_UID = 0x63C4, MKV_ELEMENT_ID_TAG_ATTACHMENT_UID = 0x63C6, MKV_ELEMENT_ID_TAG_SIMPLE_TAG = 0x67C8, MKV_ELEMENT_ID_TAG_NAME = 0x45A3, MKV_ELEMENT_ID_TAG_LANGUAGE = 0x447A, MKV_ELEMENT_ID_TAG_DEFAULT = 0x4484, MKV_ELEMENT_ID_TAG_STRING = 0x4487, MKV_ELEMENT_ID_TAG_BINARY = 0x4485, MKV_ELEMENT_ID_INVALID = 0xFFFFFFFF } MKV_ELEMENT_ID_T; /** Context for our reader */ typedef struct { unsigned int track; unsigned int flags; int64_t pts; int64_t cluster_timecode; int64_t prev_cluster_size; /* Size of the previous cluster if available */ int64_t frame_duration; int level; struct { int64_t offset; int64_t data_start; int64_t data_offset; int64_t size; MKV_ELEMENT_ID_T id; } levels[MKV_MAX_READER_STATE_LEVEL]; bool eos; bool corrupted; bool seen_ref_block; uint32_t lacing_num_frames; uint32_t lacing_size; uint16_t lacing_sizes[MKV_MAX_LACING_NUM]; uint32_t lacing_current_size; /* For header stripping compression */ uint32_t header_size; uint8_t *header_data; uint32_t header_size_backup; } MKV_READER_STATE_T; typedef struct { const MKV_ELEMENT_ID_T id; const MKV_ELEMENT_ID_T parent_id; const char *psz_name; VC_CONTAINER_STATUS_T (*pf_func)(VC_CONTAINER_T *, MKV_ELEMENT_ID_T, int64_t); } MKV_ELEMENT_T; typedef struct VC_CONTAINER_TRACK_MODULE_T { MKV_READER_STATE_T *state; MKV_READER_STATE_T track_state; /* Information extracted from the track entry */ uint32_t number; uint32_t type; int64_t timecode_scale; uint32_t duration; int64_t frame_duration; char codecid[MKV_CODECID_MAX]; union { /* video specific */ struct { unsigned int interlaced:1; unsigned int stereo_mode:2; uint32_t pixel_width; uint32_t pixel_height; uint32_t pixel_crop_bottom; uint32_t pixel_crop_top; uint32_t pixel_crop_left; uint32_t pixel_crop_right; uint32_t display_width; uint32_t display_height; uint32_t display_unit; uint32_t aspect_ratio_type; float frame_rate; } video; /* audio specific */ struct { uint32_t sampling_frequency; uint32_t output_sampling_frequency; uint32_t channels; uint32_t bit_depth; } audio; } es_type; /* content encoding (i.e. lossless compression and encryption) */ unsigned int encodings_num; struct { enum { MKV_CONTENT_ENCODING_COMPRESSION_ZLIB, MKV_CONTENT_ENCODING_COMPRESSION_HEADER, MKV_CONTENT_ENCODING_ENCRYPTION, MKV_CONTENT_ENCODING_UNKNOWN } type; unsigned int data_size; uint8_t *data; } encodings[MKV_MAX_ENCODINGS]; } VC_CONTAINER_TRACK_MODULE_T; typedef struct VC_CONTAINER_MODULE_T { MKV_ELEMENT_T *elements_list; int element_level; MKV_ELEMENT_ID_T parent_id; uint64_t element_offset; /**< Offset to the start of the current element */ uint64_t segment_offset; /**< Offset to the start of the data packets */ int64_t segment_size; int tracks_num; VC_CONTAINER_TRACK_T *tracks[MKV_TRACKS_MAX]; MKV_READER_STATE_T state; int64_t timecode_scale; float duration; uint64_t cluster_offset; /**< Offset to the first cluster */ uint64_t cues_offset; /**< Offset to the start of the seeking cues */ uint64_t tags_offset; /**< Offset to the start of the tags */ /* * Variables only used during parsing of the header */ VC_CONTAINER_TRACK_T *parsing; /**< Current track being parsed */ bool is_doctype_valid; MKV_ELEMENT_ID_T seekhead_elem_id; int64_t seekhead_elem_offset; /* Cues */ unsigned int cue_track; int64_t cue_timecode; uint64_t cue_cluster_offset; unsigned int cue_block; } VC_CONTAINER_MODULE_T; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T mkv_reader_open( VC_CONTAINER_T * ); /****************************************************************************** Prototypes for local functions ******************************************************************************/ static VC_CONTAINER_STATUS_T mkv_read_element( VC_CONTAINER_T *p_ctx, int64_t size, MKV_ELEMENT_ID_T parent_id ); static VC_CONTAINER_STATUS_T mkv_read_elements( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); static VC_CONTAINER_STATUS_T mkv_read_element_data_uint( VC_CONTAINER_T *p_ctx, int64_t size, uint64_t *value ); static VC_CONTAINER_STATUS_T mkv_read_element_data_float( VC_CONTAINER_T *p_ctx, int64_t size, double *value ); static VC_CONTAINER_STATUS_T mkv_read_element_ebml( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); static VC_CONTAINER_STATUS_T mkv_read_subelements_ebml( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); static VC_CONTAINER_STATUS_T mkv_read_element_segment( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); static VC_CONTAINER_STATUS_T mkv_read_element_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); static VC_CONTAINER_STATUS_T mkv_read_subelements_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); static VC_CONTAINER_STATUS_T mkv_read_subelements_video( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); static VC_CONTAINER_STATUS_T mkv_read_subelements_audio( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); static VC_CONTAINER_STATUS_T mkv_read_subelements_info( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); static VC_CONTAINER_STATUS_T mkv_read_element_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); static VC_CONTAINER_STATUS_T mkv_read_subelements_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); static VC_CONTAINER_STATUS_T mkv_read_subelements_compression( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); static VC_CONTAINER_STATUS_T mkv_read_subelements_seek_head( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); static VC_CONTAINER_STATUS_T mkv_read_element_cues( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); static VC_CONTAINER_STATUS_T mkv_read_subelements_cue_point( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); static VC_CONTAINER_STATUS_T mkv_read_subelements_cluster( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); /****************************************************************************** List of element IDs and their associated processing functions ******************************************************************************/ MKV_ELEMENT_T mkv_elements_list[] = { /* EBML Basics */ {MKV_ELEMENT_ID_EBML, MKV_ELEMENT_ID_UNKNOWN, "EBML", mkv_read_element_ebml}, {MKV_ELEMENT_ID_EBML_VERSION, MKV_ELEMENT_ID_EBML, "EBMLVersion", mkv_read_subelements_ebml}, {MKV_ELEMENT_ID_EBML_READ_VERSION, MKV_ELEMENT_ID_EBML, "EBMLReadVersion", mkv_read_subelements_ebml}, {MKV_ELEMENT_ID_EBML_MAX_ID_LENGTH, MKV_ELEMENT_ID_EBML, "EBMLMaxIDLength", mkv_read_subelements_ebml}, {MKV_ELEMENT_ID_EBML_MAX_SIZE_LENGTH, MKV_ELEMENT_ID_EBML, "EBMLMaxSizeLength", mkv_read_subelements_ebml}, {MKV_ELEMENT_ID_DOCTYPE, MKV_ELEMENT_ID_EBML, "DocType", mkv_read_subelements_ebml}, {MKV_ELEMENT_ID_DOCTYPE_VERSION, MKV_ELEMENT_ID_EBML, "DocTypeVersion", mkv_read_subelements_ebml}, {MKV_ELEMENT_ID_DOCTYPE_READ_VERSION, MKV_ELEMENT_ID_EBML, "DocTypeReadVersion", mkv_read_subelements_ebml}, /* Global Elements */ {MKV_ELEMENT_ID_CRC32, MKV_ELEMENT_ID_INVALID, "CRC-32", 0}, {MKV_ELEMENT_ID_VOID, MKV_ELEMENT_ID_INVALID, "Void", 0}, /* Segment */ {MKV_ELEMENT_ID_SEGMENT, MKV_ELEMENT_ID_UNKNOWN, "Segment", mkv_read_element_segment}, /* Meta Seek Information */ {MKV_ELEMENT_ID_SEEK_HEAD, MKV_ELEMENT_ID_SEGMENT, "SeekHead", mkv_read_elements}, {MKV_ELEMENT_ID_SEEK, MKV_ELEMENT_ID_SEEK_HEAD, "Seek", mkv_read_subelements_seek_head}, {MKV_ELEMENT_ID_SEEK_ID, MKV_ELEMENT_ID_SEEK, "SeekID", mkv_read_subelements_seek_head}, {MKV_ELEMENT_ID_SEEK_POSITION, MKV_ELEMENT_ID_SEEK, "SeekPosition", mkv_read_subelements_seek_head}, /* Segment Information */ {MKV_ELEMENT_ID_INFO, MKV_ELEMENT_ID_SEGMENT, "Info", mkv_read_elements}, {MKV_ELEMENT_ID_SEGMENT_UID, MKV_ELEMENT_ID_INFO, "SegmentUID", 0}, {MKV_ELEMENT_ID_SEGMENT_FILENAME, MKV_ELEMENT_ID_INFO, "SegmentFilename", 0}, {MKV_ELEMENT_ID_PREV_UID, MKV_ELEMENT_ID_INFO, "PrevUID", 0}, {MKV_ELEMENT_ID_PREV_FILENAME, MKV_ELEMENT_ID_INFO, "PrevFilename", 0}, {MKV_ELEMENT_ID_NEXT_UID, MKV_ELEMENT_ID_INFO, "NextUID", 0}, {MKV_ELEMENT_ID_NEXT_FILENAME, MKV_ELEMENT_ID_INFO, "NextFilename", 0}, {MKV_ELEMENT_ID_SEGMENT_FAMILY, MKV_ELEMENT_ID_INFO, "SegmentFamily", 0}, {MKV_ELEMENT_ID_CHAPTER_TRANSLATE, MKV_ELEMENT_ID_INFO, "ChapterTranslate", 0}, {MKV_ELEMENT_ID_CHAPTER_TRANSLATE_EDITION_UID, MKV_ELEMENT_ID_INFO, "ChapterTranslateEditionUID", 0}, {MKV_ELEMENT_ID_CHAPTER_TRANSLATE_CODEC, MKV_ELEMENT_ID_INFO, "ChapterTranslateCodec", 0}, {MKV_ELEMENT_ID_CHAPTER_TRANSLATE_ID, MKV_ELEMENT_ID_INFO, "ChapterTranslateID", 0}, {MKV_ELEMENT_ID_TIMECODE_SCALE, MKV_ELEMENT_ID_INFO, "TimecodeScale", mkv_read_subelements_info}, {MKV_ELEMENT_ID_DURATION, MKV_ELEMENT_ID_INFO, "Duration", mkv_read_subelements_info}, {MKV_ELEMENT_ID_DATE_UTC, MKV_ELEMENT_ID_INFO, "DateUTC", 0}, {MKV_ELEMENT_ID_TITLE, MKV_ELEMENT_ID_INFO, "Title", mkv_read_subelements_info}, {MKV_ELEMENT_ID_MUXING_APP, MKV_ELEMENT_ID_INFO, "MuxingApp", mkv_read_subelements_info}, {MKV_ELEMENT_ID_WRITING_APP, MKV_ELEMENT_ID_INFO, "WritingApp", mkv_read_subelements_info}, /* Cluster */ {MKV_ELEMENT_ID_CLUSTER, MKV_ELEMENT_ID_SEGMENT, "Cluster", 0}, {MKV_ELEMENT_ID_TIMECODE, MKV_ELEMENT_ID_CLUSTER, "Timecode", 0}, {MKV_ELEMENT_ID_SILENT_TRACKS, MKV_ELEMENT_ID_CLUSTER, "SilentTracks", 0}, {MKV_ELEMENT_ID_SILENT_TRACK_NUMBER, MKV_ELEMENT_ID_CLUSTER, "SilentTrackNumber", 0}, {MKV_ELEMENT_ID_POSITION, MKV_ELEMENT_ID_CLUSTER, "Position", 0}, {MKV_ELEMENT_ID_PREV_SIZE, MKV_ELEMENT_ID_CLUSTER, "PrevSize", 0}, {MKV_ELEMENT_ID_BLOCKGROUP, MKV_ELEMENT_ID_CLUSTER, "BlockGroup", 0}, {MKV_ELEMENT_ID_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "Block", 0}, {MKV_ELEMENT_ID_BLOCK_ADDITIONS, MKV_ELEMENT_ID_BLOCKGROUP, "BlockAdditions", 0}, {MKV_ELEMENT_ID_BLOCK_MORE, MKV_ELEMENT_ID_BLOCK_ADDITIONS, "BlockMore", 0}, {MKV_ELEMENT_ID_BLOCK_ADD_ID, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAddId", 0}, {MKV_ELEMENT_ID_BLOCK_ADDITIONAL, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAdditional", 0}, {MKV_ELEMENT_ID_BLOCK_DURATION, MKV_ELEMENT_ID_BLOCKGROUP, "BlockDuration", 0}, {MKV_ELEMENT_ID_REFERENCE_PRIORITY, MKV_ELEMENT_ID_BLOCKGROUP, "ReferencePriority", 0}, {MKV_ELEMENT_ID_REFERENCE_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "ReferenceBlock", 0}, {MKV_ELEMENT_ID_CODEC_STATE, MKV_ELEMENT_ID_BLOCKGROUP, "CodecState", 0}, {MKV_ELEMENT_ID_SLICES, MKV_ELEMENT_ID_BLOCKGROUP, "Slices", 0}, {MKV_ELEMENT_ID_TIME_SLICE, MKV_ELEMENT_ID_SLICES, "TimeSlice", 0}, {MKV_ELEMENT_ID_LACE_NUMBER, MKV_ELEMENT_ID_TIME_SLICE, "LaceNumber", 0}, {MKV_ELEMENT_ID_SIMPLE_BLOCK, MKV_ELEMENT_ID_CLUSTER, "SimpleBlock", 0}, /* Track */ {MKV_ELEMENT_ID_TRACKS, MKV_ELEMENT_ID_SEGMENT, "Tracks", mkv_read_elements}, {MKV_ELEMENT_ID_TRACK_ENTRY, MKV_ELEMENT_ID_TRACKS, "TrackEntry", mkv_read_element_track_entry}, {MKV_ELEMENT_ID_TRACK_NUMBER, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackNumber", mkv_read_subelements_track_entry}, {MKV_ELEMENT_ID_TRACK_UID, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackUID", mkv_read_subelements_track_entry}, {MKV_ELEMENT_ID_TRACK_TYPE, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackType", mkv_read_subelements_track_entry}, {MKV_ELEMENT_ID_FLAG_ENABLED, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagEnabled", mkv_read_subelements_track_entry}, {MKV_ELEMENT_ID_FLAG_DEFAULT, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagDefault", mkv_read_subelements_track_entry}, {MKV_ELEMENT_ID_FLAG_FORCED, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagForced", mkv_read_subelements_track_entry}, {MKV_ELEMENT_ID_FLAG_LACING, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagLacing", mkv_read_subelements_track_entry}, {MKV_ELEMENT_ID_MIN_CACHE, MKV_ELEMENT_ID_TRACK_ENTRY, "MinCache", 0}, {MKV_ELEMENT_ID_MAX_CACHE, MKV_ELEMENT_ID_TRACK_ENTRY, "MaxCache", 0}, {MKV_ELEMENT_ID_DEFAULT_DURATION, MKV_ELEMENT_ID_TRACK_ENTRY, "DefaultDuration", mkv_read_subelements_track_entry}, {MKV_ELEMENT_ID_TRACK_TIMECODE_SCALE, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackTimecodeScale", mkv_read_subelements_track_entry}, {MKV_ELEMENT_ID_MAX_BLOCK_ADDITION_ID, MKV_ELEMENT_ID_TRACK_ENTRY, "MaxBlockAdditionID", 0}, {MKV_ELEMENT_ID_NAME, MKV_ELEMENT_ID_TRACK_ENTRY, "Name", mkv_read_subelements_track_entry}, {MKV_ELEMENT_ID_LANGUAGE, MKV_ELEMENT_ID_TRACK_ENTRY, "Language", mkv_read_subelements_track_entry}, {MKV_ELEMENT_ID_TRACK_CODEC_ID, MKV_ELEMENT_ID_TRACK_ENTRY, "CodecID", mkv_read_subelements_track_entry}, {MKV_ELEMENT_ID_TRACK_CODEC_PRIVATE, MKV_ELEMENT_ID_TRACK_ENTRY, "CodecPrivate", mkv_read_subelements_track_entry}, {MKV_ELEMENT_ID_TRACK_CODEC_NAME, MKV_ELEMENT_ID_TRACK_ENTRY, "CodecName", mkv_read_subelements_track_entry}, {MKV_ELEMENT_ID_ATTACHMENT_LINK, MKV_ELEMENT_ID_TRACK_ENTRY, "AttachmentLink", 0}, {MKV_ELEMENT_ID_CODEC_DECODE_ALL, MKV_ELEMENT_ID_TRACK_ENTRY, "DecodeAll", 0}, {MKV_ELEMENT_ID_TRACK_OVERLAY, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackOverlay", 0}, {MKV_ELEMENT_ID_TRACK_TRANSLATE, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackTranslate", 0}, {MKV_ELEMENT_ID_TRACK_TRANSLATE_EDITION_UID, MKV_ELEMENT_ID_TRACK_TRANSLATE, "TrackTranslateEditionUID", 0}, {MKV_ELEMENT_ID_TRACK_TRANSLATE_CODEC, MKV_ELEMENT_ID_TRACK_TRANSLATE, "TrackTranslateCodec", 0}, {MKV_ELEMENT_ID_TRACK_TRANSLATE_TRACK_ID, MKV_ELEMENT_ID_TRACK_TRANSLATE, "TrackTranslateTrackID", 0}, /* Video */ {MKV_ELEMENT_ID_VIDEO, MKV_ELEMENT_ID_TRACK_ENTRY, "Video", mkv_read_elements}, {MKV_ELEMENT_ID_FLAG_INTERLACED, MKV_ELEMENT_ID_VIDEO, "FlagInterlaced", mkv_read_subelements_video}, {MKV_ELEMENT_ID_STEREO_MODE, MKV_ELEMENT_ID_VIDEO, "StereoMode", mkv_read_subelements_video}, {MKV_ELEMENT_ID_PIXEL_WIDTH, MKV_ELEMENT_ID_VIDEO, "PixelWidth", mkv_read_subelements_video}, {MKV_ELEMENT_ID_PIXEL_HEIGHT, MKV_ELEMENT_ID_VIDEO, "PixelHeight", mkv_read_subelements_video}, {MKV_ELEMENT_ID_PIXEL_CROP_BOTTOM, MKV_ELEMENT_ID_VIDEO, "PixelCropBottom", mkv_read_subelements_video}, {MKV_ELEMENT_ID_PIXEL_CROP_TOP, MKV_ELEMENT_ID_VIDEO, "PixelCropTop", mkv_read_subelements_video}, {MKV_ELEMENT_ID_PIXEL_CROP_LEFT, MKV_ELEMENT_ID_VIDEO, "PixelCropLeft", mkv_read_subelements_video}, {MKV_ELEMENT_ID_PIXEL_CROP_RIGHT, MKV_ELEMENT_ID_VIDEO, "PixelCropRight", mkv_read_subelements_video}, {MKV_ELEMENT_ID_DISPLAY_WIDTH, MKV_ELEMENT_ID_VIDEO, "DisplayWidth", mkv_read_subelements_video}, {MKV_ELEMENT_ID_DISPLAY_HEIGHT, MKV_ELEMENT_ID_VIDEO, "DisplayHeight", mkv_read_subelements_video}, {MKV_ELEMENT_ID_DISPLAY_UNIT, MKV_ELEMENT_ID_VIDEO, "DisplayUnit", mkv_read_subelements_video}, {MKV_ELEMENT_ID_ASPECT_RATIO_TYPE, MKV_ELEMENT_ID_VIDEO, "AspectRatioType", mkv_read_subelements_video}, {MKV_ELEMENT_ID_COLOUR_SPACE, MKV_ELEMENT_ID_VIDEO, "ColourSpace", mkv_read_subelements_video}, {MKV_ELEMENT_ID_FRAME_RATE, MKV_ELEMENT_ID_VIDEO, "FrameRate", mkv_read_subelements_video}, /* Audio */ {MKV_ELEMENT_ID_AUDIO, MKV_ELEMENT_ID_TRACK_ENTRY, "Audio", mkv_read_elements}, {MKV_ELEMENT_ID_SAMPLING_FREQUENCY, MKV_ELEMENT_ID_AUDIO, "SamplingFrequency", mkv_read_subelements_audio}, {MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY, MKV_ELEMENT_ID_AUDIO, "OutputSamplingFrequency", mkv_read_subelements_audio}, {MKV_ELEMENT_ID_CHANNELS, MKV_ELEMENT_ID_AUDIO, "Channels", mkv_read_subelements_audio}, {MKV_ELEMENT_ID_BIT_DEPTH, MKV_ELEMENT_ID_AUDIO, "BitDepth", mkv_read_subelements_audio}, /* Content Encoding */ {MKV_ELEMENT_ID_CONTENT_ENCODINGS, MKV_ELEMENT_ID_TRACK_ENTRY, "ContentEncodings", mkv_read_elements}, {MKV_ELEMENT_ID_CONTENT_ENCODING, MKV_ELEMENT_ID_CONTENT_ENCODINGS, "ContentEncoding", mkv_read_element_encoding}, {MKV_ELEMENT_ID_CONTENT_ENCODING_ORDER, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncodingOrder", mkv_read_subelements_encoding}, {MKV_ELEMENT_ID_CONTENT_ENCODING_SCOPE, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncodingScope", mkv_read_subelements_encoding}, {MKV_ELEMENT_ID_CONTENT_ENCODING_TYPE, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncodingType", mkv_read_subelements_encoding}, {MKV_ELEMENT_ID_CONTENT_COMPRESSION, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentCompression", mkv_read_elements}, {MKV_ELEMENT_ID_CONTENT_COMPRESSION_ALGO, MKV_ELEMENT_ID_CONTENT_COMPRESSION, "ContentCompAlgo", mkv_read_subelements_compression}, {MKV_ELEMENT_ID_CONTENT_COMPRESSION_SETTINGS, MKV_ELEMENT_ID_CONTENT_COMPRESSION, "ContentCompSettings", mkv_read_subelements_compression}, {MKV_ELEMENT_ID_CONTENT_ENCRYPTION, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncryption", mkv_read_elements}, {MKV_ELEMENT_ID_CONTENT_ENCRYPTION_ALGO, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentEncAlgo", 0}, {MKV_ELEMENT_ID_CONTENT_ENCRYPTION_KEY_ID, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentEncKeyID", 0}, {MKV_ELEMENT_ID_CONTENT_SIGNATURE, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSignature", 0}, {MKV_ELEMENT_ID_CONTENT_SIGNATURE_KEY_ID, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSigKeyID", 0}, {MKV_ELEMENT_ID_CONTENT_SIGNATURE_ALGO, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSigAlgo", 0}, {MKV_ELEMENT_ID_CONTENT_SIGNATURE_HASH_ALGO, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSigHashAlgo", 0}, /* Cueing data */ {MKV_ELEMENT_ID_CUES, MKV_ELEMENT_ID_SEGMENT, "Cues", mkv_read_element_cues}, {MKV_ELEMENT_ID_CUE_POINT, MKV_ELEMENT_ID_CUES, "Cue Point", mkv_read_elements}, {MKV_ELEMENT_ID_CUE_TIME, MKV_ELEMENT_ID_CUE_POINT, "Cue Time", 0}, {MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, MKV_ELEMENT_ID_CUE_POINT, "Cue Track Positions", mkv_read_elements}, {MKV_ELEMENT_ID_CUE_TRACK, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Track", 0}, {MKV_ELEMENT_ID_CUE_CLUSTER_POSITION, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Cluster Position", 0}, {MKV_ELEMENT_ID_CUE_BLOCK_NUMBER, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Block Number", 0}, /* Attachments */ {MKV_ELEMENT_ID_ATTACHMENTS, MKV_ELEMENT_ID_SEGMENT, "Attachments", 0}, /* Chapters */ {MKV_ELEMENT_ID_CHAPTERS, MKV_ELEMENT_ID_SEGMENT, "Chapters", 0}, /* Tagging */ {MKV_ELEMENT_ID_TAGS, MKV_ELEMENT_ID_SEGMENT, "Tags", mkv_read_elements}, {MKV_ELEMENT_ID_TAG, MKV_ELEMENT_ID_TAGS, "Tag", mkv_read_elements}, {MKV_ELEMENT_ID_TAG_TARGETS, MKV_ELEMENT_ID_TAG, "Tag Targets", mkv_read_elements}, {MKV_ELEMENT_ID_TAG_TARGET_TYPE_VALUE, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Target Type Value", 0}, {MKV_ELEMENT_ID_TAG_TARGET_TYPE, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Target Type", 0}, {MKV_ELEMENT_ID_TAG_TRACK_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Track UID", 0}, {MKV_ELEMENT_ID_TAG_EDITION_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Edition UID", 0}, {MKV_ELEMENT_ID_TAG_CHAPTER_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Chapter UID", 0}, {MKV_ELEMENT_ID_TAG_ATTACHMENT_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Attachment UID", 0}, {MKV_ELEMENT_ID_TAG_SIMPLE_TAG, MKV_ELEMENT_ID_TAG, "Simple Tag", mkv_read_elements}, {MKV_ELEMENT_ID_TAG_NAME, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Name", 0}, {MKV_ELEMENT_ID_TAG_LANGUAGE, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Language", 0}, {MKV_ELEMENT_ID_TAG_DEFAULT, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Default", 0}, {MKV_ELEMENT_ID_TAG_STRING, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag String", 0}, {MKV_ELEMENT_ID_TAG_BINARY, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Binary", 0}, {MKV_ELEMENT_ID_UNKNOWN, MKV_ELEMENT_ID_INVALID, "unknown", 0} }; MKV_ELEMENT_T mkv_cluster_elements_list[] = { /* Cluster */ {MKV_ELEMENT_ID_CLUSTER, MKV_ELEMENT_ID_SEGMENT, "Cluster", 0}, {MKV_ELEMENT_ID_TIMECODE, MKV_ELEMENT_ID_CLUSTER, "Timecode", mkv_read_subelements_cluster}, {MKV_ELEMENT_ID_SILENT_TRACKS, MKV_ELEMENT_ID_CLUSTER, "SilentTracks", 0}, {MKV_ELEMENT_ID_SILENT_TRACK_NUMBER, MKV_ELEMENT_ID_CLUSTER, "SilentTrackNumber", 0}, {MKV_ELEMENT_ID_POSITION, MKV_ELEMENT_ID_CLUSTER, "Position", 0}, {MKV_ELEMENT_ID_PREV_SIZE, MKV_ELEMENT_ID_CLUSTER, "PrevSize", 0}, {MKV_ELEMENT_ID_BLOCKGROUP, MKV_ELEMENT_ID_CLUSTER, "BlockGroup", 0}, {MKV_ELEMENT_ID_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "Block", 0}, {MKV_ELEMENT_ID_BLOCK_ADDITIONS, MKV_ELEMENT_ID_BLOCKGROUP, "BlockAdditions", 0}, {MKV_ELEMENT_ID_BLOCK_MORE, MKV_ELEMENT_ID_BLOCK_ADDITIONS, "BlockMore", 0}, {MKV_ELEMENT_ID_BLOCK_ADD_ID, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAddId", 0}, {MKV_ELEMENT_ID_BLOCK_ADDITIONAL, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAdditional", 0}, {MKV_ELEMENT_ID_BLOCK_DURATION, MKV_ELEMENT_ID_BLOCKGROUP, "BlockDuration", mkv_read_subelements_cluster}, {MKV_ELEMENT_ID_REFERENCE_PRIORITY, MKV_ELEMENT_ID_BLOCKGROUP, "ReferencePriority", 0}, {MKV_ELEMENT_ID_REFERENCE_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "ReferenceBlock", 0}, {MKV_ELEMENT_ID_CODEC_STATE, MKV_ELEMENT_ID_BLOCKGROUP, "CodecState", 0}, {MKV_ELEMENT_ID_SLICES, MKV_ELEMENT_ID_BLOCKGROUP, "Slices", 0}, {MKV_ELEMENT_ID_TIME_SLICE, MKV_ELEMENT_ID_SLICES, "TimeSlice", 0}, {MKV_ELEMENT_ID_LACE_NUMBER, MKV_ELEMENT_ID_TIME_SLICE, "LaceNumber", 0}, {MKV_ELEMENT_ID_SIMPLE_BLOCK, MKV_ELEMENT_ID_CLUSTER, "SimpleBlock", 0}, /* Global Elements */ {MKV_ELEMENT_ID_CRC32, MKV_ELEMENT_ID_INVALID, "CRC-32", 0}, {MKV_ELEMENT_ID_VOID, MKV_ELEMENT_ID_INVALID, "Void", 0}, {MKV_ELEMENT_ID_UNKNOWN, MKV_ELEMENT_ID_INVALID, "unknown", 0} }; MKV_ELEMENT_T mkv_cue_elements_list[] = { /* Cueing data */ {MKV_ELEMENT_ID_CUES, MKV_ELEMENT_ID_SEGMENT, "Cues", 0}, {MKV_ELEMENT_ID_CUE_POINT, MKV_ELEMENT_ID_CUES, "Cue Point", mkv_read_elements}, {MKV_ELEMENT_ID_CUE_TIME, MKV_ELEMENT_ID_CUE_POINT, "Cue Time", mkv_read_subelements_cue_point}, {MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, MKV_ELEMENT_ID_CUE_POINT, "Cue Track Positions", mkv_read_elements}, {MKV_ELEMENT_ID_CUE_TRACK, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Track", mkv_read_subelements_cue_point}, {MKV_ELEMENT_ID_CUE_CLUSTER_POSITION, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Cluster Position", mkv_read_subelements_cue_point}, {MKV_ELEMENT_ID_CUE_BLOCK_NUMBER, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Block Number", mkv_read_subelements_cue_point}, /* Global Elements */ {MKV_ELEMENT_ID_CRC32, MKV_ELEMENT_ID_INVALID, "CRC-32", 0}, {MKV_ELEMENT_ID_VOID, MKV_ELEMENT_ID_INVALID, "Void", 0}, {MKV_ELEMENT_ID_UNKNOWN, MKV_ELEMENT_ID_INVALID, "unknown", 0} }; /****************************************************************************** List of codec mapping ******************************************************************************/ static const struct { VC_CONTAINER_FOURCC_T fourcc; const char *codecid; VC_CONTAINER_FOURCC_T variant; } codecid_to_fourcc_table[] = { /* Video */ {VC_CONTAINER_CODEC_MP1V, "V_MPEG1", 0}, {VC_CONTAINER_CODEC_MP2V, "V_MPEG2", 0}, {VC_CONTAINER_CODEC_MP4V, "V_MPEG4/ISO/ASP", 0}, {VC_CONTAINER_CODEC_MP4V, "V_MPEG4/ISO/SP", 0}, {VC_CONTAINER_CODEC_MP4V, "V_MPEG4/ISO/AP", 0}, {VC_CONTAINER_CODEC_DIV3, "V_MPEG4/MS/V3", 0}, {VC_CONTAINER_CODEC_H264, "V_MPEG4/ISO/AVC", VC_CONTAINER_VARIANT_H264_AVC1}, {VC_CONTAINER_CODEC_MJPEG, "V_MJPEG", 0}, {VC_CONTAINER_CODEC_RV10, "V_REAL/RV10", 0}, {VC_CONTAINER_CODEC_RV20, "V_REAL/RV20", 0}, {VC_CONTAINER_CODEC_RV30, "V_REAL/RV30", 0}, {VC_CONTAINER_CODEC_RV40, "V_REAL/RV40", 0}, {VC_CONTAINER_CODEC_THEORA, "V_THEORA", 0}, {VC_CONTAINER_CODEC_DIRAC, "V_DIRAC", 0}, {VC_CONTAINER_CODEC_VP8, "V_VP8", 0}, /* Audio */ {VC_CONTAINER_CODEC_MPGA, "A_MPEG/L3", VC_CONTAINER_VARIANT_MPGA_L3}, {VC_CONTAINER_CODEC_MPGA, "A_MPEG/L2", VC_CONTAINER_VARIANT_MPGA_L2}, {VC_CONTAINER_CODEC_MPGA, "A_MPEG/L1", VC_CONTAINER_VARIANT_MPGA_L1}, {VC_CONTAINER_CODEC_MP4A, "A_AAC", 0}, {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/MAIN", 0}, {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/LC", 0}, {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/SSR", 0}, {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/LC/SBR", 0}, {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/MAIN", 0}, {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/LC", 0}, {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/SSR", 0}, {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/LC/SBR", 0}, {VC_CONTAINER_CODEC_AC3, "A_AC3", 0}, {VC_CONTAINER_CODEC_EAC3, "A_EAC3", 0}, {VC_CONTAINER_CODEC_DTS, "A_DTS", 0}, {VC_CONTAINER_CODEC_MLP, "A_MLP", 0}, {0, "A_TRUEHD", 0}, {VC_CONTAINER_CODEC_VORBIS, "A_VORBIS", 0}, {VC_CONTAINER_CODEC_FLAC, "A_FLAC", 0}, {VC_CONTAINER_CODEC_PCM_SIGNED_LE, "A_PCM/INT/LIT", 0}, {VC_CONTAINER_CODEC_PCM_SIGNED_BE, "A_PCM/INT/BIG", 0}, {VC_CONTAINER_CODEC_PCM_FLOAT_LE, "A_PCM/FLOAT/IEEE", 0}, {0, "A_REAL/xyzt", 0}, {0, "A_REAL/14_4", 0}, /* Text */ {VC_CONTAINER_CODEC_TEXT, "S_TEXT/ASCII", 0}, {VC_CONTAINER_CODEC_TEXT, "S_TEXT/UTF8", 0}, {VC_CONTAINER_CODEC_SSA, "S_TEXT/ASS", 0}, {VC_CONTAINER_CODEC_SSA, "S_TEXT/SSA", 0}, {VC_CONTAINER_CODEC_SSA, "S_ASS", 0}, {VC_CONTAINER_CODEC_SSA, "S_SSA", 0}, {VC_CONTAINER_CODEC_USF, "S_TEXT/USF", 0}, {VC_CONTAINER_CODEC_VOBSUB, "S_VOBSUB", 0}, {0, 0} }; /****************************************************************************** Local Functions ******************************************************************************/ static VC_CONTAINER_FOURCC_T mkv_codecid_to_fourcc(const char *codecid, VC_CONTAINER_FOURCC_T *variant) { unsigned int i; for(i = 0; codecid_to_fourcc_table[i].codecid; i++) if(!strcmp(codecid_to_fourcc_table[i].codecid, codecid)) break; if (variant) *variant = codecid_to_fourcc_table[i].variant; return codecid_to_fourcc_table[i].fourcc; } #if 0 /** Find the track associated with an MKV track number */ static VC_CONTAINER_TRACK_T *mkv_reader_find_track( VC_CONTAINER_T *p_ctx, unsigned int mkv_track_num) { VC_CONTAINER_TRACK_T *p_track = 0; unsigned int i; for(i = 0; i < p_ctx->tracks_num; i++) if(p_ctx->tracks[i]->priv->module->number == mkv_track_num) break; if(i < p_ctx->tracks_num) /* We found it */ p_track = p_ctx->tracks[i]; return p_track; } #endif /** Base function used to read an MKV/EBML element header. * This will read the element header do lots of sanity checking and return the element id * and the size of the data contained in the element */ static VC_CONTAINER_STATUS_T mkv_read_element_header(VC_CONTAINER_T *p_ctx, int64_t size, MKV_ELEMENT_ID_T *id, int64_t *element_size, MKV_ELEMENT_ID_T parent_id, MKV_ELEMENT_T **elem) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; MKV_ELEMENT_T *element; module->element_offset = STREAM_POSITION(p_ctx); *id = MKV_READ_ID(p_ctx, "Element ID"); CHECK_POINT(p_ctx); if(!*id) { LOG_DEBUG(p_ctx, "invalid element id %i", *id); return VC_CONTAINER_ERROR_CORRUPTED; } if(elem) element = *elem; else element = mkv_elements_list; /* Find out which Element we are dealing with */ while(element->id && *id != element->id) element++; *element_size = MKV_READ_UINT(p_ctx, "Element Size"); CHECK_POINT(p_ctx); LOG_FORMAT(p_ctx, "- Element %s (ID 0x%x), Size: %"PRIi64", Offset: %"PRIi64, element->psz_name, *id, *element_size, module->element_offset); /* Sanity check the element size */ if(*element_size + 1 < 0 /* Shouldn't ever get that big */ || /* Only the segment / cluster elements can really be massive */ (*id != MKV_ELEMENT_ID_SEGMENT && *id != MKV_ELEMENT_ID_CLUSTER && *element_size > MKV_MAX_ELEMENT_SIZE)) { LOG_DEBUG(p_ctx, "element %s has an invalid size (%"PRIi64")", element->psz_name, *element_size); return VC_CONTAINER_ERROR_CORRUPTED; } if(size >= 0 && *element_size > size) { LOG_DEBUG(p_ctx, "element %s is bigger than it should (%"PRIi64" > %"PRIi64")", element->psz_name, *element_size, size); return VC_CONTAINER_ERROR_CORRUPTED; } /* Sanity check that the element has the right parent */ if(element->id && element->parent_id != MKV_ELEMENT_ID_INVALID && parent_id != MKV_ELEMENT_ID_INVALID && parent_id != element->parent_id) { LOG_FORMAT(p_ctx, "Ignoring mis-placed element %s (ID 0x%x)", element->psz_name, *id); while(element->id != MKV_ELEMENT_ID_UNKNOWN) element++; } /* Sanity check that the element isn't too deeply nested */ if(module->element_level >= MKV_MAX_ELEMENT_LEVEL) { LOG_DEBUG(p_ctx, "element %s is too deep. skipping", element->psz_name); while(element->id != MKV_ELEMENT_ID_UNKNOWN) element++; } if(elem) *elem = element; return STREAM_STATUS(p_ctx); } static VC_CONTAINER_STATUS_T mkv_read_element_data(VC_CONTAINER_T *p_ctx, MKV_ELEMENT_T *element, int64_t element_size, int64_t size) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; int64_t offset; offset = STREAM_POSITION(p_ctx); if (size < 0) size = element_size; if (size < 0) size = INT64_C(1) << 62; /* Call the element specific parsing function */ if(element->pf_func) status = element->pf_func(p_ctx, element->id, element_size < 0 ? size : element_size); if(status != VC_CONTAINER_SUCCESS) LOG_DEBUG(p_ctx, "element %s appears to be corrupted (%i)", element->psz_name, status); if(element_size < 0) return STREAM_STATUS(p_ctx); /* Unknown size */ /* Skip the rest of the element */ element_size -= (STREAM_POSITION(p_ctx) - offset); if(element_size < 0) /* Check for overruns */ { /* Things have gone really bad here and we ended up reading past the end of the * element. We could maybe try to be clever and recover by seeking back to the end * of the element. However if we get there, the file is clearly corrupted so there's * no guarantee it would work anyway. */ LOG_DEBUG(p_ctx, "%"PRIi64" bytes overrun past the end of element %s", -element_size, element->psz_name); return VC_CONTAINER_ERROR_CORRUPTED; } if(element_size) LOG_FORMAT(p_ctx, "%"PRIi64" bytes left unread in element %s", element_size, element->psz_name); if(element_size < MKV_MAX_ELEMENT_SIZE) SKIP_BYTES(p_ctx, element_size); else SEEK(p_ctx, STREAM_POSITION(p_ctx) + element_size); return STREAM_STATUS(p_ctx); } /** Base function used to read an MKV/EBML element. * This will read the element header do lots of sanity checking and pass on the rest * of the reading to the element specific reading function */ static VC_CONTAINER_STATUS_T mkv_read_element(VC_CONTAINER_T *p_ctx, int64_t size, MKV_ELEMENT_ID_T parent_id) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; MKV_ELEMENT_T *element = p_ctx->priv->module->elements_list; int64_t element_size; MKV_ELEMENT_ID_T id; status = mkv_read_element_header(p_ctx, size, &id, &element_size, parent_id, &element); if(status != VC_CONTAINER_SUCCESS) return status; return mkv_read_element_data(p_ctx, element, element_size, size); } /** Reads an unsigned integer element */ static VC_CONTAINER_STATUS_T mkv_read_element_data_uint(VC_CONTAINER_T *p_ctx, int64_t size, uint64_t *value) { switch(size) { case 1: *value = READ_U8(p_ctx, "u8-integer"); break; case 2: *value = READ_U16(p_ctx, "u16-integer"); break; case 3: *value = READ_U24(p_ctx, "u24-integer"); break; case 4: *value = READ_U32(p_ctx, "u32-integer"); break; case 5: *value = READ_U40(p_ctx, "u40-integer"); break; case 6: *value = READ_U48(p_ctx, "u48-integer"); break; case 7: *value = READ_U56(p_ctx, "u56-integer"); break; case 8: *value = READ_U64(p_ctx, "u64-integer"); break; default: return VC_CONTAINER_ERROR_CORRUPTED; } return STREAM_STATUS(p_ctx); } /** Reads a float element */ static VC_CONTAINER_STATUS_T mkv_read_element_data_float(VC_CONTAINER_T *p_ctx, int64_t size, double *value) { union { uint32_t u32; uint64_t u64; float f; double d; } u; switch(size) { case 4: u.u32 = READ_U32(p_ctx, "f32-float"); *value = u.f; break; case 8: u.u64 = READ_U64(p_ctx, "f64-float"); *value = u.d; break; default: return VC_CONTAINER_ERROR_CORRUPTED; } LOG_FORMAT(p_ctx, "float: %f", *value); return STREAM_STATUS(p_ctx); } /** Reads an MKV EBML element */ static VC_CONTAINER_STATUS_T mkv_read_element_ebml(VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; int64_t offset = STREAM_POSITION(p_ctx); /* Read contained elements */ module->element_level++; while(status == VC_CONTAINER_SUCCESS && size >= MKV_ELEMENT_MIN_HEADER_SIZE) { offset = STREAM_POSITION(p_ctx); status = mkv_read_element(p_ctx, size, id); size -= (STREAM_POSITION(p_ctx) - offset); } module->element_level--; return status; } /** Reads the MKV EBML sub-element */ static VC_CONTAINER_STATUS_T mkv_read_subelements_ebml( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; uint64_t value; /* Deal with DocType first since it's a special case */ if(id == MKV_ELEMENT_ID_DOCTYPE) { char doctype[] = "matroska doctype"; /* Check we've got the right doctype string for matroska */ if(size <= 0) goto unknown_doctype; if(size > (int)sizeof(doctype)) goto unknown_doctype; if((int)READ_STRING(p_ctx, doctype, size, "string") != size) return STREAM_STATUS(p_ctx); if((size != sizeof("matroska")-1 || strncmp(doctype, "matroska", (int)size)) && (size != sizeof("webm")-1 || strncmp(doctype, "webm", (int)size))) goto unknown_doctype; module->is_doctype_valid = true; return VC_CONTAINER_SUCCESS; unknown_doctype: LOG_DEBUG(p_ctx, "invalid doctype"); return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; } /* The rest are just unsigned integers */ status = mkv_read_element_data_uint(p_ctx, size, &value); if(status != VC_CONTAINER_SUCCESS) return status; switch(id) { case MKV_ELEMENT_ID_EBML_VERSION: case MKV_ELEMENT_ID_EBML_READ_VERSION: if(value != 1) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; break; case MKV_ELEMENT_ID_EBML_MAX_ID_LENGTH: if(value > 4) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; break; case MKV_ELEMENT_ID_EBML_MAX_SIZE_LENGTH: if(value > 8) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; break; case MKV_ELEMENT_ID_DOCTYPE_VERSION: case MKV_ELEMENT_ID_DOCTYPE_READ_VERSION: default: break; } return STREAM_STATUS(p_ctx); } static VC_CONTAINER_STATUS_T mkv_read_elements( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; int64_t offset = STREAM_POSITION(p_ctx); bool unknown_size = size < 0; /* Read contained elements */ module->element_level++; while(status == VC_CONTAINER_SUCCESS && (unknown_size || size >= MKV_ELEMENT_MIN_HEADER_SIZE)) { offset = STREAM_POSITION(p_ctx); status = mkv_read_element(p_ctx, size, id); if(!unknown_size) size -= (STREAM_POSITION(p_ctx) - offset); } module->element_level--; return status; } static VC_CONTAINER_STATUS_T mkv_read_element_segment( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; int64_t offset = STREAM_POSITION(p_ctx); bool unknown_size = size < 0; /* Read contained elements */ /* Initialise state used by reader */ module->state.level = 0; module->state.levels[0].offset = STREAM_POSITION(p_ctx); module->state.levels[0].size = size; module->state.levels[0].id = MKV_ELEMENT_ID_SEGMENT; module->state.levels[0].data_start = 0; module->state.levels[0].data_offset = 0; module->timecode_scale = 1000000; module->duration = 0.0; module->segment_offset = STREAM_POSITION(p_ctx); module->segment_size = size; /* Read contained elements until we have all the information we need to start * playing the stream */ module->element_level++; while(status == VC_CONTAINER_SUCCESS && (unknown_size || size >= MKV_ELEMENT_MIN_HEADER_SIZE)) { MKV_ELEMENT_T *child = mkv_elements_list; MKV_ELEMENT_ID_T child_id; int64_t child_size; offset = STREAM_POSITION(p_ctx); status = mkv_read_element_header(p_ctx, size, &child_id, &child_size, id, &child); if(status != VC_CONTAINER_SUCCESS) break; if(child_id == MKV_ELEMENT_ID_CLUSTER) { /* We found the start of the data */ module->cluster_offset = module->element_offset; module->state.level = 1; module->state.levels[1].offset = STREAM_POSITION(p_ctx); module->state.levels[1].size = child_size; module->state.levels[1].id = MKV_ELEMENT_ID_CLUSTER; module->state.levels[1].data_start = 0; module->state.levels[1].data_offset = 0; break; } status = mkv_read_element_data(p_ctx, child, child_size, size); if(!unknown_size) size -= (STREAM_POSITION(p_ctx) - offset); } module->element_level--; return status; } static VC_CONTAINER_STATUS_T mkv_read_element_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_ES_TYPE_T es_type = VC_CONTAINER_ES_TYPE_UNKNOWN; VC_CONTAINER_TRACK_MODULE_T *track_module; VC_CONTAINER_TRACK_T *track; VC_CONTAINER_FOURCC_T fourcc = 0, variant = 0; unsigned int i, extra_size = 0, extra_offset = 0, is_wf = 0, is_bmih = 0; /* Allocate and initialise track data */ if(p_ctx->tracks_num >= MKV_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; p_ctx->tracks[p_ctx->tracks_num] = track = vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; module->parsing = track; track_module = track->priv->module; track->is_enabled = true; track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; track_module->timecode_scale = 1.0; track_module->es_type.video.frame_rate = 0; status = mkv_read_elements( p_ctx, id, size ); if(status != VC_CONTAINER_SUCCESS) goto error; /* Sanity check the data we got from the track entry */ if(!track_module->number || !track_module->type) { status = VC_CONTAINER_ERROR_FORMAT_INVALID; goto error; } /* Check the encodings for the track are supported */ if(track_module->encodings_num > MKV_MAX_ENCODINGS) { status = VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; goto error; } for(i = 0; i < track_module->encodings_num; i++) { if(track_module->encodings[i].type != MKV_CONTENT_ENCODING_COMPRESSION_HEADER || !track_module->encodings[i].data_size) { status = VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; goto error; } } /* Find out the track type */ if(track_module->type == 0x1) es_type = VC_CONTAINER_ES_TYPE_VIDEO; else if(track_module->type == 0x2) es_type = VC_CONTAINER_ES_TYPE_AUDIO; else if(track_module->type == 0x11) es_type = VC_CONTAINER_ES_TYPE_SUBPICTURE; if(es_type == VC_CONTAINER_ES_TYPE_UNKNOWN) { status = VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; goto error; } if(!strcmp(track_module->codecid, "V_MS/VFW/FOURCC")) { if(vc_container_bitmapinfoheader_to_es_format(track->format->extradata, track->format->extradata_size, &extra_offset, &extra_size, track->format) == VC_CONTAINER_SUCCESS) { fourcc = track->format->codec; is_bmih = 1; } track->format->extradata += extra_offset; track->format->extradata_size = extra_size; } else if(!strcmp(track_module->codecid, "A_MS/ACM")) { if(vc_container_waveformatex_to_es_format(track->format->extradata, track->format->extradata_size, &extra_offset, &extra_size, track->format) == VC_CONTAINER_SUCCESS) { fourcc = track->format->codec; is_wf = 1; } track->format->extradata += extra_offset; track->format->extradata_size = extra_size; } else if((!strncmp(track_module->codecid, "A_AAC/MPEG2/", sizeof("A_AAC/MPEG2/")-1) || !strncmp(track_module->codecid, "A_AAC/MPEG4/", sizeof("A_AAC/MPEG4/")-1)) && !track->format->extradata_size) { static const unsigned int sample_rates[16] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350}; unsigned int samplerate, samplerate_idx, profile, sbr = 0; uint8_t *extra; fourcc = mkv_codecid_to_fourcc(track_module->codecid, &variant); /* Create extra data */ if( !strcmp( &track_module->codecid[12], "MAIN" ) ) profile = 0; else if( !strcmp( &track_module->codecid[12], "LC" ) ) profile = 1; else if( !strcmp( &track_module->codecid[12], "SSR" ) ) profile = 2; else if( !strcmp( &track_module->codecid[12], "LC/SBR" ) ) { profile = 1; sbr = 1; } else profile = 3; samplerate = track_module->es_type.audio.sampling_frequency; for( samplerate_idx = 0; samplerate_idx < 13; samplerate_idx++ ) if( sample_rates[samplerate_idx] == samplerate ) break; status = vc_container_track_allocate_extradata(p_ctx, track, sbr ? 5 : 2); if(status != VC_CONTAINER_SUCCESS) goto error; track->format->extradata_size = sbr ? 5 : 2; extra = track->format->extradata; extra[0] = ((profile + 1) << 3) | ((samplerate_idx & 0xe) >> 1); extra[1] = ((samplerate_idx & 0x1) << 7) | (track_module->es_type.audio.channels << 3); if(sbr) { unsigned int sync_extension_type = 0x2B7; samplerate = track_module->es_type.audio.output_sampling_frequency; for(samplerate_idx = 0; samplerate_idx < 13; samplerate_idx++) if(sample_rates[samplerate_idx] == samplerate) break; extra[2] = (sync_extension_type >> 3) & 0xFF; extra[3] = ((sync_extension_type & 0x7) << 5) | 5; extra[4] = (1 << 7) | (samplerate_idx << 3); } } else fourcc = mkv_codecid_to_fourcc(track_module->codecid, &variant); if(!fourcc) { LOG_DEBUG(p_ctx, "codec id %s is not supported", track_module->codecid); } LOG_DEBUG(p_ctx, "found track %4.4s", (char *)&fourcc); track->format->codec = fourcc; track->format->codec_variant = variant; track->format->es_type = es_type; switch(es_type) { case VC_CONTAINER_ES_TYPE_VIDEO: if(!is_bmih) { track->format->type->video.width = track_module->es_type.video.pixel_width; track->format->type->video.height = track_module->es_type.video.pixel_height; } track->format->type->video.visible_width = track->format->type->video.width; track->format->type->video.visible_height = track->format->type->video.height; if(track_module->es_type.video.pixel_crop_left < track->format->type->video.visible_width && track_module->es_type.video.pixel_crop_top < track->format->type->video.visible_height) { track->format->type->video.x_offset = track_module->es_type.video.pixel_crop_left; track->format->type->video.y_offset = track_module->es_type.video.pixel_crop_right; track->format->type->video.visible_width -= track->format->type->video.x_offset; track->format->type->video.visible_height -= track->format->type->video.y_offset; } if(track_module->es_type.video.pixel_crop_right < track->format->type->video.visible_width && track_module->es_type.video.pixel_crop_bottom < track->format->type->video.visible_height) { track->format->type->video.visible_width -= track_module->es_type.video.pixel_crop_right; track->format->type->video.visible_height -= track_module->es_type.video.pixel_crop_bottom; } if(track_module->es_type.video.frame_rate) { track->format->type->video.frame_rate_den = 100; track->format->type->video.frame_rate_num = 100 * track_module->es_type.video.frame_rate; } if(track_module->es_type.video.display_width && track_module->es_type.video.display_height) { track->format->type->video.par_num = track_module->es_type.video.display_width * track->format->type->video.visible_height; track->format->type->video.par_den = track_module->es_type.video.display_height * track->format->type->video.visible_width; vc_container_maths_rational_simplify(&track->format->type->video.par_num, &track->format->type->video.par_den); } break; case VC_CONTAINER_ES_TYPE_AUDIO: if(is_wf) break; track->format->type->audio.sample_rate = track_module->es_type.audio.sampling_frequency; if(track_module->es_type.audio.output_sampling_frequency) track->format->type->audio.sample_rate = track_module->es_type.audio.output_sampling_frequency; track->format->type->audio.channels = track_module->es_type.audio.channels; track->format->type->audio.bits_per_sample = track_module->es_type.audio.bit_depth; break; default: case VC_CONTAINER_ES_TYPE_SUBPICTURE: track->format->type->subpicture.encoding = VC_CONTAINER_CHAR_ENCODING_UTF8; if(!strcmp(track_module->codecid, "S_TEXT/ASCII")) track->format->type->subpicture.encoding = VC_CONTAINER_CHAR_ENCODING_UNKNOWN; } track->is_enabled = true; p_ctx->tracks_num++; return VC_CONTAINER_SUCCESS; error: for(i = 0; i < MKV_MAX_ENCODINGS; i++) free(track_module->encodings[i].data); vc_container_free_track(p_ctx, track); return status; } static VC_CONTAINER_STATUS_T mkv_read_subelements_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = module->parsing; VC_CONTAINER_TRACK_MODULE_T *track_module = track->priv->module; uint64_t value; /* Deal with string elements */ if( id == MKV_ELEMENT_ID_NAME || id == MKV_ELEMENT_ID_LANGUAGE || id == MKV_ELEMENT_ID_TRACK_CODEC_ID || id == MKV_ELEMENT_ID_TRACK_CODEC_NAME ) { char stringbuf[MKV_MAX_STRING_SIZE+1]; if(size > MKV_MAX_STRING_SIZE) { LOG_DEBUG(p_ctx, "string truncated (%i>%i)", (int)size, MKV_MAX_STRING_SIZE); size = MKV_MAX_STRING_SIZE; } if(READ_BYTES(p_ctx, stringbuf, size) != (size_t)size) { //XXX do sane thing return STREAM_STATUS(p_ctx); } stringbuf[size] = 0; /* null terminate */ LOG_FORMAT(p_ctx, "%s", stringbuf); if(id == MKV_ELEMENT_ID_TRACK_CODEC_ID) strncpy(track_module->codecid, stringbuf, MKV_CODECID_MAX-1); return VC_CONTAINER_SUCCESS; } /* Deal with codec private data */ if( id == MKV_ELEMENT_ID_TRACK_CODEC_PRIVATE ) { status = vc_container_track_allocate_extradata(p_ctx, track, (unsigned int)size); if(status != VC_CONTAINER_SUCCESS) return status; track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size); return STREAM_STATUS(p_ctx); } /* The rest are just unsigned integers */ status = mkv_read_element_data_uint(p_ctx, size, &value); if(status != VC_CONTAINER_SUCCESS) return status; switch(id) { case MKV_ELEMENT_ID_TRACK_NUMBER: track_module->number = value; break; case MKV_ELEMENT_ID_TRACK_TYPE: track_module->type = value; break; case MKV_ELEMENT_ID_DEFAULT_DURATION: track_module->frame_duration = value; break; default: break; } return status; } static VC_CONTAINER_STATUS_T mkv_read_subelements_video( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; uint64_t value; /* Deal with floating point values first */ if(id == MKV_ELEMENT_ID_FRAME_RATE) { double fvalue; status = mkv_read_element_data_float(p_ctx, size, &fvalue); if(status != VC_CONTAINER_SUCCESS) return status; track_module->es_type.video.frame_rate = fvalue; return status; } /* The rest are just unsigned integers */ status = mkv_read_element_data_uint(p_ctx, size, &value); if(status != VC_CONTAINER_SUCCESS) return status; switch(id) { case MKV_ELEMENT_ID_PIXEL_WIDTH: track_module->es_type.video.pixel_width = value; break; case MKV_ELEMENT_ID_PIXEL_HEIGHT: track_module->es_type.video.pixel_height = value; break; case MKV_ELEMENT_ID_PIXEL_CROP_BOTTOM: track_module->es_type.video.pixel_crop_bottom = value; break; case MKV_ELEMENT_ID_PIXEL_CROP_TOP: track_module->es_type.video.pixel_crop_top = value; break; case MKV_ELEMENT_ID_PIXEL_CROP_LEFT: track_module->es_type.video.pixel_crop_left = value; break; case MKV_ELEMENT_ID_PIXEL_CROP_RIGHT: track_module->es_type.video.pixel_crop_right = value; break; case MKV_ELEMENT_ID_DISPLAY_WIDTH: track_module->es_type.video.display_width = value; break; case MKV_ELEMENT_ID_DISPLAY_HEIGHT: track_module->es_type.video.display_height = value; break; case MKV_ELEMENT_ID_DISPLAY_UNIT: track_module->es_type.video.display_unit = value; break; case MKV_ELEMENT_ID_ASPECT_RATIO_TYPE: track_module->es_type.video.aspect_ratio_type = value; break; default: break; } return status; } static VC_CONTAINER_STATUS_T mkv_read_subelements_audio( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; uint64_t value; /* Deal with floating point values first */ if(id == MKV_ELEMENT_ID_SAMPLING_FREQUENCY || id == MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY) { double fvalue; status = mkv_read_element_data_float(p_ctx, size, &fvalue); if(status != VC_CONTAINER_SUCCESS) return status; if(id == MKV_ELEMENT_ID_SAMPLING_FREQUENCY) track_module->es_type.audio.sampling_frequency = fvalue; if(id == MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY) track_module->es_type.audio.output_sampling_frequency = fvalue; return status; } /* The rest are just unsigned integers */ status = mkv_read_element_data_uint(p_ctx, size, &value); if(status != VC_CONTAINER_SUCCESS) return status; switch(id) { case MKV_ELEMENT_ID_CHANNELS: track_module->es_type.audio.channels = value; break; case MKV_ELEMENT_ID_BIT_DEPTH: track_module->es_type.audio.bit_depth = value; break; default: break; } return status; } static VC_CONTAINER_STATUS_T mkv_read_element_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; VC_CONTAINER_STATUS_T status; status = mkv_read_elements(p_ctx, id, size); track_module->encodings_num++; return status; } static VC_CONTAINER_STATUS_T mkv_read_subelements_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; VC_CONTAINER_STATUS_T status; uint64_t value; /* These are just unsigned integers */ status = mkv_read_element_data_uint(p_ctx, size, &value); if(status != VC_CONTAINER_SUCCESS) return status; if(track_module->encodings_num >= MKV_MAX_ENCODINGS) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; switch(id) { case MKV_ELEMENT_ID_CONTENT_ENCODING_TYPE: if(value == 0) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_COMPRESSION_ZLIB; if(value == 1) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_ENCRYPTION; else track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_UNKNOWN; break; default: break; } return status; } static VC_CONTAINER_STATUS_T mkv_read_subelements_compression( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; VC_CONTAINER_STATUS_T status; uint64_t value; uint8_t *data; if(id == MKV_ELEMENT_ID_CONTENT_COMPRESSION_ALGO) { status = mkv_read_element_data_uint(p_ctx, size, &value); if(status != VC_CONTAINER_SUCCESS) return status; if(value == 0) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_COMPRESSION_ZLIB; if(value == 3) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_COMPRESSION_HEADER; else track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_UNKNOWN; return status; } if(id == MKV_ELEMENT_ID_CONTENT_COMPRESSION_SETTINGS) { if(track_module->encodings[track_module->encodings_num].type == MKV_CONTENT_ENCODING_COMPRESSION_HEADER) { if(size > MKV_MAX_ENCODING_DATA) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; data = malloc((int)size); if(!data) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; track_module->encodings[track_module->encodings_num].data = data; track_module->encodings[track_module->encodings_num].data_size = READ_BYTES(p_ctx, data, size); if(track_module->encodings[track_module->encodings_num].data_size != size) track_module->encodings[track_module->encodings_num].data_size = 0; return STREAM_STATUS(p_ctx); } else { SKIP_BYTES(p_ctx, size); } return STREAM_STATUS(p_ctx); } return VC_CONTAINER_SUCCESS; } static VC_CONTAINER_STATUS_T mkv_read_subelements_info( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; uint64_t value; double fvalue; switch(id) { case MKV_ELEMENT_ID_TIMECODE_SCALE: status = mkv_read_element_data_uint(p_ctx, size, &value); if(status != VC_CONTAINER_SUCCESS) break; module->timecode_scale = value; break; case MKV_ELEMENT_ID_DURATION: status = mkv_read_element_data_float(p_ctx, size, &fvalue); if(status != VC_CONTAINER_SUCCESS) break; module->duration = fvalue; break; case MKV_ELEMENT_ID_TITLE: case MKV_ELEMENT_ID_MUXING_APP: case MKV_ELEMENT_ID_WRITING_APP: SKIP_STRING(p_ctx, size, "string"); break; default: break; } return status; } static VC_CONTAINER_STATUS_T mkv_read_subelements_seek_head( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; uint64_t value; if(id == MKV_ELEMENT_ID_SEEK) { module->seekhead_elem_id = MKV_ELEMENT_ID_UNKNOWN; module->seekhead_elem_offset = -1; status = mkv_read_elements(p_ctx, id, size); if(status == VC_CONTAINER_SUCCESS && !module->cues_offset && module->seekhead_elem_id == MKV_ELEMENT_ID_CUES && module->seekhead_elem_offset) module->cues_offset = module->seekhead_elem_offset; if(status == VC_CONTAINER_SUCCESS && !module->tags_offset && module->seekhead_elem_id == MKV_ELEMENT_ID_TAGS && module->seekhead_elem_offset) module->tags_offset = module->seekhead_elem_offset; return status; } if(id == MKV_ELEMENT_ID_SEEK_ID) { MKV_ELEMENT_T *element = mkv_elements_list; id = MKV_READ_ID(p_ctx, "Element ID"); module->seekhead_elem_id = id; /* Find out which Element we are dealing with */ while(element->id && id != element->id) element++; LOG_FORMAT(p_ctx, "element: %s (ID 0x%x)", element->psz_name, id); } else if(id == MKV_ELEMENT_ID_SEEK_POSITION) { status = mkv_read_element_data_uint(p_ctx, size, &value); if(status != VC_CONTAINER_SUCCESS) return status; LOG_FORMAT(p_ctx, "offset: %"PRIi64, value + module->segment_offset); module->seekhead_elem_offset = value + module->segment_offset; } return status; } static VC_CONTAINER_STATUS_T mkv_read_element_cues( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_PARAM_UNUSED(id); VC_CONTAINER_PARAM_UNUSED(size); module->cues_offset = module->element_offset; return VC_CONTAINER_SUCCESS; } static VC_CONTAINER_STATUS_T mkv_read_subelements_cue_point( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status; uint64_t value; /* All the values are unsigned integers */ status = mkv_read_element_data_uint(p_ctx, size, &value); if(status != VC_CONTAINER_SUCCESS) return status; switch(id) { case MKV_ELEMENT_ID_CUE_TIME: module->cue_timecode = value; break; case MKV_ELEMENT_ID_CUE_TRACK: module->cue_track = value; break; case MKV_ELEMENT_ID_CUE_CLUSTER_POSITION: module->cue_cluster_offset = value; break; case MKV_ELEMENT_ID_CUE_BLOCK_NUMBER: module->cue_block = value; break; default: break; } return status; } static VC_CONTAINER_STATUS_T mkv_read_subelements_cluster( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status; uint64_t value; /* The rest are just unsigned integers */ status = mkv_read_element_data_uint(p_ctx, size, &value); if(status != VC_CONTAINER_SUCCESS) return status; switch(id) { //XXX case MKV_ELEMENT_ID_TIMECODE: module->state.cluster_timecode = value; break; case MKV_ELEMENT_ID_BLOCK_DURATION: module->state.frame_duration = value; break; default: break; } return status; } /*******************************/ static VC_CONTAINER_STATUS_T mkv_skip_element(VC_CONTAINER_T *p_ctx, MKV_READER_STATE_T *state) { /* Skip any trailing data from the current element */ int64_t skip = state->levels[state->level].offset + state->levels[state->level].size - STREAM_POSITION(p_ctx); if(skip < 0) /* Check for overruns */ { /* Things have gone really bad here and we ended up reading past the end of the * element. We could maybe try to be clever and recover by seeking back to the end * of the element. However if we get there, the file is clearly corrupted so there's * no guarantee it would work anyway. */ LOG_DEBUG(p_ctx, "%"PRIi64" bytes overrun past the end of the element", -skip); return VC_CONTAINER_ERROR_CORRUPTED; } if(skip) LOG_FORMAT(p_ctx, "%"PRIi64" bytes left unread at the end of element", skip); state->level--; if (skip >= MKV_MAX_ELEMENT_SIZE) return SEEK(p_ctx, STREAM_POSITION(p_ctx) + skip); SKIP_BYTES(p_ctx, skip); return STREAM_STATUS(p_ctx); } static VC_CONTAINER_STATUS_T mkv_find_next_element(VC_CONTAINER_T *p_ctx, MKV_READER_STATE_T *state, MKV_ELEMENT_ID_T element_id) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; int64_t element_size, element_offset; MKV_ELEMENT_ID_T id; /* Skip all elements until we find the next requested element */ do { MKV_ELEMENT_T *element = mkv_cluster_elements_list; /* Check whether we've reach the end of the parent element */ if(STREAM_POSITION(p_ctx) >= state->levels[state->level].offset + state->levels[state->level].size) return VC_CONTAINER_ERROR_NOT_FOUND; status = mkv_read_element_header(p_ctx, INT64_C(1) << 30, &id, &element_size, state->levels[state->level].id, &element); element_offset = STREAM_POSITION(p_ctx); if(status != VC_CONTAINER_SUCCESS) return status; if(id == element_id) break; if(element_id == MKV_ELEMENT_ID_BLOCKGROUP && id == MKV_ELEMENT_ID_SIMPLE_BLOCK) break; if(element_id == MKV_ELEMENT_ID_BLOCK && id == MKV_ELEMENT_ID_REFERENCE_BLOCK) state->seen_ref_block = 1; /* Check whether we've reached the end of the parent element */ if(STREAM_POSITION(p_ctx) + element_size >= state->levels[state->level].offset + state->levels[state->level].size) return VC_CONTAINER_ERROR_NOT_FOUND; status = mkv_read_element_data(p_ctx, element, element_size, INT64_C(1) << 30); #if 0 if(element_size < MKV_MAX_ELEMENT_SIZE) SKIP_BYTES(p_ctx, element_size); else SEEK(p_ctx, STREAM_POSITION(p_ctx) + element_size); #endif } while (status == VC_CONTAINER_SUCCESS && STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS); if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) return STREAM_STATUS(p_ctx); state->level++; state->levels[state->level].offset = element_offset; state->levels[state->level].size = element_size; state->levels[state->level].id = id; return VC_CONTAINER_SUCCESS; } static VC_CONTAINER_STATUS_T mkv_find_next_segment(VC_CONTAINER_T *p_ctx, MKV_READER_STATE_T *state) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; int64_t element_size, element_offset; MKV_ELEMENT_ID_T id; /* Skip all elements until we find the next segment */ do { MKV_ELEMENT_T *element = mkv_cluster_elements_list; status = mkv_read_element_header(p_ctx, INT64_C(-1), &id, &element_size, MKV_ELEMENT_ID_INVALID, &element); element_offset = STREAM_POSITION(p_ctx); if(status != VC_CONTAINER_SUCCESS) return status; if(id == MKV_ELEMENT_ID_SEGMENT) break; status = mkv_read_element_data(p_ctx, element, element_size, INT64_C(-1)); } while (status == VC_CONTAINER_SUCCESS && STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS); if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) return STREAM_STATUS(p_ctx); state->level++; state->levels[state->level].offset = element_offset; state->levels[state->level].size = element_size; state->levels[state->level].id = id; return VC_CONTAINER_SUCCESS; } static VC_CONTAINER_STATUS_T mkv_find_next_block(VC_CONTAINER_T *p_ctx, MKV_READER_STATE_T *state) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND; do { if(state->level < 0) { #ifdef ENABLE_MKV_EXTRA_LOGGING LOG_DEBUG(p_ctx, "find segment"); #endif status = mkv_find_next_segment(p_ctx, state); if(status == VC_CONTAINER_SUCCESS) { LOG_ERROR(p_ctx, "multi-segment files not supported"); status = VC_CONTAINER_ERROR_EOS; break; } } if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK || state->levels[state->level].id == MKV_ELEMENT_ID_SIMPLE_BLOCK) { status = mkv_skip_element(p_ctx, state); } if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCKGROUP) { #ifdef ENABLE_MKV_EXTRA_LOGGING LOG_DEBUG(p_ctx, "find block"); #endif state->seen_ref_block = 0; state->frame_duration = 0; status = mkv_find_next_element(p_ctx, state, MKV_ELEMENT_ID_BLOCK); if(status == VC_CONTAINER_SUCCESS) break; if(status == VC_CONTAINER_ERROR_NOT_FOUND) status = mkv_skip_element(p_ctx, state); } if(state->levels[state->level].id == MKV_ELEMENT_ID_CLUSTER) { #ifdef ENABLE_MKV_EXTRA_LOGGING LOG_DEBUG(p_ctx, "find blockgroup or simpleblock"); #endif state->frame_duration = 0; status = mkv_find_next_element(p_ctx, state, MKV_ELEMENT_ID_BLOCKGROUP); if(status == VC_CONTAINER_SUCCESS && state->levels[state->level].id == MKV_ELEMENT_ID_SIMPLE_BLOCK) break; if(status == VC_CONTAINER_ERROR_NOT_FOUND) status = mkv_skip_element(p_ctx, state); } if(state->levels[state->level].id == MKV_ELEMENT_ID_SEGMENT) { #ifdef ENABLE_MKV_EXTRA_LOGGING LOG_DEBUG(p_ctx, "find cluster"); #endif status = mkv_find_next_element(p_ctx, state, MKV_ELEMENT_ID_CLUSTER); if(status == VC_CONTAINER_ERROR_NOT_FOUND) status = mkv_skip_element(p_ctx, state); } } while(status == VC_CONTAINER_SUCCESS && STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS); return status == VC_CONTAINER_SUCCESS ? STREAM_STATUS(p_ctx) : status; } static VC_CONTAINER_STATUS_T mkv_read_next_frame_header(VC_CONTAINER_T *p_ctx, MKV_READER_STATE_T *state, uint32_t *pi_track, uint32_t *pi_length) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = 0; unsigned int i, track, flags, is_first_lace = 0; int64_t size, pts; if ((state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK || state->levels[state->level].id == MKV_ELEMENT_ID_SIMPLE_BLOCK) && state->levels[state->level].data_start + state->levels[state->level].data_offset < state->levels[state->level].size) goto end; status = mkv_find_next_block(p_ctx, state); if (status != VC_CONTAINER_SUCCESS) return status; /* We have a block */ size = state->levels[state->level].size; track = MKV_READ_UINT(p_ctx, "Track Number"); LOG_FORMAT(p_ctx, "Track Number: %u", track); pts = (int16_t)MKV_READ_U16(p_ctx, "Timecode"); flags = MKV_READ_U8(p_ctx, "Flags"); if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK) flags &= 0x0F; //TODO improve sanity checking /* Sanity checking */ if(size < 0) return VC_CONTAINER_ERROR_CORRUPTED; if(STREAM_STATUS(p_ctx)) return STREAM_STATUS(p_ctx); for (i = 0; i < p_ctx->tracks_num; i++) if (p_ctx->tracks[i]->priv->module->number == track) { track_module = p_ctx->tracks[i]->priv->module; break; } /* Finding out if we have a keyframe when dealing with an MKV_ELEMENT_ID_BLOCK is tricky */ if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK && i < p_ctx->tracks_num && p_ctx->tracks[i]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) { /* The absence of a ReferenceBlock element tells us if we are a keyframe or not. * The problem we have is that this element can appear before as well as after the block element. * To avoid seeking to look for this element, we do some guess work. */ if(!state->seen_ref_block && state->level && state->levels[state->level].offset + state->levels[state->level].size >= state->levels[state->level-1].offset + state->levels[state->level-1].size) flags |= 0x80; } /* Take care of the lacing */ state->lacing_num_frames = 0; if(i < p_ctx->tracks_num && (flags & 0x06)) { unsigned int i, value = 0; int32_t fs = 0; state->lacing_num_frames = MKV_READ_U8(p_ctx, "Lacing Head"); state->lacing_size = 0; switch((flags & 0x06)>>1) { case 1: /* Xiph lacing */ for(i = 0; i < state->lacing_num_frames; i++, fs = 0) { do { value = vc_container_io_read_uint8(p_ctx->priv->io); fs += value; size--; } while(value == 255); LOG_FORMAT(p_ctx, "Frame Size: %i", (int)fs); if(state->lacing_num_frames > MKV_MAX_LACING_NUM) continue; state->lacing_sizes[state->lacing_num_frames-(i+1)] = fs; } break; case 3: /* EBML lacing */ for(i = 0; i < state->lacing_num_frames; i++) { if(!i) fs = MKV_READ_UINT(p_ctx, "Frame Size"); else fs += MKV_READ_SINT(p_ctx, "Frame Size"); LOG_FORMAT(p_ctx, "Frame Size: %i", (int)fs); if(state->lacing_num_frames > MKV_MAX_LACING_NUM) continue; state->lacing_sizes[state->lacing_num_frames-(i+1)] = fs; } break; default: /* Fixed-size lacing */ state->lacing_size = size / (state->lacing_num_frames+1); break; } /* There is a max number of laced frames we can support but after we can still give back * all the other frames in 1 packet */ if(state->lacing_num_frames > MKV_MAX_LACING_NUM) state->lacing_num_frames = MKV_MAX_LACING_NUM; /* Sanity check the size of the frames */ if(size < 0) return VC_CONTAINER_ERROR_CORRUPTED; if(STREAM_STATUS(p_ctx)) return STREAM_STATUS(p_ctx); state->lacing_current_size = state->lacing_size; if(!state->lacing_size) { int64_t frames_size = 0; for(i = state->lacing_num_frames; i > 0; i--) { if(frames_size + state->lacing_sizes[i-1] > size) /* return error ? */ state->lacing_sizes[i-1] = size - frames_size; frames_size += state->lacing_sizes[i-1]; } } state->lacing_current_size = 0; state->lacing_num_frames++; /* Will be decremented further down */ is_first_lace = 1; } state->track = i; state->pts = (state->cluster_timecode + pts) * module->timecode_scale; if(track_module) state->pts *= track_module->timecode_scale; state->pts /= 1000; state->flags = flags; state->frame_duration = state->frame_duration * module->timecode_scale / 1000; if(state->lacing_num_frames) state->frame_duration /= state->lacing_num_frames; if(!state->frame_duration && track_module) state->frame_duration = track_module->frame_duration / 1000; state->levels[state->level].data_start = STREAM_POSITION(p_ctx) - state->levels[state->level].offset; state->levels[state->level].data_offset = 0; /* Deal with header stripping compression */ state->header_size = 0; if(track_module && track_module->encodings_num) { state->header_size = track_module->encodings[0].data_size; state->header_data = track_module->encodings[0].data; } state->header_size_backup = state->header_size; end: *pi_length = state->levels[state->level].size - state->levels[state->level].data_start - state->levels[state->level].data_offset + state->header_size; *pi_track = state->track; /* Special case for lacing */ if(state->lacing_num_frames && state->levels[state->level].data_offset >= state->lacing_current_size) { /* We need to switch to the next lace */ if(--state->lacing_num_frames) { unsigned int lace_size = state->lacing_size; if(!state->lacing_size) lace_size = state->lacing_sizes[state->lacing_num_frames-1]; state->lacing_current_size = lace_size; } state->levels[state->level].data_start += state->levels[state->level].data_offset; state->levels[state->level].data_offset = 0; if(!is_first_lace && state->frame_duration) state->pts += state->frame_duration; else if(!is_first_lace) state->pts = VC_CONTAINER_TIME_UNKNOWN; /* Deal with header stripping compression */ state->header_data -= (state->header_size_backup - state->header_size); state->header_size = state->header_size_backup; } if(state->lacing_num_frames) *pi_length = state->lacing_current_size - state->levels[state->level].data_offset + state->header_size; return status == VC_CONTAINER_SUCCESS ? STREAM_STATUS(p_ctx) : status; } static VC_CONTAINER_STATUS_T mkv_read_frame_data(VC_CONTAINER_T *p_ctx, MKV_READER_STATE_T *state, uint8_t *p_data, uint32_t *pi_length) { uint64_t size; uint32_t header_size; size = state->levels[state->level].size - state->levels[state->level].data_start - state->levels[state->level].data_offset; /* Special case for lacing */ if(state->lacing_num_frames) { size = state->lacing_current_size - state->levels[state->level].data_offset; if(!p_data) { size = SKIP_BYTES(p_ctx, size); state->levels[state->level].data_offset += size; return STREAM_STATUS(p_ctx); } } size += state->header_size; if(!p_data) return mkv_skip_element(p_ctx, state); if(size > *pi_length) size = *pi_length; header_size = state->header_size; if(header_size) { if(header_size > size) header_size = size; memcpy(p_data, state->header_data, header_size); state->header_size -= header_size; state->header_data += header_size; size -= header_size; } size = READ_BYTES(p_ctx, p_data + header_size, size); state->levels[state->level].data_offset += size; *pi_length = size + header_size; return STREAM_STATUS(p_ctx); } /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ static VC_CONTAINER_STATUS_T mkv_reader_read(VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *p_track = 0; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; uint32_t buffer_size = 0, track = 0, data_size; MKV_READER_STATE_T *state = &module->state; /* If a specific track has been selected, we need to use the track packet state */ if(flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) { p_track = p_ctx->tracks[p_packet->track]; state = p_track->priv->module->state; } /**/ if(state->eos) return VC_CONTAINER_ERROR_EOS; if(state->corrupted) return VC_CONTAINER_ERROR_CORRUPTED; /* Look at the next frame header */ status = mkv_read_next_frame_header(p_ctx, state, &track, &data_size); if(status == VC_CONTAINER_ERROR_EOS) state->eos = true; if(status == VC_CONTAINER_ERROR_CORRUPTED) state->corrupted = true; if(status != VC_CONTAINER_SUCCESS) return status; if(track >= p_ctx->tracks_num || !p_ctx->tracks[track]->is_enabled) { /* Skip frame */ status = mkv_read_frame_data(p_ctx, state, 0, &data_size); if (status != VC_CONTAINER_SUCCESS) return status; return VC_CONTAINER_ERROR_CONTINUE; } if((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO)) /* Skip packet */ return mkv_read_frame_data(p_ctx, state, 0, &data_size); p_packet->dts = p_packet->pts = state->pts; p_packet->flags = 0; if(state->flags & 0x80) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; if(!state->levels[state->level].data_offset) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; p_packet->size = data_size; p_packet->track = track; if(flags & VC_CONTAINER_READ_FLAG_SKIP) return mkv_read_frame_data(p_ctx, state, 0, &data_size ); else if(flags & VC_CONTAINER_READ_FLAG_INFO) return VC_CONTAINER_SUCCESS; /* Read the frame data */ buffer_size = p_packet->buffer_size; status = mkv_read_frame_data(p_ctx, state, p_packet->data, &buffer_size); if(status != VC_CONTAINER_SUCCESS) { /* FIXME */ return status; } p_packet->size = buffer_size; if(buffer_size != data_size) p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mkv_reader_seek(VC_CONTAINER_T *p_ctx, int64_t *p_offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; MKV_READER_STATE_T *state = &module->state; uint64_t offset = 0, prev_offset = 0, position = STREAM_POSITION(p_ctx); int64_t time_offset = 0, prev_time_offset = 0; unsigned int i, video_track; MKV_ELEMENT_T *element = mkv_cue_elements_list; int64_t size, element_size; MKV_ELEMENT_ID_T id; VC_CONTAINER_PARAM_UNUSED(mode); VC_CONTAINER_PARAM_UNUSED(flags); /* Find out if we have a video track */ for(video_track = 0; video_track < p_ctx->tracks_num; video_track++) if(p_ctx->tracks[video_track]->is_enabled && p_ctx->tracks[video_track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) break; if(!*p_offset) goto end; /* Nothing much to do */ if(!module->cues_offset) {status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; goto error;} /* We need to do a search in the cue list */ status = SEEK(p_ctx, module->cues_offset); if(status != VC_CONTAINER_SUCCESS) goto error; /* First read the header of the cues element */ status = mkv_read_element_header(p_ctx, INT64_C(-1) /* TODO */, &id, &element_size, MKV_ELEMENT_ID_SEGMENT, &element); if(status != VC_CONTAINER_SUCCESS || id != MKV_ELEMENT_ID_CUES) goto error; size = element_size; module->elements_list = mkv_cue_elements_list; do { MKV_ELEMENT_T *element = mkv_cue_elements_list; int64_t element_offset = STREAM_POSITION(p_ctx); /* Exit condition for when we've scanned the whole cues list */ if(size <= 0) { if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) break; /* Just use the last valid entry in that case */ status = VC_CONTAINER_ERROR_EOS; goto error; } status = mkv_read_element_header(p_ctx, size, &id, &element_size, MKV_ELEMENT_ID_CUES, &element); size -= STREAM_POSITION(p_ctx) - element_offset; if(status == VC_CONTAINER_SUCCESS && element->id != MKV_ELEMENT_ID_UNKNOWN) status = mkv_read_element_data(p_ctx, element, element_size, size); if(status != VC_CONTAINER_SUCCESS || element->id == MKV_ELEMENT_ID_UNKNOWN) { if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) break; /* Just use the last valid entry in that case */ goto error; } size -= element_size; if(id != MKV_ELEMENT_ID_CUE_POINT) continue; /* Ignore cue points which don't belong to the track we want */ if(video_track != p_ctx->tracks_num && p_ctx->tracks[video_track]->priv->module->number != module->cue_track) continue; time_offset = module->cue_timecode * module->timecode_scale / 1000; offset = module->cue_cluster_offset; LOG_DEBUG(p_ctx, "INDEX: %"PRIi64, time_offset); if( time_offset > *p_offset ) { if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) { time_offset = prev_time_offset; offset = prev_offset; } break; } prev_time_offset = time_offset; prev_offset = offset; } while( 1 ); module->elements_list = mkv_elements_list; *p_offset = time_offset; end: /* Try seeking to the requested position */ status = SEEK(p_ctx, module->segment_offset + offset); if(status != VC_CONTAINER_SUCCESS && status != VC_CONTAINER_ERROR_EOS) goto error; /* Reinitialise the state */ memset(state, 0, sizeof(*state)); state->levels[0].offset = module->segment_offset; state->levels[0].size = module->segment_size; state->levels[0].id = MKV_ELEMENT_ID_SEGMENT; if(status == VC_CONTAINER_ERROR_EOS) state->eos = true; for(i = 0; i < p_ctx->tracks_num; i++) { VC_CONTAINER_TRACK_T *p_track = p_ctx->tracks[i]; p_track->priv->module->state = state; } /* If we have a video track, we skip frames until the next keyframe */ for(i = 0; video_track != p_ctx->tracks_num && i < 200 /* limit search */; ) { uint32_t track, data_size; status = mkv_read_next_frame_header(p_ctx, state, &track, &data_size); if(status != VC_CONTAINER_SUCCESS) break; //FIXME if(track == video_track) i++; if(track == video_track && (state->flags & 0x80) && state->pts >= time_offset) break; /* Skip frame */ status = mkv_read_frame_data(p_ctx, state, 0, &data_size); } return VC_CONTAINER_SUCCESS; error: /* Reset everything as it was before the seek */ SEEK(p_ctx, position); if(status == VC_CONTAINER_SUCCESS) status = VC_CONTAINER_ERROR_FAILED; return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mkv_reader_close(VC_CONTAINER_T *p_ctx) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; unsigned int i, j; for(i = 0; i < p_ctx->tracks_num; i++) { for(j = 0; j < MKV_MAX_ENCODINGS; j++) free(p_ctx->tracks[i]->priv->module->encodings[j].data); vc_container_free_track(p_ctx, p_ctx->tracks[i]); } free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T mkv_reader_open(VC_CONTAINER_T *p_ctx) { VC_CONTAINER_MODULE_T *module = 0; VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; uint8_t buffer[4]; // Can start with ASCII strings ???? /* Check for an EBML element */ if(PEEK_BYTES(p_ctx, buffer, 4) < 4 || buffer[0] != 0x1A || buffer[1] != 0x45 || buffer[2] != 0xDF || buffer[3] != 0xA3) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* * We are dealing with an MKV file */ /* Allocate our context */ module = malloc(sizeof(*module)); if(!module) {status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error;} memset(module, 0, sizeof(*module)); p_ctx->priv->module = module; p_ctx->tracks = module->tracks; module->elements_list = mkv_elements_list; /* Read and sanity check the EBML header */ status = mkv_read_element(p_ctx, INT64_C(-1), MKV_ELEMENT_ID_UNKNOWN); if(status != VC_CONTAINER_SUCCESS) goto error; if(!module->is_doctype_valid) {status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; goto error;} /* Read the other elements until we find the start of the data */ do { status = mkv_read_element(p_ctx, INT64_C(-1), MKV_ELEMENT_ID_UNKNOWN); if(status != VC_CONTAINER_SUCCESS) break; if(module->cluster_offset) break; } while(1); /* Bail out if we didn't find a track */ if(!p_ctx->tracks_num) { status = VC_CONTAINER_ERROR_NO_TRACK_AVAILABLE; goto error; } /* * We now have all the information we really need to start playing the stream */ p_ctx->priv->pf_close = mkv_reader_close; p_ctx->priv->pf_read = mkv_reader_read; p_ctx->priv->pf_seek = mkv_reader_seek; p_ctx->duration = module->duration / 1000 * module->timecode_scale; /* Check if we're done */ if(!STREAM_SEEKABLE(p_ctx)) return VC_CONTAINER_SUCCESS; if(module->cues_offset && (int64_t)module->cues_offset < p_ctx->size) p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; if(module->tags_offset) { status = SEEK(p_ctx, module->tags_offset); if(status == VC_CONTAINER_SUCCESS) status = mkv_read_element(p_ctx, INT64_C(-1) /*FIXME*/, MKV_ELEMENT_ID_SEGMENT); } /* Seek back to the start of the data */ return SEEK(p_ctx, module->state.levels[1].offset); error: LOG_DEBUG(p_ctx, "mkv: error opening stream (%i)", status); if(module) mkv_reader_close(p_ctx); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak reader_open mkv_reader_open #endif userland/containers/mp4/000077500000000000000000000000001421703157200155425ustar00rootroot00000000000000userland/containers/mp4/CMakeLists.txt000066400000000000000000000010261421703157200203010ustar00rootroot00000000000000# Container module needs to go in as a plugins so different prefix # and install path set(CMAKE_SHARED_LIBRARY_PREFIX "") # Make sure the compiler can find the necessary include files include_directories (../..) add_library(reader_mp4 ${LIBRARY_TYPE} mp4_reader.c) target_link_libraries(reader_mp4 containers) install(TARGETS reader_mp4 DESTINATION ${VMCS_PLUGIN_DIR}) add_library(writer_mp4 ${LIBRARY_TYPE} mp4_writer.c) target_link_libraries(writer_mp4 containers) install(TARGETS writer_mp4 DESTINATION ${VMCS_PLUGIN_DIR}) userland/containers/mp4/mp4_common.h000066400000000000000000000162041421703157200177660ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef MP4_COMMON_H #define MP4_COMMON_H /****************************************************************************** Type definitions. ******************************************************************************/ typedef enum { MP4_BOX_TYPE_UNKNOWN = 0, MP4_BOX_TYPE_ROOT = VC_FOURCC('r','o','o','t'), MP4_BOX_TYPE_FTYP = VC_FOURCC('f','t','y','p'), MP4_BOX_TYPE_MDAT = VC_FOURCC('m','d','a','t'), MP4_BOX_TYPE_MOOV = VC_FOURCC('m','o','o','v'), MP4_BOX_TYPE_MVHD = VC_FOURCC('m','v','h','d'), MP4_BOX_TYPE_TRAK = VC_FOURCC('t','r','a','k'), MP4_BOX_TYPE_TKHD = VC_FOURCC('t','k','h','d'), MP4_BOX_TYPE_MDIA = VC_FOURCC('m','d','i','a'), MP4_BOX_TYPE_MDHD = VC_FOURCC('m','d','h','d'), MP4_BOX_TYPE_HDLR = VC_FOURCC('h','d','l','r'), MP4_BOX_TYPE_MINF = VC_FOURCC('m','i','n','f'), MP4_BOX_TYPE_VMHD = VC_FOURCC('v','m','h','d'), MP4_BOX_TYPE_SMHD = VC_FOURCC('s','m','h','d'), MP4_BOX_TYPE_DINF = VC_FOURCC('d','i','n','f'), MP4_BOX_TYPE_DREF = VC_FOURCC('d','r','e','f'), MP4_BOX_TYPE_STBL = VC_FOURCC('s','t','b','l'), MP4_BOX_TYPE_STSD = VC_FOURCC('s','t','s','d'), MP4_BOX_TYPE_STTS = VC_FOURCC('s','t','t','s'), MP4_BOX_TYPE_CTTS = VC_FOURCC('c','t','t','s'), MP4_BOX_TYPE_STSC = VC_FOURCC('s','t','s','c'), MP4_BOX_TYPE_STSZ = VC_FOURCC('s','t','s','z'), MP4_BOX_TYPE_STCO = VC_FOURCC('s','t','c','o'), MP4_BOX_TYPE_CO64 = VC_FOURCC('c','o','6','4'), MP4_BOX_TYPE_STSS = VC_FOURCC('s','t','s','s'), MP4_BOX_TYPE_VIDE = VC_FOURCC('v','i','d','e'), MP4_BOX_TYPE_SOUN = VC_FOURCC('s','o','u','n'), MP4_BOX_TYPE_TEXT = VC_FOURCC('t','e','x','t'), MP4_BOX_TYPE_FREE = VC_FOURCC('f','r','e','e'), MP4_BOX_TYPE_SKIP = VC_FOURCC('s','k','i','p'), MP4_BOX_TYPE_WIDE = VC_FOURCC('w','i','d','e'), MP4_BOX_TYPE_PNOT = VC_FOURCC('p','m','o','t'), MP4_BOX_TYPE_PICT = VC_FOURCC('P','I','C','T'), MP4_BOX_TYPE_UDTA = VC_FOURCC('u','d','t','a'), MP4_BOX_TYPE_UUID = VC_FOURCC('u','u','i','d'), MP4_BOX_TYPE_ESDS = VC_FOURCC('e','s','d','s'), MP4_BOX_TYPE_AVCC = VC_FOURCC('a','v','c','C'), MP4_BOX_TYPE_D263 = VC_FOURCC('d','2','6','3'), MP4_BOX_TYPE_DAMR = VC_FOURCC('d','a','m','r'), MP4_BOX_TYPE_DAWP = VC_FOURCC('d','a','w','p'), MP4_BOX_TYPE_DEVC = VC_FOURCC('d','e','v','c'), MP4_BOX_TYPE_WAVE = VC_FOURCC('w','a','v','e'), MP4_BOX_TYPE_ZERO = 0 } MP4_BOX_TYPE_T; typedef enum { MP4_BRAND_ISOM = VC_FOURCC('i','s','o','m'), MP4_BRAND_MP42 = VC_FOURCC('m','p','4','2'), MP4_BRAND_3GP4 = VC_FOURCC('3','g','p','4'), MP4_BRAND_3GP5 = VC_FOURCC('3','g','p','5'), MP4_BRAND_3GP6 = VC_FOURCC('3','g','p','6'), MP4_BRAND_SKM2 = VC_FOURCC('s','k','m','2'), MP4_BRAND_SKM3 = VC_FOURCC('s','k','m','3'), MP4_BRAND_QT = VC_FOURCC('q','t',' ',' '), MP4_BRAND_NUM } MP4_BRAND_T; typedef enum { MP4_SAMPLE_TABLE_STTS = 0, /* decoding time to sample */ MP4_SAMPLE_TABLE_STSZ = 1, /* sample size */ MP4_SAMPLE_TABLE_STSC = 2, /* sample to chunk */ MP4_SAMPLE_TABLE_STCO = 3, /* sample to chunk-offset */ MP4_SAMPLE_TABLE_STSS = 4, /* sync sample */ MP4_SAMPLE_TABLE_CO64 = 5, /* sample to chunk-offset */ MP4_SAMPLE_TABLE_CTTS = 6, /* composite time to sample */ MP4_SAMPLE_TABLE_NUM } MP4_SAMPLE_TABLE_T; /* Values for object_type_indication (mp4_decoder_config_descriptor) * see ISO/IEC 14496-1:2001(E) section 8.6.6.2 table 8 p. 30 * see ISO/IEC 14496-15:2003 (draft) section 4.2.2 table 3 p. 11 * see SKT Spec 8.2.3 p. 107 * see 3GPP2 Spec v1.0 p. 22 */ #define MP4_MPEG4_VISUAL_OBJECT_TYPE 0x20 /* visual ISO/IEC 14496-2 */ #define MP4_MPEG4_H264_OBJECT_TYPE 0x21 /* visual ISO/IEC 14496-10 */ #define MP4_MPEG4_H264_PS_OBJECT_TYPE 0x22 /* visual ISO/IEC 14496-10 (used for parameter ES) */ #define MP4_MPEG4_AAC_LC_OBJECT_TYPE 0x40 /* audio ISO/IEC 14496-3 */ #define MP4_MPEG2_SP_OBJECT_TYPE 0x60 /* visual ISO/IEC 13818-2 Simple Profile */ #define MP4_MPEG2_MP_OBJECT_TYPE 0x61 /* visual ISO/IEC 13818-2 Main Profile */ #define MP4_MPEG2_SNR_OBJECT_TYPE 0x62 /* visual ISO/IEC 13818-2 SNR Profile */ #define MP4_MPEG2_AAC_LC_OBJECT_TYPE 0x67 /* audio ISO/IEC 13818-7 LowComplexity Profile */ #define MP4_MP3_OBJECT_TYPE 0x69 /* audio ISO/IEC 13818-3 */ #define MP4_MPEG1_VISUAL_OBJECT_TYPE 0x6A /* visual ISO/IEC 11172-2 */ #define MP4_MPEG1_AUDIO_OBJECT_TYPE 0x6B /* audio ISO/IEC 11172-3 */ #define MP4_JPEG_OBJECT_TYPE 0x6C /* visual ISO/IEC 10918-1 */ #define MP4_SKT_EVRC_2V1_OBJECT_TYPE 0x82 /* SKT spec V2.1 for EVRC */ #define MP4_KTF_EVRC_OBJECT_TYPE 0xC2 /* KTF spec V1.2 for EVRC */ #define MP4_KTF_AMR_OBJECT_TYPE 0xC4 /* KTF spec V1.2 for AMR */ #define MP4_KTF_MP3_OBJECT_TYPE 0xC5 /* KTF spec V1.2 for MP3 */ #define MP4_SKT_TEXT_OBJECT_TYPE 0xD0 /* SKT spec V2.2 for Text */ #define MP4_SKT_EVRC_OBJECT_TYPE 0xD1 /* SKT spec V2.2 for EVRC */ #define MP4_3GPP2_QCELP_OBJECT_TYPE 0xE1 /* 3GPP2 spec V1.0 for QCELP13K */ #endif /* MP4_COMMON_H */ userland/containers/mp4/mp4_reader.c000066400000000000000000002104441421703157200177350ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include /* Work-around for MSVC debugger issue */ #define VC_CONTAINER_MODULE_T VC_CONTAINER_MODULE_MP4_READER_T #define VC_CONTAINER_TRACK_MODULE_T VC_CONTAINER_TRACK_MODULE_MP4_READER_T #define CONTAINER_IS_BIG_ENDIAN #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_logging.h" #include "containers/mp4/mp4_common.h" #undef CONTAINER_HELPER_LOG_INDENT #define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->box_level VC_CONTAINER_STATUS_T mp4_reader_open( VC_CONTAINER_T *p_ctx ); /****************************************************************************** TODO: - aspect ratio - itunes gapless - edit list - subpicture track ******************************************************************************/ /****************************************************************************** Defines. ******************************************************************************/ #define MP4_TRACKS_MAX 16 #define MP4_BOX_MIN_HEADER_SIZE 8 #define MP4_MAX_BOX_SIZE (1<<29) /* Does not apply to the mdat box */ #define MP4_MAX_BOX_LEVEL 10 #define MP4_MAX_SAMPLES_BATCH_SIZE (16*1024) #define MP4_SKIP_U8(ctx,n) (size -= 1, SKIP_U8(ctx,n)) #define MP4_SKIP_U16(ctx,n) (size -= 2, SKIP_U16(ctx,n)) #define MP4_SKIP_U24(ctx,n) (size -= 3, SKIP_U24(ctx,n)) #define MP4_SKIP_U32(ctx,n) (size -= 4, SKIP_U32(ctx,n)) #define MP4_SKIP_U64(ctx,n) (size -= 8, SKIP_U64(ctx,n)) #define MP4_READ_U8(ctx,n) (size -= 1, READ_U8(ctx,n)) #define MP4_READ_U16(ctx,n) (size -= 2, READ_U16(ctx,n)) #define MP4_READ_U24(ctx,n) (size -= 3, READ_U24(ctx,n)) #define MP4_READ_U32(ctx,n) (size -= 4, READ_U32(ctx,n)) #define MP4_READ_U64(ctx,n) (size -= 8, READ_U64(ctx,n)) #define MP4_READ_FOURCC(ctx,n) (size -= 4, READ_FOURCC(ctx,n)) #define MP4_SKIP_FOURCC(ctx,n) (size -= 4, SKIP_FOURCC(ctx,n)) #define MP4_READ_BYTES(ctx,buffer,sz) (size -= sz, READ_BYTES(ctx,buffer,sz)) #define MP4_SKIP_BYTES(ctx,sz) (size -= sz, SKIP_BYTES(ctx,sz)) #define MP4_SKIP_STRING(ctx,sz,n) (size -= sz, SKIP_STRING(ctx,sz,n)) /****************************************************************************** Type definitions. ******************************************************************************/ typedef struct { VC_CONTAINER_STATUS_T status; int64_t duration; int64_t pts; int64_t dts; uint32_t sample; int64_t offset; unsigned int sample_offset; unsigned int sample_size; uint32_t sample_duration; uint32_t sample_duration_count; int32_t sample_composition_offset; uint32_t sample_composition_count; uint32_t next_sync_sample; bool keyframe; uint32_t samples_per_chunk; uint32_t chunks; uint32_t samples_in_chunk; struct { uint32_t entry; } sample_table[MP4_SAMPLE_TABLE_NUM]; } MP4_READER_STATE_T; typedef struct VC_CONTAINER_TRACK_MODULE_T { MP4_READER_STATE_T state; int64_t timescale; uint8_t object_type_indication; uint32_t sample_size; struct { int64_t offset; uint32_t entries; uint32_t entry_size; } sample_table[MP4_SAMPLE_TABLE_NUM]; uint32_t samples_batch_size; } VC_CONTAINER_TRACK_MODULE_T; typedef struct VC_CONTAINER_MODULE_T { int64_t box_offset; int box_level; MP4_BRAND_T brand; int64_t timescale; VC_CONTAINER_TRACK_T *tracks[MP4_TRACKS_MAX]; unsigned int current_track; bool found_moov; int64_t data_offset; int64_t data_size; } VC_CONTAINER_MODULE_T; /****************************************************************************** Static functions within this file. ******************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box( VC_CONTAINER_T *p_ctx, int64_t size, MP4_BOX_TYPE_T parent_type ); static VC_CONTAINER_STATUS_T mp4_read_boxes( VC_CONTAINER_T *p_ctx, int64_t size, MP4_BOX_TYPE_T type ); static VC_CONTAINER_STATUS_T mp4_read_box_ftyp( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_moov( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_mvhd( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_trak( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_tkhd( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_mdia( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_mdhd( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_hdlr( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_minf( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_vmhd( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_smhd( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_dinf( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_dref( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_stbl( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_stsd( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_stts( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_ctts( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_stsc( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_stsz( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_stco( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_co64( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_stss( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_vide( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_soun( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_text( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_esds( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_vide_avcC( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_vide_d263( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_soun_damr( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_soun_dawp( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_soun_devc( VC_CONTAINER_T *p_ctx, int64_t size ); static VC_CONTAINER_STATUS_T mp4_read_box_soun_wave( VC_CONTAINER_T *p_ctx, int64_t size ); static struct { const MP4_BOX_TYPE_T type; VC_CONTAINER_STATUS_T (*pf_func)( VC_CONTAINER_T *, int64_t ); const MP4_BOX_TYPE_T parent_type; } mp4_box_list[] = { {MP4_BOX_TYPE_FTYP, mp4_read_box_ftyp, MP4_BOX_TYPE_ROOT}, {MP4_BOX_TYPE_MDAT, 0, MP4_BOX_TYPE_ROOT}, {MP4_BOX_TYPE_MOOV, mp4_read_box_moov, MP4_BOX_TYPE_ROOT}, {MP4_BOX_TYPE_MVHD, mp4_read_box_mvhd, MP4_BOX_TYPE_MOOV}, {MP4_BOX_TYPE_TRAK, mp4_read_box_trak, MP4_BOX_TYPE_MOOV}, {MP4_BOX_TYPE_TKHD, mp4_read_box_tkhd, MP4_BOX_TYPE_TRAK}, {MP4_BOX_TYPE_MDIA, mp4_read_box_mdia, MP4_BOX_TYPE_TRAK}, {MP4_BOX_TYPE_MDHD, mp4_read_box_mdhd, MP4_BOX_TYPE_MDIA}, {MP4_BOX_TYPE_HDLR, mp4_read_box_hdlr, MP4_BOX_TYPE_MDIA}, {MP4_BOX_TYPE_MINF, mp4_read_box_minf, MP4_BOX_TYPE_MDIA}, {MP4_BOX_TYPE_VMHD, mp4_read_box_vmhd, MP4_BOX_TYPE_MINF}, {MP4_BOX_TYPE_SMHD, mp4_read_box_smhd, MP4_BOX_TYPE_MINF}, {MP4_BOX_TYPE_DINF, mp4_read_box_dinf, MP4_BOX_TYPE_MINF}, {MP4_BOX_TYPE_DREF, mp4_read_box_dref, MP4_BOX_TYPE_DINF}, {MP4_BOX_TYPE_STBL, mp4_read_box_stbl, MP4_BOX_TYPE_MINF}, {MP4_BOX_TYPE_STSD, mp4_read_box_stsd, MP4_BOX_TYPE_STBL}, {MP4_BOX_TYPE_STTS, mp4_read_box_stts, MP4_BOX_TYPE_STBL}, {MP4_BOX_TYPE_CTTS, mp4_read_box_ctts, MP4_BOX_TYPE_STBL}, {MP4_BOX_TYPE_STSC, mp4_read_box_stsc, MP4_BOX_TYPE_STBL}, {MP4_BOX_TYPE_STSZ, mp4_read_box_stsz, MP4_BOX_TYPE_STBL}, {MP4_BOX_TYPE_STCO, mp4_read_box_stco, MP4_BOX_TYPE_STBL}, {MP4_BOX_TYPE_CO64, mp4_read_box_co64, MP4_BOX_TYPE_STBL}, {MP4_BOX_TYPE_STSS, mp4_read_box_stss, MP4_BOX_TYPE_STBL}, {MP4_BOX_TYPE_VIDE, mp4_read_box_vide, MP4_BOX_TYPE_STSD}, {MP4_BOX_TYPE_SOUN, mp4_read_box_soun, MP4_BOX_TYPE_STSD}, {MP4_BOX_TYPE_TEXT, mp4_read_box_text, MP4_BOX_TYPE_STSD}, /* Codec specific boxes */ {MP4_BOX_TYPE_AVCC, mp4_read_box_vide_avcC, MP4_BOX_TYPE_VIDE}, {MP4_BOX_TYPE_D263, mp4_read_box_vide_d263, MP4_BOX_TYPE_VIDE}, {MP4_BOX_TYPE_ESDS, mp4_read_box_esds, MP4_BOX_TYPE_VIDE}, {MP4_BOX_TYPE_DAMR, mp4_read_box_soun_damr, MP4_BOX_TYPE_SOUN}, {MP4_BOX_TYPE_DAWP, mp4_read_box_soun_dawp, MP4_BOX_TYPE_SOUN}, {MP4_BOX_TYPE_DEVC, mp4_read_box_soun_devc, MP4_BOX_TYPE_SOUN}, {MP4_BOX_TYPE_WAVE, mp4_read_box_soun_wave, MP4_BOX_TYPE_SOUN}, {MP4_BOX_TYPE_ESDS, mp4_read_box_esds, MP4_BOX_TYPE_SOUN}, {MP4_BOX_TYPE_UNKNOWN, 0, MP4_BOX_TYPE_UNKNOWN} }; static struct { const VC_CONTAINER_FOURCC_T type; const VC_CONTAINER_FOURCC_T codec; bool batch; } mp4_codec_mapping[] = { {VC_FOURCC('a','v','c','1'), VC_CONTAINER_CODEC_H264, 0}, {VC_FOURCC('m','p','4','v'), VC_CONTAINER_CODEC_MP4V, 0}, {VC_FOURCC('s','2','6','3'), VC_CONTAINER_CODEC_H263, 0}, {VC_FOURCC('m','p','e','g'), VC_CONTAINER_CODEC_MP2V, 0}, {VC_FOURCC('m','j','p','a'), VC_CONTAINER_CODEC_MJPEGA, 0}, {VC_FOURCC('m','j','p','b'), VC_CONTAINER_CODEC_MJPEGB, 0}, {VC_FOURCC('j','p','e','g'), VC_CONTAINER_CODEC_JPEG, 0}, {VC_FOURCC('m','p','4','a'), VC_CONTAINER_CODEC_MP4A, 0}, {VC_FOURCC('s','a','m','r'), VC_CONTAINER_CODEC_AMRNB, 0}, {VC_FOURCC('s','a','w','b'), VC_CONTAINER_CODEC_AMRWB, 0}, {VC_FOURCC('s','a','w','p'), VC_CONTAINER_CODEC_AMRWBP, 0}, {VC_FOURCC('a','c','-','3'), VC_CONTAINER_CODEC_AC3, 0}, {VC_FOURCC('e','c','-','3'), VC_CONTAINER_CODEC_EAC3, 0}, {VC_FOURCC('s','e','v','c'), VC_CONTAINER_CODEC_EVRC, 0}, {VC_FOURCC('e','v','r','c'), VC_CONTAINER_CODEC_EVRC, 0}, {VC_FOURCC('s','q','c','p'), VC_CONTAINER_CODEC_QCELP, 0}, {VC_FOURCC('a','l','a','w'), VC_CONTAINER_CODEC_ALAW, 1}, {VC_FOURCC('u','l','a','w'), VC_CONTAINER_CODEC_MULAW, 1}, {VC_FOURCC('t','w','o','s'), VC_CONTAINER_CODEC_PCM_SIGNED_BE, 1}, {VC_FOURCC('s','o','w','t'), VC_CONTAINER_CODEC_PCM_SIGNED_LE, 1}, {0, 0}, }; static VC_CONTAINER_FOURCC_T mp4_box_type_to_codec(VC_CONTAINER_FOURCC_T type) { int i; for(i = 0; mp4_codec_mapping[i].type; i++ ) if(mp4_codec_mapping[i].type == type) break; return mp4_codec_mapping[i].codec; } static bool codec_needs_batch_mode(VC_CONTAINER_FOURCC_T codec) { int i; for(i = 0; mp4_codec_mapping[i].codec; i++ ) if(mp4_codec_mapping[i].codec == codec) break; return mp4_codec_mapping[i].batch; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_header( VC_CONTAINER_T *p_ctx, int64_t size, MP4_BOX_TYPE_T *box_type, int64_t *box_size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; int64_t offset = STREAM_POSITION(p_ctx); module->box_offset = offset; *box_size = _READ_U32(p_ctx); *box_type = _READ_FOURCC(p_ctx); if(!*box_type) return VC_CONTAINER_ERROR_CORRUPTED; if(*box_size == 1) *box_size = _READ_U64(p_ctx); LOG_FORMAT(p_ctx, "- Box %4.4s, Size: %"PRIi64", Offset: %"PRIi64, (const char *)box_type, *box_size, offset); /* Sanity check the box size */ if(*box_size < 0 /* Shouldn't ever get that big */ || /* Only the mdat box can really be massive */ (*box_type != MP4_BOX_TYPE_MDAT && *box_size > MP4_MAX_BOX_SIZE)) { LOG_DEBUG(p_ctx, "box %4.4s has an invalid size (%"PRIi64")", (const char *)box_type, *box_size); return VC_CONTAINER_ERROR_CORRUPTED; } #if 0 /* It is valid for a box to have a zero size (i.e unknown) if it is the last one */ if(*box_size == 0 && size >= 0) *box_size = size; else if(*box_size == 0) *box_size = INT64_C(-1); #else if(*box_size <= 0) { LOG_DEBUG(p_ctx, "box %4.4s has an invalid size (%"PRIi64")", (const char *)box_type, *box_size); return VC_CONTAINER_ERROR_CORRUPTED; } #endif /* Sanity check box size against parent */ if(size >= 0 && *box_size > size) { LOG_DEBUG(p_ctx, "box %4.4s is bigger than it should (%"PRIi64" > %"PRIi64")", (const char *)box_type, *box_size, size); return VC_CONTAINER_ERROR_CORRUPTED; } *box_size -= (STREAM_POSITION(p_ctx) - offset); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_data( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T box_type, int64_t box_size, MP4_BOX_TYPE_T parent_type ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; int64_t offset = STREAM_POSITION(p_ctx); VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; unsigned int i; /* Check if the box is a recognised one */ for(i = 0; mp4_box_list[i].type; i++) if(mp4_box_list[i].type == box_type && mp4_box_list[i].parent_type == parent_type) break; if(mp4_box_list[i].type == MP4_BOX_TYPE_UNKNOWN) for(i = 0; mp4_box_list[i].type; i++) if(mp4_box_list[i].type == box_type) break; /* Sanity check that the box has the right parent */ if(mp4_box_list[i].type != MP4_BOX_TYPE_UNKNOWN && mp4_box_list[i].parent_type != MP4_BOX_TYPE_UNKNOWN && parent_type != MP4_BOX_TYPE_UNKNOWN && parent_type != mp4_box_list[i].parent_type) { LOG_FORMAT(p_ctx, "Ignoring mis-placed box %4.4s", (const char *)&box_type); goto skip; } /* Sanity check that the element isn't too deeply nested */ if(module->box_level >= 2 * MP4_MAX_BOX_LEVEL) { LOG_DEBUG(p_ctx, "box %4.4s is too deep. skipping", (const char *)&box_type); goto skip; } module->box_level++; /* Call the box specific parsing function */ if(mp4_box_list[i].pf_func) status = mp4_box_list[i].pf_func(p_ctx, box_size); module->box_level--; if(status != VC_CONTAINER_SUCCESS) LOG_DEBUG(p_ctx, "box %4.4s appears to be corrupted (%i)", (const char *)&box_type, status); skip: /* Skip the rest of the box */ box_size -= (STREAM_POSITION(p_ctx) - offset); if(box_size < 0) /* Check for overruns */ { /* Things have gone really bad here and we ended up reading past the end of the * box. We could maybe try to be clever and recover by seeking back to the end * of the box. However if we get there, the file is clearly corrupted so there's * no guarantee it would work anyway. */ LOG_DEBUG(p_ctx, "%"PRIi64" bytes overrun past the end of box %4.4s", -box_size, (const char *)&box_type); return VC_CONTAINER_ERROR_CORRUPTED; } if(box_size) LOG_FORMAT(p_ctx, "%"PRIi64" bytes left unread in box %4.4s", box_size, (const char *)&box_type ); if(box_size < MP4_MAX_BOX_SIZE) box_size = SKIP_BYTES(p_ctx, box_size); else SEEK(p_ctx, STREAM_POSITION(p_ctx) + box_size); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box( VC_CONTAINER_T *p_ctx, int64_t size, MP4_BOX_TYPE_T parent_type ) { VC_CONTAINER_STATUS_T status; MP4_BOX_TYPE_T box_type; int64_t box_size; status = mp4_read_box_header( p_ctx, size, &box_type, &box_size ); if(status != VC_CONTAINER_SUCCESS) return status; return mp4_read_box_data( p_ctx, box_type, box_size, parent_type ); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_boxes( VC_CONTAINER_T *p_ctx, int64_t size, MP4_BOX_TYPE_T type) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; int64_t offset = STREAM_POSITION(p_ctx); bool unknown_size = size < 0; /* Read contained boxes */ module->box_level++; while(status == VC_CONTAINER_SUCCESS && (unknown_size || size >= MP4_BOX_MIN_HEADER_SIZE)) { offset = STREAM_POSITION(p_ctx); status = mp4_read_box(p_ctx, size, type); if(!unknown_size) size -= (STREAM_POSITION(p_ctx) - offset); } module->box_level--; return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_ftyp( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; module->brand = MP4_READ_FOURCC(p_ctx, "major_brand"); MP4_SKIP_U32(p_ctx, "minor_version"); while(size >= 4) MP4_SKIP_FOURCC(p_ctx, "compatible_brands"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_moov( VC_CONTAINER_T *p_ctx, int64_t size ) { return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_MOOV); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_mvhd( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; uint32_t version, i; int64_t duration; version = MP4_READ_U8(p_ctx, "version"); MP4_SKIP_U24(p_ctx, "flags"); if(version) { MP4_SKIP_U64(p_ctx, "creation_time"); MP4_SKIP_U64(p_ctx, "modification_time"); module->timescale = MP4_READ_U32(p_ctx, "timescale"); duration = MP4_READ_U64(p_ctx, "duration"); } else { MP4_SKIP_U32(p_ctx, "creation_time"); MP4_SKIP_U32(p_ctx, "modification_time"); module->timescale = MP4_READ_U32(p_ctx, "timescale"); duration = MP4_READ_U32(p_ctx, "duration"); } if(module->timescale) p_ctx->duration = duration * 1000000 / module->timescale; MP4_SKIP_U32(p_ctx, "rate"); MP4_SKIP_U16(p_ctx, "volume"); MP4_SKIP_U16(p_ctx, "reserved"); for(i = 0; i < 2; i++) MP4_SKIP_U32(p_ctx, "reserved"); for(i = 0; i < 9; i++) MP4_SKIP_U32(p_ctx, "matrix"); for(i = 0; i < 6; i++) MP4_SKIP_U32(p_ctx, "pre_defined"); MP4_SKIP_U32(p_ctx, "next_track_ID"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_trak( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track; /* We have a new track. Allocate and initialise our track context */ if(p_ctx->tracks_num >= MP4_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; p_ctx->tracks[p_ctx->tracks_num] = track = vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; track->priv->module->sample_table[MP4_SAMPLE_TABLE_STTS].entry_size = 8; track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSZ].entry_size = 4; track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSC].entry_size = 12; track->priv->module->sample_table[MP4_SAMPLE_TABLE_STCO].entry_size = 4; track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSS].entry_size = 4; track->priv->module->sample_table[MP4_SAMPLE_TABLE_CO64].entry_size = 8; track->priv->module->sample_table[MP4_SAMPLE_TABLE_CTTS].entry_size = 8; status = mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_TRAK); /* TODO: Sanity check track */ track->is_enabled = true; track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; module->current_track++; p_ctx->tracks_num++; return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_tkhd( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; uint32_t i, version; int64_t duration; version = MP4_READ_U8(p_ctx, "version"); MP4_SKIP_U24(p_ctx, "flags"); if(version) { MP4_SKIP_U64(p_ctx, "creation_time"); MP4_SKIP_U64(p_ctx, "modification_time"); MP4_SKIP_U32(p_ctx, "track_ID"); MP4_SKIP_U32(p_ctx, "reserved"); duration = MP4_READ_U64(p_ctx, "duration"); } else { MP4_SKIP_U32(p_ctx, "creation_time"); MP4_SKIP_U32(p_ctx, "modification_time"); MP4_SKIP_U32(p_ctx, "track_ID"); MP4_SKIP_U32(p_ctx, "reserved"); duration = MP4_READ_U32(p_ctx, "duration"); } if(module->timescale) duration = duration * 1000000 / module->timescale; for(i = 0; i < 2; i++) MP4_SKIP_U32(p_ctx, "reserved"); MP4_SKIP_U16(p_ctx, "layer"); MP4_SKIP_U16(p_ctx, "alternate_group"); MP4_SKIP_U16(p_ctx, "volume"); MP4_SKIP_U16(p_ctx, "reserved"); for(i = 0; i < 9; i++) MP4_SKIP_U32(p_ctx, "matrix"); MP4_SKIP_U32(p_ctx, "width"); MP4_SKIP_U32(p_ctx, "height"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_mdia( VC_CONTAINER_T *p_ctx, int64_t size ) { return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_MDIA); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_mdhd( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; uint32_t version, timescale; int64_t duration; version = MP4_READ_U8(p_ctx, "version"); MP4_SKIP_U24(p_ctx, "flags"); if(version) { MP4_SKIP_U64(p_ctx, "creation_time"); MP4_SKIP_U64(p_ctx, "modification_time"); timescale = MP4_READ_U32(p_ctx, "timescale"); duration = MP4_READ_U64(p_ctx, "duration"); } else { MP4_SKIP_U32(p_ctx, "creation_time"); MP4_SKIP_U32(p_ctx, "modification_time"); timescale = MP4_READ_U32(p_ctx, "timescale"); duration = MP4_READ_U32(p_ctx, "duration"); } if(timescale) duration = duration * 1000000 / timescale; track_module->timescale = timescale; MP4_SKIP_U16(p_ctx, "language"); /* ISO-639-2/T language code */ MP4_SKIP_U16(p_ctx, "pre_defined"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_hdlr( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; uint32_t i, fourcc, string_size; VC_CONTAINER_ES_TYPE_T es_type = VC_CONTAINER_ES_TYPE_UNKNOWN; if(size <= 24) return VC_CONTAINER_ERROR_CORRUPTED; MP4_SKIP_U8(p_ctx, "version"); MP4_SKIP_U24(p_ctx, "flags"); MP4_SKIP_U32(p_ctx, "pre-defined"); fourcc = MP4_READ_FOURCC(p_ctx, "handler_type"); if(fourcc == MP4_BOX_TYPE_VIDE) es_type = VC_CONTAINER_ES_TYPE_VIDEO; if(fourcc == MP4_BOX_TYPE_SOUN) es_type = VC_CONTAINER_ES_TYPE_AUDIO; if(fourcc == MP4_BOX_TYPE_TEXT) es_type = VC_CONTAINER_ES_TYPE_SUBPICTURE; track->format->es_type = es_type; for(i = 0; i < 3; i++) MP4_SKIP_U32(p_ctx, "reserved"); string_size = size; if(module->brand == MP4_BRAND_QT) string_size = MP4_READ_U8(p_ctx, "string_size"); if(size < 0) return VC_CONTAINER_ERROR_CORRUPTED; if(string_size > size) string_size = size; MP4_SKIP_STRING(p_ctx, string_size, "name"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_minf( VC_CONTAINER_T *p_ctx, int64_t size ) { return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_MINF); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_vmhd( VC_CONTAINER_T *p_ctx, int64_t size ) { MP4_SKIP_U8(p_ctx, "version"); MP4_SKIP_U24(p_ctx, "flags"); MP4_SKIP_U16(p_ctx, "graphicsmode"); MP4_SKIP_U16(p_ctx, "opcolor"); MP4_SKIP_U16(p_ctx, "opcolor"); MP4_SKIP_U16(p_ctx, "opcolor"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_smhd( VC_CONTAINER_T *p_ctx, int64_t size ) { MP4_SKIP_U8(p_ctx, "version"); MP4_SKIP_U24(p_ctx, "flags"); MP4_SKIP_U16(p_ctx, "balance"); MP4_SKIP_U16(p_ctx, "reserved"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_dinf( VC_CONTAINER_T *p_ctx, int64_t size ) { return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_DINF); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_dref( VC_CONTAINER_T *p_ctx, int64_t size ) { MP4_SKIP_U8(p_ctx, "version"); MP4_SKIP_U24(p_ctx, "flags"); MP4_SKIP_U32(p_ctx, "entry_count"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_stbl( VC_CONTAINER_T *p_ctx, int64_t size ) { return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_STBL); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_stsd( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; VC_CONTAINER_STATUS_T status; MP4_BOX_TYPE_T box_type; int64_t box_size; uint32_t count; MP4_SKIP_U8(p_ctx, "version"); MP4_SKIP_U24(p_ctx, "flags"); count = MP4_READ_U32(p_ctx, "entry_count"); if(!count) return VC_CONTAINER_ERROR_CORRUPTED; status = mp4_read_box_header( p_ctx, size, &box_type, &box_size ); if(status != VC_CONTAINER_SUCCESS) return status; track->format->codec = mp4_box_type_to_codec(box_type); if(!track->format->codec) track->format->codec = box_type; if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) box_type = MP4_BOX_TYPE_VIDE; if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) box_type = MP4_BOX_TYPE_SOUN; if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) box_type = MP4_BOX_TYPE_TEXT; status = mp4_read_box_data( p_ctx, box_type, box_size, MP4_BOX_TYPE_STSD ); if(status != VC_CONTAINER_SUCCESS) return status; /* Special treatment for MPEG4 */ if(track->format->codec == VC_CONTAINER_CODEC_MP4A) { switch (track->priv->module->object_type_indication) { case MP4_MPEG4_AAC_LC_OBJECT_TYPE: case MP4_MPEG2_AAC_LC_OBJECT_TYPE: track->format->codec = VC_CONTAINER_CODEC_MP4A; break; case MP4_MP3_OBJECT_TYPE: case MP4_MPEG1_AUDIO_OBJECT_TYPE: case MP4_KTF_MP3_OBJECT_TYPE: track->format->codec = VC_CONTAINER_CODEC_MPGA; break; case MP4_SKT_EVRC_2V1_OBJECT_TYPE: case MP4_SKT_EVRC_OBJECT_TYPE: track->format->codec = VC_CONTAINER_CODEC_EVRC; break; case MP4_3GPP2_QCELP_OBJECT_TYPE: track->format->codec = VC_CONTAINER_CODEC_QCELP; break; default: track->format->codec = VC_CONTAINER_CODEC_UNKNOWN; break; } } else if(track->format->codec == VC_CONTAINER_CODEC_MP4V) { switch (track->priv->module->object_type_indication) { case MP4_MPEG4_VISUAL_OBJECT_TYPE: track->format->codec = VC_CONTAINER_CODEC_MP4V; break; case MP4_JPEG_OBJECT_TYPE: track->format->codec = VC_CONTAINER_CODEC_JPEG; break; case MP4_MPEG2_SP_OBJECT_TYPE: case MP4_MPEG2_SNR_OBJECT_TYPE: case MP4_MPEG2_AAC_LC_OBJECT_TYPE: case MP4_MPEG2_MP_OBJECT_TYPE: track->format->codec = VC_CONTAINER_CODEC_MP2V; break; case MP4_MPEG1_VISUAL_OBJECT_TYPE: track->format->codec = VC_CONTAINER_CODEC_MP1V; break; default: track->format->codec = VC_CONTAINER_CODEC_UNKNOWN; break; } } /* For some codecs we process the samples in batches to be more efficient */ if(codec_needs_batch_mode(track->format->codec)) track->priv->module->samples_batch_size = MP4_MAX_SAMPLES_BATCH_SIZE; /* Fix-up some of the data */ switch(track->format->codec) { case VC_CONTAINER_CODEC_ALAW: case VC_CONTAINER_CODEC_MULAW: track->format->type->audio.bits_per_sample = 8; track->priv->module->sample_size = track->format->type->audio.channels; break; case VC_CONTAINER_CODEC_PCM_SIGNED_LE: case VC_CONTAINER_CODEC_PCM_SIGNED_BE: track->priv->module->sample_size = (track->format->type->audio.bits_per_sample + 7) / 8 * track->format->type->audio.channels; break; case VC_CONTAINER_CODEC_MP4A: /* samplerate / channels is sometimes invalid so sanity check it using the codec config data */ if(track->format->extradata_size >= 2) { static unsigned int rate[] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 }; unsigned int samplerate = 0, channels = 0; uint8_t *p = track->format->extradata; uint32_t index = (p[0] & 7) << 1 | (p[1] >> 7); if(index == 15 && track->format->extradata_size >= 5) { samplerate = (p[1] & 0x7f) << 17 | (p[2] << 9) | (p[3] << 1) | (p[4] >> 7); channels = (p[4] >> 3) & 15; } else if(index < 13) { samplerate = rate[index]; channels = (p[1] >> 3) & 15;; } if(samplerate && samplerate != track->format->type->audio.sample_rate && 2 * samplerate != track->format->type->audio.sample_rate) track->format->type->audio.sample_rate = samplerate; if(channels && channels != track->format->type->audio.channels && 2 * channels != track->format->type->audio.channels) track->format->type->audio.channels = channels; } break; default: break; } return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_cache_table( VC_CONTAINER_T *p_ctx, MP4_SAMPLE_TABLE_T table, uint32_t entries, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; uint32_t available_entries, entries_size; if(size < 0) return VC_CONTAINER_ERROR_CORRUPTED; track_module->sample_table[table].offset = STREAM_POSITION(p_ctx); track_module->sample_table[table].entries = entries; available_entries = size / track_module->sample_table[table].entry_size; if(available_entries < entries) { LOG_DEBUG(p_ctx, "table has less entries than advertised (%i/%i)", available_entries, entries); entries = available_entries; } entries_size = entries * track_module->sample_table[table].entry_size; size = vc_container_io_cache(p_ctx->priv->io, entries_size ); if(size != entries_size) { available_entries = size / track_module->sample_table[table].entry_size; LOG_DEBUG(p_ctx, "cached less table entries than advertised (%i/%i)", available_entries, entries); track_module->sample_table[table].entries = available_entries; } return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_stts( VC_CONTAINER_T *p_ctx, int64_t size ) { uint32_t entries; MP4_SKIP_U8(p_ctx, "version"); MP4_SKIP_U24(p_ctx, "flags"); entries = MP4_READ_U32(p_ctx, "entry_count"); return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STTS, entries, size ); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_ctts( VC_CONTAINER_T *p_ctx, int64_t size ) { uint32_t entries; MP4_SKIP_U8(p_ctx, "version"); MP4_SKIP_U24(p_ctx, "flags"); entries = MP4_READ_U32(p_ctx, "entry_count"); return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_CTTS, entries, size ); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_stsc( VC_CONTAINER_T *p_ctx, int64_t size ) { uint32_t entries; MP4_SKIP_U8(p_ctx, "version"); MP4_SKIP_U24(p_ctx, "flags"); entries = MP4_READ_U32(p_ctx, "entry_count"); return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STSC, entries, size ); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_stsz( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; uint32_t entries; MP4_SKIP_U8(p_ctx, "version"); MP4_SKIP_U24(p_ctx, "flags"); track_module->sample_size = READ_U32(p_ctx, "sample_size"); if(track_module->sample_size) return STREAM_STATUS(p_ctx); entries = MP4_READ_U32(p_ctx, "sample_count"); return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STSZ, entries, size ); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_stco( VC_CONTAINER_T *p_ctx, int64_t size ) { uint32_t entries; MP4_SKIP_U8(p_ctx, "version"); MP4_SKIP_U24(p_ctx, "flags"); entries = MP4_READ_U32(p_ctx, "entry_count"); return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STCO, entries, size ); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_co64( VC_CONTAINER_T *p_ctx, int64_t size ) { uint32_t entries; MP4_SKIP_U8(p_ctx, "version"); MP4_SKIP_U24(p_ctx, "flags"); entries = MP4_READ_U32(p_ctx, "entry_count"); return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_CO64, entries, size ); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_stss( VC_CONTAINER_T *p_ctx, int64_t size ) { uint32_t entries; MP4_SKIP_U8(p_ctx, "version"); MP4_SKIP_U24(p_ctx, "flags"); entries = MP4_READ_U32(p_ctx, "entry_count"); return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STSS, entries, size ); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_esds_descriptor_header(VC_CONTAINER_T *p_ctx, int64_t *size, uint32_t *descriptor_length, uint8_t *descriptor_type) { uint32_t byte, length = 0; if(*size <= 0) return VC_CONTAINER_ERROR_CORRUPTED; *descriptor_type = _READ_U8(p_ctx); (*size)--; /* Read descriptor size */ while(*size) { byte = _READ_U8(p_ctx); (*size)--; length = (length << 7) | (byte&0x7F); if(!(byte & 0x80)) break; } if(*size <= 0 || length > *size) { LOG_FORMAT(p_ctx, "esds descriptor is corrupted"); return VC_CONTAINER_ERROR_CORRUPTED; } *descriptor_length = length; LOG_FORMAT(p_ctx, "esds descriptor %x, size %i", *descriptor_type, *descriptor_length); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_esds( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; VC_CONTAINER_STATUS_T status; uint32_t descriptor_length; uint8_t descriptor_type; MP4_SKIP_U8(p_ctx, "version"); MP4_SKIP_U24(p_ctx, "flags"); status = mp4_read_esds_descriptor_header(p_ctx, &size, &descriptor_length, &descriptor_type); if(status != VC_CONTAINER_SUCCESS) return status; if(descriptor_type == 0x3) /* ES descriptor */ { uint8_t flags; MP4_SKIP_U16(p_ctx, "es_id"); flags = MP4_READ_U8(p_ctx, "flags"); if(flags & 0x80) /* Stream dependence */ MP4_SKIP_U16(p_ctx, "depend_on_es_id"); if(flags & 0x40) /* URL */ { uint32_t url_size = MP4_READ_U8(p_ctx, "url_size"); MP4_SKIP_STRING(p_ctx, url_size, "url"); } if(flags & 0x20) /* OCR_stream*/ MP4_SKIP_U16(p_ctx, "OCR_es_id"); status = mp4_read_esds_descriptor_header(p_ctx, &size, &descriptor_length, &descriptor_type); if(status != VC_CONTAINER_SUCCESS) return status; } if(descriptor_type == 0x4) /* Decoder Config descriptor */ { track->priv->module->object_type_indication = MP4_READ_U8(p_ctx, "object_type_indication"); MP4_SKIP_U8(p_ctx, "stream_type"); MP4_SKIP_U24(p_ctx, "buffer_size_db"); MP4_SKIP_U32(p_ctx, "max_bitrate"); track->format->bitrate = MP4_READ_U32(p_ctx, "avg_bitrate"); if(size <= 0 || descriptor_length <= 13) return STREAM_STATUS(p_ctx); status = mp4_read_esds_descriptor_header(p_ctx, &size, &descriptor_length, &descriptor_type); if(status != VC_CONTAINER_SUCCESS) return status; if(descriptor_type == 0x05 && descriptor_length) { status = vc_container_track_allocate_extradata(p_ctx, track, descriptor_length); if(status != VC_CONTAINER_SUCCESS) return status; track->format->extradata_size = MP4_READ_BYTES(p_ctx, track->format->extradata, descriptor_length); } } return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_vide( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; unsigned int i; for(i = 0; i < 6; i++) MP4_SKIP_U8(p_ctx, "reserved"); MP4_SKIP_U16(p_ctx, "data_reference_index"); MP4_SKIP_U16(p_ctx, "pre_defined"); MP4_SKIP_U16(p_ctx, "reserved"); for(i = 0; i < 3; i++) MP4_SKIP_U32(p_ctx, "pre_defined"); track->format->type->video.width = MP4_READ_U16(p_ctx, "width"); track->format->type->video.height = MP4_READ_U16(p_ctx, "height"); MP4_SKIP_U32(p_ctx, "horizresolution"); /* dpi */ MP4_SKIP_U32(p_ctx, "vertresolution"); /* dpi */ MP4_SKIP_U32(p_ctx, "reserved"); MP4_SKIP_U16(p_ctx, "frame_count"); MP4_SKIP_BYTES(p_ctx, 32); MP4_SKIP_U16(p_ctx, "depth"); MP4_SKIP_U16(p_ctx, "pre_defined"); if(size > 0) return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_VIDE ); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_vide_avcC( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; VC_CONTAINER_STATUS_T status; if(track->format->codec != VC_CONTAINER_CODEC_H264 || size <= 0) return VC_CONTAINER_ERROR_CORRUPTED; track->format->codec_variant = VC_FOURCC('a','v','c','C'); status = vc_container_track_allocate_extradata(p_ctx, track, (unsigned int)size); if(status != VC_CONTAINER_SUCCESS) return status; track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_vide_d263( VC_CONTAINER_T *p_ctx, int64_t size ) { MP4_SKIP_FOURCC(p_ctx, "vendor"); MP4_SKIP_U8(p_ctx, "version"); MP4_SKIP_U8(p_ctx, "level"); MP4_SKIP_U8(p_ctx, "profile"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_soun( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; unsigned int i, version = 0; for(i = 0; i < 6; i++) MP4_SKIP_U8(p_ctx, "reserved"); MP4_SKIP_U16(p_ctx, "data_reference_index"); version = MP4_READ_U16(p_ctx, "version"); MP4_SKIP_U16(p_ctx, "revision_level"); MP4_SKIP_U32(p_ctx, "vendor"); track->format->type->audio.channels = MP4_READ_U16(p_ctx, "channelcount"); track->format->type->audio.bits_per_sample = MP4_READ_U16(p_ctx, "samplesize"); MP4_SKIP_U16(p_ctx, "pre_defined"); MP4_SKIP_U16(p_ctx, "reserved"); track->format->type->audio.sample_rate = MP4_READ_U16(p_ctx, "samplerate"); MP4_SKIP_U16(p_ctx, "samplerate_fp_low"); if(version == 1) { MP4_SKIP_U32(p_ctx, "samples_per_packet"); MP4_SKIP_U32(p_ctx, "bytes_per_packet"); MP4_SKIP_U32(p_ctx, "bytes_per_frame"); MP4_SKIP_U32(p_ctx, "bytes_per_sample"); } if(size > 0) return mp4_read_box( p_ctx, size, MP4_BOX_TYPE_SOUN ); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_soun_damr( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; MP4_SKIP_FOURCC(p_ctx, "vendor"); MP4_SKIP_U8(p_ctx, "version"); MP4_SKIP_U8(p_ctx, "mode_set"); MP4_SKIP_U8(p_ctx, "mode_change_period"); MP4_SKIP_U8(p_ctx, "frame_per_second"); track->format->type->audio.channels = 1; if(track->format->codec == VC_CONTAINER_CODEC_AMRNB) track->format->type->audio.sample_rate = 8000; else if(track->format->codec == VC_CONTAINER_CODEC_AMRWB) track->format->type->audio.sample_rate = 16000; return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_soun_dawp( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; MP4_SKIP_FOURCC(p_ctx, "vendor"); MP4_SKIP_U8(p_ctx, "version"); track->format->type->audio.channels = 2; track->format->type->audio.sample_rate = 16000; return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_soun_devc( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; MP4_SKIP_FOURCC(p_ctx, "vendor"); MP4_SKIP_U8(p_ctx, "version"); MP4_SKIP_U8(p_ctx, "samples_per_frame"); track->format->type->audio.channels = 1; track->format->type->audio.sample_rate = 8000; return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_soun_wave( VC_CONTAINER_T *p_ctx, int64_t size ) { return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_SOUN); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_box_text( VC_CONTAINER_T *p_ctx, int64_t size ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_PARAM_UNUSED(module); /* TODO */if(1) return VC_CONTAINER_ERROR_FAILED; if(size > 0) return mp4_read_box( p_ctx, size, MP4_BOX_TYPE_TEXT ); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_reader_close( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; unsigned int i; for(i = 0; i < p_ctx->tracks_num; i++) vc_container_free_track(p_ctx, p_ctx->tracks[i]); free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ #ifdef ENABLE_MP4_READER_LOG_STATE static void mp4_log_state( VC_CONTAINER_T *p_ctx, MP4_READER_STATE_T *state ) { VC_CONTAINER_PARAM_UNUSED(p_ctx); LOG_DEBUG(p_ctx, "state:"); LOG_DEBUG(p_ctx, "duration: %i, pts %i, dts %i", (int)state->duration, (int)state->pts, (int)state->dts); LOG_DEBUG(p_ctx, "sample: %i, offset %i, sample_offset %i, sample_size %i", state->sample, (int)state->offset, state->sample_offset, state->sample_size); LOG_DEBUG(p_ctx, "sample_duration: %i, count %i", state->sample_duration, state->sample_duration_count); LOG_DEBUG(p_ctx, "sample_composition_offset: %i, count %i", state->sample_composition_offset, state->sample_composition_count); LOG_DEBUG(p_ctx, "next_sync_sample: %i, keyframe %i", state->next_sync_sample, state->keyframe); LOG_DEBUG(p_ctx, "samples_per_chunk: %i, chunks %i, samples_in_chunk %i", state->samples_per_chunk, state->chunks, state->samples_in_chunk); LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_STTS %i", state->sample_table[MP4_SAMPLE_TABLE_STTS].entry); LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_STSZ %i", state->sample_table[MP4_SAMPLE_TABLE_STSZ].entry); LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_STSC %i", state->sample_table[MP4_SAMPLE_TABLE_STSC].entry); LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_STCO %i", state->sample_table[MP4_SAMPLE_TABLE_STCO].entry); LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_CO64 %i", state->sample_table[MP4_SAMPLE_TABLE_CO64].entry); LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_CTTS %i", state->sample_table[MP4_SAMPLE_TABLE_CTTS].entry); } #endif /* ENABLE_MP4_READER_LOG_STATE */ /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_seek_sample_table( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_MODULE_T *track_module, MP4_READER_STATE_T *state, MP4_SAMPLE_TABLE_T table) { int64_t seek_offset; /* Seek to the next entry in the table */ if(state->sample_table[table].entry >= track_module->sample_table[table].entries) return VC_CONTAINER_ERROR_EOS; seek_offset = track_module->sample_table[table].offset + track_module->sample_table[table].entry_size * state->sample_table[table].entry; return SEEK(p_ctx, seek_offset); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_sample_table( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_MODULE_T *track_module, MP4_READER_STATE_T *state, MP4_SAMPLE_TABLE_T table, unsigned int seek) { uint32_t value; if(table == MP4_SAMPLE_TABLE_STSZ && track_module->sample_size) { state->sample_size = track_module->sample_size; return state->status; } /* CO64 support */ if(table == MP4_SAMPLE_TABLE_STCO && track_module->sample_table[MP4_SAMPLE_TABLE_CO64].entries) table = MP4_SAMPLE_TABLE_CO64; /* Seek to the next entry in the table */ if(seek) { state->status = mp4_seek_sample_table( p_ctx, track_module, state, table ); if(state->status != VC_CONTAINER_SUCCESS) return state->status; } switch(table) { case MP4_SAMPLE_TABLE_STSZ: state->sample_size = _READ_U32(p_ctx); state->status = STREAM_STATUS(p_ctx); break; case MP4_SAMPLE_TABLE_STTS: state->sample_duration_count = _READ_U32(p_ctx); state->sample_duration = _READ_U32(p_ctx); state->status = STREAM_STATUS(p_ctx); if(state->status != VC_CONTAINER_SUCCESS) break; if(!state->sample_duration_count) state->status = VC_CONTAINER_ERROR_CORRUPTED; break; case MP4_SAMPLE_TABLE_CTTS: state->sample_composition_count = _READ_U32(p_ctx); state->sample_composition_offset = _READ_U32(p_ctx); /* Converted to signed */ state->status = STREAM_STATUS(p_ctx); if(state->status != VC_CONTAINER_SUCCESS) break; if(!state->sample_composition_count) state->status = VC_CONTAINER_ERROR_CORRUPTED; break; case MP4_SAMPLE_TABLE_STSC: state->chunks = _READ_U32(p_ctx); state->samples_per_chunk = _READ_U32(p_ctx); _SKIP_U32(p_ctx); state->status = STREAM_STATUS(p_ctx); if(state->status != VC_CONTAINER_SUCCESS) break; if(state->sample_table[table].entry + 1 < track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries) value = _READ_U32(p_ctx); else value = -1; if(!state->chunks || !state->samples_per_chunk || state->chunks >= value ) {state->status = VC_CONTAINER_ERROR_CORRUPTED; break;} state->chunks = value - state->chunks; state->samples_in_chunk = state->samples_per_chunk; break; case MP4_SAMPLE_TABLE_STCO: case MP4_SAMPLE_TABLE_CO64: state->offset = table == MP4_SAMPLE_TABLE_STCO ? _READ_U32(p_ctx) : _READ_U64(p_ctx); state->status = STREAM_STATUS(p_ctx); if(state->status != VC_CONTAINER_SUCCESS) break; if(!state->offset) state->status = VC_CONTAINER_ERROR_CORRUPTED; state->samples_in_chunk = state->samples_per_chunk; break; case MP4_SAMPLE_TABLE_STSS: state->next_sync_sample = _READ_U32(p_ctx); state->status = STREAM_STATUS(p_ctx); break; default: break; } state->sample_table[table].entry++; return state->status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_sample_header( VC_CONTAINER_T *p_ctx, uint32_t track, MP4_READER_STATE_T *state ) { VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[track]->priv->module; if(state->status != VC_CONTAINER_SUCCESS) return state->status; if(state->sample_offset < state->sample_size) return state->status; /* We still have data left from the current sample */ /* Switch to the next sample */ state->offset += state->sample_size; state->sample_offset = 0; state->sample_size = 0; state->sample++; if(!state->samples_in_chunk) { /* We're switching to the next chunk */ if(!state->chunks) { /* Seek to the next entry in the STSC */ state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSC, 1 ); if(state->status != VC_CONTAINER_SUCCESS) goto error; } /* Get the offset of the new chunk */ state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STCO, 1 ); if(state->status != VC_CONTAINER_SUCCESS) goto error; state->chunks--; } state->samples_in_chunk--; /* Get the new sample size */ state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSZ, 1 ); if(state->status != VC_CONTAINER_SUCCESS) goto error; /* Get the timestamp */ if(track_module->timescale) state->pts = state->dts = state->duration * 1000000 / track_module->timescale; if(!state->sample_duration_count) { state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STTS, 1 ); if(state->status != VC_CONTAINER_SUCCESS) goto error; } state->sample_duration_count--; /* Get the composition time */ if(track_module->sample_table[MP4_SAMPLE_TABLE_CTTS].entries) { if(!state->sample_composition_count) { state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_CTTS, 1 ); if(state->status != VC_CONTAINER_SUCCESS) goto error; } if(track_module->timescale) state->pts = (state->duration + state->sample_composition_offset) * 1000000 / track_module->timescale; state->sample_composition_count--; } state->duration += state->sample_duration; /* Get the keyframe flag */ if(state->sample_table[MP4_SAMPLE_TABLE_STSS].entry < track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries && !state->next_sync_sample) { mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSS, 1 ); state->status = VC_CONTAINER_SUCCESS; /* This isn't a critical error */ } state->keyframe = track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries && state->sample == state->next_sync_sample; if(state->keyframe) state->next_sync_sample = 0; /* Try to batch several samples together if requested. We'll always stop at the chunk boundary */ if(track_module->samples_batch_size) { uint32_t size = state->sample_size; while(state->samples_in_chunk && size < track_module->samples_batch_size) { if(mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSZ, 1 )) break; if(!state->sample_duration_count) if(mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STTS, 1 )) break; state->sample_duration_count--; state->duration += state->sample_duration; size += state->sample_size; state->samples_in_chunk--; state->sample++; } state->sample_size = size; } #ifdef ENABLE_MP4_READER_LOG_STATE mp4_log_state(p_ctx, state); #endif error: return state->status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_read_sample_data( VC_CONTAINER_T *p_ctx, uint32_t track, MP4_READER_STATE_T *state, uint8_t *data, unsigned int *data_size ) { VC_CONTAINER_STATUS_T status; unsigned int size = state->sample_size - state->sample_offset; if(state->status != VC_CONTAINER_SUCCESS) return state->status; if(data_size && *data_size < size) size = *data_size; if(data) { state->status = SEEK(p_ctx, state->offset + state->sample_offset); if(state->status != VC_CONTAINER_SUCCESS) return state->status; size = READ_BYTES(p_ctx, data, size); } state->sample_offset += size; if(data_size) *data_size = size; state->status = STREAM_STATUS(p_ctx); if(state->status != VC_CONTAINER_SUCCESS) return state->status; status = state->status; /* Switch to the start of the next sample */ if(state->sample_offset >= state->sample_size) mp4_read_sample_header(p_ctx, track, state); return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_reader_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *packet, uint32_t flags ) { VC_CONTAINER_TRACK_MODULE_T *track_module; VC_CONTAINER_STATUS_T status; MP4_READER_STATE_T *state; uint32_t i, track; unsigned int data_size; uint8_t *data = 0; int64_t offset; /* Select the track to read from. If no specific track is requested by the caller, this * will be the track to which the next bit of data in the mdat belongs to */ if(!(flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK)) { for(i = 0, track = 0, offset = -1; i < p_ctx->tracks_num; i++) { track_module = p_ctx->tracks[i]->priv->module; /* Ignore tracks which have no more readable data */ if(track_module->state.status != VC_CONTAINER_SUCCESS) continue; if(offset >= 0 && track_module->state.offset >= offset) continue; offset = track_module->state.offset; track = i; } } else track = packet->track; if(track >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; track_module = p_ctx->tracks[track]->priv->module; state = &track_module->state; status = mp4_read_sample_header(p_ctx, track, state); if(status != VC_CONTAINER_SUCCESS) return status; if(!packet) /* Skip packet */ return mp4_read_sample_data(p_ctx, track, state, 0, 0); packet->dts = state->dts; packet->pts = state->pts; packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END; if(state->keyframe) packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; if(!state->sample_offset) packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; packet->track = track; packet->frame_size = state->sample_size; packet->size = state->sample_size - state->sample_offset; if(flags & VC_CONTAINER_READ_FLAG_SKIP) return mp4_read_sample_data(p_ctx, track, state, 0, 0); else if((flags & VC_CONTAINER_READ_FLAG_INFO) || !packet->data) return VC_CONTAINER_SUCCESS; data = packet->data; data_size = packet->buffer_size; status = mp4_read_sample_data(p_ctx, track, state, data, &data_size); if(status != VC_CONTAINER_SUCCESS) { /* FIXME */ return status; } packet->size = data_size; if(state->sample_offset) //? packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; return status; } /*****************************************************************************/ static uint32_t mp4_find_sample( VC_CONTAINER_T *p_ctx, uint32_t track, MP4_READER_STATE_T *state, int64_t seek_time, VC_CONTAINER_STATUS_T *p_status ) { VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[track]->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; uint32_t sample = 0, sample_duration_count; int64_t sample_duration, seek_time_up = seek_time + 1; unsigned int i; VC_CONTAINER_PARAM_UNUSED(state); seek_time = seek_time * track_module->timescale / 1000000; /* We also need to check against the time rounded up to account for * rounding errors in the timestamp (because of the timescale conversion) */ seek_time_up = seek_time_up * track_module->timescale / 1000000; status = SEEK(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STTS].offset); if(status != VC_CONTAINER_SUCCESS) goto end; /* Find the sample which corresponds to the requested time */ for(i = 0; i < track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries; i++) { sample_duration_count = _READ_U32(p_ctx); sample_duration = _READ_U32(p_ctx); status = STREAM_STATUS(p_ctx); if(status != VC_CONTAINER_SUCCESS) break; if(sample_duration_count * sample_duration <= seek_time) { seek_time -= sample_duration_count * sample_duration; seek_time_up -= sample_duration_count * sample_duration; sample += sample_duration_count; continue; } if(!sample_duration) break; seek_time /= sample_duration; seek_time_up /= sample_duration; sample += MAX(seek_time, seek_time_up); break; } end: if(p_status) *p_status = status; return sample; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_seek_track( VC_CONTAINER_T *p_ctx, uint32_t track, MP4_READER_STATE_T *state, uint32_t sample ) { VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[track]->priv->module; uint32_t chunk = 0, samples; unsigned int i; memset(state, 0, sizeof(*state)); /* Find the right chunk */ for(i = 0, samples = sample; i < track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries; i++) { state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSC, 1 ); if(state->status != VC_CONTAINER_SUCCESS) goto error; if(state->chunks * state->samples_per_chunk <= samples) { samples -= state->chunks * state->samples_per_chunk; chunk += state->chunks; continue; } while(samples >= state->samples_per_chunk) { samples -= state->samples_per_chunk; state->chunks--; chunk++; } state->chunks--; break; } /* Get the offset of the selected chunk */ state->sample_table[MP4_SAMPLE_TABLE_STCO].entry = chunk; state->sample_table[MP4_SAMPLE_TABLE_CO64].entry = chunk; state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STCO, 1 ); if(state->status != VC_CONTAINER_SUCCESS) goto error; /* Find the sample offset within the chunk */ state->sample_table[MP4_SAMPLE_TABLE_STSZ].entry = sample - samples; for(i = 0; i < samples; i++) { state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSZ, !i ); if(state->status != VC_CONTAINER_SUCCESS) goto error; state->offset += state->sample_size; state->samples_in_chunk--; } /* Get the timestamp */ for(i = 0, samples = sample; i < track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries; i++) { state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STTS, !i ); if(state->status != VC_CONTAINER_SUCCESS) goto error; if(state->sample_duration_count <= samples) { samples -= state->sample_duration_count; state->duration += state->sample_duration * state->sample_duration_count; continue; } state->sample_duration_count -= samples; state->duration += samples * state->sample_duration; break; } /* Find the right place in the sample composition table */ for(i = 0, samples = sample; i < track_module->sample_table[MP4_SAMPLE_TABLE_CTTS].entries; i++) { state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_CTTS, !i ); if(state->status != VC_CONTAINER_SUCCESS) goto error; if(state->sample_composition_count <= samples) { samples -= state->sample_composition_count; continue; } state->sample_composition_count -= samples; break; } /* Find the right place in the synchronisation table */ for(i = 0; i < track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries; i++) { state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSS, !i ); if(state->status != VC_CONTAINER_SUCCESS) goto error; if(state->next_sync_sample >= sample + 1) break; } state->sample = sample; state->sample_size = 0; mp4_read_sample_header(p_ctx, track, state); error: return state->status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_reader_seek(VC_CONTAINER_T *p_ctx, int64_t *offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module; VC_CONTAINER_STATUS_T status; uint32_t i, track, sample, prev_sample, next_sample; int64_t seek_time = *offset; VC_CONTAINER_PARAM_UNUSED(module); VC_CONTAINER_PARAM_UNUSED(mode); /* Reset the states */ for(i = 0; i < p_ctx->tracks_num; i++) memset(&p_ctx->tracks[i]->priv->module->state, 0, sizeof(p_ctx->tracks[i]->priv->module->state)); /* Deal with the easy case first */ if(!*offset) { /* Initialise tracks */ for(i = 0; i < p_ctx->tracks_num; i++) { /* FIXME: we should check we've got at least one success */ mp4_read_sample_header(p_ctx, i, &p_ctx->tracks[i]->priv->module->state); } return VC_CONTAINER_SUCCESS; } /* Find the first enabled video track */ for(track = 0; track < p_ctx->tracks_num; track++) if(p_ctx->tracks[track]->is_enabled && p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) break; if(track == p_ctx->tracks_num) goto seek_time_found; /* No video track found */ track_module = p_ctx->tracks[track]->priv->module; /* Find the sample number for the requested time */ sample = mp4_find_sample( p_ctx, track, &track_module->state, seek_time, &status ); if(status != VC_CONTAINER_SUCCESS) goto seek_time_found; /* Find the closest sync sample */ status = mp4_seek_sample_table( p_ctx, track_module, &track_module->state, MP4_SAMPLE_TABLE_STSS ); if(status != VC_CONTAINER_SUCCESS) goto seek_time_found; for(i = 0, prev_sample = 0, next_sample = 0; i < track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries; i++) { next_sample = _READ_U32(p_ctx) - 1; if(next_sample > sample) { sample = (flags & VC_CONTAINER_SEEK_FLAG_FORWARD) ? next_sample : prev_sample; break; } prev_sample = next_sample; } /* Do the seek on this track and use its timestamp as the new seek point */ status = mp4_seek_track(p_ctx, track, &track_module->state, sample); if(status != VC_CONTAINER_SUCCESS) goto seek_time_found; seek_time = track_module->state.pts; seek_time_found: for(i = 0; i < p_ctx->tracks_num; i++) { uint32_t sample; track_module = p_ctx->tracks[i]->priv->module; if(track_module->state.offset) continue; sample = mp4_find_sample( p_ctx, i, &track_module->state, seek_time, &status ); if(status != VC_CONTAINER_SUCCESS) return status; //FIXME status = mp4_seek_track(p_ctx, i, &track_module->state, sample); } *offset = seek_time; return VC_CONTAINER_SUCCESS; } /****************************************************************************** Global function definitions. ******************************************************************************/ VC_CONTAINER_STATUS_T mp4_reader_open( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; VC_CONTAINER_MODULE_T *module = 0; unsigned int i; uint8_t h[8]; /* Check for a known box type to see if we're dealing with mp4 */ if( PEEK_BYTES(p_ctx, h, 8) != 8 ) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; switch(VC_FOURCC(h[4],h[5],h[6],h[7])) { case MP4_BOX_TYPE_FTYP: case MP4_BOX_TYPE_MDAT: case MP4_BOX_TYPE_MOOV: case MP4_BOX_TYPE_FREE: case MP4_BOX_TYPE_SKIP: case MP4_BOX_TYPE_WIDE: case MP4_BOX_TYPE_PNOT: case MP4_BOX_TYPE_PICT: case MP4_BOX_TYPE_UDTA: case MP4_BOX_TYPE_UUID: break; default: /* Couldn't recognize the box type. This doesn't look like an mp4. */ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; } /* * We are dealing with an MP4 file */ LOG_DEBUG(p_ctx, "using mp4 reader"); /* Allocate our context */ module = malloc(sizeof(*module)); if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); p_ctx->priv->module = module; p_ctx->tracks = module->tracks; while(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS) { MP4_BOX_TYPE_T box_type; int64_t box_size; status = mp4_read_box_header( p_ctx, INT64_C(-1), &box_type, &box_size ); if(status != VC_CONTAINER_SUCCESS) goto error; if(box_type == MP4_BOX_TYPE_MDAT) { module->data_offset = STREAM_POSITION(p_ctx); module->data_size = box_size; if(module->found_moov) break; /* We've got everything we want */ } else if(box_type == MP4_BOX_TYPE_MOOV) module->found_moov = true; status = mp4_read_box_data( p_ctx, box_type, box_size, MP4_BOX_TYPE_ROOT ); if(status != VC_CONTAINER_SUCCESS) goto error; if(module->found_moov && module->data_offset) break; /* We've got everything we want */ } /* Initialise tracks */ for(i = 0; i < p_ctx->tracks_num; i++) { /* FIXME: we should check we've got at least one success */ status = mp4_read_sample_header(p_ctx, i, &p_ctx->tracks[i]->priv->module->state); } status = SEEK(p_ctx, module->data_offset); if(status != VC_CONTAINER_SUCCESS) goto error; p_ctx->priv->pf_close = mp4_reader_close; p_ctx->priv->pf_read = mp4_reader_read; p_ctx->priv->pf_seek = mp4_reader_seek; if(STREAM_SEEKABLE(p_ctx)) p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; return VC_CONTAINER_SUCCESS; error: LOG_DEBUG(p_ctx, "mp4: error opening stream"); if(module) mp4_reader_close(p_ctx); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak reader_open mp4_reader_open #endif userland/containers/mp4/mp4_writer.c000066400000000000000000001504761421703157200200170ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #define CONTAINER_IS_BIG_ENDIAN #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_writer_utils.h" #include "containers/core/containers_logging.h" #include "containers/mp4/mp4_common.h" #undef CONTAINER_HELPER_LOG_INDENT #define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->box_level VC_CONTAINER_STATUS_T mp4_writer_open( VC_CONTAINER_T *p_ctx ); /****************************************************************************** Defines. ******************************************************************************/ #define MP4_TRACKS_MAX 16 #define MP4_TIMESCALE 1000 #define MP4_64BITS_TIME 0 /* 0 to disable / 1 to enable */ /****************************************************************************** Type definitions. ******************************************************************************/ typedef struct VC_CONTAINER_TRACK_MODULE_T { uint32_t fourcc; uint32_t samples; uint32_t chunks; int64_t offset; int64_t timestamp; int64_t delta_timestamp; int64_t samples_in_chunk; int64_t samples_in_prev_chunk; struct { uint32_t entries; uint32_t entry_size; } sample_table[MP4_SAMPLE_TABLE_NUM]; int64_t first_pts; int64_t last_pts; } VC_CONTAINER_TRACK_MODULE_T; typedef struct VC_CONTAINER_MODULE_T { int box_level; MP4_BRAND_T brand; VC_CONTAINER_TRACK_T *tracks[MP4_TRACKS_MAX]; bool tracks_add_done; VC_CONTAINER_WRITER_EXTRAIO_T null; unsigned int current_track; unsigned moov_size; int64_t mdat_offset; int64_t data_offset; uint32_t samples; VC_CONTAINER_WRITER_EXTRAIO_T temp; VC_CONTAINER_PACKET_T sample; int64_t sample_offset; int64_t prev_sample_dts; int64_t duration; /**/ } VC_CONTAINER_MODULE_T; /****************************************************************************** Static functions within this file. ******************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_extended( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T box_type, uint32_t fourcc ); static VC_CONTAINER_STATUS_T mp4_write_box( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T box_type ); static VC_CONTAINER_STATUS_T mp4_write_box_ftyp( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_moov( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_mvhd( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_trak( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_tkhd( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_mdia( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_mdhd( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_hdlr( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_minf( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_vmhd( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_smhd( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_dinf( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_dref( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_stbl( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_stsd( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_stts( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_ctts( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_stsc( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_stsz( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_stco( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_co64( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_stss( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_vide( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_soun( VC_CONTAINER_T *p_ctx ); static VC_CONTAINER_STATUS_T mp4_write_box_esds( VC_CONTAINER_T *p_ctx ); static struct { const MP4_BOX_TYPE_T type; VC_CONTAINER_STATUS_T (*pf_func)( VC_CONTAINER_T * ); } mp4_box_list[] = { {MP4_BOX_TYPE_FTYP, mp4_write_box_ftyp}, {MP4_BOX_TYPE_MOOV, mp4_write_box_moov}, {MP4_BOX_TYPE_MVHD, mp4_write_box_mvhd}, {MP4_BOX_TYPE_TRAK, mp4_write_box_trak}, {MP4_BOX_TYPE_TKHD, mp4_write_box_tkhd}, {MP4_BOX_TYPE_MDIA, mp4_write_box_mdia}, {MP4_BOX_TYPE_MDHD, mp4_write_box_mdhd}, {MP4_BOX_TYPE_HDLR, mp4_write_box_hdlr}, {MP4_BOX_TYPE_MINF, mp4_write_box_minf}, {MP4_BOX_TYPE_VMHD, mp4_write_box_vmhd}, {MP4_BOX_TYPE_SMHD, mp4_write_box_smhd}, {MP4_BOX_TYPE_DINF, mp4_write_box_dinf}, {MP4_BOX_TYPE_DREF, mp4_write_box_dref}, {MP4_BOX_TYPE_STBL, mp4_write_box_stbl}, {MP4_BOX_TYPE_STSD, mp4_write_box_stsd}, {MP4_BOX_TYPE_STTS, mp4_write_box_stts}, {MP4_BOX_TYPE_CTTS, mp4_write_box_ctts}, {MP4_BOX_TYPE_STSC, mp4_write_box_stsc}, {MP4_BOX_TYPE_STSZ, mp4_write_box_stsz}, {MP4_BOX_TYPE_STCO, mp4_write_box_stco}, {MP4_BOX_TYPE_CO64, mp4_write_box_co64}, {MP4_BOX_TYPE_STSS, mp4_write_box_stss}, {MP4_BOX_TYPE_VIDE, mp4_write_box_vide}, {MP4_BOX_TYPE_SOUN, mp4_write_box_soun}, {MP4_BOX_TYPE_ESDS, mp4_write_box_esds}, {MP4_BOX_TYPE_UNKNOWN, 0} }; /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_extended( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T type, uint32_t fourcc ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; int64_t box_size = 0; unsigned int i; /* Find out which object we want to write */ for( i = 0; mp4_box_list[i].type && mp4_box_list[i].type != type; i++ ); /* Check we found the requested type */ if(!mp4_box_list[i].type) { vc_container_assert(0); return VC_CONTAINER_ERROR_CORRUPTED; } /* We need to find out the size of the object we're going to write it. */ if(!vc_container_writer_extraio_enable(p_ctx, &module->null)) { status = mp4_write_box_extended( p_ctx, type, fourcc ); box_size = STREAM_POSITION(p_ctx); } vc_container_writer_extraio_disable(p_ctx, &module->null); if(status != VC_CONTAINER_SUCCESS) return status; /* Write the object header */ LOG_FORMAT(p_ctx, "- Box %4.4s, size: %"PRIi64, (const char *)&fourcc, box_size); _WRITE_U32(p_ctx, (uint32_t)box_size); _WRITE_FOURCC(p_ctx, fourcc); module->box_level++; /* Call the object specific writing function */ status = mp4_box_list[i].pf_func(p_ctx); module->box_level--; if(status != VC_CONTAINER_SUCCESS) LOG_DEBUG(p_ctx, "box %4.4s appears to be corrupted", (char *)mp4_box_list[i].type); return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T type ) { return mp4_write_box_extended( p_ctx, type, type ); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_ftyp( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; WRITE_FOURCC(p_ctx, module->brand, "major_brand"); WRITE_U32(p_ctx, 512, "minor_version"); if(module->brand == MP4_BRAND_QT) { WRITE_FOURCC(p_ctx, MP4_BRAND_QT, "compatible_brands"); return STREAM_STATUS(p_ctx); } if(module->brand == MP4_BRAND_SKM2) WRITE_FOURCC(p_ctx, MP4_BRAND_SKM2, "compatible_brands"); WRITE_FOURCC(p_ctx, MP4_BRAND_ISOM, "compatible_brands"); WRITE_FOURCC(p_ctx, MP4_BRAND_MP42, "compatible_brands"); WRITE_FOURCC(p_ctx, MP4_BRAND_3GP4, "compatible_brands"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_moov( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; unsigned int i; status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MVHD); if(status != VC_CONTAINER_SUCCESS) return status; for(i = 0; i < p_ctx->tracks_num; i++) { module->current_track = i; status = mp4_write_box(p_ctx, MP4_BOX_TYPE_TRAK); if(status != VC_CONTAINER_SUCCESS) return status; } return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_mvhd( VC_CONTAINER_T *p_ctx ) { static uint32_t matrix[] = { 0x10000,0,0,0,0x10000,0,0,0,0x40000000 }; unsigned int version = MP4_64BITS_TIME; unsigned int i; WRITE_U8(p_ctx, version, "version"); WRITE_U24(p_ctx, 0, "flags"); /**/ p_ctx->duration = 0; for(i = 0; i < p_ctx->tracks_num; i++) { VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i]; VC_CONTAINER_TRACK_MODULE_T *track_module = track->priv->module; int64_t track_duration = track_module->last_pts - track_module->first_pts; if(track_duration > p_ctx->duration) p_ctx->duration = track_duration; } if(version) { WRITE_U64(p_ctx, 0, "creation_time"); WRITE_U64(p_ctx, 0, "modification_time"); WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale"); WRITE_U64(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration"); } else { WRITE_U32(p_ctx, 0, "creation_time"); WRITE_U32(p_ctx, 0, "modification_time"); WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale"); WRITE_U32(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration"); } WRITE_U32(p_ctx, 0x10000, "rate"); /* 1.0 */ WRITE_U16(p_ctx, 0x100, "volume"); /* full volume */ WRITE_U16(p_ctx, 0, "reserved"); for(i = 0; i < 2; i++) WRITE_U32(p_ctx, 0, "reserved"); for(i = 0; i < 9; i++) /* unity matrix */ WRITE_U32(p_ctx, matrix[i], "matrix"); for(i = 0; i < 6; i++) WRITE_U32(p_ctx, 0, "pre_defined"); WRITE_U32(p_ctx, p_ctx->tracks_num + 1, "next_track_ID"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_trak( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_STATUS_T status; status = mp4_write_box(p_ctx, MP4_BOX_TYPE_TKHD); if(status != VC_CONTAINER_SUCCESS) return status; status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MDIA); if(status != VC_CONTAINER_SUCCESS) return status; return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_tkhd( VC_CONTAINER_T *p_ctx ) { static uint32_t matrix[] = { 0x10000,0,0,0,0x10000,0,0,0,0x40000000 }; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; unsigned int version = MP4_64BITS_TIME; uint32_t i, width = 0, height = 0; WRITE_U8(p_ctx, version, "version"); WRITE_U24(p_ctx, 0x7, "flags"); /* track enabled */ if(version) { WRITE_U64(p_ctx, 0, "creation_time"); WRITE_U64(p_ctx, 0, "modification_time"); WRITE_U32(p_ctx, module->current_track + 1, "track_ID"); WRITE_U32(p_ctx, 0, "reserved"); WRITE_U64(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration"); } else { WRITE_U32(p_ctx, 0, "creation_time"); WRITE_U32(p_ctx, 0, "modification_time"); WRITE_U32(p_ctx, module->current_track + 1, "track_ID"); WRITE_U32(p_ctx, 0, "reserved"); WRITE_U32(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration"); } for(i = 0; i < 2; i++) WRITE_U32(p_ctx, 0, "reserved"); WRITE_U16(p_ctx, 0, "layer"); WRITE_U16(p_ctx, 0, "alternate_group"); WRITE_U16(p_ctx, track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO ? 0x100 : 0, "volume"); WRITE_U16(p_ctx, 0, "reserved"); for(i = 0; i < 9; i++) /* unity matrix */ WRITE_U32(p_ctx, matrix[i], "matrix"); if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) { width = track->format->type->video.width << 16; height = track->format->type->video.height << 16; if(track->format->type->video.par_num && track->format->type->video.par_den) width = width * (uint64_t)track->format->type->video.par_num / track->format->type->video.par_den; } else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) { /* FIXME */ } WRITE_U32(p_ctx, width, "width"); WRITE_U32(p_ctx, height, "height"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_mdia( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MDHD); if(status != VC_CONTAINER_SUCCESS) return status; status = mp4_write_box(p_ctx, MP4_BOX_TYPE_HDLR); if(status != VC_CONTAINER_SUCCESS) return status; status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MINF); if(status != VC_CONTAINER_SUCCESS) return status; return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_mdhd( VC_CONTAINER_T *p_ctx ) { unsigned int version = MP4_64BITS_TIME; WRITE_U8(p_ctx, version, "version"); WRITE_U24(p_ctx, 0, "flags"); // FIXME: take a better timescale ?? if(version) { WRITE_U64(p_ctx, 0, "creation_time"); WRITE_U64(p_ctx, 0, "modification_time"); WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale"); WRITE_U64(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration"); } else { WRITE_U32(p_ctx, 0, "creation_time"); WRITE_U32(p_ctx, 0, "modification_time"); WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale"); WRITE_U32(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration"); } WRITE_U16(p_ctx, 0x55c4, "language"); /* ISO-639-2/T language code */ WRITE_U16(p_ctx, 0, "pre_defined"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_hdlr( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; uint32_t i, handler_size, fourcc = 0; const char *handler_name; if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) fourcc = VC_FOURCC('v','i','d','e'); if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) fourcc = VC_FOURCC('s','o','u','n'); if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) fourcc = VC_FOURCC('t','e','x','t'); WRITE_U8(p_ctx, 0, "version"); WRITE_U24(p_ctx, 0, "flags"); if(module->brand == MP4_BRAND_QT) WRITE_FOURCC(p_ctx, VC_FOURCC('m','h','l','r'), "component_type"); else WRITE_U32(p_ctx, 0, "pre-defined"); WRITE_FOURCC(p_ctx, fourcc, "handler_type"); for(i = 0; i < 3; i++) WRITE_U32(p_ctx, 0, "reserved"); if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) { handler_name = "Video Media Handler"; handler_size = sizeof("Video Media Handler"); } else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) { handler_name = "Audio Media Handler"; handler_size = sizeof("Audio Media Handler"); } else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) { handler_name = "Text Media Handler"; handler_size = sizeof("Text Media Handler"); } else { handler_name = ""; handler_size = sizeof(""); } if(module->brand == MP4_BRAND_QT) { handler_size--; WRITE_U8(p_ctx, handler_size, "string_size"); } WRITE_STRING(p_ctx, handler_name, handler_size, "name"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_minf( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) status = mp4_write_box(p_ctx, MP4_BOX_TYPE_VMHD); else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) status = mp4_write_box(p_ctx, MP4_BOX_TYPE_SMHD); #if 0 else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) /*FIXME */; #endif if(status != VC_CONTAINER_SUCCESS) return status; status = mp4_write_box(p_ctx, MP4_BOX_TYPE_DINF); if(status != VC_CONTAINER_SUCCESS) return status; status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STBL); return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_vmhd( VC_CONTAINER_T *p_ctx ) { WRITE_U8(p_ctx, 0, "version"); WRITE_U24(p_ctx, 1, "flags"); WRITE_U16(p_ctx, 0, "graphicsmode"); WRITE_U16(p_ctx, 0, "opcolor"); WRITE_U16(p_ctx, 0, "opcolor"); WRITE_U16(p_ctx, 0, "opcolor"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_smhd( VC_CONTAINER_T *p_ctx ) { WRITE_U8(p_ctx, 0, "version"); WRITE_U24(p_ctx, 0, "flags"); WRITE_U16(p_ctx, 0, "balance"); WRITE_U16(p_ctx, 0, "reserved"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_dinf( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; status = mp4_write_box(p_ctx, MP4_BOX_TYPE_DREF); if(status != VC_CONTAINER_SUCCESS) return status; return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_dref( VC_CONTAINER_T *p_ctx ) { WRITE_U8(p_ctx, 0, "version"); WRITE_U24(p_ctx, 0, "flags"); WRITE_U32(p_ctx, 1, "entry_count"); /* Add a URL box */ WRITE_U32(p_ctx, 12, "box_size"); WRITE_FOURCC(p_ctx, VC_FOURCC('u','r','l',' '), "box_type"); WRITE_U8(p_ctx, 0, "version"); WRITE_U24(p_ctx, 0x1, "flags"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_stbl( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSD); if(status != VC_CONTAINER_SUCCESS) return status; status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STTS); if(status != VC_CONTAINER_SUCCESS) return status; if( 0 && track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) { status = mp4_write_box(p_ctx, MP4_BOX_TYPE_CTTS); if(status != VC_CONTAINER_SUCCESS) return status; } status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSC); if(status != VC_CONTAINER_SUCCESS) return status; status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSZ); if(status != VC_CONTAINER_SUCCESS) return status; if(1) status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STCO); else status = mp4_write_box(p_ctx, MP4_BOX_TYPE_CO64); if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) { status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSS); if(status != VC_CONTAINER_SUCCESS) return status; } return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_stsd( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; WRITE_U8(p_ctx, 0, "version"); WRITE_U24(p_ctx, 0, "flags"); WRITE_U32(p_ctx, 1, "entry_count"); if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) status = mp4_write_box_extended(p_ctx, MP4_BOX_TYPE_VIDE, track->priv->module->fourcc); else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) status = mp4_write_box_extended(p_ctx, MP4_BOX_TYPE_SOUN, track->priv->module->fourcc); #if 0 else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) /*FIXME*/; #endif return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_writer_write_sample_to_temp( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *packet) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; int32_t dts_diff = packet->dts - module->prev_sample_dts; uint8_t keyframe = (packet->flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) ? 0x80 : 0; vc_container_io_write_be_uint32(module->temp.io, packet->size); vc_container_io_write_be_uint32(module->temp.io, dts_diff); vc_container_io_write_be_uint24(module->temp.io, (uint32_t)(packet->pts - packet->dts)); vc_container_io_write_uint8(module->temp.io, packet->track | keyframe); module->prev_sample_dts = packet->dts; return module->temp.io->status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_writer_read_sample_from_temp( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *packet) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; packet->size = vc_container_io_read_be_uint32(module->temp.io); packet->dts += (int32_t)vc_container_io_read_be_uint32(module->temp.io); packet->pts = packet->dts + vc_container_io_read_be_uint24(module->temp.io); packet->track = vc_container_io_read_uint8(module->temp.io); packet->flags = (packet->track & 0x80) ? VC_CONTAINER_PACKET_FLAG_KEYFRAME : 0; packet->track &= 0x7F; return module->temp.io->status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_stts( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_PACKET_T sample; unsigned int entries = 0; int64_t last_dts = 0, delta; WRITE_U8(p_ctx, 0, "version"); WRITE_U24(p_ctx, 0, "flags"); WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries, "entry_count"); if(module->null.refcount) { /* We're not actually writing the data, we just want the size */ WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries * 8); return STREAM_STATUS(p_ctx); } /* Go through all the samples written */ vc_container_io_seek(module->temp.io, INT64_C(0)); sample.dts = 0; status = mp4_writer_read_sample_from_temp(p_ctx, &sample); while(status == VC_CONTAINER_SUCCESS) { if(sample.track != module->current_track) goto skip; delta = sample.dts * MP4_TIMESCALE / 1000000 - last_dts; if(delta < 0) delta = 0; WRITE_U32(p_ctx, 1, "sample_count"); WRITE_U32(p_ctx, delta, "sample_delta"); entries++; last_dts += delta; skip: status = mp4_writer_read_sample_from_temp(p_ctx, &sample); } vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_ctts( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; WRITE_U8(p_ctx, 0, "version"); WRITE_U24(p_ctx, 0, "flags"); WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_CTTS].entries, "entry_count"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_stsc( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_PACKET_T sample; int64_t offset = 0, track_offset = -1; unsigned int entries = 0, chunks = 0, first_chunk = 0, samples_in_chunk = 0; memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T)); WRITE_U8(p_ctx, 0, "version"); WRITE_U24(p_ctx, 0, "flags"); WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries, "entry_count"); if(module->null.refcount) { /* We're not actually writing the data, we just want the size */ WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries * 12); return STREAM_STATUS(p_ctx); } /* Go through all the samples written */ vc_container_io_seek(module->temp.io, INT64_C(0)); status = mp4_writer_read_sample_from_temp(p_ctx, &sample); while(status == VC_CONTAINER_SUCCESS) { if(sample.track != module->current_track) goto skip; /* Is it a new chunk ? */ if(track_offset != offset) { chunks++; if(samples_in_chunk) { WRITE_U32(p_ctx, first_chunk, "first_chunk"); WRITE_U32(p_ctx, samples_in_chunk, "samples_per_chunk"); WRITE_U32(p_ctx, 1, "sample_description_index"); entries++; } first_chunk = chunks; samples_in_chunk = 0; } track_offset = offset + sample.size; samples_in_chunk++; skip: offset += sample.size; status = mp4_writer_read_sample_from_temp(p_ctx, &sample); } if(samples_in_chunk) { WRITE_U32(p_ctx, first_chunk, "first_chunk"); WRITE_U32(p_ctx, samples_in_chunk, "samples_per_chunk"); WRITE_U32(p_ctx, 1, "sample_description_index"); entries++; } vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_stsz( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_PACKET_T sample; unsigned int entries = 0; memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T)); WRITE_U8(p_ctx, 0, "version"); WRITE_U24(p_ctx, 0, "flags"); WRITE_U32(p_ctx, 0, "sample_size"); WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries, "sample_count"); if(module->null.refcount) { /* We're not actually writing the data, we just want the size */ WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries * 4); return STREAM_STATUS(p_ctx); } /* Go through all the samples written */ vc_container_io_seek(module->temp.io, INT64_C(0)); status = mp4_writer_read_sample_from_temp(p_ctx, &sample); while(status == VC_CONTAINER_SUCCESS) { if(sample.track != module->current_track) goto skip; WRITE_U32(p_ctx, sample.size, "entry_size"); entries++; skip: status = mp4_writer_read_sample_from_temp(p_ctx, &sample); } vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_stco( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_PACKET_T sample; int64_t offset = module->data_offset, track_offset = -1; unsigned int entries = 0; memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T)); WRITE_U8(p_ctx, 0, "version"); WRITE_U24(p_ctx, 0, "flags"); WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries, "entry_count"); if(module->null.refcount) { /* We're not actually writing the data, we just want the size */ WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries * 4); return STREAM_STATUS(p_ctx); } /* Go through all the samples written */ vc_container_io_seek(module->temp.io, INT64_C(0)); status = mp4_writer_read_sample_from_temp(p_ctx, &sample); while(status == VC_CONTAINER_SUCCESS) { if(sample.track != module->current_track) goto skip; /* Is it a new chunk ? */ if(track_offset != offset) { WRITE_U32(p_ctx, offset, "chunk_offset"); entries++; } track_offset = offset + sample.size; skip: offset += sample.size; status = mp4_writer_read_sample_from_temp(p_ctx, &sample); } vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_co64( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; WRITE_U8(p_ctx, 0, "version"); WRITE_U24(p_ctx, 0, "flags"); WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_CO64].entries, "entry_count"); if(module->null.refcount) { /* We're not actually writing the data, we just want the size */ WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_CO64].entries * 8); return STREAM_STATUS(p_ctx); } return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_stss( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_PACKET_T sample; unsigned int entries = 0, samples = 0; memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T)); WRITE_U8(p_ctx, 0, "version"); WRITE_U24(p_ctx, 0, "flags"); WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries, "entry_count"); if(module->null.refcount) { /* We're not actually writing the data, we just want the size */ WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries * 4); return STREAM_STATUS(p_ctx); } /* Go through all the samples written */ vc_container_io_seek(module->temp.io, INT64_C(0)); status = mp4_writer_read_sample_from_temp(p_ctx, &sample); while(status == VC_CONTAINER_SUCCESS) { if(sample.track != module->current_track) goto skip; samples++; if(sample.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) { WRITE_U32(p_ctx, samples, "sample_number"); entries++; } skip: status = mp4_writer_read_sample_from_temp(p_ctx, &sample); } vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_vide_avcC( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; WRITE_U32(p_ctx, track->format->extradata_size + 8, "size"); WRITE_FOURCC(p_ctx, VC_FOURCC('a','v','c','C'), "type"); WRITE_BYTES(p_ctx, track->format->extradata, track->format->extradata_size); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_vide_d263( VC_CONTAINER_T *p_ctx ) { WRITE_U32(p_ctx, 8 + 7, "size"); WRITE_FOURCC(p_ctx, VC_FOURCC('d','2','6','3'), "type"); WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor"); WRITE_U8(p_ctx, 0, "version"); WRITE_U8(p_ctx, 10, "level"); WRITE_U8(p_ctx, 0, "profile"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_vide( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; unsigned int i; for(i = 0; i < 6; i++) WRITE_U8(p_ctx, 0, "reserved"); WRITE_U16(p_ctx, 1, "data_reference_index"); WRITE_U16(p_ctx, 0, "pre_defined"); WRITE_U16(p_ctx, 0, "reserved"); for(i = 0; i < 3; i++) WRITE_U32(p_ctx, 0, "pre_defined"); WRITE_U16(p_ctx, track->format->type->video.width, "width"); WRITE_U16(p_ctx, track->format->type->video.height, "height"); WRITE_U32(p_ctx, 0x480000, "horizresolution"); /* 72 dpi */ WRITE_U32(p_ctx, 0x480000, "vertresolution"); /* 72 dpi */ WRITE_U32(p_ctx, 0, "reserved"); WRITE_U16(p_ctx, 1, "frame_count"); for(i = 0; i < 32; i++) _WRITE_U8(p_ctx, 0); WRITE_U16(p_ctx, 0x18, "depth"); WRITE_U16(p_ctx, -1, "pre_defined"); switch(track->format->codec) { case VC_CONTAINER_CODEC_H264: return mp4_write_box_vide_avcC(p_ctx); case VC_CONTAINER_CODEC_H263: return mp4_write_box_vide_d263(p_ctx); case VC_CONTAINER_CODEC_MP4V: return mp4_write_box(p_ctx, MP4_BOX_TYPE_ESDS); default: break; } return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_soun_damr( VC_CONTAINER_T *p_ctx ) { WRITE_U32(p_ctx, 8 + 8, "size"); WRITE_FOURCC(p_ctx, VC_FOURCC('d','a','m','r'), "type"); WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor"); WRITE_U8(p_ctx, 0, "version"); WRITE_U8(p_ctx, 0x80, "mode_set"); WRITE_U8(p_ctx, 0, "mode_change_period"); WRITE_U8(p_ctx, 1, "frame_per_second"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_soun_dawp( VC_CONTAINER_T *p_ctx ) { WRITE_U32(p_ctx, 8 + 5, "size"); WRITE_FOURCC(p_ctx, VC_FOURCC('d','a','w','p'), "type"); WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor"); WRITE_U8(p_ctx, 0, "version"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_soun_devc( VC_CONTAINER_T *p_ctx ) { WRITE_U32(p_ctx, 8 + 6, "size"); WRITE_FOURCC(p_ctx, VC_FOURCC('d','e','v','c'), "type"); WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor"); WRITE_U8(p_ctx, 0, "version"); WRITE_U8(p_ctx, 1, "samples_per_frame"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_soun( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; unsigned int i, version = 0; for(i = 0; i < 6; i++) WRITE_U8(p_ctx, 0, "reserved"); WRITE_U16(p_ctx, 1, "data_reference_index"); if(module->brand == MP4_BRAND_QT) { if(track->format->codec == VC_CONTAINER_CODEC_MP4A) version = 1; WRITE_U16(p_ctx, version, "version"); WRITE_U16(p_ctx, 0, "revision_level"); WRITE_U32(p_ctx, 0, "vendor"); } else { for(i = 0; i < 2; i++) WRITE_U32(p_ctx, 0, "reserved"); } WRITE_U16(p_ctx, track->format->type->audio.channels, "channelcount"); WRITE_U16(p_ctx, 0, "samplesize"); WRITE_U16(p_ctx, 0, "pre_defined"); WRITE_U16(p_ctx, 0, "reserved"); WRITE_U32(p_ctx, track->format->type->audio.sample_rate << 16, "samplerate"); if(module->brand == MP4_BRAND_QT && version == 1) /* FIXME */ { WRITE_U32(p_ctx, 1024, "samples_per_packet"); WRITE_U32(p_ctx, 1536, "bytes_per_packet"); WRITE_U32(p_ctx, 2, "bytes_per_frame"); WRITE_U32(p_ctx, 2, "bytes_per_sample"); } switch(track->format->codec) { case VC_CONTAINER_CODEC_AMRNB: case VC_CONTAINER_CODEC_AMRWB: return mp4_write_box_soun_damr(p_ctx); case VC_CONTAINER_CODEC_AMRWBP: return mp4_write_box_soun_dawp(p_ctx); case VC_CONTAINER_CODEC_EVRC: return mp4_write_box_soun_devc(p_ctx); case VC_CONTAINER_CODEC_MP4A: case VC_CONTAINER_CODEC_MPGA: return mp4_write_box(p_ctx, MP4_BOX_TYPE_ESDS); default: break; } return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_write_box_esds( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; unsigned int decoder_specific_size = 0, decoder_config_size, sl_size; unsigned int stream_type, object_type; #define MP4_GET_DESCRIPTOR_SIZE(size) \ ((size) < 0x0080) ? 2 + (size) : ((size) < 0x4000) ? 3 + (size) : 4 + (size) #define MP4_WRITE_DESCRIPTOR_HEADER(type, size) \ LOG_FORMAT(p_ctx, "descriptor %x, size %i", type, size); _WRITE_U8(p_ctx, type); \ if((size) >= 0x4000) _WRITE_U8(p_ctx, (((size) >> 14) & 0x7F) | 0x80); \ if((size) >= 0x80 ) _WRITE_U8(p_ctx, (((size) >> 7 ) & 0x7F) | 0x80); \ _WRITE_U8(p_ctx, (size) & 0x7F) /* We only support small size descriptors */ if(track->format->extradata_size > 0x200000 - 100) return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; switch(track->format->es_type) { case VC_CONTAINER_ES_TYPE_VIDEO: stream_type = 0x4; break; case VC_CONTAINER_ES_TYPE_AUDIO: stream_type = 0x5; break; case VC_CONTAINER_ES_TYPE_SUBPICTURE: stream_type = 0x20; break; default: return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; } switch(track->format->codec) { case VC_CONTAINER_CODEC_MP4V: object_type = 0x20; break; case VC_CONTAINER_CODEC_MP1V: object_type = 0x6B; break; case VC_CONTAINER_CODEC_MP2V: object_type = 0x60; break; case VC_CONTAINER_CODEC_JPEG: object_type = 0x6C; break; case VC_CONTAINER_CODEC_MP4A: object_type = 0x40; break; case VC_CONTAINER_CODEC_MPGA: object_type = track->format->type->audio.sample_rate < 32000 ? 0x69 : 0x6B; break; default: return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; } decoder_specific_size = MP4_GET_DESCRIPTOR_SIZE(track->format->extradata_size); decoder_config_size = MP4_GET_DESCRIPTOR_SIZE(13 + decoder_specific_size); sl_size = MP4_GET_DESCRIPTOR_SIZE(1); WRITE_U8(p_ctx, 0, "version"); WRITE_U24(p_ctx, 0, "flags"); /* Write the ES descriptor */ MP4_WRITE_DESCRIPTOR_HEADER(0x3, 3 + decoder_config_size + sl_size); WRITE_U16(p_ctx, module->current_track + 1, "es_id"); WRITE_U8(p_ctx, 0x1f, "flags"); /* stream_priority = 0x1f */ /* Write the Decoder Config descriptor */ MP4_WRITE_DESCRIPTOR_HEADER(0x4, 13 + decoder_specific_size); WRITE_U8(p_ctx, object_type, "object_type_indication"); WRITE_U8(p_ctx, (stream_type << 2) | 1, "stream_type"); WRITE_U24(p_ctx, 8000, "buffer_size_db"); WRITE_U32(p_ctx, track->format->bitrate, "max_bitrate"); WRITE_U32(p_ctx, track->format->bitrate, "avg_bitrate"); if(track->format->extradata_size) { MP4_WRITE_DESCRIPTOR_HEADER(0x5, track->format->extradata_size); WRITE_BYTES(p_ctx, track->format->extradata, track->format->extradata_size); } /* Write the SL descriptor */ MP4_WRITE_DESCRIPTOR_HEADER(0x6, 1); WRITE_U8(p_ctx, 0x2, "flags"); return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_writer_close( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status; int64_t mdat_size; mdat_size = STREAM_POSITION(p_ctx) - module->mdat_offset; /* Write the moov box */ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MOOV); /* Finalise the mdat box */ SEEK(p_ctx, module->mdat_offset); WRITE_U32(p_ctx, (uint32_t)mdat_size, "mdat size" ); for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); vc_container_writer_extraio_delete(p_ctx, &module->temp); vc_container_writer_extraio_delete(p_ctx, &module->null); free(module); return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_writer_add_track( VC_CONTAINER_T *p_ctx, VC_CONTAINER_ES_FORMAT_T *format ) { VC_CONTAINER_STATUS_T status; VC_CONTAINER_TRACK_T *track; uint32_t type = 0; if(!(format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; /* Check we support this format */ switch(format->codec) { case VC_CONTAINER_CODEC_AMRNB: type = VC_FOURCC('s','a','m','r'); break; case VC_CONTAINER_CODEC_AMRWB: type = VC_FOURCC('s','a','w','b'); break; case VC_CONTAINER_CODEC_AMRWBP: type = VC_FOURCC('s','a','w','p'); break; case VC_CONTAINER_CODEC_EVRC: type = VC_FOURCC('s','e','v','c'); break; case VC_CONTAINER_CODEC_MP4A: type = VC_FOURCC('m','p','4','a'); break; case VC_CONTAINER_CODEC_MPGA: type = VC_FOURCC('m','p','4','a'); break; case VC_CONTAINER_CODEC_MP4V: type = VC_FOURCC('m','p','4','v'); break; case VC_CONTAINER_CODEC_JPEG: type = VC_FOURCC('m','p','4','v'); break; case VC_CONTAINER_CODEC_H263: type = VC_FOURCC('s','2','6','3'); break; case VC_CONTAINER_CODEC_H264: if(format->codec_variant == VC_FOURCC('a','v','c','C')) type = VC_FOURCC('a','v','c','1'); break; case VC_CONTAINER_CODEC_MJPEG: type = VC_FOURCC('j','p','e','g'); break; case VC_CONTAINER_CODEC_MJPEGA: type = VC_FOURCC('m','j','p','a'); break; case VC_CONTAINER_CODEC_MJPEGB: type = VC_FOURCC('m','j','p','b'); break; case VC_CONTAINER_CODEC_MP1V: type = VC_FOURCC('m','p','e','g'); break; case VC_CONTAINER_CODEC_MP2V: type = VC_FOURCC('m','p','e','g'); break; default: type = 0; break; } if(!type) return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; /* Allocate and initialise track data */ if(p_ctx->tracks_num >= MP4_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; p_ctx->tracks[p_ctx->tracks_num] = track = vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; if(format->extradata_size) { status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size ); if(status) goto error; } vc_container_format_copy(track->format, format, format->extradata_size); track->priv->module->fourcc = type; track->priv->module->offset = -1; track->priv->module->sample_table[MP4_SAMPLE_TABLE_STTS].entry_size = 8; track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSZ].entry_size = 4; track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSC].entry_size = 12; track->priv->module->sample_table[MP4_SAMPLE_TABLE_STCO].entry_size = 4; track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSS].entry_size = 4; track->priv->module->sample_table[MP4_SAMPLE_TABLE_CO64].entry_size = 8; track->priv->module->sample_table[MP4_SAMPLE_TABLE_CTTS].entry_size = 8; p_ctx->tracks_num++; return VC_CONTAINER_SUCCESS; error: vc_container_free_track(p_ctx, track); return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_writer_add_track_done( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; if(module->tracks_add_done) return status; /* We need to find out the size of the object we're going to write it. */ if(!vc_container_writer_extraio_enable(p_ctx, &module->null)) { status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MOOV); module->moov_size = STREAM_POSITION(p_ctx); p_ctx->size = module->moov_size; } vc_container_writer_extraio_disable(p_ctx, &module->null); if(status == VC_CONTAINER_SUCCESS) module->tracks_add_done = true; return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_writer_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; switch(operation) { case VC_CONTAINER_CONTROL_TRACK_ADD: { VC_CONTAINER_ES_FORMAT_T *p_format = (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * ); if(module->tracks_add_done) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; return mp4_writer_add_track(p_ctx, p_format); } case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: return mp4_writer_add_track_done(p_ctx); default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; } } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_writer_add_sample( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *packet ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[packet->track]; VC_CONTAINER_TRACK_MODULE_T *track_module = track->priv->module; track_module->last_pts = packet->pts; if(!track_module->samples) track_module->first_pts = packet->pts; track_module->samples++; track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries++; /* sample size */ p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entry_size; track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries++; /* time to sample */ p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entry_size; #if 0 delta_ts = packet->dts - track_module->timestamp; track_module->timestamp = packet->dts; if(!track_module->samples) track_module->delta_ts = if() #endif /* Is it a new chunk ? */ if(module->sample_offset != track_module->offset) { track_module->chunks++; track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries++; /* chunk offset */ p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entry_size; track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries++; /* sample to chunk */ p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entry_size; } track_module->offset = module->sample_offset + packet->size; if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO && (packet->flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME)) { track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries++; /* sync sample */ p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entry_size; } return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mp4_writer_write( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *packet ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_PACKET_T *sample = &module->sample; VC_CONTAINER_STATUS_T status; if(!module->tracks_add_done) { status = mp4_writer_add_track_done(p_ctx); if(status != VC_CONTAINER_SUCCESS) return status; } if(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START) ++module->samples; /* Switching to a new sample */ if(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START) { module->sample_offset = STREAM_POSITION(p_ctx); sample->size = packet->size; sample->pts = packet->pts; sample->dts = packet->pts; sample->track = packet->track; sample->flags = packet->flags; } else { sample->size += packet->size; sample->flags |= packet->flags; } if(WRITE_BYTES(p_ctx, packet->data, packet->size) != packet->size) return STREAM_STATUS(p_ctx); // TODO do something p_ctx->size += packet->size; // if(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END) { status = mp4_writer_write_sample_to_temp(p_ctx, sample); status = mp4_writer_add_sample(p_ctx, sample); } return VC_CONTAINER_SUCCESS; } /****************************************************************************** Global function definitions. ******************************************************************************/ VC_CONTAINER_STATUS_T mp4_writer_open( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; const char *extension = vc_uri_path_extension(p_ctx->priv->uri); VC_CONTAINER_MODULE_T *module = 0; MP4_BRAND_T brand; /* Check if the user has specified a container */ vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); /* Check we're the right writer for this */ if(!extension) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if(strcasecmp(extension, "3gp") && strcasecmp(extension, "skm") && strcasecmp(extension, "mov") && strcasecmp(extension, "mp4") && strcasecmp(extension, "m4v") && strcasecmp(extension, "m4a")) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Allocate our context */ module = malloc(sizeof(*module)); if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); p_ctx->priv->module = module; p_ctx->tracks = module->tracks; /* Find out which brand we're going write */ if(!strcasecmp(extension, "3gp")) brand = MP4_BRAND_3GP5; else if(!strcasecmp(extension, "skm")) brand = MP4_BRAND_SKM2; else if(!strcasecmp(extension, "mov")) brand = MP4_BRAND_QT; else brand = MP4_BRAND_ISOM; module->brand = brand; /* Create a null i/o writer to help us out in writing our data */ status = vc_container_writer_extraio_create_null(p_ctx, &module->null); if(status != VC_CONTAINER_SUCCESS) goto error; /* Create a temporary i/o writer to help us out in writing our data */ status = vc_container_writer_extraio_create_temp(p_ctx, &module->temp); if(status != VC_CONTAINER_SUCCESS) goto error; status = mp4_write_box(p_ctx, MP4_BOX_TYPE_FTYP); if(status != VC_CONTAINER_SUCCESS) goto error; /* Start the mdat box */ module->mdat_offset = STREAM_POSITION(p_ctx); WRITE_U32(p_ctx, 0, "size"); WRITE_FOURCC(p_ctx, VC_FOURCC('m','d','a','t'), "type"); module->data_offset = STREAM_POSITION(p_ctx); p_ctx->priv->pf_close = mp4_writer_close; p_ctx->priv->pf_write = mp4_writer_write; p_ctx->priv->pf_control = mp4_writer_control; return VC_CONTAINER_SUCCESS; error: LOG_DEBUG(p_ctx, "mp4: error opening stream"); if(module) { if(module->null.io) vc_container_writer_extraio_delete(p_ctx, &module->null); free(module); } return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak writer_open mp4_writer_open #endif userland/containers/mpeg/000077500000000000000000000000001421703157200157725ustar00rootroot00000000000000userland/containers/mpeg/CMakeLists.txt000066400000000000000000000005621421703157200205350ustar00rootroot00000000000000# Container module needs to go in as a plugins so different prefix # and install path set(CMAKE_SHARED_LIBRARY_PREFIX "") # Make sure the compiler can find the necessary include files include_directories (../..) add_library(reader_ps ${LIBRARY_TYPE} ps_reader.c) target_link_libraries(reader_ps containers) install(TARGETS reader_ps DESTINATION ${VMCS_PLUGIN_DIR}) userland/containers/mpeg/ps_reader.c000066400000000000000000001307471421703157200201160ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #define CONTAINER_IS_BIG_ENDIAN //#define ENABLE_CONTAINERS_LOG_FORMAT //#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE #include "containers/core/containers_bits.h" #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_logging.h" #undef CONTAINER_HELPER_LOG_INDENT #define CONTAINER_HELPER_LOG_INDENT(a) (2*(a)->priv->module->level) /****************************************************************************** Defines. ******************************************************************************/ #define PS_TRACKS_MAX 2 #define PS_EXTRADATA_MAX 256 #define PS_SYNC_FAIL_MAX 65536 /** Maximum number of byte-wise sync attempts, should be enough to stride at least one PES packet (length encoded using 16 bits). */ /** Maximum number of pack/packet start codes scanned when searching for tracks at open time or when resyncing. */ #define PS_PACK_SCAN_MAX 128 /****************************************************************************** Type definitions. ******************************************************************************/ typedef struct VC_CONTAINER_TRACK_MODULE_T { /** Coding and elementary stream id of the track */ uint32_t stream_id; /** Sub-stream id (for private_stream_1 only) */ uint32_t substream_id; /** PES packet payload offset (for private_stream_1) */ unsigned int payload_offset; uint8_t extradata[PS_EXTRADATA_MAX]; } VC_CONTAINER_TRACK_MODULE_T; typedef struct VC_CONTAINER_MODULE_T { /** Logging indentation level */ uint32_t level; /** Track data */ int tracks_num; VC_CONTAINER_TRACK_T *tracks[PS_TRACKS_MAX]; /** State flag denoting whether or not we are searching for tracks (at open time) */ bool searching_tracks; /** Size of program stream data (if known) */ uint64_t data_size; /** Offset to the first pack or PES packet start code we've seen */ uint64_t data_offset; /** The first system_clock_reference value we've seen, in (27MHz ticks) */ int64_t scr_offset; /** Most recent system_clock_reference value we've seen, in (27MHz ticks) */ int64_t scr; /** Global offset we add to PES timestamps to make them zero based and to work around discontinuity in the system_clock_reference */ int64_t scr_bias; /** Most recent program stream mux rate (in units of 50 bytes/second). */ uint32_t mux_rate; /** Offset to the most recent pack start code we've seen */ uint64_t pack_offset; /** Program stream mux rate is often incorrect or fixed to 25200 (10.08 Mbit/s) which yields inaccurate duration estimate for most files. We maintain a moving average data rate (in units of bytes/second) based on the system_clock_reference to give better estimates. */ int64_t data_rate; /** Offset to the most recent PES packet start code prefix we've seen */ unsigned int packet_data_size; unsigned int packet_data_left; int64_t packet_pts; int64_t packet_dts; int packet_track; } VC_CONTAINER_MODULE_T; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T ps_reader_open( VC_CONTAINER_T * ); /****************************************************************************** Prototypes for local functions ******************************************************************************/ /****************************************************************************** Local Functions ******************************************************************************/ /** Find the track associated with a PS stream_id */ static VC_CONTAINER_TRACK_T *ps_find_track( VC_CONTAINER_T *ctx, uint32_t stream_id, uint32_t substream_id, bool b_create ) { VC_CONTAINER_TRACK_T *track = 0; unsigned int i; for(i = 0; i < ctx->tracks_num; i++) if(ctx->tracks[i]->priv->module->stream_id == stream_id && ctx->tracks[i]->priv->module->substream_id == substream_id) break; if(i < ctx->tracks_num) /* We found it */ track = ctx->tracks[i]; if(!track && b_create && i < PS_TRACKS_MAX) { /* Allocate and initialise a new track */ ctx->tracks[i] = track = vc_container_allocate_track(ctx, sizeof(*ctx->tracks[0]->priv->module)); if(track) { track->priv->module->stream_id = stream_id; track->priv->module->substream_id = substream_id; ctx->tracks_num++; } } if(!track && b_create) LOG_DEBUG(ctx, "could not create track for stream id: %i", stream_id); return track; } /*****************************************************************************/ STATIC_INLINE VC_CONTAINER_STATUS_T ps_find_start_code( VC_CONTAINER_T *ctx, uint8_t *buffer ) { unsigned int i; /* Scan for a pack or PES packet start code prefix */ for (i = 0; i < PS_SYNC_FAIL_MAX; ++i) { if(PEEK_BYTES(ctx, buffer, 4) < 4) return VC_CONTAINER_ERROR_EOS; if(buffer[0] == 0x0 && buffer[1] == 0x0 && buffer[2] == 0x1 && buffer[3] >= 0xB9) break; if (SKIP_BYTES(ctx, 1) != 1) return VC_CONTAINER_ERROR_EOS; } if(i == PS_SYNC_FAIL_MAX) /* We didn't find a valid pack or PES packet */ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if (buffer[3] == 0xB9) /* MPEG_program_end_code */ return VC_CONTAINER_ERROR_EOS; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T ps_read_system_header( VC_CONTAINER_T *ctx ) { uint8_t header[8]; uint32_t length; VC_CONTAINER_BITS_T bits; if(_READ_U32(ctx) != 0x1BB) return VC_CONTAINER_ERROR_CORRUPTED; LOG_FORMAT(ctx, "system_header"); ctx->priv->module->level++; length = READ_U16(ctx, "header_length"); if(length < 6) return VC_CONTAINER_ERROR_CORRUPTED; if(READ_BYTES(ctx, header, 6) != 6) return VC_CONTAINER_ERROR_EOS; BITS_INIT(ctx, &bits, header, 6); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; BITS_SKIP(ctx, &bits, 22, "rate_bound"); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; BITS_SKIP(ctx, &bits, 6, "audio_bound"); BITS_SKIP(ctx, &bits, 1, "fixed_flag"); BITS_SKIP(ctx, &bits, 1, "CSPS_flag"); BITS_SKIP(ctx, &bits, 1, "system_audio_lock_flag"); BITS_SKIP(ctx, &bits, 1, "system_video_lock_flag"); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; BITS_SKIP(ctx, &bits, 5, "video_bound"); BITS_SKIP(ctx, &bits, 1, "packet_rate_restriction_flag"); BITS_SKIP(ctx, &bits, 7, "reserved_bits"); length -= 6; while(length >= 3 && (PEEK_U8(ctx) & 0x80)) { SKIP_U8(ctx, "stream_id"); SKIP_BYTES(ctx, 2); length -= 3; } SKIP_BYTES(ctx, length); ctx->priv->module->level--; return STREAM_STATUS(ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T ps_read_pack_header( VC_CONTAINER_T *ctx ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; uint8_t header[10]; int64_t scr, scr_base, scr_ext = INT64_C(0); uint64_t pack_offset = STREAM_POSITION(ctx); uint32_t mux_rate, stuffing; VC_CONTAINER_BITS_T bits; VC_CONTAINER_STATUS_T status; if(_READ_U32(ctx) != 0x1BA) return VC_CONTAINER_ERROR_CORRUPTED; LOG_FORMAT(ctx, "pack_header"); module->level++; if (PEEK_U8(ctx) & 0x40) /* program stream */ { if(READ_BYTES(ctx, header, 10) != 10) return VC_CONTAINER_ERROR_EOS; BITS_INIT(ctx, &bits, header, 10); if(BITS_READ_U32(ctx, &bits, 2, "'01' marker bits") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; scr_base = BITS_READ_U32(ctx, &bits, 3, "system_clock_reference_base [32..30]") << 30; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [29..15]") << 15; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [14..0]"); LOG_FORMAT(ctx, "system_clock_reference_base %"PRId64, scr_base); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; scr_ext = BITS_READ_U32(ctx, &bits, 9, "system_clock_reference_extension"); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; mux_rate = BITS_READ_U32(ctx, &bits, 22, "program_mux_rate"); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; BITS_SKIP(ctx, &bits, 5, "reserved"); stuffing = BITS_READ_U32(ctx, &bits, 3, "pack_stuffing_length"); SKIP_BYTES(ctx, stuffing); } else /* system stream */ { if(READ_BYTES(ctx, header, 8) != 8) return VC_CONTAINER_ERROR_EOS; BITS_INIT(ctx, &bits, header, 8); if(BITS_READ_U32(ctx, &bits, 4, "'0010' marker bits") != 0x2) return VC_CONTAINER_ERROR_CORRUPTED; scr_base = BITS_READ_U32(ctx, &bits, 3, "system_clock_reference_base [32..30]") << 30; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [29..15]") << 15; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [14..0]"); LOG_FORMAT(ctx, "system_clock_reference_base %"PRId64, scr_base); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; mux_rate = BITS_READ_U32(ctx, &bits, 22, "program_mux_rate"); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; } if ((status = STREAM_STATUS(ctx)) != VC_CONTAINER_SUCCESS) return status; module->level--; /* Set or update system_clock_reference, adjust bias if necessary */ scr = scr_base * INT64_C(300) + scr_ext; if (module->scr_offset == VC_CONTAINER_TIME_UNKNOWN) module->scr_offset = scr; if (module->scr == VC_CONTAINER_TIME_UNKNOWN) module->scr_bias = -scr; else if (scr < module->scr) module->scr_bias = module->scr - scr; if (module->scr != VC_CONTAINER_TIME_UNKNOWN) { /* system_clock_reference is not necessarily continuous across the entire stream */ if (scr > module->scr) { int64_t data_rate; data_rate = INT64_C(27000000) * (pack_offset - module->pack_offset) / (scr - module->scr); if (module->data_rate) { /* Simple moving average over data rate seen so far */ module->data_rate = (module->data_rate * 31 + data_rate) >> 5; } else { module->data_rate = mux_rate * 50; } } module->pack_offset = pack_offset; } module->scr = scr; module->mux_rate = mux_rate; /* Check for a system header */ if(PEEK_U32(ctx) == 0x1BB) return ps_read_system_header(ctx); return STREAM_STATUS(ctx); } /*****************************************************************************/ static void ps_get_stream_coding( VC_CONTAINER_T *ctx, unsigned int stream_id, VC_CONTAINER_ES_TYPE_T *p_type, VC_CONTAINER_FOURCC_T *p_codec, VC_CONTAINER_FOURCC_T *p_variant) { VC_CONTAINER_ES_TYPE_T type = VC_CONTAINER_ES_TYPE_UNKNOWN; VC_CONTAINER_FOURCC_T codec = VC_CONTAINER_CODEC_UNKNOWN; VC_CONTAINER_FOURCC_T variant = 0; VC_CONTAINER_PARAM_UNUSED(ctx); if (stream_id == 0xE2) /* FIXME: why is this stream number reserved for H264? */ { type = VC_CONTAINER_ES_TYPE_VIDEO; codec = VC_CONTAINER_CODEC_H264; } else if ((stream_id & 0xF0) == 0xE0) { type = VC_CONTAINER_ES_TYPE_VIDEO; codec = VC_CONTAINER_CODEC_MP2V; } else if ((stream_id & 0xE0) == 0xC0) { type = VC_CONTAINER_ES_TYPE_AUDIO; codec = VC_CONTAINER_CODEC_MPGA; variant = VC_CONTAINER_VARIANT_MPGA_L2; } /* FIXME: PRIVATE_EVOB_PES_PACKET with stream_id 0xFD ? */ *p_type = type; *p_codec = codec; *p_variant = variant; } /*****************************************************************************/ static int64_t ps_pes_time_to_us( VC_CONTAINER_T *ctx, int64_t time ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; if (time == VC_CONTAINER_TIME_UNKNOWN) return VC_CONTAINER_TIME_UNKNOWN; /* Need to wait for system_clock_reference first */ if (module->scr_bias == VC_CONTAINER_TIME_UNKNOWN) return VC_CONTAINER_TIME_UNKNOWN; /* Can't have valid bias without known system_clock_reference */ vc_container_assert(module->scr != VC_CONTAINER_TIME_UNKNOWN); /* 90kHz (PES) clock --> (zero based) 27MHz system clock --> microseconds */ return (INT64_C(300) * time + module->scr_bias) / INT64_C(27); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T ps_read_pes_time( VC_CONTAINER_T *ctx, uint32_t *p_length, unsigned int pts_dts, int64_t *p_pts, int64_t *p_dts ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; uint8_t header[10]; uint32_t length = *p_length; VC_CONTAINER_BITS_T bits; int64_t pts, dts; if (p_pts) *p_pts = VC_CONTAINER_TIME_UNKNOWN; if (p_dts) *p_dts = VC_CONTAINER_TIME_UNKNOWN; if (pts_dts == 0x2) { /* PTS only */ LOG_FORMAT(ctx, "PTS"); ctx->priv->module->level++; if(length < 5) return VC_CONTAINER_ERROR_CORRUPTED; if(READ_BYTES(ctx, header, 5) != 5) return VC_CONTAINER_ERROR_EOS; BITS_INIT(ctx, &bits, header, 5); if(BITS_READ_U32(ctx, &bits, 4, "'0010' marker bits") != 0x2) return VC_CONTAINER_ERROR_CORRUPTED; pts = BITS_READ_U32(ctx, &bits, 3, "PTS [32..30]") << 30; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; pts |= BITS_READ_U32(ctx, &bits, 15, "PTS [29..15]") << 15; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; pts |= BITS_READ_U32(ctx, &bits, 15, "PTS [14..0]"); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; LOG_FORMAT(ctx, "PTS %"PRId64, pts); if (p_pts) *p_pts = pts; length -= 5; ctx->priv->module->level--; } else if (pts_dts == 0x3) { /* PTS & DTS */ LOG_FORMAT(ctx, "PTS DTS"); ctx->priv->module->level++; if(length < 10) return VC_CONTAINER_ERROR_CORRUPTED; if(READ_BYTES(ctx, header, 10) != 10) return VC_CONTAINER_ERROR_EOS; BITS_INIT(ctx, &bits, header, 10); /* PTS */ if(BITS_READ_U32(ctx, &bits, 4, "'0011' marker bits") != 0x3) return VC_CONTAINER_ERROR_CORRUPTED; pts = BITS_READ_U32(ctx, &bits, 3, "PTS [32..30]") << 30; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; pts |= BITS_READ_U32(ctx, &bits, 15, "PTS [29..15]") << 15; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; pts |= BITS_READ_U32(ctx, &bits, 15, "PTS [14..0]"); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; /* DTS */ if(BITS_READ_U32(ctx, &bits, 4, "'0001' marker bits") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; dts = BITS_READ_U32(ctx, &bits, 3, "DTS [32..30]") << 30; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; dts |= BITS_READ_U32(ctx, &bits, 15, "DTS [29..15]") << 15; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; dts |= BITS_READ_U32(ctx, &bits, 15, "DTS [14..0]"); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; LOG_FORMAT(ctx, "PTS %"PRId64, pts); LOG_FORMAT(ctx, "DTS %"PRId64, dts); if (p_pts) *p_pts = pts; if (p_dts) *p_dts = dts; length -= 10; ctx->priv->module->level--; } else { status = VC_CONTAINER_ERROR_NOT_FOUND; } *p_length = *p_length - length; return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T ps_read_pes_extension( VC_CONTAINER_T *ctx, uint32_t *p_length ) { unsigned int pes_private_data, pack_header, packet_seq_counter, pstd_buffer, extension2; uint8_t header[2]; uint32_t length = *p_length; VC_CONTAINER_BITS_T bits; unsigned int i; LOG_FORMAT(ctx, "PES_extension"); ctx->priv->module->level++; if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; if(READ_BYTES(ctx, header, 1) != 1) return VC_CONTAINER_ERROR_EOS; BITS_INIT(ctx, &bits, header, 1); pes_private_data = BITS_READ_U32(ctx, &bits, 1, "PES_private_data_flag"); pack_header = BITS_READ_U32(ctx, &bits, 1, "pack_header_field_flag"); packet_seq_counter = BITS_READ_U32(ctx, &bits, 1, "program_packet_sequence_counter_flag"); pstd_buffer = BITS_READ_U32(ctx, &bits, 1, "P-STD_buffer_flag"); BITS_SKIP(ctx, &bits, 3, "3 reserved_bits"); extension2 = BITS_READ_U32(ctx, &bits, 1, "PES_extension_flag_2"); length -= 1; if (pes_private_data) { if(length < 16) return VC_CONTAINER_ERROR_CORRUPTED; SKIP_BYTES(ctx, 16); /* PES_private_data */ length -= 16; } if (pack_header) { unsigned int pack_field_len; if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; pack_field_len = READ_U8(ctx, "pack_field_length"); length -= 1; if(length < pack_field_len) return VC_CONTAINER_ERROR_CORRUPTED; SKIP_BYTES(ctx, pack_field_len); /* pack_header */ length -= pack_field_len; } if (packet_seq_counter) { if(length < 2) return VC_CONTAINER_ERROR_CORRUPTED; if(READ_BYTES(ctx, header, 2) != 2) return VC_CONTAINER_ERROR_EOS; BITS_INIT(ctx, &bits, header, 2); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; BITS_SKIP(ctx, &bits, 7, "program_packet_sequence_counter"); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; BITS_SKIP(ctx, &bits, 1, "MPEG1_MPEG2_identifier"); BITS_SKIP(ctx, &bits, 6, "original_stuff_length"); length -= 2; } if (pstd_buffer) { if(length < 2) return VC_CONTAINER_ERROR_CORRUPTED; if(READ_BYTES(ctx, header, 2) != 2) return VC_CONTAINER_ERROR_EOS; BITS_INIT(ctx, &bits, header, 2); if(BITS_READ_U32(ctx, &bits, 2, "'01' marker bits") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; BITS_SKIP(ctx, &bits, 1, "P-STD_buffer_scale"); BITS_SKIP(ctx, &bits, 13, "P-STD_buffer_size"); length -= 2; } if (extension2) { uint8_t ext_field_len; if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; if(READ_BYTES(ctx, &ext_field_len, 1) != 1) return VC_CONTAINER_ERROR_EOS; length -= 1; if((ext_field_len & 0x80) != 0x80) return VC_CONTAINER_ERROR_CORRUPTED; /* marker_bit */ ext_field_len &= ~0x80; LOG_FORMAT(ctx, "PES_extension_field_length %d", ext_field_len); for (i = 0; i < ext_field_len; i++) { SKIP_U8(ctx, "reserved"); length--; } } ctx->priv->module->level--; *p_length = *p_length - length; /* Number of bytes read from stream */ return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T ps_read_pes_packet_header( VC_CONTAINER_T *ctx, uint32_t *p_length, int64_t *p_pts, int64_t *p_dts ) { VC_CONTAINER_STATUS_T status; VC_CONTAINER_BITS_T bits; uint32_t size, length = *p_length; unsigned int pts_dts; uint8_t header[10]; if(length < 3) return VC_CONTAINER_ERROR_CORRUPTED; if ((PEEK_U8(ctx) & 0xC0) == 0x80) /* program stream */ { unsigned int escr, es_rate, dsm_trick_mode, additional_copy_info, pes_crc, pes_extension; unsigned int header_length; if(READ_BYTES(ctx, header, 3) != 3) return VC_CONTAINER_ERROR_EOS; BITS_INIT(ctx, &bits, header, 3); if (BITS_READ_U32(ctx, &bits, 2, "'10' marker bits") != 0x2) return VC_CONTAINER_ERROR_CORRUPTED; BITS_SKIP(ctx, &bits, 2, "PES_scrambling_control"); BITS_SKIP(ctx, &bits, 1, "PES_priority"); BITS_SKIP(ctx, &bits, 1, "data_alignment_indicator"); BITS_SKIP(ctx, &bits, 1, "copyright"); BITS_SKIP(ctx, &bits, 1, "original_or_copy"); pts_dts = BITS_READ_U32(ctx, &bits, 2, "PTS_DTS_flags"); escr = BITS_READ_U32(ctx, &bits, 1, "ESCR_flag"); es_rate = BITS_READ_U32(ctx, &bits, 1, "ES_rate_flag"); dsm_trick_mode = BITS_READ_U32(ctx, &bits, 1, "DSM_trick_mode_flag"); additional_copy_info = BITS_READ_U32(ctx, &bits, 1, "additional_copy_info_flag"); pes_crc = BITS_READ_U32(ctx, &bits, 1, "PES_CRC_flag"); pes_extension = BITS_READ_U32(ctx, &bits, 1, "PES_extension_flag"); header_length = BITS_READ_U32(ctx, &bits, 8, "PES_header_data_length"); length -= 3; size = length; status = ps_read_pes_time(ctx, &size, pts_dts, p_pts, p_dts); if (status && status != VC_CONTAINER_ERROR_NOT_FOUND) return status; length -= size; header_length -= size; if (escr) { /* Elementary stream clock reference */ int64_t escr; ctx->priv->module->level++; if(length < 6) return VC_CONTAINER_ERROR_CORRUPTED; if(READ_BYTES(ctx, header, 6) != 6) return VC_CONTAINER_ERROR_EOS; BITS_INIT(ctx, &bits, header, 6); BITS_SKIP(ctx, &bits, 2, "reserved_bits"); escr = BITS_READ_U32(ctx, &bits, 3, "ESCR_base [32..30]") << 30; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; escr |= BITS_READ_U32(ctx, &bits, 15, "ESCR_base [29..15]") << 15; if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; escr |= BITS_READ_U32(ctx, &bits, 15, "ESCR_base [14..0]"); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; BITS_READ_U32(ctx, &bits, 9, "ESCR_extension"); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; LOG_FORMAT(ctx, "ESCR_base %"PRId64, escr); length -= 6; header_length -= 6; ctx->priv->module->level--; } if (es_rate) { /* Elementary stream rate */ if(length < 3) return VC_CONTAINER_ERROR_CORRUPTED; if(READ_BYTES(ctx, header, 3) != 3) return VC_CONTAINER_ERROR_EOS; BITS_INIT(ctx, &bits, header, 3); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; BITS_READ_U32(ctx, &bits, 22, "ES_rate"); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; length -= 3; header_length -= 3; } if (dsm_trick_mode) { unsigned int trick_mode; if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; if(READ_BYTES(ctx, header, 1) != 1) return VC_CONTAINER_ERROR_EOS; BITS_INIT(ctx, &bits, header, 1); trick_mode = BITS_READ_U32(ctx, &bits, 3, "trick_mode_control"); if (trick_mode == 0x0 /* fast_forward */) { BITS_SKIP(ctx, &bits, 2, "field_id"); BITS_SKIP(ctx, &bits, 1, "intra_slice_refresh"); BITS_SKIP(ctx, &bits, 2, "frequency_truncation"); } else if (trick_mode == 0x1 /* slow_motion */) { BITS_SKIP(ctx, &bits, 5, "rep_cntrl"); } else if (trick_mode == 0x2 /* freeze_frame */) { BITS_SKIP(ctx, &bits, 2, "field_id"); BITS_SKIP(ctx, &bits, 3, "reserved_bits"); } else if (trick_mode == 0x3 /* fast_reverse */) { BITS_SKIP(ctx, &bits, 2, "field_id"); BITS_SKIP(ctx, &bits, 1, "intra_slice_refresh"); BITS_SKIP(ctx, &bits, 2, "frequency_truncation"); } else if (trick_mode == 0x4 /* slow_reverse */) BITS_SKIP(ctx, &bits, 5, "rep_cntrl"); else BITS_SKIP(ctx, &bits, 5, "5 reserved_bits"); length -= 1; header_length -= 1; } if (additional_copy_info) { if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; if(READ_BYTES(ctx, header, 1) != 1) return VC_CONTAINER_ERROR_EOS; BITS_INIT(ctx, &bits, header, 1); if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; BITS_SKIP(ctx, &bits, 7, "additional_copy_info"); length -= 1; header_length -= 1; } if (pes_crc) { SKIP_U16(ctx, "previous_PES_packet_CRC"); length -= 2; header_length -= 2; } if (pes_extension) { size = length; if ((status = ps_read_pes_extension(ctx, &size)) != VC_CONTAINER_SUCCESS) return status; length -= size; header_length -= size; } if (header_length <= length) { SKIP_BYTES(ctx, header_length); /* header stuffing */ length -= header_length; } } else /* MPEG 1 PES header */ { if(length < 12) return VC_CONTAINER_ERROR_CORRUPTED; while (PEEK_U8(ctx) == 0xFF && length > 0) { SKIP_U8(ctx, "stuffing"); length--; } if (length == 0) return VC_CONTAINER_ERROR_CORRUPTED; if ((PEEK_U8(ctx) & 0xC0) == 0x40) { if(length < 2) return VC_CONTAINER_ERROR_CORRUPTED; SKIP_U8(ctx, "???"); SKIP_U8(ctx, "???"); length -= 2; } pts_dts = (PEEK_U8(ctx) & 0x30) >> 4; size = length; status = ps_read_pes_time(ctx, &size, pts_dts, p_pts, p_dts); if (status && status != VC_CONTAINER_ERROR_NOT_FOUND) return status; length -= size; if (status == VC_CONTAINER_ERROR_NOT_FOUND) { if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; SKIP_U8(ctx, "???"); length -= 1; } } *p_length = length; return STREAM_STATUS(ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T ps_read_private_stream_1_coding( VC_CONTAINER_T *ctx, VC_CONTAINER_ES_TYPE_T *p_type, VC_CONTAINER_FOURCC_T *p_codec, uint32_t *substream_id, uint32_t *p_length ) { VC_CONTAINER_ES_TYPE_T type = VC_CONTAINER_ES_TYPE_UNKNOWN; VC_CONTAINER_FOURCC_T codec = VC_CONTAINER_CODEC_UNKNOWN; uint32_t length; uint8_t id = 0; length = *p_length; if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; if(READ_BYTES(ctx, &id, 1) != 1) return VC_CONTAINER_ERROR_EOS; length -= 1; LOG_FORMAT(ctx, "private_stream_1 byte: 0x%x (%u)", id, id); if (id >= 0x20 && id <= 0x3f) { type = VC_CONTAINER_ES_TYPE_SUBPICTURE; codec = VC_CONTAINER_CODEC_UNKNOWN; } else if ((id >= 0x80 && id <= 0x87) || (id >= 0xC0 && id <= 0xCF)) { type = VC_CONTAINER_ES_TYPE_AUDIO; codec = VC_CONTAINER_CODEC_AC3; } else if ((id >= 0x88 && id <= 0x8F) || (id >= 0x98 && id <= 0x9F)) { type = VC_CONTAINER_ES_TYPE_AUDIO; codec = VC_CONTAINER_CODEC_DTS; } else if (id >= 0xA0 && id <= 0xBF) { type = VC_CONTAINER_ES_TYPE_AUDIO; codec = VC_CONTAINER_CODEC_PCM_SIGNED; } else { LOG_FORMAT(ctx, "Unknown private_stream_1 byte: 0x%x (%u)", id, id); } *substream_id = id; *p_type = type; *p_codec = codec; *p_length = length; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T ps_read_private_stream_1_format( VC_CONTAINER_T *ctx, VC_CONTAINER_ES_FORMAT_T *format, uint32_t *length ) { uint8_t header[8]; VC_CONTAINER_BITS_T bits; if (format->codec == VC_CONTAINER_CODEC_PCM_SIGNED) { static const unsigned fs_tab[4] = { 48000, 96000, 44100, 32000 }; static const unsigned bps_tab[] = {16, 20, 24, 0}; unsigned fs, bps, nchan; if(*length < 6) return VC_CONTAINER_ERROR_CORRUPTED; if(READ_BYTES(ctx, header, 6) != 6) return VC_CONTAINER_ERROR_EOS; BITS_INIT(ctx, &bits, header, 6); BITS_SKIP(ctx, &bits, 8, "???"); BITS_SKIP(ctx, &bits, 8, "???"); BITS_SKIP(ctx, &bits, 8, "???"); BITS_SKIP(ctx, &bits, 1, "emphasis"); BITS_SKIP(ctx, &bits, 1, "mute"); BITS_SKIP(ctx, &bits, 1, "reserved"); BITS_SKIP(ctx, &bits, 5, "frame number"); bps = BITS_READ_U32(ctx, &bits, 2, "quant"); fs = BITS_READ_U32(ctx, &bits, 2, "freq"); BITS_SKIP(ctx, &bits, 1, "reserved"); nchan = BITS_READ_U32(ctx, &bits, 3, "channels"); *length -= 6; format->type->audio.sample_rate = fs_tab[fs]; format->type->audio.bits_per_sample = bps_tab[bps]; format->type->audio.channels = nchan + 1; format->type->audio.block_align = (format->type->audio.channels * format->type->audio.bits_per_sample + 7 ) / 8; } else { if(*length < 3) return VC_CONTAINER_ERROR_CORRUPTED; SKIP_U8(ctx, "num of frames"); SKIP_U8(ctx, "start pos hi"); SKIP_U8(ctx, "start pos lo"); *length -= 3; } return STREAM_STATUS(ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T ps_read_pes_packet( VC_CONTAINER_T *ctx ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; VC_CONTAINER_STATUS_T status; uint8_t header[10]; VC_CONTAINER_BITS_T bits; uint32_t length, stream_id, substream_id = 0; VC_CONTAINER_ES_TYPE_T type; VC_CONTAINER_FOURCC_T codec, variant = 0; VC_CONTAINER_TRACK_T *track; int64_t pts, dts; if(_READ_U24(ctx) != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; if(READ_BYTES(ctx, header, 3) != 3) return VC_CONTAINER_ERROR_EOS; LOG_FORMAT(ctx, "pes_packet_header"); module->level++; BITS_INIT(ctx, &bits, header, 3); stream_id = BITS_READ_U32(ctx, &bits, 8, "stream_id"); length = BITS_READ_U32(ctx, &bits, 16, "PES_packet_length"); if (stream_id < 0xBC) return VC_CONTAINER_ERROR_CORRUPTED; if (stream_id == 0xBC /* program_stream_map */ || stream_id == 0xBE /* padding_stream */ || stream_id == 0xBF /* private_stream_2 */ || stream_id == 0xF0 /* ECM */ || stream_id == 0xF1 /* EMM */ || stream_id == 0xFF /* program_stream_directory */ || stream_id == 0xF2 /* DSMCC_stream */ || stream_id == 0xF8 /* ITU-T Rec. H.222.1 type E */) goto skip; /* Parse PES packet header */ if ((status = ps_read_pes_packet_header(ctx, &length, &pts, &dts)) != VC_CONTAINER_SUCCESS) return status; /* For private_stream_1, encoding format is stored in the payload */ if (stream_id == 0xBD) { status = ps_read_private_stream_1_coding(ctx, &type, &codec, &substream_id, &length); if (status) return status; } else ps_get_stream_coding(ctx, stream_id, &type, &codec, &variant); /* Check that we know what to do with this track */ if(type == VC_CONTAINER_ES_TYPE_UNKNOWN || codec == VC_CONTAINER_CODEC_UNKNOWN) goto skip; track = ps_find_track(ctx, stream_id, substream_id, module->searching_tracks); if(!track) goto skip; if (module->searching_tracks) { track->is_enabled = true; track->format->es_type = type; track->format->codec = codec; track->format->codec_variant = variant; /* For private_stream_1, we need to parse payload further to get elementary stream format */ if (stream_id == 0xBD) { uint32_t current_length = length; status = ps_read_private_stream_1_format(ctx, track->format, &length); if (status) return status; track->priv->module->payload_offset = current_length - length; } goto skip; } else { unsigned i; SKIP_BYTES(ctx, track->priv->module->payload_offset); length -= track->priv->module->payload_offset; /* Find track index */ for(i = 0; i < ctx->tracks_num; i++) if(ctx->tracks[i] == track) break; vc_container_assert(i < ctx->tracks_num); module->packet_track = i; module->packet_data_size = length; module->packet_pts = pts; module->packet_dts = dts; } end: module->level--; return STREAM_STATUS(ctx); skip: SKIP_BYTES(ctx, length); /* remaining PES_packet_data */ goto end; } /*****************************************************************************/ STATIC_INLINE VC_CONTAINER_STATUS_T ps_find_pes_packet( VC_CONTAINER_T *ctx ) { VC_CONTAINER_STATUS_T status; VC_CONTAINER_MODULE_T *module = ctx->priv->module; uint8_t buffer[4]; unsigned int i; module->packet_data_size = 0; for (i = 0; i != PS_PACK_SCAN_MAX; ++i) { if((status = ps_find_start_code(ctx, buffer)) != VC_CONTAINER_SUCCESS) break; if (buffer[3] == 0xBA && ((status = ps_read_pack_header(ctx)) != VC_CONTAINER_SUCCESS)) continue; /* pack start code but parsing failed, goto resync */ if ((status = ps_read_pes_packet(ctx)) == VC_CONTAINER_SUCCESS) break; } return status; } /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ /*****************************************************************************/ static VC_CONTAINER_STATUS_T ps_reader_read( VC_CONTAINER_T *ctx, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = ctx->priv->module; vc_container_assert(!module->searching_tracks); while(!module->packet_data_left) { if(ps_find_pes_packet(ctx) != VC_CONTAINER_SUCCESS) { status = VC_CONTAINER_ERROR_EOS; goto error; } module->packet_data_left = module->packet_data_size; } p_packet->track = module->packet_track; p_packet->size = module->packet_data_left; p_packet->flags = 0; p_packet->pts = ps_pes_time_to_us(ctx, module->packet_pts); p_packet->dts = ps_pes_time_to_us(ctx, module->packet_dts); if (flags & VC_CONTAINER_READ_FLAG_SKIP) { SKIP_BYTES(ctx, module->packet_data_left); module->packet_data_left = 0; return VC_CONTAINER_SUCCESS; } if (flags & VC_CONTAINER_READ_FLAG_INFO) return VC_CONTAINER_SUCCESS; p_packet->size = MIN(p_packet->buffer_size, module->packet_data_left); p_packet->size = READ_BYTES(ctx, p_packet->data, p_packet->size); module->packet_data_left -= p_packet->size; /* Temporary work-around for lpcm audio */ { VC_CONTAINER_TRACK_T *track = ctx->tracks[module->packet_track]; if (track->format->codec == VC_CONTAINER_CODEC_PCM_SIGNED) { unsigned i; uint16_t *ptr = (uint16_t *)p_packet->data; for (i = 0; i < p_packet->size / 2; i ++) { uint32_t v = *ptr; *ptr++ = v >> 8 | ( (v & 0xFF) << 8 ); } } } if (module->packet_data_left) module->packet_pts = module->packet_dts = VC_CONTAINER_TIME_UNKNOWN; return STREAM_STATUS(ctx); error: if (status == VC_CONTAINER_ERROR_EOS) { /* Reset time reference and calculation state */ ctx->priv->module->scr = VC_CONTAINER_TIME_UNKNOWN; ctx->priv->module->scr_bias = -module->scr_offset; } return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T ps_reader_seek( VC_CONTAINER_T *ctx, int64_t *p_offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; uint64_t seekpos, position; int64_t scr; VC_CONTAINER_PARAM_UNUSED(flags); if (mode != VC_CONTAINER_SEEK_MODE_TIME || !STREAM_SEEKABLE(ctx)) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; position = STREAM_POSITION(ctx); scr = module->scr; if (*p_offset == INT64_C(0)) seekpos = module->data_offset; else { if (!ctx->duration) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; /* The following is an estimate that might be quite inaccurate */ seekpos = module->data_offset + (*p_offset * module->data_size) / ctx->duration; } SEEK(ctx, seekpos); module->scr = module->scr_offset; status = ps_find_pes_packet(ctx); if (status && status != VC_CONTAINER_ERROR_EOS) goto error; module->packet_data_left = module->packet_data_size; if (module->packet_pts != VC_CONTAINER_TIME_UNKNOWN) *p_offset = ps_pes_time_to_us(ctx, module->packet_pts); else if (module->data_size) *p_offset = (STREAM_POSITION(ctx) - module->data_offset) * ctx->duration / module->data_size; return STREAM_STATUS(ctx); error: module->scr = scr; SEEK(ctx, position); return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T ps_reader_close( VC_CONTAINER_T *ctx ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; unsigned int i; for(i = 0; i < ctx->tracks_num; i++) vc_container_free_track(ctx, ctx->tracks[i]); free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T ps_reader_open( VC_CONTAINER_T *ctx ) { const char *extension = vc_uri_path_extension(ctx->priv->uri); VC_CONTAINER_MODULE_T *module = 0; VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; uint8_t buffer[4]; unsigned int i; /* Check if the user has specified a container */ vc_uri_find_query(ctx->priv->uri, 0, "container", &extension); /* Since MPEG is difficult to auto-detect, we use the extension as part of the autodetection */ if(!extension) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if(strcasecmp(extension, "ps") && strcasecmp(extension, "vob") && strcasecmp(extension, "mpg") && strcasecmp(extension, "mp2") && strcasecmp(extension, "mp3") && strcasecmp(extension, "mpeg")) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if((status = ps_find_start_code(ctx, buffer)) != VC_CONTAINER_SUCCESS) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* We didn't find a valid pack or PES packet */ LOG_DEBUG(ctx, "using ps reader"); /* We are probably dealing with a PS file */ LOG_FORMAT(ctx, "MPEG PS reader, found start code: 0x%02x%02x%02x%02x", buffer[0], buffer[1], buffer[2], buffer[3]); /* Need to allocate context before searching for streams */ module = malloc(sizeof(*module)); if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); ctx->priv->module = module; ctx->tracks = module->tracks; /* Store offset so we can get back to what we consider the first pack or packet */ module->data_offset = STREAM_POSITION(ctx); /* Search for tracks, reset time reference and calculation state first */ ctx->priv->module->scr_offset = ctx->priv->module->scr = VC_CONTAINER_TIME_UNKNOWN; ctx->priv->module->searching_tracks = true; for (i = 0; i != PS_PACK_SCAN_MAX; ++i) { if (buffer[3] == 0xBA && (ps_read_pack_header(ctx) != VC_CONTAINER_SUCCESS)) goto resync; if (ps_read_pes_packet(ctx) == VC_CONTAINER_SUCCESS) continue; resync: LOG_DEBUG(ctx, "Lost sync, scanning for start code"); if((status = ps_find_start_code(ctx, buffer)) != VC_CONTAINER_SUCCESS) return VC_CONTAINER_ERROR_CORRUPTED; LOG_DEBUG(ctx, "MPEG PS reader, found start code: 0x%"PRIx64" (%"PRId64"): 0x%02x%02x%02x%02x", STREAM_POSITION(ctx), STREAM_POSITION(ctx), buffer[0], buffer[1], buffer[2], buffer[3]); } /* Seek back to the start of data */ SEEK(ctx, module->data_offset); /* Bail out if we didn't find any tracks */ if(!ctx->tracks_num) { status = VC_CONTAINER_ERROR_NO_TRACK_AVAILABLE; goto error; } /* Set data size (necessary for seeking) */ module->data_size = MAX(ctx->priv->io->size - module->data_offset, INT64_C(0)); /* Estimate data rate (necessary for seeking) */ if(STREAM_SEEKABLE(ctx)) { /* Estimate data rate by jumping in the stream */ #define PS_PACK_SEARCH_MAX 64 uint64_t position = module->data_offset; for (i = 0; i != PS_PACK_SEARCH_MAX; ++i) { position += (module->data_size / (PS_PACK_SEARCH_MAX + 1)); SEEK(ctx, position); for(;;) { if(ps_find_start_code(ctx, buffer) != VC_CONTAINER_SUCCESS) break; if (buffer[3] == 0xBA) { if (ps_read_pack_header(ctx) == VC_CONTAINER_SUCCESS) break; } else { /* Skip PES packet */ unsigned length; SKIP_U32(ctx, "PES packet startcode"); length = READ_U16(ctx, "PES packet length"); SKIP_BYTES(ctx, length); } } } ctx->duration = (INT64_C(1000000) * module->data_size) / (module->data_rate); if (module->scr > module->scr_offset) { int64_t delta = (module->scr - module->scr_offset) / INT64_C(27); if (delta > ctx->duration) ctx->duration = delta; } /* Seek back to the start of data */ SEEK(ctx, module->data_offset); } else { /* For most files, program_mux_rate is not reliable at all */ ctx->duration = (INT64_C(100000) * module->data_size) / (INT64_C(5) * module->mux_rate); } /* Reset time reference and calculation state, we're now ready to read data */ module->scr = VC_CONTAINER_TIME_UNKNOWN; module->scr_bias = VC_CONTAINER_TIME_UNKNOWN; ctx->priv->module->searching_tracks = false; if(STREAM_SEEKABLE(ctx)) ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; ctx->priv->pf_close = ps_reader_close; ctx->priv->pf_read = ps_reader_read; ctx->priv->pf_seek = ps_reader_seek; return STREAM_STATUS(ctx); error: LOG_DEBUG(ctx, "ps: error opening stream (%i)", status); if(module) ps_reader_close(ctx); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak reader_open ps_reader_open #endif userland/containers/mpga/000077500000000000000000000000001421703157200157665ustar00rootroot00000000000000userland/containers/mpga/CMakeLists.txt000066400000000000000000000005721421703157200205320ustar00rootroot00000000000000# Container module needs to go in as a plugins so different prefix # and install path set(CMAKE_SHARED_LIBRARY_PREFIX "") # Make sure the compiler can find the necessary include files include_directories (../..) add_library(reader_mpga ${LIBRARY_TYPE} mpga_reader.c) target_link_libraries(reader_mpga containers) install(TARGETS reader_mpga DESTINATION ${VMCS_PLUGIN_DIR}) userland/containers/mpga/mpga_common.h000066400000000000000000000146401421703157200204400ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #define MPGA_HEADER_SIZE 6 #define MPGA_MODE_STEREO 0 #define MPGA_MODE_JSTEREO 1 #define MPGA_MODE_DUAL 2 #define MPGA_MODE_MONO 3 /*****************************************************************************/ static VC_CONTAINER_STATUS_T mpga_read_header( uint8_t frame_header[MPGA_HEADER_SIZE], uint32_t *p_frame_size, unsigned int *p_frame_bitrate, unsigned int *p_version, unsigned int *p_layer, unsigned int *p_sample_rate, unsigned int *p_channels, unsigned int *p_frame_size_samples, unsigned int *p_offset ) { static const uint16_t mpga_bitrate[2][3][15] = {{{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448}, /* MPEG1, Layer 1 */ {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384}, /* MPEG1, Layer 2 */ {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}},/* MPEG1, Layer 3 */ {{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256}, /* MPEG2 and MPEG2.5, Layer 1 */ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, /* MPEG2 and MPEG2.5, Layer 2 */ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}} /* MPEG2 and MPEG2.5, Layer 3 */}; static const uint16_t mpga_sample_rate[] = {44100, 48000, 32000}; static const uint16_t mpga_frame_size[] = {384, 1152, 576}; unsigned int version, layer, br_id, sr_id, emphasis; unsigned int bitrate, sample_rate, padding, mode; /* Check frame sync, 11 bits as we want to allow for MPEG2.5 */ if (frame_header[0] != 0xff || (frame_header[1] & 0xe0) != 0xe0) return VC_CONTAINER_ERROR_FORMAT_INVALID; version = 4 - ((frame_header[1] >> 3) & 3); layer = 4 - ((frame_header[1] >> 1) & 3 ); br_id = (frame_header[2] >> 4) & 0xf; sr_id = (frame_header[2] >> 2) & 3; padding = (frame_header[2] >> 1) & 1; mode = (frame_header[3] >> 6) & 3; emphasis = (frame_header[3]) & 3; /* Check for invalid values */ if (version == 3 || layer == 4 || br_id == 15 || sr_id == 3 || emphasis == 2) return VC_CONTAINER_ERROR_FORMAT_INVALID; if (version == 4) version = 3; bitrate = mpga_bitrate[version == 1 ? 0 : 1][layer-1][br_id]; bitrate *= 1000; sample_rate = mpga_sample_rate[sr_id]; sample_rate >>= (version - 1); if (p_version) *p_version = version; if (p_layer) *p_layer = layer; if (p_sample_rate) *p_sample_rate = sample_rate; if (p_channels) *p_channels = mode == MPGA_MODE_MONO ? 1 : 2; if (p_frame_bitrate) *p_frame_bitrate = bitrate; if (p_offset) *p_offset = 0; if (p_frame_size_samples) { *p_frame_size_samples = mpga_frame_size[layer - 1]; if (version == 1 && layer == 3) *p_frame_size_samples <<= 1; } if (!p_frame_size) return VC_CONTAINER_SUCCESS; if (!bitrate) *p_frame_size = 0; else if (layer == 1) *p_frame_size = (padding + bitrate * 12 / sample_rate) * 4; else if (layer == 2) *p_frame_size = padding + bitrate * 144 / sample_rate; else *p_frame_size = padding + bitrate * (version == 1 ? 144 : 72) / sample_rate; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T adts_read_header( uint8_t frame_header[MPGA_HEADER_SIZE], uint32_t *p_frame_size, unsigned int *p_frame_bitrate, unsigned int *p_version, unsigned int *p_layer, unsigned int *p_sample_rate, unsigned int *p_channels, unsigned int *p_frame_size_samples, unsigned int *p_offset ) { static const unsigned int adts_sample_rate[16] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350}; unsigned int profile, sr_id, bitrate, sample_rate, frame_size, channels, crc; unsigned int frame_size_samples = 1024; /* Check frame sync (12 bits) */ if (frame_header[0] != 0xff || (frame_header[1] & 0xf0) != 0xf0) return VC_CONTAINER_ERROR_FORMAT_INVALID; /* Layer must be 0 */ if ((frame_header[1] >> 1) & 3) return VC_CONTAINER_ERROR_FORMAT_INVALID; crc = !(frame_header[1] & 0x1); profile = (frame_header[2] >> 6) + 1; /* MPEG-4 Audio Object Type */ sr_id = (frame_header[2] >> 2) & 0xf; sample_rate = adts_sample_rate[sr_id]; channels = ((frame_header[2] & 0x1) << 2) | ((frame_header[3] >> 6) & 0x3); frame_size = ((frame_header[3] & 0x03) << 11) | (frame_header[4] << 3) | (frame_header[5] >> 5); if (!sample_rate || !channels || !frame_size) return VC_CONTAINER_ERROR_FORMAT_INVALID; bitrate = frame_size * 8 * sample_rate / frame_size_samples; if (p_version) *p_version = profile; if (p_layer) *p_layer = 0; if (p_sample_rate) *p_sample_rate = sample_rate; if (p_channels) *p_channels = channels; if (p_frame_bitrate) *p_frame_bitrate = bitrate; if (p_frame_size) *p_frame_size = frame_size; if (p_frame_size_samples) *p_frame_size_samples = frame_size_samples; if (p_offset) *p_offset = crc ? 9 : 7; return VC_CONTAINER_SUCCESS; } userland/containers/mpga/mpga_packetizer.c000066400000000000000000000234751421703157200213120ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ /** \file * Implementation of an MPEG1/2/2.5 audio Layer I/II/III and AAC ADTS packetizer. */ #include #include #include "containers/packetizers.h" #include "containers/core/packetizers_private.h" #include "containers/core/containers_common.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_time.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_bytestream.h" #include "mpga_common.h" #define MAX_FRAME_SIZE 2881 /* MPEG 2.5 Layer II, 8000 Hz, 160 kbps */ VC_CONTAINER_STATUS_T mpga_packetizer_open( VC_PACKETIZER_T * ); /*****************************************************************************/ typedef struct VC_PACKETIZER_MODULE_T { enum { STATE_SYNC = 0, STATE_SYNC_LOST, STATE_SYNC_NEXT, STATE_SYNC_DONE, STATE_HEADER, STATE_DATA, } state; VC_CONTAINER_STATUS_T (*pf_read_header)( uint8_t frame_header[MPGA_HEADER_SIZE], uint32_t *p_frame_size, unsigned int *p_frame_bitrate, unsigned int *p_version, unsigned int *p_layer, unsigned int *p_sample_rate, unsigned int *p_channels, unsigned int *p_frame_size_samples, unsigned int *p_offset); uint32_t frame_size; unsigned int frame_bitrate; unsigned int version; unsigned int layer; unsigned int sample_rate; unsigned int channels; unsigned int frame_size_samples; unsigned int offset; unsigned int lost_sync; unsigned int stream_version; unsigned int stream_layer; uint32_t bytes_read; } VC_PACKETIZER_MODULE_T; /*****************************************************************************/ static VC_CONTAINER_STATUS_T mpga_packetizer_close( VC_PACKETIZER_T *p_ctx ) { free(p_ctx->priv->module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mpga_packetizer_reset( VC_PACKETIZER_T *p_ctx ) { VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; module->lost_sync = 0; module->state = STATE_SYNC; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mpga_packetizer_packetize( VC_PACKETIZER_T *p_ctx, VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags ) { VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream; VC_CONTAINER_TIME_T *time = &p_ctx->priv->time; uint8_t header[MPGA_HEADER_SIZE]; VC_CONTAINER_STATUS_T status; unsigned int version, layer; int64_t pts, dts; while(1) switch (module->state) { case STATE_SYNC_LOST: bytestream_skip_byte( stream ); if( !module->lost_sync++ ) LOG_DEBUG(0, "lost sync"); module->state = STATE_SYNC; case STATE_SYNC: while( bytestream_peek( stream, header, 2 ) == VC_CONTAINER_SUCCESS ) { /* 11 bits sync work (0xffe) */ if( header[0] == 0xff && (header[1] & 0xe0) == 0xe0 ) { module->state = STATE_HEADER; break; } bytestream_skip_byte( stream ); module->lost_sync++; } if( module->state != STATE_HEADER ) return VC_CONTAINER_ERROR_INCOMPLETE_DATA; /* We need more data */ case STATE_HEADER: if( bytestream_peek( stream, header, MPGA_HEADER_SIZE ) != VC_CONTAINER_SUCCESS ) return VC_CONTAINER_ERROR_INCOMPLETE_DATA; status = mpga_read_header( header, &module->frame_size, &module->frame_bitrate, &module->version, &module->layer, &module->sample_rate, &module->channels, &module->frame_size_samples, &module->offset ); if (status != VC_CONTAINER_SUCCESS) { LOG_ERROR(0, "invalid header"); module->state = STATE_SYNC_LOST; break; } /* Version and layer are not allowed to change mid-stream */ if ((module->stream_version && module->stream_version != module->version) || (module->stream_layer && module->stream_layer != module->layer)) { LOG_ERROR(0, "invalid header"); module->state = STATE_SYNC_LOST; break; } /* We currently do not support free format streams */ if (!module->frame_size) { LOG_ERROR(0, "free format not supported"); module->state = STATE_SYNC_LOST; break; } module->state = STATE_SYNC_NEXT; /* fall through to the next state */ case STATE_SYNC_NEXT: /* To avoid being caught by emulated start codes, we also look at where the next frame is supposed to be */ if( bytestream_peek_at( stream, module->frame_size, header, MPGA_HEADER_SIZE ) != VC_CONTAINER_SUCCESS ) { /* If we know there won't be anymore data then we can just assume * we've got the frame we're looking for */ if (flags & VC_PACKETIZER_FLAG_FLUSH) { module->state = STATE_SYNC_DONE; break; } return VC_CONTAINER_ERROR_INCOMPLETE_DATA; } status = mpga_read_header( header, 0, 0, &version, &layer, 0, 0, 0, 0 ); if (status != VC_CONTAINER_SUCCESS) { LOG_ERROR(0, "invalid next header"); module->state = STATE_SYNC_LOST; break; } /* Version and layer are not allowed to change mid-stream */ if (module->version != version || module->layer != layer) { LOG_ERROR(0, "invalid header"); module->state = STATE_SYNC_LOST; break; } module->state = STATE_SYNC_DONE; /* fall through to the next state */ case STATE_SYNC_DONE: if( module->lost_sync ) LOG_DEBUG(0, "recovered sync after %i bytes", module->lost_sync); module->lost_sync = 0; bytestream_skip( stream, module->offset ); module->stream_version = module->version; module->stream_layer = module->layer; vc_container_time_set_samplerate(time, module->sample_rate, 1); bytestream_get_timestamps(stream, &pts, &dts, true); vc_container_time_set(time, pts); module->bytes_read = 0; module->state = STATE_DATA; /* fall through to the next state */ case STATE_DATA: if( bytestream_size( stream ) < module->frame_size) return VC_CONTAINER_ERROR_INCOMPLETE_DATA; out->size = module->frame_size - module->bytes_read; out->pts = out->dts = VC_CONTAINER_TIME_UNKNOWN; out->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END; if(!module->bytes_read) { out->pts = out->dts = vc_container_time_get(time); out->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; } if(flags & VC_PACKETIZER_FLAG_INFO) return VC_CONTAINER_SUCCESS; if(flags & VC_PACKETIZER_FLAG_SKIP) { bytestream_skip( stream, out->size ); } else { out->size = MIN(out->size, out->buffer_size); bytestream_get( stream, out->data, out->size ); } module->bytes_read += out->size; if(module->bytes_read == module->frame_size) { vc_container_time_add(time, module->frame_size_samples); module->state = STATE_HEADER; } return VC_CONTAINER_SUCCESS; default: break; }; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T mpga_packetizer_open( VC_PACKETIZER_T *p_ctx ) { VC_PACKETIZER_MODULE_T *module; if(p_ctx->in->codec != VC_CONTAINER_CODEC_MPGA && p_ctx->in->codec != VC_CONTAINER_CODEC_MP4A) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; p_ctx->priv->module = module = malloc(sizeof(*module)); if(!module) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; memset(module, 0, sizeof(*module)); if(p_ctx->in->codec == VC_CONTAINER_CODEC_MPGA) module->pf_read_header = mpga_read_header; else module->pf_read_header = adts_read_header; vc_container_format_copy( p_ctx->out, p_ctx->in, 0); p_ctx->max_frame_size = MAX_FRAME_SIZE; p_ctx->priv->pf_close = mpga_packetizer_close; p_ctx->priv->pf_packetize = mpga_packetizer_packetize; p_ctx->priv->pf_reset = mpga_packetizer_reset; LOG_DEBUG(0, "using mpeg audio packetizer"); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_PACKETIZER_REGISTER(mpga_packetizer_open, "mpga"); userland/containers/mpga/mpga_reader.c000066400000000000000000000561451421703157200204130ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #define CONTAINER_IS_BIG_ENDIAN //#define ENABLE_CONTAINERS_LOG_FORMAT //#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE #define CONTAINER_HELPER_LOG_INDENT(a) 0 #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_logging.h" #include "mpga_common.h" /****************************************************************************** Defines and constants. ******************************************************************************/ #define MPGA_XING_HAS_FRAMES 0x00000001 #define MPGA_XING_HAS_BYTES 0x00000002 #define MPGA_XING_HAS_TOC 0x00000004 #define MPGA_XING_HAS_QUALITY 0x00000008 #define MPGA_MAX_BAD_FRAMES 4096 /*< Maximum number of failed byte-wise syncs, should be at least 2881+4 to cover the largest frame size (MPEG2.5 Layer 2, 160kbit/s 8kHz) + next frame header */ static const unsigned int mpga_sample_rate_adts[16] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350}; static const GUID_T asf_guid_header = {0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; /****************************************************************************** Type definitions ******************************************************************************/ typedef struct VC_CONTAINER_MODULE_T { VC_CONTAINER_TRACK_T *track; uint64_t data_offset; uint64_t data_size; uint64_t num_frames; /**< Total number of frames (if known) */ unsigned int frame_size_samples; /**< Frame size in samples */ unsigned int bitrate; /**< Bitrate (might change on a per-frame basis if VBR) */ unsigned int sample_rate; unsigned int channels; /* MPEG audio header information */ unsigned int version; /**< 1 for MPEG1, 2 for MPEG2, etc. */ unsigned int layer; /* VBR header information */ uint8_t xing_toc[100]; int xing_toc_valid; /* Per-frame state (updated upon a read or a seek) */ unsigned int frame_size; unsigned int frame_data_left; uint64_t frame_index; int64_t frame_offset; int64_t frame_time_pos; /**< pts of current frame */ unsigned int frame_bitrate; /**< bitrate of current frame */ VC_CONTAINER_STATUS_T (*pf_parse_header)( uint8_t frame_header[MPGA_HEADER_SIZE], uint32_t *p_frame_size, unsigned int *p_frame_bitrate, unsigned int *p_version, unsigned int *p_layer, unsigned int *p_sample_rate, unsigned int *p_channels, unsigned int *p_frame_size_samples, unsigned int *p_offset); uint8_t extradata[2]; /**< codec extra data for aac */ } VC_CONTAINER_MODULE_T; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T mpga_reader_open( VC_CONTAINER_T * ); /****************************************************************************** Local Functions ******************************************************************************/ static uint32_t PEEK_BYTES_AT( VC_CONTAINER_T *p_ctx, int64_t offset, uint8_t *buffer, int size ) { int ret; int64_t current_position = STREAM_POSITION(p_ctx); SEEK(p_ctx, current_position + offset); ret = PEEK_BYTES(p_ctx, buffer, size); SEEK(p_ctx, current_position); return ret; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mpga_check_frame_header( VC_CONTAINER_T *p_ctx, VC_CONTAINER_MODULE_T *module, uint8_t frame_header[MPGA_HEADER_SIZE] ) { VC_CONTAINER_PARAM_UNUSED(p_ctx); return module->pf_parse_header(frame_header, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mpga_sync( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status; uint8_t frame_header[MPGA_HEADER_SIZE]; uint32_t frame_size; unsigned int frame_bitrate, version, layer, sample_rate, channels; unsigned int frame_size_samples, offset; int sync_count = 0; /* If we can't see a full frame header, we treat this as EOS although it could be a bad stream as well, the caller should distinct between these two cases */ if (PEEK_BYTES(p_ctx, (uint8_t*)frame_header, MPGA_HEADER_SIZE) != MPGA_HEADER_SIZE) return VC_CONTAINER_ERROR_EOS; while (sync_count++ < MPGA_MAX_BAD_FRAMES) { status = module->pf_parse_header(frame_header, &frame_size, &frame_bitrate, &version, &layer, &sample_rate, &channels, &frame_size_samples, &offset); if (status == VC_CONTAINER_SUCCESS && frame_size /* We do not support free format streams */) { LOG_DEBUG(p_ctx, "MPEGv%d, layer %d, %d bps, %d Hz", version, layer, frame_bitrate, sample_rate); if (PEEK_BYTES_AT(p_ctx, (int64_t)frame_size, frame_header, MPGA_HEADER_SIZE) != MPGA_HEADER_SIZE || mpga_check_frame_header(p_ctx, module, frame_header) == VC_CONTAINER_SUCCESS) break; /* If we've reached an ID3 tag then the frame is valid as well */ if((frame_header[0] == 'I' && frame_header[1] == 'D' && frame_header[2] == '3') || (frame_header[0] == 'T' && frame_header[1] == 'A' && frame_header[2] == 'G')) break; } else if (status == VC_CONTAINER_SUCCESS) { LOG_DEBUG(p_ctx, "free format not supported"); } if (SKIP_BYTES(p_ctx, 1) != 1 || PEEK_BYTES(p_ctx, (uint8_t*)frame_header, MPGA_HEADER_SIZE) != MPGA_HEADER_SIZE) return VC_CONTAINER_ERROR_EOS; } if(sync_count > MPGA_MAX_BAD_FRAMES) /* We didn't find a valid frame */ return VC_CONTAINER_ERROR_FORMAT_INVALID; if (module->version) { /* FIXME: we don't currently care whether or not the number of channels changes mid-stream */ if (version != module->version || layer != module->layer) { LOG_DEBUG(p_ctx, "version or layer not allowed to change mid-stream"); return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; } } else { module->version = version; module->layer = layer; module->sample_rate = sample_rate; module->channels = channels; module->frame_size_samples = frame_size_samples; } if(offset) SKIP_BYTES(p_ctx, offset); module->frame_data_left = module->frame_size = frame_size - offset; module->frame_bitrate = frame_bitrate; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static int64_t mpga_calculate_frame_time( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; int64_t time; time = INT64_C(1000000) * module->frame_index * module->frame_size_samples / module->sample_rate; return time; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mpga_read_vbr_headers( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[0]; VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND; uint32_t peek_buf[1]; int64_t offset, start = STREAM_POSITION(p_ctx); /* Look for XING header (immediately after layer 3 side information) */ offset = (module->version == 1) ? ((module->channels == 1) ? INT64_C(21) : INT64_C(36)) : ((module->channels == 1) ? INT64_C(13) : INT64_C(21)); if (PEEK_BYTES_AT(p_ctx, offset, (uint8_t*)peek_buf, 4) != 4) return VC_CONTAINER_ERROR_FORMAT_INVALID; /* File would be way too small */ if (peek_buf[0] == VC_FOURCC('X','i','n','g') || peek_buf[0] == VC_FOURCC('I','n','f','o')) { uint32_t flags = 0, num_frames = 0, data_size = 0; /* If the first frame has a XING header then we know it's a valid (but empty) audio frame so we safely parse the header whilst skipping to the next frame */ SKIP_BYTES(p_ctx, offset); /* FIXME: we don't care about layer 3 side information? */ SKIP_FOURCC(p_ctx, "XING"); flags = READ_U32(p_ctx, "XING flags"); if (flags & MPGA_XING_HAS_FRAMES) num_frames = READ_U32(p_ctx, "XING frames"); if (flags & MPGA_XING_HAS_BYTES) data_size = READ_U32(p_ctx, "XING bytes"); if (flags & MPGA_XING_HAS_TOC) { READ_BYTES(p_ctx, module->xing_toc, sizeof(module->xing_toc)); /* TOC is useful only if we know the number of frames */ if (num_frames) module->xing_toc_valid = 1; /* Ensure time zero points to first frame even if TOC is broken */ module->xing_toc[0] = 0; } if (flags & MPGA_XING_HAS_QUALITY) SKIP_U32(p_ctx, "XING quality"); module->data_size = data_size; module->num_frames = num_frames; if (module->num_frames && module->data_size) { /* We can calculate average bitrate */ module->bitrate = module->data_size * module->sample_rate * 8 / (module->num_frames * module->frame_size_samples); } p_ctx->duration = (module->num_frames * module->frame_size_samples * 1000000LL) / module->sample_rate; /* Look for additional LAME header (follows XING) */ if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4) return VC_CONTAINER_ERROR_FORMAT_INVALID; /* File would still be way too small */ if (peek_buf[0] == VC_FOURCC('L','A','M','E')) { uint32_t encoder_delay; SKIP_FOURCC(p_ctx, "LAME"); SKIP_STRING(p_ctx, 5, "LAME encoder version"); SKIP_U8(p_ctx, "LAME tag revision/VBR method"); SKIP_U8(p_ctx, "LAME LP filter value"); SKIP_U32(p_ctx, "LAME peak signal amplitude"); SKIP_U16(p_ctx, "LAME radio replay gain"); SKIP_U16(p_ctx, "LAME audiophile replay gain"); SKIP_U8(p_ctx, "LAME encoder flags"); SKIP_U8(p_ctx, "LAME ABR/minimal bitrate"); encoder_delay = READ_U24(p_ctx, "LAME encoder delay/padding"); SKIP_U8(p_ctx, "LAME misc"); SKIP_U8(p_ctx, "LAME MP3 gain"); SKIP_U16(p_ctx, "LAME presets and surround info"); SKIP_U32(p_ctx, "LAME music length"); SKIP_U16(p_ctx, "LAME music CRC"); SKIP_U16(p_ctx, "LAME tag CRC"); track->format->type->audio.gap_delay = (encoder_delay >> 12) + module->frame_size_samples; track->format->type->audio.gap_padding = encoder_delay & 0xfff; } SEEK(p_ctx, start); status = VC_CONTAINER_SUCCESS; } /* FIXME: if not success, try to read 'VBRI' header */ return status; } /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ static VC_CONTAINER_STATUS_T mpga_reader_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_T *track = p_ctx->tracks[0]; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; if (module->frame_data_left == 0) { status = mpga_sync(p_ctx); if (status != VC_CONTAINER_SUCCESS) goto error; } if (module->bitrate) { /* Simple moving average over bitrate values seen so far */ module->bitrate = (module->bitrate * 31 + module->frame_bitrate) >> 5; } else { module->bitrate = module->frame_bitrate; } /* Check if we can skip the frame straight-away */ if (!track->is_enabled || ((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO))) { /* Just skip the frame */ SKIP_BYTES(p_ctx, module->frame_size); module->frame_data_left = 0; if(!track->is_enabled) status = VC_CONTAINER_ERROR_CONTINUE; goto end; } /* Fill in packet information */ p_packet->flags = p_packet->track = 0; if (module->frame_data_left == module->frame_size) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME; else p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; p_packet->size = module->frame_data_left; p_packet->pts = module->frame_time_pos; p_packet->dts = VC_CONTAINER_TIME_UNKNOWN; if ((flags & VC_CONTAINER_READ_FLAG_SKIP)) { SKIP_BYTES(p_ctx, module->frame_size); module->frame_data_left = 0; goto end; } if (flags & VC_CONTAINER_READ_FLAG_INFO) return VC_CONTAINER_SUCCESS; p_packet->size = MIN(p_packet->buffer_size, module->frame_data_left); p_packet->size = READ_BYTES(p_ctx, p_packet->data, p_packet->size); module->frame_data_left -= p_packet->size; end: if (module->frame_data_left == 0) { module->frame_index++; module->frame_offset += module->frame_size; module->frame_time_pos = mpga_calculate_frame_time(p_ctx); #if 0 /* FIXME: is this useful e.g. progressive download? */ module->num_frames = MAX(module->num_frames, module->frame_index); module->data_size = MAX(module->data_size, module->frame_offset); p_ctx->duration = MAX(p_ctx->duration, mpga_calculate_frame_time(p_ctx)); #endif } return status == VC_CONTAINER_SUCCESS ? STREAM_STATUS(p_ctx) : status; error: return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mpga_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *p_offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; uint64_t seekpos, position = STREAM_POSITION(p_ctx); VC_CONTAINER_PARAM_UNUSED(flags); if (mode != VC_CONTAINER_SEEK_MODE_TIME || !STREAM_SEEKABLE(p_ctx)) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; if (*p_offset != INT64_C(0)) { if (!p_ctx->duration) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; if (module->xing_toc_valid) { int64_t ppm; int percent, lower, upper, delta; ppm = (*p_offset * module->sample_rate) / (module->num_frames * module->frame_size_samples); ppm = MIN(ppm, INT64_C(999999)); percent = ppm / 10000; delta = ppm % 10000; lower = module->xing_toc[percent]; upper = percent < 99 ? module->xing_toc[percent + 1] : 256; seekpos = module->data_offset + (((module->data_size * lower) + (module->data_size * (upper - lower) * delta) / 10000) >> 8); } else { /* The following will be accurate for CBR only */ seekpos = module->data_offset + (*p_offset * module->data_size) / p_ctx->duration; } } else { seekpos = module->data_offset; } SEEK(p_ctx, seekpos); status = mpga_sync(p_ctx); if (status && status != VC_CONTAINER_ERROR_EOS) goto error; module->frame_index = (*p_offset * module->num_frames + (p_ctx->duration >> 1)) / p_ctx->duration; module->frame_offset = STREAM_POSITION(p_ctx) - module->data_offset; *p_offset = module->frame_time_pos = mpga_calculate_frame_time(p_ctx); return STREAM_STATUS(p_ctx); error: SEEK(p_ctx, position); return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mpga_reader_close( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; if (p_ctx->tracks_num != 0) vc_container_free_track(p_ctx, p_ctx->tracks[0]); p_ctx->tracks = NULL; p_ctx->tracks_num = 0; free(module); p_ctx->priv->module = 0; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T mpga_reader_open( VC_CONTAINER_T *p_ctx ) { const char *extension = vc_uri_path_extension(p_ctx->priv->uri); VC_CONTAINER_MODULE_T *module = 0; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_TRACK_T *track = NULL; unsigned int i; GUID_T guid; /* Check if the user has specified a container */ vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); /* Since mpeg audio is difficult to auto-detect, we use the extension as part of the autodetection */ if(!extension) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if(strcasecmp(extension, "mp3") && strcasecmp(extension, "mp2") && strcasecmp(extension, "aac") && strcasecmp(extension, "adts")) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Check we're not in fact dealing with an ASF file */ if(PEEK_BYTES(p_ctx, (uint8_t *)&guid, sizeof(guid)) == sizeof(guid) && !memcmp(&guid, &asf_guid_header, sizeof(guid))) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; LOG_DEBUG(p_ctx, "using mpga reader"); /* Allocate our context */ if ((module = malloc(sizeof(*module))) == NULL) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); p_ctx->priv->module = module; p_ctx->tracks = &module->track; p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0); if(!p_ctx->tracks[0]) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } p_ctx->tracks_num = 1; module->pf_parse_header = mpga_read_header; if(!strcasecmp(extension, "aac") || !strcasecmp(extension, "adts")) module->pf_parse_header = adts_read_header; if ((status = mpga_sync(p_ctx)) != VC_CONTAINER_SUCCESS) { /* An error here probably means it's not an mpga file at all */ if(status == VC_CONTAINER_ERROR_FORMAT_INVALID) status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; goto error; } /* If we got this far, we're probably dealing with an mpeg audio file */ track = p_ctx->tracks[0]; track->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; track->format->codec = VC_CONTAINER_CODEC_MPGA; if(module->pf_parse_header == adts_read_header) { uint8_t *extra = track->format->extradata = module->extradata; unsigned int sr_id; for( sr_id = 0; sr_id < 13; sr_id++ ) if( mpga_sample_rate_adts[sr_id] == module->sample_rate ) break; extra[0] = (module->version << 3) | ((sr_id & 0xe) >> 1); extra[1] = ((sr_id & 0x1) << 7) | (module->channels << 3); track->format->extradata_size = 2; track->format->codec = VC_CONTAINER_CODEC_MP4A; } track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; track->is_enabled = true; track->format->type->audio.channels = module->channels; track->format->type->audio.sample_rate = module->sample_rate; track->format->type->audio.bits_per_sample = 0; track->format->type->audio.block_align = 1; module->data_offset = STREAM_POSITION(p_ctx); /* Look for VBR headers within the first frame */ status = mpga_read_vbr_headers(p_ctx); if (status && status != VC_CONTAINER_ERROR_NOT_FOUND) goto error; /* If we couldn't get this information from VBR headers, try to determine file size, bitrate, number of frames and duration */ if (!module->data_size) module->data_size = MAX(p_ctx->priv->io->size - module->data_offset, INT64_C(0)); if (!module->bitrate) { if (STREAM_SEEKABLE(p_ctx)) { /* Scan past a few hundred frames (audio will often have silence in the beginning so we need to see more than just a few frames) and estimate bitrate */ for (i = 0; i < 256; ++i) if (mpga_reader_read(p_ctx, NULL, VC_CONTAINER_READ_FLAG_SKIP)) break; /* Seek back to start of data */ SEEK(p_ctx, module->data_offset); module->frame_index = 0; module->frame_offset = INT64_C(0); module->frame_time_pos = mpga_calculate_frame_time(p_ctx); } else { /* Bitrate will be correct for CBR only */ module->bitrate = module->frame_bitrate; } } track->format->bitrate = module->bitrate; if (!module->num_frames) { module->num_frames = (module->data_size * module->sample_rate * 8LL) / (module->bitrate * module->frame_size_samples); } if (!p_ctx->duration && module->bitrate) { p_ctx->duration = (INT64_C(8000000) * module->data_size) / module->bitrate; } p_ctx->priv->pf_close = mpga_reader_close; p_ctx->priv->pf_read = mpga_reader_read; p_ctx->priv->pf_seek = mpga_reader_seek; if(STREAM_SEEKABLE(p_ctx)) p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) goto error; return VC_CONTAINER_SUCCESS; error: if(status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_EOS) status = VC_CONTAINER_ERROR_FORMAT_INVALID; LOG_DEBUG(p_ctx, "error opening stream (%i)", status); if (p_ctx->tracks_num != 0) vc_container_free_track(p_ctx, p_ctx->tracks[0]); p_ctx->tracks = NULL; p_ctx->tracks_num = 0; if (module) free(module); p_ctx->priv->module = NULL; return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak reader_open mpga_reader_open #endif userland/containers/mpgv/000077500000000000000000000000001421703157200160135ustar00rootroot00000000000000userland/containers/mpgv/mpgv_packetizer.c000066400000000000000000000356011421703157200213560ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ /** \file * Implementation of an MPEG1/2 video packetizer. */ #include #include #include "containers/packetizers.h" #include "containers/core/packetizers_private.h" #include "containers/core/containers_common.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_time.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_bytestream.h" /** Arbitrary number which should be sufficiently high so that no sane frame will * be bigger than that. */ #define MAX_FRAME_SIZE (1920*1088*2) static uint8_t mpgv_startcode[3] = {0x0, 0x0, 0x1}; #define PICTURE_CODING_TYPE_I 0x1 #define PICTURE_CODING_TYPE_P 0x2 #define PICTURE_CODING_TYPE_B 0x3 VC_CONTAINER_STATUS_T mpgv_packetizer_open( VC_PACKETIZER_T * ); /*****************************************************************************/ typedef struct VC_PACKETIZER_MODULE_T { enum { STATE_SYNC = 0, STATE_SYNC_NEXT, STATE_FRAME_DONE, STATE_UNIT_HEADER, STATE_UNIT_SEQUENCE, STATE_UNIT_GROUP, STATE_UNIT_PICTURE, STATE_UNIT_SLICE, STATE_UNIT_OTHER, STATE_DATA, } state; size_t frame_size; size_t unit_offset; unsigned int seen_sequence_header; unsigned int seen_picture_header; unsigned int seen_slice; unsigned int lost_sync; unsigned int picture_type; unsigned int picture_temporal_ref; int64_t pts; int64_t dts; uint32_t bytes_read; unsigned int width, height; unsigned int frame_rate_num, frame_rate_den; unsigned int aspect_ratio_num, aspect_ratio_den; bool low_delay; } VC_PACKETIZER_MODULE_T; /*****************************************************************************/ static VC_CONTAINER_STATUS_T mpgv_packetizer_close( VC_PACKETIZER_T *p_ctx ) { free(p_ctx->priv->module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mpgv_packetizer_reset( VC_PACKETIZER_T *p_ctx ) { VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; module->lost_sync = 0; module->state = STATE_SYNC; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mpgv_read_sequence_header(VC_CONTAINER_BYTESTREAM_T *stream, size_t offset, unsigned int *width, unsigned int *height, unsigned int *frame_rate_num, unsigned int *frame_rate_den, unsigned int *aspect_ratio_num, unsigned int *aspect_ratio_den) { static const int frame_rate[16][2] = { {0, 0}, {24000, 1001}, {24, 1}, {25, 1}, {30000, 1001}, {30, 1}, {50, 1}, {60000, 1001}, {60, 1}, /* Unofficial values */ {15, 1001}, /* From Xing */ {5, 1001}, {10, 1001}, {12, 1001}, {15, 1001} /* From libmpeg3 */ }; static const int aspect_ratio[16][2] = { {0, 0}, {1, 1}, {4, 3}, {16, 9}, {221, 100} }; VC_CONTAINER_STATUS_T status; unsigned int w, h, fr, ar; int64_t ar_num, ar_den, div; uint8_t header[8]; status = bytestream_peek_at( stream, offset, header, sizeof(header)); if(status != VC_CONTAINER_SUCCESS) return status; w = (header[4] << 4) | (header[5] >> 4); h = ((header[5]&0x0f) << 8) | header[6]; ar = header[7] >> 4; fr = header[7]&0x0f; if (!w || !h || !ar || !fr) return VC_CONTAINER_ERROR_CORRUPTED; *width = w; *height = h; *frame_rate_num = frame_rate[fr][0]; *frame_rate_den = frame_rate[fr][1]; ar_num = (int64_t)aspect_ratio[ar][0] * h; ar_den = (int64_t)aspect_ratio[ar][1] * w; div = vc_container_maths_gcd(ar_num, ar_den); if (div) { ar_num /= div; ar_den /= div; } *aspect_ratio_num = ar_num; *aspect_ratio_den = ar_den; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mpgv_read_picture_header(VC_CONTAINER_BYTESTREAM_T *stream, size_t offset, unsigned int *type, unsigned int *temporal_ref) { VC_CONTAINER_STATUS_T status; uint8_t h[2]; status = bytestream_peek_at(stream, offset + sizeof(mpgv_startcode) + 1, h, sizeof(h)); if(status != VC_CONTAINER_SUCCESS) return status; *temporal_ref = (h[0] << 2) | (h[1] >> 6); *type = (h[1] >> 3) & 0x7; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mpgv_update_format( VC_PACKETIZER_T *p_ctx ) { VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; LOG_DEBUG(0, "mpgv format: width %i, height %i, rate %i/%i, ar %i/%i", module->width, module->height, module->frame_rate_num, module->frame_rate_den, module->aspect_ratio_num, module->aspect_ratio_den); p_ctx->out->type->video.width = p_ctx->out->type->video.visible_width = module->width; p_ctx->out->type->video.height = p_ctx->out->type->video.visible_height = module->height; p_ctx->out->type->video.par_num = module->aspect_ratio_num; p_ctx->out->type->video.par_den = module->aspect_ratio_den; p_ctx->out->type->video.frame_rate_num = module->frame_rate_num; p_ctx->out->type->video.frame_rate_den = module->frame_rate_den; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T mpgv_packetizer_packetize( VC_PACKETIZER_T *p_ctx, VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags) { VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream; VC_CONTAINER_TIME_T *time = &p_ctx->priv->time; VC_CONTAINER_STATUS_T status; uint8_t header[4]; size_t offset; while(1) switch (module->state) { case STATE_SYNC: offset = 0; status = bytestream_find_startcode( stream, &offset, mpgv_startcode, sizeof(mpgv_startcode) ); if(offset && !module->lost_sync) LOG_DEBUG(0, "lost sync"); bytestream_skip(stream, offset); module->lost_sync += offset; if(status != VC_CONTAINER_SUCCESS) return VC_CONTAINER_ERROR_INCOMPLETE_DATA; /* We need more data */ if(module->lost_sync) LOG_DEBUG(0, "recovered sync after %i bytes", module->lost_sync); module->lost_sync = 0; module->state = STATE_UNIT_HEADER; module->frame_size = 0; module->unit_offset = 0; /* fall through to the next state */ case STATE_UNIT_HEADER: status = bytestream_peek_at( stream, module->unit_offset, header, sizeof(header)); if(status != VC_CONTAINER_SUCCESS) { if (!(flags & VC_PACKETIZER_FLAG_FLUSH) || !module->seen_picture_header || !module->seen_slice) return VC_CONTAINER_ERROR_INCOMPLETE_DATA; module->state = STATE_FRAME_DONE; break; } #if defined(ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE) LOG_DEBUG(0, "found unit (%x)", header[3]); #endif /* Detect start of new frame */ if(module->seen_picture_header && module->seen_slice && (header[3] == 0x00 /* A picture header */ || (header[3] > 0xAF && header[3] != 0xB7) /* Not a slice or sequence end */)) { module->state = STATE_FRAME_DONE; break; } module->frame_size += sizeof(mpgv_startcode); module->state = STATE_SYNC_NEXT; /* fall through to the next state */ case STATE_SYNC_NEXT: status = bytestream_find_startcode( stream, &module->frame_size, mpgv_startcode, sizeof(mpgv_startcode) ); /* Sanity check the size of frames. This makes sure we don't endlessly accumulate data * to make up a new frame. */ if(module->frame_size > p_ctx->max_frame_size) { LOG_ERROR(0, "frame too big (%i/%i), dropping", module->frame_size, p_ctx->max_frame_size); bytestream_skip(stream, module->frame_size); module->state = STATE_SYNC; break; } if(status != VC_CONTAINER_SUCCESS) { if (!(flags & VC_PACKETIZER_FLAG_FLUSH) || !module->seen_picture_header || !module->seen_slice) return VC_CONTAINER_ERROR_INCOMPLETE_DATA; module->state = STATE_FRAME_DONE; break; } bytestream_peek_at( stream, module->unit_offset, header, sizeof(header)); /* Drop everything until we've seen a sequence header */ if(header[3] != 0xB3 && !module->seen_sequence_header) { LOG_DEBUG(0, "waiting for sequence header, dropping %i bytes", module->frame_size); module->state = STATE_UNIT_HEADER; bytestream_skip(stream, module->frame_size); module->unit_offset = module->frame_size = 0; break; } if(header[3] == 0x00) module->state = STATE_UNIT_PICTURE; else if(header[3] >= 0x01 && header[3] <= 0xAF) module->state = STATE_UNIT_SLICE; else if(header[3] == 0xB3) module->state = STATE_UNIT_SEQUENCE; else if(header[3] == 0xB8) module->state = STATE_UNIT_GROUP; else module->state = STATE_UNIT_OTHER; break; case STATE_UNIT_SEQUENCE: status = mpgv_read_sequence_header(stream, module->unit_offset, &module->width, &module->height, &module->frame_rate_num, &module->frame_rate_den, &module->aspect_ratio_num, &module->aspect_ratio_den); if(status != VC_CONTAINER_SUCCESS && !module->seen_sequence_header) { /* We need a sequence header so drop everything until we see one */ LOG_DEBUG(0, "invalid first sequence header, dropping %i bytes", module->frame_size); bytestream_skip(stream, module->frame_size); module->state = STATE_SYNC; break; } mpgv_update_format(p_ctx); module->seen_sequence_header = true; vc_container_time_set_samplerate(time, module->frame_rate_num, module->frame_rate_den); module->state = STATE_UNIT_HEADER; module->unit_offset = module->frame_size; break; case STATE_UNIT_PICTURE: status = mpgv_read_picture_header(stream, module->unit_offset, &module->picture_type, &module->picture_temporal_ref); if(status != VC_CONTAINER_SUCCESS) return VC_CONTAINER_ERROR_INCOMPLETE_DATA; module->seen_picture_header = true; module->state = STATE_UNIT_HEADER; module->unit_offset = module->frame_size; break; case STATE_UNIT_SLICE: module->seen_slice = true; module->state = STATE_UNIT_HEADER; module->unit_offset = module->frame_size; break; case STATE_UNIT_GROUP: case STATE_UNIT_OTHER: module->state = STATE_UNIT_HEADER; module->unit_offset = module->frame_size; break; case STATE_FRAME_DONE: bytestream_get_timestamps(stream, &module->pts, &module->dts, false); if(module->picture_type == PICTURE_CODING_TYPE_B || module->low_delay) { if(module->pts == VC_CONTAINER_TIME_UNKNOWN) module->pts = module->dts; if(module->dts == VC_CONTAINER_TIME_UNKNOWN) module->dts = module->pts; } vc_container_time_set(time, module->pts); module->bytes_read = 0; module->state = STATE_DATA; module->seen_slice = false; module->seen_picture_header = false; #if defined(ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE) LOG_DEBUG(0, "new frame, type %x, size %i, temp_ref %i)", module->picture_type, module->frame_size, module->picture_temporal_ref); #endif /* fall through to the next state */ case STATE_DATA: out->size = module->frame_size - module->bytes_read; out->pts = out->dts = VC_CONTAINER_TIME_UNKNOWN; out->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END; if(!module->bytes_read) { out->pts = module->pts; out->dts = module->dts; out->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; } if(flags & VC_PACKETIZER_FLAG_INFO) return VC_CONTAINER_SUCCESS; if(flags & VC_PACKETIZER_FLAG_SKIP) { bytestream_skip( stream, out->size ); } else { out->size = MIN(out->size, out->buffer_size); bytestream_get( stream, out->data, out->size ); } module->bytes_read += out->size; if(module->bytes_read == module->frame_size) { vc_container_time_add(time, 1); module->state = STATE_UNIT_HEADER; module->frame_size = 0; module->unit_offset = 0; } else out->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; return VC_CONTAINER_SUCCESS; default: break; }; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T mpgv_packetizer_open( VC_PACKETIZER_T *p_ctx ) { VC_PACKETIZER_MODULE_T *module; if(p_ctx->in->codec != VC_CONTAINER_CODEC_MP1V && p_ctx->in->codec != VC_CONTAINER_CODEC_MP2V) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; p_ctx->priv->module = module = malloc(sizeof(*module)); if(!module) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; memset(module, 0, sizeof(*module)); vc_container_format_copy( p_ctx->out, p_ctx->in, 0); p_ctx->max_frame_size = MAX_FRAME_SIZE; p_ctx->priv->pf_close = mpgv_packetizer_close; p_ctx->priv->pf_packetize = mpgv_packetizer_packetize; p_ctx->priv->pf_reset = mpgv_packetizer_reset; LOG_DEBUG(0, "using mpeg video packetizer"); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_PACKETIZER_REGISTER(mpgv_packetizer_open, "mpgv"); userland/containers/net/000077500000000000000000000000001421703157200156305ustar00rootroot00000000000000userland/containers/net/net_sockets.h000066400000000000000000000312521421703157200203250ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_NET_SOCKETS_H #define VC_NET_SOCKETS_H /** \file net_sockets.h * Abstraction layer for socket-style network communication, to enable porting * between platforms. * * Does not support IPv6 multicast. */ #include "containers/containers_types.h" #ifdef __cplusplus extern "C" { #endif /** Status codes that can occur in a socket instance. */ typedef enum { VC_CONTAINER_NET_SUCCESS = 0, /**< No error */ VC_CONTAINER_NET_ERROR_GENERAL, /**< An unrecognised error has occurred */ VC_CONTAINER_NET_ERROR_INVALID_SOCKET, /**< Invalid socket passed to function */ VC_CONTAINER_NET_ERROR_NOT_ALLOWED, /**< The operation requested is not allowed */ VC_CONTAINER_NET_ERROR_INVALID_PARAMETER, /**< An invalid parameter was passed in */ VC_CONTAINER_NET_ERROR_NO_MEMORY, /**< Failure due to lack of memory */ VC_CONTAINER_NET_ERROR_ACCESS_DENIED, /**< Permission denied */ VC_CONTAINER_NET_ERROR_TOO_BIG, /**< Too many handles already open */ VC_CONTAINER_NET_ERROR_WOULD_BLOCK, /**< Asynchronous operation would block */ VC_CONTAINER_NET_ERROR_IN_PROGRESS, /**< An operation is already in progress on this socket */ VC_CONTAINER_NET_ERROR_IN_USE, /**< The address/port is already in use */ VC_CONTAINER_NET_ERROR_NETWORK, /**< Network is unavailable */ VC_CONTAINER_NET_ERROR_CONNECTION_LOST, /**< The connection has been lost, closed by network, etc. */ VC_CONTAINER_NET_ERROR_NOT_CONNECTED, /**< The socket is not connected */ VC_CONTAINER_NET_ERROR_TIMED_OUT, /**< Operation timed out */ VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED, /**< Connection was refused by target */ VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND, /**< Target address could not be resolved */ VC_CONTAINER_NET_ERROR_TRY_AGAIN, /**< A temporary failure occurred that may clear */ } vc_container_net_status_t; /** Operations that can be applied to sockets */ typedef enum { /** Set the buffer size used on the socket * arg1: uint32_t - New buffer size in bytes */ VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE = 1, /** Set the timeout to be used on read operations * arg1: uint32_t - New timeout in milliseconds, or INFINITE_TIMEOUT_MS */ VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS, } vc_container_net_control_t; /** Container Input / Output Context. * This is an opaque structure that defines the context for a socket instance. * The details of the structure are contained within the platform implementation. */ typedef struct vc_container_net_tag VC_CONTAINER_NET_T; /** \name Socket open flags * The following flags can be used when opening a network socket. */ /* @{ */ typedef uint32_t vc_container_net_open_flags_t; /** Connected stream socket, rather than connectionless datagram socket */ #define VC_CONTAINER_NET_OPEN_FLAG_STREAM 1 /** Force use of IPv4 addressing */ #define VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP4 2 /** Force use of IPv6 addressing */ #define VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP6 6 /** Use IPv4 broadcast address for datagram delivery */ #define VC_CONTAINER_NET_OPEN_FLAG_IP4_BROADCAST 8 /* @} */ /** Mask of bits used in forcing address type */ #define VC_CONTAINER_NET_OPEN_FLAG_FORCE_MASK 6 /** Blocks until data is available, or an error occurs. * Used with the VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS control operation. */ #define INFINITE_TIMEOUT_MS 0xFFFFFFFFUL /** Opens a network socket instance. * The network address can be a host name, dotted IP4, hex IP6 address or NULL. Passing NULL * signifies the socket is either to be used as a datagram receiver or a stream server, * depending on the flags. * \ref VC_CONTAINER_NET_OPEN_FLAG_STREAM will open the socket for connected streaming. The default * is to use connectionless datagrams. * \ref VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP4 will force the use of IPv4 addressing or fail to open * the socket. The default is to pick the first available. * \ref VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP6 will force the use of IPv6 addressing or fail to open * the socket. The default is to pick the first available. * \ref VC_CONTAINER_NET_OPEN_FLAG_IP4_BROADCAST will use IPv4 broadcast addressing for a datagram * sender. Use with an IPv6 address, stream socket or datagram receiver will raise an error. * If the p_status parameter is not NULL, the status code will be written to it to indicate the * reason for failure, or VC_CONTAINER_NET_SUCCESS on success. * Sockets shall be bound and connected as necessary. Stream server sockets shall further need * to have vc_container_net_listen and vc_container_net_accept called on them before data can be transferred. * * \param address Network address or NULL. * \param port Network port or well-known name. This is the local port for receivers/servers. * \param flags Flags controlling socket type. * \param p_status Optional pointer to variable to receive status of operation. * \return The socket instance or NULL on error. */ VC_CONTAINER_NET_T *vc_container_net_open( const char *address, const char *port, vc_container_net_open_flags_t flags, vc_container_net_status_t *p_status ); /** Closes a network socket instance. * The p_ctx pointer must not be used after it has been closed. * * \param p_ctx The socket instance to close. * \return The status code for closing the socket. */ vc_container_net_status_t vc_container_net_close( VC_CONTAINER_NET_T *p_ctx ); /** Query the latest status of the socket. * * \param p_ctx The socket instance. * \return The status of the socket. */ vc_container_net_status_t vc_container_net_status( VC_CONTAINER_NET_T *p_ctx ); /** Read data from the socket. * The function will read up to the requested number of bytes into the buffer. * If there is no data immediately available to read, the function will block * until data arrives, an error occurs or the timeout is reached (if set). * When the function returns zero, the socket may have been closed, an error * may have occurred, a zero length datagram received, or the timeout reached. * Check vc_container_net_status() to differentiate. * Attempting to read on a datagram sender socket will trigger an error. * * \param p_ctx The socket instance. * \param buffer The buffer into which bytes will be read. * \param size The maximum number of bytes to read. * \return The number of bytes actually read. */ size_t vc_container_net_read( VC_CONTAINER_NET_T *p_ctx, void *buffer, size_t size ); /** Write data to the socket. * If the socket cannot send the requested number of bytes in one go, the function * will return a value smaller than size. * Attempting to write on a datagram receiver socket will trigger an error. * * \param p_ctx The socket instance. * \param buffer The buffer from which bytes will be written. * \param size The maximum number of bytes to write. * \return The number of bytes actually written. */ size_t vc_container_net_write( VC_CONTAINER_NET_T *p_ctx, const void *buffer, size_t size ); /** Start a stream server socket listening for connections from clients. * Attempting to use this on anything other than a stream server socket shall * trigger an error. * * \param p_ctx The socket instance. * \param maximum_connections The maximum number of queued connections to allow. * \return The status of the socket. */ vc_container_net_status_t vc_container_net_listen( VC_CONTAINER_NET_T *p_ctx, uint32_t maximum_connections ); /** Accept a client connection on a listening stream server socket. * Attempting to use this on anything other than a listening stream server socket * shall trigger an error. * When a client connection is made, the new instance representing it is returned * via pp_client_ctx. * * \param p_server ctx The server socket instance. * \param pp_client_ctx The address where the pointer to the new client's socket * instance is written. * \return The status of the socket. */ vc_container_net_status_t vc_container_net_accept( VC_CONTAINER_NET_T *p_server_ctx, VC_CONTAINER_NET_T **pp_client_ctx ); /** Non-blocking check for data being available to read. * If an error occurs, the function will return false and the error can be * obtained using socket_status(). * * \param p_ctx The socket instance. * \return True if there is data available to read immediately. */ bool vc_container_net_is_data_available( VC_CONTAINER_NET_T *p_ctx ); /** Returns the maximum size of a datagram in bytes, for sending or receiving. * The limit for reading from or writing to stream sockets will generally be * greater than this value, although the call can also be made on such sockets. * * \param p_ctx The socket instance. * \return The maximum size of a datagram in bytes. */ size_t vc_container_net_maximum_datagram_size( VC_CONTAINER_NET_T *p_ctx ); /** Get the DNS name or IP address of a stream server client, if connected. * The length of the name will be limited by name_len, taking into account a * terminating NUL character. * Calling this function on a non-stream server instance, or one that is not * connected to a client, will result in an error status. * * \param p_ctx The socket instance. * \param name Pointer where the name should be written. * \param name_len Maximum number of characters to write to name. * \return The status of the socket. */ vc_container_net_status_t vc_container_net_get_client_name( VC_CONTAINER_NET_T *p_ctx, char *name, size_t name_len ); /** Get the port of a stream server client, if connected. * The port is written to the address in host order. * Calling this function on a non-stream server instance, or one that is not * connected to a client, will result in an error status. * * \param p_ctx The socket instance. * \param port Pointer where the port should be written. * \return The status of the socket. */ vc_container_net_status_t vc_container_net_get_client_port( VC_CONTAINER_NET_T *p_ctx , unsigned short *port ); /** Perform a control operation on the socket. * See vc_container_net_control_t for more details. * * \param p_ctx The socket instance. * \param operation The control operation to perform. * \param args Variable list of additional arguments to the operation. * \return The status of the socket. */ vc_container_net_status_t vc_container_net_control( VC_CONTAINER_NET_T *p_ctx, vc_container_net_control_t operation, va_list args); /** Convert a 32-bit unsigned value from network order (big endian) to host order. * * \param value The value to be converted. * \return The converted value. */ uint32_t vc_container_net_to_host( uint32_t value ); /** Convert a 32-bit unsigned value from host order to network order (big endian). * * \param value The value to be converted. * \return The converted value. */ uint32_t vc_container_net_from_host( uint32_t value ); /** Convert a 16-bit unsigned value from network order (big endian) to host order. * * \param value The value to be converted. * \return The converted value. */ uint16_t vc_container_net_to_host_16( uint16_t value ); /** Convert a 16-bit unsigned value from host order to network order (big endian). * * \param value The value to be converted. * \return The converted value. */ uint16_t vc_container_net_from_host_16( uint16_t value ); #ifdef __cplusplus } #endif #endif /* VC_NET_SOCKETS_H */ userland/containers/net/net_sockets_bsd.c000066400000000000000000000123561421703157200211540ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include "net_sockets.h" #include "net_sockets_priv.h" #include "containers/core/containers_common.h" /*****************************************************************************/ /** Default maximum datagram size. * This is based on the default Ethernet MTU size, less the IP and UDP headers. */ #define DEFAULT_MAXIMUM_DATAGRAM_SIZE (1500 - 20 - 8) /** Maximum socket buffer size to use. */ #define MAXIMUM_BUFFER_SIZE 65536 /*****************************************************************************/ vc_container_net_status_t vc_container_net_private_last_error() { switch (errno) { case EACCES: return VC_CONTAINER_NET_ERROR_ACCESS_DENIED; case EFAULT: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; case EINVAL: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; case EMFILE: return VC_CONTAINER_NET_ERROR_TOO_BIG; case EWOULDBLOCK: return VC_CONTAINER_NET_ERROR_WOULD_BLOCK; case EINPROGRESS: return VC_CONTAINER_NET_ERROR_IN_PROGRESS; case EALREADY: return VC_CONTAINER_NET_ERROR_IN_PROGRESS; case EADDRINUSE: return VC_CONTAINER_NET_ERROR_IN_USE; case EADDRNOTAVAIL: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; case ENETDOWN: return VC_CONTAINER_NET_ERROR_NETWORK; case ENETUNREACH: return VC_CONTAINER_NET_ERROR_NETWORK; case ENETRESET: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; case ECONNABORTED: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; case ECONNRESET: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; case ENOBUFS: return VC_CONTAINER_NET_ERROR_NO_MEMORY; case ENOTCONN: return VC_CONTAINER_NET_ERROR_NOT_CONNECTED; case ESHUTDOWN: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; case ETIMEDOUT: return VC_CONTAINER_NET_ERROR_TIMED_OUT; case ECONNREFUSED: return VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED; case ELOOP: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; case ENAMETOOLONG: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; case EHOSTDOWN: return VC_CONTAINER_NET_ERROR_NETWORK; case EHOSTUNREACH: return VC_CONTAINER_NET_ERROR_NETWORK; case EUSERS: return VC_CONTAINER_NET_ERROR_NO_MEMORY; case EDQUOT: return VC_CONTAINER_NET_ERROR_NO_MEMORY; case ESTALE: return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; /* All other errors are unexpected, so just map to a general purpose error code. */ default: return VC_CONTAINER_NET_ERROR_GENERAL; } } /*****************************************************************************/ vc_container_net_status_t vc_container_net_private_init() { /* No additional initialization required */ return VC_CONTAINER_NET_SUCCESS; } /*****************************************************************************/ void vc_container_net_private_deinit() { /* No additional deinitialization required */ } /*****************************************************************************/ void vc_container_net_private_close( SOCKET_T sock ) { close(sock); } /*****************************************************************************/ void vc_container_net_private_set_reusable( SOCKET_T sock, bool enable ) { int opt = enable ? 1 : 0; (void)setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)); } /*****************************************************************************/ size_t vc_container_net_private_maximum_datagram_size( SOCKET_T sock ) { (void)sock; /* No easy way to determine this, just use the default. */ return DEFAULT_MAXIMUM_DATAGRAM_SIZE; } userland/containers/net/net_sockets_bsd.h000066400000000000000000000035251421703157200211570ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef _NET_SOCKETS_BSD_H_ #define _NET_SOCKETS_BSD_H_ #include #include #include #include #include typedef int SOCKET_T; typedef socklen_t SOCKADDR_LEN_T; typedef void *SOCKOPT_CAST_T; #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #endif /* _NET_SOCKETS_BSD_H_ */ userland/containers/net/net_sockets_common.c000066400000000000000000000454511421703157200216760ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include "containers/containers.h" #include "containers/core/containers_common.h" #include "containers/core/containers_logging.h" #include "net_sockets.h" #include "net_sockets_priv.h" /*****************************************************************************/ struct vc_container_net_tag { /** The underlying socket */ SOCKET_T socket; /** Last error raised on the socket instance. */ vc_container_net_status_t status; /** Simple socket type */ vc_container_net_type_t type; /** Socket address, used for sending datagrams. */ union { struct sockaddr_storage storage; struct sockaddr sa; struct sockaddr_in in; struct sockaddr_in6 in6; } to_addr; /** Number of bytes in to_addr that have been filled. */ SOCKADDR_LEN_T to_addr_len; /** Maximum size of datagrams. */ size_t max_datagram_size; /** Timeout to use when reading from a socket. INFINITE_TIMEOUT_MS waits forever. */ uint32_t read_timeout_ms; }; /*****************************************************************************/ static void socket_clear_address(struct sockaddr *p_addr) { switch (p_addr->sa_family) { case AF_INET: { struct sockaddr_in *p_addr_v4 = (struct sockaddr_in *)p_addr; memset(&p_addr_v4->sin_addr, 0, sizeof(p_addr_v4->sin_addr)); } break; case AF_INET6: { struct sockaddr_in6 *p_addr_v6 = (struct sockaddr_in6 *)p_addr; memset(&p_addr_v6->sin6_addr, 0, sizeof(p_addr_v6->sin6_addr)); } break; default: /* Invalid or unsupported address family */ vc_container_assert(0); } } /*****************************************************************************/ static vc_container_net_status_t socket_set_read_buffer_size(VC_CONTAINER_NET_T *p_ctx, uint32_t buffer_size) { int result; const SOCKOPT_CAST_T optptr = (const SOCKOPT_CAST_T)&buffer_size; result = setsockopt(p_ctx->socket, SOL_SOCKET, SO_RCVBUF, optptr, sizeof(buffer_size)); if (result == SOCKET_ERROR) return vc_container_net_private_last_error(); return VC_CONTAINER_NET_SUCCESS; } /*****************************************************************************/ static vc_container_net_status_t socket_set_read_timeout_ms(VC_CONTAINER_NET_T *p_ctx, uint32_t timeout_ms) { p_ctx->read_timeout_ms = timeout_ms; return VC_CONTAINER_NET_SUCCESS; } /*****************************************************************************/ static bool socket_wait_for_data( VC_CONTAINER_NET_T *p_ctx, uint32_t timeout_ms ) { int result; fd_set set; struct timeval tv; if (timeout_ms == INFINITE_TIMEOUT_MS) return true; FD_ZERO(&set); FD_SET(p_ctx->socket, &set); tv.tv_sec = timeout_ms / 1000; tv.tv_usec = (timeout_ms - tv.tv_sec * 1000) * 1000; result = select(p_ctx->socket + 1, &set, NULL, NULL, &tv); if (result == SOCKET_ERROR) p_ctx->status = vc_container_net_private_last_error(); else p_ctx->status = VC_CONTAINER_NET_SUCCESS; return (result == 1); } /*****************************************************************************/ VC_CONTAINER_NET_T *vc_container_net_open( const char *address, const char *port, vc_container_net_open_flags_t flags, vc_container_net_status_t *p_status ) { VC_CONTAINER_NET_T *p_ctx; struct addrinfo hints, *info, *p; int result; vc_container_net_status_t status; SOCKET_T sock = INVALID_SOCKET; status = vc_container_net_private_init(); if (status != VC_CONTAINER_NET_SUCCESS) { LOG_ERROR(NULL, "vc_container_net_open: platform initialization failure: %d", status); if (p_status) *p_status = status; return NULL; } p_ctx = (VC_CONTAINER_NET_T *)malloc(sizeof(VC_CONTAINER_NET_T)); if (!p_ctx) { if (p_status) *p_status = VC_CONTAINER_NET_ERROR_NO_MEMORY; LOG_ERROR(NULL, "vc_container_net_open: malloc fail for VC_CONTAINER_NET_T"); vc_container_net_private_deinit(); return NULL; } /* Initialize the net socket instance structure */ memset(p_ctx, 0, sizeof(*p_ctx)); p_ctx->socket = INVALID_SOCKET; if (flags & VC_CONTAINER_NET_OPEN_FLAG_STREAM) p_ctx->type = address ? STREAM_CLIENT : STREAM_SERVER; else p_ctx->type = address ? DATAGRAM_SENDER : DATAGRAM_RECEIVER; /* Create the address info linked list from the data provided */ memset(&hints, 0, sizeof(hints)); switch (flags & VC_CONTAINER_NET_OPEN_FLAG_FORCE_MASK) { case 0: hints.ai_family = AF_UNSPEC; break; case VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP4: hints.ai_family = AF_INET; break; case VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP6: hints.ai_family = AF_INET6; break; default: status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; LOG_ERROR(NULL, "vc_container_net_open: invalid address forcing flag"); goto error; } hints.ai_socktype = (flags & VC_CONTAINER_NET_OPEN_FLAG_STREAM) ? SOCK_STREAM : SOCK_DGRAM; result = getaddrinfo(address, port, &hints, &info); if (result) { status = vc_container_net_private_last_error(); LOG_ERROR(NULL, "vc_container_net_open: unable to get address info: %d", status); goto error; } /* Not all address infos may be useable. Search for one that is by skipping any * that provoke errors. */ for(p = info; (p != NULL) && (sock == INVALID_SOCKET) ; p = p->ai_next) { sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol); if (sock == INVALID_SOCKET) { status = vc_container_net_private_last_error(); continue; } switch (p_ctx->type) { case STREAM_CLIENT: /* Simply connect to the given address/port */ if (connect(sock, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR) status = vc_container_net_private_last_error(); break; case DATAGRAM_SENDER: /* Nothing further to do */ break; case STREAM_SERVER: /* Try to avoid socket reuse timing issues on TCP server sockets */ vc_container_net_private_set_reusable(sock, true); /* Allow any source address */ socket_clear_address(p->ai_addr); if (bind(sock, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR) status = vc_container_net_private_last_error(); break; case DATAGRAM_RECEIVER: /* Allow any source address */ socket_clear_address(p->ai_addr); if (bind(sock, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR) status = vc_container_net_private_last_error(); break; } if (status == VC_CONTAINER_NET_SUCCESS) { /* Save addressing information for later use */ p_ctx->to_addr_len = p->ai_addrlen; memcpy(&p_ctx->to_addr, p->ai_addr, p->ai_addrlen); } else { vc_container_net_private_close(sock); /* Try next entry in list */ sock = INVALID_SOCKET; } } freeaddrinfo(info); if (sock == INVALID_SOCKET) { LOG_ERROR(NULL, "vc_container_net_open: failed to open socket: %d", status); goto error; } p_ctx->socket = sock; p_ctx->max_datagram_size = vc_container_net_private_maximum_datagram_size(sock); p_ctx->read_timeout_ms = INFINITE_TIMEOUT_MS; if (p_status) *p_status = VC_CONTAINER_NET_SUCCESS; return p_ctx; error: if (p_status) *p_status = status; (void)vc_container_net_close(p_ctx); return NULL; } /*****************************************************************************/ vc_container_net_status_t vc_container_net_close( VC_CONTAINER_NET_T *p_ctx ) { if (!p_ctx) return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; if (p_ctx->socket != INVALID_SOCKET) { vc_container_net_private_close(p_ctx->socket); p_ctx->socket = INVALID_SOCKET; } free(p_ctx); vc_container_net_private_deinit(); return VC_CONTAINER_NET_SUCCESS; } /*****************************************************************************/ vc_container_net_status_t vc_container_net_status( VC_CONTAINER_NET_T *p_ctx ) { if (!p_ctx) return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; return p_ctx->status; } /*****************************************************************************/ size_t vc_container_net_read( VC_CONTAINER_NET_T *p_ctx, void *buffer, size_t size ) { int result = 0; if (!p_ctx) return 0; if (!buffer) { p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; return 0; } p_ctx->status = VC_CONTAINER_NET_SUCCESS; switch (p_ctx->type) { case STREAM_CLIENT: case STREAM_SERVER: /* Receive data from the stream */ if (socket_wait_for_data(p_ctx, p_ctx->read_timeout_ms)) { result = recv(p_ctx->socket, buffer, (int)size, 0); if (!result) p_ctx->status = VC_CONTAINER_NET_ERROR_CONNECTION_LOST; } else p_ctx->status = VC_CONTAINER_NET_ERROR_TIMED_OUT; break; case DATAGRAM_RECEIVER: { /* Receive the packet */ /* FIXME Potential for data loss, as rest of packet will be lost if buffer was not large enough */ if (socket_wait_for_data(p_ctx, p_ctx->read_timeout_ms)) { result = recvfrom(p_ctx->socket, buffer, size, 0, &p_ctx->to_addr.sa, &p_ctx->to_addr_len); if (!result) p_ctx->status = VC_CONTAINER_NET_ERROR_CONNECTION_LOST; } else p_ctx->status = VC_CONTAINER_NET_ERROR_TIMED_OUT; } break; default: /* DATAGRAM_SENDER */ p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; result = 0; break; } if (result == SOCKET_ERROR) { p_ctx->status = vc_container_net_private_last_error(); result = 0; } return (size_t)result; } /*****************************************************************************/ size_t vc_container_net_write( VC_CONTAINER_NET_T *p_ctx, const void *buffer, size_t size ) { int result; if (!p_ctx) return 0; if (!buffer) { p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; return 0; } p_ctx->status = VC_CONTAINER_NET_SUCCESS; switch (p_ctx->type) { case STREAM_CLIENT: case STREAM_SERVER: /* Send data to the stream */ result = send(p_ctx->socket, buffer, (int)size, 0); break; case DATAGRAM_SENDER: /* Send the datagram */ if (size > p_ctx->max_datagram_size) size = p_ctx->max_datagram_size; result = sendto(p_ctx->socket, buffer, size, 0, &p_ctx->to_addr.sa, p_ctx->to_addr_len); break; default: /* DATAGRAM_RECEIVER */ p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; result = 0; break; } if (result == SOCKET_ERROR) { p_ctx->status = vc_container_net_private_last_error(); result = 0; } return (size_t)result; } /*****************************************************************************/ vc_container_net_status_t vc_container_net_listen( VC_CONTAINER_NET_T *p_ctx, uint32_t maximum_connections ) { if (!p_ctx) return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; p_ctx->status = VC_CONTAINER_NET_SUCCESS; if (p_ctx->type == STREAM_SERVER) { if (listen(p_ctx->socket, maximum_connections) == SOCKET_ERROR) p_ctx->status = vc_container_net_private_last_error(); } else { p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; } return p_ctx->status; } /*****************************************************************************/ vc_container_net_status_t vc_container_net_accept( VC_CONTAINER_NET_T *p_server_ctx, VC_CONTAINER_NET_T **pp_client_ctx ) { VC_CONTAINER_NET_T *p_client_ctx = NULL; if (!p_server_ctx) return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; if (!pp_client_ctx) return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; *pp_client_ctx = NULL; if (p_server_ctx->type != STREAM_SERVER) { p_server_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; goto error; } p_client_ctx = (VC_CONTAINER_NET_T *)malloc(sizeof(VC_CONTAINER_NET_T)); if (!p_client_ctx) { p_server_ctx->status = VC_CONTAINER_NET_ERROR_NO_MEMORY; goto error; } /* Initialise the new context with the address information from the server context */ memset(p_client_ctx, 0, sizeof(*p_client_ctx)); memcpy(&p_client_ctx->to_addr, &p_server_ctx->to_addr, p_server_ctx->to_addr_len); p_client_ctx->to_addr_len = p_server_ctx->to_addr_len; p_client_ctx->socket = accept(p_server_ctx->socket, &p_client_ctx->to_addr.sa, &p_client_ctx->to_addr_len); if (p_client_ctx->socket == INVALID_SOCKET) { p_server_ctx->status = vc_container_net_private_last_error(); goto error; } /* Need to bump up the initialisation count, as a new context has been created */ p_server_ctx->status = vc_container_net_private_init(); if (p_server_ctx->status != VC_CONTAINER_NET_SUCCESS) goto error; p_client_ctx->type = STREAM_CLIENT; p_client_ctx->max_datagram_size = vc_container_net_private_maximum_datagram_size(p_client_ctx->socket); p_client_ctx->read_timeout_ms = INFINITE_TIMEOUT_MS; p_client_ctx->status = VC_CONTAINER_NET_SUCCESS; *pp_client_ctx = p_client_ctx; return VC_CONTAINER_NET_SUCCESS; error: if (p_client_ctx) free(p_client_ctx); return p_server_ctx->status; } /*****************************************************************************/ bool vc_container_net_is_data_available( VC_CONTAINER_NET_T *p_ctx ) { if (!p_ctx) return false; if (p_ctx->type == DATAGRAM_SENDER) { p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; return false; } return socket_wait_for_data(p_ctx, 0); } /*****************************************************************************/ size_t vc_container_net_maximum_datagram_size( VC_CONTAINER_NET_T *p_ctx ) { return p_ctx ? p_ctx->max_datagram_size : 0; } /*****************************************************************************/ static vc_container_net_status_t translate_getnameinfo_error( int error ) { switch (error) { case EAI_AGAIN: return VC_CONTAINER_NET_ERROR_TRY_AGAIN; case EAI_FAIL: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND; case EAI_MEMORY: return VC_CONTAINER_NET_ERROR_NO_MEMORY; case EAI_NONAME: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND; /* All other errors are unexpected, so just map to a general purpose error code. */ default: return VC_CONTAINER_NET_ERROR_GENERAL; } } /*****************************************************************************/ vc_container_net_status_t vc_container_net_get_client_name( VC_CONTAINER_NET_T *p_ctx, char *name, size_t name_len ) { int result; if (!p_ctx) return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; if (p_ctx->socket == INVALID_SOCKET) p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_CONNECTED; else if (!name || !name_len) p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; else if ((result = getnameinfo(&p_ctx->to_addr.sa, p_ctx->to_addr_len, name, name_len, NULL, 0, 0)) != 0) p_ctx->status = translate_getnameinfo_error(result); else p_ctx->status = VC_CONTAINER_NET_SUCCESS; return p_ctx->status; } /*****************************************************************************/ vc_container_net_status_t vc_container_net_get_client_port( VC_CONTAINER_NET_T *p_ctx , unsigned short *port ) { if (!p_ctx) return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; if (p_ctx->socket == INVALID_SOCKET) p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_CONNECTED; else if (!port) p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; else { p_ctx->status = VC_CONTAINER_NET_SUCCESS; switch (p_ctx->to_addr.sa.sa_family) { case AF_INET: *port = ntohs(p_ctx->to_addr.in.sin_port); break; case AF_INET6: *port = ntohs(p_ctx->to_addr.in6.sin6_port); break; default: /* Highly unexepcted address family! */ p_ctx->status = VC_CONTAINER_NET_ERROR_GENERAL; } } return p_ctx->status; } /*****************************************************************************/ vc_container_net_status_t vc_container_net_control( VC_CONTAINER_NET_T *p_ctx, vc_container_net_control_t operation, va_list args) { vc_container_net_status_t status; switch (operation) { case VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE: status = socket_set_read_buffer_size(p_ctx, va_arg(args, uint32_t)); break; case VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS: status = socket_set_read_timeout_ms(p_ctx, va_arg(args, uint32_t)); break; default: status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; } return status; } /*****************************************************************************/ uint32_t vc_container_net_to_host( uint32_t value ) { return ntohl(value); } /*****************************************************************************/ uint32_t vc_container_net_from_host( uint32_t value ) { return htonl(value); } /*****************************************************************************/ uint16_t vc_container_net_to_host_16( uint16_t value ) { return ntohs(value); } /*****************************************************************************/ uint16_t vc_container_net_from_host_16( uint16_t value ) { return htons(value); } userland/containers/net/net_sockets_null.c000066400000000000000000000137571421703157200213640ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include "net_sockets.h" #include "containers/core/containers_common.h" /*****************************************************************************/ struct vc_container_net_tag { uint32_t dummy; /* C requires structs not to be empty. */ }; /*****************************************************************************/ VC_CONTAINER_NET_T *vc_container_net_open( const char *address, const char *port, vc_container_net_open_flags_t flags, vc_container_net_status_t *p_status ) { VC_CONTAINER_PARAM_UNUSED(address); VC_CONTAINER_PARAM_UNUSED(port); VC_CONTAINER_PARAM_UNUSED(flags); if (p_status) *p_status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; return NULL; } /*****************************************************************************/ vc_container_net_status_t vc_container_net_close( VC_CONTAINER_NET_T *p_ctx ) { VC_CONTAINER_PARAM_UNUSED(p_ctx); return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; } /*****************************************************************************/ vc_container_net_status_t vc_container_net_status( VC_CONTAINER_NET_T *p_ctx ) { VC_CONTAINER_PARAM_UNUSED(p_ctx); return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; } /*****************************************************************************/ size_t vc_container_net_read( VC_CONTAINER_NET_T *p_ctx, void *buffer, size_t size ) { VC_CONTAINER_PARAM_UNUSED(p_ctx); VC_CONTAINER_PARAM_UNUSED(buffer); VC_CONTAINER_PARAM_UNUSED(size); return 0; } /*****************************************************************************/ size_t vc_container_net_write( VC_CONTAINER_NET_T *p_ctx, const void *buffer, size_t size ) { VC_CONTAINER_PARAM_UNUSED(p_ctx); VC_CONTAINER_PARAM_UNUSED(buffer); VC_CONTAINER_PARAM_UNUSED(size); return 0; } /*****************************************************************************/ vc_container_net_status_t vc_container_net_listen( VC_CONTAINER_NET_T *p_ctx, uint32_t maximum_connections ) { VC_CONTAINER_PARAM_UNUSED(p_ctx); VC_CONTAINER_PARAM_UNUSED(maximum_connections); return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; } /*****************************************************************************/ vc_container_net_status_t vc_container_net_accept( VC_CONTAINER_NET_T *p_server_ctx, VC_CONTAINER_NET_T **pp_client_ctx ) { VC_CONTAINER_PARAM_UNUSED(p_server_ctx); VC_CONTAINER_PARAM_UNUSED(pp_client_ctx); return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; } /*****************************************************************************/ bool vc_container_net_is_data_available( VC_CONTAINER_NET_T *p_ctx ) { VC_CONTAINER_PARAM_UNUSED(p_ctx); return false; } /*****************************************************************************/ size_t vc_container_net_maximum_datagram_size( VC_CONTAINER_NET_T *p_ctx ) { VC_CONTAINER_PARAM_UNUSED(p_ctx); return 0; } /*****************************************************************************/ vc_container_net_status_t vc_container_net_get_client_name( VC_CONTAINER_NET_T *p_ctx, char *name, size_t name_len ) { VC_CONTAINER_PARAM_UNUSED(p_ctx); VC_CONTAINER_PARAM_UNUSED(name); VC_CONTAINER_PARAM_UNUSED(name_len); return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; } /*****************************************************************************/ vc_container_net_status_t vc_container_net_get_client_port( VC_CONTAINER_NET_T *p_ctx , unsigned short *port ) { VC_CONTAINER_PARAM_UNUSED(p_ctx); VC_CONTAINER_PARAM_UNUSED(port); return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; } /*****************************************************************************/ vc_container_net_status_t vc_container_net_control( VC_CONTAINER_NET_T *p_ctx, vc_container_net_control_t operation, va_list args) { VC_CONTAINER_PARAM_UNUSED(p_ctx); VC_CONTAINER_PARAM_UNUSED(operation); VC_CONTAINER_PARAM_UNUSED(args); return VC_CONTAINER_NET_ERROR_NOT_ALLOWED; } /*****************************************************************************/ uint32_t vc_container_net_to_host( uint32_t value ) { return value; } /*****************************************************************************/ uint32_t vc_container_net_from_host( uint32_t value ) { return value; } /*****************************************************************************/ uint16_t vc_container_net_to_host_16( uint16_t value ) { return value; } /*****************************************************************************/ uint16_t vc_container_net_from_host_16( uint16_t value ) { return value; } userland/containers/net/net_sockets_priv.h000066400000000000000000000062011421703157200213610ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef _NET_SOCKETS_PRIV_H_ #define _NET_SOCKETS_PRIV_H_ #include "net_sockets.h" #ifdef WIN32 #include "net_sockets_win32.h" #else #include "net_sockets_bsd.h" #endif #ifdef __cplusplus extern "C" { #endif typedef enum { STREAM_CLIENT = 0, /**< TCP client */ STREAM_SERVER, /**< TCP server */ DATAGRAM_SENDER, /**< UDP sender */ DATAGRAM_RECEIVER /**< UDP receiver */ } vc_container_net_type_t; /** Perform implementation-specific per-socket initialization. * * \return VC_CONTAINER_NET_SUCCESS or one of the error codes on failure. */ vc_container_net_status_t vc_container_net_private_init( void ); /** Perform implementation-specific per-socket deinitialization. * This function is always called once for each successful call to socket_private_init(). */ void vc_container_net_private_deinit( void ); /** Return the last error from the socket implementation. */ vc_container_net_status_t vc_container_net_private_last_error( void ); /** Implementation-specific internal socket close. * * \param sock Internal socket to be closed. */ void vc_container_net_private_close( SOCKET_T sock ); /** Enable or disable socket address reusability. * * \param sock Internal socket to be closed. * \param enable True to enable reusability, false to clear it. */ void vc_container_net_private_set_reusable( SOCKET_T sock, bool enable ); /** Query the maximum datagram size for the socket. * * \param sock The socket to query. * \return The maximum supported datagram size on the socket. */ size_t vc_container_net_private_maximum_datagram_size( SOCKET_T sock ); #ifdef __cplusplus } #endif #endif /* _NET_SOCKETS_PRIV_H_ */ userland/containers/net/net_sockets_win32.c000066400000000000000000000147031421703157200213440ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include "net_sockets.h" #include "net_sockets_priv.h" #include "containers/core/containers_common.h" #pragma comment(lib, "Ws2_32.lib") /*****************************************************************************/ /** Default maximum datagram size. * This is based on the default Ethernet MTU size, less the IP and UDP headers. */ #define DEFAULT_MAXIMUM_DATAGRAM_SIZE (1500 - 20 - 8) /** Maximum socket buffer size to use. */ #define MAXIMUM_BUFFER_SIZE 65536 /*****************************************************************************/ static vc_container_net_status_t translate_error_status( int error ) { switch (error) { case WSA_INVALID_HANDLE: return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; case WSA_NOT_ENOUGH_MEMORY: return VC_CONTAINER_NET_ERROR_NO_MEMORY; case WSA_INVALID_PARAMETER: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; case WSAEACCES: return VC_CONTAINER_NET_ERROR_ACCESS_DENIED; case WSAEFAULT: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; case WSAEINVAL: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; case WSAEMFILE: return VC_CONTAINER_NET_ERROR_TOO_BIG; case WSAEWOULDBLOCK: return VC_CONTAINER_NET_ERROR_WOULD_BLOCK; case WSAEINPROGRESS: return VC_CONTAINER_NET_ERROR_IN_PROGRESS; case WSAEALREADY: return VC_CONTAINER_NET_ERROR_IN_PROGRESS; case WSAEADDRINUSE: return VC_CONTAINER_NET_ERROR_IN_USE; case WSAEADDRNOTAVAIL: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; case WSAENETDOWN: return VC_CONTAINER_NET_ERROR_NETWORK; case WSAENETUNREACH: return VC_CONTAINER_NET_ERROR_NETWORK; case WSAENETRESET: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; case WSAECONNABORTED: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; case WSAECONNRESET: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; case WSAENOBUFS: return VC_CONTAINER_NET_ERROR_NO_MEMORY; case WSAENOTCONN: return VC_CONTAINER_NET_ERROR_NOT_CONNECTED; case WSAESHUTDOWN: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; case WSAETIMEDOUT: return VC_CONTAINER_NET_ERROR_TIMED_OUT; case WSAECONNREFUSED: return VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED; case WSAELOOP: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; case WSAENAMETOOLONG: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; case WSAEHOSTDOWN: return VC_CONTAINER_NET_ERROR_NETWORK; case WSAEHOSTUNREACH: return VC_CONTAINER_NET_ERROR_NETWORK; case WSAEPROCLIM: return VC_CONTAINER_NET_ERROR_NO_MEMORY; case WSAEUSERS: return VC_CONTAINER_NET_ERROR_NO_MEMORY; case WSAEDQUOT: return VC_CONTAINER_NET_ERROR_NO_MEMORY; case WSAESTALE: return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; case WSAEDISCON: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; case WSAHOST_NOT_FOUND: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND; case WSATRY_AGAIN: return VC_CONTAINER_NET_ERROR_TRY_AGAIN; case WSANO_RECOVERY: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND; case WSANO_DATA: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND; /* All other errors are unexpected, so just map to a general purpose error code. */ default: return VC_CONTAINER_NET_ERROR_GENERAL; } } /*****************************************************************************/ vc_container_net_status_t vc_container_net_private_last_error() { return translate_error_status( WSAGetLastError() ); } /*****************************************************************************/ vc_container_net_status_t vc_container_net_private_init() { WSADATA wsa_data; int result; result = WSAStartup(MAKEWORD(2,2), &wsa_data); if (result) return translate_error_status( result ); return VC_CONTAINER_NET_SUCCESS; } /*****************************************************************************/ void vc_container_net_private_deinit() { WSACleanup(); } /*****************************************************************************/ void vc_container_net_private_close( SOCKET_T sock ) { closesocket(sock); } /*****************************************************************************/ void vc_container_net_private_set_reusable( SOCKET_T sock, bool enable ) { BOOL opt = enable ? TRUE : FALSE; (void)setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)); } /*****************************************************************************/ size_t vc_container_net_private_maximum_datagram_size( SOCKET_T sock ) { size_t max_datagram_size = DEFAULT_MAXIMUM_DATAGRAM_SIZE; int opt_size = sizeof(max_datagram_size); /* Ignore errors and use the default if necessary */ (void)getsockopt(sock, SOL_SOCKET, SO_MAX_MSG_SIZE, (char *)&max_datagram_size, &opt_size); return max_datagram_size; } userland/containers/net/net_sockets_win32.h000066400000000000000000000033401421703157200213440ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef _NET_SOCKETS_WIN32_H_ #define _NET_SOCKETS_WIN32_H_ #include #include typedef SOCKET SOCKET_T; typedef int SOCKADDR_LEN_T; typedef char *SOCKOPT_CAST_T; #endif /* _NET_SOCKETS_WIN32_H_ */ userland/containers/packetizers.h000066400000000000000000000154451421703157200175500ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_PACKETIZERS_H #define VC_PACKETIZERS_H /** \file packetizers.h * Public API for packetizing data (i.e. framing and timestamping) */ #ifdef __cplusplus extern "C" { #endif #include "containers/containers.h" /** \defgroup VcPacketizerApi Packetizer API * API for packetizers */ /* @{ */ /** \name Packetizer flags * \anchor packetizerflags * The following flags describe properties of a packetizer */ /* @{ */ #define VC_PACKETIZER_FLAG_ES_CHANGED 0x1 /**< ES definition has changed */ /* @} */ /** Definition of the packetizer type */ typedef struct VC_PACKETIZER_T { struct VC_PACKETIZER_PRIVATE_T *priv; /**< Private member used by the implementation */ uint32_t flags; /**< Flags describing the properties of a packetizer. * See \ref packetizerflags "Packetizer flags". */ VC_CONTAINER_ES_FORMAT_T *in; /**< Format of the input elementary stream */ VC_CONTAINER_ES_FORMAT_T *out; /**< Format of the output elementary stream */ uint32_t max_frame_size; /**< Maximum size of a packetized frame */ } VC_PACKETIZER_T; /** Open a packetizer to convert the input format into the requested output format. * This will create an an instance of a packetizer and its associated context. * * If no packetizer is found for the requested format, this will return a null pointer as well as * an error code indicating why this failed. * * \param in Input elementary stream format * \param out_variant Requested output variant for the output elementary stream format * \param status Returns the status of the operation * \return A pointer to the context of the new instance of the packetizer. * Returns NULL on failure. */ VC_PACKETIZER_T *vc_packetizer_open(VC_CONTAINER_ES_FORMAT_T *in, VC_CONTAINER_FOURCC_T out_variant, VC_CONTAINER_STATUS_T *status); /** Closes an instance of a packetizer. * This will free all the resources associated with the context. * * \param context Pointer to the context of the instance to close * \return the status of the operation */ VC_CONTAINER_STATUS_T vc_packetizer_close( VC_PACKETIZER_T *context ); /** \name Packetizer flags * The following flags can be passed during a packetize call */ /* @{ */ /** Type definition for the packetizer flags */ typedef uint32_t VC_PACKETIZER_FLAGS_T; /** Ask the packetizer to only return information on the next packet without reading it */ #define VC_PACKETIZER_FLAG_INFO 0x1 /** Ask the packetizer to skip the next packet */ #define VC_PACKETIZER_FLAG_SKIP 0x2 /** Ask the packetizer to flush any data being processed */ #define VC_PACKETIZER_FLAG_FLUSH 0x4 /** Force the packetizer to release an input packet */ #define VC_PACKETIZER_FLAG_FORCE_RELEASE_INPUT 0x8 /* @} */ /** Push a new packet of data to the packetizer. * This is the mechanism used to feed data into the packetizer. Once a packet has been * pushed into the packetizer it is owned by the packetizer until released by a call to * \ref vc_packetizer_pop * * \param context Pointer to the context of the packetizer to use * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet * to push into the packetizer. * \return the status of the operation */ VC_CONTAINER_STATUS_T vc_packetizer_push( VC_PACKETIZER_T *context, VC_CONTAINER_PACKET_T *packet); /** Pop a packet of data from the packetizer. * This allows the client to retrieve consumed data from the packetizer. Packets returned by * the packetizer in this manner can then be released / recycled by the client. * It is possible for the client to retrieve non-consumed data by passing the * VC_PACKETIZER_FLAG_FORCE_RELEASE_INPUT flag. This will however trigger some internal buffering * inside the packetizer and thus is less efficient. * * \param context Pointer to the context of the packetizer to use * \param packet Pointer used to return a consumed packet * \param flags Miscellaneous flags controlling the operation * * \return VC_CONTAINER_SUCCESS if a consumed packet was retrieved, * VC_CONTAINER_ERROR_INCOMPLETE_DATA if none is available. */ VC_CONTAINER_STATUS_T vc_packetizer_pop( VC_PACKETIZER_T *context, VC_CONTAINER_PACKET_T **packet, VC_PACKETIZER_FLAGS_T flags); /** Read packetized data out of the packetizer. * * \param context Pointer to the context of the packetizer to use * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet * This might need to be partially filled before the call (buffer, buffer_size) * depending on the flags used. * \param flags Miscellaneous flags controlling the operation * \return the status of the operation */ VC_CONTAINER_STATUS_T vc_packetizer_read( VC_PACKETIZER_T *context, VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags); /** Reset packetizer state. * This will reset the state of the packetizer as well as mark all data pushed to it as consumed. * * \param context Pointer to the context of the packetizer to reset * \return the status of the operation */ VC_CONTAINER_STATUS_T vc_packetizer_reset( VC_PACKETIZER_T *context ); /* @} */ #ifdef __cplusplus } #endif #endif /* VC_PACKETIZERS_H */ userland/containers/pcm/000077500000000000000000000000001421703157200156215ustar00rootroot00000000000000userland/containers/pcm/pcm_packetizer.c000066400000000000000000000232131421703157200207660ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ /** \file * Implementation of a PCM packetizer. */ #include #include #include "containers/packetizers.h" #include "containers/core/packetizers_private.h" #include "containers/core/containers_common.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_time.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_bytestream.h" #define FRAME_SIZE (16*1024) /**< Arbitrary value which is neither too small nor too big */ #define FACTOR_SHIFT 4 /**< Shift applied to the conversion factor */ VC_CONTAINER_STATUS_T pcm_packetizer_open( VC_PACKETIZER_T * ); /*****************************************************************************/ enum conversion { CONVERSION_NONE = 0, CONVERSION_U8_TO_S16L, CONVERSION_UNKNOWN }; typedef struct VC_PACKETIZER_MODULE_T { enum { STATE_NEW_PACKET = 0, STATE_DATA } state; unsigned int samples_per_frame; unsigned int bytes_per_sample; unsigned int max_frame_size; uint32_t bytes_read; unsigned int frame_size; enum conversion conversion; unsigned int conversion_factor; } VC_PACKETIZER_MODULE_T; /*****************************************************************************/ static VC_CONTAINER_STATUS_T pcm_packetizer_close( VC_PACKETIZER_T *p_ctx ) { free(p_ctx->priv->module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T pcm_packetizer_reset( VC_PACKETIZER_T *p_ctx ) { VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; module->state = STATE_NEW_PACKET; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static void convert_pcm_u8_to_s16l( uint8_t **p_out, uint8_t *in, size_t size) { int16_t *out = (int16_t *)*p_out; uint8_t tmp; while(size--) { tmp = *in++; *out++ = ((tmp - 128) << 8) | tmp; } *p_out = (uint8_t *)out; } /*****************************************************************************/ static void convert_pcm( VC_PACKETIZER_T *p_ctx, VC_CONTAINER_BYTESTREAM_T *stream, size_t size, uint8_t *out ) { VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; uint8_t tmp[256]; size_t tmp_size; while(size) { tmp_size = MIN(sizeof(tmp), size); bytestream_get(stream, tmp, tmp_size); if (module->conversion == CONVERSION_U8_TO_S16L) convert_pcm_u8_to_s16l(&out, tmp, tmp_size); else bytestream_skip(stream, tmp_size); size -= tmp_size; } } /*****************************************************************************/ static VC_CONTAINER_STATUS_T pcm_packetizer_packetize( VC_PACKETIZER_T *p_ctx, VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags ) { VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream; VC_CONTAINER_TIME_T *time = &p_ctx->priv->time; int64_t pts, dts; size_t offset, size; while(1) switch (module->state) { case STATE_NEW_PACKET: /* Make sure we've got enough data */ if(bytestream_size(stream) < module->max_frame_size && !(flags & VC_PACKETIZER_FLAG_FLUSH)) return VC_CONTAINER_ERROR_INCOMPLETE_DATA; if(!bytestream_size(stream)) return VC_CONTAINER_ERROR_INCOMPLETE_DATA; module->frame_size = bytestream_size(stream); if(module->frame_size > module->max_frame_size) module->frame_size = module->max_frame_size; bytestream_get_timestamps_and_offset(stream, &pts, &dts, &offset, true); vc_container_time_set(time, pts); if(pts != VC_CONTAINER_TIME_UNKNOWN) vc_container_time_add(time, offset / module->bytes_per_sample); module->bytes_read = 0; module->state = STATE_DATA; /* fall through to the next state */ case STATE_DATA: size = module->frame_size - module->bytes_read; out->pts = out->dts = VC_CONTAINER_TIME_UNKNOWN; out->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END; out->size = (size * module->conversion_factor) >> FACTOR_SHIFT; if(!module->bytes_read) { out->pts = out->dts = vc_container_time_get(time); out->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; } if(flags & VC_PACKETIZER_FLAG_INFO) return VC_CONTAINER_SUCCESS; if(flags & VC_PACKETIZER_FLAG_SKIP) { bytestream_skip( stream, size ); } else { out->size = MIN(out->size, out->buffer_size); size = (out->size << FACTOR_SHIFT) / module->conversion_factor; out->size = (size * module->conversion_factor) >> FACTOR_SHIFT; if(module->conversion != CONVERSION_NONE) convert_pcm(p_ctx, stream, size, out->data); else bytestream_get(stream, out->data, out->size); } module->bytes_read += size; if(module->bytes_read == module->frame_size) { vc_container_time_add(time, module->samples_per_frame); module->state = STATE_NEW_PACKET; } return VC_CONTAINER_SUCCESS; default: break; }; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T pcm_packetizer_open( VC_PACKETIZER_T *p_ctx ) { VC_PACKETIZER_MODULE_T *module; unsigned int bytes_per_sample = 0; enum conversion conversion = CONVERSION_NONE; if(p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_UNSIGNED_BE && p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_UNSIGNED_LE && p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_SIGNED_BE && p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_SIGNED_LE && p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_FLOAT_BE && p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_FLOAT_LE) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if(p_ctx->in->type->audio.block_align) bytes_per_sample = p_ctx->in->type->audio.block_align; else if(p_ctx->in->type->audio.bits_per_sample && p_ctx->in->type->audio.channels) bytes_per_sample = p_ctx->in->type->audio.bits_per_sample * p_ctx->in->type->audio.channels / 8; if(!bytes_per_sample) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Check if we support any potential conversion we've been asked to do */ if(p_ctx->out->codec_variant) conversion = CONVERSION_UNKNOWN; if(p_ctx->out->codec_variant == VC_FOURCC('s','1','6','l') && p_ctx->in->codec == VC_CONTAINER_CODEC_PCM_SIGNED_LE && p_ctx->in->type->audio.bits_per_sample == 16) conversion = CONVERSION_NONE; if(p_ctx->out->codec_variant == VC_FOURCC('s','1','6','l') && (p_ctx->in->codec == VC_CONTAINER_CODEC_PCM_UNSIGNED_LE || p_ctx->in->codec == VC_CONTAINER_CODEC_PCM_UNSIGNED_BE) && p_ctx->in->type->audio.bits_per_sample == 8) conversion = CONVERSION_U8_TO_S16L; if(conversion == CONVERSION_UNKNOWN) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; p_ctx->priv->module = module = malloc(sizeof(*module)); if(!module) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; memset(module, 0, sizeof(*module)); module->conversion = conversion; module->conversion_factor = 1 << FACTOR_SHIFT; p_ctx->out->codec_variant = 0; if(conversion == CONVERSION_U8_TO_S16L) { module->conversion_factor = 2 << FACTOR_SHIFT; p_ctx->out->type->audio.bits_per_sample *= 2; p_ctx->out->type->audio.block_align *= 2; p_ctx->out->codec = VC_CONTAINER_CODEC_PCM_SIGNED_LE; } vc_container_time_set_samplerate(&p_ctx->priv->time, p_ctx->in->type->audio.sample_rate, 1); p_ctx->max_frame_size = FRAME_SIZE; module->max_frame_size = (FRAME_SIZE << FACTOR_SHIFT) / module->conversion_factor; module->bytes_per_sample = bytes_per_sample; module->samples_per_frame = module->max_frame_size / bytes_per_sample; p_ctx->priv->pf_close = pcm_packetizer_close; p_ctx->priv->pf_packetize = pcm_packetizer_packetize; p_ctx->priv->pf_reset = pcm_packetizer_reset; LOG_DEBUG(0, "using pcm audio packetizer"); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_PACKETIZER_REGISTER(pcm_packetizer_open, "pcm"); userland/containers/qsynth/000077500000000000000000000000001421703157200163705ustar00rootroot00000000000000userland/containers/qsynth/CMakeLists.txt000066400000000000000000000006021421703157200211260ustar00rootroot00000000000000# Container module needs to go in as a plugins so different prefix # and install path set(CMAKE_SHARED_LIBRARY_PREFIX "") # Make sure the compiler can find the necessary include files include_directories (../..) add_library(reader_qsynth ${LIBRARY_TYPE} qsynth_reader.c) target_link_libraries(reader_qsynth containers) install(TARGETS reader_qsynth DESTINATION ${VMCS_PLUGIN_DIR}) userland/containers/qsynth/qsynth_reader.c000066400000000000000000000352661421703157200214200ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_logging.h" /****************************************************************************** Defines. ******************************************************************************/ #define BI32(b) (((b)[0]<<24)|((b)[1]<<16)|((b)[2]<<8)|((b)[3])) #define BI16(b) (((b)[0]<<8)|((b)[1])) #define HEADER_LENGTH 14 #define MAX_TRACKS 128 /****************************************************************************** Type definitions ******************************************************************************/ struct _QSYNTH_SEGMENT_T { struct _QSYNTH_SEGMENT_T *next; uint32_t len; uint8_t *data; }; typedef struct _QSYNTH_SEGMENT_T QSYNTH_SEGMENT_T; typedef struct VC_CONTAINER_MODULE_T { VC_CONTAINER_TRACK_T *track; uint32_t filesize; QSYNTH_SEGMENT_T *seg; QSYNTH_SEGMENT_T *pass; uint32_t sent; int64_t timestamp; uint32_t seek; } VC_CONTAINER_MODULE_T; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T qsynth_reader_open( VC_CONTAINER_T * ); /****************************************************************************** Local Functions ******************************************************************************/ static VC_CONTAINER_STATUS_T qsynth_read_header(uint8_t *data, uint32_t *tracks, uint32_t *division, uint8_t *fps, uint8_t *dpf) { if(data[0] != 'M' || data[1] != 'T' || data[2] != 'h' || data[3] != 'd' || data[4] != 0 || data[5] != 0 || data[6] != 0 || data[7] != 6) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if(data[12] < 0x80) { if(division) *division = BI16(data+12); } else { if(fps) *fps = 256-data[12]; if(dpf) *dpf = data[13]; } if(tracks) *tracks = BI16(data+10); return VC_CONTAINER_SUCCESS; } static int qsynth_read_variable(uint8_t *data, uint32_t *val) { int i = 0; *val = 0; do { *val = (*val << 7) + (data[i] & 0x7f); } while(data[i++] & 0x80); return i; } static VC_CONTAINER_STATUS_T qsynth_read_event(uint8_t *data, uint32_t *used, uint8_t *last, uint32_t *time, uint32_t *tempo, uint32_t *end) { int read; // need at least 4 bytes here read = qsynth_read_variable(data, time); if(data[read] == 0xff) // meta event { uint32_t len; uint8_t type = data[read+1]; read += 2; read += qsynth_read_variable(data+read, &len); if(type == 0x2f) // end of track { if(len != 0) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; *end = 1; } else if(type == 0x51) // tempo event { if(len != 3) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; *tempo = (data[read]<<16) | (data[read+1]<<8) | data[read+2]; } read += len; } else if(data[read] == 0xf0 || data[read] == 0xf7) // sysex events { uint32_t len; read += 1; read += qsynth_read_variable(data+read, &len) + len; } else // midi event { uint8_t type; if(data[read] < 128) type = *last; else { type = data[read] >> 4; *last = type; read++; } switch(type) { case 8: case 9: case 0xa: case 0xb: case 0xe: read += 2; break; case 0xc: case 0xd: read += 1; break; default: return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; } } *used = read; return VC_CONTAINER_SUCCESS; } static VC_CONTAINER_STATUS_T qsynth_read_track(QSYNTH_SEGMENT_T *seg, uint32_t *ticks, int64_t *time, uint32_t *us_perclock, uint32_t *tempo_ticks) { uint32_t total_ticks = 0; uint32_t used = 8; uint8_t last = 0; *time = 0LL; *tempo_ticks = 0; while(used < seg->len) { VC_CONTAINER_STATUS_T status; uint32_t event_ticks, new_tempo = 0, end = 0, event_used; if((status = qsynth_read_event(seg->data+used, &event_used, &last, &event_ticks, &new_tempo, &end)) != VC_CONTAINER_SUCCESS) return status; used += event_used; total_ticks += event_ticks; if(new_tempo != 0) { *time += ((int64_t) (total_ticks - *tempo_ticks)) * (*us_perclock); *us_perclock = new_tempo; *tempo_ticks = total_ticks; } if(end) break; } *ticks = total_ticks; return VC_CONTAINER_SUCCESS; } static VC_CONTAINER_STATUS_T qsynth_get_duration(VC_CONTAINER_T *p_ctx) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status; QSYNTH_SEGMENT_T **seg = &(module->seg); uint32_t i, tracks, division = 0, max_ticks = 0, us_perclock = 500000; uint32_t end_uspc = 0, end_ticks = 0; int64_t end_time = 0; uint8_t fps = 1, dpf = 1; if((*seg = malloc(sizeof(QSYNTH_SEGMENT_T) + HEADER_LENGTH)) == NULL) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; (*seg)->next = NULL; (*seg)->len = HEADER_LENGTH; (*seg)->data = (uint8_t *) ((*seg) + 1); if(PEEK_BYTES(p_ctx, (*seg)->data, HEADER_LENGTH) != HEADER_LENGTH) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if((status = qsynth_read_header((*seg)->data, &tracks, &division, &fps, &dpf)) != VC_CONTAINER_SUCCESS) return status; // if we have a suspiciously large number of tracks, this is probably a bad file if(tracks > MAX_TRACKS) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; SKIP_BYTES(p_ctx, HEADER_LENGTH); seg = &((*seg)->next); module->filesize = HEADER_LENGTH; if(division == 0) { us_perclock = 1000000 / (fps * dpf); division = 1; } for(i=0; i (1<<20) || (*seg = malloc(sizeof(QSYNTH_SEGMENT_T) + 8 + len)) == NULL) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; module->filesize += len+8; (*seg)->next = NULL; (*seg)->len = len + 8; (*seg)->data = (uint8_t *) ((*seg) + 1); memcpy((*seg)->data, dummy, 8); if(READ_BYTES(p_ctx, (*seg)->data+8, len) != len) return VC_CONTAINER_ERROR_FORMAT_INVALID; if((status = qsynth_read_track(*seg, &ticks, &time, &us_perclock, &tempo_ticks)) != VC_CONTAINER_SUCCESS) return status; if(end_uspc == 0) { end_uspc = us_perclock; end_ticks = tempo_ticks; end_time = time; } if(ticks > max_ticks) max_ticks = ticks; seg = &((*seg)->next); } if(end_uspc == 0) return VC_CONTAINER_ERROR_FORMAT_INVALID; module->pass = module->seg; module->sent = 0; p_ctx->duration = (end_time + (((int64_t) (max_ticks - end_ticks)) * end_uspc)) / division; module->track->format->extradata = (uint8_t *) &module->filesize; module->track->format->extradata_size = 4; return VC_CONTAINER_SUCCESS; } /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ static VC_CONTAINER_STATUS_T qsynth_reader_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *packet, uint32_t flags ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; if(module->pass) { packet->size = module->pass->len - module->sent; packet->dts = packet->pts = 0; packet->track = 0; packet->flags = module->sent ? 0 : VC_CONTAINER_PACKET_FLAG_FRAME_START; } else { if(module->timestamp > p_ctx->duration) return VC_CONTAINER_ERROR_EOS; packet->size = 5; packet->dts = packet->pts = module->timestamp; packet->track = 0; packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME; } if(flags & VC_CONTAINER_READ_FLAG_SKIP) { if(module->pass) { module->pass = module->pass->next; module->sent = 0; } else { // if we're playing then we can't really skip, but have to simulate a seek instead module->seek = 1; module->timestamp += 40; } return VC_CONTAINER_SUCCESS; } if(flags & VC_CONTAINER_READ_FLAG_INFO) return VC_CONTAINER_SUCCESS; // read frame into packet->data if(module->pass) { uint32_t copy = MIN(packet->size, packet->buffer_size); memcpy(packet->data, module->pass->data + module->sent, copy); packet->size = copy; if((module->sent += copy) == module->pass->len) { module->pass = module->pass->next; module->sent = 0; packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; } } else { if(packet->buffer_size < packet->size) return VC_CONTAINER_ERROR_BUFFER_TOO_SMALL; if(module->seek) { uint32_t current_time = module->timestamp / 1000; packet->data[0] = 'S'; packet->data[1] = (uint8_t)((current_time >> 24) & 0xFF); packet->data[2] = (uint8_t)((current_time >> 16) & 0xFF); packet->data[3] = (uint8_t)((current_time >> 8) & 0xFF); packet->data[4] = (uint8_t)((current_time ) & 0xFF); module->seek = 0; } else { packet->data[0] = 'P'; packet->data[1] = 0; packet->data[2] = 0; packet->data[3] = 0; packet->data[4] = 40; module->timestamp += 40 * 1000; } } return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T qsynth_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_PARAM_UNUSED(flags); if (mode != VC_CONTAINER_SEEK_MODE_TIME) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; if(*offset < 0) *offset = 0; else if(*offset > p_ctx->duration) *offset = p_ctx->duration; module->timestamp = *offset; module->seek = 1; return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T qsynth_reader_close( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; QSYNTH_SEGMENT_T *seg = module->seg; for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); while(seg != NULL) { QSYNTH_SEGMENT_T *next = seg->next; free(seg); seg = next; } free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T qsynth_reader_open( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = 0; VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; uint8_t header[HEADER_LENGTH]; /* Check the file header */ if((PEEK_BYTES(p_ctx, header, HEADER_LENGTH) != HEADER_LENGTH) || qsynth_read_header(header, 0, 0, 0, 0) != VC_CONTAINER_SUCCESS) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Allocate our context */ module = malloc(sizeof(*module)); if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); p_ctx->priv->module = module; p_ctx->tracks_num = 1; p_ctx->tracks = &module->track; p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0); if(!p_ctx->tracks[0]) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } p_ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; p_ctx->tracks[0]->format->codec = VC_CONTAINER_CODEC_MIDI; p_ctx->tracks[0]->is_enabled = true; if((status = qsynth_get_duration(p_ctx)) != VC_CONTAINER_SUCCESS) goto error; LOG_DEBUG(p_ctx, "using qsynth reader"); p_ctx->capabilities = VC_CONTAINER_CAPS_CAN_SEEK; p_ctx->priv->pf_close = qsynth_reader_close; p_ctx->priv->pf_read = qsynth_reader_read; p_ctx->priv->pf_seek = qsynth_reader_seek; return VC_CONTAINER_SUCCESS; error: LOG_DEBUG(p_ctx, "qsynth: error opening stream (%i)", status); if(module) qsynth_reader_close(p_ctx); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak reader_open qsynth_reader_open #endif userland/containers/raw/000077500000000000000000000000001421703157200156335ustar00rootroot00000000000000userland/containers/raw/CMakeLists.txt000066400000000000000000000011051421703157200203700ustar00rootroot00000000000000# Container module needs to go in as a plugins so different prefix # and install path set(CMAKE_SHARED_LIBRARY_PREFIX "") # Make sure the compiler can find the necessary include files include_directories (../..) add_library(reader_raw_video ${LIBRARY_TYPE} raw_video_reader.c) target_link_libraries(reader_raw_video containers) install(TARGETS reader_raw_video DESTINATION ${VMCS_PLUGIN_DIR}) add_library(writer_raw_video ${LIBRARY_TYPE} raw_video_writer.c) target_link_libraries(writer_raw_video containers) install(TARGETS writer_raw_video DESTINATION ${VMCS_PLUGIN_DIR}) userland/containers/raw/raw_video_common.h000066400000000000000000000050431421703157200213350ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef RAW_VIDEO_COMMON_H #define RAW_VIDEO_COMMON_H static struct { const char *id; VC_CONTAINER_FOURCC_T codec; unsigned int size_num; unsigned int size_den; } table[] = { {"420", VC_CONTAINER_CODEC_I420, 3, 2}, {0, 0, 0, 0} }; STATIC_INLINE bool from_yuv4mpeg2(const char *id, VC_CONTAINER_FOURCC_T *codec, unsigned int *size_num, unsigned int *size_den) { unsigned int i; for (i = 0; table[i].id; i++) if (!strcmp(id, table[i].id)) break; if (codec) *codec = table[i].codec; if (size_num) *size_num = table[i].size_num; if (size_den) *size_den = table[i].size_den; return !!table[i].id; } STATIC_INLINE bool to_yuv4mpeg2(VC_CONTAINER_FOURCC_T codec, const char **id, unsigned int *size_num, unsigned int *size_den) { unsigned int i; for (i = 0; table[i].id; i++) if (codec == table[i].codec) break; if (id) *id = table[i].id; if (size_num) *size_num = table[i].size_num; if (size_den) *size_den = table[i].size_den; return !!table[i].id; } #endif /* RAW_VIDEO_COMMON_H */ userland/containers/raw/raw_video_reader.c000066400000000000000000000365721421703157200213150ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_logging.h" #include "raw_video_common.h" /****************************************************************************** Defines. ******************************************************************************/ #define FILE_HEADER_SIZE_MAX 1024 #define FRAME_HEADER_SIZE_MAX 256 #define OPTION_SIZE_MAX 32 /****************************************************************************** Type definitions ******************************************************************************/ typedef struct VC_CONTAINER_MODULE_T { VC_CONTAINER_TRACK_T *track; VC_CONTAINER_STATUS_T status; bool yuv4mpeg2; bool non_standard; char option[OPTION_SIZE_MAX]; bool frame_header; unsigned int frame_header_size; int64_t data_offset; unsigned int block_size; unsigned int block_offset; unsigned int frames; } VC_CONTAINER_MODULE_T; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T rawvideo_reader_open( VC_CONTAINER_T * ); /****************************************************************************** Local Functions ******************************************************************************/ static VC_CONTAINER_STATUS_T read_yuv4mpeg2_option( VC_CONTAINER_T *ctx, unsigned int *bytes_left ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; unsigned int size, i; /* Start by skipping spaces */ while (*bytes_left && PEEK_U8(ctx) == ' ') (*bytes_left)--, _SKIP_U8(ctx); size = PEEK_BYTES(ctx, module->option, MIN(sizeof(module->option), *bytes_left)); /* The config option ends at next space or newline */ for (i = 0; i < size; i++) { if (module->option[i] == ' ' || module->option[i] == 0x0a) { module->option[i] = 0; break; } } if (i == 0) return VC_CONTAINER_ERROR_NOT_FOUND; *bytes_left -= i; SKIP_BYTES(ctx, i); /* If option is too long, we just discard it */ if (i == size) { while (*bytes_left && PEEK_U8(ctx) != ' ' && PEEK_U8(ctx) != 0x0a) (*bytes_left)--, _SKIP_U8(ctx); return VC_CONTAINER_ERROR_NOT_FOUND; } return VC_CONTAINER_SUCCESS; } static VC_CONTAINER_STATUS_T read_yuv4mpeg2_file_header( VC_CONTAINER_T *ctx ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; unsigned int bytes_left = FILE_HEADER_SIZE_MAX - 10; unsigned int value1, value2; char codec[OPTION_SIZE_MAX] = "420"; uint8_t h[10]; /* Check for the YUV4MPEG2 signature */ if (READ_BYTES(ctx, h, sizeof(h)) != sizeof(h)) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if (memcmp(h, "YUV4MPEG2 ", sizeof(h))) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Parse parameters */ while (read_yuv4mpeg2_option(ctx, &bytes_left) == VC_CONTAINER_SUCCESS) { if (sscanf(module->option, "W%i", &value1) == 1) ctx->tracks[0]->format->type->video.width = value1; else if (sscanf(module->option, "H%i", &value1) == 1) ctx->tracks[0]->format->type->video.height = value1; else if (sscanf(module->option, "S%i", &value1) == 1) module->block_size = value1; else if (sscanf(module->option, "F%i:%i", &value1, &value2) == 2) { ctx->tracks[0]->format->type->video.frame_rate_num = value1; ctx->tracks[0]->format->type->video.frame_rate_den = value2; } else if (sscanf(module->option, "A%i:%i", &value1, &value2) == 2) { ctx->tracks[0]->format->type->video.par_num = value1; ctx->tracks[0]->format->type->video.par_den = value2; } else if (module->option[0] == 'C') { strcpy(codec, module->option+1); } } /* Check the end marker */ if (_READ_U8(ctx) != 0x0a) { LOG_ERROR(ctx, "missing end of header marker"); return VC_CONTAINER_ERROR_CORRUPTED; } /* Find out which codec we are dealing with */ if (from_yuv4mpeg2(codec, &ctx->tracks[0]->format->codec, &value1, &value2)) { module->block_size = ctx->tracks[0]->format->type->video.width * ctx->tracks[0]->format->type->video.height * value1 / value2; } else { memcpy(&ctx->tracks[0]->format->codec, codec, 4); module->non_standard = true; } return VC_CONTAINER_SUCCESS; } static VC_CONTAINER_STATUS_T read_yuv4mpeg2_frame_header( VC_CONTAINER_T *ctx ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; unsigned int bytes_left = FRAME_HEADER_SIZE_MAX - 5; unsigned int value1; char header[5]; if (READ_BYTES(ctx, header, sizeof(header)) != sizeof(header) || memcmp(header, "FRAME", sizeof(header))) { LOG_ERROR(ctx, "missing frame marker"); return STREAM_STATUS(ctx) != VC_CONTAINER_SUCCESS ? STREAM_STATUS(ctx) : VC_CONTAINER_ERROR_CORRUPTED; } /* Parse parameters */ while (read_yuv4mpeg2_option(ctx, &bytes_left) == VC_CONTAINER_SUCCESS) { if (module->non_standard && sscanf(module->option, "S%i", &value1) == 1) module->block_size = value1; } /* Check the end marker */ if (_READ_U8(ctx) != 0x0a) { LOG_ERROR(ctx, "missing end of frame header marker"); return VC_CONTAINER_ERROR_CORRUPTED; } module->frame_header_size = FRAME_HEADER_SIZE_MAX - bytes_left - 1; return VC_CONTAINER_SUCCESS; } static VC_CONTAINER_STATUS_T rawvideo_parse_uri( VC_CONTAINER_T *ctx, VC_CONTAINER_FOURCC_T *c, unsigned int *w, unsigned int *h, unsigned int *fr_num, unsigned int *fr_den, unsigned *block_size ) { VC_CONTAINER_FOURCC_T codec = 0; unsigned int i, matches, width = 0, height = 0, fn = 0, fd = 0, size = 0; const char *uri = ctx->priv->io->uri; /* Try and find a match for the string describing the format */ for (i = 0; uri[i]; i++) { if (uri[i] != '_' && uri[i+1] != 'C') continue; matches = sscanf(uri+i, "_C%4cW%iH%iF%i#%iS%i", (char *)&codec, &width, &height, &fn, &fd, &size); if (matches >= 3) break; } if (!uri[i]) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if (!size) { switch (codec) { case VC_CONTAINER_CODEC_I420: case VC_CONTAINER_CODEC_YV12: size = width * height * 3 / 2; break; default: break; } } if (!width || !height || !size) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if (block_size) *block_size = size; if (c) *c = codec; if (w) *w = width; if (h) *h = height; if (fr_num) *fr_num = fn; if (fr_den) *fr_den = fd; if (block_size) *block_size = size; return VC_CONTAINER_SUCCESS; } /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ static VC_CONTAINER_STATUS_T rawvideo_reader_read( VC_CONTAINER_T *ctx, VC_CONTAINER_PACKET_T *packet, uint32_t flags ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; unsigned int size; if (module->status != VC_CONTAINER_SUCCESS) return module->status; if (module->yuv4mpeg2 && !module->block_offset && !module->frame_header) { module->status = read_yuv4mpeg2_frame_header(ctx); if (module->status != VC_CONTAINER_SUCCESS) return module->status; module->frame_header = true; } if (!module->block_offset) packet->pts = packet->dts = module->frames * INT64_C(1000000) * ctx->tracks[0]->format->type->video.frame_rate_den / ctx->tracks[0]->format->type->video.frame_rate_num; else packet->pts = packet->dts = VC_CONTAINER_TIME_UNKNOWN; packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END | VC_CONTAINER_PACKET_FLAG_KEYFRAME; if (!module->block_offset) packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; packet->frame_size = module->block_size; packet->size = module->block_size - module->block_offset; packet->track = 0; if (flags & VC_CONTAINER_READ_FLAG_SKIP) { size = SKIP_BYTES(ctx, packet->size); module->block_offset = 0; module->frames++; module->frame_header = 0; module->status = STREAM_STATUS(ctx); return module->status; } if (flags & VC_CONTAINER_READ_FLAG_INFO) return VC_CONTAINER_SUCCESS; size = MIN(module->block_size - module->block_offset, packet->buffer_size); size = READ_BYTES(ctx, packet->data, size); module->block_offset += size; packet->size = size; if (module->block_offset == module->block_size) { module->block_offset = 0; module->frame_header = 0; module->frames++; } module->status = size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(ctx); return module->status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T rawvideo_reader_seek( VC_CONTAINER_T *ctx, int64_t *offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; VC_CONTAINER_PARAM_UNUSED(mode); module->frames = *offset * ctx->tracks[0]->format->type->video.frame_rate_num / ctx->tracks[0]->format->type->video.frame_rate_den / INT64_C(1000000); module->block_offset = 0; if ((flags & VC_CONTAINER_SEEK_FLAG_FORWARD) && module->frames * INT64_C(1000000) * ctx->tracks[0]->format->type->video.frame_rate_den / ctx->tracks[0]->format->type->video.frame_rate_num < *offset) module->frames++; module->frame_header = 0; module->status = SEEK(ctx, module->data_offset + module->frames * (module->block_size + module->frame_header_size)); return module->status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T rawvideo_reader_close( VC_CONTAINER_T *ctx ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; for (; ctx->tracks_num > 0; ctx->tracks_num--) vc_container_free_track(ctx, ctx->tracks[ctx->tracks_num-1]); free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T rawvideo_reader_open( VC_CONTAINER_T *ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; const char *extension = vc_uri_path_extension(ctx->priv->uri); VC_CONTAINER_MODULE_T *module = 0; bool yuv4mpeg2 = false; uint8_t h[10]; /* Check if the user has specified a container */ vc_uri_find_query(ctx->priv->uri, 0, "container", &extension); /* Check for the YUV4MPEG2 signature */ if (PEEK_BYTES(ctx, h, sizeof(h)) != sizeof(h)) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if (!memcmp(h, "YUV4MPEG2 ", sizeof(h))) yuv4mpeg2 = true; /* Or check if the extension is supported */ if (!yuv4mpeg2 && !(extension && !strcasecmp(extension, "yuv"))) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; LOG_DEBUG(ctx, "using raw video reader"); /* Allocate our context */ module = malloc(sizeof(*module)); if (!module) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; memset(module, 0, sizeof(*module)); ctx->priv->module = module; ctx->tracks_num = 1; ctx->tracks = &module->track; ctx->tracks[0] = vc_container_allocate_track(ctx, 0); if (!ctx->tracks[0]) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; ctx->tracks[0]->is_enabled = true; ctx->tracks[0]->format->type->video.frame_rate_num = 25; ctx->tracks[0]->format->type->video.frame_rate_den = 1; ctx->tracks[0]->format->type->video.par_num = 1; ctx->tracks[0]->format->type->video.par_den = 1; if (yuv4mpeg2) { status = read_yuv4mpeg2_file_header(ctx); if (status != VC_CONTAINER_SUCCESS) goto error; module->data_offset = STREAM_POSITION(ctx); status = read_yuv4mpeg2_frame_header(ctx); if (status != VC_CONTAINER_SUCCESS) goto error; module->frame_header = true; } else { VC_CONTAINER_FOURCC_T codec; unsigned int width, height, fr_num, fr_den, block_size; status = rawvideo_parse_uri(ctx, &codec, &width, &height, &fr_num, &fr_den, &block_size); if (status != VC_CONTAINER_SUCCESS) goto error; ctx->tracks[0]->format->codec = codec; ctx->tracks[0]->format->type->video.width = width; ctx->tracks[0]->format->type->video.height = height; if (fr_num && fr_den) { ctx->tracks[0]->format->type->video.frame_rate_num = fr_num; ctx->tracks[0]->format->type->video.frame_rate_den = fr_den; } module->block_size = block_size; } /* * We now have all the information we really need to start playing the stream */ LOG_INFO(ctx, "rawvideo %4.4s/%ix%i/fps:%i:%i/size:%i", (char *)&ctx->tracks[0]->format->codec, ctx->tracks[0]->format->type->video.width, ctx->tracks[0]->format->type->video.height, ctx->tracks[0]->format->type->video.frame_rate_num, ctx->tracks[0]->format->type->video.frame_rate_den, module->block_size); ctx->priv->pf_close = rawvideo_reader_close; ctx->priv->pf_read = rawvideo_reader_read; ctx->priv->pf_seek = rawvideo_reader_seek; module->yuv4mpeg2 = yuv4mpeg2; return VC_CONTAINER_SUCCESS; error: LOG_DEBUG(ctx, "rawvideo: error opening stream (%i)", status); rawvideo_reader_close(ctx); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak reader_open rawvideo_reader_open #endif userland/containers/raw/raw_video_writer.c000066400000000000000000000225171421703157200213610ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_logging.h" #include "raw_video_common.h" /****************************************************************************** Defines. ******************************************************************************/ /****************************************************************************** Type definitions ******************************************************************************/ typedef struct VC_CONTAINER_MODULE_T { VC_CONTAINER_TRACK_T *track; bool yuv4mpeg2; bool header_done; bool non_standard; } VC_CONTAINER_MODULE_T; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T rawvideo_writer_open( VC_CONTAINER_T * ); /****************************************************************************** Local Functions ******************************************************************************/ static VC_CONTAINER_STATUS_T rawvideo_write_header( VC_CONTAINER_T *ctx ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; unsigned int size; char line[128]; const char *id; size = snprintf(line, sizeof(line), "YUV4MPEG2 W%i H%i", ctx->tracks[0]->format->type->video.width, ctx->tracks[0]->format->type->video.height); if (size >= sizeof(line)) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; WRITE_BYTES(ctx, line, size); if (ctx->tracks[0]->format->type->video.frame_rate_num && ctx->tracks[0]->format->type->video.frame_rate_den) { size = snprintf(line, sizeof(line), " F%i:%i", ctx->tracks[0]->format->type->video.frame_rate_num, ctx->tracks[0]->format->type->video.frame_rate_den); if (size >= sizeof(line)) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; WRITE_BYTES(ctx, line, size); } if (ctx->tracks[0]->format->type->video.par_num && ctx->tracks[0]->format->type->video.par_den) { size = snprintf(line, sizeof(line), " A%i:%i", ctx->tracks[0]->format->type->video.par_num, ctx->tracks[0]->format->type->video.par_den); if (size >= sizeof(line)) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; WRITE_BYTES(ctx, line, size); } if (to_yuv4mpeg2(ctx->tracks[0]->format->codec, &id, 0, 0)) { size = snprintf(line, sizeof(line), " C%s", id); } else { module->non_standard = true; size = snprintf(line, sizeof(line), " C%4.4s", (char *)&ctx->tracks[0]->format->codec); } if (size >= sizeof(line)) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; WRITE_BYTES(ctx, line, size); _WRITE_U8(ctx, 0x0a); module->header_done = true; return STREAM_STATUS(ctx); } static VC_CONTAINER_STATUS_T simple_write_add_track( VC_CONTAINER_T *ctx, VC_CONTAINER_ES_FORMAT_T *format ) { VC_CONTAINER_STATUS_T status; /* Sanity check that we support the type of track being created */ if (ctx->tracks_num) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; if (format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; /* Allocate and initialise track data */ ctx->tracks[0] = vc_container_allocate_track(ctx, 0); if (!ctx->tracks[0]) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; status = vc_container_track_allocate_extradata(ctx, ctx->tracks[0], format->extradata_size); if(status != VC_CONTAINER_SUCCESS) return status; vc_container_format_copy(ctx->tracks[0]->format, format, format->extradata_size); ctx->tracks_num++; return VC_CONTAINER_SUCCESS; } /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ static VC_CONTAINER_STATUS_T rawvideo_writer_close( VC_CONTAINER_T *ctx ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; for (; ctx->tracks_num > 0; ctx->tracks_num--) vc_container_free_track(ctx, ctx->tracks[ctx->tracks_num-1]); free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T rawvideo_writer_write( VC_CONTAINER_T *ctx, VC_CONTAINER_PACKET_T *packet ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; VC_CONTAINER_STATUS_T status; if (module->yuv4mpeg2 && !module->header_done) { status = rawvideo_write_header(ctx); if (status != VC_CONTAINER_SUCCESS) return status; } if (module->yuv4mpeg2 && (packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START)) { /* Write the metadata */ WRITE_BYTES(ctx, "FRAME", sizeof("FRAME")-1); /* For formats not supported by the YUV4MPEG2 spec, we prepend * each frame with its size */ if (module->non_standard) { unsigned int size; char line[32]; size = snprintf(line, sizeof(line), " S%i", packet->frame_size ? packet->frame_size : packet->size); if (size < sizeof(line)) WRITE_BYTES(ctx, line, size); } _WRITE_U8(ctx, 0x0a); } /* Write the elementary stream */ WRITE_BYTES(ctx, packet->data, packet->size); return STREAM_STATUS(ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T rawvideo_writer_control( VC_CONTAINER_T *ctx, VC_CONTAINER_CONTROL_T operation, va_list args ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; VC_CONTAINER_ES_FORMAT_T *format; switch (operation) { case VC_CONTAINER_CONTROL_TRACK_ADD: format = (VC_CONTAINER_ES_FORMAT_T *)va_arg(args, VC_CONTAINER_ES_FORMAT_T *); return simple_write_add_track(ctx, format); case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: return module->yuv4mpeg2 ? rawvideo_write_header( ctx ) : VC_CONTAINER_SUCCESS; default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; } } /*****************************************************************************/ VC_CONTAINER_STATUS_T rawvideo_writer_open( VC_CONTAINER_T *ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; const char *extension = vc_uri_path_extension(ctx->priv->uri); VC_CONTAINER_MODULE_T *module; bool yuv4mpeg2 = false; /* Check if the user has specified a container */ vc_uri_find_query(ctx->priv->uri, 0, "container", &extension); /* Check we're the right writer for this */ if(!extension) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if(!strcasecmp(extension, "y4m") || !strcasecmp(extension, "yuv4mpeg2")) yuv4mpeg2 = true; if(!yuv4mpeg2 && strcasecmp(extension, "yuv")) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; LOG_DEBUG(ctx, "using rawvideo writer"); /* Allocate our context */ module = malloc(sizeof(*module)); if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); ctx->priv->module = module; ctx->tracks = &module->track; module->yuv4mpeg2 = yuv4mpeg2; ctx->priv->pf_close = rawvideo_writer_close; ctx->priv->pf_write = rawvideo_writer_write; ctx->priv->pf_control = rawvideo_writer_control; return VC_CONTAINER_SUCCESS; error: LOG_DEBUG(ctx, "rawvideo: error opening stream (%i)", status); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak writer_open rawvideo_writer_open #endif userland/containers/rcv/000077500000000000000000000000001421703157200156345ustar00rootroot00000000000000userland/containers/rcv/CMakeLists.txt000066400000000000000000000005661421703157200204030ustar00rootroot00000000000000# Container module needs to go in as a plugins so different prefix # and install path set(CMAKE_SHARED_LIBRARY_PREFIX "") # Make sure the compiler can find the necessary include files include_directories (../..) add_library(reader_rcv ${LIBRARY_TYPE} rcv_reader.c) target_link_libraries(reader_rcv containers) install(TARGETS reader_rcv DESTINATION ${VMCS_PLUGIN_DIR}) userland/containers/rcv/rcv_reader.c000066400000000000000000000314231421703157200201170ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_index.h" #include "containers/core/containers_logging.h" /****************************************************************************** Defines. ******************************************************************************/ #define LI32(b) (((b)[3]<<24)|((b)[2]<<16)|((b)[1]<<8)|((b)[0])) #define LI24(b) (((b)[2]<<16)|((b)[1]<<8)|((b)[0])) /****************************************************************************** Type definitions ******************************************************************************/ typedef struct { unsigned int num_frames : 24; unsigned int constant_c5 : 8; int constant_4; uint32_t struct_c; uint32_t vert_size; uint32_t horiz_size; int constant_c; uint32_t struct_b[2]; uint32_t framerate; } RCV_FILE_HEADER_T; typedef struct { unsigned int framesize : 24; unsigned int res : 7; unsigned int keyframe : 1; uint32_t timestamp; } RCV_FRAME_HEADER_T; typedef struct VC_CONTAINER_MODULE_T { VC_CONTAINER_TRACK_T *track; uint8_t extradata[4]; uint8_t mid_frame; uint32_t frame_read; RCV_FRAME_HEADER_T frame; VC_CONTAINER_INDEX_T *index; /* index of key frames */ } VC_CONTAINER_MODULE_T; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T * ); /****************************************************************************** Local Functions ******************************************************************************/ static VC_CONTAINER_STATUS_T rcv_read_header(VC_CONTAINER_T *p_ctx) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; RCV_FILE_HEADER_T header; uint8_t dummy[36]; if(PEEK_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy)) return VC_CONTAINER_ERROR_EOS; header.num_frames = LI24(dummy); header.constant_c5 = dummy[3]; header.constant_4 = LI32(dummy+4); // extradata is just struct_c from the header memcpy(module->extradata, dummy+8, 4); module->track->format->extradata = module->extradata; module->track->format->extradata_size = 4; module->track->format->type->video.height = LI32(dummy+12); module->track->format->type->video.width = LI32(dummy+16); header.constant_c = LI32(dummy+20); memcpy(header.struct_b, dummy+24, 8); header.framerate = LI32(dummy+32); if(header.constant_c5 != 0xc5 || header.constant_4 != 0x4 || header.constant_c != 0xc) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if(header.framerate != 0 && header.framerate != 0xffffffffUL) { module->track->format->type->video.frame_rate_num = header.framerate; module->track->format->type->video.frame_rate_den = 1; } // fill in general information if(header.num_frames != (1<<24)-1 && header.framerate != 0 && header.framerate != 0xffffffffUL) p_ctx->duration = ((int64_t) header.num_frames * 1000000LL) / (int64_t) header.framerate; // we're happy that this is an rcv file SKIP_BYTES(p_ctx, sizeof(dummy)); return STREAM_STATUS(p_ctx); } /***************************************************************************** * Utility function to seek to the keyframe nearest the given timestamp. * * @param p_ctx Pointer to the container context. * @param timestamp The requested time. On success, this is updated with the time of the selected keyframe. * @param later If true, the selected frame is the earliest keyframe with a time greater or equal to timestamp. * If false, the selected frame is the latest keyframe with a time earlier or equal to timestamp. * @return Status code. */ static VC_CONTAINER_STATUS_T rcv_seek_nearest_keyframe(VC_CONTAINER_T *p_ctx, int64_t *timestamp, int later) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; int64_t prev_keyframe_offset = sizeof(RCV_FILE_HEADER_T); /* set to very first frame */ int64_t prev_keyframe_timestamp = 0; int use_prev_keyframe = !later; if(use_prev_keyframe || (module->frame.timestamp * 1000LL > *timestamp)) { /* A seek has been requested to an earlier keyframe, so rewind to the beginning * of the stream since there's no information available on previous frames */ SEEK(p_ctx, sizeof(RCV_FILE_HEADER_T)); memset(&module->frame, 0, sizeof(RCV_FRAME_HEADER_T)); module->mid_frame = 0; module->frame_read = 0; } if(module->mid_frame) { /* Seek back to the start of the current frame */ SEEK(p_ctx, STREAM_POSITION(p_ctx) - module->frame_read - sizeof(RCV_FILE_HEADER_T)); module->mid_frame = 0; module->frame_read = 0; } while(1) { if(PEEK_BYTES(p_ctx, &module->frame, sizeof(RCV_FRAME_HEADER_T)) != sizeof(RCV_FRAME_HEADER_T)) { status = VC_CONTAINER_ERROR_EOS; break; } if(module->frame.keyframe) { if(module->index) vc_container_index_add(module->index, module->frame.timestamp * 1000LL, STREAM_POSITION(p_ctx)); if((module->frame.timestamp * 1000LL) >= *timestamp) { if((module->frame.timestamp * 1000LL) == *timestamp) use_prev_keyframe = 0; *timestamp = module->frame.timestamp * 1000LL; break; } prev_keyframe_offset = STREAM_POSITION(p_ctx); prev_keyframe_timestamp = module->frame.timestamp * 1000LL; } SKIP_BYTES(p_ctx, module->frame.framesize + sizeof(RCV_FRAME_HEADER_T)); } if(use_prev_keyframe) { *timestamp = prev_keyframe_timestamp; status = SEEK(p_ctx, prev_keyframe_offset); } return status; } /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ static VC_CONTAINER_STATUS_T rcv_reader_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *packet, uint32_t flags ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; unsigned int size; if(!module->mid_frame) { /* Save the current position for updating the indexer */ int64_t position = STREAM_POSITION(p_ctx); if(READ_BYTES(p_ctx, &module->frame, sizeof(RCV_FRAME_HEADER_T)) != sizeof(RCV_FRAME_HEADER_T)) return VC_CONTAINER_ERROR_EOS; module->mid_frame = 1; module->frame_read = 0; if(module->index && module->frame.keyframe) vc_container_index_add(module->index, (int64_t)module->frame.timestamp * 1000LL, position); } packet->size = module->frame.framesize; packet->dts = packet->pts = module->frame.timestamp * 1000LL; packet->track = 0; packet->flags = 0; if(module->frame_read == 0) packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; if(module->frame.keyframe) packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; if(flags & VC_CONTAINER_READ_FLAG_SKIP) { size = SKIP_BYTES(p_ctx, module->frame.framesize - module->frame_read); if((module->frame_read += size) == module->frame.framesize) { module->frame_read = 0; module->mid_frame = 0; } return STREAM_STATUS(p_ctx); } if(flags & VC_CONTAINER_READ_FLAG_INFO) return VC_CONTAINER_SUCCESS; size = MIN(module->frame.framesize - module->frame_read, packet->buffer_size); size = READ_BYTES(p_ctx, packet->data, size); if((module->frame_read += size) == module->frame.framesize) { module->frame_read = 0; module->mid_frame = 0; packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; } packet->size = size; return size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T rcv_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) { int past = 1; int64_t position; int64_t timestamp = *offset; VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FAILED; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_PARAM_UNUSED(mode); if(module->index) status = vc_container_index_get(module->index, flags & VC_CONTAINER_SEEK_FLAG_FORWARD, ×tamp, &position, &past); if(status == VC_CONTAINER_SUCCESS && !past) { /* Indexed keyframe found */ module->frame_read = 0; module->mid_frame = 0; *offset = timestamp; status = SEEK(p_ctx, position); } else { /* No indexed keyframe found, so seek through all frames */ status = rcv_seek_nearest_keyframe(p_ctx, offset, flags & VC_CONTAINER_SEEK_FLAG_FORWARD); } return status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T rcv_reader_close( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); if(module->index) vc_container_index_free(module->index); free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = 0; VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; uint8_t dummy[8]; /* Quick check for a valid file header */ if((PEEK_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy)) || dummy[3] != 0xc5 || LI32(dummy+4) != 0x4) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Allocate our context */ module = malloc(sizeof(*module)); if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); p_ctx->priv->module = module; p_ctx->tracks_num = 1; p_ctx->tracks = &module->track; p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0); if(!p_ctx->tracks[0]) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } p_ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; p_ctx->tracks[0]->format->codec = VC_CONTAINER_CODEC_WMV3; p_ctx->tracks[0]->is_enabled = true; if((status = rcv_read_header(p_ctx)) != VC_CONTAINER_SUCCESS) goto error; LOG_DEBUG(p_ctx, "using rcv reader"); if(vc_container_index_create(&module->index, 512) == VC_CONTAINER_SUCCESS) vc_container_index_add(module->index, 0LL, STREAM_POSITION(p_ctx)); if(STREAM_SEEKABLE(p_ctx)) p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; p_ctx->priv->pf_close = rcv_reader_close; p_ctx->priv->pf_read = rcv_reader_read; p_ctx->priv->pf_seek = rcv_reader_seek; return VC_CONTAINER_SUCCESS; error: if(module) rcv_reader_close(p_ctx); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak reader_open rcv_reader_open #endif userland/containers/rtp/000077500000000000000000000000001421703157200156475ustar00rootroot00000000000000userland/containers/rtp/CMakeLists.txt000066400000000000000000000010161421703157200204050ustar00rootroot00000000000000# Container module needs to go in as a plugins so different prefix # and install path set(CMAKE_SHARED_LIBRARY_PREFIX "") # Make sure the compiler can find the necessary include files include_directories (../..) set(rtp_SRCS ${rtp_SRCS} rtp_reader.c) set(rtp_SRCS ${rtp_SRCS} rtp_h264.c) set(rtp_SRCS ${rtp_SRCS} rtp_mpeg4.c) set(rtp_SRCS ${rtp_SRCS} rtp_base64.c) add_library(reader_rtp ${LIBRARY_TYPE} ${rtp_SRCS}) target_link_libraries(reader_rtp containers) install(TARGETS reader_rtp DESTINATION ${VMCS_PLUGIN_DIR}) userland/containers/rtp/rtp_base64.c000066400000000000000000000145631421703157200177750ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include "rtp_base64.h" /****************************************************************************** Defines and constants. ******************************************************************************/ #define LOWEST_BASE64_CHAR '+' #define HIGHEST_BASE64_CHAR 'z' #define IN_BASE64_RANGE(C) ((C) >= LOWEST_BASE64_CHAR && (C) <= HIGHEST_BASE64_CHAR) /** Used as a marker in the lookup table to indicate an invalid Base64 character */ #define INVALID 0xFF /* Reduced lookup table for translating a character to a 6-bit value. The * table starts at the lowest Base64 character, '+' */ uint8_t base64_decode_lookup[] = { 62, INVALID, 62, INVALID, 63, /* '+' to '/' */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, /* '0' to '9' */ INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, /* ':' to '@' */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* 'A' to 'T' */ 20, 21, 22, 23, 24, 25, /* 'U' to 'Z' */ INVALID, INVALID, INVALID, INVALID, 63, INVALID, /* '[' to '`' */ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, /* 'a' to 'r' */ 44, 45, 46, 47, 48, 49, 50, 51 /* 's' to 'z' */ }; /****************************************************************************** Type definitions ******************************************************************************/ /****************************************************************************** Function prototypes ******************************************************************************/ /****************************************************************************** Local Functions ******************************************************************************/ /***************************************************************************** Functions exported as part of the Base64 API *****************************************************************************/ /*****************************************************************************/ uint32_t rtp_base64_byte_length(const char *str, uint32_t str_len) { uint32_t character_count = 0; uint32_t ii; char cc; /* Scan through string until either a pad ('=') character or the end is * reached. Ignore characters that are not part of the Base64 alphabet. * Number of bytes should then be 3/4 of the character count. */ for (ii = 0; ii < str_len; ii++) { cc = *str++; if (cc == '=') break; /* Found a pad character: stop */ if (!IN_BASE64_RANGE(cc)) continue; /* Ignore invalid character */ if (base64_decode_lookup[cc - LOWEST_BASE64_CHAR] != INVALID) character_count++; } return (character_count * 3) >> 2; } /*****************************************************************************/ uint8_t *rtp_base64_decode(const char *str, uint32_t str_len, uint8_t *buffer, uint32_t buffer_len) { uint32_t character_count = 0; uint32_t value = 0; uint32_t ii; char cc; uint8_t lookup; /* Build up sets of four characters (ignoring invalid ones) to generate * triplets of bytes, until either the end of the string or the pad ('=') * characters are reached. */ for (ii = 0; ii < str_len; ii++) { cc = *str++; if (cc == '=') break; /* Found a pad character: stop */ if (!IN_BASE64_RANGE(cc)) continue; /* Ignore invalid character */ lookup = base64_decode_lookup[cc - LOWEST_BASE64_CHAR]; if (lookup == INVALID) continue; /* Ignore invalid character */ value = (value << 6) | lookup; character_count++; if (character_count == 4) { if (buffer_len < 3) return NULL; /* Not enough room in the output buffer */ *buffer++ = (uint8_t)(value >> 16); *buffer++ = (uint8_t)(value >> 8); *buffer++ = (uint8_t)(value ); buffer_len -= 3; character_count = 0; value = 0; } } /* If there were extra characters on the end, these need to be handled to get * the last one or two bytes. */ switch (character_count) { case 0: /* Nothing more to do, the final bytes were converted in the loop */ break; case 2: /* One additional byte, padded with four zero bits */ if (!buffer_len) return NULL; *buffer++ = (uint8_t)(value >> 4); break; case 3: /* Two additional bytes, padded with two zero bits */ if (buffer_len < 2) return NULL; *buffer++ = (uint8_t)(value >> 10); *buffer++ = (uint8_t)(value >> 2); break; default: /* This is an invalid Base64 encoding */ return NULL; } /* Return number of bytes written to the buffer */ return buffer; } userland/containers/rtp/rtp_base64.h000066400000000000000000000045561421703157200200030ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef _RTP_BASE64_H_ #define _RTP_BASE64_H_ #include "containers/containers.h" /** Returns the number of bytes encoded by the given Base64 encoded string. * * \param str The Base64 encoded string. * \param str_len The number of characters in the string. * \return The number of bytes that can be decoded. */ uint32_t rtp_base64_byte_length(const char *str, uint32_t str_len); /** Decodes a Base64 encoded string into a byte buffer. * * \param str The Base64 encoded string. * \param str_len The number of characters in the string. * \param buffer The buffer to receive the decoded output. * \param buffer_len The maximum number of bytes to put in the buffer. * \return Pointer to byte after the last one converted, or NULL on error. */ uint8_t *rtp_base64_decode(const char *str, uint32_t str_len, uint8_t *buffer, uint32_t buffer_len); #endif /* _RTP_BASE64_H_ */ userland/containers/rtp/rtp_h264.c000066400000000000000000000710571421703157200173750ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include "containers/containers.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_list.h" #include "containers/core/containers_bits.h" #include "rtp_priv.h" #include "rtp_base64.h" #include "rtp_h264.h" /****************************************************************************** Defines and constants. ******************************************************************************/ /** H.264 payload flag bits */ typedef enum { H264F_NEXT_PACKET_IS_START = 0, H264F_INSIDE_FRAGMENT, H264F_OUTPUT_NAL_HEADER, } h264_flag_bit_t; /** Bit mask to extract F zero bit from NAL unit header */ #define NAL_UNIT_FZERO_MASK 0x80 /** Bit mask to extract NAL unit type from NAL unit header */ #define NAL_UNIT_TYPE_MASK 0x1F /** NAL unit type codes */ enum { /* 0 unspecified */ NAL_UNIT_NON_IDR = 1, NAL_UNIT_PARTITION_A = 2, NAL_UNIT_PARTITION_B = 3, NAL_UNIT_PARTITION_C = 4, NAL_UNIT_IDR = 5, NAL_UNIT_SEI = 6, NAL_UNIT_SEQUENCE_PARAMETER_SET = 7, NAL_UNIT_PICTURE_PARAMETER_SET = 8, NAL_UNIT_ACCESS_UNIT_DELIMITER = 9, NAL_UNIT_END_OF_SEQUENCE = 10, NAL_UNIT_END_OF_STREAM = 11, NAL_UNIT_FILLER = 12, NAL_UNIT_EXT_SEQUENCE_PARAMETER_SET = 13, NAL_UNIT_PREFIX = 14, NAL_UNIT_SUBSET_SEQUENCE_PARAMETER_SET = 15, /* 16 to 18 reserved */ NAL_UNIT_AUXILIARY = 19, NAL_UNIT_EXTENSION = 20, /* 21 to 23 reserved */ NAL_UNIT_STAP_A = 24, NAL_UNIT_STAP_B = 25, NAL_UNIT_MTAP16 = 26, NAL_UNIT_MTAP24 = 27, NAL_UNIT_FU_A = 28, NAL_UNIT_FU_B = 29, /* 30 to 31 unspecified */ }; /** Fragment unit header indicator bits */ typedef enum { FRAGMENT_UNIT_HEADER_RESERVED = 5, FRAGMENT_UNIT_HEADER_END = 6, FRAGMENT_UNIT_HEADER_START = 7, } fragment_unit_header_bit_t; #define MACROBLOCK_WIDTH 16 #define MACROBLOCK_HEIGHT 16 /** H.264 RTP timestamp clock rate */ #define H264_TIMESTAMP_CLOCK 90000 typedef enum { CHROMA_FORMAT_MONO = 0, CHROMA_FORMAT_YUV_420 = 1, CHROMA_FORMAT_YUV_422 = 2, CHROMA_FORMAT_YUV_444 = 3, CHROMA_FORMAT_YUV_444_PLANAR = 4, CHROMA_FORMAT_RGB = 5, } CHROMA_FORMAT_T; uint32_t chroma_sub_width[] = { 1, 2, 2, 1, 1, 1 }; uint32_t chroma_sub_height[] = { 1, 2, 1, 1, 1, 1 }; /****************************************************************************** Type definitions ******************************************************************************/ typedef struct h264_payload_tag { uint32_t nal_unit_size; /**< Number of NAL unit bytes left to write */ uint8_t flags; /**< H.264 payload flags */ uint8_t header_bytes_to_write; /**< Number of start code bytes left to write */ uint8_t nal_header; /**< Header for next NAL unit */ } H264_PAYLOAD_T; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T h264_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); /****************************************************************************** Local Functions ******************************************************************************/ /**************************************************************************//** * Remove emulation prevention bytes from a buffer. * These are 0x03 bytes inserted to prevent misinterprentation of a byte * sequence in a buffer as a start code. * * @param sprop The buffer from which bytes are to be removed. * @param sprop_size The number of bytes in the buffer. * @return The new number of bytes in the buffer. */ static uint32_t h264_remove_emulation_prevention_bytes(uint8_t *sprop, uint32_t sprop_size) { uint32_t offset = 0; uint8_t nal_unit_type = sprop[offset++]; uint32_t new_sprop_size = sprop_size; uint8_t first_byte, second_byte; nal_unit_type &= 0x1F; /* Just keep NAL unit type bits */ /* Certain NAL unit types need a byte triplet passed first */ if (nal_unit_type == NAL_UNIT_PREFIX || nal_unit_type == NAL_UNIT_EXTENSION) offset += 3; /* Make sure there is enough data for there to be a 0x00 0x00 0x03 sequence */ if (offset + 2 >= new_sprop_size) return new_sprop_size; /* Keep a rolling set of the last couple of bytes */ first_byte = sprop[offset++]; second_byte = sprop[offset++]; while (offset < new_sprop_size) { uint8_t next_byte = sprop[offset]; if (!first_byte && !second_byte && next_byte == 0x03) { /* Remove the emulation prevention byte (0x03) */ new_sprop_size--; if (offset == new_sprop_size) /* No more data to check */ break; memmove(&sprop[offset], &sprop[offset + 1], new_sprop_size - offset); next_byte = sprop[offset]; } else offset++; first_byte = second_byte; second_byte = next_byte; } return new_sprop_size; } /**************************************************************************//** * Skip a scaling list in a bit stream. * * @param p_ctx The container context. * @param sprop The bit stream containing the scaling list. * @param size_of_scaling_list The size of the scaling list. */ static void h264_skip_scaling_list(VC_CONTAINER_T *p_ctx, VC_CONTAINER_BITS_T *sprop, uint32_t size_of_scaling_list) { uint32_t last_scale = 8; uint32_t next_scale = 8; int32_t delta_scale; uint32_t jj; /* Algorithm taken from H.264 section 7.3.2.1.1.1 */ for (jj = 0; jj < size_of_scaling_list; jj++) { if (next_scale) { delta_scale = BITS_READ_S32_EXP(p_ctx, sprop, "delta_scale"); next_scale = (last_scale + delta_scale + 256) & 0xFF; if (next_scale) last_scale = next_scale; } } } /**************************************************************************//** * Get the chroma format from the bit stream. * * @param p_ctx The container context. * @param sprop The bit stream containing the scaling list. * @return The chroma format index. */ static uint32_t h264_get_chroma_format(VC_CONTAINER_T *p_ctx, VC_CONTAINER_BITS_T *sprop) { uint32_t chroma_format_idc; chroma_format_idc = BITS_READ_U32_EXP(p_ctx, sprop, "chroma_format_idc"); if (chroma_format_idc == 3 && BITS_READ_U32(p_ctx, sprop, 1, "separate_colour_plane_flag")) chroma_format_idc = CHROMA_FORMAT_YUV_444_PLANAR; BITS_SKIP_EXP(p_ctx, sprop, "bit_depth_luma_minus8"); BITS_SKIP_EXP(p_ctx, sprop, "bit_depth_chroma_minus8"); BITS_SKIP(p_ctx, sprop, 1, "qpprime_y_zero_transform_bypass_flag"); if (BITS_READ_U32(p_ctx, sprop, 1, "seq_scaling_matrix_present_flag")) { uint32_t scaling_lists = (chroma_format_idc == 3) ? 12 : 8; uint32_t ii; for (ii = 0; ii < scaling_lists; ii++) { if (BITS_READ_U32(p_ctx, sprop, 1, "seq_scaling_list_present_flag")) h264_skip_scaling_list(p_ctx, sprop, (ii < 6) ? 16 : 64); } } return chroma_format_idc; } /**************************************************************************//** * Decode an H.264 sequence parameter set and update track information. * * @param p_ctx The RTP container context. * @param track The track to be updated. * @param sprop The bit stream containing the sequence parameter set. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T h264_decode_sequence_parameter_set(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, VC_CONTAINER_BITS_T *sprop) { VC_CONTAINER_VIDEO_FORMAT_T *video = &track->format->type->video; uint32_t pic_order_cnt_type, chroma_format_idc; uint32_t pic_width_in_mbs_minus1, pic_height_in_map_units_minus1, frame_mbs_only_flag; uint32_t frame_crop_left_offset, frame_crop_right_offset, frame_crop_top_offset, frame_crop_bottom_offset; uint8_t profile_idc; /* This structure is defined by H.264 section 7.3.2.1.1 */ profile_idc = BITS_READ_U8(p_ctx, sprop, 8, "profile_idc"); BITS_SKIP(p_ctx, sprop, 16, "Rest of profile_level_id"); BITS_READ_U32_EXP(p_ctx, sprop, "seq_parameter_set_id"); chroma_format_idc = CHROMA_FORMAT_RGB; if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || profile_idc == 86 || profile_idc == 118 || profile_idc == 128) { chroma_format_idc = h264_get_chroma_format(p_ctx, sprop); if (chroma_format_idc > CHROMA_FORMAT_YUV_444_PLANAR) goto error; } BITS_SKIP_EXP(p_ctx, sprop, "log2_max_frame_num_minus4"); pic_order_cnt_type = BITS_READ_U32_EXP(p_ctx, sprop, "pic_order_cnt_type"); if (pic_order_cnt_type == 0) { BITS_SKIP_EXP(p_ctx, sprop, "log2_max_pic_order_cnt_lsb_minus4"); } else if (pic_order_cnt_type == 1) { uint32_t num_ref_frames_in_pic_order_cnt_cycle; uint32_t ii; BITS_SKIP(p_ctx, sprop, 1, "delta_pic_order_always_zero_flag"); BITS_SKIP_EXP(p_ctx, sprop, "offset_for_non_ref_pic"); BITS_SKIP_EXP(p_ctx, sprop, "offset_for_top_to_bottom_field"); num_ref_frames_in_pic_order_cnt_cycle = BITS_READ_U32_EXP(p_ctx, sprop, "num_ref_frames_in_pic_order_cnt_cycle"); for (ii = 0; ii < num_ref_frames_in_pic_order_cnt_cycle; ii++) BITS_SKIP_EXP(p_ctx, sprop, "offset_for_ref_frame"); } BITS_SKIP_EXP(p_ctx, sprop, "max_num_ref_frames"); BITS_SKIP(p_ctx, sprop, 1, "gaps_in_frame_num_value_allowed_flag"); pic_width_in_mbs_minus1 = BITS_READ_U32_EXP(p_ctx, sprop, "pic_width_in_mbs_minus1"); pic_height_in_map_units_minus1 = BITS_READ_U32_EXP(p_ctx, sprop, "pic_height_in_map_units_minus1"); frame_mbs_only_flag = BITS_READ_U32(p_ctx, sprop, 1, "frame_mbs_only_flag"); /* Can now set the overall width and height in pixels */ video->width = (pic_width_in_mbs_minus1 + 1) * MACROBLOCK_WIDTH; video->height = (2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1) * MACROBLOCK_HEIGHT; if (!frame_mbs_only_flag) BITS_SKIP(p_ctx, sprop, 1, "mb_adaptive_frame_field_flag"); BITS_SKIP(p_ctx, sprop, 1, "direct_8x8_inference_flag"); if (BITS_READ_U32(p_ctx, sprop, 1, "frame_cropping_flag")) { /* Visible area is restricted */ frame_crop_left_offset = BITS_READ_U32_EXP(p_ctx, sprop, "frame_crop_left_offset"); frame_crop_right_offset = BITS_READ_U32_EXP(p_ctx, sprop, "frame_crop_right_offset"); frame_crop_top_offset = BITS_READ_U32_EXP(p_ctx, sprop, "frame_crop_top_offset"); frame_crop_bottom_offset = BITS_READ_U32_EXP(p_ctx, sprop, "frame_crop_bottom_offset"); /* Need to adjust offsets for 4:2:0 and 4:2:2 chroma formats and field/frame flag */ frame_crop_left_offset *= chroma_sub_width[chroma_format_idc]; frame_crop_right_offset *= chroma_sub_width[chroma_format_idc]; frame_crop_top_offset *= chroma_sub_height[chroma_format_idc] * (2 - frame_mbs_only_flag); frame_crop_bottom_offset *= chroma_sub_height[chroma_format_idc] * (2 - frame_mbs_only_flag); if ((frame_crop_left_offset + frame_crop_right_offset) >= video->width || (frame_crop_top_offset + frame_crop_bottom_offset) >= video->height) { LOG_ERROR(p_ctx, "H.264: frame crop offsets (%u, %u, %u, %u) larger than frame (%u, %u)", frame_crop_left_offset, frame_crop_right_offset, frame_crop_top_offset, frame_crop_bottom_offset, video->width, video->height); goto error; } video->x_offset = frame_crop_left_offset; video->y_offset = frame_crop_top_offset; video->visible_width = video->width - frame_crop_left_offset - frame_crop_right_offset; video->visible_height = video->height - frame_crop_top_offset - frame_crop_bottom_offset; } else { video->visible_width = video->width; video->visible_height = video->height; } /* vui_parameters may follow, but these will not be decoded */ if (!BITS_VALID(p_ctx, sprop)) goto error; return VC_CONTAINER_SUCCESS; error: LOG_ERROR(p_ctx, "H.264: sequence_parameter_set failed to decode"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } /**************************************************************************//** * Decode an H.264 sprop and update track information. * * @param p_ctx The RTP container context. * @param track The track to be updated. * @param sprop The bit stream containing the sprop. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T h264_decode_sprop(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, VC_CONTAINER_BITS_T *sprop) { switch (BITS_READ_U32(p_ctx, sprop, 8, "nal_unit_header") & NAL_UNIT_TYPE_MASK) { case NAL_UNIT_SEQUENCE_PARAMETER_SET: return h264_decode_sequence_parameter_set(p_ctx, track, sprop); case NAL_UNIT_PICTURE_PARAMETER_SET: /* Not handled, but valid */ return VC_CONTAINER_SUCCESS; default: return VC_CONTAINER_ERROR_FORMAT_INVALID; } } /**************************************************************************//** * Decode the sprop parameter sets URI parameter and update track information. * * @param p_ctx The RTP container context. * @param track The track to be updated. * @param params The URI parameter list. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T h264_get_sprop_parameter_sets(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params) { VC_CONTAINER_STATUS_T status; PARAMETER_T param; size_t str_len; uint32_t extradata_size = 0; uint8_t *sprop; const char *set; const char *comma; /* Get the value of sprop-parameter-sets, base64 decode the (comma separated) * sets, store all of them in track->priv->extradata and also decode to * validate and fill in video format info. */ param.name = "sprop-parameter-sets"; if (!vc_containers_list_find_entry(params, ¶m) || !param.value) { LOG_ERROR(p_ctx, "H.264: sprop-parameter-sets is required, but not found"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } /* First pass, calculate total size of buffer needed */ set = param.value; do { comma = strchr(set, ','); str_len = comma ? (size_t)(comma - set) : strlen(set); /* Allow space for the NAL unit and a start code */ extradata_size += rtp_base64_byte_length(set, str_len) + 4; set = comma + 1; } while (comma); if (!extradata_size) { LOG_ERROR(p_ctx, "H.264: sprop-parameter-sets doesn't contain useful data"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } status = vc_container_track_allocate_extradata(p_ctx, track, extradata_size); if(status != VC_CONTAINER_SUCCESS) return status; track->format->extradata_size = extradata_size; sprop = track->priv->extradata; /* Now decode the data into the buffer, and validate / use it to fill in format */ set = param.value; do { uint8_t *next_sprop; uint32_t sprop_size; VC_CONTAINER_BITS_T sprop_stream; comma = strchr(set, ','); str_len = comma ? (size_t)(comma - set) : strlen(set); /* Insert a start code (0x00000001 in network order) */ *sprop++ = 0x00; *sprop++ = 0x00; *sprop++ = 0x00; *sprop++ = 0x01; extradata_size -= 4; next_sprop = rtp_base64_decode(set, str_len, sprop, extradata_size); if (!next_sprop) { LOG_ERROR(p_ctx, "H.264: sprop-parameter-sets failed to decode"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } sprop_size = next_sprop - sprop; if (sprop_size) { uint32_t new_sprop_size; /* Need to remove emulation prevention bytes before decoding */ new_sprop_size = h264_remove_emulation_prevention_bytes(sprop, sprop_size); BITS_INIT(p_ctx, &sprop_stream, sprop, new_sprop_size); status = h264_decode_sprop(p_ctx, track, &sprop_stream); if(status != VC_CONTAINER_SUCCESS) return status; /* If necessary, decode sprop again, to put back the emulation prevention bytes */ if (new_sprop_size != sprop_size) rtp_base64_decode(set, str_len, sprop, sprop_size); extradata_size -= sprop_size; sprop = next_sprop; } set = comma + 1; } while (comma); return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * Check URI parameter list for unsupported features. * * @param p_ctx The RTP container context. * @param params The URI parameter list. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T h264_check_unsupported_features(VC_CONTAINER_T *p_ctx, const VC_CONTAINERS_LIST_T *params) { uint32_t u32_unused; /* Limitation: interleaving not yet supported */ if (rtp_get_parameter_u32(params, "sprop-interleaving-depth", &u32_unused) || rtp_get_parameter_u32(params, "sprop-deint-buf-req", &u32_unused) || rtp_get_parameter_u32(params, "sprop-init-buf-time", &u32_unused) || rtp_get_parameter_u32(params, "sprop-max-don-diff", &u32_unused)) { LOG_ERROR(p_ctx, "H.264: Interleaved packetization is not supported"); return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; } return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * Get and check the packetization mode URI parameter. * * @param p_ctx The RTP container context. * @param params The URI parameter list. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T h264_get_packetization_mode(VC_CONTAINER_T *p_ctx, const VC_CONTAINERS_LIST_T *params) { uint32_t packetization_mode; if (rtp_get_parameter_u32(params, "packetization-mode", &packetization_mode)) { /* Only modes 0 and 1 are supported, no interleaving */ if (packetization_mode > 1) { LOG_ERROR(p_ctx, "H.264: Unsupported packetization mode: %u", packetization_mode); return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; } } return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * Initialise payload bit stream for a new RTP packet. * * @param p_ctx The RTP container context. * @param t_module The track module with the new RTP packet. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T h264_new_rtp_packet(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_MODULE_T *t_module) { VC_CONTAINER_BITS_T *payload = &t_module->payload; H264_PAYLOAD_T *extra = (H264_PAYLOAD_T *)t_module->extra; uint8_t unit_header; uint8_t fragment_header; /* Read the NAL unit type and process as necessary */ unit_header = BITS_READ_U8(p_ctx, payload, 8, "nal_unit_header"); /* When the top bit is set, the NAL unit is invalid */ if (unit_header & NAL_UNIT_FZERO_MASK) { LOG_DEBUG(p_ctx, "H.264: Invalid NAL unit (top bit of header set)"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } /* In most cases, a new packet means a new NAL unit, which will need a start code and the header */ extra->header_bytes_to_write = 5; extra->nal_header = unit_header; extra->nal_unit_size = BITS_BYTES_AVAILABLE(p_ctx, payload); switch (unit_header & NAL_UNIT_TYPE_MASK) { case NAL_UNIT_STAP_A: /* Single Time Aggregation Packet A */ CLEAR_BIT(extra->flags, H264F_INSIDE_FRAGMENT); /* Trigger reading NAL unit length and header */ extra->nal_unit_size = 0; break; case NAL_UNIT_FU_A: /* Fragementation Unit A */ fragment_header = BITS_READ_U8(p_ctx, payload, 8, "fragment_header"); extra->nal_unit_size--; if (BIT_IS_CLEAR(fragment_header, FRAGMENT_UNIT_HEADER_START) || BIT_IS_SET(extra->flags, H264F_INSIDE_FRAGMENT)) { /* This is a continuation packet, prevent start code and header from being output */ extra->header_bytes_to_write = 0; /* If this is the end of a fragment, the next FU will be a new one */ if (BIT_IS_SET(fragment_header, FRAGMENT_UNIT_HEADER_END)) CLEAR_BIT(extra->flags, H264F_INSIDE_FRAGMENT); } else { /* Start of a new fragment. */ SET_BIT(extra->flags, H264F_INSIDE_FRAGMENT); /* Merge type from fragment header and the rest from NAL unit header to form real NAL unit header */ fragment_header &= NAL_UNIT_TYPE_MASK; fragment_header |= (unit_header & ~NAL_UNIT_TYPE_MASK); extra->nal_header = fragment_header; } break; case NAL_UNIT_STAP_B: case NAL_UNIT_MTAP16: case NAL_UNIT_MTAP24: case NAL_UNIT_FU_B: LOG_ERROR(p_ctx, "H.264: Unsupported RTP NAL unit type: %u", unit_header & NAL_UNIT_TYPE_MASK); return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; default: /* Single NAL unit case */ CLEAR_BIT(extra->flags, H264F_INSIDE_FRAGMENT); } return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * H.264 payload handler. * Extracts/skips data from the payload according to the NAL unit headers. * * @param p_ctx The RTP container context. * @param track The track being read. * @param p_packet The container packet information, or NULL. * @param flags The container read flags. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T h264_payload_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags) { VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; VC_CONTAINER_BITS_T *payload = &t_module->payload; H264_PAYLOAD_T *extra = (H264_PAYLOAD_T *)t_module->extra; uint32_t packet_flags = 0; uint8_t header_bytes_to_write; uint32_t size, offset; uint8_t *data_ptr; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; bool last_nal_unit_in_packet = false; if (BIT_IS_SET(t_module->flags, TRACK_NEW_PACKET)) { status = h264_new_rtp_packet(p_ctx, t_module); if (status != VC_CONTAINER_SUCCESS) return status; } if (BIT_IS_SET(extra->flags, H264F_NEXT_PACKET_IS_START)) { packet_flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; if (!(flags & VC_CONTAINER_READ_FLAG_INFO)) CLEAR_BIT(extra->flags, H264F_NEXT_PACKET_IS_START); } if (!extra->nal_unit_size && BITS_BYTES_AVAILABLE(p_ctx, payload)) { uint32_t stap_unit_header; /* STAP-A packet: read NAL unit size and header from payload */ stap_unit_header = BITS_READ_U32(p_ctx, payload, 24, "STAP unit header"); extra->nal_unit_size = stap_unit_header >> 8; if (extra->nal_unit_size > BITS_BYTES_AVAILABLE(p_ctx, payload)) { LOG_ERROR(p_ctx, "H.264: STAP-A NAL unit size bigger than payload"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } extra->header_bytes_to_write = 5; extra->nal_header = (uint8_t)stap_unit_header; } header_bytes_to_write = extra->header_bytes_to_write; size = extra->nal_unit_size + header_bytes_to_write; if (p_packet && !(flags & VC_CONTAINER_READ_FLAG_SKIP)) { if (flags & VC_CONTAINER_READ_FLAG_INFO) { /* In order to set the frame end flag correctly, need to work out if this * is the only NAL unit or last in an aggregated packet */ last_nal_unit_in_packet = (extra->nal_unit_size == BITS_BYTES_AVAILABLE(p_ctx, payload)); } else { offset = 0; data_ptr = p_packet->data; if (size > p_packet->buffer_size) { /* Buffer not big enough */ size = p_packet->buffer_size; } /* Insert start code and header into the data stream */ while (offset < size && header_bytes_to_write) { uint8_t header_byte; switch (header_bytes_to_write) { case 2: header_byte = 0x01; break; case 1: header_byte = extra->nal_header; break; default: header_byte = 0x00; } data_ptr[offset++] = header_byte; header_bytes_to_write--; } extra->header_bytes_to_write = header_bytes_to_write; if (offset < size) { BITS_COPY_BYTES(p_ctx, payload, size - offset, data_ptr + offset, "Packet data"); extra->nal_unit_size -= (size - offset); } /* If we've read the final bytes of the packet, this must be the last (or only) * NAL unit in it */ last_nal_unit_in_packet = !BITS_BYTES_AVAILABLE(p_ctx, payload); } p_packet->size = size; } else { extra->header_bytes_to_write = 0; BITS_SKIP_BYTES(p_ctx, payload, extra->nal_unit_size, "Packet data"); last_nal_unit_in_packet = !BITS_BYTES_AVAILABLE(p_ctx, payload); extra->nal_unit_size = 0; } /* The marker bit on an RTP packet indicates the frame ends at the end of packet */ if (last_nal_unit_in_packet && BIT_IS_SET(t_module->flags, TRACK_HAS_MARKER)) { packet_flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; /* If this was the last packet of a frame, the next one must be the start */ if (!(flags & VC_CONTAINER_READ_FLAG_INFO)) SET_BIT(extra->flags, H264F_NEXT_PACKET_IS_START); } if (p_packet) p_packet->flags = packet_flags; return status; } /***************************************************************************** Functions exported as part of the RTP parameter handler API *****************************************************************************/ /**************************************************************************//** * H.264 parameter handler. * Parses the URI parameters to set up the track for an H.264 stream. * * @param p_ctx The reader context. * @param track The track to be updated. * @param params The URI parameter list. * @return The resulting status of the function. */ VC_CONTAINER_STATUS_T h264_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params) { H264_PAYLOAD_T *extra; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_PARAM_UNUSED(p_ctx); VC_CONTAINER_PARAM_UNUSED(params); /* See RFC3984, section 8.1, for parameter names and details. */ extra = (H264_PAYLOAD_T *)malloc(sizeof(H264_PAYLOAD_T)); if (!extra) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; track->priv->module->extra = extra; memset(extra, 0, sizeof(H264_PAYLOAD_T)); /* Mandatory parameters */ status = h264_get_sprop_parameter_sets(p_ctx, track, params); if (status != VC_CONTAINER_SUCCESS) return status; /* Unsupported parameters */ status = h264_check_unsupported_features(p_ctx, params); if (status != VC_CONTAINER_SUCCESS) return status; /* Optional parameters */ status = h264_get_packetization_mode(p_ctx, params); if (status != VC_CONTAINER_SUCCESS) return status; track->priv->module->payload_handler = h264_payload_handler; SET_BIT(extra->flags, H264F_NEXT_PACKET_IS_START); track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; track->priv->module->timestamp_clock = H264_TIMESTAMP_CLOCK; return status; } userland/containers/rtp/rtp_h264.h000066400000000000000000000037201421703157200173720ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef _RTP_H264_H_ #define _RTP_H264_H_ #include "containers/containers.h" #include "containers/core/containers_list.h" /** H.264 parameter handler * * \param p_ctx Container context. * \param track Track data. * \param params Parameter list. * \return Status of decoding the H.264 parameters. */ VC_CONTAINER_STATUS_T h264_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); #endif /* _RTP_H264_H_ */ userland/containers/rtp/rtp_mpeg4.c000066400000000000000000000663211421703157200177240ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include "containers/containers.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_bits.h" #include "containers/core/containers_list.h" #include "rtp_priv.h" #include "rtp_mpeg4.h" #ifdef _DEBUG #define RTP_DEBUG 1 #endif /****************************************************************************** Defines and constants. ******************************************************************************/ /****************************************************************************** Type definitions ******************************************************************************/ /** MPEG-4 stream types, ISO/IEC 14496-1:2010 Table 6 */ typedef enum { MPEG4_OBJECT_DESCRIPTOR_STREAM = 1, MPEG4_CLOCK_REFERENCE_STREAM = 2, MPEG4_SCENE_DESCRIPTION_STREAM = 3, MPEG4_VISUAL_STREAM = 4, MPEG4_AUDIO_STREAM = 5, MPEG4_MPEG7_STREAM = 6, MPEG4_IPMP_STREAM = 7, MPEG4_OBJECT_CONTENT_INFO_STREAM = 8, MPEG4_MPEGJ_STREAM = 9, MPEG4_INTERACTION_STREAM = 10, MPEG4_IPMP_TOOL_STREAM = 11, } mp4_stream_type_t; /** MPEG-4 audio object types, ISO/IEC 14496-3:2009 Table 1.17 */ typedef enum { MPEG4A_AAC_MAIN = 1, MPEG4A_AAC_LC = 2, MPEG4A_AAC_SSR = 3, MPEG4A_AAC_LTP = 4, MPEG4A_SBR = 5, MPEG4A_AAC_SCALABLE = 6, MPEG4A_TWIN_VQ = 7, MPEG4A_CELP = 8, MPEG4A_HVXC = 9, MPEG4A_TTSI = 12, MPEG4A_MAIN_SYNTHETIC = 13, MPEG4A_WAVETABLE_SYNTHESIS = 14, MPEG4A_GENERAL_MIDI = 15, MPEG4A_ALGORITHMIC_SYNTHESIS = 16, MPEG4A_ER_AAC_LC = 17, MPEG4A_ER_AAC_LTP = 19, MPEG4A_ER_AAC_SCALABLE = 20, MPEG4A_ER_TWIN_VQ = 21, MPEG4A_ER_BSAC = 22, MPEG4A_ER_AAC_LD = 23, MPEG4A_ER_CELP = 24, MPEG4A_ER_HVXC = 25, MPEG4A_ER_HILN = 26, MPEG4A_ER_PARAMETERIC = 27, MPEG4A_SSC = 28, MPEG4A_PS = 29, MPEG4A_MPEG_SURROUND = 30, MPEG4A_LAYER_1 = 32, MPEG4A_LAYER_2 = 33, MPEG4A_LAYER_3 = 34, MPEG4A_DST = 35, MPEG4A_ALS = 36, MPEG4A_SLS = 37, MPEG4A_SLS_NON_CORE = 38, MPEG4A_ER_AAC_ELD = 39, MPEG4A_SMR_SIMPLE = 40, MPEG4A_SMR_MAIN = 41, } mp4_audio_object_type_t; /** RTP MPEG-4 modes */ typedef enum { MP4_GENERIC_MODE = 0, MP4_CELP_CBR_MODE, MP4_CELP_VBR_MODE, MP4_AAC_LBR_MODE, MP4_AAC_HBR_MODE } mp4_mode_t; typedef struct mp4_mode_detail_tag { const char *name; mp4_mode_t mode; } MP4_MODE_ENTRY_T; /* RTP MPEG-4 mode look-up table. * Note: case-insensitive sort by name */ static MP4_MODE_ENTRY_T mp4_mode_array[] = { { "aac-hbr", MP4_AAC_HBR_MODE }, { "aac-lbr", MP4_AAC_LBR_MODE }, { "celp-cbr", MP4_CELP_CBR_MODE }, { "celp-vbr", MP4_CELP_VBR_MODE }, { "generic", MP4_GENERIC_MODE }, }; static int mp4_mode_comparator(const MP4_MODE_ENTRY_T *a, const MP4_MODE_ENTRY_T *b); VC_CONTAINERS_STATIC_LIST(mp4_mode_lookup, mp4_mode_array, mp4_mode_comparator); typedef struct au_info_tag { uint32_t available; uint32_t index; int32_t cts_delta; int32_t dts_delta; } AU_INFO_T; typedef struct mp4_payload_tag { mp4_stream_type_t stream_type; uint32_t profile_level_id; mp4_mode_t mode; uint32_t size_length; uint32_t index_length; uint32_t index_delta_length; uint32_t cts_delta_length; uint32_t dts_delta_length; uint32_t object_type; uint32_t constant_size; uint32_t constant_duration; uint32_t auxiliary_length; VC_CONTAINER_BITS_T au_headers; AU_INFO_T au_info; } MP4_PAYLOAD_T; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T mp4_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); /****************************************************************************** Local Functions ******************************************************************************/ /**************************************************************************//** * Convert a hexadecimal character to a value between 0 and 15. * Upper and lower case characters are supported. An invalid chacter return zero. * * @param hex The character to convert. * @return The value of the character. */ static uint8_t hex_to_nybble(char hex) { if (hex >= '0' && hex <= '9') return hex - '0'; if (hex >= 'A' && hex <= 'F') return hex - 'A' + 10; if (hex >= 'a' && hex <= 'f') return hex - 'a' + 10; return 0; /* Illegal character (not hex) */ } /**************************************************************************//** * Convert a sequence of hexadecimal characters to consecutive entries in a * byte array. * The string must contain at least twice as many characters as the number of * bytes to convert. * * @param hex The hexadecimal string. * @param buffer The buffer into which bytes are to be stored. * @param bytes_to_convert The number of bytes in the array to be filled. */ static void hex_to_byte_buffer(const char *hex, uint8_t *buffer, uint32_t bytes_to_convert) { uint8_t value; while (bytes_to_convert--) { value = hex_to_nybble(*hex++) << 4; value |= hex_to_nybble(*hex++); *buffer++ = value; } } /**************************************************************************//** * Retrieves and checks the stream type in the URI parameters. * * @param p_ctx The RTP container context. * @param track The track being constructed. * @param params The URI parameter list. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T mp4_get_stream_type(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params) { MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)track->priv->module->extra; uint32_t stream_type; VC_CONTAINER_ES_TYPE_T expected_es_type; if (!rtp_get_parameter_u32(params, "streamType", &stream_type)) return VC_CONTAINER_ERROR_FORMAT_INVALID; switch (stream_type) { case MPEG4_AUDIO_STREAM: extra->stream_type = MPEG4_AUDIO_STREAM; expected_es_type = VC_CONTAINER_ES_TYPE_AUDIO; break; default: LOG_ERROR(p_ctx, "Unsupported MPEG-4 stream type: %u", stream_type); return VC_CONTAINER_ERROR_FORMAT_INVALID; } if (track->format->es_type != expected_es_type) return VC_CONTAINER_ERROR_FORMAT_INVALID; return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * Decode and store audio configuration information from an MP4 audio * configuration bit stream. * * @param p_ctx The RTP container context. * @param track The track being constructed. * @param bit_stream The bit stream containing the audio configuration. * @return True if the configuration was decoded successfully, false otherwise. */ static bool mp4_decode_audio_config(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, VC_CONTAINER_BITS_T *bit_stream) { static uint32_t mp4_audio_sample_rate[] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0 }; VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio; uint32_t audio_object_type; uint32_t sampling_frequency_index; uint32_t channel_configuration; audio_object_type = BITS_READ_U32(p_ctx, bit_stream, 5, "audioObjectType"); if (audio_object_type == 31) audio_object_type = BITS_READ_U32(p_ctx, bit_stream, 6, "audioObjectTypeExt") + 32; sampling_frequency_index = BITS_READ_U32(p_ctx, bit_stream, 4, "samplingFrequencyIndex"); if (sampling_frequency_index == 0xF) audio->sample_rate = BITS_READ_U32(p_ctx, bit_stream, 24, "samplingFrequency"); else audio->sample_rate = mp4_audio_sample_rate[sampling_frequency_index]; if (!audio->sample_rate) return false; track->priv->module->timestamp_clock = audio->sample_rate; channel_configuration = BITS_READ_U32(p_ctx, bit_stream, 4, "channelConfiguration"); switch (channel_configuration) { case 1: /* 1 channel, centre front */ case 2: /* 2 channel, stereo front */ case 3: /* 3 channel, centre and stereo front */ case 4: /* 4 channel, centre and stereo front, mono surround */ case 5: /* 5 channel, centre and stereo front, stereo surround */ case 6: /* 5.1 channel, centre and stereo front, stereo surround, low freq */ audio->channels = channel_configuration; break; case 7: /* 7.1 channel, centre, stereo and stereo outside front, stereo surround, low freq */ audio->channels = channel_configuration + 1; break; default: LOG_DEBUG(p_ctx, "MPEG-4: Unsupported channel configuration (%u)", channel_configuration); return false; } switch (audio_object_type) { case MPEG4A_AAC_LC: { uint32_t ga_specific_config = BITS_READ_U32(p_ctx, bit_stream, 3, "GASpecificConfig"); /* Make sure there are no unexpected (and unsupported) additional configuration elements */ if (ga_specific_config != 0) { LOG_DEBUG(p_ctx, "MPEG-4: Unexpected additional configuration data (%u)", ga_specific_config); return false; } } break; /* Add any further supported codecs here */ default: LOG_DEBUG(p_ctx, "MPEG-4: Unsupported Audio Object Type (%u)", audio_object_type); return false; } return true; } /**************************************************************************//** * Get, store and decode the configuration information from the URI parameters. * * @param p_ctx The RTP container context. * @param track The track being constructed. * @param params The URI parameter list. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T mp4_get_config(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params) { MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)track->priv->module->extra; PARAMETER_T param; uint32_t config_len; VC_CONTAINER_STATUS_T status; uint8_t *config; VC_CONTAINER_BITS_T bit_stream; param.name = "config"; if (!vc_containers_list_find_entry(params, ¶m) || !param.value) { LOG_ERROR(p_ctx, "MPEG-4: config parameter missing"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } config_len = strlen(param.value); if (config_len & 1) { LOG_ERROR(p_ctx, "MPEG-4: config parameter invalid"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } config_len /= 2; /* Copy AudioSpecificConfig into track extradata, to be decoded by client */ status = vc_container_track_allocate_extradata(p_ctx, track, config_len); if(status != VC_CONTAINER_SUCCESS) return status; config = track->priv->extradata; track->format->extradata_size = config_len; hex_to_byte_buffer(param.value, config, config_len); /* Decode config locally, to determine sample rate, etc. */ BITS_INIT(p_ctx, &bit_stream, config, config_len); switch (extra->stream_type) { case MPEG4_AUDIO_STREAM: if (!mp4_decode_audio_config(p_ctx, track, &bit_stream)) return VC_CONTAINER_ERROR_FORMAT_INVALID; break; default: /* Other stream types not yet supported */ LOG_ERROR(p_ctx, "MPEG-4: stream type %d not supported", extra->stream_type); return VC_CONTAINER_ERROR_FORMAT_INVALID; } return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * MP4 mode comparison function. * Compare two MP4 mode structures and return whether the first is less than, * equal to or greater than the second. * * @param first The first structure to be compared. * @param second The second structure to be compared. * @return Negative if first is less than second, positive if first is greater * and zero if they are equal. */ static int mp4_mode_comparator(const MP4_MODE_ENTRY_T *a, const MP4_MODE_ENTRY_T *b) { return strcasecmp(a->name, b->name); } /**************************************************************************//** * Get and store the MP4 mode, if recognised, from the URI parameters. * * @param p_ctx The RTP container context. * @param track The track being constructed. * @param params The URI parameter list. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T mp4_get_mode(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params) { MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)track->priv->module->extra; PARAMETER_T param; MP4_MODE_ENTRY_T mode_entry; param.name = "mode"; if (!vc_containers_list_find_entry(params, ¶m) || !param.value) { LOG_ERROR(p_ctx, "MPEG-4: mode parameter missing"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } #ifdef RTP_DEBUG vc_containers_list_validate(&mp4_mode_lookup); #endif mode_entry.name = param.value; if (!vc_containers_list_find_entry(&mp4_mode_lookup, &mode_entry)) { LOG_ERROR(p_ctx, "MPEG-4: Unrecognised mode parameter \"%s\"", mode_entry.name); return VC_CONTAINER_ERROR_FORMAT_INVALID; } extra->mode = mode_entry.mode; return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * Check URI parameters for unsupported features. * * @param p_ctx The RTP container context. * @param track The track being constructed. * @param params The URI parameter list. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T mp4_check_unsupported_features(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params) { uint32_t u32_unused; VC_CONTAINER_PARAM_UNUSED(p_ctx); VC_CONTAINER_PARAM_UNUSED(track); /* Limitation: RAP flag not yet supported */ if (rtp_get_parameter_u32(params, "randomAccessIndication", &u32_unused)) { LOG_ERROR(p_ctx, "MPEG-4: random access not supported"); return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; } /* Limitation: interleaving not yet supported */ if (rtp_get_parameter_u32(params, "maxDisplacement", &u32_unused) || rtp_get_parameter_u32(params, "de-interleaveBufferSize", &u32_unused)) { LOG_ERROR(p_ctx, "MPEG-4: interleaved packetization not supported"); return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; } /* Limitation: system streams not supported */ if (rtp_get_parameter_u32(params, "streamStateIndication", &u32_unused)) { LOG_ERROR(p_ctx, "MPEG-4: system streams not supported"); return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; } return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * Validate parameters that have been read form the URI parameter list. * * @param p_ctx The RTP container context. * @param track The track being constructed. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T mp4_check_parameters(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track) { MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)track->priv->module->extra; switch (extra->mode) { case MP4_CELP_CBR_MODE: if (!extra->constant_size) { LOG_ERROR(p_ctx, "MPEG-4: CELP-cbr requires constantSize parameter."); return VC_CONTAINER_ERROR_FORMAT_INVALID; } break; case MP4_CELP_VBR_MODE: case MP4_AAC_LBR_MODE: if (extra->size_length != 6 || extra->index_length != 2 || extra->index_delta_length != 2) { LOG_ERROR(p_ctx, "MPEG-4: CELP-vbr/AAC-lbr invalid lengths (%u/%u/%u)", extra->size_length, extra->index_length, extra->index_delta_length); return VC_CONTAINER_ERROR_FORMAT_INVALID; } break; case MP4_AAC_HBR_MODE: if (extra->size_length != 13 || extra->index_length != 3 || extra->index_delta_length != 3) { LOG_ERROR(p_ctx, "MPEG-4: AAC-hbr invalid lengths (%u/%u/%u)", extra->size_length, extra->index_length, extra->index_delta_length); return VC_CONTAINER_ERROR_FORMAT_INVALID; } break; default: /* MP4_GENERIC_MODE */ if (extra->size_length > 32 || extra->index_length > 32 || extra->index_delta_length > 32) { LOG_ERROR(p_ctx, "MPEG-4: generic invalid lengths (%u/%u/%u)", extra->size_length, extra->index_length, extra->index_delta_length); return VC_CONTAINER_ERROR_FORMAT_INVALID; } } if (extra->cts_delta_length > 32 || extra->dts_delta_length > 32) { LOG_ERROR(p_ctx, "MPEG-4: CTS/DTS invalid lengths (%u/%u)", extra->cts_delta_length, extra->dts_delta_length); return VC_CONTAINER_ERROR_FORMAT_INVALID; } return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * Initialise payload bit stream for a new RTP packet. * * @param p_ctx The RTP container context. * @param t_module The track module with the new RTP packet. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T mp4_new_rtp_packet(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_MODULE_T *t_module) { VC_CONTAINER_BITS_T *payload = &t_module->payload; MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)t_module->extra; VC_CONTAINER_BITS_T *au_headers = &extra->au_headers; /* There will be an AU header section if any of its fields are non-zero. */ if (extra->size_length || extra->index_length || extra->cts_delta_length || extra->dts_delta_length) { uint32_t au_headers_length; /* Calculate how far to advance the payload, to get past the AU headers */ au_headers_length = BITS_READ_U32(p_ctx, payload, 16, "AU headers length"); au_headers_length = BITS_TO_BYTES(au_headers_length); /* Round up to bytes */ /* Record where the AU headers are in the payload */ BITS_INIT(p_ctx, au_headers, BITS_CURRENT_POINTER(p_ctx, payload), au_headers_length); BITS_SKIP_BYTES(p_ctx, &t_module->payload, au_headers_length, "Move payload past AU headers"); } /* Skip the auxiliary section, if present */ if (extra->auxiliary_length) { uint32_t auxiliary_data_size; auxiliary_data_size = BITS_READ_U32(p_ctx, payload, extra->auxiliary_length, "Auxiliary length"); auxiliary_data_size = BITS_TO_BYTES(auxiliary_data_size); /* Round up to bytes */ BITS_SKIP_BYTES(p_ctx, payload, auxiliary_data_size, "Auxiliary data"); } return BITS_VALID(p_ctx, payload) ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FORMAT_INVALID; } /**************************************************************************//** * Read a flagged delta from an AU header bit stream. * A flagged delta is an optional value in the stream that is preceded by a * flag bit that indicates whether the value is present in the stream. If the * length of the value is zero bits, the flag is never present. * * @pre The delta_length must be 32 or less. * * @param p_ctx The container context. * @param au_headers The AU header bit stream. * @param delta_length The number of bits in the delta value. * @return The delta value, or zero if not present. */ static int32_t mp4_flagged_delta(VC_CONTAINER_T *p_ctx, VC_CONTAINER_BITS_T *au_headers, uint32_t delta_length) { uint32_t value = 0; /* Flag is only present if the delta length is non-zero */ if (delta_length && BITS_READ_U32(p_ctx, au_headers, 1, "CTS/DTS delta present")) { value = BITS_READ_U32(p_ctx, au_headers, delta_length, "CTS/DTS delta"); /* Sign extend value based on bit length */ if (value & (1 << (delta_length - 1))) value |= ~((1 << delta_length) - 1); } return (int32_t)value; } /**************************************************************************//** * Read next AU header from the bit stream. * * @param p_ctx The RTP container context. * @param extra The MP4-specific track module information. * @param is_first_au True if the first AU header in the packet is being read. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T mp4_next_au_header(VC_CONTAINER_T *p_ctx, MP4_PAYLOAD_T *extra, bool is_first_au) { VC_CONTAINER_BITS_T *au_headers = &extra->au_headers; AU_INFO_T *au_info = &extra->au_info; /* See RFC3550 section 3.2.1.1 */ if (extra->constant_size) au_info->available = extra->constant_size; else au_info->available = BITS_READ_U32(p_ctx, au_headers, extra->size_length, "AU size"); if (is_first_au) au_info->index = BITS_READ_U32(p_ctx, au_headers, extra->index_length, "AU index"); else au_info->index += BITS_READ_U32(p_ctx, au_headers, extra->index_delta_length, "AU index delta") + 1; au_info->cts_delta = mp4_flagged_delta(p_ctx, au_headers, extra->cts_delta_length); au_info->dts_delta = mp4_flagged_delta(p_ctx, au_headers, extra->dts_delta_length); /* RAP and stream state not supported yet */ return BITS_VALID(p_ctx, au_headers) ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FORMAT_INVALID; } /**************************************************************************//** * MP4 payload handler. * Extracts/skips data from the payload according to the AU headers. * * @param p_ctx The RTP container context. * @param track The track being read. * @param p_packet The container packet information, or NULL. * @param flags The container read flags. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T mp4_payload_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags) { VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; VC_CONTAINER_BITS_T *payload = &t_module->payload; MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)t_module->extra; AU_INFO_T *au_info = &extra->au_info; bool is_new_packet = BIT_IS_SET(t_module->flags, TRACK_NEW_PACKET); uint32_t bytes_left_in_payload; uint32_t size; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; if (is_new_packet) { status = mp4_new_rtp_packet(p_ctx, t_module); if (status != VC_CONTAINER_SUCCESS) return status; } if (!au_info->available) { status = mp4_next_au_header(p_ctx, extra, is_new_packet); if (status != VC_CONTAINER_SUCCESS) return status; } if (p_packet) { /* Adjust the packet time stamps using deltas */ p_packet->pts += au_info->cts_delta; p_packet->dts += au_info->dts_delta; } size = au_info->available; bytes_left_in_payload = BITS_BYTES_AVAILABLE(p_ctx, payload); if (size > bytes_left_in_payload) { /* AU is fragmented across RTP packets */ size = bytes_left_in_payload; } if (p_packet && !(flags & VC_CONTAINER_READ_FLAG_SKIP)) { if (!(flags & VC_CONTAINER_READ_FLAG_INFO)) { if (size > p_packet->buffer_size) size = p_packet->buffer_size; BITS_COPY_BYTES(p_ctx, payload, size, p_packet->data, "Packet data"); } p_packet->size = size; } else { BITS_SKIP_BYTES(p_ctx, payload, size, "Packet data"); } if (!(flags & VC_CONTAINER_READ_FLAG_INFO)) au_info->available -= size; return BITS_VALID(p_ctx, payload) ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FORMAT_INVALID; } /***************************************************************************** Functions exported as part of the RTP parameter handler API *****************************************************************************/ /**************************************************************************//** * MP4 parameter handler. * Parses the URI parameters to set up the track for an MP4 stream. * * @param p_ctx The reader context. * @param track The track to be updated. * @param params The URI parameter list. * @return The resulting status of the function. */ VC_CONTAINER_STATUS_T mp4_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params) { MP4_PAYLOAD_T *extra; VC_CONTAINER_STATUS_T status; /* See RFC3640, section 4.1, for parameter names and details. */ extra = (MP4_PAYLOAD_T *)malloc(sizeof(MP4_PAYLOAD_T)); if (!extra) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; track->priv->module->extra = extra; memset(extra, 0, sizeof(MP4_PAYLOAD_T)); /* Mandatory parameters */ status = mp4_get_stream_type(p_ctx, track, params); if (status != VC_CONTAINER_SUCCESS) return status; status = mp4_get_config(p_ctx, track, params); if (status != VC_CONTAINER_SUCCESS) return status; status = mp4_get_mode(p_ctx, track, params); if (status != VC_CONTAINER_SUCCESS) return status; /* Unsupported parameters */ status = mp4_check_unsupported_features(p_ctx, track, params); if (status != VC_CONTAINER_SUCCESS) return status; /* Optional parameters */ rtp_get_parameter_u32(params, "sizeLength", &extra->size_length); rtp_get_parameter_u32(params, "indexLength", &extra->index_length); rtp_get_parameter_u32(params, "indexDeltaLength", &extra->index_delta_length); rtp_get_parameter_u32(params, "CTSDeltaLength", &extra->cts_delta_length); rtp_get_parameter_u32(params, "DTSDeltaLength", &extra->dts_delta_length); rtp_get_parameter_u32(params, "objectType", &extra->object_type); rtp_get_parameter_u32(params, "constantSize", &extra->constant_size); rtp_get_parameter_u32(params, "constantDuration", &extra->constant_duration); rtp_get_parameter_u32(params, "auxiliaryDataSizeLength", &extra->auxiliary_length); if (extra->constant_size && extra->size_length) { LOG_ERROR(p_ctx, "MPEG4: constantSize and sizeLength cannot both be set."); return VC_CONTAINER_ERROR_FORMAT_INVALID; } status = mp4_check_parameters(p_ctx, track); if (status != VC_CONTAINER_SUCCESS) return status; track->priv->module->payload_handler = mp4_payload_handler; return VC_CONTAINER_SUCCESS; } userland/containers/rtp/rtp_mpeg4.h000066400000000000000000000037241421703157200177270ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef _RTP_MPEG4_H_ #define _RTP_MPEG4_H_ #include "containers/containers.h" #include "containers/core/containers_list.h" /** MPEG-4 parameter handler * * \param p_ctx Container context. * \param track Track data. * \param params Parameter list. * \return Status of decoding the MPEG-4 parameters. */ VC_CONTAINER_STATUS_T mp4_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); #endif /* _RTP_MPEG4_H_ */ userland/containers/rtp/rtp_priv.h000066400000000000000000000117161421703157200176730ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef _RTP_PRIV_H_ #define _RTP_PRIV_H_ #include "containers/containers.h" #include "containers/core/containers_private.h" #include "containers/core/containers_bits.h" #include "containers/core/containers_list.h" typedef VC_CONTAINER_STATUS_T (*PAYLOAD_HANDLER_T)(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags); /** Parameter list entry type. */ typedef struct parameter_tag { const char *name; const char *value; } PARAMETER_T; /** Prototype for MIME parameter handling. * Each MIME type has a certain set of parameter names it uses, so a handler is * needed for each type. This is that handler's prototype. */ typedef VC_CONTAINER_STATUS_T (*PARAMETER_HANDLER_T)(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); /** Track module flag bit numbers (up to seven) */ typedef enum { TRACK_SSRC_SET = 0, TRACK_HAS_MARKER, TRACK_NEW_PACKET, } track_module_flag_bit_t; /** RTP track data */ typedef struct VC_CONTAINER_TRACK_MODULE_T { PAYLOAD_HANDLER_T payload_handler; /**< Extracts the data from the payload */ uint8_t *buffer; /**< Buffer into which the RTP packet is read */ VC_CONTAINER_BITS_T payload; /**< Payload bit bit_stream */ uint8_t flags; /**< Combination of track module flags */ uint8_t payload_type; /**< The expected payload type */ uint16_t max_seq_num; /**< Highest seq. number seen */ uint32_t timestamp; /**< RTP timestamp of packet */ uint32_t timestamp_base; /**< RTP timestamp value that equates to time zero */ uint32_t last_timestamp_top; /**< Top two bits of RTP timestamp of previous packet */ uint32_t timestamp_wraps; /**< Count of the times that the timestamp has wrapped */ uint32_t timestamp_clock; /**< Clock frequency of RTP timestamp values */ uint32_t expected_ssrc; /**< The expected SSRC, if set */ uint32_t base_seq; /**< Base seq number */ uint32_t bad_seq; /**< Last 'bad' seq number + 1 */ uint32_t probation; /**< Sequential packets till source is valid */ uint32_t received; /**< RTP packets received */ void *extra; /**< Payload specific data */ } VC_CONTAINER_TRACK_MODULE_T; /** Determine minimum number of bytes needed to hold a number of bits */ #define BITS_TO_BYTES(X) (((X) + 7) >> 3) /** Collection of bit manipulation routines */ /* @{ */ #define SET_BIT(V, B) V |= (1 << (B)) #define CLEAR_BIT(V, B) V &= ~(1 << (B)) #define BIT_IS_SET(V, B) (!(!((V) & (1 << (B))))) #define BIT_IS_CLEAR(V, B) (!((V) & (1 << (B)))) /* }@ */ /** Get a parameter's value as a decimal number. * * \param param_list The list of parameter name/value pairs. * \param name The paramter's name. * \param value Where to put the converted value. * \return True if successful, false if the parameter was not found or didn't convert. */ bool rtp_get_parameter_u32(const VC_CONTAINERS_LIST_T *param_list, const char *name, uint32_t *value); /** Get a parameter's value as a hexadecimal number. * * \param param_list The list of parameter name/value pairs. * \param name The paramter's name. * \param value Where to put the converted value. * \return True if successful, false if the parameter was not found or didn't convert. */ bool rtp_get_parameter_x32(const VC_CONTAINERS_LIST_T *param_list, const char *name, uint32_t *value); #endif /* _RTP_PRIV_H_ */ userland/containers/rtp/rtp_reader.c000066400000000000000000001273701421703157200201540ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #define CONTAINER_IS_BIG_ENDIAN //#define ENABLE_CONTAINERS_LOG_FORMAT //#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE #define CONTAINER_HELPER_LOG_INDENT(a) 0 #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_uri.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_bits.h" #include "containers/core/containers_list.h" #include "rtp_priv.h" #include "rtp_mpeg4.h" #include "rtp_h264.h" #ifdef _DEBUG /* Validates static sorted lists are correctly constructed */ #define RTP_DEBUG 1 #endif /****************************************************************************** Configurable defines and constants. ******************************************************************************/ /** Maximum size of an RTP packet */ #define MAXIMUM_PACKET_SIZE 2048 /** Maximum number of RTP packets that can be missed without restarting. */ #define MAX_DROPOUT 3000 /** Maximum number of out of sequence RTP packets that are accepted. */ #define MAX_MISORDER 0 /** Minimum number of sequential packets required for an acceptable connection * when restarting. */ #define MIN_SEQUENTIAL 2 /****************************************************************************** Defines and constants. ******************************************************************************/ #define RTP_SCHEME "rtp:" /** The RTP PKT scheme is used with test pkt files */ #define RTP_PKT_SCHEME "rtppkt:" /** \name RTP URI parameter names * @{ */ #define PAYLOAD_TYPE_NAME "rtppt" #define MIME_TYPE_NAME "mime-type" #define CHANNELS_NAME "channels" #define RATE_NAME "rate" #define SSRC_NAME "ssrc" #define SEQ_NAME "seq" /* @} */ /** A sentinel codec that is not supported */ #define UNSUPPORTED_CODEC VC_FOURCC(0,0,0,0) /** Evaluates to true if the given payload type is in the supported static audio range. */ #define IS_STATIC_AUDIO_TYPE(PT) ((PT) < countof(static_audio_payload_types)) /** Payload type number for the first static video type */ #define FIRST_STATIC_VIDEO_TYPE 24 /** Evaluates to true if the given payload type is in the supported static video range. */ #define IS_STATIC_VIDEO_TYPE(PT) ((PT) >= FIRST_STATIC_VIDEO_TYPE && \ (PT) < (FIRST_STATIC_VIDEO_TYPE + countof(static_video_payload_types))) /** Evaluates to true if the given payload type is in the dynamic range. */ #define IS_DYNAMIC_TYPE(PT) ((PT) >= 96 && (PT) < 128) /** All sequence numbers are modulo this value. */ #define RTP_SEQ_MOD (1 << 16) /** All the static video payload types use a 90kHz timestamp clock */ #define STATIC_VIDEO_TIMESTAMP_CLOCK 90000 /** Number of microseconds in a second, used to convert RTP timestamps to microseconds */ #define MICROSECONDS_PER_SECOND 1000000 /****************************************************************************** Type definitions ******************************************************************************/ /** \name MIME type parameter handlers * Function prototypes for payload parameter handlers */ /* @{ */ static VC_CONTAINER_STATUS_T audio_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); static VC_CONTAINER_STATUS_T l8_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); static VC_CONTAINER_STATUS_T l16_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); /* @} */ /** \name MIME type payload handlers */ /* @{ */ static VC_CONTAINER_STATUS_T l16_payload_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags); /* @} */ /** Static audio payload type data */ typedef struct audio_payload_type_data_tag { VC_CONTAINER_FOURCC_T codec; /**< FourCC codec for this payload type */ uint32_t channels; /**< Number of audio channels */ uint32_t sample_rate; /**< Sample rate */ uint32_t bits_per_sample; /**< Bits per sample, or 1 if not applicable */ PARAMETER_HANDLER_T param_handler; /**< Optional parameter handler */ PAYLOAD_HANDLER_T payload_handler; /**< Optional payload handler */ } AUDIO_PAYLOAD_TYPE_DATA_T; /** The details for the statically defined audio payload types from RFC3551 */ static AUDIO_PAYLOAD_TYPE_DATA_T static_audio_payload_types[] = { { VC_CONTAINER_CODEC_MULAW, 1, 8000, 8, audio_parameter_handler, NULL }, /* 0 - PCMU */ { UNSUPPORTED_CODEC }, /* 1 - reserved */ { UNSUPPORTED_CODEC }, /* 2 - reserved */ { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 3 - GSM */ { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 4 - G723 */ { UNSUPPORTED_CODEC, 1, 8000, 4, NULL, NULL }, /* 5 - DVI4 */ { UNSUPPORTED_CODEC, 1, 16000, 4, NULL, NULL }, /* 6 - DVI4 */ { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 7 - LPC */ { VC_CONTAINER_CODEC_ALAW, 1, 8000, 8, audio_parameter_handler, NULL }, /* 8 - PCMA */ { UNSUPPORTED_CODEC, 1, 8000, 8, NULL, NULL }, /* 9 - G722 */ { VC_CONTAINER_CODEC_PCM_SIGNED, 2, 44100, 16, audio_parameter_handler, l16_payload_handler }, /* 10 - L16 */ { VC_CONTAINER_CODEC_PCM_SIGNED, 1, 44100, 16, audio_parameter_handler, l16_payload_handler }, /* 11 - L16 */ { VC_CONTAINER_CODEC_QCELP, 1, 8000, 16, NULL, NULL }, /* 12 - QCELP */ { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 13 - CN */ { VC_CONTAINER_CODEC_MPGA, 1, 90000, 1, NULL, NULL }, /* 14 - MPA */ { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 15 - G728 */ { UNSUPPORTED_CODEC, 1, 11025, 4, NULL, NULL }, /* 16 - DVI4 */ { UNSUPPORTED_CODEC, 1, 22050, 4, NULL, NULL }, /* 17 - DVI4 */ { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 18 - G729 */ }; /** Static video payload type data */ typedef struct video_payload_type_data_tag { VC_CONTAINER_FOURCC_T codec; /**< FourCC codec for this payload type */ PARAMETER_HANDLER_T param_handler; /**< Optional parameter handler */ PAYLOAD_HANDLER_T payload_handler; /**< Optional payload handler */ } VIDEO_PAYLOAD_TYPE_DATA_T; /** The details for the statically defined video payload types from RFC3551 */ static VIDEO_PAYLOAD_TYPE_DATA_T static_video_payload_types[] = { { UNSUPPORTED_CODEC }, /* 24 - unassigned */ { UNSUPPORTED_CODEC }, /* 25 - CelB */ { UNSUPPORTED_CODEC }, /* 26 - JPEG */ { UNSUPPORTED_CODEC }, /* 27 - unassigned */ { UNSUPPORTED_CODEC }, /* 28 - nv */ { UNSUPPORTED_CODEC }, /* 29 - unassigned */ { UNSUPPORTED_CODEC }, /* 30 - unassigned */ { UNSUPPORTED_CODEC }, /* 31 - H261 */ { VC_CONTAINER_CODEC_MP2V, NULL, NULL }, /* 32 - MPV */ { UNSUPPORTED_CODEC }, /* 33 - MP2T */ { VC_CONTAINER_CODEC_H263, NULL, NULL } /* 34 - H263 */ }; /** MIME type details */ typedef struct mime_type_data_tag { const char *name; /**< Name of MIME type */ VC_CONTAINER_ES_TYPE_T es_type; /**< Elementary stream type */ VC_CONTAINER_FOURCC_T codec; /**< Codec to be used */ PARAMETER_HANDLER_T param_handler; /**< Parameter handler for this MIME type */ } MIME_TYPE_DATA_T; /** Comparator for MIME type details. */ static int mime_type_data_comparator(const MIME_TYPE_DATA_T *a, const MIME_TYPE_DATA_T *b); /** Dynamic audio payload details * Note: case-insensitive sort by name */ static MIME_TYPE_DATA_T dynamic_mime_details[] = { { "audio/l16", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_PCM_SIGNED, l16_parameter_handler }, { "audio/l8", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_PCM_SIGNED, l8_parameter_handler }, { "audio/mpeg4-generic", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_MP4A, mp4_parameter_handler }, { "video/h264", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H264, h264_parameter_handler }, { "video/mpeg4-generic", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP4V, mp4_parameter_handler }, }; /** Sorted list of dynamic MIME type details */ VC_CONTAINERS_STATIC_LIST(dynamic_mime, dynamic_mime_details, mime_type_data_comparator); /** RTP reader data. */ typedef struct VC_CONTAINER_MODULE_T { VC_CONTAINER_TRACK_T *track; } VC_CONTAINER_MODULE_T; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T rtp_reader_open( VC_CONTAINER_T * ); /****************************************************************************** Local Functions ******************************************************************************/ /**************************************************************************//** * Parameter comparison function. * Compare two parameter structures and return whether the first is less than, * equal to or greater than the second. * * @param first The first structure to be compared. * @param second The second structure to be compared. * @return Negative if first is less than second, positive if first is greater * and zero if they are equal. */ static int parameter_comparator(const PARAMETER_T *first, const PARAMETER_T *second) { return strcasecmp(first->name, second->name); } /**************************************************************************//** * Creates and populates a parameter list from a URI structure. * The list does not copy the parameter strings themselves, so the URI structure * must be retained (and its parameters unmodified) while the list is in use. * * @param uri The URI containing the parameters. * @return List created from the parameters of the URI, or NULL on error. */ static VC_CONTAINERS_LIST_T *fill_parameter_list(VC_URI_PARTS_T *uri) { uint32_t num_parameters = vc_uri_num_queries(uri); VC_CONTAINERS_LIST_T *parameters; uint32_t ii; parameters = vc_containers_list_create(num_parameters, sizeof(PARAMETER_T), (VC_CONTAINERS_LIST_COMPARATOR_T)parameter_comparator); if (!parameters) return NULL; for (ii = 0; ii < num_parameters; ii++) { PARAMETER_T param; vc_uri_query(uri, ii, ¶m.name, ¶m.value); if (!vc_containers_list_insert(parameters, ¶m, false)) { vc_containers_list_destroy(parameters); return NULL; } } #ifdef RTP_DEBUG vc_containers_list_validate(parameters); #endif return parameters; } /**************************************************************************//** * Decodes a static audio payload type into track information. * The static parameters may be overridden by URI parameters. * * @param p_ctx The reader context. * @param track The track to be populated. * @param param_list The URI parameter list. * @param payload_type The static payload type. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T decode_static_audio_type(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *param_list, uint32_t payload_type) { VC_CONTAINER_ES_FORMAT_T *format = track->format; AUDIO_PAYLOAD_TYPE_DATA_T *data = &static_audio_payload_types[payload_type]; VC_CONTAINER_PARAM_UNUSED(p_ctx); VC_CONTAINER_PARAM_UNUSED(param_list); vc_container_assert(payload_type < countof(static_audio_payload_types)); if (data->codec == UNSUPPORTED_CODEC) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; format->codec = data->codec; format->type->audio.channels = data->channels; format->type->audio.sample_rate = data->sample_rate; format->type->audio.bits_per_sample = data->bits_per_sample; format->type->audio.block_align = data->channels * BITS_TO_BYTES(data->bits_per_sample); track->priv->module->timestamp_clock = format->type->audio.sample_rate; track->priv->module->payload_handler = data->payload_handler; if (data->param_handler) data->param_handler(p_ctx, track, param_list); return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * Decodes a static video payload type into track information. * The static parameters may be overridden by URI parameters. * * @param p_ctx The reader context. * @param track The track to be populated. * @param param_list The URI parameter list. * @param payload_type The static payload type. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T decode_static_video_type(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *param_list, uint32_t payload_type) { VC_CONTAINER_ES_FORMAT_T *format = track->format; VIDEO_PAYLOAD_TYPE_DATA_T *data = &static_video_payload_types[payload_type - FIRST_STATIC_VIDEO_TYPE]; VC_CONTAINER_PARAM_UNUSED(p_ctx); VC_CONTAINER_PARAM_UNUSED(param_list); vc_container_assert(payload_type >= FIRST_STATIC_VIDEO_TYPE); vc_container_assert(payload_type < FIRST_STATIC_VIDEO_TYPE + countof(static_video_payload_types)); if (data->codec == UNSUPPORTED_CODEC) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; format->codec = data->codec; track->priv->module->timestamp_clock = STATIC_VIDEO_TIMESTAMP_CLOCK; track->priv->module->payload_handler = data->payload_handler; if (data->param_handler) data->param_handler(p_ctx, track, param_list); return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * Compare two MIME type structures and return whether the first is less than, * equal to or greater than the second. * * @param a The first parameter structure to be compared. * @param b The second parameter structure to be compared. * @return Negative if a is less than b, positive if a is greater and zero if * they are equal. */ static int mime_type_data_comparator(const MIME_TYPE_DATA_T *a, const MIME_TYPE_DATA_T *b) { return strcasecmp(a->name, b->name); } /**************************************************************************//** * Generic audio parameter handler. * Updates the track information with generic audio parameters, such as "rate" * and "channels". * * @param p_ctx The reader context. * @param track The track to be updated. * @param params The URI parameter list. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T audio_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params) { VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio; VC_CONTAINER_PARAM_UNUSED(p_ctx); /* See RFC3555. Generic audio parameters that can override static payload * type defaults. */ if (rtp_get_parameter_u32(params, RATE_NAME, &audio->sample_rate)) track->priv->module->timestamp_clock = audio->sample_rate; if (rtp_get_parameter_u32(params, CHANNELS_NAME, &audio->channels)) audio->block_align = audio->channels * BITS_TO_BYTES(audio->bits_per_sample); return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * L8 specific audio parameter handler. * Updates the track information with audio parameters needed by the audio/L8 * MIME type. For example, the "rate" parameter is mandatory. * * @param p_ctx The reader context. * @param track The track to be updated. * @param params The URI parameter list. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T l8_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params) { VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio; VC_CONTAINER_PARAM_UNUSED(p_ctx); /* See RFC3555, section 4.1.14, for parameter names and details. */ if (!rtp_get_parameter_u32(params, RATE_NAME, &audio->sample_rate)) return VC_CONTAINER_ERROR_FORMAT_INVALID; if (!rtp_get_parameter_u32(params, CHANNELS_NAME, &audio->channels)) audio->channels = 1; audio->bits_per_sample = 8; audio->block_align = audio->channels; track->priv->module->timestamp_clock = audio->sample_rate; return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * L16 specific audio parameter handler. * Updates the track information with audio parameters needed by the audio/L16 * MIME type. For example, the "rate" parameter is mandatory. * * @param p_ctx The reader context. * @param track The track to be updated. * @param params The URI parameter list. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T l16_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params) { VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio; VC_CONTAINER_PARAM_UNUSED(p_ctx); /* See RFC3555, section 4.1.15, for parameter names and details. */ if (!rtp_get_parameter_u32(params, RATE_NAME, &audio->sample_rate)) return VC_CONTAINER_ERROR_FORMAT_INVALID; if (!rtp_get_parameter_u32(params, CHANNELS_NAME, &audio->channels)) audio->channels = 1; audio->bits_per_sample = 16; audio->block_align = audio->channels * 2; track->priv->module->timestamp_clock = audio->sample_rate; track->priv->module->payload_handler = l16_payload_handler; /* TODO: add support for "channel-order" to set channel mapping */ return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * Decode a dynamic payload type from parameters. * Populates the track information with data for supported dynamic media types. * * @param p_ctx The reader context. * @param track The track to be updated. * @param param_list The URI parameter list. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T decode_dynamic_type(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *param_list) { VC_CONTAINER_ES_FORMAT_T *format = track->format; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; PARAMETER_T mime_type; MIME_TYPE_DATA_T mime_details; /* Get MIME type parameter */ mime_type.name = MIME_TYPE_NAME; if (!vc_containers_list_find_entry(param_list, &mime_type)) return VC_CONTAINER_ERROR_FORMAT_INVALID; #ifdef RTP_DEBUG vc_containers_list_validate(&dynamic_mime); #endif /* Look up MIME type to see if it can be handled */ mime_details.name = mime_type.value; if (!vc_containers_list_find_entry(&dynamic_mime, &mime_details)) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; format->codec = mime_details.codec; format->es_type = mime_details.es_type; /* Default number of channels for audio is one */ if (mime_details.es_type == VC_CONTAINER_ES_TYPE_AUDIO) format->type->audio.channels = 1; /* Lete MIME type specific parameter handler deal with any other parameters */ status = mime_details.param_handler(p_ctx, track, param_list); /* Ensure that the sample rate has been set for audio formats */ if (mime_details.es_type == VC_CONTAINER_ES_TYPE_AUDIO && !format->type->audio.sample_rate) return VC_CONTAINER_ERROR_FORMAT_INVALID; return status; } /**************************************************************************//** * Decode the RTP payload type. * Populates track information with data from static tables and the URI * parameter list, according to the payload and MIME types. * * @param p_ctx The reader context. * @param track The track to be updated. * @param params The URI parameter list. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T decode_payload_type(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *param_list, uint32_t payload_type) { VC_CONTAINER_TRACK_MODULE_T *module = track->priv->module; VC_CONTAINER_STATUS_T status; if (IS_STATIC_AUDIO_TYPE(payload_type)) status = decode_static_audio_type(p_ctx, track, param_list, payload_type); else if (IS_STATIC_VIDEO_TYPE(payload_type)) status = decode_static_video_type(p_ctx, track, param_list, payload_type); else if (IS_DYNAMIC_TYPE(payload_type)) status = decode_dynamic_type(p_ctx, track, param_list); else status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; module->payload_type = (uint8_t)payload_type; return status; } /**************************************************************************//** * Initialises the RTP sequence number algorithm with a new sequence number. * * @param t_module The track module. * @param seq The new sequence number. */ static void init_sequence_number(VC_CONTAINER_TRACK_MODULE_T *t_module, uint16_t seq) { t_module->base_seq = seq; t_module->max_seq_num = seq; t_module->bad_seq = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */ t_module->received = 0; } /**************************************************************************//** * Checks whether the sequence number for a packet is acceptable or not. * The packet will be unacceptable if it is out of sequence by some degree, or * if the packet sequence is still being established. * * @param t_module The track module. * @param seq The new sequence number. * @return True if the sequence number indicates the packet is acceptable */ static bool update_sequence_number(VC_CONTAINER_TRACK_MODULE_T *t_module, uint16_t seq) { uint16_t udelta = seq - t_module->max_seq_num; /* NOTE: This source is derived from the example code in RFC3550, section A.1 */ /* Source is not valid until MIN_SEQUENTIAL packets with * sequential sequence numbers have been received. */ if (t_module->probation) { /* packet is in sequence */ if (seq == t_module->max_seq_num + 1) { t_module->probation--; t_module->max_seq_num = seq; LOG_INFO(0, "RTP: Probation, %u more packet(s) to go at 0x%4.4hx", t_module->probation, seq); if (!t_module->probation) { init_sequence_number(t_module, seq); t_module->received++; return 1; } } else { t_module->probation = MIN_SEQUENTIAL - 1; t_module->max_seq_num = seq; LOG_INFO(0, "RTP: Probation reset, wait for %u packet(s) at 0x%4.4hx", t_module->probation, seq); } return 0; } else if (udelta < MAX_DROPOUT) { if (!udelta) { /* Duplicate packet, drop it */ LOG_INFO(0, "RTP: Drop duplicate packet at 0x%4.4hx", seq); return 0; } if (udelta > 1) { LOG_INFO(0, "RTP: Jumped by %hu packets to 0x%4.4hx", udelta, seq); } /* in order, with permissible gap */ t_module->max_seq_num = seq; } else #if (MAX_MISORDER != 0) /* When MAX_MISORDER is zero, always treat as out of order */ if (udelta <= RTP_SEQ_MOD - MAX_MISORDER) #endif { /* the sequence number made a very large jump */ if (seq == t_module->bad_seq) { LOG_INFO(0, "RTP: Misorder restart at 0x%4.4hx", seq); /* Two sequential packets -- assume that the other side * restarted without telling us so just re-sync * (i.e., pretend this was the first packet). */ init_sequence_number(t_module, seq); } else { LOG_INFO(0, "RTP: Misorder at 0x%4.4hx, expected 0x%4.4hx", seq, t_module->max_seq_num); t_module->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1); return 0; } } #if (MAX_MISORDER != 0) else { /* duplicate or reordered packet */ /* TODO: handle out of order packets */ } #endif t_module->received++; return 1; } /**************************************************************************//** * Extract the fields of an RTP packet and validate it. * * @param p_ctx The reader context. * @param t_module The track module. * @return True if successful, false if there were not enough bits in the * packet or the packet was invalid. */ static void decode_rtp_packet_header(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_MODULE_T *t_module) { VC_CONTAINER_BITS_T *payload = &t_module->payload; uint32_t version, has_padding, has_extension, csrc_count, has_marker; uint32_t payload_type, ssrc; uint16_t seq_num; /* Break down fixed header area into component parts */ version = BITS_READ_U32(p_ctx, payload, 2, "Version"); has_padding = BITS_READ_U32(p_ctx, payload, 1, "Has padding"); has_extension = BITS_READ_U32(p_ctx, payload, 1, "Has extension"); csrc_count = BITS_READ_U32(p_ctx, payload, 4, "CSRC count"); has_marker = BITS_READ_U32(p_ctx, payload, 1, "Has marker"); payload_type = BITS_READ_U32(p_ctx, payload, 7, "Payload type"); seq_num = BITS_READ_U16(p_ctx, payload, 16, "Sequence number"); t_module->timestamp = BITS_READ_U32(p_ctx, payload, 32, "Timestamp"); ssrc = BITS_READ_U32(p_ctx, payload, 32, "SSRC"); /* If there was only a partial header, abort immediately */ if (!BITS_VALID(p_ctx, payload)) return; /* Validate version, payload type, sequence number and SSRC, if set */ if (version != 2 || payload_type != t_module->payload_type) { BITS_INVALIDATE(p_ctx, payload); return; } if (BIT_IS_SET(t_module->flags, TRACK_SSRC_SET) && (ssrc != t_module->expected_ssrc)) { LOG_DEBUG(p_ctx, "RTP: Unexpected SSRC (0x%8.8X)", ssrc); BITS_INVALIDATE(p_ctx, payload); return; } /* Check sequence number indicates packet is usable */ if (!update_sequence_number(t_module, seq_num)) { BITS_INVALIDATE(p_ctx, payload); return; } /* Adjust to account for padding, CSRCs and extension */ if (has_padding) { VC_CONTAINER_BITS_T bit_stream; uint32_t available = BITS_BYTES_AVAILABLE(p_ctx, payload); uint8_t padding; BITS_COPY_STREAM(p_ctx, &bit_stream, payload); /* The last byte of the payload is the number of padding bytes, including itself */ BITS_SKIP_BYTES(p_ctx, &bit_stream, available - 1, "Skip to padding length"); padding = BITS_READ_U8(p_ctx, &bit_stream, 8, "Padding length"); BITS_REDUCE_BYTES(p_ctx, payload, padding, "Remove padding"); } /* Each CSRC is 32 bits, so shift count up to skip the right number of bits */ BITS_SKIP(p_ctx, payload, csrc_count << 5, "CSRC section"); if (has_extension) { uint32_t extension_bits; /* Extension header is 16-bit ID (which isn't needed), then 16-bit length in 32-bit words */ BITS_SKIP(p_ctx, payload, 16, "Extension ID"); extension_bits = BITS_READ_U32(p_ctx, payload, 16, "Extension length") << 5; BITS_SKIP(p_ctx, payload, extension_bits, "Extension data"); } /* Record whether or not this RTP packet had the marker bit set */ if (has_marker) SET_BIT(t_module->flags, TRACK_HAS_MARKER); else CLEAR_BIT(t_module->flags, TRACK_HAS_MARKER); /* If it hasn't been set independently, use the first timestamp as a baseline */ if (!t_module->timestamp_base) t_module->timestamp_base = t_module->timestamp; t_module->timestamp -= t_module->timestamp_base; } /**************************************************************************//** * Generic payload handler. * Copies/skips data verbatim from the packet payload. * * @param p_ctx The reader context. * @param track The track being read. * @param p_packet The container packet information, or NULL. * @param flags The container read flags. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T generic_payload_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags) { VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; VC_CONTAINER_BITS_T *payload = &t_module->payload; uint32_t size; VC_CONTAINER_PARAM_UNUSED(p_ctx); if (!p_packet) { /* Skip the rest of this RTP packet */ BITS_INVALIDATE(p_ctx, payload); return VC_CONTAINER_SUCCESS; } /* Copy as much as possible into the client packet buffer */ size = BITS_BYTES_AVAILABLE(p_ctx, payload); if (flags & VC_CONTAINER_READ_FLAG_SKIP) BITS_SKIP_BYTES(p_ctx, payload, size, "Packet data"); else { if (!(flags & VC_CONTAINER_READ_FLAG_INFO)) { if (size > p_packet->buffer_size) size = p_packet->buffer_size; BITS_COPY_BYTES(p_ctx, payload, size, p_packet->data, "Packet data"); } p_packet->size = size; } return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * L16 payload handler. * Copies/skips data from the packet payload. On copy, swaps consecutive bytes * in the data in order to get expected byte order. * * @param p_ctx The reader context. * @param track The track being read. * @param p_packet The container packet information, or NULL. * @param flags The container read flags. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T l16_payload_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags) { VC_CONTAINER_STATUS_T status; /* Most aspects are handled adequately by the generic handler */ status = generic_payload_handler(p_ctx, track, p_packet, flags); if (status != VC_CONTAINER_SUCCESS) return status; if (p_packet && !(flags & (VC_CONTAINER_READ_FLAG_SKIP | VC_CONTAINER_READ_FLAG_INFO))) { uint8_t *ptr, *end_ptr; /* Ensure packet size is even */ p_packet->size &= ~1; /* Swap bytes of each sample, to get host order instead of network order */ for (ptr = p_packet->data, end_ptr = ptr + p_packet->size; ptr < end_ptr; ptr += 2) { uint8_t high_byte = ptr[0]; ptr[0] = ptr[1]; ptr[1] = high_byte; } } return status; } /***************************************************************************** Utility functions for use by RTP payload handlers *****************************************************************************/ /**************************************************************************//** * Gets the value of a parameter as an unsigned 32-bit decimal integer. * * @param param_list The URI parameter list. * @param name The name of the parameter. * @param value Pointer to the variable to receive the value. * @return True if the parameter value was read and stored correctly, false * otherwise. */ bool rtp_get_parameter_u32(const VC_CONTAINERS_LIST_T *param_list, const char *name, uint32_t *value) { PARAMETER_T param; param.name = name; if (vc_containers_list_find_entry(param_list, ¶m) && param.value) { char *end; *value = strtoul(param.value, &end, 10); return (end != param.value) && (*end == '\0'); } return false; } /**************************************************************************//** * Gets the value of a parameter as an unsigned 32-bit hexadecimal integer. * * @param param_list The URI parameter list. * @param name The name of the parameter. * @param value Pointer to the variable to receive the value. * @return True if the parameter value was read and stored correctly, false * otherwise. */ bool rtp_get_parameter_x32(const VC_CONTAINERS_LIST_T *param_list, const char *name, uint32_t *value) { PARAMETER_T param; param.name = name; if (vc_containers_list_find_entry(param_list, ¶m) && param.value) { char *end; *value = strtoul(param.value, &end, 16); return (end != param.value) && (*end == '\0'); } return false; } /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ /**************************************************************************//** * Read/skip data from the container. * Can also be used to query information about the next block of data. * * @param p_ctx The reader context. * @param p_packet The container packet information, or NULL. * @param flags The container read flags. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtp_reader_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) { VC_CONTAINER_TRACK_T *track; VC_CONTAINER_TRACK_MODULE_T *t_module; VC_CONTAINER_STATUS_T status; if((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) && p_packet->track) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; track = p_ctx->tracks[0]; t_module = track->priv->module; CLEAR_BIT(t_module->flags, TRACK_NEW_PACKET); while (!BITS_AVAILABLE(p_ctx, &t_module->payload)) { uint32_t bytes_read; /* No data left from last RTP packet, get another one */ bytes_read = READ_BYTES(p_ctx, t_module->buffer, MAXIMUM_PACKET_SIZE); if (!bytes_read) return STREAM_STATUS(p_ctx); BITS_INIT(p_ctx, &t_module->payload, t_module->buffer, bytes_read); decode_rtp_packet_header(p_ctx, t_module); SET_BIT(t_module->flags, TRACK_NEW_PACKET); } if (p_packet) { uint32_t timestamp_top = t_module->timestamp >> 30; /* Determine whether timestamp has wrapped forwards or backwards around zero */ if ((timestamp_top == 0) && (t_module->last_timestamp_top == 3)) t_module->timestamp_wraps++; else if ((timestamp_top == 3) && (t_module->last_timestamp_top == 0)) t_module->timestamp_wraps--; t_module->last_timestamp_top = timestamp_top; p_packet->dts = p_packet->pts = ((int64_t)t_module->timestamp_wraps << 32) | t_module->timestamp; p_packet->track = 0; p_packet->flags = 0; } status = t_module->payload_handler(p_ctx, track, p_packet, flags); if (p_packet && status == VC_CONTAINER_SUCCESS) { /* Adjust timestamps from RTP clock rate to microseconds */ p_packet->pts = p_packet->pts * MICROSECONDS_PER_SECOND / t_module->timestamp_clock; p_packet->dts = p_packet->dts * MICROSECONDS_PER_SECOND / t_module->timestamp_clock; } STREAM_STATUS(p_ctx) = status; return status; } /**************************************************************************//** * Seek over data in the container. * * @param p_ctx The reader context. * @param p_offset The seek offset. * @param mode The seek mode. * @param flags The seek flags. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtp_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *p_offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) { VC_CONTAINER_PARAM_UNUSED(p_ctx); VC_CONTAINER_PARAM_UNUSED(p_offset); VC_CONTAINER_PARAM_UNUSED(mode); VC_CONTAINER_PARAM_UNUSED(flags); /* RTP is a non-seekable container */ return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; } /**************************************************************************//** * Apply a control operation to the container. * * @param p_ctx The reader context. * @param operation The control operation. * @param args Optional additional arguments for the operation. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtp_reader_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args) { VC_CONTAINER_STATUS_T status; VC_CONTAINER_TRACK_MODULE_T *t_module = p_ctx->tracks[0]->priv->module; switch (operation) { case VC_CONTAINER_CONTROL_SET_TIMESTAMP_BASE: { t_module->timestamp_base = va_arg(args, uint32_t); if (!t_module->timestamp_base) t_module->timestamp_base = 1; /* Zero is used to mean "not set" */ status = VC_CONTAINER_SUCCESS; } break; case VC_CONTAINER_CONTROL_SET_NEXT_SEQUENCE_NUMBER: { init_sequence_number(t_module, (uint16_t)va_arg(args, uint32_t)); t_module->probation = 0; status = VC_CONTAINER_SUCCESS; } break; case VC_CONTAINER_CONTROL_SET_SOURCE_ID: { t_module->expected_ssrc = va_arg(args, uint32_t); SET_BIT(t_module->flags, TRACK_SSRC_SET); status = VC_CONTAINER_SUCCESS; } break; default: status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; } return status; } /**************************************************************************//** * Close the container. * * @param p_ctx The reader context. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtp_reader_close( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; vc_container_assert(p_ctx->tracks_num < 2); if (p_ctx->tracks_num) { void *payload_extra; vc_container_assert(module); vc_container_assert(module->track); vc_container_assert(module->track->priv); vc_container_assert(module->track->priv->module); payload_extra = module->track->priv->module->extra; if (payload_extra) free(payload_extra); vc_container_free_track(p_ctx, module->track); } p_ctx->tracks = NULL; p_ctx->tracks_num = 0; if (module) free(module); p_ctx->priv->module = 0; return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * Open the container. * Uses the I/O URI and/or data to configure the container. * * @param p_ctx The reader context. * @return The resulting status of the function. */ VC_CONTAINER_STATUS_T rtp_reader_open( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = 0; VC_CONTAINER_TRACK_T *track = 0; VC_CONTAINER_TRACK_MODULE_T *t_module = 0; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINERS_LIST_T *parameters = NULL; uint32_t payload_type; uint32_t initial_seq_num; /* Check the URI scheme looks valid */ if (!vc_uri_scheme(p_ctx->priv->uri) || (strcasecmp(vc_uri_scheme(p_ctx->priv->uri), RTP_SCHEME) && strcasecmp(vc_uri_scheme(p_ctx->priv->uri), RTP_PKT_SCHEME))) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Make the query/parameter list more easily searchable */ parameters = fill_parameter_list(p_ctx->priv->uri); if (!parameters) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } /* Payload type parameter is mandatory and must fit in 7 bits */ if (!rtp_get_parameter_u32(parameters, PAYLOAD_TYPE_NAME, &payload_type) || payload_type > 127) { status = VC_CONTAINER_ERROR_FORMAT_INVALID; goto error; } /* Allocate our context */ module = (VC_CONTAINER_MODULE_T *)malloc(sizeof(VC_CONTAINER_MODULE_T)); if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); p_ctx->priv->module = module; p_ctx->tracks = &module->track; /* Allocate the track, including space for reading an RTP packet on the end */ track = vc_container_allocate_track(p_ctx, sizeof(VC_CONTAINER_TRACK_MODULE_T) + MAXIMUM_PACKET_SIZE); if (!track) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } module->track = track; p_ctx->tracks_num = 1; t_module = track->priv->module; /* Initialise the track data */ t_module->buffer = (uint8_t *)(t_module + 1); status = decode_payload_type(p_ctx, track, parameters, payload_type); if (status != VC_CONTAINER_SUCCESS) goto error; vc_container_assert(t_module->timestamp_clock != 0); /* Default to a generic, unstructured payload handler */ if (!t_module->payload_handler) t_module->payload_handler = generic_payload_handler; if (rtp_get_parameter_x32(parameters, SSRC_NAME, &t_module->expected_ssrc)) SET_BIT(t_module->flags, TRACK_SSRC_SET); t_module->probation = MIN_SEQUENTIAL; if (rtp_get_parameter_u32(parameters, SEQ_NAME, &initial_seq_num)) { /* If an initial sequence number is provided, avoid probation period */ t_module->max_seq_num = (uint16_t)initial_seq_num; t_module->probation = 0; } track->is_enabled = true; vc_containers_list_destroy(parameters); p_ctx->priv->pf_close = rtp_reader_close; p_ctx->priv->pf_read = rtp_reader_read; p_ctx->priv->pf_seek = rtp_reader_seek; p_ctx->priv->pf_control = rtp_reader_control; return VC_CONTAINER_SUCCESS; error: if (parameters) vc_containers_list_destroy(parameters); if(status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_EOS) status = VC_CONTAINER_ERROR_FORMAT_INVALID; LOG_DEBUG(p_ctx, "error opening RTP (%i)", status); rtp_reader_close(p_ctx); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak reader_open rtp_reader_open #endif userland/containers/rtsp/000077500000000000000000000000001421703157200160325ustar00rootroot00000000000000userland/containers/rtsp/CMakeLists.txt000066400000000000000000000006431421703157200205750ustar00rootroot00000000000000# Container module needs to go in as a plugins so different prefix # and install path set(CMAKE_SHARED_LIBRARY_PREFIX "") # Make sure the compiler can find the necessary include files include_directories (../..) set(rtsp_SRCS ${rtsp_SRCS} rtsp_reader.c) add_library(reader_rtsp ${LIBRARY_TYPE} ${rtsp_SRCS}) target_link_libraries(reader_rtsp containers) install(TARGETS reader_rtsp DESTINATION ${VMCS_PLUGIN_DIR}) userland/containers/rtsp/rtsp_reader.c000066400000000000000000002101671421703157200205170ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #define CONTAINER_IS_BIG_ENDIAN //#define ENABLE_CONTAINERS_LOG_FORMAT //#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE #define CONTAINER_HELPER_LOG_INDENT(a) 0 #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_list.h" #include "containers/core/containers_uri.h" /****************************************************************************** Configurable defines and constants. ******************************************************************************/ /** Maximum number of tracks allowed in an RTSP reader */ #define RTSP_TRACKS_MAX 4 /** Space for sending requests and receiving responses */ #define COMMS_BUFFER_SIZE 2048 /** Largest allowed RTSP URI. Must be substantially smaller than COMMS_BUFFER_SIZE * to allow for the headers that may be sent. */ #define RTSP_URI_LENGTH_MAX 1024 /** Maximum allowed length for the Session: header recevied in a SETUP response, * This is to ensure comms buffer is not overflowed. */ #define SESSION_HEADER_LENGTH_MAX 100 /** Number of milliseconds to block trying to read from the RTSP stream when no * data is available from any of the tracks */ #define DATA_UNAVAILABLE_READ_TIMEOUT_MS 1 /** Size of buffer for each track to use when receiving packets */ #define UDP_READ_BUFFER_SIZE 520000 /* Arbitrary number of different dynamic ports to try */ #define DYNAMIC_PORT_ATTEMPTS_MAX 16 /****************************************************************************** Defines and constants. ******************************************************************************/ #define RTSP_SCHEME "rtsp:" #define RTP_SCHEME "rtp" /** The RTSP PKT scheme is used with test pkt files */ #define RTSP_PKT_SCHEME "rtsppkt:" #define RTSP_NETWORK_URI_START "rtsp://" #define RTSP_NETWORK_URI_START_LENGTH (sizeof(RTSP_NETWORK_URI_START)-1) /** Initial capacity of header list */ #define HEADER_LIST_INITIAL_CAPACITY 16 /** Format of the first line of an RTSP request */ #define RTSP_REQUEST_LINE_FORMAT "%s %s RTSP/1.0\r\n" /** Format string for common headers used with all request methods. * Note: includes double new line to terminate headers */ #define TRAILING_HEADERS_FORMAT "CSeq: %u\r\nConnection: Keep-Alive\r\nUser-Agent: Broadcom/1.0\r\n\r\n" /** Format for the Transport: header */ #define TRANSPORT_HEADER_FORMAT "Transport: RTP/AVP;unicast;client_port=%hu-%hu;mode=play\r\n" /** Format for including Session: header. */ #define SESSION_HEADER_FORMAT "Session: %s\r\n" /** \name RTSP methods, used as the first item in the request line * @{ */ #define DESCRIBE_METHOD "DESCRIBE" #define SETUP_METHOD "SETUP" #define PLAY_METHOD "PLAY" #define TEARDOWN_METHOD "TEARDOWN" /* @} */ /** \name Names of headers used by the code * @{ */ #define CONTENT_PSEUDOHEADER_NAME ":" #define CONTENT_LENGTH_NAME "Content-Length" #define CONTENT_BASE_NAME "Content-Base" #define CONTENT_LOCATION_NAME "Content-Location" #define RTP_INFO_NAME "RTP-Info" #define SESSION_NAME "Session" /* @} */ /** Supported RTSP major version number */ #define RTSP_MAJOR_VERSION 1 /** Supported RTSP minor version number */ #define RTSP_MINOR_VERSION 0 /** Lowest successful status code value */ #define RTSP_STATUS_OK 200 /** Next failure status code after the set of successful ones */ #define RTSP_STATUS_MULTIPLE_CHOICES 300 /** Maximum size of a decimal string representation of a uint16_t, plus NUL */ #define PORT_BUFFER_SIZE 6 /** Start of private / dynamic port region */ #define FIRST_DYNAMIC_PORT 0xC000 /** End of private / dynamic port region */ #define LAST_DYNAMIC_PORT 0xFFF0 /** Format of RTP track file extension */ #define RTP_PATH_EXTENSION_FORMAT ".t%u.pkt" /** Extra space need for creating an RTP track file name from an RTSP URI path */ #define RTP_PATH_EXTRA 17 /** \name RTP URI parameter names * @{ */ #define PAYLOAD_TYPE_NAME "rtppt" #define MIME_TYPE_NAME "mime-type" #define SAMPLE_RATE_NAME "rate" #define CHANNELS_NAME "channels" /* @} */ /** Largest signed 64-bit integer */ #define MAXIMUM_INT64 (int64_t)((1ULL << 63) - 1) /****************************************************************************** Type definitions ******************************************************************************/ typedef int (*PARSE_IS_DELIMITER_FN_T)(int char_to_test); typedef struct rtsp_header_tag { const char *name; char *value; } RTSP_HEADER_T; typedef struct VC_CONTAINER_TRACK_MODULE_T { VC_CONTAINER_T *reader; /**< RTP reader for track */ VC_URI_PARTS_T *reader_uri; /**< URI built up from SDP and used to open reader */ char *control_uri; /**< URI used to control track playback */ char *session_header; /**< Session header to be used when sending control requests */ char *payload_type; /**< RTP payload type for track */ char *media_type; /**< MIME type for track */ VC_CONTAINER_PACKET_T info; /**< Latest track packet info block */ unsigned short rtp_port; /**< UDP listener port being used in RTP reader */ } VC_CONTAINER_TRACK_MODULE_T; typedef struct VC_CONTAINER_MODULE_T { VC_CONTAINER_TRACK_T *tracks[RTSP_TRACKS_MAX]; char *comms_buffer; /**< Buffer used for sending and receiving RTSP messages */ VC_CONTAINERS_LIST_T *header_list; /**< Parsed response headers, pointing into comms buffer */ uint32_t cseq_value; /**< CSeq header value for next request */ uint16_t next_rtp_port; /**< Next RTP port to use when opening track reader */ uint16_t media_item; /**< Current media item number during initialization */ bool uri_has_network_info; /**< True if the RTSP URI contains network info */ int64_t ts_base; /**< Base value for dts and pts */ VC_CONTAINER_TRACK_MODULE_T *current_track; /**< Next track to be read, to keep info/data on same track */ } VC_CONTAINER_MODULE_T; /****************************************************************************** Function prototypes ******************************************************************************/ static int rtsp_header_comparator(const RTSP_HEADER_T *first, const RTSP_HEADER_T *second); VC_CONTAINER_STATUS_T rtsp_reader_open( VC_CONTAINER_T * ); /****************************************************************************** Local Functions ******************************************************************************/ /**************************************************************************//** * Trim whitespace from the end and start of the string * * \param str String to be trimmed * \return Trimmed string */ static char *rtsp_trim( char *str ) { char *trim = str + strlen(str); /* Search backwards for first non-whitespace */ while (--trim >= str && isspace((int)*trim)) ; /* Everything done in the while */ trim[1] = '\0'; /* Now move start of string forwards to first non-whitespace */ trim = str; while (isspace((int)*trim)) trim++; return trim; } /**************************************************************************//** * Send out the data in the comms buffer. * * @param p_ctx The reader context. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_send( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; uint32_t to_write; uint32_t written; const char *buffer = module->comms_buffer; /* When reading from a captured file, do not attempt to send data */ if (!module->uri_has_network_info) return VC_CONTAINER_SUCCESS; to_write = strlen(buffer); while (to_write) { written = vc_container_io_write(p_ctx->priv->io, buffer, to_write); if (!written) break; to_write -= written; buffer += written; } return p_ctx->priv->io->status; } /**************************************************************************//** * Send a DESCRIBE request to the RTSP server. * * @param p_ctx The reader context. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_send_describe_request( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE; char *uri = p_ctx->priv->io->uri; if (strlen(uri) > RTSP_URI_LENGTH_MAX) { LOG_ERROR(p_ctx, "RTSP: URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX); return VC_CONTAINER_ERROR_URI_OPEN_FAILED; } ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, DESCRIBE_METHOD, uri); if (ptr < end) ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++); vc_container_assert(ptr < end); return rtsp_send(p_ctx); } /**************************************************************************//** * Send a SETUP request to the RTSP server. * * @param p_ctx The reader context. * @param t_module The track module relating to the SETUP. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_send_setup_request( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_MODULE_T *t_module) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE; char *uri = t_module->control_uri; if (strlen(uri) > RTSP_URI_LENGTH_MAX) { LOG_ERROR(p_ctx, "RTSP: Control URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX); return VC_CONTAINER_ERROR_URI_OPEN_FAILED; } ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, SETUP_METHOD, uri); if (ptr < end) ptr += snprintf(ptr, end - ptr, TRANSPORT_HEADER_FORMAT, t_module->rtp_port, t_module->rtp_port + 1); if (ptr < end) ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++); vc_container_assert(ptr < end); return rtsp_send(p_ctx); } /**************************************************************************//** * Send a PLAY request to the RTSP server. * * @param p_ctx The reader context. * @param t_module The track module relating to the PLAY. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_send_play_request( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_MODULE_T *t_module ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE; char *uri = t_module->control_uri; if (strlen(uri) > RTSP_URI_LENGTH_MAX) { LOG_ERROR(p_ctx, "RTSP: Control URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX); return VC_CONTAINER_ERROR_URI_OPEN_FAILED; } ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, PLAY_METHOD, uri); if (ptr < end) ptr += snprintf(ptr, end - ptr, SESSION_HEADER_FORMAT, t_module->session_header); if (ptr < end) ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++); vc_container_assert(ptr < end); return rtsp_send(p_ctx); } /**************************************************************************//** * Send a TEARDOWN request to the RTSP server. * * @param p_ctx The reader context. * @param t_module The track module relating to the SETUP. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_send_teardown_request( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_MODULE_T *t_module ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE; char *uri = t_module->control_uri; if (strlen(uri) > RTSP_URI_LENGTH_MAX) { LOG_ERROR(p_ctx, "RTSP: Control URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX); return VC_CONTAINER_ERROR_URI_OPEN_FAILED; } ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, TEARDOWN_METHOD, uri); if (ptr < end) ptr += snprintf(ptr, end - ptr, SESSION_HEADER_FORMAT, t_module->session_header); if (ptr < end) ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++); vc_container_assert(ptr < end); return rtsp_send(p_ctx); } /**************************************************************************//** * Check a response status line to see if the response is usable or not. * Reasons for invalidity include: * - Incorrectly formatted * - Unsupported version * - Status code is not in the 2xx range * * @param p_ctx The reader context. * @param status_line The response status line. * @return The resulting status of the function. */ static bool rtsp_successful_response_status( VC_CONTAINER_T *p_ctx, const char *status_line) { unsigned int major_version, minor_version, status_code; /* coverity[secure_coding] String is null-terminated */ if (sscanf(status_line, "RTSP/%u.%u %u", &major_version, &minor_version, &status_code) != 3) { LOG_ERROR(p_ctx, "RTSP: Invalid response status line:\n%s", status_line); return false; } if (major_version != RTSP_MAJOR_VERSION || minor_version != RTSP_MINOR_VERSION) { LOG_ERROR(p_ctx, "RTSP: Unexpected response RTSP version: %u.%u", major_version, minor_version); return false; } if (status_code < RTSP_STATUS_OK || status_code >= RTSP_STATUS_MULTIPLE_CHOICES) { LOG_ERROR(p_ctx, "RTSP: Response status unsuccessful:\n%s", status_line); return false; } return true; } /**************************************************************************//** * Get the content length header from the response headers as an unsigned * 32-bit integer. * If the content length header is not found or badly formatted, zero is * returned. * * @param header_list The response headers. * @return The content length. */ static uint32_t rtsp_get_content_length( VC_CONTAINERS_LIST_T *header_list ) { unsigned int content_length = 0; RTSP_HEADER_T header; header.name = CONTENT_LENGTH_NAME; if (header_list && vc_containers_list_find_entry(header_list, &header)) /* coverity[secure_coding] String is null-terminated */ sscanf(header.value, "%u", &content_length); return content_length; } /**************************************************************************//** * Get the session header from the response headers. * If the session header is not found, the empty string is returned. * * @param header_list The response headers. * @return The session header. */ static const char *rtsp_get_session_header(VC_CONTAINERS_LIST_T *header_list) { RTSP_HEADER_T header; header.name = SESSION_NAME; if (header_list && vc_containers_list_find_entry(header_list, &header)) return header.value; return ""; } /**************************************************************************//** * Returns pointer to the string with any leading whitespace trimmed and * terminated by a delimiter, as determined by is_delimiter_fn. The delimiter * character replaced by NUL (to terminate the string) is returned in the * variable pointed at by p_delimiter_replaced, if it is not NULL. * * The parse_str pointer is moved on to the character after the delimiter, or * the end of the string if it is reached first. * * @param parse_str Pointer to the string pointer to parse. * @param is_delimiter_fn Function to test if a character is a delimiter or not. * @param p_delimiter_replaced Pointer to variable to receive delimiter character, or NULL. * @return Pointer to extracted string. */ static char *rtsp_parse_extract(char **parse_str, PARSE_IS_DELIMITER_FN_T is_delimiter_fn, char *p_delimiter_replaced) { char *ptr; char *result; vc_container_assert(parse_str); vc_container_assert(*parse_str); vc_container_assert(is_delimiter_fn); ptr = *parse_str; while (isspace((int)*ptr)) ptr++; result = ptr; while (*ptr && !(*is_delimiter_fn)(*ptr)) ptr++; if (p_delimiter_replaced) *p_delimiter_replaced = *ptr; if (*ptr) *ptr++ = '\0'; *parse_str = ptr; return result; } /**************************************************************************//** * Specialised form of rtsp_parse_extract() where the delimiter is whitespace. * Returns pointer to the string with any leading whitespace trimmed and * terminated by further whitespace. * * The parse_str pointer is moved on to the character after the delimiter, or * the end of the string if it is reached first. * * @param parse_str Pointer to the string pointer to parse. * @return Pointer to extracted string. */ static char *rtsp_parse_extract_ws(char **parse_str) { char *ptr; char *result; vc_container_assert(parse_str); vc_container_assert(*parse_str); ptr = *parse_str; while (isspace((int)*ptr)) ptr++; result = ptr; while (*ptr && !isspace((int)*ptr)) ptr++; if (*ptr) *ptr++ = '\0'; *parse_str = ptr; return result; } /**************************************************************************//** * Returns whether the given character is a parameter name delimiter or not. * * @param char_to_test The character under test. * @return True if the character is a name delimiter, false if not. */ static int name_delimiter_fn(int char_to_test) { switch (char_to_test) { case ' ': case '\t': case '=': case ';': return true; default: return false; } } /**************************************************************************//** * Returns whether the given character is a parameter value delimiter or not. * * @param char_to_test The character under test. * @return True if the character is a value delimiter, false if not. */ static int value_delimiter_fn(int char_to_test) { switch (char_to_test) { case ' ': case '\t': case ';': return true; default: return false; } } /**************************************************************************//** * Extract a name/value pair from a given string. * Each pair consists of a name, optionally followed by '=' and a value, with * optional whitespace around either or both name and value. The parameter is * terminated by a semi-colon, ';'. * * The parse_str pointer is moved on to the next parameter, or the end of the * string if that is reached first. * * Name can be empty if there are two consecutive semi-colons, or a trailing * semi-colon. * * @param parse_str Pointer to the string pointer to be parsed. * @param p_name Pointer to where name string pointer shall be written. * @param p_value Pointer to where value string pointer shall be written. * @return True if the name is not empty. */ static bool rtsp_parse_extract_parameter(char **parse_str, char **p_name, char **p_value) { char delimiter; vc_container_assert(parse_str); vc_container_assert(*parse_str); vc_container_assert(p_name); vc_container_assert(p_value); /* General form of each parameter: * [=] * but allow for spaces before and after name and value */ *p_name = rtsp_parse_extract(parse_str, name_delimiter_fn, &delimiter); if (isspace((int)delimiter)) { /* Skip further spaces after parameter name */ do { delimiter = **parse_str; if (delimiter) (*parse_str)++; } while (isspace((int)delimiter)); } if (delimiter == '=') { /* Parameter value present (although may be empty) */ *p_value = rtsp_parse_extract(parse_str, value_delimiter_fn, &delimiter); if (isspace((int)delimiter)) { /* Skip spaces after parameter value */ do { delimiter = **parse_str; if (delimiter) (*parse_str)++; } while (isspace((int)delimiter)); } } else { *p_value = NULL; } return (**p_name != '\0'); } /**************************************************************************//** * Parses RTP-Info header and stores relevant parts. * * @param header_list The response header list. * @param t_module The track module relating to the response headers. */ static void rtsp_store_rtp_info(VC_CONTAINERS_LIST_T *header_list, VC_CONTAINER_TRACK_MODULE_T *t_module ) { RTSP_HEADER_T header; char *ptr; header.name = RTP_INFO_NAME; if (!vc_containers_list_find_entry(header_list, &header)) return; ptr = header.value; while (ptr && *ptr) { char *name; char *value; if (!rtsp_parse_extract_parameter(&ptr, &name, &value)) continue; if (strcasecmp(name, "rtptime") == 0) { unsigned int timestamp_base = 0; /* coverity[secure_coding] String is null-terminated */ if (sscanf(value, "%u", ×tamp_base) == 1) (void)vc_container_control(t_module->reader, VC_CONTAINER_CONTROL_SET_TIMESTAMP_BASE, timestamp_base); } else if (strcasecmp(name, "seq") == 0) { unsigned short int sequence_number = 0; /* coverity[secure_coding] String is null-terminated */ if (sscanf(value, "%hu", &sequence_number) == 1) (void)vc_container_control(t_module->reader, VC_CONTAINER_CONTROL_SET_NEXT_SEQUENCE_NUMBER, (uint32_t)sequence_number); } } } /**************************************************************************//** * Reads an RTSP response and parses it into headers and content. * The headers and content remain stored in the comms buffer, but referenced * by the module's header list. Content uses a special header name that cannot * occur in the real headers. * * @param p_ctx The RTSP reader context. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_read_response( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_IO_T *p_ctx_io = p_ctx->priv->io; char *next_read = module->comms_buffer; uint32_t space_available = COMMS_BUFFER_SIZE - 1; /* Allow for a NUL */ uint32_t received; char *ptr = next_read; bool found_content = false; RTSP_HEADER_T header; vc_containers_list_reset(module->header_list); /* Response status line doesn't need to be stored, just checked */ header.name = NULL; header.value = next_read; while (space_available) { received = vc_container_io_read(p_ctx_io, next_read, space_available); if (p_ctx_io->status != VC_CONTAINER_SUCCESS) break; next_read += received; space_available -= received; while (!found_content && ptr < next_read) { switch (*ptr) { case ':': if (header.value) { /* Just another character in the value */ ptr++; } else { /* End of name, expect value next */ *ptr++ = '\0'; header.value = ptr; } break; case '\n': if (header.value) { /* End of line while parsing the value part of the header, add name/value pair to list */ *ptr++ = '\0'; header.value = rtsp_trim(header.value); if (header.name) { if (!vc_containers_list_insert(module->header_list, &header, false)) { LOG_ERROR(p_ctx, "RTSP: Failed to add <%s> header to list", header.name); return VC_CONTAINER_ERROR_OUT_OF_MEMORY; } } else { /* Check response status line */ if (!rtsp_successful_response_status(p_ctx, header.value)) return VC_CONTAINER_ERROR_FORMAT_INVALID; } /* Ready for next header */ header.name = ptr; header.value = NULL; } else { uint32_t content_length; /* End of line while parsing the name of a header */ *ptr++ = '\0'; if (*header.name && *header.name != '\r') { /* A non-empty name is invalid, so fail */ LOG_ERROR(p_ctx, "RTSP: Invalid name in header - no colon:\n%s", header.name); return VC_CONTAINER_ERROR_FORMAT_INVALID; } /* An empty name signifies the start of the content has been found */ found_content = true; /* Make a pseudo-header for the content and add it to the list */ header.name = CONTENT_PSEUDOHEADER_NAME; header.value = ptr; if (!vc_containers_list_insert(module->header_list, &header, false)) { LOG_ERROR(p_ctx, "RTSP: Failed to add content pseudoheader to list"); return VC_CONTAINER_ERROR_OUT_OF_MEMORY; } /* Calculate how much content there is left to read, based on Content-Length header */ content_length = rtsp_get_content_length(module->header_list); if (ptr + content_length < next_read) { /* Final content byte already present, with extra data after it */ space_available = 0; } else { uint32_t content_to_read = content_length - (next_read - ptr); if (content_to_read >= space_available) { LOG_ERROR(p_ctx, "RTSP: Not enough room to read content"); return VC_CONTAINER_ERROR_OUT_OF_MEMORY; } /* Restrict further reading to the number of content bytes left */ space_available = content_to_read; } } break; default: /* Just another character in either the name or the value */ ptr++; } } } if (!space_available) { if (found_content) { /* Terminate content region */ *next_read = '\0'; } else { /* Ran out of buffer space and never found the content */ LOG_ERROR(p_ctx, "RTSP: Response header section too big / content missing"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } } return p_ctx_io->status; } /**************************************************************************//** * Creates a new track from an SDP media field. * Limitation: only the first payload type of the field is used. * * @param p_ctx The RTSP reader context. * @param media The media field. * @param p_track Pointer to the variable to receive the new track pointer. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_create_track_for_media_field(VC_CONTAINER_T *p_ctx, char *media, VC_CONTAINER_TRACK_T **p_track ) { VC_CONTAINER_TRACK_T *track = NULL; VC_CONTAINER_TRACK_MODULE_T *t_module = NULL; char *ptr = media; char *media_type; char *rtp_port; char *transport_type; char *payload_type; *p_track = NULL; if (p_ctx->tracks_num == RTSP_TRACKS_MAX) { LOG_DEBUG(p_ctx, "RTSP: Too many media items in SDP data, only %d are supported.", RTSP_TRACKS_MAX); return VC_CONTAINER_ERROR_FORMAT_INVALID; } /* Format of media item: * m= * Only RTP/AVP transport and the first payload type are supported */ media_type = rtsp_parse_extract_ws(&ptr); rtp_port = rtsp_parse_extract_ws(&ptr); transport_type = rtsp_parse_extract_ws(&ptr); payload_type = rtsp_parse_extract_ws(&ptr); if (!*media_type || !*rtp_port || strcmp(transport_type, "RTP/AVP") || !*payload_type) { LOG_ERROR(p_ctx, "RTSP: Failure to parse media field"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } track = vc_container_allocate_track(p_ctx, sizeof(VC_CONTAINER_TRACK_MODULE_T)); if (!track) goto out_of_memory_error; t_module = track->priv->module; /* If the port specifier is invalid, treat it as if it were zero */ /* coverity[secure_coding] String is null-terminated */ sscanf(rtp_port, "%hu", &t_module->rtp_port); t_module->payload_type = payload_type; t_module->media_type = media_type; t_module->reader_uri = vc_uri_create(); if (!t_module->reader_uri) goto out_of_memory_error; if (!vc_uri_set_scheme(t_module->reader_uri, RTP_SCHEME)) goto out_of_memory_error; if (!vc_uri_add_query(t_module->reader_uri, PAYLOAD_TYPE_NAME, payload_type)) goto out_of_memory_error; p_ctx->tracks[p_ctx->tracks_num++] = track; *p_track = track; return VC_CONTAINER_SUCCESS; out_of_memory_error: if (track) { if (t_module->reader_uri) vc_uri_release(t_module->reader_uri); vc_container_free_track(p_ctx, track); } LOG_ERROR(p_ctx, "RTSP: Memory allocation failure creating track"); return VC_CONTAINER_ERROR_OUT_OF_MEMORY; } /**************************************************************************//** * Returns whether the given character is a slash or not. * * @param char_to_test The character under test. * @return True if the character is a slash, false if not. */ static int slash_delimiter_fn(int char_to_test) { return char_to_test == '/'; } /**************************************************************************//** * Parse an rtpmap attribute and store values in the related track. * * @param p_ctx The RTSP reader context. * @param track The track relating to the rtpmap. * @param attribute The rtpmap attribute value. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_parse_rtpmap_attribute( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, char *attribute ) { VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; char *ptr = attribute; char *payload_type; char *mime_sub_type; char *sample_rate; char *full_mime_type; char *channels; /* rtpmap attribute format: * /[/] * Payload type must match the one used in the media field */ payload_type = rtsp_parse_extract_ws(&ptr); if (strcmp(payload_type, t_module->payload_type)) { /* Ignore any unsupported secondary payload type attributes */ LOG_DEBUG(p_ctx, "RTSP: Secondary payload type attribute - not supported"); return VC_CONTAINER_SUCCESS; } mime_sub_type = rtsp_parse_extract(&ptr, slash_delimiter_fn, NULL); if (!*mime_sub_type) { LOG_ERROR(p_ctx, "RTSP: rtpmap: MIME type missing"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } sample_rate = rtsp_parse_extract(&ptr, slash_delimiter_fn, NULL); if (!*sample_rate) { LOG_ERROR(p_ctx, "RTSP: rtpmap: sample rate missing"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } full_mime_type = (char *)malloc(strlen(t_module->media_type) + strlen(mime_sub_type) + 2); if (!full_mime_type) { LOG_ERROR(p_ctx, "RTSP: Failed to allocate space for full MIME type"); return VC_CONTAINER_ERROR_OUT_OF_MEMORY; } /* coverity[secure_coding] String has been allocated of the right size */ sprintf(full_mime_type, "%s/%s", t_module->media_type, mime_sub_type); if (!vc_uri_add_query(t_module->reader_uri, MIME_TYPE_NAME, full_mime_type)) { free(full_mime_type); LOG_ERROR(p_ctx, "RTSP: Failed to add MIME type to URI"); return VC_CONTAINER_ERROR_OUT_OF_MEMORY; } free(full_mime_type); if (!vc_uri_add_query(t_module->reader_uri, SAMPLE_RATE_NAME, sample_rate)) { LOG_ERROR(p_ctx, "RTSP: Failed to add sample rate to URI"); return VC_CONTAINER_ERROR_OUT_OF_MEMORY; } /* Optional channels specifier */ channels = rtsp_parse_extract_ws(&ptr); if (*channels) { if (!vc_uri_add_query(t_module->reader_uri, CHANNELS_NAME, channels)) { LOG_ERROR(p_ctx, "RTSP: Failed to add channels to URI"); return VC_CONTAINER_ERROR_OUT_OF_MEMORY; } } return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * Parse an fmtp attribute and store values in the related track. * * @param p_ctx The RTSP reader context. * @param track The track relating to the fmtp. * @param attribute The fmtp attribute value. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_parse_fmtp_attribute( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, char *attribute ) { VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; char *ptr = attribute; char *payload_type; /* fmtp attribute format: * * The payload type must match the first one in the media field, parameters * are semi-colon separated and may have additional whitespace around them. */ payload_type = rtsp_parse_extract_ws(&ptr); if (strcmp(payload_type, t_module->payload_type)) { /* Ignore any unsupported secondary payload type attributes */ LOG_DEBUG(p_ctx, "RTSP: Secondary payload type attribute - not supported"); return VC_CONTAINER_SUCCESS; } while (*ptr) { char *name; char *value; /* Only add the parameter if the name was not empty. This avoids problems with * strings like ";;", ";" or ";=value;" */ if (rtsp_parse_extract_parameter(&ptr, &name, &value)) { if (!vc_uri_add_query(t_module->reader_uri, name, value)) { if (value) LOG_ERROR(p_ctx, "RTSP: Failed to add <%s>=<%s> query to URI", name, value); else LOG_ERROR(p_ctx, "RTSP: Failed to add <%s> query to URI", name); return VC_CONTAINER_ERROR_OUT_OF_MEMORY; } } } return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * Merge base URI and relative URI strings into a merged URI string. * Always creates a new string, even if the relative URI is actually absolute. * * @param p_ctx The RTSP reader context. * @param base_uri_str The base URI string. * @param relative_uri_str The relative URI string. * @param p_merged_uri_str Pointer to where to put the merged string pointer. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_merge_uris( VC_CONTAINER_T *p_ctx, const char *base_uri_str, const char *relative_uri_str, char **p_merged_uri_str) { VC_URI_PARTS_T *base_uri = NULL; VC_URI_PARTS_T *relative_uri = NULL; VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; uint32_t merged_size; *p_merged_uri_str = NULL; relative_uri = vc_uri_create(); if (!relative_uri) goto tidy_up; if (!vc_uri_parse(relative_uri, relative_uri_str)) { status = VC_CONTAINER_ERROR_FORMAT_INVALID; goto tidy_up; } if (vc_uri_scheme(relative_uri) != NULL) { /* URI is absolute, not relative, so return it as the merged URI */ size_t len = strlen(relative_uri_str); *p_merged_uri_str = (char *)malloc(len + 1); if (!*p_merged_uri_str) goto tidy_up; strncpy(*p_merged_uri_str, relative_uri_str, len); status = VC_CONTAINER_SUCCESS; goto tidy_up; } base_uri = vc_uri_create(); if (!base_uri) goto tidy_up; if (!vc_uri_parse(base_uri, base_uri_str)) { status = VC_CONTAINER_ERROR_FORMAT_INVALID; goto tidy_up; } /* Build up merged URI in relative_uri, using base_uri as necessary */ if (!vc_uri_merge(base_uri, relative_uri)) goto tidy_up; merged_size = vc_uri_build(relative_uri, NULL, 0) + 1; *p_merged_uri_str = (char *)malloc(merged_size); if (!*p_merged_uri_str) goto tidy_up; vc_uri_build(relative_uri, *p_merged_uri_str, merged_size); status = VC_CONTAINER_SUCCESS; tidy_up: if (base_uri) vc_uri_release(base_uri); if (relative_uri) vc_uri_release(relative_uri); if (status != VC_CONTAINER_SUCCESS) LOG_ERROR(p_ctx, "RTSP: Error merging URIs: %d", (int)status); return status; } /**************************************************************************//** * Parse a control attribute and store it as an absolute URI. * * @param p_ctx The RTSP reader context. * @param attribute The control attribute value. * @param base_uri_str The base URI string. * @param p_control_uri_str Pointer to where to put the control string pointer. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_parse_control_attribute( VC_CONTAINER_T *p_ctx, const char *attribute, const char *base_uri_str, char **p_control_uri_str) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; /* control attribute format: * * The control URI is either absolute or relative to the base URI. If the * control URI is just an asterisk, the control URI matches the base URI. */ if (!*attribute || strcmp(attribute, "*") == 0) { size_t len = strlen(base_uri_str); *p_control_uri_str = (char *)malloc(len + 1); if (!*p_control_uri_str) { LOG_ERROR(p_ctx, "RTSP: Failed to allocate control URI"); return VC_CONTAINER_ERROR_OUT_OF_MEMORY; } strncpy(*p_control_uri_str, base_uri_str, len); } else { status = rtsp_merge_uris(p_ctx, base_uri_str, attribute, p_control_uri_str); } return status; } /**************************************************************************//** * Open a reader for the track using the URI that has been generated. * * @param p_ctx The RTSP reader context. * @param t_module The track module for which a reader is needed. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_open_track_reader( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_MODULE_T *t_module ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; uint32_t uri_buffer_size; char *uri_buffer; uri_buffer_size = vc_uri_build(t_module->reader_uri, NULL, 0) + 1; uri_buffer = (char *)malloc(uri_buffer_size); if (!uri_buffer) { LOG_ERROR(p_ctx, "RTSP: Failed to build RTP URI"); return VC_CONTAINER_ERROR_OUT_OF_MEMORY; } vc_uri_build(t_module->reader_uri, uri_buffer, uri_buffer_size); t_module->reader = vc_container_open_reader(uri_buffer, &status, NULL, NULL); free(uri_buffer); return status; } /**************************************************************************//** * Open a reader for the track using the network URI that has been generated. * * @param p_ctx The RTSP reader context. * @param t_module The track module for which a reader is needed. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_open_network_reader( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_MODULE_T *t_module ) { char port[PORT_BUFFER_SIZE] = {0}; if (!t_module->rtp_port) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; t_module->rtp_port = module->next_rtp_port; if (t_module->rtp_port > LAST_DYNAMIC_PORT) { LOG_ERROR(p_ctx, "RTSP: Out of dynamic ports"); return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; } module->next_rtp_port += 2; } snprintf(port, sizeof(port)-1, "%hu", t_module->rtp_port); if (!vc_uri_set_port(t_module->reader_uri, port)) { LOG_ERROR(p_ctx, "RTSP: Failed to set track reader URI port"); return VC_CONTAINER_ERROR_OUT_OF_MEMORY; } return rtsp_open_track_reader(p_ctx, t_module); } /**************************************************************************//** * Open a reader for the track using the file URI that has been generated. * * @param p_ctx The RTSP reader context. * @param t_module The track module for which a reader is needed. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_open_file_reader( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_MODULE_T *t_module ) { VC_CONTAINER_STATUS_T status; VC_URI_PARTS_T *rtsp_uri = NULL; const char *rtsp_path; int len; char *new_path = NULL; char *extension; /* Use the RTSP URI's path, with the extension changed to ".t.pkt" * where is the zero-based media item number. */ rtsp_uri = vc_uri_create(); if (!rtsp_uri) { LOG_ERROR(p_ctx, "RTSP: Failed to create RTSP URI"); status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto tidy_up; } if (!vc_uri_parse(rtsp_uri, p_ctx->priv->io->uri)) { LOG_ERROR(p_ctx, "RTSP: Failed to parse RTSP URI <%s>", p_ctx->priv->io->uri); status = VC_CONTAINER_ERROR_FORMAT_INVALID; goto tidy_up; } rtsp_path = vc_uri_path(rtsp_uri); if (!rtsp_path || !*rtsp_path) { LOG_ERROR(p_ctx, "RTSP: RTSP URI path missing <%s>", p_ctx->priv->io->uri); status = VC_CONTAINER_ERROR_FORMAT_INVALID; goto tidy_up; } len = strlen(rtsp_path); new_path = (char *)calloc(1, len + RTP_PATH_EXTRA + 1); if (!rtsp_uri) { LOG_ERROR(p_ctx, "RTSP: Failed to create buffer for RTP path"); status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto tidy_up; } strncpy(new_path, rtsp_path, len); extension = strrchr(new_path, '.'); /* Find extension, to replace it */ if (!extension) extension = new_path + strlen(new_path); /* No extension, so append instead */ snprintf(extension, len + RTP_PATH_EXTRA - (extension - new_path), RTP_PATH_EXTENSION_FORMAT, p_ctx->priv->module->media_item); if (!vc_uri_set_path(t_module->reader_uri, new_path)) { LOG_ERROR(p_ctx, "RTSP: Failed to store RTP path <%s>", new_path); status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto tidy_up; } free(new_path); vc_uri_release(rtsp_uri); return rtsp_open_track_reader(p_ctx, t_module); tidy_up: if (new_path) free(new_path); if (rtsp_uri) vc_uri_release(rtsp_uri); return status; } /**************************************************************************//** * Copy track information from the encapsulated track reader's track to the * RTSP track. * * @param p_ctx The RTSP reader context. * @param track The RTSP track requiring its information to be filled in. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_copy_track_data_from_reader( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track ) { VC_CONTAINER_T *reader = track->priv->module->reader; VC_CONTAINER_ES_FORMAT_T *src_format, *dst_format; if (reader->tracks_num != 1) { LOG_ERROR(p_ctx, "RTSP: Expected track reader to have one track, has %d", reader->tracks_num); return VC_CONTAINER_ERROR_FORMAT_INVALID; } if (reader->tracks[0]->meta_num) { LOG_DEBUG(p_ctx, "RTSP: Track reader has meta data - not supported"); } src_format = reader->tracks[0]->format; dst_format = track->format; /* Copy fields individually to avoid problems with pointers (type and extradata). */ dst_format->es_type = src_format->es_type; dst_format->codec = src_format->codec; dst_format->codec_variant = src_format->codec_variant; *dst_format->type = *src_format->type; dst_format->bitrate = src_format->bitrate; memcpy(dst_format->language, src_format->language, sizeof(dst_format->language)); dst_format->group_id = src_format->group_id; dst_format->flags = src_format->flags; if (src_format->extradata) { VC_CONTAINER_STATUS_T status; uint32_t extradata_size = src_format->extradata_size; status = vc_container_track_allocate_extradata(p_ctx, track, extradata_size); if (status != VC_CONTAINER_SUCCESS) return status; memcpy(dst_format->extradata, src_format->extradata, extradata_size); dst_format->extradata_size = extradata_size; } track->is_enabled = reader->tracks[0]->is_enabled; return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * Finalise the creation of the RTSP track by opening its reader and filling in * the track information. * * @param p_ctx The RTSP reader context. * @param track The RTSP track being finalised. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_complete_track( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; if (!t_module->control_uri) { LOG_ERROR(p_ctx, "RTSP: Track control URI is missing"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } if (module->uri_has_network_info) { int ii; if (!vc_uri_set_host(t_module->reader_uri, "")) { LOG_ERROR(p_ctx, "RTSP: Failed to set track reader URI host"); return VC_CONTAINER_ERROR_OUT_OF_MEMORY; } status = rtsp_open_network_reader(p_ctx, t_module); for (ii = 0; status == VC_CONTAINER_ERROR_URI_OPEN_FAILED && ii < DYNAMIC_PORT_ATTEMPTS_MAX; ii++) { /* Reset port to pick up next dynamic port */ t_module->rtp_port = 0; status = rtsp_open_network_reader(p_ctx, t_module); } /* Change I/O to non-blocking, so that tracks can be polled */ if (status == VC_CONTAINER_SUCCESS) status = vc_container_control(t_module->reader, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, 0); /* Set a large read buffer, to avoid dropping bursts of large packets (e.g. hi-def video) */ if (status == VC_CONTAINER_SUCCESS) status = vc_container_control(t_module->reader, VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE, UDP_READ_BUFFER_SIZE); } else { status = rtsp_open_file_reader(p_ctx, t_module); } vc_uri_release(t_module->reader_uri); t_module->reader_uri = NULL; if (status == VC_CONTAINER_SUCCESS) status = rtsp_copy_track_data_from_reader(p_ctx, track); return status; } /**************************************************************************//** * Returns whether the given character is an attribute name delimiter or not. * * @param char_to_test The character under test. * @return True if the character is an attribute name delimiter, false if not. */ static int attribute_name_delimiter_fn(int char_to_test) { return (char_to_test == ':'); } /**************************************************************************//** * Create RTSP tracks from media fields in SDP formatted data. * * @param p_ctx The RTSP reader context. * @param sdp_buffer The SDP data. * @param base_uri The RTSP base URI. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_create_tracks_from_sdp( VC_CONTAINER_T *p_ctx, char *sdp_buffer, char *base_uri ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_TRACK_T *track = NULL; char *session_base_uri = base_uri; char *ptr; char *next_line_ptr; char *attribute; for (ptr = sdp_buffer; status == VC_CONTAINER_SUCCESS && *ptr; ptr = next_line_ptr) { /* Find end of line */ char field = *ptr; next_line_ptr = ptr; while (*next_line_ptr && *next_line_ptr != '\n') next_line_ptr++; /* Terminate line */ if (*next_line_ptr) *next_line_ptr++ = '\0'; /* The format of the line has to be "=" where is a single * character. Ignore anything else. */ if (ptr[1] != '=') continue; ptr = rtsp_trim(ptr + 2); switch (field) { case 'm': /* Start of media item */ if (track) { /* Finish previous track */ status = rtsp_complete_track(p_ctx, track); track = NULL; p_ctx->priv->module->media_item++; if (status != VC_CONTAINER_SUCCESS) break; } status = rtsp_create_track_for_media_field(p_ctx, ptr, &track); break; case 'a': /* Attribute (either session or media level) */ /* Attributes of the form "a=:" */ attribute = rtsp_parse_extract(&ptr, attribute_name_delimiter_fn, NULL); if (track) { /* Media level attribute */ /* Look for known attributes */ if (strcmp(attribute, "rtpmap") == 0) status = rtsp_parse_rtpmap_attribute(p_ctx, track, ptr); else if (strcmp(attribute, "fmtp") == 0) status = rtsp_parse_fmtp_attribute(p_ctx, track, ptr); else if (strcmp(attribute, "control") == 0) { char **track_control_uri = &track->priv->module->control_uri; if (*track_control_uri) { free(*track_control_uri); *track_control_uri = NULL; } status = rtsp_parse_control_attribute(p_ctx, ptr, session_base_uri, track_control_uri); } /* Any other attributes are ignored */ } else { /* Session level attribute */ if (strcmp(attribute, "control") == 0) { /* Only need to change the session_base_uri if it differs from the base URI */ ptr = rtsp_trim(ptr); if (session_base_uri != base_uri) { free(session_base_uri); session_base_uri = base_uri; } if (strcmp(ptr, base_uri) != 0) status = rtsp_parse_control_attribute(p_ctx, ptr, base_uri, &session_base_uri); } } break; default: /* Ignore any other field names */ ; } } if (session_base_uri && session_base_uri != base_uri) free(session_base_uri); /* Having no media fields is an error, since there will be nothing to play back */ if (status == VC_CONTAINER_SUCCESS) { if (!p_ctx->tracks_num) status = VC_CONTAINER_ERROR_FORMAT_INVALID; else if (track) { /* Finish final track */ status = rtsp_complete_track(p_ctx, track); p_ctx->priv->module->media_item++; } } return status; } /**************************************************************************//** * Create RTSP tracks from the response to a DESCRIBE request. * The response must have already been filled into the comms buffer and header * list. * * @param p_ctx The RTSP reader context. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_create_tracks_from_response( VC_CONTAINER_T *p_ctx ) { VC_CONTAINERS_LIST_T *header_list = p_ctx->priv->module->header_list; RTSP_HEADER_T header; char *base_uri; char *content; header.name = CONTENT_PSEUDOHEADER_NAME; if (!vc_containers_list_find_entry(header_list, &header)) { LOG_ERROR(p_ctx, "RTSP: Content missing"); return VC_CONTAINER_ERROR_FORMAT_INVALID; } content = header.value; /* The control URI may be relative to a base URI which is the first of these * that is available: * 1. Content-Base header * 2. Content-Location header * 3. Request URI */ header.name = CONTENT_BASE_NAME; if (vc_containers_list_find_entry(header_list, &header)) base_uri = header.value; else { header.name = CONTENT_LOCATION_NAME; if (vc_containers_list_find_entry(header_list, &header)) base_uri = header.value; else base_uri = p_ctx->priv->io->uri; } return rtsp_create_tracks_from_sdp(p_ctx, content, base_uri); } /**************************************************************************//** * Header comparison function. * Compare two header structures and return whether the first is less than, * equal to or greater than the second. * * @param first The first structure to be compared. * @param second The second structure to be compared. * @return Negative if first is less than second, positive if first is greater * and zero if they are equal. */ static int rtsp_header_comparator(const RTSP_HEADER_T *first, const RTSP_HEADER_T *second) { return strcasecmp(first->name, second->name); } /**************************************************************************//** * Make a DESCRIBE request to the server and create tracks from the response. * * @param p_ctx The RTSP reader context. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_describe( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; /* Send DESCRIBE request and get response */ status = rtsp_send_describe_request(p_ctx); if (status != VC_CONTAINER_SUCCESS) return status; status = rtsp_read_response(p_ctx); if (status != VC_CONTAINER_SUCCESS) return status; /* Create tracks from SDP content */ status = rtsp_create_tracks_from_response(p_ctx); return status; } /**************************************************************************//** * Make a SETUP request to the server and get session from response. * * @param p_ctx The RTSP reader context. * @param t_module The track module to be set up. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_setup( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_MODULE_T *t_module ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; const char *session_header; size_t session_header_len; status = rtsp_send_setup_request(p_ctx, t_module); if (status != VC_CONTAINER_SUCCESS) return status; status = rtsp_read_response(p_ctx); if (status != VC_CONTAINER_SUCCESS) return status; session_header = rtsp_get_session_header(module->header_list); session_header_len = strlen(session_header); if (session_header_len > SESSION_HEADER_LENGTH_MAX) return VC_CONTAINER_ERROR_FORMAT_INVALID; t_module->session_header = (char *)malloc(session_header_len + 1); if (!t_module->session_header) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; strncpy(t_module->session_header, session_header, session_header_len); return status; } /**************************************************************************//** * Make a SETUP request to the server and get session from response. * * @param p_ctx The RTSP reader context. * @param t_module The track module to be set up. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_play( VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_MODULE_T *t_module ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; status = rtsp_send_play_request(p_ctx, t_module); if (status != VC_CONTAINER_SUCCESS) return status; status = rtsp_read_response(p_ctx); if (status != VC_CONTAINER_SUCCESS) return status; rtsp_store_rtp_info(module->header_list, t_module); return status; } /**************************************************************************//** * Blocking read/skip data from a container. * Can also be used to query information about the next block of data. * * @pre The container is set to non-blocking. * @post The container is set to non-blocking. * * @param p_ctx The reader context. * @param p_packet The container packet information, or NULL. * @param flags The container read flags. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_blocking_track_read(VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags) { VC_CONTAINER_STATUS_T status; status = vc_container_read(p_ctx, p_packet, flags); /* The ..._ABORTED status corresponds to a timeout waiting for data */ if (status == VC_CONTAINER_ERROR_ABORTED) { /* So switch to blocking temporarily to wait for some */ (void)vc_container_control(p_ctx, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, VC_CONTAINER_READ_TIMEOUT_BLOCK); status = vc_container_read(p_ctx, p_packet, flags); (void)vc_container_control(p_ctx, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, 0); } return status; } /**************************************************************************//** * Update the cached packet info blocks for all tracks. * If one or more of the tracks has data, set the current track to the one with * the earliest decode timestamp. * * @pre The track readers must not block when data is requested from them. * * @param p_ctx The RTSP reader context. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_update_track_info( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; uint32_t tracks_num = p_ctx->tracks_num; uint32_t track_idx; int64_t earliest_dts = MAXIMUM_INT64; VC_CONTAINER_TRACK_MODULE_T *earliest_track = NULL; /* Reset current track to unknown */ p_ctx->priv->module->current_track = NULL; /* Collect each track's info and return the one with earliest timestamp. */ for (track_idx = 0; track_idx < tracks_num; track_idx++) { VC_CONTAINER_TRACK_MODULE_T *t_module = p_ctx->tracks[track_idx]->priv->module; VC_CONTAINER_PACKET_T *info = &t_module->info; /* If this track has no data available, request more */ if (!info->size) { /* This is a non-blocking read, so status will be ..._ABORTED if nothing available */ status = vc_container_read(t_module->reader, info, VC_CONTAINER_READ_FLAG_INFO); /* Adjust track index to be the RTSP index instead of the RTP one */ info->track = track_idx; } if (status == VC_CONTAINER_SUCCESS) { if (info->dts < earliest_dts) { earliest_dts = info->dts; earliest_track = t_module; } } else if (status != VC_CONTAINER_ERROR_ABORTED) { /* Not a time-out failure, so abort */ return status; } } p_ctx->priv->module->current_track = earliest_track; return VC_CONTAINER_SUCCESS; } /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ /**************************************************************************//** * Read/skip data from the container. * Can also be used to query information about the next block of data. * * @param p_ctx The reader context. * @param p_packet The container packet information, or NULL. * @param flags The container read flags. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_reader_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_TRACK_MODULE_T *current_track = module->current_track; VC_CONTAINER_PACKET_T *info; if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) { vc_container_assert(p_packet); vc_container_assert(p_packet->track < p_ctx->tracks_num); current_track = p_ctx->tracks[p_packet->track]->priv->module; module->current_track = current_track; if (!current_track->info.size) { status = rtsp_blocking_track_read(current_track->reader, ¤t_track->info, VC_CONTAINER_READ_FLAG_INFO); if (status != VC_CONTAINER_SUCCESS) goto error; } } else if (!current_track || !current_track->info.size) { status = rtsp_update_track_info(p_ctx); if (status != VC_CONTAINER_SUCCESS) goto error; while (!module->current_track) { /* Check RTSP stream to see if it has closed */ status = rtsp_read_response(p_ctx); if (status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_ABORTED) { /* No data from any track yet, so keep checking */ status = rtsp_update_track_info(p_ctx); } if (status != VC_CONTAINER_SUCCESS) goto error; } current_track = module->current_track; } info = ¤t_track->info; vc_container_assert(info->size); if (flags & VC_CONTAINER_READ_FLAG_INFO) { vc_container_assert(p_packet); memcpy(p_packet, info, sizeof(*info)); } else { status = rtsp_blocking_track_read(current_track->reader, p_packet, flags); if (status != VC_CONTAINER_SUCCESS) goto error; if (p_packet) { p_packet->track = info->track; if (flags & VC_CONTAINER_READ_FLAG_SKIP) { info->size = 0; } else { vc_container_assert(info->size >= p_packet->size); info->size -= p_packet->size; } } else { info->size = 0; } } if (p_packet) { /* Adjust timestamps to be relative to zero */ if (!module->ts_base) module->ts_base = p_packet->dts; p_packet->dts -= module->ts_base; p_packet->pts -= module->ts_base; } error: STREAM_STATUS(p_ctx) = status; return status; } /**************************************************************************//** * Seek over data in the container. * * @param p_ctx The reader context. * @param p_offset The seek offset. * @param mode The seek mode. * @param flags The seek flags. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *p_offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) { VC_CONTAINER_PARAM_UNUSED(p_ctx); VC_CONTAINER_PARAM_UNUSED(p_offset); VC_CONTAINER_PARAM_UNUSED(mode); VC_CONTAINER_PARAM_UNUSED(flags); /* RTSP is a non-seekable container */ return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; } /**************************************************************************//** * Close the container. * * @param p_ctx The reader context. * @return The resulting status of the function. */ static VC_CONTAINER_STATUS_T rtsp_reader_close( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; unsigned int i; for(i = 0; i < p_ctx->tracks_num; i++) { VC_CONTAINER_TRACK_MODULE_T *t_module = p_ctx->tracks[i]->priv->module; if (t_module->control_uri && t_module->session_header) { /* Send the teardown message and wait for a response, although it * isn't important whether it was successful or not. */ if (rtsp_send_teardown_request(p_ctx, t_module) == VC_CONTAINER_SUCCESS) (void)rtsp_read_response(p_ctx); } if (t_module->reader) vc_container_close(t_module->reader); if (t_module->reader_uri) vc_uri_release(t_module->reader_uri); if (t_module->control_uri) free(t_module->control_uri); if (t_module->session_header) free(t_module->session_header); vc_container_free_track(p_ctx, p_ctx->tracks[i]); /* Also need to close track's reader */ } p_ctx->tracks = NULL; p_ctx->tracks_num = 0; if (module) { if (module->comms_buffer) free(module->comms_buffer); if (module->header_list) vc_containers_list_destroy(module->header_list); free(module); } p_ctx->priv->module = 0; return VC_CONTAINER_SUCCESS; } /**************************************************************************//** * Open the container. * Uses the I/O URI and/or data to configure the container. * * @param p_ctx The reader context. * @return The resulting status of the function. */ VC_CONTAINER_STATUS_T rtsp_reader_open( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = 0; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; uint32_t ii; /* Check the URI scheme looks valid */ if (!vc_uri_scheme(p_ctx->priv->uri) || (strcasecmp(vc_uri_scheme(p_ctx->priv->uri), RTSP_SCHEME) && strcasecmp(vc_uri_scheme(p_ctx->priv->uri), RTSP_PKT_SCHEME))) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Allocate our context */ if ((module = (VC_CONTAINER_MODULE_T *)malloc(sizeof(VC_CONTAINER_MODULE_T))) == NULL) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); p_ctx->priv->module = module; p_ctx->tracks = module->tracks; module->next_rtp_port = FIRST_DYNAMIC_PORT; module->cseq_value = 0; module->uri_has_network_info = (strncasecmp(p_ctx->priv->io->uri, RTSP_NETWORK_URI_START, RTSP_NETWORK_URI_START_LENGTH) == 0); module->comms_buffer = (char *)calloc(1, COMMS_BUFFER_SIZE+1); if (!module->comms_buffer) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } /* header_list will contain pointers into the response_buffer, so take care in re-use */ module->header_list = vc_containers_list_create(HEADER_LIST_INITIAL_CAPACITY, sizeof(RTSP_HEADER_T), (VC_CONTAINERS_LIST_COMPARATOR_T)rtsp_header_comparator); if (!module->header_list) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } status = rtsp_describe(p_ctx); for (ii = 0; status == VC_CONTAINER_SUCCESS && ii < p_ctx->tracks_num; ii++) status = rtsp_setup(p_ctx, p_ctx->tracks[ii]->priv->module); for (ii = 0; status == VC_CONTAINER_SUCCESS && ii < p_ctx->tracks_num; ii++) status = rtsp_play(p_ctx, p_ctx->tracks[ii]->priv->module); if (status != VC_CONTAINER_SUCCESS) goto error; /* Set the RTSP stream to block briefly, to allow polling for closure as well as to avoid spinning CPU */ vc_container_control(p_ctx, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, DATA_UNAVAILABLE_READ_TIMEOUT_MS); p_ctx->priv->pf_close = rtsp_reader_close; p_ctx->priv->pf_read = rtsp_reader_read; p_ctx->priv->pf_seek = rtsp_reader_seek; if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) goto error; return VC_CONTAINER_SUCCESS; error: if(status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_EOS) status = VC_CONTAINER_ERROR_FORMAT_INVALID; LOG_DEBUG(p_ctx, "error opening RTSP stream (%i)", status); rtsp_reader_close(p_ctx); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak reader_open rtsp_reader_open #endif userland/containers/rv9/000077500000000000000000000000001421703157200155625ustar00rootroot00000000000000userland/containers/rv9/CMakeLists.txt000066400000000000000000000005661421703157200203310ustar00rootroot00000000000000# Container module needs to go in as a plugins so different prefix # and install path set(CMAKE_SHARED_LIBRARY_PREFIX "") # Make sure the compiler can find the necessary include files include_directories (../..) add_library(reader_rv9 ${LIBRARY_TYPE} rv9_reader.c) target_link_libraries(reader_rv9 containers) install(TARGETS reader_rv9 DESTINATION ${VMCS_PLUGIN_DIR}) userland/containers/rv9/rv9_reader.c000066400000000000000000000275071421703157200200030ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_logging.h" /****************************************************************************** Defines. ******************************************************************************/ #define BI32(b) (((b)[0]<<24)|((b)[1]<<16)|((b)[2]<<8)|((b)[3])) #define BI16(b) (((b)[0]<<8)|((b)[1])) #define FRAME_HEADER_LEN 20 #define MAX_NUM_SEGMENTS 64 /****************************************************************************** Type definitions ******************************************************************************/ typedef struct { uint32_t len; uint32_t timestamp; uint16_t sequence; uint16_t flags; uint32_t num_segments; uint32_t seg_offset; } RV9_FRAME_HEADER_T; typedef struct VC_CONTAINER_MODULE_T { VC_CONTAINER_TRACK_T *track; uint8_t mid_frame; uint32_t frame_read; uint32_t frame_len; RV9_FRAME_HEADER_T hdr; uint8_t data[FRAME_HEADER_LEN + (MAX_NUM_SEGMENTS<<3) + 1]; uint32_t data_len; uint8_t type; } VC_CONTAINER_MODULE_T; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T rv9_reader_open( VC_CONTAINER_T * ); /****************************************************************************** Local Functions ******************************************************************************/ static VC_CONTAINER_STATUS_T rv9_read_file_header(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track) { VC_CONTAINER_STATUS_T status; VC_CONTAINER_FOURCC_T codec; uint8_t dummy[12]; uint32_t length; if(PEEK_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy)) return VC_CONTAINER_ERROR_EOS; length = BI32(dummy); if(length < 12 || length > 1024) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if(dummy[4] != 'V' || dummy[5] != 'I' || dummy[6] != 'D' || dummy[7] != 'O' || dummy[8] != 'R' || dummy[9] != 'V' || dummy[11] != '0') return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; switch(dummy[10]) { case '4': codec = VC_CONTAINER_CODEC_RV40; break; case '3': codec = VC_CONTAINER_CODEC_RV30; break; case '2': codec = VC_CONTAINER_CODEC_RV20; break; case '1': codec = VC_CONTAINER_CODEC_RV10; break; default: return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; } if (!track) return VC_CONTAINER_SUCCESS; status = vc_container_track_allocate_extradata(p_ctx, track, length); if(status != VC_CONTAINER_SUCCESS) return status; if(READ_BYTES(p_ctx, track->format->extradata, length) != length) return VC_CONTAINER_ERROR_EOS; track->format->extradata_size = length; track->format->codec = codec; return STREAM_STATUS(p_ctx); } static VC_CONTAINER_STATUS_T rv9_read_frame_header(VC_CONTAINER_T *p_ctx) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; uint32_t seg_offset = (uint32_t) -1; uint8_t *buffer = module->data + FRAME_HEADER_LEN; if(READ_BYTES(p_ctx, module->data, FRAME_HEADER_LEN) != FRAME_HEADER_LEN) return VC_CONTAINER_ERROR_EOS; module->data_len = FRAME_HEADER_LEN; module->hdr.len = BI32(module->data); module->hdr.timestamp = BI32(module->data+4); module->hdr.sequence = BI16(module->data+8); module->hdr.flags = BI16(module->data+10); module->hdr.num_segments = BI32(module->data+16); module->frame_len = FRAME_HEADER_LEN + (module->hdr.num_segments * 8) + module->hdr.len; // if we have space, we store up the segments in memory so we can tell the frame // type, since most streams have their type byte as the first follow the segment information. // if we don't have space, then we just don't know the frame type, so will not emit timestamp // information as we don't know if it's reliable. if(module->hdr.num_segments <= MAX_NUM_SEGMENTS) { uint32_t i; if(READ_BYTES(p_ctx, buffer, 8*module->hdr.num_segments) != 8*module->hdr.num_segments) return VC_CONTAINER_ERROR_EOS; module->data_len += (module->hdr.num_segments * 8); for (i=0; ihdr.num_segments; i++) { uint32_t valid_seg; uint32_t offset; valid_seg = BI32(buffer); offset = BI32(buffer+4); if (valid_seg && seg_offset > offset) seg_offset = offset; // this boolean field should have only 0 or 1 values if(valid_seg > 1) return VC_CONTAINER_ERROR_FORMAT_INVALID; buffer += 8; } } if(seg_offset == 0) { if (READ_BYTES(p_ctx, buffer, 1) != 1) return VC_CONTAINER_ERROR_EOS; module->data_len += 1; module->type = (*buffer >> 5) & 3; } else module->type = (uint8_t) -1; return VC_CONTAINER_SUCCESS; } static uint32_t rv9_get_frame_data(VC_CONTAINER_T *p_ctx, uint32_t len, uint8_t *dest) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; uint32_t ret = 0; // we may have read some data before into the data array, so // check whether we've copied all this data out first. if(module->frame_read < module->data_len) { uint32_t copy = MIN(len, module->data_len - module->frame_read); if(dest) { memcpy(dest, module->data + module->frame_read, copy); dest += copy; } ret += copy; len -= copy; } // if there is still more to do, we need to access the IO to do this. if(len > 0) { if(dest) ret += READ_BYTES(p_ctx, dest, len); else ret += SKIP_BYTES(p_ctx, len); } module->frame_read += ret; return ret; } /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ static VC_CONTAINER_STATUS_T rv9_reader_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *packet, uint32_t flags ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_STATUS_T status; unsigned int size; if(!module->mid_frame) { if((status = rv9_read_frame_header(p_ctx)) != VC_CONTAINER_SUCCESS) return status; module->mid_frame = 1; module->frame_read = 0; } packet->size = module->frame_len; packet->pts = module->type < 3 ? module->hdr.timestamp * 1000LL : VC_CONTAINER_TIME_UNKNOWN; packet->dts = packet->pts; packet->track = 0; packet->flags = module->type < 2 ? VC_CONTAINER_PACKET_FLAG_KEYFRAME : 0; if(module->frame_read == 0) packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; if(flags & VC_CONTAINER_READ_FLAG_SKIP) { size = rv9_get_frame_data(p_ctx, module->frame_len - module->frame_read, NULL); if(module->frame_read == module->frame_len) { module->frame_read = 0; module->mid_frame = 0; } return STREAM_STATUS(p_ctx); } if(flags & VC_CONTAINER_READ_FLAG_INFO) return VC_CONTAINER_SUCCESS; size = MIN(module->frame_len - module->frame_read, packet->buffer_size); size = rv9_get_frame_data(p_ctx, size, packet->data); if(module->frame_read == module->frame_len) { module->frame_read = 0; module->mid_frame = 0; packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; } packet->size = size; return size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T rv9_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; VC_CONTAINER_PARAM_UNUSED(flags); if(*offset == 0LL && mode == VC_CONTAINER_SEEK_MODE_TIME) { SEEK(p_ctx, module->track->format->extradata_size); module->mid_frame = 0; module->frame_read = 0; return STREAM_STATUS(p_ctx); } else return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T rv9_reader_close( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T rv9_reader_open( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = 0; VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Check the file header */ if(rv9_read_file_header(p_ctx, 0) != VC_CONTAINER_SUCCESS) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Allocate our context */ module = malloc(sizeof(*module)); if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); p_ctx->priv->module = module; p_ctx->tracks_num = 1; p_ctx->tracks = &module->track; p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0); if(!p_ctx->tracks[0]) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } p_ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; p_ctx->tracks[0]->format->codec = VC_CONTAINER_CODEC_RV40; p_ctx->tracks[0]->is_enabled = true; if((status = rv9_read_file_header(p_ctx, p_ctx->tracks[0])) != VC_CONTAINER_SUCCESS) goto error; LOG_DEBUG(p_ctx, "using rv9 reader"); p_ctx->priv->pf_close = rv9_reader_close; p_ctx->priv->pf_read = rv9_reader_read; p_ctx->priv->pf_seek = rv9_reader_seek; return VC_CONTAINER_SUCCESS; error: LOG_DEBUG(p_ctx, "rv9: error opening stream (%i)", status); if(module) rv9_reader_close(p_ctx); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak reader_open rv9_reader_open #endif userland/containers/simple/000077500000000000000000000000001421703157200163335ustar00rootroot00000000000000userland/containers/simple/CMakeLists.txt000066400000000000000000000010551421703157200210740ustar00rootroot00000000000000# Container module needs to go in as a plugins so different prefix # and install path set(CMAKE_SHARED_LIBRARY_PREFIX "") # Make sure the compiler can find the necessary include files include_directories (../..) add_library(reader_simple ${LIBRARY_TYPE} simple_reader.c) target_link_libraries(reader_simple containers) install(TARGETS reader_simple DESTINATION ${VMCS_PLUGIN_DIR}) add_library(writer_simple ${LIBRARY_TYPE} simple_writer.c) target_link_libraries(writer_simple containers) install(TARGETS writer_simple DESTINATION ${VMCS_PLUGIN_DIR}) userland/containers/simple/simple_common.h000066400000000000000000000040721421703157200213500ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef SIMPLE_COMMON_H #define SIMPLE_COMMON_H #define SIGNATURE_STRING "S1MPL3" #define SIGNATURE_END_STRING "3LPM1S" /** List of configuration options supported in the header */ #define CONFIG_VARIANT "VARIANT" #define CONFIG_URI "URI" #define CONFIG_CODEC_VARIANT "CODEC_VARIANT" #define CONFIG_BITRATE "BITRATE" #define CONFIG_UNFRAMED "UNFRAMED" #define CONFIG_VIDEO_CROP "VIDEO_CROP" #define CONFIG_VIDEO_ASPECT "VIDEO_ASPECT" #endif /* SIMPLE_COMMON_H */ userland/containers/simple/simple_reader.c000066400000000000000000000511671421703157200213240ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_logging.h" #include "simple_common.h" /****************************************************************************** Defines. ******************************************************************************/ #define MAX_LINE_SIZE 512 #define LINE_PADDING 3 /* 2 for newline + 1 for null */ #define MAX_TRACKS 4 #define MAX_HEADER_LINES 512 typedef enum SIMPLE_VARIANT_T { VARIANT_DEFAULT = 0, VARIANT_MMAL, VARIANT_OMX } SIMPLE_VARIANT_T; /****************************************************************************** Type definitions ******************************************************************************/ typedef struct SIMPLE_PACKET_STATE_T { unsigned int track_num; unsigned int flags; uint64_t metadata_offset; /* Offset in metadata stream */ uint32_t data_size; /* Size of current data packet */ uint32_t data_left; /* Data left to read in current packet */ int64_t pts; } SIMPLE_PACKET_STATE_T; typedef struct VC_CONTAINER_TRACK_MODULE_T { SIMPLE_PACKET_STATE_T *state; SIMPLE_PACKET_STATE_T local_state; VC_CONTAINER_IO_T *io; uint64_t data_offset; /* Current offset in data stream */ char uri[MAX_LINE_SIZE+1]; SIMPLE_VARIANT_T variant; } VC_CONTAINER_TRACK_MODULE_T; typedef struct VC_CONTAINER_MODULE_T { VC_CONTAINER_TRACK_T *tracks[MAX_TRACKS]; char line[MAX_LINE_SIZE + LINE_PADDING]; int64_t metadata_offset; /* Shared packet state. This is used when the tracks are in sync, * and for the track at the earliest position in the file when they are * not in sync */ SIMPLE_PACKET_STATE_T state; } VC_CONTAINER_MODULE_T; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T simple_reader_open( VC_CONTAINER_T * ); /****************************************************************************** Local Functions ******************************************************************************/ static VC_CONTAINER_STATUS_T simple_read_line( VC_CONTAINER_T *ctx ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; unsigned int i, bytes = PEEK_BYTES(ctx, module->line, sizeof(module->line)-1); if (!bytes) return VC_CONTAINER_ERROR_EOS; /* Find new-line marker */ for (i = 0; i < bytes; i++) if (module->line[i] == '\n') break; /* Bail out if line is bigger than the maximum allowed */ if (i == sizeof(module->line)-1) { LOG_ERROR(ctx, "line too big"); return VC_CONTAINER_ERROR_CORRUPTED; } if (i < bytes) { module->line[i++] = 0; if (i < bytes && module->line[i] == '\r') i++; } module->line[i] = 0; /* Make sure the line is null terminated */ SKIP_BYTES(ctx, i); return VC_CONTAINER_SUCCESS; } static VC_CONTAINER_STATUS_T simple_read_header( VC_CONTAINER_T *ctx ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; VC_CONTAINER_TRACK_T *track = NULL; VC_CONTAINER_FOURCC_T fourcc; int matches, width, height, channels, samplerate, bps, blockalign, value; unsigned int lines = 1; /* Skip the signature */ if (simple_read_line(ctx) != VC_CONTAINER_SUCCESS) return VC_CONTAINER_ERROR_CORRUPTED; while (lines++ < MAX_HEADER_LINES && simple_read_line(ctx) == VC_CONTAINER_SUCCESS) { /* Our exit condition is the end signature */ if (!memcmp(module->line, SIGNATURE_END_STRING, sizeof(SIGNATURE_STRING)-1)) { if (track) ctx->tracks[ctx->tracks_num++] = track; return VC_CONTAINER_SUCCESS; } /* Start of track description */ if (!memcmp(module->line, "TRACK ", sizeof("TRACK ")-1)) { /* Add track we were constructing */ if (track) ctx->tracks[ctx->tracks_num++] = track; track = NULL; if (ctx->tracks_num >= MAX_TRACKS) { LOG_ERROR(ctx, "too many tracks, ignoring: %s", module->line); continue; } track = vc_container_allocate_track(ctx, sizeof(*track->priv->module)); if (!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; track->is_enabled = true; track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; if ((matches = sscanf(module->line, "TRACK video, %4c, %i, %i", (char *)&fourcc, &width, &height)) > 0) { track->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; track->format->codec = fourcc; if (matches > 1) track->format->type->video.width = width; if (matches > 2) track->format->type->video.height = height; } else if ((matches = sscanf(module->line, "TRACK audio, %4c, %i, %i, %i, %i", (char *)&fourcc, &channels, &samplerate, &bps, &blockalign)) > 0) { track->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; track->format->codec = fourcc; if (matches > 1) track->format->type->audio.channels = channels; if (matches > 2) track->format->type->audio.sample_rate = samplerate; if (matches > 3) track->format->type->audio.bits_per_sample = bps; if (matches > 4) track->format->type->audio.block_align = blockalign; } if ((matches = sscanf(module->line, "TRACK subpicture, %4c, %i", (char *)&fourcc, &value)) > 0) { track->format->es_type = VC_CONTAINER_ES_TYPE_SUBPICTURE; track->format->codec = fourcc; if (matches > 1) track->format->type->subpicture.encoding = value; } } if (!track) continue; /* Nothing interesting */ /* VARIANT of the syntax */ if (sscanf(module->line, CONFIG_VARIANT" %i", &value) == 1) { track->priv->module->variant = value; LOG_FORMAT(ctx, CONFIG_VARIANT": %i", value); } /* URI for elementary stream */ else if (sscanf(module->line, CONFIG_URI" %s", track->priv->module->uri) == 1) LOG_FORMAT(ctx, CONFIG_URI": %s", track->priv->module->uri); /* COCDEC_VARIANT of elementary stream */ else if (sscanf(module->line, CONFIG_CODEC_VARIANT" %4c", (char *)&fourcc) == 1) { track->format->codec_variant = fourcc; LOG_FORMAT(ctx, CONFIG_CODEC_VARIANT": %4.4s", (char *)&fourcc); } /* BITRATE of elementary stream */ else if (sscanf(module->line, CONFIG_BITRATE" %i", &value) == 1) { track->format->bitrate = value; LOG_FORMAT(ctx, CONFIG_BITRATE": %i", value); } /* UNFRAMED elementary stream */ else if (!memcmp(module->line, CONFIG_UNFRAMED, sizeof(CONFIG_UNFRAMED)-1)) { track->format->flags &= ~VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; LOG_FORMAT(ctx, CONFIG_UNFRAMED); } /* VIDEO_CROP information */ else if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO && sscanf(module->line, CONFIG_VIDEO_CROP" %i, %i", &width, &height) == 2) { track->format->type->video.visible_width = width; track->format->type->video.visible_height = height; LOG_FORMAT(ctx, CONFIG_VIDEO_CROP": %i, %i", width, height); } /* VIDEO_ASPECT information */ else if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO && sscanf(module->line, CONFIG_VIDEO_ASPECT" %i, %i", &width, &height) == 2) { track->format->type->video.par_num = width; track->format->type->video.par_den = height; LOG_FORMAT(ctx, CONFIG_VIDEO_ASPECT": %i, %i", width, height); } } if (track) vc_container_free_track(ctx, track); return VC_CONTAINER_ERROR_CORRUPTED; } static uint32_t simple_convert_packet_flags(VC_CONTAINER_T *ctx, unsigned int track_num, uint32_t flags) { typedef struct { uint32_t from; uint32_t to; } convert_from_t; const convert_from_t convert_from_mmal[] = { {1<<1, VC_CONTAINER_PACKET_FLAG_FRAME_START}, {1<<2, VC_CONTAINER_PACKET_FLAG_FRAME_END}, {1<<3, VC_CONTAINER_PACKET_FLAG_KEYFRAME}, {1<<4, VC_CONTAINER_PACKET_FLAG_DISCONTINUITY}, {1<<5, VC_CONTAINER_PACKET_FLAG_CONFIG}, {1<<6, VC_CONTAINER_PACKET_FLAG_ENCRYPTED}, {0, 0} }; const convert_from_t convert_from_omx[] = { {0x10, VC_CONTAINER_PACKET_FLAG_FRAME_END}, {0x20, VC_CONTAINER_PACKET_FLAG_KEYFRAME}, {0x80, VC_CONTAINER_PACKET_FLAG_CONFIG}, {0, 0} }; const convert_from_t *convert_from = NULL; int i; switch (ctx->tracks[track_num]->priv->module->variant) { case VARIANT_MMAL: convert_from = convert_from_mmal; break; case VARIANT_OMX: convert_from = convert_from_omx; break; default: break; } if (convert_from) { uint32_t new_flags = 0; for (i = 0; convert_from[i].from; i++) if (convert_from[i].from & flags) new_flags |= convert_from[i].to; return new_flags; } return flags; } static int64_t simple_convert_packet_pts(VC_CONTAINER_T *ctx, unsigned int track_num, int64_t pts, uint32_t flags) { if (ctx->tracks[track_num]->priv->module->variant == VARIANT_OMX && flags & 0x100) return VC_CONTAINER_TIME_UNKNOWN; return pts; } /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ static VC_CONTAINER_STATUS_T simple_reader_read( VC_CONTAINER_T *ctx, VC_CONTAINER_PACKET_T *packet, uint32_t flags ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; VC_CONTAINER_TRACK_MODULE_T *track_module; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; SIMPLE_PACKET_STATE_T *state; /* If a specific track has been selected, use the track packet state */ if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) state = ctx->tracks[packet->track]->priv->module->state; else state = &module->state; /* Switch to the next packet when the current one is empty */ if (!state->data_left) { unsigned int track_num, size; int64_t pts; int flags; SEEK(ctx, state->metadata_offset); status = simple_read_line(ctx); if (status != VC_CONTAINER_SUCCESS) return status; if (sscanf(module->line, "%u %u %"PRIi64" %i", &track_num, &size, &pts, &flags) != 4 && (track_num = 0, sscanf(module->line, "%u %"PRIi64" %i", &size, &pts, &flags)) != 3) { LOG_ERROR(ctx, "invalid metadata: %s", module->line); return VC_CONTAINER_ERROR_CORRUPTED; } state->metadata_offset = STREAM_POSITION(ctx); if (track_num >= ctx->tracks_num) { LOG_DEBUG(ctx, "skipping %i bytes for track %d/%d", size, track_num, ctx->tracks_num); return VC_CONTAINER_ERROR_CONTINUE; } /* If we are reading from the global state (i.e. normal read or forced read from the track on the global state), and the track we found is not on the global state, reconnect the two */ if (state == &module->state && ctx->tracks[track_num]->priv->module->state != &module->state) { LOG_DEBUG(ctx, "reconnect track %u to the global state", track_num); ctx->tracks[track_num]->priv->module->state = &module->state; module->state = ctx->tracks[track_num]->priv->module->local_state; return VC_CONTAINER_ERROR_CONTINUE; } state->data_size = state->data_left = size; state->track_num = track_num; state->flags = simple_convert_packet_flags(ctx, track_num, flags); state->pts = simple_convert_packet_pts(ctx, track_num, pts, flags); /* Discard empty packets */ if (!state->data_size && !state->flags) return VC_CONTAINER_ERROR_CONTINUE; } /* If there is data from another track skip past it */ if ((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) && state->track_num != packet->track) { LOG_DEBUG(ctx, "skipping track %d/%d as we are ignoring it", state->track_num, ctx->tracks_num); track_module = ctx->tracks[packet->track]->priv->module; /* Handle disconnection from global state */ if (state == &module->state && ctx->tracks[state->track_num]->priv->module->state == &module->state) { /* Make a copy of the global state */ LOG_DEBUG(ctx, "using local state on track %d", packet->track); track_module->local_state = module->state; track_module->state = &track_module->local_state; } track_module->state->data_left = 0; return VC_CONTAINER_ERROR_CONTINUE; } /* * From this point we know we have the packet which was requested */ /* !!!! If we aren't in the right position in the file go there now. */ track_module = ctx->tracks[state->track_num]->priv->module; packet->track = state->track_num; packet->size = state->data_left; packet->frame_size = (state->flags & VC_CONTAINER_PACKET_FLAG_FRAME) ? state->data_size : 0; packet->flags = state->flags; packet->pts = state->pts; packet->dts = VC_CONTAINER_TIME_UNKNOWN; if (state->data_left != state->data_size) packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_START; if (flags & VC_CONTAINER_READ_FLAG_SKIP) { track_module->data_offset += state->data_left; state->data_left = 0; return VC_CONTAINER_SUCCESS; } if (flags & VC_CONTAINER_READ_FLAG_INFO) { return VC_CONTAINER_SUCCESS; } /* Now try to read data into buffer */ vc_container_io_seek(track_module->io, track_module->data_offset); packet->size = vc_container_io_read(track_module->io, packet->data, MIN(packet->buffer_size, state->data_left)); state->data_left -= packet->size; track_module->data_offset += packet->size; if (state->data_left) packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; return track_module->io->status; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T simple_reader_seek( VC_CONTAINER_T *ctx, int64_t *offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) { VC_CONTAINER_PARAM_UNUSED(ctx); VC_CONTAINER_PARAM_UNUSED(offset); VC_CONTAINER_PARAM_UNUSED(mode); VC_CONTAINER_PARAM_UNUSED(flags); return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T simple_reader_close( VC_CONTAINER_T *ctx ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; for (; ctx->tracks_num > 0; ctx->tracks_num--) { VC_CONTAINER_TRACK_T *track = ctx->tracks[ctx->tracks_num-1]; if (track->priv->module->io) vc_container_io_close(track->priv->module->io); vc_container_free_track(ctx, track); } free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T simple_reader_open( VC_CONTAINER_T *ctx ) { VC_CONTAINER_MODULE_T *module = 0; VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; uint8_t h[sizeof(SIGNATURE_STRING)]; unsigned int i; /* Check for the signature */ if (PEEK_BYTES(ctx, h, sizeof(h)) != sizeof(h) || memcmp(h, SIGNATURE_STRING, sizeof(SIGNATURE_STRING)-1)) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; LOG_DEBUG(ctx, "using simple reader"); /* Allocate our context */ module = malloc(sizeof(*module)); if (!module) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; memset(module, 0, sizeof(*module)); ctx->priv->module = module; ctx->tracks = module->tracks; status = simple_read_header(ctx); if (status != VC_CONTAINER_SUCCESS) goto error; /* Open all the elementary streams */ for (i = 0; i < ctx->tracks_num; i++) { VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; char *uri; track->priv->module->io = vc_container_io_open(track->priv->module->uri, VC_CONTAINER_IO_MODE_READ, &status); /* URI might be relative to the path of the metadata file so * try again with that new path */ if (!track->priv->module->io && (uri = malloc(strlen(ctx->priv->io->uri) + strlen(track->priv->module->uri) + 1)) != NULL) { char *end; strcpy(uri, ctx->priv->io->uri); /* Find the last directory separator */ for (end = uri + strlen(ctx->priv->io->uri) + 1; end != uri; end--) if (*(end-1) == '/' || *(end-1) == '\\') break; strcpy(end, track->priv->module->uri); track->priv->module->io = vc_container_io_open(uri, VC_CONTAINER_IO_MODE_READ, &status); if (!track->priv->module->io) LOG_ERROR(ctx, "could not open elementary stream: %s", uri); free(uri); } if (!track->priv->module->io) { LOG_ERROR(ctx, "could not open elementary stream: %s", track->priv->module->uri); goto error; } } /* * We now have all the information we really need to start playing the stream */ module->metadata_offset = STREAM_POSITION(ctx); /* Initialise state for all tracks */ module->state.metadata_offset = module->metadata_offset; for (i = 0; i < ctx->tracks_num; i++) { VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; track->priv->module->state = &module->state; } /* Look for the codec configuration data for each track so * we can store it in the track format */ for (i = 0; i < ctx->tracks_num; i++) { VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; VC_CONTAINER_PACKET_T packet; packet.track = i; status = VC_CONTAINER_ERROR_CONTINUE; while (status == VC_CONTAINER_ERROR_CONTINUE) status = simple_reader_read(ctx, &packet, VC_CONTAINER_READ_FLAG_INFO | VC_CONTAINER_READ_FLAG_FORCE_TRACK); if (status != VC_CONTAINER_SUCCESS) continue; status = vc_container_track_allocate_extradata(ctx, track, packet.size); if (status != VC_CONTAINER_SUCCESS) continue; packet.data = track->format->extradata; packet.buffer_size = packet.size; packet.size = 0; status = simple_reader_read(ctx, &packet, VC_CONTAINER_READ_FLAG_FORCE_TRACK); if (status != VC_CONTAINER_SUCCESS) continue; track->format->extradata_size = packet.size; } ctx->priv->pf_close = simple_reader_close; ctx->priv->pf_read = simple_reader_read; ctx->priv->pf_seek = simple_reader_seek; return VC_CONTAINER_SUCCESS; error: LOG_ERROR(ctx, "simple: error opening stream (%i)", status); simple_reader_close(ctx); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak reader_open simple_reader_open #endif userland/containers/simple/simple_writer.c000066400000000000000000000302061421703157200213650ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_logging.h" #include "simple_common.h" /****************************************************************************** Defines. ******************************************************************************/ #define MAX_TRACKS 4 #define MAX_LINE_SIZE 512 #define ES_SUFFIX "%s.%2.2i.%4.4s" #define ES_SUFFIX_SIZE 8 /****************************************************************************** Type definitions ******************************************************************************/ typedef struct VC_CONTAINER_TRACK_MODULE_T { VC_CONTAINER_IO_T *io; char *uri; bool config_done; } VC_CONTAINER_TRACK_MODULE_T; typedef struct VC_CONTAINER_MODULE_T { char line[MAX_LINE_SIZE + 1]; VC_CONTAINER_TRACK_T *tracks[MAX_TRACKS]; bool header_done; } VC_CONTAINER_MODULE_T; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T simple_writer_open( VC_CONTAINER_T * ); static VC_CONTAINER_STATUS_T simple_writer_write( VC_CONTAINER_T *ctx, VC_CONTAINER_PACKET_T *packet ); /****************************************************************************** Local Functions ******************************************************************************/ static VC_CONTAINER_STATUS_T simple_write_line( VC_CONTAINER_T *ctx, const char *format, ...) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; va_list args; int result; va_start(args, format); result = vsnprintf(module->line, sizeof(module->line), format, args); va_end(args); if (result >= (int)sizeof(module->line)) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; WRITE_BYTES(ctx, module->line, result); _WRITE_U8(ctx, '\n'); return STREAM_STATUS(ctx); } static VC_CONTAINER_STATUS_T simple_write_header( VC_CONTAINER_T *ctx ) { unsigned int i; simple_write_line(ctx, SIGNATURE_STRING); for (i = 0; i < ctx->tracks_num; i++) { VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) { simple_write_line(ctx, "TRACK video, %4.4s, %i, %i", (char *)&track->format->codec, (int)track->format->type->video.width, (int)track->format->type->video.height); if ((track->format->type->video.visible_width && track->format->type->video.visible_width != track->format->type->video.width) || (track->format->type->video.visible_height && track->format->type->video.visible_height != track->format->type->video.height)) simple_write_line(ctx, CONFIG_VIDEO_CROP" %i, %i", track->format->type->video.visible_width, track->format->type->video.visible_height); if (track->format->type->video.par_num && track->format->type->video.par_den) simple_write_line(ctx, CONFIG_VIDEO_ASPECT" %i, %i", track->format->type->video.par_num, track->format->type->video.par_den); } else if (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) { simple_write_line(ctx, "TRACK audio, %4.4s, %i, %i, %i, %i", (char *)&track->format->codec, (int)track->format->type->audio.channels, (int)track->format->type->audio.sample_rate, (int)track->format->type->audio.bits_per_sample, (int)track->format->type->audio.block_align); } else if (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) { simple_write_line(ctx, "TRACK subpicture, %4.4s, %i", (char *)&track->format->codec, (int)track->format->type->subpicture.encoding); } else { simple_write_line(ctx, "TRACK unknown, %4.4s", (char *)&track->format->codec); } simple_write_line(ctx, CONFIG_URI" %s", track->priv->module->io->uri); if (track->format->codec_variant) simple_write_line(ctx, CONFIG_CODEC_VARIANT" %4.4s", (char *)&track->format->codec_variant); if (track->format->bitrate) simple_write_line(ctx, CONFIG_BITRATE" %i", track->format->bitrate); if (!(track->format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) simple_write_line(ctx, CONFIG_UNFRAMED); } simple_write_line(ctx, SIGNATURE_END_STRING); ctx->priv->module->header_done = true; return STREAM_STATUS(ctx); } static VC_CONTAINER_STATUS_T simple_write_config( VC_CONTAINER_T *ctx, unsigned int track_num, VC_CONTAINER_PACKET_T *pkt) { VC_CONTAINER_TRACK_T *track = ctx->tracks[track_num]; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_PACKET_T packet; track->priv->module->config_done = true; if (track->format->extradata_size) { packet.size = track->format->extradata_size; packet.data = track->format->extradata; packet.track = track_num; packet.pts = pkt ? pkt->pts : VC_CONTAINER_TIME_UNKNOWN; packet.flags = 0; packet.flags |= VC_CONTAINER_PACKET_FLAG_CONFIG; status = simple_writer_write(ctx, &packet); } return status; } static VC_CONTAINER_STATUS_T simple_write_add_track( VC_CONTAINER_T *ctx, VC_CONTAINER_ES_FORMAT_T *format ) { VC_CONTAINER_TRACK_T *track = NULL; VC_CONTAINER_STATUS_T status; const char *uri = vc_uri_path(ctx->priv->uri); unsigned int uri_size = strlen(uri); /* Allocate and initialise track data */ if (ctx->tracks_num >= MAX_TRACKS) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; ctx->tracks[ctx->tracks_num] = track = vc_container_allocate_track(ctx, sizeof(VC_CONTAINER_TRACK_MODULE_T) + uri_size + ES_SUFFIX_SIZE + 1); if (!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; if (format->extradata_size) { status = vc_container_track_allocate_extradata(ctx, track, format->extradata_size); if (status != VC_CONTAINER_SUCCESS) goto error; } vc_container_format_copy(track->format, format, format->extradata_size); track->priv->module->uri = (char *)&track->priv->module[1]; snprintf(track->priv->module->uri, uri_size + ES_SUFFIX_SIZE + 1, ES_SUFFIX, uri, ctx->tracks_num, (char *)&track->format->codec); LOG_DEBUG(ctx, "opening elementary stream: %s", track->priv->module->uri); track->priv->module->io = vc_container_io_open(track->priv->module->uri, VC_CONTAINER_IO_MODE_WRITE, &status); if (status != VC_CONTAINER_SUCCESS) { LOG_ERROR(ctx, "error opening elementary stream: %s", track->priv->module->uri); goto error; } ctx->tracks_num++; return VC_CONTAINER_SUCCESS; error: if (track) vc_container_free_track(ctx, track); return status; } /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ static VC_CONTAINER_STATUS_T simple_writer_close( VC_CONTAINER_T *ctx ) { VC_CONTAINER_MODULE_T *module = ctx->priv->module; for (; ctx->tracks_num > 0; ctx->tracks_num--) { vc_container_io_close(ctx->tracks[ctx->tracks_num-1]->priv->module->io); vc_container_free_track(ctx, ctx->tracks[ctx->tracks_num-1]); } free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T simple_writer_write( VC_CONTAINER_T *ctx, VC_CONTAINER_PACKET_T *packet ) { VC_CONTAINER_STATUS_T status; if (!ctx->priv->module->header_done) { status = simple_write_header(ctx); if (status != VC_CONTAINER_SUCCESS) return status; } if (!ctx->tracks[packet->track]->priv->module->config_done) { status = simple_write_config(ctx, packet->track, packet); if (status != VC_CONTAINER_SUCCESS) return status; } /* Write the metadata */ status = simple_write_line(ctx, "%i %i %"PRIi64" 0x%x", (int)packet->track, (int)packet->size, packet->pts, packet->flags); if (status != VC_CONTAINER_SUCCESS) return status; /* Write the elementary stream */ vc_container_io_write(ctx->tracks[packet->track]->priv->module->io, packet->data, packet->size); return STREAM_STATUS(ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T simple_writer_control( VC_CONTAINER_T *ctx, VC_CONTAINER_CONTROL_T operation, va_list args ) { VC_CONTAINER_ES_FORMAT_T *format; switch (operation) { case VC_CONTAINER_CONTROL_TRACK_ADD: format = (VC_CONTAINER_ES_FORMAT_T *)va_arg(args, VC_CONTAINER_ES_FORMAT_T *); return simple_write_add_track(ctx, format); case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: simple_write_header( ctx ); return VC_CONTAINER_SUCCESS; default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; } } /*****************************************************************************/ VC_CONTAINER_STATUS_T simple_writer_open( VC_CONTAINER_T *ctx ) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; const char *extension = vc_uri_path_extension(ctx->priv->uri); VC_CONTAINER_MODULE_T *module; /* Check if the user has specified a container */ vc_uri_find_query(ctx->priv->uri, 0, "container", &extension); /* Check we're the right writer for this */ if(!extension) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if(strcasecmp(extension, "smpl") && strcasecmp(extension, "simple")) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; LOG_DEBUG(ctx, "using simple writer"); /* Allocate our context */ module = malloc(sizeof(*module)); if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); ctx->priv->module = module; ctx->tracks = module->tracks; ctx->priv->pf_close = simple_writer_close; ctx->priv->pf_write = simple_writer_write; ctx->priv->pf_control = simple_writer_control; return VC_CONTAINER_SUCCESS; error: LOG_DEBUG(ctx, "simple: error opening stream (%i)", status); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak writer_open simple_writer_open #endif userland/containers/test/000077500000000000000000000000001421703157200160215ustar00rootroot00000000000000userland/containers/test/CMakeLists.txt000066400000000000000000000053031421703157200205620ustar00rootroot00000000000000# Generate test application add_executable(containers_test test.c) target_link_libraries(containers_test -Wl,--no-whole-archive containers) install(TARGETS containers_test DESTINATION bin) # Generate test application add_executable(containers_check_frame_int check_frame_int.c) target_link_libraries(containers_check_frame_int -Wl,--no-whole-archive containers) install(TARGETS containers_check_frame_int DESTINATION bin) # Generate autotest application #add_executable(containers_autotest autotest.cpp crc_32.c) #target_link_libraries(containers_autotest -Wl,--no-whole-archive containers}) #install(TARGETS containers_autotest DESTINATION bin) # Helper code to provide non-blocking console input if (WIN32) set( NB_IO_SOURCE nb_io_win32.c ) elseif (UNIX) set( NB_IO_SOURCE nb_io_unix.c ) endif (WIN32) set(extra_test_SRCS nb_io_win32.c autotest.cpp crc_32.c) add_custom_target(containers_test_extra COMMAND touch ${extra_test_SRCS} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/containers/test) add_dependencies(containers_test containers_test_extra) # Generate net test applications add_executable(containers_stream_client stream_client.c ${NB_IO_SOURCE}) target_link_libraries(containers_stream_client containers) install(TARGETS containers_stream_client DESTINATION bin) add_executable(containers_stream_server stream_server.c) target_link_libraries(containers_stream_server containers) install(TARGETS containers_stream_server DESTINATION bin) add_executable(containers_datagram_sender datagram_sender.c) target_link_libraries(containers_datagram_sender containers) install(TARGETS containers_datagram_sender DESTINATION bin) add_executable(containers_datagram_receiver datagram_receiver.c) target_link_libraries(containers_datagram_receiver containers) install(TARGETS containers_datagram_receiver DESTINATION bin) add_executable(containers_rtp_decoder rtp_decoder.c ${NB_IO_SOURCE}) target_link_libraries(containers_rtp_decoder containers) install(TARGETS containers_rtp_decoder DESTINATION bin) # Generate URI test application add_executable(containers_test_uri test_uri.c) target_link_libraries(containers_test_uri containers) install(TARGETS containers_test_uri DESTINATION bin) # Generate URI pipe application add_executable(containers_uri_pipe uri_pipe.c ${NB_IO_SOURCE}) target_link_libraries(containers_uri_pipe containers) install(TARGETS containers_uri_pipe DESTINATION bin) # Generate bit stream test application add_executable(containers_test_bits test_bits.c) target_link_libraries(containers_test_bits containers) install(TARGETS containers_test_bits DESTINATION bin) # Generate packet file dump application add_executable(containers_dump_pktfile dump_pktfile.c) install(TARGETS containers_dump_pktfile DESTINATION bin) userland/containers/test/autotest.cpp000066400000000000000000002233441421703157200204050ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include #include #include #include #include #include // MS compilers require __cdecl calling convention on some callbacks. Other compilers reject it. #if (!defined(_MSC_VER) && !defined(__cdecl)) #define __cdecl #endif extern "C" { #include "containers/containers.h" #include "containers/core/containers_common.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_io.h" // Declare the CRC32 function from Snippets.org (obtained from the wayback machine, see crc_32.c) uint32_t crc32buf(const uint8_t *buf, size_t len); } // Error logger. It looks a little like std::cout, but it will stop the program if required on an end-of-line, // returning an error to the caller. This is intended to allow automated tests to run to first fail, // but interactive tests to run all the way through (using -k) class ERROR_LOGGER_T { public: ERROR_LOGGER_T(): error_is_fatal(true), had_any_error(false) {} ~ERROR_LOGGER_T() { // Flush out any bits of message that have been assembled, but not yet flushed out. std::cerr << stream.str(); if (had_any_error) { exit(VC_CONTAINER_ERROR_FAILED); } } // Tell the logger that we're going to keep going after an error if we possibly can void set_errors_not_fatal() { error_is_fatal = false; } // Tell the error logger to redirect its output to this stream instead of the default stderr. // This is used when dumping is enabled, so that errors become part of the stream description. void set_output(std::ostream* new_stream) { output_stream = new_stream; } // Generic inserter. Always calls through to the inserter for the base class. template ERROR_LOGGER_T& operator<<(const T& object) { stream << object; had_any_error = true; return *this; } // specialised inserter for iomanip type objects. Ensures that this object, and not the contained stream, // is passed to the object. ERROR_LOGGER_T& operator<<(ERROR_LOGGER_T& (__cdecl *_Pfn)(ERROR_LOGGER_T&)) { return _Pfn(*this); } // implementation of flush. This is called by endl, and if the flags are set the program will stop. ERROR_LOGGER_T& flush() { // If required send it to the quoted stream if (output_stream) { *output_stream << stream.str(); } else { // just send it to stderr std::cerr << stream.str(); } stream.clear(); // reset any odd flags stream.str(""); // empty the string if (error_is_fatal) { exit(VC_CONTAINER_ERROR_FAILED); } return *this; } private: // set true if should stop on first error (usual for smoke testing) // or keep going (usual if you want to look at the logs). Controlled by config -k flag (as for make) bool error_is_fatal; // Set true if we've ever had an error. This way the app will return an error even if it kept going after it. bool had_any_error; // The current error message std::ostringstream stream; // The output stream, if not defaulted std::ostream* output_stream; }; namespace std { // GCC insists I say this twice: ERROR_LOGGER_T& ends(ERROR_LOGGER_T& logger); // implementation of std::ends for the error logger - flushes the message. ERROR_LOGGER_T& ends(ERROR_LOGGER_T& logger) { logger << "\n"; logger.flush(); return logger; } } // Internal to this file, PTS is stored in one of these. other code uses int64_t directly. typedef int64_t PTS_T; struct PACKET_DATA_T { VC_CONTAINER_PACKET_T info; // copy of the API's data std::vector buffer; // copy of the data contained, or empty if we have exceeded configuration.mem_max uint32_t crc; // CRC32 of the data. // Constructor. Zeroes the whole content. PACKET_DATA_T(): /* info((VC_CONTAINER_PACKET_T){0}), */ buffer(), crc(0) { // The syntax above for initialising info is probably correct according to C++ 0x. // Unfortunately the MS C++ compiler in VS2012 isn't really C++0x compliant. memset(&info, 0, sizeof(info)); } }; typedef uint32_t STREAM_T; // Packets in a file in an arbitrary order, usually the order they were read. typedef std::list ALL_PACKETS_T; // Packets keyed by time (excludes packets with no PTS). This collection must be on a per-stream basis. typedef std::map TIMED_PACKETS_T; // Packets with times. Must be split per-stream as multiple streams may have the same PTS. // (It's actually unusual for more than one stream to have key frames) typedef std::map STREAM_TIMED_PACKETS_T; // structure parsing and holding configuration information for the program. struct CONFIGURATION_T { // maximum size of in-memory buffers holding file data. Set by -m size_t mem_max; // The source file or URL being processed std::string source_name; // trace verbosity, to reflect the older test application. Set by -v. int32_t verbosity, verbosity_input, verbosity_output; // fatal errors flag set by -k bool errors_not_fatal; // dump-path for packet summary set by -d. May be std::cout. mutable std::ostream* dump_packets; // tolerance values set by -t. // Ideally when we seek to a time all the tracks would be right on it, but that's not always possible. // When we don't have seek_forwards set: // - The video must not be after the requested time // - The video must not be more than PTS_T tolerance_video_early; // microseconds before the desired time. A 'good' container will hit it bang on if the supplied time is a video key frame. PTS_T tolerance_video_late; // - The other tracks must not be more than PTS_T tolerance_other_early; PTS_T tolerance_other_late; // from the time of the video frame. If forcing is not supported we have to take what's in the file. // If set by -p a test is performed re-reading the file using a packet buffer smaller than the expected packet: // If <1 it's a proportion (e.g 0.5 will read half the packet, then the other half on a subsequent read) // If 1 or more it's a size (1 will read 1 byte at a time; 100 not more than 100 bytes in each read) // Defaults to -ve value, which means don't do this test. double packet_buffer_size; // Constructor CONFIGURATION_T(int argc, char** argv) : mem_max(0x40000000) // 1Gb for 32-bit system friendliness. , verbosity(0) // no trace , verbosity_input(0) , verbosity_output(0) , errors_not_fatal(false) , dump_packets(nullptr) , tolerance_video_early(100000) // 100k uS = 100mS , tolerance_video_late(0) , tolerance_other_early(100000) // 100k uS = 100mS , tolerance_other_late(1000000) // 1000k uS = 1S , packet_buffer_size(-1) { if (argc < 2) { std::cout << "-d produce packet dump (to file if specified)" << std::endl << "-k keep going on errors" << std::endl << "-m max memory buffer for packet data (default 1Gb). If the file is large not all will be validated properly." << std::endl << "-p re-read using a small packet buffer. If >= 1 this is a size; if < 1 a proportion of the packet" << std::endl << "-v verbose mode." << std::endl << " -vi input verbosity only" << std::endl << " -vo output verbosity only" << std::endl << " add more vvv to make it more verbose" << std::endl << "-t seek error tolerance in microseconds" << std::endl << " tv video streams" << std::endl << " to other streams" << std::endl << " te, tl all streams earliness, lateness" << std::endl << " tvl, tol video, other lateness" << std::endl << " toe, tve video, other earliness" << std::endl << std::endl << "example: autotest -k 1-128.wmv -vvvvv -t1000000" << std::endl << " tests 1-128.wmv. Keeps going on errors. Very verbose. Tolerant of errors up to 1s in seeks." << std::endl; exit(0); } // Parse each argument for (int arg = 1; arg > mem_max; if (!number.eof()) { error(arg, argstr, "Size cannot be parsed"); } } break; case 'p': { std::istringstream number(argstr); // throw away the p number.ignore(1); if (number.eof()) { error(arg, argstr, "Packet re-read size not supplied"); } // read the number number >> packet_buffer_size; if (!number.eof()) { error(arg, argstr, "Size cannot be parsed"); } } break; case 't': // error tolerance { std::istringstream stream(argstr); stream.ignore(1); // throw away the t // flags for which tolerance we are processing. bool video = true, other = true, earliness=true, lateness = true; int64_t tolerance; // If there's a v or an o it's video or other only if (!stream.eof()) { switch (stream.peek()) { case 'v': other = false; // if he said video we don't touch the other params stream.ignore(1); // throw away the letter break; case 'o': video = false; stream.ignore(1); // throw away the letter break; // do nothing on other chars. } } // If there's an l or an e it's late or early only if (!stream.eof()) { switch (stream.peek()) { case 'l': earliness = false; stream.ignore(1); // throw away the letter break; case 'e': lateness = false; stream.ignore(1); // throw away the letter break; // do nothing on other chars. } } // read the number that follows if (stream.eof()) { error(arg, argstr, "tolerance not supplied"); } else { // read the number stream >> tolerance; if (!stream.eof()) { error(arg, argstr, "Number cannot be parsed"); } if (video && earliness) { tolerance_video_early = tolerance; } if (video && lateness) { tolerance_video_late = tolerance; } if (other && earliness) { tolerance_other_early = tolerance; } if (other && lateness) { tolerance_other_late = tolerance; } } } break; // verbosity. v, vi or vo followed by zero or more extra v. case 'v': process_vees(arg, argstr); break; // anything else default: error(arg, argstr, "is not understood"); } } } if (source_name.empty()) { std::cerr << "No source name supplied"; exit(VC_CONTAINER_ERROR_URI_NOT_FOUND); } if (verbosity != 0) { if (verbosity_input == 0) { verbosity_input = verbosity; } if (verbosity_output == 0) { verbosity_output = verbosity; } } std::cout << "Source: " << source_name << std::endl; std::cout << "Max buffer size: " << mem_max << " 0x" << std::hex << mem_max << std::dec << std::endl; std::cout << "Verbosity: " << verbosity << std::endl; std::cout << "Input verbosity: " << verbosity_input << std::endl; std::cout << "Output verbosity: " << verbosity_output << std::endl; std::cout << "Continue on errors: " << (errors_not_fatal ? 'Y' : 'N') << std::endl; std::cout << "Seek tolerance (uS)" << std::endl; std::cout << " Video Early: " << tolerance_video_early << std::endl; std::cout << " Video Late: " << tolerance_video_late << std::endl; std::cout << " Other Early: " << tolerance_other_early << std::endl; std::cout << " Other Late: " << tolerance_other_late << std::endl; std::cout << "Dump Summary: " << (dump_packets ? 'Y' : 'N') << std::endl; } private: // processing for -v parameter. void process_vees(int arg, const std::string& argstr) { std::istringstream vees(argstr); // we know we have v, so we can drop it. vees.ignore(1); int32_t* which_param = &verbosity; int32_t value = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO; // process the rest of the characters, if any switch (vees.peek()) { case 'v': // do nothing yet break; case 'i': which_param = &verbosity_input; vees.ignore(1); break; case 'o': which_param = &verbosity_output; vees.ignore(1); break; default: if (vees.peek() != std::char_traits::eof()) { error(arg, argstr, "verbosity is not understood"); } break; } while (1) { int next = vees.get(); if (next == 'v') { // add verbosity value = (value << 1) | 1; } else if (next == std::char_traits::eof()) { break; } else { error(arg, argstr, "verbosity is not understood"); } } // store what we parsed. *which_param = value; } // error handling function. Does not return. void error(int arg, const std::string& argstr, const std::string& msg) { std::cerr << "Argument " << arg << " \"" << argstr << "\" " << msg; exit(VC_CONTAINER_ERROR_INVALID_ARGUMENT); } }; // Most of the functionality of the program is in this class, or its subsidiaries. class TESTER_T { public: // Construct, passing command line parameters TESTER_T(int argc, char**argv) : configuration(argc, argv) , video_stream(std::numeric_limits::max()) { // If the configuration said keep going on errors tell the logger. if (configuration.errors_not_fatal) { error_logger.set_errors_not_fatal(); } // Tell it where any -d routed the file summary error_logger.set_output(configuration.dump_packets); } // Run the tests VC_CONTAINER_STATUS_T run() { /* Set the verbosity */ vc_container_log_set_verbosity(0, configuration.verbosity_input); // Open the container p_ctx = vc_container_open_reader(configuration.source_name.c_str(), &status, 0, 0); if(!p_ctx || (status != VC_CONTAINER_SUCCESS)) { error_logger << "error opening file " << configuration.source_name << " Code " << status << std::ends; } // Find the video stream. for(size_t i = 0; i < p_ctx->tracks_num; i++) { if (p_ctx->tracks[i]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) { if (video_stream == std::numeric_limits::max()) { video_stream = i; } else { // we've found two video streams. This is not necessarily an error, but it's certainly odd - perhaps it's the angles // from a DVD. We don't expect to see it, but report it anyway. Don't stop, just assume that the first one is the one we want. error_logger << "Both track " << video_stream << " and " << i << " are marked as video streams" << std::ends; } } } if (video_stream == std::numeric_limits::max()) { error_logger << "No video track found" << std::ends; } // Read all the packets sequentially. This will gve us all the metadata of the packets, // and (up to configuration.mem_max) will also store the packet data. read_sequential(); // Now we have some packets we can initialise our internal RNG. init_crg(); // Find all the keyframes in the collection we just read. find_key_packets(); // Seeking tests. Only if the container supports it. // We do this first, because one of the checks is whether or not the container supports seeking, // and we want to be able to seek back between tests. if (p_ctx->capabilities & VC_CONTAINER_CAPS_CAN_SEEK) { /// Seek to PTS 0 (this ought to be the beginning) PTS_T beginning = 0; status = vc_container_seek(p_ctx, &beginning, VC_CONTAINER_SEEK_MODE_TIME, 0); if (status != VC_CONTAINER_SUCCESS) { error_logger << "Container failed to seek to the beginning - status " << status << std::ends; // This is fatal. exit(status); } // Ask for info about the first packet PACKET_DATA_T actual; status = vc_container_read(p_ctx, &actual.info, VC_CONTAINER_READ_FLAG_INFO); if (status != VC_CONTAINER_SUCCESS) { error_logger << "Read info failed after seek to the beginning - status " << status << std::ends; } else { PACKET_DATA_T& expected = all_packets.front(); // Compare some selected data to check that this genuinely is the first packet. // We don't actually do a read. if ((expected.info.pts != actual.info.pts) || (expected.info.dts != actual.info.dts) || (expected.info.size != actual.info.size) || (expected.info.frame_size != actual.info.frame_size) || (expected.info.track != actual.info.track) || (expected.info.flags != actual.info.flags)) { // Copy the CRC from the expected value. That will stop compare_packets whinging. actual.crc = expected.crc; // report the discrepancy error_logger << "Seek to time zero did not arrive at beginning: " << compare_packets(expected, actual) << std::ends; } } // Perform seeks to find all the packets that a seek will go to. check_indices(0); check_indices(VC_CONTAINER_SEEK_FLAG_FORWARD); // If it supports forcing then seek to each of those locations, and force read all the tracks. if (p_ctx->capabilities & VC_CONTAINER_CAPS_FORCE_TRACK) { check_seek_then_force(0); check_seek_then_force(VC_CONTAINER_SEEK_FLAG_FORWARD); } seek_randomly(0); seek_randomly(VC_CONTAINER_SEEK_FLAG_FORWARD); // todo more? } else { // The file claims not to support seeking. Perform a seek, just to check this out. PTS_T beginning = 0; status = vc_container_seek(p_ctx, &beginning, VC_CONTAINER_SEEK_MODE_TIME, 0); if (status != VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION) { error_logger << "Container did not reject seek when its capabilities said it is not supported" << std::ends; } } // If the config didn't ask for this test don't do it. if (configuration.packet_buffer_size > 0) { re_seek_to_beginning(); // Read through the file, reading the packet in several bites (bite size controlled by -p parameter) check_partial_reads(); // Check forcing (this doesn't require seek) if (p_ctx->capabilities & VC_CONTAINER_CAPS_FORCE_TRACK) { re_seek_to_beginning(); check_forcing(); } } std::cout << "Test Complete" << std::endl; // If anything failed then the error logger will replace this error code. So always return 0 from here. return VC_CONTAINER_SUCCESS; } private: // read packets from the file, and stash them away. void read_sequential() { // Use the same buffer for reading each of the packets. It's cheaper to copy the (usually small) data // from this buffer than to create a new one for each packet, then resize it. // 256k is the size used in the other program, and is bigger than any known packet. Increase it // if a bigger one is found. std::vector buffer(256*1024, 0); std::map packet_counts; // Count of memory used for packet buffers. size_t memory_buffered = 0; if (configuration.dump_packets) { *configuration.dump_packets << "pts" << ",\t" << "track" << ",\t" << "size" << ",\t" << "crc" << std::endl; } while (1) // We break out at EOF. { // Packet object PACKET_DATA_T packet; // Use the shared buffer for the moment, packet.info.data = &buffer[0]; packet.info.buffer_size = buffer.size(); // Read data. status = vc_container_read(p_ctx, &packet.info, 0); if (packet.info.size >= buffer.size()) { std::cerr << "Packet size limit exceeeded. Increase internal buffer size!"; exit(VC_CONTAINER_ERROR_FAILED); } if (status == VC_CONTAINER_SUCCESS) { // Calculate the CRC of the packet packet.crc = crc32buf(&buffer[0], packet.info.size); // If there is any data, and we haven't exceeded our size limit... if ((packet.info.size > 0) && (memory_buffered < configuration.mem_max)) { // Copy the data we read across from the big buffer to our local one packet.buffer.assign(buffer.begin(), buffer.begin() + packet.info.size); // count how much we have buffered memory_buffered += packet.info.size; // wipe the bit of the big buffer we used memset(&buffer[0], 0, packet.info.size); } else { // not storing any data, either because there is none or we have no space. packet.buffer.clear(); } // Clear the pointer in our data. It won't be valid later. packet.info.data = 0; // Set the size of the buffer to match what is really there. packet.info.buffer_size = packet.info.size; // count the packet ++packet_counts[packet.info.track]; // store it all_packets.push_back(packet); // perhaps dump it if (configuration.dump_packets) { if (packet.info.pts < 0) { *configuration.dump_packets << "-"; } else { *configuration.dump_packets << packet.info.pts; } *configuration.dump_packets << ",\t" << packet.info.track << ",\t" << packet.info.size << ",\t" << packet.crc << std::endl; } } else if (status == VC_CONTAINER_ERROR_EOS) { break; } else { error_logger << "error reading file " << configuration.source_name << " Code " << status << std::ends; } } std::cout << std::dec << "File has " << packet_counts.size() << " tracks." << std::endl; // Print the packet count for each stream. Also, while iterating, save all the stream numbers. for(std::map::const_iterator stream = packet_counts.begin(); stream != packet_counts.end(); ++stream) { // Print the packet count std::cout << "Stream " << stream->first << " has " << stream->second << " packets." << std::endl; // Note that we have a stream with this number. all_streams.insert(stream->first); }; if (p_ctx->tracks_num != packet_counts.size()) { error_logger << "The file header claims " << p_ctx->tracks_num << " but " << packet_counts.size() << " streams were found" << std::ends; } } // Search the all_packets collection for key frames, and store them in all_key_packets void find_key_packets() { // The last known PTS for each stream. std::map last_pts_in_stream; for (ALL_PACKETS_T::const_iterator packet = all_packets.begin(); packet != all_packets.end(); ++packet) { PTS_T pts = packet->info.pts; STREAM_T stream = packet->info.track; // If it has a PTS check they are an ascending sequence. if (pts >= 0) { // Find the last PTS, if any, for this stream std::map::const_iterator last_known_pts = last_pts_in_stream.find(stream); if ((last_known_pts != last_pts_in_stream.end()) && (pts <= last_known_pts->second)) { // the PTS isn't bigger than the previous best. This is an error. error_logger << "Out of sequence PTS " << pts << " found. Previous largest was " << last_pts_in_stream[stream] << std::ends; } // store it (even if bad) last_pts_in_stream[stream] = pts; // Store in the collection of packets with PTSes all_pts_packets[stream].insert(std::make_pair(pts, packet)); // if it is also a keyframe if (packet->info.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) { // Put it into the collection for its track. all_key_packets[stream].insert(std::make_pair(pts, packet)); } } } } // Check which locations can be accessed by a seek, with or without VC_CONTAINER_SEEK_FLAG_FORWARD. void check_indices(VC_CONTAINER_SEEK_FLAGS_T direction) { STREAM_TIMED_PACKETS_T& index_positions = direction == 0 ? reverse_index_positions : forward_index_positions; // Go through each stream that contains key packets (usually only one) for (STREAM_TIMED_PACKETS_T::const_iterator stream_keys = all_key_packets.begin(); stream_keys != all_key_packets.end(); ++stream_keys) { // Start with a PTS just after the last key packet, and repeat until we fall off the beginning. for (PTS_T target_pts = stream_keys->second.rbegin()->first + 1; target_pts >= 0; /* no decrement */) { // copy the PTS value to pass to vc_container_seek PTS_T actual_pts = target_pts; // Seek to that position in the file. status = vc_container_seek(p_ctx, &actual_pts, VC_CONTAINER_SEEK_MODE_TIME, direction); if (status != VC_CONTAINER_SUCCESS) { error_logger << "Error " << status << " seeking to PTS " << target_pts << std::ends; continue; // if errors are not fatal we'll try again 1uS earlier. } // Check whether this seek reported that it went somewhere sensible check_correct_seek(direction, actual_pts, target_pts); // Validate that the place it said we arrived at is correct - read info for the first packet in any stream { PACKET_DATA_T packet; status = vc_container_read(p_ctx, &packet.info, VC_CONTAINER_READ_FLAG_INFO); if (status != VC_CONTAINER_SUCCESS) { error_logger << "Error " << status << " reading info for packet at PTS " << target_pts << std::ends; continue; // stop trying to read at this PTS. } if (packet.info.pts != actual_pts) { error_logger << "Incorrect seek. Container claimed to have arrived at " << actual_pts << " but first packet "; if (packet.info.pts < 0) { error_logger << " had no PTS"; } else { error_logger << "was at " << packet.info.pts; } error_logger << std::ends; } } // We'll perform reads until we've had a packet with PTS from every stream. for (std::set unfound_streams = all_streams; !unfound_streams.empty(); /* unfound_streams.erase(packet.info.track) */ ) { // Read a packet. We can't be sure what track it will be from, so first read the info... PACKET_DATA_T packet; status = vc_container_read(p_ctx, &packet.info, VC_CONTAINER_READ_FLAG_INFO); if (status != VC_CONTAINER_SUCCESS) { error_logger << "Error " << status << " reading info for packet following PTS " << target_pts << std::ends; break; // stop trying to read at this PTS. } // allocate a buffer. packet.buffer.resize(packet.info.size); packet.info.data = &packet.buffer[0]; packet.info.buffer_size = packet.info.size; // perform the read status = vc_container_read(p_ctx, &packet.info, 0); if (status != VC_CONTAINER_SUCCESS) { error_logger << "Error " << status << " reading packet following PTS " << target_pts << std::ends; break; // stop trying to read at this PTS. } STREAM_T stream = packet.info.track; // If it has no PTS we can't use it. Read another one. if (packet.info.pts < 0) { // The first packet we found for a stream has no PTS. // That's odd, because we wouldn't be able to play it. However, // if the stream doesn't permit forcing it may be inevitable. if ((unfound_streams.find(stream) != unfound_streams.end()) && ((p_ctx->capabilities & VC_CONTAINER_CAPS_FORCE_TRACK) != 0)) { error_logger << "Packet in stream " << stream << " has no PTS after seeking to PTS " << target_pts << std::ends; } continue; } // Search for the PTS collection for this stream STREAM_TIMED_PACKETS_T::iterator stream_packets = all_pts_packets.find(stream); if (stream_packets == all_pts_packets.end()) { error_logger << "Packet from unknown stream " << stream << "found when reading packet at PTS " << target_pts << std::ends; continue; // try reading another packet. } // Look for the packet for this PTS in this stream. TIMED_PACKETS_T::const_iterator expected_packet = stream_packets->second.find(packet.info.pts); if (expected_packet == stream_packets->second.end()) { error_logger << "Read packet from stream " << stream << " has unknown PTS " << packet.info.pts << std::ends; continue; } // calculate the CRC packet.crc = crc32buf(&packet.buffer[0], packet.info.size); // Validate that the data is the data we found when we did the sequential reads. std::string anyerror = compare_packets(*expected_packet->second, packet); if (!anyerror.empty()) { error_logger << "Incorrect data found at PTS " << actual_pts << anyerror << std::ends; } // If this is the first packet we found for this stream if (unfound_streams.find(stream) != unfound_streams.end()) { // Store the packet we found. Note we key it by the PTS we used for the seek, // not its own PTS. This should mean next time we seek to that PTS we'll get this packet. index_positions[stream].insert(std::make_pair(target_pts, expected_packet->second)); // Check whether the time on the packet is reasonable - it ought to be close to the time achieved. check_seek_tolerance(packet, actual_pts); // Now we've found a packet from this stream we're done with it, so note we don't care about it any more. unfound_streams.erase(stream); } } // repeat until we've had a packet for every track // Adjust the target location if (actual_pts > target_pts) { // Find the next packet down from where we looked, and try there. TIMED_PACKETS_T this_stream_keys = stream_keys->second; TIMED_PACKETS_T::const_iterator next_packet = this_stream_keys.lower_bound(target_pts); // If there is such a packet that it has more than this PTS, and it isn't the first packet if ((next_packet != this_stream_keys.begin()) && (next_packet != this_stream_keys.end())) { // pull out the PTS from the one before, and ask for a microsecond past it. PTS_T new_target_pts = (--next_packet)->first + 1; if (new_target_pts >= target_pts) { --target_pts; } else { target_pts = new_target_pts; } } else { // There's no packet earlier than where we are looking. // First time try 1 next time try zero. target_pts = target_pts != 1 ? 1 : 0; } } else if (actual_pts < (target_pts - 1)) { // The place we arrived at is well before where we wanted. // Next time go just after where we arrived. target_pts = actual_pts + 1; } else { // If the place we asked for is where we arrived, next time try one microsecond down. --target_pts; } } } } // Seek to all feasible locations and perform force reads on all possible tracks void check_seek_then_force(VC_CONTAINER_SEEK_FLAGS_T direction) { // Depending on whether we are doing forward or reverse seeks pick a collection of places a seek can go to. STREAM_TIMED_PACKETS_T index_positions = direction == 0 ? reverse_index_positions : forward_index_positions; // Go through all the streams for (STREAM_TIMED_PACKETS_T::const_iterator index_stream = index_positions.begin(); index_stream != index_positions.end(); ++index_stream) { // Go through all the packets in each stream that can be indexed for (TIMED_PACKETS_T::const_iterator location = index_stream->second.begin(); location != index_stream->second.end(); ++location ) { // This is the time we expect PTS_T actual_pts = location->first; // Seek to that position in the file. status = vc_container_seek(p_ctx, &actual_pts, VC_CONTAINER_SEEK_MODE_TIME, direction); if (status != VC_CONTAINER_SUCCESS) { error_logger << "Error " << status << " seeking to PTS " << location->first << std::ends; continue; // if errors are not fatal we'll try the next position. } // We'll perform force-reads until we've had a packet from every stream that has any PTS in it. for (STREAM_TIMED_PACKETS_T::const_iterator stream = all_pts_packets.begin(); stream != all_pts_packets.end(); ++stream) { // Read a packet. PACKET_DATA_T packet; packet.info.track = stream->first; // This loop repeats occasionally with a continue, but usually stops on the break at the end first time around. while(true) { status = vc_container_read(p_ctx, &packet.info, VC_CONTAINER_READ_FLAG_INFO | VC_CONTAINER_READ_FLAG_FORCE_TRACK); if (status != VC_CONTAINER_SUCCESS) { error_logger << "Error " << status << " force-reading info for stream " << location->first << " after seeking to PTS " << stream->first << std::ends; // If we can't read the info there's no point in anything else on this stream break; } // allocate a buffer. packet.buffer.resize(packet.info.size); packet.info.data = &packet.buffer[0]; packet.info.buffer_size = packet.info.size; // perform the actual read status = vc_container_read(p_ctx, &packet.info, VC_CONTAINER_READ_FLAG_FORCE_TRACK); if (status != VC_CONTAINER_SUCCESS) { error_logger << "Error " << status << " force-reading stream " << location->first << " after seeking to PTS " << stream->first << std::ends; // If we didn't read successfully we can't check the data. Try the next stream. break; } // If it has no PTS we can't use it - and it can't be played either. if (packet.info.pts < 0) { error_logger << "Packet force-read on stream " << stream->first << " has no PTS after seeking to PTS " << location->first << std::ends; // Try force-reading another. continue; } // Make sure that the packet is near the time we wanted. check_seek_tolerance(packet, actual_pts); // Look for the packet for this PTS in this stream. TIMED_PACKETS_T::const_iterator expected_packet = stream->second.find(packet.info.pts); if (expected_packet == stream->second.end()) { error_logger << "Packet force-read on stream " << stream->first << " has unknown PTS " << packet.info.pts << std::ends; // Try force-reading another. continue; } packet.crc = crc32buf(packet.info.data, packet.info.size); // Validate that the data is the data we found when we did the sequential reads. std::string anyerror = compare_packets(*expected_packet->second, packet); if (!anyerror.empty()) { error_logger << "Incorrect data found at PTS " << actual_pts << anyerror << std::ends; } // If we arrive here we're done for this stream. break; } } // repeat until we've had a packet for every stream at this index position } // repeat for every index position in the stream } // repeat for every stream that has index positions } // seek to a random selection of places, and see if we get the correct data void seek_randomly(VC_CONTAINER_SEEK_FLAGS_T direction) { // Depending on whether we are doing forward or reverse seeks pick a collection of places a seek can go to. const STREAM_TIMED_PACKETS_T& index_positions = direction == 0 ? reverse_index_positions : forward_index_positions; // Go through each stream in that collection for (STREAM_TIMED_PACKETS_T::const_iterator i_all_index_packets = index_positions.begin(); i_all_index_packets != index_positions.end(); ++i_all_index_packets) { // These are the index packets in the stream. const TIMED_PACKETS_T& all_index_packets = i_all_index_packets->second; // this is the number of seeks we'll perform. size_t seek_count = all_index_packets.size(); // If there are more than 100 locations limit it to 100. if (seek_count > 100) { seek_count = 100; } // We want an unsorted list of PTSes. std::list selected_index_packets; { // Picking 100 at random out of a potentially large collection of timestamps in a set is going // to be an expensive operation - search by key is really fast, but search by position is not. // This is an attempt to come up with something reasonably efficient even if the collection is large. std::vector all_ptses; // Reserve enough memory to hold every PTS. This collection should be several orders of magnitude smaller than the file. all_ptses.reserve(all_index_packets.size()); // Copy all the PTS values into the vector std::transform( all_index_packets.begin(), all_index_packets.end(), std::back_inserter(all_ptses), [](const TIMED_PACKETS_T::value_type& source){ return source.first; } ); // Based roughly on the Knuth shuffle, get a random subset of the PTS to the front of the collection. std::vector::iterator some_pts; size_t count; for (count = 0, some_pts = all_ptses.begin(); count < seek_count; ++count, ++some_pts) { // Swap some random PTS into one of the first seek_count locations. // Note this may well be another of the first seek_count locations, especially if this is most of them std::iter_swap(some_pts, (all_ptses.begin() + rand() % seek_count)); } // Throw away the ones we don't want all_ptses.resize(seek_count); // Copy the iterators for each packet with a selected PTS into the list. std::transform( all_ptses.begin(), all_ptses.end(), std::back_inserter(selected_index_packets), [&all_index_packets](PTS_T time){ return *all_index_packets.find(time); }); // End scope. That will free the rest of all_ptses. } // Loop through the selected packets. for (std::list::iterator expected = selected_index_packets.begin(); expected != selected_index_packets.end(); ++expected) { // Seek to their position & check we got there const PACKET_DATA_T& target = *expected->second; PTS_T target_pts = expected->first; status = vc_container_seek(p_ctx, &target_pts, VC_CONTAINER_SEEK_MODE_TIME, direction); check_correct_seek(direction, target_pts, expected->first); // Start by initialising the new packet object to match the old one - it's mostly right. PACKET_DATA_T found_packet = target; // If forcing is supported, read the first packet & check it's OK. if (p_ctx->capabilities & VC_CONTAINER_CAPS_FORCE_TRACK) { // There might not be a buffer if we ran out of memory found_packet.buffer.resize(found_packet.info.size); // Set the address found_packet.info.data = &found_packet.buffer[0]; // Perform the read status = vc_container_read(p_ctx, &found_packet.info, VC_CONTAINER_READ_FLAG_FORCE_TRACK); } else { // as forcing is not supported repeat reads until a packet is found for this track. do { status = vc_container_read(p_ctx, &found_packet.info, VC_CONTAINER_READ_FLAG_INFO); // Give up on any error if (status != VC_CONTAINER_SUCCESS) break; // Set the buffer to match the actual packet found_packet.info.buffer_size = found_packet.info.size; found_packet.buffer.resize(found_packet.info.size); found_packet.info.data = &found_packet.buffer[0]; status = vc_container_read(p_ctx, &found_packet.info, 0); if (status != VC_CONTAINER_SUCCESS) break; } while (found_packet.info.track != target.info.track); } if (status != VC_CONTAINER_SUCCESS) { error_logger << "Error " << status << " after random seek to PTS " << target_pts << std::ends; } else { found_packet.crc = crc32buf(&found_packet.buffer[0], found_packet.info.size); // We now have a packet from the right stream - it ought to be the one we asked for. const std::string& compare_result = compare_packets(found_packet, target); if (!compare_result.empty()) { error_logger << "Incorrect result from reading packet after random seek: " << compare_result << std::ends; } } } } } // Seek back to the beginning of the file, either with a seek or by closing and re-opening it void re_seek_to_beginning() { // If the container supports seek do it. if (p_ctx->capabilities & VC_CONTAINER_CAPS_CAN_SEEK) { PTS_T beginning = 0; status = vc_container_seek(p_ctx, &beginning, VC_CONTAINER_SEEK_MODE_TIME, 0); if (status != VC_CONTAINER_SUCCESS) { error_logger << "Failed to seek back to the beginning" << std::ends; } } else { // The file claims not to support seeking. Close it, and re-open - this should do it. status = vc_container_close(p_ctx); if (status != VC_CONTAINER_SUCCESS) { error_logger << "Error " << status << " Failed to close the container." << std::ends; } p_ctx = vc_container_open_reader(configuration.source_name.c_str(), &status, 0, 0); if (status != VC_CONTAINER_SUCCESS) { error_logger << "Error " << status << " Failed to re-open the container." << std::ends; // Even with -k this is a fatal error. Give up. exit(status); } } } // Read through the file, checking to see that the packets we get match the ones we read first time around - // given that we're going to read with a buffer of a different size, either a fixed value or a proportion // of the actual packet size. void check_partial_reads() { // This is used for some reads, and for compares. PACKET_DATA_T actual; // Repeat until we meet EOF, which will be in the middle of the loop somewhere. ALL_PACKETS_T::const_iterator expected; for (expected = all_packets.begin(); expected != all_packets.end(); ++expected) { // Ask for info about the first packet status = vc_container_read(p_ctx, &actual.info, VC_CONTAINER_READ_FLAG_INFO); if (status != VC_CONTAINER_SUCCESS) { error_logger << "Error " << status << " reading info for partial packet" << std::ends; // Not much point in carrying on if we can't read - but usually this is the end-of-file break. break; } size_t whole_packet = actual.info.size; // Work out how big a read we want to do. size_t wanted_read_size = (configuration.packet_buffer_size >= 1.0) ? (size_t)configuration.packet_buffer_size : (size_t)(configuration.packet_buffer_size * (double)whole_packet); // Make sure it's at least 1 byte (a small proportion of a small packet might round down to zero) if (wanted_read_size == 0) { wanted_read_size = 1; } // Ensure our buffer is at least big enough to contain the _whole_ packet. if (whole_packet > actual.buffer.size()) { actual.buffer.resize(whole_packet); } // We'll need to collect some data from the bits of the packet as they go by. PTS_T first_valid_pts = std::numeric_limits::min(); PTS_T first_valid_dts = std::numeric_limits::min(); uint32_t total_flags = 0; size_t amount_read; // Loop around several times, reading part of the packet each time for (amount_read = 0; amount_read < whole_packet; /* increment elsewhere */) { // Somewhere in the buffer actual.info.data = &actual.buffer[amount_read]; // Not more than our calculated size actual.info.buffer_size = wanted_read_size; // read some data. status = vc_container_read(p_ctx, &actual.info, 0); if (status != VC_CONTAINER_SUCCESS) { error_logger << "Unable to read " << wanted_read_size << " bytes from packet of size " << expected->info.size << " error " << status << std::ends; // Guess we're at end of packet. We _might_ be able to recover from this. break; } amount_read += actual.info.size; // validate the amount read is less than the request if (actual.info.size > wanted_read_size) { error_logger << "Too much data read from packet. Request size was " << whole_packet << " but " << actual.info.size << "read in" << std::ends; } // and that the total amount read for this packet is not too much if (amount_read > whole_packet) { error_logger << "Too much data read from packet. Total size is " << whole_packet << " but " << amount_read << "read in" << std::ends; } // OR all the flags together total_flags |= actual.info.flags; // Save the PTS if we don't have one yet if (first_valid_pts < 0) { first_valid_pts = actual.info.pts; } // Ditto the DTS if (first_valid_dts < 0) { first_valid_dts = actual.info.dts; } } // The buffer should now contain all the correct data. However, the size field // reflects the last read only - so correct it before the compare. actual.info.size = amount_read; // store back the other stuff we got in the loop actual.info.flags = total_flags; actual.info.pts = first_valid_pts; actual.info.dts = first_valid_dts; // Calculate the CRC of the whole lot of data actual.crc = crc32buf(&actual.buffer[0], amount_read); // It's possible that the packet read isn't the one we expected. // (This happens with the Supremecy3_20sec_WMV_MainProfile_VGA@29.97fps_2mbps_WMA9.2.wmv sample, // where the first packet has no PTS and is not a key frame - seek won't go there) if ((expected->info.pts != actual.info.pts) && (actual.info.pts >= 0)) { ALL_PACKETS_T::const_iterator candidate = std::find_if (all_packets.begin(), all_packets.end(), [&actual](ALL_PACKETS_T::value_type& expected) -> bool { // If we find one with the correct track and PTS, that will do. return (expected.info.pts == actual.info.pts) && (expected.info.track == actual.info.track); }); if (candidate != all_packets.end()) { // We've seen the packet before error_logger << "Partial read returned an unexpected packet. Expect PTS " << expected->info.pts << " but got PTS " << actual.info.pts << std::ends; // switch to expecting this packet expected = candidate; } // If we didn't find the packet anywhere in the expected list do nothing, and let the compare fail. } const std::string& compare_result = compare_packets(*expected, actual); if (!compare_result.empty()) { error_logger << "Incorrect result from reading packet in parts: " << compare_result << std::ends; } } // check end reached if we were OK this far if (status == VC_CONTAINER_SUCCESS) { // We should be at the end of the expected collection if (expected != all_packets.end()) { error_logger << "Failed to read entire file when reading partial packets" << std::ends; } // We should not be able to read any more. status = vc_container_read(p_ctx, &actual.info, VC_CONTAINER_READ_FLAG_INFO); if (status == VC_CONTAINER_ERROR_EOS) { status = VC_CONTAINER_SUCCESS; } else { error_logger << "Should have reached end of stream, but got status " << status << " while reading partial packets." << std::ends; } } } // Information used in the next function about where we are in each stream. struct FORCING_INFO : public PACKET_DATA_T { void new_packet() { first_valid_pts = std::numeric_limits::min(); first_valid_dts = std::numeric_limits::min(); total_flags = 0; // Adjust the buffer to hold the amount we want. // (note - resize smaller does not re-allocate, // it'll grow until it reaches the biggest packet then stay at that size) buffer.resize(info.buffer_size); info.data = &buffer[0]; } PTS_T first_valid_pts; PTS_T first_valid_dts; uint32_t total_flags; ALL_PACKETS_T::const_iterator position; }; // Check forcing. Read bits of the streams so that they get out of sync, and make sure the data is correct. void check_forcing() { // Data for each stream std::map stream_positions; std::map::iterator stream_pos; // Set stream_positions to the first packet for each stream for (std::set::const_iterator stream = all_streams.begin(); stream != all_streams.end(); ++stream) { const STREAM_T stream_number = *stream; FORCING_INFO stream_data; stream_data.position = all_packets.begin(); // Insert an entry for this stream, starting at the beginning stream_pos = stream_positions.insert(std::make_pair(stream_number, stream_data)).first; // Then search for a packet in this stream. next_in_stream(stream_pos); stream_pos->second.info.track = stream_number; } // Repeat until explicit break when all have reached last packet bool alldone = false; while (!alldone) { // go through each stream in turn and get some data. At end of packet check it. for (stream_pos = stream_positions.begin(); stream_pos != stream_positions.end(); ++stream_pos) { FORCING_INFO& stream_data = stream_pos->second; if (stream_data.position == all_packets.end()) { // This stream has reached the end of the expected packets. Force-read an info, // to make sure it really is at the end. status = vc_container_read(p_ctx, &stream_data.info, VC_CONTAINER_READ_FLAG_INFO | VC_CONTAINER_READ_FLAG_FORCE_TRACK); if (status != VC_CONTAINER_ERROR_EOS) { error_logger << "Reading from stream " << stream_pos->first << " after end gave error " << status << "instead of EOS" << std::ends; // Erase the buffer so we don't repeat the check. We'd just end up with loads of errors. stream_positions.erase(stream_pos); // Then reset the iterator, as that made it invalid. stream_pos = stream_positions.begin(); } } else { // This stream has not reached the end. Read some more, maybe check it. // If we haven't read anything in yet if (stream_data.info.size == 0) { if (configuration.packet_buffer_size <= 0) { // if unspecified read whole packets. stream_data.info.buffer_size = stream_data.position->info.size; } else if (configuration.packet_buffer_size < 1.0) { // If a proportion work it out stream_data.info.buffer_size = (uint32_t)(configuration.packet_buffer_size * stream_data.position->info.size); if (stream_data.info.buffer_size < 1) { // but never less than 1 byte stream_data.info.buffer_size = 1; } } else { // Use the amount specified stream_data.info.buffer_size = (uint32_t)configuration.packet_buffer_size; } stream_data.new_packet(); } else { // We've already read part of the packet. Read some more. size_t read_so_far = stream_data.buffer.size(); // expand the buffer to hold another lump of data stream_data.buffer.resize(read_so_far + stream_data.info.buffer_size); // point the read at the new part. stream_data.info.data = &stream_data.buffer[read_so_far]; } // Read some data status = vc_container_read(p_ctx, &stream_data.info, VC_CONTAINER_READ_FLAG_FORCE_TRACK); stream_data.total_flags |= stream_data.info.flags; // Save the PTS if we don't have one yet if (stream_data.first_valid_pts < 0) { stream_data.first_valid_pts = stream_data.info.pts; } // Ditto the DTS if (stream_data.first_valid_dts < 0) { stream_data.first_valid_dts = stream_data.info.dts; } // work out how much we have. uint32_t total_read = (stream_data.info.data + stream_data.info.size) - &stream_data.buffer[0]; if (total_read > stream_data.position->info.size) { // we've read more than a packet. That's an error. error_logger << "Read more data than expected from a packet - read " << total_read << " Expect " << stream_data.position->info.size; } if (total_read < stream_data.position->info.size) { // more to read. Just go back around the loop. continue; } stream_data.info.size = total_read; stream_data.crc = crc32buf(&stream_data.buffer[0], total_read); stream_data.info.flags = stream_data.total_flags; stream_data.info.pts = stream_data.first_valid_pts; stream_data.info.dts = stream_data.first_valid_dts; // Validate that the data is the data we found when we did the sequential reads. std::string anyerror = compare_packets(*stream_data.position, stream_data); if (!anyerror.empty()) { error_logger << "Incorrect data found at PTS " << stream_data.info.pts << anyerror << std::ends; } // Move to the next packet ++stream_data.position; // Then search until we get one for this stream, or end. next_in_stream(stream_pos); // Note we've read nothing of the new packet yet. stream_data.info.size = 0; } } // go through each stream in turn. If they have all reached the end we are complete. for (alldone = true, stream_pos = stream_positions.begin(); alldone && stream_pos != stream_positions.end(); ++stream_pos) { if (stream_pos->second.position != all_packets.end()) { // If any stream has not reached the end we have more to do alldone = false; } } } } // Helper for the above: Advance the supplied FORCING_INFO to the next packet in the stream void next_in_stream(std::map::iterator& pos) { // We're searching for this stream const STREAM_T track = pos->first; ALL_PACKETS_T::const_iterator& position = pos->second.position; // Loop until no more packets, or found one for this stream while ((position != all_packets.end()) && (position->info.track != track)) { // advance this iterator to the next packet. ++position; } }; // Compare two packets, and return a non-empty string if they don't match std::string compare_packets(const PACKET_DATA_T& expected, const PACKET_DATA_T& actual) { std::ostringstream errors; // Check the fields in the structure if (expected.info.size != actual.info.size) { errors << "size mismatch. Expect " << expected.info.size << " actual " << actual.info.size << std::endl; } if (expected.info.frame_size != actual.info.frame_size) { errors << "frame_size mismatch. Expect " << expected.info.frame_size << " actual " << actual.info.frame_size << std::endl; } if (expected.info.pts != actual.info.pts) { errors << "pts mismatch. Expect " << expected.info.pts << " actual " << actual.info.pts << std::endl; } if (expected.info.dts != actual.info.dts) { errors << "dts mismatch. Expect " << expected.info.dts << " actual " << actual.info.dts << std::endl; } if (expected.info.track != actual.info.track) { errors << "track mismatch. Expect " << expected.info.track << " actual " << actual.info.track << std::endl; } if (expected.info.flags != actual.info.flags) { errors << "flags mismatch. Expect " << expected.info.flags << " actual " << actual.info.flags << std::endl; } // check the buffer. We won't bother if there isn't one in either - if the expected hasn't got one, // it's probably because we hit our memory limit. If the actual hasn't, the there should be a size error. for (size_t i = 0, err_count = 0; i < expected.buffer.size() && i < actual.buffer.size(); ++i) { if (expected.buffer[i] != actual.buffer[i]) { errors << "Data mismatch at " << std::setw(8) << std::hex << i << std::setw(3) << (unsigned)expected.buffer[i] << std::setw(3)<< (unsigned)actual.buffer[i] << std::endl; if (++err_count > 20) { errors << "Too many errors to report" << std::endl; break; } } } if (expected.crc != actual.crc) { errors << "CRC mismatch. Expect " << expected.crc << " actual " << actual.crc << std::endl; } // If there were errors put a new line on the front. This aids formatting. const std::string& error_list = errors.str(); if (error_list.empty()) { // There were no errors return error_list; } else { return std::string("\n") + error_list; } } void check_correct_seek(VC_CONTAINER_SEEK_FLAGS_T direction, PTS_T actual_pts, PTS_T target_pts) { if (status != VC_CONTAINER_SUCCESS) { error_logger << "Error " << status << " returned by seek" << std::ends; } if (direction) { if (actual_pts < target_pts) { // We shouldn't normally arrive at a time earlier than we requested. // However there's a special case - when the time requested is after the last keyframe PTS_T highest_pts_in_video = all_key_packets.at(video_stream).rbegin()->first; if (actual_pts < highest_pts_in_video) { error_logger << "Incorrect forward seek. Should not be before " << target_pts << " but arrived at " << actual_pts << std::ends; } } } else { if (actual_pts > target_pts) { // We shouldn't normally arrive at a time later than we requested. // However there's a special case - when the time requested is before the first keyframe in the file. PTS_T lowest_pts_in_video = all_key_packets.at(video_stream).begin()->first; if (actual_pts > lowest_pts_in_video) { error_logger << "Incorrect reverse seek. Should not be after " << target_pts << " but arrived at " << actual_pts << std::ends; } } } } // Check whether a packet is close enough to the PTS supplied. Used after a seek. void check_seek_tolerance(const PACKET_DATA_T& packet, PTS_T actual_pts) { // Check whether the time on the packet is reasonable - it ought to be close to the time achieved. if (packet.info.track == video_stream) { if ((packet.info.pts + configuration.tolerance_video_early) < actual_pts) { error_logger << "Video stream seek location is bad " << actual_pts - packet.info.pts << "uS early" << std::ends; } if ((packet.info.pts - configuration.tolerance_video_late) > actual_pts) { // We shouldn't normally arrive at a time later than we requested. // However there's a special case - when the time requested is before the first PTS in the file. PTS_T lowest_pts_in_video = all_pts_packets.at(video_stream).begin()->first; if (actual_pts > lowest_pts_in_video) { error_logger << "Video stream seek location is bad " << packet.info.pts - actual_pts << "uS late" << std::ends; } } } else { if ((packet.info.pts + configuration.tolerance_other_early) < actual_pts) { error_logger << "Non-video stream seek location is bad " << actual_pts - packet.info.pts << "uS early" << std::ends; } if ((packet.info.pts - configuration.tolerance_other_late) > actual_pts) { error_logger << "Non-video stream seek location is bad " << packet.info.pts - actual_pts << "uS late" << std::ends; } } } // Initialise a Congruential Random Number Generator from the file contents. // Used rather than rand() for the complete control that this offers - this app // should behave the same regardless of the platform and word size. void init_crg() { if (all_packets.empty()) { static const char* msg = "You must read some packets before initialising the RNG"; assert(!msg); std::cerr << msg; exit(1); } rng_value = 0; // XOR all the CRCs together to act as a file-specific seed. for (ALL_PACKETS_T::const_iterator packet = all_packets.begin(); packet != all_packets.end(); ++packet) { rng_value ^= packet->crc; } } // Get a pseudo-random number. uint32_t rand() { // Constants from "Numerical recipes in 'C'" return rng_value = 1664525 * rng_value + 1013904223; } // configuration const CONFIGURATION_T configuration; // Error logger. class ERROR_LOGGER_T error_logger; // The status from the last call made VC_CONTAINER_STATUS_T status; // Pointer to the file being processed VC_CONTAINER_T *p_ctx; // All the packets in the file ALL_PACKETS_T all_packets; // All the streams std::set all_streams; // All the streams and all the packets in them with a PTS, whether or not they are keyframes. STREAM_TIMED_PACKETS_T all_pts_packets; // Subset of the above that hold key frames. STREAM_TIMED_PACKETS_T all_key_packets; // Places where a seek without VC_CONTAINER_SEEK_FLAG_FORWARD can go to. A subset of all_key_packets. STREAM_TIMED_PACKETS_T reverse_index_positions; // Places where a seek with VC_CONTAINER_SEEK_FLAG_FORWARD can go to. STREAM_TIMED_PACKETS_T forward_index_positions; // The first stream containing video packets STREAM_T video_stream; // The stored value of the RNG uint32_t rng_value; }; int main(int argc, char** argv) { // Read and parse the configuration information from the command line. TESTER_T test(argc, argv); // Run the tests and return their error code return test.run(); } userland/containers/test/check_frame_int.c000066400000000000000000000267221421703157200212770ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include "containers/containers.h" #include "containers/core/containers_common.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_io.h" #define BUFFER_SIZE 256*1024 #define MAX_TRACKS 16 #define MAX_SEEKS 16 static int container_test_info(VC_CONTAINER_T *ctx, bool b_reader); static int container_test_parse_cmdline(int argc, char **argv); static const char *psz_in = 0; static long packets_num = 0; static long track_num = -1; static int fps = 30; static int margin = 5; // margin for frame interval, percent static struct { uint32_t mapping; uint32_t frames; uint32_t packets; uint64_t bytes; uint32_t frame_size; int64_t first_dts; int64_t first_pts; int64_t last_dts; int64_t last_pts; } tracks[MAX_TRACKS]; static int32_t verbosity = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO; /*****************************************************************************/ int main(int argc, char **argv) { int retval = 0; VC_CONTAINER_T *p_ctx = 0; VC_CONTAINER_STATUS_T status; unsigned int i; uint8_t *buffer = malloc(BUFFER_SIZE); int32_t interval; int64_t last_packet_pts = -1; int32_t max_interval = 0, max_interval_after_first = 0; int fail = 0; if(container_test_parse_cmdline(argc, argv)) goto error_silent; LOG_INFO (0, "Require that no frame interval greater than 1/%d seconds (%d%% margin)", fps, margin); /* Set the general verbosity */ vc_container_log_set_verbosity(0, verbosity); vc_container_log_set_default_verbosity(verbosity); p_ctx = vc_container_open_reader(psz_in, &status, 0, 0); if(!p_ctx) { LOG_ERROR(0, "error opening file %s (%i)", psz_in, status); goto error; } if (verbosity & VC_CONTAINER_LOG_DEBUG) { container_test_info(p_ctx, true); } LOG_INFO (0, "Search for video track only"); /* Disabling tracks which are not requested and enable packetisation if requested */ for(i = 0; i < p_ctx->tracks_num; i++) { VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i]; track->is_enabled = (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO); } LOG_DEBUG(0, "TEST start reading"); for(i = 0; !packets_num || (long)i < packets_num; i++) { VC_CONTAINER_PACKET_T packet = {0}; int32_t frame_num = 0; status = vc_container_read(p_ctx, &packet, VC_CONTAINER_READ_FLAG_INFO); if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST info status: %i", status); break;} if(packet.track < MAX_TRACKS) { if((packet.flags & VC_CONTAINER_PACKET_FLAG_FRAME_START)) { tracks[packet.track].frames++; tracks[packet.track].frame_size = 0; } frame_num = tracks[packet.track].frames; } tracks[packet.track].frame_size += packet.size; // LOG_DEBUG(0, "packet info: track %i, size %i/%i/%i, pts %"PRId64", flags %x%s, num %i", // packet.track, packet.size, packet.frame_size, tracks[packet.track].frame_size, packet.pts, packet.flags, // (packet.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) ? " (keyframe)" : "", // frame_num-1); if (last_packet_pts != -1) interval = packet.pts - last_packet_pts; else interval = 0; // packet.pts; last_packet_pts = packet.pts; max_interval = MAX (max_interval, interval); if (i >= 2) max_interval_after_first = MAX (max_interval_after_first, interval); /* Check if interval (in us) exceeds 1/fps, with percentage margin */ if (interval * fps > 1e4 * (100+margin)) { LOG_INFO (0, "Frame %d, interval %.3f FAILED", i, interval / 1000.0f); fail = 1; } LOG_DEBUG (0, "Frame %d, interval %.3f", i, interval / 1000.0f); if(track_num >= 0 && packet.track != (uint32_t)track_num) { status = vc_container_read(p_ctx, 0, VC_CONTAINER_READ_FLAG_SKIP); if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST skip status: %i", status); break;} continue; } packet.buffer_size = BUFFER_SIZE; packet.data = buffer; packet.size = 0; status = vc_container_read(p_ctx, &packet, 0); if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST read status: %i", status); break;} // LOG_DEBUG(0, "packet: track %i, size %i, pts %"PRId64", flags %x", packet.track, packet.size, packet.pts, packet.flags); if (tracks[packet.track].packets) { tracks[packet.track].last_dts = packet.dts; tracks[packet.track].last_pts = packet.pts; } else { tracks[packet.track].first_dts = packet.dts; tracks[packet.track].first_pts = packet.pts; } if(packet.track < MAX_TRACKS) { tracks[packet.track].packets++; tracks[packet.track].bytes += packet.size; } } LOG_DEBUG(0, "TEST stop reading"); /* Output stats */ for(i = 0; i < p_ctx->tracks_num; i++) { LOG_INFO(0, "track %u: read %u samples in %u packets for a total of %"PRIu64" bytes", i, tracks[i].frames, tracks[i].packets, tracks[i].bytes); LOG_INFO(0, "Starting at %"PRId64"us (decode at %"PRId64"us), ending at %"PRId64"us (decode at %"PRId64"us)", tracks[i].first_pts, tracks[i].first_dts, tracks[i].last_pts, tracks[i].last_dts); } LOG_INFO (0, "---\nMax interval = %.3f ms; max interval (after first) = %.3f ms\n", (float) max_interval / 1000.0, (float) max_interval_after_first / 1000.0); end: if(p_ctx) vc_container_close(p_ctx); free(buffer); #ifdef _MSC_VER getchar(); #endif retval = fail; if (fail) { LOG_INFO (0, "TEST FAILED: highest frame interval = %.3f ms", max_interval / 1000.0f); } else LOG_INFO (0, "TEST PASSED"); return retval; error: LOG_ERROR(0, "TEST FAILED TO RUN"); error_silent: retval = -1; goto end; } static int container_test_parse_cmdline(int argc, char **argv) { int i, j, k; int32_t *p_verbosity; /* Parse the command line arguments */ for(i = 1; i < argc; i++) { if(!argv[i]) continue; if(argv[i][0] != '-') { /* Not an option argument so will be the input URI */ psz_in = argv[i]; continue; } /* We are now dealing with command line options */ switch(argv[i][1]) { case 'v': j = 2; p_verbosity = &verbosity; *p_verbosity = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO; for(k = 0; k < 2 && argv[i][j+k] == 'v'; k++) *p_verbosity = (*p_verbosity << 1) | 1 ; break; case 'f': if(i+1 == argc || !argv[i+1]) goto invalid_option; fps = strtol(argv[++i], 0, 0); break; case 'h': goto usage; default: goto invalid_option; } continue; } /* Sanity check that we have at least an input uri */ if(!psz_in) { LOG_ERROR(0, "missing uri argument"); goto usage; } return 0; invalid_option: LOG_ERROR(0, "invalid command line option (%s)", argv[i]); usage: psz_in = strrchr(argv[0], '\\'); if(psz_in) psz_in++; if(!psz_in) {psz_in = strrchr(argv[0], '/'); if(psz_in) psz_in++;} if(!psz_in) psz_in = argv[0]; LOG_INFO(0, ""); LOG_INFO(0, "usage: %s [options] uri", psz_in); LOG_INFO(0, "options list:"); LOG_INFO(0, " -vxx : general verbosity level (replace xx with a number of \'v\')"); LOG_INFO(0, " -f : required frame rate/second (frame interval must not exceed 1/f)"); LOG_INFO(0, " -h : help"); return 1; } static int container_test_info(VC_CONTAINER_T *ctx, bool b_reader) { const char *name_type; unsigned int i; LOG_INFO(0, ""); if(b_reader) LOG_INFO(0, "----Reader Information----"); else LOG_INFO(0, "----Writer Information----"); LOG_INFO(0, "duration: %2.2fs, size: %"PRId64, ctx->duration/1000000.0, ctx->size); LOG_INFO(0, "capabilities: %x", ctx->capabilities); LOG_INFO(0, ""); for(i = 0; i < ctx->tracks_num; i++) { VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; switch(track->format->es_type) { case VC_CONTAINER_ES_TYPE_AUDIO: name_type = "audio"; break; case VC_CONTAINER_ES_TYPE_VIDEO: name_type = "video"; break; case VC_CONTAINER_ES_TYPE_SUBPICTURE: name_type = "subpicture"; break; default: name_type = "unknown"; break; } if (!strcmp (name_type, "video")) { LOG_INFO(0, "track: %i, type: %s, fourcc: %4.4s", i, name_type, (char *)&track->format->codec); LOG_INFO(0, " bitrate: %i, framed: %i, enabled: %i", track->format->bitrate, !!(track->format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED), track->is_enabled); LOG_INFO(0, " extra data: %i, %p", track->format->extradata_size, track->format->extradata); switch(track->format->es_type) { case VC_CONTAINER_ES_TYPE_VIDEO: LOG_INFO(0, " width: %i, height: %i, (%i,%i,%i,%i)", track->format->type->video.width, track->format->type->video.height, track->format->type->video.x_offset, track->format->type->video.y_offset, track->format->type->video.visible_width, track->format->type->video.visible_height); LOG_INFO(0, " pixel aspect ratio: %i/%i, frame rate: %i/%i", track->format->type->video.par_num, track->format->type->video.par_den, track->format->type->video.frame_rate_num, track->format->type->video.frame_rate_den); break; default: break; } } } for (i = 0; i < ctx->meta_num; ++i) { const char *name, *value; if (i == 0) LOG_INFO(0, ""); name = vc_container_metadata_id_to_string(ctx->meta[i]->key); value = ctx->meta[i]->value; if(!name) continue; LOG_INFO(0, "metadata(%i) : %s : %s", i, name, value); } LOG_INFO(0, "--------------------------"); LOG_INFO(0, ""); return 0; } userland/containers/test/crc_32.c000066400000000000000000000240661421703157200172500ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #ifndef Boolean_T typedef unsigned Boolean_T; #endif #ifndef Error_ #define Error_ 1 #endif #ifndef Success_ #define Success_ 0 #endif #define UPDC32(octet,crc) (crc_32_tab[((crc)\ ^ ((uint8_t)octet)) & 0xff] ^ ((crc) >> 8)) #ifdef __TURBOC__ #pragma warn -cln #endif /**********************************************************************\ |* Demonstration program to compute the 32-bit CRC used as the frame *| |* check sequence in ADCCP (ANSI X3.66, also known as FIPS PUB 71 *| |* and FED-STD-1003, the U.S. versions of CCITT's X.25 link-level *| |* protocol). The 32-bit FCS was added via the Federal Register, *| |* 1 June 1982, p.23798. I presume but don't know for certain that *| |* this polynomial is or will be included in CCITT V.41, which *| |* defines the 16-bit CRC (often called CRC-CCITT) polynomial. FIPS *| |* PUB 78 says that the 32-bit FCS reduces otherwise undetected *| |* errors by a factor of 10^-5 over 16-bit FCS. *| \**********************************************************************/ /* Need an unsigned type capable of holding 32 bits; */ typedef uint32_t UNS_32_BITS; /* Copyright (C) 1986 Gary S. Brown. You may use this program, or code or tables extracted from it, as desired without restriction.*/ /* First, the polynomial itself and its table of feedback terms. The */ /* polynomial is */ /* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ /* Note that we take it "backwards" and put the highest-order term in */ /* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ /* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ /* the MSB being 1. */ /* Note that the usual hardware shift register implementation, which */ /* is what we're using (we're merely optimizing it by doing eight-bit */ /* chunks at a time) shifts bits into the lowest-order term. In our */ /* implementation, that means shifting towards the right. Why do we */ /* do it this way? Because the calculated CRC must be transmitted in */ /* order from highest-order term to lowest-order term. UARTs transmit */ /* characters in order from LSB to MSB. By storing the CRC this way, */ /* we hand it to the UART in the order low-byte to high-byte; the UART */ /* sends each low-bit to hight-bit; and the result is transmission bit */ /* by bit from highest- to lowest-order term without requiring any bit */ /* shuffling on our part. Reception works similarly. */ /* The feedback terms table consists of 256, 32-bit entries. Notes: */ /* */ /* 1. The table can be generated at runtime if desired; code to do so */ /* is shown later. It might not be obvious, but the feedback */ /* terms simply represent the results of eight shift/xor opera- */ /* tions for all combinations of data and CRC register values. */ /* */ /* 2. The CRC accumulation logic is the same for all CRC polynomials, */ /* be they sixteen or thirty-two bits wide. You simply choose the */ /* appropriate table. Alternatively, because the table can be */ /* generated at runtime, you can start by generating the table for */ /* the polynomial in question and use exactly the same "updcrc", */ /* if your application needn't simultaneously handle two CRC */ /* polynomials. (Note, however, that XMODEM is strange.) */ /* */ /* 3. For 16-bit CRCs, the table entries need be only 16 bits wide; */ /* of course, 32-bit entries work OK if the high 16 bits are zero. */ /* */ /* 4. The values must be right-shifted by eight bits by the "updcrc" */ /* logic; the shift must be unsigned (bring in zeroes). On some */ /* hardware you could probably optimize the shift in assembler by */ /* using byte-swap instructions. */ static UNS_32_BITS crc_32_tab[] = { /* CRC polynomial 0xedb88320 */ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; uint32_t updateCRC32(unsigned char ch, uint32_t crc) { return UPDC32(ch, crc); } Boolean_T crc32file(char *name, uint32_t *crc, long *charcnt) { register FILE *fin; register uint32_t oldcrc32; register int c; oldcrc32 = 0xFFFFFFFF; *charcnt = 0; #ifdef MSDOS if ((fin=fopen(name, "rb"))==NULL) #else if ((fin=fopen(name, "r"))==NULL) #endif { perror(name); return Error_; } while ((c=getc(fin))!=EOF) { ++*charcnt; oldcrc32 = UPDC32(c, oldcrc32); } if (ferror(fin)) { perror(name); *charcnt = -1; } fclose(fin); *crc = oldcrc32 = ~oldcrc32; return Success_; } uint32_t crc32buf(const uint8_t *buf, size_t len) { register uint32_t oldcrc32; oldcrc32 = 0xFFFFFFFF; for ( ; len; --len, ++buf) { oldcrc32 = UPDC32(*buf, oldcrc32); } return ~oldcrc32; } #ifdef TEST main(int argc, char *argv[]) { uint32_t crc; long charcnt; register errors = 0; while(--argc > 0) { errors |= crc32file(*++argv, &crc, &charcnt); printf("%08lX %7ld %s\n", crc, charcnt, *argv); } return(errors != 0); } #endif /* TEST */ userland/containers/test/datagram_receiver.c000066400000000000000000000051321421703157200216320ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include "containers/net/net_sockets.h" int main(int argc, char **argv) { VC_CONTAINER_NET_T *sock; vc_container_net_status_t status; char *buffer; size_t buffer_size; size_t received; if (argc < 2) { printf("Usage:\n%s \n", argv[0]); return 1; } sock = vc_container_net_open(NULL, argv[1], 0, &status); if (!sock) { printf("vc_container_net_open failed: %d\n", status); return 2; } buffer_size = vc_container_net_maximum_datagram_size(sock); buffer = (char *)malloc(buffer_size); if (!buffer) { vc_container_net_close(sock); printf("Failure allocating buffer\n"); return 3; } while ((received = vc_container_net_read(sock, buffer, buffer_size)) != 0) { char *ptr = buffer; while (received--) putchar(*ptr++); } if (vc_container_net_status(sock) != VC_CONTAINER_NET_SUCCESS) { printf("vc_container_net_read failed: %d\n", vc_container_net_status(sock)); } vc_container_net_close(sock); return 0; } userland/containers/test/datagram_sender.c000066400000000000000000000051201421703157200213030ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include "containers/net/net_sockets.h" int main(int argc, char **argv) { VC_CONTAINER_NET_T *sock; vc_container_net_status_t status; char *buffer; size_t buffer_size; if (argc < 3) { printf("Usage:\n%s
\n", argv[0]); return 1; } sock = vc_container_net_open(argv[1], argv[2], 0, &status); if (!sock) { printf("vc_container_net_open failed: %d\n", status); return 2; } buffer_size = vc_container_net_maximum_datagram_size(sock); buffer = (char *)malloc(buffer_size); if (!buffer) { vc_container_net_close(sock); printf("Failure allocating buffer\n"); return 3; } printf("Don't enter more than %d characters in one line!\n", (int)buffer_size); while (fgets(buffer, buffer_size, stdin)) { if (!vc_container_net_write(sock, buffer, strlen(buffer))) { printf("vc_container_net_write failed: %d\n", vc_container_net_status(sock)); break; } } vc_container_net_close(sock); return 0; } userland/containers/test/dump_pktfile.c000066400000000000000000000111221421703157200206450ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #define PACKET_BUFFER_SIZE 32768 enum { SUCCESS = 0, SHOWED_USAGE, FAILED_TO_OPEN_PKTFILE, FAILED_TO_OPEN_OUTPUT_FILE, BYTE_ORDER_MARK_MISSING, INVALID_BYTE_ORDER_MARK, MEMORY_ALLOCATION_FAILURE, PACKET_TOO_BIG, }; /** Native byte order word */ #define NATIVE_BYTE_ORDER 0x50415753U /** Reverse of native byte order - need to swap bytes around */ #define SWAP_BYTE_ORDER 0x53574150U static unsigned long reverse_byte_order( unsigned long value ) { /* Reverse the order of the bytes in the word */ return ((value << 24) | ((value & 0xFF00) << 8) | ((value >> 8) & 0xFF00) | (value >> 24)); } int main(int argc, char **argv) { int status = SUCCESS; FILE *pktfile = NULL, *output_file = NULL; uint32_t byte_order, packet_size, packet_read; char *buffer = NULL; if (argc < 2) { printf("\ Usage:\n\ %s []\n\ is the file to read.\n\ , if given, will receive the unpacketized data.\n", argv[0]); status = SHOWED_USAGE; goto end_program; } pktfile = fopen(argv[1], "rb"); if (!pktfile) { printf("Failed to open pkt file <%s> for reading.\n", argv[1]); status = FAILED_TO_OPEN_PKTFILE; goto end_program; } if (fread(&byte_order, 1, sizeof(byte_order), pktfile) != sizeof(byte_order)) { printf("Failed to read byte order header from pkt file.\n"); status = BYTE_ORDER_MARK_MISSING; goto end_program; } if (byte_order != NATIVE_BYTE_ORDER && byte_order != SWAP_BYTE_ORDER) { printf("Invalid byte order header: 0x%08x (%u)\n", byte_order, byte_order); status = INVALID_BYTE_ORDER_MARK; goto end_program; } buffer = (char *)malloc(PACKET_BUFFER_SIZE); if (!buffer) { printf("Memory allocation failed\n"); status = MEMORY_ALLOCATION_FAILURE; goto end_program; } if (argc > 2) { output_file = fopen(argv[2], "wb"); if (!output_file) { printf("Failed to open <%s> for output.\n", argv[2]); status = FAILED_TO_OPEN_OUTPUT_FILE; goto end_program; } } while (fread(&packet_size, 1, sizeof(packet_size), pktfile) == sizeof(packet_size)) { if (byte_order == SWAP_BYTE_ORDER) packet_size = reverse_byte_order(packet_size); if (packet_size >= PACKET_BUFFER_SIZE) { printf("*** Packet size is bigger than buffer (%u > %u)\n", packet_size, PACKET_BUFFER_SIZE - 1); status = PACKET_TOO_BIG; goto end_program; } packet_read = fread(buffer, 1, packet_size, pktfile); if (output_file) { fwrite(buffer, 1, packet_size, output_file); } else { buffer[packet_size] = '\0'; printf("%s", buffer); getchar(); } if (packet_read != packet_size) printf("*** Invalid packet size (expected %u, got %u)\n", packet_size, packet_read); } end_program: if (pktfile) fclose(pktfile); if (output_file) fclose(output_file); if (buffer) free(buffer); return -status; } userland/containers/test/nb_io.h000066400000000000000000000041101421703157200172540ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef _NB_IO_H_ #define _NB_IO_H_ /** Set whether console input is non-blocking or not. * * \param enable Pass true to set input as non-blocking, false to set it as blocking again.*/ void nb_set_nonblocking_input(int enable); /** \return Non-zero when there is a character available to read using nb_get_char(). */ int nb_char_available(void); /** \return The next character available from the console, or zero. */ char nb_get_char(void); /** Put the character out to the console. * * \param ch The character to be output. */ void nb_put_char(char ch); #endif /* _NB_IO_H_ */ userland/containers/test/nb_io_unix.c000066400000000000000000000046541421703157200203270ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include #include "nb_io.h" void nb_set_nonblocking_input(int enable) { struct termios ttystate; //get the terminal state tcgetattr(STDIN_FILENO, &ttystate); if (enable) { //turn off canonical mode ttystate.c_lflag &= ~ICANON; //minimum of number input read. ttystate.c_cc[VMIN] = 1; } else { //turn on canonical mode ttystate.c_lflag |= ICANON; } //set the terminal attributes. tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); } int nb_char_available(void) { struct timeval tv; fd_set fds; tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&fds); FD_SET(STDIN_FILENO, &fds); select(STDIN_FILENO+1, &fds, NULL, NULL, &tv); return (FD_ISSET(0, &fds)); } char nb_get_char(void) { return getchar(); } void nb_put_char(char ch) { putchar(ch); } userland/containers/test/nb_io_win32.c000066400000000000000000000035341421703157200203020ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include /* For _kbhit() */ #include "nb_io.h" void nb_set_nonblocking_input(int enable) { /* No need to do anything in Win32 */ (void)enable; } int nb_char_available() { return _kbhit(); } char nb_get_char() { char c = _getch(); _putch(c); /* Echo back */ return c; } void nb_put_char(char ch) { _putch(ch); } userland/containers/test/rtp_decoder.c000066400000000000000000000316521421703157200204660ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include "containers/containers.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_io.h" #include "nb_io.h" #define MAXIMUM_BUFFER_SIZE 65000 #define MINIMUM_BUFFER_SPACE 1500 #define INITIAL_READ_BUFFER_SIZE 8000 #define MAXIMUM_READ_BUFFER_SIZE 64000 #define BYTES_PER_ROW 32 #define HAS_PADDING 0x20 #define HAS_EXTENSION 0x10 #define CSRC_COUNT_MASK 0x0F #define HAS_MARKER 0x80 #define PAYLOAD_TYPE_MASK 0x7F #define EXTENSION_LENGTH_MASK 0x0000FFFF #define EXTENSION_ID_SHIFT 16 #define LOWEST_VERBOSITY 1 #define BASIC_HEADER_VERBOSITY 2 #define FULL_HEADER_VERBOSITY 3 #define FULL_PACKET_VERBOSITY 4 #define ESCAPE_CHARACTER 0x1B static bool seen_first_packet; static uint16_t expected_next_seq_num; static bool do_print_usage; static uint32_t verbosity; static const char *read_uri; static const char *packet_save_file; static bool packet_save_is_pktfile; static uint16_t network_to_host_16(const uint8_t *buffer) { return (buffer[0] << 8) | buffer[1]; } static uint32_t network_to_host_32(const uint8_t *buffer) { return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; } /** Avoid alignment problems when writing a word value to the buffer */ static void store_u32(uint8_t *buffer, uint32_t value) { buffer[0] = (uint8_t)value; buffer[1] = (uint8_t)(value >> 8); buffer[2] = (uint8_t)(value >> 16); buffer[3] = (uint8_t)(value >> 24); } /** Avoid alignment problems when reading a word value from the buffer */ static uint32_t fetch_u32(uint8_t *buffer) { return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0]; } static bool marker_bit_set(const uint8_t *buffer, size_t buffer_len) { if (buffer_len < 2) return false; return (buffer[1] & HAS_MARKER); } static void dump_bytes(const uint8_t *buffer, size_t buffer_len) { char dump_str[3 * BYTES_PER_ROW + 1]; int in_row = 0; while (buffer_len--) { sprintf(dump_str + 3 * in_row, "%2.2X ", *buffer++); if (++in_row == BYTES_PER_ROW) { LOG_INFO(NULL, dump_str); in_row = 0; } } if (in_row) { LOG_INFO(NULL, dump_str); } } static bool decode_packet(const uint8_t *buffer, size_t buffer_len) { uint8_t flags; uint8_t payload_type; uint16_t seq_num; uint32_t timestamp; uint32_t ssrc; uint32_t csrc_count; if (buffer_len < 12) { LOG_ERROR(NULL, "Packet too small: basic header missing"); return false; } flags = buffer[0]; payload_type = buffer[1]; seq_num = network_to_host_16(buffer + 2); timestamp = network_to_host_32(buffer + 4); ssrc = network_to_host_32(buffer + 8); if (seen_first_packet && seq_num != expected_next_seq_num) { int16_t missing_packets = seq_num - expected_next_seq_num; LOG_INFO(NULL, "*** Sequence break, expected %hu, got %hu ***", expected_next_seq_num, seq_num); if (missing_packets > 0) LOG_INFO(NULL, "*** Jumped forward %hd packets ***", missing_packets); else LOG_INFO(NULL, "*** Jumped backward %hd packets ***", -missing_packets); } seen_first_packet = true; expected_next_seq_num = seq_num + 1; /* Dump the basic header information */ if (verbosity >= BASIC_HEADER_VERBOSITY) { LOG_INFO(NULL, "Version: %d\nPayload type: %d%s\nSequence: %d\nTimestamp: %u\nSSRC: 0x%8.8X", flags >> 6, payload_type & PAYLOAD_TYPE_MASK, (const char *)((payload_type & HAS_MARKER) ? " (M)" : ""), seq_num, timestamp, ssrc); } buffer += 12; buffer_len -= 12; if (verbosity >= FULL_HEADER_VERBOSITY) { /* Dump the CSRCs, if any */ csrc_count = flags & CSRC_COUNT_MASK; if (csrc_count) { uint32_t ii; if (buffer_len < (csrc_count * 4)) { LOG_ERROR(NULL, "Packet too small: CSRCs missing"); return false; } LOG_INFO(NULL, "CSRCs:"); for (ii = 0; ii < csrc_count; ii++) { LOG_INFO(NULL, " 0x%8.8X", network_to_host_32(buffer)); buffer += 4; buffer_len -= 4; } } /* Dump any extension, if present */ if (flags & HAS_EXTENSION) { uint32_t extension_hdr; uint32_t extension_id; size_t extension_len; if (buffer_len < 4) { LOG_ERROR(NULL, "Packet too small: extension header missing"); return false; } extension_hdr = network_to_host_32(buffer); buffer += 4; buffer_len -= 4; extension_len = (size_t)(extension_hdr & EXTENSION_LENGTH_MASK); extension_id = extension_hdr >> EXTENSION_ID_SHIFT; if (buffer_len < extension_len) { LOG_ERROR(NULL, "Packet too small: extension content missing"); return false; } LOG_INFO(NULL, "Extension: 0x%4.4X (%u bytes)", extension_id, (unsigned)extension_len); dump_bytes(buffer, extension_len); buffer += extension_len; buffer_len -= extension_len; } } /* And finally the payload data */ if (verbosity >= FULL_PACKET_VERBOSITY) { LOG_INFO(NULL, "Data: (%u bytes)", (unsigned)buffer_len); dump_bytes(buffer, buffer_len); } return true; } static void increase_read_buffer_size(VC_CONTAINER_IO_T *p_ctx) { uint32_t buffer_size = INITIAL_READ_BUFFER_SIZE; /* Iteratively enlarge read buffer until either operation fails or maximum is reached. */ while (vc_container_io_control(p_ctx, VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE, buffer_size) == VC_CONTAINER_SUCCESS) { buffer_size <<= 1; /* Double and try again */ if (buffer_size > MAXIMUM_READ_BUFFER_SIZE) break; } } static void parse_command_line(int argc, char **argv) { int arg = 1; while (arg < argc) { if (*argv[arg] != '-') /* End of options, next should be URI */ break; switch (argv[arg][1]) { case 'h': do_print_usage = true; break; case 's': arg++; if (arg >= argc) break; packet_save_file = argv[arg]; packet_save_is_pktfile = (strncmp(packet_save_file, "pktfile:", 8) == 0); break; case 'v': { const char *ptr = &argv[arg][2]; verbosity = 1; while (*ptr++ == 'v') verbosity++; } break; default: LOG_ERROR(NULL, "Unrecognised option: %s", argv[arg]); return; } arg++; } if (arg < argc) read_uri = argv[arg]; } static void print_usage(char *program_name) { LOG_INFO(NULL, "Usage:"); LOG_INFO(NULL, " %s [opts] ", program_name); LOG_INFO(NULL, "Reads RTP packets from , decodes to standard output."); LOG_INFO(NULL, "Press the escape key to terminate the program."); LOG_INFO(NULL, "Options:"); LOG_INFO(NULL, " -h Print this information"); LOG_INFO(NULL, " -s x Save packets to URI x"); LOG_INFO(NULL, " -v Dump standard packet header"); LOG_INFO(NULL, " -vv Dump entire header"); LOG_INFO(NULL, " -vvv Dump entire header and data"); } int main(int argc, char **argv) { int result = 0; uint8_t *buffer = NULL; VC_CONTAINER_IO_T *read_io = NULL; VC_CONTAINER_IO_T *write_io = NULL; VC_CONTAINER_STATUS_T status; size_t received_bytes; bool ready = true; uint32_t available_bytes; uint8_t *packet_ptr; parse_command_line(argc, argv); if (do_print_usage || !read_uri) { print_usage(argv[0]); result = 1; goto tidyup; } buffer = (uint8_t *)malloc(MAXIMUM_BUFFER_SIZE); if (!buffer) { LOG_ERROR(NULL, "Allocating %d bytes for the buffer failed", MAXIMUM_BUFFER_SIZE); result = 2; goto tidyup; } read_io = vc_container_io_open(read_uri, VC_CONTAINER_IO_MODE_READ, &status); if (!read_io) { LOG_ERROR(NULL, "Opening <%s> for read failed: %d", read_uri, status); result = 3; goto tidyup; } increase_read_buffer_size(read_io); if (packet_save_file) { write_io = vc_container_io_open(packet_save_file, VC_CONTAINER_IO_MODE_WRITE, &status); if (!write_io) { LOG_ERROR(NULL, "Opening <%s> for write failed: %d", packet_save_file, status); result = 4; goto tidyup; } if (!packet_save_is_pktfile) { store_u32(buffer, 0x50415753); vc_container_io_write(write_io, buffer, sizeof(uint32_t)); } } /* Use non-blocking I/O for both network and console */ vc_container_io_control(read_io, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, 20); nb_set_nonblocking_input(1); packet_ptr = buffer; available_bytes = MAXIMUM_BUFFER_SIZE - sizeof(uint32_t); while (ready) { /* Read a packet and store its length in the word before it */ received_bytes = vc_container_io_read(read_io, packet_ptr + sizeof(uint32_t), available_bytes); if (received_bytes) { bool packet_has_marker; store_u32(packet_ptr, received_bytes); packet_ptr += sizeof(uint32_t); packet_has_marker = marker_bit_set(packet_ptr, received_bytes); packet_ptr += received_bytes; available_bytes -= received_bytes + sizeof(uint32_t); if (packet_has_marker || (available_bytes < MINIMUM_BUFFER_SPACE)) { uint8_t *decode_ptr; if (write_io && !packet_save_is_pktfile) { uint32_t total_bytes = packet_ptr - buffer; if (vc_container_io_write(write_io, buffer, total_bytes) != total_bytes) { LOG_ERROR(NULL, "Error saving packets to file"); break; } if (verbosity >= LOWEST_VERBOSITY) LOG_INFO(NULL, "Written %u bytes to file", total_bytes); } for (decode_ptr = buffer; decode_ptr < packet_ptr;) { received_bytes = fetch_u32(decode_ptr); decode_ptr += sizeof(uint32_t); if (write_io && packet_save_is_pktfile) { if (vc_container_io_write(write_io, buffer, received_bytes) != received_bytes) { LOG_ERROR(NULL, "Error saving packets to file"); break; } if (verbosity >= LOWEST_VERBOSITY) LOG_INFO(NULL, "Written %u bytes to file", received_bytes); } if (!decode_packet(decode_ptr, received_bytes)) { LOG_ERROR(NULL, "Failed to decode packet"); break; } decode_ptr += received_bytes; } /* Reset to start of buffer */ packet_ptr = buffer; available_bytes = MAXIMUM_BUFFER_SIZE - sizeof(uint32_t); } } if (nb_char_available()) { if (nb_get_char() == ESCAPE_CHARACTER) ready = false; } switch (read_io->status) { case VC_CONTAINER_SUCCESS: case VC_CONTAINER_ERROR_CONTINUE: break; default: ready = false; } } nb_set_nonblocking_input(0); tidyup: if (write_io) vc_container_io_close(write_io); if (read_io) vc_container_io_close(read_io); if (buffer) free(buffer); return result; } userland/containers/test/stream_client.c000066400000000000000000000106621421703157200210230ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include "containers/net/net_sockets.h" #include "nb_io.h" #define MAX_BUFFER_LEN 1024 /* Time in milliseconds to yield when nothing is happening */ #define YIELD_PERIOD_MS 33 static vc_container_net_status_t local_net_control(VC_CONTAINER_NET_T *sock, vc_container_net_control_t operation, ...) { vc_container_net_status_t result; va_list args; va_start(args, operation); result = vc_container_net_control(sock, operation, args); va_end(args); return result; } int main(int argc, char **argv) { VC_CONTAINER_NET_T *sock; vc_container_net_status_t status; char send_buffer[MAX_BUFFER_LEN]; char recv_buffer[MAX_BUFFER_LEN]; int ready = 1; int to_send = 0; size_t received; if (argc < 3) { printf("Usage:\n%s
\n", argv[0]); return 1; } sock = vc_container_net_open(argv[1], argv[2], VC_CONTAINER_NET_OPEN_FLAG_STREAM, &status); if (!sock) { printf("vc_container_net_open failed: %d\n", status); return 2; } /* Use non-blocking I/O for both network and console */ local_net_control(sock, VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS, YIELD_PERIOD_MS); nb_set_nonblocking_input(1); while (ready) { if (nb_char_available()) { char c = nb_get_char(); if (c == 26) /* CTRL+Z */ break; send_buffer[to_send++] = c; if (c == '\r') /* Translate CR into CRLF */ { c = '\n'; nb_put_char(c); send_buffer[to_send++] = c; } if (c == '\n' || to_send == sizeof(send_buffer) - 1) /* Allow for next character needing translation */ { int already_sent = 0, sent; /* Send a line at a time */ while (to_send) { sent = vc_container_net_write(sock, send_buffer + already_sent, to_send); if (!sent) { printf("vc_container_net_write failed: %d\n", vc_container_net_status(sock)); to_send = 0; ready = 0; break; } to_send -= sent; already_sent += sent; } } } /* Read back and print any data from the server */ received = vc_container_net_read(sock, recv_buffer, sizeof(recv_buffer) - 1); if (received) { char *ptr = recv_buffer; while (received--) nb_put_char(*ptr++); } else { vc_container_net_status_t net_status = vc_container_net_status(sock); if (net_status != VC_CONTAINER_NET_ERROR_TIMED_OUT && net_status != VC_CONTAINER_NET_SUCCESS) { printf("vc_container_net_read failed: %d\n", net_status); ready = 0; } } } nb_set_nonblocking_input(0); vc_container_net_close(sock); return 0; } userland/containers/test/stream_server.c000066400000000000000000000106651421703157200210560ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include #include "containers/net/net_sockets.h" #define MAX_BUFFER_LEN 1024 #define MAX_NAME_LEN 256 #define MAX_PORT_LEN 32 int main(int argc, char **argv) { VC_CONTAINER_NET_T *server_sock, *sock; vc_container_net_status_t status; char buffer[MAX_BUFFER_LEN]; char name[MAX_NAME_LEN]; unsigned short port; int ii, connections = 1; size_t received; if (argc < 2) { printf("Usage:\n%s []\n", argv[0]); return 1; } server_sock = vc_container_net_open(NULL, argv[1], VC_CONTAINER_NET_OPEN_FLAG_STREAM, &status); if (!server_sock) { printf("vc_container_net_open failed: %d\n", status); return 2; } if (argc > 2) { sscanf(argv[2], "%d", &connections); } status = vc_container_net_listen(server_sock, connections); if (status != VC_CONTAINER_NET_SUCCESS) { printf("vc_container_net_listen failed: %d\n", status); vc_container_net_close(server_sock); return 3; } for (ii = 0; ii < connections; ii++) { status = vc_container_net_accept(server_sock, &sock); if (status != VC_CONTAINER_NET_SUCCESS) { printf("vc_container_net_accept failed: %d\n", status); vc_container_net_close(server_sock); return 3; } strcpy(name, ""); vc_container_net_get_client_name(sock, name, sizeof(name)); vc_container_net_get_client_port(sock, &port); printf("Connection from %s:%hu\n", name, port); while ((received = vc_container_net_read(sock, buffer, sizeof(buffer) - 1)) != 0) { char *ptr = buffer; size_t jj; printf("Rx:"); /* Flip case and echo data back to client */ for (jj = 0; jj < received; jj++, ptr++) { char c = *ptr; putchar(c); if (isalpha((unsigned char)c)) *ptr ^= 0x20; /* Swaps case of ASCII alphabetic characters */ } ptr = buffer; printf("Tx:"); while (received) { size_t sent; sent = vc_container_net_write(sock, ptr, received); if (!sent) { status = vc_container_net_status(sock); printf("vc_container_net_write failed: %d\n", status); break; } /* Print out bytes actually sent */ while (sent--) { received--; putchar(*ptr++); } } } status = vc_container_net_status(sock); if (status != VC_CONTAINER_NET_SUCCESS && status != VC_CONTAINER_NET_ERROR_CONNECTION_LOST) break; vc_container_net_close(sock); } if (status != VC_CONTAINER_NET_SUCCESS) { printf("vc_container_net_read failed: %d\n", status); } vc_container_net_close(server_sock); return 0; } userland/containers/test/test.c000066400000000000000000000526371421703157200171610ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include "containers/containers.h" #include "containers/core/containers_common.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_io.h" #define BUFFER_SIZE 256*1024 #define MAX_TRACKS 16 #define MAX_SEEKS 16 static int container_test_info(VC_CONTAINER_T *ctx, bool b_reader); static int container_test_parse_cmdline(int argc, char **argv); /* Client i/o wrapper */ static VC_CONTAINER_IO_T *client_io_open(const char *, VC_CONTAINER_STATUS_T *); static bool b_info = 0, b_seek = 0, b_dump = 0; static bool b_audio = 1, b_video = 1, b_subs = 1, b_errorcode = 1; static const char *psz_in = 0, *psz_out = 0; static long packets_num = 0; static long track_num = -1; static FILE *dump_file = 0; static bool b_client_io = 0; static bool b_packetize = 0; static struct { uint32_t mapping; uint32_t frames; uint32_t packets; uint64_t bytes; uint32_t frame_size; int64_t first_dts; int64_t first_pts; int64_t last_dts; int64_t last_pts; } tracks[MAX_TRACKS]; static unsigned int seeks = 0; static long seek_offsets[MAX_SEEKS]; static long seek_flags[MAX_SEEKS]; static int32_t verbosity = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO; static int32_t verbosity_input = -1, verbosity_output = -1; /*****************************************************************************/ int main(int argc, char **argv) { int retval = 0; VC_CONTAINER_T *p_ctx = 0, *p_writer_ctx = 0; VC_CONTAINER_STATUS_T status; unsigned int i, j; uint8_t *buffer = malloc(BUFFER_SIZE); int64_t seek_time; if(container_test_parse_cmdline(argc, argv)) goto error_silent; /* Set the general verbosity */ vc_container_log_set_verbosity(0, verbosity); if(verbosity_input < 0) verbosity_input = verbosity; if(verbosity_output < 0) verbosity_output = verbosity; /* Open a dump file if it was requested */ if(b_dump) { char *psz_dump; if (psz_out) { psz_dump = vcos_strdup(psz_out); } else { psz_dump = strrchr(psz_in, '\\'); if(psz_dump) psz_dump++; if(!psz_dump) {psz_dump = strrchr(psz_in, '/'); if(psz_dump) psz_dump++;} if(!psz_dump) psz_dump = vcos_strdup(psz_in); else psz_dump = vcos_strdup(psz_dump); psz_dump[strlen(psz_dump)-1] = '1'; } dump_file = fopen(psz_dump, "wb"); if(!dump_file) LOG_ERROR(0, "error opening dump file %s", psz_dump); else LOG_INFO(0, "data packets will dumped to %s", psz_dump); free(psz_dump); if(!dump_file) goto error; } /* Open a writer if an output was requested */ if(psz_out && !b_dump) { vc_container_log_set_default_verbosity(verbosity_output); p_writer_ctx = vc_container_open_writer(psz_out, &status, 0, 0); if(!p_writer_ctx) { LOG_ERROR(0, "error opening file %s (%i)", psz_out, status); goto error; } } vc_container_log_set_default_verbosity(verbosity_input); /* Open the container */ if(b_client_io) { VC_CONTAINER_IO_T *p_io; LOG_INFO(0, "Using client I/O for %s", psz_in); p_io = client_io_open(psz_in, &status); if(!p_io) { LOG_ERROR(0, "error creating io for %s (%i)", psz_in, status); goto error; } p_ctx = vc_container_open_reader_with_io(p_io, psz_in, &status, 0, 0); if(!p_ctx) vc_container_io_close(p_io); } else { p_ctx = vc_container_open_reader(psz_in, &status, 0, 0); } if(!p_ctx) { LOG_ERROR(0, "error opening file %s (%i)", psz_in, status); goto error; } /* Disabling tracks which are not requested and enable packetisation if requested */ for(i = 0; i < p_ctx->tracks_num; i++) { VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i]; unsigned int disable = 0; switch(track->format->es_type) { case VC_CONTAINER_ES_TYPE_VIDEO: if(!b_video) disable = 1; break; case VC_CONTAINER_ES_TYPE_AUDIO: if(!b_audio) disable = 1; break; case VC_CONTAINER_ES_TYPE_SUBPICTURE: if(!b_subs) disable = 1; break; default: break; } if(disable) { track->is_enabled = 0; LOG_INFO(0, "disabling track: %i, fourcc: %4.4s", i, (char *)&track->format->codec); } if(track->is_enabled && b_packetize && !(track->format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) { status = vc_container_control(p_ctx, VC_CONTAINER_CONTROL_TRACK_PACKETIZE, i, track->format->codec_variant); if(status != VC_CONTAINER_SUCCESS) { LOG_ERROR(0, "packetization not supported on track: %i, fourcc: %4.4s", i, (char *)&track->format->codec); track->is_enabled = 0; } } } container_test_info(p_ctx, true); if(b_info) goto end; if(p_writer_ctx) { LOG_INFO(0, "----Writer Information----"); for(i = 0; i < p_ctx->tracks_num; i++) { VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i]; if(!track->is_enabled) continue; tracks[p_writer_ctx->tracks_num].mapping = i; LOG_INFO(0, "adding track: %i, fourcc: %4.4s", i, (char *)&track->format->codec); status = vc_container_control(p_writer_ctx, VC_CONTAINER_CONTROL_TRACK_ADD, track->format); if(status) { LOG_INFO(0, "unsupported track type (%i, %i)", status, i); track->is_enabled = 0; /* disable track */ } } if(p_writer_ctx->tracks_num) { status = vc_container_control(p_writer_ctx, VC_CONTAINER_CONTROL_TRACK_ADD_DONE); if(status) LOG_INFO(0, "could not add tracks (%i)", status); } LOG_INFO(0, "--------------------------"); LOG_INFO(0, ""); } for(i = 0; i < seeks; i++) { LOG_DEBUG(0, "TEST seek to %ims", seek_offsets[i]); seek_time = ((int64_t)(seek_offsets[i])) * 1000; status = vc_container_seek(p_ctx, &seek_time, VC_CONTAINER_SEEK_MODE_TIME, seek_flags[i]); LOG_DEBUG(0, "TEST seek done (%i) to %ims", status, (int)(seek_time/1000)); } LOG_DEBUG(0, "TEST start reading"); for(i = 0; !packets_num || (long)i < packets_num; i++) { VC_CONTAINER_PACKET_T packet = {0}; int32_t frame_num = 0; status = vc_container_read(p_ctx, &packet, VC_CONTAINER_READ_FLAG_INFO); if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST info status: %i", status); break;} if(packet.track < MAX_TRACKS) { if((packet.flags & VC_CONTAINER_PACKET_FLAG_FRAME_START)) { tracks[packet.track].frames++; tracks[packet.track].frame_size = 0; } frame_num = tracks[packet.track].frames; } tracks[packet.track].frame_size += packet.size; LOG_DEBUG(0, "packet info: track %i, size %i/%i/%i, pts %"PRId64", flags %x%s, num %i", packet.track, packet.size, packet.frame_size, tracks[packet.track].frame_size, packet.pts, packet.flags, (packet.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) ? " (keyframe)" : "", frame_num-1); if(track_num >= 0 && packet.track != (uint32_t)track_num) { status = vc_container_read(p_ctx, 0, VC_CONTAINER_READ_FLAG_SKIP); if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST skip status: %i", status); break;} continue; } packet.buffer_size = BUFFER_SIZE; packet.data = buffer; packet.size = 0; status = vc_container_read(p_ctx, &packet, 0); if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST read status: %i", status); break;} LOG_DEBUG(0, "packet: track %i, size %i, pts %"PRId64", flags %x", packet.track, packet.size, packet.pts, packet.flags); if (tracks[packet.track].packets) { tracks[packet.track].last_dts = packet.dts; tracks[packet.track].last_pts = packet.pts; } else { tracks[packet.track].first_dts = packet.dts; tracks[packet.track].first_pts = packet.pts; } if(packet.track < MAX_TRACKS) { tracks[packet.track].packets++; tracks[packet.track].bytes += packet.size; } if(dump_file) fwrite(packet.data, packet.size, 1, dump_file); if(p_writer_ctx) { packet.track = tracks[packet.track].mapping; status = vc_container_write(p_writer_ctx, &packet); if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST write status: %i", status); break;} } } LOG_DEBUG(0, "TEST stop reading"); if(b_seek) { LOG_DEBUG(0, "TEST start seeking"); for(j = 0, seek_time = 100; j < 20; j++) { LOG_DEBUG(0, "seeking to %ims", (int)(seek_time/1000)); status = vc_container_seek(p_ctx, &seek_time, VC_CONTAINER_SEEK_MODE_TIME, VC_CONTAINER_SEEK_FLAG_FORWARD); LOG_DEBUG(0, "seek done (%i) to %ims", status, (int)(seek_time/1000)); for(i = 0; i < 1; i++) { VC_CONTAINER_PACKET_T packet = {0}; packet.buffer_size = BUFFER_SIZE; packet.data = buffer; status = vc_container_read(p_ctx, &packet, 0); if(status) LOG_DEBUG(0, "TEST read status: %i", status); if(status == VC_CONTAINER_ERROR_EOS) break; if(status == VC_CONTAINER_ERROR_CORRUPTED) break; if(status == VC_CONTAINER_ERROR_FORMAT_INVALID) break; seek_time = packet.pts + 800000; } } LOG_DEBUG(0, "TEST stop seeking"); } /* Output stats */ for(i = 0; i < p_ctx->tracks_num; i++) { LOG_INFO(0, "track %u: read %u samples in %u packets for a total of %"PRIu64" bytes", i, tracks[i].frames, tracks[i].packets, tracks[i].bytes); LOG_INFO(0, "Starting at %"PRId64"us (decode at %"PRId64"us), ending at %"PRId64"us (decode at %"PRId64"us)", tracks[i].first_pts, tracks[i].first_dts, tracks[i].last_pts, tracks[i].last_dts); } end: if(p_ctx) vc_container_close(p_ctx); if(p_writer_ctx) { container_test_info(p_writer_ctx, false); vc_container_close(p_writer_ctx); } if(dump_file) fclose(dump_file); free(buffer); #ifdef _MSC_VER getchar(); #endif LOG_ERROR(0, "TEST ENDED (%i)", retval); return b_errorcode ? retval : 0; error: LOG_ERROR(0, "TEST FAILED"); error_silent: retval = -1; goto end; } static int container_test_parse_cmdline(int argc, char **argv) { int i, j, k; int32_t *p_verbosity; /* Parse the command line arguments */ for(i = 1; i < argc; i++) { if(!argv[i]) continue; if(argv[i][0] != '-') { /* Not an option argument so will be the input URI */ psz_in = argv[i]; continue; } /* We are now dealing with command line options */ switch(argv[i][1]) { case 'i': b_info = 1; break; case 'S': b_seek = 1; break; case 'd': b_dump = 1; break; case 'c': b_client_io = 1; break; case 'v': if(argv[i][2] == 'i') {j = 3; p_verbosity = &verbosity_input;} else if(argv[i][2] == 'o') {j = 3; p_verbosity = &verbosity_output;} else {j = 2; p_verbosity = &verbosity;} *p_verbosity = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO; for(k = 0; k < 2 && argv[i][j+k] == 'v'; k++) *p_verbosity = (*p_verbosity << 1) | 1 ; break; case 's': if(i+1 == argc || !argv[i+1]) goto invalid_option; if(seeks >= MAX_SEEKS) goto invalid_option; seek_flags[seeks] = argv[i][2] == 'f' ? VC_CONTAINER_SEEK_FLAG_FORWARD : 0; seek_offsets[seeks] = strtol(argv[++i], 0, 0); if(seek_offsets[seeks] < 0 || seek_offsets[seeks] == LONG_MAX) goto invalid_option; seeks++; break; case 'n': if(argv[i][2] == 'a') b_audio = 0; else if(argv[i][2] == 'v') b_video = 0; else if(argv[i][2] == 's') b_subs = 0; else if(argv[i][2] == 'r') b_errorcode = 0; else goto invalid_option; break; case 'e': if(argv[i][2] == 'p') b_packetize = 1; else goto invalid_option; break; case 'o': if(i+1 == argc || !argv[i+1] || argv[i+1][0] == '-') goto invalid_option; psz_out = argv[++i]; break; case 'p': if(i+1 == argc || !argv[i+1]) goto invalid_option; packets_num = strtol(argv[++i], 0, 0); if(packets_num < 0 || packets_num == LONG_MAX) goto invalid_option; break; case 't': if(i+1 == argc || !argv[i+1]) goto invalid_option; track_num = strtol(argv[++i], 0, 0); if(track_num == LONG_MIN || track_num == LONG_MAX) goto invalid_option; break; case 'h': goto usage; default: goto invalid_option; } continue; } /* Sanity check that we have at least an input uri */ if(!psz_in) { LOG_ERROR(0, "missing uri argument"); goto usage; } return 0; invalid_option: LOG_ERROR(0, "invalid command line option (%s)", argv[i]); usage: psz_in = strrchr(argv[0], '\\'); if(psz_in) psz_in++; if(!psz_in) {psz_in = strrchr(argv[0], '/'); if(psz_in) psz_in++;} if(!psz_in) psz_in = argv[0]; LOG_INFO(0, ""); LOG_INFO(0, "usage: %s [options] uri", psz_in); LOG_INFO(0, "options list:"); LOG_INFO(0, " -i : only print information on the container"); LOG_INFO(0, " -p X : read only X packets from the container"); LOG_INFO(0, " -t X : read only packets from track X"); LOG_INFO(0, " -s X : seek to X milliseconds before starting reading"); LOG_INFO(0, " -sf X : seek forward to X milliseconds before starting reading"); LOG_INFO(0, " -S : do seek testing"); LOG_INFO(0, " -d : dump the data read from the container to files (-o to name file)"); LOG_INFO(0, " -o uri: output to another uri (i.e. re-muxing)"); LOG_INFO(0, " -na : disable audio"); LOG_INFO(0, " -nv : disable video"); LOG_INFO(0, " -ns : disable subtitles"); LOG_INFO(0, " -nr : always return an error code of 0 (even in case of failure)"); LOG_INFO(0, " -ep : enable packetization if data is not already packetized"); LOG_INFO(0, " -c : use the client i/o functions"); LOG_INFO(0, " -vxx : general verbosity level (replace xx with a number of \'v\')"); LOG_INFO(0, " -vixx : verbosity specific to the input container"); LOG_INFO(0, " -voxx : verbosity specific to the output container"); LOG_INFO(0, " -h : help"); return 1; } static int container_test_info(VC_CONTAINER_T *ctx, bool b_reader) { const char *name_type; unsigned int i; LOG_INFO(0, ""); if(b_reader) LOG_INFO(0, "----Reader Information----"); else LOG_INFO(0, "----Writer Information----"); LOG_INFO(0, "duration: %2.2fs, size: %"PRId64, ctx->duration/1000000.0, ctx->size); LOG_INFO(0, "capabilities: %x", ctx->capabilities); LOG_INFO(0, ""); for(i = 0; i < ctx->tracks_num; i++) { VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; switch(track->format->es_type) { case VC_CONTAINER_ES_TYPE_AUDIO: name_type = "audio"; break; case VC_CONTAINER_ES_TYPE_VIDEO: name_type = "video"; break; case VC_CONTAINER_ES_TYPE_SUBPICTURE: name_type = "subpicture"; break; default: name_type = "unknown"; break; } LOG_INFO(0, "track: %i, type: %s, fourcc: %4.4s", i, name_type, (char *)&track->format->codec); LOG_INFO(0, " bitrate: %i, framed: %i, enabled: %i", track->format->bitrate, !!(track->format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED), track->is_enabled); LOG_INFO(0, " extra data: %i, %p", track->format->extradata_size, track->format->extradata); switch(track->format->es_type) { case VC_CONTAINER_ES_TYPE_AUDIO: LOG_INFO(0, " samplerate: %i, channels: %i, bps: %i, block align: %i", track->format->type->audio.sample_rate, track->format->type->audio.channels, track->format->type->audio.bits_per_sample, track->format->type->audio.block_align); LOG_INFO(0, " gapless delay: %i gapless padding: %i", track->format->type->audio.gap_delay, track->format->type->audio.gap_padding); LOG_INFO(0, " language: %4.4s", track->format->language); break; case VC_CONTAINER_ES_TYPE_VIDEO: LOG_INFO(0, " width: %i, height: %i, (%i,%i,%i,%i)", track->format->type->video.width, track->format->type->video.height, track->format->type->video.x_offset, track->format->type->video.y_offset, track->format->type->video.visible_width, track->format->type->video.visible_height); LOG_INFO(0, " pixel aspect ratio: %i/%i, frame rate: %i/%i", track->format->type->video.par_num, track->format->type->video.par_den, track->format->type->video.frame_rate_num, track->format->type->video.frame_rate_den); break; case VC_CONTAINER_ES_TYPE_SUBPICTURE: LOG_INFO(0, " language: %4.4s, encoding: %i", track->format->language, track->format->type->subpicture.encoding); break; default: break; } } for (i = 0; i < ctx->meta_num; ++i) { const char *name, *value; if (i == 0) LOG_INFO(0, ""); name = vc_container_metadata_id_to_string(ctx->meta[i]->key); value = ctx->meta[i]->value; if(!name) continue; LOG_INFO(0, "metadata(%i) : %s : %s", i, name, value); } LOG_INFO(0, "--------------------------"); LOG_INFO(0, ""); return 0; } /***************************************************************************** * Client I/O wrapper. Only used when the right cmd line option is passed. *****************************************************************************/ static VC_CONTAINER_STATUS_T client_io_close( VC_CONTAINER_IO_T *p_ctx ) { FILE *fd = (FILE *)p_ctx->module; fclose(fd); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ static size_t client_io_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) { FILE *fd = (FILE *)p_ctx->module; size_t ret = fread(buffer, 1, size, fd); if(ret != size) { /* Sanity check return value. Some platforms (e.g. Android) can return -1 */ if( ((int)ret) < 0 ) ret = 0; if( feof(fd) ) p_ctx->status = VC_CONTAINER_ERROR_EOS; else p_ctx->status = VC_CONTAINER_ERROR_FAILED; } LOG_DEBUG( 0, "read: %i", ret ); return ret; } /*****************************************************************************/ static VC_CONTAINER_STATUS_T client_io_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset) { VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; FILE *fd = (FILE *)p_ctx->module; int ret; //FIXME: no large file support if (offset > (int64_t)UINT_MAX) { LOG_ERROR( 0, "no large file support"); p_ctx->status = VC_CONTAINER_ERROR_EOS; return VC_CONTAINER_ERROR_EOS; } ret = fseek(fd, (long)offset, SEEK_SET); if(ret) { if( feof(fd) ) status = VC_CONTAINER_ERROR_EOS; else status = VC_CONTAINER_ERROR_FAILED; } p_ctx->status = status; return status; } /*****************************************************************************/ static VC_CONTAINER_IO_T *client_io_open( const char *psz_uri, VC_CONTAINER_STATUS_T *status ) { VC_CONTAINER_IO_T *p_io; VC_CONTAINER_IO_CAPABILITIES_T capabilities = VC_CONTAINER_IO_CAPS_NO_CACHING; FILE *fd; fd = fopen(psz_uri, "rb"); if (!fd) { *status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; return NULL; } p_io = vc_container_io_create( psz_uri, 0, capabilities, status ); if(!p_io) { LOG_ERROR(0, "error creating io (%i)", *status); fclose(fd); return NULL; } p_io->module = (struct VC_CONTAINER_IO_MODULE_T *)fd; p_io->pf_close = client_io_close; p_io->pf_read = client_io_read; p_io->pf_seek = client_io_seek; //FIXME: no large file support fseek(fd, 0, SEEK_END); p_io->size = ftell(fd); fseek(fd, 0, SEEK_SET); *status = VC_CONTAINER_SUCCESS; return p_io; } userland/containers/test/test_bits.c000066400000000000000000000431461421703157200201750ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #define BITS_LOG_INDENT(ctx) indent_level #include "containers/containers.h" #include "containers/core/containers_common.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_bits.h" uint32_t indent_level; /** Bit stream containing the values 0 to 10, with each value in that many bits. * At the end there is one further zero bit before the end of the stream. */ static uint8_t bits_0_to_10[] = { 0xCD, 0x0A, 0x30, 0x70, 0x80, 0x48, 0x14 }; /** Bit stream containing the values 0 to 10, encoded using Exp-Golomb. * At the end there is one further one bit before the end of the stream. */ static uint8_t exp_golomb_0_to_10[] = { 0xA6, 0x42, 0x98, 0xE2, 0x04, 0x8A, 0x17 }; /** Array of signed values for the Exp-Golomb encoding of each index. */ static int32_t exp_golomb_values[] = { 0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5 }; /** Bit stream containing two large 32-bit values, encoded using Exp-Golomb. */ static uint8_t exp_golomb_large[] = { 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 }; /** Bit stream containing a 33-bit value, encoded using Exp-Golomb. */ static uint8_t exp_golomb_oversize[] = { 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80 }; static const char *plural_ext(uint32_t val) { return (val == 1) ? "" : "s"; } static int test_reset_and_available(void) { VC_CONTAINER_BITS_T bit_stream; int error_count = 0; LOG_DEBUG(NULL, "Testing vc_container_bits_reset and vc_container_bits_available"); BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); if (!BITS_AVAILABLE(NULL, &bit_stream)) { LOG_ERROR(NULL, "Expected initialised stream to contain bits"); error_count++; } BITS_RESET(NULL, &bit_stream); if (BITS_AVAILABLE(NULL, &bit_stream)) { LOG_ERROR(NULL, "Expected reset stream not to contain bits"); error_count++; } return error_count; } static int test_read_u32(void) { VC_CONTAINER_BITS_T bit_stream; uint32_t ii, value; int error_count = 0; LOG_DEBUG(NULL, "Testing vc_container_bits_get_u32"); BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); for (ii = 0; ii < 11; ii++) { value = BITS_READ_U32(NULL, &bit_stream, ii, "test_read_u32"); if (value != ii) { LOG_ERROR(NULL, "Expected %u, got %u", ii, value); error_count++; } } value = BITS_READ_U32(NULL, &bit_stream, 1, "Final bit"); if (!BITS_VALID(NULL, &bit_stream) || value) { LOG_ERROR(NULL, "Failed to get final bit"); error_count++; } value = BITS_READ_U32(NULL, &bit_stream, 1, "Beyond final bit"); if (BITS_VALID(NULL, &bit_stream) || value) { LOG_ERROR(NULL, "Unexpectedly succeeded reading beyond expected end of stream"); error_count++; } return error_count; } static int test_skip(void) { VC_CONTAINER_BITS_T bit_stream; uint32_t ii; int error_count = 0; uint32_t last_bits_left, bits_left; LOG_DEBUG(NULL, "Testing vc_container_bits_skip"); BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); last_bits_left = BITS_AVAILABLE(NULL, &bit_stream); for (ii = 0; ii < 11; ii++) { BITS_SKIP(NULL, &bit_stream, ii, "test_skip"); bits_left = BITS_AVAILABLE(NULL, &bit_stream); if (bits_left + ii != last_bits_left) { int32_t actual = last_bits_left - bits_left; LOG_ERROR(NULL, "Tried to skip %u bit%s, actually skipped %d bit%s", ii, plural_ext(ii), actual, plural_ext(actual)); error_count++; } last_bits_left = bits_left; } BITS_SKIP(NULL, &bit_stream, 1, "Final bit"); if (!BITS_VALID(NULL, &bit_stream)) { LOG_ERROR(NULL, "Failed to skip final bit"); error_count++; } if (BITS_AVAILABLE(NULL, &bit_stream)) { LOG_ERROR(NULL, "End of stream not reached by skipping"); error_count++; } BITS_SKIP(NULL, &bit_stream, 1, "Beyond final bit"); if (BITS_VALID(NULL, &bit_stream)) { LOG_ERROR(NULL, "Unexpectedly succeeded skipping beyond expected end of stream"); error_count++; } return error_count; } static int test_ptr_and_skip_bytes(void) { VC_CONTAINER_BITS_T bit_stream; uint32_t ii; const uint8_t *expected_ptr; int error_count = 0; uint32_t last_bytes_left, bytes_left; LOG_DEBUG(NULL, "Testing vc_container_bits_current_pointer, vc_container_bits_skip_bytes and vc_container_bits_bytes_available"); BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); last_bytes_left = BITS_BYTES_AVAILABLE(NULL, &bit_stream); if (last_bytes_left != countof(bits_0_to_10)) { LOG_ERROR(NULL, "Expected bytes available to initially match given size"); error_count++; } if (BITS_CURRENT_POINTER(NULL, &bit_stream) != bits_0_to_10) { LOG_ERROR(NULL, "Expected initial current pointer to match original buffer"); error_count++; } expected_ptr = bits_0_to_10; for (ii = 0; ii < 4; ii++) { BITS_SKIP_BYTES(NULL, &bit_stream, ii, "test_ptr_and_skip_bytes"); expected_ptr += ii; if (BITS_CURRENT_POINTER(NULL, &bit_stream) != expected_ptr) { LOG_ERROR(NULL, "Expected current pointer to have moved by %u byte%s", ii, plural_ext(ii)); error_count++; } bytes_left = BITS_BYTES_AVAILABLE(NULL, &bit_stream); if (bytes_left + ii != last_bytes_left) { int32_t actual = last_bytes_left - bytes_left; LOG_ERROR(NULL, "Tried to skip %u byte%s, actually skipped %d byte%s", ii, plural_ext(ii), actual, plural_ext(actual)); error_count++; } last_bytes_left = bytes_left; } if (!bytes_left) { LOG_ERROR(NULL, "Reached end of stream too soon"); error_count++; } if (!BITS_VALID(NULL, &bit_stream)) { LOG_ERROR(NULL, "Expected stream to be valid"); error_count++; } BITS_SKIP_BYTES(NULL, &bit_stream, bytes_left + 1, "Beyond end of stream"); if (BITS_VALID(NULL, &bit_stream)) { LOG_ERROR(NULL, "Unexpectedly succeeded skipping bytes beyond end of stream"); error_count++; } if (BITS_BYTES_AVAILABLE(NULL, &bit_stream)) { LOG_ERROR(NULL, "Expected stream to have been reset"); error_count++; } return error_count; } static int test_reduce_bytes(void) { VC_CONTAINER_BITS_T bit_stream; uint32_t ii; int error_count = 0; uint32_t last_bytes_left, bytes_left; LOG_DEBUG(NULL, "Testing vc_container_bits_reduce_bytes"); BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); last_bytes_left = BITS_BYTES_AVAILABLE(NULL, &bit_stream); if (last_bytes_left != countof(bits_0_to_10)) { LOG_ERROR(NULL, "Expected bytes available to initially match given size"); error_count++; } if (BITS_CURRENT_POINTER(NULL, &bit_stream) != bits_0_to_10) { LOG_ERROR(NULL, "Expected initial current pointer to match original buffer"); error_count++; } for (ii = 0; ii < 4; ii++) { BITS_REDUCE_BYTES(NULL, &bit_stream, ii, "test_reduce_bytes"); if (BITS_CURRENT_POINTER(NULL, &bit_stream) != bits_0_to_10) { LOG_ERROR(NULL, "Did not expect current pointer to have moved"); error_count++; } bytes_left = BITS_BYTES_AVAILABLE(NULL, &bit_stream); if (bytes_left + ii != last_bytes_left) { int32_t actual = last_bytes_left - bytes_left; LOG_ERROR(NULL, "Tried to reduce by %u byte%s, actually reduced by %d byte%s", ii, plural_ext(ii), actual, plural_ext(actual)); error_count++; } last_bytes_left = bytes_left; } if (!bytes_left) { LOG_ERROR(NULL, "Reached end of stream too soon"); error_count++; } if (!BITS_VALID(NULL, &bit_stream)) { LOG_ERROR(NULL, "Expected stream to be valid"); error_count++; } BITS_REDUCE_BYTES(NULL, &bit_stream, bytes_left + 1, "Reducing an empty stream"); if (BITS_VALID(NULL, &bit_stream)) { LOG_ERROR(NULL, "Unexpectedly succeeded reducing by too many bytes"); error_count++; } if (BITS_AVAILABLE(NULL, &bit_stream)) { LOG_ERROR(NULL, "Expected stream to have been reset"); error_count++; } return error_count; } static int test_copy_bytes(void) { VC_CONTAINER_BITS_T bit_stream; int error_count = 0; uint8_t buffer[countof(bits_0_to_10)]; uint32_t ii; LOG_DEBUG(NULL, "Testing vc_container_bits_copy_bytes"); BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); memset(buffer, 0, sizeof(buffer)); /* Copy whole buffer in one go */ BITS_COPY_BYTES(NULL, &bit_stream, countof(buffer), buffer, "Copy whole buffer"); if (!BITS_VALID(NULL, &bit_stream)) { LOG_ERROR(NULL, "Failed to copy the whole buffer"); error_count++; } if (memcmp(buffer, bits_0_to_10, countof(bits_0_to_10)) != 0) { LOG_ERROR(NULL, "Single copy doesn't match original"); error_count++; } BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); memset(buffer, 0, sizeof(buffer)); /* Copy whole buffer one byte at a time */ for (ii = 0; ii < countof(bits_0_to_10); ii++) { BITS_COPY_BYTES(NULL, &bit_stream, 1, buffer + ii, "Copy buffer piecemeal"); } if (!BITS_VALID(NULL, &bit_stream)) { LOG_ERROR(NULL, "Failed to copy the buffer piecemeal"); error_count++; } if (memcmp(buffer, bits_0_to_10, countof(bits_0_to_10)) != 0) { LOG_ERROR(NULL, "Multiple copy doesn't match original"); error_count++; } BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); memset(buffer, 0, sizeof(buffer)); /* Copy part of buffer */ BITS_SKIP_BYTES(NULL, &bit_stream, 1, "Copy part of buffer"); BITS_REDUCE_BYTES(NULL, &bit_stream, 1, "Copy part of buffer"); BITS_COPY_BYTES(NULL, &bit_stream, countof(buffer) - 2, buffer, "Copy part of buffer"); if (!BITS_VALID(NULL, &bit_stream)) { LOG_ERROR(NULL, "Failed to copy part of buffer"); error_count++; } if (memcmp(buffer, bits_0_to_10 + 1, countof(bits_0_to_10) - 2) != 0) { LOG_ERROR(NULL, "Partial copy doesn't match original"); error_count++; } return error_count; } static int test_skip_exp_golomb(void) { VC_CONTAINER_BITS_T bit_stream; uint32_t ii; int error_count = 0; LOG_DEBUG(NULL, "Testing vc_container_bits_skip_exp_golomb"); BITS_INIT(NULL, &bit_stream, exp_golomb_0_to_10, countof(exp_golomb_0_to_10)); for (ii = 0; ii < 12; ii++) { BITS_SKIP_EXP(NULL, &bit_stream, "test_skip_exp_golomb"); } if (!BITS_VALID(NULL, &bit_stream)) { LOG_ERROR(NULL, "Failed to skip through buffer"); error_count++; } BITS_SKIP_EXP(NULL, &bit_stream, "Skip beyond end of stream"); if (BITS_VALID(NULL, &bit_stream)) { LOG_ERROR(NULL, "Unexpectedly succeeded skipping beyond expected end of stream"); error_count++; } return error_count; } static int test_read_u32_exp_golomb(void) { VC_CONTAINER_BITS_T bit_stream; uint32_t ii, value; int error_count = 0; LOG_DEBUG(NULL, "Testing vc_container_bits_get_u32_exp_golomb"); BITS_INIT(NULL, &bit_stream, exp_golomb_0_to_10, countof(exp_golomb_0_to_10)); for (ii = 0; ii < 11; ii++) { value = BITS_READ_U32_EXP(NULL, &bit_stream, "test_read_u32_exp_golomb"); if (value != ii) { LOG_ERROR(NULL, "Expected %u, got %u", ii, value); error_count++; } } value = BITS_READ_U32(NULL, &bit_stream, 1, "Final bit"); if (!BITS_VALID(NULL, &bit_stream) || !value) { LOG_ERROR(NULL, "Failed to get final bit (expected a 1)"); error_count++; } value = BITS_READ_U32_EXP(NULL, &bit_stream, "Beyond end of stream"); if (BITS_VALID(NULL, &bit_stream) || value) { LOG_ERROR(NULL, "Unexpectedly succeeded reading beyond expected end of stream"); error_count++; } /* Test getting two large (32 bit) Exp-Golomb values */ BITS_INIT(NULL, &bit_stream, exp_golomb_large, countof(exp_golomb_large)); value = BITS_READ_U32_EXP(NULL, &bit_stream, "Second largest 32-bit value"); if (value != 0xFFFFFFFE) { LOG_ERROR(NULL, "Failed to get second largest 32-bit value"); error_count++; } value = BITS_READ_U32_EXP(NULL, &bit_stream, "Largest 32-bit value"); if (value != 0xFFFFFFFF) { LOG_ERROR(NULL, "Failed to get largest 32-bit value"); error_count++; } /* Test getting an oversize (33 bit) Exp-Golomb value */ BITS_INIT(NULL, &bit_stream, exp_golomb_oversize, countof(exp_golomb_oversize)); value = BITS_READ_U32_EXP(NULL, &bit_stream, "Unsigned 33-bit value"); if (BITS_VALID(NULL, &bit_stream) || value) { LOG_ERROR(NULL, "Unexpectedly got 33-bit value: %u", value); error_count++; } return error_count; } static int test_read_s32_exp_golomb(void) { VC_CONTAINER_BITS_T bit_stream; uint32_t ii; int32_t value; int error_count = 0; LOG_DEBUG(NULL, "Testing vc_container_bits_get_s32_exp_golomb"); BITS_INIT(NULL, &bit_stream, exp_golomb_0_to_10, countof(exp_golomb_0_to_10)); for (ii = 0; ii < 11; ii++) { value = BITS_READ_S32_EXP(NULL, &bit_stream, "test_read_s32_exp_golomb"); if (value != exp_golomb_values[ii]) { LOG_ERROR(NULL, "Expected %u, got %u", ii, value); error_count++; } } value = BITS_READ_S32_EXP(NULL, &bit_stream, "Final bit"); if (!BITS_VALID(NULL, &bit_stream) || value) { LOG_ERROR(NULL, "Failed to get final Exp-Golomb value (expected a zero)"); error_count++; } value = BITS_READ_S32_EXP(NULL, &bit_stream, "Beyond final bit"); if (BITS_VALID(NULL, &bit_stream) || value) { LOG_ERROR(NULL, "Unexpectedly succeeded reading beyond expected end of stream"); error_count++; } /* Test getting two large (32 bit) Exp-Golomb values */ BITS_INIT(NULL, &bit_stream, exp_golomb_large, countof(exp_golomb_large)); value = BITS_READ_S32_EXP(NULL, &bit_stream, "Largest signed 32-bit value"); if (!BITS_VALID(NULL, &bit_stream) || value != -0x7FFFFFFF) { LOG_ERROR(NULL, "Failed to get largest signed 32-bit value: %d", value); error_count++; } value = BITS_READ_S32_EXP(NULL, &bit_stream, "Just too large signed 33-bit value"); if (BITS_VALID(NULL, &bit_stream) || value) { LOG_ERROR(NULL, "Unexpectedly got slightly too large signed 32-bit value: %d", value); error_count++; } /* Test getting an oversize (33 bit) Exp-Golomb value */ BITS_INIT(NULL, &bit_stream, exp_golomb_oversize, countof(exp_golomb_oversize)); value = BITS_READ_S32_EXP(NULL, &bit_stream, "Larger signed 33-bit value"); if (BITS_VALID(NULL, &bit_stream) || value) { LOG_ERROR(NULL, "Unexpectedly got signed 33-bit value: %d", value); error_count++; } return error_count; } #ifdef ENABLE_CONTAINERS_LOG_FORMAT static int test_indentation(void) { VC_CONTAINER_BITS_T bit_stream; uint32_t ii; LOG_DEBUG(NULL, "Testing logging indentation"); BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); for (ii = 0; ii < 11; ii++) { indent_level = ii; BITS_READ_U32(NULL, &bit_stream, ii, "test_indentation (unit)"); } BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); for (ii = 0; ii < 11; ii++) { indent_level = ii * 10; BITS_READ_U32(NULL, &bit_stream, ii, "test_indentation (tens)"); } return 0; } #endif int main(int argc, char **argv) { int error_count = 0; VC_CONTAINER_PARAM_UNUSED(argc); VC_CONTAINER_PARAM_UNUSED(argv); error_count += test_reset_and_available(); error_count += test_read_u32(); error_count += test_skip(); error_count += test_ptr_and_skip_bytes(); error_count += test_reduce_bytes(); error_count += test_copy_bytes(); error_count += test_skip_exp_golomb(); error_count += test_read_u32_exp_golomb(); error_count += test_read_s32_exp_golomb(); #ifdef ENABLE_CONTAINERS_LOG_FORMAT error_count += test_indentation(); #endif if (error_count) { LOG_ERROR(NULL, "*** %d errors reported", error_count); getchar(); } return error_count; } userland/containers/test/test_uri.c000066400000000000000000000724161421703157200200350ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include "containers/containers.h" #include "containers/core/containers_common.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_uri.h" #define TEST_CHAR '.' #define TEST_STRING "test" #define TEST_NAME "name" #define TEST_VALUE "value" #define ARRAY_SIZE(X) (sizeof(X)/sizeof(*(X))) struct { const char *before; const char *after; } test_parse_uris[] = { {"", NULL}, {"C:\\test\\filename", NULL}, {"/usr/local/nix/filename", NULL}, {"scheme-only:", NULL}, {"scheme:/and/path", NULL}, {"scheme:/and/path#with_fragment", NULL}, {"scheme:/and/path?query=true", NULL}, {"scheme:/and/path?multiple+queries=true&more=false", NULL}, {"scheme:/and/path?multiple+queries=true&more=false#and+a+fragment,+too", NULL}, {"scheme:C:\\Windows\\path", "scheme:C:%5CWindows%5Cpath"}, {"scheme:C:\\Windows\\path#with_fragment", "scheme:C:%5CWindows%5Cpath#with_fragment"}, {"scheme:C:\\Windows\\path?query=true", "scheme:C:%5CWindows%5Cpath?query=true"}, {"scheme:C:\\Windows\\path?query#and+fragment", "scheme:C:%5CWindows%5Cpath?query#and+fragment"}, {"scheme://just.host", NULL}, {"scheme://host/and/path", NULL}, {"scheme://host:port", NULL}, {"scheme://host:port/and/path", NULL}, {"scheme://127.0.0.1:port/and/path", NULL}, {"scheme://userinfo@host:port/and/path?query#fragment", NULL}, {"HTTP://www.EXAMPLE.com/", "http://www.example.com/"}, {"%48%54%54%50://%54%45%53%54/", "http://test/"}, {"scheme:esc%", "scheme:esc%25"}, {"scheme:esc%%", "scheme:esc%25%25"}, {"scheme:esc%%%", "scheme:esc"}, {"scheme:esc%1%", "scheme:esc%10"}, {"scheme:esc%%1", "scheme:esc%01"}, {"s+-.1234567890:", NULL}, {"scheme:///", NULL}, {"scheme://:just_port", NULL}, {"scheme://:port/and/path", NULL}, {"scheme://just_userinfo@", NULL}, {"scheme://userinfo@/and/path", NULL}, {"scheme://userinfo@:port/and/path", NULL}, {"%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f:", "%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F:"}, {"%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F:", "%20%21%22%23%24%25%26%27%28%29%2A+%2C-.%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E_%60%7B%7C%7D~%7F:"}, {"scheme://%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F@", "scheme://%20!%22%23$%25&'()*+,-.%2F:;%3C=%3E%3F%40%5B%5C%5D%5E_%60%7B%7C%7D~%7F@"}, {"scheme://%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F", "scheme://%20!%22%23$%25&'()*+,-.%2F:;%3C=%3E%3F%40[%5C]%5E_%60%7B%7C%7D~%7F"}, {"scheme://:%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F", "scheme://:%20%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E_%60%7B%7C%7D~%7F"}, {"scheme:///%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F", "scheme:///%20!%22%23$%25&'()*+,-./:;%3C=%3E%3F@%5B%5C%5D%5E_%60%7B%7C%7D~%7F"}, {"scheme://?%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F", "scheme://?%20!%22%23$%25&'()*+,-./:;%3C=%3E?@%5B%5C%5D%5E_%60%7B%7C%7D~%7F"}, {"scheme://#%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F", "scheme://#%20!%22%23$%25&'()*+,-./:;%3C=%3E?@%5B%5C%5D%5E_%60%7B%7C%7D~%7F"}, {"scheme://[v6.1234:5678]/", NULL}, {"scheme://[::1]/", NULL}, {"scheme://[1234:5678:90ab:cdef:1234:5678:90ab:cdef]/", NULL}, {"scheme://[::1]:1/", NULL}, {"scheme://?", "scheme://"}, {"scheme://?#", "scheme://#"}, {"scheme://?q&&;&n=&=v&=&n=v", "scheme://?q&n=&n=v"}, {"ldap://[2001:db8::7]/c=GB?objectClass?one", NULL}, {"mailto:John.Doe@example.com", NULL}, {"news:comp.infosystems.www.servers.unix", NULL}, {"tel:+1-816-555-1212", NULL}, {"urn:oasis:names:specification:docbook:dtd:xml:4.1.2", NULL}, }; typedef struct build_uri_tag { const char *expected_uri; const char *scheme; const char *userinfo; const char *host; const char *port; const char *path; const char *fragment; const char **queries; /* Held as name then value. NULL values allowed. NULL name terminates list */ } BUILD_URI_T; /* Test query lists */ const char *no_queries[] = { NULL }; const char *query_true[] = { "query", "true", NULL }; const char *multiple_queries[] = { "multiple+queries", "true", "more", "false", NULL }; const char *just_query[] = {"query", NULL, NULL}; const char *complex_query[] = { "q", NULL, "n", "", "n", "v", NULL }; const char *objectClass_one[] = { "objectClass?one", NULL, NULL }; BUILD_URI_T test_build_uris[] = { {"", NULL, NULL, NULL, NULL, NULL, NULL, no_queries }, {"C:\\test\\filename", NULL, NULL, NULL, NULL, "C:\\test\\filename", NULL, no_queries}, {"/usr/local/nix/filename", NULL, NULL, NULL, NULL, "/usr/local/nix/filename", NULL, no_queries}, {"scheme-only:", "scheme-only", NULL, NULL, NULL, NULL, NULL, no_queries}, {"scheme:/and/path", "scheme", NULL, NULL, NULL, "/and/path", NULL, no_queries}, {"scheme:/and/path#with_fragment", "scheme", NULL, NULL, NULL, "/and/path", "with_fragment", no_queries}, {"scheme:/and/path?query=true", "scheme", NULL, NULL, NULL, "/and/path", NULL, query_true}, {"scheme:/and/path?multiple+queries=true&more=false", "scheme", NULL, NULL, NULL, "/and/path", NULL, multiple_queries}, {"scheme:/and/path?multiple+queries=true&more=false#and+a+fragment,+too", "scheme", NULL, NULL, NULL, "/and/path", "and+a+fragment,+too", multiple_queries}, {"scheme://just.host", "scheme", NULL, "just.host", NULL, NULL, NULL, no_queries}, {"scheme://host/and/path", "scheme", NULL, "host", NULL, "/and/path", NULL, no_queries}, {"scheme://host:port", "scheme", NULL, "host", "port", NULL, NULL, no_queries}, {"scheme://host:port/and/path", "scheme", NULL, "host", "port", "/and/path", NULL, no_queries}, {"scheme://127.0.0.1:port/and/path", "scheme", NULL, "127.0.0.1", "port", "/and/path", NULL, no_queries}, {"scheme://userinfo@host:port/and/path?query#fragment", "scheme", "userinfo", "host", "port", "/and/path", "fragment", just_query}, {"scheme:///", "scheme", NULL, "", NULL, "/", NULL, no_queries }, {"scheme://:just_port", "scheme", NULL, "", "just_port", NULL, NULL, no_queries }, {"scheme://:port/and/path", "scheme", NULL, "", "port", "/and/path", NULL, no_queries }, {"scheme://just_userinfo@", "scheme", "just_userinfo", "", NULL, NULL, NULL, no_queries }, {"scheme://userinfo@/and/path", "scheme", "userinfo", "", NULL, "/and/path", NULL, no_queries }, {"scheme://userinfo@:port/and/path", "scheme", "userinfo", "", "port", "/and/path", NULL, no_queries }, {"scheme://", "scheme", NULL, "", NULL, NULL, NULL, no_queries }, {"scheme://#", "scheme", NULL, "", NULL, NULL, "", no_queries }, {"scheme://?q&n=&n=v", "scheme", NULL, "", NULL, NULL, NULL, complex_query}, {"ldap://[2001:db8::7]/c=GB?objectClass?one", "ldap", NULL, "[2001:db8::7]", NULL, "/c=GB", NULL, objectClass_one}, {"mailto:John.Doe@example.com", "mailto", NULL, NULL, NULL, "John.Doe@example.com", NULL, no_queries }, {"news:comp.infosystems.www.servers.unix", "news", NULL, NULL, NULL, "comp.infosystems.www.servers.unix", NULL, no_queries }, {"tel:+1-816-555-1212", "tel", NULL, NULL, NULL, "+1-816-555-1212", NULL, no_queries }, {"urn:oasis:names:specification:docbook:dtd:xml:4.1.2", "urn", NULL, NULL, NULL, "oasis:names:specification:docbook:dtd:xml:4.1.2", NULL, no_queries }, }; typedef struct merge_uri_tag { const char *base; const char *relative; const char *merged; } MERGE_URI_T; MERGE_URI_T test_merge_uris[] = { /* Normal examples */ { "http://a/b/c/d;p?q#f", "ftp:h", "ftp:h" }, { "http://a/b/c/d;p?q#f", "g", "http://a/b/c/g" }, { "http://a/b/c/d;p?q#f", "./g", "http://a/b/c/g" }, { "http://a/b/c/d;p?q#f", "g/", "http://a/b/c/g/" }, { "http://a/b/c/d;p?q#f", "/g", "http://a/g" }, { "http://a/b/c/d;p?q#f", "//g", "http://g" }, { "http://a/b/c/d;p?q#f", "?y", "http://a/b/c/d;p?y" }, { "http://a/b/c/d;p?q#f", "g?y", "http://a/b/c/g?y" }, { "http://a/b/c/d;p?q#f", "g?y/./x", "http://a/b/c/g?y/./x" }, { "http://a/b/c/d;p?q#f", "#s", "http://a/b/c/d;p?q#s" }, { "http://a/b/c/d;p?q#f", "g#s", "http://a/b/c/g#s" }, { "http://a/b/c/d;p?q#f", "g#s/./x", "http://a/b/c/g#s/./x" }, { "http://a/b/c/d;p?q#f", "g?y#s", "http://a/b/c/g?y#s" }, { "http://a/b/c/d;p?q#f", ";x", "http://a/b/c/d;x" }, { "http://a/b/c/d;p?q#f", "g;x", "http://a/b/c/g;x" }, { "http://a/b/c/d;p?q#f", "g;x?y#s", "http://a/b/c/g;x?y#s" }, { "http://a/b/c/d;p?q#f", ".", "http://a/b/c/" }, { "http://a/b/c/d;p?q#f", "./", "http://a/b/c/" }, { "http://a/b/c/d;p?q#f", "..", "http://a/b/" }, { "http://a/b/c/d;p?q#f", "../", "http://a/b/" }, { "http://a/b/c/d;p?q#f", "../g", "http://a/b/g" }, { "http://a/b/c/d;p?q#f", "../..", "http://a/" }, { "http://a/b/c/d;p?q#f", "../../", "http://a/" }, { "http://a/b/c/d;p?q#f", "../../g", "http://a/g" }, /* Normal examples, without base network info */ { "http:/b/c/d;p?q#f", "g", "http:/b/c/g" }, { "http:/b/c/d;p?q#f", "./g", "http:/b/c/g" }, { "http:/b/c/d;p?q#f", "g/", "http:/b/c/g/" }, { "http:/b/c/d;p?q#f", "/g", "http:/g" }, { "http:/b/c/d;p?q#f", "//g", "http://g" }, { "http:/b/c/d;p?q#f", "?y", "http:/b/c/d;p?y" }, { "http:/b/c/d;p?q#f", "g?y", "http:/b/c/g?y" }, { "http:/b/c/d;p?q#f", "g?y/./x", "http:/b/c/g?y/./x" }, { "http:/b/c/d;p?q#f", "#s", "http:/b/c/d;p?q#s" }, { "http:/b/c/d;p?q#f", "g#s", "http:/b/c/g#s" }, { "http:/b/c/d;p?q#f", "g#s/./x", "http:/b/c/g#s/./x" }, { "http:/b/c/d;p?q#f", "g?y#s", "http:/b/c/g?y#s" }, { "http:/b/c/d;p?q#f", ";x", "http:/b/c/d;x" }, { "http:/b/c/d;p?q#f", "g;x", "http:/b/c/g;x" }, { "http:/b/c/d;p?q#f", "g;x?y#s", "http:/b/c/g;x?y#s" }, { "http:/b/c/d;p?q#f", ".", "http:/b/c/" }, { "http:/b/c/d;p?q#f", "./", "http:/b/c/" }, { "http:/b/c/d;p?q#f", "..", "http:/b/" }, { "http:/b/c/d;p?q#f", "../", "http:/b/" }, { "http:/b/c/d;p?q#f", "../g", "http:/b/g" }, { "http:/b/c/d;p?q#f", "../..", "http:/" }, { "http:/b/c/d;p?q#f", "../../", "http:/" }, { "http:/b/c/d;p?q#f", "../../g", "http:/g" }, /* Normal examples, without base network info or path root */ { "http:b/c/d;p?q#f", "g", "http:b/c/g" }, { "http:b/c/d;p?q#f", "./g", "http:b/c/g" }, { "http:b/c/d;p?q#f", "g/", "http:b/c/g/" }, { "http:b/c/d;p?q#f", "/g", "http:/g" }, { "http:b/c/d;p?q#f", "//g", "http://g" }, { "http:b/c/d;p?q#f", "?y", "http:b/c/d;p?y" }, { "http:b/c/d;p?q#f", "g?y", "http:b/c/g?y" }, { "http:b/c/d;p?q#f", "g?y/./x", "http:b/c/g?y/./x" }, { "http:b/c/d;p?q#f", "#s", "http:b/c/d;p?q#s" }, { "http:b/c/d;p?q#f", "g#s", "http:b/c/g#s" }, { "http:b/c/d;p?q#f", "g#s/./x", "http:b/c/g#s/./x" }, { "http:b/c/d;p?q#f", "g?y#s", "http:b/c/g?y#s" }, { "http:b/c/d;p?q#f", ";x", "http:b/c/d;x" }, { "http:b/c/d;p?q#f", "g;x", "http:b/c/g;x" }, { "http:b/c/d;p?q#f", "g;x?y#s", "http:b/c/g;x?y#s" }, { "http:b/c/d;p?q#f", ".", "http:b/c/" }, { "http:b/c/d;p?q#f", "./", "http:b/c/" }, { "http:b/c/d;p?q#f", "..", "http:b/" }, { "http:b/c/d;p?q#f", "../", "http:b/" }, { "http:b/c/d;p?q#f", "../g", "http:b/g" }, { "http:b/c/d;p?q#f", "../..", "http:" }, { "http:b/c/d;p?q#f", "../../", "http:" }, { "http:b/c/d;p?q#f", "../../g", "http:g" }, /* Normal examples, without base path */ { "http://a?q#f", "g", "http://a/g" }, { "http://a?q#f", "./g", "http://a/g" }, { "http://a?q#f", "g/", "http://a/g/" }, { "http://a?q#f", "/g", "http://a/g" }, { "http://a?q#f", "//g", "http://g" }, { "http://a?q#f", "?y", "http://a?y" }, { "http://a?q#f", "g?y", "http://a/g?y" }, { "http://a?q#f", "g?y/./x", "http://a/g?y/./x" }, { "http://a?q#f", "#s", "http://a?q#s" }, { "http://a?q#f", "g#s", "http://a/g#s" }, { "http://a?q#f", "g#s/./x", "http://a/g#s/./x" }, { "http://a?q#f", "g?y#s", "http://a/g?y#s" }, { "http://a?q#f", ";x", "http://a/;x" }, { "http://a?q#f", "g;x", "http://a/g;x" }, { "http://a?q#f", "g;x?y#s", "http://a/g;x?y#s" }, { "http://a?q#f", ".", "http://a/" }, { "http://a?q#f", "./", "http://a/" }, /* Normal examples, without base network info or path */ { "http:?q#f", "g", "http:g" }, { "http:?q#f", "./g", "http:g" }, { "http:?q#f", "g/", "http:g/" }, { "http:?q#f", "/g", "http:/g" }, { "http:?q#f", "//g", "http://g" }, { "http:?q#f", "?y", "http:?y" }, { "http:?q#f", "g?y", "http:g?y" }, { "http:?q#f", "g?y/./x", "http:g?y/./x" }, { "http:?q#f", "#s", "http:?q#s" }, { "http:?q#f", "g#s", "http:g#s" }, { "http:?q#f", "g#s/./x", "http:g#s/./x" }, { "http:?q#f", "g?y#s", "http:g?y#s" }, { "http:?q#f", ";x", "http:;x" }, { "http:?q#f", "g;x", "http:g;x" }, { "http:?q#f", "g;x?y#s", "http:g;x?y#s" }, /* Abnormal (but valid) examples */ { "http://a/b/c/d;p?q#f", "../../../g", "http://a/../g" }, { "http://a/b/c/d;p?q#f", "../../../../g", "http://a/../../g" }, { "http://a/b/c/d;p?q#f", "/./g", "http://a/./g" }, { "http://a/b/c/d;p?q#f", "/../g", "http://a/../g" }, { "http://a/b/c/d;p?q#f", "g.", "http://a/b/c/g." }, { "http://a/b/c/d;p?q#f", ".g", "http://a/b/c/.g" }, { "http://a/b/c/d;p?q#f", "g..", "http://a/b/c/g.." }, { "http://a/b/c/d;p?q#f", "..g", "http://a/b/c/..g" }, { "http://a/b/c/d;p?q#f", "./../g", "http://a/b/g" }, { "http://a/b/c/d;p?q#f", "./g/.", "http://a/b/c/g/" }, { "http://a/b/c/d;p?q#f", "g/./h", "http://a/b/c/g/h" }, { "http://a/b/c/d;p?q#f", "g/../h", "http://a/b/c/h" }, { "http://a/b/c/d;p?q#f", "./g:h", "http://a/b/c/g:h" }, { "http://a/b/c/d;p?q#f", "g/..", "http://a/b/c/" }, /* Abnormal examples without base path */ { "http://a?q#f", "../g", "http://a/../g" }, { "http://a?q#f", "./../g", "http://a/../g" }, /* Abnormal examples without base network info or path */ { "http:?q#f", "../g", "http:../g" }, { "http:?q#f", "./../g", "http:../g" }, { "http:?q#f", ".", "http:" }, { "http:?q#f", "./", "http:" }, }; /** Dump a URI structure to the log. * * \param uri URI structure to be dumped. */ static void dump_uri(VC_URI_PARTS_T *uri) { const char *str; uint32_t query_count, ii; str = vc_uri_scheme(uri); if (str) LOG_DEBUG(NULL, "Scheme: <%s>", str); str = vc_uri_userinfo(uri); if (str) LOG_DEBUG(NULL, "Userinfo: <%s>", str); str = vc_uri_host(uri); if (str) LOG_DEBUG(NULL, "Host: <%s>", str); str = vc_uri_port(uri); if (str) LOG_DEBUG(NULL, "Port: <%s>", str); str = vc_uri_path(uri); if (str) LOG_DEBUG(NULL, "Path: <%s>", str); query_count = vc_uri_num_queries(uri); for (ii = 0; ii < query_count; ii++) { const char *value; vc_uri_query(uri, ii, &str, &value); if (str) { if (value) LOG_DEBUG(NULL, "Query %d: <%s>=<%s>", ii, str, value); else LOG_DEBUG(NULL, "Query %d: <%s>", ii, str); } } str = vc_uri_fragment(uri); if (str) LOG_DEBUG(NULL, "Fragment: <%s>", str); } static int check_uri(VC_URI_PARTS_T *uri, const char *expected) { uint32_t built_len; char *built; built_len = vc_uri_build(uri, NULL, 0) + 1; built = (char *)malloc(built_len); if (!built) { LOG_ERROR(NULL, "*** Unexpected memory allocation failure: %d bytes", built_len); return 1; } vc_uri_build(uri, built, built_len); if (strcmp(built, expected) != 0) { LOG_ERROR(NULL, "*** Built URI <%s>\nexpected <%s>", built, expected); free(built); return 1; } free(built); return 0; } static int check_null_uri_pointer(void) { int error_count = 0; char buffer[1]; /* Check NULL URI can be passed without failure to all routines */ vc_uri_release( NULL ); vc_uri_clear( NULL ); if (vc_uri_parse( NULL, NULL )) error_count++; if (vc_uri_parse( NULL, "" )) error_count++; if (vc_uri_build( NULL, NULL, 0 ) != 0) error_count++; buffer[0] = TEST_CHAR; if (vc_uri_build( NULL, buffer, sizeof(buffer) ) != 0) error_count++; if (buffer[0] != TEST_CHAR) error_count++; if (vc_uri_scheme( NULL )) error_count++; if (vc_uri_userinfo( NULL )) error_count++; if (vc_uri_host( NULL )) error_count++; if (vc_uri_port( NULL )) error_count++; if (vc_uri_path( NULL )) error_count++; if (vc_uri_fragment( NULL )) error_count++; if (vc_uri_num_queries( NULL ) != 0) error_count++; vc_uri_query( NULL, 0, NULL, NULL ); if (vc_uri_set_scheme( NULL, NULL )) error_count++; if (vc_uri_set_userinfo( NULL, NULL )) error_count++; if (vc_uri_set_host( NULL, NULL )) error_count++; if (vc_uri_set_port( NULL, NULL )) error_count++; if (vc_uri_set_path( NULL, NULL )) error_count++; if (vc_uri_set_fragment( NULL, NULL )) error_count++; if (vc_uri_add_query( NULL, NULL, NULL )) error_count++; if (error_count) LOG_ERROR(NULL, "NULL URI parameter testing failed"); return error_count; } static int check_parse_parameters(VC_URI_PARTS_T *uri) { int error_count = 0; if (vc_uri_parse( uri, NULL )) { LOG_ERROR(NULL, "Parsing NULL URI failed"); error_count++; } return error_count; } static int check_build_parameters(VC_URI_PARTS_T *uri) { int error_count = 0; char buffer[1]; vc_uri_set_path( uri, TEST_STRING ); if (vc_uri_build( uri, NULL, 0 ) != strlen(TEST_STRING)) { LOG_ERROR(NULL, "Retrieving URI length failed"); error_count++; } buffer[0] = TEST_CHAR; if (vc_uri_build( uri, buffer, 1 ) != strlen(TEST_STRING)) { LOG_ERROR(NULL, "Building URI to small buffer failed"); error_count++; } if (buffer[0] != TEST_CHAR) { LOG_ERROR(NULL, "Building URI to small buffer modified buffer"); error_count++; } vc_uri_set_path( uri, NULL ); /* Reset uri */ return error_count; } static int check_get_defaults(VC_URI_PARTS_T *uri) { int error_count = 0; const char *name = NULL, *value = NULL; char buffer[1]; if (vc_uri_scheme( uri )) error_count++; if (vc_uri_userinfo( uri )) error_count++; if (vc_uri_host( uri )) error_count++; if (vc_uri_port( uri )) error_count++; if (vc_uri_path( uri )) error_count++; if (vc_uri_fragment( uri )) error_count++; if (vc_uri_num_queries( uri ) != 0) error_count++; vc_uri_query( uri, 0, &name, &value ); if (name != NULL || value != NULL) error_count++; if (vc_uri_build(uri, NULL, 0) != 0) error_count++; buffer[0] = ~*TEST_STRING; /* Initialize with something */ vc_uri_build(uri, buffer, sizeof(buffer)); if (buffer[0] != '\0') /* Expect empty string */ error_count++; if (error_count) LOG_ERROR(NULL, "Getting default values gave unexpected values"); return error_count; } static int check_accessors(VC_URI_PARTS_T *uri) { int error_count = 0; const char *str; if (vc_uri_set_scheme( uri, TEST_STRING )) { str = vc_uri_scheme(uri); if (!str || strcmp(TEST_STRING, str)) error_count++; if (!vc_uri_set_scheme( uri, NULL )) error_count++; if (vc_uri_scheme(uri)) error_count++; } else error_count++; if (vc_uri_set_userinfo( uri, TEST_STRING )) { str = vc_uri_userinfo(uri); if (!str || strcmp(TEST_STRING, str)) error_count++; if (!vc_uri_set_userinfo( uri, NULL )) error_count++; if (vc_uri_userinfo(uri)) error_count++; } else error_count++; if (vc_uri_set_host( uri, TEST_STRING )) { str = vc_uri_host(uri); if (!str || strcmp(TEST_STRING, str)) error_count++; if (!vc_uri_set_host( uri, NULL )) error_count++; if (vc_uri_host(uri)) error_count++; } else error_count++; if (vc_uri_set_port( uri, TEST_STRING )) { str = vc_uri_port(uri); if (!str || strcmp(TEST_STRING, str)) error_count++; if (!vc_uri_set_port( uri, NULL )) error_count++; if (vc_uri_port(uri)) error_count++; } else error_count++; if (vc_uri_set_path( uri, TEST_STRING )) { str = vc_uri_path(uri); if (!str || strcmp(TEST_STRING, str)) error_count++; if (!vc_uri_set_path( uri, NULL )) error_count++; if (vc_uri_path(uri)) error_count++; } else error_count++; if (vc_uri_set_fragment( uri, TEST_STRING )) { str = vc_uri_fragment(uri); if (!str || strcmp(TEST_STRING, str)) error_count++; if (!vc_uri_set_fragment( uri, NULL )) error_count++; if (vc_uri_fragment(uri)) error_count++; } else error_count++; if (vc_uri_add_query( uri, NULL, NULL )) error_count++; if (vc_uri_add_query( uri, NULL, TEST_VALUE )) error_count++; if (!vc_uri_add_query( uri, TEST_STRING, NULL )) error_count++; if (!vc_uri_add_query( uri, TEST_NAME, TEST_VALUE )) error_count++; if (vc_uri_num_queries(uri) == 2) { const char *name = NULL, *value = NULL; vc_uri_query(uri, 0, &name, &value); if (!name || strcmp(TEST_STRING, name)) error_count++; if (value) error_count++; vc_uri_query(uri, 1, &name, &value); if (!name || strcmp(TEST_NAME, name)) error_count++; if (!value || strcmp(TEST_VALUE, value)) error_count++; } else error_count++; if (error_count) LOG_ERROR(NULL, "Accessors failed"); return error_count; } /** Test parameter validation * * \param uri Pre-created URI structure. * \return 1 on error, 0 on success. */ static int test_parameter_validation(VC_URI_PARTS_T *uri) { int error_count = 0; error_count += check_null_uri_pointer(); error_count += check_get_defaults(uri); error_count += check_parse_parameters(uri); error_count += check_build_parameters(uri); error_count += check_accessors(uri); return error_count; } /** Test parsing and rebuilding a URI. * * \param uri Pre-created URI structure. * \param original The original URI string. * \param expected The expected, re-built string, or NULL if original is expected. * \return 1 on error, 0 on success. */ static int test_parsing_uri(VC_URI_PARTS_T *uri, const char *original, const char *expected) { bool parsed; LOG_INFO(NULL, "URI: <%s>", original); parsed = vc_uri_parse( uri, original ); if (!parsed) { LOG_ERROR(NULL, "*** Expected <%s> to parse, but it didn't", original); return 1; } dump_uri(uri); return check_uri(uri, expected ? expected : original); } /** Test building a URI from component parts. * * \param uri Pre-created URI structure. * \param uri_data The data for building the URI and the expected output. * \return 1 on error, 0 on success. */ static int test_building_uri(VC_URI_PARTS_T *uri, BUILD_URI_T *uri_data) { const char **p_str; const char *name, *value; LOG_INFO(NULL, "Building URI <%s>", uri_data->expected_uri); vc_uri_clear(uri); if (!vc_uri_set_scheme(uri, uri_data->scheme)) { LOG_ERROR(NULL, "*** Failed to set scheme"); return 1; } if (!vc_uri_set_userinfo(uri, uri_data->userinfo)) { LOG_ERROR(NULL, "*** Failed to set userinfo"); return 1; } if (!vc_uri_set_host(uri, uri_data->host)) { LOG_ERROR(NULL, "*** Failed to set host"); return 1; } if (!vc_uri_set_port(uri, uri_data->port)) { LOG_ERROR(NULL, "*** Failed to set port"); return 1; } if (!vc_uri_set_path(uri, uri_data->path)) { LOG_ERROR(NULL, "*** Failed to set path"); return 1; } if (!vc_uri_set_fragment(uri, uri_data->fragment)) { LOG_ERROR(NULL, "*** Failed to set fragment"); return 1; } p_str = uri_data->queries; name = *p_str++; while (name) { value = *p_str++; if (!vc_uri_add_query(uri, name, value)) { LOG_ERROR(NULL, "*** Failed to add query"); return 1; } name = *p_str++; } dump_uri(uri); return check_uri(uri, uri_data->expected_uri); } /** Test merging a relative URI with a base URI. * * \param uri Pre-created URI structure. * \param uri_data The nase and relative URIs and the expected output. * \return 1 on error, 0 on success. */ static int test_merging_uri(VC_URI_PARTS_T *uri, MERGE_URI_T *uri_data) { VC_URI_PARTS_T *base_uri; LOG_INFO(NULL, "Base <%s>, relative <%s>, expect <%s>", uri_data->base, uri_data->relative, uri_data->merged); vc_uri_clear(uri); base_uri = vc_uri_create(); if (!base_uri) { LOG_ERROR(NULL, "*** Failed to allocate base URI structure"); return 1; } if (!vc_uri_parse(base_uri, uri_data->base)) { LOG_ERROR(NULL, "*** Failed to parse base URI structure"); return 1; } if (!vc_uri_parse(uri, uri_data->relative)) { LOG_ERROR(NULL, "*** Failed to parse relative URI structure"); return 1; } if (!vc_uri_merge(base_uri, uri)) { LOG_ERROR(NULL, "*** Failed to merge base and relative URIs"); return 1; } vc_uri_release(base_uri); return check_uri(uri, uri_data->merged); } int main(int argc, char **argv) { VC_URI_PARTS_T *uri; int error_count = 0; size_t ii; uri = vc_uri_create(); if (!uri) { LOG_ERROR(NULL, "*** Failed to create URI structure."); return 1; } LOG_INFO(NULL, "Test parameter validation"); error_count += test_parameter_validation(uri); LOG_INFO(NULL, "Test parsing URIs:"); for (ii = 0; ii < ARRAY_SIZE(test_parse_uris); ii++) { error_count += test_parsing_uri(uri, test_parse_uris[ii].before, test_parse_uris[ii].after); } LOG_INFO(NULL, "Test building URIs:"); for (ii = 0; ii < ARRAY_SIZE(test_build_uris); ii++) { error_count += test_building_uri(uri, &test_build_uris[ii]); } LOG_INFO(NULL, "Test merging URIs:"); for (ii = 0; ii < ARRAY_SIZE(test_merge_uris); ii++) { error_count += test_merging_uri(uri, &test_merge_uris[ii]); } if (argc > 1) { LOG_INFO(NULL, "Test parsing URIs from command line:"); while (argc-- > 1) { /* Test URIs passed on the command line are expected to parse and to * match themselves when rebuilt. */ error_count += test_parsing_uri(uri, argv[argc], NULL); } } vc_uri_release(uri); if (error_count) LOG_ERROR(NULL, "*** %d errors reported", error_count); #ifdef _MSC_VER LOG_INFO(NULL, "Press return to complete test."); getchar(); #endif return error_count; } userland/containers/test/uri_pipe.c000066400000000000000000000071601421703157200200050ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include "containers/containers.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_io.h" #include "nb_io.h" #define MAX_BUFFER_SIZE 2048 int main(int argc, char **argv) { char buffer[MAX_BUFFER_SIZE]; VC_CONTAINER_IO_T *read_io, *write_io; VC_CONTAINER_STATUS_T status; size_t received; bool ready = true; if (argc < 3) { LOG_INFO(NULL, "Usage:\n%s \n", argv[0]); return 1; } read_io = vc_container_io_open(argv[1], VC_CONTAINER_IO_MODE_READ, &status); if (!read_io) { LOG_INFO(NULL, "Opening <%s> for read failed: %d\n", argv[1], status); return 2; } write_io = vc_container_io_open(argv[2], VC_CONTAINER_IO_MODE_WRITE, &status); if (!write_io) { vc_container_io_close(read_io); LOG_INFO(NULL, "Opening <%s> for write failed: %d\n", argv[2], status); return 3; } nb_set_nonblocking_input(1); while (ready) { size_t total_written = 0; received = vc_container_io_read(read_io, buffer, sizeof(buffer)); while (ready && total_written < received) { total_written += vc_container_io_write(write_io, buffer + total_written, received - total_written); ready &= (write_io->status == VC_CONTAINER_SUCCESS); } ready &= (read_io->status == VC_CONTAINER_SUCCESS); if (nb_char_available()) { char c = nb_get_char(); switch (c) { case 'q': case 'Q': case 0x04: /* CTRL+D */ case 0x1A: /* CTRL+Z */ case 0x1B: /* Escape */ ready = false; break; default: ;/* Do nothing */ } } } if (read_io->status != VC_CONTAINER_SUCCESS && read_io->status != VC_CONTAINER_ERROR_EOS) { LOG_INFO(NULL, "Read failed: %d\n", read_io->status); } if (write_io->status != VC_CONTAINER_SUCCESS) { LOG_INFO(NULL, "Write failed: %d\n", write_io->status); } vc_container_io_close(read_io); vc_container_io_close(write_io); nb_set_nonblocking_input(0); return 0; } userland/containers/wav/000077500000000000000000000000001421703157200156375ustar00rootroot00000000000000userland/containers/wav/CMakeLists.txt000066400000000000000000000005661421703157200204060ustar00rootroot00000000000000# Container module needs to go in as a plugins so different prefix # and install path set(CMAKE_SHARED_LIBRARY_PREFIX "") # Make sure the compiler can find the necessary include files include_directories (../..) add_library(reader_wav ${LIBRARY_TYPE} wav_reader.c) target_link_libraries(reader_wav containers) install(TARGETS reader_wav DESTINATION ${VMCS_PLUGIN_DIR}) userland/containers/wav/wav_reader.c000066400000000000000000000331651421703157200201320ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #define CONTAINER_IS_LITTLE_ENDIAN //#define ENABLE_CONTAINERS_LOG_FORMAT //#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE #define CONTAINER_HELPER_LOG_INDENT(a) 0 #include "containers/core/containers_private.h" #include "containers/core/containers_io_helpers.h" #include "containers/core/containers_utils.h" #include "containers/core/containers_logging.h" #include "containers/core/containers_waveformat.h" /****************************************************************************** Defines. ******************************************************************************/ #define WAV_EXTRADATA_MAX 16 #define BLOCK_SIZE (16*1024) /****************************************************************************** GUID list for the different codecs ******************************************************************************/ static const GUID_T atracx_guid = {0xbfaa23e9, 0x58cb, 0x7144, {0xa1, 0x19, 0xff, 0xfa, 0x01, 0xe4, 0xce, 0x62}}; static const GUID_T pcm_guid = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; /****************************************************************************** Type definitions ******************************************************************************/ typedef struct VC_CONTAINER_MODULE_T { uint64_t data_offset; /**< Offset to the start of the data packets */ int64_t data_size; /**< Size of the data contained in the data element */ uint32_t block_size; /**< Size of a block of audio data */ int64_t position; uint64_t frame_data_left; VC_CONTAINER_TRACK_T *track; uint8_t extradata[WAV_EXTRADATA_MAX]; } VC_CONTAINER_MODULE_T; /****************************************************************************** Function prototypes ******************************************************************************/ VC_CONTAINER_STATUS_T wav_reader_open( VC_CONTAINER_T * ); /****************************************************************************** Local Functions ******************************************************************************/ /***************************************************************************** Functions exported as part of the Container Module API *****************************************************************************/ static VC_CONTAINER_STATUS_T wav_reader_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; uint32_t packet_flags = 0, size, data_size; int64_t pts; pts = module->position * 8000000 / p_ctx->tracks[0]->format->bitrate; data_size = module->frame_data_left; if(!data_size) { data_size = module->block_size; packet_flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; } module->frame_data_left = 0; if(module->position + data_size > module->data_size) data_size = module->data_size - module->position; if(data_size == 0) return VC_CONTAINER_ERROR_EOS; if((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO)) /* Skip packet */ { size = SKIP_BYTES(p_ctx, data_size); module->frame_data_left = data_size - size; module->position += size; return STREAM_STATUS(p_ctx); } p_packet->flags = packet_flags; p_packet->dts = p_packet->pts = pts; p_packet->track = 0; if(flags & VC_CONTAINER_READ_FLAG_SKIP) { size = SKIP_BYTES(p_ctx, data_size); module->frame_data_left = data_size - size; if(!module->frame_data_left) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; module->position += size; p_packet->size += size; return STREAM_STATUS(p_ctx); } if(flags & VC_CONTAINER_READ_FLAG_INFO) return VC_CONTAINER_SUCCESS; size = MIN(data_size, p_packet->buffer_size - p_packet->size); size = READ_BYTES(p_ctx, p_packet->data, size); module->frame_data_left = data_size - size; if(!module->frame_data_left) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; module->position += size; p_packet->size += size; return STREAM_STATUS(p_ctx); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T wav_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *p_offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; int64_t position; VC_CONTAINER_PARAM_UNUSED(mode); VC_CONTAINER_PARAM_UNUSED(flags); position = *p_offset * p_ctx->tracks[0]->format->bitrate / 8000000; if(p_ctx->tracks[0]->format->type->audio.block_align) position = position / p_ctx->tracks[0]->format->type->audio.block_align * p_ctx->tracks[0]->format->type->audio.block_align; if(position > module->data_size) position = module->data_size; module->position = position; module->frame_data_left = 0; if(position >= module->data_size) return VC_CONTAINER_ERROR_EOS; return SEEK(p_ctx, module->data_offset + position); } /*****************************************************************************/ static VC_CONTAINER_STATUS_T wav_reader_close( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; unsigned int i; for(i = 0; i < p_ctx->tracks_num; i++) vc_container_free_track(p_ctx, p_ctx->tracks[i]); free(module); return VC_CONTAINER_SUCCESS; } /*****************************************************************************/ VC_CONTAINER_STATUS_T wav_reader_open( VC_CONTAINER_T *p_ctx ) { VC_CONTAINER_MODULE_T *module = 0; VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; VC_CONTAINER_FOURCC_T codec; int64_t chunk_size, chunk_pos; uint32_t format, channels, samplerate, bitrate, block_align, bps, cbsize = 0; uint8_t buffer[12]; /* Check the RIFF chunk descriptor */ if( PEEK_BYTES(p_ctx, buffer, 12) != 12 ) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if( VC_FOURCC(buffer[0], buffer[1], buffer[2], buffer[3]) != VC_FOURCC('R','I','F','F') ) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; if( VC_FOURCC(buffer[8], buffer[9], buffer[10], buffer[11]) != VC_FOURCC('W','A','V','E') ) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* * We are dealing with a WAV file */ SKIP_FOURCC(p_ctx, "Chunk ID"); SKIP_U32(p_ctx, "Chunk size"); SKIP_FOURCC(p_ctx, "WAVE ID"); /* We're looking for the 'fmt' sub-chunk */ do { chunk_pos = STREAM_POSITION(p_ctx) + 8; if( READ_FOURCC(p_ctx, "Chunk ID") == VC_FOURCC('f','m','t',' ') ) break; /* Not interested in this chunk. Skip it. */ chunk_size = READ_U32(p_ctx, "Chunk size"); SKIP_BYTES(p_ctx, chunk_size); } while(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS); if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* 'fmt' not found */ /* Parse the 'fmt' sub-chunk */ chunk_size = READ_U32(p_ctx, "Chunk size"); format = READ_U16(p_ctx, "wFormatTag"); channels = READ_U16(p_ctx, "nChannels"); samplerate = READ_U32(p_ctx, "nSamplesPerSec"); bitrate = READ_U32(p_ctx, "nAvgBytesPerSec") * 8; block_align = READ_U16(p_ctx, "nBlockAlign"); bps = READ_U16(p_ctx, "wBitsPerSample"); if(STREAM_POSITION(p_ctx) - chunk_pos <= chunk_size - 2) cbsize = READ_U16(p_ctx, "cbSize"); if(format == WAVE_FORMAT_EXTENSIBLE && chunk_size >= 18 + 22 && cbsize >= 22) { GUID_T guid; codec = VC_CONTAINER_CODEC_UNKNOWN; SKIP_U16(p_ctx, "wValidBitsPerSample"); SKIP_U32(p_ctx, "dwChannelMask"); READ_GUID(p_ctx, &guid, "SubFormat"); if(!memcmp(&guid, &pcm_guid, sizeof(guid))) codec = VC_CONTAINER_CODEC_PCM_SIGNED_LE; else if(!memcmp(&guid, &atracx_guid, sizeof(guid))) codec = VC_CONTAINER_CODEC_ATRAC3; cbsize -= 22; /* TODO: deal with channel mapping */ } else { codec = waveformat_to_codec(format); } /* Bail out if we don't recognise the codec */ if(codec == VC_CONTAINER_CODEC_UNKNOWN) return VC_CONTAINER_ERROR_FORMAT_FEATURE_NOT_SUPPORTED; /* Do some sanity checking on the info we got */ if(!channels || !samplerate || !block_align || !bitrate) return VC_CONTAINER_ERROR_FORMAT_INVALID; if(codec == VC_CONTAINER_CODEC_ATRAC3 && channels > 2) return VC_CONTAINER_ERROR_FORMAT_INVALID; /* Allocate our context */ module = malloc(sizeof(*module)); if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } memset(module, 0, sizeof(*module)); p_ctx->priv->module = module; p_ctx->tracks_num = 1; p_ctx->tracks = &module->track; p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0); if(!p_ctx->tracks[0]) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; p_ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; p_ctx->tracks[0]->format->codec = codec; p_ctx->tracks[0]->format->type->audio.channels = channels; p_ctx->tracks[0]->format->type->audio.sample_rate = samplerate; p_ctx->tracks[0]->format->type->audio.block_align = block_align; p_ctx->tracks[0]->format->type->audio.bits_per_sample = bps; p_ctx->tracks[0]->format->bitrate = bitrate; p_ctx->tracks[0]->is_enabled = true; p_ctx->tracks[0]->format->extradata_size = 0; p_ctx->tracks[0]->format->extradata = module->extradata; module->block_size = block_align; /* Prepare the codec extradata */ if(codec == VC_CONTAINER_CODEC_ATRAC3) { uint16_t h, mode; SKIP_U16(p_ctx, "len"); SKIP_U16(p_ctx, "layer"); SKIP_U32(p_ctx, "bytes_per_frame"); mode = READ_U16(p_ctx, "mode"); SKIP_U16(p_ctx, "mode_ext"); SKIP_U16(p_ctx, "num_subframes"); SKIP_U16(p_ctx, "flags"); h = (1 << 15); if(channels == 2) { h |= (1 << 13); if(mode == 1) h |= (1 << 14); } h |= block_align & 0x7ff; p_ctx->tracks[0]->format->extradata[0] = h >> 8; p_ctx->tracks[0]->format->extradata[1] = h & 255; p_ctx->tracks[0]->format->extradata_size = 2; } else if(codec == VC_CONTAINER_CODEC_ATRACX && cbsize >= 6) { SKIP_BYTES(p_ctx, 2); p_ctx->tracks[0]->format->extradata_size = READ_BYTES(p_ctx, p_ctx->tracks[0]->format->extradata, 6); } else if(codec == VC_CONTAINER_CODEC_PCM_SIGNED_LE) { /* Audioplus can no longer be given anything other than a multiple-of-16 number of samples */ block_align *= 16; module->block_size = (BLOCK_SIZE / block_align) * block_align; } /* Skip the rest of the 'fmt' sub-chunk */ SKIP_BYTES(p_ctx, chunk_pos + chunk_size - STREAM_POSITION(p_ctx)); /* We also need the 'data' sub-chunk */ do { chunk_pos = STREAM_POSITION(p_ctx) + 8; if( READ_FOURCC(p_ctx, "Chunk ID") == VC_FOURCC('d','a','t','a') ) break; /* Not interested in this chunk. Skip it. */ chunk_size = READ_U32(p_ctx, "Chunk size"); SKIP_BYTES(p_ctx, chunk_size); } while(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS); if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) { status = VC_CONTAINER_ERROR_FORMAT_INVALID; /* 'data' not found */; goto error; } module->data_offset = chunk_pos; module->data_size = READ_U32(p_ctx, "Chunk size"); p_ctx->duration = module->data_size * 8000000 / bitrate; if(STREAM_SEEKABLE(p_ctx)) p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; /* * We now have all the information we really need to start playing the stream */ p_ctx->priv->pf_close = wav_reader_close; p_ctx->priv->pf_read = wav_reader_read; p_ctx->priv->pf_seek = wav_reader_seek; /* Seek back to the start of the data */ status = SEEK(p_ctx, module->data_offset); if(status != VC_CONTAINER_SUCCESS) goto error; return VC_CONTAINER_SUCCESS; error: LOG_DEBUG(p_ctx, "wav: error opening stream (%i)", status); if(module) wav_reader_close(p_ctx); return status; } /******************************************************************************** Entrypoint function ********************************************************************************/ #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) # pragma weak reader_open wav_reader_open #endif userland/helpers/000077500000000000000000000000001421703157200143375ustar00rootroot00000000000000userland/helpers/dtoverlay/000077500000000000000000000000001421703157200163505ustar00rootroot00000000000000userland/helpers/dtoverlay/CMakeLists.txt000066400000000000000000000013521421703157200211110ustar00rootroot00000000000000# ============================================================================= # Copyright (c) 2016 Raspberry Pi (Trading) Ltd. # All rights reserved. # # FILE DESCRIPTION # CMake build file for dtoverlay library. # ============================================================================= cmake_minimum_required (VERSION 2.8) include_directories (${VIDEOCORE_ROOT} ${VIDEOCORE_ROOT}/helpers ${VIDEOCORE_ROOT}/opensrc/helpers/libfdt ${VIDEOCORE_HEADERS_BUILD_DIR}) if (CMAKE_COMPILER_IS_GNUCC) add_definitions (-ffunction-sections) endif () add_library (dtovl ${SHARED} dtoverlay.c) target_link_libraries(dtovl fdt) install (TARGETS dtovl DESTINATION lib) userland/helpers/dtoverlay/dtoverlay.c000066400000000000000000002420711421703157200205330ustar00rootroot00000000000000/* Copyright (c) 2016-2019 Raspberry Pi (Trading) Ltd. 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include #include #include "dtoverlay.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) typedef enum { FIXUP_ABSOLUTE, FIXUP_RELATIVE } fixup_type_t; #define DTOVERRIDE_END 0 #define DTOVERRIDE_INTEGER 1 #define DTOVERRIDE_BOOLEAN 2 #define DTOVERRIDE_BOOLEAN_INV 3 #define DTOVERRIDE_STRING 4 #define DTOVERRIDE_OVERLAY 5 #define DTOVERRIDE_BYTE_STRING 6 static int dtoverlay_extract_override(const char *override_name, char *override_value, int value_size, int *phandle_ptr, const char **datap, const char *dataendp, const char **namep, int *namelenp, int *offp, int *sizep); static const char *dtoverlay_lookup_key(const char *lookup_string, const char *data_end, const char *key, char *buf, int buf_len); static int dtoverlay_set_node_name(DTBLOB_T *dtb, int node_off, const char *name); static void dtoverlay_stdio_logging(dtoverlay_logging_type_t type, const char *fmt, va_list args); #define phandle_debug if (0) dtoverlay_debug static DTOVERLAY_LOGGING_FUNC *dtoverlay_logging_func = dtoverlay_stdio_logging; static int dtoverlay_debug_enabled = 0; static DTBLOB_T *overlay_map; static const char *platform_name; static int platform_name_len; static int strmemcmp(const char *mem, int mem_len, const char *str) { int ret = strncmp(mem, str, mem_len); if (ret == 0 && str[mem_len] != 0) ret = 1; return ret; } uint8_t dtoverlay_read_u8(const void *src, int off) { const unsigned char *p = src; return (p[off + 0] << 0); } uint16_t dtoverlay_read_u16(const void *src, int off) { const unsigned char *p = src; return (p[off + 0] << 8) | (p[off + 1] << 0); } uint32_t dtoverlay_read_u32(const void *src, int off) { const unsigned char *p = src; return (p[off + 0] << 24) | (p[off + 1] << 16) | (p[off + 2] << 8) | (p[off + 3] << 0); } uint64_t dtoverlay_read_u64(const void *src, int off) { const unsigned char *p = src; return ((uint64_t)p[off + 0] << 56) | ((uint64_t)p[off + 1] << 48) | ((uint64_t)p[off + 2] << 40) | ((uint64_t)p[off + 3] << 32) | (p[off + 4] << 24) | (p[off + 5] << 16) | (p[off + 6] << 8) | (p[off + 7] << 0); } void dtoverlay_write_u8(void *dst, int off, uint32_t val) { unsigned char *p = dst; p[off] = (val >> 0) & 0xff; } void dtoverlay_write_u16(void *dst, int off, uint32_t val) { unsigned char *p = dst; p[off + 0] = (val >> 8) & 0xff; p[off + 1] = (val >> 0) & 0xff; } void dtoverlay_write_u32(void *dst, int off, uint32_t val) { unsigned char *p = dst; p[off + 0] = (val >> 24) & 0xff; p[off + 1] = (val >> 16) & 0xff; p[off + 2] = (val >> 8) & 0xff; p[off + 3] = (val >> 0) & 0xff; } void dtoverlay_write_u64(void *dst, int off, uint64_t val) { unsigned char *p = dst; p[off + 0] = (val >> 56) & 0xff; p[off + 1] = (val >> 48) & 0xff; p[off + 2] = (val >> 40) & 0xff; p[off + 3] = (val >> 32) & 0xff; p[off + 4] = (val >> 24) & 0xff; p[off + 5] = (val >> 16) & 0xff; p[off + 6] = (val >> 8) & 0xff; p[off + 7] = (val >> 0) & 0xff; } // Returns the offset of the node indicated by the absolute path, creating // it and any intermediates as necessary, or a negative error code. int dtoverlay_create_node(DTBLOB_T *dtb, const char *node_path, int path_len) { const char *path_ptr; const char *path_end; int node_off = 0; if (!path_len) path_len = strlen(node_path); path_ptr = node_path; path_end = node_path + path_len; if ((path_len > 0) && (path_ptr[path_len - 1] == '/')) path_end--; while (path_ptr < path_end) { const char *path_next; int subnode_off; if (*path_ptr != '/') return -FDT_ERR_BADPATH; // find the next path separator (or the end of the string) path_ptr++; for (path_next = path_ptr; (path_next != path_end) && (*path_next != '/'); path_next++) continue; subnode_off = fdt_subnode_offset_namelen(dtb->fdt, node_off, path_ptr, path_next - path_ptr); if (subnode_off >= 0) node_off = subnode_off; else node_off = fdt_add_subnode_namelen(dtb->fdt, node_off, path_ptr, path_next - path_ptr); if (node_off < 0) break; path_ptr = path_next; } if ((node_off >= 0) && (path_ptr != path_end)) return -FDT_ERR_BADPATH; return node_off; } // Returns 0 on success, otherwise <0 error code int dtoverlay_delete_node(DTBLOB_T *dtb, const char *node_path, int path_len) { int node_off = 0; if (!path_len) path_len = strlen(node_path); dtoverlay_debug("delete_node(%.*s)", path_len, node_path); node_off = fdt_path_offset_namelen(dtb->fdt, node_path, path_len); if (node_off < 0) return node_off; return fdt_del_node(dtb->fdt, node_off); } // Returns the offset of the node indicated by the absolute path or a negative // error code. int dtoverlay_find_node(DTBLOB_T *dtb, const char *node_path, int path_len) { if (!path_len) path_len = strlen(node_path); return fdt_path_offset_namelen(dtb->fdt, node_path, path_len); } // Returns 0 on success, otherwise <0 error code int dtoverlay_set_node_properties(DTBLOB_T *dtb, const char *node_path, DTOVERLAY_PARAM_T *properties, unsigned int num_properties) { int err = 0; int node_off; node_off = fdt_path_offset(dtb->fdt, node_path); if (node_off < 0) node_off = dtoverlay_create_node(dtb, node_path, 0); if (node_off >= 0) { int i; for (i = 0; (i < num_properties) && (err == 0); i++) { DTOVERLAY_PARAM_T *p; p = properties + i; err = fdt_setprop(dtb->fdt, node_off, p->param, p->b, p->len); } } else err = node_off; return err; } struct dynstring { char *buf; int size; int len; }; static void dynstring_init(struct dynstring *ds) { ds->size = 0; ds->len = 0; ds->buf = NULL; } static int dynstring_init_size(struct dynstring *ds, int initial_size) { if (initial_size < 32) initial_size = 32; ds->size = initial_size; ds->len = 0; ds->buf = malloc(initial_size); if (!ds->buf) { dtoverlay_error(" out of memory"); return -FDT_ERR_NOSPACE; } return 0; } static int dynstring_set_size(struct dynstring *ds, int size) { if (size > ds->size) { size = (size * 5)/4; // Add a 25% headroom ds->buf = realloc(ds->buf, size); if (!ds->buf) { dtoverlay_error(" out of memory"); return -FDT_ERR_NOSPACE; } ds->size = size; } return 0; } static int dynstring_dup(struct dynstring *ds, const char *src, int len) { int err = 0; if (!len) len = strlen(src); err = dynstring_set_size(ds, len + 1); if (!err) { memcpy(ds->buf, src, len + 1); ds->len = len; } return err; } static int dynstring_patch(struct dynstring *ds, int pos, int width, const char *src, int len) { int newlen = ds->len + (len - width); int err = dynstring_set_size(ds, newlen + 1); if (!err) { if (width != len) { // Move any data following the patch memmove(ds->buf + pos + len, ds->buf + pos + width, ds->len + 1 - (pos + width)); ds->len = newlen; } memcpy(ds->buf + pos, src, len); } return err; } static int dynstring_grow(struct dynstring *ds) { return dynstring_set_size(ds, (3*ds->size)/2); } static void dynstring_free(struct dynstring *ds) { free(ds->buf); dynstring_init(ds); } static int dtoverlay_set_node_name(DTBLOB_T *dtb, int node_off, const char *name) { struct dynstring path_buf; struct dynstring prop_buf; char *old_path; const char *old_name; const char *fixup_nodes[] = { "/__fixups__", "/__local_fixups__", // For old-style dtbos "/__symbols__" // Just in case the kernel cares }; int old_name_len; int old_path_len; // All of it int dir_len; // Excluding the node name, but with the trailling slash int name_len; int offset; int fixup_idx; int err = 0; // Fixups and local-fixups both use node names, so this // function must be patch them up when a node is renamed // unless the fixups have already been applied. // Calculating a node's name is expensive, so only do it if // necessary. Since renaming a node can move things around, // don't use node_off afterwards. err = dynstring_init_size(&path_buf, 100); if (err) return err; if (!dtb->fixups_applied) { while (1) { err = fdt_get_path(dtb->fdt, node_off, path_buf.buf, path_buf.size); if (!err) break; if (err != -FDT_ERR_NOSPACE) return err; dynstring_grow(&path_buf); } } old_path = path_buf.buf; err = fdt_set_name(dtb->fdt, node_off, name); if (err || dtb->fixups_applied) goto clean_up; // Find the node name in old_path old_name = strrchr(old_path, '/'); assert(old_name); if (!old_name) return -FDT_ERR_INTERNAL; old_name++; old_name_len = strlen(old_name); dir_len = old_name - old_path; old_path_len = dir_len + old_name_len; // Short-circuit the case where the name isn't changing if (strcmp(name, old_name) == 0) goto clean_up; name_len = strlen(name); // Search the fixups and symbols for the old path (including as // a parent) and replace with the new name dynstring_init(&prop_buf); for (fixup_idx = 0; fixup_idx < ARRAY_SIZE(fixup_nodes); fixup_idx++) { int prop_off; offset = fdt_path_offset(dtb->fdt, fixup_nodes[fixup_idx]); if (offset > 0) { // Iterate through the properties for (prop_off = fdt_first_property_offset(dtb->fdt, offset); (prop_off >= 0) && (err == 0); prop_off = fdt_next_property_offset(dtb->fdt, prop_off)) { const char *prop_name; const char *prop_val; int prop_len; int pos; int changed = 0; prop_val = fdt_getprop_by_offset(dtb->fdt, prop_off, &prop_name, &prop_len); err = dynstring_dup(&prop_buf, prop_val, prop_len); if (err) break; // Scan each property for matching paths pos = 0; while (pos < prop_len) { if ((pos + old_path_len < prop_len) && (memcmp(prop_buf.buf + pos, old_path, old_path_len) == 0) && ((prop_buf.buf[pos + old_path_len] == ':') || (prop_buf.buf[pos + old_path_len] == '/') || (prop_buf.buf[pos + old_path_len] == '\0'))) { // Patch the string, replacing old name with new err = dynstring_patch(&prop_buf, pos + dir_len, old_name_len, name, name_len); if (err) break; prop_len += name_len - old_name_len; changed = 1; } pos += strlen(prop_buf.buf + pos) + 1; } if (!err && changed) { // Caution - may change offsets, but only by shuffling everything // afterwards, i.e. the offset to this node or property does not // change. err = fdt_setprop(dtb->fdt, offset, prop_name, prop_buf.buf, prop_len); } } } } dynstring_free(&prop_buf); if (err) goto clean_up; // Then look for a "/__local_fixups__" node, and rename // that as well. offset = fdt_path_offset(dtb->fdt, "/__local_fixups__"); if (offset > 0) { const char *p, *end; p = old_path; end = old_path + old_path_len; while (p < end) { const char *q; while (*p == '/') { p++; if (p == end) break; } q = memchr(p, '/', end - p); if (! q) q = end; offset = fdt_subnode_offset_namelen(dtb->fdt, offset, p, q-p); if (offset < 0) break; p = q; } if (offset > 0) err = fdt_set_name(dtb->fdt, offset, name); } // __overrides__ don't need patching because nodes are identified // using phandles, which are unaffected by renaming and resizing nodes. clean_up: dynstring_free(&path_buf); return err; } // Returns 0 on success, otherwise <0 error code int dtoverlay_create_prop_fragment(DTBLOB_T *dtb, int idx, int target_phandle, const char *prop_name, const void *prop_data, int prop_len) { char fragment_name[20]; int frag_off, ovl_off; int ret; snprintf(fragment_name, sizeof(fragment_name), "fragment-%u", idx); frag_off = fdt_add_subnode(dtb->fdt, 0, fragment_name); if (frag_off < 0) return frag_off; ret = fdt_setprop_u32(dtb->fdt, frag_off, "target", target_phandle); if (ret < 0) return ret; ovl_off = fdt_add_subnode(dtb->fdt, frag_off, "__overlay__"); if (ovl_off < 0) return ovl_off; return fdt_setprop(dtb->fdt, ovl_off, prop_name, prop_data, prop_len); } // Returns 0 on success, otherwise <0 error code static int dtoverlay_merge_fragment(DTBLOB_T *base_dtb, int target_off, const DTBLOB_T *overlay_dtb, int overlay_off, int depth) { int prop_off, subnode_off; int err = 0; if (dtoverlay_debug_enabled) { char base_path[DTOVERLAY_MAX_PATH]; char overlay_path[DTOVERLAY_MAX_PATH]; fdt_get_path(base_dtb->fdt, target_off, base_path, sizeof(base_path)); fdt_get_path(overlay_dtb->fdt, overlay_off, overlay_path, sizeof(overlay_path)); dtoverlay_debug("merge_fragment(%s,%s)", base_path, overlay_path); } // Merge each property of the node for (prop_off = fdt_first_property_offset(overlay_dtb->fdt, overlay_off); (prop_off >= 0) && (err == 0); prop_off = fdt_next_property_offset(overlay_dtb->fdt, prop_off)) { const char *prop_name; const void *prop_val; int prop_len; struct fdt_property *target_prop; int target_len; prop_val = fdt_getprop_by_offset(overlay_dtb->fdt, prop_off, &prop_name, &prop_len); /* Skip these system properties (only phandles in the first level) */ if ((strcmp(prop_name, "name") == 0) || ((depth == 0) && ((strcmp(prop_name, "phandle") == 0) || (strcmp(prop_name, "linux,phandle") == 0)))) continue; dtoverlay_debug(" +prop(%s)", prop_name); if ((strcmp(prop_name, "bootargs") == 0) && ((target_prop = fdt_get_property_w(base_dtb->fdt, target_off, prop_name, &target_len)) != NULL) && (target_len > 0) && *target_prop->data) { target_prop->data[target_len - 1] = ' '; err = fdt_appendprop(base_dtb->fdt, target_off, prop_name, prop_val, prop_len); } else err = fdt_setprop(base_dtb->fdt, target_off, prop_name, prop_val, prop_len); } // Merge each subnode of the node for (subnode_off = fdt_first_subnode(overlay_dtb->fdt, overlay_off); (subnode_off >= 0) && (err == 0); subnode_off = fdt_next_subnode(overlay_dtb->fdt, subnode_off)) { const char *subnode_name; int name_len; int subtarget_off; subnode_name = fdt_get_name(overlay_dtb->fdt, subnode_off, &name_len); subtarget_off = fdt_subnode_offset_namelen(base_dtb->fdt, target_off, subnode_name, name_len); if (subtarget_off < 0) subtarget_off = fdt_add_subnode_namelen(base_dtb->fdt, target_off, subnode_name, name_len); if (subtarget_off >= 0) { err = dtoverlay_merge_fragment(base_dtb, subtarget_off, overlay_dtb, subnode_off, depth + 1); } else { err = subtarget_off; } } dtoverlay_debug("merge_fragment() end"); return err; } static int dtoverlay_phandle_relocate(DTBLOB_T *dtb, int node_off, const char *prop_name, uint32_t phandle_increment) { int len; const fdt32_t *prop_val = fdt_getprop(dtb->fdt, node_off, prop_name, &len); int err = 0; // The absence of the property is not an error if (prop_val) { uint32_t phandle; if (len < 4) { dtoverlay_error("%s property too small", prop_name); return -FDT_ERR_BADSTRUCTURE; } phandle = fdt32_to_cpu(*prop_val) + phandle_increment; phandle_debug(" phandle_relocate %d->%d", fdt32_to_cpu(*prop_val), phandle); err = fdt_setprop_inplace_u32(dtb->fdt, node_off, prop_name, phandle); } return err; } // Returns 0 on success, or an FDT error code static int dtoverlay_apply_fixups(DTBLOB_T *dtb, const char *fixups_stringlist, uint32_t phandle, fixup_type_t type) { // The fixups arrive as a sequence of NUL-terminated strings, of the form: // "path:property:offset" // Use an empty string as an end marker, since: // 1) all tags begin 0x00 0x00 0x00, // 2) all string properties must be followed by a tag, // 3) an empty string is not a valid fixup, and // 4) the code is simpler as a result. const char *fixup = fixups_stringlist; while (fixup[0]) { const char *prop_name, *offset_str; char *offset_end; const void *prop_ptr; int prop_len; int node_off; unsigned long offset; uint32_t patch; prop_name = strchr(fixup, ':'); if (!prop_name) return -FDT_ERR_BADSTRUCTURE; prop_name++; offset_str = strchr(prop_name, ':'); if (!offset_str) return -FDT_ERR_BADSTRUCTURE; offset_str++; offset = strtoul(offset_str, &offset_end, 10); if ((offset_end == offset_str) || (offset_end[0] != 0)) return -FDT_ERR_BADSTRUCTURE; node_off = fdt_path_offset_namelen(dtb->fdt, fixup, prop_name - 1 - fixup); if (node_off < 0) return node_off; prop_ptr = fdt_getprop_namelen(dtb->fdt, node_off, prop_name, offset_str - 1 - prop_name, &prop_len); if (!prop_ptr) return prop_len; if (offset > (prop_len - 4)) return -FDT_ERR_BADSTRUCTURE; // Now apply the patch. Yes, prop_ptr is a const void *, but the // alternative (copying the whole property, patching, then updating as // a whole) is ridiculous. if (type == FIXUP_RELATIVE) { patch = phandle + dtoverlay_read_u32(prop_ptr, offset); phandle_debug(" phandle fixup %d+%d->%d", phandle, patch - phandle, patch); } else { patch = phandle; phandle_debug(" phandle ref '%s'->%d", prop_name, patch); } dtoverlay_write_u32((void *)prop_ptr, offset, patch); fixup = offset_end + 1; } return 0; } // Returns 0 on success, or an FDT error code static int dtoverlay_apply_fixups_node(DTBLOB_T *dtb, int fix_off, int target_off, uint32_t phandle_offset) { // The fixups are arranged as a subtree mirroring the structure of the // overall tree. Walk this tree in order. Each property is an array of cells // containing offsets to patch within the corresponding node/property of // the target tree. int err = 0; int prop_off; int subfix_off; // Merge each property of the node for (prop_off = fdt_first_property_offset(dtb->fdt, fix_off); (prop_off >= 0) && (err == 0); prop_off = fdt_next_property_offset(dtb->fdt, prop_off)) { const char *prop_name; const void *prop_val; int prop_len; void *target_ptr; int target_len; int off; prop_val = fdt_getprop_by_offset(dtb->fdt, prop_off, &prop_name, &prop_len); if (!prop_val) return -FDT_ERR_INTERNAL; target_ptr = fdt_getprop_w(dtb->fdt, target_off, prop_name, &target_len); if (!target_ptr) return -FDT_ERR_BADSTRUCTURE; for (off = 0; (off + 4) <= prop_len; off += 4) { uint32_t patch; int patch_offset = dtoverlay_read_u32(prop_val, off); if ((patch_offset + 4) > target_len) return -FDT_ERR_BADSTRUCTURE; patch = phandle_offset + dtoverlay_read_u32(target_ptr, patch_offset); phandle_debug(" phandle fixup %d+%d->%d", phandle_offset, patch - phandle_offset, patch); dtoverlay_write_u32(target_ptr, patch_offset, patch); } } // Merge each subnode of the node for (subfix_off = fdt_first_subnode(dtb->fdt, fix_off); (subfix_off >= 0) && (err == 0); subfix_off = fdt_next_subnode(dtb->fdt, subfix_off)) { const char *subnode_name; int name_len; int subtarget_off; subnode_name = fdt_get_name(dtb->fdt, subfix_off, &name_len); subtarget_off = fdt_subnode_offset_namelen(dtb->fdt, target_off, subnode_name, name_len); if (subtarget_off >= 0) { err = dtoverlay_apply_fixups_node(dtb, subfix_off, subtarget_off, phandle_offset); } else { err = subtarget_off; } } return err; } // Returns 0 on success, or a negative FDT error. static int dtoverlay_resolve_phandles(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb) { int local_fixups_off; int node_off; int err = 0; // First find and update the phandles in the overlay for (node_off = 0; node_off >= 0; node_off = fdt_next_node(overlay_dtb->fdt, node_off, NULL)) { dtoverlay_phandle_relocate(overlay_dtb, node_off, "phandle", base_dtb->max_phandle); dtoverlay_phandle_relocate(overlay_dtb, node_off, "linux,phandle", base_dtb->max_phandle); } local_fixups_off = fdt_path_offset(overlay_dtb->fdt, "/__local_fixups__"); if (local_fixups_off >= 0) { const char *fixups_stringlist; // Update the references to local phandles using the local fixups. // The property name is "fixup". // The value is a NUL-separated stringlist of descriptors of the form: // path:property:offset fixups_stringlist = fdt_getprop(overlay_dtb->fdt, local_fixups_off, "fixup", &err); if (fixups_stringlist) { // Relocate the overlay phandle references err = dtoverlay_apply_fixups(overlay_dtb, fixups_stringlist, base_dtb->max_phandle, FIXUP_RELATIVE); } else { err = dtoverlay_apply_fixups_node(overlay_dtb, local_fixups_off, 0, base_dtb->max_phandle); } if (err < 0) { dtoverlay_error("error applying local fixups"); return err; } } overlay_dtb->max_phandle += base_dtb->max_phandle; phandle_debug(" +overlay max phandle +%d -> %d", base_dtb->max_phandle, overlay_dtb->max_phandle); return err; } // Returns 0 on success, or an FDT error code static int dtoverlay_resolve_fixups(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb) { int fixups_off; int err = 0; fixups_off = fdt_path_offset(overlay_dtb->fdt, "/__fixups__"); if (fixups_off >= 0) { int fixup_off, symbols_off = -1; fixup_off = fdt_first_property_offset(overlay_dtb->fdt, fixups_off); if (fixup_off >= 0) { // Find the symbols, which will be needed to resolve the fixups symbols_off = fdt_path_offset(base_dtb->fdt, "/__symbols__"); if (symbols_off < 0) { dtoverlay_error("no symbols found"); return -FDT_ERR_NOTFOUND; } } for (; fixup_off >= 0; fixup_off = fdt_next_property_offset(overlay_dtb->fdt, fixup_off)) { const char *fixups_stringlist, *symbol_name, *target_path; const char *ref_type; int target_off; uint32_t target_phandle; // The property name identifies a symbol (or alias) in the base. // The value is a comma-separated list of descriptors of the form: // path:property:offset fixups_stringlist = fdt_getprop_by_offset(overlay_dtb->fdt, fixup_off, &symbol_name, &err); if (!fixups_stringlist) { dtoverlay_error("__fixups__ are borked"); break; } // 1) Find the target node. if (symbol_name[0] == '/') { /* This is a new-style path reference */ target_path = symbol_name; ref_type = "path"; } else { target_path = fdt_getprop(base_dtb->fdt, symbols_off, symbol_name, &err); if (!target_path) { dtoverlay_error("can't find symbol '%s'", symbol_name); break; } ref_type = "symbol"; } target_off = fdt_path_offset(base_dtb->fdt, target_path); if (target_off < 0) { dtoverlay_error("%s '%s' is invalid", ref_type, symbol_name); err = target_off; break; } // 2) Ensure that the target node has a phandle. target_phandle = fdt_get_phandle(base_dtb->fdt, target_off); if (!target_phandle) { // It doesn't, so give it one fdt32_t temp; target_phandle = ++base_dtb->max_phandle; temp = cpu_to_fdt32(target_phandle); err = fdt_setprop(base_dtb->fdt, target_off, "phandle", &temp, 4); if (err != 0) { dtoverlay_error("failed to add a phandle"); break; } phandle_debug(" phandle '%s'->%d", target_path, target_phandle); // The symbols may have moved, so recalculate symbols_off = fdt_path_offset(base_dtb->fdt, "/__symbols__"); } // Now apply the valid target_phandle to the items in the fixup string err = dtoverlay_apply_fixups(overlay_dtb, fixups_stringlist, target_phandle, FIXUP_ABSOLUTE); if (err) break; } } return err; } static int dtoverlay_get_target_offset(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb, int frag_off) { const char *target_path; int target_off; int len; target_path = fdt_getprop(overlay_dtb->fdt, frag_off, "target-path", &len); if (target_path) { if (!base_dtb) return -FDT_ERR_NOTFOUND; if (len && (target_path[len - 1] == '\0')) len--; target_off = fdt_path_offset_namelen(base_dtb->fdt, target_path, len); if (target_off < 0) { dtoverlay_error("invalid target-path '%.*s'", len, target_path); return target_off; } } else { const void *target_prop; int phandle; target_prop = fdt_getprop(overlay_dtb->fdt, frag_off, "target", &len); if (!target_prop) { dtoverlay_error("no target or target-path"); return len; } if (len != 4) return -FDT_ERR_BADSTRUCTURE; phandle = fdt32_to_cpu(*(fdt32_t *)target_prop); if (!base_dtb) { if (phandle < 0 || phandle > overlay_dtb->max_phandle) return -FDT_ERR_NOTFOUND; return fdt_node_offset_by_phandle(overlay_dtb->fdt, phandle); } target_off = fdt_node_offset_by_phandle(base_dtb->fdt, phandle); if (target_off < 0) { dtoverlay_error("invalid target (phandle %d)", phandle); return target_off; } } return target_off; } // Copy a node full of path strings (__symbols__, aliases) from an overlay to // the base dtb, rebasing any fragment-relative paths to make them relative // to the respective fragment target. Note that this should not be called for // intra-overlay fragments, and that overlay_dtb is not modified. static int dtoverlay_apply_overlay_paths(DTBLOB_T *base_dtb, int strings_off, DTBLOB_T *overlay_dtb, int frag_off, const char *type) { int sym_off; int err = 0; fdt_for_each_property_offset(sym_off, overlay_dtb->fdt, frag_off) { char target_path[DTOVERLAY_MAX_PATH]; const char *sym_name = NULL; const char *sym_path; const char *p; int sym_len; int sym_frag_off; int target_off; int target_path_len; int new_path_len; sym_path = fdt_getprop_by_offset(overlay_dtb->fdt, sym_off, &sym_name, &sym_len); if (!sym_path) break; /* Skip non-overlay symbols * Overlay symbol paths should be of the form: * //__overlay__/ * It doesn't actually matter what is. */ if (sym_path[0] != '/') goto copy_verbatim; p = strchr(sym_path + 1, '/'); if (!p || strncmp(p + 1, "__overlay__", 11) != 0 || (p[11] != '/' && p[11] != '\0')) goto copy_verbatim; /* Rebase the symbol path so that * /fragment@0/__overlay__/ * becomes * / */ /* Find the offset to the fragment */ sym_frag_off = dtoverlay_find_node(overlay_dtb, sym_path, p - sym_path); p += 12; /* p points to / */ /* Locate the path to the fragment target */ target_off = dtoverlay_get_target_offset(base_dtb, overlay_dtb, sym_frag_off); if (target_off < 0) return NON_FATAL(target_off); err = fdt_get_path(base_dtb->fdt, target_off, target_path, sizeof(target_path)); if (err) { dtoverlay_error("bad target path for %s", sym_path); break; } /* Append the fragment-relative path to the target path */ target_path_len = strlen(target_path); if (strcmp(target_path, "/") == 0) p++; // Avoid a '//' if the target is the root new_path_len = target_path_len + (sym_path + sym_len - p); if (new_path_len >= sizeof(target_path)) { dtoverlay_error("exported symbol path too long for %s", sym_path); err = -FDT_ERR_NOSPACE; break; } strcpy(target_path + target_path_len, p); fdt_setprop(base_dtb->fdt, strings_off, sym_name, target_path, new_path_len); dtoverlay_debug("set %s '%s' path to '%s'", type, sym_name, target_path); continue; copy_verbatim: fdt_setprop(base_dtb->fdt, strings_off, sym_name, sym_path, sym_len); } return err; } // Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors int dtoverlay_merge_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb) { // Merge each fragment node int frag_off; int frag_idx; int err = 0; int overlay_size = fdt_totalsize(overlay_dtb->fdt); void *overlay_copy = NULL; dtoverlay_filter_symbols(overlay_dtb); for (frag_off = fdt_first_subnode(overlay_dtb->fdt, 0), frag_idx = 0; frag_off >= 0; frag_off = fdt_next_subnode(overlay_dtb->fdt, frag_off), frag_idx++) { const char *node_name; const char *frag_name; int target_off, overlay_off; DTBLOB_T clone_dtb; int idx; node_name = fdt_get_name(overlay_dtb->fdt, frag_off, NULL); if (strncmp(node_name, "fragment@", 9) != 0 && strncmp(node_name, "fragment-", 9) != 0) continue; frag_name = node_name + 9; // Find the target and overlay nodes overlay_off = fdt_subnode_offset(overlay_dtb->fdt, frag_off, "__overlay__"); if (overlay_off < 0) { if (fdt_subnode_offset(overlay_dtb->fdt, frag_off, "__dormant__") >= 0) dtoverlay_debug("fragment %s disabled", frag_name); else dtoverlay_error("no overlay in fragment %s", frag_name); continue; } target_off = dtoverlay_get_target_offset(NULL, overlay_dtb, frag_off); if (target_off < 0) continue; // Merge the fragment with the overlay // We can't just call dtoverlay_merge_fragment with the overlay_dtb // as source and destination because the source is not expected to // change. Instead, clone the overlay, apply the fragment, then switch. if (!overlay_copy) { overlay_copy = malloc(overlay_size); if (!overlay_copy) { err = -FDT_ERR_NOSPACE; break; } } memcpy(overlay_copy, overlay_dtb->fdt, overlay_size); memcpy(&clone_dtb, overlay_dtb, sizeof(DTBLOB_T)); clone_dtb.fdt = overlay_copy; err = dtoverlay_merge_fragment(&clone_dtb, target_off, overlay_dtb, overlay_off, 0); if (err) break; // Swap the buffers { void *temp = overlay_dtb->fdt; overlay_dtb->fdt = overlay_copy; overlay_copy = temp; } // Disable this fragment (and resync with the changed overlay) for (frag_off = fdt_first_subnode(overlay_dtb->fdt, 0), idx = 0; idx < frag_idx; frag_off = fdt_next_subnode(overlay_dtb->fdt, frag_off), idx++) continue; overlay_off = fdt_subnode_offset(overlay_dtb->fdt, frag_off, "__overlay__"); if (overlay_off >= 0) dtoverlay_set_node_name(overlay_dtb, overlay_off, "__dormant__"); // As the new name is the same length, the offsets are still valid } if (overlay_copy) free(overlay_copy); if (err || !base_dtb) goto no_base_dtb; for (frag_off = fdt_first_subnode(overlay_dtb->fdt, 0), frag_idx = 0; frag_off >= 0; frag_off = fdt_next_subnode(overlay_dtb->fdt, frag_off), frag_idx++) { const char *node_name; const char *frag_name; int target_off, overlay_off; node_name = fdt_get_name(overlay_dtb->fdt, frag_off, NULL); if (strcmp(node_name, "__symbols__") == 0) { /* At this point, only exported symbols should remain */ int symbols_off = dtoverlay_find_node(base_dtb, "/__symbols__", 0); dtoverlay_apply_overlay_paths(base_dtb, symbols_off, overlay_dtb, frag_off, "label"); continue; } else if (strncmp(node_name, "fragment@", 9) != 0 && strncmp(node_name, "fragment-", 9) != 0) { continue; } frag_name = node_name + 9; // Find the target and overlay nodes overlay_off = fdt_subnode_offset(overlay_dtb->fdt, frag_off, "__overlay__"); if (overlay_off < 0) { if (fdt_subnode_offset(overlay_dtb->fdt, frag_off, "__dormant__") >= 0) dtoverlay_debug("fragment %s disabled", frag_name); else dtoverlay_error("no overlay in fragment %s", frag_name); continue; } target_off = dtoverlay_get_target_offset(base_dtb, overlay_dtb, frag_off); if (target_off < 0) { err = NON_FATAL(target_off); break; } // Now do the merge node_name = fdt_get_name(base_dtb->fdt, target_off, NULL); if (node_name && strcmp(node_name, "aliases") == 0) err = dtoverlay_apply_overlay_paths(base_dtb, target_off, overlay_dtb, overlay_off, "alias"); else err = dtoverlay_merge_fragment(base_dtb, target_off, overlay_dtb, overlay_off, 0); } if (err == 0) base_dtb->max_phandle = overlay_dtb->max_phandle; no_base_dtb: if (err) dtoverlay_error("merge failed"); return err; } // Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors int dtoverlay_fixup_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb) { int err; // To do: Check the "compatible" string? err = dtoverlay_resolve_fixups(base_dtb, overlay_dtb); if (err >= 0) err = dtoverlay_resolve_phandles(base_dtb, overlay_dtb); overlay_dtb->fixups_applied = 1; return NON_FATAL(err); } // Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors int dtoverlay_merge_params(DTBLOB_T *dtb, const DTOVERLAY_PARAM_T *params, unsigned int num_params) { int err = 0; unsigned int i; for (i=0; (iparam; slash = strrchr(node_name, '/'); if (!slash) { err = NON_FATAL(FDT_ERR_BADPATH); break; } // Ensure that root properties ("/xxx") work if (slash == node_name) path_len = 1; else path_len = slash - node_name; // find node, create if it does not exist yet node_off = dtoverlay_create_node(dtb, node_name, path_len); if (node_off >= 0) { const char *prop_name = slash + 1; int prop_len; struct fdt_property *prop; if ((strcmp(prop_name, "bootargs") == 0) && ((prop = fdt_get_property_w(dtb->fdt, node_off, prop_name, &prop_len)) != NULL) && (prop_len > 0) && *prop->data) { prop->data[prop_len - 1] = ' '; err = fdt_appendprop(dtb->fdt, node_off, prop_name, p->b, p->len); } else err = fdt_setprop(dtb->fdt, node_off, prop_name, p->b, p->len); } else err = node_off; } return err; } int dtoverlay_filter_symbols(DTBLOB_T *dtb) { int symbols_off; int exports_off; struct str_item *exports = NULL; int prop_off; struct str_item { struct str_item *next; char str[0]; }; symbols_off = dtoverlay_find_node(dtb, "/__symbols__", 0); if (symbols_off < 0) return 0; exports_off = dtoverlay_find_node(dtb, "/__exports__", 0); if (exports_off < 0) { /* There are no exports, so keep all symbols private. */ fdt_del_node(dtb->fdt, symbols_off); return 0; } /* Internalise the names of the exported properties for speed * and to protect against the FDT contents moving. */ fdt_for_each_property_offset(prop_off, dtb->fdt, exports_off) { struct str_item *new_str; const char *name = NULL; fdt_getprop_by_offset(dtb->fdt, prop_off, &name, NULL); if (!name) break; new_str = malloc(sizeof(*new_str) + strlen(name) + 1); if (!new_str) { /* Free all of the internalised exports */ while (exports) { struct str_item *str = exports; exports = str->next; free(str); } dtoverlay_error(" out of memory"); return -FDT_ERR_NOSPACE; } strcpy(new_str->str, name); new_str->next = exports; exports = new_str; } /* Iterate through the symbols, deleting any that aren't * exported. */ prop_off = fdt_first_property_offset(dtb->fdt, symbols_off); while (prop_off >= 0) { const char *name = NULL; struct str_item *str; (void)fdt_getprop_by_offset(dtb->fdt, prop_off, &name, NULL); if (!name) break; for (str = exports; str; str = str->next) { if (!strcmp(str->str, name)) break; } if (str) /* This symbol is exported */ prop_off = fdt_next_property_offset(dtb->fdt, prop_off); else fdt_delprop(dtb->fdt, symbols_off, name); } /* Free all of the internalised exports */ while (exports) { struct str_item *str = exports; exports = str->next; free(str); } return 0; } /* Returns a pointer to the override data and (through data_len) its length. On error, sets *data_len to be the error code. */ const char *dtoverlay_find_override(DTBLOB_T *dtb, const char *override_name, int *data_len) { int overrides_off; const char *data; int len; // Find the table of overrides overrides_off = fdt_path_offset(dtb->fdt, "/__overrides__"); if (overrides_off < 0) { dtoverlay_debug("/__overrides__ node not found"); *data_len = overrides_off; return NULL; } // Locate the property data = fdt_getprop(dtb->fdt, overrides_off, override_name, &len); *data_len = len; if (data) dtoverlay_debug("found override %s", override_name); else dtoverlay_debug("/__overrides__ has no %s property", override_name); return data; } int hex_digit(char c) { if (c >= '0' && c <= '9') return c - '0'; else if (c >= 'A' && c <= 'F') return 10 + c - 'A'; else if (c >= 'a' && c <= 'f') return 10 + c - 'a'; else return -1; } int dtoverlay_override_one_target(int override_type, const char *override_value, DTBLOB_T *dtb, int node_off, const char *prop_name, int target_phandle, int target_off, int target_size, void *callback_state) { int err = 0; if (override_type == DTOVERRIDE_STRING) { char *prop_val; int prop_len; /* Replace the whole property with the string */ if ((strcmp(prop_name, "bootargs") == 0) && ((prop_val = fdt_getprop_w(dtb->fdt, node_off, prop_name, &prop_len)) != NULL) && (prop_len > 0) && prop_val[0]) { prop_val[prop_len - 1] = ' '; err = fdt_appendprop_string(dtb->fdt, node_off, prop_name, override_value); } else if (strcmp(prop_name, "name") == 0) // "name" is a pseudo-property { err = dtoverlay_set_node_name(dtb, node_off, override_value); } else err = fdt_setprop_string(dtb->fdt, node_off, prop_name, override_value); } else if (override_type == DTOVERRIDE_BYTE_STRING) { /* Replace the whole property with the byte string */ uint8_t bytes_buf[32]; // For efficiency/laziness, place a limit on the length const char *p = override_value; int byte_count = 0; while (*p) { int nib1, nib2; // whitespace and colons are legal separators if (*p == ':' || *p == ' ' || *p == '\t') { p++; continue; } nib1 = hex_digit(*p++); nib2 = hex_digit(*p++); if (nib1 < 0 || nib2 < 0) { dtoverlay_error("invalid bytestring '%s'", override_value); return NON_FATAL(FDT_ERR_BADVALUE); } if (byte_count == sizeof(bytes_buf)) { dtoverlay_error("bytestring '%s' too long", override_value); return NON_FATAL(FDT_ERR_BADVALUE); } bytes_buf[byte_count++] = (nib1 << 4) | nib2; } err = fdt_setprop(dtb->fdt, node_off, prop_name, bytes_buf, byte_count); } else if (override_type != DTOVERRIDE_END) { const char *p; char *end; char *prop_val; void *prop_buf = NULL; int prop_len; int new_prop_len; uint64_t override_int; uint32_t frag_num; /* Parse as an integer */ override_int = strtoull(override_value, &end, 0); if (end[0] != '\0') { if ((strcmp(override_value, "y") == 0) || (strcmp(override_value, "yes") == 0) || (strcmp(override_value, "on") == 0) || (strcmp(override_value, "true") == 0) || (strcmp(override_value, "down") == 0)) override_int = 1; else if ((strcmp(override_value, "n") == 0) || (strcmp(override_value, "no") == 0) || (strcmp(override_value, "off") == 0) || (strcmp(override_value, "false") == 0)) override_int = 0; else if (strcmp(override_value, "up") == 0) override_int = 2; else { dtoverlay_error("invalid override value '%s' - ignored", override_value); return NON_FATAL(FDT_ERR_INTERNAL); } } switch (override_type) { case DTOVERRIDE_INTEGER: /* Patch a word within the property */ prop_val = (void *)fdt_getprop(dtb->fdt, node_off, prop_name, &prop_len); new_prop_len = target_off + target_size; if (prop_len < new_prop_len) { /* This property either doesn't exist or isn't long enough. Create a buffer containing any existing property data with zero padding, which will later be patched and written back. */ prop_buf = calloc(1, new_prop_len); if (!prop_buf) { dtoverlay_error(" out of memory"); return NON_FATAL(FDT_ERR_NOSPACE); } if (prop_len > 0) memcpy(prop_buf, prop_val, prop_len); prop_val = prop_buf; } switch (target_size) { case 1: dtoverlay_write_u8(prop_val, target_off, (uint32_t)override_int); break; case 2: dtoverlay_write_u16(prop_val, target_off, (uint32_t)override_int); break; case 4: dtoverlay_write_u32(prop_val, target_off, (uint32_t)override_int); break; case 8: dtoverlay_write_u64(prop_val, target_off, override_int); break; default: break; } if (prop_buf) { /* Add/extend the property by setting it */ if (strcmp(prop_name, "reg") != 0) // Don't create or extend "reg" - it must be a pseudo-property err = fdt_setprop(dtb->fdt, node_off, prop_name, prop_buf, new_prop_len); free(prop_buf); } if (strcmp(prop_name, "reg") == 0 && target_off == 0) { const char *old_name = fdt_get_name(dtb->fdt, node_off, NULL); const char *atpos = strchr(old_name, '@'); if (atpos) { int name_len = (atpos - old_name); char *new_name = malloc(name_len + 1 + 16 + 1); if (!new_name) return -FDT_ERR_NOSPACE; sprintf(new_name, "%.*s@%x", name_len, old_name, (uint32_t)override_int); err = dtoverlay_set_node_name(dtb, node_off, new_name); free(new_name); } } break; case DTOVERRIDE_BOOLEAN: case DTOVERRIDE_BOOLEAN_INV: /* This is a boolean property (present->true, absent->false) */ if (override_int ^ (override_type == DTOVERRIDE_BOOLEAN_INV)) err = fdt_setprop(dtb->fdt, node_off, prop_name, NULL, 0); else { err = fdt_delprop(dtb->fdt, node_off, prop_name); if (err == -FDT_ERR_NOTFOUND) err = 0; } break; case DTOVERRIDE_OVERLAY: /* Apply the overlay-wide override. The supported options are ( is a fragment number): + Enable a fragment - Disable a fragment = Enable/disable the fragment according to the override value ! Disable/enable the fragment according to the inverse of the override value */ p = prop_name; while (*p && !err) { char type = *p; int frag_off; switch (type) { case '+': case '-': case '=': case '!': frag_num = strtoul(p + 1, &end, 0); if (end != p) { /* Change fragment@/__overlay__<->__dormant__ as necessary */ const char *states[2] = { "__dormant__", "__overlay__" }; char node_name[24]; int active = (type == '+') || ((type == '=') && (override_int != 0)) || ((type == '!') && (override_int == 0)); snprintf(node_name, sizeof(node_name), "/fragment@%u", frag_num); frag_off = fdt_path_offset(dtb->fdt, node_name); if (frag_off < 0) { snprintf(node_name, sizeof(node_name), "/fragment-%u", frag_num); frag_off = fdt_path_offset(dtb->fdt, node_name); } if (frag_off >= 0) { frag_off = fdt_subnode_offset(dtb->fdt, frag_off, states[!active]); if (frag_off >= 0) (void)dtoverlay_set_node_name(dtb, frag_off, states[active]); } else { dtoverlay_error(" fragment %u not found", frag_num); err = NON_FATAL(frag_off); } p = end; } else { dtoverlay_error(" invalid overlay override '%s'", prop_name); err = NON_FATAL(FDT_ERR_BADVALUE); } break; default: err = NON_FATAL(FDT_ERR_BADVALUE); break; } } break; } } return err; } /* The problem is the split between inline string values and inline cell values passed to the callback. For strings properties the returned data is strings; no conversion from cells is required. The special handling for "status" is performed before the callback. For all other property types the returned values are binary/opaque data. Any string data should have been converted to binary data already in the framework. Translation: 1. The override value (the value assigned to the parameter) is always a string. 2. Strings are converted according to type of the parameter at the point of use. 3. A single override value can result in multiple different values being assigned to properties as the result of type conversions and set lookups. 4. Cell literals have a binary value. 5. Lookups convert strings to either a string or a cell literal. 6. Cell literals are primarily (only?) useful for label references, which are really just integers. There is nothing stopping them (or other integers) being converted to strings. 7. Therefore dtoverlay_extract_override always returns a string value, either the input override value, a literal, or the result of a lookup. */ // Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors // After calling this, assume all node offsets are no longer valid int dtoverlay_foreach_override_target(DTBLOB_T *dtb, const char *override_name, const char *override_data, int data_len, const char *override_value, override_callback_t callback, void *callback_state) { int err = 0; int target_phandle = 0; char *data_buf, *data, *data_end; /* Short-circuit the degenerate case of an empty parameter, avoiding an apparent memory allocation failure. */ if (!data_len) return 0; /* Copy the override data in case it moves */ data_buf = malloc(data_len); if (!data_buf) { dtoverlay_error(" out of memory"); return NON_FATAL(FDT_ERR_NOSPACE); } memcpy(data_buf, override_data, data_len); data = data_buf; data_end = data + data_len; while (err == 0) { const char *target_prop = NULL; static char prop_name[256]; static char target_value[256]; int name_len = 0; int target_off = 0; int target_size = 0; int override_type; int node_off = 0; strcpy(target_value, override_value); override_type = dtoverlay_extract_override(override_name, target_value, sizeof(target_value), &target_phandle, (const char **)&data, data_end, &target_prop, &name_len, &target_off, &target_size); if (override_type < 0) { err = override_type; break; } /* Pass DTOVERRIDE_END to the callback, in case it is interested */ if (target_phandle != 0) { node_off = fdt_node_offset_by_phandle(dtb->fdt, target_phandle); if (node_off < 0) { dtoverlay_error(" phandle %d not found", target_phandle); err = NON_FATAL(node_off); break; } } /* Sadly there are no '_namelen' setprop variants, so copies are required */ if (target_prop) { memcpy(prop_name, target_prop, name_len); prop_name[name_len] = '\0'; } err = callback(override_type, target_value, dtb, node_off, prop_name, target_phandle, target_off, target_size, callback_state); if (override_type == DTOVERRIDE_END) break; } free(data_buf); return err; } // Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors int dtoverlay_apply_override(DTBLOB_T *dtb, const char *override_name, const char *override_data, int data_len, const char *override_value) { return dtoverlay_foreach_override_target(dtb, override_name, override_data, data_len, override_value, dtoverlay_override_one_target, NULL); } /* Returns an override type (DTOVERRIDE_INTEGER, DTOVERRIDE_BOOLEAN, DTOVERRIDE_STRING, DTOVERRIDE_OVERLAY), DTOVERRIDE_END (0) at the end, or an error code (< 0) */ static int dtoverlay_extract_override(const char *override_name, char *override_value, int value_size, int *phandle_ptr, const char **datap, const char *data_end, const char **namep, int *namelenp, int *offp, int *sizep) { const char *data; const char *prop_name, *override_end; int len, override_len, name_len, target_len, phandle; const char *offset_seps = ".;:#?![{="; const char *literal_value = NULL; char literal_type = '?'; int type; data = *datap; len = data_end - data; if (len <= 0) { if (len < 0) return len; *phandle_ptr = 0; *namep = NULL; return DTOVERRIDE_END; } // Check for space for a phandle, a terminating NUL and at least one char if (len < (sizeof(fdt32_t) + 1 + 1)) { dtoverlay_error(" override %s: data is truncated or mangled", override_name); return -FDT_ERR_BADSTRUCTURE; } phandle = dtoverlay_read_u32(data, 0); *phandle_ptr = phandle; data += sizeof(fdt32_t); len -= sizeof(fdt32_t); override_end = memchr(data, 0, len); if (!override_end) { dtoverlay_error(" override %s: string is not NUL-terminated", override_name); return -FDT_ERR_BADSTRUCTURE; } prop_name = data; override_len = override_end - prop_name; data += (override_len + 1); *datap = data; if (phandle <= 0) { if (phandle < 0) return -FDT_ERR_BADPHANDLE; /* This is an "overlay" override, signalled using <0> as the phandle. */ *namep = prop_name; *namelenp = override_len; return DTOVERRIDE_OVERLAY; } target_len = strcspn(prop_name, "={"); name_len = strcspn(prop_name, offset_seps); *namep = prop_name; *namelenp = name_len; if (target_len < override_len) { /* Literal assignment or lookup table * Can't have '=' and '{' (or at least, don't need to support it. * = is an override value replacement * { is an override value transformation */ literal_type = prop_name[target_len]; literal_value = prop_name + target_len + 1; } if (name_len < target_len) { /* There is a separator specified */ char sep = prop_name[name_len]; if (sep == '?') { /* The target is a boolean parameter (present->true, absent->false) */ *offp = 0; *sizep = 0; type = DTOVERRIDE_BOOLEAN; dtoverlay_debug(" override %s: boolean target %.*s", override_name, name_len, prop_name); } else if (sep == '!') { /* The target is a boolean parameter (present->true, absent->false), * but the sense of the value is inverted */ *offp = 0; *sizep = 0; type = DTOVERRIDE_BOOLEAN_INV; dtoverlay_debug(" override %s: inverted boolean target %.*s", override_name, name_len, prop_name); } else if (sep == '[') { /* The target is a byte-string */ *offp = -1; *sizep = 0; type = DTOVERRIDE_BYTE_STRING; dtoverlay_debug(" override %s: byte-string target %.*s", override_name, name_len, prop_name); } else { /* The target is a cell/integer */ *offp = atoi(prop_name + name_len + 1); *sizep = 1 << (strchr(offset_seps, sep) - offset_seps); type = DTOVERRIDE_INTEGER; dtoverlay_debug(" override %s: cell target %.*s @ offset %d (size %d)", override_name, name_len, prop_name, *offp, *sizep); } } else { *offp = -1; *sizep = 0; type = DTOVERRIDE_STRING; dtoverlay_debug(" override %s: string target '%.*s'", override_name, name_len, prop_name); } if (literal_value) { if (literal_type == '=') { /* Immediate value */ if (type == DTOVERRIDE_STRING || type == DTOVERRIDE_BYTE_STRING || literal_value[0]) { /* String */ strcpy(override_value, literal_value); } else { /* Cell */ sprintf(override_value, "%d", dtoverlay_read_u32(data, 0)); *datap = data + 4; } } else if (literal_type == '{') { /* Lookup */ data = dtoverlay_lookup_key(literal_value, data_end, override_value, override_value, value_size); *datap = data; if (!data) return -FDT_ERR_BADSTRUCTURE; } else { return -FDT_ERR_INTERNAL; } } if ((type == DTOVERRIDE_STRING) && (strmemcmp(prop_name, name_len, "status") == 0)) { /* Convert booleans to okay/disabled */ if ((strcmp(override_value, "y") == 0) || (strcmp(override_value, "yes") == 0) || (strcmp(override_value, "on") == 0) || (strcmp(override_value, "true") == 0) || (strcmp(override_value, "enable") == 0) || (strcmp(override_value, "1") == 0)) strcpy(override_value, "okay"); else if ((strcmp(override_value, "n") == 0) || (strcmp(override_value, "no") == 0) || (strcmp(override_value, "off") == 0) || (strcmp(override_value, "false") == 0) || (strcmp(override_value, "0") == 0)) strcpy(override_value, "disabled"); } return type; } /* Read the string or (if permitted) cell value, storing the result in buf. Returns a pointer to the first byte after the successfully parsed immediate, or NULL on error. */ static const char *dtoverlay_extract_immediate(const char *data, const char *data_end, char *buf, int buf_len) { if ((data + 1) < data_end && !data[0]) { uint32_t val; data++; if (data + 4 > data_end) { dtoverlay_error(" truncated cell immediate"); return NULL; } val = dtoverlay_read_u32(data, 0); if (buf) snprintf(buf, buf_len, "%d", val); data += 4; } else if (data[0] == '\'') { // Continue to closing "'", error on end-of-string int len; data++; len = strcspn(data, "'"); if (!data[len]) { dtoverlay_error(" unterminated quoted string: '%s", data); return NULL; } if (len >= buf_len) { dtoverlay_error(" immediate string too long: '%s", data); return NULL; } if (buf) { memcpy(buf, data, len); buf[len] = '\0'; } data += len + 1; if (*data == ',') // Skip a comma, preserve a brace data++; } else { // Continue to a comma, right brace or end-of-string NUL int len = strcspn(data, ",}"); if (len >= buf_len) { dtoverlay_error(" immediate string too long: '%s", data); return NULL; } if (buf) { memcpy(buf, data, len); buf[len] = '\0'; } data += len; if (*data == ',') // Skip a comma, preserve a brace data++; } return data; } static const char *dtoverlay_lookup_key(const char *lookup_string, const char *data_end, const char *key, char *buf, int buf_len) { const char *p = lookup_string; int found = 0; while (p < data_end && *p && *p != '}') { int key_len = strcspn(p, "=,}"); char *q = NULL; char sep = p[key_len]; if (!key_len) { if (sep) // default value { if (!found) { q = buf; found = 2; } } } else { if (found != 1 && strmemcmp(p, key_len, key) == 0) { q = buf; found = 1; } } p += key_len; if (sep == '=') { p = dtoverlay_extract_immediate(p + 1, data_end, q, buf_len); } else { if (q && q != key) { strncpy(q, key, buf_len); q[buf_len - 1] = 0; } if (sep == ',') p++; } } if (!found) { dtoverlay_error("lookup -> no match for '%s'", key); return NULL; } if (p == data_end) return p; if (!*p) { dtoverlay_error(" malformed lookup"); return NULL; } assert(p[0] != 0 && p[1] == 0); return p + 2; } int dtoverlay_set_synonym(DTBLOB_T *dtb, const char *dst, const char *src) { /* Add/update all aliases, symbols and overrides named dst to be equivalent to those named src. An absent src is ignored. */ int err; err = dtoverlay_dup_property(dtb, "/aliases", dst, src); if (err == 0) err = dtoverlay_dup_property(dtb, "/__symbols__", dst, src); if (err == 0) dtoverlay_dup_property(dtb, "/__overrides__", dst, src); return err; } int dtoverlay_dup_property(DTBLOB_T *dtb, const char *node_name, const char *dst, const char *src) { /* Find the node and src property */ const DTBLOB_T *src_prop; int node_off; int prop_len = 0; int err = 0; node_off = fdt_path_offset(dtb->fdt, node_name); if (node_off < 0) return 0; src_prop = fdt_getprop(dtb->fdt, node_off, src, &prop_len); if (!src_prop) return 0; err = fdt_setprop_inplace(dtb->fdt, node_off, dst, src_prop, prop_len); if (err != 0) { void *prop_data; /* Copy the src property, just in case things move */ prop_data = malloc(prop_len); memcpy(prop_data, src_prop, prop_len); err = fdt_setprop(dtb->fdt, node_off, dst, prop_data, prop_len); free(prop_data); } if (err == 0) dtoverlay_debug("%s:%s=%s", node_name, dst, src); return err; } int dtoverlay_find_pins_for_device(DTBLOB_T *dtb, const char *symbol, PIN_ITER_T *iter) { int pos = dtoverlay_find_symbol(dtb, symbol); memset(iter, 0, sizeof(*iter)); if (pos < 0) return pos; iter->dtb = dtb; if (dtoverlay_node_is_enabled(dtb, pos)) iter->pinctrl = dtoverlay_get_property(dtb, pos, "pinctrl-0", &iter->pinctrl_len); return 0; } int dtoverlay_next_pin(PIN_ITER_T *iter, int *pin, int *func, int *pull) { if (pin) *pin = -1; if (func) *func = -1; if (pull) *pull = -1; while (1) { int phandle, pos; if ((iter->pin_off) + 4 <= iter->pins_len) { int off = iter->pin_off; *pin = GETBE4(iter->pins, off); if (func && iter->funcs_len) *func = GETBE4(iter->funcs, (iter->funcs_len > 4) ? off : 0); if (pull && iter->pulls_len) *pull = GETBE4(iter->pulls, (iter->pulls_len > 4) ? off : 0); iter->pin_off = off + 4; return 1; } if ((iter->pinctrl_off + 4) > iter->pinctrl_len) break; phandle = GETBE4(iter->pinctrl, iter->pinctrl_off); iter->pinctrl_off += 4; pos = dtoverlay_find_phandle(iter->dtb, phandle); iter->pins = dtoverlay_get_property(iter->dtb, pos, "brcm,pins", &iter->pins_len); iter->funcs = dtoverlay_get_property(iter->dtb, pos, "brcm,function", &iter->funcs_len); iter->pulls = dtoverlay_get_property(iter->dtb, pos, "brcm,pull", &iter->pulls_len); iter->pin_off = 0; } return 0; } DTBLOB_T *dtoverlay_create_dtb(int max_size) { DTBLOB_T *dtb = NULL; void *fdt = NULL; fdt = malloc(max_size); if (!fdt) { dtoverlay_error("out of memory"); goto error_exit; } if (fdt_create_empty_tree(fdt, max_size) != 0) { dtoverlay_error("failed to create empty dtb"); goto error_exit; } dtb = calloc(1, sizeof(DTBLOB_T)); if (!dtb) { dtoverlay_error("out of memory"); goto error_exit; } dtb->fdt = fdt; dtb->max_phandle = 0; // Not a valid phandle return dtb; error_exit: free(fdt); if (dtb) free(dtb->trailer); free(dtb); return NULL; } DTBLOB_T *dtoverlay_load_dtb_from_fp(FILE *fp, int max_size) { DTBLOB_T *dtb = NULL; void *fdt = NULL; if (fp) { long len; long bytes_read; int dtb_len; fseek(fp, 0, SEEK_END); len = ftell(fp); fseek(fp, 0, SEEK_SET); if (max_size > 0) { if (max_size < len) { dtoverlay_error("file too large (%d bytes) for max_size", len); goto error_exit; } } else if (max_size < 0) { max_size = len - max_size; } else { max_size = len; } fdt = malloc(max_size); if (!fdt) { dtoverlay_error("out of memory"); goto error_exit; } bytes_read = fread(fdt, 1, len, fp); fclose(fp); if (bytes_read != len) { dtoverlay_error("fread failed"); goto error_exit; } // Record the total size before any expansion dtb_len = fdt_totalsize(fdt); dtb = dtoverlay_import_fdt(fdt, max_size); if (!dtb) goto error_exit; dtb->fdt_is_malloced = 1; if (len > dtb_len) { /* Load the trailer */ dtb->trailer_len = len - dtb_len; dtb->trailer = malloc(dtb->trailer_len); if (!dtb->trailer) { dtoverlay_error("out of memory"); goto error_exit; } dtb->trailer_is_malloced = 1; memcpy(dtb->trailer, (char *)fdt + dtb_len, dtb->trailer_len); } } return dtb; error_exit: free(fdt); if (dtb) free(dtb->trailer); free(dtb); return NULL; } DTBLOB_T *dtoverlay_load_dtb(const char *filename, int max_size) { FILE *fp = fopen(filename, "rb"); if (fp) return dtoverlay_load_dtb_from_fp(fp, max_size); dtoverlay_error("failed to open '%s'", filename); return NULL; } void dtoverlay_init_map_from_fp(FILE *fp, const char *compatible, int compatible_len) { if (!compatible) return; while (compatible_len > 0) { const char *p; int len; // Look for a string containing a comma p = memchr(compatible, ',', compatible_len); if (p) { p++; len = compatible + compatible_len - p; } else { // Otherwise treat it as a simple string p = compatible; len = compatible_len; } /* Group the members of the BCM2835 family */ if (strncmp(p, "bcm2708", len) == 0 || strncmp(p, "bcm2709", len) == 0 || strncmp(p, "bcm2710", len) == 0 || strncmp(p, "bcm2835", len) == 0 || strncmp(p, "bcm2836", len) == 0 || strncmp(p, "bcm2837", len) == 0) { platform_name = "bcm2835"; break; } else if (strncmp(p, "bcm2711", len) == 0) { platform_name = "bcm2711"; break; } compatible_len -= (p - compatible); compatible = p; len = strnlen(compatible, compatible_len) + 1; compatible += len; compatible_len -= len; } if (platform_name) { dtoverlay_debug("using platform '%s'", platform_name); platform_name_len = strlen(platform_name); if (fp) overlay_map = dtoverlay_load_dtb_from_fp(fp, 0); } else { dtoverlay_warn("no matching platform found"); } dtoverlay_debug("overlay map %sloaded", overlay_map ? "" : "not "); } void dtoverlay_init_map(const char *overlay_dir, const char *compatible, int compatible_len) { char map_file[DTOVERLAY_MAX_PATH]; int dir_len = strlen(overlay_dir); FILE *fp; static int tried; if (tried) return; tried = 1; if (!compatible) return; /* Handle the possibility that the supplied directory may or may not end with a slash */ sprintf(map_file, "%s%soverlay_map.dtb", overlay_dir, (!dir_len || overlay_dir[dir_len - 1] != '/') ? "/" : ""); fp = fopen(map_file, "rb"); dtoverlay_init_map_from_fp(fp, compatible, compatible_len); } const char *dtoverlay_remap_overlay(const char *overlay) { while (overlay_map) { const char *deprecated_msg; const char *new_name; int root_off; int overlay_off; int prop_len; root_off = fdt_path_offset(overlay_map->fdt, "/"); overlay_off = fdt_subnode_offset(overlay_map->fdt, root_off, overlay); if (overlay_off < 0) break; new_name = fdt_getprop_namelen(overlay_map->fdt, overlay_off, platform_name, platform_name_len, &prop_len); if (new_name) { if (new_name[0]) overlay = new_name; break; } // Has it been renamed or deprecated? new_name = fdt_getprop_namelen(overlay_map->fdt, overlay_off, "renamed", 7, &prop_len); if (new_name) { dtoverlay_warn("overlay '%s' has been renamed '%s'", overlay, new_name); overlay = new_name; continue; } deprecated_msg = fdt_getprop_namelen(overlay_map->fdt, overlay_off, "deprecated", 10, &prop_len); if (deprecated_msg) dtoverlay_error("overlay '%s' is deprecated: %s", overlay, deprecated_msg); else dtoverlay_error("overlay '%s' is not supported on the '%s' platform", overlay, platform_name); return NULL; } return overlay; } DTBLOB_T *dtoverlay_import_fdt(void *fdt, int buf_size) { DTBLOB_T *dtb = NULL; int node_off; int dtb_len; int err; err = fdt_check_header(fdt); if (err != 0) { dtoverlay_error("not a valid FDT - err %d", err); goto error_exit; } dtb_len = fdt_totalsize(fdt); if (buf_size < dtb_len) { dtoverlay_error("fdt is too large"); err = -FDT_ERR_NOSPACE; goto error_exit; } if (buf_size > dtb_len) fdt_set_totalsize(fdt, buf_size); dtb = calloc(1, sizeof(DTBLOB_T)); if (!dtb) { dtoverlay_error("out of memory"); goto error_exit; } dtb->fdt = fdt; dtb->max_phandle = 0; // Not a valid phandle // Find the minimum and maximum phandles, in case it is necessary to // relocate existing ones or create new ones. for (node_off = 0; node_off >= 0; node_off = fdt_next_node(fdt, node_off, NULL)) { uint32_t phandle = fdt_get_phandle(fdt, node_off); if (phandle > dtb->max_phandle) dtb->max_phandle = phandle; } error_exit: return dtb; } int dtoverlay_save_dtb(const DTBLOB_T *dtb, const char *filename) { FILE *fp = fopen(filename, "wb"); int err = 0; if (fp) { long len = fdt_totalsize(dtb->fdt); if (len != fwrite(dtb->fdt, 1, len, fp)) { dtoverlay_error("fwrite failed"); err = -2; goto error_exit; } if (dtb->trailer_len && (fwrite(dtb->trailer, 1, dtb->trailer_len, fp) != dtb->trailer_len)) { dtoverlay_error("fwrite failed"); err = -2; goto error_exit; } dtoverlay_debug("wrote %ld bytes to '%s'", len, filename); fclose(fp); } else { dtoverlay_debug("failed to create '%s'", filename); err = -1; } error_exit: return err; } int dtoverlay_extend_dtb(DTBLOB_T *dtb, int new_size) { int size = fdt_totalsize(dtb->fdt); int err = 0; if (new_size < 0) new_size = size - new_size; if (new_size > size) { void *fdt; fdt = malloc(new_size); if (fdt) { memcpy(fdt, dtb->fdt, size); fdt_set_totalsize(fdt, new_size); if (dtb->fdt_is_malloced) free(dtb->fdt); dtb->fdt = fdt; dtb->fdt_is_malloced = 1; } else { err = -FDT_ERR_NOSPACE; } } else if (new_size < size) { /* Can't shrink it */ err = -FDT_ERR_NOSPACE; } return err; } int dtoverlay_dtb_totalsize(DTBLOB_T *dtb) { return fdt_totalsize(dtb->fdt); } void dtoverlay_pack_dtb(DTBLOB_T *dtb) { fdt_pack(dtb->fdt); } void dtoverlay_free_dtb(DTBLOB_T *dtb) { if (dtb) { if (dtb->fdt_is_malloced) free(dtb->fdt); if (dtb->trailer_is_malloced) free(dtb->trailer); free(dtb); } } int dtoverlay_find_phandle(DTBLOB_T *dtb, int phandle) { return fdt_node_offset_by_phandle(dtb->fdt, phandle); } int dtoverlay_find_symbol(DTBLOB_T *dtb, const char *symbol_name) { int symbols_off, path_len; const char *node_path; node_path = dtoverlay_get_alias(dtb, symbol_name); if (node_path) { path_len = strlen(node_path); } else { symbols_off = fdt_path_offset(dtb->fdt, "/__symbols__"); if (symbols_off < 0) { dtoverlay_error("no symbols found"); return -FDT_ERR_NOTFOUND; } node_path = fdt_getprop(dtb->fdt, symbols_off, symbol_name, &path_len); if (path_len < 0) return -FDT_ERR_NOTFOUND; //Ensure we don't have trailing NULLs if (path_len > strnlen(node_path, path_len)) path_len = strnlen(node_path, path_len); } return fdt_path_offset_namelen(dtb->fdt, node_path, path_len); } int dtoverlay_find_matching_node(DTBLOB_T *dtb, const char **node_names, int pos) { while (1) { const char *node_name; pos = fdt_next_node(dtb->fdt, pos, NULL); if (pos < 0) break; node_name = fdt_get_name(dtb->fdt, pos, NULL); if (node_name) { int i; for (i = 0; node_names[i]; i++) { const char *node = node_names[i]; int matchlen = strlen(node); if ((strncmp(node_name, node, matchlen) == 0) && ((node[matchlen] == '\0') || (node[matchlen] == '@'))) return pos; } } } return -1; } int dtoverlay_node_is_enabled(DTBLOB_T *dtb, int pos) { if (pos >= 0) { const void *prop = dtoverlay_get_property(dtb, pos, "status", NULL); if (prop && ((strcmp((const char *)prop, "okay") == 0) || (strcmp((const char *)prop, "ok") == 0))) return 1; } return 0; } const void *dtoverlay_get_property(DTBLOB_T *dtb, int pos, const char *prop_name, int *prop_len) { return fdt_getprop(dtb->fdt, pos, prop_name, prop_len); } int dtoverlay_set_property(DTBLOB_T *dtb, int pos, const char *prop_name, const void *prop, int prop_len) { int err = fdt_setprop(dtb->fdt, pos, prop_name, prop, prop_len); if (err < 0) dtoverlay_error("failed to set property '%s'", prop_name); return err; } const char *dtoverlay_get_alias(DTBLOB_T *dtb, const char *alias_name) { int node_off; int prop_len; const char *alias; node_off = fdt_path_offset(dtb->fdt, "/aliases"); alias = fdt_getprop(dtb->fdt, node_off, alias_name, &prop_len); if (alias && !prop_len) alias = ""; return alias; } int dtoverlay_set_alias(DTBLOB_T *dtb, const char *alias_name, const char *value) { int node_off; node_off = fdt_path_offset(dtb->fdt, "/aliases"); if (node_off < 0) node_off = fdt_add_subnode(dtb->fdt, 0, "aliases"); return fdt_setprop_string(dtb->fdt, node_off, alias_name, value); } void dtoverlay_set_logging_func(DTOVERLAY_LOGGING_FUNC *func) { dtoverlay_logging_func = func; } void dtoverlay_enable_debug(int enable) { dtoverlay_debug_enabled = enable; } void dtoverlay_error(const char *fmt, ...) { va_list args; va_start(args, fmt); (*dtoverlay_logging_func)(DTOVERLAY_ERROR, fmt, args); va_end(args); } void dtoverlay_warn(const char *fmt, ...) { va_list args; va_start(args, fmt); (*dtoverlay_logging_func)(DTOVERLAY_WARN, fmt, args); va_end(args); } void dtoverlay_debug(const char *fmt, ...) { va_list args; if (dtoverlay_debug_enabled) { va_start(args, fmt); (*dtoverlay_logging_func)(DTOVERLAY_DEBUG, fmt, args); va_end(args); } } static void dtoverlay_stdio_logging(dtoverlay_logging_type_t type, const char *fmt, va_list args) { const char *type_str; switch (type) { case DTOVERLAY_ERROR: type_str = "error"; break; case DTOVERLAY_WARN: type_str = "warn"; break; case DTOVERLAY_DEBUG: type_str = "debug"; break; default: type_str = "?"; } fprintf(stderr, "DTOVERLAY[%s]: ", type_str); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); } userland/helpers/dtoverlay/dtoverlay.h000066400000000000000000000211321421703157200205310ustar00rootroot00000000000000/* Copyright (c) 2016 Raspberry Pi (Trading) Ltd. 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 name of the copyright holder nor the names of its 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. */ #ifndef DTOVERLAY_H #define DTOVERLAY_H #include #define BE4(x) ((x)>>24)&0xff, ((x)>>16)&0xff, ((x)>>8)&0xff, ((x)>>0)&0xff #define GETBE4(p, off) ((((unsigned char *)p)[off + 0]<<24) + (((unsigned char *)p)[off + 1]<<16) + \ (((unsigned char *)p)[off + 2]<<8) + (((unsigned char *)p)[off + 3]<<0)) #define SETBE4(p, off, x) do { \ ((unsigned char *)p)[off + 0] = ((x>>24) & 0xff); \ ((unsigned char *)p)[off + 1] = ((x>>16) & 0xff); \ ((unsigned char *)p)[off + 2] = ((x>>8) & 0xff); \ ((unsigned char *)p)[off + 3] = ((x>>0) & 0xff); \ } while (0) #define NON_FATAL(err) (((err) < 0) ? -(err) : (err)) #define IS_FATAL(err) ((err) < 0) #define ONLY_FATAL(err) (IS_FATAL(err) ? (err) : 0) #define DTOVERLAY_PADDING(size) (-(size)) #define DTOVERLAY_MAX_PATH 256 typedef enum { DTOVERLAY_ERROR, DTOVERLAY_DEBUG, DTOVERLAY_WARN, // Append to preserve backwards compatibility } dtoverlay_logging_type_t; typedef struct dtoverlay_struct { const char *param; int len; const char *b; } DTOVERLAY_PARAM_T; typedef struct dtblob_struct { void *fdt; char fdt_is_malloced; char trailer_is_malloced; char fixups_applied; int min_phandle; int max_phandle; void *trailer; int trailer_len; } DTBLOB_T; typedef struct pin_iter_struct { DTBLOB_T *dtb; const void *pinctrl; int pinctrl_len; int pinctrl_off; const void *pins; const void *funcs; const void *pulls; int pins_len; int pin_off; int funcs_len; int pulls_len; } PIN_ITER_T; typedef void DTOVERLAY_LOGGING_FUNC(dtoverlay_logging_type_t type, const char *fmt, va_list args); typedef int (*override_callback_t)(int override_type, const char *override_value, DTBLOB_T *dtb, int node_off, const char *prop_name, int target_phandle, int target_off, int target_size, void *callback_state); uint8_t dtoverlay_read_u8(const void *src, int off); uint16_t dtoverlay_read_u16(const void *src, int off); uint32_t dtoverlay_read_u32(const void *src, int off); uint64_t dtoverlay_read_u64(const void *src, int off); void dtoverlay_write_u8(void *dst, int off, uint32_t val); void dtoverlay_write_u16(void *dst, int off, uint32_t val); void dtoverlay_write_u32(void *dst, int off, uint32_t val); void dtoverlay_write_u64(void *dst, int off, uint64_t val); /* Return values: -ve = fatal error, positive = non-fatal error */ int dtoverlay_create_node(DTBLOB_T *dtb, const char *node_name, int path_len); int dtoverlay_delete_node(DTBLOB_T *dtb, const char *node_name, int path_len); int dtoverlay_find_node(DTBLOB_T *dtb, const char *node_path, int path_len); int dtoverlay_set_node_properties(DTBLOB_T *dtb, const char *node_path, DTOVERLAY_PARAM_T *properties, unsigned int num_properties); int dtoverlay_create_prop_fragment(DTBLOB_T *dtb, int idx, int target_phandle, const char *prop_name, const void *prop_data, int prop_len); int dtoverlay_fixup_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb); int dtoverlay_merge_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb); int dtoverlay_merge_params(DTBLOB_T *dtb, const DTOVERLAY_PARAM_T *params, unsigned int num_params); int dtoverlay_filter_symbols(DTBLOB_T *dtb); const char *dtoverlay_find_override(DTBLOB_T *dtb, const char *override_name, int *data_len); int dtoverlay_override_one_target(int override_type, const char *override_value, DTBLOB_T *dtb, int node_off, const char *prop_name, int target_phandle, int target_off, int target_size, void *callback_state); int dtoverlay_foreach_override_target(DTBLOB_T *dtb, const char *override_name, const char *override_data, int data_len, const char *override_value, override_callback_t callback, void *callback_value); int dtoverlay_apply_override(DTBLOB_T *dtb, const char *override_name, const char *override_data, int data_len, const char *override_value); int dtoverlay_set_synonym(DTBLOB_T *dtb, const char *dst, const char *src); int dtoverlay_dup_property(DTBLOB_T *dtb, const char *node_name, const char *dst, const char *src); DTBLOB_T *dtoverlay_create_dtb(int max_size); DTBLOB_T *dtoverlay_load_dtb_from_fp(FILE *fp, int max_size); DTBLOB_T *dtoverlay_load_dtb(const char *filename, int max_size); void dtoverlay_init_map_from_fp(FILE *fp, const char *compatible, int compatible_len); void dtoverlay_init_map(const char *overlay_dir, const char *compatible, int compatible_len); const char *dtoverlay_remap_overlay(const char *overlay); DTBLOB_T *dtoverlay_import_fdt(void *fdt, int max_size); int dtoverlay_save_dtb(const DTBLOB_T *dtb, const char *filename); int dtoverlay_extend_dtb(DTBLOB_T *dtb, int new_size); int dtoverlay_dtb_totalsize(DTBLOB_T *dtb); void dtoverlay_pack_dtb(DTBLOB_T *dtb); void dtoverlay_free_dtb(DTBLOB_T *dtb); static inline void *dtoverlay_dtb_trailer(DTBLOB_T *dtb) { return dtb->trailer; } static inline int dtoverlay_dtb_trailer_len(DTBLOB_T *dtb) { return dtb->trailer_len; } static inline void dtoverlay_dtb_set_trailer(DTBLOB_T *dtb, void *trailer, int trailer_len) { dtb->trailer = trailer; dtb->trailer_len = trailer_len; dtb->trailer_is_malloced = 0; } int dtoverlay_find_pins_for_device(DTBLOB_T *dtb, const char *symbol, PIN_ITER_T *iter); int dtoverlay_next_pin(PIN_ITER_T *iter, int *pin, int *func, int *pull); int dtoverlay_find_phandle(DTBLOB_T *dtb, int phandle); int dtoverlay_find_symbol(DTBLOB_T *dtb, const char *symbol_name); int dtoverlay_find_matching_node(DTBLOB_T *dtb, const char **node_names, int pos); int dtoverlay_node_is_enabled(DTBLOB_T *dtb, int pos); const void *dtoverlay_get_property(DTBLOB_T *dtb, int pos, const char *prop_name, int *prop_len); int dtoverlay_set_property(DTBLOB_T *dtb, int pos, const char *prop_name, const void *prop, int prop_len); const char *dtoverlay_get_alias(DTBLOB_T *dtb, const char *alias_name); int dtoverlay_set_alias(DTBLOB_T *dtb, const char *alias_name, const char *value); void dtoverlay_set_logging_func(DTOVERLAY_LOGGING_FUNC *func); void dtoverlay_enable_debug(int enable); void dtoverlay_error(const char *fmt, ...); void dtoverlay_warn(const char *fmt, ...); void dtoverlay_debug(const char *fmt, ...); #endif userland/helpers/v3d/000077500000000000000000000000001421703157200150335ustar00rootroot00000000000000userland/helpers/v3d/v3d_common.h000066400000000000000000000035271421703157200172570ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef V3D_COMMON_H #define V3D_COMMON_H #include "helpers/v3d/v3d_ver.h" #include "interface/vcos/vcos_assert.h" #include "interface/vcos/vcos_stdbool.h" #include "interface/vcos/vcos_stdint.h" #include "interface/vcos/vcos_types.h" /* for vcos_unused and VCOS_STATIC_INLINE */ typedef uint32_t V3D_ADDR_T; typedef uint16_t float16_t; #endif userland/helpers/v3d/v3d_ver.h000066400000000000000000000047621421703157200165650ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef V3D_VER_H #define V3D_VER_H /* if V3D_TECH_VERSION/V3D_REVISION aren't defined, try to figure them out from * other defines... */ #ifndef V3D_TECH_VERSION #define V3D_TECH_VERSION 2 #endif #ifndef V3D_REVISION #ifdef __BCM2708A0__ #ifdef HERA /* bcg a1, hera */ #define V3D_REVISION 1 #else /* 2708 a0 */ #define V3D_REVISION 0 #endif #elif defined(__BCM2708B0__) /* 2708 b0 */ #define V3D_REVISION 2 #elif defined(__BCM2708C0__) || defined(__BCM2708C1__) /* 2708 c0/1 */ #define V3D_REVISION 3 #else /* capri */ #define V3D_REVISION 6 #endif #endif #define V3D_MAKE_VER(TECH_VERSION, REVISION) (((TECH_VERSION) * 100) + (REVISION)) #define V3D_VER (V3D_MAKE_VER(V3D_TECH_VERSION, V3D_REVISION)) #define V3D_VER_AT_LEAST(TECH_VERSION, REVISION) (V3D_VER >= V3D_MAKE_VER(TECH_VERSION, REVISION)) /* TODO this is temporary */ #if V3D_VER == V3D_MAKE_VER(9, 9) #define V3D_VER_D3D #endif #endif userland/helpers/vc_image/000077500000000000000000000000001421703157200161115ustar00rootroot00000000000000userland/helpers/vc_image/metadata_fourcc.h000066400000000000000000000225201421703157200214040ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef _METADATA_FOURCC_H #define _METADATA_FOURCC_H /****************************************************************************** Definition of 4CCs assigned to metadata items. This list acts more as a central repository of 4CCs for different metadata items so as to avoid clashes. They are not otherwise necessary for library to function correctly. ******************************************************************************/ /* Note: Multi-character constants are not valid C and although supported by some compilers such as Metaware, they are ambiguous in how they should be packed into a long due to endian-ness (and also due to size for 2 and 3 character constants). */ typedef enum { METADATA_CDI = ('C'<<24)+('D'<<16)+('M'<<8)+('D'), // 'CDMD' struct CDI_METADATA_T in /middleware/camplus/cdi/cdi.h METADATA_CAM_CAL = ('C'<<24)+('C'<<16)+('A'<<8)+('L'), // 'CCAL' struct CAMERA_CALIBRATION_METADATA_T in /middleware/camplus/cdi/cdi.h METADATA_TIMING = ('T'<<24)+('I'<<16)+('M'<<8)+('E'), // 'TIME' struct TIMING_METADATA_T in /middleware/camplus/cdi/cdi.h METADATA_CDI_FILE = ( 0 <<24)+('C'<<16)+('D'<<8)+('F'), // '\x00CDF' struct CDI_FILE_METADATA_T in /middleware/camplus/cdi/cdi_file.h METADATA_CDI_RAW = ('C'<<24)+('D'<<16)+('R'<<8)+('W'), // 'CDRW' struct CDI_FILE_RAW_METADATA_T in /middleware/camplus/cdi/cdi_file_raw.h METADATA_STABILISE = ('S'<<24)+('T'<<16)+('A'<<8)+('B'), // 'STAB' struct GLOBAL_MOTION_VECTOR_T in /middleware/camplus/sw/stabilise/stabilise.h METADATA_LOCAL_MV1 = ('L'<<24)+('M'<<16)+('V'<<8)+('1'), // 'LMV1' struct ALL_LOCAL_MOTION_VECTORS_T in /middleware/camplus/sw/stabilise/stabilise.h METADATA_LOCAL_MV2 = ('L'<<24)+('M'<<16)+('V'<<8)+('2'), // 'LMV2' struct ALL_LOCAL_MOTION_VECTORS_T in /middleware/camplus/sw/stabilise/stabilise.h METADATA_TUNER_AGC = ( 0 <<24)+('A'<<16)+('G'<<8)+('C'), // '\x00AGC' struct ISP_AGC_METADATA_T in /middleware/ISP/tuner/isp_tuner_ctrl.h METADATA_AGC_DEBUG = ('A'<<24)+('G'<<16)+('C'<<8)+('D'), // 'AGCD' struct ISP_TUNER_BRCM_AGC_DEBUG_T in /middleware/ISP/tuner/isp_tuner_agc.h METADATA_FOCUS_REGION = ('F'<<24)+('R'<<16)+('G'<<8)+('N'), // 'FRGN' struct ISP_TUNER_BRCM_AF_STATISTICS_PARAMS_T in /middleware/ISP/tuner/isp_tuner_brcm_common.h METADATA_FOCUS_WOI = ('F'<<24)+('W'<<16)+('O'<<8)+('I'), // 'FWOI' struct ISP_WOI_METADATA_T in /middleware/ISP/tuner/isp_tuner_ctrl.h METADATA_FOCUS_CAF = ('F'<<24)+('C'<<16)+('A'<<8)+('F'), // 'FCAF' struct ISP_CAF_METADATA_T in /middleware/ISP/tuner/isp_tuner_ctrl.h METADATA_AUTOFOCUS = ( 0 <<24)+( 0 <<16)+('A'<<8)+('F'), // '\x00\x00AF' struct ISP_AF_METADATA_T in /middleware/ISP/tuner/isp_tuner_ctrl.h METADATA_EV = ('E'<<24)+('V'<<16)+('M'<<8)+('D'), // 'EVMD' struct ISP_TUNER_BRCM_EV_METADATA_T in /middleware/ISP/tuner/isp_tuner_brcm_common.h METADATA_ISP = ('I'<<24)+('S'<<16)+('P'<<8)+('M'), // 'ISPM' struct ISP_ISP_METADATA_T in /middleware/ISP/tuner/isp_tuner_ctrl.h METADATA_FACETRACKING = ('F'<<24)+('A'<<16)+('C'<<8)+('E'), // 'FACE' struct FACE_METADATA_T defined in /middleware/camplus/sw/face_metadata.h METADATA_ANTISHAKE = ('S'<<24)+('H'<<16)+('A'<<8)+('K'), // 'SHAK' struct SHAKE_METADATA_T defined in /middleware/camplus/sw/shake_metadata.h METADATA_RER = ( 0 <<24)+('R'<<16)+('E'<<8)+('R'), // '\x00RER' struct RER_METADATA_T defined in /middleware/camplus/sw/rer_metadata.h METADATA_SCENEDETECTION = ( 0 <<24)+('A'<<16)+('S'<<8)+('D'), // '\x00ASD' struct ASD_METADATA_T defined in /middleware/camplus/sw/asd_metadata.h METADATA_TUNER_SYNC = ('S'<<24)+('Y'<<16)+('N'<<8)+('C'), // 'SYNC' NULL data, just adds the item header. METADATA_DARK_FRAME_CORRECT = ('D'<<24)+('F'<<16)+('R'<<8)+('C'), // 'DFRC' 5 byte literal string "dfrc" METADATA_DARK_FRAME_SUB = ('D'<<24)+('F'<<16)+('S'<<8)+('B'), // 'DFSB' 3 byte literal string "on" METADATA_TUNER_DROP_FRAME = ('T'<<24)+('D'<<16)+('R'<<8)+('P'), METADATA_ABL = ( 0 <<24)+('A'<<16)+('B'<<8)+('L'), // '\x00ABL' struct ISP_TUNER_BRCM_BLACK_LEVEL_ABL_T defined in /middleware/ISP/tuner/isp_tuner_brcm_black_level.h METADATA_DRC = ( 0 <<24)+('D'<<16)+('R'<<8)+('C'), // 'DRC' struct DRC_METADATA_T defined in /middleware/camplus/sw/drc/drc.h METADATA_REGISTRATION = ( 0 <<24)+('R'<<16)+('E'<<8)+('G'), // 'REG' struct REGISTRATION_OFFSETS_T defined in /middleware/camplus/sw/registration/registration.h METADATA_RAW_CAPTURE = ('R'<<24)+('W'<<16)+('M'<<8)+('D'), // 'RWMD' struct RAW_CAPTURE_METADATA_T defined in /middleware/camplus/sw/raw_metadata.h // structure definitions for IL metadata are // in middleware/openmaxil/headers/ilmetadata.h METADATA_IL_CAMERA_NAME = ('I'<<24)+('L'<<16)+('C'<<8)+('A'), // 'ILCA' METADATA_IL_CROP_RECTANGLE = ('I'<<24)+('L'<<16)+('C'<<8)+('R'), // 'ILCR' METADATA_IL_PIXEL_ASPECT_RATIO = ('I'<<24)+('L'<<16)+('P'<<8)+('A'), // 'ILPA' METADATA_TUNER_FLASH_MONITOR = ('F'<<24)+('L'<<16)+('M'<<8)+('N'), // 'FLMN' Flash monitor - type ISP_TUNER_BRCM_FLASH_MONITOR_T from isp_tuner_brcm.h // VoWIFI video analysis METADATA_SPATIAL_ACT_A = ( 'S'<<24)+('P'<<16)+('T'<<8)+('A'), // 'SPTA' : SPATIAL_ACTIVITY_METADATA_T defined in /middleware/camplus/sw/perceptual/spatial_activity.h METADATA_TEMPORAL_ACT_A = ( 'T'<<24)+('M'<<16)+('P'<<8)+('A'), // 'TMPA' : TEMPORAL_ACTIVITY_METADATA_T defined in /middleware/camplus/sw/perceptual/temporal_activity.h METADATA_FILMGRAIN_NOISE_A = ( 'F'<<24)+('G'<<16)+('N'<<8)+('A'), // 'FGNA' : FILMGRAIN_NOISE_METADATA_T defined in /middleware/camplus/sw/perceptual/filmgrain_noise.h METADATA_COLORSPACE_A = ( 'C'<<24)+('L'<<16)+('R'<<8)+('A'), // 'CLRA' : COLORSPACE_METADATA_T defined in /middleware/camplus/sw/perceptual/colorspace.h METADATA_FLAT_AREA_A = ( 'F'<<24)+('L'<<16)+('T'<<8)+('A'), // 'FLTA' : FLAT_AREA_METADATA_T defined in /middleware/camplus/sw/perceptual/flatarea.h METADATA_STILL_AREA_A = ( 'S'<<24)+('T'<<16)+('L'<<8)+('A'), // 'STLA' : FLAT_AREA_METADATA_T defined in /middleware/camplus/sw/perceptual/stillarea.h METADATA_DDITHER_A = ( 'D'<<24)+('D'<<16)+('T'<<8)+('A'), // 'DDTA' : DDITHER_METADATA_T defined in /middleware/camplus/sw/perceptual/... METADATA_LINKED_MULTICHANN = ( 'I'<<24)+('L'<<16)+('M'<<8)+('C'), // 'ILMC' : VC_IMAGE_LINKED_MULTICHANN_T defined in /helpers/vc_image/vc_image.h METADATA_HDR = ( 0 <<24)+( 'H'<<16)+('D'<<8)+('R'), // 'HDR' : HDR_METADATA_T defined in /middleware/camplus/sw/hdr/hdr_metadata.h METADATA_FOCUS_STATS_PREPROC = ('F'<<24)+('S'<<16)+('P'<<8)+('M'), // 'FSPM' : FOCUS_STATS_PREPROC_METADATA defined in /middleware/camplus/sw/hdr/focus_stats_preproc/focus_stats_preproc.h METADATA_ACUTE_AWB_LOG = ('A'<<24)+('E'<<16)+('L'<<8)+('C'), // 'AELC' : ISP_ACUTE_AWB_LOG METADATA_DF = ( 0 <<24)+( 0<<16)+('D'<<8)+('F'), // '\x00\x00DF' : DF_METADATA_T defined in /middleware/camplus/sw/df/df_metadata.h METADATA_MAGIC_MEASURE = ('S'<<24)+('S'<<16)+('M'<<8) + ('M'), // 'SSMM' : A statistic from the ISP used to determine the JPEG quality setting for a certain customer. METADATA_SNAPSHOT_JPEG_QUANTISER = ('S'<<24)+('S'<<16)+('J'<<8) + ('S'), // 'SSJQ' : The size of the snapshot frame when JPEG-encoded. METADATA_SUPPLEMENTARY_INFO = ('S'<<24)+('U'<<16)+('P'<<8) + ('P'), // 'SUPP' : Supplimentary info defined in /codecs/video/hw/enc/venc_supplementary_info.h METADATA_UNKNOWN = ('U'<<24)+('N'<<16)+('K'<<8)+('N') // 'UNKN' } METADATA_CODE_T; #endif userland/helpers/vc_image/vc_image.h000066400000000000000000001142701421703157200200410ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ /** * \file * * This file describes the public interfaces to the old vc_image library. */ #ifndef VC_IMAGE_H #define VC_IMAGE_H #if !defined(__VIDEOCORE__) && !defined(__vce__) && !defined(VIDEOCORE_CODE_IN_SIMULATION) #error This code is only for use on VideoCore. If it is being included #error on the host side, then you have done something wrong. Defining #error __VIDEOCORE__ is *not* the right answer! #endif #ifdef __cplusplus extern "C" { #endif #include /* Definition for VC_IMAGE_TYPE_T live in here */ #include "vcinclude/vc_image_types.h" #include "helpers/vc_image/vc_image_helper.h" #if !defined __SYMBIAN32__ #if !defined( _MSC_VER) && !defined(__WIN32__) #include "vcfw/rtos/rtos.h" #endif #include "vcfw/rtos/common/rtos_common_mem.h" #include "helpers/vc_image/vc_image_metadata.h" #if defined(__VIDEOCORE3__) && !defined(RGB888) #ifdef __GNUC__ #warning "RGB888 should be defined on all VideoCore3 platforms, please check your platform makefile." #else # warn "RGB888 should be defined on all VideoCore3 platforms, please check your platform makefile." #endif #endif /* Types of extra properties that can be passed to vc_image_configure() */ typedef enum { VC_IMAGE_PROP_END = 0, VC_IMAGE_PROP_NONE = 0x57EAD400, VC_IMAGE_PROP_ALIGN, VC_IMAGE_PROP_STAGGER, VC_IMAGE_PROP_PADDING, VC_IMAGE_PROP_PRIORITY, VC_IMAGE_PROP_MALLOCFN, VC_IMAGE_PROP_CACHEALIAS, VC_IMAGE_PROP_CLEARVALUE, /* insert only allocation-related properties above this line */ VC_IMAGE_PROP_REF_IMAGE = 0x57EAD47f, VC_IMAGE_PROP_ROTATED, VC_IMAGE_PROP_PITCH, VC_IMAGE_PROP_DATA, VC_IMAGE_PROP_SIZE, VC_IMAGE_PROP_PALETTE, VC_IMAGE_PROP_MIPMAPS, VC_IMAGE_PROP_BAYER_ORDER, VC_IMAGE_PROP_BAYER_FORMAT, VC_IMAGE_PROP_BAYER_BLOCK_SIZE, VC_IMAGE_PROP_CODEC_FOURCC, VC_IMAGE_PROP_CODEC_MAXSIZE, VC_IMAGE_PROP_CUBEMAP, VC_IMAGE_PROP_OPENGL_FORMAT_AND_TYPE, VC_IMAGE_PROP_INFO, VC_IMAGE_PROP_METADATA, VC_IMAGE_PROP_NEW_LIST, /* Multi-channel properties */ VC_IMAGE_PROP_NUM_CHANNELS, VC_IMAGE_PROP_IS_TOP_BOTTOM, VC_IMAGE_PROP_IS_DECIMATED, VC_IMAGE_PROP_IS_PACKED, VC_IMAGE_PROP_YUV_COLOURSPACE, /* Linked-multichannel properties*/ VC_IMAGE_PROP_LINKED_MULTICHANN, VC_IMAGE_PROP_VPITCH, VC_IMAGE_PROP_TALL_YUVUV, VC_IMAGE_PROP_YUVUV_4K_CHROMA_ALIGN, } VC_IMAGE_PROPERTY_T; /* A property key and value */ typedef struct { VC_IMAGE_PROPERTY_T prop; unsigned long value; } VC_IMAGE_PROPLIST_T; #define VC_IMAGE_PROPLIST_COUNT(x) (sizeof(x)/sizeof(x[0])) #define VC_IMAGE_PROPLIST_NULL ((VC_IMAGE_PROPLIST_T*)0) /* Useful bits that can be set, to check validity we'll assume that all other bits should be zero and enforce this in vc_image functions to catch people who aren't initialising the VC_IMAGE_T structure nicely; update when other bits are added */ #define VC_IMAGE_INFO_VALIDBITS 0xFF0F /* Define the bits of info used to denote the colourspace */ #define VC_IMAGE_INFO_COLOURSPACE 0x0F #endif //#if !defined __SYMBIAN32__ #define is_valid_vc_image_hndl(_img, _type) ((_img) != NULL \ && (_img)->image_data == NULL \ && ((_img)->info.info & ~VC_IMAGE_INFO_VALIDBITS) == 0 \ && (_type) > VC_IMAGE_MIN && (_type) < VC_IMAGE_MAX \ && (_img)->type == (_type) \ ) #define is_valid_vc_image_buf(_img, _type) ((_img) != NULL \ && (_img)->image_data != NULL \ && ((_img)->info.info & ~VC_IMAGE_INFO_VALIDBITS) == 0 \ && (_type) > VC_IMAGE_MIN && (_type) < VC_IMAGE_MAX \ && (_img)->type == (_type) \ ) #define is_within_rectangle(x, y, w, h, cont_x, cont_y, cont_w, cont_h) ( \ (x) >= (cont_x) \ && (y) >= (cont_y) \ && ((x) + (w)) <= ((cont_x) + (cont_w)) \ && ((y) + (h)) <= ((cont_y) + (cont_h)) \ ) #if !defined __SYMBIAN32__ /** * \brief storage for pointers to image headers of the previous and next channel * * * When linked-multichannel structure is being used, this structure stores the pointers * necessary to link the current image header in to make represent a multichannel image. */ struct VC_IMAGE_LINKED_MULTICHANN_T { VC_IMAGE_T* prev; VC_IMAGE_T* next; }; typedef struct VC_IMAGE_LINKED_MULTICHANN_T VC_IMAGE_LINKED_MULTICHANN_T; /* Point type for use in polygon drawing. */ typedef struct vc_image_point_s { int x, y; } VC_IMAGE_POINT_T; /* Useful for rectangular crop regions in a vc_image */ typedef struct vc_image_rect_s { unsigned int x, y; unsigned int width; unsigned int height; } VC_IMAGE_RECT_T; /* CRC type for the return of CRC results */ typedef struct VC_IMAGE_CRC_T_ { unsigned int y; unsigned int uv; } VC_IMAGE_CRC_T; /* To make stack-based initialisation convenient: */ #define NULL_VC_IMAGE_T {0, 0, 0, 0, 0, 0} /* Convenient constants that can be passed more readably to vc_image_text. */ #define VC_IMAGE_COLOUR_TRANSPARENT -1 #define VC_IMAGE_COLOUR_FADE -2 #define CLIP_DEST(x_offset, y_offset, width, height, dest_width, dest_height) \ if (x_offset<0) \ width-=-x_offset, x_offset=0; \ if (y_offset<0) \ height-=-y_offset, y_offset=0; \ if (x_offset+width>dest_width) \ width-=x_offset+width-dest_width; \ if (y_offset+height>dest_height) \ height-=y_offset+height-dest_height; \ /* Publicly exported functions. */ /****************************************************************************** General functions. ******************************************************************************/ //routine to return a bitmask (bit referenced by VC_IMAGE_TYPE_T) of the supported image formats built in //this particular version of the vc_image library unsigned int vc_image_get_supported_image_formats( void ); /****************************************************************************** Data member access. ******************************************************************************/ /* Initialise all fields of a VC_IMAGE_T to zero. */ void vc_image_initialise(VC_IMAGE_T *image); /* Return specified channel of a multi-channel VC_IMAGE_T as a single-channel VC_IMAGE_T. */ void vc_image_extract_channel(VC_IMAGE_T *extract, VC_IMAGE_T *image, uint8_t chan_idx); /* Fill out all fields of a VC_IMAGE_T in the same way as vc_image_malloc(), but non-allocating. */ #if defined(va_start) /* can't publish this without including */ int vc_image_vconfigure(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type, long width, long height, va_list proplist); int vc_image_vreconfigure(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type, long width, long height, va_list proplist); #endif int vc_image_configure_unwrapped(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type, long width, long height, VC_IMAGE_PROPERTY_T prop, ...); int vc_image_reconfigure_unwrapped(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type, long width, long height, VC_IMAGE_PROPERTY_T prop, ...); /* remaining arguments are a VC_IMAGE_PROPERTY_T list terminated with VC_IMAGE_END */ int vc_image_configure_proplist(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type, long width, long height, VC_IMAGE_PROPLIST_T *props, unsigned long n_props); void vc_image_lock( VC_IMAGE_BUF_T *dst, const VC_IMAGE_T *src ); void vc_image_lock_extract( VC_IMAGE_BUF_T *dst, const VC_IMAGE_T *src, uint8_t chan_idx ); void vc_image_lock_channel(VC_IMAGE_BUF_T *dst, const VC_IMAGE_T *chann); void vc_image_lock_channel_perma(VC_IMAGE_BUF_T *dst, const VC_IMAGE_T *chann); //lightweight version of lock channel void vc_image_unlock( VC_IMAGE_BUF_T *img ); void vc_image_lock_perma( VC_IMAGE_BUF_T *dst, const VC_IMAGE_T *src ); void vc_image_unlock_perma( VC_IMAGE_BUF_T *img ); /* Mipmap-related functions. Image must be brcm1. */ /* Take the base image of an image and scale it down into its mipmaps. */ /* The filter argument type is likely to change, but 0 should always be a * reasonable value (even if it becomes a pointer). */ void vc_image_rebuild_mipmaps(VC_IMAGE_BUF_T *dst, int filter); /****************************************************************************** Image/bitmap manipulation. ******************************************************************************/ /* Fill a region of an image with solid colour. */ void vc_image_fill(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, int value); /* Blt a region of an image to another. */ void vc_image_blt(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); /* Blt a region of an image to another, changing the format on the fly. */ void vc_image_convert_blt(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); /* Expose the special cases of above */ void vc_image_yuv420_to_rgba32_part(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T const *src, int src_x_offset, int src_y_offset, int alpha); void vc_image_rgb565_to_rgba32_part(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T const *src, int src_x_offset, int src_y_offset, int alpha); /* Blt a region of an image to another with a nominated transparent colour. */ void vc_image_transparent_blt(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int transparent_colour); /* Blt a region of an image to another but only overwriting pixels of a given colour. */ void vc_image_overwrite_blt(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int overwrite_colour); /* Blt a region of an image to another only where a mask bitmap has 1s. */ void vc_image_masked_blt (VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, VC_IMAGE_BUF_T *mask, int mask_x_offset, int mask_y_offset, int invert); /* Masked fill a region with a solid colour. */ void vc_image_masked_fill(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, int value, VC_IMAGE_BUF_T *mask, int mask_x_offset, int mask_y_offset, int invert); /* Blt an image into 3 separate buffers, either YUV or RGB Will always copy the entire image. In the case of RGB image: Y = RGB, U = NULL, V = NULL */ void vc_image_fetch(VC_IMAGE_BUF_T *src, unsigned char* Y, unsigned char* U, unsigned char* V); /* Blt the data in Y U V (or RGB) buffers into a VC_IMAGE structure Opposite to vc_image_put above */ void vc_image_put(VC_IMAGE_BUF_T *src, unsigned char* Y, unsigned char* U, unsigned char* V); /* NOT a region of an image. */ void vc_image_not(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height); /* Fade (or brighten) a region of an image. fade is an 8.8 value. */ void vc_image_fade(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, int fade); /* OR two images together. */ void vc_image_or(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); /* XOR two images together. */ void vc_image_xor(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); /* Copy an image in its entirety (works on 1bpp images). */ void vc_image_copy(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src); /* Transpose an image. */ void vc_image_transpose(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src); int vc_image_transpose_ret(VC_IMAGE_T *dest, VC_IMAGE_T *src); /* Transpose an image and subsample it by some integer factor */ void vc_image_transpose_and_subsample(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, int factor); /* Vertically flip an image (turn "upside down"). */ void vc_image_vflip(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src); /* Horizontally flip an image ("mirror"). */ void vc_image_hflip(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src); /* Transpose an image in place. Relies on being able to preserve the pitch. */ void vc_image_transpose_in_place(VC_IMAGE_BUF_T *dest); /* Vertically flip an image (turn "upside down") in place. */ void vc_image_vflip_in_place(VC_IMAGE_BUF_T *dest); /* Horizontally flip an image ("mirror") in place. */ void vc_image_hflip_in_place(VC_IMAGE_BUF_T *dest); /* Add text string to an image. */ void vc_image_text(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int fg_colour, int bg_colour, const char *text, int *max_x, int *max_y); void vc_image_small_text(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int fg_colour, int bg_colour, const char *text, int *max_x, int *max_y); void vc_image_text_20(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int fg_colour, int bg_colour, const char *text, int *max_x, int *max_y); void vc_image_text_24(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int fg_colour, int bg_colour, const char *text, int *max_x, int *max_y); void vc_image_text_32(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int fg_colour, int bg_colour, const char *text, int *max_x, int *max_y); /* Experimental vector font thingy */ #define VC_IMAGE_ATEXT_FIXED (1<<16) // Add this to "size" if you want fixed-width int vc_image_atext_measure(int size, const char * str); int vc_image_atext_clip_length(int size, const char * str, int width); int vc_image_atext(VC_IMAGE_BUF_T * dest, int x, int y, int fg, int bg, int size, const char * str); /* Add optionally rotated text to an image */ void vc_image_textrotate(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int fg_colour, int bg_colour, const char *text, int rotate); /* Line drawing. */ void vc_image_line(VC_IMAGE_BUF_T *dest, int x_start, int y_start, int x_end, int y_end, int fg_colour); /* Unfilled Rect. */ void vc_image_rect(VC_IMAGE_BUF_T *dest, int x_start, int y_start, int width, int height, int fg_colour); /* Add frame . */ void vc_image_frame(VC_IMAGE_BUF_T *dest, int x_start, int y_start, int width, int height); void vc_image_aa_line(VC_IMAGE_BUF_T *dest, int x_start, int y_start, int x_end, int y_end, int thickness, int colour); void vc_image_aa_ellipse(VC_IMAGE_BUF_T *dest, int cx, int cy, int a, int b, int thickness, int colour, int filled); void vc_image_aa_polygon(VC_IMAGE_BUF_T *dest, VC_IMAGE_POINT_T *p, int n, int thickness, int colour, int filled); /* Copy and convert image to 48bpp. WARNING: offsets, width and height must be 16-pixel aligned. */ void vc_image_convert_to_48bpp(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); /* Copy and convert image from 16bpp to 24bpp. WARNING: offsets, width and height must be 16-pixel aligned. */ void vc_image_convert_to_24bpp(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); /* Convert and overlay image from 16bpp to 24bpp with nominated transparent colour. WARNING: offsets, width and height must be 16-pixel aligned. */ void vc_image_overlay_to_24bpp(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int transparent_colour); /* Copy and convert image from 24bpp to 16bpp. WARNING: offsets, width and height must be 16-pixel aligned. */ void vc_image_convert_to_16bpp(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); /* Copy all, or a portion of src to dest with resize. This function is specialized for YUV images (other cases are not yet coded, and code could be rather large to link all cases by default). All dimensions should be multiples of 2. If smooth_flag nonzero, appropriate smoothing will be applied. */ void vc_image_resize_yuv(VC_IMAGE_BUF_T *dest, int dest_x_offset, int dest_y_offset, int dest_width, int dest_height, VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int src_width, int src_height, int smooth_flag); /* RGB565 resize. Pitch must be 32-byte aligned, dimensions need not be. XXX kept YUV and RGB565 version separate to avoid unnecessary linking. However if we're going down the DLL route they ought to be combined. */ void vc_image_resize_rgb565(VC_IMAGE_BUF_T * dest, int dest_x_offset, int dest_y_offset, int dest_width, int dest_height, VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int src_width, int src_height, int smooth_flag); void vc_image_resize(VC_IMAGE_BUF_T *dest, int dest_x_offset, int dest_y_offset, int dest_width, int dest_height, VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int src_width, int src_height, int smooth_flag); /* Resize YUV in strips. XXX this function has rather bizarre arguments. */ void vc_image_resize_yuv_strip(VC_IMAGE_BUF_T * dest, VC_IMAGE_BUF_T * src, int src_x_offset, int src_width, int pos, int step, int smooth_flag, void * tmp_buf, int tmp_buf_size); /* Blit from a YUV image to an RGB image with optional mirroring followed by optional clockwise rotation */ void vc_image_convert(VC_IMAGE_BUF_T *dest,VC_IMAGE_BUF_T *src,int mirror,int rotate); /* Convert bitmap from one type to another, doing vertical flip as part of the conversion */ void vc_image_convert_vflip(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src); /* Convert from YUV to RGB, with optional downsample by 2 in each direction. */ void vc_image_yuv2rgb(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, int downsample); void vc_image_convert_yuv2rgb(unsigned char *datay, unsigned char *datau , unsigned char *datav, int realwidth, int realheight , unsigned short *buffer, int screenwidth, int screenheight); /* Frees up (using free_256bit) the source bytes and vc_image header */ void vc_image_free(VC_IMAGE_T *img); /* Returns an image based on decompressing the given bytes (made by helpers/vc_image/runlength.m) */ VC_IMAGE_BUF_T *vc_runlength_decode(unsigned char *data); /* Returns a 16bit image based on decompressing the given bytes (made by helpers/vc_image/covnertbmp2run.m) */ VC_IMAGE_BUF_T *vc_run_decode(VC_IMAGE_BUF_T *img,unsigned char *data); /* Returns an image based on decompressing the given bytes (made by helpers/vc_image/runlength8.m) */ VC_IMAGE_BUF_T *vc_runlength_decode8(unsigned char *data); /* Returns an image based on decompressing an image (made by helpers/vc_image/enc4by4.m) Will place the decompressed data into an existing image if supplied */ VC_IMAGE_BUF_T *vc_4by4_decode(VC_IMAGE_BUF_T *img,unsigned short *data); /* Deblocks a YUV image using a simple averaging scheme on 8 by 8 blocks */ void vc_image_deblock(VC_IMAGE_BUF_T *img); /* Pack the bytes (i.e. remove the padding bytes) in a vc_image. */ void vc_image_pack(VC_IMAGE_BUF_T *img, int x, int y, int w, int h); /* Pack bytes as above, but copy them to another memory block in the process. */ void vc_image_copy_pack (unsigned char *dest, VC_IMAGE_BUF_T *img, int x, int y, int w, int h); /* Unpack bytes to have the correct padding. */ void vc_image_unpack(VC_IMAGE_BUF_T *img, int w, int h); /* Unpack bytes as above, but also copy them to another memory block in the process. */ void vc_image_copy_unpack(VC_IMAGE_BUF_T *img, int dest_x_off, int dest_y_off, unsigned char *src, int w, int h); /* swap red/blue */ void vc_image_swap_red_blue(VC_IMAGE_BUF_T *img); #if defined(va_start) /* can't publish this without including */ VC_IMAGE_BUF_T *vc_image_vparmalloc_unwrapped(VC_IMAGE_TYPE_T type, char const *description, long width, long height, va_list proplist); #endif VC_IMAGE_BUF_T *vc_image_parmalloc_unwrapped(VC_IMAGE_TYPE_T type, char const *description, long width, long height, VC_IMAGE_PROPERTY_T prop, ...); void vc_image_parfree(VC_IMAGE_BUF_T *p); VC_IMAGE_BUF_T *vc_image_malloc( VC_IMAGE_TYPE_T type, long width, long height, int rotate ); VC_IMAGE_BUF_T *vc_image_prioritymalloc( VC_IMAGE_TYPE_T type, long width, long height, int rotate, int priority, char const *name); #define vc_image_priorityfree(p) vc_image_free(p) VC_IMAGE_T *vc_image_relocatable_alloc(VC_IMAGE_TYPE_T type, const char *description, long width, long height, MEM_FLAG_T flags, VC_IMAGE_PROPERTY_T prop, ...); void vc_image_relocatable_free(VC_IMAGE_T *img); void vc_image_set_palette( short *colourmap ); short *vc_image_get_palette( VC_IMAGE_T *src ); void vc_image_convert_in_place(VC_IMAGE_BUF_T *image, VC_IMAGE_TYPE_T new_type); /* Image transformations (flips and 90 degree rotations) */ VC_IMAGE_TRANSFORM_T vc_image_inverse_transform(VC_IMAGE_TRANSFORM_T transform); VC_IMAGE_TRANSFORM_T vc_image_combine_transforms(VC_IMAGE_TRANSFORM_T transform1, VC_IMAGE_TRANSFORM_T transform2); void vc_image_transform_point(int *pX, int *pY, int w, int h, VC_IMAGE_TRANSFORM_T transform); void vc_image_transform_rect(int *pX, int *pY, int *pW, int *pH, int w, int h, VC_IMAGE_TRANSFORM_T transform); void vc_image_transform_dimensions(int *pW, int *pH, VC_IMAGE_TRANSFORM_T transform); void vc_image_transform(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, VC_IMAGE_TRANSFORM_T transform); int vc_image_transform_ret(VC_IMAGE_T *dest, VC_IMAGE_T *src, VC_IMAGE_TRANSFORM_T transform); void vc_image_transform_in_place(VC_IMAGE_BUF_T *image, VC_IMAGE_TRANSFORM_T transform); void vc_image_transform_brcm1s(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, VC_IMAGE_TRANSFORM_T transform, int hdeci); const char *vc_image_transform_string(VC_IMAGE_TRANSFORM_T xfm); /* Blt a region of an image to another with a nominated transparent colour and alpha blending. Alpha should be between 0 and 256 */ void vc_image_transparent_alpha_blt(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int transparent_colour, int alpha); /* Blt a region of an image to another with a nominated transparent colour and alpha blending. Alpha should be between 0 and 256 */ void vc_image_transparent_alpha_blt_rotated(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, VC_IMAGE_BUF_T *src, int transparent_colour, int alpha, int scale, int sin_theta, int cos_theta); /* Blt a special 4.4 font image generated from a PNG file. Only works with a 565 destination image. Can choose a colour via red,green,blue arguments. Each component should be between 0 and 255. The alpha value present in the font image is scaled by the alpha argument given to the routine Alpha should be between 0 and 256 */ void vc_image_font_alpha_blt(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int red, int green, int blue, int alpha); /* Utilities for image editor */ extern void im_split_components(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, int width, int height, int pitch); extern void im_merge_components(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, int width, int height, int pitch); extern void im_merge_components_adjacent(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, int width, int height, int pitch, int badjacent); /* Deflicker a YUV image using a simple low pass filter */ void vc_image_deflicker(VC_IMAGE_BUF_T *img, unsigned char *context); /* Blend an Interlaced YUV image using a simple low pass filter which works on colour as well as luma*/ void vc_image_blend_interlaced(VC_IMAGE_BUF_T *img, unsigned char *context); /* Stripe resizing. Resize a 16-high stripe horizontally to have the given width, or a 16-wide column to have the given height. Work in place, uses bilinear filtering. */ void vc_image_resize_stripe_h(VC_IMAGE_BUF_T *image, int d_width, int s_width); void vc_image_resize_stripe_v(VC_IMAGE_BUF_T *image, int d_height, int s_height); /* Stripe resizing. Resize a horizontal stripe to 16-high, or a vertical stripe to 16 wide. Work in place, uses bilinear filtering. */ void vc_image_decimate_stripe_h(VC_IMAGE_BUF_T *src, int offset, int step); void vc_image_decimate_stripe_v(VC_IMAGE_BUF_T *src, int offset, int step); extern int vc_image_set_alpha(VC_IMAGE_BUF_T *image, const int x, const int y, const int width, const int height, const int alpha ); /* Make a horizontal alpha gradient mask for used in alpha blending below */ extern void vc_image_make_alpha_gradient(VC_IMAGE_BUF_T *pimage, int start_alpha, int end_alpha); /* Alpha blend two images together using an alpha mask */ extern void vc_image_alphablend(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, VC_IMAGE_BUF_T *mask, int dest_xoffset, int dest_yoffset, int src_xoffset, int src_yoffset, int width, int height); typedef struct vc_image_pool_s * VC_IMAGE_POOL_HANDLE_T; typedef VC_IMAGE_POOL_HANDLE_T VC_IMAGE_POOL_HANDLE; /* legacy */ /* Blt a region of an image onto a particular mipmap of a brcm1 image */ /* XXX do not use this function - the interface is likely to change as I decide * what options are available here */ void vc_image_XXX_mipmap_blt_XXX(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int miplvl, int cubeface, VC_IMAGE_TRANSFORM_T transform); /* Function to calculate the crc of the VC_IMAGE */ VC_IMAGE_CRC_T vc_image_calc_crc_interlaced(VC_IMAGE_BUF_T *img, int cl, int cr, int ct, int cb, int field); /* make compatible with old function */ #define vc_image_calc_crc(img,cl,cr,ct,cb) vc_image_calc_crc_interlaced((img),(cl),(cr),(ct),(cb),-1) #define vc_image_calc_crc_fast vc_image_calc_crc #if defined(NDEBUG) # define vc_image_debugstr(x) NULL #else # define vc_image_debugstr(x) (x) #endif #define vc_image_vparmalloc(type, desc, width, height, proplist) vc_image_vpmalloc(type, vc_image_debugstr(desc), width, height, proplist) #if __STDC_VERSION__ >= 199901L || _MSC_VER >= 1400 # define vc_image_parmalloc(type, desc, ...) vc_image_parmalloc_unwrapped(type, vc_image_debugstr(desc), __VA_ARGS__, VC_IMAGE_PROP_END) # define vc_image_configure(...) vc_image_configure_unwrapped(__VA_ARGS__, VC_IMAGE_PROP_END) # define vc_image_reconfigure(...) vc_image_reconfigure_unwrapped(__VA_ARGS__, VC_IMAGE_PROP_END) #elif !defined(_MSC_VER) && !defined(VCMODS_LCC) # define vc_image_parmalloc(type, desc, arglist...) vc_image_parmalloc_unwrapped(type, vc_image_debugstr(desc), arglist, VC_IMAGE_PROP_END) # define vc_image_configure(image, arglist...) vc_image_configure_unwrapped(image, arglist, VC_IMAGE_PROP_END) # define vc_image_reconfigure(image, arglist...) vc_image_reconfigure_unwrapped(image, arglist, VC_IMAGE_PROP_END) #else /*# warning "Your compiler does not support variadic macros. You'll have no vc_image_configure() or vc_image_parmalloc() functions."*/ #endif // now included in vc_image.c as openmaxil uses it regardless of which formats are built for extern void vc_subsample(VC_IMAGE_BUF_T *sub,VC_IMAGE_BUF_T *cur); #ifdef USE_YUV_UV extern void vc_save_YUV( VC_IMAGE_BUF_T *frame, const char * const sz_filename); extern void vc_save_YUV_1file( VC_IMAGE_BUF_T *frame, const char * const sz_filename, int framenum ); extern void vc_load_YUVUV( VC_IMAGE_BUF_T *frame, const char * const sz_filename,int yuvwidth,int yuvheight); extern void vc_load_YUV( VC_IMAGE_BUF_T *frame, const char * const sz_filename,int framenum); extern void vc_endianreverse32(uint32_t *addr,int count); /* ** This is my preferred function to load a YUV_UV image. It works with either a collection of images ** e.g. foo%03u.yuv or one giant concatenated file e.g. foo.yuv. ** It will tile or truncate the output image to what you want which is why there are two heights & widths. ** Return 0 if successful; -1 if the params are unsuitable, -2 if we couldn't open the file. */ extern int vc_load_image( const char * const sz_file_fmt, /* in */ const unsigned iim, /* in */ const int pitch, /* in */ const int i_height, /* in */ /* (i)nput image: i.e. raw file */ const int i_width, /* in */ const int o_height, /* in */ /* (o)utput image: i.e. framebuffer */ const int o_width, /* in */ const VC_IMAGE_BUF_T * const frame /* in */ ); /* Modified version of vc_load_image (for maximum efficiency): * - must be supplied with a buffer to read a whole YUV frame into * - reads the next sequential image in an already-open file (no fopen/fseek/fclose). * - no tiling: output size = input size. */ void vc_slurp_image (FILE * fid, /* in */ const int pitch, /* in */ const int height, /* in */ const int width, /* in */ const VC_IMAGE_T * const frame, /* in */ unsigned char * frame_buf, /* in */ int rewind); /* in */ extern int vc_load_image_vowifi( void * infile_fid, const unsigned iim, /* in */ const int pitch, /* in */ const int i_height, /* in */ /* (i)nput image: i.e. raw file */ const int i_width, /* in */ const int o_height, /* in */ /* (o)utput image: i.e. framebuffer */ const int o_width, /* in */ const VC_IMAGE_BUF_T * const frame /* in */ ) ; extern int vc_load_image_vowifi_brcm1d( void * infile_fid, const unsigned iim, /* in */ const int pitch, /* in */ const int i_height, /* in */ /* (i)nput image: i.e. raw file */ const int i_width, /* in */ const int o_height, /* in */ /* (o)utput image: i.e. framebuffer */ const int o_width, /* in */ const VC_IMAGE_BUF_T * const frame, /* in */ const unsigned char *frame_buf) ; /* ** Vector routine to difference a pair of images ( d |-|= a ). ** Should probably dcache flush after this if using via uncached alias? */ extern void vc_image_difference( VC_IMAGE_BUF_T * const d, /* in/out */ const VC_IMAGE_BUF_T * const a, /* in */ float * const psnr_y, /* out */ float * const psnr_uv, /* out */ unsigned * const max_y, /* out */ unsigned * const max_uv /* out */ ); /* ** The h.263 Annex J deblocker. ** ("dest" & "src" may point to the same storage.) */ extern void vc_image_deblock_h263( VC_IMAGE_BUF_T * const dest, /*(in)out */ const VC_IMAGE_BUF_T * const src, /* in */ const int QUANT /* in */ ); /* * Clone the top field of an interlaced image (lines 0,2,4,..) over the bottom field (lines 1,3,5,...) * or vice versa */ extern void vc_image_clone_field(VC_IMAGE_T * image, int clone_top_field); #endif /* RGBA32 colour macros */ #define RGBA_ALPHA_TRANSPARENT 0 #define RGBA_ALPHA_OPAQUE 255 #define RGBA32_TRANSPARENT_COLOUR 0x00000000 #ifdef RGB888 #define RGBA32_APIXEL(r, g, b, a) (((unsigned long)(a) << 24) | ((b) << 16) | ((g) << 8) | (r)) #define RGBA32_SPIXEL(r, g, b, a) ((RGBA_ALPHA_OPAQUE << 24) | ((b) << 16) | ((g) << 8) | (r)) #else #define RGBA32_APIXEL(r, g, b, a) (((unsigned long)(a) << 24) | ((r) << 16) | ((g) << 8) | (b)) #define RGBA32_SPIXEL(r, g, b, a) (((unsigned long)RGBA_ALPHA_OPAQUE << 24) | ((r) << 16) | ((g) << 8) | (b)) #endif #define RGBA32_PIXEL(r, g, b, a) ((a) >= 255 ? RGBA32_SPIXEL(r, g, b, a) : RGBA32_APIXEL(r, g, b, a)) #define RGBA32_ALPHA(rgba) ((unsigned long)rgba >> 24) /* RGBA16 colour macros */ #define RGBA16_APIXEL(r, g, b, a) (((r) >> 4 << 12) | ((g) >> 4 << 8) | ((b) >> 4 << 4) | ((a) >> 4 << 0)) #define RGBA16_SPIXEL(r, g, b) (((r) >> 4 << 12) | ((g) >> 4 << 8) | ((b) >> 4 << 4) | 0x000F) #define RGBA16_PIXEL(r, g, b, a) RGBA16_APIXEL(r, g, b, a) /* RGBA565 colour macros */ #define RGBA565_TRANSPARENTBITS 0x18DF #define RGBA565_TRANSPARENTKEY 0xE700 #define RGBA565_ALPHABITS 0x001C #define RGBA565_TRANSPARENT_COLOUR RGBA565_TRANSPARENTKEY #define RGBA565_APIXEL(r, g, b, a) (((r) >> 6 << 11) | ((g) >> 6 << 6) | (((a)+16) >> 5 << 2) | ((b) >> 6) | RGBA565_TRANSPARENTKEY) #define RGBA565_SPIXEL(r, g, b) (((r) >> 3 << 11) | ((g) >> 2 << 5) | ((b) >> 3) | (((r) & (g) & 0xE0) == 0xE0 ? 0x20 : 0x00)) #define RGBA565_PIXEL(r, g, b, a) ((a) >= 0xF0 ? RGBA565_SPIXEL(r, g, b) : RGBA565_APIXEL(r, g, b, a)) #define RGBA565_FROM_RGB565(rgb) ((((rgb) & ~RGBA565_TRANSPARENTBITS) == RGBA565_TRANSPARENTKEY) ? (rgb) ^ 0x0020 : (rgb)) #define RGBA565_TO_RGB565(rgba) ((((rgba) & ~RGBA565_TRANSPARENTBITS) == RGBA565_TRANSPARENTKEY) ? ((rgba) & (RGBA565_TRANSPARENTBITS ^ RGBA565_ALPHABITS)) * 10 : (rgba)) #define RGBA565_ALPHA(rgba) ((((rgba) & ~RGBA565_TRANSPARENTBITS) == RGBA565_TRANSPARENTKEY) ? ((rgba) & RGBA565_ALPHABITS) << 3 : 0x100) #endif //#if !defined __SYMBIAN32__ #ifdef __cplusplus } #endif #endif userland/helpers/vc_image/vc_image_helper.h000066400000000000000000000242371421703157200214030ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_IMAGE_HELPER_H #define VC_IMAGE_HELPER_H #include "interface/vctypes/vc_image_structs.h" #ifdef __cplusplus extern "C" { #endif /** * \brief Image buffer object, with image data locked in memory and ready for access. * * This data type is fully compatible with \c VC_IMAGE_T for backwards * compatibility. New code should use this type where the object refers to * a locked image. */ typedef VC_IMAGE_T VC_IMAGE_BUF_T; /* Macros to determine which format a vc_image is */ typedef struct { unsigned bits_per_pixel : 8, is_rgb : 1, is_yuv : 1, is_raster_order : 1, is_tformat_order : 1, has_alpha : 1; } VC_IMAGE_TYPE_INFO_T; #define VC_IMAGE_COMPONENT_ORDER(red_lsb, green_lsb, blue_lsb, alpha_lsb) \ ( (((red_lsb) & 0x1f) << 0) \ | (((green_lsb) & 0x1f) << 6) \ | (((blue_lsb) & 0x1f) << 12) \ | (((alpha_lsb) & 0x1f) << 18) ) #define VC_IMAGE_RED_OFFSET(component_order) (((component_order) >> 0) & 0x1f) #define VC_IMAGE_GREEN_OFFSET(component_order) (((component_order) >> 6) & 0x1f) #define VC_IMAGE_BLUE_OFFSET(component_order) (((component_order) >> 12) & 0x1f) #define VC_IMAGE_ALPHA_OFFSET(component_order) (((component_order) >> 18) & 0x1f) extern const VC_IMAGE_TYPE_INFO_T vc_image_type_info[VC_IMAGE_MAX + 1]; extern const unsigned int vc_image_rgb_component_order[VC_IMAGE_MAX + 1]; #define VC_IMAGE_IS_YUV(type) (vc_image_type_info[type].is_yuv) #define VC_IMAGE_IS_RGB(type) (vc_image_type_info[type].is_rgb) #define VC_IMAGE_IS_RASTER(type) (vc_image_type_info[type].is_raster_order) #define VC_IMAGE_IS_TFORMAT(type) (vc_image_type_info[type].is_tformat_order) #define VC_IMAGE_BITS_PER_PIXEL(type) (vc_image_type_info[type].bits_per_pixel) #define VC_IMAGE_HAS_ALPHA(type) (vc_image_type_info[type].has_alpha) #define case_VC_IMAGE_ANY_YUV \ case VC_IMAGE_YUV420: \ case VC_IMAGE_YUV420SP: \ case VC_IMAGE_YUV422: \ case VC_IMAGE_YUV_UV: \ case VC_IMAGE_YUV_UV32: \ case VC_IMAGE_YUV420_S: \ case VC_IMAGE_YUV422PLANAR: \ case VC_IMAGE_YUV444PLANAR: \ case VC_IMAGE_YUV420_16: \ case VC_IMAGE_YUV_UV_16: \ case VC_IMAGE_YUV422YUYV: \ case VC_IMAGE_YUV422YVYU: \ case VC_IMAGE_YUV422UYVY: \ case VC_IMAGE_YUV422VYUY: \ case VC_IMAGE_YUV10COL #define case_VC_IMAGE_ANY_RGB \ case VC_IMAGE_RGB565: \ case VC_IMAGE_RGB2X9: \ case VC_IMAGE_RGB666: \ case VC_IMAGE_RGBA32: \ case VC_IMAGE_RGBX32: \ case VC_IMAGE_RGBA16: \ case VC_IMAGE_RGBA565: \ case VC_IMAGE_RGB888: \ case VC_IMAGE_TF_RGBA32: \ case VC_IMAGE_TF_RGBX32: \ case VC_IMAGE_TF_RGBA16: \ case VC_IMAGE_TF_RGBA5551: \ case VC_IMAGE_TF_RGB565: \ case VC_IMAGE_BGR888: \ case VC_IMAGE_BGR888_NP: \ case VC_IMAGE_ARGB8888: \ case VC_IMAGE_XRGB8888: \ case VC_IMAGE_RGBX8888: \ case VC_IMAGE_BGRX8888: \ case VC_IMAGE_RGBA1010102 #define case_VC_IMAGE_ANY_RGB_NOT_TF \ case VC_IMAGE_RGB565: \ case VC_IMAGE_RGB2X9: \ case VC_IMAGE_RGB666: \ case VC_IMAGE_RGBA32: \ case VC_IMAGE_RGBX32: \ case VC_IMAGE_RGBA16: \ case VC_IMAGE_RGBA565: \ case VC_IMAGE_RGB888: \ case VC_IMAGE_BGR888: \ case VC_IMAGE_BGR888_NP: \ case VC_IMAGE_ARGB8888: \ case VC_IMAGE_XRGB8888: \ case VC_IMAGE_RGBX8888: \ case VC_IMAGE_BGRX8888: \ case VC_IMAGE_RGBA1010102 #define case_VC_IMAGE_ANY_TFORMAT \ case VC_IMAGE_TF_RGBA32: \ case VC_IMAGE_TF_RGBX32: \ case VC_IMAGE_TF_FLOAT: \ case VC_IMAGE_TF_RGBA16: \ case VC_IMAGE_TF_RGBA5551: \ case VC_IMAGE_TF_RGB565: \ case VC_IMAGE_TF_YA88: \ case VC_IMAGE_TF_BYTE: \ case VC_IMAGE_TF_PAL8: \ case VC_IMAGE_TF_PAL4: \ case VC_IMAGE_TF_ETC1: \ case VC_IMAGE_TF_Y8: \ case VC_IMAGE_TF_A8: \ case VC_IMAGE_TF_SHORT: \ case VC_IMAGE_TF_1BPP: \ case VC_IMAGE_TF_U8: \ case VC_IMAGE_TF_V8 /****************************************************************************** General functions. ******************************************************************************/ int vc_image_bits_per_pixel(VC_IMAGE_TYPE_T type); int calculate_pitch(VC_IMAGE_TYPE_T type, int width, int height, uint8_t num_channels, VC_IMAGE_INFO_T *info, VC_IMAGE_EXTRA_T *extra); /* Check if an image will use an alternate memory layout, in order to cope with * codec limitation. Applies to YUV_UV images taller than 1344 lines. */ int vc_image_is_tall_yuv_uv(VC_IMAGE_T *image); /****************************************************************************** Data member access. ******************************************************************************/ /* Set the type of the VC_IMAGE_T. */ void vc_image_set_type(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type); /* Set the image_data field, noting how big it is. */ void vc_image_set_image_data(VC_IMAGE_BUF_T *image, int size, void *image_data); /* Set the image data with added u and v pointers */ void vc_image_set_image_data_yuv(VC_IMAGE_BUF_T *image, int size, void *image_y, void *image_u, void *image_v); /* Set the dimensions of the image. */ void vc_image_set_dimensions(VC_IMAGE_T *image, int width, int height); /* Check the integrity of a VC_IMAGE_T structure - checks structure values + data ptr */ int vc_image_verify(const VC_IMAGE_T *image); /* Set the pitch (internal_width) of the image. */ void vc_image_set_pitch(VC_IMAGE_T *image, int pitch); /* Specify the vertical pitch for YUV planar images */ void vc_image_set_vpitch(VC_IMAGE_T *image, int vpitch); /* Specify that the YUV image is V/U interleaved. */ void vc_image_set_is_vu(VC_IMAGE_T *image); /* Return 1 if the YUV image is V/U interleaved, else return 0. */ int vc_image_get_is_vu(const VC_IMAGE_T *image); int vc_image_info_get_is_vu(const VC_IMAGE_INFO_T *info); /* Reset the shape of an image */ int vc_image_reshape(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type, int width, int height); /* Return the space required (in bytes) for an image of this type and dimensions. */ int vc_image_required_size(VC_IMAGE_T *image); /* Return the space required (in bytes) for an image of this type's palette. */ int vc_image_palette_size (VC_IMAGE_T *image); /* Return 1 if image is high-definition, else return 0. */ int vc_image_is_high_definition(const VC_IMAGE_T *image); /* Return true if palette is 32bpp */ int vc_image_palette_is_32bit(VC_IMAGE_T *image); /* Retrieve Y, U and V pointers from a YUV image. Note that these are macros. */ #define vc_image_get_y(p) ((unsigned char *)((p)->image_data)) // replaced with functions to allow assert - revert to #define when fixed //#define vc_image_get_u(p) ((unsigned char *)((p)->extra.uv.u)) //#define vc_image_get_v(p) ((unsigned char *)((p)->extra.uv.v)) unsigned char *vc_image_get_u(const VC_IMAGE_BUF_T *image); unsigned char *vc_image_get_v(const VC_IMAGE_BUF_T *image); /* Calculate the address of the given pixel coordinate -- the result may point * to a word containing data for several pixels (ie., for sub-8bpp and * compressed formats). */ void *vc_image_pixel_addr(VC_IMAGE_BUF_T *image, int x, int y); void *vc_image_pixel_addr_mm(VC_IMAGE_BUF_T *image, int x, int y, int miplevel); void *vc_image_pixel_addr_u(VC_IMAGE_BUF_T *image, int x, int y); void *vc_image_pixel_addr_v(VC_IMAGE_BUF_T *image, int x, int y); /* As above, but with (0,0) in the bottom-left corner */ void *vc_image_pixel_addr_gl(VC_IMAGE_BUF_T *image, int x, int y, int miplevel); #define vc_image_get_y_422(p) vc_image_get_y(p) #define vc_image_get_u_422(p) vc_image_get_u(p) #define vc_image_get_v_422(p) vc_image_get_v(p) #define vc_image_get_y_422planar(p) vc_image_get_y(p) #define vc_image_get_u_422planar(p) vc_image_get_u(p) #define vc_image_get_v_422planar(p) vc_image_get_v(p) /* Mipmap-related functions. Image must be t-format. */ /* Return the pitch of the selected mipmap */ unsigned int vc_image_get_mipmap_pitch(VC_IMAGE_T *image, int miplvl); /* Return the padded height of the selected mipmap (mipmaps must be padded to a * power of 2) */ unsigned int vc_image_get_mipmap_padded_height(VC_IMAGE_T *image, int miplvl); /* Return the offset, in bytes, of the selected mipmap. */ int vc_image_get_mipmap_offset(VC_IMAGE_T *image, int miplvl); /* Return whether the selected mipmap is stored in t-format or linear microtile * format. */ #define VC_IMAGE_MIPMAP_TFORMAT 0 #define VC_IMAGE_MIPMAP_LINEAR_TILE 1 int vc_image_get_mipmap_type(VC_IMAGE_T const *image, int miplvl); /* Ensure required alignment for format */ long fix_alignment(VC_IMAGE_TYPE_T type, long value, VC_IMAGE_INFO_T *info); #ifdef __cplusplus } #endif #endif userland/helpers/vc_image/vc_image_metadata.h000066400000000000000000000120471421703157200217000ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VC_IMAGE_METADATA_H #define VC_IMAGE_METADATA_H #include "vcfw/rtos/rtos.h" #include "helpers/vc_image/metadata_fourcc.h" #define VC_METADATA_ALIGN 3 /****************************************************************************** Types of metadata. ******************************************************************************/ typedef unsigned int VC_METADATA_TYPE_T; /****************************************************************************** Metadata and item headers. ******************************************************************************/ typedef struct vc_metadata_item_s { VC_METADATA_TYPE_T type; int len; } VC_METADATA_ITEM_T; typedef struct vc_metadata_header_s { int size; #ifdef VCMODS_LCC unsigned char readonly; #else unsigned char readonly:1; #endif int offset_next; RTOS_LATCH_T latch; } VC_METADATA_HEADER_T; /****************************************************************************** Public declarations. ******************************************************************************/ // Initialises a metadata header structure int vc_metadata_initialise(VC_METADATA_HEADER_T *header, int size); // Add a metadata entry to the buffer int vc_metadata_add(VC_METADATA_HEADER_T *header, void *data, VC_METADATA_TYPE_T type, int len); // Get a (pointer to a) particular metadata item from the buffer (optionally copy the data to dest), and return the length of the item void *vc_metadata_get_with_length(void *dest, VC_METADATA_HEADER_T *header, VC_METADATA_TYPE_T type, int *retcode, int *len); // Get a (pointer to a) particular metadata item from the buffer (optionally copy the data to dest) void *vc_metadata_get(void *dest, VC_METADATA_HEADER_T *header, VC_METADATA_TYPE_T type, int *retcode); // Clear the metadata buffer and reset the header (clears readonly flag) int vc_metadata_clear(VC_METADATA_HEADER_T *header); // Set or clear the read-only flag in the metadata buffer int vc_metadata_set_readonly(VC_METADATA_HEADER_T *header, int value); // Copies the contents of one metadata buffer to the other (appends the destination buffer) int vc_metadata_copy(VC_METADATA_HEADER_T *dest, VC_METADATA_HEADER_T *src); // Overwrites an item in the metadata buffer (caller must make sure the sizes match!) int vc_metadata_overwrite(VC_METADATA_HEADER_T *header, void *data, VC_METADATA_TYPE_T type); // Flush the metadata buffer out of the cache int vc_metadata_cache_flush(VC_METADATA_HEADER_T *header); // Locks the vc_metadata library semaphore int vc_metadata_lock(VC_METADATA_HEADER_T *header); // Unlocks the vc_metadata library semaphore int vc_metadata_unlock(VC_METADATA_HEADER_T *header); /****************************************************************************** Wrappers for the above functions using VC_IMAGEs. ******************************************************************************/ #define vc_image_metadata_initialise(i, s) vc_metadata_initialise((i)->metadata, s) #define vc_image_metadata_add(i, d, t, l) vc_metadata_add((i)->metadata, d, t, l) #define vc_image_metadata_get(d, i, t, r) vc_metadata_get(d, (i)->metadata, t, r) #define vc_image_metadata_clear(i) vc_metadata_clear((i)->metadata) #define vc_image_metadata_set_readonly(i, v) vc_metadata_set_readonly((i)->metadata, v) #define vc_image_metadata_copy(d, s) vc_metadata_copy((d)->metadata, (s)->metadata) #define vc_image_metadata_overwrite(i, d, t) vc_metadata_overwrite((i)->metadata, d, t) #define vc_image_metadata_cache_flush(i) vc_metadata_cache_flush((i)->metadata) #endif userland/host_applications/000077500000000000000000000000001421703157200164205ustar00rootroot00000000000000userland/host_applications/android/000077500000000000000000000000001421703157200200405ustar00rootroot00000000000000userland/host_applications/android/apps/000077500000000000000000000000001421703157200210035ustar00rootroot00000000000000userland/host_applications/android/apps/vidtex/000077500000000000000000000000001421703157200223065ustar00rootroot00000000000000userland/host_applications/android/apps/vidtex/CMakeLists.txt000066400000000000000000000011271421703157200250470ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) SET(COMPILE_DEFINITIONS -Werror -Wall) # Set --no-as-needed to stop the linker discarding mmal_vc_client # as it can't see that the constructor registers a load of functionality # with the MMAL core. SET( CMAKE_EXE_LINKER_FLAGS "-Wl,--no-as-needed" ) include_directories(${PROJECT_SOURCE_DIR}/host_applications/linux/libs/bcm_host/include) set (VIDTEX_SOURCES main.c launcher_rpi.c svp.c vidtex.c) add_executable(vidtex ${VIDTEX_SOURCES}) target_link_libraries(vidtex GLESv2 EGL m bcm_host mmal_core mmal_components mmal_util mmal_vc_client vcos) userland/host_applications/android/apps/vidtex/applog.h000066400000000000000000000037371421703157200237530ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef APPLOG_H #define APPLOG_H #ifdef __cplusplus extern "C" { #endif /** \file Application support for VCOS logging. * This header file is used to abstract the logging for files that are designed to be included * in different applications. It should be included before any other VMCS header files. */ #define VCOS_LOG_CATEGORY (&app_log_category) #include "interface/vcos/vcos.h" extern VCOS_LOG_CAT_T app_log_category; #ifdef __cplusplus } #endif #endif /* APPLOG_H */ userland/host_applications/android/apps/vidtex/launcher.h000066400000000000000000000066201421703157200242640ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef LAUNCHER_H #define LAUNCHER_H #include #include #include #include #include /** * Android-specific launcher for native graphical application. * See runApp(). */ class Launcher : public android::Thread { public: /** Entry point function for application-specific code. * @param params Application-specific parameters. * @param win Native window/surface. * @return 0 on success; -1 on failure. */ typedef int (*RUN_APP_FN_T)(const void *params, EGLNativeWindowType win); /** Run a function in a separate thread. * A separate thread is launched to run the function, and a native surface created for * that application. * @param name Application name. * @param run_app_fn Entry point function for application. * @param params Application-specific parameters. This will be bitwise copyable, and * between C and C++, so don't use pointers, bool or anything else liable * for problems. * @param param_size Size, in bytes, of the application parameters. * @return 0 on success; -1 on failure. */ static int runApp(const char *name, RUN_APP_FN_T run_app_fn, const void *params, size_t param_size); Launcher(const char *name, RUN_APP_FN_T run_fn, const void *params, size_t param_size); virtual ~Launcher(); android::sp session() const; protected: virtual android::status_t readyToRun(); virtual bool threadLoop(); private: virtual void onFirstRef(); android::sp m_session; android::sp m_sf_control; android::sp m_sf_surface; android::String8 m_name; RUN_APP_FN_T m_run_fn; android::Vector m_params; int m_result; }; #endif userland/host_applications/android/apps/vidtex/launcher_rpi.c000066400000000000000000000062251421703157200251320ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include "launcher_rpi.h" #include #include #include #include "bcm_host.h" static int create_native_window(EGL_DISPMANX_WINDOW_T *native_win) { int32_t status = 0; DISPMANX_DISPLAY_HANDLE_T disp; DISPMANX_ELEMENT_HANDLE_T elem; DISPMANX_UPDATE_HANDLE_T update; VC_DISPMANX_ALPHA_T alpha = {DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 255, 0}; VC_RECT_T src_rect = {0}; VC_RECT_T dest_rect = {0}; uint32_t display_width, display_height; uint32_t disp_num = 0; // Primary uint32_t layer_num = 0; status = graphics_get_display_size(0, &display_width, &display_height); if (status != 0) return status; /* Fullscreen */ display_width = 1280; display_height = 720; dest_rect.width = display_width; dest_rect.height = display_height; src_rect.width = display_width << 16; src_rect.height = display_height << 16; disp = vc_dispmanx_display_open(disp_num); update = vc_dispmanx_update_start(0); elem = vc_dispmanx_element_add(update, disp, layer_num, &dest_rect, 0, &src_rect, DISPMANX_PROTECTION_NONE, &alpha, NULL, DISPMANX_NO_ROTATE); native_win->element = elem; native_win->width = display_width; native_win->height = display_height; vc_dispmanx_update_submit_sync(update); return 0; } int runApp(const char *name, RUN_APP_FN_T run_app_fn, const void *params, size_t param_size) { EGL_DISPMANX_WINDOW_T win; (void) param_size; vcos_log_trace("Initialsing BCM HOST"); bcm_host_init(); vcos_log_trace("Starting '%s'", name); if (create_native_window(&win) != 0) return -1; return run_app_fn(params, (EGLNativeWindowType *) &win); } userland/host_applications/android/apps/vidtex/launcher_rpi.h000066400000000000000000000046331421703157200251400ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef LAUNCHER_RPI_H #define LAUNCHER_RPI_H #include #include #include "applog.h" #ifdef __cplusplus extern "C" { #endif /** Entry point function for application-specific code. * @param params Application-specific parameters. * @param win Native window/surface. * @return 0 on success; -1 on failure. */ typedef int (*RUN_APP_FN_T)(const void *params, EGLNativeWindowType win); /** Create native window and runs the function. * * No need to run in a separate thread on Linux / RPI. * * @param name Application name. * @param run_app_fn Entry point function for application. * @param params Application-specific parameters. * @param param_size Size, in bytes, of the application parameters. * @return 0 on success; -1 on failure. */ int runApp(const char *name, RUN_APP_FN_T run_app_fn, const void *params, size_t param_size); #ifdef __cplusplus } #endif #endif userland/host_applications/android/apps/vidtex/main.c000066400000000000000000000064671421703157200234130ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include "applog.h" #include "vidtex.h" #ifdef __ANDROID__ #include "launcher.h" #else #include "launcher_rpi.h" #endif VCOS_LOG_CAT_T app_log_category; static int launch_vidtex(const void *params, EGLNativeWindowType win) { return vidtex_run((const VIDTEX_PARAMS_T *)params, win); } static void usage(const char *argv0) { fprintf(stderr, "Usage: %s [-d duration-ms] [-i] [uri]\n", argv0); } int main(int argc, char **argv) { // Parse command line options/arguments. VIDTEX_PARAMS_T params = {0}; int rc; int status = 0; opterr = 0; while ((rc = getopt(argc, argv, "d:iuvy")) != -1) { switch (rc) { case 'd': params.duration_ms = atoi(optarg); break; case 'i': params.opts |= VIDTEX_OPT_IMG_PER_FRAME; break; case 'y': params.opts |= VIDTEX_OPT_Y_TEXTURE; break; case 'u': params.opts |= VIDTEX_OPT_U_TEXTURE; break; case 'v': params.opts |= VIDTEX_OPT_V_TEXTURE; break; default: usage(argv[0]); return 2; } } if (optind < argc - 1) { usage(argv[0]); return 2; } if (optind < argc) { strncpy(params.uri, argv[optind], sizeof(params.uri) - 1); params.uri[sizeof(params.uri) - 1] = '\0'; } // Init vcos logging. vcos_log_set_level(VCOS_LOG_CATEGORY, VCOS_LOG_INFO); vcos_log_register("vidtex", VCOS_LOG_CATEGORY); // Run video-on-texture application in a separate thread. #ifdef __ANDROID__ status = Launcher::runApp("vidtex", launch_vidtex, ¶ms, sizeof(params)); #else status = runApp("vidtex", launch_vidtex, ¶ms, sizeof(params)); #endif return (status == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } userland/host_applications/android/apps/vidtex/svp.c000066400000000000000000000457741421703157200233030ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include "applog.h" #include "interface/vcos/vcos_stdbool.h" #include "interface/vcos/vcos_inttypes.h" #include "interface/mmal/mmal.h" #include "interface/mmal/mmal_logging.h" #include "interface/mmal/util/mmal_connection.h" #include "interface/mmal/util/mmal_util.h" #include "interface/mmal/util/mmal_default_components.h" #include "interface/mmal/util/mmal_util_params.h" #include "svp.h" #define CHECK_STATUS(s, m) \ if ((s) != MMAL_SUCCESS) { \ LOG_ERROR("%s: %s", (m), mmal_status_to_string((s))); \ goto error; \ } /* Flags specifying fields of SVP_T struct */ #define SVP_CREATED_SEM (1 << 0) #define SVP_CREATED_THREAD (1 << 1) #define SVP_CREATED_MUTEX (1 << 2) #define SVP_CREATED_TIMER (1 << 3) #define SVP_CREATED_WD_TIMER (1 << 4) /* Hard-coded camera parameters */ #if 0 #define SVP_CAMERA_WIDTH 1920 #define SVP_CAMERA_HEIGHT 1080 #else #define SVP_CAMERA_WIDTH 1280 #define SVP_CAMERA_HEIGHT 720 #endif #define SVP_CAMERA_FRAMERATE 30 #define SVP_CAMERA_DURATION_MS 10000 /* Watchdog timeout - elapsed time to allow for no video frames received */ #define SVP_WATCHDOG_TIMEOUT_MS 5000 /** Simple video player instance */ struct SVP_T { /* Player options */ SVP_OPTS_T opts; /* Bitmask of SVP_CREATED_XXX values indicating which fields have been created. * Only used for those fields which can't be (portably) determined from their * own value. */ uint32_t created; /* Semaphore used for synchronising buffer handling for decoded frames */ VCOS_SEMAPHORE_T sema; /* User supplied callbacks */ SVP_CALLBACKS_T callbacks; /* Container reader component */ MMAL_COMPONENT_T *reader; /* Video decoder component */ MMAL_COMPONENT_T *video_decode; /* Camera component */ MMAL_COMPONENT_T *camera; /* Connection: container reader -> video decoder */ MMAL_CONNECTION_T *connection; /* Output port from video decoder or camera */ MMAL_PORT_T *video_output; /* Pool of buffers for video decoder output */ MMAL_POOL_T *out_pool; /* Queue to hold decoded video frames */ MMAL_QUEUE_T *queue; /* Worker thread */ VCOS_THREAD_T thread; /* Timer to trigger stop in camera preview case */ VCOS_TIMER_T timer; /* Watchdog timer */ VCOS_TIMER_T wd_timer; /* Mutex to synchronise access to all following fields */ VCOS_MUTEX_T mutex; /* Stop control: 0 to process stream; bitmask of SVP_STOP_XXX values to stop */ uint32_t stop; /* Player stats */ SVP_STATS_T stats; }; /* Local function prototypes */ static MMAL_STATUS_T svp_setup_reader(MMAL_COMPONENT_T *reader, const char *uri, MMAL_PORT_T **video_port); static void svp_timer_cb(void *ctx); static void svp_watchdog_cb(void *ctx); static MMAL_STATUS_T svp_port_enable(SVP_T *svp, MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb); static void *svp_worker(void *arg); static void svp_bh_control_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); static void svp_bh_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); static void svp_reset_stop(SVP_T *svp); static void svp_set_stop(SVP_T *svp, uint32_t stop_flags); static uint32_t svp_get_stop(SVP_T *svp); /* Create Simple Video Player instance. */ SVP_T *svp_create(const char *uri, SVP_CALLBACKS_T *callbacks, const SVP_OPTS_T *opts) { SVP_T *svp; MMAL_STATUS_T st; VCOS_STATUS_T vst; MMAL_PORT_T *reader_output = NULL; MMAL_COMPONENT_T *video_decode = NULL; MMAL_PORT_T *video_output = NULL; LOG_TRACE("Creating player for %s", (uri ? uri : "camera preview")); vcos_assert(callbacks->video_frame_cb); vcos_assert(callbacks->stop_cb); svp = vcos_calloc(1, sizeof(*svp), "svp"); CHECK_STATUS((svp ? MMAL_SUCCESS : MMAL_ENOMEM), "Failed to allocate context"); svp->opts = *opts; svp->callbacks = *callbacks; /* Semaphore used for synchronising buffer handling for decoded frames */ vst = vcos_semaphore_create(&svp->sema, "svp-sem", 0); CHECK_STATUS((vst == VCOS_SUCCESS ? MMAL_SUCCESS : MMAL_ENOMEM), "Failed to create semaphore"); svp->created |= SVP_CREATED_SEM; vst = vcos_mutex_create(&svp->mutex, "svp-mutex"); CHECK_STATUS((vst == VCOS_SUCCESS ? MMAL_SUCCESS : MMAL_ENOMEM), "Failed to create mutex"); svp->created |= SVP_CREATED_MUTEX; vst = vcos_timer_create(&svp->timer, "svp-timer", svp_timer_cb, svp); CHECK_STATUS((vst == VCOS_SUCCESS ? MMAL_SUCCESS : MMAL_ENOMEM), "Failed to create timer"); svp->created |= SVP_CREATED_TIMER; vst = vcos_timer_create(&svp->wd_timer, "svp-wd-timer", svp_watchdog_cb, svp); CHECK_STATUS((vst == VCOS_SUCCESS ? MMAL_SUCCESS : MMAL_ENOMEM), "Failed to create timer"); svp->created |= SVP_CREATED_WD_TIMER; /* Create components */ svp->reader = NULL; svp->video_decode = NULL; svp->camera = NULL; svp->connection = NULL; if (uri) { /* Video from URI: setup container_reader -> video_decode */ /* Create and set up container reader */ st = mmal_component_create(MMAL_COMPONENT_DEFAULT_CONTAINER_READER, &svp->reader); CHECK_STATUS(st, "Failed to create container reader"); st = svp_setup_reader(svp->reader, uri, &reader_output); if (st != MMAL_SUCCESS) goto error; st = mmal_component_enable(svp->reader); CHECK_STATUS(st, "Failed to enable container reader"); st = svp_port_enable(svp, svp->reader->control, svp_bh_control_cb); CHECK_STATUS(st, "Failed to enable container reader control port"); /* Create and set up video decoder */ st = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &svp->video_decode); CHECK_STATUS(st, "Failed to create video decoder"); video_decode = svp->video_decode; video_output = video_decode->output[0]; st = mmal_component_enable(video_decode); CHECK_STATUS(st, "Failed to enable video decoder"); st = svp_port_enable(svp, video_decode->control, svp_bh_control_cb); CHECK_STATUS(st, "Failed to enable video decoder control port"); } else { /* Camera preview */ st = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &svp->camera); CHECK_STATUS(st, "Failed to create camera"); st = mmal_component_enable(svp->camera); CHECK_STATUS(st, "Failed to enable camera"); st = svp_port_enable(svp, svp->camera->control, svp_bh_control_cb); CHECK_STATUS(st, "Failed to enable camera control port"); video_output = svp->camera->output[0]; /* Preview port */ } st = mmal_port_parameter_set_boolean(video_output, MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE); CHECK_STATUS((st == MMAL_ENOSYS ? MMAL_SUCCESS : st), "Failed to enable zero copy"); if (uri) { /* Create connection: container_reader -> video_decoder */ st = mmal_connection_create(&svp->connection, reader_output, video_decode->input[0], MMAL_CONNECTION_FLAG_TUNNELLING); CHECK_STATUS(st, "Failed to create connection"); } /* Set video output port format. * Opaque encoding ensures we get buffer data as handles to relocatable heap. */ video_output->format->encoding = MMAL_ENCODING_OPAQUE; if (!uri) { /* Set video format for camera preview */ MMAL_VIDEO_FORMAT_T *vfmt = &video_output->format->es->video; CHECK_STATUS((video_output->format->type == MMAL_ES_TYPE_VIDEO) ? MMAL_SUCCESS : MMAL_EINVAL, "Output port isn't video format"); vfmt->width = SVP_CAMERA_WIDTH; vfmt->height = SVP_CAMERA_HEIGHT; vfmt->crop.x = 0; vfmt->crop.y = 0; vfmt->crop.width = vfmt->width; vfmt->crop.height = vfmt->height; vfmt->frame_rate.num = SVP_CAMERA_FRAMERATE; vfmt->frame_rate.den = 1; } st = mmal_port_format_commit(video_output); CHECK_STATUS(st, "Failed to set output port format"); /* Finally, set buffer num/size. N.B. For container_reader/video_decode, must be after * connection created, in order for port format to propagate. * Don't enable video output port until want to produce frames. */ video_output->buffer_num = video_output->buffer_num_recommended; video_output->buffer_size = video_output->buffer_size_recommended; /* Pool + queue to hold decoded video frames */ svp->out_pool = mmal_port_pool_create(video_output, video_output->buffer_num, video_output->buffer_size); CHECK_STATUS((svp->out_pool ? MMAL_SUCCESS : MMAL_ENOMEM), "Error allocating pool"); svp->queue = mmal_queue_create(); CHECK_STATUS((svp ? MMAL_SUCCESS : MMAL_ENOMEM), "Error allocating queue"); svp->video_output = video_output; return svp; error: svp_destroy(svp); return NULL; } /** * Setup container reader component. * Sets URI, to initialize processing, and finds a video track. * @param reader Container reader component. * @param uri Media URI. * @param video_port On success, the output port for the first video track is returned here. * @return MMAL_SUCCESS if the container reader was successfully set up and a video track located. */ static MMAL_STATUS_T svp_setup_reader(MMAL_COMPONENT_T *reader, const char *uri, MMAL_PORT_T **video_port) { MMAL_STATUS_T st; uint32_t track; st = mmal_util_port_set_uri(reader->control, uri); if (st != MMAL_SUCCESS) { LOG_ERROR("%s: couldn't open uri %s", reader->name, uri); return st; } /* Find a video track */ for (track = 0; track < reader->output_num; track++) { if (reader->output[track]->format->type == MMAL_ES_TYPE_VIDEO) { break; } } if (track == reader->output_num) { LOG_ERROR("%s: no video track", uri); return MMAL_EINVAL; } *video_port = reader->output[track]; return MMAL_SUCCESS; } /* Destroy SVP instance. svp may be NULL. */ void svp_destroy(SVP_T *svp) { if (svp) { MMAL_COMPONENT_T *components[] = { svp->reader, svp->video_decode, svp->camera }; MMAL_COMPONENT_T **comp; /* Stop thread, disable connection and components */ svp_stop(svp); for (comp = components; comp < components + vcos_countof(components); comp++) { mmal_component_disable(*comp); } /* Destroy connection + components */ if (svp->connection) { mmal_connection_destroy(svp->connection); } for (comp = components; comp < components + vcos_countof(components); comp++) { mmal_component_destroy(*comp); } /* Free remaining resources */ if (svp->out_pool) { mmal_pool_destroy(svp->out_pool); } if (svp->queue) { mmal_queue_destroy(svp->queue); } if (svp->created & SVP_CREATED_WD_TIMER) { vcos_timer_delete(&svp->wd_timer); } if (svp->created & SVP_CREATED_TIMER) { vcos_timer_delete(&svp->timer); } if (svp->created & SVP_CREATED_MUTEX) { vcos_mutex_delete(&svp->mutex); } if (svp->created & SVP_CREATED_SEM) { vcos_semaphore_delete(&svp->sema); } vcos_free(svp); } } /* Start SVP. Enables MMAL connection + creates worker thread. */ int svp_start(SVP_T *svp) { MMAL_STATUS_T st; VCOS_STATUS_T vst; /* Ensure SVP is stopped first */ svp_stop(svp); /* Reset the worker thread stop status, before enabling ports that might trigger a stop */ svp_reset_stop(svp); if (svp->connection) { /* Enable reader->decoder connection */ st = mmal_connection_enable(svp->connection); CHECK_STATUS(st, "Failed to create connection"); } /* Enable video output port */ st = svp_port_enable(svp, svp->video_output, svp_bh_output_cb); CHECK_STATUS(st, "Failed to enable output port"); /* Reset stats */ svp->stats.video_frame_count = 0; /* Create worker thread */ vst = vcos_thread_create(&svp->thread, "svp-worker", NULL, svp_worker, svp); CHECK_STATUS((vst == VCOS_SUCCESS ? MMAL_SUCCESS : MMAL_ENOMEM), "Failed to create connection"); svp->created |= SVP_CREATED_THREAD; /* Set timer */ if (svp->camera) { unsigned ms = svp->opts.duration_ms; vcos_timer_set(&svp->timer, ((ms == 0) ? SVP_CAMERA_DURATION_MS : ms)); } /* Start watchdog timer */ vcos_timer_set(&svp->wd_timer, SVP_WATCHDOG_TIMEOUT_MS); return 0; error: return -1; } /* Stop SVP. Stops worker thread + disables MMAL connection. */ void svp_stop(SVP_T *svp) { vcos_timer_cancel(&svp->wd_timer); vcos_timer_cancel(&svp->timer); /* Stop worker thread */ if (svp->created & SVP_CREATED_THREAD) { svp_set_stop(svp, SVP_STOP_USER); vcos_semaphore_post(&svp->sema); vcos_thread_join(&svp->thread, NULL); svp->created &= ~SVP_CREATED_THREAD; } if (svp->connection) { mmal_connection_disable(svp->connection); } mmal_port_disable(svp->video_output); } /* Get stats since last call to svp_start() */ void svp_get_stats(SVP_T *svp, SVP_STATS_T *stats) { vcos_mutex_lock(&svp->mutex); *stats = svp->stats; vcos_mutex_unlock(&svp->mutex); } /** Timer callback - stops playback */ static void svp_timer_cb(void *ctx) { SVP_T *svp = ctx; svp_set_stop(svp, SVP_STOP_TIMEUP); vcos_semaphore_post(&svp->sema); } /** Watchdog timer callback - stops playback */ static void svp_watchdog_cb(void *ctx) { SVP_T *svp = ctx; LOG_ERROR("%s: no frames received for %d ms, aborting", svp->video_output->name, SVP_WATCHDOG_TIMEOUT_MS); svp_set_stop(svp, SVP_STOP_ERROR); vcos_semaphore_post(&svp->sema); } /** Enable MMAL port, setting SVP instance as port userdata. */ static MMAL_STATUS_T svp_port_enable(SVP_T *svp, MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb) { port->userdata = (struct MMAL_PORT_USERDATA_T *)svp; return mmal_port_enable(port, cb); } /** Process decoded buffers queued by video decoder output callback */ static void svp_process_returned_bufs(SVP_T *svp) { SVP_CALLBACKS_T *callbacks = &svp->callbacks; MMAL_BUFFER_HEADER_T *buf; while ((buf = mmal_queue_get(svp->queue)) != NULL) { if ((svp_get_stop(svp) & SVP_STOP_ERROR) == 0) { callbacks->video_frame_cb(callbacks->ctx, buf->data); } svp->stats.video_frame_count++; mmal_buffer_header_release(buf); } } /** Worker thread. Ensures video decoder output is supplied with buffers and sends decoded frames * via user-supplied callback. * @param arg Pointer to SVP instance. * @return NULL always. */ static void *svp_worker(void *arg) { SVP_T *svp = arg; MMAL_PORT_T *video_output = svp->video_output; SVP_CALLBACKS_T *callbacks = &svp->callbacks; MMAL_BUFFER_HEADER_T *buf; MMAL_STATUS_T st; uint32_t stop; while (svp_get_stop(svp) == 0) { /* Send empty buffers to video decoder output port */ while ((buf = mmal_queue_get(svp->out_pool->queue)) != NULL) { st = mmal_port_send_buffer(video_output, buf); if (st != MMAL_SUCCESS) { LOG_ERROR("Failed to send buffer to %s", video_output->name); } } /* Process returned buffers */ svp_process_returned_bufs(svp); /* Block for buffer release */ vcos_semaphore_wait(&svp->sema); } /* Might have the last few buffers queued */ svp_process_returned_bufs(svp); /* Notify caller if we stopped unexpectedly */ stop = svp_get_stop(svp); LOG_TRACE("Worker thread exiting: stop=0x%x", (unsigned)stop); callbacks->stop_cb(callbacks->ctx, stop); return NULL; } /** Callback from a control port. */ static void svp_bh_control_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) { SVP_T *svp = (SVP_T *)port->userdata; switch (buf->cmd) { case MMAL_EVENT_EOS: LOG_TRACE("%s: EOS", port->name); svp_set_stop(svp, SVP_STOP_EOS); break; case MMAL_EVENT_ERROR: LOG_ERROR("%s: MMAL error: %s", port->name, mmal_status_to_string(*(MMAL_STATUS_T *)buf->data)); svp_set_stop(svp, SVP_STOP_ERROR); break; default: LOG_TRACE("%s: buf %p, event %4.4s", port->name, buf, (char *)&buf->cmd); break; } mmal_buffer_header_release(buf); vcos_semaphore_post(&svp->sema); } /** Callback from video decode output port. */ static void svp_bh_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) { SVP_T *svp = (SVP_T *)port->userdata; if (buf->length == 0) { LOG_TRACE("%s: zero-length buffer => EOS", port->name); svp_set_stop(svp, SVP_STOP_EOS); // This shouldn't be necessary, but it is ... mmal_buffer_header_release(buf); } else if (buf->data == NULL) { LOG_ERROR("%s: zero buffer handle", port->name); mmal_buffer_header_release(buf); } else { /* Reset watchdog timer */ vcos_timer_set(&svp->wd_timer, SVP_WATCHDOG_TIMEOUT_MS); /* Enqueue the decoded frame so we can return quickly to MMAL core */ mmal_queue_put(svp->queue, buf); } /* Notify worker */ vcos_semaphore_post(&svp->sema); } /** Reset svp->stop to 0, with locking. */ static void svp_reset_stop(SVP_T *svp) { vcos_mutex_lock(&svp->mutex); svp->stop = 0; vcos_mutex_unlock(&svp->mutex); } /** Set additional flags in svp->stop, with locking. */ static void svp_set_stop(SVP_T *svp, uint32_t stop_flags) { vcos_mutex_lock(&svp->mutex); svp->stop |= stop_flags; vcos_mutex_unlock(&svp->mutex); } /** Get value of svp->stop, with locking. */ static uint32_t svp_get_stop(SVP_T *svp) { uint32_t stop; vcos_mutex_lock(&svp->mutex); stop = svp->stop; vcos_mutex_unlock(&svp->mutex); return stop; } userland/host_applications/android/apps/vidtex/svp.h000066400000000000000000000120721421703157200232710ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef SVP_H #define SVP_H #ifdef __cplusplus extern "C" { #endif /** \file * Simple video player using MMAL. * Uses MMAL container reader plus video decode component, and provides callback to retrieve * buffers of decoded video frames. * * Thread-safety: The public API functions must be called from the same thread for a given SVP_T * instance. The user is notified of decoded frames and other events from a separate * thread, started by svp_start(). */ /** Flags indicating reason for processing stop */ /** Stop on to user request */ #define SVP_STOP_USER (1 << 0) /** Stop on end-of-stream */ #define SVP_STOP_EOS (1 << 1) /** Stop on error */ #define SVP_STOP_ERROR (1 << 2) /** Stop on timer */ #define SVP_STOP_TIMEUP (1 << 3) /** * Callback functions and context for player to communicate asynchronous data * and events to user. */ typedef struct SVP_CALLBACKS_T { /** Private pointer specified by caller, passed to callback functions. * Not examined by svp_ functions, so may have any value, including NULL. */ void *ctx; /** Callback for decoded video frames. * The buffer is released when this function returns, so the callback must either have processed * the buffer or acquired a reference before returning. * @param ctx Caller's private context, as specified by SVP_CALLBACKS_T::ctx. * @param ob MMAL opaque buffer handle. */ void (*video_frame_cb)(void *ctx, void *buf); /** Callback for end of processing. * @param ctx Caller's private context, as specified by SVP_CALLBACKS_T::ctx. * @param reason Bitmask of SVP_STOP_XXX values giving reason for stopping. */ void (*stop_cb)(void *ctx, uint32_t stop_reason); } SVP_CALLBACKS_T; /** Options to SVP playback */ typedef struct SVP_OPTS_T { /** Duration of playback, in milliseconds. 0 = default, which is full duration for media and * a limited duration for camera. */ unsigned duration_ms; } SVP_OPTS_T; /** Playback stats */ typedef struct SVP_STATS_T { /** Total number of video frames processed since the last call to svp_start(). * 0 if svp_start() has never been called. */ unsigned video_frame_count; } SVP_STATS_T; /** Simple Video Player instance. Opaque structure. */ typedef struct SVP_T SVP_T; /** * Create a simple video player instance. * @param uri URI to media, or NULL to use camera preview. * @param callbacks Callbacks for caller to receive notification of events, * such as decoded buffers. * @param opts Player options. * @return Newly created simple video player instance, or NULL on error. */ SVP_T *svp_create(const char *uri, SVP_CALLBACKS_T *callbacks, const SVP_OPTS_T *opts); /** * Destroy a simple video player instance. * @param svp Simple video player instance. May be NULL, in which case this * function does nothing. */ void svp_destroy(SVP_T *svp); /** * Start a simple video player instance. * Decoded frames are returned to the SVP_CALLBACKS_T::video_frame_cb function * passed to svp_create(). * @param svp Simple video player instance. * @return 0 on success; -1 on failure. */ int svp_start(SVP_T *svp); /** * Stop a simple video player instance. * If the player is not running, then this function does nothing. * @param svp Simple video player instance. */ void svp_stop(SVP_T *svp); /** * Get player stats. * @param svp Simple video player instance. * @param stats Buffer in which stats are returned. */ void svp_get_stats(SVP_T *svp, SVP_STATS_T *stats); #ifdef __cplusplus } #endif #endif /* SVP_H */ userland/host_applications/android/apps/vidtex/vidtex.c000066400000000000000000000355211421703157200237630ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include #include #include "applog.h" #include "interface/vcos/vcos.h" #include "interface/vcos/vcos_stdbool.h" #include "interface/khronos/include/EGL/eglext_brcm.h" #include "vidtex.h" /** Max number of simultaneous EGL images supported = max number of distinct video decoder * buffers. */ #define VT_MAX_IMAGES 32 /** Maximum permitted difference between the number of EGL buffer swaps and number of video * frames. */ #define VT_MAX_FRAME_DISPARITY 2 /** Mapping of MMAL opaque buffer handle to EGL image */ typedef struct VIDTEX_IMAGE_SLOT_T { /* Decoded video frame, as MMAL opaque buffer handle. NULL => unused slot. */ void *video_frame; /* Corresponding EGL image */ EGLImageKHR image; } VIDTEX_IMAGE_SLOT_T; /** * Video Texture. Displays video from a URI or camera onto an EGL surface. */ typedef struct VIDTEX_T { /** Test options; bitmask of VIDTEX_OPT_XXX values */ uint32_t opts; /* Semaphore to synchronise use of decoded frame (video_frame). */ VCOS_SEMAPHORE_T sem_decoded; /* Semaphore to synchronise drawing of video frame. */ VCOS_SEMAPHORE_T sem_drawn; /* Mutex to guard access to quit field. */ VCOS_MUTEX_T mutex; /* Signal thread to quit. */ bool quit; /* Reason for quitting */ uint32_t stop_reason; /* EGL display/surface/context on which to render the video */ EGLDisplay display; EGLSurface surface; EGLContext context; /* EGL texture name */ GLuint texture; /* Table of EGL images corresponding to MMAL opaque buffer handles */ VIDTEX_IMAGE_SLOT_T slots[VT_MAX_IMAGES]; /* Current decoded video frame, as MMAL opaque buffer handle. * NULL if no buffer currently available. */ void *video_frame; /* Number of EGL buffer swaps */ unsigned num_swaps; } VIDTEX_T; /* Vertex co-ordinates: * * v0----v1 * | | * | | * | | * v3----v2 */ static const GLfloat vt_vertices[] = { #define VT_V0 -0.8, 0.8, 0.8, #define VT_V1 0.8, 0.8, 0.8, #define VT_V2 0.8, -0.8, 0.8, #define VT_V3 -0.8, -0.8, 0.8, VT_V0 VT_V3 VT_V2 VT_V2 VT_V1 VT_V0 }; /* Texture co-ordinates: * * (0,0) b--c * | | * a--d * * b,a,d d,c,b */ static const GLfloat vt_tex_coords[] = { 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0 }; /* Local function prototypes */ static VIDTEX_T *vidtex_create(EGLNativeWindowType win); static void vidtex_destroy(VIDTEX_T *vt); static int vidtex_gl_init(VIDTEX_T *vt, EGLNativeWindowType win); static void vidtex_gl_term(VIDTEX_T *vt); static void vidtex_destroy_images(VIDTEX_T *vt); static int vidtex_play(VIDTEX_T *vt, const VIDTEX_PARAMS_T *params); static void vidtex_check_gl(VIDTEX_T *vt, uint32_t line); static void vidtex_draw(VIDTEX_T *vt, void *video_frame); static void vidtex_flush_gl(VIDTEX_T *vt); static bool vidtex_set_quit(VIDTEX_T *vt, bool quit); static void vidtex_video_frame_cb(void *ctx, void *ob); static void vidtex_stop_cb(void *ctx, uint32_t stop_reason); #define VIDTEX_CHECK_GL(VT) vidtex_check_gl(VT, __LINE__) /** Create a new vidtex instance */ static VIDTEX_T *vidtex_create(EGLNativeWindowType win) { VIDTEX_T *vt; VCOS_STATUS_T st; vt = vcos_calloc(1, sizeof(*vt), "vidtex"); if (vt == NULL) { vcos_log_trace("Memory allocation failure"); return NULL; } st = vcos_semaphore_create(&vt->sem_decoded, "vidtex-dec", 0); if (st != VCOS_SUCCESS) { vcos_log_trace("Error creating semaphore"); goto error_ctx; } st = vcos_semaphore_create(&vt->sem_drawn, "vidtex-drw", 0); if (st != VCOS_SUCCESS) { vcos_log_trace("Error creating semaphore"); goto error_sem1; } st = vcos_mutex_create(&vt->mutex, "vidtex"); if (st != VCOS_SUCCESS) { vcos_log_trace("Error creating semaphore"); goto error_sem2; } if (vidtex_gl_init(vt, win) != 0) { vcos_log_trace("Error initialising EGL"); goto error_mutex; } vt->quit = false; vt->stop_reason = 0; return vt; error_mutex: vcos_mutex_delete(&vt->mutex); error_sem2: vcos_semaphore_delete(&vt->sem_drawn); error_sem1: vcos_semaphore_delete(&vt->sem_decoded); error_ctx: vcos_free(vt); return NULL; } /** Destroy a vidtex instance */ static void vidtex_destroy(VIDTEX_T *vt) { vidtex_gl_term(vt); vcos_mutex_delete(&vt->mutex); vcos_semaphore_delete(&vt->sem_drawn); vcos_semaphore_delete(&vt->sem_decoded); vcos_free(vt); } /** Init GL using a native window */ static int vidtex_gl_init(VIDTEX_T *vt, EGLNativeWindowType win) { const EGLint attribs[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_DEPTH_SIZE, 0, EGL_NONE }; EGLConfig config; EGLint num_configs; vt->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(vt->display, 0, 0); eglChooseConfig(vt->display, attribs, &config, 1, &num_configs); vt->surface = eglCreateWindowSurface(vt->display, config, win, NULL); vt->context = eglCreateContext(vt->display, config, NULL, NULL); if (!eglMakeCurrent(vt->display, vt->surface, vt->surface, vt->context)) { vidtex_gl_term(vt); return -1; } glGenTextures(1, &vt->texture); glShadeModel(GL_FLAT); glDisable(GL_DITHER); glDisable(GL_SCISSOR_TEST); glEnable(GL_TEXTURE_EXTERNAL_OES); glDisable(GL_TEXTURE_2D); return 0; } /** Terminate GL */ static void vidtex_gl_term(VIDTEX_T *vt) { vidtex_destroy_images(vt); /* Delete texture name */ glDeleteTextures(1, &vt->texture); /* Terminate EGL */ eglMakeCurrent(vt->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(vt->display, vt->context); eglDestroySurface(vt->display, vt->surface); eglTerminate(vt->display); } /** Destroy all EGL images */ static void vidtex_destroy_images(VIDTEX_T *vt) { VIDTEX_IMAGE_SLOT_T *slot; for (slot = vt->slots; slot < vt->slots + vcos_countof(vt->slots); slot++) { slot->video_frame = NULL; if (slot->image) { vcos_log_trace("Destroying EGL image %p", slot->image); eglDestroyImageKHR(vt->display, slot->image); slot->image = NULL; } } } /** Play video - from URI or camera - on EGL surface. */ static int vidtex_play(VIDTEX_T *vt, const VIDTEX_PARAMS_T *params) { const char *uri; SVP_CALLBACKS_T callbacks; SVP_T *svp; SVP_OPTS_T opts; SVP_STATS_T stats; int rv = -1; uri = (params->uri[0] == '\0') ? NULL : params->uri; vt->opts = params->opts; callbacks.ctx = vt; callbacks.video_frame_cb = vidtex_video_frame_cb; callbacks.stop_cb = vidtex_stop_cb; opts.duration_ms = params->duration_ms; svp = svp_create(uri, &callbacks, &opts); if (svp) { /* Reset stats */ vt->num_swaps = 0; /* Run video player until receive quit notification */ if (svp_start(svp) == 0) { while (!vidtex_set_quit(vt, false)) { vcos_semaphore_wait(&vt->sem_decoded); if (vt->video_frame) { vidtex_draw(vt, vt->video_frame); vcos_semaphore_post(&vt->sem_drawn); } } vcos_semaphore_post(&vt->sem_drawn); /* Dump stats */ svp_get_stats(svp, &stats); vcos_log_info("video frames decoded: %6u", stats.video_frame_count); vcos_log_info("EGL buffer swaps: %6u", vt->num_swaps); /* Determine status of operation and log errors */ if (vt->stop_reason & SVP_STOP_ERROR) { vcos_log_error("vidtex exiting on error"); } else if (vt->num_swaps == 0) { vcos_log_error("vidtex completed with no EGL buffer swaps"); } else if (abs((int)vt->num_swaps - (int)stats.video_frame_count) > VT_MAX_FRAME_DISPARITY) { vcos_log_error("vidtex completed with %u EGL buffer swaps, but %u video frames", vt->num_swaps, (int)stats.video_frame_count); } else { rv = 0; } } svp_destroy(svp); } vidtex_flush_gl(vt); return rv; } /** Check for OpenGL errors - logs any errors and sets quit flag */ static void vidtex_check_gl(VIDTEX_T *vt, uint32_t line) { GLenum error = glGetError(); int abort = 0; while (error != GL_NO_ERROR) { vcos_log_error("GL error: line %d error 0x%04x", line, error); abort = 1; error = glGetError(); } if (abort) vidtex_stop_cb(vt, SVP_STOP_ERROR); } /* Draw one video frame onto EGL surface. * @param vt vidtex instance. * @param video_frame MMAL opaque buffer handle for decoded video frame. Can't be NULL. */ static void vidtex_draw(VIDTEX_T *vt, void *video_frame) { EGLImageKHR image; VIDTEX_IMAGE_SLOT_T *slot; static uint32_t frame_num = 0; vcos_assert(video_frame); glClearColor(0, 0, 0, 0); glClearDepthf(1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glBindTexture(GL_TEXTURE_EXTERNAL_OES, vt->texture); VIDTEX_CHECK_GL(vt); /* Lookup or create EGL image corresponding to supplied buffer handle. * N.B. Slot array is filled in sequentially, with the images all destroyed together on * vidtex termination; it never has holes. */ image = EGL_NO_IMAGE_KHR; for (slot = vt->slots; slot < vt->slots + vcos_countof(vt->slots); slot++) { if (slot->video_frame == video_frame) { vcos_assert(slot->image); image = slot->image; break; } if (slot->video_frame == NULL) { EGLenum target; vcos_assert(slot->image == NULL); if (vt->opts & VIDTEX_OPT_Y_TEXTURE) target = EGL_IMAGE_BRCM_MULTIMEDIA_Y; else if (vt->opts & VIDTEX_OPT_U_TEXTURE) target = EGL_IMAGE_BRCM_MULTIMEDIA_U; else if (vt->opts & VIDTEX_OPT_V_TEXTURE) target = EGL_IMAGE_BRCM_MULTIMEDIA_V; else target = EGL_IMAGE_BRCM_MULTIMEDIA; image = eglCreateImageKHR(vt->display, EGL_NO_CONTEXT, target, (EGLClientBuffer)video_frame, NULL); if (image == EGL_NO_IMAGE_KHR) { vcos_log_error("EGL image conversion error"); } else { vcos_log_trace("Created EGL image %p for buf %p", image, video_frame); slot->video_frame = video_frame; slot->image = image; } VIDTEX_CHECK_GL(vt); break; } } if (slot == vt->slots + vcos_countof(vt->slots)) { vcos_log_error("Exceeded configured max number of EGL images"); } /* Draw the EGL image */ if (image != EGL_NO_IMAGE_KHR) { /* Assume 30fps */ int frames_per_rev = 30 * 15; GLfloat angle = (frame_num * 360) / (GLfloat) frames_per_rev; frame_num = (frame_num + 1) % frames_per_rev; glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image); VIDTEX_CHECK_GL(vt); glRotatef(angle, 0.0, 0.0, 1.0); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, vt_vertices); glDisableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(2, GL_FLOAT, 0, vt_tex_coords); glDrawArrays(GL_TRIANGLES, 0, vcos_countof(vt_tex_coords) / 2); eglSwapBuffers(vt->display, vt->surface); if (vt->opts & VIDTEX_OPT_IMG_PER_FRAME) { vidtex_destroy_images(vt); } vt->num_swaps++; } VIDTEX_CHECK_GL(vt); } /** Do some GL stuff in order to ensure that any multimedia-related GL buffers have been released * if they are going to be released. */ static void vidtex_flush_gl(VIDTEX_T *vt) { int i; glFlush(); glClearColor(0, 0, 0, 0); for (i = 0; i < 10; i++) { glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(vt->display, vt->surface); VIDTEX_CHECK_GL(vt); } glFlush(); VIDTEX_CHECK_GL(vt); } /** Set quit flag, with locking. * @param quit New value of the quit flag: true - command thread to quit; false - command thread * to continue. * @return Old value of the quit flag. */ static bool vidtex_set_quit(VIDTEX_T *vt, bool quit) { vcos_mutex_lock(&vt->mutex); bool old_quit = vt->quit; vt->quit = quit; vcos_mutex_unlock(&vt->mutex); return old_quit; } /** Callback to receive decoded video frame */ static void vidtex_video_frame_cb(void *ctx, void *ob) { if (ob) { VIDTEX_T *vt = ctx; /* coverity[missing_lock] Coverity gets confused by the semaphore locking scheme */ vt->video_frame = ob; vcos_semaphore_post(&vt->sem_decoded); vcos_semaphore_wait(&vt->sem_drawn); vt->video_frame = NULL; } } /** Callback to receive stop notification. Sets quit flag and posts semaphore. * @param ctx VIDTEX_T instance. Declared as void * in order to use as SVP callback. * @param stop_reason SVP stop reason. */ static void vidtex_stop_cb(void *ctx, uint32_t stop_reason) { VIDTEX_T *vt = ctx; vt->stop_reason = stop_reason; vidtex_set_quit(vt, true); vcos_semaphore_post(&vt->sem_decoded); } /* Convenience function to create/play/destroy */ int vidtex_run(const VIDTEX_PARAMS_T *params, EGLNativeWindowType win) { VIDTEX_T *vt; int rv = -1; vt = vidtex_create(win); if (vt) { rv = vidtex_play(vt, params); vidtex_destroy(vt); } return rv; } userland/host_applications/android/apps/vidtex/vidtex.h000066400000000000000000000063271421703157200237720ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef VIDTEX_H #define VIDTEX_H #ifdef __cplusplus extern "C" { #endif /** \file Video-on-texture display. * Displays video decoded from a media URI or from camera preview onto an EGL surface. */ #include #include "interface/vcos/vcos.h" #include "interface/vcos/vcos_stdint.h" #include "svp.h" /** Max length of a vidtex URI */ #define VIDTEX_MAX_URI 256 /** Test option - create + destroy EGL image every frame */ #define VIDTEX_OPT_IMG_PER_FRAME (1 << 0) /** Test option - display the Y' plane as greyscale texture */ #define VIDTEX_OPT_Y_TEXTURE (1 << 1) /** Test option - display the U plane as greyscale texture */ #define VIDTEX_OPT_U_TEXTURE (1 << 2) /** Test option - display the V plane as greyscale texture */ #define VIDTEX_OPT_V_TEXTURE (1 << 3) /** Parameters to vidtex run. * N.B. Types need to be bitwise copyable, and between C and C++, so don't use pointers, bool * or anything else liable for problems. */ typedef struct VIDTEX_PARAMS_T { /** Duration of playback, in milliseconds. 0 = default, which is full duration for media and * a limited duration for camera. */ uint32_t duration_ms; /** Media URI, or empty string to use camera preview */ char uri[VIDTEX_MAX_URI]; /** Test options; bitmask of VIDTEX_OPT_XXX values */ uint32_t opts; } VIDTEX_PARAMS_T; /** Run video-on-texture. * @param params Parameters to video-on-texture display. * @param win Native window handle. * @return 0 on success; -1 on failure. * It is considered successful if the video decode completed without error and at least * one EGL image was shown. */ int vidtex_run(const VIDTEX_PARAMS_T *params, EGLNativeWindowType win); #ifdef __cplusplus } #endif #endif userland/host_applications/framework/000077500000000000000000000000001421703157200204155ustar00rootroot00000000000000userland/host_applications/framework/common/000077500000000000000000000000001421703157200217055ustar00rootroot00000000000000userland/host_applications/framework/common/host_ilcore.h000066400000000000000000000064541421703157200244010ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef HOST_ILCORE_H #define HOST_ILCORE_H #ifdef WANT_OMX_NAME_MANGLE OMX_API OMX_ERRORTYPE OMX_APIENTRY host_OMX_Init(void); OMX_API OMX_ERRORTYPE OMX_APIENTRY host_OMX_Deinit(void); OMX_API OMX_ERRORTYPE OMX_APIENTRY host_OMX_ComponentNameEnum( OMX_OUT OMX_STRING cComponentName, OMX_IN OMX_U32 nNameLength, OMX_IN OMX_U32 nIndex); OMX_API OMX_ERRORTYPE OMX_APIENTRY host_OMX_GetHandle( OMX_OUT OMX_HANDLETYPE* pHandle, OMX_IN OMX_STRING cComponentName, OMX_IN OMX_PTR pAppData, OMX_IN OMX_CALLBACKTYPE* pCallBacks); OMX_API OMX_ERRORTYPE OMX_APIENTRY host_OMX_FreeHandle( OMX_IN OMX_HANDLETYPE hComponent); OMX_API OMX_ERRORTYPE OMX_APIENTRY host_OMX_SetupTunnel( OMX_IN OMX_HANDLETYPE hOutput, OMX_IN OMX_U32 nPortOutput, OMX_IN OMX_HANDLETYPE hInput, OMX_IN OMX_U32 nPortInput); OMX_API OMX_ERRORTYPE host_OMX_GetComponentsOfRole ( OMX_IN OMX_STRING role, OMX_INOUT OMX_U32 *pNumComps, OMX_INOUT OMX_U8 **compNames); OMX_API OMX_ERRORTYPE host_OMX_GetRolesOfComponent ( OMX_IN OMX_STRING compName, OMX_INOUT OMX_U32 *pNumRoles, OMX_OUT OMX_U8 **roles); OMX_ERRORTYPE host_OMX_GetDebugInformation ( OMX_OUT OMX_STRING debugInfo, OMX_INOUT OMX_S32 *pLen); #define OMX_Init host_OMX_Init #define OMX_Deinit host_OMX_Deinit #define OMX_ComponentNameEnum host_OMX_ComponentNameEnum #define OMX_GetHandle host_OMX_GetHandle #define OMX_FreeHandle host_OMX_FreeHandle #define OMX_SetupTunnel host_OMX_SetupTunnel #define OMX_GetComponentsOfRole host_OMX_GetComponentsOfRole #define OMX_GetRolesOfComponent host_OMX_GetRolesOfComponent #define OMX_GetDebugInformation host_OMX_GetDebugInformation #else OMX_ERRORTYPE OMX_GetDebugInformation ( OMX_OUT OMX_STRING debugInfo, OMX_INOUT OMX_S32 *pLen); #endif #endif // HOST_ILCORE_H userland/host_applications/framework/common/ilcore.c000066400000000000000000000235361421703157200233370ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include //includes #include #include #include #include #include "interface/vmcs_host/khronos/IL/OMX_Component.h" #include "interface/vmcs_host/vcilcs.h" #include "interface/vmcs_host/vchost.h" #include "interface/vmcs_host/vcilcs_common.h" #include "host_ilcore.h" #ifdef WANT_OMX_NAME_MANGLE #define OMX_Deinit host_OMX_Deinit #define OMX_Init host_OMX_Init #define OMX_SetupTunnel host_OMX_SetupTunnel #define OMX_FreeHandle host_OMX_FreeHandle #define OMX_GetHandle host_OMX_GetHandle #define OMX_GetRolesOfComponent host_OMX_GetRolesOfComponent #define OMX_GetComponentsOfRole host_OMX_GetComponentsOfRole #define OMX_ComponentNameEnum host_OMX_ComponentNameEnum #define OMX_GetDebugInformation host_OMX_GetDebugInformation #endif #ifdef WANT_LOCAL_OMX // xxx: we need to link to local OpenMAX IL core as well OMX_API OMX_ERRORTYPE OMX_APIENTRY vc_OMX_Init(void); OMX_API OMX_ERRORTYPE OMX_APIENTRY vc_OMX_Deinit(void); OMX_API OMX_ERRORTYPE OMX_APIENTRY vc_OMX_GetHandle( OMX_OUT OMX_HANDLETYPE* pHandle, OMX_IN OMX_STRING cComponentName, OMX_IN OMX_PTR pAppData, OMX_IN OMX_CALLBACKTYPE* pCallBacks); OMX_API OMX_ERRORTYPE OMX_APIENTRY vc_OMX_FreeHandle( OMX_IN OMX_HANDLETYPE hComponent); #endif static int coreInit = 0; static int nActiveHandles = 0; static ILCS_SERVICE_T *ilcs_service = NULL; static VCOS_MUTEX_T lock; static VCOS_ONCE_T once = VCOS_ONCE_INIT; /* Atomic creation of lock protecting shared state */ static void initOnce(void) { VCOS_STATUS_T status; status = vcos_mutex_create(&lock, VCOS_FUNCTION); vcos_demand(status == VCOS_SUCCESS); } /* OMX_Init */ OMX_ERRORTYPE OMX_APIENTRY OMX_Init(void) { VCOS_STATUS_T status; OMX_ERRORTYPE err = OMX_ErrorNone; status = vcos_once(&once, initOnce); vcos_demand(status == VCOS_SUCCESS); vcos_mutex_lock(&lock); #ifdef WANT_LOCAL_OMX vc_OMX_Init(); // initialise local core first #endif if(coreInit == 0) { // we need to connect via an ILCS connection to VideoCore VCHI_INSTANCE_T initialise_instance; VCHI_CONNECTION_T *connection; ILCS_CONFIG_T config; vc_host_get_vchi_state(&initialise_instance, &connection); vcilcs_config(&config); #ifdef USE_VCHIQ_ARM ilcs_service = ilcs_init((VCHIQ_INSTANCE_T) initialise_instance, (void **) &connection, &config, 0); #else ilcs_service = ilcs_init((VCHIQ_STATE_T *) initialise_instance, (void **) &connection, &config, 0); #endif if(ilcs_service == NULL) { err = OMX_ErrorHardware; goto end; } coreInit = 1; } #ifdef USE_VCHIQ_ARM else coreInit++; #endif end: vcos_mutex_unlock(&lock); return err; } /* OMX_Deinit */ OMX_ERRORTYPE OMX_APIENTRY OMX_Deinit(void) { if(coreInit == 0) // || (coreInit == 1 && nActiveHandles > 0)) return OMX_ErrorNotReady; vcos_mutex_lock(&lock); #ifdef USE_VCHIQ_ARM coreInit--; #endif if(coreInit == 0) { // we need to teardown the ILCS connection to VideoCore ilcs_deinit(ilcs_service); ilcs_service = NULL; } #ifdef WANT_LOCAL_OMX vc_OMX_Deinit(); // deinitialise local core as well #endif vcos_mutex_unlock(&lock); return OMX_ErrorNone; } /* OMX_ComponentNameEnum */ OMX_ERRORTYPE OMX_APIENTRY OMX_ComponentNameEnum( OMX_OUT OMX_STRING cComponentName, OMX_IN OMX_U32 nNameLength, OMX_IN OMX_U32 nIndex) { if(ilcs_service == NULL) return OMX_ErrorBadParameter; return vcil_out_component_name_enum(ilcs_get_common(ilcs_service), cComponentName, nNameLength, nIndex); } /* OMX_GetHandle */ OMX_ERRORTYPE OMX_APIENTRY OMX_GetHandle( OMX_OUT OMX_HANDLETYPE* pHandle, OMX_IN OMX_STRING cComponentName, OMX_IN OMX_PTR pAppData, OMX_IN OMX_CALLBACKTYPE* pCallBacks) { OMX_ERRORTYPE eError; OMX_COMPONENTTYPE *pComp; OMX_HANDLETYPE hHandle = 0; if (pHandle == NULL || cComponentName == NULL || pCallBacks == NULL || ilcs_service == NULL) { if(pHandle) *pHandle = NULL; return OMX_ErrorBadParameter; } #if defined(WANT_LOCAL_OMX) && 0 if ((eError = vc_OMX_GetHandle(pHandle, cComponentName, pAppData, pCallBacks)) != OMX_ErrorNone) #endif { pComp = (OMX_COMPONENTTYPE *)malloc(sizeof(OMX_COMPONENTTYPE)); if (!pComp) { vcos_assert(0); return OMX_ErrorInsufficientResources; } memset(pComp, 0, sizeof(OMX_COMPONENTTYPE)); hHandle = (OMX_HANDLETYPE)pComp; pComp->nSize = sizeof(OMX_COMPONENTTYPE); pComp->nVersion.nVersion = OMX_VERSION; eError = vcil_out_create_component(ilcs_get_common(ilcs_service), hHandle, cComponentName); if (eError == OMX_ErrorNone) { // Check that all function pointers have been filled in. // All fields should be non-zero. int i; uint32_t *p = (uint32_t *) pComp; for(i=0; i>2; i++) if(*p++ == 0) eError = OMX_ErrorInvalidComponent; if(eError != OMX_ErrorNone && pComp->ComponentDeInit) pComp->ComponentDeInit(hHandle); } if (eError == OMX_ErrorNone) { eError = pComp->SetCallbacks(hHandle,pCallBacks,pAppData); if (eError != OMX_ErrorNone) pComp->ComponentDeInit(hHandle); } if (eError == OMX_ErrorNone) { *pHandle = hHandle; } else { *pHandle = NULL; free(pComp); } } if (eError == OMX_ErrorNone) { vcos_mutex_lock(&lock); nActiveHandles++; vcos_mutex_unlock(&lock); } return eError; } /* OMX_FreeHandle */ OMX_ERRORTYPE OMX_APIENTRY OMX_FreeHandle( OMX_IN OMX_HANDLETYPE hComponent) { OMX_ERRORTYPE eError = OMX_ErrorNone; OMX_COMPONENTTYPE *pComp; if (hComponent == NULL || ilcs_service == NULL) return OMX_ErrorBadParameter; pComp = (OMX_COMPONENTTYPE*)hComponent; #ifdef WANT_LOCAL_OMX // xxx: a bit of a bodge, we rely on knowing that // the local core doesn't make use of this field but // ILCS does... if (pComp->pApplicationPrivate == NULL) return vc_OMX_FreeHandle(hComponent); #endif if (ilcs_service == NULL) return OMX_ErrorBadParameter; eError = (pComp->ComponentDeInit)(hComponent); if (eError == OMX_ErrorNone) { vcos_mutex_lock(&lock); --nActiveHandles; vcos_mutex_unlock(&lock); free(pComp); } vcos_assert(nActiveHandles >= 0); return eError; } /* OMX_SetupTunnel */ OMX_ERRORTYPE OMX_APIENTRY OMX_SetupTunnel( OMX_IN OMX_HANDLETYPE hOutput, OMX_IN OMX_U32 nPortOutput, OMX_IN OMX_HANDLETYPE hInput, OMX_IN OMX_U32 nPortInput) { OMX_ERRORTYPE eError = OMX_ErrorNone; OMX_COMPONENTTYPE *pCompIn, *pCompOut; OMX_TUNNELSETUPTYPE oTunnelSetup; if ((hOutput == NULL && hInput == NULL) || ilcs_service == NULL) return OMX_ErrorBadParameter; oTunnelSetup.nTunnelFlags = 0; oTunnelSetup.eSupplier = OMX_BufferSupplyUnspecified; pCompOut = (OMX_COMPONENTTYPE*)hOutput; if (hOutput){ eError = pCompOut->ComponentTunnelRequest(hOutput, nPortOutput, hInput, nPortInput, &oTunnelSetup); } if (eError == OMX_ErrorNone && hInput) { pCompIn = (OMX_COMPONENTTYPE*)hInput; eError = pCompIn->ComponentTunnelRequest(hInput, nPortInput, hOutput, nPortOutput, &oTunnelSetup); if (eError != OMX_ErrorNone && hOutput) { /* cancel tunnel request on output port since input port failed */ pCompOut->ComponentTunnelRequest(hOutput, nPortOutput, NULL, 0, NULL); } } return eError; } /* OMX_GetComponentsOfRole */ OMX_ERRORTYPE OMX_GetComponentsOfRole ( OMX_IN OMX_STRING role, OMX_INOUT OMX_U32 *pNumComps, OMX_INOUT OMX_U8 **compNames) { OMX_ERRORTYPE eError = OMX_ErrorNone; *pNumComps = 0; return eError; } /* OMX_GetRolesOfComponent */ OMX_ERRORTYPE OMX_GetRolesOfComponent ( OMX_IN OMX_STRING compName, OMX_INOUT OMX_U32 *pNumRoles, OMX_OUT OMX_U8 **roles) { OMX_ERRORTYPE eError = OMX_ErrorNone; *pNumRoles = 0; return eError; } /* OMX_GetDebugInformation */ OMX_ERRORTYPE OMX_GetDebugInformation ( OMX_OUT OMX_STRING debugInfo, OMX_INOUT OMX_S32 *pLen) { if(ilcs_service == NULL) return OMX_ErrorBadParameter; return vcil_out_get_debug_information(ilcs_get_common(ilcs_service), debugInfo, pLen); } /* File EOF */ userland/host_applications/linux/000077500000000000000000000000001421703157200175575ustar00rootroot00000000000000userland/host_applications/linux/CMakeLists.txt000066400000000000000000000010421421703157200223140ustar00rootroot00000000000000# linux apps add_subdirectory(libs/bcm_host) add_subdirectory(apps/gencmd) add_subdirectory(apps/tvservice) add_subdirectory(apps/vcmailbox) if(NOT ARM64) add_subdirectory(apps/raspicam) add_subdirectory(libs/sm) add_subdirectory(apps/smem) endif() add_subdirectory(libs/debug_sym) add_subdirectory(apps/dtoverlay) add_subdirectory(apps/dtmerge) if(ALL_APPS) add_subdirectory(apps/vcdbg) add_subdirectory(libs/elftoolchain) # add_subdirectory(apps/smct) add_subdirectory(apps/edid_parser) add_subdirectory(apps/hello_pi) endif() userland/host_applications/linux/apps/000077500000000000000000000000001421703157200205225ustar00rootroot00000000000000userland/host_applications/linux/apps/dtmerge/000077500000000000000000000000001421703157200221515ustar00rootroot00000000000000userland/host_applications/linux/apps/dtmerge/CMakeLists.txt000077500000000000000000000010521421703157200247120ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) get_filename_component (VIDEOCORE_ROOT ../../../.. ABSOLUTE) include (${VIDEOCORE_ROOT}/makefiles/cmake/global_settings.cmake) if (NOT WIN32) add_definitions(-Wall -Werror) endif () include_directories ( ${VIDEOCORE_HEADERS_BUILD_DIR} ${VIDEOCORE_ROOT} ${VIDEOCORE_ROOT}/opensrc/helpers/libfdt ${VIDEOCORE_ROOT}/helpers/dtoverlay ) add_executable(dtmerge dtmerge.c) target_link_libraries(dtmerge dtovl) install(TARGETS dtmerge RUNTIME DESTINATION bin) install(FILES dtmerge.1 DESTINATION man/man1) userland/host_applications/linux/apps/dtmerge/dtmerge.1000066400000000000000000000027061421703157200236670ustar00rootroot00000000000000.TH DTMERGE 1 . .SH NAME dtmerge \- merge an overlay or parameters into a base device-tree . . .SH SYNOPSIS .SY dtmerge .OP \-d .I base-dtb .I merged-dtb .I overlay-dtb .RI [ param=val \|.\|.\|.] .YS . .SY dtmerge .B \-h .YS . . .SH DESCRIPTION .B dtmerge is a command line utility for merging overlays and/or device-tree parameters with a base device-tree. See .B [DTREE] for more information. . .PP The base device-tree (in dtb format) is always specified as the first (non-option) argument. The second argument is the output filename, which will also be in dtb format. The third argument provides the name of the overlay to merge into the base tree. If this is "-" then no overlay is used and the utility will simply customize the base tree with any parameters given. . . .SH OPTIONS . .TP .BR \-d Show debug output during operation. . .TP .BR \-h Displays help on the application. . . .SH EXAMPLES . .TP .B dtmerge /boot/bcm2711-rpi-4-b.dtb out.dtb - spi=on i2c=on Produce a device-tree for the Raspberry Pi 4 in "out.dtb" which has the SPI and I2C interfaces activated. . .TP .B dtmerge /boot/bcm2710-rpi-3-b-plus.dtb out.dtb /boot/overlays/gpio-shutdown.dtbo Produce a device-tree for the Raspberry Pi 3+ in "out.dtb" which includes the GPIO shutdown overlay (with all parameters set to their default). . . .SH SEE ALSO .BR dtoverlay (1), .BR dtparam (1), .B [DTREE] . . .SH REFERENCES .TP .B [DTREE] https://www.raspberrypi.org/documentation/configuration/device-tree.md userland/host_applications/linux/apps/dtmerge/dtmerge.c000066400000000000000000000152701421703157200237510ustar00rootroot00000000000000/* Copyright (c) 2016 Raspberry Pi (Trading) Ltd. 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include "dtoverlay.h" static void usage(void) { printf("Usage:\n"); printf(" dtmerge [ - [param=value] ...\n"); printf(" to apply a parameter to the base dtb (like dtparam)\n"); printf(" dtmerge [ [param=value] ...\n"); printf(" to apply an overlay with parameters (like dtoverlay)\n"); printf(" where is any of:\n"); printf(" -d Enable debug output\n"); printf(" -h Show this help message\n"); exit(1); } int main(int argc, char **argv) { const char *base_file; const char *merged_file; const char *overlay_file; const char *compatible; char *overlay_dir; char *p; DTBLOB_T *base_dtb; DTBLOB_T *overlay_dtb; int err; int argn = 1; int max_dtb_size = 100000; int compatible_len; while ((argn < argc) && (argv[argn][0] == '-')) { const char *arg = argv[argn++]; if ((strcmp(arg, "-d") == 0) || (strcmp(arg, "--debug") == 0)) dtoverlay_enable_debug(1); else if ((strcmp(arg, "-h") == 0) || (strcmp(arg, "--help") == 0)) usage(); else { printf("* Unknown option '%s'\n", arg); usage(); } } if (argc < (argn + 3)) { usage(); } base_file = argv[argn++]; merged_file = argv[argn++]; overlay_file = argv[argn++]; base_dtb = dtoverlay_load_dtb(base_file, max_dtb_size); if (!base_dtb) { printf("* failed to load '%s'\n", base_file); return -1; } if (strnlen(overlay_file, DTOVERLAY_MAX_PATH) == DTOVERLAY_MAX_PATH) { printf("* overlay filename too long\n"); return -1; } overlay_dir = strdup(overlay_file); p = strrchr(overlay_dir, '/'); if (p) *p = 0; else overlay_dir = "."; compatible = dtoverlay_get_property(base_dtb, dtoverlay_find_node(base_dtb, "/", 1), "compatible", &compatible_len); dtoverlay_init_map(overlay_dir, compatible, compatible_len); err = dtoverlay_set_synonym(base_dtb, "i2c", "i2c0"); err = dtoverlay_set_synonym(base_dtb, "i2c_arm", "i2c0"); err = dtoverlay_set_synonym(base_dtb, "i2c_vc", "i2c1"); err = dtoverlay_set_synonym(base_dtb, "i2c_baudrate", "i2c0_baudrate"); err = dtoverlay_set_synonym(base_dtb, "i2c_arm_baudrate", "i2c0_baudrate"); err = dtoverlay_set_synonym(base_dtb, "i2c_vc_baudrate", "i2c1_baudrate"); if (strcmp(overlay_file, "-") == 0) { overlay_dtb = base_dtb; } else { char new_file[DTOVERLAY_MAX_PATH]; char *overlay_name; const char *new_name; char *p; int len; strcpy(new_file, overlay_file); overlay_name = strrchr(new_file, '/'); if (overlay_name) overlay_name++; else overlay_name = new_file; p = strrchr(overlay_name, '.'); if (p) *p = 0; new_name = dtoverlay_remap_overlay(overlay_name); if (new_name) { if (strcmp(overlay_name, new_name)) dtoverlay_debug("mapped overlay '%s' to '%s'", overlay_name, new_name); len = strlen(new_name); memmove(overlay_name, new_name, len); strcpy(overlay_name + len, ".dtbo"); overlay_dtb = dtoverlay_load_dtb(new_file, max_dtb_size); if (overlay_dtb) err = dtoverlay_fixup_overlay(base_dtb, overlay_dtb); else err = -1; } else { overlay_dtb = NULL; err = -2; } } while (!err && (argn < argc)) { char *param_name = argv[argn++]; char *param_value = param_name + strcspn(param_name, "="); const void *override_data; int data_len; if (*param_value == '=') { *(param_value++) = '\0'; } else { /* This isn't a well-formed parameter assignment, but it can be treated as an assignment of true. */ param_value = "true"; } override_data = dtoverlay_find_override(overlay_dtb, param_name, &data_len); if (override_data) { err = dtoverlay_apply_override(overlay_dtb, param_name, override_data, data_len, param_value); } else { override_data = dtoverlay_find_override(base_dtb, param_name, &data_len); if (override_data) { err = dtoverlay_apply_override(base_dtb, param_name, override_data, data_len, param_value); } else { printf("* unknown param '%s'\n", param_name); err = data_len; } } } if (!err && (overlay_dtb != base_dtb)) { err = dtoverlay_merge_overlay(base_dtb, overlay_dtb); dtoverlay_free_dtb(overlay_dtb); } if (!err) { dtoverlay_pack_dtb(base_dtb); err = dtoverlay_save_dtb(base_dtb, merged_file); } dtoverlay_free_dtb(base_dtb); return err; } userland/host_applications/linux/apps/dtoverlay/000077500000000000000000000000001421703157200225335ustar00rootroot00000000000000userland/host_applications/linux/apps/dtoverlay/CMakeLists.txt000077500000000000000000000015571421703157200253060ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) get_filename_component (VIDEOCORE_ROOT ../../../.. ABSOLUTE) include (${VIDEOCORE_ROOT}/makefiles/cmake/global_settings.cmake) if (NOT WIN32) add_definitions(-Wall -Werror) endif () include_directories ( ${VIDEOCORE_HEADERS_BUILD_DIR} ${VIDEOCORE_ROOT} ${VIDEOCORE_ROOT}/opensrc/helpers/libfdt ${VIDEOCORE_ROOT}/helpers/dtoverlay ) add_executable(dtoverlay dtoverlay_main.c utils.c) target_link_libraries(dtoverlay dtovl) install(TARGETS dtoverlay RUNTIME DESTINATION bin) install(FILES dtoverlay.1 DESTINATION man/man1) add_custom_command(TARGET dtoverlay POST_BUILD COMMAND ln;-sf;dtoverlay;dtparam) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dtparam DESTINATION bin) install(FILES dtparam.1 DESTINATION man/man1) set(DTOVERLAY_SCRIPTS dtoverlay-pre dtoverlay-post) install(PROGRAMS ${DTOVERLAY_SCRIPTS} DESTINATION bin) userland/host_applications/linux/apps/dtoverlay/dtoverlay-post000066400000000000000000000004321421703157200254510ustar00rootroot00000000000000#!/bin/bash if ! hash lxpanelctl 2> /dev/null; then exit 0 fi CMD="lxpanelctl command volumealsabt start > /dev/null" for PID in $(pgrep -x lxpanel); do eval "$(grep -z "DISPLAY=" "/proc/$PID/environ" | tr -d '\0')" user="$(ps -p "$PID" -o uname=)" su "$user" -c "$CMD" done userland/host_applications/linux/apps/dtoverlay/dtoverlay-pre000066400000000000000000000004301421703157200252500ustar00rootroot00000000000000#!/bin/bash if ! hash lxpanelctl 2> /dev/null; then exit 0 fi CMD="lxpanelctl command volumealsabt stop >/dev/null" for PID in $(pgrep -x lxpanel); do eval "$(grep -z "DISPLAY=" "/proc/$PID/environ" | tr -d '\0')" user="$(ps -p "$PID" -o uname=)" su "$user" -c "$CMD" done userland/host_applications/linux/apps/dtoverlay/dtoverlay.1000066400000000000000000000066451421703157200246410ustar00rootroot00000000000000.TH DTOVERLAY 1 . .SH NAME dtoverlay \- load a device-tree overlay . . .SH SYNOPSIS .SY dtoverlay .OP \-d dir .OP \-p string .OP \-v .OP \-D .OP \-r .OP \-R .OP \-l .OP \-a .RI [ overlay " [" param=val \|.\|.\|.]] .YS . .SY dtoverlay .B \-h .RI [ overlay " [" param ]] .YS . . .SH DESCRIPTION .B dtoverlay is a command line utility for manipulating the system's runtime device-tree by adding or removing overlays. It can also customize the base device-tree or overlays by setting parameters within them. See .B [DTREE] for more information. . .PP By default, without the .B -r or .B -R options, the application adds the specified overlay. Manipulation of active device-tree overlays usually requires root privileges. . . .SH OPTIONS . .TP .BR \-a Lists all defined device-tree overlays, placing a "*" next to those that are currently loaded. . .TP .BR \-d " \fIdir\fR" Specifies an alternate location path from which to load overlays. The utility defaults to "/boot/overlays" or "/flash/overlays". . .TP .BR \-D Dry-run; prepares the specified overlay but doesn't apply it. The resulting prepared overlay is saved as "dry-run.dtbo". . .TP .BR \-h [\fIoverlay\fR] [\fIparam\fR] If given without .I overlay or .I param displays help on the application overall. If .I overlay is specified, prints help on that overlay. Finally, if .I param is also specified, prints help on that parameter of the overlay. To query parameters on the base overlay, specify "dtparam" as the .IR overlay . . .TP .BR \-l Lists all currently loaded device-tree overlays, in the order they were loaded. This also specifies the index of each overlay. . .TP .BR \-p " \fIstring\fR" Override the platform's "compatible" string, normally read from "/proc/device-tree/compatible". This is used with the overlay map to determine which platform-specific overlay to read (if necessary). . .TP .BR \-r Remove overlay. With this option, the utility will remove the specified overlay. If no overlay is given, the last overlay loaded will be removed. Overlays can be specified by name or by index. . .TP .BR \-R Remove the specified overlay, and all subsequent overlays that were loaded after it. If no overlay is given, all overlays will be removed. Overlays can be specified by name or by index. . .TP .BR \-v Verbose operation; the utility will produce more output. . . .SH EXAMPLES . .TP .B sudo dtoverlay w1-gpio Load the w1-gpio overlay (to enable Dallas 1-Wire on GPIO4). Note that root privileges are usually required for manipulating the device-tree overlays (hence, sudo in the example). . .TP .B dtoverlay -l Show the loaded overlays. . .TP .B sudo dtoverlay -r Remove the last loaded overlay. . .TP .B sudo dtoverlay gpio-shutdown gpio_pin=7 active_low=0 gpio_pull=down Load the gpio-shutdown overlay specifying that it should pull GPIO7 down and monitor it for a rising edge to initiate shutdown. . .TP .B dtoverlay -h gpio-shutdown Display help for the gpio-shutdown overlay . .TP .B sudo dtoverlay -r gpio-shutdown Remove the gpio-shutdown overlay, wherever it is in the load order. . . .SH HOOKS .B dtoverlay attempts to call two executables (shell scripts by default) during its operation: .B dtoverlay-pre prior to making device-tree modifications, and .B dtoverlay-post afterwards. Each executable is optional and will be ignored if not present. . . .SH SEE ALSO .BR dtparam (1), .BR dtmerge (1), .B [DTREE] . . .SH REFERENCES .TP .B [DTREE] https://www.raspberrypi.org/documentation/configuration/device-tree.md userland/host_applications/linux/apps/dtoverlay/dtoverlay_main.c000066400000000000000000001034571421703157200257260ustar00rootroot00000000000000/* Copyright (c) 2016 Raspberry Pi (Trading) Ltd. 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include #include #include #include #include #include "dtoverlay.h" #include "utils.h" #define CFG_DIR_1 "/sys/kernel/config" #define CFG_DIR_2 "/config" #define DT_SUBDIR "/device-tree" #define WORK_DIR "/tmp/.dtoverlays" #define OVERLAY_SRC_SUBDIR "overlays" #define README_FILE "README" #define DT_OVERLAYS_SUBDIR "overlays" #define DTOVERLAY_PATH_MAX 128 #define DIR_MODE 0755 enum { OPT_ADD, OPT_REMOVE, OPT_REMOVE_FROM, OPT_LIST, OPT_LIST_ALL, OPT_HELP }; static const char *boot_dirs[] = { #ifdef FORCE_BOOT_DIR FORCE_BOOT_DIR, #else "/boot", "/flash", #ifdef OTHER_BOOT_DIR OTHER_BOOT_DIR, #endif #endif NULL /* Terminator */ }; typedef struct state_struct { int count; struct dirent **namelist; } STATE_T; static int dtoverlay_add(STATE_T *state, const char *overlay, int argc, const char **argv); static int dtoverlay_remove(STATE_T *state, const char *overlay, int and_later); static int dtoverlay_list(STATE_T *state); static int dtoverlay_list_all(STATE_T *state); static void usage(void); static void root_check(void); static void overlay_help(const char *overlay, const char **params); static int apply_overlay(const char *overlay_file, const char *overlay); static int overlay_applied(const char *overlay_dir); static STATE_T *read_state(const char *dir); static void free_state(STATE_T *state); const char *cmd_name; const char *work_dir = WORK_DIR; const char *overlay_src_dir; const char *dt_overlays_dir; const char *error_file = NULL; const char *platform_string; int platform_string_len; int dry_run = 0; int main(int argc, const char **argv) { int argn = 1; int opt = OPT_ADD; int is_dtparam; const char *overlay = NULL; const char **params = NULL; int ret = 0; STATE_T *state = NULL; const char *cfg_dir; cmd_name = argv[0]; if (strrchr(cmd_name, '/')) cmd_name = strrchr(cmd_name, '/') + 1; is_dtparam = (strcmp(cmd_name, "dtparam") == 0); while ((argn < argc) && (argv[argn][0] == '-')) { const char *arg = argv[argn++]; if (strcmp(arg, "-r") == 0) { if (opt != OPT_ADD) usage(); opt = OPT_REMOVE; } else if (strcmp(arg, "-R") == 0) { if (opt != OPT_ADD) usage(); opt = OPT_REMOVE_FROM; } else if (strcmp(arg, "-D") == 0) { if (opt != OPT_ADD) usage(); dry_run = 1; work_dir = "."; } else if ((strcmp(arg, "-l") == 0) || (strcmp(arg, "--list") == 0)) { if (opt != OPT_ADD) usage(); opt = OPT_LIST; } else if ((strcmp(arg, "-a") == 0) || (strcmp(arg, "--listall") == 0) || (strcmp(arg, "--all") == 0)) { if (opt != OPT_ADD) usage(); opt = OPT_LIST_ALL; } else if (strcmp(arg, "-d") == 0) { if (argn == argc) usage(); overlay_src_dir = argv[argn++]; } else if (strcmp(arg, "-p") == 0) { if (argn == argc) usage(); platform_string = argv[argn++]; platform_string_len = strlen(platform_string) + 1; } else if (strcmp(arg, "-v") == 0) { opt_verbose = 1; } else if (strcmp(arg, "-h") == 0) { opt = OPT_HELP; } else { fprintf(stderr, "* unknown option '%s'\n", arg); usage(); } } if ((opt == OPT_ADD) || (opt == OPT_REMOVE) || (opt == OPT_REMOVE_FROM) || (opt == OPT_HELP)) { if ((argn == argc) && ((!is_dtparam && ((opt == OPT_ADD) || (opt == OPT_HELP))) || (is_dtparam && (opt == OPT_HELP)))) usage(); if (is_dtparam && (opt == OPT_ADD) && (argn == argc)) opt = OPT_HELP; if (is_dtparam && ((opt == OPT_ADD) || (opt == OPT_HELP))) overlay = "dtparam"; else if (argn < argc) overlay = argv[argn++]; } if ((opt == OPT_HELP) && (argn < argc)) { params = &argv[argn]; argn = argc; } if ((opt != OPT_ADD) && (argn != argc)) usage(); dtoverlay_enable_debug(opt_verbose); if (!overlay_src_dir) { /* Find the overlays and README */ int i; for (i = 0; boot_dirs[i]; i++) { overlay_src_dir = sprintf_dup("%s/" OVERLAY_SRC_SUBDIR, boot_dirs[i]); if (dir_exists(overlay_src_dir)) break; free_string(overlay_src_dir); overlay_src_dir = NULL; } if (!overlay_src_dir) fatal_error("Failed to find overlays directory"); } if (opt == OPT_HELP) { overlay_help(overlay, params); goto orderly_exit; } if (!dir_exists(work_dir)) { if (mkdir(work_dir, DIR_MODE) != 0) fatal_error("Failed to create '%s' - %d", work_dir, errno); } error_file = sprintf_dup("%s/%s", work_dir, "error.dtb"); cfg_dir = CFG_DIR_1 DT_SUBDIR; if (!dry_run && !dir_exists(cfg_dir)) { root_check(); cfg_dir = CFG_DIR_2; if (!dir_exists(cfg_dir)) { if (mkdir(cfg_dir, DIR_MODE) != 0) fatal_error("Failed to create '%s' - %d", cfg_dir, errno); } cfg_dir = CFG_DIR_2 DT_SUBDIR; if (!dir_exists(cfg_dir) && (run_cmd("mount -t configfs none '%s'", cfg_dir) != 0)) fatal_error("Failed to mount configfs - %d", errno); } if (!platform_string) { FILE *fp = fopen("/proc/device-tree/compatible", "r"); if (fp) { long len; long bytes_read; char *prop_buf; fseek(fp, 0, SEEK_END); len = ftell(fp); fseek(fp, 0, SEEK_SET); prop_buf = malloc(len); bytes_read = fread(prop_buf, 1, len, fp); if (bytes_read == len) { platform_string = prop_buf; platform_string_len = len; } else fatal_error("Failed to read 'compatible' property"); fclose(fp); } } if (platform_string) dtoverlay_init_map(overlay_src_dir, platform_string, platform_string_len); if (!dry_run) { dt_overlays_dir = sprintf_dup("%s/%s", cfg_dir, DT_OVERLAYS_SUBDIR); if (!dir_exists(dt_overlays_dir)) fatal_error("configfs overlays folder not found - incompatible kernel"); state = read_state(work_dir); if (!state) fatal_error("Failed to read state"); } switch (opt) { case OPT_ADD: case OPT_REMOVE: case OPT_REMOVE_FROM: if (!dry_run) { root_check(); run_cmd("which dtoverlay-pre >/dev/null 2>&1 && dtoverlay-pre"); } break; default: break; } switch (opt) { case OPT_ADD: ret = dtoverlay_add(state, overlay, argc - argn, argv + argn); break; case OPT_REMOVE: ret = dtoverlay_remove(state, overlay, 0); break; case OPT_REMOVE_FROM: ret = dtoverlay_remove(state, overlay, 1); break; case OPT_LIST: ret = dtoverlay_list(state); break; case OPT_LIST_ALL: ret = dtoverlay_list_all(state); break; default: ret = 1; break; } switch (opt) { case OPT_ADD: case OPT_REMOVE: case OPT_REMOVE_FROM: if (!dry_run) run_cmd("which dtoverlay-post >/dev/null 2>&1 && dtoverlay-post"); break; default: break; } orderly_exit: if (state) free_state(state); free_strings(); if ((ret == 0) && error_file) unlink(error_file); return ret; } int dtparam_callback(int override_type, const char *override_value, DTBLOB_T *dtb, int node_off, const char *prop_name, int target_phandle, int target_off, int target_size, void *callback_state) { STRING_VEC_T *used_props = callback_state; char prop_id[80]; int err; err = dtoverlay_override_one_target(override_type, override_value, dtb, node_off, prop_name, target_phandle, target_off, target_size, callback_state); if ((err == 0) && (target_phandle != 0)) { if (snprintf(prop_id, sizeof(prop_id), "%08x%s", target_phandle, prop_name) < 0) err = FDT_ERR_INTERNAL; else if (string_vec_find(used_props, prop_id, 0) < 0) string_vec_add(used_props, prop_id, 0); } return err; } // Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors int dtparam_apply(DTBLOB_T *dtb, const char *override_name, const char *override_data, int data_len, const char *override_value, STRING_VEC_T *used_props) { void *data; int err; /* Copy the override data in case it moves */ data = malloc(data_len); if (data) { memcpy(data, override_data, data_len); err = dtoverlay_foreach_override_target(dtb, override_name, data, data_len, override_value, dtparam_callback, used_props); free(data); } else { dtoverlay_error("out of memory"); err = NON_FATAL(FDT_ERR_NOSPACE); } return err; } static int apply_parameter(DTBLOB_T *overlay_dtb, const char *arg, int is_dtparam, STRING_VEC_T *used_props, char **param_string) { const char *param_val = strchr(arg, '='); const char *param, *override; char *p = NULL; int override_len; int err = 0; if (param_val) { int len = (param_val - arg); p = sprintf_dup("%.*s", len, arg); param = p; param_val++; } else { /* Use the default parameter value - true */ param = arg; param_val = "true"; } override = dtoverlay_find_override(overlay_dtb, param, &override_len); if (!override) return error("Unknown parameter '%s'", param); if (is_dtparam) err = dtparam_apply(overlay_dtb, param, override, override_len, param_val, used_props); else err = dtoverlay_apply_override(overlay_dtb, param, override, override_len, param_val); if (err != 0) return error("Failed to set %s=%s", param, param_val); *param_string = sprintf_dup("%s %s=%s", *param_string ? *param_string : "", param, param_val); free_string(p); return 0; } static int dtoverlay_add(STATE_T *state, const char *overlay, int argc, const char **argv) { const char *overlay_name; const char *overlay_file; const char *overrides = NULL; char *param_string = NULL; int is_dtparam; DTBLOB_T *base_dtb = NULL; DTBLOB_T *overlay_dtb; STRING_VEC_T used_props; int err; int len; int i; len = strlen(overlay) - 5; is_dtparam = (strcmp(overlay, "dtparam") == 0); if (is_dtparam) { /* Convert /proc/device-tree to a .dtb and load it */ overlay_file = sprintf_dup("%s/%s", work_dir, "base.dtb"); if (run_cmd("dtc -I fs -O dtb -o '%s' /proc/device-tree 1>/dev/null 2>&1", overlay_file) != 0) return error("Failed to read active DTB"); } else if ((len > 0) && (strcmp(overlay + len, ".dtbo") == 0)) { const char *p; overlay_file = overlay; p = strrchr(overlay, '/'); if (p) { overlay = p + 1; len = strlen(overlay) - 5; } overlay = sprintf_dup("%.*s", len, overlay); } else { const char *remapped = dtoverlay_remap_overlay(overlay); int name_len; if (!remapped) return error("Failed to load '%s'", overlay); if (strcmp(overlay, remapped)) dtoverlay_debug("mapped overlay '%s' to '%s'", overlay, remapped); overlay = remapped; name_len = strcspn(overlay, ","); if (overlay[name_len]) overrides = overlay + name_len + 1; overlay = sprintf_dup("%.*s", name_len, overlay); overlay_file = sprintf_dup("%s/%s.dtbo", overlay_src_dir, overlay); } if (dry_run) overlay_name = "dry_run"; else overlay_name = sprintf_dup("%d_%s", state->count, overlay); dtoverlay_debug("loading file '%s'", overlay_file); overlay_dtb = dtoverlay_load_dtb(overlay_file, DTOVERLAY_PADDING(4096)); if (!overlay_dtb) return error("Failed to read '%s'", overlay_file); if (is_dtparam) { base_dtb = overlay_dtb; string_vec_init(&used_props); } /* Apply any parameters that came from the overlay map */ while (overrides && *overrides) { int override_len = strcspn(overrides, ","); char *override = strndup(overrides, override_len); if (!override) return error("Out of memory applying parameter"); err = apply_parameter(overlay_dtb, override, is_dtparam, &used_props, ¶m_string); free(override); if (err) return err; overrides += override_len + 1; } /* Then any supplied on the commad line */ for (i = 0; i < argc; i++) { err = apply_parameter(overlay_dtb, argv[i], is_dtparam, &used_props, ¶m_string); if (err) return err; } /* Apply any intra-overlay fragments, filtering the symbols */ err = dtoverlay_merge_overlay(NULL, overlay_dtb); if (err != 0) return error("Failed to apply intra-overlay fragments"); if (is_dtparam) { /* Build an overlay DTB */ overlay_dtb = dtoverlay_create_dtb(2048 + 256 * used_props.num_strings); for (i = 0; i < used_props.num_strings; i++) { int phandle, node_off, prop_len; const char *str, *prop_name; const void *prop_data; str = used_props.strings[i]; sscanf(str, "%8x", &phandle); prop_name = str + 8; node_off = dtoverlay_find_phandle(base_dtb, phandle); prop_data = dtoverlay_get_property(base_dtb, node_off, prop_name, &prop_len); err = dtoverlay_create_prop_fragment(overlay_dtb, i, phandle, prop_name, prop_data, prop_len); } dtoverlay_free_dtb(base_dtb); } if (param_string) dtoverlay_dtb_set_trailer(overlay_dtb, param_string, strlen(param_string) + 1); /* Create a filename with the sequence number */ overlay_file = sprintf_dup("%s/%s.dtbo", work_dir, overlay_name); /* then write the overlay to the file */ dtoverlay_pack_dtb(overlay_dtb); dtoverlay_save_dtb(overlay_dtb, overlay_file); dtoverlay_free_dtb(overlay_dtb); if (!dry_run && !apply_overlay(overlay_file, overlay_name)) { if (error_file) { rename(overlay_file, error_file); free_string(error_file); } return 1; } return 0; } static int dtoverlay_remove(STATE_T *state, const char *overlay, int and_later) { const char *overlay_dir; const char *dir_name = NULL; char *end; int overlay_len; int count = state->count; int rmpos; int i; if (chdir(work_dir) != 0) fatal_error("Failed to chdir to '%s'", work_dir); if (overlay) { overlay_len = strlen(overlay); rmpos = strtoul(overlay, &end, 10); if (end && (*end == '\0')) { if (rmpos >= count) return error("Overlay index (%d) too large", rmpos); dir_name = state->namelist[rmpos]->d_name; } /* Locate the most recent reference to the overlay */ else for (rmpos = count - 1; rmpos >= 0; rmpos--) { const char *left, *right; dir_name = state->namelist[rmpos]->d_name; left = strchr(dir_name, '_'); if (!left) return error("Internal error"); left++; right = strchr(left, '.'); if (!right) return error("Internal error"); if (((right - left) == overlay_len) && (memcmp(overlay, left, overlay_len) == 0)) break; dir_name = NULL; } if (rmpos < 0) return error("Overlay '%s' is not loaded", overlay); } else { if (!count) return error("No overlays loaded"); rmpos = and_later ? 0 : (count - 1); dir_name = state->namelist[rmpos]->d_name; } if (rmpos < count) { /* Unload it and all subsequent overlays in reverse order */ for (i = count - 1; i >= rmpos; i--) { const char *left, *right; left = state->namelist[i]->d_name; right = strrchr(left, '.'); if (!right) return error("Internal error"); overlay_dir = sprintf_dup("%s/%.*s", dt_overlays_dir, right - left, left); if (rmdir(overlay_dir) != 0) return error("Failed to remove directory '%s'", overlay_dir); free_string(overlay_dir); } /* Replay the sequence, deleting files for the specified overlay, and renumbering and reloading all other overlays. */ for (i = rmpos, state->count = rmpos; i < count; i++) { const char *left, *right; const char *filename = state->namelist[i]->d_name; left = strchr(filename, '_'); if (!left) return error("Internal error"); left++; right = strchr(left, '.'); if (!right) return error("Internal error"); if (and_later || (i == rmpos)) { /* This one is being deleted */ unlink(filename); } else { /* Keep this one - renumber and reload */ int len = right - left; char *new_name = sprintf_dup("%d_%.*s", state->count, len, left); char *new_file = sprintf_dup("%s.dtbo", new_name); int ret = 0; if ((len == 7) && (memcmp(left, "dtparam", 7) == 0)) { /* Regenerate the overlay in case multiple overlays target different parts of the same property. */ DTBLOB_T *dtb; char *params; const char **paramv; int paramc; int j; char *p; /* Extract the parameters */ dtb = dtoverlay_load_dtb(filename, 0); unlink(filename); if (!dtb) { error("Failed to re-apply dtparam"); continue; } params = (char *)dtoverlay_dtb_trailer(dtb); if (!params) { error("Failed to re-apply dtparam"); dtoverlay_free_dtb(dtb); continue; } /* Count and NUL-separate the params */ p = params; paramc = 0; while (*p) { int paramlen; *(p++) = '\0'; paramlen = strcspn(p, " "); paramc++; p += paramlen; } paramv = malloc((paramc + 1) * sizeof(const char *)); if (!paramv) { error("out of memory re-applying dtparam"); dtoverlay_free_dtb(dtb); continue; } for (j = 0, p = params + 1; j < paramc; j++) { paramv[j] = p; p += strlen(p) + 1; } paramv[j] = NULL; /* Create the new overlay */ ret = dtoverlay_add(state, "dtparam", paramc, paramv); free(paramv); dtoverlay_free_dtb(dtb); } else { rename(filename, new_file); ret = !apply_overlay(new_file, new_name); } if (ret != 0) { error("Failed to re-apply dtparam"); continue; } state->count++; } } } return 0; } static int dtoverlay_list(STATE_T *state) { if (state->count == 0) { printf("No overlays loaded\n"); } else { int i; printf("Overlays (in load order):\n"); for (i = 0; i < state->count; i++) { const char *name, *left, *right; const char *saved_overlay; DTBLOB_T *dtb; name = state->namelist[i]->d_name; left = strchr(name, '_'); if (!left) return error("Internal error"); left++; right = strchr(left, '.'); if (!right) return error("Internal error"); saved_overlay = sprintf_dup("%s/%s", work_dir, name); dtb = dtoverlay_load_dtb(saved_overlay, 0); if (dtoverlay_dtb_trailer(dtb)) printf("%d: %.*s %.*s\n", i, (int)(right - left), left, dtoverlay_dtb_trailer_len(dtb), (char *)dtoverlay_dtb_trailer(dtb)); else printf("%d: %.*s\n", i, (int)(right - left), left); dtoverlay_free_dtb(dtb); } } return 0; } static int dtoverlay_list_all(STATE_T *state) { int i; DIR *dh; struct dirent *de; STRING_VEC_T strings; string_vec_init(&strings); /* Enumerate .dtbo files in the /boot/overlays directory */ dh = opendir(overlay_src_dir); while ((de = readdir(dh)) != NULL) { int len = strlen(de->d_name) - 5; if ((len >= 0) && strcmp(de->d_name + len, ".dtbo") == 0) { char *str = string_vec_add(&strings, de->d_name, len + 2); str[len] = '\0'; str[len + 1] = ' '; } } closedir(dh); /* Merge in active overlays, marking them */ for (i = 0; i < state->count; i++) { const char *left, *right; char *str; int len, idx; left = strchr(state->namelist[i]->d_name, '_'); if (!left) return error("Internal error"); left++; right = strchr(left, '.'); if (!right) return error("Internal error"); len = right - left; if ((len == 7) && (memcmp(left, "dtparam", 7) == 0)) continue; idx = string_vec_find(&strings, left, len); if (idx >= 0) { str = strings.strings[idx]; len = strlen(str); } else { str = string_vec_add(&strings, left, len + 2); str[len] = '\0'; } str[len + 1] = '*'; } if (strings.num_strings == 0) { printf("No overlays found\n"); } else { /* Sort */ string_vec_sort(&strings); /* Display */ printf("All overlays (* = loaded):\n"); for (i = 0; i < strings.num_strings; i++) { const char *str = strings.strings[i]; printf("%c %s\n", str[strlen(str)+1], str); } } string_vec_uninit(&strings); return 0; } static void usage(void) { printf("Usage:\n"); if (strcmp(cmd_name, "dtparam") == 0) { printf(" %s Display help on all parameters\n", cmd_name); printf(" %s =...\n", cmd_name); printf(" %*s Add an overlay (with parameters)\n", (int)strlen(cmd_name), ""); printf(" %s -D Dry-run (prepare overlay, but don't apply -\n", cmd_name); printf(" %*s save it as dry-run.dtbo)\n", (int)strlen(cmd_name), ""); printf(" %s -r [] Remove an overlay (by index, or the last)\n", cmd_name); printf(" %s -R [] Remove from an overlay (by index, or all)\n", cmd_name); printf(" %s -l List active overlays/dtparams\n", cmd_name); printf(" %s -a List all overlays/dtparams (marking the active)\n", cmd_name); printf(" %s -h Show this usage message\n", cmd_name); printf(" %s -h ... Display help on the listed parameters\n", cmd_name); } else { printf(" %s [=...]\n", cmd_name); printf(" %*s Add an overlay (with parameters)\n", (int)strlen(cmd_name), ""); printf(" %s -D Dry-run (prepare overlay, but don't apply -\n", cmd_name); printf(" %*s save it as dry-run.dtbo)\n", (int)strlen(cmd_name), ""); printf(" %s -r [] Remove an overlay (by name, index or the last)\n", cmd_name); printf(" %s -R [] Remove from an overlay (by name, index or all)\n", cmd_name); printf(" %s -l List active overlays/params\n", cmd_name); printf(" %s -a List all overlays (marking the active)\n", cmd_name); printf(" %s -h Show this usage message\n", cmd_name); printf(" %s -h Display help on an overlay\n", cmd_name); printf(" %s -h .. Or its parameters\n", cmd_name); printf(" where is the name of an overlay or 'dtparam' for dtparams\n"); } printf("Options applicable to most variants:\n"); printf(" -d Specify an alternate location for the overlays\n"); printf(" (defaults to /boot/overlays or /flash/overlays)\n"); printf(" -p Force a compatible string for the platform\n"); printf(" -v Verbose operation\n"); printf("\n"); printf("Adding or removing overlays and parameters requires root privileges.\n"); exit(1); } static void root_check(void) { if (getuid() != 0) fatal_error("Must be run as root - try 'sudo %s ...'", cmd_name); } static void overlay_help(const char *overlay, const char **params) { OVERLAY_HELP_STATE_T *state; const char *readme_path = sprintf_dup("%s/%s", overlay_src_dir, README_FILE); state = overlay_help_open(readme_path); free_string(readme_path); if (state) { if (strcmp(overlay, "dtparam") == 0) overlay = ""; if (overlay_help_find(state, overlay)) { if (params && overlay_help_find_field(state, "Params")) { int in_param = 0; while (1) { const char *line = overlay_help_field_data(state); if (!line) break; if (line[0] == '\0') continue; if (line[0] != ' ') { /* This is a parameter name */ int param_len = strcspn(line, " "); const char **p = params; const char **q = p; in_param = 0; while (*p) { if ((param_len == strlen(*p)) && (memcmp(line, *p, param_len) == 0)) in_param = 1; else *(q++) = *p; p++; } *(q++) = 0; } if (in_param) printf("%s\n", line); } /* This only shows the first unknown parameter, but * that is enough. */ if (*params) fatal_error("Unknown parameter '%s'", *params); } else { printf("Name: %s\n\n", overlay); overlay_help_print_field(state, "Info", "Info:", 8, 0); overlay_help_print_field(state, "Load", "Usage:", 8, 0); overlay_help_print_field(state, "Params", "Params:", 8, 0); } } else { fatal_error("No help found for overlay '%s'", overlay); } overlay_help_close(state); } else { fatal_error("Help file not found"); } } static int apply_overlay(const char *overlay_file, const char *overlay) { const char *overlay_dir = sprintf_dup("%s/%s", dt_overlays_dir, overlay); int ret = 0; if (dir_exists(overlay_dir)) { error("Overlay '%s' is already loaded", overlay); } else if (mkdir(overlay_dir, DIR_MODE) == 0) { DTBLOB_T *dtb = dtoverlay_load_dtb(overlay_file, 0); if (!dtb) { error("Failed to apply overlay '%s' (load)", overlay); } else { const char *dest_file = sprintf_dup("%s/dtbo", overlay_dir); /* then write the overlay to the file */ if (dtoverlay_save_dtb(dtb, dest_file) != 0) error("Failed to apply overlay '%s' (save)", overlay); else if (!overlay_applied(overlay_dir)) error("Failed to apply overlay '%s' (kernel)", overlay); else ret = 1; free_string(dest_file); dtoverlay_free_dtb(dtb); } if (!ret) rmdir(overlay_dir); } else { error("Failed to create overlay directory"); } return ret; } static int overlay_applied(const char *overlay_dir) { char status[7] = { '\0' }; const char *status_path = sprintf_dup("%s/status", overlay_dir); FILE *fp = fopen(status_path, "r"); int bytes = 0; if (fp) { bytes = fread(status, 1, sizeof(status), fp); fclose(fp); } free_string(status_path); return (bytes == sizeof(status)) && (memcmp(status, "applied", sizeof(status)) == 0); } int seq_filter(const struct dirent *de) { int num; return (sscanf(de->d_name, "%d_", &num) == 1); } int seq_compare(const struct dirent **de1, const struct dirent **de2) { int num1 = atoi((*de1)->d_name); int num2 = atoi((*de2)->d_name); if (num1 < num2) return -1; else if (num1 == num2) return 0; else return 1; } static STATE_T *read_state(const char *dir) { STATE_T *state = malloc(sizeof(STATE_T)); int i; if (state) { state->count = scandir(dir, &state->namelist, seq_filter, seq_compare); for (i = 0; i < state->count; i++) { int num = atoi(state->namelist[i]->d_name); if (i != num) error("Overlay sequence error"); } } return state; } static void free_state(STATE_T *state) { int i; for (i = 0; i < state->count; i++) { free(state->namelist[i]); } free(state->namelist); free(state); } userland/host_applications/linux/apps/dtoverlay/dtparam.1000066400000000000000000000022571421703157200242530ustar00rootroot00000000000000.TH DTPARAM 1 . .SH NAME dtparam \- mainpulate parameters of the base device-tree . . .SH SYNOPSIS .SY dtparam .RI [ param=val \|.\|.\|.] .YS . .SY dtparam .B \-h .RI [ param ] .YS . . .SH DESCRIPTION .B dtparam is a command line utility for manipulating the base device-tree's parameters. For example, it can be used to enable the SPI or I2C interfaces at runtime without rebooting. If .B dtparam is run without any argument, it prints the names of all base device-tree parameters and their description. . . .SH OPTIONS . .TP .BR \-h " [\fIparam\fR]" If given without .I param displays help on the application overall. If .I param is specified, prints help about that parameter in the base device-tree. . . .SH EXAMPLES . .TP .B sudo dtparam spi=on Enable the SPI interface on the GPIO header. Note that root privileges are usually required for manipulating the base device-tree (hence, sudo in the example). . .TP .B dtparam -h spi Print help about the "spi" parameter. . .TP .B sudo dtparam audio=off Disable the audio output. . . .SH SEE ALSO .BR dtoverlay (1), .BR dtmerge (1), .B [DTREE] . . .SH REFERENCES .TP .B [DTREE] https://www.raspberrypi.org/documentation/configuration/device-tree.md userland/host_applications/linux/apps/dtoverlay/utils.c000066400000000000000000000242711421703157200240450ustar00rootroot00000000000000/* Copyright (c) 2016 Raspberry Pi (Trading) Ltd. 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include #include #include #include "utils.h" #define OVERLAY_HELP_INDENT 8 int opt_verbose; int opt_dry_run; static STRING_T *allocated_strings; struct overlay_help_state_struct { FILE *fp; long rec_pos; int line_len; int line_pos; int blank_count; int end_of_field; char line_buf[82]; }; static int overlay_help_get_line(OVERLAY_HELP_STATE_T *state); OVERLAY_HELP_STATE_T *overlay_help_open(const char *helpfile) { OVERLAY_HELP_STATE_T *state = NULL; FILE *fp = fopen(helpfile, "r"); if (fp) { state = calloc(1, sizeof(OVERLAY_HELP_STATE_T)); if (!state) fatal_error("Out of memory"); state->fp = fp; state->line_pos = -1; state->rec_pos = -1; } return state; } void overlay_help_close(OVERLAY_HELP_STATE_T *state) { fclose(state->fp); free(state); } int overlay_help_find(OVERLAY_HELP_STATE_T *state, const char *name) { state->line_pos = -1; state->rec_pos = -1; state->blank_count = 0; fseek(state->fp, 0, SEEK_SET); while (overlay_help_find_field(state, "Name")) { const char *overlay = overlay_help_field_data(state); if (overlay && (strcmp(overlay, name) == 0)) { state->rec_pos = (long)ftell(state->fp); return 1; } } return 0; } int overlay_help_find_field(OVERLAY_HELP_STATE_T *state, const char *field) { int field_len = strlen(field); int found = 0; if (state->rec_pos >= 0) fseek(state->fp, state->rec_pos, SEEK_SET); while (!found) { int line_len = overlay_help_get_line(state); if (line_len < 0) break; /* Check for the ":" prefix */ if ((line_len >= (field_len + 1)) && (state->line_buf[field_len] == ':') && (memcmp(state->line_buf, field, field_len) == 0)) { /* Found it If this initial line has no content then skip it */ if (line_len > OVERLAY_HELP_INDENT) state->line_pos = OVERLAY_HELP_INDENT; else state->line_pos = -1; state->end_of_field = 0; found = 1; } else { state->line_pos = -1; } } return found; } const char *overlay_help_field_data(OVERLAY_HELP_STATE_T *state) { int line_len, pos; if (state->end_of_field) return NULL; line_len = state->line_len; if ((state->line_pos < 0) || (state->line_pos >= line_len)) { line_len = overlay_help_get_line(state); /* Fields end at the start of the next field or the end of the record */ if ((line_len < 0) || (state->line_buf[0] != ' ')) { state->end_of_field = 1; return NULL; } if (line_len == 0) return ""; } /* Return field data starting at OVERLAY_HELP_INDENT, if there is any */ pos = line_len; if (pos > OVERLAY_HELP_INDENT) pos = OVERLAY_HELP_INDENT; state->line_pos = -1; return &state->line_buf[pos]; } void overlay_help_print_field(OVERLAY_HELP_STATE_T *state, const char *field, const char *label, int indent, int strip_blanks) { if (!overlay_help_find_field(state, field)) return; while (1) { const char *line = overlay_help_field_data(state); if (!line) break; if (label) { int spaces = indent - strlen(label); if (spaces < 0) spaces = 0; printf("%s%*s%s\n", label, spaces, "", line); label = NULL; } else if (line[0]) { printf("%*s%s\n", indent, "", line); } else if (!strip_blanks) { printf("\n"); } } if (!strip_blanks) printf("\n"); } /* Returns the length of the line, or -1 on end of file or record */ static int overlay_help_get_line(OVERLAY_HELP_STATE_T *state) { int line_len; if (state->line_pos >= 0) return state->line_len; get_next_line: state->line_buf[sizeof(state->line_buf) - 1] = ' '; line_len = -1; if (fgets(state->line_buf, sizeof(state->line_buf), state->fp)) { // Check for overflow // Strip the newline line_len = strlen(state->line_buf); if (line_len && (state->line_buf[line_len - 1] == '\n')) { line_len--; state->line_buf[line_len] = '\0'; } } if (state->rec_pos >= 0) { if (line_len == 0) { state->blank_count++; if (state->blank_count >= 2) return -1; state->line_pos = 0; goto get_next_line; } else if (state->blank_count) { /* Return a single blank line now - the non-empty line will be returned next time */ state->blank_count = 0; return 0; } } state->line_len = line_len; state->line_pos = (line_len >= 0) ? 0 : -1; return line_len; } int run_cmd(const char *fmt, ...) { va_list ap; char *cmd; int ret; va_start(ap, fmt); cmd = vsprintf_dup(fmt, ap); va_end(ap); if (opt_dry_run || opt_verbose) fprintf(stderr, "run_cmd: %s\n", cmd); ret = opt_dry_run ? 0 : system(cmd); free_string(cmd); return ret; } /* Not thread safe */ void free_string(const char *string) { STRING_T *str; if (!string) return; str = (STRING_T *)(string - sizeof(STRING_T)); if (str == allocated_strings) { allocated_strings = str->next; if (allocated_strings == str) allocated_strings = NULL; } str->prev->next = str->next; str->next->prev = str->prev; free(str); } /* Not thread safe */ void free_strings(void) { if (allocated_strings) { STRING_T *str = allocated_strings; do { STRING_T *t = str; str = t->next; free(t); } while (str != allocated_strings); allocated_strings = NULL; } } /* Not thread safe */ char *sprintf_dup(const char *fmt, ...) { va_list ap; char *str; va_start(ap, fmt); str = vsprintf_dup(fmt, ap); va_end(ap); return str; } /* Not thread safe */ char *vsprintf_dup(const char *fmt, va_list ap) { char scratch[512]; int len; STRING_T *str; len = vsnprintf(scratch, sizeof(scratch), fmt, ap) + 1; if (len > sizeof(scratch)) fatal_error("Maximum string length exceeded"); str = malloc(sizeof(STRING_T) + len); if (!str) fatal_error("Out of memory"); memcpy(str->data, scratch, len); if (allocated_strings) { str->next = allocated_strings; str->prev = allocated_strings->prev; str->next->prev = str; str->prev->next = str; } else { str->next = str; str->prev = str; allocated_strings = str; } return str->data; } int dir_exists(const char *dirname) { struct stat finfo; return (stat(dirname, &finfo) == 0) && S_ISDIR(finfo.st_mode); } int file_exists(const char *dirname) { struct stat finfo; return (stat(dirname, &finfo) == 0) && S_ISREG(finfo.st_mode); } void string_vec_init(STRING_VEC_T *vec) { vec->num_strings = 0; vec->max_strings = 0; vec->strings = NULL; } char *string_vec_add(STRING_VEC_T *vec, const char *str, int len) { char *copy; if (vec->num_strings == vec->max_strings) { if (vec->max_strings) vec->max_strings *= 2; else vec->max_strings = 16; vec->strings = realloc(vec->strings, vec->max_strings * sizeof(const char *)); if (!vec->strings) fatal_error("Out of memory"); } if (len) { copy = malloc(len + 1); strncpy(copy, str, len); copy[len] = '\0'; } else copy = strdup(str); if (!copy) fatal_error("Out of memory"); vec->strings[vec->num_strings++] = copy; return copy; } int string_vec_find(STRING_VEC_T *vec, const char *str, int len) { int i; for (i = 0; i < vec->num_strings; i++) { if (len) { if ((strncmp(vec->strings[i], str, len) == 0) && (vec->strings[i][len] == '\0')) return i; } else if (strcmp(vec->strings[i], str) == 0) return i; } return -1; } int string_vec_compare(const void *a, const void *b) { return strcmp(*(const char **)a, *(const char **)b); } void string_vec_sort(STRING_VEC_T *vec) { qsort(vec->strings, vec->num_strings, sizeof(char *), &string_vec_compare); } void string_vec_uninit(STRING_VEC_T *vec) { int i; for (i = 0; i < vec->num_strings; i++) free(vec->strings[i]); free(vec->strings); } int error(const char *fmt, ...) { va_list ap; fprintf(stderr, "* "); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); return 1; } void fatal_error(const char *fmt, ...) { va_list ap; fprintf(stderr, "* "); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); exit(1); } userland/host_applications/linux/apps/dtoverlay/utils.h000066400000000000000000000060251421703157200240470ustar00rootroot00000000000000/* Copyright (c) 2016 Raspberry Pi (Trading) Ltd. 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 name of the copyright holder nor the names of its 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. */ #ifndef UTILS_H #define UTILS_H typedef struct string_struct { struct string_struct *prev; struct string_struct *next; char data[0]; } STRING_T; typedef struct string_vec_struct { int num_strings; int max_strings; char **strings; } STRING_VEC_T; typedef struct overlay_help_state_struct OVERLAY_HELP_STATE_T; extern int opt_verbose; extern int opt_dry_run; OVERLAY_HELP_STATE_T *overlay_help_open(const char *helpfile); void overlay_help_close(OVERLAY_HELP_STATE_T *state); int overlay_help_find(OVERLAY_HELP_STATE_T *state, const char *name); int overlay_help_find_field(OVERLAY_HELP_STATE_T *state, const char *field); const char *overlay_help_field_data(OVERLAY_HELP_STATE_T *state); void overlay_help_print_field(OVERLAY_HELP_STATE_T *state, const char *field, const char *label, int indent, int strip_blanks); int run_cmd(const char *fmt, ...); void free_string(const char *string); /* Not thread safe */ void free_strings(void); /* Not thread safe */ char *sprintf_dup(const char *fmt, ...); /* Not thread safe */ char *vsprintf_dup(const char *fmt, va_list ap); /* Not thread safe */ int dir_exists(const char *dirname); int file_exists(const char *dirname); void string_vec_init(STRING_VEC_T *vec); char *string_vec_add(STRING_VEC_T *vec, const char *str, int len); int string_vec_find(STRING_VEC_T *vec, const char *str, int len); void string_vec_sort(STRING_VEC_T *vec); void string_vec_uninit(STRING_VEC_T *vec); int error(const char *fmt, ...); void fatal_error(const char *fmt, ...); #endif userland/host_applications/linux/apps/gencmd/000077500000000000000000000000001421703157200217575ustar00rootroot00000000000000userland/host_applications/linux/apps/gencmd/CMakeLists.txt000066400000000000000000000011331421703157200245150ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) if (WIN32) set(VCOS_PLATFORM win32) else () set(VCOS_PLATFORM pthreads) add_definitions(-Wall -Werror) endif () include_directories( ../../../.. ../../../../interface/vcos ../../../../interface/vcos/${VCOS_PLATFORM} ) #add_subdirectory( ../../../../interface/vcos/${VCOS_PLATFORM} vcos) #add_subdirectory( ../../bin/gencmd) add_executable(vcgencmd gencmd.c) target_link_libraries(vcgencmd vcos vchiq_arm vchostif) install(TARGETS vcgencmd RUNTIME DESTINATION bin) install(FILES vcgencmd.1 DESTINATION man/man1) userland/host_applications/linux/apps/gencmd/gencmd.c000066400000000000000000000126251421703157200233660ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ /* ---- Include Files ---------------------------------------------------- */ #include #include #include #include #include #include "interface/vmcs_host/vc_vchi_gencmd.h" #include "interface/vmcs_host/vc_gencmd_defs.h" void show_usage() { puts( "Usage: vcgencmd [-t] command" ); puts( "Send a command to the VideoCore and print the result.\n" ); puts( " -t Time how long the command takes to complete" ); puts( " -h, --help Show this information\n" ); puts( "Use the command 'vcgencmd commands' to get a list of available commands\n" ); puts( "Exit status:" ); puts( " 0 command completed successfully" ); puts( " -1 problem with VCHI" ); puts( " -2 VideoCore returned an error\n" ); puts( "For further documentation please see" ); puts( "https://www.raspberrypi.org/documentation/computers/os.html#vcgencmd\n" ); } int main( int argc, char **argv ) { VCHI_INSTANCE_T vchi_instance; VCHI_CONNECTION_T *vchi_connection = NULL; if ( argc == 1 ) { // no arguments passed, so show basic usage show_usage(); return 0; } vcos_init(); if ( vchi_initialise( &vchi_instance ) != 0) { fprintf( stderr, "VCHI initialization failed\n" ); return -1; } //create a vchi connection if ( vchi_connect( NULL, 0, vchi_instance ) != 0) { fprintf( stderr, "VCHI connection failed\n" ); return -1; } vc_vchi_gencmd_init(vchi_instance, &vchi_connection, 1 ); if (argc > 1) { // first check if we were invoked with either -h or --help // in which case show basic usage and exit if( strcmp( argv[1], "-h" ) == 0 || strcmp( argv[1], "--help" ) == 0 ) { show_usage(); return 0; } int i = 1; char buffer[ GENCMDSERVICE_MSGFIFO_SIZE ]; size_t buffer_offset = 0; clock_t before=0, after=0; double time_diff; uint32_t show_time = 0; int ret; //reset the string buffer[0] = '\0'; //first, strip out a potential leading -t if( strcmp( argv[1], "-t" ) == 0 ) { show_time = 1; i++; } for (; i <= argc-1; i++) { buffer_offset = vcos_safe_strcpy( buffer, argv[i], sizeof(buffer), buffer_offset ); buffer_offset = vcos_safe_strcpy( buffer, " ", sizeof(buffer), buffer_offset ); } if( show_time ) before = clock(); //send the gencmd for the argument if (( ret = vc_gencmd_send( "%s", buffer )) != 0 ) { printf( "vc_gencmd_send returned %d\n", ret ); } //get + print out the response! if (( ret = vc_gencmd_read_response( buffer, sizeof( buffer ) )) != 0 ) { printf( "vc_gencmd_read_response returned %d\n", ret ); } buffer[ sizeof(buffer) - 1 ] = 0; if( show_time ) { after = clock(); time_diff = ((double) (after - before)) / CLOCKS_PER_SEC; printf( "Time taken: %f seconds (%f msecs) (%f usecs)\n", time_diff, time_diff * 1000, time_diff * 1000000 ); } if ( buffer[0] != '\0' ) { if ( buffer[ strlen( buffer) - 1] == '\n' ) { fputs( buffer, stdout ); } else { if (strncmp( buffer, "error=", 6) == 0 ) { fprintf (stderr, "%s\n", buffer); if ( strcmp( buffer, "error=1 error_msg=\"Command not registered\"" ) == 0 ) { fprintf( stderr, "Use 'vcgencmd commands' to get a list of commands\n" ); } return -2; } else { puts(buffer); } } } } vc_gencmd_stop(); //close the vchi connection if ( vchi_disconnect( vchi_instance ) != 0) { fprintf( stderr, "VCHI disconnect failed\n" ); return -1; } return 0; } userland/host_applications/linux/apps/gencmd/vcgencmd.1000066400000000000000000000145061421703157200236350ustar00rootroot00000000000000.TH VCGENCMD 1 . .SH NAME vcgencmd \- query the VideoCore for information . . .SH SYNOPSIS .SY vcgencmd .OP \-t .IR command \ [ params ] .YS . .SY vcgencmd .B \-h .SY vcgencmd .B \-\-help .YS . . .SH DESCRIPTION .B vcgencmd is a command line utility that can get various pieces of information from the VideoCore GPU on the Raspberry Pi. . . .SH OPTIONS .TP .B \-t Time how long the command takes to complete . .TP .BR \-h ", " \-\-help Show this information . . .SH COMMANDS To get a list of all the commands that .B vcgencmd supports, type .IR "vcgencmd\ commands" . Some of the more useful commands are described below. . .TP .BI vcos \ sub-command The .B vcos command has a number of sub-commands: .RS .TP .B version Displays the build date and version of the firmware on the VideoCore. .TP .B log status Displays the error log status of the various VideoCore software areas. .RE . .TP .B version Displays the build date and version of the firmware on the VideoCore. . .TP .B get_camera Displays the enabled and detected state of the official camera. 1 means yes, 0 means no. Whilst all firmware (except cutdown versions) will support the camera, this support needs to be enabled by using the .I start_x boot option .BR [BOOT] . . .TP .B get_throttled Returns the throttled state of the system. This is a bit pattern - a bit being set indicates the following meanings: .TS tab(|); l l . Bit|Meaning \_|\_ .T& n l . 0|Under-voltage detected 1|Arm frequency capped 2|Currently throttled 3|Soft temperature limit active 16|Under-voltage has occurred 17|Arm frequency capping has occurred 18|Throttling has occurred 19|Soft temperature limit has occurred .TE .IP A value of zero indicates that none of the above conditions is true. .IP To find if one of these bits has been set, convert the value returned to binary, then number each bit along the top. You can then see which bits are set. For example: .IP .EX 0x50000 = 0101 0000 0000 0000 0000 .EE .IP Adding the bit numbers along the top we get: .TS tab( ); n n n n n n n n n n n n n n n n n n n . 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .TE .IP From this we can see that bits 18 and 16 are set, indicating that the Pi has previously been throttled due to under-voltage, but is not currently throttled for any reason. . .TP .B measure_temp Returns the temperature of the SoC as measured by the on-board temperature sensor. . .TP .BI measure_clock \ clock This returns the current frequency of the specified clock. The options are: .TS tab(|); l l . Clock|Description \_|\_ arm|ARM cores core|VC4 scaler cores h264|H.264 block isp|Image Signal Processor v3d|3D block uart|UART pwm|PWM block (analog audio output) emmc|SD card interface pixel|Pixel valve vec|Analog video encoder hdmi|HDMI dpi|Display Peripheral Interface .TE .IP For example, .IR "vcgencmd measure_clock arm" . . .TP .BI measure_volts \ block Displays the current voltages used by the specific block. .TS tab(|); l l . Block|Description \_|\_ core|VC4 core voltage sdram_c| sdram_i| sdram_p| .TE . .TP .B otp_dump Displays the content of the One Time Programmable (OTP) memory, which is part of the SoC. These are 32 bit values, indexed from 8 to 64. See the .BR raspi-otp (7) for more details. . .TP .BI get_mem \ type Reports on the amount of memory allocated to the ARM cores with .I vcgencmd get_mem arm or the VC4 with .IR "vcgencmd get_mem gpu" . .IP .B Note: On a Raspberry Pi 4 with greater than 1GB of RAM, the .I arm option is inaccurate. This is because the GPU firmware which implements this command is only aware of the first gigabyte of RAM on the system, so the .I arm setting will always return 1GB minus the .I gpu memory value. To get an accurate report of the amount of ARM memory, use one of the standard Linux commands, such as .I free or .IR "cat /proc/meminfo" . . .TP .BI codec_enabled \ type Reports whether the specified CODEC type is enabled. Possible options for type are AGIF, FLAC, H263, H264, MJPA, MJPB, MJPG, MPG2, MPG4, MVC0, PCM, THRA, VORB, VP6, VP8, WMV9, WVC1. .IP MPG2, WMV9, and WVC1 currently require a paid for licence (see the .B [FAQ] for more info), except on the Pi4, where these hardware codecs are disabled in preference to software decoding, which requires no licence. Note that because the H265 hardware block on the Raspberry Pi4 is not part of the VideoCore GPU, its status is not accessed via this command. . .TP .BI get_config \ type|name This returns all the configuration items of the specified type that have been set in config.txt, or a single configuration item. Possible values for type parameter are .IR int ", " str ", " or simply use the name of the configuration item. . .TP .B get_lcd_info Displays the resolution and colour depth of any attached display. . .TP .B mem_oom Displays statistics on any Out Of Memory events occuring in the VC4 memory space. . .TP .B mem_reloc_stats Displays statistics from the relocatable memory allocator on the VC4. . .TP .B read_ring_osc Returns the curent speed voltage and temperature of the ring oscillator. . .TP .B hdmi_timings Displays the current HDMI settings timings. See .B [VIDEO] for details of the values returned. . .TP .B dispmanx_list Dump a list of all dispmanx items currently being displayed. . .TP .BI display_power \ 0|1|-1 .TQ .BI display_power " 0|1|-1 display" Show current display power state, or set the display power state. .I vcgencmd display_power 0 will turn off power to the current display. .I vcgencmd display_power 1 will turn on power to the display. If no parameter is set, this will display the current power state. The final parameter is an optional display ID, as returned by .I tvservice -l or from the table below, which allows a specific display to be turned on or off. .IP .I vcgencmd display_power 0 7 will turn off power to display ID 7, which is HDMI 1 on a Raspberry Pi 4. .TS tab(|); l l . Display|ID \_|\_ .T& l n . Main LCD|0 Secondary LCD|1 HDMI 0|2 Composite|3 HDMI 1|7 .TE .IP To determine if a specific display ID is on or off, use -1 as the first parameter. .IP .I vcgencmd display_power -1 7 will return 0 if display ID 7 is off, 1 if display ID 7 is on, or -1 if display ID 7 is in an unknown state, for example undetected. . . .SH EXIT STATUS . .IP 0 Command completed successfully .IP -1 Problem with VHCI .IP -2 VideoCore returned an error . . .SH SEE ALSO .B [DOCS] https://www.raspberrypi.org/\:documentation/\:computers/\:os.html#vcgencmd . userland/host_applications/linux/apps/hello_pi/000077500000000000000000000000001421703157200223155ustar00rootroot00000000000000userland/host_applications/linux/apps/hello_pi/CMakeLists.txt000066400000000000000000000022641421703157200250610ustar00rootroot00000000000000set(BUILD_FONT FALSE) include_directories(${PROJECT_SOURCE_DIR}) include_directories(${PROJECT_SOURCE_DIR}/host_applications/linux/libs/bcm_host/include) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs/ilclient) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs/vgfont) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs/revision) set(ILCLIENT_SRCS libs/ilclient/ilclient.c libs/ilclient/ilcore.c) add_library(ilclient ${ILCLIENT_SRCS}) set(REVISION_SRCS libs/revision/revision.c ) add_library(revision ${REVISION_SRCS}) set(HELLO_PI_LIBS ilclient openmaxil bcm_host vcos vchiq_arm revision) add_subdirectory(hello_world) add_subdirectory(hello_video) add_subdirectory(hello_audio) add_subdirectory(hello_triangle) add_subdirectory(hello_triangle2) add_subdirectory(hello_dispmanx) add_subdirectory(hello_tiger) add_subdirectory(hello_encode) add_subdirectory(hello_jpeg) add_subdirectory(hello_videocube) add_subdirectory(hello_teapot) if(BUILD_FONT) set(VGFONT_SRCS libs/vgfont/font.c libs/vgfont/vgft.c libs/vgfont/graphics.c) set_source_files_properties(${VGFONT_SRCS} PROPERTIES COMPILE_DEFINITIONS) add_library(vgfont ${VGFONT_SRCS}) add_subdirectory(hello_font) endif(BUILD_FONT) userland/host_applications/linux/apps/hello_pi/Makefile000066400000000000000000000022021421703157200237510ustar00rootroot00000000000000all: apps libs/ilclient/libilclient.a: $(MAKE) -C libs/ilclient libs/vgfont/libvgfont.a: $(MAKE) -C libs/vgfont libs/revision/librevision.a: $(MAKE) -C libs/revision apps: libs/ilclient/libilclient.a libs/vgfont/libvgfont.a libs/revision/librevision.a $(MAKE) -C hello_world $(MAKE) -C hello_triangle $(MAKE) -C hello_triangle2 $(MAKE) -C hello_video $(MAKE) -C hello_audio $(MAKE) -C hello_font $(MAKE) -C hello_dispmanx $(MAKE) -C hello_tiger $(MAKE) -C hello_encode $(MAKE) -C hello_jpeg $(MAKE) -C hello_videocube $(MAKE) -C hello_teapot $(MAKE) -C hello_fft $(MAKE) -C hello_mmal_encode clean: $(MAKE) -C libs/ilclient clean $(MAKE) -C libs/vgfont clean $(MAKE) -C libs/revision clean $(MAKE) -C hello_world clean $(MAKE) -C hello_triangle clean $(MAKE) -C hello_triangle2 clean $(MAKE) -C hello_video clean $(MAKE) -C hello_audio clean $(MAKE) -C hello_font clean $(MAKE) -C hello_dispmanx clean $(MAKE) -C hello_tiger clean $(MAKE) -C hello_encode clean $(MAKE) -C hello_jpeg clean $(MAKE) -C hello_videocube clean $(MAKE) -C hello_teapot clean $(MAKE) -C hello_fft clean $(MAKE) -C hello_mmal_encode clean userland/host_applications/linux/apps/hello_pi/Makefile.include000077500000000000000000000025051421703157200254040ustar00rootroot00000000000000 CFLAGS+=-DSTANDALONE -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -DTARGET_POSIX -D_LINUX -fPIC -DPIC -D_REENTRANT -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -U_FORTIFY_SOURCE -Wall -g -DHAVE_LIBOPENMAX=2 -DOMX -DOMX_SKIP64BIT -ftree-vectorize -pipe -DUSE_EXTERNAL_OMX -DHAVE_LIBBCM_HOST -DUSE_EXTERNAL_LIBBCM_HOST -DUSE_VCHIQ_ARM -Wno-psabi LDFLAGS+=-L$(SDKSTAGE)/opt/vc/lib/ -lbrcmGLESv2 -lbrcmEGL -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt -lm -L$(SDKSTAGE)/opt/vc/src/hello_pi/libs/ilclient -L$(SDKSTAGE)/opt/vc/src/hello_pi/libs/vgfont -L$(SDKSTAGE)/opt/vc/src/hello_pi/libs/revision INCLUDES+=-I$(SDKSTAGE)/opt/vc/include/ -I$(SDKSTAGE)/opt/vc/include/interface/vcos/pthreads -I$(SDKSTAGE)/opt/vc/include/interface/vmcs_host/linux -I./ -I$(SDKSTAGE)/opt/vc/src/hello_pi/libs/ilclient -I$(SDKSTAGE)/opt/vc/src/hello_pi/libs/vgfont -I$(SDKSTAGE)/opt/vc/src/hello_pi/libs/revision all: $(BIN) $(LIB) %.o: %.c @rm -f $@ $(CC) $(CFLAGS) $(INCLUDES) -g -c $< -o $@ -Wno-deprecated-declarations %.o: %.cpp @rm -f $@ $(CXX) $(CFLAGS) $(INCLUDES) -g -c $< -o $@ -Wno-deprecated-declarations %.bin: $(OBJS) $(CC) -o $@ -Wl,--whole-archive $(OBJS) $(LDFLAGS) -Wl,--no-whole-archive -rdynamic %.a: $(OBJS) $(AR) r $@ $^ clean: for i in $(OBJS); do (if test -e "$$i"; then ( rm $$i ); fi ); done @rm -f $(BIN) $(LIB) userland/host_applications/linux/apps/hello_pi/README000077500000000000000000000010161421703157200231760ustar00rootroot00000000000000Building on Pi ++++++++++++++ To build the test apps on the pi, first build the libs: make -C libs/ilclient make -C libs/vgfont then by entering each test app directory and run make. E.g. cd hello_world make ./hello_world.bin Running ./rebuild.sh will rebuild the all libs and and apps. Building on a different PC ++++++++++++++++++++++++++ If you want to build the samples on a different machine (cross-compile) then set: SDKSTAGE= and CC= before running make. userland/host_applications/linux/apps/hello_pi/hello_audio/000077500000000000000000000000001421703157200246015ustar00rootroot00000000000000userland/host_applications/linux/apps/hello_pi/hello_audio/CMakeLists.txt000066400000000000000000000003021421703157200273340ustar00rootroot00000000000000set(EXEC hello_audio.bin) set(SRCS audio.c sinewave.c) add_executable(${EXEC} ${SRCS}) target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) install(TARGETS ${EXEC} RUNTIME DESTINATION bin) userland/host_applications/linux/apps/hello_pi/hello_audio/Makefile000077500000000000000000000001361421703157200262440ustar00rootroot00000000000000OBJS=audio.o sinewave.o BIN=hello_audio.bin LDFLAGS+=-lilclient include ../Makefile.include userland/host_applications/linux/apps/hello_pi/hello_audio/audio.c000077500000000000000000000306241421703157200260560ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ // Audio output demo using OpenMAX IL though the ilcient helper library #include #include #include #include #include #include #include "bcm_host.h" #include "ilclient.h" #define N_WAVE 1024 /* dimension of Sinewave[] */ #define PI (1<<16>>1) #define SIN(x) Sinewave[((x)>>6) & (N_WAVE-1)] #define COS(x) SIN((x)+(PI>>1)) #define OUT_CHANNELS(num_channels) ((num_channels) > 4 ? 8: (num_channels) > 2 ? 4: (num_channels)) extern short Sinewave[]; #ifndef countof #define countof(arr) (sizeof(arr) / sizeof(arr[0])) #endif #define BUFFER_SIZE_SAMPLES 1024 typedef int int32_t; typedef struct { sem_t sema; ILCLIENT_T *client; COMPONENT_T *audio_render; COMPONENT_T *list[2]; OMX_BUFFERHEADERTYPE *user_buffer_list; // buffers owned by the client uint32_t num_buffers; uint32_t bytes_per_sample; } AUDIOPLAY_STATE_T; static void input_buffer_callback(void *data, COMPONENT_T *comp) { // do nothing - could add a callback to the user // to indicate more buffers may be available. } int32_t audioplay_create(AUDIOPLAY_STATE_T **handle, uint32_t sample_rate, uint32_t num_channels, uint32_t bit_depth, uint32_t num_buffers, uint32_t buffer_size) { uint32_t bytes_per_sample = (bit_depth * OUT_CHANNELS(num_channels)) >> 3; int32_t ret = -1; *handle = NULL; // basic sanity check on arguments if(sample_rate >= 8000 && sample_rate <= 192000 && (num_channels >= 1 && num_channels <= 8) && (bit_depth == 16 || bit_depth == 32) && num_buffers > 0 && buffer_size >= bytes_per_sample) { // buffer lengths must be 16 byte aligned for VCHI int size = (buffer_size + 15) & ~15; AUDIOPLAY_STATE_T *st; // buffer offsets must also be 16 byte aligned for VCHI st = calloc(1, sizeof(AUDIOPLAY_STATE_T)); if(st) { OMX_ERRORTYPE error; OMX_PARAM_PORTDEFINITIONTYPE param; OMX_AUDIO_PARAM_PCMMODETYPE pcm; int32_t s; ret = 0; *handle = st; // create and start up everything s = sem_init(&st->sema, 0, 1); assert(s == 0); st->bytes_per_sample = bytes_per_sample; st->num_buffers = num_buffers; st->client = ilclient_init(); assert(st->client != NULL); ilclient_set_empty_buffer_done_callback(st->client, input_buffer_callback, st); error = OMX_Init(); assert(error == OMX_ErrorNone); ilclient_create_component(st->client, &st->audio_render, "audio_render", ILCLIENT_ENABLE_INPUT_BUFFERS | ILCLIENT_DISABLE_ALL_PORTS); assert(st->audio_render != NULL); st->list[0] = st->audio_render; // set up the number/size of buffers memset(¶m, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); param.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); param.nVersion.nVersion = OMX_VERSION; param.nPortIndex = 100; error = OMX_GetParameter(ILC_GET_HANDLE(st->audio_render), OMX_IndexParamPortDefinition, ¶m); assert(error == OMX_ErrorNone); param.nBufferSize = size; param.nBufferCountActual = num_buffers; error = OMX_SetParameter(ILC_GET_HANDLE(st->audio_render), OMX_IndexParamPortDefinition, ¶m); assert(error == OMX_ErrorNone); // set the pcm parameters memset(&pcm, 0, sizeof(OMX_AUDIO_PARAM_PCMMODETYPE)); pcm.nSize = sizeof(OMX_AUDIO_PARAM_PCMMODETYPE); pcm.nVersion.nVersion = OMX_VERSION; pcm.nPortIndex = 100; pcm.nChannels = OUT_CHANNELS(num_channels); pcm.eNumData = OMX_NumericalDataSigned; pcm.eEndian = OMX_EndianLittle; pcm.nSamplingRate = sample_rate; pcm.bInterleaved = OMX_TRUE; pcm.nBitPerSample = bit_depth; pcm.ePCMMode = OMX_AUDIO_PCMModeLinear; switch(num_channels) { case 1: pcm.eChannelMapping[0] = OMX_AUDIO_ChannelCF; break; case 3: pcm.eChannelMapping[2] = OMX_AUDIO_ChannelCF; pcm.eChannelMapping[1] = OMX_AUDIO_ChannelRF; pcm.eChannelMapping[0] = OMX_AUDIO_ChannelLF; break; case 8: pcm.eChannelMapping[7] = OMX_AUDIO_ChannelRS; case 7: pcm.eChannelMapping[6] = OMX_AUDIO_ChannelLS; case 6: pcm.eChannelMapping[5] = OMX_AUDIO_ChannelRR; case 5: pcm.eChannelMapping[4] = OMX_AUDIO_ChannelLR; case 4: pcm.eChannelMapping[3] = OMX_AUDIO_ChannelLFE; pcm.eChannelMapping[2] = OMX_AUDIO_ChannelCF; case 2: pcm.eChannelMapping[1] = OMX_AUDIO_ChannelRF; pcm.eChannelMapping[0] = OMX_AUDIO_ChannelLF; break; } error = OMX_SetParameter(ILC_GET_HANDLE(st->audio_render), OMX_IndexParamAudioPcm, &pcm); assert(error == OMX_ErrorNone); ilclient_change_component_state(st->audio_render, OMX_StateIdle); if(ilclient_enable_port_buffers(st->audio_render, 100, NULL, NULL, NULL) < 0) { // error ilclient_change_component_state(st->audio_render, OMX_StateLoaded); ilclient_cleanup_components(st->list); error = OMX_Deinit(); assert(error == OMX_ErrorNone); ilclient_destroy(st->client); sem_destroy(&st->sema); free(st); *handle = NULL; return -1; } ilclient_change_component_state(st->audio_render, OMX_StateExecuting); } } return ret; } int32_t audioplay_delete(AUDIOPLAY_STATE_T *st) { OMX_ERRORTYPE error; ilclient_change_component_state(st->audio_render, OMX_StateIdle); error = OMX_SendCommand(ILC_GET_HANDLE(st->audio_render), OMX_CommandStateSet, OMX_StateLoaded, NULL); assert(error == OMX_ErrorNone); ilclient_disable_port_buffers(st->audio_render, 100, st->user_buffer_list, NULL, NULL); ilclient_change_component_state(st->audio_render, OMX_StateLoaded); ilclient_cleanup_components(st->list); error = OMX_Deinit(); assert(error == OMX_ErrorNone); ilclient_destroy(st->client); sem_destroy(&st->sema); free(st); return 0; } uint8_t *audioplay_get_buffer(AUDIOPLAY_STATE_T *st) { OMX_BUFFERHEADERTYPE *hdr = NULL; hdr = ilclient_get_input_buffer(st->audio_render, 100, 0); if(hdr) { // put on the user list sem_wait(&st->sema); hdr->pAppPrivate = st->user_buffer_list; st->user_buffer_list = hdr; sem_post(&st->sema); } return hdr ? hdr->pBuffer : NULL; } int32_t audioplay_play_buffer(AUDIOPLAY_STATE_T *st, uint8_t *buffer, uint32_t length) { OMX_BUFFERHEADERTYPE *hdr = NULL, *prev = NULL; int32_t ret = -1; if(length % st->bytes_per_sample) return ret; sem_wait(&st->sema); // search through user list for the right buffer header hdr = st->user_buffer_list; while(hdr != NULL && hdr->pBuffer != buffer && hdr->nAllocLen < length) { prev = hdr; hdr = hdr->pAppPrivate; } if(hdr) // we found it, remove from list { ret = 0; if(prev) prev->pAppPrivate = hdr->pAppPrivate; else st->user_buffer_list = hdr->pAppPrivate; } sem_post(&st->sema); if(hdr) { OMX_ERRORTYPE error; hdr->pAppPrivate = NULL; hdr->nOffset = 0; hdr->nFilledLen = length; error = OMX_EmptyThisBuffer(ILC_GET_HANDLE(st->audio_render), hdr); assert(error == OMX_ErrorNone); } return ret; } int32_t audioplay_set_dest(AUDIOPLAY_STATE_T *st, const char *name) { int32_t success = -1; OMX_CONFIG_BRCMAUDIODESTINATIONTYPE ar_dest; if (name && strlen(name) < sizeof(ar_dest.sName)) { OMX_ERRORTYPE error; memset(&ar_dest, 0, sizeof(ar_dest)); ar_dest.nSize = sizeof(OMX_CONFIG_BRCMAUDIODESTINATIONTYPE); ar_dest.nVersion.nVersion = OMX_VERSION; strcpy((char *)ar_dest.sName, name); error = OMX_SetConfig(ILC_GET_HANDLE(st->audio_render), OMX_IndexConfigBrcmAudioDestination, &ar_dest); assert(error == OMX_ErrorNone); success = 0; } return success; } uint32_t audioplay_get_latency(AUDIOPLAY_STATE_T *st) { OMX_PARAM_U32TYPE param; OMX_ERRORTYPE error; memset(¶m, 0, sizeof(OMX_PARAM_U32TYPE)); param.nSize = sizeof(OMX_PARAM_U32TYPE); param.nVersion.nVersion = OMX_VERSION; param.nPortIndex = 100; error = OMX_GetConfig(ILC_GET_HANDLE(st->audio_render), OMX_IndexConfigAudioRenderingLatency, ¶m); assert(error == OMX_ErrorNone); return param.nU32; } #define CTTW_SLEEP_TIME 10 #define MIN_LATENCY_TIME 20 static const char *audio_dest[] = {"local", "hdmi"}; void play_api_test(int samplerate, int bitdepth, int nchannels, int dest) { AUDIOPLAY_STATE_T *st; int32_t ret; unsigned int i, j, n; int phase = 0; int inc = 256<<16; int dinc = 0; int buffer_size = (BUFFER_SIZE_SAMPLES * bitdepth * OUT_CHANNELS(nchannels))>>3; assert(dest == 0 || dest == 1); ret = audioplay_create(&st, samplerate, nchannels, bitdepth, 10, buffer_size); assert(ret == 0); ret = audioplay_set_dest(st, audio_dest[dest]); assert(ret == 0); // iterate for 5 seconds worth of packets for (n=0; n<((samplerate * 1000)/ BUFFER_SIZE_SAMPLES); n++) { uint8_t *buf; int16_t *p; uint32_t latency; while((buf = audioplay_get_buffer(st)) == NULL) usleep(10*1000); p = (int16_t *) buf; // fill the buffer for (i=0; i>16; inc += dinc; if (inc>>16 < 512) dinc++; else dinc--; for(j=0; j (samplerate * (MIN_LATENCY_TIME + CTTW_SLEEP_TIME) / 1000)) usleep(CTTW_SLEEP_TIME*1000); ret = audioplay_play_buffer(st, buf, buffer_size); assert(ret == 0); } audioplay_delete(st); } int main (int argc, char **argv) { // 0=headphones, 1=hdmi int audio_dest = 0; // audio sample rate in Hz int samplerate = 48000; // numnber of audio channels int channels = 2; // number of bits per sample int bitdepth = 16; bcm_host_init(); if (argc > 1) audio_dest = atoi(argv[1]); if (argc > 2) channels = atoi(argv[2]); if (argc > 3) samplerate = atoi(argv[3]); printf("Outputting audio to %s\n", audio_dest==0 ? "analogue":"hdmi"); play_api_test(samplerate, bitdepth, channels, audio_dest); return 0; } userland/host_applications/linux/apps/hello_pi/hello_audio/audioplay.h000077500000000000000000000164061421703157200267530ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ // API for host applications to deliver raw PCM samples to rendered on VideoCore #ifndef AUDIOPLAY_H #define AUDIOPLAY_H /** * \file * * \brief This API allows the host to provide PCM samples to be * rendered via audio_render. * * This file describes a simple API for host applications to play sound * using VideoCore. It includes the functionality to: * * \li open/close * \li set pcm parameters * \li set buffer size parameters * \li retrieve empty buffer available to use * \li send full buffer to be played * \li retrieve current buffering level * * This API has no thread context of it's own, so the caller must be * aware that the IL API will be used in context. This has * implications on executing calls inside callback contexts, and on * the minimum size of stack that the caller requires. See the * ilclient_stack_size() function for assistance. * * This API will use a single audio_render IL component, and * supply buffers to the input port using the OpenMAX IL base profile mode. ********************************************************************************/ struct AUDIOPLAY_STATE_T; /** * The AUDIOPLAY_STATE_T is an opaque type that represents the * audioplus engine handle. *******************************************************************************/ typedef struct AUDIOPLAY_STATE_T AUDIOPLAY_STATE_T; /** * The audioplay_create() function creates the audioplay object. * * @param handle On success, this is filled in with a handle to use in other * API functions. * * @param sample_rate The sample rate, in samples per second, for the PCM data. * This shall be between 8000 and 96000. * * @param num_channels The number of channels for the PCM data. Must be 1, 2, 4, or 8. * Channels must be sent interleaved. * * @param bit_depth The bitdepth per channel per sample. Must be 16 or 32. * * @param num_buffers The number of buffers that will be created to write PCM * samples into. * * @param buffer_size The size in bytes of each buffer that will be used to write * PCM samples into. Note that small buffers of less than a few * Kb in size may be faster than larger buffers, although this is * platform dependent. * * @return 0 on success, -1 on failure. *********************************************************************************/ VCHPRE_ int32_t VCHPOST_ audioplay_create(AUDIOPLAY_STATE_T **handle, uint32_t sample_rate, uint32_t num_channels, uint32_t bit_depth, uint32_t num_buffers, uint32_t buffer_size); /** * The audioplay_delete() function deletes the audioplay object. * * @param handle Must be a handle previously created by * audioplay_create(). After calling this * function that handle is no longer valid. Any * buffers provided by audioplay_get_buffer() * are also no longer valid and must not be referenced. * * @return 0 on success, -1 on failure. ********************************************************************************/ VCHPRE_ int32_t VCHPOST_ audioplay_delete(AUDIOPLAY_STATE_T *handle); /** * The audioplay_get_buffer() function requests an empty * buffer. Any buffer returned will have the valid size indicated in * the call to audioplay_create(). * * @param handle Must be a handle previously created by * audioplay_create(). * * @return A pointer to an available buffer. If no buffers are * available, then NULL will be returned. *********************************************************************************/ VCHPRE_ uint8_t * VCHPOST_ audioplay_get_buffer(AUDIOPLAY_STATE_T *handle); /** * The audioplay_play_buffer() sends a buffer containing * raw samples to be rendered. * * @param handle Must be a handle previously created by * audioplay_create(). * * @param buffer Must be a pointer previously returned by * audioplay_get_buffer(). After calling this function * the buffer pointer must not be referenced until returned * again by another call to audioplay_get_buffer(). * * @param length Length in bytes of valid data. Must be a whole number of * samples, ie a multiple of (num_channels*bit_depth/8). * * @return 0 on success, -1 on failure ********************************************************************************/ VCHPRE_ int32_t VCHPOST_ audioplay_play_buffer(AUDIOPLAY_STATE_T *handle, uint8_t *buffer, uint32_t length); /** * The audioplay_get_latency() requests the current audio * playout buffer size in samples, which is the latency until the next * sample supplied is to be rendered. * * @param handle Must be a handle previously created by * audioplay_create(). * * @return Number of samples currently buffered. *********************************************************************************/ VCHPRE_ uint32_t VCHPOST_ audioplay_get_latency(AUDIOPLAY_STATE_T *handle); #endif /* AUDIOPLAY_H */ userland/host_applications/linux/apps/hello_pi/hello_audio/sinewave.c000066400000000000000000000235351421703157200265760ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ // Lookup table for audio output demo short Sinewave[] = { 0, 201, 402, 603, 804, 1005, 1206, 1406, 1607, 1808, 2009, 2209, 2410, 2610, 2811, 3011, 3211, 3411, 3611, 3811, 4011, 4210, 4409, 4608, 4807, 5006, 5205, 5403, 5601, 5799, 5997, 6195, 6392, 6589, 6786, 6982, 7179, 7375, 7571, 7766, 7961, 8156, 8351, 8545, 8739, 8932, 9126, 9319, 9511, 9703, 9895, 10087, 10278, 10469, 10659, 10849, 11038, 11227, 11416, 11604, 11792, 11980, 12166, 12353, 12539, 12724, 12909, 13094, 13278, 13462, 13645, 13827, 14009, 14191, 14372, 14552, 14732, 14911, 15090, 15268, 15446, 15623, 15799, 15975, 16150, 16325, 16499, 16672, 16845, 17017, 17189, 17360, 17530, 17699, 17868, 18036, 18204, 18371, 18537, 18702, 18867, 19031, 19194, 19357, 19519, 19680, 19840, 20000, 20159, 20317, 20474, 20631, 20787, 20942, 21096, 21249, 21402, 21554, 21705, 21855, 22004, 22153, 22301, 22448, 22594, 22739, 22883, 23027, 23169, 23311, 23452, 23592, 23731, 23869, 24006, 24143, 24278, 24413, 24546, 24679, 24811, 24942, 25072, 25201, 25329, 25456, 25582, 25707, 25831, 25954, 26077, 26198, 26318, 26437, 26556, 26673, 26789, 26905, 27019, 27132, 27244, 27355, 27466, 27575, 27683, 27790, 27896, 28001, 28105, 28208, 28309, 28410, 28510, 28608, 28706, 28802, 28897, 28992, 29085, 29177, 29268, 29358, 29446, 29534, 29621, 29706, 29790, 29873, 29955, 30036, 30116, 30195, 30272, 30349, 30424, 30498, 30571, 30643, 30713, 30783, 30851, 30918, 30984, 31049, 31113, 31175, 31236, 31297, 31356, 31413, 31470, 31525, 31580, 31633, 31684, 31735, 31785, 31833, 31880, 31926, 31970, 32014, 32056, 32097, 32137, 32176, 32213, 32249, 32284, 32318, 32350, 32382, 32412, 32441, 32468, 32495, 32520, 32544, 32567, 32588, 32609, 32628, 32646, 32662, 32678, 32692, 32705, 32717, 32727, 32736, 32744, 32751, 32757, 32761, 32764, 32766, 32767, 32766, 32764, 32761, 32757, 32751, 32744, 32736, 32727, 32717, 32705, 32692, 32678, 32662, 32646, 32628, 32609, 32588, 32567, 32544, 32520, 32495, 32468, 32441, 32412, 32382, 32350, 32318, 32284, 32249, 32213, 32176, 32137, 32097, 32056, 32014, 31970, 31926, 31880, 31833, 31785, 31735, 31684, 31633, 31580, 31525, 31470, 31413, 31356, 31297, 31236, 31175, 31113, 31049, 30984, 30918, 30851, 30783, 30713, 30643, 30571, 30498, 30424, 30349, 30272, 30195, 30116, 30036, 29955, 29873, 29790, 29706, 29621, 29534, 29446, 29358, 29268, 29177, 29085, 28992, 28897, 28802, 28706, 28608, 28510, 28410, 28309, 28208, 28105, 28001, 27896, 27790, 27683, 27575, 27466, 27355, 27244, 27132, 27019, 26905, 26789, 26673, 26556, 26437, 26318, 26198, 26077, 25954, 25831, 25707, 25582, 25456, 25329, 25201, 25072, 24942, 24811, 24679, 24546, 24413, 24278, 24143, 24006, 23869, 23731, 23592, 23452, 23311, 23169, 23027, 22883, 22739, 22594, 22448, 22301, 22153, 22004, 21855, 21705, 21554, 21402, 21249, 21096, 20942, 20787, 20631, 20474, 20317, 20159, 20000, 19840, 19680, 19519, 19357, 19194, 19031, 18867, 18702, 18537, 18371, 18204, 18036, 17868, 17699, 17530, 17360, 17189, 17017, 16845, 16672, 16499, 16325, 16150, 15975, 15799, 15623, 15446, 15268, 15090, 14911, 14732, 14552, 14372, 14191, 14009, 13827, 13645, 13462, 13278, 13094, 12909, 12724, 12539, 12353, 12166, 11980, 11792, 11604, 11416, 11227, 11038, 10849, 10659, 10469, 10278, 10087, 9895, 9703, 9511, 9319, 9126, 8932, 8739, 8545, 8351, 8156, 7961, 7766, 7571, 7375, 7179, 6982, 6786, 6589, 6392, 6195, 5997, 5799, 5601, 5403, 5205, 5006, 4807, 4608, 4409, 4210, 4011, 3811, 3611, 3411, 3211, 3011, 2811, 2610, 2410, 2209, 2009, 1808, 1607, 1406, 1206, 1005, 804, 603, 402, 201, 0, -201, -402, -603, -804, -1005, -1206, -1406, -1607, -1808, -2009, -2209, -2410, -2610, -2811, -3011, -3211, -3411, -3611, -3811, -4011, -4210, -4409, -4608, -4807, -5006, -5205, -5403, -5601, -5799, -5997, -6195, -6392, -6589, -6786, -6982, -7179, -7375, -7571, -7766, -7961, -8156, -8351, -8545, -8739, -8932, -9126, -9319, -9511, -9703, -9895, -10087, -10278, -10469, -10659, -10849, -11038, -11227, -11416, -11604, -11792, -11980, -12166, -12353, -12539, -12724, -12909, -13094, -13278, -13462, -13645, -13827, -14009, -14191, -14372, -14552, -14732, -14911, -15090, -15268, -15446, -15623, -15799, -15975, -16150, -16325, -16499, -16672, -16845, -17017, -17189, -17360, -17530, -17699, -17868, -18036, -18204, -18371, -18537, -18702, -18867, -19031, -19194, -19357, -19519, -19680, -19840, -20000, -20159, -20317, -20474, -20631, -20787, -20942, -21096, -21249, -21402, -21554, -21705, -21855, -22004, -22153, -22301, -22448, -22594, -22739, -22883, -23027, -23169, -23311, -23452, -23592, -23731, -23869, -24006, -24143, -24278, -24413, -24546, -24679, -24811, -24942, -25072, -25201, -25329, -25456, -25582, -25707, -25831, -25954, -26077, -26198, -26318, -26437, -26556, -26673, -26789, -26905, -27019, -27132, -27244, -27355, -27466, -27575, -27683, -27790, -27896, -28001, -28105, -28208, -28309, -28410, -28510, -28608, -28706, -28802, -28897, -28992, -29085, -29177, -29268, -29358, -29446, -29534, -29621, -29706, -29790, -29873, -29955, -30036, -30116, -30195, -30272, -30349, -30424, -30498, -30571, -30643, -30713, -30783, -30851, -30918, -30984, -31049, -31113, -31175, -31236, -31297, -31356, -31413, -31470, -31525, -31580, -31633, -31684, -31735, -31785, -31833, -31880, -31926, -31970, -32014, -32056, -32097, -32137, -32176, -32213, -32249, -32284, -32318, -32350, -32382, -32412, -32441, -32468, -32495, -32520, -32544, -32567, -32588, -32609, -32628, -32646, -32662, -32678, -32692, -32705, -32717, -32727, -32736, -32744, -32751, -32757, -32761, -32764, -32766, -32767, -32766, -32764, -32761, -32757, -32751, -32744, -32736, -32727, -32717, -32705, -32692, -32678, -32662, -32646, -32628, -32609, -32588, -32567, -32544, -32520, -32495, -32468, -32441, -32412, -32382, -32350, -32318, -32284, -32249, -32213, -32176, -32137, -32097, -32056, -32014, -31970, -31926, -31880, -31833, -31785, -31735, -31684, -31633, -31580, -31525, -31470, -31413, -31356, -31297, -31236, -31175, -31113, -31049, -30984, -30918, -30851, -30783, -30713, -30643, -30571, -30498, -30424, -30349, -30272, -30195, -30116, -30036, -29955, -29873, -29790, -29706, -29621, -29534, -29446, -29358, -29268, -29177, -29085, -28992, -28897, -28802, -28706, -28608, -28510, -28410, -28309, -28208, -28105, -28001, -27896, -27790, -27683, -27575, -27466, -27355, -27244, -27132, -27019, -26905, -26789, -26673, -26556, -26437, -26318, -26198, -26077, -25954, -25831, -25707, -25582, -25456, -25329, -25201, -25072, -24942, -24811, -24679, -24546, -24413, -24278, -24143, -24006, -23869, -23731, -23592, -23452, -23311, -23169, -23027, -22883, -22739, -22594, -22448, -22301, -22153, -22004, -21855, -21705, -21554, -21402, -21249, -21096, -20942, -20787, -20631, -20474, -20317, -20159, -20000, -19840, -19680, -19519, -19357, -19194, -19031, -18867, -18702, -18537, -18371, -18204, -18036, -17868, -17699, -17530, -17360, -17189, -17017, -16845, -16672, -16499, -16325, -16150, -15975, -15799, -15623, -15446, -15268, -15090, -14911, -14732, -14552, -14372, -14191, -14009, -13827, -13645, -13462, -13278, -13094, -12909, -12724, -12539, -12353, -12166, -11980, -11792, -11604, -11416, -11227, -11038, -10849, -10659, -10469, -10278, -10087, -9895, -9703, -9511, -9319, -9126, -8932, -8739, -8545, -8351, -8156, -7961, -7766, -7571, -7375, -7179, -6982, -6786, -6589, -6392, -6195, -5997, -5799, -5601, -5403, -5205, -5006, -4807, -4608, -4409, -4210, -4011, -3811, -3611, -3411, -3211, -3011, -2811, -2610, -2410, -2209, -2009, -1808, -1607, -1406, -1206, -1005, -804, -603, -402, -201, }; userland/host_applications/linux/apps/hello_pi/hello_dispmanx/000077500000000000000000000000001421703157200253235ustar00rootroot00000000000000userland/host_applications/linux/apps/hello_pi/hello_dispmanx/CMakeLists.txt000066400000000000000000000002751421703157200300670ustar00rootroot00000000000000set(EXEC hello_dispmanx.bin) set(SRCS dispmanx.c) add_executable(${EXEC} ${SRCS}) target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) install(TARGETS ${EXEC} RUNTIME DESTINATION bin) userland/host_applications/linux/apps/hello_pi/hello_dispmanx/Makefile000077500000000000000000000001051421703157200267620ustar00rootroot00000000000000OBJS=dispmanx.o BIN=hello_dispmanx.bin include ../Makefile.include userland/host_applications/linux/apps/hello_pi/hello_dispmanx/dispmanx.c000077500000000000000000000140261421703157200273200ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ // A simple demo using dispmanx to display an overlay #include #include #include #include #include #include #include "bcm_host.h" #define WIDTH 200 #define HEIGHT 200 #ifndef ALIGN_UP #define ALIGN_UP(x,y) ((x + (y)-1) & ~((y)-1)) #endif typedef struct { DISPMANX_DISPLAY_HANDLE_T display; DISPMANX_MODEINFO_T info; void *image; DISPMANX_UPDATE_HANDLE_T update; DISPMANX_RESOURCE_HANDLE_T resource; DISPMANX_ELEMENT_HANDLE_T element; uint32_t vc_image_ptr; } RECT_VARS_T; static RECT_VARS_T gRectVars; static void FillRect( VC_IMAGE_TYPE_T type, void *image, int pitch, int aligned_height, int x, int y, int w, int h, int val ) { int row; int col; uint16_t *line = (uint16_t *)image + y * (pitch>>1) + x; for ( row = 0; row < h; row++ ) { for ( col = 0; col < w; col++ ) { line[col] = val; } line += (pitch>>1); } } int main(void) { RECT_VARS_T *vars; uint32_t screen = 0; int ret; VC_RECT_T src_rect; VC_RECT_T dst_rect; VC_IMAGE_TYPE_T type = VC_IMAGE_RGB565; int width = WIDTH, height = HEIGHT; int pitch = ALIGN_UP(width*2, 32); int aligned_height = ALIGN_UP(height, 16); VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 120, /*alpha 0->255*/ 0 }; vars = &gRectVars; bcm_host_init(); printf("Open display[%i]...\n", screen ); vars->display = vc_dispmanx_display_open( screen ); ret = vc_dispmanx_display_get_info( vars->display, &vars->info); assert(ret == 0); printf( "Display is %d x %d\n", vars->info.width, vars->info.height ); vars->image = calloc( 1, pitch * height ); assert(vars->image); FillRect( type, vars->image, pitch, aligned_height, 0, 0, width, height, 0xFFFF ); FillRect( type, vars->image, pitch, aligned_height, 0, 0, width, height, 0xF800 ); FillRect( type, vars->image, pitch, aligned_height, 20, 20, width - 40, height - 40, 0x07E0 ); FillRect( type, vars->image, pitch, aligned_height, 40, 40, width - 80, height - 80, 0x001F ); vars->resource = vc_dispmanx_resource_create( type, width, height, &vars->vc_image_ptr ); assert( vars->resource ); vc_dispmanx_rect_set( &dst_rect, 0, 0, width, height); ret = vc_dispmanx_resource_write_data( vars->resource, type, pitch, vars->image, &dst_rect ); assert( ret == 0 ); vars->update = vc_dispmanx_update_start( 10 ); assert( vars->update ); vc_dispmanx_rect_set( &src_rect, 0, 0, width << 16, height << 16 ); vc_dispmanx_rect_set( &dst_rect, ( vars->info.width - width ) / 2, ( vars->info.height - height ) / 2, width, height ); vars->element = vc_dispmanx_element_add( vars->update, vars->display, 2000, // layer &dst_rect, vars->resource, &src_rect, DISPMANX_PROTECTION_NONE, &alpha, NULL, // clamp VC_IMAGE_ROT0 ); ret = vc_dispmanx_update_submit_sync( vars->update ); assert( ret == 0 ); printf( "Sleeping for 10 seconds...\n" ); sleep( 10 ); vars->update = vc_dispmanx_update_start( 10 ); assert( vars->update ); ret = vc_dispmanx_element_remove( vars->update, vars->element ); assert( ret == 0 ); ret = vc_dispmanx_update_submit_sync( vars->update ); assert( ret == 0 ); ret = vc_dispmanx_resource_delete( vars->resource ); assert( ret == 0 ); ret = vc_dispmanx_display_close( vars->display ); assert( ret == 0 ); return 0; } userland/host_applications/linux/apps/hello_pi/hello_encode/000077500000000000000000000000001421703157200247355ustar00rootroot00000000000000userland/host_applications/linux/apps/hello_pi/hello_encode/CMakeLists.txt000066400000000000000000000002711421703157200274750ustar00rootroot00000000000000set(EXEC hello_encode.bin) set(SRCS encode.c) add_executable(${EXEC} ${SRCS}) target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) install(TARGETS ${EXEC} RUNTIME DESTINATION bin) userland/host_applications/linux/apps/hello_pi/hello_encode/Makefile000077500000000000000000000001251421703157200263760ustar00rootroot00000000000000OBJS=encode.o BIN=hello_encode.bin LDFLAGS+=-lilclient include ../Makefile.include userland/host_applications/linux/apps/hello_pi/hello_encode/encode.c000066400000000000000000000247401421703157200263450ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd Copyright (c) 2012, Kalle Vahlman Tuomas Kulve 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 name of the copyright holder nor the names of its 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. */ // Video encode demo using OpenMAX IL though the ilcient helper library #include #include #include #include "bcm_host.h" #include "ilclient.h" #define NUMFRAMES 300 #define WIDTH 640 #define HEIGHT ((WIDTH)*9/16) // generate an animated test card in YUV format static int generate_test_card(void *buf, OMX_U32 * filledLen, int frame, OMX_PARAM_PORTDEFINITIONTYPE *def) { int i, j; OMX_VIDEO_PORTDEFINITIONTYPE *vid = &def->format.video; char *y = buf; char *u = y + vid->nStride * vid->nSliceHeight; char *v = u + (vid->nStride >> 1) * (vid->nSliceHeight >> 1); for (j = 0; j < vid->nFrameHeight / 2; j++) { char *py = y + 2 * j * vid->nStride; char *pu = u + j * (vid->nStride >> 1); char *pv = v + j * (vid->nStride >> 1); for (i = 0; i < vid->nFrameWidth / 2; i++) { int z = (((i + frame) >> 4) ^ ((j + frame) >> 4)) & 15; py[0] = py[1] = py[vid->nStride] = py[vid->nStride + 1] = 0x80 + z * 0x8; pu[0] = 0x00 + z * 0x10; pv[0] = 0x80 + z * 0x30; py += 2; pu++; pv++; } } *filledLen = (vid->nStride * vid->nSliceHeight * 3) >> 1; return 1; } static void print_def(OMX_PARAM_PORTDEFINITIONTYPE def) { printf("Port %u: %s %u/%u %u %u %s,%s,%s %ux%u %ux%u @%u %u\n", def.nPortIndex, def.eDir == OMX_DirInput ? "in" : "out", def.nBufferCountActual, def.nBufferCountMin, def.nBufferSize, def.nBufferAlignment, def.bEnabled ? "enabled" : "disabled", def.bPopulated ? "populated" : "not pop.", def.bBuffersContiguous ? "contig." : "not cont.", def.format.video.nFrameWidth, def.format.video.nFrameHeight, def.format.video.nStride, def.format.video.nSliceHeight, def.format.video.xFramerate, def.format.video.eColorFormat); } static int video_encode_test(char *outputfilename) { OMX_VIDEO_PARAM_PORTFORMATTYPE format; OMX_PARAM_PORTDEFINITIONTYPE def; COMPONENT_T *video_encode = NULL; COMPONENT_T *list[5]; OMX_BUFFERHEADERTYPE *buf; OMX_BUFFERHEADERTYPE *out; OMX_ERRORTYPE r; ILCLIENT_T *client; int status = 0; int framenumber = 0; FILE *outf; memset(list, 0, sizeof(list)); if ((client = ilclient_init()) == NULL) { return -3; } if (OMX_Init() != OMX_ErrorNone) { ilclient_destroy(client); return -4; } // create video_encode r = ilclient_create_component(client, &video_encode, "video_encode", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS | ILCLIENT_ENABLE_OUTPUT_BUFFERS); if (r != 0) { printf ("ilclient_create_component() for video_encode failed with %x!\n", r); exit(1); } list[0] = video_encode; // get current settings of video_encode component from port 200 memset(&def, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); def.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); def.nVersion.nVersion = OMX_VERSION; def.nPortIndex = 200; if (OMX_GetParameter (ILC_GET_HANDLE(video_encode), OMX_IndexParamPortDefinition, &def) != OMX_ErrorNone) { printf("%s:%d: OMX_GetParameter() for video_encode port 200 failed!\n", __FUNCTION__, __LINE__); exit(1); } print_def(def); // Port 200: in 1/1 115200 16 enabled,not pop.,not cont. 320x240 320x240 @1966080 20 def.format.video.nFrameWidth = WIDTH; def.format.video.nFrameHeight = HEIGHT; def.format.video.xFramerate = 30 << 16; def.format.video.nSliceHeight = ALIGN_UP(def.format.video.nFrameHeight, 16); def.format.video.nStride = def.format.video.nFrameWidth; def.format.video.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar; print_def(def); r = OMX_SetParameter(ILC_GET_HANDLE(video_encode), OMX_IndexParamPortDefinition, &def); if (r != OMX_ErrorNone) { printf ("%s:%d: OMX_SetParameter() for video_encode port 200 failed with %x!\n", __FUNCTION__, __LINE__, r); exit(1); } memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE)); format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE); format.nVersion.nVersion = OMX_VERSION; format.nPortIndex = 201; format.eCompressionFormat = OMX_VIDEO_CodingAVC; printf("OMX_SetParameter for video_encode:201...\n"); r = OMX_SetParameter(ILC_GET_HANDLE(video_encode), OMX_IndexParamVideoPortFormat, &format); if (r != OMX_ErrorNone) { printf ("%s:%d: OMX_SetParameter() for video_encode port 201 failed with %x!\n", __FUNCTION__, __LINE__, r); exit(1); } OMX_VIDEO_PARAM_BITRATETYPE bitrateType; // set current bitrate to 1Mbit memset(&bitrateType, 0, sizeof(OMX_VIDEO_PARAM_BITRATETYPE)); bitrateType.nSize = sizeof(OMX_VIDEO_PARAM_BITRATETYPE); bitrateType.nVersion.nVersion = OMX_VERSION; bitrateType.eControlRate = OMX_Video_ControlRateVariable; bitrateType.nTargetBitrate = 1000000; bitrateType.nPortIndex = 201; r = OMX_SetParameter(ILC_GET_HANDLE(video_encode), OMX_IndexParamVideoBitrate, &bitrateType); if (r != OMX_ErrorNone) { printf ("%s:%d: OMX_SetParameter() for bitrate for video_encode port 201 failed with %x!\n", __FUNCTION__, __LINE__, r); exit(1); } // get current bitrate memset(&bitrateType, 0, sizeof(OMX_VIDEO_PARAM_BITRATETYPE)); bitrateType.nSize = sizeof(OMX_VIDEO_PARAM_BITRATETYPE); bitrateType.nVersion.nVersion = OMX_VERSION; bitrateType.nPortIndex = 201; if (OMX_GetParameter (ILC_GET_HANDLE(video_encode), OMX_IndexParamVideoBitrate, &bitrateType) != OMX_ErrorNone) { printf("%s:%d: OMX_GetParameter() for video_encode for bitrate port 201 failed!\n", __FUNCTION__, __LINE__); exit(1); } printf("Current Bitrate=%u\n",bitrateType.nTargetBitrate); printf("encode to idle...\n"); if (ilclient_change_component_state(video_encode, OMX_StateIdle) == -1) { printf ("%s:%d: ilclient_change_component_state(video_encode, OMX_StateIdle) failed", __FUNCTION__, __LINE__); } printf("enabling port buffers for 200...\n"); if (ilclient_enable_port_buffers(video_encode, 200, NULL, NULL, NULL) != 0) { printf("enabling port buffers for 200 failed!\n"); exit(1); } printf("enabling port buffers for 201...\n"); if (ilclient_enable_port_buffers(video_encode, 201, NULL, NULL, NULL) != 0) { printf("enabling port buffers for 201 failed!\n"); exit(1); } printf("encode to executing...\n"); ilclient_change_component_state(video_encode, OMX_StateExecuting); outf = fopen(outputfilename, "w"); if (outf == NULL) { printf("Failed to open '%s' for writing video\n", outputfilename); exit(1); } printf("looping for buffers...\n"); do { buf = ilclient_get_input_buffer(video_encode, 200, 1); if (buf == NULL) { printf("Doh, no buffers for me!\n"); } else { /* fill it */ generate_test_card(buf->pBuffer, &buf->nFilledLen, framenumber++, &def); if (OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_encode), buf) != OMX_ErrorNone) { printf("Error emptying buffer!\n"); } out = ilclient_get_output_buffer(video_encode, 201, 1); if (out != NULL) { if (out->nFlags & OMX_BUFFERFLAG_CODECCONFIG) { int i; for (i = 0; i < out->nFilledLen; i++) printf("%x ", out->pBuffer[i]); printf("\n"); } r = fwrite(out->pBuffer, 1, out->nFilledLen, outf); if (r != out->nFilledLen) { printf("fwrite: Error emptying buffer: %d!\n", r); } else { printf("Writing frame %d/%d, len %u\n", framenumber, NUMFRAMES, out->nFilledLen); } out->nFilledLen = 0; } else { printf("Not getting it :(\n"); } r = OMX_FillThisBuffer(ILC_GET_HANDLE(video_encode), out); if (r != OMX_ErrorNone) { printf("Error sending buffer for filling: %x\n", r); } } } while (framenumber < NUMFRAMES); fclose(outf); printf("Teardown.\n"); printf("disabling port buffers for 200 and 201...\n"); ilclient_disable_port_buffers(video_encode, 200, NULL, NULL, NULL); ilclient_disable_port_buffers(video_encode, 201, NULL, NULL, NULL); ilclient_state_transition(list, OMX_StateIdle); ilclient_state_transition(list, OMX_StateLoaded); ilclient_cleanup_components(list); OMX_Deinit(); ilclient_destroy(client); return status; } int main(int argc, char **argv) { if (argc < 2) { printf("Usage: %s \n", argv[0]); exit(1); } bcm_host_init(); return video_encode_test(argv[1]); } userland/host_applications/linux/apps/hello_pi/hello_fft/000077500000000000000000000000001421703157200242575ustar00rootroot00000000000000userland/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.c000066400000000000000000000112271421703157200260600ustar00rootroot00000000000000/* BCM2835 "GPU_FFT" release 3.0 Copyright (c) 2015, Andrew Holme. 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 name of the copyright holder nor the names of its 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. */ #include #include "gpu_fft.h" #define GPU_FFT_BUSY_WAIT_LIMIT (5<<12) // ~1ms typedef struct GPU_FFT_COMPLEX COMPLEX; int gpu_fft_prepare( int mb, // mailbox file_desc int log2_N, // log2(FFT_length) = 8...22 int direction, // GPU_FFT_FWD: fft(); GPU_FFT_REV: ifft() int jobs, // number of transforms in batch struct GPU_FFT **fft) { unsigned info_bytes, twid_bytes, data_bytes, code_bytes, unif_bytes, mail_bytes; unsigned size, *uptr, vc_tw, vc_data; int i, q, shared, unique, passes, ret; struct GPU_FFT_BASE *base; struct GPU_FFT_PTR ptr; struct GPU_FFT *info; if (gpu_fft_twiddle_size(log2_N, &shared, &unique, &passes)) return -2; info_bytes = 4096; data_bytes = (1+((sizeof(COMPLEX)<x = 1<y = jobs; // Ping-pong buffers leave results in or out of place info->in = info->out = ptr.arm.cptr; info->step = data_bytes / sizeof(COMPLEX); if (passes&1) info->out += info->step * jobs; // odd => out of place vc_data = gpu_fft_ptr_inc(&ptr, data_bytes*jobs*2); // Shader code memcpy(ptr.arm.vptr, gpu_fft_shader_code(log2_N), code_bytes); base->vc_code = gpu_fft_ptr_inc(&ptr, code_bytes); // Twiddles gpu_fft_twiddle_data(log2_N, direction, ptr.arm.fptr); vc_tw = gpu_fft_ptr_inc(&ptr, twid_bytes); uptr = ptr.arm.uptr; // Uniforms for (q=0; qvc_unifs[q] = gpu_fft_ptr_inc(&ptr, sizeof(int)*(5+jobs*2)); } if ((jobs<vc_msg = 0; } else { // Mailbox message for (q=0; qvc_unifs[q]; *uptr++ = base->vc_code; } base->vc_msg = ptr.vc; } *fft = info; return 0; } unsigned gpu_fft_execute(struct GPU_FFT *info) { return gpu_fft_base_exec(&info->base, GPU_FFT_QPUS); } void gpu_fft_release(struct GPU_FFT *info) { gpu_fft_base_release(&info->base); } userland/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.h000066400000000000000000000062041421703157200260640ustar00rootroot00000000000000/* BCM2835 "GPU_FFT" release 3.0 Copyright (c) 2015, Andrew Holme. 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 name of the copyright holder nor the names of its 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. */ #ifndef __GPU_FFT__ #define __GPU_FFT__ #define GPU_FFT_QPUS 8 #define GPU_FFT_PI 3.14159265358979323846 #define GPU_FFT_FWD 0 // forward FFT #define GPU_FFT_REV 1 // inverse FFT struct GPU_FFT_COMPLEX { float re, im; }; struct GPU_FFT_PTR { unsigned vc; union { struct GPU_FFT_COMPLEX *cptr; void *vptr; char *bptr; float *fptr; unsigned *uptr; } arm; }; struct GPU_FFT_BASE { int mb; unsigned handle, size, vc_msg, vc_code, vc_unifs[GPU_FFT_QPUS], peri_size; volatile unsigned *peri; }; struct GPU_FFT { struct GPU_FFT_BASE base; struct GPU_FFT_COMPLEX *in, *out; int x, y, step; }; int gpu_fft_prepare( int mb, // mailbox file_desc int log2_N, // log2(FFT_length) = 8...22 int direction, // GPU_FFT_FWD: fft(); GPU_FFT_REV: ifft() int jobs, // number of transforms in batch struct GPU_FFT **fft); unsigned gpu_fft_execute( struct GPU_FFT *info); void gpu_fft_release( struct GPU_FFT *info); // private int gpu_fft_twiddle_size(int, int *, int *, int *); void gpu_fft_twiddle_data(int, int, float *); unsigned int gpu_fft_shader_size(int); unsigned int *gpu_fft_shader_code(int); // gpu_fft_base: unsigned gpu_fft_base_exec ( struct GPU_FFT_BASE *base, int num_qpus); int gpu_fft_alloc ( int mb, unsigned size, struct GPU_FFT_PTR *ptr); void gpu_fft_base_release( struct GPU_FFT_BASE *base); unsigned gpu_fft_ptr_inc ( struct GPU_FFT_PTR *ptr, int bytes); #endif // __GPU_FFT__ userland/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.txt000066400000000000000000000135541421703157200264620ustar00rootroot00000000000000BCM2835 "GPU_FFT" release 3.0 by Andrew Holme, 2015. GPU_FFT is an FFT library for the Raspberry Pi which exploits the BCM2835 SoC 3D hardware to deliver ten times more data throughput than is possible on the 700 MHz ARM of the Pi 1. Kernels are provided for all power-of-2 FFT lengths between 256 and 4,194,304 points inclusive. A transpose function, which also uses the 3D hardware, is provided to support 2-dimensional transforms. *** Accuracy *** GPU_FFT uses single-precision floats for data and twiddle factors. The output is not scaled. The relative root-mean-square (rms) error in parts-per-million (ppm) for different transform lengths (N) is typically: log2(N) | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 ppm rms | 0.33 | 0.46 | 0.52 | 0.59 | 0.78 | 0.83 | 0.92 | 0.98 log2(N) | 16 | 17 | 18 | 19 | 20 | 21 | 22 ppm rms | 1.0 | 1.3 | 1.3 | 1.4 | 1.5 | 1.5 | 1.5 Accuracy has improved significantly over previous releases at the expense of a small (2%) performance hit; however, FFTW is still one order of magnitude more accurate than GPU_FFT. *** Throughput *** GPU_FFT 1.0 had to be invoked through a "mailbox" which added a 100us overhead on every call. To mitigate this, batches of transforms could be submitted via a single call. GPU_FFT now avoids this 100us overhead by poking GPU registers directly from the ARM if total batch runtime will be short; but still uses the mailbox for longer jobs to avoid busy waiting at 100% CPU for too long. Typical per-transform runtimes for batch sizes of 1 and 10; and comparative figures for FFTW (FFTW_MEASURE mode) on a Pi 1 with L2 cache enabled are: log2(N) | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 1 | 0.033 | 0.049 | 0.070 | 0.12 | 0.25 | 0.61 | 1.2 | 3.5 10 | 0.017 | 0.029 | 0.049 | 0.11 | 0.27 | 0.66 | 1.2 | 3.3 FFTW | 0.092 | 0.22 | 0.48 | 0.95 | 3.0 | 5.1 | 12 | 31 log2(N) | 16 | 17 | 18 | 19 | 20 | 21 | 22 All times in 1 | 7.0 | 17 | 43 | 97 | 194 | 388 | 786 milliseconds FFTW | 83 | 180 | 560 | 670 | 1600 | 3400 | 8800 2 sig. figs. *** API functions *** gpu_fft_prepare() Call once to allocate memory and initialise data structures. Returns 0 for success. gpu_fft_execute() Call one or more times to execute a previously prepared FFT batch. Returns 0 for success. gpu_fft_release() Call once to release resources after use. GPU memory is permanently lost if not freed. *** Parameters *** int mb Mailbox file descriptor obtained by calling mbox_open() int log2_N log2(FFT length) = 8 to 22 int direction FFT direction: GPU_FFT_FWD for forward FFT GPU_FFT_REV for inverse FFT int jobs Number of transforms in batch = 1 or more GPU_FFT ** Output parameter from prepare: control structure. GPU_FFT * Input parameter to execute and release *** Data format *** Complex data arrays are stored as alternate real and imaginary parts: struct GPU_FFT_COMPLEX { float re, im; }; The GPU_FFT struct created by gpu_fft_prepare() contains pointers to the input and output arrays: struct GPU_FFT { struct GPU_FFT_COMPLEX *in, *out; When executing a batch of transforms, buffer pointers are obtained as follows: struct GPU_FFT *fft = gpu_fft_prepare( ... , jobs); for (int j=0; jin + j*fft->step; struct GPU_FFT_COMPLEX *out = fft->out + j*fft->step; GPU_FFT.step is greater than FFT length because a guard space is left between buffers for caching and alignment reasons. GPU_FFT performs multiple passes between ping-pong buffers. The final output lands in the same buffer as input after an even number of passes. Transforms where log2_N=12...16 use an odd number of passes and the final result is left out-of-place. The input data is never preserved. *** Example program *** The code that produced the above accuracy and performance figures is included as a demo with the latest Raspbian distro. Build and run it as follows: cd /opt/vc/src/hello_pi/hello_fft make sudo ./hello_fft.bin 12 It accepts three optional command-line arguments: The special character device is required for the ioctl mailbox through which the ARM communicates with the Videocore GPU. *** With Open GL on Pi 1 *** GPU_FFT and Open GL will run concurrently on Pi 1 if GPU_FFT is configured not to use VC4 L2 cache by zeroing a define in file gpu_fft_base.c as follows: #define GPU_FFT_USE_VC4_L2_CACHE 0 // Pi 1 only: cached=1; direct=0 Overall performance will probably be higher if GPU_FFT and Open GL take turns at using the 3D hardware. Since eglSwapBuffers() returns immediately without waiting for rendering, call glFlush() and glFinish() afterwards as follows: for (;;) { .... eglSwapBuffers(....); // non-blocking call returns immediately glFlush(); glFinish(); // wait until V3D hardware is idle .... gpu_fft_execute(....); // blocking call .... } *** 2-dimensional FFT *** Please study the hello_fft_2d demo source, which is built and executed thus: make hello_fft_2d.bin sudo ./hello_fft_2d.bin This generates a Windows BMP file: "hello_fft_2d.bmp" The demo uses a square 512x512 array; however, rectangular arrays are allowed. The following lines in gpu_fft_trans.c will do what is safe: ptr.arm.uptr[6] = src->x < dst->y? src->x : dst->y; ptr.arm.uptr[7] = src->y < dst->x? src->y : dst->x; One may transpose the output from the second FFT pass back into the first pass input buffer, by preparing and executing a second transposition; however, this is probably unnecessary. It depends on how the final output will be accessed. userland/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_base.c000066400000000000000000000137701421703157200270570ustar00rootroot00000000000000/* BCM2835 "GPU_FFT" release 3.0 Copyright (c) 2015, Andrew Holme. 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 name of the copyright holder nor the names of its 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. */ #include #include "gpu_fft.h" #include "mailbox.h" #define BUS_TO_PHYS(x) ((x)&~0xC0000000) // V3D spec: https://docs.broadcom.com/doc/12358545 #define V3D_L2CACTL (0xC00020>>2) #define V3D_SLCACTL (0xC00024>>2) #define V3D_SRQPC (0xC00430>>2) #define V3D_SRQUA (0xC00434>>2) #define V3D_SRQCS (0xC0043c>>2) #define V3D_DBCFG (0xC00e00>>2) #define V3D_DBQITE (0xC00e2c>>2) #define V3D_DBQITC (0xC00e30>>2) // Setting this define to zero on Pi 1 allows GPU_FFT and Open GL // to co-exist and also improves performance of longer transforms: #define GPU_FFT_USE_VC4_L2_CACHE 1 // Pi 1 only: cached=1; direct=0 #define GPU_FFT_NO_FLUSH 1 #define GPU_FFT_TIMEOUT 2000 // ms struct GPU_FFT_HOST { unsigned mem_flg, mem_map, peri_addr, peri_size; }; int gpu_fft_get_host_info(struct GPU_FFT_HOST *info) { void *handle; unsigned (*bcm_host_get_sdram_address) (void); unsigned (*bcm_host_get_peripheral_address)(void); unsigned (*bcm_host_get_peripheral_size) (void); // Pi 1 defaults info->peri_addr = 0x20000000; info->peri_size = 0x01000000; info->mem_flg = GPU_FFT_USE_VC4_L2_CACHE? 0xC : 0x4; info->mem_map = GPU_FFT_USE_VC4_L2_CACHE? 0x0 : 0x20000000; // Pi 1 only handle = dlopen("libbcm_host.so", RTLD_LAZY); if (!handle) return -1; *(void **) (&bcm_host_get_sdram_address) = dlsym(handle, "bcm_host_get_sdram_address"); *(void **) (&bcm_host_get_peripheral_address) = dlsym(handle, "bcm_host_get_peripheral_address"); *(void **) (&bcm_host_get_peripheral_size) = dlsym(handle, "bcm_host_get_peripheral_size"); if (bcm_host_get_sdram_address && bcm_host_get_sdram_address()!=0x40000000) { // Pi 2? info->mem_flg = 0x4; // ARM cannot see VC4 L2 on Pi 2 info->mem_map = 0x0; } if (bcm_host_get_peripheral_address) info->peri_addr = bcm_host_get_peripheral_address(); if (bcm_host_get_peripheral_size) info->peri_size = bcm_host_get_peripheral_size(); dlclose(handle); return 0; } unsigned gpu_fft_base_exec_direct ( struct GPU_FFT_BASE *base, int num_qpus) { unsigned q, t; base->peri[V3D_DBCFG] = 0; // Disallow IRQ base->peri[V3D_DBQITE] = 0; // Disable IRQ base->peri[V3D_DBQITC] = -1; // Resets IRQ flags base->peri[V3D_L2CACTL] = 1<<2; // Clear L2 cache base->peri[V3D_SLCACTL] = -1; // Clear other caches base->peri[V3D_SRQCS] = (1<<7) | (1<<8) | (1<<16); // Reset error bit and counts for (q=0; qperi[V3D_SRQUA] = base->vc_unifs[q]; base->peri[V3D_SRQPC] = base->vc_code; } // Busy wait polling for (;;) { if (((base->peri[V3D_SRQCS]>>16) & 0xff) == num_qpus) break; // All done? } return 0; } unsigned gpu_fft_base_exec( struct GPU_FFT_BASE *base, int num_qpus) { if (base->vc_msg) { // Use mailbox // Returns: 0x0 for success; 0x80000000 for timeout return execute_qpu(base->mb, num_qpus, base->vc_msg, GPU_FFT_NO_FLUSH, GPU_FFT_TIMEOUT); } else { // Direct register poking return gpu_fft_base_exec_direct(base, num_qpus); } } int gpu_fft_alloc ( int mb, unsigned size, struct GPU_FFT_PTR *ptr) { struct GPU_FFT_HOST host; struct GPU_FFT_BASE *base; volatile unsigned *peri; unsigned handle; if (gpu_fft_get_host_info(&host)) return -5; if (qpu_enable(mb, 1)) return -1; // Shared memory handle = mem_alloc(mb, size, 4096, host.mem_flg); if (!handle) { qpu_enable(mb, 0); return -3; } peri = (volatile unsigned *) mapmem(host.peri_addr, host.peri_size); if (!peri) { mem_free(mb, handle); qpu_enable(mb, 0); return -4; } ptr->vc = mem_lock(mb, handle); ptr->arm.vptr = mapmem(BUS_TO_PHYS(ptr->vc+host.mem_map), size); base = (struct GPU_FFT_BASE *) ptr->arm.vptr; base->peri = peri; base->peri_size = host.peri_size; base->mb = mb; base->handle = handle; base->size = size; return 0; } void gpu_fft_base_release(struct GPU_FFT_BASE *base) { int mb = base->mb; unsigned handle = base->handle, size = base->size; unmapmem((void*)base->peri, base->peri_size); unmapmem((void*)base, size); mem_unlock(mb, handle); mem_free(mb, handle); qpu_enable(mb, 0); } unsigned gpu_fft_ptr_inc ( struct GPU_FFT_PTR *ptr, int bytes) { unsigned vc = ptr->vc; ptr->vc += bytes; ptr->arm.bptr += bytes; return vc; } userland/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_shaders.c000066400000000000000000000066461421703157200276020ustar00rootroot00000000000000/* BCM2835 "GPU_FFT" release 3.0 Copyright (c) 2015, Andrew Holme. 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 name of the copyright holder nor the names of its 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. */ static unsigned int shader_256[] = { #include "hex/shader_256.hex" }; static unsigned int shader_512[] = { #include "hex/shader_512.hex" }; static unsigned int shader_1k[] = { #include "hex/shader_1k.hex" }; static unsigned int shader_2k[] = { #include "hex/shader_2k.hex" }; static unsigned int shader_4k[] = { #include "hex/shader_4k.hex" }; static unsigned int shader_8k[] = { #include "hex/shader_8k.hex" }; static unsigned int shader_16k[] = { #include "hex/shader_16k.hex" }; static unsigned int shader_32k[] = { #include "hex/shader_32k.hex" }; static unsigned int shader_64k[] = { #include "hex/shader_64k.hex" }; static unsigned int shader_128k[] = { #include "hex/shader_128k.hex" }; static unsigned int shader_256k[] = { #include "hex/shader_256k.hex" }; static unsigned int shader_512k[] = { #include "hex/shader_512k.hex" }; static unsigned int shader_1024k[] = { #include "hex/shader_1024k.hex" }; static unsigned int shader_2048k[] = { #include "hex/shader_2048k.hex" }; static unsigned int shader_4096k[] = { #include "hex/shader_4096k.hex" }; static struct { unsigned int size, *code; } shaders[] = { {sizeof(shader_256), shader_256}, {sizeof(shader_512), shader_512}, {sizeof(shader_1k), shader_1k}, {sizeof(shader_2k), shader_2k}, {sizeof(shader_4k), shader_4k}, {sizeof(shader_8k), shader_8k}, {sizeof(shader_16k), shader_16k}, {sizeof(shader_32k), shader_32k}, {sizeof(shader_64k), shader_64k}, {sizeof(shader_128k), shader_128k}, {sizeof(shader_256k), shader_256k}, {sizeof(shader_512k), shader_512k}, {sizeof(shader_1024k), shader_1024k}, {sizeof(shader_2048k), shader_2048k}, {sizeof(shader_4096k), shader_4096k} }; unsigned int gpu_fft_shader_size(int log2_N) { return shaders[log2_N-8].size; } unsigned int *gpu_fft_shader_code(int log2_N) { return shaders[log2_N-8].code; } userland/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_trans.c000066400000000000000000000066611421703157200272750ustar00rootroot00000000000000/* BCM2835 "GPU_FFT" release 2.0 Copyright (c) 2014, Andrew Holme. 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 name of the copyright holder nor the names of its 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. */ #include #include "gpu_fft_trans.h" static unsigned int shader_trans[1024] = { #include "hex/shader_trans.hex" }; int gpu_fft_trans_prepare( int mb, struct GPU_FFT *src, struct GPU_FFT *dst, struct GPU_FFT_TRANS **out) { unsigned size, info_bytes, code_bytes, unif_bytes, mail_bytes; int ret; struct GPU_FFT_TRANS *info; struct GPU_FFT_BASE *base; struct GPU_FFT_PTR ptr; info_bytes = code_bytes = unif_bytes = mail_bytes = 4096; // 4k align size = info_bytes + // control code_bytes + // shader, aligned unif_bytes + // uniforms mail_bytes; // mailbox message ret = gpu_fft_alloc(mb, size, &ptr); if (ret) return ret; // Control header info = (struct GPU_FFT_TRANS *) ptr.arm.vptr; base = (struct GPU_FFT_BASE *) info; gpu_fft_ptr_inc(&ptr, info_bytes); // Shader code memcpy(ptr.arm.vptr, shader_trans, code_bytes); base->vc_code = gpu_fft_ptr_inc(&ptr, code_bytes); // Uniforms ptr.arm.uptr[0] = src->base.vc_msg; ptr.arm.uptr[1] = ((char*)src->out) - ((char*)src->in); // output buffer offset ptr.arm.uptr[2] = dst->base.vc_msg; ptr.arm.uptr[3] = 0; ptr.arm.uptr[4] = src->step * sizeof(struct GPU_FFT_COMPLEX); ptr.arm.uptr[5] = dst->step * sizeof(struct GPU_FFT_COMPLEX); ptr.arm.uptr[6] = src->x < dst->y? src->x : dst->y; ptr.arm.uptr[7] = src->y < dst->x? src->y : dst->x; base->vc_unifs[0] = gpu_fft_ptr_inc(&ptr, unif_bytes); // Mailbox message ptr.arm.uptr[0] = base->vc_unifs[0]; ptr.arm.uptr[1] = base->vc_code; base->vc_msg = gpu_fft_ptr_inc(&ptr, mail_bytes); *out = info; return 0; } unsigned gpu_fft_trans_execute(struct GPU_FFT_TRANS *info) { return gpu_fft_base_exec(&info->base, 1); } void gpu_fft_trans_release(struct GPU_FFT_TRANS *info) { gpu_fft_base_release(&info->base); } userland/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_trans.h000066400000000000000000000035741421703157200273020ustar00rootroot00000000000000/* BCM2835 "GPU_FFT" release 2.0 Copyright (c) 2014, Andrew Holme. 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 name of the copyright holder nor the names of its 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. */ #include "gpu_fft.h" struct GPU_FFT_TRANS { struct GPU_FFT_BASE base; }; int gpu_fft_trans_prepare( int mb, struct GPU_FFT *src, struct GPU_FFT *dst, struct GPU_FFT_TRANS **out); unsigned gpu_fft_trans_execute( // src->out ==> T ==> dst->in struct GPU_FFT_TRANS *info); void gpu_fft_trans_release( struct GPU_FFT_TRANS *info); userland/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_twiddles.c000066400000000000000000000231301421703157200277530ustar00rootroot00000000000000/* BCM2835 "GPU_FFT" release 3.0 Copyright (c) 2015, Andrew Holme. 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 name of the copyright holder nor the names of its 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. */ #include #include "gpu_fft.h" #define ALPHA(dx) (2*pow(sin((dx)/2),2)) #define BETA(dx) (sin(dx)) static double k[16] = {0,8,4,4,2,2,2,2,1,1,1,1,1,1,1,1}; static double m[16] = {0,0,0,1,0,1,2,3,0,1,2,3,4,5,6,7}; /****************************************************************************/ static float *twiddles_base_16(double two_pi, float *out, double theta) { int i; for (i=0; i<16; i++) { *out++ = cos(two_pi/16*k[i]*m[i] + theta*k[i]); *out++ = sin(two_pi/16*k[i]*m[i] + theta*k[i]); } return out; } static float *twiddles_base_32(double two_pi, float *out, double theta) { int i; for (i=0; i<16; i++) { *out++ = cos(two_pi/32*i + theta); *out++ = sin(two_pi/32*i + theta); } return twiddles_base_16(two_pi, out, 2*theta); } static float *twiddles_base_64(double two_pi, float *out) { int i; for (i=0; i<32; i++) { *out++ = cos(two_pi/64*i); *out++ = sin(two_pi/64*i); } return twiddles_base_32(two_pi, out, 0); } /****************************************************************************/ static float *twiddles_step_16(double two_pi, float *out, double theta) { int i; for (i=0; i<16; i++) { *out++ = ALPHA(theta*k[i]); *out++ = BETA(theta*k[i]); } return out; } static float *twiddles_step_32(double two_pi, float *out, double theta) { int i; for (i=0; i<16; i++) { *out++ = ALPHA(theta); *out++ = BETA(theta); } return twiddles_step_16(two_pi, out, 2*theta); } static float *twiddles_step_64(double two_pi, float *out, double theta) { int i; for (i=0; i<32; i++) { *out++ = ALPHA(theta); *out++ = BETA(theta); } return twiddles_step_32(two_pi, out, 2*theta); } /****************************************************************************/ static void twiddles_256(double two_pi, float *out) { double N=256; int q; out = twiddles_base_16(two_pi, out, 0); out = twiddles_step_16(two_pi, out, two_pi/N * GPU_FFT_QPUS); for (q=0; q22) return -1; *shared = shaders[log2_N-8].shared; *unique = shaders[log2_N-8].unique; *passes = shaders[log2_N-8].passes; return 0; } void gpu_fft_twiddle_data(int log2_N, int direction, float *out) { shaders[log2_N-8].twiddles((direction==GPU_FFT_FWD?-2:2)*GPU_FFT_PI, out); } userland/host_applications/linux/apps/hello_pi/hello_fft/hello_fft.c000066400000000000000000000102541421703157200263670ustar00rootroot00000000000000/* BCM2835 "GPU_FFT" release 3.0 Copyright (c) 2015, Andrew Holme. 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include #include "mailbox.h" #include "gpu_fft.h" char Usage[] = "Usage: hello_fft.bin log2_N [jobs [loops]]\n" "log2_N = log2(FFT_length), log2_N = 8...22\n" "jobs = transforms per batch, jobs>0, default 1\n" "loops = number of test repeats, loops>0, default 1\n"; unsigned Microseconds(void) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); return ts.tv_sec*1000000 + ts.tv_nsec/1000; } int main(int argc, char *argv[]) { int i, j, k, ret, loops, freq, log2_N, jobs, N, mb = mbox_open(); unsigned t[2]; double tsq[2]; struct GPU_FFT_COMPLEX *base; struct GPU_FFT *fft; log2_N = argc>1? atoi(argv[1]) : 12; // 8 <= log2_N <= 22 jobs = argc>2? atoi(argv[2]) : 1; // transforms per batch loops = argc>3? atoi(argv[3]) : 1; // test repetitions if (argc<2 || jobs<1 || loops<1) { printf(Usage); return -1; } N = 1<in + j*fft->step; // input buffer for (i=0; iout + j*fft->step; // output buffer freq = (j+1) & (N/2-1); for (i=0; i #include #include #include "gpu_fft_trans.h" #include "hello_fft_2d_bitmap.h" #define log2_N 9 #define N (1<io+(fft)->step*(y)) unsigned Microseconds(void) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); return ts.tv_sec*1000000 + ts.tv_nsec/1000; } int main(int argc, char *argv[]) { int x, y, ret, mb = mbox_open(); unsigned t[4]; struct GPU_FFT_COMPLEX *row; struct GPU_FFT_TRANS *trans; struct GPU_FFT *fft_pass[2]; BITMAPFILEHEADER bfh; BITMAPINFOHEADER bih; // Create Windows bitmap file FILE *fp = fopen("hello_fft_2d.bmp", "wb"); if (!fp) return -666; // Write bitmap header memset(&bfh, 0, sizeof(bfh)); bfh.bfType = 0x4D42; //"BM" bfh.bfSize = N*N*3; bfh.bfOffBits = sizeof(bfh) + sizeof(bih); fwrite(&bfh, sizeof(bfh), 1, fp); // Write bitmap info memset(&bih, 0, sizeof(bih)); bih.biSize = sizeof(bih); bih.biWidth = N; bih.biHeight = N; bih.biPlanes = 1; bih.biBitCount = 24; bih.biCompression = BI_RGB; fwrite(&bih, sizeof(bih), 1, fp); // Prepare 1st FFT pass ret = gpu_fft_prepare(mb, log2_N, GPU_FFT_REV, N, fft_pass+0); if (ret) { return ret; } // Prepare 2nd FFT pass ret = gpu_fft_prepare(mb, log2_N, GPU_FFT_REV, N, fft_pass+1); if (ret) { gpu_fft_release(fft_pass[0]); return ret; } // Transpose from 1st pass output to 2nd pass input ret = gpu_fft_trans_prepare(mb, fft_pass[0], fft_pass[1], &trans); if (ret) { gpu_fft_release(fft_pass[0]); gpu_fft_release(fft_pass[1]); return ret; } // Clear input array for (y=0; y FFT() ==> T() ==> FFT() ==> usleep(1); /* yield to OS */ t[0] = Microseconds(); gpu_fft_execute(fft_pass[0]); t[1] = Microseconds(); gpu_fft_trans_execute(trans); t[2] = Microseconds(); gpu_fft_execute(fft_pass[1]); t[3] = Microseconds(); // Write output to bmp file for (y=0; y> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffd78, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149c01c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149c01c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149da1c0, 0x10020867, // and r1, r0, mask 0x0e9db1c0, 0x10020827, // shr r0, r0, shift 0x149da1c0, 0x10020827, // and r0, r0, mask 0x119db3c0, 0x10020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c91c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffc30, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f549e7, // bra -, ra_save_32 0x95682ff6, 0x10024682, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x956c7ff6, 0x100246c7, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffba0, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffb50, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f549e7, // bra -, ra_save_32 0x95682ff6, 0x10024682, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x956c7ff6, 0x100246c7, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst 0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 0x119c51c0, 0xd0020827, // shl r0, r0, 5 0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif 0x15827d80, 0x100210e7, // mov rb_addr_y, unif 0x00001258, 0xf00809e7, // brr.allz -, r:end 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149c01c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149c01c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149da1c0, 0x10020867, // and r1, r0, mask 0x0e9db1c0, 0x10020827, // shr r0, r0, shift 0x149da1c0, 0x10020827, // and r0, r0, mask 0x119db3c0, 0x10020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c91c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff998, 0xf0f80227, // brr ra_link_1, r:pass_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xfffff970, 0xf00809e7, // brr.allz -, r:pass_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000003, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff9d0, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x00007fff, 0xe0020827, // mov r0, 0x7FFF 0x141e7c00, 0x100229e7, // and.setf -, ra_points, r0 0xfffff9a0, 0xf01809e7, // brr.allnz -, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100601e7, // add.ifnz ra_points, ra_points, rb_0x100 0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x024e7c80, 0x10020827, // fsub r0, a, b 0x024e7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x024e7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01527380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x024e7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020527, // fadd a+1, r0, r1 0x029d3ec0, 0x10020827, // fsub r0, a, b 0x029d31c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d3e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d43c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d3e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021527, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02467c80, 0x10020827, // fsub r0, a, b 0x02467180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02467c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02467c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204a7, // fadd a+1, r0, r1 0x029d1ec0, 0x10020827, // fsub r0, a, b 0x029d11c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d1e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d23c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d1e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214a7, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xfffff710, 0xf00809e7, // brr.allz -, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000005, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000004, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff4a0, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0xfffff480, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0xfffff460, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0xfffff440, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x024e7c80, 0x10020827, // fsub r0, a, b 0x024e7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x024e7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01527380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x024e7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020527, // fadd a+1, r0, r1 0x029d3ec0, 0x10020827, // fsub r0, a, b 0x029d31c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d3e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d43c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d3e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021527, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02467c80, 0x10020827, // fsub r0, a, b 0x02467180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02467c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02467c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204a7, // fadd a+1, r0, r1 0x029d1ec0, 0x10020827, // fsub r0, a, b 0x029d11c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d1e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d23c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d1e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214a7, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xfffff1b0, 0xf00809e7, // brr.allz -, r:pass_3 0x00000060, 0xe0020827, // mov r0, 3*4*8 0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000007, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000006, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xffffef40, 0xf0f80227, // brr ra_link_1, r:pass_4 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x024e7c80, 0x10020827, // fsub r0, a, b 0x024e7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x024e7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01527380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x024e7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020527, // fadd a+1, r0, r1 0x029d3ec0, 0x10020827, // fsub r0, a, b 0x029d31c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d3e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d43c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d3e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021527, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02467c80, 0x10020827, // fsub r0, a, b 0x02467180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02467c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02467c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204a7, // fadd a+1, r0, r1 0x029d1ec0, 0x10020827, // fsub r0, a, b 0x029d11c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d1e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d23c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d1e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214a7, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xffffecb0, 0xf00809e7, // brr.allz -, r:pass_4 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0xffffed78, 0xf0f809e7, // brr -, r:loop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c3fc0, 0x100209a7, // mov interrupt, flag 0x009e7000, 0x300009e7, // nop; nop; thrend 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_128k.hex000066400000000000000000001123131421703157200275650ustar00rootroot000000000000000x00000011, 0xe0021227, // mov rb_STAGES, STAGES 0x00000010, 0xe00216a7, // mov rb_0x10, 0x10 0x00000040, 0xe00216e7, // mov rb_0x40, 0x40 0x00000080, 0xe0021727, // mov rb_0x80, 0x80 0x000000f0, 0xe0021767, // mov rb_0xF0, 0xF0 0x00000100, 0xe00217a7, // mov rb_0x100, 0x100 0x00000fff, 0xe00217e7, // mov rb_0xFFF, 0xFFF 0x55555555, 0xe0020767, // mov rx_0x55555555, 0x55555555 0x33333333, 0xe00207a7, // mov rx_0x33333333, 0x33333333 0x0f0f0f0f, 0xe00207e7, // mov rx_0x0F0F0F0F, 0x0F0F0F0F 0x00ff00ff, 0xe0021627, // mov rx_0x00FF00FF, 0x00FF00FF 0x0000ffff, 0xe0021667, // mov rx_0x0000FFFF, 0x0000FFFF 0x88104000, 0xe00206e7, // mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) 0x88105000, 0xe0021027, // mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) 0x90104000, 0xe0020727, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) 0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) 0x15827d80, 0x100202e7, // mov rx_tw_shared, unif 0x15827d80, 0x100212e7, // mov rx_tw_unique, unif 0x15827d80, 0x10021167, // mov rb_inst, unif 0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) 0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) 0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) 0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst 0xcc9e7081, 0x10024660, // add out_0, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100246a0, // add out_1, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 0x0c9e7080, 0x100211e7, // add out_3, r0, r2 0x000000b0, 0xf0f80127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, arg 0x159e7000, 0x10020c27, // mov vpm, r0 0x159e7240, 0x10020c27, // mov vpm, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x156e7d80, 0x10021c67, // mov vw_setup, arg_vdw 0xc000ffc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS16_STRIDE-16*4 0x8c05bdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr 0x00000038, 0xf0f81127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, arg 0x159e7000, 0x10020c27, // mov vpm, r0 0x159e7240, 0x10020c27, // mov vpm, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10020c67, // mov vr_setup, arg_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x000000c8, 0xf0f802a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x15727d80, 0x10021c67, // mov vw_setup, ra_vdw_32 0xc0007fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 0x8c05bdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr 0x00000050, 0xf0f812a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10020c67, // mov vr_setup, ra_vpm_lo 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000080, 0xf0f801a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000560, 0xf0f811a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000009, 0xe80009e7, // mov -, srel(i+9) 0x00000011, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000a, 0xe80009e7, // mov -, srel(i+9) 0x00000012, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000b, 0xe80009e7, // mov -, srel(i+9) 0x00000013, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000c, 0xe80009e7, // mov -, srel(i+9) 0x00000014, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000d, 0xe80009e7, // mov -, srel(i+9) 0x00000015, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000e, 0xe80009e7, // mov -, srel(i+9) 0x00000016, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000f, 0xe80009e7, // mov -, srel(i+9) 0x00000017, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffd78, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c15adf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149d81c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149d81c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149d91c0, 0x10020867, // and r1, r0, mask 0x0e9da1c0, 0x10020827, // shr r0, r0, shift 0x149d91c0, 0x10020827, // and r0, r0, mask 0x119da3c0, 0x10020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9cc1c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffc30, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f549e7, // bra -, ra_save_32 0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffba0, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x00000000, 0xf0f489e7, // bra -, ra_save_16 0x009e7000, 0x100009e7, // nop 0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x956c0ff6, 0x100246c0, // mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst 0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 0x119c51c0, 0xd0020827, // shl r0, r0, 5 0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif 0x15827d80, 0x100210e7, // mov rb_addr_y, unif 0x00000d00, 0xf00809e7, // brr.allz -, r:end 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15adf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149d81c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149d81c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149d91c0, 0x10020867, // and r1, r0, mask 0x0e9da1c0, 0x10020827, // shr r0, r0, shift 0x149d91c0, 0x10020827, // and r0, r0, mask 0x119da3c0, 0x10020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9cc1c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffa08, 0xf0f80227, // brr ra_link_1, r:pass_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xfffff9e0, 0xf00809e7, // brr.allz -, r:pass_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c41c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffaf0, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x141dfdc0, 0x100229e7, // and.setf -, ra_points, rb_0xFFF 0xfffffac8, 0xf01809e7, // brr.allnz -, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dcdc0, 0x100601e7, // add.ifnz ra_points, ra_points, rb_0x80 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02467c80, 0x10020827, // fsub r0, a, b 0x02467180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02467c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02467c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204a7, // fadd a+1, r0, r1 0x029d1ec0, 0x10020827, // fsub r0, a, b 0x029d11c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d1e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d23c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d1e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214a7, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xfffff938, 0xf00809e7, // brr.allz -, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000003, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c41c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff778, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0xfffff758, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02467c80, 0x10020827, // fsub r0, a, b 0x02467180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02467c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02467c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204a7, // fadd a+1, r0, r1 0x029d1ec0, 0x10020827, // fsub r0, a, b 0x029d11c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d1e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d23c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d1e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214a7, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xfffff5c8, 0xf00809e7, // brr.allz -, r:pass_3 0x00000020, 0xe0020827, // mov r0, 4*8 0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000004, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c41c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff408, 0xf0f80227, // brr ra_link_1, r:pass_4 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02467c80, 0x10020827, // fsub r0, a, b 0x02467180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02467c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02467c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204a7, // fadd a+1, r0, r1 0x029d1ec0, 0x10020827, // fsub r0, a, b 0x029d11c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d1e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d23c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d1e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214a7, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xfffff278, 0xf00809e7, // brr.allz -, r:pass_4 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0xfffff2d0, 0xf0f809e7, // brr -, r:loop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c3fc0, 0x100209a7, // mov interrupt, flag 0x009e7000, 0x300009e7, // nop; nop; thrend 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_16k.hex000066400000000000000000001051521421703157200275040ustar00rootroot000000000000000x00000010, 0xe00216e7, // mov rb_0x10, 0x10 0x00000040, 0xe0021727, // mov rb_0x40, 0x40 0x00000080, 0xe0021767, // mov rb_0x80, 0x80 0x000000f0, 0xe00217a7, // mov rb_0xF0, 0xF0 0x00000100, 0xe00217e7, // mov rb_0x100, 0x100 0x00005555, 0xe0020767, // mov rx_0x5555, 0x5555 0x00003333, 0xe00207a7, // mov rx_0x3333, 0x3333 0x00000f0f, 0xe00207e7, // mov rx_0x0F0F, 0x0F0F 0x000000ff, 0xe00216a7, // mov rx_0x00FF, 0x00FF 0x88104000, 0xe00206e7, // mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) 0x88105000, 0xe0021027, // mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) 0x90104000, 0xe0020727, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) 0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) 0x15827d80, 0x100202e7, // mov rx_tw_shared, unif 0x15827d80, 0x100212e7, // mov rx_tw_unique, unif 0x15827d80, 0x10021167, // mov rb_inst, unif 0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) 0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) 0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) 0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst 0xcc9e7081, 0x10024660, // add out_0, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100246a0, // add out_1, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 0x0c9e7080, 0x100211e7, // add out_3, r0, r2 0x000000b0, 0xf0f80127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, arg 0x159e7000, 0x10020c27, // mov vpm, r0 0x159e7240, 0x10020c27, // mov vpm, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x156e7d80, 0x10021c67, // mov vw_setup, arg_vdw 0xc0001fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS16_STRIDE-16*4 0x8c05cdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr 0x00000038, 0xf0f81127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, arg 0x159e7000, 0x10020c27, // mov vpm, r0 0x159e7240, 0x10020c27, // mov vpm, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10020c67, // mov vr_setup, arg_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x000000c8, 0xf0f802a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x15727d80, 0x10021c67, // mov vw_setup, ra_vdw_32 0xc0000fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 0x8c05cdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr 0x00000050, 0xf0f812a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10020c67, // mov vr_setup, ra_vpm_lo 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000080, 0xf0f801a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x000005f0, 0xf0f811a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000009, 0xe80009e7, // mov -, srel(i+9) 0x00000011, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000a, 0xe80009e7, // mov -, srel(i+9) 0x00000012, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000b, 0xe80009e7, // mov -, srel(i+9) 0x00000013, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000c, 0xe80009e7, // mov -, srel(i+9) 0x00000014, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000d, 0xe80009e7, // mov -, srel(i+9) 0x00000015, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000e, 0xe80009e7, // mov -, srel(i+9) 0x00000016, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000f, 0xe80009e7, // mov -, srel(i+9) 0x00000017, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffda0, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149da1c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149da1c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x119c11c0, 0xd0020827, // shl r0, r0, STAGES-13 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffc80, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f549e7, // bra -, ra_save_32 0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffbf0, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffba0, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f549e7, // bra -, ra_save_32 0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffb10, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x00000000, 0xf0f489e7, // bra -, ra_save_16 0x009e7000, 0x100009e7, // nop 0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x956c0ff6, 0x100246c0, // mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst 0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 0x119c51c0, 0xd0020827, // shl r0, r0, 5 0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif 0x15827d80, 0x100210e7, // mov rb_addr_y, unif 0x00000b10, 0xf00809e7, // brr.allz -, r:end 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149da1c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149da1c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x119c11c0, 0xd0020827, // shl r0, r0, STAGES-13 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff9a0, 0xf0f80227, // brr ra_link_1, r:pass_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x0e1cedc0, 0xd00229e7, // shr.setf -, ra_points, STAGES 0xfffff978, 0xf00809e7, // brr.allz -, r:pass_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000003, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff988, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0xfffff968, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x024e7c80, 0x10020827, // fsub r0, a, b 0x024e7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x024e7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01527380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x024e7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020527, // fadd a+1, r0, r1 0x029d3ec0, 0x10020827, // fsub r0, a, b 0x029d31c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d3e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d43c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d3e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021527, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02467c80, 0x10020827, // fsub r0, a, b 0x02467180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02467c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02467c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204a7, // fadd a+1, r0, r1 0x029d1ec0, 0x10020827, // fsub r0, a, b 0x029d11c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d1e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d23c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d1e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214a7, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1cedc0, 0xd00229e7, // shr.setf -, ra_points, STAGES 0xfffff6d8, 0xf00809e7, // brr.allz -, r:pass_2 0x00000020, 0xe0020827, // mov r0, 4*8 0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000004, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c41c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff5f8, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02467c80, 0x10020827, // fsub r0, a, b 0x02467180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02467c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02467c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204a7, // fadd a+1, r0, r1 0x029d1ec0, 0x10020827, // fsub r0, a, b 0x029d11c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d1e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d23c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d1e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214a7, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1cedc0, 0xd00229e7, // shr.setf -, ra_points, STAGES 0xfffff468, 0xf00809e7, // brr.allz -, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0xfffff4c0, 0xf0f809e7, // brr -, r:loop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c3fc0, 0x100209a7, // mov interrupt, flag 0x009e7000, 0x300009e7, // nop; nop; thrend 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_1k.hex000066400000000000000000000646211421703157200274230ustar00rootroot000000000000000x00000010, 0xe00216e7, // mov rb_0x10, 0x10 0x00000040, 0xe0021727, // mov rb_0x40, 0x40 0x000000f0, 0xe0021767, // mov rb_0xF0, 0xF0 0x00005555, 0xe00207a7, // mov rx_0x5555, 0x5555 0x00003333, 0xe00217a7, // mov rx_0x3333, 0x3333 0x00000f0f, 0xe00207e7, // mov rx_0x0F0F, 0x0F0F 0x000000ff, 0xe00217e7, // mov rx_0x00FF, 0x00FF 0x90104000, 0xe0020767, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) 0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) 0x15827d80, 0x100202a7, // mov rx_tw_shared, unif 0x15827d80, 0x100212a7, // mov rx_tw_unique, unif 0x15827d80, 0x10021167, // mov rb_inst, unif 0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) 0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) 0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) 0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst 0xcc9e7081, 0x100246e0, // add out_0, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x10024720, // add out_1, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 0x0c9e7080, 0x100211e7, // add out_3, r0, r2 0x000000c8, 0xf0f80127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x156e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x15727d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x15767d80, 0x10021c67, // mov vw_setup, ra_vdw_32 0xc00000c0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 0x8c05cdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr 0x00000050, 0xf0f81127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x156e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x15727d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x156e7d80, 0x10020c67, // mov vr_setup, ra_vpm_lo 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000080, 0xf0f801a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000588, 0xf0f811a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000009, 0xe80009e7, // mov -, srel(i+9) 0x00000011, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000a, 0xe80009e7, // mov -, srel(i+9) 0x00000012, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000b, 0xe80009e7, // mov -, srel(i+9) 0x00000013, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000c, 0xe80009e7, // mov -, srel(i+9) 0x00000014, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000d, 0xe80009e7, // mov -, srel(i+9) 0x00000015, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000e, 0xe80009e7, // mov -, srel(i+9) 0x00000016, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000f, 0xe80009e7, // mov -, srel(i+9) 0x00000017, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffda0, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149de1c0, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x149de1c0, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149df1c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149df1c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c31c0, 0xd0020827, // shr r0, r0, 13-STAGES 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffc80, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x204a7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d200f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x204a700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22092c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f489e7, // bra -, ra_save_32 0x956c2ff6, 0x100246c2, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x95707ff6, 0x10024707, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x95741ff6, 0x10024741, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffbf0, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffba0, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x204a7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d200f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x204a700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22092c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f489e7, // bra -, ra_save_32 0x956c2ff6, 0x100246c2, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x95707ff6, 0x10024707, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x95741ff6, 0x10024741, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst 0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 0x119c51c0, 0xd0020827, // shl r0, r0, 5 0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 0x159c4fc0, 0x10060127, // mov.ifnz ra_save_32, rx_save_slave_32 0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif 0x15827d80, 0x100210e7, // mov rb_addr_y, unif 0x000007a0, 0xf00809e7, // brr.allz -, r:end 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204a7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214a7, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024451, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe00244d3, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149de1c0, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x149de1c0, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149df1c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149df1c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c31c0, 0xd0020827, // shr r0, r0, 13-STAGES 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffa10, 0xf0f80227, // brr ra_link_1, r:pass_1 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x0e1cadc0, 0xd00229e7, // shr.setf -, ra_points, STAGES 0xfffff9e8, 0xf00809e7, // brr.allz -, r:pass_1 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cae00, 0x10020e27, // add t0s, ptr, r0 0x0c9cae40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cae00, 0x10020e27, // add t0s, ptr, r0 0x0c9cae40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204a7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214a7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000003, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100202e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100212e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024451, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe00244d3, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff9f8, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x95492dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x024a7c80, 0x10020827, // fsub r0, a, b 0x024a7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x024a7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014e7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025892, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x024a7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204e7, // fadd a+1, r0, r1 0x029d2ec0, 0x10020827, // fsub r0, a, b 0x029d21c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d2e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d33c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024892, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d2e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214e7, // fadd a+1, r0, r1 0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x202e7016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cb017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cb01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x212e709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02427c80, 0x10020827, // fsub r0, a, b 0x02427180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02427c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01467380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025890, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02427c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020467, // fadd a+1, r0, r1 0x029d0ec0, 0x10020827, // fsub r0, a, b 0x029d01c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d0e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d13c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024890, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d0e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021467, // fadd a+1, r0, r1 0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1cadc0, 0xd00229e7, // shr.setf -, ra_points, STAGES 0xfffff768, 0xf00809e7, // brr.allz -, r:pass_2 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0xfffff830, 0xf0f809e7, // brr -, r:loop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c3fc0, 0x100209a7, // mov interrupt, flag 0x009e7000, 0x300009e7, // nop; nop; thrend 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_2048k.hex000066400000000000000000002071471421703157200276620ustar00rootroot000000000000000x00000010, 0xe0021227, // mov rb_0x10, 0x10 0x000001d0, 0xe0021967, // mov r5rep, 0x1D0 0x15827d80, 0x100203e7, // mov rx_tw_shared, unif 0x15827d80, 0x100213e7, // mov rx_tw_unique, unif 0x15827d80, 0x10021167, // mov rb_inst, unif 0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) 0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) 0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) 0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst 0xcc9e7081, 0x10025020, // add out_0, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x10025060, // add out_1, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 0x0c9e7080, 0x100211e7, // add out_3, r0, r2 0x000002e8, 0xf0f80127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x153a7d80, 0x10020827, // mov r0, ra_vdw_32 0x8c04ddf6, 0x10024061, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr 0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) 0x00080000, 0xe00208e7, // mov r3, PASS32_STRIDE 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000050, 0xf0f81127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x152e7d80, 0x10020c67, // mov vr_setup, ra_vpm_lo 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000520, 0xf0f802a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm 0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x80904000, 0xe0020827, // mov r0, vdw_setup_0(1, 16, dma_h32(0,0)) 0x00000040, 0xe0020867, // mov r1, 0x40 0x8c067c76, 0x10024061, // add ra_save_ptr, ra_save_ptr, r1; mov r1, ra_save_ptr 0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) 0x00040000, 0xe00208e7, // mov r3, PASS64_STRIDE 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x000002b8, 0xf0f812a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd00200a7, // shl ra_temp, r0, 5 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0fc49e7, // brr -, ra_temp 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x000000e0, 0xf0f809e7, // brr -, r:2f 0x00000010, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x000000c0, 0xf0f809e7, // brr -, r:2f 0x00000011, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x000000a0, 0xf0f809e7, // brr -, r:2f 0x00000012, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000080, 0xf0f809e7, // brr -, r:2f 0x00000013, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000060, 0xf0f809e7, // brr -, r:2f 0x00000014, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000040, 0xf0f809e7, // brr -, r:2f 0x00000015, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000020, 0xf0f809e7, // brr -, r:2f 0x00000016, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f809e7, // brr -, r:2f 0x00000017, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm 0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 0x00000000, 0xf0fc49e7, // brr -, ra_temp 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000008, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000009, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000a, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000b, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000c, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000d, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000e, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000f, 0xe80009e7, // mov -, srel(i+8) 0x00000080, 0xf0f801a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000998, 0xf0f811a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000009, 0xe80009e7, // mov -, srel(i+9) 0x00000011, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000a, 0xe80009e7, // mov -, srel(i+9) 0x00000012, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000b, 0xe80009e7, // mov -, srel(i+9) 0x00000013, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000c, 0xe80009e7, // mov -, srel(i+9) 0x00000014, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000d, 0xe80009e7, // mov -, srel(i+9) 0x00000015, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000e, 0xe80009e7, // mov -, srel(i+9) 0x00000016, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000f, 0xe80009e7, // mov -, srel(i+9) 0x00000017, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffd50, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x55555555, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x33333333, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0f0f0f0f, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x00ff00ff, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0000ffff, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c81c0, 0x10020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c83c0, 0x10020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c81c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffbe0, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x01267c00, 0x100202e7, // fadd ra_64+0, ra_32_re, r0 0x019c9e40, 0x10020327, // fadd ra_64+1, rb_32_im, r1 0x02267c00, 0x10020367, // fsub ra_64+2, ra_32_re, r0 0x029c9e40, 0x100203a7, // fsub ra_64+3, rb_32_im, r1 0x8c167d76, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x55555555, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x33333333, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0f0f0f0f, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x00ff00ff, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0000ffff, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c81c0, 0x10020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c83c0, 0x10020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c81c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffa30, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x55555555, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x33333333, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0f0f0f0f, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x00ff00ff, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0000ffff, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c81c0, 0x10020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c83c0, 0x10020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c81c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffff8c0, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x029c9e40, 0x100208e7, // fsub r3, rb_32_im, r1 0x02267c00, 0x100208a7, // fsub r2, ra_32_re, r0 0x019c9e40, 0x10020867, // fadd r1, rb_32_im, r1 0x01267c00, 0x10020827, // fadd r0, ra_32_re, r0 0x2066700e, 0x100049c9, // nop; fmul rb_32_im, r1, ra_tw_re+TW48 0x209d900f, 0x100059c9, // nop; fmul ra_32_re, r1, rb_tw_im+TW48 0x209d9007, 0x100049e1, // nop; fmul r1, r0, rb_tw_im+TW48 0x216493c6, 0x10025320, // fadd rb_64+1, r1, rb_32_im; fmul r0, r0, ra_tw_re+TW48 0x2225a19f, 0x100252c9, // fsub rb_64+0, r0, ra_32_re; fmul ra_32_re, r3, rb_tw_im+TW64 0x206a701e, 0x100049c9, // nop; fmul rb_32_im, r3, ra_tw_re+TW64 0x00000000, 0xf0f549e7, // bra -, ra_save_64 0x209da017, 0x100049e3, // nop; fmul r3, r2, rb_tw_im+TW64 0x216897d6, 0x100253a2, // fadd rb_64+3, r3, rb_32_im; fmul r2, r2, ra_tw_re+TW64 0x02267580, 0x10021367, // fsub rb_64+2, r2, ra_32_re 0x8c14cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff7e0, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff790, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f489e7, // bra -, ra_save_32 0x952c2ff6, 0x100242c2, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x95307ff6, 0x10024307, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x9538eff6, 0x1002438e, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst 0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 0x119c51c0, 0xd0020827, // shl r0, r0, 5 0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 0x159c4fc0, 0x10060127, // mov.ifnz ra_save_32, rx_save_slave_32 0x159cafc0, 0x100602a7, // mov.ifnz ra_save_64, rx_save_slave_64 0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif 0x15827d80, 0x100210e7, // mov rb_addr_y, unif 0x00001378, 0xf00809e7, // brr.allz -, r:end 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000003, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020667, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021667, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100206a7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100216a7, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c61c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x55555555, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x33333333, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0f0f0f0f, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x00ff00ff, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0000ffff, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c81c0, 0x10020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c83c0, 0x10020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c81c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff4f8, 0xf0f80227, // brr ra_link_1, r:pass_1 0x009e7000, 0x100009e7, // nop 0x00000200, 0xe0020827, // mov r0, 0x200 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x00000015, 0xe0020867, // mov r1, STAGES 0x0e1e7c40, 0x100229e7, // shr.setf -, ra_points, r1 0xfffff4c8, 0xf00809e7, // brr.allz -, r:pass_1 0x009e7000, 0x100009e7, // nop 0x00000200, 0xe0020827, // mov r0, 0x200 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x159c0fc0, 0x100202e7, // mov ra_vpm_lo, rb_vpm 0x159c1fc0, 0x10020327, // mov ra_vpm_hi, rb_vpm_16 0x80904000, 0xe00203a7, // mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) 0x80905000, 0xe00213a7, // mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) 0x00000015, 0xe00212e7, // mov rb_STAGES, STAGES 0x000000f0, 0xe0021327, // mov rb_0xF0, 0xF0 0x00000040, 0xe0021367, // mov rb_0x40, 0x40 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000003, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000005, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000004, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff8b0, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x00007fff, 0xe0020827, // mov r0, 0x7FFF 0x141e7c00, 0x100229e7, // and.setf -, ra_points, r0 0xfffff880, 0xf01809e7, // brr.allnz -, r:pass_2 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100601e7, // add.ifnz ra_points, ra_points, r0 0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x025e7c80, 0x10020827, // fsub r0, a, b 0x025e7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x025e7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01627380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x025e7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020627, // fadd a+1, r0, r1 0x029d7ec0, 0x10020827, // fsub r0, a, b 0x029d71c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d7e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d83c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d7e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021627, // fadd a+1, r0, r1 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02567c80, 0x10020827, // fsub r0, a, b 0x02567180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02567c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x015a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02567c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100205a7, // fadd a+1, r0, r1 0x029d5ec0, 0x10020827, // fsub r0, a, b 0x029d51c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d5e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d63c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d5e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100215a7, // fadd a+1, r0, r1 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1cbdc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xfffff5f0, 0xf00809e7, // brr.allz -, r:pass_2 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000003, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000007, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000006, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff380, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0xfffff360, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0xfffff340, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0xfffff320, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x025e7c80, 0x10020827, // fsub r0, a, b 0x025e7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x025e7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01627380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x025e7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020627, // fadd a+1, r0, r1 0x029d7ec0, 0x10020827, // fsub r0, a, b 0x029d71c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d7e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d83c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d7e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021627, // fadd a+1, r0, r1 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02567c80, 0x10020827, // fsub r0, a, b 0x02567180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02567c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x015a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02567c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100205a7, // fadd a+1, r0, r1 0x029d5ec0, 0x10020827, // fsub r0, a, b 0x029d51c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d5e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d63c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d5e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100215a7, // fadd a+1, r0, r1 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1cbdc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0x00000100, 0xe0020827, // mov r0, 0x100 0xfffff088, 0xf00809e7, // brr.allz -, r:pass_3 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x00000060, 0xe0020827, // mov r0, (4-1)*4*8 0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000009, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000008, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xffffee18, 0xf0f80227, // brr ra_link_1, r:pass_4 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x025e7c80, 0x10020827, // fsub r0, a, b 0x025e7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x025e7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01627380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x025e7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020627, // fadd a+1, r0, r1 0x029d7ec0, 0x10020827, // fsub r0, a, b 0x029d71c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d7e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d83c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d7e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021627, // fadd a+1, r0, r1 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02567c80, 0x10020827, // fsub r0, a, b 0x02567180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02567c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x015a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02567c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100205a7, // fadd a+1, r0, r1 0x029d5ec0, 0x10020827, // fsub r0, a, b 0x029d51c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d5e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d63c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d5e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100215a7, // fadd a+1, r0, r1 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1cbdc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xffffeb88, 0xf00809e7, // brr.allz -, r:pass_4 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0xffffec58, 0xf0f809e7, // brr -, r:loop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c3fc0, 0x100209a7, // mov interrupt, flag 0x009e7000, 0x300009e7, // nop; nop; thrend 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_256.hex000066400000000000000000000444201421703157200274170ustar00rootroot000000000000000x00000040, 0xe00217a7, // mov rb_0x40, 0x40 0x00000080, 0xe00217e7, // mov rb_0x80, 0x80 0x00005555, 0xe0020767, // mov rx_0x5555, 0x5555 0x00003333, 0xe00207a7, // mov rx_0x3333, 0x3333 0x00000f0f, 0xe00207e7, // mov rx_0x0F0F, 0x0F0F 0x88104000, 0xe0020727, // mov ra_vdw, vdw_setup_0(16, 16, dma_h32( 0,0)) 0x88104800, 0xe0021727, // mov rb_vdw, vdw_setup_0(16, 16, dma_h32(16,0)) 0x15827d80, 0x10020227, // mov rx_tw_shared, unif 0x15827d80, 0x10021227, // mov rx_tw_unique, unif 0x15827d80, 0x10021167, // mov rb_inst, unif 0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) 0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) 0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) 0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst 0xcc9e7081, 0x100246e0, // add out_0, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100256e0, // add out_1, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100049e0, // add out_2, r0, r2; v8adds r0, r0, r1 0x0c9e7080, 0x100009e7, // add out_3, r0, r2 0x000000b0, 0xf0f80127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x156e7d80, 0x10021c67, // mov vw_setup, arg 0x159e7000, 0x10020c27, // mov vpm, r0 0x159e7240, 0x10020c27, // mov vpm, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000000, 0xf0f409e7, // bra -, ra_link_1 0x15727d80, 0x10021c67, // mov vw_setup, arg_vdw 0xc0000040, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS16_STRIDE-16*4 0x8c05edf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr 0x00000038, 0xf0f81127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x156e7d80, 0x10021c67, // mov vw_setup, arg 0x159e7000, 0x10020c27, // mov vpm, r0 0x159e7240, 0x10020c27, // mov vpm, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x156e7d80, 0x10020c67, // mov vr_setup, arg_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000080, 0xf0f801a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000000, 0xf0f409e7, // bra -, ra_link_1 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000248, 0xf0f811a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f409e7, // bra -, ra_link_1 0x00000009, 0xe80009e7, // mov -, srel(i+9) 0x00000011, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f409e7, // bra -, ra_link_1 0x0000000a, 0xe80009e7, // mov -, srel(i+9) 0x00000012, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f409e7, // bra -, ra_link_1 0x0000000b, 0xe80009e7, // mov -, srel(i+9) 0x00000013, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f409e7, // bra -, ra_link_1 0x0000000c, 0xe80009e7, // mov -, srel(i+9) 0x00000014, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f409e7, // bra -, ra_link_1 0x0000000d, 0xe80009e7, // mov -, srel(i+9) 0x00000015, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f409e7, // bra -, ra_link_1 0x0000000e, 0xe80009e7, // mov -, srel(i+9) 0x00000016, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f409e7, // bra -, ra_link_1 0x0000000f, 0xe80009e7, // mov -, srel(i+9) 0x00000017, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<>i) 0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c41c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffd50, 0xf0f80027, // brr ra_link_1, r:pass_1 0x009e7000, 0x100009e7, // nop 0x956dbff6, 0x100246db, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm 0x9571cff6, 0x1002471c, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw 0xfffffd30, 0xf0f80027, // brr ra_link_1, r:pass_1 0x009e7000, 0x100009e7, // nop 0x956dbff6, 0x100246db, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm 0x9571cff6, 0x1002471c, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw 0x00000000, 0xf0f4c027, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9c8e00, 0x10020e27, // add t0s, ptr, r0 0x0c9c8e40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c227c00, 0x10020e27, // add t0s, ptr, r0 0x0c227c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020267, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021267, // mov rb_tw_im+dst, r4 0x00000000, 0xe002438e, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c41c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffb50, 0xf0f80027, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x956dbff6, 0x100246db, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm 0x9571cff6, 0x1002471c, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw 0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20267016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209c9017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209c901f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2126709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02367c80, 0x10020827, // fsub r0, a, b 0x02367180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02367c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x013a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x1002588d, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02367c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100203a7, // fadd a+1, r0, r1 0x029cdec0, 0x10020827, // fsub r0, a, b 0x029cd1c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029cde40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019ce3c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x1002488d, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029cde80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100213a7, // fadd a+1, r0, r1 0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0xfffff9c8, 0xf0f80027, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x956dbff6, 0x100246db, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm 0x9571cff6, 0x1002471c, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw 0x00000000, 0xf0f4c027, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0xfffff9d0, 0xf0f809e7, // brr -, r:loop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c3fc0, 0x100209a7, // mov interrupt, flag 0x009e7000, 0x300009e7, // nop; nop; thrend 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_256k.hex000066400000000000000000001270501421703157200275730ustar00rootroot000000000000000x00000012, 0xe0021227, // mov rb_STAGES, STAGES 0x00000010, 0xe00216a7, // mov rb_0x10, 0x10 0x00000040, 0xe00216e7, // mov rb_0x40, 0x40 0x00000080, 0xe0021727, // mov rb_0x80, 0x80 0x000000f0, 0xe0021767, // mov rb_0xF0, 0xF0 0x00000100, 0xe00217a7, // mov rb_0x100, 0x100 0x00001fff, 0xe00217e7, // mov rb_0x1FFF, 0x1FFF 0x55555555, 0xe0020767, // mov rx_0x55555555, 0x55555555 0x33333333, 0xe00207a7, // mov rx_0x33333333, 0x33333333 0x0f0f0f0f, 0xe00207e7, // mov rx_0x0F0F0F0F, 0x0F0F0F0F 0x00ff00ff, 0xe0021627, // mov rx_0x00FF00FF, 0x00FF00FF 0x0000ffff, 0xe0021667, // mov rx_0x0000FFFF, 0x0000FFFF 0x80904000, 0xe00206e7, // mov ra_vdw_16, vdw_setup_0( 1, 16, dma_h32( 0,0)) 0x80905000, 0xe0021027, // mov rb_vdw_16, vdw_setup_0( 1, 16, dma_h32(32,0)) 0x90104000, 0xe0020727, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) 0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) 0x15827d80, 0x100202e7, // mov rx_tw_shared, unif 0x15827d80, 0x100212e7, // mov rx_tw_unique, unif 0x15827d80, 0x10021167, // mov rb_inst, unif 0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) 0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) 0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) 0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst 0xcc9e7081, 0x10024660, // add out_0, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100246a0, // add out_1, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 0x0c9e7080, 0x100211e7, // add out_3, r0, r2 0x000001d0, 0xf0f80127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, arg 0x159e7000, 0x10020c27, // mov vpm, r0 0x159e7240, 0x10020c27, // mov vpm, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x156e7d80, 0x10020827, // mov r0, arg_vdw 0x8c05bdf6, 0x10024061, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr 0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) 0x00020000, 0xe00208e7, // mov r3, PASS16_STRIDE 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000038, 0xf0f81127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, arg 0x159e7000, 0x10020c27, // mov vpm, r0 0x159e7240, 0x10020c27, // mov vpm, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10020c67, // mov vr_setup, arg_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x000000c8, 0xf0f802a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x15727d80, 0x10021c67, // mov vw_setup, ra_vdw_32 0xc000ffc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 0x8c05bdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr 0x00000050, 0xf0f812a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10020c67, // mov vr_setup, ra_vpm_lo 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000080, 0xf0f801a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000640, 0xf0f811a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000009, 0xe80009e7, // mov -, srel(i+9) 0x00000011, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000a, 0xe80009e7, // mov -, srel(i+9) 0x00000012, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000b, 0xe80009e7, // mov -, srel(i+9) 0x00000013, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000c, 0xe80009e7, // mov -, srel(i+9) 0x00000014, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000d, 0xe80009e7, // mov -, srel(i+9) 0x00000015, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000e, 0xe80009e7, // mov -, srel(i+9) 0x00000016, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000f, 0xe80009e7, // mov -, srel(i+9) 0x00000017, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffd78, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c15adf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149d81c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149d81c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149d91c0, 0x10020867, // and r1, r0, mask 0x0e9da1c0, 0x10020827, // shr r0, r0, shift 0x149d91c0, 0x10020827, // and r0, r0, mask 0x119da3c0, 0x10020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9cb1c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffc30, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f549e7, // bra -, ra_save_32 0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffba0, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x00000000, 0xf0f489e7, // bra -, ra_save_16 0x009e7000, 0x100009e7, // nop 0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x956c0ff6, 0x100246c0, // mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffb38, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c15adf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffae8, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f549e7, // bra -, ra_save_32 0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst 0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 0x119c51c0, 0xd0020827, // shl r0, r0, 5 0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif 0x15827d80, 0x100210e7, // mov rb_addr_y, unif 0x00000ef0, 0xf00809e7, // brr.allz -, r:end 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15adf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149d81c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149d81c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149d91c0, 0x10020867, // and r1, r0, mask 0x0e9da1c0, 0x10020827, // shr r0, r0, shift 0x149d91c0, 0x10020827, // and r0, r0, mask 0x119da3c0, 0x10020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9cb1c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff928, 0xf0f80227, // brr ra_link_1, r:pass_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xfffff900, 0xf00809e7, // brr.allz -, r:pass_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c41c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffa10, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x141dfdc0, 0x100229e7, // and.setf -, ra_points, rb_0x1FFF 0xfffff9e8, 0xf01809e7, // brr.allnz -, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dcdc0, 0x100601e7, // add.ifnz ra_points, ra_points, rb_0x80 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02467c80, 0x10020827, // fsub r0, a, b 0x02467180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02467c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02467c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204a7, // fadd a+1, r0, r1 0x029d1ec0, 0x10020827, // fsub r0, a, b 0x029d11c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d1e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d23c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d1e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214a7, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xfffff858, 0xf00809e7, // brr.allz -, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000003, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c41c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff698, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0xfffff678, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0xfffff658, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0xfffff638, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02467c80, 0x10020827, // fsub r0, a, b 0x02467180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02467c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02467c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204a7, // fadd a+1, r0, r1 0x029d1ec0, 0x10020827, // fsub r0, a, b 0x029d11c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d1e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d23c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d1e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214a7, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xfffff4a8, 0xf00809e7, // brr.allz -, r:pass_3 0x00000060, 0xe0020827, // mov r0, 3*4*8 0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000005, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000004, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15adf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff2a0, 0xf0f80227, // brr ra_link_1, r:pass_4 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x024e7c80, 0x10020827, // fsub r0, a, b 0x024e7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x024e7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01527380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x024e7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020527, // fadd a+1, r0, r1 0x029d3ec0, 0x10020827, // fsub r0, a, b 0x029d31c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d3e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d43c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d3e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021527, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02467c80, 0x10020827, // fsub r0, a, b 0x02467180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02467c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02467c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204a7, // fadd a+1, r0, r1 0x029d1ec0, 0x10020827, // fsub r0, a, b 0x029d11c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d1e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d23c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d1e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214a7, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xfffff010, 0xf00809e7, // brr.allz -, r:pass_4 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0xfffff0e0, 0xf0f809e7, // brr -, r:loop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c3fc0, 0x100209a7, // mov interrupt, flag 0x009e7000, 0x300009e7, // nop; nop; thrend 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_2k.hex000066400000000000000000001127501421703157200274210ustar00rootroot000000000000000x00000010, 0xe0021727, // mov rb_0x10, 0x10 0x00000040, 0xe0021767, // mov rb_0x40, 0x40 0x000000f0, 0xe00217a7, // mov rb_0xF0, 0xF0 0x000001d0, 0xe00217e7, // mov rb_0x1D0, 0x1D0 0x00005555, 0xe0020727, // mov rx_0x5555, 0x5555 0x00003333, 0xe0020767, // mov rx_0x3333, 0x3333 0x00000f0f, 0xe00207a7, // mov rx_0x0F0F, 0x0F0F 0x000000ff, 0xe00207e7, // mov rx_0x00FF, 0x00FF 0x15827d80, 0x100203e7, // mov rx_tw_shared, unif 0x15827d80, 0x100213e7, // mov rx_tw_unique, unif 0x15827d80, 0x10021167, // mov rb_inst, unif 0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) 0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) 0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) 0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst 0xcc9e7081, 0x10025020, // add out_0, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x10025060, // add out_1, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 0x0c9e7080, 0x100211e7, // add out_3, r0, r2 0x000000c8, 0xf0f80127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x15367d80, 0x10021c67, // mov vw_setup, ra_vdw_32 0xc00001c0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 0x8c05ddf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr 0x00000050, 0xf0f81127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x152e7d80, 0x10020c67, // mov vr_setup, ra_vpm_lo 0x15c27d80, 0x100009e7, // mov -, vpm 0x000000f8, 0xf0f802a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm 0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0xa0104000, 0xe0021c67, // mov vw_setup, vdw_setup_0(64, 16, dma_h32(0,0)) 0xc00000c0, 0xe0021c67, // mov vw_setup, vdw_setup_1(PASS64_STRIDE-16*4) 0x8c05ddf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, step; mov vw_addr, ra_save_ptr 0x000002b8, 0xf0f812a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd00200a7, // shl ra_temp, r0, 5 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0fc49e7, // brr -, ra_temp 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x000000e0, 0xf0f809e7, // brr -, r:2f 0x00000010, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x000000c0, 0xf0f809e7, // brr -, r:2f 0x00000011, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x000000a0, 0xf0f809e7, // brr -, r:2f 0x00000012, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000080, 0xf0f809e7, // brr -, r:2f 0x00000013, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000060, 0xf0f809e7, // brr -, r:2f 0x00000014, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000040, 0xf0f809e7, // brr -, r:2f 0x00000015, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000020, 0xf0f809e7, // brr -, r:2f 0x00000016, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f809e7, // brr -, r:2f 0x00000017, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm 0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 0x00000000, 0xf0fc49e7, // brr -, ra_temp 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000008, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000009, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000a, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000b, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000c, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000d, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000e, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000f, 0xe80009e7, // mov -, srel(i+8) 0x00000080, 0xf0f801a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000858, 0xf0f811a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000009, 0xe80009e7, // mov -, srel(i+9) 0x00000011, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000a, 0xe80009e7, // mov -, srel(i+9) 0x00000012, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000b, 0xe80009e7, // mov -, srel(i+9) 0x00000013, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000c, 0xe80009e7, // mov -, srel(i+9) 0x00000014, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000d, 0xe80009e7, // mov -, srel(i+9) 0x00000015, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000e, 0xe80009e7, // mov -, srel(i+9) 0x00000016, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000f, 0xe80009e7, // mov -, srel(i+9) 0x00000017, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffda0, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14727180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14727180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c21c0, 0xd0020827, // shr r0, r0, 13-STAGES 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffc80, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x01267c00, 0x100202e7, // fadd ra_64+0, ra_32_re, r0 0x019c9e40, 0x10020327, // fadd ra_64+1, rb_32_im, r1 0x02267c00, 0x10020367, // fsub ra_64+2, ra_32_re, r0 0x029c9e40, 0x100203a7, // fsub ra_64+3, rb_32_im, r1 0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14727180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14727180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c21c0, 0xd0020827, // shr r0, r0, 13-STAGES 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffb20, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14727180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14727180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c21c0, 0xd0020827, // shr r0, r0, 13-STAGES 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffa00, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x029c9e40, 0x100208e7, // fsub r3, rb_32_im, r1 0x02267c00, 0x100208a7, // fsub r2, ra_32_re, r0 0x019c9e40, 0x10020867, // fadd r1, rb_32_im, r1 0x01267c00, 0x10020827, // fadd r0, ra_32_re, r0 0x2066700e, 0x100049c9, // nop; fmul rb_32_im, r1, ra_tw_re+TW48 0x209d900f, 0x100059c9, // nop; fmul ra_32_re, r1, rb_tw_im+TW48 0x209d9007, 0x100049e1, // nop; fmul r1, r0, rb_tw_im+TW48 0x216493c6, 0x10025320, // fadd rb_64+1, r1, rb_32_im; fmul r0, r0, ra_tw_re+TW48 0x2225a19f, 0x100252c9, // fsub rb_64+0, r0, ra_32_re; fmul ra_32_re, r3, rb_tw_im+TW64 0x206a701e, 0x100049c9, // nop; fmul rb_32_im, r3, ra_tw_re+TW64 0x00000000, 0xf0f549e7, // bra -, ra_save_64 0x209da017, 0x100049e3, // nop; fmul r3, r2, rb_tw_im+TW64 0x216897d6, 0x100253a2, // fadd rb_64+3, r3, rb_32_im; fmul r2, r2, ra_tw_re+TW64 0x02267580, 0x10021367, // fsub rb_64+2, r2, ra_32_re 0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff920, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff8d0, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f489e7, // bra -, ra_save_32 0x952c2ff6, 0x100242c2, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x95307ff6, 0x10024307, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x9534dff6, 0x1002434d, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst 0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 0x119c51c0, 0xd0020827, // shl r0, r0, 5 0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 0x159c4fc0, 0x10060127, // mov.ifnz ra_save_32, rx_save_slave_32 0x159cafc0, 0x100602a7, // mov.ifnz ra_save_64, rx_save_slave_64 0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif 0x15827d80, 0x100210e7, // mov rb_addr_y, unif 0x00000870, 0xf00809e7, // brr.allz -, r:end 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000003, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020667, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021667, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100206a7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100216a7, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c61c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14727180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14727180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c21c0, 0xd0020827, // shr r0, r0, 13-STAGES 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff688, 0xf0f80227, // brr ra_link_1, r:pass_1 0x009e7000, 0x100009e7, // nop 0x00000200, 0xe0020827, // mov r0, 0x200 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x0e1cbdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES 0xfffff660, 0xf00809e7, // brr.allz -, r:pass_1 0x009e7000, 0x100009e7, // nop 0x00000200, 0xe0020827, // mov r0, 0x200 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x159c0fc0, 0x100202e7, // mov ra_vpm_lo, rb_vpm 0x159c1fc0, 0x10020327, // mov ra_vpm_hi, rb_vpm_16 0x90104000, 0xe0020367, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) 0x90105000, 0xe0021367, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000005, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000004, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff920, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x025e7c80, 0x10020827, // fsub r0, a, b 0x025e7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x025e7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01627380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x025e7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020627, // fadd a+1, r0, r1 0x029d7ec0, 0x10020827, // fsub r0, a, b 0x029d71c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d7e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d83c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d7e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021627, // fadd a+1, r0, r1 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02567c80, 0x10020827, // fsub r0, a, b 0x02567180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02567c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x015a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02567c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100205a7, // fadd a+1, r0, r1 0x029d5ec0, 0x10020827, // fsub r0, a, b 0x029d51c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d5e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d63c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d5e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100215a7, // fadd a+1, r0, r1 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1cbdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES 0xfffff690, 0xf00809e7, // brr.allz -, r:pass_2 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0xfffff760, 0xf0f809e7, // brr -, r:loop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c3fc0, 0x100209a7, // mov interrupt, flag 0x009e7000, 0x300009e7, // nop; nop; thrend 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_32k.hex000066400000000000000000001056511421703157200275060ustar00rootroot000000000000000x00000010, 0xe00216e7, // mov rb_0x10, 0x10 0x00000040, 0xe0021727, // mov rb_0x40, 0x40 0x00000080, 0xe0021767, // mov rb_0x80, 0x80 0x000000f0, 0xe00217a7, // mov rb_0xF0, 0xF0 0x00000100, 0xe00217e7, // mov rb_0x100, 0x100 0x00005555, 0xe0020767, // mov rx_0x5555, 0x5555 0x00003333, 0xe00207a7, // mov rx_0x3333, 0x3333 0x00000f0f, 0xe00207e7, // mov rx_0x0F0F, 0x0F0F 0x000000ff, 0xe00216a7, // mov rx_0x00FF, 0x00FF 0x90104000, 0xe0020727, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) 0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) 0x15827d80, 0x100202a7, // mov rx_tw_shared, unif 0x15827d80, 0x100212a7, // mov rx_tw_unique, unif 0x15827d80, 0x10021167, // mov rb_inst, unif 0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) 0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) 0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) 0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst 0xcc9e7081, 0x100246a0, // add out_0, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100246e0, // add out_1, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 0x0c9e7080, 0x100211e7, // add out_3, r0, r2 0x000000c8, 0xf0f80127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x156e7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x15727d80, 0x10021c67, // mov vw_setup, ra_vdw_32 0xc0001fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 0x8c05cdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr 0x00000050, 0xf0f81127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x156e7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x156a7d80, 0x10020c67, // mov vr_setup, ra_vpm_lo 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000080, 0xf0f801a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000588, 0xf0f811a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000009, 0xe80009e7, // mov -, srel(i+9) 0x00000011, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000a, 0xe80009e7, // mov -, srel(i+9) 0x00000012, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000b, 0xe80009e7, // mov -, srel(i+9) 0x00000013, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000c, 0xe80009e7, // mov -, srel(i+9) 0x00000014, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000d, 0xe80009e7, // mov -, srel(i+9) 0x00000015, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000e, 0xe80009e7, // mov -, srel(i+9) 0x00000016, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000f, 0xe80009e7, // mov -, srel(i+9) 0x00000017, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffda0, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149da1c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149da1c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x119c21c0, 0xd0020827, // shl r0, r0, STAGES-13 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffc80, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x204a7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d200f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x204a700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22092c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f489e7, // bra -, ra_save_32 0x95682ff6, 0x10024682, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x956c7ff6, 0x100246c7, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffbf0, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffba0, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x204a7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d200f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x204a700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22092c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f489e7, // bra -, ra_save_32 0x95682ff6, 0x10024682, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x956c7ff6, 0x100246c7, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst 0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 0x119c51c0, 0xd0020827, // shl r0, r0, 5 0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 0x159c4fc0, 0x10060127, // mov.ifnz ra_save_32, rx_save_slave_32 0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif 0x15827d80, 0x100210e7, // mov rb_addr_y, unif 0x00000d00, 0xf00809e7, // brr.allz -, r:end 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204a7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214a7, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024451, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe00244d3, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149da1c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149da1c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x119c21c0, 0xd0020827, // shl r0, r0, STAGES-13 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffa10, 0xf0f80227, // brr ra_link_1, r:pass_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x0e1cfdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES 0xfffff9e8, 0xf00809e7, // brr.allz -, r:pass_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204a7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214a7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000003, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100202e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100212e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024451, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe00244d3, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff9f8, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0xfffff9d8, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0xfffff9b8, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0xfffff998, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x95492dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x024a7c80, 0x10020827, // fsub r0, a, b 0x024a7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x024a7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014e7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025892, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x024a7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204e7, // fadd a+1, r0, r1 0x029d2ec0, 0x10020827, // fsub r0, a, b 0x029d21c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d2e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d33c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024892, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d2e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214e7, // fadd a+1, r0, r1 0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x202e7016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cb017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cb01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x212e709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02427c80, 0x10020827, // fsub r0, a, b 0x02427180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02427c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01467380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025890, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02427c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020467, // fadd a+1, r0, r1 0x029d0ec0, 0x10020827, // fsub r0, a, b 0x029d01c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d0e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d13c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024890, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d0e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021467, // fadd a+1, r0, r1 0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1cfdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES 0xfffff708, 0xf00809e7, // brr.allz -, r:pass_2 0x00000060, 0xe0020827, // mov r0, 3*4*8 0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cae00, 0x10020e27, // add t0s, ptr, r0 0x0c9cae40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cae00, 0x10020e27, // add t0s, ptr, r0 0x0c9cae40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204a7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214a7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000005, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100202e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100212e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000004, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024451, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe00244d3, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff498, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x95492dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x024a7c80, 0x10020827, // fsub r0, a, b 0x024a7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x024a7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014e7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025892, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x024a7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204e7, // fadd a+1, r0, r1 0x029d2ec0, 0x10020827, // fsub r0, a, b 0x029d21c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d2e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d33c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024892, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d2e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214e7, // fadd a+1, r0, r1 0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x202e7016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cb017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cb01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x212e709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02427c80, 0x10020827, // fsub r0, a, b 0x02427180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02427c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01467380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025890, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02427c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020467, // fadd a+1, r0, r1 0x029d0ec0, 0x10020827, // fsub r0, a, b 0x029d01c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d0e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d13c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024890, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d0e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021467, // fadd a+1, r0, r1 0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1cfdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES 0xfffff208, 0xf00809e7, // brr.allz -, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0xfffff2d0, 0xf0f809e7, // brr -, r:loop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c3fc0, 0x100209a7, // mov interrupt, flag 0x009e7000, 0x300009e7, // nop; nop; thrend 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_4096k.hex000066400000000000000000002300401421703157200276530ustar00rootroot000000000000000x00000010, 0xe0021227, // mov rb_0x10, 0x10 0x000001d0, 0xe0021967, // mov r5rep, 0x1D0 0x15827d80, 0x100203e7, // mov rx_tw_shared, unif 0x15827d80, 0x100213e7, // mov rx_tw_unique, unif 0x15827d80, 0x10021167, // mov rb_inst, unif 0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) 0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) 0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) 0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst 0xcc9e7081, 0x10025020, // add out_0, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x10025060, // add out_1, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 0x0c9e7080, 0x100211e7, // add out_3, r0, r2 0x000002e8, 0xf0f80127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x153a7d80, 0x10020827, // mov r0, ra_vdw_32 0x8c04ddf6, 0x10024061, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr 0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) 0x00100000, 0xe00208e7, // mov r3, PASS32_STRIDE 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000050, 0xf0f81127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x152e7d80, 0x10020c67, // mov vr_setup, ra_vpm_lo 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000520, 0xf0f802a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm 0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x80904000, 0xe0020827, // mov r0, vdw_setup_0(1, 16, dma_h32(0,0)) 0x00000040, 0xe0020867, // mov r1, 0x40 0x8c067c76, 0x10024061, // add ra_save_ptr, ra_save_ptr, r1; mov r1, ra_save_ptr 0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) 0x00080000, 0xe00208e7, // mov r3, PASS64_STRIDE 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x000002b8, 0xf0f812a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd00200a7, // shl ra_temp, r0, 5 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0fc49e7, // brr -, ra_temp 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x000000e0, 0xf0f809e7, // brr -, r:2f 0x00000010, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x000000c0, 0xf0f809e7, // brr -, r:2f 0x00000011, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x000000a0, 0xf0f809e7, // brr -, r:2f 0x00000012, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000080, 0xf0f809e7, // brr -, r:2f 0x00000013, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000060, 0xf0f809e7, // brr -, r:2f 0x00000014, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000040, 0xf0f809e7, // brr -, r:2f 0x00000015, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000020, 0xf0f809e7, // brr -, r:2f 0x00000016, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f809e7, // brr -, r:2f 0x00000017, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm 0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 0x00000000, 0xf0fc49e7, // brr -, ra_temp 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000008, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000009, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000a, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000b, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000c, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000d, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000e, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000f, 0xe80009e7, // mov -, srel(i+8) 0x00000080, 0xf0f801a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000ba8, 0xf0f811a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000009, 0xe80009e7, // mov -, srel(i+9) 0x00000011, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000a, 0xe80009e7, // mov -, srel(i+9) 0x00000012, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000b, 0xe80009e7, // mov -, srel(i+9) 0x00000013, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000c, 0xe80009e7, // mov -, srel(i+9) 0x00000014, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000d, 0xe80009e7, // mov -, srel(i+9) 0x00000015, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000e, 0xe80009e7, // mov -, srel(i+9) 0x00000016, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000f, 0xe80009e7, // mov -, srel(i+9) 0x00000017, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffd50, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x55555555, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x33333333, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0f0f0f0f, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x00ff00ff, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0000ffff, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c81c0, 0x10020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c83c0, 0x10020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c71c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffbe0, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x01267c00, 0x100202e7, // fadd ra_64+0, ra_32_re, r0 0x019c9e40, 0x10020327, // fadd ra_64+1, rb_32_im, r1 0x02267c00, 0x10020367, // fsub ra_64+2, ra_32_re, r0 0x029c9e40, 0x100203a7, // fsub ra_64+3, rb_32_im, r1 0x8c167d76, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x55555555, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x33333333, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0f0f0f0f, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x00ff00ff, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0000ffff, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c81c0, 0x10020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c83c0, 0x10020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c71c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffa30, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x55555555, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x33333333, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0f0f0f0f, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x00ff00ff, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0000ffff, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c81c0, 0x10020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c83c0, 0x10020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c71c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffff8c0, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x029c9e40, 0x100208e7, // fsub r3, rb_32_im, r1 0x02267c00, 0x100208a7, // fsub r2, ra_32_re, r0 0x019c9e40, 0x10020867, // fadd r1, rb_32_im, r1 0x01267c00, 0x10020827, // fadd r0, ra_32_re, r0 0x2066700e, 0x100049c9, // nop; fmul rb_32_im, r1, ra_tw_re+TW48 0x209d900f, 0x100059c9, // nop; fmul ra_32_re, r1, rb_tw_im+TW48 0x209d9007, 0x100049e1, // nop; fmul r1, r0, rb_tw_im+TW48 0x216493c6, 0x10025320, // fadd rb_64+1, r1, rb_32_im; fmul r0, r0, ra_tw_re+TW48 0x2225b19f, 0x100252c9, // fsub rb_64+0, r0, ra_32_re; fmul ra_32_re, r3, rb_tw_im+TW64 0x206e701e, 0x100049c9, // nop; fmul rb_32_im, r3, ra_tw_re+TW64 0x00000000, 0xf0f549e7, // bra -, ra_save_64 0x209db017, 0x100049e3, // nop; fmul r3, r2, rb_tw_im+TW64 0x216c97d6, 0x100253a2, // fadd rb_64+3, r3, rb_32_im; fmul r2, r2, ra_tw_re+TW64 0x02267580, 0x10021367, // fsub rb_64+2, r2, ra_32_re 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff7e0, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff790, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x01267c00, 0x100202e7, // fadd ra_64+0, ra_32_re, r0 0x019c9e40, 0x10020327, // fadd ra_64+1, rb_32_im, r1 0x02267c00, 0x10020367, // fsub ra_64+2, ra_32_re, r0 0x029c9e40, 0x100203a7, // fsub ra_64+3, rb_32_im, r1 0x8c167d76, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff700, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff6b0, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x029c9e40, 0x100208e7, // fsub r3, rb_32_im, r1 0x02267c00, 0x100208a7, // fsub r2, ra_32_re, r0 0x019c9e40, 0x10020867, // fadd r1, rb_32_im, r1 0x01267c00, 0x10020827, // fadd r0, ra_32_re, r0 0x2066700e, 0x100049c9, // nop; fmul rb_32_im, r1, ra_tw_re+TW48 0x209d900f, 0x100059c9, // nop; fmul ra_32_re, r1, rb_tw_im+TW48 0x209d9007, 0x100049e1, // nop; fmul r1, r0, rb_tw_im+TW48 0x216493c6, 0x10025320, // fadd rb_64+1, r1, rb_32_im; fmul r0, r0, ra_tw_re+TW48 0x2225b19f, 0x100252c9, // fsub rb_64+0, r0, ra_32_re; fmul ra_32_re, r3, rb_tw_im+TW64 0x206e701e, 0x100049c9, // nop; fmul rb_32_im, r3, ra_tw_re+TW64 0x00000000, 0xf0f549e7, // bra -, ra_save_64 0x209db017, 0x100049e3, // nop; fmul r3, r2, rb_tw_im+TW64 0x216c97d6, 0x100253a2, // fadd rb_64+3, r3, rb_32_im; fmul r2, r2, ra_tw_re+TW64 0x02267580, 0x10021367, // fsub rb_64+2, r2, ra_32_re 0x8c14cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff5d0, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff580, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f489e7, // bra -, ra_save_32 0x952c2ff6, 0x100242c2, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x95307ff6, 0x10024307, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x9538eff6, 0x1002438e, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst 0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 0x119c51c0, 0xd0020827, // shl r0, r0, 5 0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 0x159c4fc0, 0x10060127, // mov.ifnz ra_save_32, rx_save_slave_32 0x159cafc0, 0x100602a7, // mov.ifnz ra_save_64, rx_save_slave_64 0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif 0x15827d80, 0x100210e7, // mov rb_addr_y, unif 0x000016b8, 0xf00809e7, // brr.allz -, r:end 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000003, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020667, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021667, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100206e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100216e7, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c61c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x55555555, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x33333333, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0f0f0f0f, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x00ff00ff, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0000ffff, 0xe00208a7, // mov r2, mask 0x149e7080, 0x10020867, // and r1, r0, r2 0x0e9c81c0, 0x10020827, // shr r0, r0, shift 0x149e7080, 0x10020827, // and r0, r0, r2 0x119c83c0, 0x10020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c71c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff2e8, 0xf0f80227, // brr ra_link_1, r:pass_1 0x009e7000, 0x100009e7, // nop 0x00000200, 0xe0020827, // mov r0, 0x200 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x00000016, 0xe0020867, // mov r1, STAGES 0x0e1e7c40, 0x100229e7, // shr.setf -, ra_points, r1 0xfffff2b8, 0xf00809e7, // brr.allz -, r:pass_1 0x009e7000, 0x100009e7, // nop 0x00000200, 0xe0020827, // mov r0, 0x200 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000003, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020667, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021667, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100206e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100216e7, // mov rb_tw_im+dst, r4 0x00000000, 0xe002469a, // mov ra_tw_re+TW48+1, 0; mov rb_tw_im+TW48+1, 0 0x00000000, 0xe002471c, // mov ra_tw_re+TW64+1, 0; mov rb_tw_im+TW64+1, 0 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000007, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000006, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000004, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020767, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021767, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000005, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100207a7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100217a7, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c61c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff568, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x00000200, 0xe0020827, // mov r0, 0x200 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x0000ffff, 0xe0020827, // mov r0, 0xFFFF 0x141e7c00, 0x100229e7, // and.setf -, ra_points, r0 0xfffff538, 0xf01809e7, // brr.allnz -, r:pass_2 0x009e7000, 0x100009e7, // nop 0x00000200, 0xe0020827, // mov r0, 0x200 0x0c1e7c00, 0x100601e7, // add.ifnz ra_points, ra_points, r0 0x956dbdbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x207a7016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209de017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209de01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x217a709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x026e7c80, 0x10020827, // fsub r0, a, b 0x026e7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x026e7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01727380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x1002589b, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x026e7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020727, // fadd a+1, r0, r1 0x029dbec0, 0x10020827, // fsub r0, a, b 0x029db1c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029dbe40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019dc3c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x1002489b, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029dbe80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021727, // fadd a+1, r0, r1 0x95659dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20767016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209dd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209dd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2176709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02667c80, 0x10020827, // fsub r0, a, b 0x02667180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02667c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x016a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025899, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02667c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100206a7, // fadd a+1, r0, r1 0x029d9ec0, 0x10020827, // fsub r0, a, b 0x029d91c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d9e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019da3c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024899, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d9e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100216a7, // fadd a+1, r0, r1 0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x025e7c80, 0x10020827, // fsub r0, a, b 0x025e7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x025e7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01627380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x025e7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020627, // fadd a+1, r0, r1 0x029d7ec0, 0x10020827, // fsub r0, a, b 0x029d71c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d7e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d83c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d7e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021627, // fadd a+1, r0, r1 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02567c80, 0x10020827, // fsub r0, a, b 0x02567180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02567c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x015a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02567c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100205a7, // fadd a+1, r0, r1 0x029d5ec0, 0x10020827, // fsub r0, a, b 0x029d51c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d5e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d63c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d5e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100215a7, // fadd a+1, r0, r1 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x00000016, 0xe0020867, // mov r1, STAGES 0x0e1e7c40, 0x100229e7, // shr.setf -, ra_points, r1 0xfffff0a0, 0xf00809e7, // brr.allz -, r:pass_2 0x009e7000, 0x100009e7, // nop 0x00000200, 0xe0020827, // mov r0, 0x200 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x159c0fc0, 0x100202e7, // mov ra_vpm_lo, rb_vpm 0x159c1fc0, 0x10020327, // mov ra_vpm_hi, rb_vpm_16 0x80904000, 0xe00203a7, // mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) 0x80905000, 0xe00213a7, // mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) 0x00000016, 0xe00212e7, // mov rb_STAGES, STAGES 0x000000f0, 0xe0021327, // mov rb_0xF0, 0xF0 0x00000040, 0xe0021367, // mov rb_0x40, 0x40 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000003, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000009, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000008, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff008, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x000003ff, 0xe0020827, // mov r0, 0x3FF 0x141e7c00, 0x100229e7, // and.setf -, ra_points, r0 0xffffefd8, 0xf01809e7, // brr.allnz -, r:pass_3 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100601e7, // add.ifnz ra_points, ra_points, r0 0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x025e7c80, 0x10020827, // fsub r0, a, b 0x025e7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x025e7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01627380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x025e7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020627, // fadd a+1, r0, r1 0x029d7ec0, 0x10020827, // fsub r0, a, b 0x029d71c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d7e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d83c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d7e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021627, // fadd a+1, r0, r1 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02567c80, 0x10020827, // fsub r0, a, b 0x02567180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02567c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x015a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02567c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100205a7, // fadd a+1, r0, r1 0x029d5ec0, 0x10020827, // fsub r0, a, b 0x029d51c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d5e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d63c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d5e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100215a7, // fadd a+1, r0, r1 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1cbdc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xffffed48, 0xf00809e7, // brr.allz -, r:pass_3 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x0000000b, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x0000000a, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xffffead8, 0xf0f80227, // brr ra_link_1, r:pass_4 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x025e7c80, 0x10020827, // fsub r0, a, b 0x025e7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x025e7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01627380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x025e7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020627, // fadd a+1, r0, r1 0x029d7ec0, 0x10020827, // fsub r0, a, b 0x029d71c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d7e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d83c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d7e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021627, // fadd a+1, r0, r1 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02567c80, 0x10020827, // fsub r0, a, b 0x02567180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02567c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x015a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02567c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100205a7, // fadd a+1, r0, r1 0x029d5ec0, 0x10020827, // fsub r0, a, b 0x029d51c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d5e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d63c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d5e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100215a7, // fadd a+1, r0, r1 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1cbdc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xffffe848, 0xf00809e7, // brr.allz -, r:pass_4 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0xffffe918, 0xf0f809e7, // brr -, r:loop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c3fc0, 0x100209a7, // mov interrupt, flag 0x009e7000, 0x300009e7, // nop; nop; thrend 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_4k.hex000066400000000000000000000655011421703157200274240ustar00rootroot000000000000000x00000020, 0xe0021767, // mov rb_0x20, 0x20 0x00000040, 0xe00217a7, // mov rb_0x40, 0x40 0x00000080, 0xe00217e7, // mov rb_0x80, 0x80 0x00005555, 0xe0020727, // mov rx_0x5555, 0x5555 0x00003333, 0xe0020767, // mov rx_0x3333, 0x3333 0x00000f0f, 0xe00207a7, // mov rx_0x0F0F, 0x0F0F 0x000000ff, 0xe00207e7, // mov rx_0x00FF, 0x00FF 0x88104000, 0xe00206e7, // mov ra_vdw, vdw_setup_0(16, 16, dma_h32( 0,0)) 0x88104800, 0xe00216e7, // mov rb_vdw, vdw_setup_0(16, 16, dma_h32(16,0)) 0x15827d80, 0x10020227, // mov rx_tw_shared, unif 0x15827d80, 0x10021227, // mov rx_tw_unique, unif 0x15827d80, 0x10021167, // mov rb_inst, unif 0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) 0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) 0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) 0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst 0xcc9e7081, 0x100246a0, // add out_0, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100256a0, // add out_1, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100049e0, // add out_2, r0, r2; v8adds r0, r0, r1 0x0c9e7080, 0x100009e7, // add out_3, r0, r2 0x000000b0, 0xf0f80127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x156a7d80, 0x10021c67, // mov vw_setup, arg 0x159e7000, 0x10020c27, // mov vpm, r0 0x159e7240, 0x10020c27, // mov vpm, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000000, 0xf0f409e7, // bra -, ra_link_1 0x156e7d80, 0x10021c67, // mov vw_setup, arg_vdw 0xc00007c0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS16_STRIDE-16*4 0x8c05edf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr 0x00000038, 0xf0f81127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x156a7d80, 0x10021c67, // mov vw_setup, arg 0x159e7000, 0x10020c27, // mov vpm, r0 0x159e7240, 0x10020c27, // mov vpm, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x156a7d80, 0x10020c67, // mov vr_setup, arg_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000080, 0xf0f801a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000000, 0xf0f409e7, // bra -, ra_link_1 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x000003e8, 0xf0f811a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f409e7, // bra -, ra_link_1 0x00000009, 0xe80009e7, // mov -, srel(i+9) 0x00000011, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f409e7, // bra -, ra_link_1 0x0000000a, 0xe80009e7, // mov -, srel(i+9) 0x00000012, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f409e7, // bra -, ra_link_1 0x0000000b, 0xe80009e7, // mov -, srel(i+9) 0x00000013, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f409e7, // bra -, ra_link_1 0x0000000c, 0xe80009e7, // mov -, srel(i+9) 0x00000014, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f409e7, // bra -, ra_link_1 0x0000000d, 0xe80009e7, // mov -, srel(i+9) 0x00000015, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f409e7, // bra -, ra_link_1 0x0000000e, 0xe80009e7, // mov -, srel(i+9) 0x00000016, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f409e7, // bra -, ra_link_1 0x0000000f, 0xe80009e7, // mov -, srel(i+9) 0x00000017, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x000000cc, 0xe20229e7, // mov.setf -, [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] 0x959fa000, 0xd002c8a0, // mov r2, r0; mov.ifnz r0, r0 << 6 0x959fa249, 0xd002c8e1, // mov r3, r1; mov.ifnz r1, r1 << 6 0x00003300, 0xe20229e7, // mov.setf -, [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0] 0x809f6012, 0xd000c9e0, // nop; mov.ifnz r0, r2 >> 6 0x809f601b, 0xd000c9e1, // nop; mov.ifnz r1, r3 >> 6 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x000000cc, 0xe20229e7, // mov.setf -, [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] 0x959fa000, 0xd002c8a0, // mov r2, r0; mov.ifnz r0, r0 << 6 0x959fa249, 0xd002c8e1, // mov r3, r1; mov.ifnz r1, r1 << 6 0x00003300, 0xe20229e7, // mov.setf -, [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0] 0x809f6012, 0xd000c9e0, // nop; mov.ifnz r0, r2 >> 6 0x809f601b, 0xd000c9e1, // nop; mov.ifnz r1, r3 >> 6 0xfffffd40, 0xf0f809e7, // brr -, r:fft_16 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffcf8, 0xf0f809e7, // brr -, r:fft_16 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst 0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 0x119c51c0, 0xd0020827, // shl r0, r0, 5 0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif 0x15827d80, 0x100210e7, // mov rb_addr_y, unif 0x00000928, 0xf00809e7, // brr.allz -, r:end 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c227c00, 0x10020e27, // add t0s, ptr, r0 0x0c227c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 0x00000000, 0xe002438e, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c41c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14727180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14727180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c11c0, 0xd0020827, // shr r0, r0, 13-STAGES 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x000000cc, 0xe20229e7, // mov.setf -, [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] 0x959fa000, 0xd002c8a0, // mov r2, r0; mov.ifnz r0, r0 << 6 0x959fa249, 0xd002c8e1, // mov r3, r1; mov.ifnz r1, r1 << 6 0x00003300, 0xe20229e7, // mov.setf -, [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0] 0x809f6012, 0xd000c9e0, // nop; mov.ifnz r0, r2 >> 6 0x809f601b, 0xd000c9e1, // nop; mov.ifnz r1, r3 >> 6 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffbe0, 0xf0f80027, // brr ra_link_1, r:pass_1 0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm 0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x0e1ccdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES 0xfffffbb8, 0xf00809e7, // brr.allz -, r:pass_1 0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm 0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x00000000, 0xf0f4c027, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c227c00, 0x10020e27, // add t0s, ptr, r0 0x0c227c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c227c00, 0x10020e27, // add t0s, ptr, r0 0x0c227c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020267, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021267, // mov rb_tw_im+dst, r4 0x00000000, 0xe002438e, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c41c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffb78, 0xf0f80027, // brr ra_link_1, r:pass_2 0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm 0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0xfffffb58, 0xf0f80027, // brr ra_link_1, r:pass_2 0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm 0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x0d01ddc0, 0x10020027, // sub ra_link_1, ra_link_1, rb_0x20 0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20267016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209c9017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209c901f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2126709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02367c80, 0x10020827, // fsub r0, a, b 0x02367180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02367c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x013a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x1002588d, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02367c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100203a7, // fadd a+1, r0, r1 0x029cdec0, 0x10020827, // fsub r0, a, b 0x029cd1c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029cde40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019ce3c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x1002488d, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029cde80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100213a7, // fadd a+1, r0, r1 0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1ccdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES 0xfffff9c0, 0xf00809e7, // brr.allz -, r:pass_2 0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm 0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x00000000, 0xf0f4c027, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9c8e00, 0x10020e27, // add t0s, ptr, r0 0x0c9c8e40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c227c00, 0x10020e27, // add t0s, ptr, r0 0x0c227c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020267, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021267, // mov rb_tw_im+dst, r4 0x00000000, 0xe002438e, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c41c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff808, 0xf0f80027, // brr ra_link_1, r:pass_3 0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm 0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20267016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209c9017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209c901f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2126709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02367c80, 0x10020827, // fsub r0, a, b 0x02367180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02367c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x013a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x1002588d, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02367c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100203a7, // fadd a+1, r0, r1 0x029cdec0, 0x10020827, // fsub r0, a, b 0x029cd1c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029cde40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019ce3c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x1002488d, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029cde80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100213a7, // fadd a+1, r0, r1 0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1ccdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES 0xfffff678, 0xf00809e7, // brr.allz -, r:pass_3 0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm 0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x00000000, 0xf0f4c027, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0xfffff6a8, 0xf0f809e7, // brr -, r:loop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c3fc0, 0x100209a7, // mov interrupt, flag 0x009e7000, 0x300009e7, // nop; nop; thrend 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_512.hex000066400000000000000000000614351421703157200274170ustar00rootroot000000000000000x00000010, 0xe0021727, // mov rb_0x10, 0x10 0x00000040, 0xe0021767, // mov rb_0x40, 0x40 0x00000080, 0xe00217a7, // mov rb_0x80, 0x80 0x000000f0, 0xe00217e7, // mov rb_0xF0, 0xF0 0x00005555, 0xe0020727, // mov rx_0x5555, 0x5555 0x00003333, 0xe0020767, // mov rx_0x3333, 0x3333 0x00000f0f, 0xe00207a7, // mov rx_0x0F0F, 0x0F0F 0x000000ff, 0xe00207e7, // mov rx_0x00FF, 0x00FF 0x88104000, 0xe00206a7, // mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) 0x88105000, 0xe0021027, // mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) 0x90104000, 0xe00206e7, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) 0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) 0x15827d80, 0x100202e7, // mov rx_tw_shared, unif 0x15827d80, 0x100212e7, // mov rx_tw_unique, unif 0x15827d80, 0x10021167, // mov rb_inst, unif 0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) 0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) 0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) 0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst 0xcc9e7081, 0x10024620, // add out_0, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x10024660, // add out_1, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 0x0c9e7080, 0x100211e7, // add out_3, r0, r2 0x000000b0, 0xf0f80127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15627d80, 0x10021c67, // mov vw_setup, arg 0x159e7000, 0x10020c27, // mov vpm, r0 0x159e7240, 0x10020c27, // mov vpm, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x156a7d80, 0x10021c67, // mov vw_setup, arg_vdw 0xc00000c0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS16_STRIDE-16*4 0x8c05ddf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr 0x00000038, 0xf0f81127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15627d80, 0x10021c67, // mov vw_setup, arg 0x159e7000, 0x10020c27, // mov vpm, r0 0x159e7240, 0x10020c27, // mov vpm, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x15627d80, 0x10020c67, // mov vr_setup, arg_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x000000c8, 0xf0f802a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15627d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x156e7d80, 0x10021c67, // mov vw_setup, ra_vdw_32 0xc0000040, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 0x8c05ddf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr 0x00000050, 0xf0f812a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15627d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x15627d80, 0x10020c67, // mov vr_setup, ra_vpm_lo 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000080, 0xf0f801a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000510, 0xf0f811a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000009, 0xe80009e7, // mov -, srel(i+9) 0x00000011, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000a, 0xe80009e7, // mov -, srel(i+9) 0x00000012, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000b, 0xe80009e7, // mov -, srel(i+9) 0x00000013, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000c, 0xe80009e7, // mov -, srel(i+9) 0x00000014, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000d, 0xe80009e7, // mov -, srel(i+9) 0x00000015, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000e, 0xe80009e7, // mov -, srel(i+9) 0x00000016, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000f, 0xe80009e7, // mov -, srel(i+9) 0x00000017, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffda0, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14727180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14727180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c41c0, 0xd0020827, // shr r0, r0, 13-STAGES 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffc80, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f549e7, // bra -, ra_save_32 0x95602ff6, 0x10024602, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x95647ff6, 0x10024647, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x956c1ff6, 0x100246c1, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffbf0, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x00000000, 0xf0f489e7, // bra -, ra_save_16 0x009e7000, 0x100009e7, // nop 0x95602ff6, 0x10024602, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x95680ff6, 0x10024680, // mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst 0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 0x119c51c0, 0xd0020827, // shl r0, r0, 5 0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif 0x15827d80, 0x100210e7, // mov rb_addr_y, unif 0x000005e8, 0xf00809e7, // brr.allz -, r:end 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14727180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14727180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9c41c0, 0xd0020827, // shr r0, r0, 13-STAGES 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffa80, 0xf0f80227, // brr ra_link_1, r:pass_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0xfffffa60, 0xf0f80227, // brr ra_link_1, r:pass_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c41c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffb20, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02467c80, 0x10020827, // fsub r0, a, b 0x02467180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02467c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02467c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204a7, // fadd a+1, r0, r1 0x029d1ec0, 0x10020827, // fsub r0, a, b 0x029d11c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d1e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d23c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d1e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214a7, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1c9dc0, 0xd00229e7, // shr.setf -, ra_points, STAGES 0xfffff990, 0xf00809e7, // brr.allz -, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0xfffff9e8, 0xf0f809e7, // brr -, r:loop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c3fc0, 0x100209a7, // mov interrupt, flag 0x009e7000, 0x300009e7, // nop; nop; thrend 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_512k.hex000066400000000000000000001435241421703157200275720ustar00rootroot000000000000000x00000013, 0xe0021227, // mov rb_STAGES, STAGES 0x00000010, 0xe00216e7, // mov rb_0x10, 0x10 0x00000040, 0xe0021727, // mov rb_0x40, 0x40 0x00000080, 0xe0021767, // mov rb_0x80, 0x80 0x000000f0, 0xe00217a7, // mov rb_0xF0, 0xF0 0x00000100, 0xe00217e7, // mov rb_0x100, 0x100 0x55555555, 0xe0020767, // mov rx_0x55555555, 0x55555555 0x33333333, 0xe00207a7, // mov rx_0x33333333, 0x33333333 0x0f0f0f0f, 0xe00207e7, // mov rx_0x0F0F0F0F, 0x0F0F0F0F 0x00ff00ff, 0xe0021667, // mov rx_0x00FF00FF, 0x00FF00FF 0x0000ffff, 0xe00216a7, // mov rx_0x0000FFFF, 0x0000FFFF 0x80904000, 0xe00206e7, // mov ra_vdw_16, vdw_setup_0(1, 16, dma_h32( 0,0)) 0x80905000, 0xe0021027, // mov rb_vdw_16, vdw_setup_0(1, 16, dma_h32(32,0)) 0x80904000, 0xe0020727, // mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) 0x80905000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) 0x15827d80, 0x100202e7, // mov rx_tw_shared, unif 0x15827d80, 0x100212e7, // mov rx_tw_unique, unif 0x15827d80, 0x10021167, // mov rb_inst, unif 0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) 0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) 0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) 0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst 0xcc9e7081, 0x10024660, // add out_0, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100246a0, // add out_1, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 0x0c9e7080, 0x100211e7, // add out_3, r0, r2 0x000001d0, 0xf0f80127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, arg 0x159e7000, 0x10020c27, // mov vpm, r0 0x159e7240, 0x10020c27, // mov vpm, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x156e7d80, 0x10020827, // mov r0, arg_vdw 0x8c05cdf6, 0x10024061, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr 0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) 0x00040000, 0xe00208e7, // mov r3, PASS16_STRIDE 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000038, 0xf0f81127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, arg 0x159e7000, 0x10020c27, // mov vpm, r0 0x159e7240, 0x10020c27, // mov vpm, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10020c67, // mov vr_setup, arg_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x000002e8, 0xf0f802a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x15727d80, 0x10020827, // mov r0, ra_vdw_32 0x8c05cdf6, 0x10024061, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr 0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) 0x00020000, 0xe00208e7, // mov r3, PASS32_STRIDE 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000050, 0xf0f812a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10020c67, // mov vr_setup, ra_vpm_lo 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000080, 0xf0f801a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000640, 0xf0f811a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000009, 0xe80009e7, // mov -, srel(i+9) 0x00000011, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000a, 0xe80009e7, // mov -, srel(i+9) 0x00000012, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000b, 0xe80009e7, // mov -, srel(i+9) 0x00000013, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000c, 0xe80009e7, // mov -, srel(i+9) 0x00000014, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000d, 0xe80009e7, // mov -, srel(i+9) 0x00000015, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000e, 0xe80009e7, // mov -, srel(i+9) 0x00000016, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000f, 0xe80009e7, // mov -, srel(i+9) 0x00000017, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffd78, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149d91c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149d91c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149da1c0, 0x10020867, // and r1, r0, mask 0x0e9db1c0, 0x10020827, // shr r0, r0, shift 0x149da1c0, 0x10020827, // and r0, r0, mask 0x119db3c0, 0x10020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9ca1c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffc30, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f549e7, // bra -, ra_save_32 0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffba0, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x00000000, 0xf0f489e7, // bra -, ra_save_16 0x009e7000, 0x100009e7, // nop 0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x956c0ff6, 0x100246c0, // mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffb38, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffae8, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f549e7, // bra -, ra_save_32 0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst 0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 0x119c51c0, 0xd0020827, // shl r0, r0, 5 0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif 0x15827d80, 0x100210e7, // mov rb_addr_y, unif 0x000010a8, 0xf00809e7, // brr.allz -, r:end 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149d91c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149d91c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149da1c0, 0x10020867, // and r1, r0, mask 0x0e9db1c0, 0x10020827, // shr r0, r0, shift 0x149da1c0, 0x10020827, // and r0, r0, mask 0x119db3c0, 0x10020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0e9ca1c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff928, 0xf0f80227, // brr ra_link_1, r:pass_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xfffff900, 0xf00809e7, // brr.allz -, r:pass_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c41c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffa10, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x00003fff, 0xe0020827, // mov r0, 0x3FFF 0x141e7c00, 0x100229e7, // and.setf -, ra_points, r0 0xfffff9e0, 0xf01809e7, // brr.allnz -, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dddc0, 0x100601e7, // add.ifnz ra_points, ra_points, rb_0x80 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02467c80, 0x10020827, // fsub r0, a, b 0x02467180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02467c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02467c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204a7, // fadd a+1, r0, r1 0x029d1ec0, 0x10020827, // fsub r0, a, b 0x029d11c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d1e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d23c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d1e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214a7, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xfffff850, 0xf00809e7, // brr.allz -, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000004, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000003, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff648, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0xfffff628, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0xfffff608, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0xfffff5e8, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x024e7c80, 0x10020827, // fsub r0, a, b 0x024e7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x024e7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01527380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x024e7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020527, // fadd a+1, r0, r1 0x029d3ec0, 0x10020827, // fsub r0, a, b 0x029d31c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d3e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d43c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d3e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021527, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02467c80, 0x10020827, // fsub r0, a, b 0x02467180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02467c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02467c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204a7, // fadd a+1, r0, r1 0x029d1ec0, 0x10020827, // fsub r0, a, b 0x029d11c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d1e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d23c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d1e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214a7, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xfffff358, 0xf00809e7, // brr.allz -, r:pass_3 0x00000060, 0xe0020827, // mov r0, 3*4*8 0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000006, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000005, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff0e8, 0xf0f80227, // brr ra_link_1, r:pass_4 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x024e7c80, 0x10020827, // fsub r0, a, b 0x024e7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x024e7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01527380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x024e7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020527, // fadd a+1, r0, r1 0x029d3ec0, 0x10020827, // fsub r0, a, b 0x029d31c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d3e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d43c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d3e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021527, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02467c80, 0x10020827, // fsub r0, a, b 0x02467180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02467c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02467c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204a7, // fadd a+1, r0, r1 0x029d1ec0, 0x10020827, // fsub r0, a, b 0x029d11c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d1e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d23c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d1e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214a7, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xffffee58, 0xf00809e7, // brr.allz -, r:pass_4 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0xffffef28, 0xf0f809e7, // brr -, r:loop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c3fc0, 0x100209a7, // mov interrupt, flag 0x009e7000, 0x300009e7, // nop; nop; thrend 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_64k.hex000066400000000000000000001340741421703157200275140ustar00rootroot000000000000000x00000010, 0xe0021227, // mov rb_0x10, 0x10 0x000001d0, 0xe0021967, // mov r5rep, 0x1D0 0x00005555, 0xe00207a7, // mov rx_0x5555, 0x5555 0x00003333, 0xe00217a7, // mov rx_0x3333, 0x3333 0x00000f0f, 0xe00207e7, // mov rx_0x0F0F, 0x0F0F 0x000000ff, 0xe00217e7, // mov rx_0x00FF, 0x00FF 0x15827d80, 0x100203e7, // mov rx_tw_shared, unif 0x15827d80, 0x100213e7, // mov rx_tw_unique, unif 0x15827d80, 0x10021167, // mov rb_inst, unif 0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) 0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) 0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) 0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst 0xcc9e7081, 0x10025020, // add out_0, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x10025060, // add out_1, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 0x0c9e7080, 0x100211e7, // add out_3, r0, r2 0x000000c8, 0xf0f80127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x153a7d80, 0x10021c67, // mov vw_setup, ra_vdw_32 0xc0003fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 0x8c04ddf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr 0x00000050, 0xf0f81127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x152e7d80, 0x10020c67, // mov vr_setup, ra_vpm_lo 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000100, 0xf0f802a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000040, 0xe0020827, // mov r0, 0x40 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm 0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0xa0104000, 0xe0021c67, // mov vw_setup, vdw_setup_0(64, 16, dma_h32(0,0)) 0xc0001fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(PASS64_STRIDE-16*4) 0x8c067c36, 0x10024072, // add ra_save_ptr, ra_save_ptr, step; mov vw_addr, ra_save_ptr 0x000002b8, 0xf0f812a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd00200a7, // shl ra_temp, r0, 5 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0fc49e7, // brr -, ra_temp 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x000000e0, 0xf0f809e7, // brr -, r:2f 0x00000010, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x000000c0, 0xf0f809e7, // brr -, r:2f 0x00000011, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x000000a0, 0xf0f809e7, // brr -, r:2f 0x00000012, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000080, 0xf0f809e7, // brr -, r:2f 0x00000013, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000060, 0xf0f809e7, // brr -, r:2f 0x00000014, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000040, 0xf0f809e7, // brr -, r:2f 0x00000015, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000020, 0xf0f809e7, // brr -, r:2f 0x00000016, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f809e7, // brr -, r:2f 0x00000017, 0xe80009e7, // mov -, sacq(i) 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm 0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 0x00000000, 0xf0fc49e7, // brr -, ra_temp 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000008, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000009, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000a, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000b, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000c, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000d, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000e, 0xe80009e7, // mov -, srel(i+8) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x0000000f, 0xe80009e7, // mov -, srel(i+8) 0x00000080, 0xf0f801a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000858, 0xf0f811a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000009, 0xe80009e7, // mov -, srel(i+9) 0x00000011, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000a, 0xe80009e7, // mov -, srel(i+9) 0x00000012, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000b, 0xe80009e7, // mov -, srel(i+9) 0x00000013, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000c, 0xe80009e7, // mov -, srel(i+9) 0x00000014, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000d, 0xe80009e7, // mov -, srel(i+9) 0x00000015, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000e, 0xe80009e7, // mov -, srel(i+9) 0x00000016, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000f, 0xe80009e7, // mov -, srel(i+9) 0x00000017, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffda0, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149de1c0, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x149de1c0, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149df1c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149df1c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x119c31c0, 0xd0020827, // shl r0, r0, STAGES-13 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffc80, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x01267c00, 0x100202e7, // fadd ra_64+0, ra_32_re, r0 0x019c9e40, 0x10020327, // fadd ra_64+1, rb_32_im, r1 0x02267c00, 0x10020367, // fsub ra_64+2, ra_32_re, r0 0x029c9e40, 0x100203a7, // fsub ra_64+3, rb_32_im, r1 0x8c167d76, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149de1c0, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x149de1c0, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149df1c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149df1c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x119c31c0, 0xd0020827, // shl r0, r0, STAGES-13 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffb20, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149de1c0, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x149de1c0, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149df1c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149df1c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x119c31c0, 0xd0020827, // shl r0, r0, STAGES-13 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffa00, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x029c9e40, 0x100208e7, // fsub r3, rb_32_im, r1 0x02267c00, 0x100208a7, // fsub r2, ra_32_re, r0 0x019c9e40, 0x10020867, // fadd r1, rb_32_im, r1 0x01267c00, 0x10020827, // fadd r0, ra_32_re, r0 0x2066700e, 0x100049c9, // nop; fmul rb_32_im, r1, ra_tw_re+TW48 0x209d900f, 0x100059c9, // nop; fmul ra_32_re, r1, rb_tw_im+TW48 0x209d9007, 0x100049e1, // nop; fmul r1, r0, rb_tw_im+TW48 0x216493c6, 0x10025320, // fadd rb_64+1, r1, rb_32_im; fmul r0, r0, ra_tw_re+TW48 0x2225a19f, 0x100252c9, // fsub rb_64+0, r0, ra_32_re; fmul ra_32_re, r3, rb_tw_im+TW64 0x206a701e, 0x100049c9, // nop; fmul rb_32_im, r3, ra_tw_re+TW64 0x00000000, 0xf0f549e7, // bra -, ra_save_64 0x209da017, 0x100049e3, // nop; fmul r3, r2, rb_tw_im+TW64 0x216897d6, 0x100253a2, // fadd rb_64+3, r3, rb_32_im; fmul r2, r2, ra_tw_re+TW64 0x02267580, 0x10021367, // fsub rb_64+2, r2, ra_32_re 0x8c14cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff920, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff8d0, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f489e7, // bra -, ra_save_32 0x952c2ff6, 0x100242c2, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x95307ff6, 0x10024307, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x9538eff6, 0x1002438e, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst 0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 0x119c51c0, 0xd0020827, // shl r0, r0, 5 0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 0x159c4fc0, 0x10060127, // mov.ifnz ra_save_32, rx_save_slave_32 0x159cafc0, 0x100602a7, // mov.ifnz ra_save_64, rx_save_slave_64 0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif 0x15827d80, 0x100210e7, // mov rb_addr_y, unif 0x00000df0, 0xf00809e7, // brr.allz -, r:end 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000003, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020667, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021667, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100206a7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100216a7, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c61c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149de1c0, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x149de1c0, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149df1c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149df1c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x119c31c0, 0xd0020827, // shl r0, r0, STAGES-13 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff688, 0xf0f80227, // brr ra_link_1, r:pass_1 0x009e7000, 0x100009e7, // nop 0x00000200, 0xe0020827, // mov r0, 0x200 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x00000010, 0xe0020867, // mov r1, STAGES 0x0e1e7c40, 0x100229e7, // shr.setf -, ra_points, r1 0xfffff658, 0xf00809e7, // brr.allz -, r:pass_1 0x009e7000, 0x100009e7, // nop 0x00000200, 0xe0020827, // mov r0, 0x200 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x159c0fc0, 0x100202e7, // mov ra_vpm_lo, rb_vpm 0x159c1fc0, 0x10020327, // mov ra_vpm_hi, rb_vpm_16 0x90104000, 0xe00203a7, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) 0x90105000, 0xe00213a7, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) 0x00000060, 0xe00212e7, // mov rb_3x4x8, 3*4*8 0x000000f0, 0xe0021327, // mov rb_0xF0, 0xF0 0x00000040, 0xe0021367, // mov rb_0x40, 0x40 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000003, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000005, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000004, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff900, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0xfffff8e0, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0xfffff8c0, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0xfffff8a0, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x025e7c80, 0x10020827, // fsub r0, a, b 0x025e7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x025e7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01627380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x025e7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020627, // fadd a+1, r0, r1 0x029d7ec0, 0x10020827, // fsub r0, a, b 0x029d71c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d7e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d83c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d7e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021627, // fadd a+1, r0, r1 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02567c80, 0x10020827, // fsub r0, a, b 0x02567180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02567c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x015a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02567c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100205a7, // fadd a+1, r0, r1 0x029d5ec0, 0x10020827, // fsub r0, a, b 0x029d51c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d5e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d63c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d5e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100215a7, // fadd a+1, r0, r1 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xfffff610, 0xf00809e7, // brr.allz -, r:pass_2 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x0d20bdc0, 0x10020227, // sub ra_link_1, ra_link_1, rb_3x4x8 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000007, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000006, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff3a0, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x025e7c80, 0x10020827, // fsub r0, a, b 0x025e7180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x025e7c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x01627380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x025e7c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10020627, // fadd a+1, r0, r1 0x029d7ec0, 0x10020827, // fsub r0, a, b 0x029d71c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d7e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d83c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d7e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x10021627, // fadd a+1, r0, r1 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02567c80, 0x10020827, // fsub r0, a, b 0x02567180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02567c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x015a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02567c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100205a7, // fadd a+1, r0, r1 0x029d5ec0, 0x10020827, // fsub r0, a, b 0x029d51c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d5e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d63c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d5e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100215a7, // fadd a+1, r0, r1 0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES 0xfffff110, 0xf00809e7, // brr.allz -, r:pass_3 0x009e7000, 0x100009e7, // nop 0x00000100, 0xe0020827, // mov r0, 0x100 0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0xfffff1e0, 0xf0f809e7, // brr -, r:loop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c3fc0, 0x100209a7, // mov interrupt, flag 0x009e7000, 0x300009e7, // nop; nop; thrend 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_8k.hex000066400000000000000000000747661421703157200274450ustar00rootroot000000000000000x00000010, 0xe00216e7, // mov rb_0x10, 0x10 0x00000040, 0xe0021727, // mov rb_0x40, 0x40 0x00000080, 0xe0021767, // mov rb_0x80, 0x80 0x000000f0, 0xe00217a7, // mov rb_0xF0, 0xF0 0x00000100, 0xe00217e7, // mov rb_0x100, 0x100 0x00005555, 0xe0020767, // mov rx_0x5555, 0x5555 0x00003333, 0xe00207a7, // mov rx_0x3333, 0x3333 0x00000f0f, 0xe00207e7, // mov rx_0x0F0F, 0x0F0F 0x000000ff, 0xe00216a7, // mov rx_0x00FF, 0x00FF 0x88104000, 0xe00206e7, // mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) 0x88105000, 0xe0021027, // mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) 0x90104000, 0xe0020727, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) 0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) 0x15827d80, 0x100202e7, // mov rx_tw_shared, unif 0x15827d80, 0x100212e7, // mov rx_tw_unique, unif 0x15827d80, 0x10021167, // mov rb_inst, unif 0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) 0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) 0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) 0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst 0xcc9e7081, 0x10024660, // add out_0, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100246a0, // add out_1, r0, r2; v8adds r0, r0, r1 0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 0x0c9e7080, 0x100211e7, // add out_3, r0, r2 0x000000b0, 0xf0f80127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, arg 0x159e7000, 0x10020c27, // mov vpm, r0 0x159e7240, 0x10020c27, // mov vpm, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x156e7d80, 0x10021c67, // mov vw_setup, arg_vdw 0xc0000fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS16_STRIDE-16*4 0x8c05cdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr 0x00000038, 0xf0f81127, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, arg 0x159e7000, 0x10020c27, // mov vpm, r0 0x159e7240, 0x10020c27, // mov vpm, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10020c67, // mov vr_setup, arg_vpm 0x15c27d80, 0x100009e7, // mov -, vpm 0x000000c8, 0xf0f802a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x15727d80, 0x10021c67, // mov vw_setup, ra_vdw_32 0xc00007c0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 0x8c05cdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr 0x00000050, 0xf0f812a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo 0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi 0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 0x00000000, 0xf0f4c9e7, // bra -, ra_sync 0x009e7000, 0x100009e7, // nop 0x15667d80, 0x10020c67, // mov vr_setup, ra_vpm_lo 0x15c27d80, 0x100009e7, // mov -, vpm 0x00000080, 0xf0f801a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x00000019, 0xe80009e7, // mov -, sacq(i+9) 0x00000001, 0xe80009e7, // mov -, srel(i+1) 0x0000001a, 0xe80009e7, // mov -, sacq(i+9) 0x00000002, 0xe80009e7, // mov -, srel(i+1) 0x0000001b, 0xe80009e7, // mov -, sacq(i+9) 0x00000003, 0xe80009e7, // mov -, srel(i+1) 0x0000001c, 0xe80009e7, // mov -, sacq(i+9) 0x00000004, 0xe80009e7, // mov -, srel(i+1) 0x0000001d, 0xe80009e7, // mov -, sacq(i+9) 0x00000005, 0xe80009e7, // mov -, srel(i+1) 0x0000001e, 0xe80009e7, // mov -, sacq(i+9) 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000006, 0xe80009e7, // mov -, srel(i+1) 0x0000001f, 0xe80009e7, // mov -, sacq(i+9) 0x00000007, 0xe80009e7, // mov -, srel(i+1) 0x00000500, 0xf0f811a7, // brr rx_ptr, label 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x00000009, 0xe80009e7, // mov -, srel(i+9) 0x00000011, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000a, 0xe80009e7, // mov -, srel(i+9) 0x00000012, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000b, 0xe80009e7, // mov -, srel(i+9) 0x00000013, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000c, 0xe80009e7, // mov -, srel(i+9) 0x00000014, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000d, 0xe80009e7, // mov -, srel(i+9) 0x00000015, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000e, 0xe80009e7, // mov -, srel(i+9) 0x00000016, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x00000000, 0xf0f509e7, // bra -, ra_link_1 0x0000000f, 0xe80009e7, // mov -, srel(i+9) 0x00000017, 0xe80009e7, // mov -, sacq(i+1) 0x009e7000, 0x100009e7, // nop 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffda8, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149da1c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149da1c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0xfffffc90, 0xf0f80027, // brr ra_link_0, call 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 0x019e72c0, 0x10020867, // fadd r1, r1, r3 0x00000000, 0xf0f549e7, // bra -, ra_save_32 0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi 0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffc00, 0xf0f80027, // brr ra_link_0, call 0x009e7000, 0xa00009e7, // nop; ldtmu0 0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 0x159e7900, 0x10020867, // mov r1, r4 0x00000000, 0xf0f489e7, // bra -, ra_save_16 0x009e7000, 0x100009e7, // nop 0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo 0x956c0ff6, 0x100246c0, // mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst 0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 0x119c51c0, 0xd0020827, // shl r0, r0, 5 0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif 0x15827d80, 0x100210e7, // mov rb_addr_y, unif 0x00000958, 0xf00809e7, // brr.allz -, r:end 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c51c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x14767180, 0x10020867, // and r1, r0, mask 0x0e9c11c0, 0xd0020827, // shr r0, r0, shift 0x14767180, 0x10020827, // and r0, r0, mask 0x119c13c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147a7180, 0x10020867, // and r1, r0, mask 0x0e9c21c0, 0xd0020827, // shr r0, r0, shift 0x147a7180, 0x10020827, // and r0, r0, mask 0x119c23c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x147e7180, 0x10020867, // and r1, r0, mask 0x0e9c41c0, 0xd0020827, // shr r0, r0, shift 0x147e7180, 0x10020827, // and r0, r0, mask 0x119c43c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x149da1c0, 0x10020867, // and r1, r0, mask 0x0e9c81c0, 0xd0020827, // shr r0, r0, shift 0x149da1c0, 0x10020827, // and r0, r0, mask 0x119c83c0, 0xd0020867, // shl r1, r1, shift 0x159e7040, 0x10020827, // or r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffa98, 0xf0f80227, // brr ra_link_1, r:pass_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x0e1cddc0, 0xd00229e7, // shr.setf -, ra_points, STAGES 0xfffffa70, 0xf00809e7, // brr.allz -, r:pass_1 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000001, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000002, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c41c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffffb20, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0xfffffb00, 0xf0f80227, // brr ra_link_1, r:pass_2 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02467c80, 0x10020827, // fsub r0, a, b 0x02467180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02467c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02467c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204a7, // fadd a+1, r0, r1 0x029d1ec0, 0x10020827, // fsub r0, a, b 0x029d11c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d1e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d23c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d1e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214a7, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1cddc0, 0xd00229e7, // shr.setf -, ra_points, STAGES 0xfffff970, 0xf00809e7, // brr.allz -, r:pass_2 0x00000020, 0xe0020827, // mov r0, 4*8 0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000000, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 0x00000003, 0xe0020867, // mov r1, src 0x119c73c0, 0xd0020867, // shl r1, r1, 7 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x159c5fc0, 0x10020827, // mov r0, rb_inst 0x119c41c0, 0xd0020827, // shl r0, r0, m 0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num 0x00000000, 0xe00201e7, // mov ra_points, 0 0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y 0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx 0x119c31c0, 0xd0020827, // shl r0, r0, 3 0x0c9c41c0, 0xd0020867, // add r1, r0, 4 0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 0xfffff7b0, 0xf0f80227, // brr ra_link_1, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base 0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step 0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step 0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step 0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step 0x029e7640, 0x100208e7, // fsub r3, r3, r1 0x02467c80, 0x10020827, // fsub r0, a, b 0x02467180, 0x10020867, // fsub r1, r0, a 0x019e7280, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x02467c40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x014a7380, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x02467c80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100204a7, // fadd a+1, r0, r1 0x029d1ec0, 0x10020827, // fsub r0, a, b 0x029d11c0, 0x10020867, // fsub r1, r0, a 0x019e72c0, 0x100208a7, // fadd r2, r1, b 0x029e7040, 0x10020867, // fsub r1, r0, r1 0x029d1e40, 0x10020867, // fsub r1, a, r1 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x019d23c0, 0x10020867, // fadd r1, r1, a+1 0x019e7040, 0x100208a7, // fadd r2, r0, r1 0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 0x029e7280, 0x10020867, // fsub r1, r1, r2 0x029d1e80, 0x100208a7, // fsub r2, a, r2 0x029e7080, 0x10020827, // fsub r0, r0, r2 0x019e7040, 0x100214a7, // fadd a+1, r0, r1 0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) 0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) 0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) 0x0e1cddc0, 0xd00229e7, // shr.setf -, ra_points, STAGES 0xfffff620, 0xf00809e7, // brr.allz -, r:pass_3 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync 0x009e7000, 0x100009e7, // nop 0x009e7000, 0xa00009e7, // ldtmu0 0x009e7000, 0xa00009e7, // ldtmu0 0xfffff678, 0xf0f809e7, // brr -, r:loop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x159c3fc0, 0x100209a7, // mov interrupt, flag 0x009e7000, 0x300009e7, // nop; nop; thrend 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_trans.hex000066400000000000000000000135651421703157200302400ustar00rootroot000000000000000x15827d80, 0x10020e27, // mov t0s, unif 0x009e7000, 0xa00009e7, // ldtmu0 0x0c9cc9c0, 0xd0020e27, // add t0s, r4, 3*4 0x009e7000, 0xa00009e7, // ldtmu0 0x0c827980, 0x100200a7, // add ra_src_base, r4, unif 0x15827d80, 0x10020e27, // mov t0s, unif 0x009e7000, 0xa00009e7, // ldtmu0 0x0c9cc9c0, 0xd0020e27, // add t0s, r4, 3*4 0x009e7000, 0xa00009e7, // ldtmu0 0x0c827980, 0x100200e7, // add ra_dst_base, r4, unif 0x15827d80, 0x100214a7, // mov rb_Y_STRIDE_SRC, unif 0x15827d80, 0x100214e7, // mov rb_Y_STRIDE_DST, unif 0x15827d80, 0x10021527, // mov rb_NX, unif 0x15827d80, 0x10021567, // mov rb_NY, unif 0x00000008, 0xe0021467, // mov rb_X_STRIDE, 2*4 0x00000010, 0xe0021427, // mov rb_0x10, 0x10 0xc0000000, 0xe0020827, // mov r0, vdw_setup_1(0) 0x0c9d31c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_DST 0x00000040, 0xe0020867, // mov r1, 16*4 0x0d9e7040, 0x100201a7, // sub ra_vdw_stride, r0, r1 0x40991037, 0x100049e0, // nop; mul24 r0, elem_num, rb_X_STRIDE 0x159e7000, 0x10021027, // mov rb_offsets_re+i, r0 0x0c9c41c0, 0xd0021227, // add rb_offsets_im+i, r0, 4 0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC 0x159e7000, 0x10021067, // mov rb_offsets_re+i, r0 0x0c9c41c0, 0xd0021267, // add rb_offsets_im+i, r0, 4 0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC 0x159e7000, 0x100210a7, // mov rb_offsets_re+i, r0 0x0c9c41c0, 0xd00212a7, // add rb_offsets_im+i, r0, 4 0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC 0x159e7000, 0x100210e7, // mov rb_offsets_re+i, r0 0x0c9c41c0, 0xd00212e7, // add rb_offsets_im+i, r0, 4 0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC 0x159e7000, 0x10021127, // mov rb_offsets_re+i, r0 0x0c9c41c0, 0xd0021327, // add rb_offsets_im+i, r0, 4 0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC 0x159e7000, 0x10021167, // mov rb_offsets_re+i, r0 0x0c9c41c0, 0xd0021367, // add rb_offsets_im+i, r0, 4 0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC 0x159e7000, 0x100211a7, // mov rb_offsets_re+i, r0 0x0c9c41c0, 0xd00213a7, // add rb_offsets_im+i, r0, 4 0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC 0x159e7000, 0x100211e7, // mov rb_offsets_re+i, r0 0x0c9c41c0, 0xd00213e7, // add rb_offsets_im+i, r0, 4 0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC 0x00000000, 0xe0020067, // mov ra_y, 0 0x00000000, 0xe0020027, // mov ra_x, 0 0x40052037, 0x100049e1, // nop; mul24 r1, ra_y, rb_Y_STRIDE_SRC 0x40011037, 0x100049e0, // nop; mul24 r0, ra_x, rb_X_STRIDE 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c0a7c00, 0x10020127, // add ra_src_cell, ra_src_base, r0 0x40013037, 0x100049e1, // nop; mul24 r1, ra_x, rb_Y_STRIDE_DST 0x40051037, 0x100049e0, // nop; mul24 r0, ra_y, rb_X_STRIDE 0x0c9e7040, 0x10020827, // add r0, r0, r1 0x0c0e7c00, 0x10020167, // add ra_dst_cell, ra_dst_base, r0 0x00001200, 0xe0021c67, // mov vw_setup, vpm_setup(16, 1, v32(0,0)) 0x0c100dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re 0x0c108dc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im 0x0c101dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i 0x0c109dc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020c27, // mov vpm, r4 0x009e7000, 0xb00009e7, // ldtmu1 0x159e7900, 0x10020c27, // mov vpm, r4 0x0c102dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i 0x0c10adc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020c27, // mov vpm, r4 0x009e7000, 0xb00009e7, // ldtmu1 0x159e7900, 0x10020c27, // mov vpm, r4 0x0c103dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i 0x0c10bdc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020c27, // mov vpm, r4 0x009e7000, 0xb00009e7, // ldtmu1 0x159e7900, 0x10020c27, // mov vpm, r4 0x0c104dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i 0x0c10cdc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020c27, // mov vpm, r4 0x009e7000, 0xb00009e7, // ldtmu1 0x159e7900, 0x10020c27, // mov vpm, r4 0x0c105dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i 0x0c10ddc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020c27, // mov vpm, r4 0x009e7000, 0xb00009e7, // ldtmu1 0x159e7900, 0x10020c27, // mov vpm, r4 0x0c106dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i 0x0c10edc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020c27, // mov vpm, r4 0x009e7000, 0xb00009e7, // ldtmu1 0x159e7900, 0x10020c27, // mov vpm, r4 0x0c107dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i 0x0c10fdc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020c27, // mov vpm, r4 0x009e7000, 0xb00009e7, // ldtmu1 0x159e7900, 0x10020c27, // mov vpm, r4 0x009e7000, 0xa00009e7, // ldtmu0 0x159e7900, 0x10020c27, // mov vpm, r4 0x009e7000, 0xb00009e7, // ldtmu1 0x159e7900, 0x10020c27, // mov vpm, r4 0x88104000, 0xe0021c67, // mov vw_setup, vdw_setup_0(16, 16, dma_h32(0,0)) 0x151a7d80, 0x10021c67, // mov vw_setup, ra_vdw_stride 0x15167d80, 0x10021ca7, // mov vw_addr, ra_dst_cell 0x159f2fc0, 0x100009e7, // mov -, vw_wait 0x0c010dc0, 0x10020027, // add ra_x, ra_x, rb_0x10 0x009e7000, 0x100009e7, // nop 0x0d014dc0, 0x100229e7, // sub.setf -, ra_x, rb_NX 0xfffffde0, 0xf01809e7, // brr.allnz -, r:inner 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x0c048dc0, 0xd0020067, // add ra_y, ra_y, 8 0x009e7000, 0x100009e7, // nop 0x0d055dc0, 0x100229e7, // sub.setf -, ra_y, rb_NY 0xfffffda0, 0xf01809e7, // brr.allnz -, r:outer 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop 0x00000001, 0xe00209a7, // mov interrupt, 1 0x009e7000, 0x300009e7, // nop; nop; thrend 0x009e7000, 0x100009e7, // nop 0x009e7000, 0x100009e7, // nop userland/host_applications/linux/apps/hello_pi/hello_fft/mailbox.c000066400000000000000000000154211421703157200260610ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd. 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include #include #include #include #include #include #include "mailbox.h" #define PAGE_SIZE (4*1024) void *mapmem(unsigned base, unsigned size) { int mem_fd; unsigned offset = base % PAGE_SIZE; base = base - offset; size = size + offset; /* open /dev/mem */ if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) { printf("can't open /dev/mem\nThis program should be run as root. Try prefixing command with: sudo\n"); exit (-1); } void *mem = mmap( 0, size, PROT_READ|PROT_WRITE, MAP_SHARED/*|MAP_FIXED*/, mem_fd, base); #ifdef DEBUG printf("base=0x%x, mem=%p\n", base, mem); #endif if (mem == MAP_FAILED) { printf("mmap error %d\n", (int)mem); exit (-1); } close(mem_fd); return (char *)mem + offset; } void unmapmem(void *addr, unsigned size) { const intptr_t offset = (intptr_t)addr % PAGE_SIZE; addr = (char *)addr - offset; size = size + offset; int s = munmap(addr, size); if (s != 0) { printf("munmap error %d\n", s); exit (-1); } } /* * use ioctl to send mbox property message */ static int mbox_property(int file_desc, void *buf) { int ret_val = ioctl(file_desc, IOCTL_MBOX_PROPERTY, buf); if (ret_val < 0) { printf("ioctl_set_msg failed:%d\n", ret_val); } #ifdef DEBUG unsigned *p = buf; int i; unsigned size = *(unsigned *)buf; for (i=0; i #define MAJOR_NUM 100 #define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *) #define DEVICE_FILE_NAME "/dev/vcio" int mbox_open(); void mbox_close(int file_desc); unsigned mem_alloc(int file_desc, unsigned size, unsigned align, unsigned flags); unsigned mem_free(int file_desc, unsigned handle); unsigned mem_lock(int file_desc, unsigned handle); unsigned mem_unlock(int file_desc, unsigned handle); void *mapmem(unsigned base, unsigned size); void unmapmem(void *addr, unsigned size); unsigned execute_code(int file_desc, unsigned code, unsigned r0, unsigned r1, unsigned r2, unsigned r3, unsigned r4, unsigned r5); unsigned execute_qpu(int file_desc, unsigned num_qpus, unsigned control, unsigned noflush, unsigned timeout); unsigned qpu_enable(int file_desc, unsigned enable); userland/host_applications/linux/apps/hello_pi/hello_fft/makefile000066400000000000000000000015271421703157200257640ustar00rootroot00000000000000S = hex/shader_256.hex \ hex/shader_512.hex \ hex/shader_1k.hex \ hex/shader_2k.hex \ hex/shader_4k.hex \ hex/shader_8k.hex \ hex/shader_16k.hex \ hex/shader_32k.hex \ hex/shader_64k.hex \ hex/shader_128k.hex \ hex/shader_256k.hex \ hex/shader_512k.hex \ hex/shader_1024k.hex \ hex/shader_2048k.hex \ hex/shader_4096k.hex C = mailbox.c gpu_fft.c gpu_fft_base.c gpu_fft_twiddles.c gpu_fft_shaders.c C1D = $(C) hello_fft.c C2D = $(C) hello_fft_2d.c gpu_fft_trans.c H1D = gpu_fft.h mailbox.h H2D = gpu_fft.h mailbox.h gpu_fft_trans.h hello_fft_2d_bitmap.h F = -lrt -lm -ldl all: hello_fft.bin hello_fft_2d.bin hello_fft.bin: $(S) $(C1D) $(H1D) gcc -o hello_fft.bin $(F) $(C1D) hello_fft_2d.bin: $(S) hex/shader_trans.hex $(C2D) $(H2D) gcc -o hello_fft_2d.bin $(F) $(C2D) clean: rm -f *.bin userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/000077500000000000000000000000001421703157200252205ustar00rootroot00000000000000userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft.qinc000066400000000000000000000325511421703157200275340ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 3.0 # # Copyright (c) 2015, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. ############################################################################## # Bit-rotated write .set PASS16_STRIDE, ((1<> (1<> (1<> 1 mov.ifnz r1, r1; mov.ifz r1, r2 << 1 .endm ############################################################################## .macro read_rev, stride add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx bit_rev 1, rx_0x5555 # 16 SIMD bit_rev 2, rx_0x3333 bit_rev 4, rx_0x0F0F bit_rev 8, rx_0x00FF # reversal creates left shift by 16-STAGES .if STAGES>13 shl r0, r0, STAGES-13 .endif .if STAGES<13 shr r0, r0, 13-STAGES .endif # r0 = re = {idx[0:STAGES-1], 1'b0, 2'b0} add r1, r0, 4 # r1 = im = {idx[0:STAGES-1], 1'b1, 2'b0} interleave swizzle add t0s, ra_addr_x, r0 add t0s, ra_addr_x, r1 .endm .macro load_rev, stride, call read_rev stride nop; ldtmu0 mov r0, r4; ldtmu0 mov r1, r4 brr ra_link_0, call interleave .endm ############################################################################## .macro read_lin, stride add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx shl r0, r0, 3 add r1, r0, 4 add t0s, ra_addr_x, r0 add t0s, ra_addr_x, r1 .endm .macro load_lin, stride, call read_lin stride brr ra_link_0, call nop; ldtmu0 mov r0, r4; ldtmu0 mov r1, r4 .endm ############################################################################## # Unpack twiddles .macro unpack_twiddles mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 .rep i, 4 and.setf -, elem_num, (8>>i) mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) .endr .endm ############################################################################## # float-float enhanced-precision subtract (corrects rounding errors) .macro df64_sub32, a, b # df64_sub32(float2 &a, float b) fsub r0, a, b # float2 s = twoSub(a.x, b); fsub r1, r0, a fadd r2, r1, b fsub r1, r0, r1 fsub r1, a, r1 fsub r1, r1, r2 fadd r1, r1, a+1 # s.y += a.y; fadd r2, r0, r1 # a = twoSum(s.x, s,y); fsub r2, r2, r0; mov a, r2 fsub r1, r1, r2 fsub r2, a, r2 fsub r0, r0, r2 fadd a+1, r0, r1 .endm ############################################################################## # Rotate twiddles using enhanced-precision trig recurrence .macro rotate, base, step mov r2, ra_tw_re+base; mov r3, rb_tw_im+base nop; fmul r0, r2, ra_tw_re+step # a.cos nop; fmul r1, r2, rb_tw_im+step # b.cos nop; fmul r2, r3, rb_tw_im+step # b.sin fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step # a.sin fsub r3, r3, r1 df64_sub32 ra_tw_re+base, r2 df64_sub32 rb_tw_im+base, r3 .endm .macro next_twiddles_32 rotate TW32, TW32_STEP .endm .macro next_twiddles_16 rotate TW16+3, TW16_STEP unpack_twiddles .endm ############################################################################## # Alternate input/output buffers between stages .macro swap_buffers mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y .endm ############################################################################## # Reset counters and twiddles .macro init_stage, m mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 .ifset TW32 mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 .endif unpack_twiddles mov r0, rb_inst shl r0, r0, m add ra_load_idx, r0, elem_num mov ra_points, 0 mov ra_save_ptr, rb_addr_y .endm ############################################################################## .set LOAD_STRAIGHT, 0 .set LOAD_REVERSED, 1 .macro loader_16, stride, mode .if mode==LOAD_REVERSED load_rev stride, r:fft_16 .else load_lin stride, r:fft_16 .endif .endm .macro body_pass_16, mode loader_16 rb_0x80, mode bra -, ra_save_16 nop mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 .endm .macro body_pass_32, mode loader_16 rb_0xF0, mode mov ra_32_re, r0; mov rb_32_im, r1 loader_16 rb_0x10, mode fft_twiddles_32 bra -, ra_save_32 mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 .endm .macro body_pass_64, mode, step loader_16 rb_0x10, mode mov ra_32_re, r0; mov rb_32_im, r1 loader_16 rb_0x10, mode fft_twiddles_32 fadd ra_64+0, ra_32_re, r0 fadd ra_64+1, rb_32_im, r1 fsub ra_64+2, ra_32_re, r0 fsub ra_64+3, rb_32_im, r1 loader_16 step, mode mov ra_32_re, r0; mov rb_32_im, r1 loader_16 rb_0x10, mode fft_twiddles_32 fsub r3, rb_32_im, r1 fsub r2, ra_32_re, r0 fadd r1, rb_32_im, r1 fadd r0, ra_32_re, r0 nop; fmul rb_32_im, r1, ra_tw_re+TW48 # ir nop; fmul ra_32_re, r1, rb_tw_im+TW48 # ii nop; fmul r1, r0, rb_tw_im+TW48 # ri fadd rb_64+1, r1, rb_32_im; fmul r0, r0, ra_tw_re+TW48 # rr fsub rb_64+0, r0, ra_32_re; fmul ra_32_re, r3, rb_tw_im+TW64 # ii nop; fmul rb_32_im, r3, ra_tw_re+TW64 # ir bra -, ra_save_64 nop; fmul r3, r2, rb_tw_im+TW64 # ri fadd rb_64+3, r3, rb_32_im; fmul r2, r2, ra_tw_re+TW64 # rr fsub rb_64+2, r2, ra_32_re .endm ############################################################################## .macro exit, flag mov interrupt, flag nop; nop; thrend nop nop .endm ############################################################################## userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_1024k.qasm000066400000000000000000000174771421703157200303760ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 3.0 # # Copyright (c) 2015, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. .set STAGES, 20 .include "gpu_fft_ex.qinc" ############################################################################## # Twiddles: src .set TW32_BASE, 0 # rx_tw_shared .set TW16_BASE, 1 .set TW32_P2_STEP, 2 .set TW16_P2_STEP, 3 .set TW32_P3_STEP, 4 .set TW16_P3_STEP, 5 .set TW32_P4_STEP, 6 .set TW16_P4_STEP, 7 .set TW32_P4_BASE, 0 # rx_tw_unique .set TW16_P4_BASE, 1 ############################################################################## # Twiddles: dst .set TW16_STEP, 0 # 1 .set TW32_STEP, 1 # 1 .set TW16, 2 # 5 .set TW32, 7 # 2 ############################################################################## # Registers .set ra_link_0, ra0 # rx_0x00FF00FF rb0 .set ra_save_ptr, ra1 .set rb_vdw_32, rb1 .set ra_temp, ra2 .set rb_vpm_lo, rb2 .set ra_addr_x, ra3 .set rb_addr_y, rb3 # spare ra4 # spare rb4 .set ra_load_idx, ra5 .set rb_inst, rb5 .set ra_sync, ra6 .set rx_sync_slave, rb6 .set ra_points, ra7 .set rb_vpm_hi, rb7 .set ra_link_1, ra8 .set rb_STAGES, rb8 .set ra_32_re, ra9 .set rb_32_im, rb9 .set ra_save_32, ra10 .set rx_save_slave_32, rb10 .set rx_tw_shared, ra11 .set rx_tw_unique, rb11 .set ra_tw_re, ra12 # 9 .set rb_tw_im, rb12 # 9 .set ra_vpm_lo, ra26 .set ra_vpm_hi, ra27 .set ra_vdw_32, ra28 .set rx_0x55555555, ra29 .set rx_0x33333333, ra30 .set rx_0x0F0F0F0F, ra31 .set rx_0x00FF00FF, rb0 .set rx_0x0000FFFF, rb26 .set rb_0x10, rb27 .set rb_0x40, rb28 .set rb_0x80, rb29 .set rb_0xF0, rb30 .set rb_0x100, rb31 ############################################################################## # Constants mov rb_STAGES, STAGES mov rb_0x10, 0x10 mov rb_0x40, 0x40 mov rb_0x80, 0x80 mov rb_0xF0, 0xF0 mov rb_0x100, 0x100 mov rx_0x55555555, 0x55555555 mov rx_0x33333333, 0x33333333 mov rx_0x0F0F0F0F, 0x0F0F0F0F mov rx_0x00FF00FF, 0x00FF00FF mov rx_0x0000FFFF, 0x0000FFFF mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) ############################################################################## # Twiddles: ptr mov rx_tw_shared, unif mov rx_tw_unique, unif ############################################################################## # Instance mov rb_inst, unif inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi ############################################################################## # Master/slave procedures proc ra_save_32, r:1f body_ra_save_32 :1 proc rx_save_slave_32, r:1f body_rx_save_slave_32 :1 proc ra_sync, r:1f body_ra_sync :1 proc rx_sync_slave, r:main body_rx_sync_slave ############################################################################## # Subroutines :fft_16 body_fft_16 :pass_1 body_pass_32 LOAD_REVERSED :pass_2 :pass_3 :pass_4 body_pass_32 LOAD_STRAIGHT ############################################################################## # Top level :main mov.setf r0, rb_inst sub r0, r0, 1 shl r0, r0, 5 add.ifnz ra_sync, rx_sync_slave, r0 mov.ifnz ra_save_32, rx_save_slave_32 :loop mov.setf ra_addr_x, unif # Ping buffer or null mov rb_addr_y, unif # Pong buffer or IRQ enable brr.allz -, r:end ############################################################################## # Pass 1 load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE init_stage 5 read_rev rb_0x10 brr ra_link_1, r:pass_1 nop nop add ra_points, ra_points, rb_0x100 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_1 nop nop add ra_points, ra_points, rb_0x100 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 2 swap_buffers load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP init_stage 5 read_lin rb_0x10 brr ra_link_1, r:pass_2 nop nop add ra_points, ra_points, rb_0x100 mov r0, 0x7FFF and.setf -, ra_points, r0 brr.allnz -, r:pass_2 nop nop add.ifnz ra_points, ra_points, rb_0x100 next_twiddles_32 next_twiddles_16 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_2 nop nop add ra_points, ra_points, rb_0x100 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 3 swap_buffers load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P3_STEP init_stage 5 read_lin rb_0x10 .rep i, 4 brr ra_link_1, r:pass_3 nop nop add ra_points, ra_points, rb_0x100 .endr next_twiddles_32 next_twiddles_16 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_3 mov r0, 3*4*8 sub ra_link_1, ra_link_1, r0 add ra_points, ra_points, rb_0x100 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 4 swap_buffers load_tw rx_tw_unique, TW16+3, TW16_P4_BASE load_tw rx_tw_unique, TW32+0, TW32_P4_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P4_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P4_STEP init_stage 5 read_lin rb_0x10 brr ra_link_1, r:pass_4 nop nop add ra_points, ra_points, rb_0x100 next_twiddles_32 next_twiddles_16 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_4 nop nop add ra_points, ra_points, rb_0x100 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## brr -, r:loop nop nop nop :end exit rb_addr_y userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_128k.qasm000066400000000000000000000173051421703157200303100ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 3.0 # # Copyright (c) 2015, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. .set STAGES, 17 .include "gpu_fft_ex.qinc" ############################################################################## # Twiddles: src .set TW32_BASE, 0 # rx_tw_shared .set TW16_BASE, 1 .set TW16_P2_STEP, 2 .set TW16_P3_STEP, 3 .set TW16_P4_STEP, 4 .set TW16_P4_BASE, 0 # rx_tw_unique ############################################################################## # Twiddles: dst .set TW16_STEP, 0 # 1 .set TW32_STEP, 1 # 1 .set TW16, 2 # 5 .set TW32, 7 # 2 ############################################################################## # Registers .set ra_link_0, ra0 .set rb_vdw_16, rb0 .set ra_save_ptr, ra1 .set rb_vdw_32, rb1 .set ra_temp, ra2 .set rb_vpm_lo, rb2 .set ra_addr_x, ra3 .set rb_addr_y, rb3 .set ra_save_16, ra4 .set rx_save_slave_16, rb4 .set ra_load_idx, ra5 .set rb_inst, rb5 .set ra_sync, ra6 .set rx_sync_slave, rb6 .set ra_points, ra7 .set rb_vpm_hi, rb7 .set ra_link_1, ra8 .set rb_STAGES, rb8 .set ra_32_re, ra9 .set rb_32_im, rb9 .set ra_save_32, ra10 .set rx_save_slave_32, rb10 .set rx_tw_shared, ra11 .set rx_tw_unique, rb11 .set ra_tw_re, ra12 # 9 .set rb_tw_im, rb12 # 9 .set ra_vpm_lo, ra25 .set ra_vpm_hi, ra26 .set ra_vdw_16, ra27 .set ra_vdw_32, ra28 .set rx_0x55555555, ra29 .set rx_0x33333333, ra30 .set rx_0x0F0F0F0F, ra31 .set rx_0x00FF00FF, rb24 .set rx_0x0000FFFF, rb25 .set rb_0x10, rb26 .set rb_0x40, rb27 .set rb_0x80, rb28 .set rb_0xF0, rb29 .set rb_0x100, rb30 .set rb_0xFFF, rb31 ############################################################################## # Constants mov rb_STAGES, STAGES mov rb_0x10, 0x10 mov rb_0x40, 0x40 mov rb_0x80, 0x80 mov rb_0xF0, 0xF0 mov rb_0x100, 0x100 mov rb_0xFFF, 0xFFF mov rx_0x55555555, 0x55555555 mov rx_0x33333333, 0x33333333 mov rx_0x0F0F0F0F, 0x0F0F0F0F mov rx_0x00FF00FF, 0x00FF00FF mov rx_0x0000FFFF, 0x0000FFFF mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) ############################################################################## # Twiddles: ptr mov rx_tw_shared, unif mov rx_tw_unique, unif ############################################################################## # Instance mov rb_inst, unif inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi ############################################################################## # Master/slave procedures proc ra_save_16, r:1f body_ra_save_16 ra_vpm_lo, ra_vdw_16 :1 proc rx_save_slave_16, r:1f body_rx_save_slave_16 ra_vpm_lo :1 proc ra_save_32, r:1f body_ra_save_32 :1 proc rx_save_slave_32, r:1f body_rx_save_slave_32 :1 proc ra_sync, r:1f body_ra_sync :1 proc rx_sync_slave, r:main body_rx_sync_slave ############################################################################## # Subroutines :fft_16 body_fft_16 :pass_1 body_pass_32 LOAD_REVERSED :pass_2 :pass_3 :pass_4 body_pass_16 LOAD_STRAIGHT ############################################################################## # Top level :main mov.setf r0, rb_inst sub r0, r0, 1 shl r0, r0, 5 add.ifnz ra_sync, rx_sync_slave, r0 mov.ifnz ra_save_16, rx_save_slave_16 mov.ifnz ra_save_32, rx_save_slave_32 :loop mov.setf ra_addr_x, unif # Ping buffer or null mov rb_addr_y, unif # Pong buffer or IRQ enable brr.allz -, r:end ############################################################################## # Pass 1 load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE init_stage 5 read_rev rb_0x10 brr ra_link_1, r:pass_1 nop nop add ra_points, ra_points, rb_0x100 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_1 nop nop add ra_points, ra_points, rb_0x100 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 2 swap_buffers load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP init_stage 4 read_lin rb_0x80 brr ra_link_1, r:pass_2 nop nop add ra_points, ra_points, rb_0x80 and.setf -, ra_points, rb_0xFFF brr.allnz -, r:pass_2 nop nop add.ifnz ra_points, ra_points, rb_0x80 next_twiddles_16 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_2 nop nop add ra_points, ra_points, rb_0x80 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 3 swap_buffers load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP init_stage 4 read_lin rb_0x80 .rep i, 2 brr ra_link_1, r:pass_3 nop nop add ra_points, ra_points, rb_0x80 .endr next_twiddles_16 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_3 mov r0, 4*8 sub ra_link_1, ra_link_1, r0 add ra_points, ra_points, rb_0x80 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 4 swap_buffers load_tw rx_tw_unique, TW16+3, TW16_P4_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P4_STEP init_stage 4 read_lin rb_0x80 brr ra_link_1, r:pass_4 nop nop add ra_points, ra_points, rb_0x80 next_twiddles_16 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_4 nop nop add ra_points, ra_points, rb_0x80 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## brr -, r:loop nop nop nop :end exit rb_addr_y userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_16k.qasm000066400000000000000000000157511421703157200302270ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 3.0 # # Copyright (c) 2015, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. .set STAGES, 14 .include "gpu_fft.qinc" ############################################################################## # Twiddles: src .set TW32_BASE, 0 # rx_tw_shared .set TW16_BASE, 1 .set TW32_P2_STEP, 2 .set TW16_P2_STEP, 3 .set TW16_P3_STEP, 4 .set TW16_P3_BASE, 0 # rx_tw_unique ############################################################################## # Twiddles: dst .set TW16_STEP, 0 # 1 .set TW32_STEP, 1 # 1 .set TW16, 2 # 5 .set TW32, 7 # 2 ############################################################################## # Registers .set ra_link_0, ra0 .set rb_vdw_16, rb0 .set ra_save_ptr, ra1 .set rb_vdw_32, rb1 .set ra_temp, ra2 .set rb_vpm_lo, rb2 .set ra_addr_x, ra3 .set rb_addr_y, rb3 .set ra_save_16, ra4 .set rx_save_slave_16, rb4 .set ra_load_idx, ra5 .set rb_inst, rb5 .set ra_sync, ra6 .set rx_sync_slave, rb6 .set ra_points, ra7 .set rb_vpm_hi, rb7 .set ra_link_1, ra8 # rb8 .set ra_32_re, ra9 .set rb_32_im, rb9 .set ra_save_32, ra10 .set rx_save_slave_32, rb10 .set rx_tw_shared, ra11 .set rx_tw_unique, rb11 .set ra_tw_re, ra12 # 9 .set rb_tw_im, rb12 # 9 .set ra_vpm_lo, ra25 .set ra_vpm_hi, ra26 .set ra_vdw_16, ra27 .set ra_vdw_32, ra28 .set rx_0x5555, ra29 .set rx_0x3333, ra30 .set rx_0x0F0F, ra31 .set rx_0x00FF, rb26 .set rb_0x10, rb27 .set rb_0x40, rb28 .set rb_0x80, rb29 .set rb_0xF0, rb30 .set rb_0x100, rb31 ############################################################################## # Constants mov rb_0x10, 0x10 mov rb_0x40, 0x40 mov rb_0x80, 0x80 mov rb_0xF0, 0xF0 mov rb_0x100, 0x100 mov rx_0x5555, 0x5555 mov rx_0x3333, 0x3333 mov rx_0x0F0F, 0x0F0F mov rx_0x00FF, 0x00FF mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) ############################################################################## # Twiddles: ptr mov rx_tw_shared, unif mov rx_tw_unique, unif ############################################################################## # Instance mov rb_inst, unif inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi ############################################################################## # Master/slave procedures proc ra_save_16, r:1f body_ra_save_16 ra_vpm_lo, ra_vdw_16 :1 proc rx_save_slave_16, r:1f body_rx_save_slave_16 ra_vpm_lo :1 proc ra_save_32, r:1f body_ra_save_32 :1 proc rx_save_slave_32, r:1f body_rx_save_slave_32 :1 proc ra_sync, r:1f body_ra_sync :1 proc rx_sync_slave, r:main body_rx_sync_slave ############################################################################## # Subroutines :fft_16 body_fft_16 :pass_1 body_pass_32 LOAD_REVERSED :pass_2 body_pass_32 LOAD_STRAIGHT :pass_3 body_pass_16 LOAD_STRAIGHT ############################################################################## # Top level :main mov.setf r0, rb_inst sub r0, r0, 1 shl r0, r0, 5 add.ifnz ra_sync, rx_sync_slave, r0 mov.ifnz ra_save_16, rx_save_slave_16 mov.ifnz ra_save_32, rx_save_slave_32 :loop mov.setf ra_addr_x, unif # Ping buffer or null mov rb_addr_y, unif # Pong buffer or IRQ enable brr.allz -, r:end ############################################################################## # Pass 1 load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE init_stage 5 read_rev rb_0x10 brr ra_link_1, r:pass_1 nop nop add ra_points, ra_points, rb_0x100 shr.setf -, ra_points, STAGES brr.allz -, r:pass_1 nop nop add ra_points, ra_points, rb_0x100 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 2 swap_buffers load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP init_stage 5 read_lin rb_0x10 .rep i, 2 brr ra_link_1, r:pass_2 nop nop add ra_points, ra_points, rb_0x100 .endr next_twiddles_32 next_twiddles_16 shr.setf -, ra_points, STAGES brr.allz -, r:pass_2 mov r0, 4*8 sub ra_link_1, ra_link_1, r0 add ra_points, ra_points, rb_0x100 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 3 swap_buffers load_tw rx_tw_unique, TW16+3, TW16_P3_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP init_stage 4 read_lin rb_0x80 brr ra_link_1, r:pass_3 nop nop add ra_points, ra_points, rb_0x80 next_twiddles_16 shr.setf -, ra_points, STAGES brr.allz -, r:pass_3 nop nop add ra_points, ra_points, rb_0x80 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## brr -, r:loop nop nop nop :end exit rb_addr_y userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_1k.qasm000066400000000000000000000136531421703157200301400ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 3.0 # # Copyright (c) 2015, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. .set STAGES, 10 .include "gpu_fft.qinc" ############################################################################## # Twiddles: src .set TW32_P1_BASE, 0 # rx_tw_shared .set TW16_P1_BASE, 1 .set TW32_P2_STEP, 2 .set TW16_P2_STEP, 3 .set TW32_P2_BASE, 0 # rx_tw_unique .set TW16_P2_BASE, 1 ############################################################################## # Twiddles: dst .set TW16_STEP, 0 # 1 .set TW32_STEP, 1 # 1 .set TW16, 2 # 5 .set TW32, 7 # 2 ############################################################################## # Registers .set ra_link_0, ra0 # rb0 .set ra_save_ptr, ra1 .set rb_vdw_32, rb1 .set ra_temp, ra2 .set rb_vpm_lo, rb2 .set ra_addr_x, ra3 .set rb_addr_y, rb3 .set ra_save_32, ra4 .set rx_save_slave_32, rb4 .set ra_load_idx, ra5 .set rb_inst, rb5 .set ra_sync, ra6 .set rx_sync_slave, rb6 .set ra_points, ra7 .set rb_vpm_hi, rb7 .set ra_link_1, ra8 # rb8 .set ra_32_re, ra9 .set rb_32_im, rb9 .set rx_tw_shared, ra10 .set rx_tw_unique, rb10 .set ra_tw_re, ra11 # 9 .set rb_tw_im, rb11 # 9 .set ra_vpm_lo, ra27 .set ra_vpm_hi, ra28 .set ra_vdw_32, ra29 .set rb_0x10, rb27 .set rb_0x40, rb28 .set rb_0xF0, rb29 .set rx_0x5555, ra30 .set rx_0x3333, rb30 .set rx_0x0F0F, ra31 .set rx_0x00FF, rb31 ############################################################################## # Constants mov rb_0x10, 0x10 mov rb_0x40, 0x40 mov rb_0xF0, 0xF0 mov rx_0x5555, 0x5555 mov rx_0x3333, 0x3333 mov rx_0x0F0F, 0x0F0F mov rx_0x00FF, 0x00FF mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) ############################################################################## # Twiddles: ptr mov rx_tw_shared, unif mov rx_tw_unique, unif ############################################################################## # Instance mov rb_inst, unif inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi ############################################################################## # Master/slave procedures proc ra_save_32, r:1f body_ra_save_32 :1 proc rx_save_slave_32, r:1f body_rx_save_slave_32 :1 proc ra_sync, r:1f body_ra_sync :1 proc rx_sync_slave, r:main body_rx_sync_slave ############################################################################## # Subroutines :fft_16 body_fft_16 :pass_1 body_pass_32 LOAD_REVERSED :pass_2 body_pass_32 LOAD_STRAIGHT ############################################################################## # Top level :main mov.setf r0, rb_inst sub r0, r0, 1 shl r0, r0, 5 add.ifnz ra_sync, rx_sync_slave, r0 mov.ifnz ra_save_32, rx_save_slave_32 :loop mov.setf ra_addr_x, unif # Ping buffer or null mov rb_addr_y, unif # Pong buffer or IRQ enable brr.allz -, r:end ############################################################################## # Pass 1 load_tw rx_tw_shared, TW16+3, TW16_P1_BASE load_tw rx_tw_shared, TW32+0, TW32_P1_BASE init_stage 5 read_rev rb_0x10 brr ra_link_1, r:pass_1 nop mov r0, 0x100 add ra_points, ra_points, r0 shr.setf -, ra_points, STAGES brr.allz -, r:pass_1 nop mov r0, 0x100 add ra_points, ra_points, r0 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 2 swap_buffers load_tw rx_tw_unique, TW16+3, TW16_P2_BASE load_tw rx_tw_unique, TW32+0, TW32_P2_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP init_stage 5 read_lin rb_0x10 brr ra_link_1, r:pass_2 nop mov r0, 0x100 add ra_points, ra_points, r0 next_twiddles_32 next_twiddles_16 shr.setf -, ra_points, STAGES brr.allz -, r:pass_2 nop mov r0, 0x100 add ra_points, ra_points, r0 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## brr -, r:loop nop nop nop :end exit rb_addr_y userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2048k.qasm000066400000000000000000000204241421703157200303670ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 3.0 # # Copyright (c) 2015, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. .set STAGES, 21 .include "gpu_fft_2048k.qinc" ############################################################################## # Twiddles: src .set TW64_BASE0, 0 # rx_tw_shared .set TW64_BASE1, 1 .set TW32_BASE, 2 .set TW16_BASE, 3 .set TW32_P2_STEP, 4 .set TW16_P2_STEP, 5 .set TW32_P3_STEP, 6 .set TW16_P3_STEP, 7 .set TW32_P4_STEP, 8 .set TW16_P4_STEP, 9 .set TW32_P4_BASE, 0 # rx_tw_unique .set TW16_P4_BASE, 1 ############################################################################## # Twiddles: dst .set TW16_STEP, 0 # 1 .set TW32_STEP, 1 # 1 .set TW16, 2 # 5 .set TW32, 7 # 2 .set TW48, 9 # 1 .set TW64, 10 # 1 ############################################################################## # Registers .set ra_link_0, ra0 .set rb_vpm, rb0 .set ra_save_ptr, ra1 .set rb_vpm_16, rb1 .set ra_temp, ra2 .set rb_vpm_32, rb2 .set ra_addr_x, ra3 .set rb_addr_y, rb3 .set ra_save_32, ra4 .set rx_save_slave_32, rb4 .set ra_load_idx, ra5 .set rb_inst, rb5 .set ra_sync, ra6 .set rx_sync_slave, rb6 .set ra_points, ra7 .set rb_vpm_48, rb7 .set ra_link_1, ra8 .set rb_0x10, rb8 .set ra_32_re, ra9 .set rb_32_im, rb9 .set ra_save_64, ra10 .set rx_save_slave_64, rb10 .set ra_64, ra11 # 4 .set rb_64, rb11 # 4 .set rx_tw_shared, ra15 .set rx_tw_unique, rb15 .set ra_tw_re, ra16 # 11 .set rb_tw_im, rb16 # 11 ############################################################################## # Dual-use registers .set rb_STAGES, rb_64+0 .set rb_0xF0, rb_64+1 .set rb_0x40, rb_64+2 .set ra_vpm_lo, ra_64+0 .set ra_vpm_hi, ra_64+1 .set rb_vpm_lo, rb_vpm_32 .set rb_vpm_hi, rb_vpm_48 .set ra_vdw_32, ra_64+3 .set rb_vdw_32, rb_64+3 ############################################################################## # Constants mov rb_0x10, 0x10 mov r5rep, 0x1D0 ############################################################################## # Twiddles: ptr mov rx_tw_shared, unif mov rx_tw_unique, unif ############################################################################## # Instance mov rb_inst, unif inst_vpm rb_inst, rb_vpm, rb_vpm_16, rb_vpm_32, rb_vpm_48 ############################################################################## # Master/slave procedures proc ra_save_32, r:1f body_ra_save_32 :1 proc rx_save_slave_32, r:1f body_rx_save_slave_32 :1 proc ra_save_64, r:1f body_ra_save_64 :1 proc rx_save_slave_64, r:1f body_rx_save_slave_64 :1 proc ra_sync, r:1f body_ra_sync :1 proc rx_sync_slave, r:main body_rx_sync_slave ############################################################################## # Subroutines :fft_16 body_fft_16 :pass_1 body_pass_64 LOAD_REVERSED, r5 :pass_2 :pass_3 :pass_4 body_pass_32 LOAD_STRAIGHT ############################################################################## # Top level :main mov.setf r0, rb_inst sub r0, r0, 1 shl r0, r0, 5 add.ifnz ra_sync, rx_sync_slave, r0 mov.ifnz ra_save_32, rx_save_slave_32 mov.ifnz ra_save_64, rx_save_slave_64 :loop mov.setf ra_addr_x, unif # Ping buffer or null mov rb_addr_y, unif # Pong buffer or IRQ enable brr.allz -, r:end ############################################################################## # Pass 1 load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE load_tw rx_tw_shared, TW48, TW64_BASE0 load_tw rx_tw_shared, TW64, TW64_BASE1 init_stage 6 read_rev rb_0x10 brr ra_link_1, r:pass_1 nop mov r0, 0x200 add ra_points, ra_points, r0 mov r1, STAGES shr.setf -, ra_points, r1 brr.allz -, r:pass_1 nop mov r0, 0x200 add ra_points, ra_points, r0 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Dual-use registers mov ra_vpm_lo, rb_vpm mov ra_vpm_hi, rb_vpm_16 mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) mov rb_STAGES, STAGES mov rb_0xF0, 0xF0 mov rb_0x40, 0x40 ############################################################################## # Pass 2 swap_buffers load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP init_stage 5 read_lin rb_0x10 brr ra_link_1, r:pass_2 nop mov r0, 0x100 add ra_points, ra_points, r0 mov r0, 0x7FFF and.setf -, ra_points, r0 brr.allnz -, r:pass_2 nop mov r0, 0x100 add.ifnz ra_points, ra_points, r0 next_twiddles_32 next_twiddles_16 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_2 nop mov r0, 0x100 add ra_points, ra_points, r0 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 3 swap_buffers load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P3_STEP init_stage 5 read_lin rb_0x10 .rep i, 4 brr ra_link_1, r:pass_3 nop mov r0, 0x100 add ra_points, ra_points, r0 .endr next_twiddles_32 next_twiddles_16 shr.setf -, ra_points, rb_STAGES mov r0, 0x100 brr.allz -, r:pass_3 add ra_points, ra_points, r0 mov r0, (4-1)*4*8 sub ra_link_1, ra_link_1, r0 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 4 swap_buffers load_tw rx_tw_unique, TW16+3, TW16_P4_BASE load_tw rx_tw_unique, TW32+0, TW32_P4_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P4_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P4_STEP init_stage 5 read_lin rb_0x10 brr ra_link_1, r:pass_4 nop mov r0, 0x100 add ra_points, ra_points, r0 next_twiddles_32 next_twiddles_16 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_4 nop mov r0, 0x100 add ra_points, ra_points, r0 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## brr -, r:loop nop nop nop :end exit rb_addr_y userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2048k.qinc000066400000000000000000000057631421703157200303710ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 3.0 # # Copyright (c) 2015, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. ############################################################################## # Macro baseline .include "gpu_fft_ex.qinc" ############################################################################## # Redefining some macros .macro body_ra_save_64 mov -, vw_wait .rep i, 7 mov -, srel(i+1) # Master releases slaves .endr write_vpm_64 .rep i, 7 mov -, sacq(i+9) # Master waits for slaves .endr mov r0, vdw_setup_0(1, 16, dma_h32(0,0)) mov r1, 0x40 add ra_save_ptr, ra_save_ptr, r1; mov r1, ra_save_ptr mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) mov r3, PASS64_STRIDE .rep i, 64 add r0, r0, r2; mov vw_setup, r0 add r1, r1, r3; mov vw_addr, r1 .endr bra -, ra_link_1 nop nop nop .endm .macro bit_rev, shift, mask mov r2, mask and r1, r0, r2 shr r0, r0, shift and r0, r0, r2 shl r1, r1, shift or r0, r0, r1 .endm .macro read_rev, stride add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx bit_rev 1, 0x55555555 # 16 SIMD bit_rev 2, 0x33333333 bit_rev 4, 0x0F0F0F0F bit_rev 8, 0x00FF00FF bit_rev rb_0x10, 0x0000FFFF shr r0, r0, 32-STAGES-3 # r0 = re = {idx[0:STAGES-1], 1'b0, 2'b0} add r1, r0, 4 # r1 = im = {idx[0:STAGES-1], 1'b1, 2'b0} interleave add t0s, ra_addr_x, r0 add t0s, ra_addr_x, r1 .endm userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_256.qasm000066400000000000000000000137201421703157200301340ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 3.0 # # Copyright (c) 2015, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. .set STAGES, 8 .include "gpu_fft.qinc" ############################################################################## # Twiddles: src .set TW16_P1_BASE, 0 # rx_tw_shared .set TW16_P2_STEP, 1 .set TW16_P2_BASE, 0 # rx_tw_unique ############################################################################## # Twiddles: dst .set TW16_STEP, 0 # 1 .set TW16, 1 # 5 ############################################################################## # Registers .set ra_link_1, ra0 # rb0 .set ra_save_ptr, ra1 # rb1 .set ra_temp, ra2 # rb2 .set ra_addr_x, ra3 .set rb_addr_y, rb3 .set ra_save_16, ra4 .set rx_save_slave_16, rb4 .set ra_load_idx, ra5 .set rb_inst, rb5 .set ra_sync, ra6 .set rx_sync_slave, rb6 .set ra_points, ra7 # rb7 .set rx_tw_shared, ra8 .set rx_tw_unique, rb8 .set ra_tw_re, ra9 # 6 .set rb_tw_im, rb9 # 6 .set ra_vpm, ra27 .set rb_vpm, rb27 .set ra_vdw, ra28 .set rb_vdw, rb28 .set rx_0x5555, ra29 .set rx_0x3333, ra30 .set rx_0x0F0F, ra31 .set rb_0x40, rb30 .set rb_0x80, rb31 ############################################################################## # Register alias .set ra_link_0, ra_save_16 ############################################################################## # Constants mov rb_0x40, 0x40 mov rb_0x80, 0x80 mov rx_0x5555, 0x5555 mov rx_0x3333, 0x3333 mov rx_0x0F0F, 0x0F0F mov ra_vdw, vdw_setup_0(16, 16, dma_h32( 0,0)) mov rb_vdw, vdw_setup_0(16, 16, dma_h32(16,0)) ############################################################################## # Twiddles: ptr mov rx_tw_shared, unif mov rx_tw_unique, unif ############################################################################## # Instance mov rb_inst, unif inst_vpm rb_inst, ra_vpm, rb_vpm, -, - ############################################################################## # Master/slave procedures proc ra_save_16, r:1f body_ra_save_16 ra_vpm, ra_vdw :1 proc rx_save_slave_16, r:1f body_rx_save_slave_16 ra_vpm :1 proc ra_sync, r:1f body_ra_sync :1 proc rx_sync_slave, r:main body_rx_sync_slave ############################################################################## # Redefining this macro .macro read_rev, stride add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx bit_rev 1, rx_0x5555 # 16 SIMD bit_rev 2, rx_0x3333 bit_rev 4, rx_0x0F0F shl r0, r0, 3 # {idx[0:7], 1'b0, 2'b0} add r1, r0, 4 # {idx[0:7], 1'b1, 2'b0} add t0s, ra_addr_x, r0 add t0s, ra_addr_x, r1 .endm ############################################################################## # Subroutines :fft_16 body_fft_16 :pass_1 :pass_2 brr -, r:fft_16 nop; ldtmu0 mov r0, r4; ldtmu0 mov r1, r4 ############################################################################## # Top level :main mov.setf r0, rb_inst sub r0, r0, 1 shl r0, r0, 5 add.ifnz ra_sync, rx_sync_slave, r0 mov.ifnz ra_save_16, rx_save_slave_16 :loop mov.setf ra_addr_x, unif # Ping buffer or null mov rb_addr_y, unif # Pong buffer or IRQ enable brr.allz -, r:end ############################################################################## # Pass 1 load_tw rx_tw_shared, TW16+3, TW16_P1_BASE init_stage 4 read_rev rb_0x80 read_rev rb_0x80 .rep i, 2 brr ra_link_1, r:pass_1 nop mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw .endr bra ra_link_1, ra_sync nop nop nop ############################################################################## # Pass 2 swap_buffers load_tw rx_tw_unique, TW16+3, TW16_P2_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP init_stage 4 read_lin rb_0x80 read_lin rb_0x80 brr ra_link_1, r:pass_2 nop mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw next_twiddles_16 brr ra_link_1, r:pass_2 nop mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw bra ra_link_1, ra_sync nop nop nop ############################################################################## brr -, r:loop nop nop nop :end exit rb_addr_y userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_256k.qasm000066400000000000000000000176211421703157200303130ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 3.0 # # Copyright (c) 2015, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. .set STAGES, 18 .include "gpu_fft_ex.qinc" ############################################################################## # Twiddles: src .set TW32_BASE, 0 # rx_tw_shared .set TW16_BASE, 1 .set TW16_P2_STEP, 2 .set TW16_P3_STEP, 3 .set TW32_P4_STEP, 4 .set TW16_P4_STEP, 5 .set TW32_P4_BASE, 0 # rx_tw_unique .set TW16_P4_BASE, 1 ############################################################################## # Twiddles: dst .set TW16_STEP, 0 # 1 .set TW32_STEP, 1 # 1 .set TW16, 2 # 5 .set TW32, 7 # 2 ############################################################################## # Registers .set ra_link_0, ra0 .set rb_vdw_16, rb0 .set ra_save_ptr, ra1 .set rb_vdw_32, rb1 .set ra_temp, ra2 .set rb_vpm_lo, rb2 .set ra_addr_x, ra3 .set rb_addr_y, rb3 .set ra_save_16, ra4 .set rx_save_slave_16, rb4 .set ra_load_idx, ra5 .set rb_inst, rb5 .set ra_sync, ra6 .set rx_sync_slave, rb6 .set ra_points, ra7 .set rb_vpm_hi, rb7 .set ra_link_1, ra8 .set rb_STAGES, rb8 .set ra_32_re, ra9 .set rb_32_im, rb9 .set ra_save_32, ra10 .set rx_save_slave_32, rb10 .set rx_tw_shared, ra11 .set rx_tw_unique, rb11 .set ra_tw_re, ra12 # 9 .set rb_tw_im, rb12 # 9 .set ra_vpm_lo, ra25 .set ra_vpm_hi, ra26 .set ra_vdw_16, ra27 .set ra_vdw_32, ra28 .set rx_0x55555555, ra29 .set rx_0x33333333, ra30 .set rx_0x0F0F0F0F, ra31 .set rx_0x00FF00FF, rb24 .set rx_0x0000FFFF, rb25 .set rb_0x10, rb26 .set rb_0x40, rb27 .set rb_0x80, rb28 .set rb_0xF0, rb29 .set rb_0x100, rb30 .set rb_0x1FFF, rb31 ############################################################################## # Constants mov rb_STAGES, STAGES mov rb_0x10, 0x10 mov rb_0x40, 0x40 mov rb_0x80, 0x80 mov rb_0xF0, 0xF0 mov rb_0x100, 0x100 mov rb_0x1FFF, 0x1FFF mov rx_0x55555555, 0x55555555 mov rx_0x33333333, 0x33333333 mov rx_0x0F0F0F0F, 0x0F0F0F0F mov rx_0x00FF00FF, 0x00FF00FF mov rx_0x0000FFFF, 0x0000FFFF mov ra_vdw_16, vdw_setup_0( 1, 16, dma_h32( 0,0)) mov rb_vdw_16, vdw_setup_0( 1, 16, dma_h32(32,0)) mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) ############################################################################## # Twiddles: ptr mov rx_tw_shared, unif mov rx_tw_unique, unif ############################################################################## # Instance mov rb_inst, unif inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi ############################################################################## # Master/slave procedures proc ra_save_16, r:1f body_ra_save_16 ra_vpm_lo, ra_vdw_16 :1 proc rx_save_slave_16, r:1f body_rx_save_slave_16 ra_vpm_lo :1 proc ra_save_32, r:1f body_ra_save_32 :1 proc rx_save_slave_32, r:1f body_rx_save_slave_32 :1 proc ra_sync, r:1f body_ra_sync :1 proc rx_sync_slave, r:main body_rx_sync_slave ############################################################################## # Subroutines :fft_16 body_fft_16 :pass_1 body_pass_32 LOAD_REVERSED :pass_2 :pass_3 body_pass_16 LOAD_STRAIGHT :pass_4 body_pass_32 LOAD_STRAIGHT ############################################################################## # Top level :main mov.setf r0, rb_inst sub r0, r0, 1 shl r0, r0, 5 add.ifnz ra_sync, rx_sync_slave, r0 mov.ifnz ra_save_16, rx_save_slave_16 mov.ifnz ra_save_32, rx_save_slave_32 :loop mov.setf ra_addr_x, unif # Ping buffer or null mov rb_addr_y, unif # Pong buffer or IRQ enable brr.allz -, r:end ############################################################################## # Pass 1 load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE init_stage 5 read_rev rb_0x10 brr ra_link_1, r:pass_1 nop nop add ra_points, ra_points, rb_0x100 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_1 nop nop add ra_points, ra_points, rb_0x100 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 2 swap_buffers load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP init_stage 4 read_lin rb_0x80 brr ra_link_1, r:pass_2 nop nop add ra_points, ra_points, rb_0x80 and.setf -, ra_points, rb_0x1FFF brr.allnz -, r:pass_2 nop nop add.ifnz ra_points, ra_points, rb_0x80 next_twiddles_16 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_2 nop nop add ra_points, ra_points, rb_0x80 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 3 swap_buffers load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP init_stage 4 read_lin rb_0x80 .rep i, 4 brr ra_link_1, r:pass_3 nop nop add ra_points, ra_points, rb_0x80 .endr next_twiddles_16 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_3 mov r0, 3*4*8 sub ra_link_1, ra_link_1, r0 add ra_points, ra_points, rb_0x80 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 4 swap_buffers load_tw rx_tw_unique, TW16+3, TW16_P4_BASE load_tw rx_tw_unique, TW32+0, TW32_P4_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P4_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P4_STEP init_stage 5 read_lin rb_0x10 brr ra_link_1, r:pass_4 nop nop add ra_points, ra_points, rb_0x100 next_twiddles_32 next_twiddles_16 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_4 nop nop add ra_points, ra_points, rb_0x100 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## brr -, r:loop nop nop nop :end exit rb_addr_y userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2k.qasm000066400000000000000000000154471421703157200301440ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 3.0 # # Copyright (c) 2015, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. .set STAGES, 11 .include "gpu_fft.qinc" ############################################################################## # Twiddles: src .set TW64_P1_BASE0, 0 # rx_tw_shared .set TW64_P1_BASE1, 1 .set TW32_P1_BASE, 2 .set TW16_P1_BASE, 3 .set TW32_P2_STEP, 4 .set TW16_P2_STEP, 5 .set TW32_P2_BASE, 0 # rx_tw_unique .set TW16_P2_BASE, 1 ############################################################################## # Twiddles: dst .set TW16_STEP, 0 # 1 .set TW32_STEP, 1 # 1 .set TW16, 2 # 5 .set TW32, 7 # 2 .set TW48, 9 # 1 .set TW64, 10 # 1 ############################################################################## # Registers .set ra_link_0, ra0 .set rb_vpm, rb0 .set ra_save_ptr, ra1 .set rb_vpm_16, rb1 .set ra_temp, ra2 .set rb_vpm_32, rb2 .set ra_addr_x, ra3 .set rb_addr_y, rb3 .set ra_save_32, ra4 .set rx_save_slave_32, rb4 .set ra_load_idx, ra5 .set rb_inst, rb5 .set ra_sync, ra6 .set rx_sync_slave, rb6 .set ra_points, ra7 .set rb_vpm_48, rb7 .set ra_link_1, ra8 # rb8 .set ra_32_re, ra9 .set rb_32_im, rb9 .set ra_save_64, ra10 .set rx_save_slave_64, rb10 .set ra_64, ra11 # 4 .set rb_64, rb11 # 4 .set rx_tw_shared, ra15 .set rx_tw_unique, rb15 .set ra_tw_re, ra16 # 11 .set rb_tw_im, rb16 # 11 .set rx_0x5555, ra28 .set rx_0x3333, ra29 .set rx_0x0F0F, ra30 .set rx_0x00FF, ra31 .set rb_0x10, rb28 .set rb_0x40, rb29 .set rb_0xF0, rb30 .set rb_0x1D0, rb31 ############################################################################## # Dual-use registers .set ra_vpm_lo, ra_64+0 .set ra_vpm_hi, ra_64+1 .set rb_vpm_lo, rb_vpm_32 .set rb_vpm_hi, rb_vpm_48 .set ra_vdw_32, ra_64+2 .set rb_vdw_32, rb_64+2 ############################################################################## # Constants mov rb_0x10, 0x10 mov rb_0x40, 0x40 mov rb_0xF0, 0xF0 mov rb_0x1D0, 0x1D0 mov rx_0x5555, 0x5555 mov rx_0x3333, 0x3333 mov rx_0x0F0F, 0x0F0F mov rx_0x00FF, 0x00FF ############################################################################## # Twiddles: ptr mov rx_tw_shared, unif mov rx_tw_unique, unif ############################################################################## # Instance mov rb_inst, unif inst_vpm rb_inst, rb_vpm, rb_vpm_16, rb_vpm_32, rb_vpm_48 ############################################################################## # Master/slave procedures proc ra_save_32, r:1f body_ra_save_32 :1 proc rx_save_slave_32, r:1f body_rx_save_slave_32 :1 proc ra_save_64, r:1f body_ra_save_64 rb_0x40 :1 proc rx_save_slave_64, r:1f body_rx_save_slave_64 :1 proc ra_sync, r:1f body_ra_sync :1 proc rx_sync_slave, r:main body_rx_sync_slave ############################################################################## # Subroutines :fft_16 body_fft_16 :pass_1 body_pass_64 LOAD_REVERSED, rb_0x1D0 :pass_2 body_pass_32 LOAD_STRAIGHT ############################################################################## # Top level :main mov.setf r0, rb_inst sub r0, r0, 1 shl r0, r0, 5 add.ifnz ra_sync, rx_sync_slave, r0 mov.ifnz ra_save_32, rx_save_slave_32 mov.ifnz ra_save_64, rx_save_slave_64 :loop mov.setf ra_addr_x, unif # Ping buffer or null mov rb_addr_y, unif # Pong buffer or IRQ enable brr.allz -, r:end ############################################################################## # Pass 1 load_tw rx_tw_shared, TW16+3, TW16_P1_BASE load_tw rx_tw_shared, TW32+0, TW32_P1_BASE load_tw rx_tw_shared, TW48, TW64_P1_BASE0 load_tw rx_tw_shared, TW64, TW64_P1_BASE1 init_stage 6 read_rev rb_0x10 brr ra_link_1, r:pass_1 nop mov r0, 0x200 add ra_points, ra_points, r0 shr.setf -, ra_points, STAGES brr.allz -, r:pass_1 nop mov r0, 0x200 add ra_points, ra_points, r0 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Dual-use registers mov ra_vpm_lo, rb_vpm mov ra_vpm_hi, rb_vpm_16 mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) ############################################################################## # Pass 2 swap_buffers load_tw rx_tw_unique, TW16+3, TW16_P2_BASE load_tw rx_tw_unique, TW32+0, TW32_P2_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP init_stage 5 read_lin rb_0x10 brr ra_link_1, r:pass_2 nop mov r0, 0x100 add ra_points, ra_points, r0 next_twiddles_32 next_twiddles_16 shr.setf -, ra_points, STAGES brr.allz -, r:pass_2 nop mov r0, 0x100 add ra_points, ra_points, r0 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## brr -, r:loop nop nop nop :end exit rb_addr_y userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_32k.qasm000066400000000000000000000154171421703157200302240ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 3.0 # # Copyright (c) 2015, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. .set STAGES, 15 .include "gpu_fft.qinc" ############################################################################## # Twiddles: src .set TW32_BASE, 0 # rx_tw_shared .set TW16_BASE, 1 .set TW32_P2_STEP, 2 .set TW16_P2_STEP, 3 .set TW32_P3_STEP, 4 .set TW16_P3_STEP, 5 .set TW32_P3_BASE, 0 # rx_tw_unique .set TW16_P3_BASE, 1 ############################################################################## # Twiddles: dst .set TW16_STEP, 0 # 1 .set TW32_STEP, 1 # 1 .set TW16, 2 # 5 .set TW32, 7 # 2 ############################################################################## # Registers .set ra_link_0, ra0 # rb0 .set ra_save_ptr, ra1 .set rb_vdw_32, rb1 .set ra_temp, ra2 .set rb_vpm_lo, rb2 .set ra_addr_x, ra3 .set rb_addr_y, rb3 .set ra_save_32, ra4 .set rx_save_slave_32, rb4 .set ra_load_idx, ra5 .set rb_inst, rb5 .set ra_sync, ra6 .set rx_sync_slave, rb6 .set ra_points, ra7 .set rb_vpm_hi, rb7 .set ra_link_1, ra8 # rb8 .set ra_32_re, ra9 .set rb_32_im, rb9 .set rx_tw_shared, ra10 .set rx_tw_unique, rb10 .set ra_tw_re, ra11 # 9 .set rb_tw_im, rb11 # 9 .set ra_vpm_lo, ra26 .set ra_vpm_hi, ra27 .set ra_vdw_32, ra28 .set rx_0x5555, ra29 .set rx_0x3333, ra30 .set rx_0x0F0F, ra31 .set rx_0x00FF, rb26 .set rb_0x10, rb27 .set rb_0x40, rb28 .set rb_0x80, rb29 .set rb_0xF0, rb30 .set rb_0x100, rb31 ############################################################################## # Constants mov rb_0x10, 0x10 mov rb_0x40, 0x40 mov rb_0x80, 0x80 mov rb_0xF0, 0xF0 mov rb_0x100, 0x100 mov rx_0x5555, 0x5555 mov rx_0x3333, 0x3333 mov rx_0x0F0F, 0x0F0F mov rx_0x00FF, 0x00FF mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) ############################################################################## # Twiddles: ptr mov rx_tw_shared, unif mov rx_tw_unique, unif ############################################################################## # Instance mov rb_inst, unif inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi ############################################################################## # Master/slave procedures proc ra_save_32, r:1f body_ra_save_32 :1 proc rx_save_slave_32, r:1f body_rx_save_slave_32 :1 proc ra_sync, r:1f body_ra_sync :1 proc rx_sync_slave, r:main body_rx_sync_slave ############################################################################## # Subroutines :fft_16 body_fft_16 :pass_1 body_pass_32 LOAD_REVERSED :pass_2 :pass_3 body_pass_32 LOAD_STRAIGHT ############################################################################## # Top level :main mov.setf r0, rb_inst sub r0, r0, 1 shl r0, r0, 5 add.ifnz ra_sync, rx_sync_slave, r0 mov.ifnz ra_save_32, rx_save_slave_32 :loop mov.setf ra_addr_x, unif # Ping buffer or null mov rb_addr_y, unif # Pong buffer or IRQ enable brr.allz -, r:end ############################################################################## # Pass 1 load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE init_stage 5 read_rev rb_0x10 brr ra_link_1, r:pass_1 nop nop add ra_points, ra_points, rb_0x100 shr.setf -, ra_points, STAGES brr.allz -, r:pass_1 nop nop add ra_points, ra_points, rb_0x100 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 2 swap_buffers load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP init_stage 5 read_lin rb_0x10 .rep i, 4 brr ra_link_1, r:pass_2 nop nop add ra_points, ra_points, rb_0x100 .endr next_twiddles_32 next_twiddles_16 shr.setf -, ra_points, STAGES brr.allz -, r:pass_2 mov r0, 3*4*8 sub ra_link_1, ra_link_1, r0 add ra_points, ra_points, rb_0x100 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 3 swap_buffers load_tw rx_tw_unique, TW16+3, TW16_P3_BASE load_tw rx_tw_unique, TW32+0, TW32_P3_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P3_STEP init_stage 5 read_lin rb_0x10 brr ra_link_1, r:pass_3 nop nop add ra_points, ra_points, rb_0x100 next_twiddles_32 next_twiddles_16 shr.setf -, ra_points, STAGES brr.allz -, r:pass_3 nop nop add ra_points, ra_points, rb_0x100 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## brr -, r:loop nop nop nop :end exit rb_addr_y userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_4096k.qasm000066400000000000000000000215411421703157200303750ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 3.0 # # Copyright (c) 2015, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. .set STAGES, 22 .include "gpu_fft_2048k.qinc" ############################################################################## # Twiddles: src .set TW64_BASE0, 0 # rx_tw_shared .set TW64_BASE1, 1 .set TW32_BASE, 2 .set TW16_BASE, 3 .set TW48_P2_STEP, 4 .set TW64_P2_STEP, 5 .set TW32_P2_STEP, 6 .set TW16_P2_STEP, 7 .set TW32_P3_STEP, 8 .set TW16_P3_STEP, 9 .set TW32_P4_STEP, 10 .set TW16_P4_STEP, 11 .set TW32_P4_BASE, 0 # rx_tw_unique .set TW16_P4_BASE, 1 ############################################################################## # Twiddles: dst .set TW16_STEP, 0 # 1 .set TW32_STEP, 1 # 1 .set TW16, 2 # 5 .set TW32, 7 # 2 .set TW48, 9 # 2 .set TW64, 11 # 2 .set TW48_STEP, 13 # 1 .set TW64_STEP, 14 # 1 ############################################################################## # Registers .set ra_link_0, ra0 .set rb_vpm, rb0 .set ra_save_ptr, ra1 .set rb_vpm_16, rb1 .set ra_temp, ra2 .set rb_vpm_32, rb2 .set ra_addr_x, ra3 .set rb_addr_y, rb3 .set ra_save_32, ra4 .set rx_save_slave_32, rb4 .set ra_load_idx, ra5 .set rb_inst, rb5 .set ra_sync, ra6 .set rx_sync_slave, rb6 .set ra_points, ra7 .set rb_vpm_48, rb7 .set ra_link_1, ra8 .set rb_0x10, rb8 .set ra_32_re, ra9 .set rb_32_im, rb9 .set ra_save_64, ra10 .set rx_save_slave_64, rb10 .set ra_64, ra11 # 4 .set rb_64, rb11 # 4 .set rx_tw_shared, ra15 .set rx_tw_unique, rb15 .set ra_tw_re, ra16 # 15 .set rb_tw_im, rb16 # 15 ############################################################################## # Dual-use registers .set rb_STAGES, rb_64+0 .set rb_0xF0, rb_64+1 .set rb_0x40, rb_64+2 .set ra_vpm_lo, ra_64+0 .set ra_vpm_hi, ra_64+1 .set rb_vpm_lo, rb_vpm_32 .set rb_vpm_hi, rb_vpm_48 .set ra_vdw_32, ra_64+3 .set rb_vdw_32, rb_64+3 ############################################################################## # Constants mov rb_0x10, 0x10 mov r5rep, 0x1D0 ############################################################################## # Twiddles: ptr mov rx_tw_shared, unif mov rx_tw_unique, unif ############################################################################## # Instance mov rb_inst, unif inst_vpm rb_inst, rb_vpm, rb_vpm_16, rb_vpm_32, rb_vpm_48 ############################################################################## # Master/slave procedures proc ra_save_32, r:1f body_ra_save_32 :1 proc rx_save_slave_32, r:1f body_rx_save_slave_32 :1 proc ra_save_64, r:1f body_ra_save_64 :1 proc rx_save_slave_64, r:1f body_rx_save_slave_64 :1 proc ra_sync, r:1f body_ra_sync :1 proc rx_sync_slave, r:main body_rx_sync_slave ############################################################################## # Subroutines :fft_16 body_fft_16 :pass_1 body_pass_64 LOAD_REVERSED, r5 :pass_2 body_pass_64 LOAD_STRAIGHT, r5 :pass_3 :pass_4 body_pass_32 LOAD_STRAIGHT ############################################################################## # Top level :main mov.setf r0, rb_inst sub r0, r0, 1 shl r0, r0, 5 add.ifnz ra_sync, rx_sync_slave, r0 mov.ifnz ra_save_32, rx_save_slave_32 mov.ifnz ra_save_64, rx_save_slave_64 :loop mov.setf ra_addr_x, unif # Ping buffer or null mov rb_addr_y, unif # Pong buffer or IRQ enable brr.allz -, r:end ############################################################################## # Pass 1 load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE load_tw rx_tw_shared, TW48+0, TW64_BASE0 load_tw rx_tw_shared, TW64+0, TW64_BASE1 init_stage 6 read_rev rb_0x10 brr ra_link_1, r:pass_1 nop mov r0, 0x200 add ra_points, ra_points, r0 mov r1, STAGES shr.setf -, ra_points, r1 brr.allz -, r:pass_1 nop mov r0, 0x200 add ra_points, ra_points, r0 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 2 swap_buffers load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE load_tw rx_tw_shared, TW48+0, TW64_BASE0 load_tw rx_tw_shared, TW64+0, TW64_BASE1 mov ra_tw_re+TW48+1, 0; mov rb_tw_im+TW48+1, 0 mov ra_tw_re+TW64+1, 0; mov rb_tw_im+TW64+1, 0 load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP load_tw rx_tw_shared, TW48_STEP, TW48_P2_STEP load_tw rx_tw_shared, TW64_STEP, TW64_P2_STEP init_stage 6 read_lin rb_0x10 brr ra_link_1, r:pass_2 nop mov r0, 0x200 add ra_points, ra_points, r0 mov r0, 0xFFFF and.setf -, ra_points, r0 brr.allnz -, r:pass_2 nop mov r0, 0x200 add.ifnz ra_points, ra_points, r0 rotate TW64, TW64_STEP rotate TW48, TW48_STEP next_twiddles_32 next_twiddles_16 mov r1, STAGES shr.setf -, ra_points, r1 brr.allz -, r:pass_2 nop mov r0, 0x200 add ra_points, ra_points, r0 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Dual-use registers mov ra_vpm_lo, rb_vpm mov ra_vpm_hi, rb_vpm_16 mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) mov rb_STAGES, STAGES mov rb_0xF0, 0xF0 mov rb_0x40, 0x40 ############################################################################## # Pass 3 swap_buffers load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P3_STEP init_stage 5 read_lin rb_0x10 brr ra_link_1, r:pass_3 nop mov r0, 0x100 add ra_points, ra_points, r0 mov r0, 0x3FF and.setf -, ra_points, r0 brr.allnz -, r:pass_3 nop mov r0, 0x100 add.ifnz ra_points, ra_points, r0 next_twiddles_32 next_twiddles_16 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_3 nop mov r0, 0x100 add ra_points, ra_points, r0 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 4 swap_buffers load_tw rx_tw_unique, TW16+3, TW16_P4_BASE load_tw rx_tw_unique, TW32+0, TW32_P4_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P4_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P4_STEP init_stage 5 read_lin rb_0x10 brr ra_link_1, r:pass_4 nop mov r0, 0x100 add ra_points, ra_points, r0 next_twiddles_32 next_twiddles_16 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_4 nop mov r0, 0x100 add ra_points, ra_points, r0 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## brr -, r:loop nop nop nop :end exit rb_addr_y userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_4k.qasm000066400000000000000000000156471421703157200301500ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 3.0 # # Copyright (c) 2015, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. .set STAGES, 12 .include "gpu_fft.qinc" ############################################################################## # Twiddles: src .set TW16_BASE, 0 # rx_tw_shared .set TW16_P2_STEP, 1 .set TW16_P3_STEP, 2 .set TW16_P3_BASE, 0 # rx_tw_unique ############################################################################## # Twiddles: dst .set TW16_STEP, 0 # 1 .set TW16, 1 # 5 ############################################################################## # Registers .set ra_link_1, ra0 # rb0 .set ra_save_ptr, ra1 # rb1 .set ra_temp, ra2 # rb2 .set ra_addr_x, ra3 .set rb_addr_y, rb3 .set ra_save_16, ra4 .set rx_save_slave_16, rb4 .set ra_load_idx, ra5 .set rb_inst, rb5 .set ra_sync, ra6 .set rx_sync_slave, rb6 .set ra_points, ra7 # rb7 .set rx_tw_shared, ra8 .set rx_tw_unique, rb8 .set ra_tw_re, ra9 # 6 .set rb_tw_im, rb9 # 6 .set ra_vpm, ra26 .set rb_vpm, rb26 .set ra_vdw, ra27 .set rb_vdw, rb27 .set rx_0x5555, ra28 .set rx_0x3333, ra29 .set rx_0x0F0F, ra30 .set rx_0x00FF, ra31 .set rb_0x20, rb29 .set rb_0x40, rb30 .set rb_0x80, rb31 ############################################################################## # Register alias .set ra_link_0, ra_save_16 ############################################################################## # Constants mov rb_0x20, 0x20 mov rb_0x40, 0x40 mov rb_0x80, 0x80 mov rx_0x5555, 0x5555 mov rx_0x3333, 0x3333 mov rx_0x0F0F, 0x0F0F mov rx_0x00FF, 0x00FF mov ra_vdw, vdw_setup_0(16, 16, dma_h32( 0,0)) mov rb_vdw, vdw_setup_0(16, 16, dma_h32(16,0)) ############################################################################## # Twiddles: ptr mov rx_tw_shared, unif mov rx_tw_unique, unif ############################################################################## # Instance mov rb_inst, unif inst_vpm rb_inst, ra_vpm, rb_vpm, -, - ############################################################################## # Macros .macro swap_vpm_vdw mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw .endm .macro swizzle mov.setf -, [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] mov r2, r0; mov.ifnz r0, r0 << 6 mov r3, r1; mov.ifnz r1, r1 << 6 mov.setf -, [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0] nop; mov.ifnz r0, r2 >> 6 nop; mov.ifnz r1, r3 >> 6 .endm ############################################################################## # Master/slave procedures proc ra_save_16, r:1f body_ra_save_16 ra_vpm, ra_vdw :1 proc rx_save_slave_16, r:1f body_rx_save_slave_16 ra_vpm :1 proc ra_sync, r:1f body_ra_sync :1 proc rx_sync_slave, r:main body_rx_sync_slave ############################################################################## # Subroutines :fft_16 body_fft_16 :pass_1 read_rev rb_0x80 nop; ldtmu0 mov r0, r4; ldtmu0 mov r1, r4 swizzle brr -, r:fft_16 interleave :pass_2 :pass_3 read_lin rb_0x80 brr -, r:fft_16 nop; ldtmu0 mov r0, r4; ldtmu0 mov r1, r4 ############################################################################## # Top level :main mov.setf r0, rb_inst sub r0, r0, 1 shl r0, r0, 5 add.ifnz ra_sync, rx_sync_slave, r0 mov.ifnz ra_save_16, rx_save_slave_16 :loop mov.setf ra_addr_x, unif # Ping buffer or null mov rb_addr_y, unif # Pong buffer or IRQ enable brr.allz -, r:end ############################################################################## # Pass 1 load_tw rx_tw_shared, TW16+3, TW16_BASE init_stage 4 read_rev rb_0x80 brr ra_link_1, r:pass_1 swap_vpm_vdw add ra_points, ra_points, rb_0x80 shr.setf -, ra_points, STAGES brr.allz -, r:pass_1 swap_vpm_vdw add ra_points, ra_points, rb_0x80 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 2 swap_buffers load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP init_stage 4 read_lin rb_0x80 .rep i, 2 brr ra_link_1, r:pass_2 swap_vpm_vdw add ra_points, ra_points, rb_0x80 .endr sub ra_link_1, ra_link_1, rb_0x20 next_twiddles_16 shr.setf -, ra_points, STAGES brr.allz -, r:pass_2 swap_vpm_vdw add ra_points, ra_points, rb_0x80 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 3 swap_buffers load_tw rx_tw_unique, TW16+3, TW16_P3_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP init_stage 4 read_lin rb_0x80 brr ra_link_1, r:pass_3 swap_vpm_vdw add ra_points, ra_points, rb_0x80 next_twiddles_16 shr.setf -, ra_points, STAGES brr.allz -, r:pass_3 swap_vpm_vdw add ra_points, ra_points, rb_0x80 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## brr -, r:loop nop nop nop :end exit rb_addr_y userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_512.qasm000066400000000000000000000140461421703157200301310ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 3.0 # # Copyright (c) 2015, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. .set STAGES, 9 .include "gpu_fft.qinc" ############################################################################## # Twiddles: src .set TW32_P1_BASE, 0 # rx_tw_shared .set TW16_P1_BASE, 1 .set TW16_P2_STEP, 2 .set TW16_P2_BASE, 0 # rx_tw_unique ############################################################################## # Twiddles: dst .set TW16_STEP, 0 # 1 .set TW32_STEP, 1 # 1 .set TW16, 2 # 5 .set TW32, 7 # 2 ############################################################################## # Registers .set ra_link_0, ra0 .set rb_vdw_16, rb0 .set ra_save_ptr, ra1 .set rb_vdw_32, rb1 .set ra_temp, ra2 .set rb_vpm_lo, rb2 .set ra_addr_x, ra3 .set rb_addr_y, rb3 .set ra_save_16, ra4 .set rx_save_slave_16, rb4 .set ra_load_idx, ra5 .set rb_inst, rb5 .set ra_sync, ra6 .set rx_sync_slave, rb6 .set ra_points, ra7 .set rb_vpm_hi, rb7 .set ra_link_1, ra8 # rb8 .set ra_32_re, ra9 .set rb_32_im, rb9 .set ra_save_32, ra10 .set rx_save_slave_32, rb10 .set rx_tw_shared, ra11 .set rx_tw_unique, rb11 .set ra_tw_re, ra12 # 9 .set rb_tw_im, rb12 # 9 .set ra_vpm_lo, ra24 .set ra_vpm_hi, ra25 .set ra_vdw_16, ra26 .set ra_vdw_32, ra27 .set rx_0x5555, ra28 .set rx_0x3333, ra29 .set rx_0x0F0F, ra30 .set rx_0x00FF, ra31 .set rb_0x10, rb28 .set rb_0x40, rb29 .set rb_0x80, rb30 .set rb_0xF0, rb31 ############################################################################## # Constants mov rb_0x10, 0x10 mov rb_0x40, 0x40 mov rb_0x80, 0x80 mov rb_0xF0, 0xF0 mov rx_0x5555, 0x5555 mov rx_0x3333, 0x3333 mov rx_0x0F0F, 0x0F0F mov rx_0x00FF, 0x00FF mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) ############################################################################## # Twiddles: ptr mov rx_tw_shared, unif mov rx_tw_unique, unif ############################################################################## # Instance mov rb_inst, unif inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi ############################################################################## # Master/slave procedures proc ra_save_16, r:1f body_ra_save_16 ra_vpm_lo, ra_vdw_16 :1 proc rx_save_slave_16, r:1f body_rx_save_slave_16 ra_vpm_lo :1 proc ra_save_32, r:1f body_ra_save_32 :1 proc rx_save_slave_32, r:1f body_rx_save_slave_32 :1 proc ra_sync, r:1f body_ra_sync :1 proc rx_sync_slave, r:main body_rx_sync_slave ############################################################################## # Subroutines :fft_16 body_fft_16 :pass_1 body_pass_32 LOAD_REVERSED :pass_2 body_pass_16 LOAD_STRAIGHT ############################################################################## # Top level :main mov.setf r0, rb_inst sub r0, r0, 1 shl r0, r0, 5 add.ifnz ra_sync, rx_sync_slave, r0 mov.ifnz ra_save_16, rx_save_slave_16 mov.ifnz ra_save_32, rx_save_slave_32 :loop mov.setf ra_addr_x, unif # Ping buffer or null mov rb_addr_y, unif # Pong buffer or IRQ enable brr.allz -, r:end ############################################################################## # Pass 1 load_tw rx_tw_shared, TW16+3, TW16_P1_BASE load_tw rx_tw_shared, TW32+0, TW32_P1_BASE init_stage 5 read_rev rb_0x10 brr ra_link_1, r:pass_1 nop nop nop brr ra_link_1, r:pass_1 nop nop nop bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 2 swap_buffers load_tw rx_tw_unique, TW16+3, TW16_P2_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP init_stage 4 read_lin rb_0x80 brr ra_link_1, r:pass_2 nop nop add ra_points, ra_points, rb_0x80 next_twiddles_16 shr.setf -, ra_points, STAGES brr.allz -, r:pass_2 nop nop add ra_points, ra_points, rb_0x80 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## brr -, r:loop nop nop nop :end exit rb_addr_y userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_512k.qasm000066400000000000000000000177701421703157200303130ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 3.0 # # Copyright (c) 2015, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. .set STAGES, 19 .include "gpu_fft_ex.qinc" ############################################################################## # Twiddles: src .set TW32_BASE, 0 # rx_tw_shared .set TW16_BASE, 1 .set TW16_P2_STEP, 2 .set TW32_P3_STEP, 3 .set TW16_P3_STEP, 4 .set TW32_P4_STEP, 5 .set TW16_P4_STEP, 6 .set TW32_P4_BASE, 0 # rx_tw_unique .set TW16_P4_BASE, 1 ############################################################################## # Twiddles: dst .set TW16_STEP, 0 # 1 .set TW32_STEP, 1 # 1 .set TW16, 2 # 5 .set TW32, 7 # 2 ############################################################################## # Registers .set ra_link_0, ra0 .set rb_vdw_16, rb0 .set ra_save_ptr, ra1 .set rb_vdw_32, rb1 .set ra_temp, ra2 .set rb_vpm_lo, rb2 .set ra_addr_x, ra3 .set rb_addr_y, rb3 .set ra_save_16, ra4 .set rx_save_slave_16, rb4 .set ra_load_idx, ra5 .set rb_inst, rb5 .set ra_sync, ra6 .set rx_sync_slave, rb6 .set ra_points, ra7 .set rb_vpm_hi, rb7 .set ra_link_1, ra8 .set rb_STAGES, rb8 .set ra_32_re, ra9 .set rb_32_im, rb9 .set ra_save_32, ra10 .set rx_save_slave_32, rb10 .set rx_tw_shared, ra11 .set rx_tw_unique, rb11 .set ra_tw_re, ra12 # 9 .set rb_tw_im, rb12 # 9 .set ra_vpm_lo, ra25 .set ra_vpm_hi, ra26 .set ra_vdw_16, ra27 .set ra_vdw_32, ra28 .set rx_0x55555555, ra29 .set rx_0x33333333, ra30 .set rx_0x0F0F0F0F, ra31 .set rx_0x00FF00FF, rb25 .set rx_0x0000FFFF, rb26 .set rb_0x10, rb27 .set rb_0x40, rb28 .set rb_0x80, rb29 .set rb_0xF0, rb30 .set rb_0x100, rb31 ############################################################################## # Constants mov rb_STAGES, STAGES mov rb_0x10, 0x10 mov rb_0x40, 0x40 mov rb_0x80, 0x80 mov rb_0xF0, 0xF0 mov rb_0x100, 0x100 mov rx_0x55555555, 0x55555555 mov rx_0x33333333, 0x33333333 mov rx_0x0F0F0F0F, 0x0F0F0F0F mov rx_0x00FF00FF, 0x00FF00FF mov rx_0x0000FFFF, 0x0000FFFF mov ra_vdw_16, vdw_setup_0(1, 16, dma_h32( 0,0)) mov rb_vdw_16, vdw_setup_0(1, 16, dma_h32(32,0)) mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) ############################################################################## # Twiddles: ptr mov rx_tw_shared, unif mov rx_tw_unique, unif ############################################################################## # Instance mov rb_inst, unif inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi ############################################################################## # Master/slave procedures proc ra_save_16, r:1f body_ra_save_16 ra_vpm_lo, ra_vdw_16 :1 proc rx_save_slave_16, r:1f body_rx_save_slave_16 ra_vpm_lo :1 proc ra_save_32, r:1f body_ra_save_32 :1 proc rx_save_slave_32, r:1f body_rx_save_slave_32 :1 proc ra_sync, r:1f body_ra_sync :1 proc rx_sync_slave, r:main body_rx_sync_slave ############################################################################## # Subroutines :fft_16 body_fft_16 :pass_1 body_pass_32 LOAD_REVERSED :pass_2 body_pass_16 LOAD_STRAIGHT :pass_3 :pass_4 body_pass_32 LOAD_STRAIGHT ############################################################################## # Top level :main mov.setf r0, rb_inst sub r0, r0, 1 shl r0, r0, 5 add.ifnz ra_sync, rx_sync_slave, r0 mov.ifnz ra_save_16, rx_save_slave_16 mov.ifnz ra_save_32, rx_save_slave_32 :loop mov.setf ra_addr_x, unif # Ping buffer or null mov rb_addr_y, unif # Pong buffer or IRQ enable brr.allz -, r:end ############################################################################## # Pass 1 load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE init_stage 5 read_rev rb_0x10 brr ra_link_1, r:pass_1 nop nop add ra_points, ra_points, rb_0x100 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_1 nop nop add ra_points, ra_points, rb_0x100 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 2 swap_buffers load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP init_stage 4 read_lin rb_0x80 brr ra_link_1, r:pass_2 nop nop add ra_points, ra_points, rb_0x80 mov r0, 0x3FFF and.setf -, ra_points, r0 brr.allnz -, r:pass_2 nop nop add.ifnz ra_points, ra_points, rb_0x80 next_twiddles_16 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_2 nop nop add ra_points, ra_points, rb_0x80 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 3 swap_buffers load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P3_STEP init_stage 5 read_lin rb_0x10 .rep i, 4 brr ra_link_1, r:pass_3 nop nop add ra_points, ra_points, rb_0x100 .endr next_twiddles_32 next_twiddles_16 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_3 mov r0, 3*4*8 sub ra_link_1, ra_link_1, r0 add ra_points, ra_points, rb_0x100 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 4 swap_buffers load_tw rx_tw_unique, TW16+3, TW16_P4_BASE load_tw rx_tw_unique, TW32+0, TW32_P4_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P4_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P4_STEP init_stage 5 read_lin rb_0x10 brr ra_link_1, r:pass_4 nop nop add ra_points, ra_points, rb_0x100 next_twiddles_32 next_twiddles_16 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_4 nop nop add ra_points, ra_points, rb_0x100 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## brr -, r:loop nop nop nop :end exit rb_addr_y userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_64k.qasm000066400000000000000000000171631421703157200302310ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 3.0 # # Copyright (c) 2015, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. .set STAGES, 16 .include "gpu_fft.qinc" ############################################################################## # Twiddles: src .set TW64_BASE0, 0 # rx_tw_shared .set TW64_BASE1, 1 .set TW32_BASE, 2 .set TW16_BASE, 3 .set TW32_P2_STEP, 4 .set TW16_P2_STEP, 5 .set TW32_P3_STEP, 6 .set TW16_P3_STEP, 7 .set TW32_P3_BASE, 0 # rx_tw_unique .set TW16_P3_BASE, 1 ############################################################################## # Twiddles: dst .set TW16_STEP, 0 # 1 .set TW32_STEP, 1 # 1 .set TW16, 2 # 5 .set TW32, 7 # 2 .set TW48, 9 # 1 .set TW64, 10 # 1 ############################################################################## # Registers .set ra_link_0, ra0 .set rb_vpm, rb0 .set ra_save_ptr, ra1 .set rb_vpm_16, rb1 .set ra_temp, ra2 .set rb_vpm_32, rb2 .set ra_addr_x, ra3 .set rb_addr_y, rb3 .set ra_save_32, ra4 .set rx_save_slave_32, rb4 .set ra_load_idx, ra5 .set rb_inst, rb5 .set ra_sync, ra6 .set rx_sync_slave, rb6 .set ra_points, ra7 .set rb_vpm_48, rb7 .set ra_link_1, ra8 .set rb_0x10, rb8 .set ra_32_re, ra9 .set rb_32_im, rb9 .set ra_save_64, ra10 .set rx_save_slave_64, rb10 .set ra_64, ra11 # 4 .set rb_64, rb11 # 4 .set rx_tw_shared, ra15 .set rx_tw_unique, rb15 .set ra_tw_re, ra16 # 11 .set rb_tw_im, rb16 # 11 .set rx_0x5555, ra30 .set rx_0x3333, rb30 .set rx_0x0F0F, ra31 .set rx_0x00FF, rb31 ############################################################################## # Dual-use registers .set rb_STAGES, rb_0x10 .set rb_3x4x8, rb_64+0 .set rb_0xF0, rb_64+1 .set rb_0x40, rb_64+2 .set ra_vpm_lo, ra_64+0 .set ra_vpm_hi, ra_64+1 .set rb_vpm_lo, rb_vpm_32 .set rb_vpm_hi, rb_vpm_48 .set ra_vdw_32, ra_64+3 .set rb_vdw_32, rb_64+3 ############################################################################## # Constants mov rb_0x10, 0x10 mov r5rep, 0x1D0 mov rx_0x5555, 0x5555 mov rx_0x3333, 0x3333 mov rx_0x0F0F, 0x0F0F mov rx_0x00FF, 0x00FF ############################################################################## # Twiddles: ptr mov rx_tw_shared, unif mov rx_tw_unique, unif ############################################################################## # Instance mov rb_inst, unif inst_vpm rb_inst, rb_vpm, rb_vpm_16, rb_vpm_32, rb_vpm_48 ############################################################################## # Master/slave procedures proc ra_save_32, r:1f body_ra_save_32 :1 proc rx_save_slave_32, r:1f body_rx_save_slave_32 :1 proc ra_save_64, r:1f mov r0, 0x40 body_ra_save_64 r0 :1 proc rx_save_slave_64, r:1f body_rx_save_slave_64 :1 proc ra_sync, r:1f body_ra_sync :1 proc rx_sync_slave, r:main body_rx_sync_slave ############################################################################## # Subroutines :fft_16 body_fft_16 :pass_1 body_pass_64 LOAD_REVERSED, r5 :pass_2 :pass_3 body_pass_32 LOAD_STRAIGHT ############################################################################## # Top level :main mov.setf r0, rb_inst sub r0, r0, 1 shl r0, r0, 5 add.ifnz ra_sync, rx_sync_slave, r0 mov.ifnz ra_save_32, rx_save_slave_32 mov.ifnz ra_save_64, rx_save_slave_64 :loop mov.setf ra_addr_x, unif # Ping buffer or null mov rb_addr_y, unif # Pong buffer or IRQ enable brr.allz -, r:end ############################################################################## # Pass 1 load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE load_tw rx_tw_shared, TW48, TW64_BASE0 load_tw rx_tw_shared, TW64, TW64_BASE1 init_stage 6 read_rev rb_0x10 brr ra_link_1, r:pass_1 nop mov r0, 0x200 add ra_points, ra_points, r0 mov r1, STAGES shr.setf -, ra_points, r1 brr.allz -, r:pass_1 nop mov r0, 0x200 add ra_points, ra_points, r0 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Dual-use registers mov ra_vpm_lo, rb_vpm mov ra_vpm_hi, rb_vpm_16 mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) mov rb_3x4x8, 3*4*8 mov rb_0xF0, 0xF0 mov rb_0x40, 0x40 ############################################################################## # Pass 2 swap_buffers load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP init_stage 5 read_lin rb_0x10 .rep i, 4 brr ra_link_1, r:pass_2 nop mov r0, 0x100 add ra_points, ra_points, r0 .endr next_twiddles_32 next_twiddles_16 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_2 mov r0, 0x100 add ra_points, ra_points, r0 sub ra_link_1, ra_link_1, rb_3x4x8 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 3 swap_buffers load_tw rx_tw_unique, TW16+3, TW16_P3_BASE load_tw rx_tw_unique, TW32+0, TW32_P3_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP load_tw rx_tw_shared, TW32_STEP, TW32_P3_STEP init_stage 5 read_lin rb_0x10 brr ra_link_1, r:pass_3 nop mov r0, 0x100 add ra_points, ra_points, r0 next_twiddles_32 next_twiddles_16 shr.setf -, ra_points, rb_STAGES brr.allz -, r:pass_3 nop mov r0, 0x100 add ra_points, ra_points, r0 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## brr -, r:loop nop nop nop :end exit rb_addr_y userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_8k.qasm000066400000000000000000000154721421703157200301500ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 3.0 # # Copyright (c) 2015, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. .set STAGES, 13 .include "gpu_fft.qinc" ############################################################################## # Twiddles: src .set TW32_BASE, 0 # rx_tw_shared .set TW16_BASE, 1 .set TW16_P2_STEP, 2 .set TW16_P3_STEP, 3 .set TW16_P3_BASE, 0 # rx_tw_unique ############################################################################## # Twiddles: dst .set TW16_STEP, 0 # 1 .set TW32_STEP, 1 # 1 .set TW16, 2 # 5 .set TW32, 7 # 2 ############################################################################## # Registers .set ra_link_0, ra0 .set rb_vdw_16, rb0 .set ra_save_ptr, ra1 .set rb_vdw_32, rb1 .set ra_temp, ra2 .set rb_vpm_lo, rb2 .set ra_addr_x, ra3 .set rb_addr_y, rb3 .set ra_save_16, ra4 .set rx_save_slave_16, rb4 .set ra_load_idx, ra5 .set rb_inst, rb5 .set ra_sync, ra6 .set rx_sync_slave, rb6 .set ra_points, ra7 .set rb_vpm_hi, rb7 .set ra_link_1, ra8 # rb8 .set ra_32_re, ra9 .set rb_32_im, rb9 .set ra_save_32, ra10 .set rx_save_slave_32, rb10 .set rx_tw_shared, ra11 .set rx_tw_unique, rb11 .set ra_tw_re, ra12 # 9 .set rb_tw_im, rb12 # 9 .set ra_vpm_lo, ra25 .set ra_vpm_hi, ra26 .set ra_vdw_16, ra27 .set ra_vdw_32, ra28 .set rx_0x5555, ra29 .set rx_0x3333, ra30 .set rx_0x0F0F, ra31 .set rx_0x00FF, rb26 .set rb_0x10, rb27 .set rb_0x40, rb28 .set rb_0x80, rb29 .set rb_0xF0, rb30 .set rb_0x100, rb31 ############################################################################## # Constants mov rb_0x10, 0x10 mov rb_0x40, 0x40 mov rb_0x80, 0x80 mov rb_0xF0, 0xF0 mov rb_0x100, 0x100 mov rx_0x5555, 0x5555 mov rx_0x3333, 0x3333 mov rx_0x0F0F, 0x0F0F mov rx_0x00FF, 0x00FF mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) ############################################################################## # Twiddles: ptr mov rx_tw_shared, unif mov rx_tw_unique, unif ############################################################################## # Instance mov rb_inst, unif inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi ############################################################################## # Master/slave procedures proc ra_save_16, r:1f body_ra_save_16 ra_vpm_lo, ra_vdw_16 :1 proc rx_save_slave_16, r:1f body_rx_save_slave_16 ra_vpm_lo :1 proc ra_save_32, r:1f body_ra_save_32 :1 proc rx_save_slave_32, r:1f body_rx_save_slave_32 :1 proc ra_sync, r:1f body_ra_sync :1 proc rx_sync_slave, r:main body_rx_sync_slave ############################################################################## # Subroutines :fft_16 body_fft_16 :pass_1 body_pass_32 LOAD_REVERSED :pass_2 :pass_3 body_pass_16 LOAD_STRAIGHT ############################################################################## # Top level :main mov.setf r0, rb_inst sub r0, r0, 1 shl r0, r0, 5 add.ifnz ra_sync, rx_sync_slave, r0 mov.ifnz ra_save_16, rx_save_slave_16 mov.ifnz ra_save_32, rx_save_slave_32 :loop mov.setf ra_addr_x, unif # Ping buffer or null mov rb_addr_y, unif # Pong buffer or IRQ enable brr.allz -, r:end ############################################################################## # Pass 1 load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW32+0, TW32_BASE init_stage 5 read_rev rb_0x10 brr ra_link_1, r:pass_1 nop nop add ra_points, ra_points, rb_0x100 shr.setf -, ra_points, STAGES brr.allz -, r:pass_1 nop nop add ra_points, ra_points, rb_0x100 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 2 swap_buffers load_tw rx_tw_shared, TW16+3, TW16_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP init_stage 4 read_lin rb_0x80 .rep i, 2 brr ra_link_1, r:pass_2 nop nop add ra_points, ra_points, rb_0x80 .endr next_twiddles_16 shr.setf -, ra_points, STAGES brr.allz -, r:pass_2 mov r0, 4*8 sub ra_link_1, ra_link_1, r0 add ra_points, ra_points, rb_0x80 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## # Pass 3 swap_buffers load_tw rx_tw_unique, TW16+3, TW16_P3_BASE load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP init_stage 4 read_lin rb_0x80 brr ra_link_1, r:pass_3 nop nop add ra_points, ra_points, rb_0x80 next_twiddles_16 shr.setf -, ra_points, STAGES brr.allz -, r:pass_3 nop nop add ra_points, ra_points, rb_0x80 bra ra_link_1, ra_sync nop ldtmu0 ldtmu0 ############################################################################## brr -, r:loop nop nop nop :end exit rb_addr_y userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_ex.qinc000066400000000000000000000066321421703157200302310ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 3.0 # # Copyright (c) 2015, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. ############################################################################## # Macro baseline .include "gpu_fft.qinc" ############################################################################## # Redefining some macros .if STAGES>16 .macro read_rev, stride add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx bit_rev 1, rx_0x55555555 # 16 SIMD bit_rev 2, rx_0x33333333 bit_rev 4, rx_0x0F0F0F0F bit_rev 8, rx_0x00FF00FF bit_rev rb_0x10, rx_0x0000FFFF shr r0, r0, 32-STAGES-3 # r0 = re = {idx[0:STAGES-1], 1'b0, 2'b0} add r1, r0, 4 # r1 = im = {idx[0:STAGES-1], 1'b1, 2'b0} interleave add t0s, ra_addr_x, r0 add t0s, ra_addr_x, r1 .endm .endif .if STAGES>17 .macro body_ra_save_16, arg_vpm, arg_vdw write_vpm_16 arg_vpm mov -, vw_wait .rep i, 7 mov -, sacq(i+9) # Master waits for slave mov -, srel(i+1) # Master releases slave .endr mov r0, arg_vdw add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) mov r3, PASS16_STRIDE .rep i, 16 add r0, r0, r2; mov vw_setup, r0 add r1, r1, r3; mov vw_addr, r1 .endr bra -, ra_link_1 nop nop nop .endm .endif .if STAGES>18 .macro body_ra_save_32 write_vpm_32 mov -, vw_wait .rep i, 7 mov -, sacq(i+9) # Master waits for slave mov -, srel(i+1) # Master releases slave .endr mov r0, ra_vdw_32 add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) mov r3, PASS32_STRIDE .rep i, 32 add r0, r0, r2; mov vw_setup, r0 add r1, r1, r3; mov vw_addr, r1 .endr bra -, ra_link_1 nop nop nop .endm .endif userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_trans.qasm000066400000000000000000000077611421703157200307570ustar00rootroot00000000000000# BCM2835 "GPU_FFT" release 2.0 # # Copyright (c) 2014, Andrew Holme. # 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 name of the copyright holder nor the # names of its 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. .set rb_offsets_re, rb0 # 8 .set rb_offsets_im, rb8 # 8 .set rb_0x10, rb16 .set rb_X_STRIDE, rb17 .set rb_Y_STRIDE_SRC, rb18 .set rb_Y_STRIDE_DST, rb19 .set rb_NX, rb20 .set rb_NY, rb21 .set ra_x, ra0 .set ra_y, ra1 .set ra_src_base, ra2 .set ra_dst_base, ra3 .set ra_src_cell, ra4 .set ra_dst_cell, ra5 .set ra_vdw_stride, ra6 mov t0s, unif # src->vc_msg ldtmu0 # r4 = vc_unifs add t0s, r4, 3*4 # 3rd unif ldtmu0 # r4 = src->in add ra_src_base, r4, unif # optional offset mov t0s, unif # dst->vc_msg ldtmu0 # r4 = vc_unifs add t0s, r4, 3*4 # 3rd unif ldtmu0 # r4 = src->in add ra_dst_base, r4, unif # optional offset mov rb_Y_STRIDE_SRC, unif mov rb_Y_STRIDE_DST, unif mov rb_NX, unif mov rb_NY, unif mov rb_X_STRIDE, 2*4 # sizeof complex mov rb_0x10, 0x10 mov r0, vdw_setup_1(0) add r0, r0, rb_Y_STRIDE_DST mov r1, 16*4 sub ra_vdw_stride, r0, r1 nop; mul24 r0, elem_num, rb_X_STRIDE .rep i, 8 mov rb_offsets_re+i, r0 add rb_offsets_im+i, r0, 4 add r0, r0, rb_Y_STRIDE_SRC .endr mov ra_y, 0 :outer mov ra_x, 0 :inner nop; mul24 r1, ra_y, rb_Y_STRIDE_SRC nop; mul24 r0, ra_x, rb_X_STRIDE add r0, r0, r1 add ra_src_cell, ra_src_base, r0 nop; mul24 r1, ra_x, rb_Y_STRIDE_DST nop; mul24 r0, ra_y, rb_X_STRIDE add r0, r0, r1 add ra_dst_cell, ra_dst_base, r0 mov vw_setup, vpm_setup(16, 1, v32(0,0)) add t0s, ra_src_cell, rb_offsets_re add t1s, ra_src_cell, rb_offsets_im .rep i, 7 add t0s, ra_src_cell, rb_offsets_re+1+i add t1s, ra_src_cell, rb_offsets_im+1+i ldtmu0 mov vpm, r4 ldtmu1 mov vpm, r4 .endr ldtmu0 mov vpm, r4 ldtmu1 mov vpm, r4 mov vw_setup, vdw_setup_0(16, 16, dma_h32(0,0)) mov vw_setup, ra_vdw_stride mov vw_addr, ra_dst_cell mov -, vw_wait add ra_x, ra_x, rb_0x10 nop sub.setf -, ra_x, rb_NX brr.allnz -, r:inner nop nop nop add ra_y, ra_y, 8 nop sub.setf -, ra_y, rb_NY brr.allnz -, r:outer nop nop nop mov interrupt, 1 nop; nop; thrend nop nop userland/host_applications/linux/apps/hello_pi/hello_font/000077500000000000000000000000001421703157200244465ustar00rootroot00000000000000userland/host_applications/linux/apps/hello_pi/hello_font/CMakeLists.txt000066400000000000000000000003461421703157200272110ustar00rootroot00000000000000set(EXEC hello_font.bin) set(SRCS main.c) add_executable(${EXEC} ${SRCS}) target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) target_link_libraries(${EXEC} vgfont freetype z) install(TARGETS ${EXEC} RUNTIME DESTINATION bin) userland/host_applications/linux/apps/hello_pi/hello_font/Makefile000077500000000000000000000001521421703157200261070ustar00rootroot00000000000000OBJS=main.o BIN=hello_font.bin LDFLAGS+=-lvgfont -lfreetype -lz -lrevision include ../Makefile.include userland/host_applications/linux/apps/hello_pi/hello_font/Vera.ttf000066400000000000000000002006141421703157200260650ustar00rootroot00000000000000OS/2´_ôcëpVPCLTÑŠ^—ëÈ6cmap¤Ãè ±lXcvt ÿÓ9üüfpgmç´ñÄ&`‹gaspH glyf tAÏ&ìŠ~hdmx4ð!ìHheadÝ„¢ÐT6hheaEoëL$hmtx ÆŽ²´Ä0kernÜRÕ™½ -ŠlocaóËÒ=»„maxpG:ë, nameټȵßpost´Z/»¸ôŽprep;ñ øh::_:: dM0­l  ƒ p t › &   Y &  &   c . 5 ` õ s 0¡ & {Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera SansBitstreamVeraSans-RomanRelease 1.10Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.comCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera SansBitstreamVeraSans-RomanRelease 1.10Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.com5¸ËËÁªœ¦¸fqË ²…u¸Ãˉ-˦ðÓª‡ËªJ3ËÙôT´œ99N´R¸çÍ7sÍ`s3¢V¦V9Åɸßsºé3¼Dßͪåªˤ{¸o{RÇÍššoËÍžÓðºƒÕ˜HžÕÁËöƒT3fÓǤ͚sÕ þ+¤´œbœ-ÕÕÕð{T¤¸#Ӹ˦Ãì“ Ó\qÛ…#¨H99`Õš#fy```{œw`ªé`b{Å{´RÍf¼fwÍ;…‰{ÍJ/œœ}oo5jo{®²-–{öƒT7öœáföÍD)fîs¸€@ÿûþúù%ø2÷–öõþôþó%òñ–ð%ïŠAïþî–í–ìúëúêþé:èBçþæ2åäSå–äŠAäSãâ/ãúâ/áþàþß2ÞÝ–ÜþÛÚ}Ù»ØþÖŠAÖ}ÕÔGÕ}ÔGÓÒÓþÒÑþÐþÏþÎþÍ–ÌËÌþËÊ2ÉþÆ…ÆÅÄþÃþÂþÁþÀþ¿þ¾þ½þ¼þ»þº¹†%¹þ¸·»¸þ·¶]·»·€¶µ%¶]@ÿ¶@µ%´þ³–²þ±þ°þ¯þ®d­¬«%¬d«ª«%ª©ŠA©ú¨þ§þ¦þ¥¤þ£¢£2¢¡d ŠA –Ÿþž žþ œ›œd›š›š™ ˜þ—– —þ– •ŠA•–”“”(“’ú‘»‘þ]»€Ž%]@Ž%þŒ‹.Œþ‹.І%ŠA‰ˆ ‰ˆ ‡†%‡d†…†%…„þƒ‚ƒþ‚þ€þþ@ÿ~}}~þ}}|d{T{%zþyþxw v uþtúsúrúqúpþoþnþl!kþjBjSiþh}gBfþeþdþcþbþa:`ú^ ]þ[þZþYX YúX WW2VþUTUBTSSRQJQþP OþNMNþMLþKJKþJIJI IH GþF–E–DþC-CúB»AK@þ?þ>=>=<=<; <@ÿ; :þ9þ878ú76765 65 43 21 2þ1 0/ 0 / .- .- ,2+*%+d*)*%)('%(A'%&% &% $þ#þ"!! dú d BþúBBþdþþþþBþ-B}dþ  þ   þ  þ-þdþ@-þ-þ¸d…++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++¶, °%Id°@QX ÈY!-,°%Id°@QX ÈY!-,  °P° y ¸ÿÿPXY°°%°%#á °P° y ¸ÿÿPXY°°%á-,KPX °ýEDY!-,°%E`D-,KSX°%°%EDY!!-,ED-fþ–f¤@ ûû/ÄÔì1ÔìÔì0!%!!füsüåþ–øòr)5Õ @@ƒ ü<ì2991/äüÌ0K° TX½ @ ÿÀ878Y¶ P ]%3#3#5ËËË¢þþÕýqþ›eŪéÕM@„üüÜì1ô<ì20K°TK°T[X½@ÿÀ878Y@0 @ P ` p   ¿ ]#!#oª$ªÕýÕ+ýÕ+ž¾`@1 ‡  ‡   üÌ91/<Ô<<ü<<Ô<<Ä2ì220@   ]!! !3!!!!#!#!5!!5!þÝT%Dh$i g8þ¡R>þ›h gþÛg¡hþÅ`Tþ¾if…þ²‡þaŸþašþ²™þbžþbž™NšŸªþÓm!(/Õ@U" '&( /)/))/B" ) *!††#Љ*Љ- ) " & 0ü<ìô<ü<ôäì1/äìÄÔäì2Äîî99990KSXíí9í9íY"K° TX½0@00ÿÀ878YK° TK°T[K°T[X½0ÿÀ00@878Y#.'5.546753.'>54&´diÒjfÑoÝÉÚÌd]®SS¯\ãÖãÖdtzqá{þÓ---´@AÈ$¬–£¼ëè¯*.þU#´œ©Ãš jXV`ÕþOnZXhqÿã)ð #'3•@6$%&%&'$'B’ ’.’$’ &Œ($‘4'!%   ! + 1 4üÄìôìîöî991ä2ô<äìîöîî0KSXííY"K° TK° T[K° T[K°T[K°T[K° T[X½4@44ÿÀ878Y"32654&'2#"&546"32654&%3#2#"&546ÑWccWUccUžº» º»ü—VcbWWcd1 üZ ž¼»ŸŸ¹º‘”„‚••‚ƒ•Ü»»ÛÛ»¼Ûa•‚„””„–ùó Û»½ÚÛ¼ºÜÿãþð 0Í@–  † †  † †††  !         B  (('•+•'”$‘Œ .  .'.'!!1üìÄÔÔìÆî99999991/ÆäöæîîÆ9990KSXíí9í9í9í9í9íí9í9ííí9Y"²2]@² " ) **&:4D ^YZ UZZY0g{›š™—• “••"™-  ' (   2'') #**(/2; 49?2J LKFO2VZ Y UY\_2j i`2uy z““—•œœŸš › š 2 2°29]]3267 >73#'#"5467.54632.#"ò[UÔ _¦Iþ{ü;Bº h]ühäƒñþΆ†02Þ¸S¥UWžDiƒ;#Q¡X’Â?@ýøYËr„þþ~þã“YW×€ác?}<¢Å$$¶/1oX3gŪoÕB@ „üì1ôì0K°TK°T[X½@ÿÀ878Y@ @P`p ]#oªÕýÕ+°þò{ O@˜—  Üä2ì991üì0K°TX½@ÿÀ878YK°TX½ÿÀ@878Y#&547{†‚ƒ… –•”—æþ>ççþ;åëÆàßÄì¤þòo @˜— Ü<ôì991üì03#654¤ –••– …ƒƒìþ<ßàþ:ëåÅççÂ=JÃðN@,  ™ ™ ‘    Ô<ä2Ü<ä2991ôÔ<ì2Äì2990%#'%%73%Ãþ™g:þ°rþ°:gþ™:PrPßÂÃbËþ‡yËbÃÂcËyþ‡ËÙÛ #@ œ  Üü<ü<ì1/Ô<ü<Ä0!!#!5!®-ýÓ¨ýÓ-ýÓªýÓ-ª-žÿÃþ@ žƒüìÔÌ1üì073#ðÓ¤Rþ¬þÀ@d߃¶œÜÌ1Ôì0!!dý僤ۮþ·ƒüì1/ì073#ÛÓÓþþÿB²Õ-@BŸ/Ä991ôì0KSXííY"3#ªýøªÕùm‡ÿãð #@   ‘Œ üìôì1äôìî0"32'2#"‹œœû þ÷ûûþ÷ PþÍþÌþÍþÍ3343 þsþ†þ‡þsyzáZÕ K@B     ÔìÄüì1/ì2ôìÔì0KSXY"K°TX½ ÿÀ @878Y´]7!5%3!!þJþ™eÊJü¤ªsH¸HúÕª–Jð¥@'B¡”  ‘   üÄÔìÀÀ91/ì2ôìôì0KSXíí9Y"K°TK°T[K°T[X½@ÿÀ878Y@2UVVzzv‡tvust‚†‚‚‚¨¨]]%!!567>54&#"5>32‰ÁüLs3aM§†_ÓxzÔXèE[þôªªªw‘:m—Iw–BCÌ12èÂ\¥pþëœÿãsð({@. † †     “  “#‘Œ£)&  )üÄÄÔìôì991ìäôäìæîîîî90K°TK°T[X½)@))ÿÀ878Y@ daa d!]!"&'532654&+532654&#"5>32?‘£þÐþè^ÇjTÈm¾Ç¹¥®¶•ž£˜S¾rsÉYæ Ž%ÄÝò%%Ã12–„•¦wps{$&´ Ѳ|«d¤Õ Œ@   B     ÜÔ<Äì291/äÔ<ì290KSXÉÉY"K° TK° T[X½@ÿÀ878Y@* *HYiwŠ+&+6NO O Vfuz… ]] !33##!5þþ5þÕÕÉý^%üãÍü3¨þ `ÞÿãdÕu@#†  ‰   Œ¤  üÄÔìÄî1ääôìæîþÄî90K°TK°T[X½@ÿÀ878YK°TX½ÿÀ@878Y!!>32!"&'532654&#"Ýý ,X,ú$þÔþï^ÃhZÀk­ÊÊ­Q¡TÕªþ’þîêñþõ Ë10¶œœ¶$&ÿã–ð $X@$ †   ¥  ‰"‘Œ% " !%üììôìä1äôäüäîîî90@ËËÍÍÍËˤ²]]"32654&.#">32# !2¤ˆŸŸˆˆŸŸ L›LÈÓ;²káþðâþýþîPL›;º¢¡»»¡¢ºy¸$&þòþïW]þïëæþêyb¥¨hÕc@B üÌÄ991/ôì0KSXííY"K°TX½@ÿÀ878Y@X9Hg°°]]!#!¨ÀýâÓþý3ÕVú+‹ÿã‹ð #/C@%  ' - ‘Œ'£0 $*$ !0üÄìôÄìîî991ìäôìîî990"32654&%&&54632#"$54632654&#"‹¥¥¦¥þ¥‚‘ÿÞßþ‘’£þ÷÷÷þ÷¤H‘ƒ‚““‚ƒ‘Åš‡‡š›†‡šV ²€³Ðг€² "ÆÙèèÙÆat‚‚tt‚‚ÿã‡ð$X@#†  ¥ ‰ ‘Œ%!"" %üìäôìì1äôìæþõîî90@ÄÂÀÀÀÂμé]]7532#"543 !"&2654&#"áLœKÈÓ:²làþûâþ±þåLœ>ˆŸŸˆˆŸŸ¸$& V\ëæþsþ†þŸþ[—º¢¡»»¡¢ºðÃ#@ƒ¦ƒü<ì21/ìôì073#3#ðÓÓÓÓþþ#þžÿÃ# %@ƒžƒ¦  ü<ì2ÔÌ1äüìî03#3#ðÓÓÓ¤R#þýÙ¬þÀ@Ù^Û¦M@*œœœœB¨§$#üì291ôì90KSXííííY" 5Ûûøúþðþ‘þ“¶ѦÑÙ`Û¢@ œœ#ü<Ä21ÔìÔì0!!!!Ùúþúþ¢¨ðªÙ^Û¦O@+œœœœB¨§$#ü<ì91ôì90KSXííííY"55Ùúþð¶þ/¦þ/¶m“°ð$p@+$  †ˆ•‘ƒ   &%ÜÄüìÔìî99991/îöþôîÍ9990K° TX½%@%%ÿÀ878Y¶y z z ]%3##546?>54&#"5>32‡ËËÅ¿8ZZ93ƒlO³a^Ág¸ßHZX/'þþ‘še‚VY5^1YnFC¼98ŸL‰VV/5<4‡þœq¢ L•@2  ©©L43¬0©7¬$©7CM34( (+(I+*(I,=MÜìüìþýþ<Æî991ÔÄüìþíÔÆÅî2Äî990K° TK° T[K°T[K°T[K°T[X½MÿÀMM@878Y@ NN/N?N]32654&#"#"&5463253>54&'&$#"3267#"$'&5476$32úŽ|{zy!<›g¬×Ø«gœ;’¥?@hþÕ°{â`±smiùhZ}þÙ˜¹þ¸€€†ˆ~R½Ôk{KOþÂþè£¤ŽŒ¥¤þHMIùÈÈúKLƒý ß±k¼Pƒ‹A@fþµÁŸþêjhmWQoagƒ}}I½¶J}‡® bæ{þùþÐhÕ º@A       B•    ÔÄ91/<äÔì90KSXííííííííY"² ]@:XvpŒ VXP ghxv|rwx‡ˆ€ ˜™–]] !3#!#¼þî%þ{å9Òˆý_ˆÕý®ú+þÉìÕ C@#• •• ­ . !üì2üìÔì9991/ììôìî90²"]!2654&#!2654&#%!2#!“D££þ¼+”‘‘”þ çú€|•¥þðûýèÉý݇‹Œ…fþ>orqp¦À±‰¢ ˘ÈÚsÿã'ð6@ ¡® •¡®•‘Œ 0üì2ì1äôìôìîöî0´].# !267# !2'fç‚ÿþð‚çfjí„þ­þz†S†íbÕ_^þÇþØþÙþÇ^_ÓHHŸghŸGɰÕ.@• •  2 üìôì99991/ìôì0²`]3 !%! )“ô5þáþËþBŸ²–þhþPþa/ûw.,¦þ—þ€þ~þ–É‹Õ .@•••­   üì2ÔÄÄ1/ììôìî0² ]!!!!!!ɰýÇý9øü>ÕªþFªýãªÉ#Õ )@••­ üì2ÔÄ1/ìôìî0² ]!!!!#ÉZýpPý°ÊÕªþHªý7sÿã‹ð9@ ••¡®•‘Œ43 üìüäüÄ1äôìôìþÔî990%!5!# !2&&# !26Ãþ¶uþæ þ¢þu‹^’opü‹þîþík¨Õ‘¦ýSU™mn™HF×_`þÎþÑþÒþÎ%É;Õ ,@•­ 8  üì2üì21/<ä2üì0²P ]3!3#!#ÉÊÞÊÊý"ÊÕýœdú+Çý9É“Õ9·¯üì1/ì0K°TX½ÿÀ@878Y@ 0@P`Ÿ]3#ÉÊÊÕú+ÿ–þf“Õ M@ •° 9 üìä991äüì990K°TX½ ÿÀ @878Y@ 0 @ P ` Ÿ ]3+53265ÉÊÍãM?†nÕú“þòôª–ÂÉjÕ ï@(B¯  üì2ÔÄ91/<ì290KSXííííY"²]@’ ((764GFCUgvwƒˆ”›ç    (+*66650 A@E@@@ b`hgwp ‹‹Ž š¶µÅÅ×Öèéèê÷øù,]q]q3! !#ÉÊžýþöý3ÊÕý‰wýHüãÏý1ÉjÕ%@ •:üìì1/äì0@ 0P€€]3!!ÉÊ×ü_ÕúÕªÉÕ ¿@4  B ¯   >  üìüì91/<Äì290KSXííííY"²p]@V   && & 45 i|{y €‚‚  #,'( 4<VY ej vy •›]]! !###É-}-ÅþËþÄÕüøú+üúáÉ3Õ y@B¯6 üìüì991/<ì2990KSXííY"² ]@068HGif€ FIWXeiy…Š•šŸ ]]!3!#É–ÄþðýjÄÕûáú+áûsÿãÙð #@•• ‘Œ 3üìüì1äôìî0"32' ! 'ÜþýÜÜþÿÜ:xþˆþÆþÅþ‡yLþ¸þåþæþ¸HH¤þ[þžþŸþ[¤bb¥ÉÕ:@••   ? üì2üì91/ôìÔì0@ ?_¯]32654&#%!2+#“þššþ8ÈûþÿûþÊ/ýÏ’‡†’¦ãÛÝâý¨sþøÙð R@*  B ••‘Œ    3üìüì9991Ääôìî990KSXíí9Y""32#'# ! 'ÜþýÜÜþÿ? ôÝ!#þÅþ‡y;:xÑLþ¸þåþæþ¸HHúÏþÝï¥ab¥þ[þžþüþŽÉTÕ±@5  B• •   ?  üì2üÄì99991/<ôìÔì9990KSXíí9Y"²@]@Bz%%%&'&&& 66FFhuuwˆˆ˜˜]]#.+#! 32654&#A{>ÍÙ¿J‹xÜÊÈüƒý‰þ’••’¼~þh–bý‰ÕÖØºOýƒ…‡ÿã¢ð'~@<    B ¡”••”%‘Œ( "-"(ÜÄìüìä99991äôäìîöîÆ90KSXí9í9Y"²)]¶)/)O)].#"!"&'532654&/.54$32HsÌ_¥³w¦zâ×þÝþçjï€{ìr­¼‡š{âÊõiÚ¤Å76€vce+Ù¶Ùà0/ÐEFˆ~n|-À«Æä&ÿúéÕJ@•@@Ôäüä1/ôì20K° TX½@ÿÀ878Y@  @ p Ÿ ]!!#!ïýîËýîÕªúÕ+²ÿã)ÕK@ •Œ  8Aüìüì1ä2ôì99990K°TX½@ÿÀ878Y¶Ÿ]332653! ²Ë®Ã®ËþßþæþåþßÕüuðÓÓð‹ü\þÜþÖ*$hÕ·@'B¯ÔÄ91/ì290KSXííííY"²P]@b*GGZ}ƒ *&&))% 833<<7HEEIIGYVfiizvvyyu€˜—)]]!3 3JýÆÓÙÚÒýÇÕûéú+D¦Õ {@I      B ¯    ÔÌ91/<ì2290KSXííííííííY"²]@ò  ($ >>4 0 LMB @ Yjkg ` {|€ –•     !   # $ %  <:5306 9 ? 0FFJ@E@BBB@@ D M @@XVY Pfgab```d d d wv{xwtyywpx  †‡ˆ‰… Š —Ÿ¯[]]3 3 3# #DÌ:9ã:9Íþ‰þþÅþÂþÕûîûîú+úð=;Õ ]@F      B ¯   ÔÄÜÄ91/<ì290KSXííííííííY"K° TK° T[K°T[X½ ÿÀ @878Y@¸ '' 486 KX[fkww †€‡‹… ”—–     &()&(' ) 54<;:;4 4 8 ? H O X _ eejjhiil l xyyx}  x €€ƒˆ…„ƒ ”——•“ Ÿ ¯ @]]3 3 # #ÙsuÙþ Ùþ\þYÚÕýÕ+ý3üø{ý…ÿüçÕ”@(B¯@@ Ôäüä91/ì290KSXííííY"² ]@<5000F@@@QQQe„“ &)78@ ghxp Ÿ ]]3 3#Ùž›ÙýðËÕýšfüòý9Ç\Õ ›@B••B ÜÄÔä991/ìôì0KSXííY"K° TK° T[X½ @ ÿÀ878Y@@ )&8HGH    / 59? GJO UYfio wx Ÿ ]]!!!5!s•üPÇû=°ügÕšûoªš‘°þòXS@©²©±CÜüÌ21üìôì0K° TX½ÿÀ@878YK°TK°T[X½@ÿÀ878Y!#3!°¨ððþXùüÿB²Õ-@BŸ/Ä991ôì0KSXííY"#ªªýøÕùm“Çþòo<@©²©±Cü<Üì1üìôì0K°TK°T[X½ÿÀ@878Y!53#5oþXïïøÞÙ¨ÛÕ@ ÜÌ91ôÌ290##¼ÉþHþHÉÕýÓ‹þu-ÿìþþ¬µ©ÄÄ1Ôì0!5ûØþ¬ªð‰f1@ ´³DÜì1ôì0K° TK°T[X½ÿÀ@878Y #o™þºfþŠv{ÿã-{ %¼@'  ©¹ †º¹#¸Œ   E&üìÌÔì22991/ÄäôüôìÆîî9990@n0000 0!0"?'@@@@ @!@"PPPP P!P"P'p'…‡‡‡ ‡!…"' 'ð'000 0!@@@ @!PPP P!``` `!ppp p!€€€ €!]]"326=7#5#"&5463!54&#"5>32¾ß¬o™¹¸¸?¼ˆ¬Ëýû§—`¶Te¾Zóð3f{bsÙ´)LýªfaÁ¢½À‹..ª''üºÿ㤠8@¹  ¹Œ¸—G Füì22ôì1/ìäôÄìÆî0¶`€ ]4&#"326>32#"&'#3å§’’§§’’§ýŽ:±{ÌÿÿÌ{±:¹¹/ËççËËççRdaþ¼þøþøþ¼ad¨qÿãç{?@†ˆ† ˆ ¹¹¸Œ HEüä2ì1äôìþôîõî0@ € ].#"3267#"!2çNP³ÆÆ³PNM¥]ýþÖ-U¢5¬++ãÍÍã++ª$$>:#qÿãZ8@¹¹Œ¸—G Eüìôì221/ìäôÄìÄî0¶`€ ]3#5#"3232654&#"¢¸¸:±|ËÿÿË|±ýǧ’’¨¨’’§¶^ùì¨daDDaþËççËËççqÿã{p@$ †ˆ©¹ »¹¸ ŒKEüìôìÄ91äôìäîîôî90@)?p Ðð?????,// , ooooo ]q]!3267# 32.#"ü² Í·jÇbcÐkþôþÇ)ü⸥ˆš¹^Z¾Ç44®*,8 CþÝÄ—´®ž/øp@ ©‡—¼    Lü<Äü<ÄÄ991/ä2üìî2990K° TX½ÿÀ@878YK°TX½@ÿÀ878Y¶@P ]#"!!##535463ø°cM/þѹ°°®½™Phcü/ÑN»«qþVZ{ (J@#  †¹¹&#¸'¼ ¹½& G E)üÄìôì221/ÄäìäôÄìþÕî990¶`*€* *]4&#"326!"&'5326=#"3253¢¥•”¥¥”•¥¸þþúa¬QQžRµ´9²|ÎüüÎ|²9¸=ÈÜÜÈÇÜÜëþâþé³,*½¿[cb::bcªºd4@ ‡¸ — N  Füì2ôì1/<ìôÄì90²`]#4&#"#3>32d¸||•¬¹¹B³uÁƤý\žŸž¾¤ý‡ýžedïÁy+@¾±¼Fü<ì21/äüì0@  @ P ` p ]3#3#Á¸¸¸¸`û éÿÛþVy D@ ¾ ‡½¼ ±O  Fü<ì2ä991ìäôìî990@ @P`p]3+532653#Á¸£µF1iL¸¸`ûŒÖÀœa™(麜 ¼@)B¼— F üì2ÔÄ91/<ìä90KSXííííY"² ]@_ ')+Vfgsw‚‰Ž“–—£    ('(++@ h` ‰…‰š—ª§¶ÅÖ÷ð÷ð]q]33 ##º¹%ëý®kðýǹüiãýôý¬#ýÝÁy"·—Füì1/ì0@ @P`pð]3#Á¸¸ùìº{"Z@&  ‡ ¸¼PPF#üì2üüüì91/<<äô<Äì290@0$P$p$$ $ $¿$ß$ÿ$ ]>32#4&#"#4&#"#3>32)EÀ‚¯¾¹ru¦¹rw¦¹¹?°yz«‰|võâý\ž¡œ¾¤ý‡ž¢›¿£ý‡`®gb|ºd{6@ ‡¸ ¼ N  Füì2ôì1/<äôÄì90´`Ï]#4&#"#3>32d¸||•¬¹¹B³uÁƤý\žŸž¾¤ý‡`®edïqÿãu{ J@¹¹ ¸Œ QEüìôì1äôìî0@#?{{   {  { ð]"32654&'2#"s”¬«•“¬¬“ðþîðñþïßçÉÉçèÈÇéœþÈþìþíþÇ98ºþV¤{>@¹¹¸Œ½¼ GFüì22ôì1äääôÄìÄî0@ `€ à]%#3>32#"&4&#"326s¹¹:±{ÌÿÿÌ{±8§’’§§’’§¨ý® ªdaþ¼þøþøþ¼aëËççËËççqþVZ{ >@¹  ¹¸Œ½¼ GEüìôì221äääôÄìÆî0@ `€ à]32654&#"#"3253#/§’’¨¨’’§s:±|ËÿÿË|±:¸¸/ËççËËççý®daDDadªùöºJ{0@  ‡¸ ¼ FüÄì21/äôìÄÔÌ90´PŸ].#"#3>32JI,œ§¹¹:º….´˾ý²`®fcoÿãÇ{'ç@<  S  SB †‰†‰¹¹%¸Œ( R"E(üÄìÔìä99991äôìþõîõî90KSXí9í9Y"²']@m   . , , , ; ; ; ; $( ( *//*(() )!$'† † † †      '/)?)_))€)) )ð)]]q.#"#"&'532654&/.54632‹N¨Z‰‰b”?Ä¥÷ØZÃlfÆa‚Œe«@«˜àÎf´?®((TT@I!*™‰œ¶##¾55YQKP%$•‚ž¬7òž8@©¼‡  Fü<Äü<Ä2991/ìô<Äì2990²¯]!!;#"&5#53w{þ…Ks½½Õ¢‡‡žþÂý ‰NšŸÒ`>®ÿãX`6@ ‡Œ ¼  NFüìôì21/ä2ôÄì90´`Ï]332653#5#"&®¸||•­¸¸C±uÁȺ¦ýaŸŸ¾¤{û ¬fcð=`@'B¿ÔÄ91/ì290KSXííííY"K° TX½ÿÀ@878YK°TK°T[X½@ÿÀ878Y@ŽHj{†€‘¤  &&)) 55::0FFIIFH@VVYYPffiigh`ut{{uz……‰‰‰†––—š˜˜—¨§°Àßÿ>]]3 3#=Ã^^Ãþ\ú`üT¬û V5` @IU U U U   B ¿    ÔÌ91/<ì2290KSXííííííííY"K° TK°T[K°T[K°T[K° T[X½ ÿÀ @878YK° TK° T[K°T[X½ @ ÿÀ878Y@ÿ" 5 IIF @ [[U P nnf yy‡™˜” ¼¼ÎÇÏ         %%#'!%""%' $ ! # 9669 0FHF@B@@@D D D @@VVVPQRRPS T U cdejejjjn a g ouuy}x}zzxy  { v } ‡ˆ——”“œ›˜˜™@/– Ÿ¦¦¤¤««©©«¤ ¯µ±½»¸ ¿ÄÃÌÊy]]333# #V¸æåÙæå¸þÛÙñòÙ`ü–jü–jû –üj;y` Z@F      B ¿  ÔÄÔÄ91/<ì290KSXííííííííY"K° TK°T[K°T[K°T[X½ ÿÀ @878YK°TX½ @ ÿÀ878Y@˜   & =1 UWX f vzvt ‚ ™Ÿ—’ ¦©¯¥£       )&% * :9746 9 0 IFE J @ YVYYWVYVV Y P o x ›”«¤° Ï ß ÿ /]] # # 3 dþkªÙþºþºÙ³þrÙ))`ýßýÁ¸þHJþq=þV`¢@C        B  ‡½ ¼  ÔÄÄ91ä2ôì9990KSXíííííí2Y"K° TK°T[X½ÿÀ@878YK°TX½@ÿÀ878Y@ð     # 5 I O N Z Z j ‡ € “        '$$  )( % $ $ ' ** 755008 6 6 8 990A@@@@@@@@B E G II@TQQUPPVUVW W U U YYPffh ii`{xx‰Š … … ‰ ‰‰™ • • šš¤ ¤ ««°Ïßÿe]]+5326?3 3“N”|“lLT3!þ;Ã^^ÃhÈzšH†TNü”lXÛ` ´@B©¼© ÜÄ2Ä991/ìôì0KSXííY"K° TK° T[X½ @ ÿÀ878YK°TX½ ÿÀ @878Y@B&GI  + 690 @@E@@CWY_ ``f``b € ¯ ]]!!!5!qjýL´ü}´ýe`¨üÛ“¨%þ²$‚@4 %   ! © ©À ©±% $  C %Ô<Äü<Ä299999991üìÄôìî99999990K° TX½%ÿÀ%%@878Y²&]#"&=4&+5326=46;#"3>ù©lŽ==k©ù>DV[noZV¾”Ýï—ts•ðÝ“XøŽŽœøXþ®·±Ôì1üÌ0#®ªøþ²$ž@6%   ©©#À©±%#C %Ô<Ä2ü<Ä99999991üìÄôìî99999990K° TX½%@%%ÿÀ878YK°TX½%ÿÀ%%@878Y²&]326=467.=4&+532;#"+FŒUZooZUŒF?ù§lŽ>>Žl§ù?¾VøœŽŽøŽW“Ýð•st—ïÝ”ÙÓÛ1#@ œœ ÔÄ1ÔüÔìÀ990#"'&'&'&#"56632326Ûi³an’ ›^X¬bi³an“ ›^V©1²OD;>MS²OE<>LÿÿhN'$¼uhm !Ë@T   !!  ! !!!B  Á • Ž  !  VV!"ÔÄÔì2Ôî299999991/<æÖîÔî9990KSXííííííííY"² #]@  s › P#f iu {yyv v!€# ]]4&#"326!.54632#!#TY?@WX??Y˜þð!þX=>Ÿsr¡?<Òˆý_ˆÕZ?YWA?XXþóýN)sIs ¡rFv)ú‹þÿÿsþu'ð'&Ý-ÿÿÉ‹k'(žuÿÿÉ3^'1þuÿÿsÿãÙN'2'uÿÿ²ÿã)N'8îuÿÿ{ÿã-f'DRÿÿ{ÿã-f'DCRÿÿ{ÿã-f'D×Rÿÿ{ÿã-'DŽRÿÿ{ÿã-7'DØRÿÿ{ÿã-'DÜRÿÿqþuç{'FÝÿÿqÿãf'H‹ÿÿqÿãf'HC‹ÿÿqÿãf'H׋ÿÿqÿã'HŽ‹ÿÿof'ÖÿÿÿÿǦf'ÖCÿÿÿÿÞ\f'Ö×ÿÿÿÿôF'ÖŽÿÿÿºd7'Qؘÿÿqÿãuf'Rsÿÿqÿãuf'RCsÿÿqÿãuf'R×sÿÿqÿãu'RŽsÿÿqÿãu7'RØsÿÿ®ÿãXf'X{ÿÿ®ÿãXf'XC{ÿÿ®ÿãXf'X×{ÿÿ®ÿãX'XŽ{9ÿ;ÇÕ '@¹  YW Y Ô<ìü<ì1äôÔ<ì203!!#!5!¨°oþ‘°þ‘oÕþ\™û£]™Ãu=ð  @ÃÄà ‘ Z[ZÜìüì1ôìüì0"32654&'2#"&546PnnPPnoO@v+..¹†‡´¸ooPOmmOOp1.-rB„·´‡†º¬þÇ#˜!Q@+  †ˆ †ˆ ¹ ¹¸Œ"  "ÜìÔ<Ô<<ì221äô<ÄìÄþôîõî9990%&&'667#&73¦“¤¤JˆDF‰HA‰Mfñþ÷ ñfI‰ƒX⸹⡬)*ü *'ª#þä 32þá!bð`@!† ©  ”‘   Ü<ÌÌü<ÄÔÄ1/ì2ôäìÔ<î2î990K° TX½ÿÀ@878Y´66].#"!!!!53#535632NLˆ=”t‡þy-üìÇÇÖè=—´¶))›Ô×þ/ªªÑîó\ÿ=¢ð >‘@54&.#"#"&'532654/.5467.54632{?>‹ú?>ÌS8alÎÓƒ\]>9Ì­IšXW”:fqÝÖ€][;;ȦI™¨.Z.L…‡-[.Kˆ“¤''PGZswšeZŒ54m@ލ¤''TLf{x™f[1,pE‚Ÿ3Ñ…! · Ç \ Ôì1Ôì04632#"&3­~|«¬}}¬ú|««|}¬¬žÿ;9Õ %@Á]] ÔÔüÜì91Ä2ôì90!###&&54$yÀ¾Ž×ëÕùfùáNݸ¾èºÿã¬/š@0-'!  *†¹*¹—Œ.  !' $'$-F0üÄüÌÆîÔîî99991/äþîþÕî990@@'(Š Š     ! "&  : :!MM I!I"jj ¥¥¦ ]]4632#"&'532654&/.5467.#"#ºïÚÐÛ—¨:A9¦`áÓ@ˆIPŒAtx;e\`W§—ƒq‚ˆ»qÈÛèàs`/Q*%jŽd¬·¤_[?T>7;‡[¬gp‹ƒû“åÍ/8L`@6EBC?2ÉH0É9JCÊ 9ÊÉÈ É$HE301BKL?gwyVpMIßÑ`3þœDåÍ/IC@&=Ë>:ÌAÊ$1Ë04ÌGÊÉÈ$É 7aD=0^* D^ JÜÌüìþí2î1/îöþýîÖîýîÖî02#"$'&5476$"32676654&'&&&&#"3267#"&54632˜mmllmmþù˜˜þùmmllmm˜ƒâ^^``^^⃄ã^]]^\^ã§B‚B•§«›@zBC‰FØûûØIˆÍnmmþúš˜þûmmnnmm˜šmmng^^^å‚ã^^__^]⃅ã]^^õ! ¯Ÿ®"ôÐÑò'“FÕ >@!  É  b b cbcÔäüäÔìÔì91ô<<ì2Ô<<Ä903#######5J®¤ªqÃ7ËrqËrÉÕÿý¾äþÑ/þB^þä^sîRf1@ ´³DÔì1ôì0K° TK°T[X½ÿÀ@878Y3#‹Çþº™fþˆ×F)’@ÎÍddÜüÔì1ü<ì20K° TK° T[X½@ÿÀ878YK° TK° T[K°T[K°T[X½ÿÀ@878YK°TK°T[X½@ÿÀ878Y@````pppp]3#%3#^ËËþyËËÊÊÊÙ'ÛÝ>@" Ïœ Ï œ  Ü<Ä291Ô<Ì2ü<ìþ<ì990!!!!!'7!5!7!Ù}®/þHÃ{üúþþ}®þÕ¶Ãý‡¢;fÕ¨ðªþÇfÓªðHÕ‡@9  B• ••••­    ÔÔ<ì2ÔÄÄ91/<ììÄôììîî0KSXííííY"²€]@gww† …– ¿ ]!!!!!!#!5ýÇý9øü=ýð Íq‹þ¶ËÕªþFªýãªþÕžüðfÿºå +ž@< +,  )&  *&•& •‘&Œ,+,* # )#3,üìüìÀ999999991äôìîÀÀ99999990@*WZWU!je!{vu! FYVjddj(|svz( ]] 324&'.#"&5!27!"&''¶ý3>¡_Ü'y=¡_Üþý''†NOy;‚ÝW¢fªNPþˆþÆ€Ý[¢gXü²@CHp¸¸@Cþ¸þåp¼Džf b¥MK¿YÆgþöžþŸþ[KK¿XÝÝÏî /ÿ@- !$'!!0 $*0ÔÄÔÄ99991ÔÄÔÄÀ9990@¾     $$$   $$ $ ***///***55500055 5 :::???:::EEE@@@EE E JJJOOOJJJV´° °!°"°&°'°(´)]]32654&#".#"326#"&54632>32#"&“1†Te€vYR…Ä1…UfvYR†F^ˆº§†_™HDža†¼§†^•/XZ‡ie†‡7XX„je†ˆ‡ߦ¯Ø~ŠŠƒá§¯ÖwÙÛ .@МР œ   Ô<ì2ü<ì21/ìÔ<ìü<ì0!!#!5!!!®-ýÓ¨ýÓ-ýÓúþþ}ªþ}ƒªƒû¦ªÙÛ¨ T@.œœœœBѧœ $# ü<ì2291/ìôì90KSXííííY" 5!!Ûü@Àúþúþúþøþëþî²pªoüªÙÛ¨ V@/œœœœBѧœ$ # ü<<ì291/ìôì90KSXííííY"55!5ÙúþÁAúþø°þ‘ªþ²ýǪªRÃÕÆ@F  B Ó Ó   fe f eÔ<ì2ìüì2ì99991/ä2Ô<ì2Ô<ì290KSXííííY"K° TX½ÿÀ@878Y@(†¦ µ' ' ')((79‡ ˆ¦ ¥ª©]]!#!5!5'!5!3 3!!!þcÉþ` Tþ´þþ{y¿þÂþµTŸÇþ9Ç{3›{JýD¼ý¶{›3®þVå` M@% ‡Œ ¼½!   NF!üì2ôìÄ91ää2ô<ìÜÄ990¶"`"Ï"]3326533267#"&'#"&'®¸Š‡”•¸#% )I#ER2‘bf*þV ýH‘”¨¨ü¢<9 ”NPOONNý×hÿçÁ-)b@'! '!Õ* $$*ÔÌÜÌ9991äÌÜÌÎÎ990K° TK° T[K°T[K°T[K°T[X½*@**ÿÀ878Y>54&#"#"&54632#"&54324&#"32ôIH7$$0e´ÖþßÕ˜ËÝ¢e‚ WOmVPmmW£Kƒt,>bþÊþùþ±þFØ£Æ[àt}þþÏt{þw;Á ]@    ÔÄ91ÄÔÌÎ990@0QVPZ spvupz €€ Z pp{ t €€ ]]!! !!5 7êüA ýJïúÞÕýIÁÁý3ýÀ•!ãœþwqÁ@×Ö¯ggÔìÔì1üìì20!#!#œÕðý ïÁø¶}ùƒÿáÿðª/#Ë@1 ÚÙ"ØÕ $ #" #h#$ÔÔÔì9999991/<äôì22î9990K° TX½$ÿÀ$$@878Y@V             ##(]]#3267#"&5467!##"#>3!‡¶i/7.%7vy"PþºÂµÃ)6<  ¥y‘þJ\:1fd.¡xüo‘@E¦}/þú%&@ Û Ûܱ& iji&Üìüì1üìÜäÞä026732#"&'&&#"#"&546327j ¾ÊPd@7*8  k½ÄOeD=!0 þú°l9¼TA6?&#Hý•Ánþ!þbSA8?SsÕ;ð)_@3(%ãÝá%Ý ßÞÝ à‘* "(kl"k *ÜìÌüì22ÀÀ9991ôäüôìÄîíÖîî99990!!#5#"&5463354&#"56632"32655‹°ýP®•,]€˜¿¼¶uu>ˆDI‘E·³þì¡~bRh‚P{¸þ@p?D‡q‡Š[[""°ðCO@Mr`Õdð.@ãáÝ àÝ‘ klk Üìüì991ôìôìüì0!!2#"&546"32654&‹°ýPX³Îγ³Ðгi~hi}|P{Ý¿¿Ûܾ¿Ýs¡ˆ…  …‰ NÏç@@" å‘å  mm  ÔììÔììÀÀ9991/<ì2ôì0%!5654#"!5!&5! Ïý¨±ÆþøØØþ÷Dzý¨?ž‘1/Ž¡²²²aLÊð"þÝïÊþ´a²²‹*¸>ŠþwþËÂþØ{ÿão{3>@C'-%= 4©%†ˆ©:¹.†-º*¹»1 ¸Œ%?47&%7& =&-7"E?üìÌÔü<ÔìÄ999991Ää2ô<Ääü<ôìÄî2îôîî9990@0+0,0-0.0/00@+@,@-@.@/@0P+P,P-P.P/P0…+…0€@@ @°@À@Ð@à@à@ð@??? ??0,0-0.0/@,@-@.@/P,P-P.P/ooo oo`,`-`.`/p,p-p.p/€,€-€.€/]q].#">32!3267#"&'#"&5463!54&#"5>32"326=¶¥‰™¹DJÔ„âü² Ì·hÈddÐj§øMIؽÒýû§—`¶Te¾ZŽÕï߬o™¹”—´®ž0Z^þÝúZ¿È55®*,ywxx»¨½À‹..ª''`þf{bsÙ´)Hÿ¢œ¼ +ä@<+,&  )&  *&¹& ¹¸&Œ,+,* # #Q)E,üì2ôì2À9999991äôìîÀÀ99999990@p(?-YVUV jf!{    { z{ {!"#$%{&›•%¨ -ð-&YVUZ(ifej(ztvz(‰•š$¢­$]] 32654&'.#".5327#"&''‰þ)gA“¬\*g>—©}66ñ]ŸC‹_’56þîð`¡?‹`!ý°*(èÈOuš))ëÓHn.—MÅw834¨O³MÆxþíþÇ43¨Nÿã¬Õ $†@/ †ˆ !ƒ# •Œ#%" " "!& %ÜìÔüìÔì99991äôìþÍôî9990K°TK°T[K°T[X½%ÿÀ%%@878Y@ ttttv]33267#"&546?>7>5#53ô¾7ZZ:3ƒmN´`^Àg¸àIYX0&ÄÊÊDœe‚WX5^1YnFC¼98ŸL‰VV/5<6þ5Õ b@ƒ ü<ì2991/ôüÌ0K° TX½ @ ÿÀ878YK°TK°T[K°T[X½ ÿÀ @878Y¶ P ]#53#3ËËË¢×þú+eþ›ÙÛ^@ œÜÔì1ÔÄì0!#!Ù¨û¦^ýÁ•=ÿ×} *@    ÔÌ91ÔÌÄ903##'%\½sý®BþÁ}}`ùºs-Pbý;þV#Š@@   B   ©Šæ©Šæ©!—$  $ÔÌ91Ä2Äüìôìîöîî299990KSXí2í9Y"K° TX½$ÿÀ$$@878Y.#"!!#"&'53267#5!>32&P,`r<þÃ:¼º:d/4a/am"‰ø?$Æ—5dð¤z„þÉý…þãÓ¦!!‰¦­J·ÃÙÛô;?@.9*-" *œ19œ"œ œ<-<Ô<Ä21ÔìÔìÜüÔìÀ9999990#"'&'&'&#"56632326#"'&'&'&#"56632326Ûi³an’ ›^X¬bi³an“ ›^V©gi³an’ ›^X¬bi³an“ ›^V©o³NE;=LT³NE;=KÚ²OE;=LS²NE;=Kÿú`Á8@ÔÌ91/ÄÌ90@cmpxyvn]] !3!¬þ^DýïàCúšîûÄú?ž%# †@Ièèèè è è è  è B  ç¦ o o nüü<Ôì2991ô<ì2990KSXííííííííY"55%þÓ-þ+#þÓ-þ+#¿þôþô¿¢R¢¿þôþô¿¢RÁH# †@I è è è è èèèèB  ç¦ o opü<üÔ<ì991ô<ì2990KSXííííííííY"5%5ÁÕþ+-þÓ²Õþ+-þÓ#þ^Rþ^¿  ¿þ^Rþ^¿  ìþ #@ƒ   ÔüÔìÔì1/<<ì220%3#%3#%3#–ÔÔ©ÕÕú­ÕÕþþþþþþÿÿhk'$¼uÿÿh^'$¼uÿÿsÿãÙ^'2'us Õ;@•••­   üìÔÄÄÔì299991/ìì2ôì2î0!!!!! !# !3úýÇý9øû×þOþA¿±gþ¿þÀ@AÕªþFªýãª|pm|ªþáþàþßþßqÿãÃ{'3„@1†ˆ ©. ¹(¹»"%¸Œ4"1 K1 Q+E4üìôüôìÄ9991ä2ô<Ääì2Äî2îôî90@%?5_5p5Ÿ5Ï5Ð5ð5????? ooooo ]q].#"!3267#"&'#"32>32%"32654& ¤‰™¹Hü² Ì·jÈbdÐj òQGÑŒñþïñŒÓBNèâú°”¬«•“¬¬”˜³®ž5Z¾Ç44®*,nmnm98olkpþ݇çÉÉçèÈÇééy¶©é/Æ1üì0!!üyéyµ©/Ì1Ôì0!!øy®émÕ '@ž   ÜüÌÔÌþÔÎ1ô<ì20#53#53Ó¤RšÓ¤Ré­?þÁ­­?þÁ®émÕ '@ ž  ÜìÔÌÜîÔÎ1ô<ì203#%3#Ó¤RšÓ¤RÕ¬þÀ@¬¬þÀ@®éÓÕ@ žÜüÔÌ1ôì0#53Ó¤Ré­?þÁ²þ×Õ@ žqÜìÔÌ1ôì03#Ó¤RÕ˜þÁ?Ù–Ûo )@êêœ r ÜÔ<ü<Ä1ÔÄüÄîî03#3#!!ßööööýúúþoöþõAªþ#îu"@ÔÌ91ÔÌ990 úþþôþ þ üÏüÇ9%ûÛûÓ-ÿÿ=þV'\Ž^ÿÿÿüçN'<suþ‰ÿãÍð+@BŒ‘ÔÌ1ää0KSXííY"3#- ü\ ðùó^R¼²#/ƒ@I -'! - -¹ëì'¹ë!0 *$0* $ $(st*(s0Üäìôäì9999999991ÔäìôäìÀ9999999907'#"&''7&&5467'766324&#"326{ÏrÎ%$&(ÑrÏ;t=:x=ÏqÏ%%&&ÏsÏ7t@?s9ÏqÏ(&%%ÏsÎ>v:@t8ÎsÏ'%$þ|pššprœžs#G@%èèèèBç¦onüì291ôì90KSXííííY"5sþÓ-þ+#¿þôþô¿¢RÁ–#I@&èèèèBç¦opü<ì91ôì90KSXííííY"5ÁÕþ+-þÓ#þ^Rþ^¿  /J›@( ©‡¾±— ¼ Lü<Ä2Äü<Äî2991/<æ2îþîîî2990K° TX½ÿÀ@878YK°TX½@ÿÀ878Y@0P€€€ Ðï]]#!##53546;#"3#J¹þ¹°°­³¹°cMù¹¹`û Ñü/ÑN·¯™Phc²é/J„@! © ‡— ¼   Lü<ÄÄü<Äî991/<æ2þîî2990K° TX½ÿÀ@878YK°TX½@ÿÀ878Y@0P€ € € Ðï]!#!"!!##53546J¹þ·cM/þѹ°°®ùì{Phcü/ÑN»«9ÿ;ÇÕ>@ ¹¹  ÂY W Y Ô<<ì2ü<<ì21äôÄ2Ä2î2î20%!#!5!!5!3!!!Çþ‘°þ‘oþ‘o°oþ‘oßþ\¤š™¤þ\™ýáÛH®F·ƒÔì1Ôì03#ÛÓÓFþ®ÿÓþ@ žƒÔìÔÌ1üì0%3#Ó¤Rþ¬þÀ@®ÿmþ '@ žƒ   ÜìÔÌÜîÔÎ1ü<ì20%3#%3#šÓ¤RþfÓ¤Rþ¬þÀ@¬¬þÀ@qÿã Lð #'3?K®@D$%&%&'$'B@’ .’(’F’4 :&Œ$‘L%IC'1+C =  1 =I 7+ ! LüäìÔÄìäîîöîî991ä2ô<<ä2ì2îöîî20KSXííY"K°TK° T[K° T[K° T[K° T[K°T[X½L@LLÿÀ878Y"32654&'2#"&5462#"&546!3#"32654&2#"&546"32654&ôWddWUccUžº» º»ùtž¼»ŸŸ¹º% üZ VcbWWcd²žº» º»ŸWccWUcc‘”„‚••‚ƒ•Ü»»ÛÛ»¼ÛàÛ»½ÚÛ¼ºÜùóŽ•‚„””„–ýŸÜ»»ÛÛ»¼Û”„‚••‚ƒ•ÿÿhm'$¼uÿÿÉ‹m'(žuÿÿhk'$¼uÿÿÉ‹N'(žuÿÿÉ‹k'(žuÿÿ¢k',ÿ/uÿÿÿþ`m',ÿ/uÿÿXN',ÿ/uÿÿ;ºk',ÿ/uÿÿsÿãÙk'2'uÿÿsÿãÙm'2'uÿÿsÿãÙk'2'uÿÿ²ÿã)k'8îuÿÿ²ÿã)m'8îuÿÿ²ÿã)k'8îuÁy` ·¿Füì1/ì0@ @P`p]3#Á¸¸`û Áî?f7@ ´³uÜì91ôì290K° TK°T[X½ÿÀ@878Y3#'#¶”õ‹´´‹fþˆõõ¶J7c@$  Ãà íVwVvôìüì99991ü<üÔ<ì99990K° TK° T[X½ÿÀ@878Y'.#"#>3232673#"&ü9! &$}f[&@%9! &$}f[&@Z7IR‡“!7IR‡“Õb+ö/·ïîÔÌ1üì0K° TK°T[X½ÿÀ@878Y!!ÕVýªö”Ç)9H W@ ð³VVÜìÔì1ô<Ôì0K° TX½ÿÀ@878YK°TK°T[K°T[X½@ÿÀ878Y332673#"&Çv aWV` v ž‘‘žHKKJLšDf,@ ÎÍdÔì1üì0K° TX½ÿÀ@878Y3#šÌÌÌîá _@Áò ÁñV xVÔìôì1ôìôì0K° TK° T[X½ÿÀ@878YK° TK° T[K° T[X½ÿÀ@878Y4&#"3267#"&54632˜X@AWWA@XzŸssŸŸssŸô?XW@AWX@s  ssŸŸ#þuÁ@  ó' ÜÔìÔÌ1/ÔüÄ90!#"&'532654&'T76xv.W+"J/;<+->i0Y[ ƒ0.W=ðî®fB@´³ÔÜÔÌ991ô<ì20K° TK°T[X½ÿÀ@878Y3#3#ü²ø‡ªß‰fþˆxþˆLþuÁ @  óô 'ÔìÄÔÌ1/üüÄ90!33267#"&546¸w-+76 >&Dzs5=X..… W]0iÁî?f7@ ´³uÜì91ô<ì90K° TK°T[X½ÿÀ@878Y373¶õ‹´´‹õîxõõþˆÿòuÕ ?@ •  : yô<ìÄü<Ä991/äì90´0P]3%!!'7ÓË9Pþw×ü^”MáÕý˜Ûoþîýãª;jnžH ^@ — z z Ô<äü<ä991/ì90K°TX½ @ ÿÀ878Y@ @ P ` sz p à ð ]37#'7Ǹ}Lɸ{JÅý¦ZjüãšXjÿÿ‡ÿã¢m'6‹uÿÿoÿãÇf'Vàÿÿ\m'=¾uÿÿXÛf']àþ¢®˜@ õõÜ<ì21ÔìÔì0##®ªªª˜ý öý ö ºÕ g@  © ••  2  yô<ì2ÄôìÄ91/Æ2îöîî20@( °Ÿ Ÿ Ÿ Ÿ ŸŸŸŸ¿ ¿ ¿ ¿ ¿¿¿¿]]! )#53!!3 !Ó ±–þiþPþ`ÉÉËPþ°ó5þáþËÕþ—þ€þ~þ–¼ãþýê.,qÿãu('@^%{&%#${##{#({'(#&'('%$%(('"#" ! B('&%"! ##¹ ¹Œ#±)&' ! (%#" QE)üìôì99999991ìÄôìî9990KSXÉÉÉÉííííY"²?*]@v%+("/#/$)%-&-'*(6%F%X X!` `!f"u u!u"%#%$&&&''(6$6%F$E%Z Z!b b!z{     {zzv v!x" *ð*']].#"32654&#"5432''%'3%F2X)§¹®’‘®6 ~rþäæçþåÝ4*ŸþÁ!µäM!þÙ“ØÃ¼ÞÞ¼z¼&þà­ÿþÉ7ÿú7´kc\Ì‘oabÿÿÿüçk'<suÿÿ=þVf'\^ÉÕ =@• •ö  ? üì22üì91/ôüìÔì0@ ?_]332+#32654&#ÉÊþûþÿûþÊÊþš™ŽÕþøáÜÜâþ®'ýÑ’††‘ºþV¤>@¹¹Œ¸½— GFüì22ôì1ìääôÄìÆî0@ `€ à]%#3>32#"&4&#"326s¹¹:±{ÌÿÿÌ{±8§’’§§’’§¨ý®¾ý¢daþ¼þøþøþ¼aëËççËËççÙ-Û×¶œÔÄ1Ôì0!!Ùúþת?œÅ …@M œ  œœœœœ œ œ B   Ô<Ì291Ô<Ì290KSXííííííííY" '7œþ7Éwþ5þ5vÈþ8vËËLþ5þ7yËþ5yÉËyþ5ˉœÅß ,@Ý ÝÝ ÷‘ |]|| Üôäüä1ôììÔìî2035733!œÌßæ‰Íý× c)t'ý+n^œ´ðJ@$}}BÝÝ÷ Ý‘~ÜÄÔÄì91ôÄìüìî90KSXí2íY"!!56754&#"56632 ¨ýª"?XhU4zHM…9‘®þµ8rn81^BQ##{„l‹þä0bÍð(H@' Ý Ý Ý Ý ø÷Ý ø#‘)~&~ )ÜÄÄÔìÔì9991ôäìüäìÔìîî90#"&'532654&##532654&#"56632 \e¾±9}F4wCmxolV^^ad_(fQI€7©Z`mR|†yOFJLl?<:=svcE`ÿÿ‰ÿãð'ð'¼5 ‹ýdÿÿ‰ÿã?ð'ð'¼5ñ‹ýdÿÿbÿãð'ò'¼5 ‹ýdÿÿsÿã‹m'* uÿÿqþVZH'JÚ‹ÿÿÉ•P', ÿ/uÿÿ‡þu¢ð'6Ý‹ÿÿoþuÇ{'VÝÿÿsÿã'k'&-uÿÿqÿãçf'F‰ÿÿsÿã'm'&-uÿÿqÿãçf'Fà‰qÿãô$J@$Ó ù"¹¹ Œ¸—   GE%üìô<Äü<Ä1/ìäôÄìÄîý<î20¶`&€& &]!5!533##5#"3232654&#"¢þºF¸šš¸:±|ËÿÿË|±ýǧ’’¨¨’’§¶N}““}úü¨daDDaþËççËËççd߃¶œÜÌ1Ôì0!!dý僤ÛH®F·ƒÔì1Ôì03#ÛÓÓFþÿãð1@: Ó"+Ó ¡®•¡®•/‘Œ) 2+"!)#&  , & &*!/<ÔÄ2üÄÄ99999999991Ä2äôìôìîöîî2Ý<î20K° TK° T[K° T[K°T[K°T[K°T[X½2ÿÀ22@878Y@z  1Ti lnooooiko o!o"o#n$l%i'i-ŸŸŸ Ÿ Ÿ Ÿ Ÿ ŸŸŸŸŸŸ–Ÿ Ÿ!Ÿ"Ÿ#Ÿ$Ÿ%Ÿ&Ÿ'Ÿ(Ÿ)Ÿ*Ÿ+Ÿ,-2   USjg ]].#"!!!!3267#"#734&5465#7332[©fÊ A7ýæ¾8þŠ Êf©[Y¹`íþË(Ó7‹Â7œ(6ìb¹bÕiZÈ»{.# .{»ÊZiÓHH"{/ #/{"G×)Ù¥@ ÎddÔüÜì1Ô<ì20K°TK°T[X½@ÿÀ878YK°TK° T[K°T[X½ÿÀ@878YK°TK°T[X½@ÿÀ878YK°TX½ÿÀ@878Y@````pppp]3#%3#^ËËþyËËÙËËËsîðö@BúÄÀ1ôÌ0KSXÉÉY"K° TX½ÿÀ@878YK°TX½@ÿÀ878Y@ %%6FVjg //]]3#7¹ä™öþø¶Jéu@!  ÃÃúVV ÔìÔì99991ô<ìÔì2990K° TX½ÿÀ@878YK°TX½@ÿÀ878Y´ ]'.#"#4632326=3#"&ü9 $(}gV$=09" (}gT";9! 2-ev 3)dw î‹ö‰@BúÄÀ1ôÌ0KSXÉÉY"K° TX½ÿÀ@878YK°TX½@ÿÀ878Y@*$$5CUUŸŸ¯¯//]]#ÇÄ™æöþøÏî1øw@ úÔÄ91ô<Ä90K° TX½ÿÀ@878YK°TX½@ÿÀ878YK°TX½ÿÀ@878Y@ //- ]3#'#¢¼Ó‹¦¦‹øþö²²Ïî1ø†@ úÔÄ91ôÄ290K° TK° T[K° T[K° T[X½ÿÀ@878YK°TX½@ÿÀ878YK°TX½ÿÀ@878Y@ "  ]373¢Ó‹¦¦‹Óî ²²þö?œôß Ô@ Ý ÷‘ ] ÜÔ<Äì291ôüÔ<ì290K°TK°T[K°T[K°T[K° T[K° T[X½@ÿÀ878YK°TK°T[X½ÿÀ@878Y@T /9IFYi‹«»       "5GK S[ e„¥µ]] !33##5!5ÝþË5¦‡‡þbfþ]ýämººyÇ9ø j@à úVVÔìÔì1ôüÄ20K° TX½ÿÀ@878YK°TX½@ÿÀ878YK°TK°T[X½ÿÀ@878Y332673#"&Çv cSRav  Ÿø6978w{zšfÛ¶úÔÌ1ôÌ03#šÌÌÛÍ  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßà>: ~ÿ1BSax~’ÇÝ©À & 0 : ¬!""""+"H"e%Êûÿÿ   0AR^x}’ÆØ©À  0 9 ¬!""""+"H"`%ÊûÿÿÿãÿõÿØÿ ÿ^ÿCÿhÿüöüÛà–à…àVßjÞqÞ_Úï¿8ôüúúü (B¬£„…½–熎‹©¤ŠÙƒ“ñò—ˆÃÝðžªóôõ¢­ÉÇ®bcdËeÈÊÏÌÍÎèfÒÐѯgï‘ÕÓÔhêì‰jikmln oqprsutvwéxzy{}|¸¡~€ëíºýþÿøÖùúãä×àÚÛÜßØÞ²³¶·Ä´µÅ‚‡«˜¨š™î¼¥’”•Íf‹‹55®Å´žªšq=3ۤ=´Ù‹žãd‹Û²‡á–œdž¨‹²ð²ž´Ù´Ù´Ù?“‡y}É–s)ÉÉšÉ3sÉ\É\ÿ–?ÉuÉçÉüÉLsÓÉLsɇãÿúÛ²yéD{=ãÿü{\°²Ç´Ùÿìªç{ºfqqìqÑ/qº9Á9ÿÛ¢º9Á˺ºåqºqJº+o#7®¼=‹V¼;¼=3X²´Ùyy–sÉüÉLsÛ²ç{ç{ç{ç{ç{ç{fqìqìqìqìq99ÿÇ9ÿÞ9ÿôºåqåqåqåqåq®®®®9ì\¸3ž º's×´ÙËLfªÝ´Ù´Ù´ÙR®#hdœ¶ÿá+/ÅsÅ`NÛ{åH?55´Ù=´ÙZÿúåžåÁìyyLss/q%®%®‹®‹²´Ùô¼=ãÿüVþ‰^3ž3Á / /9‹Û‹®%® ¼qyÉyÉÉ\¢\ÿþ\\;LsLsLsÛ²Û²Û²9ÁÁ¶ÕÇšî#ðLÁÿòF‡+o{\3X²3 åqãÿü¼=×ɺ´Ù´5‰5^5bÁ‰Á‰Áb3sq\ɇ+o–sfq–sfqqãd‹Û×s¶ ÏÏ5?Çšÿ+   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóõôöøùúûüýþÿ     sfthyphenperiodcenteredEuroc6459c6460c6461c6462c6463c6466c6467c6468c6469""""X“ÿ¶Oƒ²ö!n˜´ÊÞE‚~áL·üeÏî  R s ®  ß X ° û : i “ æ  = z /¬E…ëuñ)pཊëP‹±á@Ö"m¹#{ßC€øw³R¡Ø‡Äº‡wè [ r ó!5!B!â!ï!ü" ""#"0"="J"W"d"q"~"‹"˜"¥"²"¿"Ì"Ù"æ"ó## ##'#4#A#N#[#h#”#Ï$4$%3%S%&&º'K''·((X(Ã)_*%*\*£*é+z+Ó,D,,±-P- ..R.ª/‡0A0½11!1P1Ï2H2z2ß3F3p3p3}3Š3—3æ4z44£4Ñ4ÿ5595g5‘5ž5«5Ï6[6“6Í7C7©7ë888J999)969C9P9]9j9w9„9‘9ž9«9¸9Å9Ò9ï::{: :å;;^;Ž;Ä;ô<"<_<§<´<Á<Î<Û<þ=c>;>H>U>˜>ç>ý?a??Ü@:@K@\@m@z@‡@”@¡@®@»@È@Õ@âA@AVAkBEBªB÷C_C²CÿDUDÛE*E?-†” x$ÿÓ%ÿ·&')*K+-r./2934K57ÿD9ÿˆ:ÿ­;ÿš<ÿ =IQR&UYÿÉZ\ÿÜbÿÓdg9xy&z&{&|&}&‰­ÿÓ®ÿÓ¯9ºÿÜ»ÿ ÇÿÓÉÿÓÐ9Ñ9Ò9åéêÿ ëÿÜìöKûý$ÿÓ$ÿÜ$ÿÜ$$9$&ÿÜ$*ÿÜ$2ÿÜ$4ÿÜ$6$7ÿa$8$9ÿ}$:ÿ$;$<ÿa$FÿÜ$GÿÜ$HÿÜ$Iÿ·$RÿÜ$TÿÜ$WÿÜ$X$Yÿˆ$Zÿ­$\ÿu$b9$dÿÜ$gÿÜ$h$oÿÜ$pÿÜ$qÿÜ$rÿÜ$sÿÜ$yÿÜ$zÿÜ${ÿÜ$|ÿÜ$}ÿÜ$~$$€$$©ÿ·$ª$­9$®9$¯ÿÜ$´þø$µÿ$ºÿu$»ÿa$Å/$Ç9$É9$ÐÿÜ$ÑÿÜ$ÒÿÜ$Ó$Ô$Õ$ã$êÿa$ëÿu$öÿÜ$ù$ûÿÜ$üÿÜ$ýÿÜ$þÿÜ%%&ÿÜ%*ÿÜ%2ÿÜ%6ÿÜ%9ÿÁ%:ÿ·%<ÿ%dÿÜ%gÿÜ%©ÿÁ%ªÿÜ%¯ÿÜ%´ÿ%µÿ%»ÿ%Åÿ­%ÐÿÜ%ÑÿÜ%ÒÿÜ%ãÿÜ%êÿ%öÿÜ%ùÿÜ%ûÿÜ%ýÿÜ&&$&6&<ÿÜ&b&©ÿÜ&ªÿÜ&­&®&´&µ&&»ÿÜ&Å&Ç&É&ã&êÿÜ&ù''$ÿÜ'9ÿÜ':'<ÿ'bÿÜ'©ÿÜ'ªÿÜ'­ÿÜ'®ÿÜ'´ÿÓ'µÿÉ'»ÿ'ÅÿD'ÇÿÜ'ÉÿÜ'êÿ))þ·)ÿa)$ÿD)6ÿÜ)7ÿÜ)DÿD)Hÿ)Lÿk)Rÿ·)Uÿk)Xÿ)\ÿD)bÿD)iÿD)jÿD)kÿD)lÿD)mÿD)nÿD)pÿ)qÿ)rÿ)sÿ)yÿ·)zÿ·){ÿ·)|ÿ·)}ÿ·)~ÿ)ÿ)€ÿ)ÿ)©)ª)­ÿD)®ÿD)´ÿÓ)µ)ºÿD)Åþˆ)ÇÿD)ÉÿD)ãÿÜ)ëÿD)ùÿÜ**$*7ÿ·*:*<ÿš*b*©ÿÜ*ªÿÜ*­*®*´ÿÓ*µÿÓ*»ÿš*ÅÿÉ*Ç*É*êÿš++ÿÜ++©+ª+´ÿ·+µÿÁ+Åÿ·-ÿ·-$ÿÜ-bÿÜ-©ÿÜ-ªÿÜ-­ÿÜ-®ÿÜ-´ÿ·-µÿÁ-Åÿ-ÇÿÜ-ÉÿÜ.ÿ).$ÿÜ.&ÿ.2ÿ.7ÿa.8ÿÉ.:ÿ·.<ÿ·.DÿÜ.Hÿš.Rÿš.Xÿš.\ÿk.bÿÜ.dÿ.gÿ.hÿÉ.iÿÜ.jÿÜ.kÿÜ.lÿÜ.mÿÜ.nÿÜ.pÿš.qÿš.rÿš.sÿš.yÿš.zÿš.{ÿš.|ÿš.}ÿš.~ÿš.ÿš.€ÿš.ÿš.©ÿ}.ª.­ÿÜ.®ÿÜ.¯ÿ.´ÿÁ.µÿÁ.ºÿk.»ÿ·.Å.ÇÿÜ.ÉÿÜ.Ðÿ.Ñÿ.Òÿ.ÓÿÉ.ÔÿÉ.ÕÿÉ.êÿ·.ëÿk.ûÿ.ýÿ/ÿÜ/$//2ÿ·/7þæ/8ÿš/9ÿ/:ÿD/<þð/D/HÿÜ/RÿÜ/XÿÜ/\ÿD/b//gÿ·/hÿš/i/j/k/l/m/n/pÿÜ/qÿÜ/rÿÜ/sÿÜ/yÿÜ/zÿÜ/{ÿÜ/|ÿÜ/}ÿÜ/~ÿÜ/ÿÜ/€ÿÜ/ÿÜ/©/ª/­//®//¯ÿ·/´þa/µýæ/ºÿD/»þð/Å/Ç//É//Ðÿ·/Ñÿ·/Òÿ·/Óÿš/Ôÿš/Õÿš/êþð/ëÿD292ÿ­2ÿÜ2$ÿÜ29ÿÜ2;ÿ}2<ÿ2bÿÜ2©ÿÜ2ª2­ÿÜ2®ÿÜ2´ÿÓ2µÿÜ2»ÿ2ÅÿD2ÇÿÜ2ÉÿÜ2êÿ3ÿÓ3þÁ33$ÿ}383:3<ÿÓ3Dÿ¤3Hÿ·3LÿÓ3QÿÜ3Rÿ·3UÿÜ3VÿÜ3XÿÜ3\3bÿ}3h3iÿ¤3jÿ¤3kÿ¤3lÿ¤3mÿ¤3nÿ¤3pÿ·3qÿ·3rÿ·3sÿ·3xÿÜ3yÿ·3zÿ·3{ÿ·3|ÿ·3}ÿ·3~ÿÜ3ÿÜ3€ÿÜ3ÿÜ3©ÿÜ3ª3­ÿ}3®ÿ}3´&3µ&3º3»ÿÓ3Åþ·3Çÿ}3Éÿ}3Ó3Ô3Õ3äÿÜ3êÿÓ3ë3úÿÜ494©4ª4´ÿÓ4µÿÜ4Åÿ}5ÿ­5ÿ·5ÿÁ5$ÿ­5&ÿš57ÿk59ÿ5:ÿ­5<ÿ}5DÿÓ5Hÿ¤5Rÿ¤5Xÿ¤5\ÿ5bÿ­5dÿš5iÿÓ5jÿÓ5kÿÓ5lÿÓ5mÿÓ5nÿÓ5pÿ¤5qÿ¤5rÿ¤5sÿ¤5yÿ¤5zÿ¤5{ÿ¤5|ÿ¤5}ÿ¤5~ÿ¤5ÿ¤5€ÿ¤5ÿ¤5©ÿ5ªÿÜ5­ÿ­5®ÿ­5´ÿk5µÿ}5ºÿ5»ÿ}5ÅÿÜ5Çÿ­5Éÿ­5êÿ}5ëÿ5ûÿš5ýÿš6$&6&6*6264666b&6d6g6­&6®&6¯6Ç&6É&6Ð6Ñ6Ò6ã6ö6ù6û6ý7ÿD7ÿ 7ÿ7$ÿa7&ÿˆ77ÿÜ7Dþ­7Fþ¤7Hþ¤7LÿÁ7Rþ¤7UþÓ7Vþ­7XþÉ7Zþ­7\þÁ7bÿa7dÿˆ7iþ­7jþ­7kþ­7lþ­7mþ­7nþ­7oþ¤7pþ¤7qþ¤7rþ¤7sþ¤7yþ¤7zþ¤7{þ¤7|þ¤7}þ¤7~þÉ7þÉ7€þÉ7þÉ7©ÿD7ªÿ7­ÿa7®ÿa7´7µÿÓ7ºþÁ7Åþø7Çÿa7Éÿa7äþ­7ëþÁ7úþ­7ûÿˆ7üþ¤7ýÿˆ7þþ¤8$8-8=ÿÜ8b8­8®8Ç8É8åÿÜ9ÿˆ9þø9ÿY9$ÿ}92ÿÜ9Dÿa9Hÿa9LÿÓ9Rÿa9Xÿu9\ÿÉ9bÿ}9gÿÜ9iÿa9jÿa9kÿa9lÿa9mÿa9nÿa9pÿa9qÿa9rÿa9sÿa9yÿa9zÿa9{ÿa9|ÿa9}ÿa9~ÿu9ÿu9€ÿu9ÿu9©ÿN9ªÿ9­ÿ}9®ÿ}9¯ÿÜ9´9µ9ºÿÉ9Åþæ9Çÿ}9Éÿ}9ÐÿÜ9ÑÿÜ9ÒÿÜ9ëÿÉ:ÿ­:ÿ:ÿˆ:$ÿ:Dÿ}:Hÿˆ:LÿÓ:Rÿˆ:Uÿ¤:Xÿ·:\ÿÜ:bÿ:iÿ}:jÿ}:kÿ}:lÿ}:mÿ}:nÿ}:pÿˆ:qÿˆ:rÿˆ:sÿˆ:yÿˆ:zÿˆ:{ÿˆ:|ÿˆ:}ÿˆ:~ÿ·:ÿ·:€ÿ·:ÿ·:©ÿ:ªÿÜ:­ÿ:®ÿ:´ÿÜ:µ:ºÿÜ:Åþø:Çÿ:Éÿ:ëÿÜ;ÿš;$;&ÿk;2ÿ};7ÿÜ;Hÿ¤;b;dÿk;gÿ};pÿ¤;qÿ¤;rÿ¤;sÿ¤;©ÿ;ª;­;®;¯ÿ};´ÿa;µÿ­;ÅÿÓ;Ç;É;Ðÿ};Ñÿ};Òÿ};ûÿk;ýÿk<ÿ <þa<þð<$ÿa<&ÿ<2ÿ<Dþæ<Hþð<Lÿ·<Rþð<Xÿ<bÿa<dÿ<gÿ<iþæ<jþæ<kþæ<lþæ<mþæ<nþæ<pþð<qþð<rþð<sþð<yþð<zþð<{þð<|þð<}þð<~ÿ<ÿ<€ÿ<ÿ<©ÿ<ªÿk<­ÿa<®ÿa<¯ÿ<´ÿ<µÿÜ<Åþø<Çÿa<Éÿa<Ðÿ<Ñÿ<Òÿ<ûÿ<ýÿ=ÿÜ=©=ª=´ÿÜ=µÿÜ=ÅÿÜH[ÿÜIÿIÿkIÿ·IWÿÜIZÿÜI\ÿÜI©ÿ·IªÿÜI´AIµIºÿÜIÅÿIëÿÜNDÿÜNHÿ·NRÿ·NXÿÁN\ÿ·NiÿÜNjÿÜNkÿÜNlÿÜNmÿÜNnÿÜNpÿ·Nqÿ·Nrÿ·Nsÿ·Nyÿ·Nzÿ·N{ÿ·N|ÿ·N}ÿ·N~ÿÁNÿÁN€ÿÁNÿÁNºÿ·Nëÿ·QQQQ©QªQ´ÿkQµÿQÅÿ¤R&RÿÜRR[ÿÁR©RªR´ÿkRµÿ·RÅÿ}Uÿ}UÿDUÿÜUFÿÓUGÿÜUHÿÓUIUJÿÜUKÿÜUPÿÜUQÿÜURÿÓUTÿÜUUÿÜUXUYUZU[ÿÉU\U]UoÿÓUpÿÓUqÿÓUrÿÓUsÿÓUxÿÜUyÿÓUzÿÓU{ÿÓU|ÿÓU}ÿÓU~UU€UU©ÿ·UªU´UµVUºUÅþÉUæUëU÷ÿÜUüÿÓUþÿÓYÿÉYÿaYÿY©ÿÜYªÿÜY´YµÿÜYÅþðZZÿDZÿZ©ÿÜZªÿÜZ´ZµZÅÿ)[FÿÜ[HÿÁ[RÿÁ[oÿÜ[pÿÁ[qÿÁ[rÿÁ[sÿÁ[yÿÁ[zÿÁ[{ÿÁ[|ÿÁ[}ÿÁ[üÿÜ[þÿÜ\ÿÜ\þÜ\ÿk\©ÿÜ\ªÿÜ\´\µ\ÅþÓbÿÓbÿÜbÿÜb$9b&ÿÜb*ÿÜb2ÿÜb4ÿÜb6b7ÿab8b9ÿ}b:ÿb;b<ÿabFÿÜbGÿÜbHÿÜbIÿ·bRÿÜbTÿÜbWÿÜbXbYÿˆbZÿ­b\ÿubb9bdÿÜbgÿÜbhboÿÜbpÿÜbqÿÜbrÿÜbsÿÜbyÿÜbzÿÜb{ÿÜb|ÿÜb}ÿÜb~bb€bb©ÿ·bªb­9b®9b¯ÿÜb´þøbµÿbºÿub»ÿabÅ/bÇ9bÉ9bÐÿÜbÑÿÜbÒÿÜbÓbÔbÕbãbêÿabëÿuböÿÜbùbûÿÜbüÿÜbýÿÜbþÿÜdd$d6d<ÿÜdbd©ÿÜdªÿÜd­d®d´dµ&d»ÿÜdÅdÇdÉdãdêÿÜdùg9gÿ­gÿÜg$ÿÜg9ÿÜg;ÿ}g<ÿgbÿÜg©ÿÜgªg­ÿÜg®ÿÜg´ÿÓgµÿÜg»ÿgÅÿDgÇÿÜgÉÿÜgêÿh$h-h=ÿÜhbh­h®hÇhÉhåÿÜp[ÿÜq[ÿÜr[ÿÜs[ÿÜxxxx©xªx´ÿkxµÿxÅÿ¤y&yÿÜyy[ÿÁy©yªy´ÿkyµÿ·yÅÿ}z&zÿÜzz[ÿÁz©zªz´ÿkzµÿ·zÅÿ}{&{ÿÜ{{[ÿÁ{©{ª{´ÿk{µÿ·{Åÿ}|&|ÿÜ||[ÿÁ|©|ª|´ÿk|µÿ·|Åÿ}}&}ÿÜ}}[ÿÁ}©}ª}´ÿk}µÿ·}Åÿ}‰&‰©‰ª‰´ÿ‰µÿ‰Åÿ­©ª´ÿ­µÿ¤Åÿ©$©%ÿÜ©&ÿÜ©'ÿÜ©)©*ÿÜ©+©-ÿÜ©.©/©2©3©4©5©7ÿ©9ÿ©:ÿÜ©;©<ÿk©=©I©Q©R©U©YÿÜ©ZÿÜ©\ÿÜ©b©dÿÜ©g©x©y©z©{©|©}©‰©—©­©®©¯©ºÿÜ©»ÿk©Ç©É©Ð©Ñ©Ò©å©é©êÿk©ëÿÜ©ì©öÿÜ©ûÿÜ©ýÿܪ$ÿ·ª%ÿ·ª&ÿܪ'ÿܪ)ª*ª+ª-ÿܪ.ª/ª2ÿܪ3ª4ª5ª7ÿDª9ÿNª:ÿª;ÿª<ÿª=ªIªQªRªUªYÿܪZÿܪ\ÿܪbÿ·ªdÿܪgÿܪxªyªzª{ª|ª}ª‰ªª­ÿ·ª®ÿ·ª¯ÿܪºÿܪ»ÿªÇÿ·ªÉÿ·ªÐÿܪÑÿܪÒÿܪåªéªêÿªëÿܪìªöªûÿܪýÿÜ­ÿÓ­ÿÜ­ÿÜ­$9­&ÿÜ­*ÿÜ­2ÿÜ­4ÿÜ­6­7ÿa­8­9ÿ}­:ÿ­;­<ÿa­FÿÜ­GÿÜ­HÿÜ­Iÿ·­RÿÜ­TÿÜ­WÿÜ­X­Yÿˆ­Zÿ­­\ÿu­b9­dÿÜ­gÿÜ­h­oÿÜ­pÿÜ­qÿÜ­rÿÜ­sÿÜ­yÿÜ­zÿÜ­{ÿÜ­|ÿÜ­}ÿÜ­~­­€­­©ÿ·­ª­­9­®9­¯ÿÜ­´þø­µÿ­ºÿu­»ÿa­Å/­Ç9­É9­ÐÿÜ­ÑÿÜ­ÒÿÜ­Ó­Ô­Õ­ã­êÿa­ëÿu­öÿÜ­ù­ûÿÜ­üÿÜ­ýÿÜ­þÿÜ®ÿÓ®ÿÜ®ÿÜ®$9®&ÿÜ®*ÿÜ®2ÿÜ®4ÿÜ®6®7ÿa®8®9ÿ}®:ÿ®;®<ÿa®FÿÜ®GÿÜ®HÿÜ®Iÿ·®RÿÜ®TÿÜ®WÿÜ®X®Yÿˆ®Zÿ­®\ÿu®b9®dÿÜ®gÿÜ®h®oÿÜ®pÿÜ®qÿÜ®rÿÜ®sÿÜ®yÿÜ®zÿÜ®{ÿÜ®|ÿÜ®}ÿÜ®~®®€®®©ÿ·®ª®­9®®9®¯ÿÜ®´þø®µÿ®ºÿu®»ÿa®Å/®Ç9®É9®ÐÿÜ®ÑÿÜ®ÒÿܮӮԮծã®êÿa®ëÿu®öÿÜ®ù®ûÿÜ®üÿÜ®ýÿÜ®þÿܯ9¯ÿ­¯ÿܯ$ÿܯ9ÿܯ;ÿ}¯<ÿ¯bÿܯ©ÿܯª¯­ÿܯ®ÿܯ´ÿÓ¯µÿܯ»ÿ¯ÅÿD¯ÇÿܯÉÿܯêÿ´$þø´%ÿÁ´&ÿ·´'ÿÁ´)ÿÁ´*ÿ·´+ÿÁ´-ÿÁ´.ÿÁ´/ÿÁ´2ÿ·´3ÿÁ´4ÿ·´5ÿÁ´7´9´:´;ÿˆ´<´=ÿÜ´Iÿ·´Qÿ´Rÿk´Uÿ´Yÿ·´Zÿ·´\ÿ·´bþø´dÿ·´gÿ·´xÿ´yÿk´zÿk´{ÿk´|ÿk´}ÿk´‰ÿÁ´þ}´­þø´®þø´¯ÿ·´ºÿ·´»´Çþø´Éþø´Ðÿ·´Ñÿ·´Òÿ·´åÿÜ´éÿ·´ê´ëÿ·´ìÿÁ´öÿ·´ûÿ·´ýÿ·ºÿܺþܺÿkº©ÿܺªÿܺ´ºµºÅþÓ»ÿ »þa»þð»$ÿa»&ÿ»2ÿ»Dþæ»Hþð»Lÿ·»Rþð»Xÿ»bÿa»dÿ»gÿ»iþæ»jþæ»kþæ»lþæ»mþæ»nþæ»pþð»qþð»rþð»sþð»yþð»zþð»{þð»|þð»}þð»~ÿ»ÿ»€ÿ»ÿ»©ÿ»ªÿk»­ÿa»®ÿa»¯ÿ»´ÿ»µÿÜ»Åþø»Çÿa»Éÿa»Ðÿ»Ñÿ»Òÿ»ûÿ»ýÿÅ$&Å%ÿ·Å&ÿÅ'ÿ·Å)ÿ·Å*ÿ·Å+ÿ·Å-/Å.ÿ·Å/ÿ·Å2ÿÅ3ÿ·Å4ÿÅ5ÿ·Å7þæÅ9þˆÅ:ÿÅ;ÿ·Å<þˆÅ=ÅIÿÜÅQÿ·ÅRÿ·ÅUÿ·ÅYÿÅZÿ<Å\ÿÅb&ÅdÿÅgÿÅxÿ·Åyÿ·Åzÿ·Å{ÿ·Å|ÿ·Å}ÿ·Å‰ÿ·Å&Å­&Å®&ůÿźÿÅ»þˆÅÇ&ÅÉ&ÅÐÿÅÑÿÅÒÿÅåÅéÿ·ÅêþˆÅëÿÅìÿ·Åöÿ·ÅûÿÅýÿÇÿÓÇÿÜÇÿÜÇ$9Ç&ÿÜÇ*ÿÜÇ2ÿÜÇ4ÿÜÇ6Ç7ÿaÇ8Ç9ÿ}Ç:ÿÇ;Ç<ÿaÇFÿÜÇGÿÜÇHÿÜÇIÿ·ÇRÿÜÇTÿÜÇWÿÜÇXÇYÿˆÇZÿ­Ç\ÿuÇb9ÇdÿÜÇgÿÜÇhÇoÿÜÇpÿÜÇqÿÜÇrÿÜÇsÿÜÇyÿÜÇzÿÜÇ{ÿÜÇ|ÿÜÇ}ÿÜÇ~ÇÇ€ÇÇ©ÿ·ÇªÇ­9Ç®9ǯÿÜÇ´þøÇµÿǺÿuÇ»ÿaÇÅ/ÇÇ9ÇÉ9ÇÐÿÜÇÑÿÜÇÒÿÜÇÓÇÔÇÕÇãÇêÿaÇëÿuÇöÿÜÇùÇûÿÜÇüÿÜÇýÿÜÇþÿÜÉÿÓÉÿÜÉÿÜÉ$9É&ÿÜÉ*ÿÜÉ2ÿÜÉ4ÿÜÉ6É7ÿaÉ8É9ÿ}É:ÿÉ;É<ÿaÉFÿÜÉGÿÜÉHÿÜÉIÿ·ÉRÿÜÉTÿÜÉWÿÜÉXÉYÿˆÉZÿ­É\ÿuÉb9ÉdÿÜÉgÿÜÉhÉoÿÜÉpÿÜÉqÿÜÉrÿÜÉsÿÜÉyÿÜÉzÿÜÉ{ÿÜÉ|ÿÜÉ}ÿÜÉ~ÉÉ€ÉÉ©ÿ·ÉªÉ­9É®9ɯÿÜÉ´þøÉµÿɺÿuÉ»ÿaÉÅ/ÉÇ9ÉÉ9ÉÐÿÜÉÑÿÜÉÒÿÜÉÓÉÔÉÕÉãÉêÿaÉëÿuÉöÿÜÉùÉûÿÜÉüÿÜÉýÿÜÉþÿÜÐ9Ðÿ­ÐÿÜÐ$ÿÜÐ9ÿÜÐ;ÿ}Ð<ÿÐbÿÜЩÿÜЪЭÿÜЮÿÜдÿÓеÿÜлÿÐÅÿDÐÇÿÜÐÉÿÜÐêÿÑ9Ñÿ­ÑÿÜÑ$ÿÜÑ9ÿÜÑ;ÿ}Ñ<ÿÑbÿÜÑ©ÿÜѪѭÿÜÑ®ÿÜÑ´ÿÓѵÿÜÑ»ÿÑÅÿDÑÇÿÜÑÉÿÜÑêÿÒ9Òÿ­ÒÿÜÒ$ÿÜÒ9ÿÜÒ;ÿ}Ò<ÿÒbÿÜÒ©ÿÜÒªÒ­ÿÜÒ®ÿÜÒ´ÿÓÒµÿÜÒ»ÿÒÅÿDÒÇÿÜÒÉÿÜÒêÿÓ$Ó-Ó=ÿÜÓbÓ­Ó®ÓÇÓÉÓåÿÜÔ$Ô-Ô=ÿÜÔbÔ­Ô®ÔÇÔÉÔåÿÜÕ$Õ-Õ=ÿÜÕbÕ­Õ®ÕÇÕÉÕåÿÜã$&ã&ã*ã2ã4ã6ãb&ãdãgã­&ã®&ã¯ãÇ&ãÉ&ãÐãÑãÒãããöãùãûãýåÿÜå©åªå´ÿÜåµÿÜåÅÿÜéé©éªé´ÿ¤éµÿéÅÿ·êÿ êþaêþðê$ÿaê&ÿê2ÿêDþæêHþðêLÿ·êRþðêXÿêbÿaêdÿêgÿêiþæêjþæêkþæêlþæêmþæênþæêpþðêqþðêrþðêsþðêyþðêzþðê{þðê|þðê}þðê~ÿêÿê€ÿêÿê©ÿêªÿkê­ÿaê®ÿaê¯ÿê´ÿêµÿÜêÅþøêÇÿaêÉÿaêÐÿêÑÿêÒÿêûÿêýÿëÿÜëþÜëÿkë©ÿÜëªÿÜë´ëµëÅþÓììÿkìÿ·ì©ìªì´ÿÜìµìÅÿDöö$ö7ÿ·ö:ö<ÿšöbö©ÿÜöªÿÜö­ö®ö´ÿÓöµÿÓö»ÿšöÅÿÉöÇöÉöêÿšù$&ù&ù*ù2ù4ù6ùb&ùdùgù­&ù®&ù¯ùÇ&ùÉ&ùÐùÑùÒùãùöùùùûùýûû$û6û<ÿÜûbû©ÿÜûªÿÜû­û®û´ûµ&û»ÿÜûÅûÇûÉûãûêÿÜûùýý$ý6ý<ÿÜýbý©ÿÜýªÿÜý­ý®ý´ýµ&ý»ÿÜýÅýÇýÉýãýêÿÜýù MB@hmþ ¼þ‰þ‰ L GÌþBGÌSf €¯ JBits@ ûþšmãB±‹`#cÕVeraSansÿÿÿÿ6ÿÿþ628R00@                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        "                      "       #                       #     $               $    &              &    ÿÿ P ì_<õº¹ð¸ºÂg‘þ‰þ Lmuserland/host_applications/linux/apps/hello_pi/hello_font/main.c000066400000000000000000000121351421703157200255400ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ // Test app for VG font library. #include #include #include #include #include #include "bcm_host.h" #include "vgfont.h" #include "revision.h" static const char *strnchr(const char *str, size_t len, char c) { const char *e = str + len; do { if (*str == c) { return str; } } while (++str < e); return NULL; } int32_t render_subtitle(GRAPHICS_RESOURCE_HANDLE img, const char *text, const int skip, const uint32_t text_size, const uint32_t y_offset) { uint32_t text_length = strlen(text)-skip; uint32_t width=0, height=0; const char *split = text; int32_t s=0; int len = 0; // length of pre-subtitle uint32_t img_w, img_h; graphics_get_resource_size(img, &img_w, &img_h); if (text_length==0) return 0; while (split[0]) { s = graphics_resource_text_dimensions_ext(img, split, text_length-(split-text), &width, &height, text_size); if (s != 0) return s; if (width > img_w) { const char *space = strnchr(split, text_length-(split-text), ' '); if (!space) { len = split+1-text; split = split+1; } else { len = space-text; split = space+1; } } else { break; } } // split now points to last line of text. split-text = length of initial text. text_length-(split-text) is length of last line if (width) { s = graphics_resource_render_text_ext(img, (img_w - width)>>1, y_offset-height, GRAPHICS_RESOURCE_WIDTH, GRAPHICS_RESOURCE_HEIGHT, GRAPHICS_RGBA32(0xff,0xff,0xff,0xff), /* fg */ GRAPHICS_RGBA32(0,0,0,0x80), /* bg */ split, text_length-(split-text), text_size); if (s!=0) return s; } return render_subtitle(img, text, skip+text_length-len, text_size, y_offset - height); } int main(void) { GRAPHICS_RESOURCE_HANDLE img; uint32_t width, height; int LAYER=1; bcm_host_init(); int s; if (get_processor_id() == PROCESSOR_BCM2838) { puts("This demo application is not available on the Pi4\n\n"); exit(0); } s = gx_graphics_init("."); assert(s == 0); s = graphics_get_display_size(0, &width, &height); assert(s == 0); s = gx_create_window(0, width, height, GRAPHICS_RESOURCE_RGBA32, &img); assert(s == 0); // transparent before display to avoid screen flash graphics_resource_fill(img, 0, 0, width, height, GRAPHICS_RGBA32(0,0,0,0x00)); graphics_display_resource(img, 0, LAYER, 0, 0, GRAPHICS_RESOURCE_WIDTH, GRAPHICS_RESOURCE_HEIGHT, VC_DISPMAN_ROT0, 1); uint32_t text_size = 10; while (1) { const char *text = "The quick brown fox jumps over the lazy dog"; uint32_t y_offset = height-60+text_size/2; graphics_resource_fill(img, 0, 0, width, height, GRAPHICS_RGBA32(0,0,0,0x00)); // blue, at the top (y=40) graphics_resource_fill(img, 0, 40, width, 1, GRAPHICS_RGBA32(0,0,0xff,0xff)); // green, at the bottom (y=height-40) graphics_resource_fill(img, 0, height-40, width, 1, GRAPHICS_RGBA32(0,0xff,0,0xff)); // draw the subtitle text render_subtitle(img, text, 0, text_size, y_offset); graphics_update_displayed_resource(img, 0, 0, 0, 0); text_size += 1; if (text_size > 50) text_size = 10; } graphics_display_resource(img, 0, LAYER, 0, 0, GRAPHICS_RESOURCE_WIDTH, GRAPHICS_RESOURCE_HEIGHT, VC_DISPMAN_ROT0, 0); graphics_delete_resource(img); return 0; } userland/host_applications/linux/apps/hello_pi/hello_jpeg/000077500000000000000000000000001421703157200244255ustar00rootroot00000000000000userland/host_applications/linux/apps/hello_pi/hello_jpeg/CMakeLists.txt000066400000000000000000000002651421703157200271700ustar00rootroot00000000000000set(EXEC hello_jpeg.bin) set(SRCS jpeg.c) add_executable(${EXEC} ${SRCS}) target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) install(TARGETS ${EXEC} RUNTIME DESTINATION bin) userland/host_applications/linux/apps/hello_pi/hello_jpeg/Makefile000077500000000000000000000001211421703157200260620ustar00rootroot00000000000000OBJS=jpeg.o BIN=hello_jpeg.bin LDFLAGS+=-lilclient include ../Makefile.include userland/host_applications/linux/apps/hello_pi/hello_jpeg/jpeg.c000066400000000000000000000550671421703157200255330ustar00rootroot00000000000000/* Copyright (c) 2012, Matt Ownby Anthong Sale 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 name of the copyright holder nor the names of its 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. */ #include #include #include "jpeg.h" #define TIMEOUT_MS 2000 typedef struct _COMPONENT_DETAILS { COMPONENT_T *component; OMX_HANDLETYPE handle; int inPort; int outPort; } COMPONENT_DETAILS; struct _OPENMAX_JPEG_DECODER { ILCLIENT_T *client; COMPONENT_DETAILS *imageDecoder; COMPONENT_DETAILS *imageResizer; OMX_BUFFERHEADERTYPE **ppInputBufferHeader; int inputBufferHeaderCount; OMX_BUFFERHEADERTYPE *pOutputBufferHeader; }; int bufferIndex = 0; // index to buffer array int portSettingsChanged(OPENMAX_JPEG_DECODER * decoder) { OMX_PARAM_PORTDEFINITIONTYPE portdef; // need to setup the input for the resizer with the output of the // decoder portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); portdef.nVersion.nVersion = OMX_VERSION; portdef.nPortIndex = decoder->imageDecoder->outPort; OMX_GetParameter(decoder->imageDecoder->handle, OMX_IndexParamPortDefinition, &portdef); unsigned int uWidth = (unsigned int) portdef.format.image.nFrameWidth; unsigned int uHeight = (unsigned int) portdef.format.image.nFrameHeight; // tell resizer input what the decoder output will be providing portdef.nPortIndex = decoder->imageResizer->inPort; OMX_SetParameter(decoder->imageResizer->handle, OMX_IndexParamPortDefinition, &portdef); // establish tunnel between decoder output and resizer input OMX_SetupTunnel(decoder->imageDecoder->handle, decoder->imageDecoder->outPort, decoder->imageResizer->handle, decoder->imageResizer->inPort); // enable ports OMX_SendCommand(decoder->imageDecoder->handle, OMX_CommandPortEnable, decoder->imageDecoder->outPort, NULL); OMX_SendCommand(decoder->imageResizer->handle, OMX_CommandPortEnable, decoder->imageResizer->inPort, NULL); // put resizer in idle state (this allows the outport of the decoder // to become enabled) OMX_SendCommand(decoder->imageResizer->handle, OMX_CommandStateSet, OMX_StateIdle, NULL); // wait for state change complete ilclient_wait_for_event(decoder->imageResizer->component, OMX_EventCmdComplete, OMX_CommandStateSet, 1, OMX_StateIdle, 1, 0, TIMEOUT_MS); // once the state changes, both ports should become enabled and the // resizer // output should generate a settings changed event ilclient_wait_for_event(decoder->imageDecoder->component, OMX_EventCmdComplete, OMX_CommandPortEnable, 1, decoder->imageDecoder->outPort, 1, 0, TIMEOUT_MS); ilclient_wait_for_event(decoder->imageResizer->component, OMX_EventCmdComplete, OMX_CommandPortEnable, 1, decoder->imageResizer->inPort, 1, 0, TIMEOUT_MS); ilclient_wait_for_event(decoder->imageResizer->component, OMX_EventPortSettingsChanged, decoder->imageResizer->outPort, 1, 0, 1, 0, TIMEOUT_MS); ilclient_disable_port(decoder->imageResizer->component, decoder->imageResizer->outPort); // query output buffer requirements for resizer portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); portdef.nVersion.nVersion = OMX_VERSION; portdef.nPortIndex = decoder->imageResizer->outPort; OMX_GetParameter(decoder->imageResizer->handle, OMX_IndexParamPortDefinition, &portdef); // change output color format and dimensions to match input portdef.format.image.eCompressionFormat = OMX_IMAGE_CodingUnused; portdef.format.image.eColorFormat = OMX_COLOR_Format32bitABGR8888; portdef.format.image.nFrameWidth = uWidth; portdef.format.image.nFrameHeight = uHeight; portdef.format.image.nStride = 0; portdef.format.image.nSliceHeight = 0; portdef.format.image.bFlagErrorConcealment = OMX_FALSE; OMX_SetParameter(decoder->imageResizer->handle, OMX_IndexParamPortDefinition, &portdef); // grab output requirements again to get actual buffer size // requirement (and buffer count requirement!) OMX_GetParameter(decoder->imageResizer->handle, OMX_IndexParamPortDefinition, &portdef); // move resizer into executing state ilclient_change_component_state(decoder->imageResizer->component, OMX_StateExecuting); // show some logging so user knows it's working printf ("Width: %u Height: %u Output Color Format: 0x%x Buffer Size: %u\n", (unsigned int) portdef.format.image.nFrameWidth, (unsigned int) portdef.format.image.nFrameHeight, (unsigned int) portdef.format.image.eColorFormat, (unsigned int) portdef.nBufferSize); fflush(stdout); // enable output port of resizer OMX_SendCommand(decoder->imageResizer->handle, OMX_CommandPortEnable, decoder->imageResizer->outPort, NULL); // allocate the buffer // void* outputBuffer = 0; // if (posix_memalign(&outputBuffer, portdef.nBufferAlignment, // portdef.nBufferSize) != 0) // { // perror("Allocating output buffer"); // return OMXJPEG_ERROR_MEMORY; // } // set the buffer // int ret = OMX_UseBuffer(decoder->imageResizer->handle, // &decoder->pOutputBufferHeader, // decoder->imageResizer->outPort, NULL, // portdef.nBufferSize, // (OMX_U8 *) outputBuffer); int ret = OMX_AllocateBuffer(decoder->imageResizer->handle, &decoder->pOutputBufferHeader, decoder->imageResizer-> outPort, NULL, portdef.nBufferSize); if (ret != OMX_ErrorNone) { perror("Eror allocating buffer"); fprintf(stderr, "OMX_AllocateBuffer returned 0x%x allocating buffer size 0x%x\n", ret, portdef.nBufferSize); return OMXJPEG_ERROR_MEMORY; } ilclient_wait_for_event(decoder->imageResizer->component, OMX_EventCmdComplete, OMX_CommandPortEnable, 1, decoder->imageResizer->outPort, 1, 0, TIMEOUT_MS); return OMXJPEG_OK; } int portSettingsChangedAgain(OPENMAX_JPEG_DECODER * decoder) { ilclient_disable_port(decoder->imageDecoder->component, decoder->imageDecoder->outPort); ilclient_disable_port(decoder->imageResizer->component, decoder->imageResizer->inPort); OMX_PARAM_PORTDEFINITIONTYPE portdef; // need to setup the input for the resizer with the output of the // decoder portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); portdef.nVersion.nVersion = OMX_VERSION; portdef.nPortIndex = decoder->imageDecoder->outPort; OMX_GetParameter(decoder->imageDecoder->handle, OMX_IndexParamPortDefinition, &portdef); // tell resizer input what the decoder output will be providing portdef.nPortIndex = decoder->imageResizer->inPort; OMX_SetParameter(decoder->imageResizer->handle, OMX_IndexParamPortDefinition, &portdef); // enable output of decoder and input of resizer (ie enable tunnel) ilclient_enable_port(decoder->imageDecoder->component, decoder->imageDecoder->outPort); ilclient_enable_port(decoder->imageResizer->component, decoder->imageResizer->inPort); // need to wait for this event ilclient_wait_for_event(decoder->imageResizer->component, OMX_EventPortSettingsChanged, decoder->imageResizer->outPort, 1, 0, 0, 0, TIMEOUT_MS); return OMXJPEG_OK; } int prepareResizer(OPENMAX_JPEG_DECODER * decoder) { decoder->imageResizer = malloc(sizeof(COMPONENT_DETAILS)); if (decoder->imageResizer == NULL) { perror("malloc image resizer"); return OMXJPEG_ERROR_MEMORY; } int ret = ilclient_create_component(decoder->client, &decoder-> imageResizer-> component, "resize", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS | ILCLIENT_ENABLE_OUTPUT_BUFFERS); if (ret != 0) { perror("image resizer"); return OMXJPEG_ERROR_CREATING_COMP; } // grab the handle for later use decoder->imageResizer->handle = ILC_GET_HANDLE(decoder->imageResizer->component); // get and store the ports OMX_PORT_PARAM_TYPE port; port.nSize = sizeof(OMX_PORT_PARAM_TYPE); port.nVersion.nVersion = OMX_VERSION; OMX_GetParameter(ILC_GET_HANDLE(decoder->imageResizer->component), OMX_IndexParamImageInit, &port); if (port.nPorts != 2) { return OMXJPEG_ERROR_WRONG_NO_PORTS; } decoder->imageResizer->inPort = port.nStartPortNumber; decoder->imageResizer->outPort = port.nStartPortNumber + 1; decoder->pOutputBufferHeader = NULL; return OMXJPEG_OK; } int prepareImageDecoder(OPENMAX_JPEG_DECODER * decoder) { decoder->imageDecoder = malloc(sizeof(COMPONENT_DETAILS)); if (decoder->imageDecoder == NULL) { perror("malloc image decoder"); return OMXJPEG_ERROR_MEMORY; } int ret = ilclient_create_component(decoder->client, &decoder-> imageDecoder-> component, "image_decode", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS); if (ret != 0) { perror("image decode"); return OMXJPEG_ERROR_CREATING_COMP; } // grab the handle for later use in OMX calls directly decoder->imageDecoder->handle = ILC_GET_HANDLE(decoder->imageDecoder->component); // get and store the ports OMX_PORT_PARAM_TYPE port; port.nSize = sizeof(OMX_PORT_PARAM_TYPE); port.nVersion.nVersion = OMX_VERSION; OMX_GetParameter(decoder->imageDecoder->handle, OMX_IndexParamImageInit, &port); if (port.nPorts != 2) { return OMXJPEG_ERROR_WRONG_NO_PORTS; } decoder->imageDecoder->inPort = port.nStartPortNumber; decoder->imageDecoder->outPort = port.nStartPortNumber + 1; return OMXJPEG_OK; } int startupImageDecoder(OPENMAX_JPEG_DECODER * decoder) { // move to idle ilclient_change_component_state(decoder->imageDecoder->component, OMX_StateIdle); // set input image format OMX_IMAGE_PARAM_PORTFORMATTYPE imagePortFormat; memset(&imagePortFormat, 0, sizeof(OMX_IMAGE_PARAM_PORTFORMATTYPE)); imagePortFormat.nSize = sizeof(OMX_IMAGE_PARAM_PORTFORMATTYPE); imagePortFormat.nVersion.nVersion = OMX_VERSION; imagePortFormat.nPortIndex = decoder->imageDecoder->inPort; imagePortFormat.eCompressionFormat = OMX_IMAGE_CodingJPEG; OMX_SetParameter(decoder->imageDecoder->handle, OMX_IndexParamImagePortFormat, &imagePortFormat); // get buffer requirements OMX_PARAM_PORTDEFINITIONTYPE portdef; portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); portdef.nVersion.nVersion = OMX_VERSION; portdef.nPortIndex = decoder->imageDecoder->inPort; OMX_GetParameter(decoder->imageDecoder->handle, OMX_IndexParamPortDefinition, &portdef); // enable the port and setup the buffers OMX_SendCommand(decoder->imageDecoder->handle, OMX_CommandPortEnable, decoder->imageDecoder->inPort, NULL); decoder->inputBufferHeaderCount = portdef.nBufferCountActual; // allocate pointer array decoder->ppInputBufferHeader = (OMX_BUFFERHEADERTYPE **) malloc(sizeof(void) * decoder->inputBufferHeaderCount); // allocate each buffer int i; for (i = 0; i < decoder->inputBufferHeaderCount; i++) { if (OMX_AllocateBuffer(decoder->imageDecoder->handle, &decoder->ppInputBufferHeader[i], decoder->imageDecoder->inPort, (void *) i, portdef.nBufferSize) != OMX_ErrorNone) { perror("Allocate decode buffer"); return OMXJPEG_ERROR_MEMORY; } } // wait for port enable to complete - which it should once buffers are // assigned int ret = ilclient_wait_for_event(decoder->imageDecoder->component, OMX_EventCmdComplete, OMX_CommandPortEnable, 0, decoder->imageDecoder->inPort, 0, 0, TIMEOUT_MS); if (ret != 0) { fprintf(stderr, "Did not get port enable %d\n", ret); return OMXJPEG_ERROR_EXECUTING; } // start executing the decoder ret = OMX_SendCommand(decoder->imageDecoder->handle, OMX_CommandStateSet, OMX_StateExecuting, NULL); if (ret != 0) { fprintf(stderr, "Error starting image decoder %x\n", ret); return OMXJPEG_ERROR_EXECUTING; } ret = ilclient_wait_for_event(decoder->imageDecoder->component, OMX_EventCmdComplete, OMX_StateExecuting, 0, 0, 1, 0, TIMEOUT_MS); if (ret != 0) { fprintf(stderr, "Did not receive executing stat %d\n", ret); // return OMXJPEG_ERROR_EXECUTING; } return OMXJPEG_OK; } // this function run the boilerplate to setup the openmax components; int setupOpenMaxJpegDecoder(OPENMAX_JPEG_DECODER ** pDecoder) { *pDecoder = malloc(sizeof(OPENMAX_JPEG_DECODER)); if (pDecoder[0] == NULL) { perror("malloc decoder"); return OMXJPEG_ERROR_MEMORY; } memset(*pDecoder, 0, sizeof(OPENMAX_JPEG_DECODER)); if ((pDecoder[0]->client = ilclient_init()) == NULL) { perror("ilclient_init"); return OMXJPEG_ERROR_ILCLIENT_INIT; } if (OMX_Init() != OMX_ErrorNone) { ilclient_destroy(pDecoder[0]->client); perror("OMX_Init"); return OMXJPEG_ERROR_OMX_INIT; } // prepare the image decoder int ret = prepareImageDecoder(pDecoder[0]); if (ret != OMXJPEG_OK) return ret; ret = prepareResizer(pDecoder[0]); if (ret != OMXJPEG_OK) return ret; ret = startupImageDecoder(pDecoder[0]); if (ret != OMXJPEG_OK) return ret; return OMXJPEG_OK; } // this function passed the jpeg image buffer in, and returns the decoded // image int decodeImage(OPENMAX_JPEG_DECODER * decoder, char *sourceImage, size_t imageSize) { char *sourceOffset = sourceImage; // we store a separate // buffer ot image so we // can offset it size_t toread = 0; // bytes left to read from buffer toread += imageSize; int bFilled = 0; // have we filled our output // buffer bufferIndex = 0; while (toread > 0) { // get next buffer from array OMX_BUFFERHEADERTYPE *pBufHeader = decoder->ppInputBufferHeader[bufferIndex]; // step index and reset to 0 if required bufferIndex++; if (bufferIndex >= decoder->inputBufferHeaderCount) bufferIndex = 0; // work out the next chunk to load into the decoder if (toread > pBufHeader->nAllocLen) pBufHeader->nFilledLen = pBufHeader->nAllocLen; else pBufHeader->nFilledLen = toread; toread = toread - pBufHeader->nFilledLen; // pass the bytes to the buffer memcpy(pBufHeader->pBuffer, sourceOffset, pBufHeader->nFilledLen); // update the buffer pointer and set the input flags sourceOffset = sourceOffset + pBufHeader->nFilledLen; pBufHeader->nOffset = 0; pBufHeader->nFlags = 0; if (toread <= 0) { pBufHeader->nFlags = OMX_BUFFERFLAG_EOS; } // empty the current buffer int ret = OMX_EmptyThisBuffer(decoder->imageDecoder->handle, pBufHeader); if (ret != OMX_ErrorNone) { perror("Empty input buffer"); fprintf(stderr, "return code %x\n", ret); return OMXJPEG_ERROR_MEMORY; } // wait for buffer to empty or port changed event int done = 0; while ((done == 0) && (decoder->pOutputBufferHeader == NULL)) { if (decoder->pOutputBufferHeader == NULL) { ret = ilclient_wait_for_event (decoder->imageDecoder->component, OMX_EventPortSettingsChanged, decoder->imageDecoder->outPort, 0, 0, 1, 0, 5); if (ret == 0) { ret = portSettingsChanged(decoder); if (ret != OMXJPEG_OK) return ret; } } else { ret = ilclient_remove_event(decoder->imageDecoder->component, OMX_EventPortSettingsChanged, decoder->imageDecoder->outPort, 0, 0, 1); if (ret == 0) portSettingsChangedAgain(decoder); } // check to see if buffer is now empty if (pBufHeader->nFilledLen == 0) done = 1; if ((done == 0) || (decoder->pOutputBufferHeader == NULL)) sleep(1); } // fill the buffer if we have created the buffer if ((bFilled == 0) && (decoder->pOutputBufferHeader != NULL)) { ret = OMX_FillThisBuffer(decoder->imageResizer->handle, decoder->pOutputBufferHeader); if (ret != OMX_ErrorNone) { perror("Filling output buffer"); fprintf(stderr, "Error code %x\n", ret); return OMXJPEG_ERROR_MEMORY; } bFilled = 1; } } // wait for buffer to fill /* * while(pBufHeader->nFilledLen == 0) { sleep(5); } */ // wait for end of stream events int ret = ilclient_wait_for_event(decoder->imageDecoder->component, OMX_EventBufferFlag, decoder->imageDecoder->outPort, 1, OMX_BUFFERFLAG_EOS, 1, 0, 2); if (ret != 0) { fprintf(stderr, "No EOS event on image decoder %d\n", ret); } ret = ilclient_wait_for_event(decoder->imageResizer->component, OMX_EventBufferFlag, decoder->imageResizer->outPort, 1, OMX_BUFFERFLAG_EOS, 1, 0, 2); if (ret != 0) { fprintf(stderr, "No EOS event on image resizer %d\n", ret); } return OMXJPEG_OK; } // this function cleans up the decoder. void cleanup(OPENMAX_JPEG_DECODER * decoder) { // flush everything through OMX_SendCommand(decoder->imageDecoder->handle, OMX_CommandFlush, decoder->imageDecoder->outPort, NULL); ilclient_wait_for_event(decoder->imageDecoder->component, OMX_EventCmdComplete, OMX_CommandFlush, 0, decoder->imageDecoder->outPort, 0, 0, TIMEOUT_MS); OMX_SendCommand(decoder->imageResizer->handle, OMX_CommandFlush, decoder->imageResizer->inPort, NULL); ilclient_wait_for_event(decoder->imageResizer->component, OMX_EventCmdComplete, OMX_CommandFlush, 0, decoder->imageResizer->inPort, 1, 0, TIMEOUT_MS); OMX_SendCommand(decoder->imageDecoder->handle, OMX_CommandPortDisable, decoder->imageDecoder->inPort, NULL); int i = 0; for (i = 0; i < decoder->inputBufferHeaderCount; i++) { OMX_BUFFERHEADERTYPE *vpBufHeader = decoder->ppInputBufferHeader[i]; OMX_FreeBuffer(decoder->imageDecoder->handle, decoder->imageDecoder->inPort, vpBufHeader); } ilclient_wait_for_event(decoder->imageDecoder->component, OMX_EventCmdComplete, OMX_CommandPortDisable, 0, decoder->imageDecoder->inPort, 0, 0, TIMEOUT_MS); OMX_SendCommand(decoder->imageResizer->handle, OMX_CommandPortDisable, decoder->imageResizer->outPort, NULL); OMX_FreeBuffer(decoder->imageResizer->handle, decoder->imageResizer->outPort, decoder->pOutputBufferHeader); ilclient_wait_for_event(decoder->imageResizer->component, OMX_EventCmdComplete, OMX_CommandPortDisable, 0, decoder->imageResizer->outPort, 0, 0, TIMEOUT_MS); OMX_SendCommand(decoder->imageDecoder->handle, OMX_CommandPortDisable, decoder->imageDecoder->outPort, NULL); ilclient_wait_for_event(decoder->imageDecoder->component, OMX_EventCmdComplete, OMX_CommandPortDisable, 0, decoder->imageDecoder->outPort, 0, 0, TIMEOUT_MS); OMX_SendCommand(decoder->imageResizer->handle, OMX_CommandPortDisable, decoder->imageResizer->inPort, NULL); ilclient_wait_for_event(decoder->imageResizer->component, OMX_EventCmdComplete, OMX_CommandPortDisable, 0, decoder->imageResizer->inPort, 0, 0, TIMEOUT_MS); OMX_SetupTunnel(decoder->imageDecoder->handle, decoder->imageDecoder->outPort, NULL, 0); OMX_SetupTunnel(decoder->imageResizer->handle, decoder->imageResizer->inPort, NULL, 0); ilclient_change_component_state(decoder->imageDecoder->component, OMX_StateIdle); ilclient_change_component_state(decoder->imageResizer->component, OMX_StateIdle); ilclient_wait_for_event(decoder->imageDecoder->component, OMX_EventCmdComplete, OMX_CommandStateSet, 0, OMX_StateIdle, 0, 0, TIMEOUT_MS); ilclient_wait_for_event(decoder->imageResizer->component, OMX_EventCmdComplete, OMX_CommandStateSet, 0, OMX_StateIdle, 0, 0, TIMEOUT_MS); ilclient_change_component_state(decoder->imageDecoder->component, OMX_StateLoaded); ilclient_change_component_state(decoder->imageResizer->component, OMX_StateLoaded); ilclient_wait_for_event(decoder->imageDecoder->component, OMX_EventCmdComplete, OMX_CommandStateSet, 0, OMX_StateLoaded, 0, 0, TIMEOUT_MS); ilclient_wait_for_event(decoder->imageResizer->component, OMX_EventCmdComplete, OMX_CommandStateSet, 0, OMX_StateLoaded, 0, 0, TIMEOUT_MS); OMX_Deinit(); if (decoder->client != NULL) { ilclient_destroy(decoder->client); } } int main(int argc, char *argv[]) { OPENMAX_JPEG_DECODER *pDecoder; char *sourceImage; size_t imageSize; int s; if (argc < 2) { printf("Usage: %s \n", argv[0]); return -1; } FILE *fp = fopen(argv[1], "rb"); if (!fp) { printf("File %s not found.\n", argv[1]); } fseek(fp, 0L, SEEK_END); imageSize = ftell(fp); fseek(fp, 0L, SEEK_SET); sourceImage = malloc(imageSize); assert(sourceImage != NULL); s = fread(sourceImage, 1, imageSize, fp); assert(s == imageSize); fclose(fp); bcm_host_init(); s = setupOpenMaxJpegDecoder(&pDecoder); assert(s == 0); s = decodeImage(pDecoder, sourceImage, imageSize); assert(s == 0); cleanup(pDecoder); free(sourceImage); return 0; } userland/host_applications/linux/apps/hello_pi/hello_jpeg/jpeg.h000066400000000000000000000051231421703157200255240ustar00rootroot00000000000000/* Copyright (c) 2012, Matt Ownby Anthong Sale 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 name of the copyright holder nor the names of its 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. */ #ifndef _OPTION_H_ #define _OPTION_H_ /* Defines the methods for interacting with openmax il and ilclient to decode jpeg images from the camera */ #include #include #include #include #include "bcm_host.h" #include "ilclient.h" #define OMXJPEG_OK 0 #define OMXJPEG_ERROR_ILCLIENT_INIT -1024 #define OMXJPEG_ERROR_OMX_INIT -1025 #define OMXJPEG_ERROR_MEMORY -1026 #define OMXJPEG_ERROR_CREATING_COMP -1027 #define OMXJPEG_ERROR_WRONG_NO_PORTS -1028 #define OMXJPEG_ERROR_EXECUTING -1029 #define OMXJPEG_ERROR_NOSETTINGS -1030 typedef struct _OPENMAX_JPEG_DECODER OPENMAX_JPEG_DECODER; //this function run the boilerplate to setup the openmax components; int setupOpenMaxJpegDecoder(OPENMAX_JPEG_DECODER** decoder); //this function passed the jpeg image buffer in, and returns the decoded image int decodeImage(OPENMAX_JPEG_DECODER* decoder, char* sourceImage, size_t imageSize); //this function cleans up the decoder. void cleanup(OPENMAX_JPEG_DECODER* decoder); #endif userland/host_applications/linux/apps/hello_pi/hello_mmal_encode/000077500000000000000000000000001421703157200257435ustar00rootroot00000000000000userland/host_applications/linux/apps/hello_pi/hello_mmal_encode/Makefile000066400000000000000000000005231421703157200274030ustar00rootroot00000000000000OBJS=mmal_encode.o BIN=hello_mmal_encode.bin LDFLAGS+=-lmmal -lmmal_core -lmmal_components -lmmal_util -lmmal_vc_client # Set --no-as-needed to stop the linker discarding mmal_vc_client # as it can't see that the constructor registers a load of functionality # with the MMAL core. LDFLAGS+=-Wl,--no-as-needed include ../Makefile.include userland/host_applications/linux/apps/hello_pi/hello_mmal_encode/mmal_encode.c000066400000000000000000000203171421703157200303550ustar00rootroot00000000000000/* Copyright (C) 2016 RealVNC Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of the copyright holder nor the names of its 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. */ // Image encoding example using MMAL #include #include #include #include #include #include #include #include #include #include #include // Format for test image const unsigned int WIDTH = 512; const unsigned int HEIGHT = 512; const MMAL_FOURCC_T INPUT_ENC = MMAL_ENCODING_RGBA; const unsigned int BYTESPP = 4; const uint32_t RED = 0xff; const uint32_t GREEN = 0xff << 8; const uint32_t BLUE = 0xff << 16; const uint32_t ALPHA = 0xff << 24; static MMAL_WRAPPER_T* encoder; static VCOS_SEMAPHORE_T sem; // This callback and the above semaphore is used when waiting for // data to be returned. static void mmalCallback(MMAL_WRAPPER_T* encoder) { vcos_semaphore_post(&sem); } // Create a test input image in the supplied buffer consisting of 8 vertical bars of // white | black | red | green | blue | cyan | magenta | yellow void create_rgba_test_image(void* buf, unsigned int length, unsigned int stride) { uint32_t* pixel = buf; int i; for (i=0; iinput[0]; encoder->status = MMAL_SUCCESS; if (portIn->is_enabled) { if (mmal_wrapper_port_disable(portIn) != MMAL_SUCCESS) { fprintf(stderr, "Failed to disable input port\n"); exit(1); } } portIn->format->encoding = INPUT_ENC; portIn->format->es->video.width = VCOS_ALIGN_UP(WIDTH, 32); portIn->format->es->video.height = VCOS_ALIGN_UP(HEIGHT, 16); portIn->format->es->video.crop.x = 0; portIn->format->es->video.crop.y = 0; portIn->format->es->video.crop.width = WIDTH; portIn->format->es->video.crop.height = HEIGHT; if (mmal_port_format_commit(portIn) != MMAL_SUCCESS) { fprintf(stderr, "Failed to commit input port format\n"); exit(1); } portIn->buffer_size = portIn->buffer_size_recommended; portIn->buffer_num = portIn->buffer_num_recommended; if (mmal_wrapper_port_enable(portIn, MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE) != MMAL_SUCCESS) { fprintf(stderr, "Failed to enable input port\n"); exit(1); } printf("- input %4.4s %ux%u\n", (char*)&portIn->format->encoding, portIn->format->es->video.width, portIn->format->es->video.height); // Configure output portOut = encoder->output[0]; if (portOut->is_enabled) { if (mmal_wrapper_port_disable(portOut) != MMAL_SUCCESS) { fprintf(stderr, "Failed to disable output port\n"); exit(1); } } portOut->format->encoding = encoding; if (mmal_port_format_commit(portOut) != MMAL_SUCCESS) { fprintf(stderr, "Failed to commit output port format\n"); exit(1); } mmal_port_parameter_set_uint32(portOut, MMAL_PARAMETER_JPEG_Q_FACTOR, 100); portOut->buffer_size = portOut->buffer_size_recommended; portOut->buffer_num = portOut->buffer_num_recommended; if (mmal_wrapper_port_enable(portOut, MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE) != MMAL_SUCCESS) { fprintf(stderr, "Failed to enable output port\n"); exit(1); } printf("- output %4.4s\n", (char*)&encoding); // Perform the encoding outFile = fopen(filename, "w"); if (!outFile) { fprintf(stderr, "Failed to open file %s (%s)\n", filename, strerror(errno)); exit(1); } while (!eos) { // Send output buffers to be filled with encoded image. while (mmal_wrapper_buffer_get_empty(portOut, &out, 0) == MMAL_SUCCESS) { if (mmal_port_send_buffer(portOut, out) != MMAL_SUCCESS) { fprintf(stderr, "Failed to send buffer\n"); break; } } // Send image to be encoded. if (!sent && mmal_wrapper_buffer_get_empty(portIn, &in, 0) == MMAL_SUCCESS) { printf("- sending %u bytes to encoder\n", in->alloc_size); create_rgba_test_image(in->data, in->alloc_size, portIn->format->es->video.width); in->length = in->alloc_size; in->flags = MMAL_BUFFER_HEADER_FLAG_EOS; if (mmal_port_send_buffer(portIn, in) != MMAL_SUCCESS) { fprintf(stderr, "Failed to send buffer\n"); break; } sent = 1; } // Get filled output buffers. status = mmal_wrapper_buffer_get_full(portOut, &out, 0); if (status == MMAL_EAGAIN) { // No buffer available, wait for callback and loop. vcos_semaphore_wait(&sem); continue; } else if (status != MMAL_SUCCESS) { fprintf(stderr, "Failed to get full buffer\n"); exit(1); } printf("- received %i bytes\n", out->length); eos = out->flags & MMAL_BUFFER_HEADER_FLAG_EOS; nw = fwrite(out->data, 1, out->length, outFile); if (nw != out->length) { fprintf(stderr, "Failed to write complete buffer\n"); exit(1); } outputWritten += nw; mmal_buffer_header_release(out); } mmal_port_flush(portOut); fclose(outFile); printf("- written %u bytes to %s\n\n", outputWritten, filename); } int main(int argc, const char** argv) { bcm_host_init(); if (vcos_semaphore_create(&sem, "encoder sem", 0) != VCOS_SUCCESS) { fprintf(stderr, "Failed to create semaphore\n"); exit(1); } if (mmal_wrapper_create(&encoder, MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER) != MMAL_SUCCESS) { fprintf(stderr, "Failed to create mmal component\n"); exit(1); } encoder->callback = mmalCallback; // Perform test encodings in various formats mmal_encode_test(MMAL_ENCODING_PNG, "out.png"); mmal_encode_test(MMAL_ENCODING_JPEG, "out.jpg"); mmal_encode_test(MMAL_ENCODING_GIF, "out.gif"); mmal_encode_test(MMAL_ENCODING_BMP, "out.bmp"); mmal_wrapper_destroy(encoder); vcos_semaphore_delete(&sem); return 0; } userland/host_applications/linux/apps/hello_pi/hello_teapot/000077500000000000000000000000001421703157200247745ustar00rootroot00000000000000userland/host_applications/linux/apps/hello_pi/hello_teapot/CMakeLists.txt000066400000000000000000000003141421703157200275320ustar00rootroot00000000000000set(EXEC hello_teapot.bin) set(SRCS triangle.c video.c models.c) add_executable(${EXEC} ${SRCS}) target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) install(TARGETS ${EXEC} RUNTIME DESTINATION bin) userland/host_applications/linux/apps/hello_pi/hello_teapot/Makefile000066400000000000000000000001641421703157200264350ustar00rootroot00000000000000OBJS=triangle.o video.o models.o BIN=hello_teapot.bin LDFLAGS+=-lilclient -lrevision include ../Makefile.include userland/host_applications/linux/apps/hello_pi/hello_teapot/README.md000066400000000000000000000001631421703157200262530ustar00rootroot00000000000000hello_videocube =============== Sample for Raspberry Pi that uses egl_render to display video on an animated cube.userland/host_applications/linux/apps/hello_pi/hello_teapot/cube_texture_and_coords.h000066400000000000000000000047721421703157200320500ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ // Spatial coordinates for the cube static const GLbyte quadx[6*4*3] = { /* FRONT */ -10, -10, 10, 10, -10, 10, -10, 10, 10, 10, 10, 10, /* BACK */ -10, -10, -10, -10, 10, -10, 10, -10, -10, 10, 10, -10, /* LEFT */ -10, -10, 10, -10, 10, 10, -10, -10, -10, -10, 10, -10, /* RIGHT */ 10, -10, -10, 10, 10, -10, 10, -10, 10, 10, 10, 10, /* TOP */ -10, 10, 10, 10, 10, 10, -10, 10, -10, 10, 10, -10, /* BOTTOM */ -10, -10, 10, -10, -10, -10, 10, -10, 10, 10, -10, -10, }; /** Texture coordinates for the quad. */ static const GLfloat texCoords[6 * 4 * 2] = { 0.f, 0.f, 0.f, 1.f, 1.f, 0.f, 1.f, 1.f, 0.f, 0.f, 0.f, 1.f, 1.f, 0.f, 1.f, 1.f, 0.f, 0.f, 0.f, 1.f, 1.f, 0.f, 1.f, 1.f, 0.f, 0.f, 0.f, 1.f, 1.f, 0.f, 1.f, 1.f, 0.f, 0.f, 0.f, 1.f, 1.f, 0.f, 1.f, 1.f, 0.f, 0.f, 0.f, 1.f, 1.f, 0.f, 1.f, 1.f }; userland/host_applications/linux/apps/hello_pi/hello_teapot/models.c000066400000000000000000000442061421703157200264310ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #include #include #include #include "GLES/gl.h" #include "EGL/egl.h" #include "EGL/eglext.h" #include "models.h" #define VMCS_RESOURCE(a,b) (b) /****************************************************************************** Private typedefs, macros and constants ******************************************************************************/ enum {VBO_VERTEX, VBO_NORMAL, VBO_TEXTURE, VBO_MAX}; #define MAX_MATERIALS 4 #define MAX_MATERIAL_NAME 32 typedef struct wavefront_material_s { GLuint vbo[VBO_MAX]; int numverts; char name[MAX_MATERIAL_NAME]; GLuint texture; } WAVEFRONT_MATERIAL_T; typedef struct wavefront_model_s { WAVEFRONT_MATERIAL_T material[MAX_MATERIALS]; int num_materials; GLuint texture; } WAVEFRONT_MODEL_T; /****************************************************************************** Static Data ******************************************************************************/ /****************************************************************************** Static Function Declarations ******************************************************************************/ /****************************************************************************** Static Function Definitions ******************************************************************************/ static void create_vbo(GLenum type, GLuint *vbo, int size, void *data) { glGenBuffers(1, vbo); vc_assert(*vbo); glBindBuffer(type, *vbo); glBufferData(type, size, data, GL_STATIC_DRAW); glBindBuffer(type, 0); } static void destroy_vbo(GLuint *vbo) { glDeleteBuffers(1, vbo); *vbo = 0; } #define MAX_VERTICES 100000 static void *allocbuffer(int size) { return malloc(size); } static void freebuffer(void *p) { free (p); } static void centre_and_rescale(float *verts, int numvertices) { float cx=0.0f, cy=0.0f, cz=0.0f, scale=0.0f; float minx=0.0f, miny=0.0f, minz=0.0f; float maxx=0.0f, maxy=0.0f, maxz=0.0f; int i; float *v = verts; minx = maxx = verts[0]; miny = maxy = verts[1]; minz = maxz = verts[2]; for (i=0; i= 3) *dst++ = src[ind + 2]; indexes += 3; } } int draw_wavefront(MODEL_T m, GLuint texture) { int i; WAVEFRONT_MODEL_T *model = (WAVEFRONT_MODEL_T *)m; for (i=0; inum_materials; i++) { WAVEFRONT_MATERIAL_T *mat = model->material + i; if (mat->texture == -1) continue; glBindTexture(GL_TEXTURE_2D, mat->texture ? mat->texture:texture); if (mat->vbo[VBO_VERTEX]) { glBindBuffer(GL_ARRAY_BUFFER, mat->vbo[VBO_VERTEX]); glVertexPointer(3, GL_FLOAT, 0, NULL); } if (mat->vbo[VBO_NORMAL]) { glEnableClientState(GL_NORMAL_ARRAY); glBindBuffer(GL_ARRAY_BUFFER, mat->vbo[VBO_NORMAL]); glNormalPointer(GL_FLOAT, 0, NULL); } else { glDisableClientState(GL_NORMAL_ARRAY); } if (mat->vbo[VBO_TEXTURE]) { glEnableClientState(GL_TEXTURE_COORD_ARRAY); glBindBuffer(GL_ARRAY_BUFFER, mat->vbo[VBO_TEXTURE]); glTexCoordPointer(2, GL_FLOAT, 0, NULL); } else { glDisableClientState(GL_TEXTURE_COORD_ARRAY); } glDrawArrays(GL_TRIANGLES, 0, mat->numverts); } glBindBuffer(GL_ARRAY_BUFFER, 0); return 0; } struct wavefront_model_loading_s { unsigned short material_index[MAX_MATERIALS]; int num_materials; int numv, numt, numn, numf; unsigned int data[0]; }; static int load_wavefront_obj(const char *modelname, WAVEFRONT_MODEL_T *model, struct wavefront_model_loading_s *m) { char line[256+1]; unsigned short pp[54+1]; FILE *fp; int i, valid; float *qv = (float *)m->data; float *qt = (float *)m->data + 3 * MAX_VERTICES; float *qn = (float *)m->data + (3+2) * MAX_VERTICES; unsigned short *qf = (unsigned short *)((float *)m->data + (3+2+3) * MAX_VERTICES); float *pv = qv, *pt = qt, *pn = qn; unsigned short *pf = qf; fp = fopen(modelname, "r"); if (!fp) return -1; m->num_materials = 0; m->material_index[0] = 0; valid = fread(line, 1, sizeof(line)-1, fp); while (valid > 0) { char *s, *end = line; while((end-line < valid) && *end != '\n' && *end != '\r') end++; *end++ = 0; if((end-line < valid) && *end != '\n' && *end != '\r') *end++ = 0; s = line; if (s[strlen(s)-1] == 10) s[strlen(s)-1]=0; switch (s[0]) { case '#': break; // comment case '\r': case '\n': case '\0': break; // blank line case 'm': vc_assert(strncmp(s, "mtllib", sizeof "mtllib"-1)==0); break; case 'o': break; case 'u': if (sscanf(s, "usemtl %s", /*MAX_MATERIAL_NAME-1, */model->material[m->num_materials].name) == 1) { if (m->num_materials < MAX_MATERIALS) { if (m->num_materials > 0 && ((pf-qf)/3 == m->material_index[m->num_materials-1] || strcmp(model->material[m->num_materials-1].name, model->material[m->num_materials].name)==0)) { strcpy(model->material[m->num_materials-1].name, model->material[m->num_materials].name); m->num_materials--; } else m->material_index[m->num_materials] = (pf-qf)/3; m->num_materials++; } } else { printf(s); vc_assert(0); } break; case 'g': vc_assert(strncmp(s, "g ", sizeof "g "-1)==0); break; case 's': vc_assert(strncmp(s, "s ", sizeof "s "-1)==0); break; case 'v': case 'f': if (sscanf(s, "v %f %f %f", pv+0, pv+1, pv+2) == 3) { pv += 3; } else if (sscanf(s, "vt %f %f", pt+0, pt+1) == 2) { pt += 2; } else if (sscanf(s, "vn %f %f %f", pn+0, pn+1, pn+2) == 3) { pn += 3; } else if (i = sscanf(s, "f"" %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu" " %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu" " %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu", pp+ 0, pp+ 1, pp+ 2, pp+ 3, pp+ 4, pp+ 5, pp+ 6, pp+ 7, pp+ 8, pp+ 9, pp+10, pp+11, pp+12, pp+13, pp+14, pp+15, pp+16, pp+17, pp+18, pp+19, pp+20, pp+21, pp+22, pp+23, pp+24, pp+25, pp+26, pp+27, pp+28, pp+29, pp+30, pp+32, pp+32, pp+33, pp+34, pp+35, pp+36), i >= 6) { int poly = i/2; //vc_assert(i < countof(pp)); // may need to increment poly count and pp array for (i=1; i= 6) { int poly = i/2; //vc_assert(i < countof(pp); // may need to increment poly count and pp array for (i=1; i= 9) { int poly = i/3; //vc_assert(i < countof(pp); // may need to increment poly count and pp array for (i=1; i valid ? valid : end-line; memmove(line, end, valid - i); valid -= i; valid += fread(line+valid, 1, sizeof(line)-1-valid, fp); } fclose(fp); if (m->num_materials==0) m->material_index[m->num_materials++] = 0; centre_and_rescale(qv, (pv-qv)/3); renormalise(qn, (pn-qn)/3); //centre_and_rescale2(qt, (pt-qt)/2); m->numv = pv-qv; m->numt = pt-qt; m->numn = pn-qn; m->numf = pf-qf; // compress array //memcpy((float *)m->data, (float *)m->data, m->numv * sizeof *qv); - nop memcpy((float *)m->data + m->numv, (float *)m->data + 3 * MAX_VERTICES, m->numt * sizeof *qt); memcpy((float *)m->data + m->numv + m->numt,(float *) m->data + (3 + 2) * MAX_VERTICES, m->numn * sizeof *qn); memcpy((float *)m->data + m->numv + m->numt + m->numn, (float *)m->data + (3 + 2 + 3) * MAX_VERTICES, m->numf * sizeof *qf); return 0; } static int load_wavefront_dat(const char *modelname, WAVEFRONT_MODEL_T *model, struct wavefront_model_loading_s *m) { FILE *fp; int s; const int size = sizeof *m + sizeof(float)*(3+2+3)*MAX_VERTICES + // 3 vertices + 2 textures + 3 normals sizeof(unsigned short)*3*MAX_VERTICES; //each face has 9 vertices fp = fopen(modelname, "r"); if (!fp) return -1; s = fread(m, 1, size, fp); if (s < 0) return -1; fclose(fp); return 0; } MODEL_T load_wavefront(const char *modelname, const char *texturename) { WAVEFRONT_MODEL_T *model; float *temp, *qv, *qt, *qn; unsigned short *qf; int i; int numverts = 0, offset = 0; struct wavefront_model_loading_s *m; int s=-1; char modelname_obj[128]; model = malloc(sizeof *model); if (!model || !modelname) return NULL; memset (model, 0, sizeof *model); model->texture = 0; //load_texture(texturename); m = allocbuffer(sizeof *m + sizeof(float)*(3+2+3)*MAX_VERTICES + // 3 vertices + 2 textures + 3 normals sizeof(unsigned short)*3*MAX_VERTICES); //each face has 9 vertices if (!m) return 0; if (strlen(modelname) + 5 <= sizeof modelname_obj) { strcpy(modelname_obj, modelname); strcat(modelname_obj, ".dat"); s = load_wavefront_dat(modelname_obj, model, m); } if (s==0) {} else if (strncmp(modelname + strlen(modelname) - 4, ".obj", 4) == 0) { #ifdef DUMP_OBJ_DAT int size; FILE *fp; #endif s = load_wavefront_obj(modelname, model, m); #ifdef DUMP_OBJ_DAT strcpy(modelname_obj, modelname); strcat(modelname_obj, ".dat"); size = sizeof *m + sizeof(float)*(3*m->numv+2*m->numt+3*m->numn) + // 3 vertices + 2 textures + 3 normals sizeof(unsigned short)*3*m->numf; //each face has 9 vertices fp = host_file_open(modelname_obj, "w"); fwrite(m, 1, size, fp); fclose(fp); #endif } else if (strncmp(modelname + strlen(modelname) - 4, ".dat", 4) == 0) { s = load_wavefront_dat(modelname, model, m); } if (s != 0) return 0; qv = (float *)(m->data); qt = (float *)(m->data + m->numv); qn = (float *)(m->data + m->numv + m->numt); qf = (unsigned short *)(m->data + m->numv + m->numt + m->numn); numverts = m->numf/3; vc_assert(numverts <= MAX_VERTICES); temp = allocbuffer(3*numverts*sizeof *temp); for (i=0; inum_materials; i++) { WAVEFRONT_MATERIAL_T *mat = model->material + i; mat->numverts = i < m->num_materials-1 ? m->material_index[i+1]-m->material_index[i] : numverts - m->material_index[i]; // vertex, texture, normal deindex(temp, qv, qf+3*offset+0, 3, mat->numverts); create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_VERTEX, 3 * mat->numverts * sizeof *qv, temp); // 3 deindex(temp, qt, qf+3*offset+1, 2, mat->numverts); create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_TEXTURE, 2 * mat->numverts * sizeof *qt, temp); // 2 deindex(temp, qn, qf+3*offset+2, 3, mat->numverts); create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_NORMAL, 3 * mat->numverts * sizeof *qn, temp); // 3 offset += mat->numverts; mat->texture = model->texture; } model->num_materials = m->num_materials; vc_assert(offset == numverts); freebuffer(temp); freebuffer(m); return (MODEL_T)model; } void unload_wavefront(MODEL_T m) { WAVEFRONT_MODEL_T *model = (WAVEFRONT_MODEL_T *)m; int i; for (i=0; inum_materials; i++) { WAVEFRONT_MATERIAL_T *mat = model->material + i; if (mat->vbo[VBO_VERTEX]) destroy_vbo(mat->vbo+VBO_VERTEX); if (mat->vbo[VBO_TEXTURE]) destroy_vbo(mat->vbo+VBO_TEXTURE); if (mat->vbo[VBO_NORMAL]) destroy_vbo(mat->vbo+VBO_NORMAL); } } // create a cube model that looks like a wavefront model, MODEL_T cube_wavefront(void) { static const float qv[] = { -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, }; static const float qn[] = { 0.0f, -1.0f, -0.0f, 0.0f, 1.0f, -0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, -0.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, -0.0f, }; static const float qt[] = { 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, }; static const unsigned short qf[] = { 1,1,1, 2,2,1, 3,3,1, 3,3,1, 4,4,1, 1,1,1, 5,4,2, 6,1,2, 7,2,2, 7,2,2, 8,3,2, 5,4,2, 1,4,3, 4,1,3, 6,2,3, 6,2,3, 5,3,3, 1,4,3, 4,4,4, 3,1,4, 7,2,4, 7,2,4, 6,3,4, 4,4,4, 3,4,5, 2,1,5, 8,2,5, 8,2,5, 7,3,5, 3,4,5, 2,4,6, 1,1,6, 5,2,6, 5,2,6, 8,3,6, 2,4,6, }; WAVEFRONT_MODEL_T *model = malloc(sizeof *model); if (model) { WAVEFRONT_MATERIAL_T *mat = model->material; float *temp; const int offset = 0; memset(model, 0, sizeof *model); temp = allocbuffer(3*MAX_VERTICES*sizeof *temp); mat->numverts = countof(qf)/3; // vertex, texture, normal deindex(temp, qv, qf+3*offset+0, 3, mat->numverts); create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_VERTEX, 3 * mat->numverts * sizeof *qv, temp); // 3 deindex(temp, qt, qf+3*offset+1, 2, mat->numverts); create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_TEXTURE, 2 * mat->numverts * sizeof *qt, temp); // 2 deindex(temp, qn, qf+3*offset+2, 3, mat->numverts); create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_NORMAL, 3 * mat->numverts * sizeof *qn, temp); // 3 freebuffer(temp); model->num_materials = 1; } return (MODEL_T)model; } userland/host_applications/linux/apps/hello_pi/hello_teapot/models.h000066400000000000000000000034071421703157200264340ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #ifndef MODELS_T #define MODELS_T typedef struct opqaue_model_s * MODEL_T; MODEL_T load_wavefront(const char *modelname, const char *texturename); MODEL_T cube_wavefront(void); void unload_wavefront(MODEL_T m); int draw_wavefront(MODEL_T m, GLuint texture); #endif userland/host_applications/linux/apps/hello_pi/hello_teapot/teapot.obj.dat000066400000000000000000023302101421703157200275340ustar00rootroot00000000000000þ%ô“'áÇ™>DÁ>‰L²%½—>DÁ>ËËO=+–>ÍÄ>ò´M=¨/˜>ÍÄ>˜ R²•>•%> ãL=Á—>•%>.V²/Á•>gt)>U(M=ŽÄ—>gt)>6ÌY²+¥–>? ,>kWN=p«˜>? ,>ׯ[²j˜>->‹BP=Œ!š>->Øo\²âò™>? ,>#¼R=Dœ>? ,>ׯ[²Lœ>gt)>ž–U=¾0ž>gt)>6ÌY²'dž>•%>l¤X=[„ >•%>.V²i´ >ÍÄ>ß·[=AÜ¢>ÍÄ>˜ R²æ¢>DÁ>~£^=Ñ¥>DÁ>‰L²Ó‘>DÁ>hÊ=#P>ÍÄ>IýÇ=9¸>•%>1Ç=yê>gt)>¨tÇ=©Å>? ,>8›È=)’>->»xÊ=kó“>? ,>¸àÌ=þ–>gt)>3§Ï=ž9˜>•%>;ŸÒ=hsš>ÍÄ>ÑœÕ=^œ>DÁ>‚sØ=ËVˆ>DÁ>¦“>Cì†>ÍÄ>i>õ]†>•%>3†>÷Œ†>gt)>L·>`Z‡>? ,> >D§ˆ>->{ç>×TŠ>? ,>l¦>óCŒ>gt)>­©>VŽ>•%>Ñ>Ïk>ÍÄ>œü>f’>DÁ>è >,w>DÁ>+ <>Š˜t>ÍÄ>,&:>ß•s>•%>Gh9>Sës>gt)> §9>Û`u>? ,>8¹:>m¾w>->u<>ÄËz>? ,>ÿ²>>˜P~>gt)>ëGA>5 >•%>b D>µï‚>ÍÄ>+ÔF>©¼„>DÁ>›xI>D½W>DÁ>¡Ê`>ËzU>ÍÄ>(ˆ^>ž—T>•%>ü¤]>¥âT>gt)>ð]>Ž*V>? ,>²7_>Ë=X>->)Ka>ëZ>? ,>døc>e^>gt)>Ãg>Oa>•%>z\j>£d>ÍÄ>q°m>½Ëg>DÁ>Ùp>Íü2>DÁ>¸€>Ï1>ÍÄ>è¥}>éZ0>•%><£|>¬™0>gt)>°ø|>Û«1>? ,>8n~>@h3>->åe€>¢¥5>? ,>ì>Ž:8>gt)>ú®ƒ>þ:>•%>ã…>ÎÆ=>ÍÄ>dv‡>>k@>DÁ>WC‰>I† >DÁ>zÝŒ> >ÍÄ>ñr‹>Öx>•%>¤äŠ>î©>gt)>¦‹>­>? ,>á‹>Ú >->ó->™ >? ,>…ÛŽ>Pœ >gt)>ÀÊ>²Ã>•%>µÜ’>?ï>ÍÄ>~ò”>‹þ>DÁ>Lí–>­ê·=DÁ>ÈY–>Žâµ=ÍÄ>ÑÖ”>ŵ=•%>è>”>îYµ=gt)>(q”>}€¶=? ,>XL•>^¸=->²¯–>þź=? ,>z˜>xŒ½=gt)>¬Šš>„À=•%>LÀœ>‚Ã=ÍÄ>úž>ÇXÆ=DÁ> ¡>>–+=DÁ>ÓCœ>|)=ÍÄ>²±š>–­(=•%>š>ßò(=gt)>ÝGš>õ!*=? ,>Ú+›>þ ,=->œ>•†.=? ,>yž>)a1=gt)>ûž >Þn4=•%>Öê¢>i‚7=ÍÄ>;¥>ðm:=DÁ>>m§> Ö¼DÁ>ÌMž> Ö¼ÍÄ>W¶œ> Ö¼•%>pœ> Ö¼gt)> Ö¼? ,>2> Ö¼->;¨ž> Ö¼? ,>óŠ >Ö¼gt)>m·¢>Ö¼•%> ¥>Ö¼ÍÄ>ïb§>Ö¼DÁ>œ©>æäƒ½DÁ>ÓCœ>8+€½ÍÄ>²±š>“9{½•%>š>õ&x½gt)>ÝGš>ÑÒv½? ,>Ú+›>ñv½->œ>°5x½? ,>yž>ÆTz½gt)>ûž >V}½•%>Öê¢>gò½ÍÄ>;¥>wl½DÁ>>m§>ÂÁë½DÁ>ÈY–>f}å½ÍÄ>ÑÖ”>¼Lὕ%>è>”>(ìÞ½gt)>(q”>¥Þ½? ,>XL•>§‡Þ½->²¯–>ûß½? ,>z˜>Ô-â½gt)>¬Šš>èÙ何%>LÀœ>>»ç½ÍÄ>úž>:Žê½DÁ> ¡>ïš$¾DÁ>zÝŒ>² ¾ÍÄ>ñr‹>3,¾•%>¤äŠ>¶Ø¾gt)>¦‹>«Š¾? ,>á‹>þ¾->ó->ÔF¾? ,>…ÛŽ>õ¾gt)>¢Ê>òð!¾•%>µÜ’>I $¾ÍÄ>~ò”>D&¾DÁ>Lí–>qâM¾DÁ>¸€>zœI¾ÍÄ>è¥}>öõF¾•%><£|>x¸E¾gt)>°ø|>©¬E¾? ,>8n~>WœF¾->åe€>fPH¾? ,>ì>.’J¾gt)>ú®ƒ>Í*M¾•%>ã…>žãO¾ÍÄ>dv‡>÷…R¾DÁ>WC‰>zq¾DÁ>¡Ê`>ð%m¾ÍÄ>(ˆ^>Àšj¾•%>ü¤]>›i¾gt)>ð]>7ëi¾? ,>²7_>™Lk¾->)Ka>ƒm¾? ,>døc>ëPp¾gt)>Ãg>Yys¾•%>z\j>¨¿v¾ÍÄ>q°m>wæy¾DÁ>Ùp>´‘‡¾DÁ>+ <>|…¾ÍÄ>,&:>^X„¾•%>Gh9>N„¾gt)> §9>ä_„¾? ,>8¹:>]J…¾->u<>K£†¾? ,>ÿ²>>ìIˆ¾gt)>ëGA>—о•%>b D>áý‹¾ÍÄ>+ÔF>ʾDÁ>›xI>šP“¾DÁ>¦“>‘`‘¾ÍÄ>i>cg¾•%>3†>&C¾gt)>L·>Ò¾? ,> >:ò‘¾->{ç>²“¾? ,>l¦> ^•¾gt)>­©>Sg—¾•%>Ñ>¢y™¾ÍÄ>œü>ús›¾DÁ>è >Ú›¾DÁ>hÊ=Ýš¾ÍÄ>IýÇ=¥E™¾•%>1Ç=…M™¾gt)>¨tÇ= š¾? ,>8›È=£U›¾->»xÊ=à¾? ,>¸àÌ=Ÿ¾gt)>3§Ï=I¡¾•%>;ŸÒ=£¾ÍÄ>ÑœÕ=»¥¾DÁ>‚sØ=Ê¡¾DÁ>³ËO=kŸ¾ÍÄ>ò´M=s¾ž¾•%>ôâL=¾æž¾gt)>U(M=³ÁŸ¾? ,>SWN=”,¡¾->sBP=£¾? ,> ¼R=ƒ'¥¾gt)>ˆ–U=r§¾•%>T¤X=ÅÁ©¾ÍÄ>ß·[=ìó«¾DÁ>f£^=zÔ¢¾DÁ>“|ž³=¡¾ÍÄ>Íá³ ¾•%>üí³êÑ ¾gt)>Ô„ž³Ì¸¡¾? ,>m„Ÿ³é.£¾->&Ö ³¡¥¾? ,>vR¢³>§¾gt)>Q裳¸‘©¾•%>ßu¥³é«¾ÍÄ>Ù¦³-#®¾DÁ>~û§³Ê ¾DÁ>ÅËO½`8Ÿ¾ÍÄ>µM½pšž¾•%>ãL½‹Îž¾gt)>g(M½ˆ²Ÿ¾? ,>eWN½Ç#¡¾->…BP½>£¾? ,>¼R½¨%¥¾gt)>˜–U½„q§¾•%>f¤X½ÅÁ©¾ÍÄ>ñ·[½ìó«¾DÁ>x£^½vàš¾DÁ>dʽ]™¾ÍÄ>Fýǽ–Ř¾•%>|1ǽÖ÷˜¾gt)>¥tǽÓ™¾? ,>5›È½`6›¾->¸xʽȾ? ,>µà̽ZŸ¾gt)>0§Ï½úF¡¾•%>8ŸÒ½Å€£¾ÍÄ>Μս»¥¾DÁ>sؽ(d‘¾DÁ>¤“¾Ÿù¾ÍÄ>g¾Qk¾•%>m†¾Tš¾gt)>J·¾½g¾? ,> ¾¡´‘¾->yç¾3b“¾? ,>j¦¾PQ•¾gt)>«©¾cc—¾•%>Ѿ+y™¾ÍÄ>›ü¾ús›¾DÁ>ç ¾e£„¾DÁ>) <¾¢Yƒ¾ÍÄ>+&:¾LØ‚¾•%>Eh9¾ƒ¾gt)>§9¾Ê½ƒ¾? ,>6¹:¾“섾->œu<¾>s†¾? ,>þ²>¾¨5ˆ¾gt)>éGA¾‘о•%>` D¾ý‹¾ÍÄ>*ÔF¾ʾDÁ>™xI¾ý×i¾DÁ> Ê`¾„•g¾ÍÄ>'ˆ^¾W²f¾•%>ú¤]¾^ýf¾gt)>ð]¾GEh¾? ,>ê7_¾…Xj¾->'Ka¾Àm¾? ,>žøc¾p¾gt)>Ág¾Öis¾•%>x\j¾Í½v¾ÍÄ>p°m¾wæy¾DÁ>Ùp¾†E¾DÁ>·€¾ˆ3C¾ÍÄ>æ¥}¾£uB¾•%>;£|¾e´B¾gt)>¯ø|¾”ÆC¾? ,>7n~¾ù‚E¾->äe€¾[ÀG¾? ,>ì¾GUJ¾gt)>ú®ƒ¾¾M¾•%>ã…¾‡áO¾ÍÄ>cv‡¾÷…R¾DÁ>VC‰¾¡¾DÁ>yÝŒ¾Å'¾ÍÄ>ðr‹¾“¾•%>£äо¨Ä¾gt)>¥‹¾fš¾? ,>ዾ×ô¾->ò-¾È³¾? ,>…ÛŽ¾ ·¾gt)>¿Ê¾kÞ!¾•%>µÜ’¾ø $¾ÍÄ>}ò”¾D&¾DÁ>Kí–¾ ܽDÁ>ÈY–¾Ú½ÍÄ>ÑÖ”¾7LÙ½•%>ç>”¾`Ù½gt)>'q”¾ðµÚ½? ,>WL•¾s“ܽ->±¯–¾pûÞ½? ,>z˜¾ëÁá½gt)>¬Šš¾ó¹ä½•%>LÀœ¾‰·ç½ÍÄ>úž¾:Žê½DÁ> ¡¾#t½DÁ>ÓCœ¾aêq½ÍÄ>±±š¾{q½•%>Áš¾Ä]q½gt)>úGš¾ÚŒr½? ,>Ù+›¾ãwt½->œ¾zñv½? ,>yž¾Ìy½gt)>úž ¾ÃÙ|½•%>ÕꢾNí½ÍÄ>;¥¾kl½DÁ>=m§¾‡Õ¼DÁ>ËMž¾ˆÕ¼ÍÄ>V¶œ¾ˆÕ¼•%>oœ¾ˆÕ¼gt)>;Kœ¾ˆÕ¼? ,>2¾‡Õ¼->:¨ž¾‡Õ¼? ,>òŠ ¾†Õ¼gt)>l·¢¾…Õ¼•%> ¥¾„Õ¼ÍÄ>ïb§¾ƒÕ¼DÁ>~œ©¾V–+=DÁ>ÓCœ¾|)=ÍÄ>±±š¾–­(=•%>Áš¾÷ò(=gt)>úGš¾õ!*=? ,>Ù+›¾ ,=->œ¾­†.=? ,>yž¾)a1=gt)>úž ¾ön4=•%>Õꢾi‚7=ÍÄ>;¥¾n:=DÁ>=m§¾­ê·=DÁ>ÈY–¾Žâµ=ÍÄ>ÑÖ”¾Åµ=•%>ç>”¾îYµ=gt)>'q”¾}€¶=? ,>WL•¾^¸=->±¯–¾þź=? ,>z˜¾xŒ½=gt)>¬Šš¾„À=•%>LÀœ¾‚Ã=ÍÄ>úž¾ÇXÆ=DÁ> ¡¾I† >DÁ>yÝŒ¾ >ÍÄ>ðr‹¾y>•%>£äоî©>gt)>¥‹¾­>? ,>ዾÚ >->ò-¾™ >? ,>…ÛŽ¾Pœ >gt)>¿Ê¾²Ã>•%>µÜ’¾?ï>ÍÄ>}ò”¾‹þ>DÁ>Kí–¾Íü2>DÁ>·€¾Ï1>ÍÄ>æ¥}¾éZ0>•%>;£|¾¬™0>gt)>¯ø|¾Û«1>? ,>7n~¾@h3>->äe€¾¢¥5>? ,>쾎:8>gt)>ú®ƒ¾þ:>•%>ã…¾ÎÆ=>ÍÄ>cv‡¾>k@>DÁ>VC‰¾D½W>DÁ> Ê`¾ËzU>ÍÄ>'ˆ^¾ž—T>•%>ú¤]¾¥âT>gt)>ð]¾Ž*V>? ,>ê7_¾Ë=X>->'Ka¾ëZ>? ,>žøc¾e^>gt)>Ág¾Oa>•%>x\j¾£d>ÍÄ>p°m¾½Ëg>DÁ>Ùp¾,w>DÁ>) <¾Š˜t>ÍÄ>+&:¾ß•s>•%>Eh9¾Sës>gt)>§9¾Û`u>? ,>6¹:¾m¾w>->œu<¾ÄËz>? ,>þ²>¾˜P~>gt)>éGA¾5 >•%>` D¾µï‚>ÍÄ>*ÔF¾©¼„>DÁ>™xI¾ËVˆ>DÁ>¤“¾Cì†>ÍÄ>g¾õ]†>•%>m†¾÷Œ†>gt)>J·¾`Z‡>? ,> ¾D§ˆ>->yç¾×TŠ>? ,>j¦¾DŒ>gt)>«©¾VŽ>•%>ѾÏk>ÍÄ>›ü¾f’>DÁ>ç ¾Ó‘>DÁ>dʽ#P>ÍÄ>Fýǽ9¸>•%>|1ǽyê>gt)>¥tǽ©Å>? ,>5›È½)’>->¸xʽkó“>? ,>µà̽þ–>gt)>0§Ï½ž9˜>•%>8ŸÒ½hsš>ÍÄ>Μս^œ>DÁ>sؽ%½—>DÁ>ÅËO½+–>ÍÄ>ì´M½•>•%>ãL½/Á•>gt)>O(M½+¥–>? ,>eWN½j˜>->…BP½âò™>? ,>¼R½Lœ>gt)>˜–U½'dž>•%>f¤X½i´ >ÍÄ>Ù·[½æ¢>DÁ>x£^½€>«>ºNæ=¹»i=Œ‰­>ºNæ=\2²{k³>ÌNŸ=ýšt=øÑµ>ÌNŸ=¹ä²çB»>!n1=R=ŽÃ½>!n1=wü±®™Â>öÞö޻ɼwÔˆ=fôË>»É¼Î–±ðÏ>¶*m½$µŒ=ÓÜÑ>¶*m½übH±¾íÓ>3ƒ¹½Ñê=ºÀÖ>3ƒ¹½ØÝ˰‹•×>û½Y’=ÌtÚ>û½Î¯É®~æÙ>r¾Aã“=dÍÜ>r¾ûܯ0£µÚ>ºw<¾þl”=PŸÝ>ºw<¾¢¢21}—¤>ºNæ=Ñ<ã=Fu¬>ÌNŸ=Îí=Ÿ´>!n1= ò÷=M»>öÞüzÁ>»É¼ß>tÇ>¶*m½Ì>˜¼Ë>3ƒ¹½ë >ö@Ï>û½åG>r{Ñ>r¾'Ç>·BÒ>ºw<¾M> ì™>ºNæ=ß$>ÑJ¡>ÌNŸ=Ί,>Z\¨>!n1=…å3>ûù®>öÞ<ÐÇ:> ý´>»É¼d A>Ø>º>¶*m½¼F>ô˜¾>3ƒ¹½ÿK>xäÁ>û½­vN>òúÃ>r¾ë¢P>¶µÄ>ºw<¾6eQ>¸“‹>ºNæ=Þ‚S>²G’>ÌNŸ=|Y]>eµ˜>!n1=Éf>¸¹ž>öÞ<³o>{1¤>»É¼?¤w>zù¨>¶*m½Ï¨~>Ÿî¬>3ƒ¹½ÿ;‚>Õí¯>û½o„>éÓ±>r¾ÐÓ…>Æ}²>ºw<¾gP†>KÌs>ºNæ=©Ù|>>>ÌNŸ=NN„>bk…>!n1=ò‰>¾²Š>öÞË~>»É¼y”>œ°“>¶*m½K7˜>ƒ)—>3ƒ¹½2°›>–Ê™>û½EQž> u›>r¾¸ûŸ> œ>ºw<¾½ >uJ>ºNæ=g>LT>ÌNŸ=aΖ>±»]>!n1=<>Vf>öÞá–n>»É¼ ¸¨>r›u>¶*m½(€­> j{>3ƒ¹½Nu±>ÇÐ>û½ƒt´>"M>r¾˜Z¶>¸É>ºw<¾t·>$Ò>ºNæ=¼rž>q}#>ÌNŸ=€Ñ¥>'Ø*>!n1=ã¬>rº1>öÞ<ª€³>ü7>»É¼¸ƒ¹>^t=>¶*m½†Å¾>¢ûA>3ƒ¹½£Ã>OiE>û½&kÆ>Ž•G>r¾¡È>ÙWH>ºw<¾e<É>"Ñ=ºNæ=,©>Ò³Û=ÌNŸ=õû°>S×å=!n1=M‡¸>Tï=öÞ<û–¿>ó÷=»É¼«Æ>n}ÿ=¶*m½"žË>§Ý>3ƒ¹½FCÐ>‡:>û½¥ÇÓ>ɹ>r¾!Ö>£?>ºw<¾fÉÖ>D†E=ºNæ=.ů>ˆeP=ÌNŸ=*ò·>ÜÒZ=!n1=–É¿>•d=öÞ<\ Ç>ysm=»É¼¨ËÍ>º4u=¶*m½Ÿ Ó>- {=3ƒ¹½mtØ>I>€=û½:Ü>zÈ=r¾-mÞ>7R‚=ºw<¾R<ß>Ö¼ºNæ=:²>Ö¼ÌNŸ=§Xº>Ö¼!n1==JÂ>Ö¼öÞ"Ö¼»É¼{Ð>$Ö¼¶*m½cÖ>&Ö¼3ƒ¹½iGÛ>(Ö¼û½zûÞ>)Ö¼r¾Tá>)Ö¼ºw<¾ÿ%â>”ø†½ºNæ=.ů>BhŒ½ÌNŸ=*ò·>áž‘½!n1=–É¿> €–½öÞ<\ Ç>/ïš½»É¼¨ËÍ>ÛÏž½¶*m½Ÿ Ó>‰¢½3ƒ¹½mtØ>¼s¤½û½:Ü>øý¥½r¾-mÞ>¶‡¦½ºw<¾R<ß>ˆWõ½ºNæ=,©>Eéÿ½ÌNŸ=õû°>c¾!n1=M‡¸>ÅÄ ¾öÞ<û–¿>v¾»É¼«Æ>pÙ¾¶*m½"žË>`ø¾3ƒ¹½FCÐ>AU¾û½¥ÇÓ>ƒÔ¾r¾!Ö>\Z¾ºw<¾fÉÖ>Ýì-¾ºNæ=¼rž>*˜5¾ÌNŸ=€Ñ¥>áò<¾!n1=ã¬>,ÕC¾öÞ<ª€³>ÀJ¾»É¼¸ƒ¹>O¾¶*m½†Å¾>[T¾3ƒ¹½£Ã> „W¾û½&kÆ>G°Y¾r¾¡È>’rZ¾ºw<¾e<É>:\¾ºNæ=g>Øff¾ÌNŸ=aΖ>jÖo¾!n1=<>«x¾öÞÍX€¾»É¼ ¸¨>Ûƒ¾¶*m½(€­>­Â†¾3ƒ¹½Nu±>Àõˆ¾û½ƒt´>~Zоr¾˜Z¶>׊¾ºw<¾t·>‚󂾺Næ=©Ù|>üÔˆ¾ÌNŸ=NN„>¿x޾!n1=ò‰>À“¾öÞ'Œ˜¾»É¼y”>ù½œ¾¶*m½K7˜>à6 ¾3ƒ¹½2°›>ó×¢¾û½EQž>f‚¤¾r¾¸ûŸ>k¥¾ºw<¾½ >¡”¾ºNæ=Þ‚S>U›¾ÌNŸ=|Y]>Á¡¾!n1=Éf>ǧ¾öÞ<³o>×>­¾»É¼?¤w>Ö²¾¶*m½Ï¨~>üûµ¾3ƒ¹½ÿ;‚>1û¸¾û½o„>FẾr¾ÐÓ…>"‹»¾ºw<¾gP†>jù¢¾ºNæ=ß$>.Xª¾ÌNŸ=“Š,>¶i±¾!n1=…å3>X¸¾öÞ<ÐÇ:>f ¾¾»É¼d A>4Lþ¶*m½¼F>Q¦Ç¾3ƒ¹½ÿK>Ôñʾû½­vN>O;r¾ë¢P>Ã;ºw<¾6eQ>Ú¤­¾ºNæ=Ñ<ã=£‚µ¾ÌNŸ=Îí=û ½¾!n1= ò÷=©ľöÞYˆÊ¾»É¼ß>Ð$о¶*m½Ì>ôÉÔ¾3ƒ¹½ë >SNؾû½åG>ψھr¾'Ç>PÛ¾ºw<¾M>ÜK´¾ºNæ=¹»i=Øx¼¾ÌNŸ=ýšt=CPľ!n1=:= §Ë¾öÞ­¾»É¼=¤w¾Ö²¾¶*m½Î¨~¾üûµ¾3ƒ¹½þ;‚¾1û¸¾û½o„¾FẾr¾ÏÓ…¾"‹»¾ºw<¾fP†¾‚󂾺Næ=§Ù|¾üÔˆ¾ÌNŸ=MN„¾¿x޾!n1=ò‰¾À“¾öÞ€=û½9ܾ†È=r¾,mÞ¾CR‚=ºw<¾Q<ß¾"Ñ=ºNæ=+©¾Ò³Û=ÌNŸ=ôû°¾S×å=!n1=M‡¸¾Tï=öÞ<û–¿¾zó÷=»É¼ªƾn}ÿ=¶*m½"žË¾§Ý>3ƒ¹½ECо‡:>û½¤ÇӾɹ>r¾ Ö¾£?>ºw<¾eÉÖ¾$Ò>ºNæ=»rž¾q}#>ÌNŸ=Ñ¥¾'Ø*>!n1=㬾rº1>öÞ<©€³¾ü7>»É¼¸ƒ¹¾^t=>¶*m½†Å¾¾¢ûA>3ƒ¹½¢þOiE>û½%kÆ¾Ž•G>r¾ È¾ÙWH>ºw<¾d<ɾuJ>ºNæ=f¾LT>ÌNŸ=`Ζ¾±»]>!n1=<¾Vf>ö޻ɼ)¸¨¾r›u>¶*m½(€­¾ j{>3ƒ¹½Mu±¾ÇÐ>û½ƒt´¾"M>r¾—Z¶¾¸É>ºw<¾s·¾KÌs>ºNæ=§Ù|¾>>ÌNŸ=MN„¾bk…>!n1=ò‰¾¾²Š>ö޻ɼx”¾œ°“>¶*m½J7˜¾ƒ)—>3ƒ¹½1°›¾–Ê™>û½DQž¾ u›>r¾·ûŸ¾ œ>ºw<¾¼ ¾¸“‹>ºNæ=Ý‚S¾²G’>ÌNŸ={Y]¾eµ˜>!n1= Éf¾¸¹ž>öÞ<²o¾{1¤>»É¼=¤w¾zù¨>¶*m½Î¨~¾Ÿî¬>3ƒ¹½þ;‚¾Õí¯>û½o„¾éÓ±>r¾ÏÓ…¾Æ}²>ºw<¾fP†¾ ì™>ºNæ=€ß$¾ÑJ¡>ÌNŸ=ÍŠ,¾Z\¨>!n1=ƒå3¾ûù®>öÞ<ÎÇ:¾ ý´>»É¼b A¾Ø>º>¶*m½ºF¾ô˜¾>3ƒ¹½þK¾xäÁ>û½«vN¾òúÃ>r¾ê¢P¾¶µÄ>ºw<¾4eQ¾}—¤>ºNæ=Î<ã½Fu¬>ÌNŸ=ŠÎí½Ÿ´>!n1= ò÷½M»>ö޻ɼݾtÇ>¶*m½Ì¾˜¼Ë>3ƒ¹½ë ¾ö@Ï>û½ãG¾r{Ñ>r¾%Ǿ·BÒ>ºw<¾ÿL¾€>«>ºNæ=³»i½{k³>ÌNŸ=÷št½çB»>!n1=L½®™Â>ö޻ɼtÔˆ½ðÏ>¶*m½!µŒ½¾íÓ>3ƒ¹½Î꽋•×>û½Y’½~æÙ>r¾1㓽£µÚ>ºw<¾ïl”½˜%Ù>ê|Y¾c“= Ü>ê|Y¾úøƒ1»çÔ>´Žs¾‘=¾×>´Žs¾ò ª1¨§Î>ƒc…¾#iŒ= iÑ>ƒc…¾ÌAÌ1žÇ>韾D]‡=¤¸É>韾=ê1ξ>§‰˜¾_ß=ZÁ>ª‰˜¾z,2•‹¶>À- ¾ñÂx=}ü¸>À- ¾ìw 2Šô®>9™¦¾4«n=L±>9™¦¾“Ó2w´¨>Ù«¾L[f=÷ª>Ù«¾•l2›v¤>`ú¯¾x·`=«¦>`ú¯¾[$2æ¢> ³¾~£^=Ñ¥> ³¾K )2×ÁÐ>ê|Y¾UJ>­Ì>´Žs¾mŒ >t©Æ>ƒc…¾5‚>\¿>韾$š>‹i·>§‰˜¾Á†ü= w¯>À- ¾;Ùñ=£)¨>6™¦¾ è=&¢>Ù«¾©ôß=@ž>`ú¯¾ØxÚ=^œ> ³¾‚sØ=%MÃ>ê|Y¾îO>Fz¿>´Žs¾–óK>Ê×¹>ƒc…¾ƒF>D³>韾ø>>)Ž«>§‰˜¾87>¤>À- ¾y/>‰D>6™¦¾›Z(> ¢—>Ù«¾ˆ}">-Ï“>^ú¯¾ƒ>f’> ³¾è >¿5±>ê|Y¾®_…>‘»­>´Žs¾gÒ‚>°›¨>ƒc…¾A~>Ëb¢>韾Cýt>7›>§‰˜¾µ k>£×”>À- ¾%a>¾žŽ>6™¦¾'úW>Ý~‰>Ù«¾štP>‘†>^ú¯¾ ZK>©¼„> ³¾›xI>Sêš>ê|Y¾qŸ>UÝ—>´Žs¾dœ>f^“>ƒc…¾å—>×è>韾†o’>÷÷‡>§‰˜¾¥~Œ>‚>À- ¾Å†> #y>6™¦¾5>1%p>Ù«¾Ž2y>6 j>^ú¯¾“s>½Ëg> ³¾Ùp>ÿØ€>ê|Y¾‹¼µ>q—|>´Žs¾?B²>äu>ƒc…¾^"­>æïk>韾yé¦>Xÿa>§‰˜¾æ# >ÈX>À- ¾R^™>ÊìN>6™¦¾l%“>=gG>Ù«¾ŒŽ>¯LB>^ú¯¾@‹Š>>k@> ³¾WC‰>±àF>ê|Y¾ÔÓÇ>9æB>´Žs¾õÄ>% =>ƒc…¾y^¾>¤ê5>韾ó†·>2+.>§‰˜¾Ø°>¿k&>À- ¾¾¢¨>>M>6™¦¾7Ë¡>+p>Ù«¾»(œ>²u>^ú¯¾ÜU˜>‹þ> ³¾Lí–>ø<>ê|Y¾†HÕ>>´Žs¾Ä3Ñ>°éþ=ƒc…¾#0Ë>õ=韾½âÃ>lê=§‰˜¾:ð»>€¾ß=À- ¾·ý³>^îÕ=6™¦¾Q°¬>wÙÍ=Ù«¾°¬¦>^È=^ú¯¾î—¢>ÇXÆ= ³¾ ¡>;H=ê|Y¾F¬Ý> ì|=´Žs¾jnÙ>Ñœt=ƒc…¾V.Ó>û„j=韾M—Ë>0‰_=§‰˜¾ÇTÃ>|T=À- ¾C»>¦uJ=6™¦¾9{³>×%B=Ù«¾&;­>ë<=^ú¯¾Jý¨>ðm:= ³¾>m§>(Ö¼ê|Y¾½à>&Ö¼´Žs¾ÀDÜ>$Ö¼ƒc…¾¸ïÕ>!ּ韾S?Î>Ö¼§‰˜¾?áÅ>Ö¼À- ¾+ƒ½>Ö¼6™¦¾ÆÒµ>Ö¼Ù«¾¾}¯>Ö¼^ú¯¾Â1«>Ö¼ ³¾œ©>¸}¥½ê|Y¾F¬Ý>Ï«¢½´Žs¾jnÙ>Ûƒž½ƒc…¾V.Ó>üw™½éŸ¾M—Ë>ú“½§‰˜¾ÇTÃ>0|޽À- ¾C»>Rp‰½6™¦¾9{³>^H…½Ù«¾&;­>tv‚½^ú¯¾Jý¨>wl½ ³¾>m§>±W¾ê|Y¾†HÕ>É™¾´Žs¾Ä3Ñ>‘¾ƒc…¾#0Ë>€§ ¾éŸ¾½âÃ>½P¾§‰˜¾:ð»>ùù¾À- ¾·ý³>Ð#ú½6™¦¾Q°¬>aò½Ù«¾°¬¦>“ì½^ú¯¾î—¢>:Žê½ ³¾ ¡>jûX¾ê|Y¾ÔÓÇ>òU¾´Žs¾õÄ>ß#O¾ƒc…¾y^¾>^H¾éŸ¾ó†·>ëE@¾§‰˜¾Ø°>x†8¾À- ¾¾¢¨>÷g1¾6™¦¾7Ë¡>äŠ+¾Ù«¾»(œ>l'¾^ú¯¾ÜU˜>D&¾ ³¾Lí–>\找ê|Y¾n¼µ>Y‡¾´Žs¾?B²>O–ƒ¾ƒc…¾^"­>Ÿ ~¾éŸ¾yé¦>t¾§‰˜¾æ# >)j¾À- ¾R^™>ƒa¾6™¦¾l%“>öY¾Ù«¾ŒŽ>hgT¾^ú¯¾@‹Š>÷…R¾ ³¾WC‰>¯÷£¾ê|Y¾qŸ>²ê ¾´Žs¾dœ>Ãkœ¾ƒc…¾å—>3ö–¾éŸ¾†o’>S‘¾§‰˜¾¥~Œ>s‹¾À- ¾Å†>ãž…¾6™¦¾5>õ¾Ù«¾Ž2y>ï%|¾^ú¯¾“s>wæy¾ ³¾Ùp>9Cº¾ê|Y¾®_…>íȶ¾´Žs¾gÒ‚> ©±¾ƒc…¾A~>'p«¾éŸ¾Cýt>”ª¤¾§‰˜¾µ k>å¾À- ¾%a>¬—¾6™¦¾'úW>:Œ’¾Ù«¾štP>î¾^ú¯¾ ZK>ʾ ³¾›xI>‚Z̾ê|Y¾îO>£‡È¾´Žs¾–óK>'徃c…¾ƒF>  ¼¾éŸ¾ø>>†›´¾§‰˜¾87>l)­¾À- ¾y/>åQ¦¾6™¦¾›Z(>i¯ ¾Ù«¾ˆ}">ŠÜœ¾^ú¯¾ƒ>ús›¾ ³¾è >3ÏÙ¾ê|Y¾UJ>rºÕ¾´Žs¾mŒ >ѶϾƒc…¾5‚>kiȾ韾$š>èvÀ¾§‰˜¾Á†ü=e„¸¾À- ¾;Ùñ=ÿ6±¾6™¦¾ è=^3«¾Ù«¾2ôß=œ§¾`ú¯¾ØxÚ=»¥¾ ³¾‚sØ=ô2â¾ê|Y¾õb“=õݾ´Žs¾ ‘=µ×¾ƒc…¾#iŒ=ûо韾8]‡=uÛǾ§‰˜¾Sß=ñ˜¿¾À- ¾ñÂx=績9™¦¾«n=ÔÁ±¾Ù«¾L[f=øƒ­¾`ú¯¾`·`=ìó«¾ ³¾f£^=kå¾ê|Y¾f´³nËྴŽs¾öe®³fvÚ¾ƒc…¾ç靈ÆÒ¾éŸ¾Œž³ígʾª‰˜¾Hð•³Ù ¾À- ¾Ÿ†³tYº¾9™¦¾â…³l´¾Ù«¾Ž.³o¸¯¾`ú¯¾—zv³-#®¾ ³¾X­r³ô2â¾ê|Y¾þb“½õݾ´Žs¾‘½µ×¾ƒc…¾ iŒ½ûо韾A]‡½uÛǾª‰˜¾\ß½ñ˜¿¾À- ¾ëÂx½ç¸¾9™¦¾.«n½ÔÁ±¾Ù«¾F[f½øƒ­¾`ú¯¾r·`½ìó«¾ ³¾x£^½3ÏÙ¾ê|Y¾TJ¾rºÕ¾´Žs¾§Œ ¾Ñ¶Ï¾ƒc…¾4‚¾kiȾ韾"š¾èvÀ¾ª‰˜¾¾†ü½e„¸¾À- ¾8Ùñ½ÿ6±¾9™¦¾ è½^3«¾Ù«¾¦ôß½œ§¾`ú¯¾ÕxÚ½»¥¾ ³¾sؽ‚Z̾ê|Y¾ îO¾£‡È¾´Žs¾•óK¾'徃c…¾F¾  ¼¾éŸ¾ø>¾†›´¾ª‰˜¾Ž87¾l)­¾À- ¾y/¾åQ¦¾9™¦¾šZ(¾i¯ ¾Ù«¾‡}"¾ŠÜœ¾`ú¯¾ƒ¾ús›¾ ³¾ç ¾Cº¾ê|Y¾­_…¾íȶ¾´Žs¾gÒ‚¾ ©±¾ƒc…¾@~¾'p«¾éŸ¾Být¾”ª¤¾ª‰˜¾´ k¾å¾À- ¾$a¾¬—¾9™¦¾&úW¾:Œ’¾Ù«¾™tP¾î¾`ú¯¾ ZK¾ʾ ³¾™xI¾¯÷£¾ê|Y¾qŸ¾²ê ¾´Žs¾dœ¾Ãkœ¾ƒc…¾å—¾3ö–¾éŸ¾…o’¾S‘¾ª‰˜¾¥~Œ¾s‹¾À- ¾Ä†¾ãž…¾9™¦¾4¾õ¾Ù«¾2y¾ï%|¾`ú¯¾‘s¾wæy¾ ³¾Ùp¾\找ê|Y¾‹¼µ¾Y‡¾´Žs¾?B²¾O–ƒ¾ƒc…¾^"­¾Ÿ ~¾éŸ¾y馾t¾ª‰˜¾å# ¾)j¾À- ¾Q^™¾ƒa¾9™¦¾l%“¾öY¾Ù«¾‹޾hgT¾`ú¯¾?‹Š¾÷…R¾ ³¾VC‰¾jûX¾ê|Y¾ÓÓǾòU¾´Žs¾ôľß#O¾ƒc…¾x^¾¾^H¾éŸ¾ò†·¾ëE@¾ª‰˜¾×°¾x†8¾À- ¾½¢¨¾÷g1¾9™¦¾7Ë¡¾äŠ+¾Ù«¾º(œ¾l'¾`ú¯¾ÛU˜¾D&¾ ³¾Kí–¾±W¾ê|Y¾…Hվə¾´Žs¾Ä3Ѿ‘¾ƒc…¾"0˾€§ ¾éŸ¾½âþ½P¾ª‰˜¾9ð»¾ùù¾À- ¾·ý³¾Ð#ú½9™¦¾P°¬¾êò½Ù«¾¯¬¦¾“ì½`ú¯¾î—¢¾:Žê½ ³¾ ¡¾­}¥½ê|Y¾F¬Ý¾Ã«¢½´Žs¾inÙ¾Ûƒž½ƒc…¾V.Ó¾ðw™½éŸ¾L—˾ ú“½ª‰˜¾ÇTþ0|޽À- ¾C»¾Fp‰½9™¦¾8{³¾^H…½Ù«¾%;­¾hv‚½`ú¯¾Iý¨¾kl½ ³¾=m§¾lÕ¼ê|Y¾½à¾mÕ¼´Žs¾¿DܾpÕ¼ƒc…¾¸ïÕ¾sռ韾R?ξwÕ¼ª‰˜¾>ážzÕ¼À- ¾+ƒ½¾~Õ¼9™¦¾ÅÒµ¾€Õ¼Ù«¾½}¯¾‚Õ¼`ú¯¾Á1«¾ƒÕ¼ ³¾~œ©¾FH=ê|Y¾F¬Ý¾¸ì|=´Žs¾inپќt=ƒc…¾V.Ó¾…j=韾L—˾H‰_=ª‰˜¾ÇTþ|T=À- ¾C»¾¾uJ=9™¦¾8{³¾×%B=Ù«¾%;­¾‚<=`ú¯¾Iý¨¾n:= ³¾=m§¾ø<>ê|Y¾…HÕ¾>´Žs¾Ä3Ѿ°éþ=ƒc…¾"0˾õ=韾½âþlê=ª‰˜¾9𻾀¾ß=À- ¾·ý³¾^îÕ=9™¦¾P°¬¾îÙÍ=Ù«¾¯¬¦¾^È=`ú¯¾î—¢¾ÇXÆ= ³¾ ¡¾±àF>ê|Y¾ÓÓǾ9æB>´Žs¾ôľ% =>ƒc…¾x^¾¾¤ê5>韾ò†·¾2+.>ª‰˜¾×°¾¿k&>À- ¾½¢¨¾>M>9™¦¾7Ë¡¾+p>Ù«¾º(œ¾²u>`ú¯¾ÛU˜¾‹þ> ³¾Kí–¾ÿØ€>ê|Y¾m¼µ¾q—|>´Žs¾?B²¾äu>ƒc…¾^"­¾æïk>韾y馾Xÿa>ª‰˜¾å# ¾ÈX>À- ¾Q^™¾ÊìN>9™¦¾l%“¾=gG>Ù«¾‹޾¯LB>`ú¯¾?‹Š¾>k@> ³¾VC‰¾Sêš>ê|Y¾qŸ¾UÝ—>´Žs¾dœ¾f^“>ƒc…¾å—¾×è>韾…o’¾÷÷‡>ª‰˜¾¥~Œ¾‚>À- ¾Ä†¾ #y>9™¦¾4¾1%p>Ù«¾2y¾6 j>`ú¯¾‘s¾½Ëg> ³¾Ùp¾Ý5±>ê|Y¾­_…¾‘»­>´Žs¾gÒ‚¾°›¨>ƒc…¾@~¾Ëb¢>韾Být¾7›>ª‰˜¾´ k¾£×”>À- ¾$a¾¾žŽ>9™¦¾&úW¾Ý~‰>Ù«¾™tP¾‘†>`ú¯¾ ZK¾©¼„> ³¾™xI¾%MÃ>ê|Y¾ îO¾Fz¿>´Žs¾•óK¾Ê×¹>ƒc…¾F¾D³>韾ø>¾)Ž«>ª‰˜¾Ž87¾¤>À- ¾y/¾‰D>9™¦¾šZ(¾ ¢—>Ù«¾‡}"¾-Ï“>`ú¯¾ƒ¾f’> ³¾ç ¾×ÁÐ>ê|Y¾TJ¾­Ì>´Žs¾kŒ ¾t©Æ>ƒc…¾4‚¾\¿>韾"š¾‹i·>ª‰˜¾¾†ü½ w¯>À- ¾8Ùñ½£)¨>9™¦¾ è½&¢>Ù«¾/ôß½@ž>`ú¯¾ÕxÚ½^œ> ³¾sؽ˜%Ù>ê|Y¾þb“½»çÔ>´Žs¾‘½¨§Î>ƒc…¾ iŒ½žÇ>韾5]‡½ξ>ª‰˜¾Pß½•‹¶>À- ¾ëÂx½Šô®>9™¦¾«n½w´¨>Ù«¾F[f½›v¤>`ú¯¾Z·`½æ¢> ³¾`£^½Ó¢>>“µ¾Œ^=ί¤>>“µ¾ÿµ,2õÁ >\¸¾ðÉ[=ê¢>\¸¾Ç`02;Ìœ>iqº¾û…V=çž>iqº¾{Þ32FÆ•>e¬¼¾/M=¦É—>e¬¼¾)/72@ÕŠ>D³¾¾m¢>=%´Œ>D³¾¾«%:2Œ=v>yÀ¾d½)=6’y>yÀ¾Â<2K>šðÁ¾Q] =DVN>šðÁ¾9×>2hí> þ¿Ð<²ù> þj’@2Á?›=3Áþ…f<Õ‚=3ÁþC™A2ÊÕ¼)ľÕëA2t/œ>>“µ¾]ñ×=~€š>\¸¾k®Õ=6±–>iqº¾³Ð=9ï>e¬¼¾${Ç=Ah…>D³¾¾8V¹=Ú“l>yÀ¾¸¥=(„C>šðÁ¾‡o‰=jü > þÎñJ="°”=3Áþà<Ö ’>>“µ¾m­>x>\¸¾X >IæŒ>iqº¾R>|‘†>e¬¼¾î»>ýhy>D³¾¾²x>¹]>yÀ¾Ívï=œ6>šðÁ¾oÇ=ÿt> þ(?“=N*Š=3Áþ›"=j„>>“µ¾~ÿH>Òú‚>\¸¾aäF>Ÿw>iqº¾« B>nós>e¬¼¾­9>Ñb>D³¾¾x‚,>?H>yÀ¾š>,B%>šðÁ¾Ùÿ=]Jï= þ)æ¼= x=3Áþ\šP=Æ:g>>“µ¾$Hp>‰¶d>\¸¾æÃm>L_>iqº¾©h>ÓéT>e¬¼¾0÷]>¢,E>D³¾¾ÿ9N>’.>yÀ¾aŸ7>·ß>šðÁ¾í>ú¶Ï= þµÑá=O*U=3ÁþÄ_y=!ò?>>“µ¾Èðˆ>×=>\¸¾‡>N9>iqº¾~B„>°Ÿ0>e¬¼¾Ë}>u#>D³¾¾.k>´Œ>yÀ¾nLQ>`¾í=šðÁ¾‰O.>o˪= þŒ²>æd,=3Áþ;Ž= >>“µ¾…’–>ûû>\¸¾Äþ”>!E>iqº¾÷l‘>®>e¬¼¾*‹>ªÖú=D³¾¾-;>\Ý=yÀ¾"f>^Tµ=šðÁ¾f©?>m$= þ\‚ >DËü<3Áþ Eœ=¢ÖÅ=>“µ¾#¶ >±“Ã=\¸¾-Ÿ>øt¾=iqº¾å7›>i`µ=e¬¼¾èu”>};§=D³¾¾ðî‰>ýê’=yÀ¾7¡u>™©n=šðÁ¾†‘L>Y¼&= Ã¾È >&²—<3ÁþÝʦ=þç9=>“µ¾‚§>z”7=\¸¾£H¥>…P2=iqº¾êR¡>‹ù(=e¬¼¾õLš>ßl=D³¾¾î[>ׇ=yÀ¾êJ>ˆOÒ<šðÁ¾cT>óSˆ< þÆú>]^«;3Áþ|Z­=Ö¼>“µ¾}6©>Ö¼\¸¾·p§>Ö¼iqº¾Ëm£> Ö¼e¬¼¾UPœ>Ö¼D³¾¾Ô:‘>Ö¼yÀ¾ÉO>÷Õ¼šðÁ¾¡cW>ëÕ¼ þ>ÜÕ¼3Áþ¯=~)½>“µ¾‚§>_ÿ½\¸¾£H¥>j»z½iqº¾êR¡>pdq½e¬¼¾õLš>Ü×b½D³¾¾î[>ÔòM½yÀ¾êJ>À’1½šðÁ¾cT>ö” ½ þÆú>a­»¼3Áþ|Z­= ê½>“µ¾#¶ >#Éç½\¸¾-Ÿ>jªâ½iqº¾å7›>Û•Ù½e¬¼¾èu”>ðp˽D³¾¾ðî‰>o ·½yÀ¾7¡u>?Š›½šðÁ¾†‘L>>'o½ Ã¾È >øC½3ÁþÝʦ=ɺ%¾>“µ¾…’–>´$¾\¸¾Äþ”>Û_ ¾iqº¾÷l‘>Iɾe¬¼¾*‹>†¾D³¾¾-;>ÂȾyÀ¾"f>щٽšðÁ¾f©?>àY¥½ þ\‚ >‡ÐF½3Áþ Eœ=Ú R¾>“µ¾Èðˆ>½ñO¾\¸¾‡>.K¾iqº¾~B„>jºB¾e¬¼¾Ë}>Ô5¾D³¾¾.k>m§"¾yÀ¾nLQ>éù¾šðÁ¾‰O.>áϽ þŒ²>ËÏt½3Áþ;Ž=€Uy¾>“µ¾$Hp>BÑv¾\¸¾æÃm>q¾iqº¾©h>Œg¾e¬¼¾0÷]>[GW¾D³¾¾ÿ9N>½¬@¾yÀ¾aŸ7>pú!¾šðÁ¾í>mìó½ þµÑá=šÊ޽3ÁþÄ_y=vw¾>“µ¾~ÿH>/Œ¾\¸¾aäF>,Ɉ¾iqº¾« B>ƒ¾e¬¼¾­9>Št¾D³¾¾x‚,>ÊYZ¾yÀ¾š>å\7¾šðÁ¾Ùÿ=è¿ ¾ þ)æ¼=ó9 ½3Áþ\šP=3›¾>“µ¾m­>r…™¾\¸¾X >¥ó•¾iqº¾R>Øž¾e¬¼¾î»>ÛÁ…¾D³¾¾²x>s/o¾yÀ¾Ívï=¶H¾šðÁ¾oÇ=¸¾ þ(?“=À_®½3Áþ›"=Ñ<¥¾>“µ¾]ñ×=Û£¾\¸¾k®Õ=“¾Ÿ¾iqº¾³Ð=•ü˜¾e¬¼¾${Ç=žu޾D³¾¾8V¹=“®~¾yÀ¾¸¥=ážU¾šðÁ¾‡o‰=$ ¾ þÎñJ=•帽3Áþà<0«¾>“µ¾t^=QÏ©¾\¸¾ðÉ[=˜Ù¥¾iqº¾û…V=£Óž¾e¬¼¾/M=œâ“¾D³¾¾U¢>=#,„¾yÀ¾L½)=¿ª]¾šðÁ¾9] ="&¾ þ޾Ð<4u¿½3Áþ…f<+½­¾>“µ¾q³e÷«¾\¸¾r-m³yô§¾iqº¾T—e³× ¾e¬¼¾ØÊX³‚Á•¾D³¾¾ÏtE³wÖ…¾yÀ¾j *³ýp`¾šðÁ¾e5³k(¾ þœðª²H¸Á½3Áþ†èʱ0«¾>“µ¾†^½QÏ©¾\¸¾êÉ[½˜Ù¥¾lqº¾õ…V½£Óž¾e¬¼¾ú.M½œâ“¾G³¾¾g¢>½#,„¾yÀ¾^½)½¿ª]¾šðÁ¾K] ½"&¾ þ¿Ð¼4u¿½3Áþì„f¼Ñ<¥¾A“µ¾Zñ×½Û£¾\¸¾h®Õ½“¾Ÿ¾lqº¾°Ð½•ü˜¾e¬¼¾!{ǽžu޾G³¾¾5V¹½“®~¾yÀ¾´¥½ážU¾šðÁ¾„o‰½$ ¾ þÈñJ½•帽3Áþà¼3›¾A“µ¾k­¾r…™¾\¸¾W ¾¥ó•¾lqº¾}R¾Øž¾e¬¼¾ì»¾ÛÁ…¾G³¾¾±x¾s/o¾yÀ¾Êvï½Â¶H¾šðÁ¾oǽ¸¾ þ%?“½À_®½3Áþ›"½vw¾A“µ¾|ÿH¾/Œ¾\¸¾`äF¾,Ɉ¾lqº¾ª B¾ƒ¾e¬¼¾ ­9¾Št¾G³¾¾v‚,¾ÊYZ¾yÀ¾š¾å\7¾šðÁ¾Ùÿ½è¿ ¾ þ&æ¼½ó9 ½4ÁþVšP½€Uy¾A“µ¾"Hp¾BÑv¾\¸¾äÃm¾q¾lqº¾¨h¾Œg¾e¬¼¾/÷]¾[GW¾G³¾¾þ9N¾½¬@¾yÀ¾`Ÿ7¾pú!¾šðÁ¾í¾mìó½ þ²Ñ὚ʎ½4Áþ¾_y½Ú R¾A“µ¾Çðˆ¾½ñO¾\¸¾€‡¾.K¾lqº¾~B„¾jºB¾e¬¼¾É}¾Ô5¾G³¾¾-k¾m§"¾yÀ¾mLQ¾éù¾šðÁ¾ˆO.¾áϽ þ‹²¾ËÏt½4Áþ8Ž½Éº%¾A“µ¾„’–¾´$¾\¸¾Ãþ”¾Û_ ¾lqº¾÷l‘¾Iɾe¬¼¾*‹¾†¾G³¾¾,;¾ÂȾyÀ¾"f¾Ñ‰Ù½šðÁ¾d©?¾àY¥½ þZ‚ ¾‡ÐF½4ÁþEœ½ ê½A“µ¾"¶ ¾#Éç½\¸¾,Ÿ¾jªâ½lqº¾ä7›¾Û•Ù½e¬¼¾çu”¾ðp˽G³¾¾ïo ·½yÀ¾6¡u¾?Š›½šðÁ¾„‘L¾>'o½ Ã¾Æ ¾øC½4ÁþÚʦ½r)½A“µ¾§¾_ÿ½\¸¾¢H¥¾j»z½lqº¾éR¡¾pdq½e¬¼¾ôLš¾Ä×b½G³¾¾í[¾¼òM½yÀ¾èJ¾©’1½šðÁ¾bT¾Þ” ½ þÄú¾a­»¼4ÁþyZ­½ƒÕ¼A“µ¾|6©¾„Õ¼\¸¾¶p§¾…Õ¼lqº¾Êm£¾ˆÕ¼e¬¼¾TPœ¾Õ¼G³¾¾Ó:‘¾”Õ¼yÀ¾ÉO¾Õ¼šðÁ¾ cW¾©Õ¼ þ¾¸Õ¼4Áþ¯½è9=A“µ¾§¾z”7=\¸¾¢H¥¾…P2=lqº¾éR¡¾‹ù(=e¬¼¾ôLš¾÷l=G³¾¾í[¾ï‡=yÀ¾èJ¾¶OÒ<šðÁ¾bT¾#Tˆ< þÄú¾]^«;4ÁþyZ­½¢ÖÅ=A“µ¾"¶ ¾±“Ã=\¸¾,Ÿ¾øt¾=lqº¾ä7›¾i`µ=e¬¼¾çu”¾};§=G³¾¾ïýê’=yÀ¾6¡u¾™©n=šðÁ¾„‘L¾Y¼&= Ã¾Æ ¾&²—<4ÁþÚʦ½ >A“µ¾„’–¾ûû>\¸¾Ãþ”¾!E>lqº¾÷l‘¾®>e¬¼¾*‹¾ªÖú=G³¾¾,;¾\Ý=yÀ¾"f¾^Tµ=šðÁ¾d©?¾m$= þZ‚ ¾DËü<4ÁþEœ½!ò?>A“µ¾Çðˆ¾×=>\¸¾€‡¾N9>lqº¾~B„¾°Ÿ0>e¬¼¾É}¾u#>G³¾¾-k¾´Œ>yÀ¾mLQ¾`¾í=šðÁ¾ˆO.¾o˪= þ‹²¾æd,=4Áþ8޽Æ:g>A“µ¾"Hp¾‰¶d>\¸¾äÃm¾L_>lqº¾¨h¾ÓéT>e¬¼¾/÷]¾¢,E>G³¾¾þ9N¾’.>yÀ¾`Ÿ7¾·ß>šðÁ¾í¾ú¶Ï= þ²Ñá½O*U=4Áþ¾_y½j„>A“µ¾|ÿH¾Òú‚>\¸¾`äF¾Ÿw>lqº¾ª B¾nós>e¬¼¾ ­9¾Ñb>G³¾¾v‚,¾?H>yÀ¾š¾,B%>šðÁ¾Ùÿ½]Jï= þ&æ¼½ x=4ÁþVšP½Ö ’>A“µ¾k­¾x>\¸¾W ¾IæŒ>lqº¾}R¾|‘†>e¬¼¾ì»¾ýhy>G³¾¾±x¾¹]>yÀ¾Êv|6>šðÁ¾oǽÿt> þ%?“½N*Š=3Áþ›"½t/œ>A“µ¾Zñ×½~€š>\¸¾h®Õ½6±–>lqº¾°Ð½9ï>e¬¼¾!{ǽAh…>G³¾¾5V¹½Ú“l>yÀ¾´¥½(„C>šðÁ¾„o‰½jü > þÈñJ½"°”=3Áþà¼Ó¢>>“µ¾n^½õÁ >\¸¾êÉ[½;Ìœ>lqº¾õ…V½FÆ•>e¬¼¾ú.M½@ÕŠ>G³¾¾O¢>½Œ=v>yÀ¾F½)½K>šðÁ¾3] ½hí> þҾмÁ?›=3Áþì„f¼àq¹¾ æƒ=_ë ²Ó ¹¾¿†=U‹’¿‚–=gwõ±Ûµ¿1ïé³2N¿½³½=膳ê=$¿9Ú£=ó1 ³m{(¿Åt=Ñ ³¼+¿·“*=pú³Öß+¿å q<ÖÕÿ²:t®¾ßÐæ=I‹’¼íiξuæ=I‹’¼yë¾ 6å=I‹’¼µ!¿¹eá=I‹’¼ ¿óøÙ=I‹’¼/V¿ÏºÍ=I‹’¼­ê¿¾w»=I‹’¼aÐ#¿Qû¡=I‹’¼(¿œ€=I‹’¼nŽ*¿ )=I‹’¼Bf+¿å q»U‹’<‘¿OÛϺ¸±Mõ¿v«¼U‹’<Ø‚¿N ¦¼ÛÀœ±Û¿Ÿ~*½U‹’<$µ¿?á&½ÇŒ{±_}¿÷÷½U‹’<³¿Ùž½»Æ:±Y¿ËÕ¯½U‹’<ع¿Üf­½¿Çï°5· ¿ú¨Ý½U‹’<âz ¿KþÚ½&T°U¿_ ¾U‹’<#Z¿Œ®¾!P¾U‹’<­¬æ¾ºw<¾¢¢21[N¿£LB»ôB=æ+¿%º¼ôB=Ç=¿²O4½ôB=´{¿1Ó‡½ôB=uÝ¿9q¶½ôB=àZ¿ƒæä½ôB= ë¿ ¾ôB=Ї¿©u¾ôB=°Lö¾æ"2¾ôB= å¾†C¾ôB=}-¿6Ï‘»ß÷*=ºõ¿QÈϼß÷*=£ä¿ ÆB½ß÷*=‚ò¿Zu½ß÷*=ˆ¿s-À½ß÷*=L¿A‘ï½ß÷*=Iˆ ¿Ó¾ß÷*=rÄ¿ ¸$¾ß÷*=®ñõ¾±û8¾ß÷*=l;ä¾AK¾ß÷*=5s ¿òãÌ»mdC=‘!¿,ê¼mdC=æ¿jVT½mdC=¹¿dðš½mdC=°ÿ˽mdC=óp¿â…ü½mdC=†F ¿3×¾mdC=}¿S,¾mdC=Uƒõ¾XLA¾mdC=¾±â¾UYT¾mdC=Uì"¿›¼›ˆK=€~!¿RT½›ˆK=Õ¿&sg½›ˆK=Ö¨¿X¦½›ˆK=ú3¿)ÝØ½›ˆK=¶¯¿aO¾›ˆK=Ž ¿´y¾›ˆK=_¿™4¾›ˆK=$ õ¾XJ¾›ˆK=Sá¾Óc^¾›ˆK=he%¿WÀ&¼mdC=pÛ#¿¡½mdC=ŸC!¿Ðz½mdC=˜¿½À±½mdC=Ó¿£ºå½mdC=zî¿Ò[ ¾mdC=–ä ¿4%¾mdC=¯¿ß<¾mdC=õ’ô¾eS¾mdC=èXß¾Qnh¾mdC=/«'¿ˆJD¼ß÷*=G&¿{Ľß÷*=E#¿†½ß÷*=+_¿Ç;¼½ß÷*=^P¿Sñ½ß÷*=]¿"Ö¾ß÷*=Ó¢ ¿N ,¾ß÷*=™ù¿hyD¾ß÷*=$ô¾oµ[¾ß÷*=;Ïݾe¨q¾ß÷*=AŠ)¿'Ÿ\¼ôB=Ñ'¿#–)½ôB=ãë$¿ÄK½ôB=ùÕ ¿ðÝĽôB=qŠ¿Iû½ôB=Œ¿ƒ+¾ôB=‹? ¿bç1¾ôB=6¿`¼J¾ôB=™Éó¾wŽb¾ôB=‹Ü¾¢Ay¾ôB=cÏ*¿·"m¼U‹’<´)¿™í0½U‹’<Ý &¿Ô3’½U‹’»I‹’¼Mõ¿v«¼I‹’¼Û¿Ÿ~*½I‹’¼_}¿÷÷½I‹’¼Y¿ËÕ¯½I‹’¼5· ¿ú¨Ý½I‹’¼U¿_ ¾I‹’¼%^¿ò5¾I‹’¼gŠö¾J}-¾I‹’¼ [æ¾ ^>¾I‹’¼7³»>Âz‹½!¥)±7³»>v핽Ù2!=ÅÖ>ÝHŒ½e=Ï_Ö> Ë‚½n6±ˆÙê>¢f½ÃÈ=82ê>IU½5ÁY±”Cù>6ø½˜‘ =otø>ü½ ½ Þ‡±ùª?$i¼Wæû<:8?'£:¼q婱·1?õL<>BÞ<#¸?å q<á4ѱ[ß? Ë(=%žÀ<ò^?ìS/=lUû±;] ?ØG=La¥<¦Ñ ?Òj’=/B²½T ?X]Ê=õòŽ<>µ ?ÙŠË=d%(²Io?*¾ÿ=»t<º®?H>úr;²BV?DÁ>=t<c?DÁ>‰L²7³»>^I²½¤I=‹××>L ¦½šËŒ=‰Ÿì><°‰½É†=öuû>§–@½Dx=€â?­®³¼.é_=¶{?ùÕ;5E=Ñ; ?F=;7+=Ø ?ùzŠ=%=º?ó*Ç=¯!þ<z?Vˆþ=hãCÙ<7³»>,ܽ§¼=lÙ>Âþ˽F˸=Ë<ï>J«½7ê¯=вþ>iy½„Ô¢=š­?-X½ñ’=b?Œ»¢¦=V= ?UÕù<|¸`=\? î=ñ@=¹ƒ?ætÂ=Æ&=Y|?4¿ü==ß¶?DÁ>;y=7³»>Ìi¾uîÖ=eWÛ>ú½–1Ó=Giò>l¸Ó½Å É=nP?rNŸ½Zº=Û?Ð@½âî§=а ?–°T¼',”=ê¬ ?FZº¬?o=·\=„Š?i¼¼=A™>=½#?â“ú=ÍM*=èS"?DÁ>°Ó"=7³»>§#¾ãß=nÝ>L¾åýÛ=ŒÝõ>—æÿ½)lÑ=îs?éĽlØÁ=­9 ?€C~½+î®=)3 ?»É¼¨Xš=ŒS?•Šj<&Ã…=ü?2‚X=ɱe=¡Õ?´‚¶=NŠF=¢?î7ø=`f1=(Y'?DÁ>‚œ)=7³»>‚£>¾uîÖ=Ñ„ß>G2/¾–1Ó=ÓQù>a ¾Å É=o—?«ƒê½Zº=J˜ ?tÛ½âî§=ɵ?P½',”=.ú?;ÁÀ;ki€=ºo?JëA=·\=½ ?I°=A™>=†"?úÛõ=ÍM*=g^,?DÁ>°Ó"=7³»>7X¾§¼=pá>r=F¾F˸=N~ü>ñV*¾7ê¯=˜Ž?´ˆ¾„Ô¢=ÀÅ ?fº½ñ’=N?´c@½¢¦=Âi? ¬ôº|¸`=›?#(-=ñ@=ˆ'?ƒª=Æ&=ë¾%?§°ó==qû0?DÁ>;y=7³»>Ÿèl¾¤I=ªã>¬7Y¾šËŒ=ÿ>x;¾É†=â, ?)þDx=Ù?ÔWѽ.é_=ê?–{d½5E=Gk?$¼;7+=ðC?_=%=†¥ ?vÚ¥=¯!þCÙ<7³»>“{¾Ù2!=3ä>df¾e=Ép?ofF¾ÃÈ=F ?Åj¾˜‘ =aÈ?üá½Wæû<œ4?ºù|½>BÞ<¾Ç?N¼%žÀ<½¾? u=La¥<„V"?¨¢=õòŽ<ûË*?5±ð=»t< \7?DÁ>=t<7³»>ö'€¾ÚJH³f|ä>ÆÖj¾nRH³qÄ?O”J¾{©C³¥­ ?Tù!¾;³;?ïæ½˜#1³0®?ÿ‚½tµ%³&H?µ:h¼§³RJ?­/ =¯¨³ö"?z¡=Ùx ³‰Œ+?E?ð=Ð!³4O8?DÁ>qÕ³7³»>“{¾Ó2!½3ä>df¾ e½Ép?ofF¾½È½F ?Åj¾’‘ ½aÈ?üá½Jæû¼œ4?ºù|½2BÞ¼¾Ç?N¼žÀ¼½¾? u=@a¥¼„V"?¨¢=éò޼ûË*?5±ð=£t¼ \7?DÁ>x=t¼7³»>Ÿèl¾¡I½ªã>¬7Y¾—ËŒ½ÿ>x;¾Æ†½â, ?)þ>x½Ù?ÔWѽ(é_½ê?–{d½/E½Gk?$¼57+½ðC?_=½†¥ ?vÚ¥=£!þ¼BÁ(?…çñ=\ã¼È4?DÁ>7Ù¼7³»>7X¾¤¼½pá>r=F¾C˸½N~ü>ñV*¾4꯽˜Ž?´ˆ¾Ô¢½ÀÅ ?fº½ñ’½N?´c@½Ÿ¦½Âi? ¬ôºv¸`½›?#(-=yñ@½ˆ'?ƒª=Æ&½ë¾%?§°ó=½qû0?DÁ>5y½7³»>‚£>¾rîֽфß>G2/¾“1Ó½ÓQù>a ¾Â ɽo—?«ƒê½Wº½J˜ ?tÛ½ßɵ?P½$,”½.ú?;ÁÀ;hi€½ºo?JëA=±\½½ ?I°=;™>½†"?úÛõ=ÇM*½g^,?DÁ>ªÓ"½7³»>§#¾ ãß½nÝ>L¾YþÛ½ŒÝõ>—æÿ½&lѽîs?éĽiØÁ½­9 ?€C~½()3 ?»É¼¥Xš½ŒS?•Šj<"Ã…½ü?2‚X=ñe½¡Õ?´‚¶=HŠF½¢?î7ø=Zf1½(Y'?DÁ>|œ)½7³»>Ìi¾rîÖ½eWÛ>ú½“1Ó½Giò>l¸Ó½Â ɽnP?rNŸ½Wº½Û?Ð@½ßî§½Š° ?–°T¼$,”½ê¬ ?FZº¬?o=±\½„Š?i¼¼=;™>½½#?â“ú=ÇM*½èS"?DÁ>ªÓ"½7³»>,ܽ¤¼½lÙ>Lþ˽C˸½Ë<ï>J«½4ê¯½Š²þ>iy½Ô¢½š­?-X½ñ’½b?Œ»Ÿ¦½V= ?UÕù5y½7³»>^I²½¡I½‹××>Õ ¦½—ËŒ½‰Ÿì><°‰½Æ†½öuû>§–@½>x½€â?­®³¼(é_½¶{?tÖ;/E½Ñ; ?F=57+½Ø ?ùzŠ=½º?ó*Ç=£!þ¼z?Vˆþ=\ã¼Aê?DÁ>7Ù¼7³»>v핽Ó2!½ÅÖ>ÝHŒ½ e½ˆÙê>¢f½½È½”Cù>6ø½’‘ ½ùª?$i¼Jæû¼·1?õL<2BÞ¼[ß? Ë(=žÀ¼;] ?ØG=@a¥¼½T ?X]Ê=éò޼Io?*¾ÿ=£t¼BV?DÁ>x=t¼D?‰^>FqÔtO²\´?÷>j<[°?tå>ÛR² /?¨‰!>\#_<‘+?¥p!>›T²´p?#>øÙQ<ªr?Œ÷"> U²¹h?õ›#>vdCÄpU²Š?‚#>óî4<?Œ÷"> U²©9?L‘!>¥'<¨a?¥p!>›T²iñ?ÿ>j´<¨*?tå>ÛR²:?@e>¤G<·h?U>ÔtO²¬?DÁ>[‹‰L²ûÄ?w>É«Ö<%v?†&>4ÐMXÆ<*" ?@i#>׈ºœ®­<Ýz!?±y#>aÔ <æƒ!?Âé!>ì•<Ï !?òF>K‹q±„<Žd?DÁ>÷B‚»à ='†!?ul>ñ‹=@ý"?Ù0">ñ)=4$?úã#>™Óô<×Ñ$?¯$>+õã<Ò%?Ž$>½Ó<èã$?çk">t–Ã<Ï'$?°>tÒ¶<=Ù"?ìÍ>â(®<÷ì ?DÁ>â÷ª<–$?5Ç>Õ!=u&?sÁ>¥ =‹é'?»©">8Â=ûë(?¦x$>¡æ =u)?º'%>ôB=.})?ï$>Ž>ñ<ý(? #>`‡ß<í'?­/ >…ðÐ<üE&?§>& ÇpdÃ<Ù)?éö>2¶'=uÓ+? >7Ž"= E-?D-#>úô=¨*.?…%>æº=û€.?YÜ%>i°=¶D.?Íh%>ÕKû<¬r-? ¶#>­×è<,?dº >4¥Ù<#*?l>=UÏ<(Y'?DÁ>žˆË</?&>Õ!=Í11?µz >¥ =Ÿ 2?ΰ#>8Â=Vi3?Ÿ¼%>¡æ =ãŒ3?6‘&>ôB=> 3?Ô!&>Ž>ñ`‡ß< "0?E!>…ðÐ& Ç<ü±*?DÁ>pdÃ<Úñ3?cR>»à = 6?±Ï >ñ‹=êŒ7?¯)$>ñ)=;8?LQ&>™Óô<08?A7'>+õã<šp7? Ì&>½Ót–ÃtÒ¶<'1?A >â(®â÷ª<1í7?‚v>É«Ö<Å0:?¢!>4Ð<Õš;?D$>MXÆ<(3׈º<Åœ®­<Ž;?èW'>aÔ ì•K‹<ù3?‹H>q±„<ÂM0?DÁ>÷B‚<Ú :?>Fq<òj<[>?áÐ$>\#_<ä>?,'>øÙQ<-™>?À(>vdC<á‚=?·'>óî4<¯«;?¼Ú%>¥'<·9?+u">j´< ã5?ír>¤G<Á2?DÁ>[‹<³Ÿ;?˜>Fﳎö=?µV!>†<³™^??äé$>l³§â??½='>ÔÌý²›???(>‘(ô²Xj>?Ú'>ë겯ƒ1à²vä9?T">Ë3ײ—6?p‚>ó,вۧ2?DÁ>¬Ò˲ڠ:?>.q¼òhj¼[>?áÐ$>D#_¼ä>?,'>ßÙQ¼-™>?À(>^dC¼á‚=?·'>Úî4¼¯«;?¼Ú%>w¥'¼·9?+u">R´¼ ã5?ír>ŒG¼Á2?DÁ>C‹¼1í7?‚v>¼«Ö¼Å0:?¢!>(м՚;?D$>AXƼ(3ˈº¼Å®­¼Ž;?èW'>UÔ ¼ea9?‚%>à•¼Q7?Ö-">ùJ‹¼ù3?‹H>d±„¼ÂM0?DÁ>ëB‚¼Úñ3?cR>µà ½Â 6?±Ï >닽êŒ7?¯)$>ë)½;8?LQ&>Óô¼08?A7'>õ㼚p7? Ì&>±Ó¼q6?%>h–üPç3?³Ä!>hÒ¶¼'1?A >Ö(®¼XÅ-?DÁ>Ö÷ª¼/?&>Î!½Í11?µz >Ÿ ½Ÿ 2?ΰ#>2½Vi3?Ÿ¼%>›æ ½ãŒ3?6‘&>îB½> 3?Ô!&>‚>ñ¼Wè1?1b$>T‡ß¼ "0?E!>yðмJº-?†¾> Ǽü±*?DÁ>ddüÙ)?éö>,¶'½uÓ+? >1Ž"½ E-?D-#>ôô½¨*.?…%>ຽû€.?YÜ%>b°½¶D.?Íh%>ÉKû¼¬r-? ¶#>¡×è¼,?dº >'¥Ù¼#*?l>1Uϼ(Y'?DÁ>’ˆË¼–$?5Ç>Î!½u&?sÁ>Ÿ ½‹é'?»©">2½ûë(?¦x$>›æ ½u)?º'%>îB½.})?ï$>‚>ñ¼ý(? #>T‡ß¼í'?­/ >yðмüE&?§> ǼS$?DÁ>ddüSÀ?4›>µà ½'†!?ul>닽@ý"?Ù0">ë)½4$?úã#>Óô¼×Ñ$?¯$>õã¼Ò%?Ž$>±Ó¼èã$?çk">h–üÏ'$?°>hÒ¶¼=Ù"?ìÍ>Ö(®¼÷ì ?DÁ>Ö÷ª¼íÄ?w>¼«Ö¼%v?†&>(мSï?EÍ!>AXƼ*" ?@i#>ˈº¼!!?Óø#>®­¼Ýz!?±y#>UÔ ¼æƒ!?Âé!>à•¼Ï !?òF>ùJ‹¼/ ?¢>d±„¼Žd?DÁ>ëB‚¼D?‰^>.q¼\´?÷>hj¼ /?¨‰!>D#_¼´p?#>ßÙQ¼¹h?õ›#>^dC¼Š?‚#>Úî4¼©9?L‘!>w¥'¼iñ?ÿ>R´¼:?@e>ŒG¼¬?DÁ>C‹¼ÊÕ¼á. > ?¤²ß‘ =q7Ÿ>Sìç;=Ó =q7Ÿ>Z‹£²o=…œ>®8<7!s=…œ>B…¡²Õ=ðe˜>~pU<‰è=ðe˜>‡ž²çë=/(“>ÍŠU<±ÿ=/(“>ʧš²~=>¸œA<_ä€=>FA–²HP=´‰†>^ñ"<¾IS=´‰†>j‘²¹ =UŠ>ùÓ<ôž"=UŠ>|Œ²dŸ÷<š5r>ÎÕ;ðÉû<š5r>ÙŽ‡²¾Šâ< ±e>ÂÝÆ;¾pæ< ±e>Dû‚²5a=w™Z> {í;Àµ=w™Z>ÅÝ}²=q7Ÿ>yRa<–)e=…œ>Ʋ<¢Î‡=ðe˜>ò]ÏAx϶¼<`çF=´‰†>£Rž<"ª=UŠ>mC~<œˆë<š5r>ß"O< <×< ±e>RA<àž=w™Z>¦áfœa£<.}T=…œ> ¡=ÌE|=ðe˜>Ù\=Gn|=/(“>ep=§†a=>h=T8=´‰†>Ÿåàf¸<Î#Ø<š5r>P?–<«Å< ±e> AŒ1„§<åÁÔèzÑ<é >=…œ>d4&=‹?b=ðe˜>:Ê@=Úcb=/(“>âã@=ÂèI=>Äæ.=E<$=´‰†>ä7=Șù"yìÒ³À<2µ¬< ±e>rê³<iÜçÖ+Lú<Ña"=…œ>F—F=&B=ðe˜>|[f=UEB=/(“>Êzf=ÔÇ,=>JýP=™µ =´‰†>ë/=A-ÒL =4æ<š5r>Qæ<¦Ž< ±e>À×<®|¸Ís=ý‰Q–=ÿ=…œ>^Cb=Ä”=ðe˜>:ƒ=l®=/(“>¨Lƒ=N± =>8n=ÝÞ<´‰†>ºqH=7¤Ù!=Αp<š5r>˜<=ÿV< ±e>íõ<%|Žùi=bí5‘¹=&׺<…œ>£²x=ÈNä<ðe˜>¡== väÞQ=eÈ<>Þ‚=Z4<´‰†>²T\=ë÷_µý0=(©<š5r>]G=A¬< ±e>³Á=‹2> ý =Gù ;q7Ÿ>w;'=`¶T<…œ>†¯„=ó†<ðe˜>Qé™=V ‡!ÿ™=–cg<>Ú¤‹=pÏ+<´‰†>Õk=-ÛÚ;UŠ>˜ß<=#4y;š5r>Ãù=óA; ±e>dÓ=Ÿ¬;w™Z>UÔ+=ƒüæºq7Ÿ>=Ç-=_³;…œ>0á‰=O5‰;ðe˜>ÌïŸ=íi‰;/(“>¢ =‡C;>»‘=1ß:´‰†>½Rt=ž`ºUŠ>.GD=q»š5r> =ˆ›5» ±e>Õz=åÁкw™Z>«–2=ÓÕ¼q7Ÿ>²0=ÙÕ¼…œ>V«‹=ÛÕ¼ðe˜>C¢=ÛÕ¼/(“>l¢=ÙÕ¼>ÿ’=×Õ¼´‰†>3w=ÔÕ¼UŠ>iÔF=ÓÕ¼š5r>n"=ÒÕ¼ ±e>Õm=ÓÕ¼w™Z>5ë4=f‚¼q7Ÿ>=Ç-=6l¤¼…œ>0á‰=#³¼ðe˜>ÌïŸ=u0³¼/(“>¢ =j9©¼>»‘=½ã™¼´‰†>½Rt=êÔ‰¼UŠ>.GD=Ae{¼š5r> =»Dt¼ ±e>Õz=°Éƒ¼w™Z>«–2=K¹¼q7Ÿ>w;'=ú0û¼…œ>†¯„=iä ½ðe˜>Qé™=ñ ½/(“>!ÿ™=ÊC½>Ú¤‹=‚½æ¼´‰†>Õk=•ŒÇ¼UŠ>ß<=Nü¯¼š5r>Ãù=-©¼ ±e>dÓ=²Û»¼w™Z>UÔ+={Ìë¼q7Ÿ>‘¹=xÖ%½…œ>£²x=I’:½ðe˜>¡==ì¥:½/(“>ÞQ=p,½>Þ‚=½´‰†>²T\=àh½UŠ>µý0=^ªÞ¼š5r>]G=ë«Ô¼ ±e>³Á=ïï¼w™Z> ý =äò ½q7Ÿ>Q–=ìiJ½…œ>^Cb=©ÿd½ðe˜>:ƒ=Qe½/(“>¨Lƒ=3S½>8n=Tm7½´‰†>ºqH=r½UŠ>Ù!=Y½š5r>˜<=QUü¼ ±e>íõ<ø¨½w™Z>ùi=[!½q7Ÿ>+Lú<¶Ìj½…œ>F—F=vH…½ðe˜>|[f=X…½/(“>Êzf=¹2u½>JýP=~ T½´‰†>ë/=†1½UŠ>L =ÿ]½š5r>Qæ<ç½½ ±e>À×<<©$½w™Z>Ís=×Ë2½q7Ÿ>èzÑd4&=8U•½ðe˜>:Ê@=`g•½/(“>âã@=Ô)‰½>Äæ.=*§l½´‰†>ä7=I7E½UŠ>"yì<r'½š5r>Ò³À<~Ž ±e>rê³çÖ<ï@½q7Ÿ>ma£< t޽…œ>ñ =dX¢½ðe˜>Ù\=–l¢½/(“>ep=Æø”½>ég=E€½´‰†>Ÿå<;3U½UŠ>àf¸<Ì|4½š5r>P?–<:÷*½ ±e> AŒ<2E½w™Z>1„§<æpK½q7Ÿ>yRa<>Ê–½…œ>Ʋ<¬½ðe˜>ò]Ï<Ù¬½/(“>AxÏ<’¿½>¶¼<"©‡½´‰†>£Rž<a½UŠ>mC~½š5r>ß"O<ê4½ ±e>RA<Å P½w™Z>¦áf<ÄüQ½q7Ÿ>?ìç;óû›½…œ>®8< ²½ðe˜>~pUÍŠU<~7£½>¸œA<DŒ½´‰†>^ñ"<ž|h½UŠ>ïÓ<—:D½š5r>ºÕ;D°9½ ±e>®ÝÆ;ÌV½w™Z>úzí;">T½q7Ÿ>’Öí²ƽ…œ>º® ³û´½ðe˜>.˜³$5´½/(“>ɳ³Ò¥½>X$ ³QÚ½´‰†>R¾ù²Ù k½UŠ>K;à²ÝOF½š5r>ØË²D£;½ ±e>z¬Â²¤ Y½w™Z>žçʲÄüQ½q7Ÿ>¦ìç»óû›½…œ>ô8¼ ²½ðe˜>ÄpU¼Y!²½/(“>‹U¼~7£½>þœA¼DŒ½´‰†>¤ñ"¼ž|h½UŠ>Ô¼—:D½š5r>Õ»D°9½ ±e>ÞÆ»ÌV½w™Z>O{í»æpK½q7Ÿ>ÀRa¼>Ê–½…œ>>Ʋ¼¬½ðe˜>æ]ϼÙ¬½/(“>dxϼ’¿½>ª¼¼"©‡½´‰†>ÆRž¼a½UŠ>³C~¼3/>½š5r>%#O¼ê4½ ±e>…RA¼Å P½w™Z>áf¼ï@½q7Ÿ>a£¼ t޽…œ>¡½XX¢½ðe˜>Ó\½–l¢½/(“>vp½Æø”½>úg½E€½´‰†>9Ÿå¼;3U½UŠ>g¸¼Ì|4½š5r>s?–¼:÷*½ ±e>/AŒ¼2E½w™Z>$„§¼×Ë2½q7Ÿ>ÜzѼg<ƒ½…œ>v4&½8U•½ðe˜>4Ê@½`g•½/(“>ôã@½Ô)‰½>¾æ.½*§l½´‰†>õ7½I7E½UŠ>Fyì¼r'½š5r>õ³À¼~Ž ±e>fê³¼hŸ6½w™Z>çÖ¼[!½q7Ÿ>OLú¼¶Ìj½…œ>@—F½vH…½ðe˜>v[f½X…½/(“>Äzf½¹2u½>DýP½~ T½´‰†>ë/½†1½UŠ>(L ½ÿ]½š5r>Qæ¼ç½½ ±e>â×¼<©$½w™Z>Çs½äò ½q7Ÿ>b–½ìiJ½…œ>XCb½©ÿd½ðe˜>‰:ƒ½Qe½/(“>¥Lƒ½3S½>2n½Tm7½´‰†>ÌqH½r½UŠ>ë!½Y½š5r>©<½QUü¼ ±e> õ¼ø¨½w™Z>ói½{Ìë¼q7Ÿ>£¹½xÖ%½…œ>µ²x½I’:½ðe˜>ª=½ì¥:½/(“>ÛQ½p,½>Þ‚½½´‰†>ÄT\½àh½UŠ>Æý0½^ªÞ¼š5r>WG½ë«Ô¼ ±e>ÅÁ½ïï¼w™Z>ý ½¹¼q7Ÿ>q;'½ú0û¼…œ>ƒ¯„½iä ½ðe˜>Z陽ñ ½/(“>*ÿ™½ÊC½>פ‹½‚½æ¼´‰†>Ïk½•ŒÇ¼UŠ>‘ß<½Nü¯¼š5r>Õù½-©¼ ±e>uÓ½²Û»¼w™Z>OÔ+½f‚¼q7Ÿ>OÇ-½6l¤¼…œ>8ች#³¼ðe˜>ÕE0³¼/(“>Ÿ ½;9©¼>ѽ½ã™¼´‰†>·Rt½åÔ‰¼UŠ>(GD½8e{¼š5r>" ½²Dt¼ ±e>çz½¬Éƒ¼w™Z>¤–2½ÁÕ¼q7Ÿ>¬0½»Õ¼…œ>S«‹½¹Õ¼ðe˜>@¢½¹Õ¼/(“>i¢½ºÕ¼>ÿ’½½Õ¼´‰†>-w½ÀÕ¼UŠ>cÔF½ÁÕ¼š5r>h"½ÂÕ¼ ±e>Ïm½ÀÕ¼w™Z>/ë4½9üæºq7Ÿ>OÇ-½_³;…œ>8ችO5‰;ðe˜>Õ«j‰;/(“>Ÿ ½C;>ѽ1ß:´‰†>·Rt½ `ºUŠ>(GD½L»š5r>" ½c›5» ±e>çz½œÁкw™Z>¤–2½ú ;q7Ÿ>q;'½`¶T<…œ>ƒ¯„½ó†<ðe˜>Z陽V ‡*ÿ™½–cg<>פ‹½pÏ+<´‰†>Ïk½-ÛÚ;UŠ>‘ß<½#4y;š5r>Õù½óA; ±e>uÓ½Ÿ¬;w™Z>OÔ+½bí5£¹½&׺<…œ>µ²x½ÈNä<ðe˜>ª=½ väÛQ½eÈ<>Þ‚½Z4<´‰†>ÄT\½ë÷_Æý0½(©<š5r>WG½A¬< ±e>ÅÁ½‹2>ý ½ý‰b–½ÿ=…œ>XCb½Ä”=ðe˜>‰:ƒ½l®=/(“>¥Lƒ½N± =>2n½ÝÞ<´‰†>ÌqH½7¤ë!½Î‘p<š5r>©<½ÿV< ±e> õ¼%|Žói½pá±OLú¼Ña"=…œ>@—F½&B=ðe˜>v[f½UEB=/(“>Äzf½ÔÇ,=>DýP½™µ =´‰†>ë/½A-Ò(L ½4æ<š5r>Q漦Ž< ±e>â×¼®|¸Çs½åÁÔÜzѼé >=…œ>v4&½‹?b=ðe˜>4Ê@½Úcb=/(“>ôã@½ÂèI=>¾æ.½E<$=´‰†>õ7½È˜ùFyì¼s¾<š5r>õ³À¼2µ¬< ±e>fê³¼iÜçÖ¼hña£¼.}T=…œ>¡½äE|=ðe˜>Ó\½Gn|=/(“>vp½§†a=>úg½T8=´‰†>9Ÿå¼VÈ =UŠ>Ôf¸¼Î#Ø<š5r>s?–¼«Å< ±e>/AŒ¼Uù$„§¼=q7Ÿ>ÀRa¼–)e=…œ>Ʋ¼¢Î‡=ðe˜>æ]ϼfä‡=/(“>dxϼ?s=>ª¼¼`çF=´‰†>ÆRž¼"ª=UŠ>³C~¼Êˆë<š5r>Ç"O¼ <×< ±e>…RA¼àž=w™Z>áf¼ß‘ =q7Ÿ>”ìç»o=…œ>ô8¼Õ=ðe˜>ÄpU¼çë=/(“>‹U¼~=>þœA¼HP=´‰†>¤ñ"¼¹ =UŠ>Ô¼dŸ÷<š5r>üÕ»¾Šâ< ±e>ðÝÆ»5a=w™Z>;{í»|GU=b_Q>†á%<áˆX=b_Q>Bw²Žœ='ÆI>¤Ah<‰Õž='ÆI>|q²|¦Ø=‹eC> œ<™¶Û=‹eC>ìÊl²¿Q >ZÕ=>nõÇ<H>ZÕ=>€Íh²â/>]­8> çô]­8>'ýd²L“O>a…3>H=ùfR>a…3>Î,a²#l>/õ->KL#=œÁo>/õ->b/]²…ó>“”'>ÉÒ2=ª´ƒ>“”'>~X²÷½‰>Xû>ú.==*™‹>Xû> ëR²º“Œ>DÁ>ô@=jxŽ>DÁ>‰L²4ÖK=b_Q>4E¡<×ñ•='ÆI>GÍá<óÃÏ=‹eC>)¿=ç >ZÕ=>äfB=˜%(>]­8>ªn=†`G>a…3>Œ=vAc>/õ->xž=¹y>“”'>€Ú­=ƒ[„>Xû>öì·=Ô‡>DÁ>N—»=@±<=b_Q>ê<¹W‹='ÆI>½Ô#=³ƒÁ=‹eC>A3\="ý=ZÕ=>q =‹÷>]­8>NÀ¬=::>a…3>t2Ë=ÜXT>/õ->c`æ=¢ei>“”'>ÅGü=‹qw>Xû>Žr>ò|>DÁ>c>ØT(=b_Q>=I-z='ÆI>§,R=a[®=‹eC>¦>=uä=ZÕ=>œò´=éï >]­8>DžÝ=.Œ(>a…3>¡V>™M@>/õ->ÊÅ>qrS>“”'>fÒ!>Á8`>Xû>s2+>ÔÞd>DÁ>º›.>4==b_Q>ªr3=> W='ÆI>³@{=¿–=‹eC>¿Ù¨=“5Æ=ZÕ=>NPØ=SÔö=]­8>Kw>­Â>a…3> Ð>ô™'>/õ->Q§0> e8>“”'>ýrA>žšC>Xû>û§L>Ë®G>DÁ>(¼P>ÍãNŠL=I÷-='ÆI>_1=×Gv=‹eC>vÀ=â×¢=ZÕ=>¿ö=‰ƒË=]­8>Fý>‡’ò=a…3>Œ™1>m¸ >/õ->öZI>Å>“”'>Ï\>%">Xû>Fi>\Ž%>DÁ>1ìm>^š¡¶æ`=Ž>ÿ<'ÆI>hr=Ëý7=‹eC>mžÓ=…ãu=ZÕ=>n>“¥š=]­8>é&>¹¹=a…3>xGC>¨EÔ=/õ->9f]> -ê=“”'>sr>aÊø=Xû>t?€> þ=DÁ>¨Í‚>’´1ª p=[b™<'ÆI>’ ¨=gç<‹eC>­Þá=o1=ZÕ=>D®>4ãI=]­8>õ21>¤Ùs=a…3>ãmP>¾§Œ=/õ->ÓNl>Å¿›=“”'>>c>;Ò¥=Xû>2âˆ>”|©=DÁ>ƒœ‹>]¨:b_Q>ò|y=œ×®;'ÆI>Ö¨®=jU'<‹eC>7Áê=_>"|¬<]­8>@+8>¤¥×ª X>ª-þ€šu><=“”'>4z†>nù=Xû>¦DŽ>о=DÁ>h‘>×Õ¼b_Q>V¾|=ÝÕ¼'ÆI>Dð°=ãÕ¼‹eC>TÑí=êÕ¼ZÕ=>fU>ñÕ¼]­8>Œ’:>øÕ¼a…3>Vt[>þÕ¼/õ->ùÎx>Ö¼“”'>X;ˆ>Ö¼Xû>Ø>Ö¼DÁ>ÿ’>¢[›¼b_Q>ò|y=±‹¼¼'ÆI>Ö¨®=€ä¼‹eC>7Áê=>0½ZÕ=>_>ö¨½]­8>@+8>·=4½a…3>ª X>ºG½/õ->€šu>8W½“”'>4z†>ida½Xû>¦DŽ>‡)e½DÁ>h‘>°é¼b_Q>ª p=½'ÆI>’ ¨=™ô;½‹eC>­Þá=Tœf½ZÕ=>D®> '‰½]­8>õ21>D"ž½a…3>ãmP><ݰ½/õ->ÓNl>Dõ¿½“”'>>c>®ʽXû>2âˆ>²Í½DÁ>ƒœ‹>8½b_Q>¶æ`=, H½'ÆI>hr=X4€½‹eC>mžÓ=5'Ÿ½ZÕ=>n>Û¾½]­8>é&>,Mݽa…3>xGC>{ø½/õ->9f]>>1¾“”'>sr>ê¾Xû>V?€>¿(¾DÁ>¨Í‚>qQ:½b_Q>NŠL=.bv½'ÆI>_1=^YŸ½‹eC>vÀ=T ǽZÕ=>¿ö=ü¸ï½]­8>Fý>ýc ¾a…3>Œ™1>&Ó¾/õ->öZI>Âß*¾“”'>Ï\>Ï?4¾Xû>Fi>©7¾DÁ>1ìm>¨W½b_Q>ªr3=»½'ÆI>³@{=wôº½‹eC>¿Ù¨=kê½ZÕ=>NPØ=ã„ ¾]­8>Kw>fÝ$¾a…3> Ð>­´9¾/õ->Q§0>Y€J¾“”'>ýrA>WµU¾Xû>û§L>„ÉY¾DÁ>(¼P>½¿p½b_Q>=L¡½'ÆI>§,R=ÔÒ½‹eC>¦>=;U¾ZÕ=>œò´=¢ ¾]­8>DžÝ=è¦:¾a…3>¡V>RhR¾/õ->ÊÅ>*e¾“”'>fÒ!>zSr¾Xû>s2+>ùv¾DÁ>º›.>Ž‚½b_Q>ê<,¯½'ÆI>¥Ô#=%¹å½‹eC>A3\=Êš¾ZÕ=>q =€/¾]­8>NÀ¬=ÔTL¾a…3>t2Ë=Ðsf¾/õ->c`æ=[€{¾“”'>ÅGü="Æ„¾Xû>Žr>VT‡¾DÁ>c> нb_Q>4E¡GÍá)¿= »¾ZÕ=>äfB=Q@:¾]­8>’n=?{Y¾a…3>Œ=k\u¾/õ->xž=ìé…¾“”'>€Ú­=àh¾Xû>öì·=1#¾DÁ>N—»=1ÙŽ½b_Q>'á%<ŽÃÀ½'ÆI>¤Ah<îÛü½‹eC> œnõÇ<œ8A¾]­8>Þæô<®a¾a…3>0=ܧ~¾/õ->3L#=⋾“”'>±Ò2=TË’¾Xû>ã.==¡•¾DÁ>ô@=ãy½b_Q>¡¶å²ü ý'ÆI>‹µ³ ìÿ½‹eC>³Âb!¾ZÕ=>ô(:³èŸC¾]­8>öU³²d¾a…3>k¨p³+/õ->¢!„³ÂŒ¾“”'>™{³†¦”¾Xû>o“³Ç…—¾DÁ>uý”³1ÙŽ½b_Q>ná%¼ŽÃÀ½'ÆI>ìAh¼îÛü½‹eC>”œ¼xl¾ZÕ=>’õǼœ8A¾]­8>çô¼®a¾a…3>B½Ü§~¾/õ->EL#½â‹¾“”'>ÃÒ2½TË’¾Xû>ô.=½¡•¾DÁ>ô@½ нb_Q>WE¡¼J'º½'ÆI>:Íá¼eùó½‹eC>#¿½ »¾ZÕ=>öfB½Q@:¾]­8>¤n½?{Y¾a…3>ŠŒ½/\u¾/õ->ž½ìé…¾“”'>‰Ú­½àh¾Xû>óì·½1#¾DÁ>K—»½Ž‚½b_Q>=ê¼,¯½'ÆI>·Ô#½%¹å½‹eC>;3\½Êš¾ZÕ=>z ½E/¾]­8>KÀ¬½ÔTL¾a…3>q2˽•sf¾/õ->``æ½[€{¾“”'>ÂGü½"Æ„¾Xû>Œr¾VT‡¾DÁ>a¾½¿p½b_Q>ü½L¡½'ÆI>¹,R½ÔÒ½‹eC>£>½;U¾ZÕ=>™ò´½¢ ¾]­8>AžÝ½è¦:¾a…3>ŸV¾RhR¾/õ->Éž*e¾“”'>dÒ!¾zSr¾Xû>r2+¾ùv¾DÁ>¸›.¾¨W½b_Q>¤r3½»½'ÆI>­@{½wôº½‹eC>¼Ù¨½kê½ZÕ=>KPؽㄠ¾]­8>…w¾fÝ$¾a…3> о­´9¾/õ->‹§0¾Y€J¾“”'>ürA¾WµU¾Xû>5¨L¾„ÉY¾DÁ>'¼P¾qQ:½b_Q>HŠL½.bv½'ÆI>\1½^YŸ½‹eC>vÀ½T ǽZÕ=>¼ö½ü¸ï½]­8>Eý¾ýc ¾a…3>Š™1¾&Ó¾/õ->õZI¾Âß*¾“”'>Í\¾Ï?4¾Xû>Fi¾©7¾DÁ>0ìm¾8½b_Q>Èæ`½, H½'ÆI>qr½X4€½‹eC>ážÓ½5'Ÿ½ZÕ=>m¾Û¾½]­8>#&¾,Mݽa…3>vGC¾{ø½/õ->sf]¾>1¾“”'>þrr¾ê¾Xû>s?€¾¿(¾DÁ>§Í‚¾°é¼b_Q>» p½½'ÆI>› ¨½™ô;½‹eC>ªÞá½Tœf½ZÕ=>B®¾ '‰½]­8>ó21¾D"ž½a…3>âmP¾0ݰ½/õ-> Ol¾8õ¿½“”'>=c¾®ʽXû>1∾²Í½DÁ>‚œ‹¾¢[›¼b_Q>}y½±‹¼¼'ÆI>Þ¨®½€ä¼‹eC>4Áê½&0½ZÕ=>_¾ö¨½]­8>z+8¾·=4½a…3>¨ X¾ºG½/õ->šu¾!W½“”'>3z†¾Sda½Xû>¥D޾o)e½DÁ>h‘¾½Õ¼b_Q>P¾|½·Õ¼'ÆI>Að°½±Õ¼‹eC>QÑí½ªÕ¼ZÕ=>dU¾£Õ¼]­8>‹’:¾œÕ¼a…3>Ut[¾–Õ¼/õ->øÎx¾‘Õ¼“”'>W;ˆ¾Õ¼Xû>ؾŒÕ¼DÁ>ÿ’¾]¨:b_Q>}y½œ×®;'ÆI>Þ¨®½jU'<‹eC>4Áê½f_¾"|¬<]­8>z+8¾¤¥×¨ X¾ª-þšu¾S=“”'>3z†¾„ù=Xû>¥D޾¢¾=DÁ>h‘¾’´1» p½[b™<'ÆI>› ¨½gç<‹eC>ªÞá½o1=ZÕ=>B®¾4ãI=]­8>ó21¾¤Ùs=a…3>âmP¾Ê§Œ=/õ->ÒNl¾Ñ¿›=“”'>=c¾;Ò¥=Xû>1∾”|©=DÁ>‚œ‹¾^š¡°æ`½Ž>ÿ<'ÆI>qr½Ëý7=‹eC>jžÓ½…ãu=ZÕ=>m¾“¥š=]­8>#&¾¹¹=a…3>vGC¾¨EÔ=/õ->8f]¾ -ê=“”'>þrr¾aÊø=Xû>s?€¾ þ=DÁ>§Í‚¾ÍãHŠL½I÷-='ÆI>\1½×Gv=‹eC>vÀ½â×¢=ZÕ=>¼ö½‰ƒË=]­8>Eý¾‡’ò=a…3>Š™1¾m¸ >/õ->õZI¾Å>“”'>Í\¾%">Xû>Fi¾\Ž%>DÁ>0ìm¾4==b_Q>¤r3½> W='ÆI>­@{½¿–=‹eC>¼Ù¨½“5Æ=ZÕ=>KPؽSÔö=]­8>…w¾­Â>a…3> оô™'>/õ->‹§0¾ e8>“”'>ürA¾žšC>Xû>5¨L¾Ë®G>DÁ>'¼P¾ØT(=b_Q>ü½I-z='ÆI>¹,R½a[®=‹eC>£>½uä=ZÕ=>™ò´½éï >]­8>AžÝ½.Œ(>a…3>ŸV¾™M@>/õ->ÉžqrS>“”'>dÒ!¾Á8`>Xû>r2+¾ÔÞd>DÁ>¸›.¾@±<=b_Q>=ê¼¹W‹='ÆI>·Ô#½³ƒÁ=‹eC>;3\½"ý=ZÕ=>z ½Ç÷>]­8>KÀ¬½::>a…3>q2˽YT>/õ->``æ½¢ei>“”'>ÂGü½‹qw>Xû>Œr¾ò|>DÁ>a¾4ÖK=b_Q>WE¡¼×ñ•='ÆI>:Íá¼óÃÏ=‹eC>#¿½ç >ZÕ=>öfB½˜%(>]­8>¤n½†`G>a…3>ŠŒ½±Ac>/õ->ž½¹y>“”'>}Ú­½ƒ[„>Xû>óì·½Ô‡>DÁ>K—»½|GU=b_Q>ná%¼Žœ='ÆI>ŒAh¼|¦Ø=‹eC>”œ¼¿Q >ZÕ=>’õǼâ/>]­8>çô¼L“O>a…3>B½#l>/õ->EL#½…ó>“”'>ÃÒ2½÷½‰>Xû>ô.=½º“Œ>DÁ>ô@½S$/??±l.?E â>3ß-?æëâ>ÿ”.??±§-?… ã>³\.?? º-?Òÿâ>Po.??1 .?öÔâ>ƒÀ.??Œ.?~â>D/??¹3/?ø5â>éí/??íô/?¯Îá>«±0??ÂÃ0?1`á>0ƒ1??”1?éðà>6V2??ÑY2?#‡à>Ž3??X,?†âÆ>öÏ+?—uÇ>š+?B¯Ç>.¬+?AœÇ>Qù+?äHÇ>Kv,?ãÁÆ>”-?ÓÆ>Ñ-?KÅ>x˜.?2tÄ>ò`/?ª›Ã>20?%ÎÂ>â)?Ú­>Z‚(?ô­>HP(?ÜG®>Ý`(?4,®>©(?[³­>2)?‰ï¬>Rµ)?²ò«>„c*?tϪ>ç+?¢—©>ºÙ+?t]¨>ü‹,?D3§>n…$?­•>€$?¾–>óã#?*—>õò#?’—>±4$?ƒk–>0Ÿ$??p•>ž(%?,”>Ç%?J¶’> p&?B&‘>i'?W“>Œ½'?¾Ž>.þ?å€>g˜?E,‚>mp?¸¬‚>ª}?A‚‚>E·?ëÈ>º?€œ€>P?×1~>P ?1´z>­ ?ä÷v>åB!?ƒ4s>Ñ!?ä¡o>U‡?l>^>32?9(a>Ø?ÇLb>Ú?Nìa>L?»E`>Fš?¿˜]>ÿ?S%Z>Ts?¢*V>Èï?£èQ>&m?„žM>ä?ø‹I>h<?ZfA>ú?VšD>à?@ÜE>§è?ÒqE>4?M¡C>'K?l°@>Ì™?ëä<>^ô?Ç„8>\U?AÖ3>·?Ñ/>Ï?v¤*>½8 ?,ò+>þ ?y]/>ù?á´0>ÿ?_C0>ã ?¨S.>åB ?ì/+>y ?â"'>· ?w">fú ?>x>´= ?‹o>š} ?¨>·—?É‘>0€?m">óv?w„#>öy?€#>U‡? !>åœ?ïÇ>¿¸?C’>ÜØ?³·>Dû?†>Þ?ŒJ >Ä>?&S>6éî>Zõ>6éî>ÉŽ>6éî>Œø>6éî>>6éî>Ñv>6éî>ž(>6éî> å>6éî><ú>6éî>V· >6éî>8j>6éî>Nb>½åÚ>É‘>aÛ>m">—Ü>w„#>ªEÜ>€#>®cÜ> !>î`Ü>ïÇ>|DÜ>C’>¥Ü>³·>ZØÛ>†>J–Û>ŒJ >ãTÛ>&S>¡È>,ò+>I»É>y]/>æwÊ>á´0>ãÊ>_C0>šË>¨S.>®ôÊ>ì/+>F³Ê>%#'>rPÊ>w">ØÉ>>x>\VÉ>‹o>8×È>¨>H1¸>ZfA>‘¹>VšD>Ÿtº>@ÜE>æëº>ÒqE>l»>M¡C>׺>l°@>/kº>ëä<>ÌÓ¹>Ç„8>G!¹>AÖ3>|c¸>Ñ/>Ϊ·>v¤*>ó«©>l>^>Ç,«>9(a>v¬>ÇLb>"‹¬>Nìa>T¬>»E`> ;¬>¿˜]>¡«>S%Z>gÖª>¢*V>’ì©>£èQ>w÷¨>„žM>9 ¨>ø‹I>Ù&>å€>¨¬ž>E,‚>£‘Ÿ>¸¬‚>lëŸ>A‚‚>_ÏŸ>ëÈ>SŸ>€œ€>㋞>×1~>P>1´z>ârœ>ä÷v>L›>ƒ4s>40š>ä¡o>»·’>­•>º.”>¾¾–>aü”>*—>~7•>’—>÷”>ƒk–>R”>?p•>_“>,”>6’>J¶’>í>B&‘>5›>W“>uWŽ>àŽ>:tŠ>Ú­>FÑ‹>ô­>„€Œ>ÜG®>ò™Œ>4,®>i5Œ>[³­>Æj‹>‰ï¬>ÂQŠ>Ôò«>‰>tϪ>Þ“‡>¢—©>´†>t]¨>sº„>f3§>\r„>¨âÆ>O²…>—uÇ>FC†>B¯Ç>Å=†>AœÇ>ǹ…>IÇ>ÑÏ„>ÂÆ>ÿ—ƒ>ÓÆ>²*‚>KÅ>  €>2tÄ>Í ~>ª›Ã>({>FÎÂ>ŸÇ€>f â>úï>æëâ>¾i‚>¦ ã>nM‚>Òÿâ>l³>öÔâ>´€>~â>êÏ~>ø5â>…Î{>¯Îá>•x>1`á>vTu>éðà>’=r>D‡à>Ï>?¨€>?>?íó€>?eQ€>?h”~>?1í{>?+Þx>?[˜u>?ýKr>?*o>?+ù€>Þ¯?‚> Š?,ƒ‚>>{?x^‚>€?+¾>…•?Aº€>A¸?4Ö~>å?$Ñ{>¨?–x>èO?¹Tu>‹‡?’=r>o¼?B"…>½Ž?2†>5E?s†>_(?z†>ß1?Êß…>Ž[?Öå„>Ÿ?D£ƒ>ö?j/‚>}Z?{¡€>çÅ?S!~>+2?({>î˜?§Î‹>“p)?¸ÍŒ>ó)?Û1>Ü(?Ô>æé(?A€Œ>R&)?–‹><ˆ)?ègŠ>§*?… ‰>F˜*?ž–‡>/4+?†>FÑ+?•º„>^f,?Ç”>x)5?ޝ•>² 4?¦ –>ôj4?ì•>·|4? i•>>Ê4? ””>àG5?0“>òé5?cD’>Û¤6?Nñ>ßl7?»›>T68?uWŽ>¡õ8?0ÖŸ>q??¾¡ >Ýé>?‘ñ >¤©>?8× >à¾>?àc >‹??ø¨Ÿ>À±??í·ž>Šs@?ì¡>ôRA?dxœ>BB?ÂL›>ß2C?40š>‡D?Âì>epH?n­>òµG?Þ°­>ÎlG?·š­>ì„G?`:­>‘îG?ž¬>ЙH?JÔ«>«vI?Äëª>XuJ?Üò©>×…K?@ø¨>_˜L?9 ¨>M?›Y»>j¦O?aÞ»>jÙN?j¼>ðˆN??¼>Œ£N?¶»>­O?<»>åÓO?õžº>ÅÆP?¯é¹>ÎÞQ?³'¹>p S?Ed¸>L8T?Ϊ·>âVU?ò`Ë>uU?o¼Ë>¢(T?YàË>ÈÒS?kÔË>(ïS?¥ Ë>kT?¡LË>4U?]àÊ>H7V?scÊ> bW?ÂÝÉ>ñ¡X?&WÉ>äY?8×È>ù[?£Ü>Ž[X?-ÒÜ>%xW?¨äÜ>âW?ÞÜ>`8½W?¢˜Ü>ŽX?î`Ü>o›Y?´ Ü>ÒZ?äÛÛ>y\?¯–Û>]m]?UÛ>6«^?6éî>ª‚Y?6éî>NœX?6éî>ÝAX?6éî>À_X?6éî>LâX?6éî>ÙµY?6éî>½ÆZ?6éî>q\?6éî>*R]?6éî>r¥^?6éî>mç_?·—?Ž[X?0€?%xW?óv?âW?z?`?6«^?½8 ?uU? ?¢(T?ù?ÈÒS?ÿ?(ïS?ã ?kT?åB ?4U?y ?77V?· ? bW?fú ?ñ¡X?´= ?äY?š} ?ù[?h<?j¦O?ú?jÙN?à?ðˆN?§è?Œ£N?4?­O?'K?åÓO?Ì™?ÅÆP?oô?ÎÞQ?mU?p S?·?L8T?à?âVU?U‡?epH?D2?òµG?Ø?ÎlG?Ú?ì„G?L?‘îG?Fš?ЙH?"ÿ?«vI?ds?XuJ?Èï?×…K?&m?_˜L?ä?M?.þ?q??g˜?Ýé>?mp?¤©>?ª}?à¾>?V·?‹??º?À±??P?Šs@?P ?ôRA?­ ?BB?åB!?ß2C?Ñ!?‡D?n…$?x)5?€$?¡ 4?óã#?ôj4?õò#?·|4?±4$?>Ê4?0Ÿ$?àG5?ž(%?òé5?Ç%?Û¤6? p&?ßl7?i'?T68?Œ½'?õ8?â)?“p)?Z‚(?ó)?HP(?Ü(?Ý`(?æé(?©(?R&)?2)?<ˆ)?Rµ)?–*?„c*?F˜*?ç+?/4+?ºÙ+?FÑ+?ü‹,?Mf,?X,?¬Ž?öÏ+?5E?š+?_(?.¬+?ß1?Qù+?}[?Kv,?þž?”-?ö?Ñ-?}Z?x˜.?çÅ?ò`/?+2?20?ݘ?±l.?ͯ?3ß-? Š?±§-?-{? º-?€?1 .?…•?Œ.?A¸?¹3/?å?íô/?¨?ÂÃ0?èO?”1?‹‡?ÑY2?^¼?0I5?©õÞ>±6??|)8?QlÝ>Ÿ9??’ë:?õòÛ>óÌ;??o€=?¸‘Ú>fj>??Ù??ÛPÙ>ÕÊ@??6æA?;8Ø>ÛÞB??ú˜C?×O×>B—D??âD?ñŸÖ>ÅäE??³²E?ˆ0Ö>¸F??”ûE?š Ö>ÚG??ò2?{Á¿>€¶5?‹Ä¼>ê]8?Íæ¹>ÏÙ:?8·>¤=?È´>ì??n¦²>=·@?Áâ°>éóA?¨Œ¯>”¼B?ÿ³®>´C?M1/?iÆ¢>üÈ1?]pž>‰E4?ªGš>B™6?Ac–>¦¶8?®Ù’>:?ßÁ>ü¼@=?$B‹>Âü=?’Š>l>>?»™‰>…%*?Œgˆ>,?Rׂ> Ä.?{>Öá0?hq>6Î2?óg>È|4?h`>*á5?ÁqY>ï6?BxT>š7?aQQ>ËÕ7?“7P>…í#?,b>ÿ%?/ÂT>û'?¨H>ZÖ)?R<>M†+?ç61> -?#º'>¹8.?Âß>%/?|í>„»/?Ì(>òï/?ñ×>*¨?:>0c?tê*>þ ?F`>ª™!?¤Ä>#?ºf>û>$?s,ï=‰D%?¦EÝ=¦ &?ݶÏ=ˆ&?C Ç=î³&? Ä=2t?º¡>Í?Iö>¹?bóñ=¥N?}Ô=Qh?…Ô¸=«^?} ¡=*?#]=ãÄ?‡ê|=»&? j=ñH?Žmc=zp ? €>p^ ?”jß=¢B ?óI½=7?Y=UÚ?†R€=„?áM=•?ÂÛ#={?å =b¾?ÒÁß<úÕ?Y«Ñ<®»?2èä= 6?ì¿=i«?Ztœ=??µƒv=}?Þ-:=kÔ?¾j=¤?š|³<]S?d°b<v?à<(‚?y.ã;6éî>­ˆÚ=6éî>$µ=6éî>‚"‘=6éî>Y_=6éî>ßâ!=6éî>ôÞØ<6éî>&b€<6éî>ûƒõ;6éî>g;6éî>ð:1[Ú>2èä=ZfÙ>ì¿=š{Ø>htœ=íŸ×>Ѓv=MØÖ>ù-:=·)Ö>Ùj=$™Õ>š|³<²+Õ>d°b<\æÔ>ƒà<ÎÔ>ù.ã;™ñÆ> €>­Å>”jß='MÃ>J½=ý¡Á>4Y=ÁÀ>†R€=Gʾ>áM=B±½>ÂÛ#=‡Ü¼> =¨U¼>Âß<š&¼>«Ñ<ê´>º¡>i7²>Iö>¡¯>bóñ="5­>}Ô=É«>“Ô¸=8©>Š ¡=t}§>>]=¦H¦>¢ê|=õ„¥>;j=«@¥>¨mc=‚¤>:>, ¡>tê*>oº>F`>8Ÿš>¤Ä>}Ì—>ºf>vT•>ù,ï=ZI“>¦EÝ=@½‘>c·Ï=@Â>] Ç=j> Ä=b÷•>,b>RÔ‘>/ÂT>ƒÜ>¨H>Ù%Š>R<>Òņ>ç61>RÒƒ>#º'>ú`>Âß>Ô>|í>ȶ|>Ì(>å{>ñ×>ƒ‡‰>Œgˆ>5Є>sׂ>sJ€>{>~x>hq>lp>Ióg>¸±i>h`>. d>ÁqY>«è_>BxT>¼<]>aQQ>©M\>“7P>çß~>‹Æ¢>é€t>]pž>÷Žj>ËGš>Ñ?a>Ac–>>ÊX>ÏÙ’>„dQ>ßÁ>åDK>ƒ2>è¡F>$B‹>²C>³Š>&«B>»™‰>œÜo>œÁ¿>ÕÊd>‹Ä¼>s-Z>Íæ¹>Þ=P>8·>‹6G>6È´>&Q?>n¦²>&È8>Áâ°>4Õ3>ÊŒ¯>…²0>ÿ³®>š/>^h®>Z€f>©õÞ>*ÿZ>QlÝ>öO>õòÛ>£E>Ú‘Ú>Ñ@<>ÛPÙ>þ 4>;8Ø>1A->øO×>q(> Ö> Ú$>ˆ0Ö>̶#>š Ö>Fc>?žW>?PqL>?@ûA>?y8>?k)0>?ÐG)>?$>?ßÄ >?o>?Z€f>+…?*ÿZ>ØI?ÓöO>…?£E>$·?Ñ@<>“W?þ 4>ãã?1A->X?q(>°?MÚ$>¼ç?̶#>3û?œÜo>C ?ÕÊd>»!?s-Z>™ #?Þ=P>ñc$?‹6G>ö›%?&Q?>ɬ&?&È8>ŸŽ'?4Õ3>¬9(?…²0>¦(?š/>âË(?çß~>Ëœ.?é€t>ÒÇ0?÷Žj>+Ü2?Ñ?a>_Î4?>ÊX>)“6?„dQ>8?åDK>Ïf9?è¡F>î^:?²C>7ü:?&«B>"3;?ƒ‡‰>:Ì;?5Є>W”>?sJ€>:?A?~x>f¾C?lp>>F?¸±i>&ÿG?. d>£I?«è_>ïáJ?¼<]>¨«K?©M\>òK?b÷•>5|G?RÔ‘>tÏJ?ƒÜ>ÖÿM?Ù%Š>ìûP?Òņ>F²S?RÒƒ>wV?ú`>X?Ô>¡„Y?ȶ|>ÍuZ?å{>ÊZ?‚¤> {Q?, ¡>cEU?oº>ïçX?8Ÿš>×N\?}Ì—>Qf_?vT•>rb?ZI“>KWd?@½‘>$ f?@Â>g?j>|g?ê´>’—Y?i7²>nÂ]?¡¯>”Áa?"5­>0e?É«>qåh?8©>rÞk?t}§>aTn?¦H¦>Y1p?õ„¥>‰_q?«@¥>.Éq?™ñÆ>ýŸ_?­Å>®d?'MÃ>ÅVh?ý¡Á>ÞTl?ÁÀ>¶õo?Gʾ>÷!s?B±½>LÂu?‡Ü¼>b¿w?¨U¼>óy?š&¼>¬ry?1[Ú>úbc?ZfÙ>mh?»{Ø>qql?íŸ×>È—p?MØÖ> ]t?·)Ö>L©w?$™Õ>dz?²+Õ>>u|?\æÔ>‡Ä}?>ÎÔ>¤9~?6éî>ê®d?6éî>Õ]i?6éî>©Ûm?6éî>Ôr?6éî>Òáu?6éî>9y?6éî>ëü{?6éî>ð~?6éî>h?6éî>Lß?®»?úbc? 6?mh?i«?qql???È—p? }? ]t?kÔ?L©w?¤?dz?]S?>u|?v?vÄ}?(‚?¤9~?zp ?ýŸ_?p^ ?®d?¢B ?ÅVh?7?ÞTl?UÚ?¶õo?„?æ!s?•?LÂu?{?b¿w?b¾?óy?úÕ?¬ry?Ct?’—Y?’Í?nÂ]?¹?”Áa?¥N?0e?Qh?qåh?«^?rÞk?*?aTn?ôÄ?Y1p?»&?‰_q?ñH?Éq?*¨? {Q?0c?cEU? ?ïçX?ª™!?×N\?#?Qf_?û>$?ab?‰D%?KWd?¦ &? f?ˆ&?ïg?î³&?|g?…í#?5|G?ÿ%?tÏJ?û'?ÖÿM?ZÖ)?ìûP?M†+?F²S? -?wV?¹8.?X?%/?¡„Y?„»/?ÍuZ?òï/?ÊZ?…%*?:Ì;?,?F”>? Ä.?:?A?Öá0?f¾C?6Î2?.F?È|4?&ÿG?;á5?£I?ï6?ïáJ?š7?¨«K?ËÕ7?òK?M1/?»œ.?üÈ1?ÒÇ0?‰E4?Ü2?R™6?_Î4?¦¶8?“6?:?8?ü>?"3;?ò2?2 ?€¶5?»!?ê]8?™ #?ÏÙ:?ñc$?¤=?å›%?ì??ɬ&?=·@?ŸŽ'?ùóA?›9(?¥¼B?¦(?´C?ÑË(?0I5?+…?|)8?ØI?’ë:?…?o€=?·?Ù??“W?6æA?ãã?ú˜C?X?*âD?÷¯?³²E?¼ç?”ûE?3û?ßnE?ÖTÖ>PsF??íðC?ê ×>cðD?? ¾A?ºMØ>(¶B????äºÙ>´@??»*==??åB9?ØÕÜ>´:??l—6?$CÞ>Qj7??ˆd4?Òoß>05??†æ2?<à>­3??N{B?ƒú®>Ø A?f‡°>Bî>?>в>š\ó9?0›¸>LÅ6? »>¤34?Ef¾>2?¯À>˜¦0?<Â>œ¿=?ðmŠ>Ig×k:?þ>¼8? ”>=e5?€f˜>¬Æ2?óÇœ>’^0?’Π> c.?C¤>Ý -?1_¦>tb7?ÉWR>R)6?~X>Ñ[4?Üž`>|+2?ój>¬É/?©0v>Üg-?ú¶€>†7+?á…>j)?É!Š>ä0(?£>Š/?•b>x.?éH>&ã,?t(>÷*?îÌ4>‡à(?ÉŽÉ&?£¬O>èÝ$?’\>I#?©0f>]6"?ým>B_&?ŒëÉ=}y%?/¦Ù=Ì&$?™Ôð=‹"?5}>Ì ?Î>‰ ?¸%>Lq?¡13>›?ÖÈ>>Å8?]¦F>ò?,p=ÅS?©a‰=ËK?”Þ¢=A ?¬ÑÁ=h®?€ã=Q?ö–>õ?¿>ú? Ï>ÞU?ït'>~¨?’ßì<-?V[=w?ýÃQ=,š?šê‰=Ê© ?{Ý­=g¹ ?BÐÑ={Ü ?Ùò=& ?’>« ?‰A>Âj?À#*6éî>öð‚;6éî>{ö\<6éî>8á<6éî>ø6=6éî>+å€=6éî>̾¦=6éî>džÉ=6éî>®+æ=6éî>,›ù=çüÔ>+$*<ê{Õ>ÕС<7Ö>Ùt =;×>}N=Ø>˜lŒ=ÄÙ>qɱ= ìÙ>›Ô=7§Ú>ídð=&Û>:Ê>‘¼>Çßì<`x½>‹[=;ä¾>ýÃQ=žÀ>¨ê‰=Ø~Â>{Ý­=À_Ä>BÐÑ=˜Æ>Ùò=Q…Ç>’>B|È>‰A>¨Ä¥>3,p=à*§>¶a‰=Ö:©>¢Þ¢= ¼«>ÇÑÁ=¼u®>€ã=o/±>9—>°³>¿>vÀµ> Ï>Ð&·>ït'>è‘>§ëÉ=“ß’>/¦Ù=Ó„•>™Ôð=M»˜>5}>d:œ>Î>|¹Ÿ>¸%>öï¢>ä13>W•¥>ÖÈ>>á`§>]¦F>z}>•b>~â€>,I> „>Ct(>mã‡>1Í4>^Œ>Ép?>£¬O>œ”>’\>_@—>©0f>Ôe™>@m> ^>ÉWR>Òÿb>~X>“5j>Ÿ`>+÷r>ój>k~|>©0v>´ƒ>ú¶€>€c‡>á…>aþŠ>É!Š>Åp>£>¨¦D>ðmŠ>öJ>Þ­Œ>{õQ>þ>)–[> ”>(f>€f˜>&Šp>Èœ>Ô*z>’Π>, >C¤>Ó¼ƒ>1_¦>ž·1>ƒú®>¸u7>f‡°>Ñë?>>в>n2J>d–µ> aU>R›¸>¨`> »>EÖj>Ef¾>¡Ls>¯À>x y><Â>Ÿé%>ÖTÖ>eá+>ê ×>ó¬4>ºMØ>ÙZ?>äºÙ>/úJ>oHÛ>…™V>ØÕÜ>kGa>$CÞ>ùj>ôoß>¿ p><à>™×!>?ã'>?yÌ0>?ž;>?(dG>?*S>?×û]>?Áäf>?tðl>?Ÿé%>•Õ?eá+>‹o?ó¬4>#Ù?ÙZ?>Ž"?/úJ>È[?…™V>•?kGa>nÞ?ùj>H?¿ p>üá?ž·1>¿‚(?¸u7>M¼'?ì?>á—&?n2J>Î4%? aU>h²#?¨`>ñ/"?EÖj>ÞÌ ?¡Ls>r¨?x y>â?¨¦D>É:?öJ>©9?{õQ>¹8?)–[>zý5?(f>ÀÌ3?&Šp>œ1?Ô*z>·˜/?, >_ð-?Ó¼ƒ>hÐ,?L^>jK?Òÿb>¡øI?“5j>IØG?+÷r>9CE?k~|>ÖsB?´ƒ>ƒ¤??€c‡>s=?aþŠ>ï:?Åp>¯}9?z}>['Z?~â€>ÆmX?A „>ãU?mã‡>ÄÌR?^Œ>ÎpO?p?>×L?œ”>›þH?_@—>ÖsF?Ôe™>AºD?è‘>‹Âf?“ß’>:Ëd?Ó„•>måa?M»˜>³`^?d:œ>zŒZ?|¹Ÿ>R¸V?öï¢>˜3S?W•¥>ËMP?á`§>iVN?¨Ä¥>;ýp?à*§>ÈÓn?÷:©>/¤k? ¼«>ÎÅg?¼u®>c?o/±>BZ_?°³>Ð{[?˜Àµ>8LX?Ð&·>Ä"V?‘¼>™x?x½>IJv?\ä¾>Åãr?žÀ>­Ân?ú~Â>RDj?À_Ä>øÅe?˜Æ>ߤa?s…Ç>\>^?B|È>žï[? ýÔ>iW}?ê{Õ>xñz?7Ö>­hw?]×>s?Ø>urn?æÙ>ÒÆi? ìÙ>-|e?7§Ú>bóa?9&Û>q_?6éî>ú~?6éî>Œ|?6éî>ˆ÷x?6éî>ˆžt?6éî>\ão?6éî> (k?6éî> Ïf?6éî>Š:c?6éî>šÌ`?Âj?iW}?A+?xñz?¼Í?­hw?\?s?~à?ern?Ôd?ÒÆi?0ó?-|e?«•?bóa?*V?q_?~¨?™x?-?IJv?w?Åãr?,š?­Ân?Ê© ?RDj?g¹ ?øÅe?{Ü ?ߤa?& ?\>^?« ?žï[?ò?;ýp?ÅS?ÈÓn?ËK?/¤k?A ?ÎÅg?h®?c?Q?2Z_?õ?Ð{[?ú?8LX?ÞU?Ä"V?B_&?‹Âf?}y%?:Ëd?Ì&$?måa?‹"?³`^?Ì ?zŒZ?‰ ?R¸V?Lq?‡3S?›?ËMP?Å8?iVN?Š/?['Z?x.?µmX?&ã,?ïâU?÷*?´ÌR?‡à(?ÎpO?ŽÉ&?×L?èÝ$?›þH?I#?ÖsF?]6"?0ºD?tb7?jK?R)6?¡øI?Ñ[4?8ØG?|+2?9CE?¬É/?ÖsB?Üg-?ƒ¤??†7+?s=?j)?ï:?ä0(?¯}9?œ¿=?É:?Ig?á—&?š\¤ú2??И1?Jîà>[2??L40?Þ¬á>Ôñ0??Ï»-?çþâ>q.??zâ)?J å>çŠ*??}[$?qè>€ñ$??èÙ?dì>ÒV??Ë??ñ>m??W³?ƒÚ÷>Cæ??6éî>?ý/?úòÂ>ˆe/?¯–Ã>s.?9 Å>â­+?kšÇ>èù'?šË>v¨"?SXÑ>o?ò$Ù>G?èOã>Ÿ?å(ð>l,?¾h§>ýÝ+??V¨>™œ*?çoª>ob(?•)®>:ê$?'÷³>³î?L¼>s*?ÔÇ>GX?î^Ö>Æ2?§é>s '?KYŽ>G'? Š>ýú%?¼;’>fô#?*—>«Ì ?ótž>D?ÿ%©>2?Ϊ·>n?F˜Ê>4ô?,ƒâ>ž·!?»Ep>NF!?~s>ÝE ?`y>é~?1~‚>4º?|d‹>À?o,˜>WZ?¥‡©>P ?=(À>µk?±¿Ü>ÍÎ?áFJ>øo?Y…M>x™?ìÜT>ç?ßâa>Ì?º,v>Àx?Á§‰>ô ?ap>^?Þ:·>à?ÝÑ×>:?üq+>N¹?/>?Æ7>pé?™gE>X?g¶[>ºƒ ?׿{>zþ?¥ “>g?Tü¯> 6ý>îÑÓ>2r ?Eƒ>6? ?ìQ>ÿË ?Ýï >—ÿ? 80>'Á?ÿH>Æ÷?‚9j>Š?zTŒ>a?™ª>ôÃø>EØÐ>Þ8?ô6>¨?à+ >iã?– >Jz?:#>­ÿ>vÂ;>Öý>+L_>ÅWû>¤Ç‡>ðø>=§>±úó>eýÎ>6éî>þH>6éî>IK>6éî>á]>6éî>yu>6éî>H‡7>6éî>ˆ[>6éî>*6†>6éî>œ¦>6éî>°YÎ>¯`Û>ô6>•Û>à+ >š Ü>– >×ÝÜ>:#>…%Þ>vÂ;>Îûß>+L_>§zâ>¤Ç‡>{»å>=§>Ü×é>eýÎ>(îÈ>Eƒ>TÉ>ìQ>m:Ê>Ýï >_ÓË> 80>@PÎ>ÿH>ßâÑ>‚9j> ½Ö>zTŒ>MÝ>™ª>xå>EØÐ>÷Ë·>üq+>Ð_¸>/>O®¹>Æ7>‹ÿ»>ÜgE>»›¿>g¶[>ËÄ>׿{>xÕË>¥ “>SÕ>Tü¯>€œà>îÑÓ>Ò4¨>áFJ>|ò¨>Y…M>{Ÿª>ìÜT>ž˜­>ßâa>K:²>º,v> á¸>Á§‰>KéÁ>ap>ѯÍ>Þ:·>æÜ>ÝÑ×>0cš>»Ep>ÐE›>~s>ÔF>`y>™Ô >1~‚>^¦>|d‹>Q®>o,˜>Þ¹>Ƈ©>n1Ç>=(À>ûØ>±¿Ü>†‘Ž>KYŽ>Þ“>+Š>”Ü‘>Þ;’>Ÿé•>*—>79œ>už>RI¥>ÿ%©>)˜±>Ϊ·>±£Á>F˜Ê>êÕ>,ƒâ>jú„>¾h§>r†>`V¨>[™ˆ>çoª>® >•)®>øý“>'÷³>'õ>L¼>…}«>õÇ>ÿ!½>î^Ö>mÓ>§é>£°{>óÂ>ú~>Ñ–Ã>¦µ>9 Å>§v†>kšÇ>¾Þ>9šË>¡˜>uXÑ>Mó¦>%Ù>Þǹ>èOã>-“Ñ>å(ð>dËr>$šà>–Au>Jîà>éÓz>Þ¬á>îZ‚>çþâ>x Š>k å>q•>“è>½¤>dì>Õ°·>?ñ>¾kÐ>ƒÚ÷>Eºo>?Ú8r>?‡Ýw>?Að€>?¼ˆ>?ï“>?ê$£>?jø¶>?æÐ>?dËr>î²?–Au>Ûˆ?éÓz>‘)?îZ‚>€?x Š>[y ?q•>Gÿ ?½¤>Îý ?Õ°·>|`?¾kÐ>¾?£°{>ƒ†?ú~>¨4?¦µ>c{?Év†>Ê2?¾Þ>ô2?¡˜>ÅS?Mó¦>‡m?Þǹ> X?-“Ñ>Žë?jú„>¡K,?r†>áÔ+?[™ˆ> È*?® >5ë(?þ“>l&?'õ>²Ù!?…}«>1?ÿ!½>‰Ð?mÓ>,~ ?†‘Ž>[Ó8?Þ“>û:8?”Ü‘>"â6?Ÿé•>k~4?79œ>‡Å0?RI¥>m+?)˜±>™*$?±£Á>ݳ?êÕ>j¾?0cš>‘îC?ÐE›>a8C?ÔF>(œA?»Ô >èÀ>?^¦>ÂM:?Q®>Éé3?Þ¹>.<+?n1Ç>áë?ûØ>' ?Ò4¨>HnM?|ò¨>ªžL?Ÿª>ÅÈJ?ž˜­>H‡G?K:²>ÑtB? á¸>,;?KéÁ>ÐG1?ѯÍ>‘b$?æÜ>?Ì·>#U?Ð_¸>>?T?O®¹>Ž:R?‹ÿ»>¦N?»›¿>fI?ËÄ> A?xÕË>­/6?SÕ>Ö(?€œà> ?(îÈ>/ßZ?TÉ>…ëY?Ž:Ê> ÄW?_ÓË>ØñS?@PÎ>þM?ßâÑ>ŸqE? ½Ö>ÃÕ9?MÝ>x³*?xå>Þ“?¯`Û>Cr^?•Û>u]?š Ü>Ú7[?øÝÜ>1?W?…%Þ>bQ?ðûß>õ,H?§zâ>.ta,?Ü×é>M?6éî>Á­_?6éî>.­^?6éî>ˆh\?6éî>¢bX?6éî>.R?6éî>I?6éî>ëä²õ,?6éî>(Ó?Þ8?Cr^?¸?u]?iã?Ú7[?Jz?1?W?­ÿ>bQ?Öý>õ,H?ÅWû>.ta,?±úó>M?2r ?/ßZ?F? ?…ëY?ÿË ? ÄW?—ÿ?ØñS?'Á?þM?Æ÷?ŸqE?Š?ÃÕ9?a?x³*?ôÃø>Þ“?:?#U?^¹?>?T??Ž:R?pé? ¦N?X?fI?ºƒ ? A?zþ?­/6?g?Ö(? 6ý> ?ÍÎ?HnM? p?ªžL?x™?ÅÈJ?ç?H‡G?Ì?ÑtB?Àx?,;?ô ?ÐG1?^?‘b$?à??ž·!?‘îC?NF!?a8C?ÝE ?(œA?é~?èÀ>?4º?ÂM:?À?Éé3?WZ?<+?P ?áë?µk?' ?s '?[Ó8?G'?ë:8?ýú%?â6?fô#?k~4?«Ì ?vÅ0?D?m+?2?™*$?n?ݳ?Eô?j¾?l,?¡K,?ýÝ+?ÐÔ+?™œ*? È*?ob(?5ë(?:ê$?l&?³î?²Ù!?s*?1?GX?‰Ð?Æ2?,~ ?ý/?r†?ˆe/?˜4?s.?c{?â­+?Ê2?èù'?ä2?v¨"?ÅS?o?vm?G? X?Ÿ?Žë?m62?î²?á˜1?Ûˆ?L40?‘)?Ï»-?€?zâ)?Jy ?}[$?7ÿ ?èÙ?Îý ?Ë?|`?W³?¾?¯A_>?·³_>j¤õ>ò\?>j¤õ>ÜJ?>?á">j¤õ>¡I">?Ï>j¤õ>£V>?üŽá=j¤õ>ã=?º=j¤õ>Aø»=?6[™=j¤õ>\‹›=? +=j¤õ>þ=?'äY=j¤õ>Y_=?Ç@C=j¤õ>‹H=?Ο;=j¤õ>hø@=?Oé`> –í>;Ž?> –í>>Y!> –í>Ù_> –í>pÝ= –í>ôï´= –í>žj“= –í>r= –í>L= –í>yä4= –í>:-= –í>p±b>Õç>ØÖ?>Õç>üU >Õç>BB>Õç>š]×=Õç>0]­=Õç>ꩊ=Õç>æÓ^=Õç>V‡7=Õç>æº=Õç>»=Õç>YÛd>1aä>ì.@>1aä>i>1aä>Ͱ>1aä>¤ýÏ=1aä>ú*¤=1aä>÷€=1aä>¿nG=1aä>Ø«=1aä>s=1aä>–‡û<1aä>H6g>Š:ã>›Ž@>Š:ã> Å>Š:ã>KÊý=Š:ã>S÷Ç=Š:ã>þ(š=Š:ã>ãïh=Š:ã>ù-=Š:ã>»ž=Š:ã>Í"Ô<Š:ã>,Ã<Š:ã>ói>1aä>î@>1aä>±n>1aä>ƒ3ø=1aä>ôð¿=1aä>'=1aä>ØÍQ=1aä>•ƒ=1aä>>#Ñ<1aä>´4œ<1aä>€Š<1aä>ܺk>Õç>¢FA>Õç>Û3>Õç>™ó=Õç>&‘¸=Õç>Ùô†=Õç> Œ<=Õç>Ý<ú<Õç> lŸ<Õç>/ŸQ<Õç>‘$-<Õç>ý‚m> –í>?A> –í>Û0> –í>kÕî= –í>z~²= –í>+Ä~= –í>p += –í>H´Ó< –í>aõl< –í>Éñù; –í>{7¯; –í>•¸n>j¤õ>DÀA>j¤õ>õ€>j¤õ>^öë=j¤õ>_®=j¤õ>ú|t=j¤õ>v)=j¤õ>—޹Ñd5ÿ‡;j¤õ>Šì:j¤õ>*o>?žÒA>?5@>?Õçê=?ÚÚ¬=?’³p=?É=?ì¯<?_ì <?‰y9;?ò:?•¸n>Ë-?DÀA>Ë-?õ€>Ë-?^öë=Ë-?_®=Ë-?ú|t=Ë-?v)=Ë-?—޹<Ë-?=e5<Ë-?ÿ‡;Ë-?Šì:Ë-?ý‚m>ð4 ??A>ð4 ?Û0>ð4 ?kÕî=ð4 ?z~²=ð4 ?+Ä~=ð4 ?p +=ð4 ?H´Ó<ð4 ?aõl<ð4 ? òù;ð4 ?¼7¯;ð4 ?ܺk> ?¢FA> ?Û3> ?™ó= ?&‘¸= ?Ùô†= ? Œ<= ?Ý<ú< ? lŸ< ?šŸQ< ?‘$-< ?ói>hÏ ?î@>hÏ ?±n>hÏ ?ƒ3ø=hÏ ?ôð¿=hÏ ?'=hÏ ?ØÍQ=hÏ ?•ƒ=hÏ ?>#Ñ»b?›Ž@>»b? Å>»b?KÊý=»b?S÷Ç=»b?þ(š=»b?ãïh=»b?8ù-=»b?»ž=»b?Í"Ô<»b?,Ã<»b?YÛd>hÏ ?ì.@>hÏ ?i>hÏ ?Ͱ>hÏ ?¤ýÏ=hÏ ?ú*¤=hÏ ?÷€=hÏ ?¿nG=hÏ ?Ø«=hÏ ?s=hÏ ?–‡û ?ØÖ?> ?üU > ?BB> ?š]×= ?0]­= ?ꩊ= ?æÓ^= ?q‡7= ?æº= ?»= ?Oé`>ð4 ?;Ž?>ð4 ?>Y!>ð4 ?Ù_>ð4 ?pÝ=ð4 ?ôï´=ð4 ?«j“=ð4 ?0r=ð4 ?L=ð4 ?yä4=ð4 ?:-=ð4 ?·³_>Ë-?ò\?>Ë-?á">Ë-?Ï>Ë-?üŽá=Ë-?šº=Ë-?6[™=Ë-? +=Ë-?'äY=Ë-?Ç@C=Ë-?éŸ;=Ë-?ý•?=j¤õ>?ÚD=?›¸K=j¤õ>ãÀP=? h`=j¤õ> e=?é~=j¤õ>Ö=?·w’=j¤õ>–1”=?ùê=j¤õ>X¬=?iÈ=j¤õ>ÒóÈ=?¢ê=j¤õ>øê=?oJ >j¤õ>s* >?w >j¤õ>o>?uJ1= –í>ë>= –í>MÊS= –í>1Ör= –í>2È= –í>²*§= –í> ÁÅ= –í>R¹é= –í>4¡ > –í>E!> –í>l9=Õç>çî)=Õç>e2A=Õç>u[b=Õç>‚à†=Õç>Ý¡=Õç>=OÂ=Õç>obè=Õç>!! >Õç>0 #>Õç>Û¤=1aä>¡}=1aä>¿ž*=1aä>£XN=1aä>ïû|=1aä>£l›=1aä>| ¾=1aä>'Âæ=1aä>”¼ >1aä>7%>1aä>ÖœÍ<Š:ã>Èí<Š:ã>ø =Š:ã>â‘8=Š:ã>¼j=Š:ã>¡j”=Š:ã>B“¹=Š:ã>¤üä=Š:ã>ªe >Š:ã>’'>Š:ã>÷ï•<1aä>¨•¸<1aä>böò<1aä>Ë"=1aä>c|X=1aä>¬h=1aä>µ=1aä>"7ã=1aä>¿ >1aä>³ì)>1aä>=E<Õç>S³‡<Õç>áÎÅ<Õç>5È=Õç>j·G=Õç>'ø†=Õç>F×°=Õç>Ù–á=Õç>2ª >Õç>œ,>Õç>“â; –í>”â>< –í>GŸ < –í>ñšü< –í>îç9= –í>ª= –í>ye­= –í>p?à= –í> * > –í>ß-> –í>”m`;j¤õ>À?¢c‡äˆ0=j¤õ>«"|=j¤õ>«=j¤õ>¨Vß=j¤õ>å€ >j¤õ>U/>j¤õ>o) ;?=è;?‘/|<?ûÿÝ<? -=?Ò{y=?¤2ª=?2ß=?à  >?¡†/>?”m`;Ë-?À?<Ë-?¢c‡<Ë-?=æ<Ë-?äˆ0=Ë-?«"|=Ë-?«=Ë-?¨Vß=Ë-?å€ >Ë-?˜/>Ë-?H“â;ð4 ?”â><ð4 ?GŸ <ð4 ?ñšü<ð4 ?îç9=ð4 ?ª=ð4 ?ye­=ð4 ?p?à=ð4 ? * >ð4 ?ß->ð4 ?=E< ?S³‡< ?áÎÅ< ?5È= ?j·G= ?'ø†= ?F×°= ?Ù–á= ?2ª > ?œ,> ?÷ï•hÏ ?³ì)>hÏ ?ÖœÍ<»b?Èí<»b?ø =»b?â‘8=»b?¼j=»b?¡j”=»b?B“¹=»b?¤üä=»b?ªe >»b?’'>»b?Û¤=hÏ ?¡}=hÏ ?¿ž*=hÏ ?£XN=hÏ ?ïû|=hÏ ?£l›=hÏ ?| ¾=hÏ ?'Âæ=hÏ ?”¼ >hÏ ?7%>hÏ ?l9= ?çî)= ?€2A= ?u[b= ?‚à†= ?Ý¡= ?=OÂ= ?obè= ?!! > ?0 #> ?uJ1=ð4 ?ë>=ð4 ?MÊS=ð4 ?1Ör=ð4 ?2È=ð4 ?²*§=ð4 ? ÁÅ=ð4 ?R¹é=ð4 ?4¡ >ð4 ?E!>ð4 ?ý•?=Ë-?›¸K=Ë-? h`=Ë-?é~=Ë-?·w’=Ë-?ùê=Ë-?iÈ=Ë-?¢ê=Ë-?oJ >Ë-?w >Ë-?;??;?‚6é>ĘD?œé>.uD?? ©K?y¯ê>/nK??»P?FEì>;rP??FT?2î>QõS??+ÁV?sJð>¥kV??ƒ£X?Ìbò>4IX??NdZ? Oô>!Z??¯z\?måõ>| \??Ì]_?ðøö>EÖ^??Ë„c?c^÷>¾Ùb??;?t}×>]ùD?Ï1Ø>¿HL?~Ú>å€Q? íÜ>G!U?!Yà>]©W?Œä>¯˜Y?Ìç>Ön[?.8ë>K«]?» î>…Í`?kóï>Ue?Ƨð>;?²ÔÊ>¥‡E?jÁË>&4M?DÎ>f¤R?÷Ñ>=dV?ótÖ>vÿX?mXÛ>ü[?Å;à>˜÷\?˹ä>'l_?Êlè>pëb?pïê>`h?'Üë>;?><Ã>v4F?ÇJÄ>ôQN?,)Ç>[T?ocË>hìW?²…Ð>õžZ?óÖ>®¸\?²Û>‘Ô^?VÔà>7a?™å>}e?ÿìç>O@k?©ûè>;?´À>ðF?æÍÁ> ‰O?÷ÊÄ>”‡U?Q2É>-—Y?;‹Î>c\? ]Ô>–^?ä.Ú>ŒÛ`?χß>aÞc?Jïã>ªIh?9ìæ>‚Èn?)è>£¬G?ÇJÄ>ÀP?,)Ç>¼W?ocË>B[?²…Ð>('^?óÖ>Šs`?²Û>—âb?VÔà>Œ/f?™å>×k?ÿìç>µPr?©ûè>uYH?jÁË>ãÝQ?DÎ>±jX?÷Ñ>.Ê\?ótÖ>—Æ_?mXÛ>N*b?Å;à>¿d?˹ä>œPh?Êlè>ã§m?pïê>£u?'Üë>ÍçH?Ï1Ø>KÉR?~Ú>2ŽY? íÜ>$ ^?!Yà>Áa?Œä>š“c?Ìç>RHf?.8ë>ˆj?» î>ßÅo?kóï>æ;x?Ƨð>gHI?œé>iS?y¯ê> TZ?FEì>Kè^?2î>âb?sJð>ƈd?Ìbò>ÛRg? Oô>$Bk?måõ>˜5q?ðøö>9 z?c^÷>;??ýkI??Û£S??íœZ??9_??hZb??ãd??µg??W²k??½q??E·z??;?®d ?gHI?ô1 ?iS?D¨ ? TZ?]Ý ?Kè^?âæ?âb?ÇÚ?ƈd?šÎ?ÛRg?0Ø?$Bk?9 ?˜5q?ˆƒ?9 z?ÎP?;?FA?ÍçH?ç?KÉR?Aò?2ŽY?z‰?$ ^?oÓ?Áa?©ö ?š“c?ô ?RHf?Øc ?ˆj?"û?ßÅo?K?æ;x?¬?;?§•?uYH?K?ãÝQ?øÝ?±jX?y?.Ê\?vÅ?—Æ_?ÊS?N*b? â?¿d?£ ?œPh?›É ?ã§m?Hˆ ?£u?Ü ?;?áa?£¬G?Ú?ÀP?jk?¼W?8N?B[?'½?('^?ò?Šs`?ö&?—âb?Õ•?Œ/f?£x ?×k?p ?µPr?,‚ ?;?ô¥?ðF? ? ‰O?„š?”‡U?Øf?-—Y?Rº?c\?pÑ?–^?Žè?ŒÛ`?<?aÞc?[?ªIh?Ò‰ ?‚Èn?ëü ?v4F?Ú?ôQN?jk?[T?8N?hìW?'½?õžZ?ò?¿¸\?ö&?‘Ô^?Õ•?7a?£x ?}e?p ?O@k?,‚ ?¥‡E?K?&4M?øÝ?f¤R?y?=dV?vÅ?vÿX?ÊS?ü[? â?˜÷\?£ ?'l_?›É ?pëb?Hˆ ?`h?Ü ?]ùD?ç?¿HL?Aò?å€Q?z‰?G!U?oÓ?]©W?©ö ?¯˜Y?ô ?Ön[?Øc ?K«]?"û?…Í`?K?Ue?¬?;?®d ?ĘD?ô1 ? ©K?D¨ ?»P?]Ý ?FT?âæ?+ÁV?ÇÚ?ƒ£X?šÎ?NdZ?0Ø?¯z\?9 ?Ì]_?ˆƒ?Ë„c?ÎP?j¼d?w÷>- d??Tãe?Eº÷>Z,e??»íf?;ø>&7f??Ðg?l•ø>Pg??|~h?;ù>…Òg??‘íh? ›ù>¶Jh??{i?<ú>’yh??±Þh?Svú>ÖRh??fIh?o¹ú>dÊg??Fg?5Òú>úÓf??£f?®Óð>ÔÓg?8Kñ>/Ýh?ûñ>µi?åÐò>;Qj?k¹ó>œ§j?ñ¡ô>÷­j?–wõ>%Zj?ž'ö>¢i?Ÿö>š{h?Ëö>?pi?ôì>Нj?·²ì>c·k?ª™í>íl?)²î>m?Lãï> 3m?ñ>Ð m?-ò>n‰l?ó>žk?İó>»Cj?pêó>×l?–=é>(n?Ãðé>.o?¾øê>óão?79ì>JDp? –í>Jp?éòî>áïo?ƒ3ð>”0o?~;ñ>ên?‰îñ>µml?˜0ò>­Šp?ØJè>þîq?}é> ór?zê>…”s?Qfë>FÑs?¦Ñì>ߦs?=î>s?ôŠï>¡r?ñð>3¦p?uXñ>‚Èn?Eñ>d>t?–=é>éµu?Ãðé>í·v?¾øê>(Ew?79ì>1^w? –í>°w?éòî>L6v?ƒ3ð>­öt?~;ñ>|Es?‰îñ>O#q?˜0ò>+¥w?ôì>b.y?·²ì>¡.z?ª™í>.©z?)²î>b¡z?Lãï>µz?ñ>]y?-ò>Ôw?ó>O®u?İó>8Ms?pêó>Trz?®Óð> |?8Kñ>Õ}?ûñ>ûs}?åÐò>@Q}?k¹ó>"¦|?ñ¡ô>6x{?–wõ> Íy?ž'ö>Gªw?Ÿö>ju?Ëö>ïX|?w÷>˜ú}?Eº÷>Hø~?;ø>Y?l•ø>ÿ#?;ù>-`~? ›ù>±}?<ú>‘H{?Svú>y?o¹ú>Kv?5Òú>= }??’±~??Þ®??æ€??öÏ????›¬}??kÔ{??‚y?? ½v??ïX|?kD?˜ú}?Ý"?Hø~?Rñ?Y?9µ?ÿ#?Òs?-`~?j2?±}?Qö?‘H{?ÖÄ?y?8£?Kv?æ–?Trz?–? |?dZ?Õ}?`?ûs}?—?@Q}?K#?"¦|?¯?6x{?$D? Íy?1ì?Gªw?l°?ju?xš?+¥w?õ ?b.y?¤¦ ?¡.z?+3 ?.©z?ì¦?b¡z?I?µz?¸u?]y?yé?Ôw?ÿu?O®u?ž'?8Ms?· ?d>t?$a ?Ùµu?ž ?í·v?¡ƒ ?(Ew?Tã ?1^w?ð4 ?°w?{†?L6v??æ?­öt?Ab?|Es?«?O#q?´ç?­Šp?”Ú ?þîq?A} ? ór?Ãó ?…”s?×L ?FÑs?— ?ߦs?rá?s?†:?¡r?±?3¦p?µS?‚Èn?]1?×l?$a ?(n?ž ?.o?¡ƒ ?óão?Tã ?JDp?ð4 ?Jp?Œ†?áïo??æ?”0o?Ab?ên?«?µml?´ç??pi?õ ?Нj?¤¦ ?t·k?+3 ?íl?ì¦?m?I? 3m?¸u?Ð m?yé?n‰l?ÿu?žk?ž'?»Cj?· ?£f?–?ÔÓg?dZ?/Ýh?`?µi?—?;Qj?K#?œ§j?¯?÷­j?$D?%Zj?1ì?¢i?}°?š{h?xš?j¼d?kD?Tãe?Ý"?»íf?Rñ?Ðg?9µ?|~h?Òs?‘íh?j2?{i?Qö?±Þh?ÖÄ?fIh?8£?Fg?æ–?”1þ>ææû>Ndþ>?¸”?Aù>½??I…? uø>´??Q‡?!tø> ¶??“7?[(ù>úa??ä2?Õ=ú>›V???k`û>Ê2??Ûûü>÷;ü>¦*ý>?|ü>t|ü>d:ü>?Øþ>¼Íû>FÒþ>?žý>‘ ø>Í?F]ó> ý?ïWñ>—ÿ?Vñ>o¼?S´ò>6Ë?JÏô>Q…ÿ>°÷>¬sü>®ø>6û>ú*ù>«þ>=×÷>«±ü>Ísô>d?­í>ê#?­¾ê>¯%?í»ê>×ö?³·ì>Ñ$?HÅï>îyþ>n÷ò>p™û>paõ>Ãú>Qö>ý>ö(ô>Ksû>©1ñ>™g?^è>øþ?M¿ä>›?ûä>í?ªFç>òD?s0ë>ý>IIï>Ösú>9aò>аù>‘Hó>ŠÉû>žÏð>¼êù>Oî>0?tíã>«•?Òoß> —?kß>'¥?%uâ>®aþ>÷!ç>TVû>Àì>Ð ù>²¸ï>^ø>šÌð> 5ú>ˆ×í>Tø>Øë>‡ý>à>‡Ýÿ>CæÚ>Âßÿ>&áÚ>àJþ>WÞ>¡Ûû>Xªã>'Où>u=é>tb÷>Þrí>‰Òö>ɬî>F\ø>ŒMë>jö>„Øé>©Oú>4ØÜ>h"ü>‚8×>$ü>¾2×>:èú>XÛ>óù>ÂÚà>ÿ÷> ûæ>„õ>ášë>ùõ>wóì>ñFö>>é>vÝó>F\è>Åö>¿|Ú>yø>•|Ô>§ø>lvÔ>®.÷>+…Ø>eßõ>ÛÃÞ>A€ô>Må>wó><ê>_)ó>C«ë>¨üó>еç>óuñ>Loç>ñôò>ÕÙ>|šó>‚ÈÒ>$›ó>ÂÒ>ñ*ó>vùÖ>R~ò>ŠvÝ>“Éñ>Aä>ÿ@ñ>=aé>ãñ>©Þê>Œ…ñ><Áæ>6éî>Åç>6éî>VƒØ>6éî>+2Ò>6éî>+Ò>6éî>qÖ>6éî>—Ý>6éî>Íäã>6éî>ßé>6éî>$˜ê>6éî>ôlæ>š\ì>Loç>œÝê>ÕÙ>ï7ê>‚ÈÒ>i7ê>ÂÒ>z§ê>vùÖ>Të>ŠvÝ>Ùì>Aä>m‘ì>=aé>ª¹ì>©Þê>àLì><Áæ>õé>F\è>Ý ç>¿|Ú>óÊå>•|Ô>æÉå>lvÔ>¾£æ>+…Ø>óç>ÛÃÞ>*Ré>Må>u[ê><ê>.©ê>C«ë>ÃÕé>еç>#ºç>„Øé>ä‚ã>4ØÜ>%°á>‚8×>q®á>¾2×>Sêâ>XÛ>xÐä>ÂÚà>ŽÍæ> ûæ>ýMè>ášë>s¾è>wóì>œ‹ç>>é>9³å>Øë>Kà>à>äôÝ>CæÚ>ªòÝ>&áÚ>­‡ß>WÞ>ìöá>Xªã>fƒä>u=é>pæ>Þrí>ç>ɬî>%vå>ŒMë>°çã>Oî>XrÝ>tíã>7§Ú>Òoß>v¤Ú>kß>@ˆÜ>%uâ>½pß>÷!ç>9|â>Àì>›Èä>²¸ï>Otå>šÌð>^ã>ˆ×í> _â>©1ñ>\Û>^è>|Ô×>M¿ä>WÑ×>ûä>øÙ>ªFç>ªHÝ>s0ë>jÀà>IIï>·^ã>9aò>"ä>‘Hó> â>žÏð>â á>Ísô>5 Ù>­í>˜ŠÕ>­¾ê>‡Õ>í»ê>½ä×>Ô·ì>ìˆÛ>HÅï>ŸXß>n÷ò>9â>paõ>kã>Qö>ðÀà>ö(ô>L4à>‘ ø>ó’×>F]ó>,×Ó>ïWñ>_ÓÓ>Vñ>ŽYÖ>S´ò>!<Ú>JÏô>MÞ>°÷>À^á>®ø>5Câ>ú*ù>âÌß>=×÷>ù ß>ææû>ü¨Ö>Aù>ÚÇÒ> uø>ëÃÒ>!tø>gcÕ>[(ù>ÆlÙ>Õ=ú>a¦Ý>k`û>²Öà>÷;ü>ðÃá>t|ü>µ4ß>¼Íû>nß>?‚XÖ>?fjÒ>?VfÒ>?xÕ>?W%Ù>?ølÝ>?Ƨà>?)˜á>?&ß>?ù ß> ?ü¨Ö>_@?ÚÇÒ>zÅ?ëÃÒ>ïÅ?gcÕ>Ók?ÆlÙ>á?a¦Ý>ÊO?²Öà>â?ðÃá>ÆÁ?µ4ß>"?L4à>7û?ó’×>]Q?,×Ó> T?_ÓÓ>óT?ŽYÖ>×¥?!<Ú>[˜?MÞ>(~?À^á>ø¨?5Câ>ƒj?âÌß>b?â á>Æ?5 Ù>x) ?˜ŠÕ>©  ?‡Õ> ¢ ?½ä×>'¤ ?ìˆÛ>\?ŸXß>I„?9â>HO?kã>Øô?ðÀà>…ë? _â>,g?\Û>Q¿ ?|Ô×>Z  ?WÑ×>¢ ?øÙ>«\ ?ªHÝ>Çg ?jÀà>[[?·^ã>dÏ?"ä>¸[? â>1˜?°çã>wØ?XrÝ>F ?7§Ú>H?v¤Ú>AJ?@ˆÜ>mÅ?½pß>o ?9|â> ü ?›Èä>§#?Otå>³™?^ã>< ?9³å>õ ?Kà>øý?äôÝ>ߌ?ªòÝ>m?­‡ß>xÔ?ìöá>Ô*?fƒä>Fa ?pæ>‘F ?ç>›©?%vå>:Y ?#ºç>¾ ?ä‚ã>æ“?%°á>¿c?q®á>¡f?Sêâ>Ô?xÐä>Ÿ’?ŽÍæ>{‚ ?ýMè>2 ?s¾è>D† ?œ‹ç>ò` ?õé>ÝÑ ?Ý ç> Á?óÊå>µÁ?æÉå>ÊÄ?¾£æ>j½?óç>ž?*Ré>qY ?u[ê>â ?.©ê>^* ?ÃÕé>% ?š\ì>ZH ?œÝê>•}?ï7ê>¿›?i7ê>õž?z§ê>Eƒ?Të>»D?Ùì>vß ?Ž‘ì>aO ?ª¹ì>¬ ?àLì>bŸ ?6éî>q ?6éî>U¾?6éî>ëæ?6éî>1ê?6éî>~Ç?6éî>5~?6éî>™ ?6éî>u ?6éî>î³ ?6éî>†É ?óuñ>ZH ?ñôò>•}?|šó>¿›?$›ó>õž?ñ*ó>Eƒ?R~ò>»D?“Éñ>vß ?ÿ@ñ>aO ?ãñ>¬ ?Œ…ñ>bŸ ?vÝó>ÝÑ ?°Åö> Á?yø>µÁ?§ø>ÊÄ?®.÷>j½?eßõ>ž?A€ô>qY ?wó>â ?_)ó>^* ?¨üó>% ?jö>¾ ?©Oú>æ“?h"ü>¿c?$ü>¡f?:èú>Ô?óù>Ÿ’?ÿ÷>{‚ ?„õ>2 ?ùõ>D† ?ñFö>ò` ?Tø>õ ?‡ý>øý?‡Ýÿ>ߌ?Âßÿ>m?àJþ>xÔ?¡Ûû>Ô*?'Où>Fa ?tb÷>‘F ?‰Òö>›©?F\ø>:Y ?¼êù>wØ?0?F ?«•?H? —?AJ?'¥?mÅ?®aþ>o ?TVû> ü ?Ð ù>§#?^ø>³™? 5ú>< ?Ksû>,g?™g?Q¿ ?øþ?Z  ?›?¢ ?í?«\ ?òD?Çg ?ý>[[?Ösú>dÏ?аù>¸[?ŠÉû>1˜?«±ü>Æ?d?x) ?ê#?©  ?¯%? ¢ ?×ö?¤ ?Ñ$?\?îyþ>I„?p™û>HO?Ãú>Øô?ý>…ë?žý>7û?Í?]Q? ý? T?—ÿ?óT?o¼?×¥?6Ë?[˜?Q…ÿ>(~?¬sü>ø¨?6û>ƒj?«þ>b?”1þ> ?¸”?_@?I…?zÅ?Q‡?ïÅ?“7?Ók?ä2?á??ÊO?Ûûü>â?|ü>ÆÁ?Øþ>"?m?:#ú>§‘??ÄÐ?ÉÊ÷>??± ?Ë÷ô>¢^ ??Sç?ãÝñ>«???Ù?¸°î>FE??—Ž?Ê£ë>Ü ??H§"?žêè>†7#??œÂ&?·¸æ>¯`'??=€)?ÙAå>k'*??‘*?g¹ä>*+??¸?ò™ô>ï;?[ ð>­Q ?œŒê>ç?ù„ä>8Ÿ?ÚWÞ>m?@iØ>´!?´Ó>Qø$?8ÙÎ>V›'?2Ì>÷(?èöÊ>CX?¸uï>@M?Z×è>à ?zßà> L?uØ>ç§? )Ï>OÍ?vÆ>ke?UÞ¾>3"?%­¸>°‘$?D‹´>Éw%?N ³>s?ÚÇê>R ?IJâ>„a?MØ># ?Ï×Ì>?2XÁ>O±?^M¶>ìÞ?´q¬>ê<?€¤>| ?3Ÿ>bM!?AE>>±þ>+¢æ>à€?«{Ü>ôM?_CÐ>…z ?ØÂ>Á?Yµ>MÜ?ëå§>¶†?*œ>îz? ž’>Žs?¡GŒ>0+?Hù‰>¤ü>ã>t²?E„×>zà?t–É>S^?ñJº>íñ ?# ª>Ja?1”›>Gr?…%Ž>Õê?eRƒ>ó?v2x>‘*?áðr>3ù>Y5à>“Qý>»|Ó>O#?Ø+Ä>Ü?§Z³>Æ¥?y ¢>7S ?•‘> · ?Ђ>8¤ ?kÒm>Fí? ð]>e?=(X>ö>VÞ>õÖø>N}Ð>xAü>’$À>ë?Q1®>Äì?BΛ>&Å?–%Š>Ïj?Ät>¾?÷[[>^¡?¥gJ>àó?:ÖŽò>¡»Ü>dô>äžÎ>;Æõ>ˆ¡½>´÷>‡ùª> ®ù>DÝ—>Í“û>Ü‚…>¦Eý>Aj>£þ>ñØO>eŒÿ>÷9>>3áÿ>¯Ð7>6éî>ÌEÜ>6éî>úÍ>6éî>Âü>6éî>Ý©>6éî>g–>6éî>§éƒ>6éî>b¡f>6éî>"áK>6éî>-:>6éî>fˆ3>–Cë>¡»Ü>Îé>äžÎ>R è>ˆ¡½>qæ>‡ùª>$ä>DÝ—>Ÿ>â>Ü‚…>çŒà>Aj>U/ß>ñØO>(FÞ>÷9>>9ñÝ>¯Ð7>ÝÑç>VÞ>vûä>N}Ð>óá>’$À>–ÐÝ>Q1®>äøÙ>BΛ>AHÖ>–%Š>ðüÒ>HÄt>2UÐ>÷[[>°Î>¥gJ>«êÍ>:ížä>Y5à>Ø€à>»|Ó>΋Û>Ø+Ä>aÖ>§Z³>à†Ð>y ¢>þ+Ë>%•‘>,dÆ>Ђ>ü‰Â>kÒm>à÷¿> ð]>h¿>=(X>ȵá>ã>ƒmÜ>f„×>wÖ>t–É>ÆÏ>ñJº>‘îÇ># ª>ùÁ>1”›>þíº>…%Ž>Âüµ>eRƒ>…°²>º2x>J}±>áðr>O!ß>+¢æ>ÌÐØ>«{Ü>‚6Ñ>_CÐ>aÝÈ>ØÂ>YPÀ>Yµ>ô¸>ëå§>"Ű>*œ>±Üª> ž’>pë¦>ÃGŒ>-|¥>jù‰>cìÜ>ÚÇê>ǹÕ>IJâ>bÍ>MØ>‹‹Ã>Ï×Ì>2̹>2XÁ>ïo°>^M¶>”¨>Öq¬>¸X¡>€¤>MÚœ>"3Ÿ>¨7›>AE>"Û>¸uï> 8Ó>Z×è>ͰÉ>zßà>X:¿>uØ>¿‚´> )Ï>Î7ª>vÆ>–¡>UÞ¾> ™>%­¸>-¯”>D‹´>Úâ’>N ³>ÍÙ>ò™ô>ŽZÑ>[ ð>4/Ç>œŒê>d¼>ù„ä>û“°>ÚWÞ>³—¥>@iØ>É›>´Ó>ëá“>8ÙÎ>¿›Ž>TÌ>}°Œ> ÷Ê>ƒøØ>:#ú>1Ð>ÉÊ÷>,ŸÅ>Ë÷ô>æº>Þñ>G ®>¸°î>_µ¢>Ê£ë>ýƒ˜>žêè>4M>ظæ>ÒŠ>ÙAå>kÓˆ>g¹ä>>¯Ø>?dÊÏ>?'Å>?7S¹>?àG­>?Ô¶¡>?^c—>?0>?¶ƒ‰>?_~‡>?ƒøØ>cî?1Ð>œ?,ŸÅ>„?æº>?G ®>¤§?_µ¢>. ?ýƒ˜>±Š ?4M>¥£ ?ÒŠ>_ ?kÓˆ>M£ ?ÍÙ>³?ŽZÑ>Óú?4/Ç>²¹ ?d¼>„½ ?û“°>Ô?³—¥>`Ë?É›>&q?ëá“>d“?¿›Ž>çÿ?}°Œ>{„?"Û>$E? 8Ó>S” ?ͰÉ>C?X:¿>Eð?¿‚´>{k?Î7ª>E¹?–¡>Ö ? ™>m©#?-¯”>^º%?Úâ’>Ùz&?cìÜ>œ ?ǹÕ>ÜÚ?bÍ>Y÷?‹‹Ã>”?2̹>çS?ïo°>QÙ$?”¨>&Ç)?¸X¡>ð¿-?MÚœ>€f0?¨7›>_]1?O!ß>ê® ?ÌÐØ>+Â?‚6Ñ>QÞ?aÝÈ>ó“?YPÀ>Ts%?ô¸> ,?"Ű>kñ1?±Üª>û°6?pë¦>/Ü9?-|¥>\;?ȵá>ót?ƒmÜ>Þ=?wÖ>Æ4?ÆÏ>ˆÚ"?‘îÇ>î¯*?ùÁ>ç52?þíº>>í8?Âüµ>ÍV>?…°²>bóA?J}±>ÈCC?ížä>Tå?Ø€à>¢A?΋Û>ê?aÖ>¬R&?à†Ð>Ãï.?þ+Ë>~57?,dÆ>ò—>?ü‰Â>e‹D?à÷¿>ýƒH?h¿>ñõI?ÝÑç>U÷?vûä>YÁ?óá>·í?–ÐÝ>Xç(?äøÙ>ß2?AHÖ>5í:?ðüÒ>ÿÎB?TUÐ>)I?°Î>fM?«êÍ>ñðN?–Cë>/¢?Îé>ް?R è><ƒ*?$ä>^4?Ÿ>â>’>=?çŒà>¹oE?U/ß>Ä L?(FÞ>‚qP?9ñÝ>Ô R?6éî>Ý?6éî>?6éî>ž!?6éî>A+?6éî>M¿4?6éî>- >?6éî>¨WF?6éî>¸M?6éî>5~Q?6éî>çS?ÖŽò>/¢?dô>ް?;Æõ><ƒ*? ®ù>^4?î“û>’>=?¦Eý>¹oE?£þ>Ä L?eŒÿ>‚qP?3áÿ>Ô R?ö>U÷?õÖø>YÁ?šAü>·í?ë?Xç(?Äì?ß2?&Å?5í:?Ïj?îÎB?¾?)I?^¡?fM?àó?ñðN?3ù>Tå?“Qý>¢A?O#?ê?Ü?¬R&?Æ¥?Ãï.?7S ?n57? · ?ò—>?8¤ ?e‹D?Fí?ýƒH?e?ñõI?¤ü>ót?t²?Í=?zà?Æ4?S^?ˆÚ"?íñ ?î¯*?Ja?ç52?Gr?>í8?Õê?ÍV>?ó?RóA?‘*?ÈCC?>±þ>ê® ?à€?+Â?ôM?QÞ?…z ?ó“?Á?Ts%?MÜ? ,?¶†?kñ1?îz?û°6?Žs?Ü9?0+?K;?s?œ ?R ?ÜÚ?„a?Y÷?# ?”??çS?O±?QÙ$?ìÞ?Ç)?ê<?ð¿-?| ?of0?bM!?_]1?CX?$E?@M?S” ?à ?C? L?Eð?ç§?{k?OÍ?E¹?ke?Ö ?3"?m©#?°‘$?^º%?Éw%?Ùz&?¸?³?ï;?Óú?­Q ?²¹ ?ç?„½ ?8Ÿ?Ô?m?`Ë?´!?&q?Qø$?d“?V›'?Öÿ?÷(?{„?m?cî?ÄÐ?œ?± ?„?Sç?ý?Ù?¤§?—Ž?. ?H§"?±Š ?œÂ&?”£ ?=€)?_ ?‘*?M£ ?ÆÙm¿ðï¼¾ª'Ǽåi¿F½¾ýþ-¾Vbr¿=|‘¾×¡¾uuu¿±h‘¾"/ºIy|¿ ÍW½‹ ¾¥¿ÆW½ýR¹ð'n¿ik¬>Èä¾®q¿Q¬>Fý;YR¿Ó@?ØÀݽ]T¿ß5?Àœ±<44•¾át?6Ýù»3¡’¾ìåt?yZ½Sº½îÜ?üy;·£½Ý?³tç;åvÀ>ªl?_X‚=NÃ>¤l? Ö~;ŸJ?D¼J?ÛŽÈ=E`?ý¯J?;d;cóý¸V?\d ?g€:"Ê`?€Nê>o&>„¦c?³0ê>[bÆ9k`¿—G½¾¡Ÿ¾º@i¿¨¥‘¾‚§˜¾ws¿( X½èÄž¾(Le¿¯–¬>4|”¾HK¿ _?”çq¾ YŒ¾±ìt?!ÅǽF>½ýÜ?ÐæM»¤¿¸>p´l?3¦ù=èN?·ÓJ? D>j·5?M&*?În>îK?ž’ ?̬…>ðKX?ëˆê>Nl>ÞÍP¿Kv½¾µã¾aZ¿]Í‘¾¸äß¾c¿!PX½é¾QËV¿T½¬>·‰Ú¾ô1?¿ú}?ß2·¾H¨‚¾¥òt?5z¾ R½Ý?(ؼ@…¬>¾l?15>Ù° ?«éJ?Â5>[*?_B*?kÒ®> Ý>?«¯ ?<èÃ>Ò{J?<Àê>ftÏ>‚<¿a“½¾þ÷¿I?F¿葾ü›¿Ë›N¿9†X½/¿ÑC¿·Õ¬>Qh ¿zƒ.¿§“?/Žð¾Ék¾Vöt?R5¾wô¼9Ý?Ô V¼X6œ>=Äl?ørh>âŒû>-øJ?››¸>ÑC?ýT*? á>±8-?"à ?õü>=Ï7?åê>)?Ǹ#¿»š½¾´v,¿jc-¿ò‘¾X¡-¿Êº4¿ä X½gÍ4¿µË*¿Çܬ>Ôû)¿[²¿÷œ?øç¿}ÀL¾F÷t?f¯W¾c˜à¼LÝ?vš¼²?ˆ>,Æl?o ‹>yÍÛ>"ýJ?ˆcÝ>JÝ?g[*?µ_?ïx?ßÉ ?’Ó?VÄ ? òê>vç ?ªô¿|‹½¾ÏC¿éT¿ꑾ±rF¿äw¿r X½3«N¿ìV¿_Ѭ>ûqB¿€2¿q˜?P(¿Ò(¾\õt?¸ót¾KǼQÝ?Ó‰°¼8b>±Ãl?>«Ê¶>øJ?àü>.rà>U*?‡°?1%ü>eà ?0„-?î×?{åê>iì7?‚†Í¾•g½¾<~V¿PGß¾¥Ð‘¾¼ˆZ¿ÌÓ辂ƒX½™c¿õ™Ü¾õ´¬>×EV¿¿õʾņ?â:¿|¡¾Éðt?ë@†¾þ©¼LÝ?MTͼFõ->½l?ÎY®>ë2>LéJ?5 ?”‡­>B*?ÑY*? Ã>O° ?z??fÏ>Áê>]’J?Eª‡¾º4½¾“d¿Ñþ—¾û©‘¾˜[i¿K’ž¾ûJX½ s¿k²–¾OŒ¬>°ñd¿|2޾_j?FÈG¿]÷©½Sêt?ØŽ¾Ž$ˆ¼<Ý? vå¼»nê=/³l?çº>¶?>DÓJ?Щ?§l>Ž&*?%ñ5?ßµ„>Y“ ?\L?Å >Šê>=[X?¾®ø½)½¾åàk¿æC¾î‘¾¬or¿u$ ¾œX½E}|¿¶{¾äb¬>Ùúm¿r%¾›J?…P¿¯è½Þãt?Õ꓾WE¼!Ý?Zø¼™ód=í¨l?tÁ>ÛŽ¿=Ú»J?áx?‘(í=ä*?ƒ=?±¤>Žt ?ßT??`>HOê>ÌÑ`?”/¨< ‡¾¾†Žm¿o¸Ñºgb’¾DPu¿Þ »ºXMa½·œ¿-/O»ôM«>©>q¿¦1µ¼~5?ÞT¿›ô;ÞËt?Е¾ÑÊ练Ý?쩽Äêz»âl?[lÃ>Û »šªJ?Eg?BU·º ÷)?o??û/€º d ?3¹V?nƹ³0ê>„¦c?Q~"> ˆË¾D]g¿ï?>À»¾íÉp¿ª>…F§½.|¿|µ>+½ž>\‘p¿rŒÝ=C¤ ? ÄT¿B`=M!t?Æ—¾”\ »yÜ?™‘½#ƒ½2\l?ÅëÁ>ÇðȽˆfJ? ¹?ó½ðÚ)?Ö=?©¾Æf ?oT?­&¾•Kê>âÊ`?Xá•>XÒà¾ÆrY¿D‘>!›´¾§Nd¿Õ®š>¡š ¾h‡q¿`—>–ƒ>Ö¨k¿ ¼z>Å£?"ET¿ŠØ=bçr?UU˜¾ƒ­‚;ôÓ?°A½GØþ½¾Ïk?cÔ¼>´šE¾‡ÇI?ü™?Áco¾‘)?l66?•½…¾Õc ?Î L?Ãm¾Î}ê>·NX?Š„Ö>Xóó¾cßE¿°ÚÔ>¹—˾meQ¿ ä>PûD¾ýØ_¿Õ‘ã>3I>w¾_¿©Å>FÜä>ä“N¿L">¨œq?”¾HHF<Å?4^&½†¹;¾é6k?uø²>MR‘¾ÿI?ôÇ ?€¡¯¾Â;)?¯Õ*?oľùZ ?Ž??ùxϾƒ«ê>¨€J?( ?¾þ¾j¿.¿wŠ ?uÚ¾?:¿™«?‡j¾eºH¿LF?dÚ>›%L¿ß}?1gÍ>Ê@¿Õ¯T>°Ëp?»~‰¾øž¯<µ?‡ó.½Â¥s¾Ìj?&¦£>7¼¾¿™H?7?`ã¾Õý(? 9?ûIý¾ºR ?/t-?G¿DÉê>Õ7?ÚH$?ëóþ¾ÑQ¿<ä%?µ­Ý¾ch ¿òô1?èjr¾ýÃ-¿_;4?ûT>Ïí1¿º#?ûýÃ> ª*¿‚ }>­Ðp?¸n¾U/õ<Ç«?V†'½¤!’¾±´j?úùŽ>È â¾ëwH?Gà>ãH¿áë(?ü¿?¹ ¿P ?Ó°?ùì ¿‚Óê>úÉ ?ÁßɈ¿JL;?·ŸË> ½ ¿±Ú‹>{žq?•m>¾1V=®?–?½y°¥¾Ôój?n¡k>BÔ¿'·H?B º>!£¿ )?j¾á>‰¿-¿ÃS ?Äxü>Oò7¿MÉê>0Ü?bçQ?såã¾aN¸¾ŠU?ŽÃ¾×ºË¾Õ¶b? Ð/¾[óܾ¹_?@“]>ŸÙÞ¾yýJ?ngà>è·Ø¾Bµ’>òÏr?Âi ¾‘ù=·¹?–-à¼Ó ´¾¤ok?ú2>¯+ ¿Ô=I?JóŽ>À#+¿àH)?…=®>ÐI?¿ÊZ ?_1Ã>r—J¿-«ê>I Ï>Åáa?ðо¡q¾…±f?ÂÖ­¾о¥ôr?Mƒô½èR•¾çk?¢TŠ>ޤ”¾¨ S?ôú>_’¾Y•>ïs?p®½jö=QÈ?רž¼Qa½¾Òûk?]…í=êß¿ZÝI?x×@>Û6L¿Ô_ ?‚Æ„>^X¿w|ê>–>cSk?•ÿÁ¾øïÚ½ yq?ºf›¾“ô ¾)…|?¨ª›½r.¾uKp?ŠY >KܾdgU?f?1¾”„•>Цt?a ½së =—Ô?§ƒM¼7T¾Hil?Êõd=¤ð¿V`J?Y’¿=3C=¿oÐ)?’)í=fT¿ã_ ?ú¥>BÓ`¿ªIê>C`>¢Ôm?Úë¼¾$Ïá<7Du?L®’¾¤Ÿ;Ú™?œ0d½ãC[; ]q?ó£ª>` >¹=U?ê ?¶=¦¼64•>pát?3—·ǹåi?F½¾ýþ->Vbr?=|‘¾×¡>Iy|? ÍW½‹ >ð'n?ik¬>Èä>YR?Ó@?ØÀÝ=3¡’>ìåt?yZ=Sº=îÜ?Ñy»åvÀ¾ªl?_X‚½ŸJ¿D¼J?èŽÈ½có<¿«*?øíò½èüS¿ñs ?¯¥¾"Ê`¿€Nê>o&¾k`?—G½¾¡Ÿ>º@i?¨¥‘¾‚§˜>ws?( X½èÄž>(Le?¯–¬>4|”>HK? _?”çq> YŒ>±ìt?!ÅÇ=F>=ýÜ?ûæM;¤¿¸¾p´l?3¦ù½èN¿·ÓJ? D¾j·5¿M&*?În¾îK¿ž’ ?̬…¾ðKX¿ëˆê>Nl¾ÞÍP?Kv½¾µã>aZ?]Í‘¾¸äß>c?!PX½é>QËV?T½¬>·‰Ú>ô1??ú}?ß2·>H¨‚>¥òt?5z> R=Ý?(Ø<@…¬¾¾l?15¾Ù° ¿«éJ?Â5¾[*¿_B*?kÒ®¾ Ý>¿«¯ ?<èþÒ{J¿<Àê>ftϾ‚Qh ?zƒ.?§“?/Žð>Ék>Vöt?R5>wô<9Ý?Ô V)¿Ç¸#?»š½¾´v,?jc-?ò‘¾X¡-?ʺ4?ä X½gÍ4?µË*?Çܬ>Ôû)?[²?÷œ?øç?}ÀL>F÷t?f¯W>c˜àvç ¿ªô?|‹½¾ÏC?éT?ꑾ±rF?äw?r X½3«N?ìV?_Ѭ>ûqB?€2?q˜?P(?Ò(>\õt?¸ót>KÇiì7¿‚†Í>•g½¾<~V?PGß>¥Ð‘¾¼ˆZ?ÌÓè>‚ƒX½™c?õ™Ü>õ´¬>×EV?¿õÊ>ņ?â:?|¡>Éðt?ë@†>þ©]’J¿Eª‡>º4½¾“d?Ñþ—>û©‘¾˜[i?K’ž>ûJX½ s?k²–>OŒ¬>°ñd?|2Ž>_j?FÈG?]÷©=Sêt?ØŽ>Ž$ˆ<<Ý? vå<»nê½/³l?纾¶?¾DÓJ?Щ¿§l¾Ž&*?%ñ5¿ßµ„¾Y“ ?\L¿Å ¾Šê>=[X¿¾®ø=)½¾åàk?æC>¬or?u$ >œX½E}|?¶{>äb¬>Ùúm?r%>›J?…P?¯è=Þãt?Õê“>WEÌÑ`¿ª'Ǽðï¼¾ÆÙm?O /º±h‘¾uuu?(R¹ÆW½¥?Äû;Q¬>®q?Àœ±<ß5?]T?aÝù»át?44•>³tç;Ý?·£=ŽÖ~;¤l?Nþe;ý¯J?E`¿É¹:jù)?‡m?¿õi€:\d ?ý¸V¿nÆ9³0ê>„¦c¿ýþ-¾F½¾åi?ס¾=|‘¾Vbr?‹ ¾ ÍW½Iy|?Èä¾ik¬>ð'n?ØÀݽÓ@?YR?yZ½ìåt?3¡’>Ñy;îÜ?Sº=_X‚=ªl?åvÀ¾èŽÈ=D¼J?ŸJ¿øíò=«*?có<¿¯¥>ñs ?èüS¿o&>€Nê>"Ê`¿¡Ÿ¾—G½¾k`?‚§˜¾¨¥‘¾º@i?èÄž¾( X½ws?4|”¾¯–¬>(Le?”çq¾ _?HK?!Åǽ±ìt? YŒ>ÐæM»ýÜ?F>=3¦ù=p´l?¤¿¸¾ D>·ÓJ?èN¿În>M&*?j·5¿Ì¬…>ž’ ?îK¿Nl>ëˆê>ðKX¿µã¾Kv½¾ÞÍP?¸äß¾]Í‘¾aZ?é¾!PX½c?·‰Ú¾T½¬>QËV?ß2·¾ú}?ô1??5z¾¥òt?H¨‚>(ؼÝ? R=15>¾l?@…¬¾Â5>«éJ?Ù° ¿kÒ®>_B*?[*¿<èÃ>«¯ ? Ý>¿ftÏ><Àê>Ò{J¿þ÷¿a“½¾‚ÑC?/Žð¾§“?zƒ.?R5¾Vöt?Ék>Ô V¼9Ý?wô<ørh>=Äl?X6œ¾››¸>-øJ?âŒû¾ á>ýT*?ÑC¿õü>"à ?±8-¿)?åê>=Ï7¿´v,¿»š½¾Ç¸#?X¡-¿ò‘¾jc-?gÍ4¿ä X½Êº4?Ôû)¿Çܬ>µË*?øç¿÷œ?[²?f¯W¾F÷t?}ÀL>vš¼LÝ?c˜à,Æl?²?ˆ¾ˆcÝ>"ýJ?yÍÛ¾µ_?g[*?JÝ¿’Ó?ßÉ ?ïx¿vç ? òê>VÄ ¿ÏC¿|‹½¾ªô?±rF¿ꑾéT?3«N¿r X½äw?ûqB¿_Ѭ>ìV?P(¿q˜?€2?¸ót¾\õt?Ò(>Ó‰°¼QÝ?KÇ<>±Ãl?8b¾àü>øJ?«Ê¶¾‡°?U*?.rà¾0„-?eà ?1%ü¾iì7?{åê>î׿<~V¿•g½¾‚†Í>¼ˆZ¿¥Ð‘¾PGß>™c¿‚ƒX½ÌÓè>×EV¿õ´¬>õ™Ü>â:¿Å†?¿õÊ>ë@†¾Éðt?|¡>MTͼLÝ?þ©<ÎY®>½l?Fõ-¾5 ?LéJ?ë2¾ÑY*?B*?”‡­¾z??O° ? þ]’J?Áê>fϾ“d¿º4½¾Eª‡>˜[i¿û©‘¾Ñþ—> s¿ûJX½K’ž>°ñd¿OŒ¬>k²–>FÈG¿_j?|2Ž>ØŽ¾Sêt?]÷©= vå¼<Ý?Ž$ˆ<çº>/³l?»nê½Ð©?DÓJ?¶?¾%ñ5?Ž&*?§l¾\L?Y“ ?ßµ„¾=[X?Šê>Å ¾åàk¿)½¾¾®ø=¬or¿î‘¾æC>E}|¿‚X½u$ >Ùúm¿äb¬>¶{>…P¿›J?r%>Õ꓾Þãt?¯è=Zø¼!Ý?WEí¨l?™ód½áx?Ú»J?莿½Š=?Ü*?†(í½ßT?Žt ?±¤¾ÌÑ`?HOê>?`¾qºd?†,Ú>YK>Ÿg?lÚ>>ÿ8 Ðe?jqÕ>º>9¸h?ÚTÕ>‰r›8œg?ñPÍ>¼3>ø‰j?þ4Í>Ѿæ8þj?XiÁ>ØÐ>“ m?„NÁ>´89æ4m?d+±>6Ó>î3p?Y±>9Ê69¢Ôp?EÔ›>€&>‡Þs?Ÿ½›>*M9)¾t?xk€>¤>†Ów?|X€>åàS9Ù‹x?àš;>þ > ¬{?;>T?9‡”{?&<Ë=׿>Þ¼~?æË=öü8¼|?å¼Õr >oæ?ÞÍä¼síu¸­\?-eÚ>ã¹>ø&]?c©Õ>“m>@â^?o‡Í>”‘> Ga?ÓÁ>")“>\Gd?Ç\±> #•>üÅg?Øœ>_n—>þ‹k?|‘€>$æ™>7o?ÉÓ;>Iœ>Ž%r?ÅzË=8*ž>Es?Ñ_å¼Íž>±N?{šÚ>€îÒ> O?ÞÕ> ôÓ>u¬P?ýºÍ>¾¢Õ> ëR?zÏÁ>óó×>y»U?q‹±>ïÙÚ>¢Y?5+œ>)8Þ> \?‹µ€>ìØá>ÿ_?G <>Õ\å>“¿b?Í·Ë=ñ$è>_Ïc?0›å¼Ÿ é>d;?í½Ú>·G?ƒù;?Ö>0ð?¾r=?NÝÍ>ß ?¸|??}ðÁ>Ú„ ?a B?ƒª±> d ?E?|Gœ>‘?>H?¦Í€>Vê?Y`K??/<>1?ÇáM?®áË=Dÿ? ÛN?ºå¼9¦? §#?1ÊÚ>‡ª#?^m$?B Ö>@t$?·%?éÍ>AÁ%?r'?ïûÁ>ÑŒ'?þ»)?µ±>Ì)?GW,?rQœ>^i,?q(/?*Ö€>/;/?Îæ1?]<<>·÷1? 4?¦ñË=-$4?rõ4?ºå¼ð4?ÃC?¾Ú>>;?Eè?;Ö>;ÿ;?.ú ?fÝÍ>;{=?‡u ?ÅðÁ>Ї??¦Q ?ꪱ>ÀB?¨|?Hœ>$E?ÇÔ?O΀>ºMH? ?Ô0<>cnK?sò?æË= ëM?€¬?†šå¼€ÖN?½åÒ>€šÚ>ðN?·âÓ>9ÞÕ>uO?ÞˆÕ>3»Í>³P?Ò×>àÏÁ>¤óR?è°Ú>Œ±>ÔÅU? Þ>,œ>HY?M©á>³¶€>™\?Û1å>o <>Õ `?è>MÀË=¹Æb?d.é>`å¼èËc?°>#eÚ>6\?ñZ> ©Õ>õ)]?Tx‘>݇Í>®æ^?Г>WžÁ>ñLa?"÷”>‘]±>`Nd?õ<—>œ>ÙÍg?³™>“€>”k?ñœ>ø×;>â>o?Ö ž>r…Ë=b*r?ÖÛž>~å¼¹Bs?ú7>‘,Ú>5»d?òÞ>³qÕ>…Ñe?ú>4QÍ>Nžg?Y…>ÞiÁ>àj?òw>",±>b8m?À>6Õ›>ŒØp?­:>Ël€>1Ât?Rª>ž;>yx?ð§>EË=é–{?Å‘ >vÑä¼d»|?ËX¸lÚ>Ÿg? ¤›¸ÚTÕ>9¸h?aëæ¸þ4Í>ø‰j?SP¹„NÁ>“ m?â6¹Y±>î3p?˜CM¹Ÿ½›>‡Þs?3ûS¹|X€>†Ów? m?¹;> ¬{?ZMü¸æË=Þ¼~?!Šu8ÞÍä¼oæ?YK¾†,Ú>qºd?¾uqÕ>Ðe?¼3¾ñPÍ>œg?Üо:iÁ>j?6Ó¾d+±>æ4m?€&¾EÔ›>¢Ôp?¤¾xk€>)¾t?þ ¾àš;>Ù‹x?׿¾&<Ë=‡”{?Õr ¾å¼¼|?ã¹¾-eÚ>­\?“m¾c©Õ>ø&]?”‘¾o‡Í>@â^?")“¾ÓÁ> Ga? #•¾Ç\±>\Gd?_n—¾Øœ>üÅg?$晾|‘€>þ‹k?Iœ¾ÉÓ;>7o?8*ž¾ÅzË=Ž%r?Íž¾Ñ_å¼Es?€îÒ¾{šÚ>±N? ôÓ¾ÞÕ> O?Ú¢Õ¾øºÍ>p¬P?ô×¾sÏÁ>ëR?ïÙÚ¾q‹±>y»U?%8Þ¾T+œ>žY?ìØá¾‹µ€> \?Õ\å¾G <>ÿ_?ñ$辿·Ë=“¿b?Ÿ é¾0›å¼_Ïc?·G¿í½Ú>d;?0ð¿Ö>ƒù;?ß ¿NÝÍ>¾r=?Ú„ ¿}ðÁ>¸|??,d ¿|ª±>Y B?‘¿|Gœ>E?V꿦̀>>H?1¿7/<>Q`K?Dÿ¿®áË=ÇáM?9¦¿ºå¼ ÛN?‡ª#¿1ÊÚ> §#?@t$¿B Ö>^m$?AÁ%¿éÍ>·%?ÑŒ'¿ïûÁ>r'?Ì)¿µ±>þ»)?^i,¿rQœ>GW,?/;/¿*Ö€>q(/?·÷1¿]<<>Îæ1?-$4¿¦ñË= 4?ð4¿ºå¼rõ4?>;¿¾Ú>ÃC?;ÿ;¿;Ö>Eè?;{=¿fÝÍ>.ú ?Õ‡?¿¨ðÁ>Šu ?ÀB¿êª±>¦Q ?$E¿Hœ>¨|?ºMH¿O΀>ÇÔ?cnK¿Ô0<> ? ëM¿æË=sò?€ÖN¿†šå¼€¬?ðN¿€šÚ>½åÒ>uO¿9ÞÕ>·âÓ>³P¿3»Í>ÞˆÕ>¤óR¿àÏÁ>Ò×>ÔÅU¿Œ±>è°Ú>HY¿,œ> Þ>™\¿¶¶€>1©á>Ó `¿± <>Ù1å>¹Æb¿ZÀË=è>èËc¿`å¼d.é>6\¿#eÚ>°>*]¿ˆ©Õ>ÖZ>³æ^¿Ò‡Í>Lx‘>ñLa¿WžÁ>Г>`Nd¿‘]±>"÷”>ÙÍg¿œ>õ<—>”k¿“€>³™>â>o¿ø×;>ñœ>b*r¿r…Ë=Ö ž>¹Bs¿~å¼ÖÛž>5»d¿‘,Ú>ú7>…Ñe¿³qÕ>òÞ>Nžg¿4QÍ>ú>àj¿ÞiÁ>Y…>g8m¿,±>öw>ŒØp¿6Õ›>À>1Ât¿Ël€>­:>yx¿ž;>Rª>é–{¿EË=ð§>d»|¿vÑä¼Å‘ >Ÿg¿lÚ>)^¸9¸h¿ÚTÕ>¤¢›¸ø‰j¿þ4Í>aëæ¸“ m¿„NÁ>SP¹î3p¿Y±>¦â6¹‡Þs¿Ÿ½›>ABM¹†Ów¿|X€>xûS¹ ¬{¿;> m?¹Þ¼~¿æË=ÑLü¸oæ¿ÞÍä¼×ˆu8qºd¿†,Ú>YK¾ Ðe¿jqÕ>º¾œg¿ñPÍ>¼3¾j¿:iÁ>Üоæ4m¿d+±>6Ó¾¢Ôp¿EÔ›>€&¾)¾t¿xk€>¤¾Ù‹x¿àš;>þ ¾‡”{¿&<Ë=׿¾¼|¿å¼Õr ¾­\¿-eÚ>ã¹¾ø&]¿c©Õ>“m¾@â^¿o‡Í>”‘¾ Ga¿ÓÁ>")“¾\Gd¿Ç\±> #•¾üÅg¿Øœ>_n—¾þ‹k¿|‘€>$晾7o¿ÉÓ;>Iœ¾Ž%r¿ÅzË=8*ž¾Es¿Ñ_å¼Íž¾±N¿{šÚ>€îÒ¾ O¿ÞÕ> ôÓ¾u¬P¿ýºÍ>¾¢Õ¾ ëR¿zÏÁ>óó×¾y»U¿q‹±>ïÙÚ¾¢Y¿5+œ>)8Þ¾ \¿‹µ€>ìØá¾ÿ_¿G <>Õ\従¿b¿²·Ë=ñ$è¾_Ïc¿0›å¼Ÿ é¾d;¿í½Ú>·G¿ƒù;¿Ö>0ð¿¾r=¿NÝÍ>ß ¿¸|?¿}ðÁ>Ú„ ¿Y B¿|ª±>,d ¿E¿|Gœ>‘¿>H¿¦Í€>Vê¿]`K¿þ.<>1¿ÍáM¿{áË=;ÿ¿ ÛN¿ºå¼9¦¿ §#¿1ÊÚ>‡ª#¿^m$¿B Ö>@t$¿·%¿éÍ>AÁ%¿r'¿ïûÁ>ÑŒ'¿¼)¿cµ±>!Ì)¿KW,¿UQœ>bi,¿q(/¿*Ö€>/;/¿Îæ1¿]<<>·÷1¿ 4¿eñË=.$4¿rõ4¿ºå¼ð4¿ÃC¿¾Ú>>;¿Eè¿;Ö>;ÿ;¿.ú ¿fÝÍ>;{=¿Šu ¿¨ðÁ>Õ‡?¿¦Q ¿êª±>ÀB¿¨|¿Hœ>$E¿ÇÔ¿O΀>ºMH¿ ¿Ô0<>cnK¿sò¿ÔåË= ëM¿€¬¿»šå¼€ÖN¿½åÒ¾€šÚ>ðN¿·âÓ¾9ÞÕ>uO¿ÄˆÕ¾:»Í> ³P¿Ò×¾àÏÁ>¤óR¿í°Ú¾û‹±>ÙÅU¿ Þ¾,œ>HY¿1©á¾¶¶€>™\¿Û1å¾o <>Õ `¿ƒè¾3ÀË=»Æb¿d.é¾`å¼èËc¿o°¾&eÚ>9\¿ÖZ¾ˆ©Õ>*]¿Lx‘¾Ò‡Í>³æ^¿Ð“¾WžÁ>ñLa¿"÷”¾‘]±>`Nd¿õ<—¾œ>ÙÍg¿³™¾“€>”k¿ñœ¾ø×;>â>o¿Ö ž¾r…Ë=b*r¿ÖÛž¾~å¼¹Bs¿ñ7¾ƒ,Ú>8»d¿éÞ¾¦qÕ>†Ñe¿ú¾4QÍ>Nžg¿Y…¾ÞiÁ>àj¿öw¾,±>g8m¿ À¾Õ›>‘Øp¿­:¾Ël€>1Ât¿Rª¾ž;>yx¿ð§¾EË=é–{¿Å‘ ¾vÑä¼d»|¿)^8lÚ>Ÿg¿¬ ›8ÀTÕ>@¸h¿aëæ8þ4Í>ø‰j¿SP9„NÁ>“ m¿“á69Y±>î3p¿ABM9Ÿ½›>‡Þs¿3ûS9|X€>†Ów¿ m?9;> ¬{¿ZMü8æË=Þ¼~¿!Šu¸ÞÍä¼oæ¿]K>l,Ú>xºd¿º>jqÕ> Ðe¿¼3>ñPÍ>œg¿ÜÐ>:iÁ>j¿6Ó>d+±>æ4m¿ƒ&>&Ô›>¦Ôp¿¤>xk€>)¾t¿þ >àš;>Ù‹x¿×æ>&<Ë=‡”{¿Õr >å¼¼|¿ã¹>-eÚ>­\¿—m>H©Õ>ÿ&]¿”‘>o‡Í>@â^¿")“>ÓÁ> Ga¿ #•>Ç\±>\Gd¿_n—>Øœ>üÅg¿$æ™>|‘€>þ‹k¿Iœ>ÉÓ;>7o¿8*ž>ÅzË=Ž%r¿Íž>Ñ_å¼Es¿€îÒ>{šÚ>±N¿ ôÓ>ÞÕ> O¿¾¢Õ>ýºÍ>u¬P¿øó×>]ÏÁ>ëR¿ôÙÚ>S‹±>~»U¿)8Þ>5+œ>¢Y¿ìØá>‹µ€> \¿Õ\å>G <>ÿ_¿ñ$è>¿·Ë=“¿b¿Ÿ é>0›å¼_Ïc¿·G?í½Ú>d;¿0ð?Ö>ƒù;¿ß ?NÝÍ>¾r=¿Ú„ ?}ðÁ>¸|?¿ d ?ƒª±>a B¿‘?|Gœ>E¿Vê?¦Í€>>H¿1?þ.<>]`K¿;ÿ?{áË=ÍáM¿9¦?ºå¼ ÛN¿‡ª#?1ÊÚ> §#¿@t$?B Ö>^m$¿AÁ%?éÍ>·%¿ÑŒ'?ïûÁ>r'¿!Ì)?cµ±>¼)¿bi,?UQœ>KW,¿/;/?*Ö€>q(/¿·÷1?]<<>Îæ1¿.$4?eñË= 4¿ð4?ºå¼rõ4¿>;?¾Ú>ÃC¿;ÿ;?;Ö>Eè¿;{=?fÝÍ>.ú ¿Õ‡??¨ðÁ>Šu ¿ÀB?ꪱ>¦Q ¿$E?Hœ>¨|¿ºMH?O΀>ÇÔ¿cnK?Ô0<> ¿ ëM?áåË=sò¿€ÖN?†šå¼€¬¿ðN?€šÚ>½åÒ¾uO?9ÞÕ>·âÓ¾³P?3»Í>ÞˆÕ¾¤óR?àÏÁ>Ò×¾ÔÅU?Œ±>è°Ú¾HY?,œ> Þ¾™\?¶¶€>1©á¾Õ `?o <>Û1å¾»Æb?3ÀË=ƒè¾èËc?`å¼d.é¾6\?#eÚ>°¾*]?ˆ©Õ>ÖZ¾³æ^?Ò‡Í>Lx‘¾ñLa?WžÁ>Г¾`Nd?‘]±>"÷”¾ÙÍg?œ>õ<—¾”k?“€>³™¾â>o?ø×;>ñœ¾b*r?r…Ë=Ö ž¾¹Bs?~å¼ÖÛž¾5»d?‘,Ú>ú7¾…Ñe?³qÕ>òÞ¾Nžg?4QÍ>ú¾àj?ÞiÁ>Y…¾g8m?,±>öw¾‘Øp?Õ›> À¾1Ât?Ël€>­:¾yx?ž;>Rª¾é–{?EË=ð§¾d»|?vÑä¼Å‘ ¾Úw?]-Y¾©R>%/z?”Y¾àºh?߭˾fZ>eåj?è˾,ùuºÝT?‡˜ ¿†ò>$GW?mˆ ¿>†º¡„A?ÀË$¿ZØó=žôC?ù»$¿))qºÆƒ1?%R6¿Öñß=Ä3?C6¿:B;ºD&?9à@¿V=Ò=ìc(?­Ò@¿–™ä¹ì¤!?ÙD¿£5Í=ˆº#?óËD¿Ð¸bO(?à?¿8?×=Ý*?ô>¿µd;:xtJ?Þ3¿|D>Ä#M?%¿_);W3p?|Ÿ¾«ë>„Fs?!iŸ¾F¬:œÛm?ŸnY¾_ûš>ÒN_?—ã˾ÖH‘>²žL?Q· ¿Ð…>)6:?ê$¿à#r>ƒÉ*?n6¿l8^>bñ?šú@¿‡\P> z?ŽòD¿ ÷J>=Ý!?©?¿àT>Æ®B?EQ¿b€>g??§Ÿ¾—>ÎÀ^?Ñ©Y¾ü¥ã>ÜQ?Ž̾a{Õ>З??/Ô ¿û~Ã>ÆT.?½%¿#ã±>1Þ?G‰6¿Þ3£>P²?uA¿Ìó˜>?Œ E¿oâ”>Çr?Õ5?¿Ñh›>Ð 6?m¿ä»>4ZX?÷П¾61Þ>ÍGJ?"ÐY¾°&?ðå=?s6̾ ?Gù-?Nç ¿»Èü>œH?´%¿þå>¢"?›6¿øÒ>câ?ù#A¿«Å>@ ?{E¿ÊVÀ>Äl ?…F?¿Ô£È>7%?ª¿_Nò>I`D?e퟾€s?öú0?°ÛY¾Å0?!)&?YA̾)Ò%?—:?Ìí ¿äÛ?—~ ?, %¿†) ?Éõý>¡6¿íqý>ä¾í>ª)A¿iní>Gç>÷E¿ºùæ>kTð>=L?¿GØð>Œc?C†¿R?¸+?IøŸ¾Ô1,?}d?jËY¾J?dh ?5̾¢=?ì¡ý>ûæ ¿qª-?ùÀæ>œ%¿½?Ó>›6¿›ë?AÆ>ù#A¿ÕÀ?^À>‚E¿Š ?½ È>~F?¿×£ ?‚+ð>+€¿Íý%?Éç?)ðŸ¾|ÅD?à.ä>7¡Y¾Pž^?^XÖ>ï̾õåP?]oÄ>˜Ó ¿¼Z??溲>%¿½.?Û£>G‰6¿e³?âY™>~A¿%˜?°ê”>‹ E¿æ|?”Áš>Æ5?¿¥?˜†¹>ôm¿»6?÷ûÜ>NÖŸ¾X¨X? Ž›> cY¾KÄm?¸5’><à˾î(_?È †>¶ ¿uL?ós>ãé$¿‰:?Ÿ_>‘n6¿+¬*?­7Q>ªú@¿lß?’K>•òD¿”x?¹·R>…?¿¸ú!?@ï{>WR¿ùC?hQ–>s®Ÿ¾ïSg?þ‚>è#Y¾Kw?-F>ûªË¾8îg?` >æ— ¿‰T?‚›÷=ƒË$¿¸qA?vÝâ=3R6¿ât1?tÔ=Gà@¿å:&?ÐYÍ=ÙD¿4¤!?‘SÔ=Ê?¿O^(?çûû=Â4¿g©J?Í;>¶‚Ÿ¾žMp?‘:”Y¾%/z?Jóu:Ë˾kåj?†:mˆ ¿$GW?v$q:ù»$¿žôC?IA;:C6¿Ä3?¦˜ä9­Ò@¿ìc(?‘˜8óËD¿ˆº#?3h;ºô>¿Ý*?6)»%¿Ä#M?Ÿ¬º!iŸ¾„Fs?©R¾]-Y¾Úw?fZ¾ß­Ë¾h?†ò¾‡˜ ¿ÝT?ZØó½ÀË$¿¡„A?Öñß½%R6¿Æƒ1?J=Ò½?à@¿úC&?£5ͽÙD¿ì¤!?8?×½à?¿bO(?D¾Ô3¿€tJ?«ë¾|Ÿ¾W3p?_ûš¾ŸnY¾œÛm?ÖH‘¾—ã˾ÒN_?Ð…¾Q· ¿²žL?à#r¾ê$¿)6:?l8^¾n6¿ƒÉ*?‡\P¾šú@¿bñ? ÷J¾ŽòD¿ z?àT¾©?¿=Ý!?b€¾EQ¿Æ®B?—¾?§Ÿ¾g?ü¥ã¾Ñ©Y¾ÎÀ^?a{Õ¾Ž̾ÜQ?û~þ/Ô ¿Ð—??#ã±¾½%¿ÆT.?Þ3£¾G‰6¿1Þ?Ìó˜¾uA¿P²?o┾Œ E¿?Ñh›¾Õ5?¿Çr?þ㻾m¿Ø 6?61Þ¾÷П¾4ZX?°&¿"ÐY¾ÍGJ? ¿s6̾ðå=?»Èü¾Nç ¿Gù-?þå¾­%¿§H?øÒ¾›6¿¢"?«Å¾ù#A¿câ?¬VÀ¾E¿C ?¸£È¾‹F?¿Èl ?_Nò¾ª¿7%?€s¿e퟾I`D?Å0¿°ÛY¾öú0?)Ò%¿YA̾!)&?äÛ¿Ìí ¿—:?†) ¿, %¿—~ ?íqý¾¡6¿Éõý>iní¾ª)A¿ä¾í>ºùæ¾÷E¿Gç>GØð¾=L?¿kTð>R¿C†¿Œc?Ô1,¿IøŸ¾¸+?J¿jËY¾}d?œ=¿5̾ph ?qª-¿ûæ ¿ì¡ý>½¿œ%¿ùÀæ>›ë¿›6¿Ó>ÎÀ¿ÿ#A¿6Æ>Š ¿‚E¿^À>×£ ¿~F?¿½ È>Óý%¿ €¿Œ+ð>|ÅD¿)ðŸ¾Éç?Pž^¿7¡Y¾à.ä>õåP¿ï̾^XÖ>¼Z?¿˜Ó ¿]oÄ>½.¿%¿æº²>e³¿G‰6¿Û£>%˜¿~A¿âY™>æ|¿‹ E¿°ê”>¥¿Æ5?¿”Áš>»6¿ôm¿˜†¹>X¨X¿NÖŸ¾÷ûÜ>KÄm¿ cY¾ Ž›>é(_¿7à˾Ö5’>uL¿¶ ¿È †>‰:¿ãé$¿ós>+¬*¿‘n6¿ÂŸ_>lß¿ªú@¿­7Q>”x¿•òD¿’K>®ú!¿Œ?¿Â·R>ùC¿WR¿@ï{>ïSg¿s®Ÿ¾hQ–>Kw¿è#Y¾þ‚>8îg¿ûªË¾-F>øˆT¿ò— ¿[ >®qA¿Ë$¿v›÷=ât1¿3R6¿vÝâ=å:&¿Gà@¿tÔ=3¤!¿ÙD¿VZÍ=O^(¿Ê?¿‘SÔ=g©J¿Â4¿çûû=žMp¿¶‚Ÿ¾Í;>%/z¿”Y¾o:kåj¿Ë˾óu:$GW¿mˆ ¿†:žôC¿ù»$¿f%q:Ä3¿C6¿‚D;:ìc(¿­Ò@¿ ä9ˆº#¿óËD¿¨8Ý*¿ô>¿wh;ºÄ#M¿%¿à)»„Fs¿!iŸ¾I¬ºÚw¿]-Y¾©R¾h¿ß­Ë¾fZ¾ÞT¿ˆ˜ ¿Dò¾¡„A¿ÀË$¿ZØó½Æƒ1¿%R6¿Öñß½úC&¿?à@¿J=Ò½ì¤!¿ÙD¿£5ͽbO(¿à?¿8?×½€tJ¿Ô3¿D¾W3p¿|Ÿ¾«ë¾œÛm¿ŸnY¾_ûš¾ÒN_¿—ã˾ÖH‘¾²žL¿Q· ¿Ð…¾)6:¿ê$¿à#r¾{É*¿•n6¿b8^¾bñ¿šú@¿‡\P¾z¿”òD¿÷J¾=Ý!¿©?¿àT¾Æ®B¿EQ¿b€¾g¿?§Ÿ¾—¾ÎÀ^¿Ñ©Y¾ü¥ã¾ÜQ¿Ž̾a{վЗ?¿/Ô ¿û~þÆT.¿½%¿#ã±¾1Þ¿G‰6¿Þ3£¾P²¿uA¿Ì󘾿Œ E¿o┾Çr¿Õ5?¿Ñh›¾Ø 6¿m¿þ㻾4ZX¿÷П¾61Þ¾ÍGJ¿"ÐY¾°&¿ðå=¿s6̾ ¿Gù-¿Nç ¿»Èü¾œH¿´%¿þå¾¢"¿›6¿øÒ¾câ¿ù#A¿«Å¾C ¿E¿¬VÀ¾Èl ¿‹F?¿¸£È¾7%¿ª¿_Nò¾I`D¿e퟾€s¿öú0¿°ÛY¾Å0¿!)&¿YA̾)Ò%¿—:¿Ìí ¿äÛ¿—~ ¿, %¿†) ¿Éõý¾¡6¿íqý¾ä¾í¾ª)A¿iní¾Gç¾÷E¿ºùæ¾kTð¾=L?¿GØð¾’c¿I†¿R¿¸+¿IøŸ¾Ô1,¿}d¿jËY¾J¿ph ¿5̾œ=¿ì¡ý¾ûæ ¿qª-¿ùÀ澜%¿½¿Ó¾›6¿›ë¿<ƾ$A¿ÁÀ¿^À¾‚E¿Š ¿½ Ⱦ~F?¿×£ ¿‚+ð¾+€¿Íý%¿Éç¿)ðŸ¾|ÅD¿à.ä¾7¡Y¾Pž^¿^XÖ¾ï̾õåP¿xoľ”Ó ¿·Z?¿Ýº²¾—%¿µ.¿Û£¾G‰6¿e³¿âY™¾~A¿%˜¿°ê”¾‹ E¿æ|¿”Áš¾Æ5?¿¥¿˜†¹¾ôm¿»6¿÷ûܾNÖŸ¾X¨X¿ Ž›¾ cY¾KÄm¿¸5’¾<à˾î(_¿È †¾¶ ¿uL¿ós¾ãé$¿‰:¿ÂŸ_¾‘n6¿+¬*¿­7Q¾ªú@¿lß¿’K¾•òD¿”x¿Â·R¾Œ?¿®ú!¿@ï{¾WR¿ùC¿hQ–¾s®Ÿ¾ïSg¿þ‚¾è#Y¾Kw¿-F¾ûªË¾8îg¿` ¾æ— ¿‰T¿v›÷½Ë$¿®qA¿vÝâ½3R6¿ât1¿tÔ½Gà@¿å:&¿ÐYͽÙD¿4¤!¿‘SÔ½Ê?¿O^(¿çûû½Â4¿g©J¿Í;¾¶‚Ÿ¾žMp¿‘º”Y¾%/z¿JóuºË˾kåj¿†ºmˆ ¿$GW¿Ì$qºù»$¿žôC¿A;ºC6¿Ä3¿¦˜ä¹­Ò@¿ìc(¿‘˜¸óËD¿ˆº#¿Íh;:ô>¿Ý*¿a);%¿Ä#M¿Ÿ¬:!iŸ¾„Fs¿©R>]-Y¾Úw¿fZ>߭˾h¿†ò>‡˜ ¿ÝT¿ZØó=ÀË$¿¡„A¿Öñß=%R6¿Æƒ1¿J=Ò=?à@¿úC&¿£5Í=ÙD¿ì¤!¿8?×=à?¿bO(¿|D>Þ3¿xtJ¿«ë>|Ÿ¾W3p¿_ûš>ŸnY¾œÛm¿ÖH‘>—ã˾ÒN_¿Ð…>Q· ¿²žL¿à#r>ê$¿)6:¿b8^>•n6¿{É*¿‡\P>šú@¿bñ¿ ÷J>ŽòD¿ z¿àT>©?¿=Ý!¿b€>EQ¿Æ®B¿—>?§Ÿ¾g¿ü¥ã>Ñ©Y¾ÎÀ^¿a{Õ>Ž̾ÜQ¿û~Ã>/Ô ¿Ð—?¿#ã±>½%¿ÆT.¿Þ3£>G‰6¿1Þ¿Ìó˜>uA¿P²¿oâ”>Œ E¿¿Ñh›>Õ5?¿Çr¿ä»>m¿Ð 6¿61Þ>÷П¾4ZX¿°&?"ÐY¾ÍGJ¿ ?s6̾ðå=¿»Èü>Nç ¿Gù-¿þå>´%¿œH¿øÒ>›6¿¢"¿«Å>ù#A¿câ¿ÊVÀ>{E¿@ ¿Ô£È>…F?¿Äl ¿_Nò>ª¿7%¿€s?e퟾I`D¿Å0?°ÛY¾öú0¿)Ò%?YA̾!)&¿äÛ?Ìí ¿—:¿†) ?, %¿—~ ¿íqý>¡6¿Éõý¾iní>ª)A¿ä¾í¾ºùæ>÷E¿Gç¾GØð>=L?¿kTð¾R?C†¿Œc¿Ô1,?IøŸ¾¸+¿J?jËY¾}d¿œ=?5̾ph ¿qª-?ûæ ¿ì¡ý¾½?œ%¿ùÀæ¾›ë?›6¿Ó¾ÎÀ?ÿ#A¿6Æ¾Š ?‚E¿^À¾×£ ?~F?¿½ ȾÍý%?+€¿‚+ð¾|ÅD?)ðŸ¾Éç¿Pž^?7¡Y¾à.ä¾õåP?ï̾^XÖ¾¼Z??˜Ó ¿]oľµ.?—%¿Ýº²¾e³?G‰6¿Û£¾%˜?~A¿âY™¾æ|?‹ E¿°ê”¾¥?Æ5?¿”Áš¾»6?ôm¿˜†¹¾X¨X?NÖŸ¾÷ûܾKÄm? cY¾ Ž›¾î(_?<à˾¸5’¾uL?¶ ¿È †¾‰:?ãé$¿ós¾+¬*?‘n6¿ÂŸ_¾lß?ªú@¿­7Q¾”x?•òD¿’K¾¸ú!?…?¿¹·R¾ùC?WR¿@ï{¾ïSg?s®Ÿ¾hQ–¾Kw?è#Y¾þ‚¾8îg?ûªË¾-F¾‰T?æ— ¿` ¾¸qA?ƒË$¿‚›÷½ât1?3R6¿vÝâ½å:&?Gà@¿tÔ½4¤!?ÙD¿ÐYͽO^(?Ê?¿‘SÔ½g©J?Â4¿çûû½žMp?¶‚Ÿ¾Í;¾‡j?5ÂÁ¾ ?>’ûl?Å¥Á¾ëP»(>+? ~<¿-Ñ=•F-?2q<¿X‹„»CÎ>íÈi¿‡Dz=‚·Ð>(Ãi¿[€6»ÇÈr>‰‡x¿o›=(´u>b…x¿ÇÇ̺¤>¹H}¿™*³<øØ>éG}¿{mºÃ„´=û~¿}Ð[šä$?—<¿úçR>ØÆ>ÑÔi¿j6ý=É­i>ü‹x¿Q(•=Š >fJ}¿CÝ4=Ǻ­=¢û~¿&òÝ<¬¤P=G¢¿ôü„V~?Š®<¿ûÇ›>Vº>(ài¿v&;>äØZ>?x¿`Ü=ÞŠ>ÿK}¿p…= ¯¢=?ü~¿\è#=¥fC=¢¿‹Ä<Ø<~ã¿ [X<³ZO<…ù¿ùËÈ;Ð??»8¾ù ?Ìg ?+¾<¿†ùÉ>«©>Éçi¿(¶r>×F>“x¿µÛ>Ôð=M}¿§ ­=ÎÌ“=§ü~¿©xT=/‹1=¦¢¿ÃåþŽÃ<¿–ó>·”>êi¿¿’>\2.>+”x¿Êô+>íöÒ=xM}¿›]Ð=y=Îü~¿9¹=”=³¢¿Ém=¾¬<ã¿+©<–:&<‰ù¿<"ð ?M5¾è??DPÍ>Q¾<¿Q0 ?mIw>/èi¿ðY§>k>J“x¿öD>ݰ=#M}¿Æ¦î=ª'X=­ü~¿×r’=à=§¢¿èÂ/=¸Ë<‹ã¿ âÁ<ÆŠ <‰ù¿¿7H¾€œR?kzŸ>Ô®<¿Ñ‹?•7@>éài¿°°¸>É â=‹x¿¤_Y>}Ùˆ=L}¿ø¯>Õü'=Jü~¿ùŸ¡=ÂñÉ<ƒ¢¿\B=áÏ_<€ã¿¸Ö&éÁ¾ a?æØZ>d—<¿T>$?Y>ÁÕi¿»¨Å>òC›=_Œx¿å§h>åñ;=ŠJ}¿ò >µºæ<±û~¿þ¬=qÇŠå<ËÆ˜;ƒù¿:Y<‹>£ºÁ¾Åði?¸ˆá=`~<¿Òé*?͈=¶Éi¿ÌÍ>ÏZ =Û‡x¿Br>{òÁ<ØH}¿BÃ>''n< û~¿F#´=ª„<¢¿òEX=19 ;^ã¿ÃÏî<üÿ$;ù¿<&c<¿O;Å¥Á¾’ûl?-‹„;2q<¿•F-?[€6;(Ãi¿‚·Ð>sÈÌ:b…x¿(´u>§}m:éG}¿øØ>¥S:±ú~¿u³¶=MGÂ9ï¡¿¸_[=V¤†9Tã¿ÚNò<¬¢9{ù¿5þf< ?¾5ÂÁ¾‡j?-ѽ ~<¿(>+?lDz½íÈi¿CÎ>o›½‰‡x¿ÇÈr>™*³¼¹H}¿¤>}Ð[¼û~¿Ð„´=@m¼ ¢¿‹ÆX=Öv»\ã¿‚ïC(•½ü‹x¿É­i>(Ý4½fJ}¿Š >&òݼ¢û~¿Õº­=¾ü„¼G¢¿Ç¤P=¼mã¿y—æ`ܽ?x¿äØZ>d…½L}¿ßŠ>\è#½?ü~¿ ¯¢=Vļ¢¿¥fC= ZX¼~ã¿QØ<£ËÈ»…ù¿³ZO<ù ¿»8¾Ð??†ùɾ+¾<¿Ìg ?(¶r¾Éçi¿«©>µÛ¾“x¿×F>š ­½M}¿Ôð=ŽxT½§ü~¿ÎÌ“=Ãåþ¼¦¢¿/‹1=ÿmŒ¼‰ã¿žZÄ<˜h¼‰ù¿»ý<¿’¾êi¿·”>Êô+¾+”x¿\2.>›]нxM}¿íöÒ=¹½Îü~¿y=®m½³¢¿®=Ñ*©¼ã¿¾¬<꼉ù¿–:&<è?¿M5¾"ð ?Q0 ¿Q¾<¿DPÍ>ðY§¾/èi¿mIw>öD¾J“x¿k>Ʀî½#M}¿Ý°=Ér’½­ü~¿ª'X=èÂ/½§¢¿¨à= âÁ¼‹ã¿¸ËÑ‹¿Ô®<¿kzŸ>®°¸¾åài¿Ô7@>d_Y¾x¿Ì â=ø¯¾L}¿}Ùˆ=ùŸ¡½Jü~¿ðü'=\B½ƒ¢¿÷ñÉ<ƒÖ¼€ã¿áÏ_<·ªJ¼‡ù¿´ÐÚ; a¿&éÁ¾Ý1”>R>$¿b—<¿%ÙZ>»¨Å¾ÁÕi¿Y>å§h¾_Œx¿òC›=ò ¾ŠJ}¿åñ;=þ¬½±û~¿êºæ<«O½M¢¿qÇŠå¼oã¿)<ÏŒY¼ƒù¿!ǘ;Åði¿£ºÁ¾‹>Òé*¿`~<¿¸ˆá=ÿË;´Éi¿Ùˆ=Br¾Û‡x¿éZ =BþØH}¿±òÁ+¿ ~<¿-ѽCξíÈi¿lDz½ÇÈr¾‰‡x¿o›½¤¾¹H}¿c*³¼Ã„´½û~¿Ð[¼pÆX½ ¢¿ m¼‚ï¼\ã¿€v»üÔd¼}ù¿¬ùºgia¿¶ñÁ¾Y瑾šä$¿—<¿úçR¾ÙƾÓÔi¿å5ý½É­i¾ü‹x¿C(•½Š ¾fJ}¿(Ý4½Çº­½¢û~¿ðñݼ¬¤P½G¢¿¾ü„¼C—æ¼mã¿Ý¼ Ï\¼ù¿a…»ô&S¿Ð¾xÃÖ¾M~¿‘®<¿òÇ›¾Vº¾(ài¿v&;¾äØZ¾?x¿`ܽߊ¾L}¿d…½ ¯¢½?ü~¿Bè#½¥fC½¢¿Vļؼ~ã¿ ZX¼³ZO¼…ù¿7ËÈ»Ð?¿»8¾ù ¿Ìg ¿+¾<¿†ùɾ«©¾Éçi¿(¶r¾×F¾“x¿µÛ¾Ôð½M}¿š ­½ÎÌ“½§ü~¿ŽxT½/‹1½¦¢¿åþ¼jZļ‹ã¿ËmŒ¼Pý<¼‰ù¿ch¼¢ç'¿AA¾H'¿¥ö¾ŽÃ<¿–ó¾·”¾êi¿¿’¾\2.¾+”x¿Êô+¾íöÒ½xM}¿›]нy½Îü~¿¹½”½³¢¿®m½¾¬¼ã¿Ñ*©¼–:&¼‰ù¿¿¼"ð ¿M5¾è?¿DP;Q¾<¿Q0 ¿gIw¾)èi¿ Z§¾k¾J“x¿öD¾Ý°½#M}¿Æ¦î½ª'X½­ü~¿Ér’½à½§¢¿ÍÂ/½¸Ë¼‹ã¿êáÁ¼»Š ¼‰ù¿T7¼uæØ¾H¾€œR¿kzŸ¾Ô®<¿Ñ‹¿•7@¾éài¿°°¸¾Ì â½x¿d_Y¾}Ùˆ½L}¿ø¯¾Õü'½Jü~¿ìŸ¡½Âñɼƒ¢¿\B½áÏ_¼€ã¿ƒÖ¼^ÐÚ»‡ù¿·ªJ¼Ý1”¾&éÁ¾ a¿æØZ¾d—<¿T>$¿Y¾ÁÕi¿»¨Å¾òC›½_Œx¿å§h¾åñ;½ŠJ}¿ò ¾µºæ¼±û~¿úý¬½qÇŠ¼M¢¿§«O½þ¼oã¿>å¼ËƘ»ƒù¿ÏŒY¼‹¾£ºÁ¾Åði¿¸ˆá½`~<¿Òé*¿Ýˆ½»Éi¿äË;ÏZ ½Û‡x¿Br¾{òÁ¼ØH}¿Bþ''n¼ û~¿9#´½ª„¼¢¿ØEX½19 »^㿎Ïî¼üÿ$»ù¿Ñ%c¼ÉO»Ò¥Á¾ûl¿‹„»2q<¿•F-¿[€6»(Ãi¿‚·Ð¾ÉÈ̺b…x¿(´u¾§}mºéG}¿øØ¾¶Sº±ú~¿h³¶½oG¹ï¡¿¸_[½4¤†¹T㿤Nò¼Ù«¢¹{ù¿Éýf¼ ?>5ÂÁ¾‡j¿-Ñ= ~<¿(>+¿‡Dz=íÈi¿Cξo›=‰‡x¿ÇÈr¾™*³<¹H}¿¤¾}Ð[<û~¿Ã„´½5m< ¢¿pÆX½Öv;\ã¿‚ï¼Zù:}ù¿üÔd¼Yç‘>¶ñÁ¾gia¿úçR>—<¿šä$¿j6ý=ÑÔi¿ØÆ¾E(•=Œx¿Š­i¾(Ý4=fJ}¿Š ¾&òÝ<¢û~¿Çº­½¾ü„оô&S¿òÇ›>‘®<¿M~¿v&;>(ài¿Vº¾`Ü=?x¿äØZ¾d…=L}¿ßо\è#=?ü~¿ ¯¢½VÄ<¢¿ŠfC½ ZX<~ã¿ؼ£ËÈ;…ù¿HZO¼ù ?»8¾Ð?¿†ùÉ>+¾<¿Ìg ¿(¶r>Éçi¿«©¾µÛ>“x¿×F¾§ ­=M}¿Ôð½ŽxT=§ü~¿ÁÌ“½Ãåþ<¦¢¿‹1½nŒ<‹ã¿jZļ˜h<‰ù¿Pý<¼H'?AA¾¢ç'¿–ó>ŽÃ<¿¥ö¾¿’>êi¿·”¾Êô+>+”x¿\2.¾›]Ð=xM}¿íöÒ½9¹=Îü~¿y½®m=³¢¿”½Ñ*©<㿾¬¼ê<‰ù¿*:&¼è??M5¾"ð ¿Q0 ?Q¾<¿DP;ðY§>/èi¿mIw¾öD>J“x¿k¾Æ¦î=#M}¿Ï°½×r’=­ü~¿'X½èÂ/=§¢¿à½ âÁ<‹ã¿ƒË¼T7<‰ù¿Š ¼€œR?H¾uæØ¾Ñ‹?Ô®<¿kzŸ¾°°¸>éài¿•7@¾d_Y>x¿Ì â½ø¯>L}¿}Ùˆ½ùŸ¡=Jü~¿Õü'½\B=ƒ¢¿ÂñɼƒÖ<€ã¿uÏ_¼·ªJ<‡ù¿óÏÚ» a?&éÁ¾Ý1”¾T>$?d—<¿æØZ¾»¨Å>ÁÕi¿Y¾å§h>_Œx¿åC›½ò >ŠJ}¿Êñ;½þ¬=±û~¿µºæ¼Â«O=M¢¿;ÇŠ¼J>å¶Éi¿Íˆ½Br>Û‡x¿ÏZ ½BÃ>ØH}¿{òÁ¼F#´= û~¿¼&n¼ØEX=¢¿„¼ÃÏî<^ã¿Ü8 »<&c<ù¿Pÿ$»àˆ“:ÿé¿ÜÔ<®”:ÎÄz¿çòM>¨f¡; D|¿­..>߯ ;7ÿ¿á<A|¿^›-> †<òõ¿]ò¹p.=@|¿î%->šw-=3Å¿¤…¹8º®=óY{¿è€->x!®=®¿Î¶¹û7>y¿]ò/>ˆÇ>\ç|¿Žñǹþs‰>õVr¿(œ6>Oe‰>8œv¿¿^¹í~ã>² `¿aÿC>¿ä>¶e¿xK"8à‡.?ßk3¿ÆV>MP1?¦8¿Ã¯c9>\e?„Áľƒd>Õk?Èʾo‘i¹2y?ì\½*Ye>Žž?‚H_½ó½e¹mµ:¿Øh¿‡ÆÔ>¼ ;ÿm¿¨>Á>➌<m¿­ÅÀ>{D*=Èùl¿9ƒÀ>îüª=hl¿)1Á>}w>•Ei¿¿Ä>o3…>aa¿ºË>¶Ø>LM¿ÂÙ×>…×!?ëÌ ¿Cè>zúO?†‘­¾åò>ÛÛ`?ßgC½Ö„ó>Þûv:»Ò=¿ÂÃ+?<>ˆ;ÃWG¿‹ ?vv<ý}G¿+c ?µ§=ùVG¿¤Y ?kX–=þ-F¿bõ ?#ð>*ƒB¿íí"?H[e>419¿R0'?ï(µ>AM$¿B,.?ö)?‹Æø¾êý5?ì¿"?Ãgƒ¾J]:?×;/?sW½’c:?Üp :æÎ¾Å*j?xÓ ;WÏò¾taa?Ðb3Ú¾Œüd?D÷S>f¼¾»!h?.0“>³=о×?k?¾¢´>A>¾9Ål?¨ÅÂ>'§¹¼®l?P½¹¯õ·=÷~?\Xí¹EmN9þÿ?SÒ‘ºI 9õÿ?gà »ä‹m9Úÿ? Qi» vŸ¹•ÿ?Õµ»½ßºåþ?¼¼;W‘»Yý?xÒ¼¦g ¼Ïú?L, ¼ßiC¼èø?ÁSŸ»Ð}J¼8ú? ºsüïý?‡ÿPº?z ?GPW?;¼_»Îäò>u[a?†B¼&ó>Ha?†ç¼%<ò>‹ka?u e½1ˆî>ab?gmʽçä>¼–c?¡î#¾AtÐ>Ì3f?ÝNr¾O‰«>jyi?Ý ¾Úäi>êl?0ʺ¾ï³Ø=¹Ïl?B_þ¡;B l?ãÈ¡º¸ P?è-?Åa°»¾TG?¯  ?‹Äœ¼å]G?²ƒ ?¡=½áÊF?Ý ?™×½½|‰D?‘P"?„O*¾ƒv>?ß«%?~ËŒ¾»ƒ0?à‡+?àÕ¾è¯? 3?ô¿?iÏ>ô29?ø¼'¿‹¡E>ù:?*‡/¿·1e<†P:?äl¿ºÊp?Ç×­>ÕÒ»Á m?OPÁ>#p¼¼ùm? &Á>êTe½¿l?AÛÁ>“,ç½a™j?Ä>•Q¾Se?ùÊ>gó¯¾7W?GÖ>U¿÷r8?:_å>¨&8¿Ôo?º¿ñ>åØW¿U–z>ˆõ>þ(a¿Ihœ<oó>£̺}?ÀÌ>¢Äà»ÈA|?bS.>ùwʼ˜0|?=/.>Ö,w½ Ã{?øö.>Œú½()z?Ûì1>Õçc¾®Du?¹¾8>þßÀ¾øg?‹E>Ûã¿W‡H?ðÏU>)ÄL¿I¯?ºõc>š¸o¿oO‰>¿»g>ony¿|„®@vκõé?Ô¼gÉã»kþ?Qûd8>·Í¼Uë?‰›8–…{½S„?qV8%Êþ½Ú~?P>¸þ™h¾àNy?÷7¹XLž¼:l?ô‰º¼¿ì²L? Íº[]R¿Îâ?7 !8¡/v¿ÀgŒ>ÿ:ð¿d]´J¿€æH?YñV¾hcL¿í:?æéc¾ªo¿R%Š>°f¾†qy¿vT¸<êe¾—Œ¶ºÙh?ÔÂÔ¾moÊ»åm?kAÁ¾Ñ·¼½m?ÅÁ¾wa½$‹l?–ÁÁ¾Y|ã½oªj?„ľ‹‡N¾VCe?ì˾Bž­¾“W?’Ö¾O¿ßc9?¼å¾ñ7¿4è?aÈñ¾‹W¿Ëå>œÐô¾ª*a¿Wƒº<¹S󾿱º!Ô=?4Â+¿¯±¡»îVG?7ž ¿ñþ“¼dG?)~ ¿öŠ5½?ÚF?fÓ ¿b ·½”®D?ÞB"¿~Þ$¾ÔÐ>?œ%¿½ˆ¾0]1?™y+¿ªýϾ:}?‘3¿R ¿øðÔ>l;9¿óÚ&¿”P>N;¿«w/¿Ÿk°< S:¿ Xº@çÎ>‚*j¿†3/»Ïò>Zaa¿X2%¼íþò>Qa¿+ ͼW9ò>´ra¿B"O½¼Üî>3b¿ÿy¹½jæ>I„c¿¹æ¾bÓ>è f¿kÉc¾¤>±>pRi¿!¢™¾¶žz>[l¿ØT·¾öÑû=¹ñl¿ÚøÂ¾†‘Š<¬l¿ žø8ô·½÷~¿äÙÐ9g=M¹þÿ¿à o:Oò¹¹øÿ¿LÓ:kú¹éÿ¿!†*;„nÞ¹Æÿ¿lx‚;”•í8|ÿ¿cº;-NÕ:Úþ¿À¡é; ’;¯ý¿Ýxå;<\ü¿â›;*!<9ü¿å:úzï;=þ¿—ù6:°x ¿JQW¿¿ÞC;Zåò¾j[a¿'m*<‹Bó¾”>a¿IœË<Áóò¾Û@a¿“*J= ð¾ÿ¡a¿¨K´=K_é¾=¾b¿Ÿ°>8¡Ø¾oõd¿GWb>èc·¾P8h¿Qßš>¼ï€¾EVk¿=S¹>Uhò½±µl¿¦JÃ>ðÿ仡£l¿}“‚: P¿60¿0 Ž;UG¿ã  ¿¥|<zG¿Zg ¿©“=³SG¿öZ ¿Lט=T,F¿î ¿Õ* >½}B¿hÖ"¿% i> 9¿$'¿·*¹>fN#¿A.¿û?™‘ò¾Ñ 6¿m§$?ÿ&r¾Ýp:¿»`/?@%¿¼e:¿õ:¸Ép¿èÜ­¾´D;, m¿aQÁ¾B‘Œ<¼m¿véÀ¾/ *=šïl¿ÌµÀ¾»Ùª=äl¿{fÁ¾Žs>¯?i¿•+ľ s…>v_a¿=ãʾ·ôÙ>¢M¿´w×¾ò\#?M¿¼"辫OQ?Ç~¦¾;Dó¾(î`?aڽİó¾ß’:_}¿ÊÒ¾#y¿œ¤0¾',‰>ô\r¿¾ô6¾W„ã>6`¿¦C¾…/?Éî2¿%KV¾ f?ÖZÁ¾Ä¸d¾˜&y?I½Óe¾§²v?_=>¾:e>ñ}?ö$>H»,·Ëp?ÈAˆ>—)d>Ê`v?3 ‹>]Òü¹6Ig?Gß¼>ì—_>±Hm?G+À>ɘºû‘\?¦™ì>–W>Lb?H/ð>3£ïºq¼O?ž¸ ?r%K>tT?•?P »/o@? "?A¤=>DšD?×õ#?'»7Z.?Ò#6?á01>|Ì1?Á.8?Û!»5Z?‘H?óU)>±?uæJ?µ7׺í’?8¦X?E)*>‘¥?Ž[?[๹À¯ê>´Ë`?‹{ >bí>ôÐb?’Ö¼Ùµ^?£‹>^Yó>S¡X?y>Àò>¤èP?®>²Gï> ëG?èÓÛ>áEè>3[=?—º?}Þ>|¤0? ³?ÚêÑ>º1!?o,?o.Æ>«?ˆ>?Û:¾>Aò>¹‰L?^ ¾>Þ>ãV?³ ¬>4¤-?rÙÏ=#N:?$¿(? pD>P$:?w#? E‹>º³8?Çô?Þ ²>d’5?"G?\yØ>¦½0?r2?…þ>Ü¥*?lÁ?#s?Ua$?Sì>í[!?‰Í?ÈTÊ>Ð-?7B?ꩾ>íw9?r}?€TÁ>Êæ]=g¤l?ñ“»>·Ù=¤l?Z?µ>í%>y-l?%^¯>çëK>Ó k?‰¸©>Ì£{>[0i?ÉB£>/z–>*­f?ìZš>}ï®>ác?X4>–‡Ä>˜™a?1Tu>t3Ó>Ìþ`?Æ]{>¢÷>1:W?OÄX:¦a¦»"ÿ?àØV:Â奻$ÿ?¾9ê9¶»üþ?”8XòÑ»¨þ?”®¸óòñ»7þ?P÷69GÁ¼Úý?w:%œ¼ãý?=f;Ï»„þ?÷ˆs;¡?»Eÿ?pæ#=‘Û¡=[þ~?¡yÀ¾7ƒ½ö§l?Bع¾í:ô½”l?‚&²¾¨ê.¾gük?Ÿtª¾˜†a¾A¸j?±Æ¢¾½Ž‰¾æÄh?.pš¾º¹¡¾ÃJf?c¾j¸¾@´c?Œ‹ƒ¾Rsʾ5Àa?¯—f¾3¥Õ¾cda?¢7¾©à¸¾µEj?Ë8-¿Þâ½ÛZ:?ø&¿¿QY¾`K:?u±¿Ö‘œ¾Iÿ8?£w¿]iȾQ6?ôº ¿qð¾m¥1?g~¿C ¿ß1,?å'í¾x¬&?ÝšÕ¾½‰&¿šv"?Ð:¼¾´2/¿2!? 1§¾°)¿<~,?ªR^¿þx¾Æ}ó>ŸV¿ ™‹¾&ˆó>Ä"J¿>—ɾôùð>™ì;¿æ¿œ ë>š,¿ðƒ¿*â>³é¿„$+¿h¢×>ËL ¿»';¿S\Í> ü¾¯¸G¿€¦Å>ŽqÞ¾‚õP¿nôÂ>kH̾‹ÀP¿$·Ö> Yv¿\ñ¾Îke>$m¿Qòš¾¸e>% _¿—$à¾Òc>ìÎM¿—æ ¿™¨\>þ;¿û&¿VåR>{S(¿ýR:¿ˆlG>)B¿ØI¿Z<>ó^¿¹ÔU¿²ú3>}­ë¾åï^¿È—0>çuÛ¾‚Ta¿­P>{Í|¿ÚU!¾`긅\s¿ðâž¾Ž±ª9άd¿‘)æ¾UÒl:YˆR¿œ¤¿$%Á:¢×>¿‘¡*¿³ô:Ö +¿ˆe>¿#;Î]¿ ¸M¿(ê:ë ¿zY¿úú¤:Z¹î¾Wxb¿{Õ9w«ß¾û-f¿ìÂÙ<Íiv¿S6¾@{e¾Um¿úš¾Êe¾:\_¿bfß¾Ó/a¾n+N¿¯ ¿EwY¾pv;¿+&¿àÉN¾Ê•(¿.b:¿¥üB¾ái¿6öI¿P8¾·k¿ÆòU¿."1¾J–ë¾Î_¿j½/¾_›Ý¾Ãòc¿ï¾|^¿›I ¾õó¾u‚V¿;y‰¾÷Hó¾2ÆJ¿ÔǾCLð¾x­<¿Â ÿ¾¯áé¾W-¿üC¿æ‘ྤ‡¿©'+¿ÃÉÕ¾°º ¿kP;¿—˾˜yü¾ÒäG¿ždľóyÞ¾3Q¿Ò«Â¾ÅÓÒ¾….X¿ÿ`¯¾‚-¿üFÔ½ôY:¿j‡'¿WCR¾@L:¿¨q¿Lm™¾Š9¿P¿Çßžg6¿Ý– ¿^žî¾h•1¿J¿(³ ¿ˆ ,¿Hy¤¿Lx&¿é‰Ö¾Ša&¿Q"¿u¼¾t/¿Ø>!¿:›¶¾ô::¿_¿DÁ¾_½D¦l¿Ýº¾W‹á½áªl¿|³¾É%¾×=Œ¤l¿h»>LJè=}ˆl¿àµ>åe$>æk¿ù…¯>ùÚS>ÿ”j¿kì©>åD‚>RŠh¿ï£>4F›>õèe¿&\™>¸¡³>þ!c¿z‹>?fÈ>ãa¿§tn>š]Õ>Qò`¿=Ú:>Á¹>>j¿Dr-?kMÚ=ÔL:¿\}(?VPH>×:¿T°"?»óŒ>wª8¿„?³·³>m5¿¦?NúÙ>Ð0¿ÇT ?|âÿ>Û*¿j¨?¤ ?m¹$¿üÆé><ê!?>- ¿¿ŽÇ>ý™-?ø¿-Ϭ>TU)?:t+¿XŸ^?Ðæ>¡Wó¾ètX?†?z>, ó¾Ó™P?H®>ð¾×sG?Ü>í¢é¾ô¼Ô¾__ ?àƒ,?ȾäÍ ?ÞF>?ÄÀ¾Úzð>xéL?V­¾¾6«Õ>j)O?{¸Ó¾K¬v?ºà>®=e¾˜p?‹Iˆ>d'e¾ã&g??μ>b¾»[\? ì>•íZ¾÷tO?D« ?´9P¾@?»"?*MC¾ý-?¾(6?…6¾=õ?ªH?2&-¾¦%?AÛX?ô +¾‚ç>ŽŠ^?šrL¾ ª½<è~?:%%=q®½'>r?Oß>Šà8¾‘ s?n–ƒ>C…A¾áb{?m»;ùüa?/{{>AÍ×¾0&h?lìѺ[Ü ¿Ã$>?ÑÑl>O?(¿Œò@?L¿äG?ÞÖ]>¾S¿á?7½ º7b¿¥“Õ>¹–Y>ùÂi¿9¹Ð>äG¹:Jøh¿â½±>1÷g>´Iq¿«>Y,ƒ;¶^e¿_ú¸>ï:„>žÐo¿!³>(Ý;ä6W¿wÃç>º/˜>ðRd¿cƒç>Ú<ï;¿O?†¤>]bH¿?O?±hÏ;b¿!¸>?ž>[å!¿šNF?Ãò*:´K£½€âM?wÄ?ˆ™¾f@U?¾ ?>Ū¾è`J?&|?Ãç¿)w0?âdü>û{0¿Îò ?yñ>hF¿´fÜ>îÜì>eK¿W ½>åcö>”ŒC¿0PÁ> þ?v51¿Œ á>ðN?Èn¿ö8 ?U_?Iƒô¾ì'?¸¢?ÞÑn½W¥?NR?ðNн%0? ÊF?;[Y¾Æj?T| :?õ¿*i½>îŽßÉB?öò¾àÉ>è©I?ôX̾ÀCâ>ð©M?i¨¾ù?ÒJ?‰d¼ª/Š>„uv?±¢Æ¼h®«>iq?€0:½u¿³>;lo?i<°½÷à¶>~n?ð<¾bþ²>m?NI¾Ì•©>±?l?}Bm¾ÃâŸ>Úk?î“u¾.Bš>Bl?^Mg¾Â¼˜>gm?AL¾9m—>)(o?Qš-¾à]¦>Í/n?Å„"=ù©`½©i?Q=AÖB:@=x&}?ƒ/L>ƒ#Ç=ùžy?Ac>ø>½v?wNL>—5:>Þ~v?áØ>˜E>Vtx?µè¦=#æ;>uÉz?úQ=ç >ö·|?ª%;Ä7Í=„µ~?Z躑R•=xQ?ÜÙ=îV¾¾l?#.>‹·¾Œ¡o?8F¢><¾± j?ýæå>Uõ"¾Wa?r™?(àt½w8Y?]G?ÎMQ<õ¯X?(Êô>×/=J”`?1þÅ>{+í<¼÷k?÷I”>ÊlÞ¼Ûít?‹•X>Î ¾*‘w?i%->MÍn¾3&u?=›.>ŽW%¿Œ>?˜ó„>Íâ¿Y™D?fê>ü²¿ÑÔ;?!?»¾¾|/?Aç:?j„g¾Ò%?|A?«Ï¾îA$?¦Ñ8?ý9˽©N/?; $?ÑO¾HA?ß?Þk¾·PQ?=Ï>6ŠË¾¹ÝR?Òï¡>k}¿èßH?<Êa>‹W¿8ü>IÒ¦>âSI¿²U?Ò’ ?§-¿dû>ÑÁlX?´v¬¾Ò=Ô>tÄa?ýèk¾M–Ò>Á^?Š…S¾\å>YÇP?%‰¾BR?<Œ5?ÉdѾ3?ûq ?¤¿õì?ã+Ò>X¾?¿ô$?ûé}>вq¿,O^>v¹>òxe¿¼Õ‚>!P?q­D¿Ñq>‡ªI?½ô¿4Z> »e?œÇʾ2G>û]p?lB’¾œoD>R¨p?²³‰¾¥V>‡‚g?C)³¾€Cz>~ªN?©¿T>¿?Ù:¿ÇåŽ>lDç>¶«[¿œz>ã>iw¿Â%%½ÿÁ¾>r‘m¿˜ìû:i?êmK¿ÿ‡Ü:ßñL? h¿‚Y¢:¨i?½ÕÓ¾,?¹¥t?Ùš¾p»T'u?`o“¾dw™»½Tm?迾ÒÕ»,AU?æž ¿ò¦·»Š¸$?‰÷C¿€kC¹Â ì>r+c¿˜@V:/Úu>¶µk¿Cx¾0*¹>Ïe¿/ှ 8?E¿ ;n¾¢I?Ä?¿R}W¾æ¨e?9ýʾ=¦G¾#%p?Sò‘¾—¤I¾W5p?z¼ˆ¾àê`¾ÿæf?Pe±¾Ÿóƒ¾|TN?Ô¿¾ ”¾¯ú?€¥:¿®è޾֭ç>Ò®[¿:hx¾JØP>8kI¿á!¿ëv¥>JøI¿Ê¿ ?~ø-¿ôEú¾>Ût?¿¿3>iH¿bÀP¿»Ç>ûf¿¼D¿Lç>áù¿üÚ;¿-; ?€¾¾x/¿e:?JGk¾ÔS%¿-ç@?®K¾rê$¿sG8?ƒ ƽ÷/¿;$?;֟A¿í†?e¾ÒÙP¿;½Ó>{ȾKmR¿kk¤>ËR¿Ò(I¿§½§=¸é‡¾_ìu¿¶#>ؽ¾p¿Ô¿œ>'䃾¨j¿ìá>Ê1+¾µa¿é±?GŠˆ½·¥Y¿DP?¢<”­X¿•:÷>1+=ì_¿É=Ë>wüö<õÖj¿j›>¢ażÅãs¿Dœe>N† ¾çw¿÷h3>Ôg¾8Iu¿ŸÏž<§Bn=³„¿|Ój=yòU<Ž¿eº>Kò+=ݽ}¿Õ¸>>ó´³=ñƒz¿¦€]>Žk >#vw¿$Q>Àà1>o v¿5F">Éã@>^x¿ƒRÍ=|C:>ájz¿Q¦L=²Ö >ª|¿¨H–<â8×=Š~¿!£;¢kŸ=T8¿ýƒú¼Ò)Á>ôól¿ýî½\À«>ïöp¿»€½ŒØ°>v´o¿ ³Ð½Ûs±>¶n¿d;˜½¬>kñm¿¨D¾¿Ë¤>{^m¿fá]¾ •>1,m¿feb¾™Ú™>›„m¿Š¦U¾Dt™>¸Qn¿ë´>¾W¼™>Î|o¿Š¯&¾Òȧ>6?n¿™½ÁR(?¼4@¿œCë½™?ãÆF¿ 'i¾Ò²?K°<¿é3 ¿—€Ð>©;¿t¯¿EE¼>0Ð=¿C¿öò»>&D¿paí¾PË>PÄJ¿¤êƾÂÁå>ÖN¿;¥¾¿¼?ƵJ¿û³Ÿ½åÇ\?O ¿‹’'¾BdT?¤¿?°¾5ÖH?“ ¿w ¿²`.?Àˆý¾9¤1¿" ?%Ûñ¾ªG¿&Ú>d£ì¾÷L¿Wм>¹ûô¾+æC¿IUÂ>¿‡¾0¿©áä>Ûš¿KÚ¿ ¶ ?¢¿{Íñ¾®õ(?.’¿|@§½b¼x?|Oc¾0è=¾r?…Á…¾Ê²Ï¾]'a?NÈ~¾gÏ!¿ï=?/Îo¾ÿ’L¿ip?z_¾¤b¿“vÔ>žÖV¾½’i¿*±>à`¾ä-f¿êD¹> |¾ÑW¿×ôé>øB‘¾%:¿ÇF?,Øž¾N‡¿t…??bp¾´Éù¾¼\T?#‹>Ó¿³Z?áÙ̺rÌѾ<Áa?Þän>§‰ß¾­Of?¬/»ðe£¾',n?{Ô8>º­¾aÏp?‹à»¸I¾^®y?ÝMÌ=¦S¾Az??ÿ»ú5"<€¶?à¦=½—pï<Û?Ü“‡¼U8)>oòz?dCÞ½Ô <>b”{?É´üj‡¾ráe?+´>R[,¾Ý÷v?ÒNO>q̃¼Í±?ûÞ<½ˆ›u>‹„\?k=å¾Ýú>dè·>)WK¿üK ?5 G¾¸BR¿@öú>’ü¾Ì&8¿7[û>–¿œÏ¿å×¾qÐ?9j:?ƒ@w¾5ž „¾x«r?šþ”>t ½Ê?4b·¼”ÊÛ=Úb?Ì ê¾ðâ{>½©þ>òøT¿í8>DØ!=ßks¿¨Ÿ>’ؾ¢®h¿(`«>Ê4Ú¾À&W¿«'¾+ä>äïa?h’¾ðP?¶ÖG?Û2ç½¾ÿF?1n?Ü)­½b¾l?Eò½>)÷'½ÄÃ?ðpL ü>(¶u¿>iu7½}¿5Ø4>ZS¾¹_v¿ÿ¨º¹)E>¯5{?Ê‘¹»¬i×>>?þë¼Ád?è>À7½„t?Ï0W=ð ½Àzm?é=¾¾ä³×¼žu5?Ìs4¿’îY¼5 á>Këe¿õ‹» #K>êèz¿+öÆ<ô”0=º¯¿ïÂ&>˾ Ôy?éÄ>¹Nü=îz?£gú=v÷>©ê]?QZ=»ÄU?N0 ?¤ˆÏ¼1½~?’GÄ=¦½H_r?ªŸ¾}ù佄ËJ?õ—¿µ¾ue?cH¿d ¾ãŒâ>kÖb¿s6ö½Ù³ >Vq¿V°>Ö3¿pO?d\©>™ñ޾XÆf?¶¦>ÕÕ>o?¸©H>Í;?Òr'?Û\¥;è콇¡v?¡žw¾¹"8¾ö]?í,ñ¾!.`¾ÆÐA?³“¿ÏA~¾hî(?øŠ5¿ù0{¾~?¬TH¿k7É>«A¿ÿ§?/§æ>‚&#¿. ?Õ ?£¡¥¾•€F?™Ñ?£+}=ÝáZ?6í’>)j]?6ÚÒ>r–ˆ=Ð{? Ž2>凾åþy?®Ÿ%¾µm¾ñ;k?¼”£¾©»‘¾Íe]?ÀÓ¾ó9¦¾o\Q?]Gó¾g «¾ÀE?R@ ¿ߨ>¥_¿tDu>Rû>ùqR¿¢u“>âµ#?¼^*¿\Å>ìø7?E¹Ü¾® ?ú¡?ÝkQ?šÙ‡>ñ0>Íðx?&à >X"¾Ñ|?*š½ ‡¾n´s?ÇC¾Ó¥¾´äl?tdL¾ªËº¾6 g?Äi¾rä¾ôa?Ò½¾‹ÏÛ>4g¿²8h»î–þ>‰^¿ù‘µ»±K'?Þ¾A¿RiR¼aA?P '¿HÒ•=Óˆ?ßvN?Oä{½O?Œ®N?_wp=Ù«±½û?Tr®;Ò'¾f|?B<Œ¾~)v?±>‚;«¾yLq?V.;ý±À¾d-m?ïŸ|:óʾÔk?ç,½ØkÙ>Ú^¿È·~¾ÍÌü>‹©P¿k*›¾õ(%?£þ#¿4Õ¾rõ;?Þ¿ô`¾ólÕ>B!Y?^§¾–H?·QQ?Ή¾ˆJ¬½„ä~?Ј!=Ä8¾Ç{?m:½=^ ‡¾Hs?˜H)>õI¥¾½}l?íÑR>9»¾ãÑf?øyl>ÃRƾGÀf?ÞDF>¤’É>úõ>¿h… ¿o$ç>+ß¿›!$¿ŸI ?¼—¾‘¢I¿åÿ?K(¾øH¿ÉCX>X®d?K'˾éÞ=`[z?y6¾uè ¾ù‰y?VË4>§l¾å4j?ù´©>ÜÄ‘¾D\?2eØ>b†¦¾÷VP?Kö>b£´¾Œ»Q?±oç>úi>g)û¾a¼P¿Ýÿ¨>Óæˆ¾;Ág¿âú¨>ÜW>ho¿@U¬>Ìõ¥>±Tb¿ÖG¨=À.m?I¼¾ÎÐë<Ìâ|?B{¾hÅÞ½v?op>¨s5¾ip[?ö•÷>„§^¾ÿÀ??`6 ?U.}¾Õ&?š7?w¨Ž¾õ¦&?MÃ4?W³'>}Œ¾9ßy¿Á¿!>¿ ô=Nðz¿¤%>aíî>Eë_¿‡ÃŽ=L‡Q?nÿ¿í­7¼š…~?€Ú½YK–½Žåq?T£>ªáÛ½ÐÄH?Ïo?àÿ½)·?Z7J?Ù¡ ¾÷Ý>ÓHd?#(¾ˆåÏ>"%f?+ ë:U@>”/i¿P ¼Øè(?[@¿ ùž¼ÐRb?Äï¾1zç¼?o?Õ>v½dé¼Õm?|çÀ><=¥¼¶K3?Y§6?›Þ¼‚ÀÛ>r5g?(R²ºú£A>€a{?œï¼è>XÊ}?:޾1Ká>'Èb¿ÈB¾‘Ô?,I¿,"ܽ§£E?ï] ¿'ž½Bùk?p‹Â¾t˜ü¼jÖ?’¼À=ïeg?ŒZÚ>ì9À=Œ?¾ªJ? 5>ÅÍw>Lv?†´>kÇM½²â|?ØU> ¾ÖŽ{?ª;¾Ùë?||;¿ôms¾’¹;?Ý#¿Â@¾„4X?U¿¹ú½µBr?8™¾áú­¼¥è?¿„<¾¼>a?k`ë>틃>¿Yù>(²U?1ð>k=-Ys?eŸ>—O޾@²h?Ê”>༾#Ûb?¾Ë¾ã??¶j¿ý«¾mðR?THê¾z1…¾Jge?m¸¾á$¾y®v?Ï‹Z¾h ”¹%Ý?b’=-[‹>úîZ?áÒá>?uG°>QÉJ?ÚY?‰(F¾WíR?ŽÈø>l?ú¾}9?èÞ>F±¿b3?„ù¾Ç>T?| ¾ãо/a?+çt¾¢¾\òm?wB¾ ŽD¾¼„y?èÝê½¼¹¿<;â?팛<\n>'èz?Õþ>Ü'8?Åò?`RÅ>(.??!B?šóZ?Ф¾–zè>ÆC?ýÉϾÔ?T#?Û&¿ÚïÑ>¬Ø?õk5¿Õ>fø>:=:?fø>uç‰>Ö9r?]©7>Ã4‹>çÒr?íF&>`üÍ>$µi?®Œ=ÇÑ>.­i?®*©;Ú°?wN?~¥=Êù?søM?‡?|¼[é?#;ËoÔwPs?¢ Œ¾HL>Av?A싾´ì»á¢J?97¿uëü=6&M?"¿È8¨ºÿžXk?ªˆÊ¾ÄË ;):x?L¡U¾~§>TJz?$yU¾ÚÄм(U?cª ?K{²<˜Q?K» ?Ô>§ž(?mï>?¦Ë=Ä’*?æã>?»¢¥»)A‡>Âq?ºžH>f¬Å>˜Ãi?>÷>“—?„N?{9,>¬Qq?ÓhŠ;+áª>Vj?ÿAŒ¾²—>p C?éa¿ûò{>ñx5?O¬*¿'Çk>‹ã>?tÅ¿£sy>CK_?²˾4‘’>Yøo?z,V¾½˜Ž>ËH? Õ ?†ÚŽ>®l"?¹??²ŠN>K`ƒ>¢mq?¥X>R€¸>NÑi?£B>“â?·í`?㫈;–yô>Ž[?tŒ¾nµÞ>M¢6? Œ¿^„¹>ˆØ)?Ö*¿¸>­>}ž2?.ò¿`ò¶>…øP?7a˾ڸÖ>ƒÆa?"ÌV¾Ê Ø>ã;?0ë ?*òË>c=???ãݘ>{Ë|>ý¹é¦>wÚi?9Fy>Ý¿ù>QN?GÁ«>- K?\„‡;%é?GiG?ù”Œ¾ÃS?·Í%?©¿¸Gð>u ?Ôò*¿êà> "?4¿…jì>·š=?Ǡ˾õ¢ ?qN?sIW¾ú ?Dà(?Âø ?Ù?j ?$??”iÆ>¦àp>G0q?é‹t>$_‘>]Ýi?¼!•>uÊÛ>¼ZN?Ž•Ð>ó80?N‡;y°9? o.?žŒ¾±®-?<?5³¿†?¼?éü*¿²´?  ?¿ˆþ ?µ%?õ·Ë¾7p&?L„5?ÞƒW¾çJ,?«¨?ü ?Z}?u¡ò>§(??Ñøî>†:c>xGq?}ê>I­p>’Ùi?Ȫ>‘¸>3VN?Ì_ð>?bˆ;‹íR?‹0?}Œ¾ÊF?Ä_ñ>¸¨¿;h%?˜/à>©ò*¿•?»‘ë>ò¿!\"?ÜË ?»¢Ë¾¾6>?Š?%oW¾DiF?ÀÂñ>"ö ?“j/?G›Ê> $??Ìã?¬T>“‚q?$¡„>qž8>¤Ïi?¨ôº>VÉ>˜DN?w9?/Ü>¡‰; g?…œà>rfŒ¾[?鸺>a‹¿.T6?ÜO­>¼Õ*¿qÔ)?J¶>Ëñ¿ŸÜ2?„ÙÔ>#e˾rQ?þ€ï>²W¾ŸÈ[?¸>åå ?,@?‚>4???—½C>váq?ª)ˆ>„‘÷=…Ái?7]Ç>¾”J>)N?Ù?F9‘>Ór‹;ß{u?^™><1Œ¾Rj?äƒ~>a¿»ÖB?#çk>ü«*¿§v5?2ew>Å¿±??Þ‰>ç ˾§ž_?­§>DyV¾SÝk?/ýr>±Í ?5LL?HX>Ã??ß!?G˜2>Ccr?ñmŠ>­o=v³i?2ÙÎ><°Ü= N?$g?ÙÐ>€ÚŒ;ÌÕ}?‹x>åþ‹¾V(s?>—6¿²ˆJ?Û1î=ð*¿&žü´Ê¾eFh?9É6>hÚU¾<&v?ɬÞ=x´ ?ò`S?¯/à=Óî>?O6(?Æ3!>{s?ÀQ‹>+©».­i?ÇÑ>‡?|?Ä’*?BÊ>oÇs?ÐÉŠ>®Œ½$µi?`üÍ>~¥½wN?Ú°?Qú9¾ž!Œ;u½{?KL¾‚ Œ¾{Ps?uëü½97¿á¢J?Ìúí½1‚*¿ÿž?§ž(?ƈý=¹£t?ê݈>>÷¾˜Ãi?f¬Å>{9,¾„N?“—?+᪾ÓhŠ;¬Qq?²—¾ÿAŒ¾Vj?ûò{¾éa¿p C?'Çk¾O¬*¿ñx5?£sy¾tÅ¿‹ã>?4‘’¾²˾CK_?½˜Ž¾z,V¾Yøo?†Ú޾ Õ ?ËH?²ŠN¾¹??®l"?³QÝ=Ç’u?Ê¡…>£B¾NÑi?R€¸>j ‚¾­¾Ö*¿ˆØ)?`ò¶¾.ò¿}ž2?Ú¸Ö¾7a˾…øP?Ê Ø¾"ÌV¾ƒÆa?*ò˾0ë ?ã;?ãݘ¾??c=?˜¹¿=0Žv?é.>9Fy¾wÚi?¹é¦>GÁ«¾QN?Ý¿ù>%é¿\„‡;- K?ÃS¿ù”Œ¾GiG?¸G𾩿·Í%?êà¾Ôò*¿u ?…jì¾4¿ "?õ¢ ¿Ç Ë¾·š=?ú ¿sIW¾qN?Ù¿Âø ?Dà(?”iƾ$??j ?Ñ`¥=ow?¥>w>¼!•¾]Ýi?$_‘>Ž•Ð¾¼ZN?uÊÛ>y°9¿N‡;ó80?±®-¿žŒ¾ o.?†¿5³¿<?²´¿éü*¿¼?ˆþ ¿¿  ?7p&¿õ·Ë¾µ%?çJ,¿ÞƒW¾L„5?Z}¿ü ?«¨?Ñøî¾§(??u¡ò>pÓŽ=v”x?˜j>Ȫ¾’Ùi?I­p>Ì_ð¾3VN?‘¸>‹íR¿bˆ;? ÊF¿…Œ¾“0?;h%¿¸¨¿Ä_ñ>•¿©ò*¿˜/à>!\"¿ò¿»‘ë>¾6>¿»¢Ë¾ÜË ?DiF¿%oW¾Š?“j/¿"ö ?ÀÂñ>Ìã¿ $??G›Ê>8;y=ð”y??,[>¨ôº¾¤Ïi?qž8>w9¿˜DN?VÉ> g¿¡‰;/Ü>[¿rfŒ¾…œà>.T6¿a‹¿é¸º>qÔ)¿¼Õ*¿ÜO­>ŸÜ2¿Ëñ¿J¶>rQ¿#e˾„ÙÔ>ŸÈ[¿²W¾þ€ï>,@¿åå ?¸>¿4??‚>5¦^=¨‹z?ÌÀJ>7]Ǿ…Ái?„‘÷=Ù¿)N?¾”J>ß{u¿èr‹;F9‘>Rj¿<1Œ¾^™>»ÖB¿a¿äƒ~>§v5¿ü«*¿#çk>±?¿Å¿2ew>§ž_¿ç ˾މ>SÝk¿DyV¾­§>5LL¿±Í ?/ýr>ß!¿Ã??HX>¿ O=Gq{?'D9>2Ùξv³i?­o=$g¿ N?<°Ü=ÌÕ}¿«ÚŒ;ÙÐ>R(s¿ÿ‹¾‰x>²ˆJ¿—6¿>&ž<¿ð*¿Û1î=̉F¿6˜¿Æ¯ø=eFh¿ü´Ê¾#à><&v¿hÚU¾9É6>ò`S¿x´ ?ɬÞ=O6(¿Óî>?¯/à=ŸUK=W=|?Q'>ÇѾ.­i?+©»Êù¿søM?‡?|<[é¿#;ËoÔ¼Av¿A싾2ë;6&M¿"¿6¨:P?¿{m*¿± :8-I¿h‚¿ꌺXk¿ªˆÊ¾GÍ »TJz¿$yU¾¤ÄÐ<(U¿cª ?€{²¼Ä’*¿æã>?%¢¥;6ÞS=ì|?´.>`ü;$µi?®Œ½Ú°¿wN?~¥½u½{¿‰!Œ;Qú9¾wPs¿¢ Œ¾HL¾á¢J¿97¿uëü½ÿž<¿1‚*¿Ìúí½¨sF¿k˜¿Uý½Ah¿D°Ê¾.#¾):x¿L¡U¾~§¾˜Q¿K» ?Ô¾§ž(¿mï>?¦Ë½æDh=Yx}?¨W>f¬Å¾˜Ãi?>÷¾“—¿„N?{9,¾¬Qq¿ÓhŠ;+᪾Vj¿ÿAŒ¾²—¾p C¿éa¿ûò{¾ñx5¿O¬*¿'Çk¾‹ã>¿tÅ¿£sy¾CK_¿²˾4‘’¾Yøo¿z,V¾½˜Ž¾ËH¿ Õ ?†Ú޾®l"¿¹??²ŠN¾Ôƒ=Ý}?HÊä=R€¸¾NÑi?£B¾“â¿­¾}ž2¿.ò¿`ò¶¾…øP¿7a˾ڸ־ƒÆa¿"ÌV¾Ê ؾã;¿0ë ?*ò˾c=¿??ãݘ¾sy˜=~?ï•Å=½é¦¾}Úi?üEy¾Ý¿ù¾QN?GÁ«¾- K¿\„‡;%é¿GiG¿ù”Œ¾ÃS¿·Í%¿©¿¸Gð¾u ¿Ôò*¿êྠ"¿4¿…jì¾·š=¿Ç Ë¾õ¢ ¿qN¿sIW¾ú ¿Dà(¿Âø ?Ù¿j ¿$??”iƾƒ±=í&~?qÇ©='_‘¾bÝi?ž!•¾—ÊÛ¾¼ZN?l•оó80¿N‡;y°9¿ o.¿žŒ¾±®-¿<¿5³¿†¿¼¿ðü*¿·´¿  ¿¿ˆþ ¿µ%¿õ·Ë¾7p&¿L„5¿ÞƒW¾çJ,¿«¨¿ü ?Z}¿u¡ò¾§(??Ñøî¾÷_Î=å ~?äö‘=I­p¾’Ùi?Ȫ¾‘¸¾3VN?Ì_ð¾¿bˆ;‹íR¿‘0¿¤Œ¾ ÊF¿Ð_ñ¾¿¨¿3h%¿˜/ྩò*¿•¿²‘ë¾ü¿\"¿ÜË ¿»¢Ë¾¾6>¿Š¿%oW¾DiF¿ÀÂñ¾"ö ?“j/¿G›Ê¾ $??Ìã¿Ürî=áÃ}?®h}=qž8¾¤Ïi?¨ôº¾Vɾ˜DN?w9¿/ܾ¡‰; g¿…œà¾rfŒ¾[¿é¸º¾a‹¿.T6¿ÜO­¾¼Õ*¿qÔ)¿J¶¾Ëñ¿ŸÜ2¿„ÙÔ¾#e˾rQ¿þ€ï¾²W¾ŸÈ[¿¸¾åå ?,@¿‚¾4??¿ê|>JS}?•a=„‘÷½…Ái?7]Ǿ¾”J¾)N?Ù¿F9‘¾Ór‹;ß{u¿^™¾<1Œ¾Rj¿äƒ~¾a¿»ÖB¿#çk¾ü«*¿§v5¿2ew¾Å¿±?¿Þ‰¾ç ˾§ž_¿­§¾DyV¾SÝk¿/ýr¾±Í ?5LL¿HX¾Ã??ß!¿š|>C¼|?XP=­o½v³i?2Ùξ<°Ü½ N?$g¿Ùо€ÚŒ;ÌÕ}¿‰x¾ÿ‹¾R(s¿¾ 6¿¨ˆJ¿Û1î½ð*¿&ž<¿Æ¯ø½6˜¿Ì‰F¿#à¾ü´Ê¾eFh¿9É6¾hÚU¾<&v¿É¬Þ½x´ ?ò`S¿¯/à½Óî>?O6(¿Þ’,>É|?(K=+©;.­i?ÇѾ‡?|¼søM?Êù¿ËoÔ<#;[é¿2ë»A싾Av¿6¨º"¿6&M¿S:¸{m*¿P?¿êŒ:h‚¿-I¿GÍ ;ªˆÊ¾Xk¿¤Äм$yU¾TJz¿€{²?Ä’*¿6u>>b0{?›jR=®Œ=$µi?`ü;~¥=wN?Ú°¿Qú9>ž!Œ;u½{¿HL>¢ Œ¾wPs¿uëü=97¿á¢J¿Ìúí=1‚*¿ÿž<¿Uý=k˜¿¨sF¿.#>D°Ê¾Ah¿~§>L¡U¾):x¿Ô>K» ?˜Q¿¦Ë=mï>?§ž(¿¾®O>üDz?Ve=>÷>˜Ãi?f¬Å¾{9,>„N?“—¿+áª>ÓhŠ;¬Qq¿²—>ÿAŒ¾Vj¿ñò{>ôa¿h C¿'Çk>O¬*¿ñx5¿£sy>tÅ¿‹ã>¿4‘’>²˾CK_¿½˜Ž>z,V¾Yøo¿†ÚŽ> Õ ?ËH¿²ŠN>¹??®l"¿´_>¼Jy?lt=£B>NÑi?R€¸¾j ‚>㫈;·í`¿wµÞ>6tŒ¾Ž[¿^„¹> Œ¿M¢6¿¸>­>Ö*¿ˆØ)¿`ò¶>.ò¿}ž2¿Ú¸Ö>7a˾…øP¿Ê Ø>"ÌV¾ƒÆa¿*òË>0ë ?ã;¿ãݘ>??c=¿0#n>¥Hx?/ý”=9Fy>wÚi?¹é¦¾GÁ«>QN?Ý¿ù¾%é?\„‡;- K¿ÃS?ù”Œ¾GiG¿¸Gð>©¿·Í%¿êà>Ôò*¿u ¿…jì>4¿ "¿õ¢ ?Ǡ˾·š=¿ú ?sIW¾qN¿Ù?Âø ?Dà(¿”iÆ>$??j ¿¶z>œDw?´¬=¼!•>]Ýi?$_‘¾Ž•Ð>¼ZN?uÊÛ¾y°9?N‡;ó80¿±®-?žŒ¾ o.¿†?5³¿<¿²´?éü*¿¼¿ˆþ ?¿  ¿7p&?õ·Ë¾µ%¿çJ,?ÞƒW¾L„5¿Z}?ü ?«¨¿Ñøî>§(??u¡ò¾?š‚>ÐCv?È=Ȫ>’Ùi?I­p¾Ì_ð>3VN?‘¸¾‹íR?bˆ;¿ÊF?}Œ¾‹0¿3h%?¿¨¿Ð_ñ¾•?©ò*¿˜/à¾\"?ü¿²‘ë¾¾6>?»¢Ë¾ÜË ¿DiF?%oW¾Š¿“j/?"ö ?ÀÂñ¾Ìã? $??G›Ê¾)µ†>bKu?q}æ=¨ôº>¤Ïi?qž8¾w9?˜DN?Vɾ' g?¡‰;ܾ[?rfŒ¾…œà¾.T6?a‹¿é¸º¾qÔ)?¼Õ*¿ÜO­¾ŸÜ2?Ëñ¿J¶¾ÿqQ?e˾ ÙÔ¾ŸÈ[?²W¾þ€ï¾,@?åå ?¸¾?4??‚¾»‘‰> at?ÿª>7]Ç>…Ái?„‘÷½Ù?)N?¾”J¾ß{u?½r‹;F9‘¾Rj?<1Œ¾^™¾»ÖB?a¿äƒ~¾§v5?ü«*¿#çk¾±??Å¿2ew¾§ž_?ç ˾މ¾SÝk?DyV¾­§¾5LL?±Í ?/ýr¾ß!?Ã??HX¾F‹>˜‹s?}Þ>2ÙÎ>v³i?Èo½$g? N?<°Ü½ÌÕ}?jÚŒ;ÙоR(s?ÿ‹¾‰x¾²ˆJ?—6¿¾&ž?¯/à½ð0½>~qm?-òf=y¿>|lm?8”»Î}y>[x?èÞ=¸”|>x?Þþº¾º:>Hš{?´¢è<‡=>ï˜{?Õ1 º¦C>çØ|?IKÇé×|?plQ¹„‡>Q}?ÚœÃ<ã>[}?\¤¤8\W,>¯@|?eEÞt?|?R Ö9¦a>‰y?T=çÆd>r‡y?Æ9~:ð¯>Høo?xóh=zn²>Oóo?ê;;ú…(?¯ >?iØá=ë*?‰”>?2á¸;:uW?ZN?Ñ$ã=ßEY?gC?^íµ¼Í¶>Æ{m?jîè= p>x?~Zš=Ï›3>þœ{?e-h=L/>ÜÚ|?CÓE=Ž–>& }?iA=ªž%>C|?¥Z=+ÈX>€y?!ž=~ú¨>dp?†á=dâ!?a·>?î€Y>\:P?ol?ôÊw>AŠª>Ž…m?Çù+>•¹`>–"x?å}ã=½(>ŽŸ{?¤Ëª=Sb>´Ü|?FA‘=† >ã }?3r=©Ù>-E|?ÆiŸ=·žJ>+‘y?›–Ñ=ýã>× p?^4$>ÜF?’Ì>?ž><ÛC?ÒŠ?Xª»>@ñš>#Œm?è^> L>œ%x?ôH>™›>B¡{?gôÜ=Á8>ðÝ|?ƒ»»=Kuú= }?ï¶=§m >›F|?¬–Í=­7>’“y?Áÿ>>>Zp?ßFS>È ?™Ú>?9Ë>ôÅ2?Q ?zmö>6¹‡>€Žm?ì †>²2>±&x?‘$1>Æ>Ý¡{?mÌ>câ=[Þ|?–†á=RÛ=o }?þ;Û=’wõ=G|?ý£ö=s >_”y?Y×!>+Ñy>*p?î}>Ã8ï>Oß>?6Ió>Vs?o©?@x?Îb>qŒm?P›™>Â>¸%x?ªÉJ>-µÞ=M¡{?õö>c¼=ôÝ|? û>\¶= }? ¦ú=?Ì=•F|?yë >¡h>y“y?—×8>ÉO>p?·p>b‘Æ>TÚ>?& ?Z?¥?f,?ð0>%†m?W~©>mç=Ñ"x?ý³_>Ô¼¬=¡Ÿ{?Å–'>âú‘=ºÜ|?2>A)=â }?n >2í=E|?~<>ÊÎ=þy?ňK>= >H p?öž>Ìë˜>-Ì>?™?,éÏ>§“?î¦>?~Ÿñ=|m?w_µ>»–ž=^x?®Vo>q[l={?#B3>ÍbG=æÚ|?Ä >Sj@=$ }?©£>|ÎV=îB|?Wã%>ŽÓ‹=Gy?­jY>Þ¦Ø=¬p?­¸©>¡mN>Ô¶>?lÊ"?﨑>¯w?œ¥L?’y= rm?¢Ñ¼>¨µ"=™x?D y>h\ñ<_š{?ŸŒ:>Ê<ïØ|?^2>×TÂ)—×<œ@|?{,>Ij =]‰y?ôùa>Â]V=¯÷o?©Q°>žÊÊ=1 >?¦û(?(ë>óW?l¢U?6“;|lm?y¿>2þ:x?¸”|> 0 :ï˜{?‡=>jhQ9é×|?iO > ¬¤¸[}?ã>š¢Ö¹t?|?R­.>/;~ºr‡y?çÆd>–<»Oóo?zn²>²á¸»‰”>?ë*?(íµèÞ½[x?Î}y>´¢è¼Hš{?¾º:>IKǼçØ|?¦C>ڜüQ}?„‡>eEÞ¼¯@|?\W,>T½‰y?¦a>xóh½Høo?ð¯>iØá½¯ >?ú…(?Ñ$ã½ZN?:uW?jîè½Æ{m?Ͷ>~Zš½x? p>e-h½þœ{?Ï›3>CÓE½ÜÚ|?L/>iA½& }?Ž–>¥Z½C|?ªž%>!ž½€y?+ÈX>†á½dp?~ú¨>ú€Y¾Z·>?mâ!?ôÊw¾ol?\:P?Çù+¾Ž…m?AŠª>å}ã½–"x?•¹`>¤Ëª½ŽŸ{?½(>FA‘½´Ü|?Sb>3r½ã }?† >ÆiŸ½-E|?©Ù>›–ѽ+‘y?·žJ>^4$¾× p?ýã>ž¾’Ì>?ÜF?Xª»¾ÒŠ?<ÛC?è^¾#Œm?@ñš>ôH¾œ%x? L>gôܽB¡{?™›>ƒ»»½ðÝ|?Á8>ï¶½ }?Kuú=¬–ͽ›F|?§m >Áÿ¾’“y?­7>ßFS¾Zp?>>9˾™Ú>?È ?zmö¾Q ?ôÅ2?ì †¾€Žm?6¹‡>‘$1¾±&x?²2>m̾ݡ{?Æ>–†á½[Þ|?câ=þ;Û½o }?RÛ=ý£ö½G|?’wõ=Y×!¾_”y?s >î}¾*p?+Ñy>6Ió¾Oß>?Ã8ï>@x¿o©?Vs?P›™¾qŒm?Îb>ªÉJ¾¸%x?Â>õö¾M¡{?-µÞ= û¾ôÝ|?c¼= ¦ú½ }?\¶=yë ¾•F|??Ì=—×8¾y“y?¡h>·p¾p?ÉO>& ¿TÚ>?b‘Æ>f,¿¥?Z?W~©¾%†m?ð0>ý³_¾Ñ"x?mç=Å–'¾¡Ÿ{?Ô¼¬=2¾ºÜ|?âú‘=n ¾â }?A)=~<¾E|?2í=ňK¾þy?ÊÎ=öž¾H p?= >™¿-Ì>?Ìë˜>î¦>¿§“?,éÏ>w_µ¾|m?~Ÿñ=®Vo¾^x?È–ž=#B3¾{?Œ[l=Ä ¾æÚ|?ÍbG=©£¾$ }?Sj@=Wã%¾îB|?|ÎV=­jY¾Gy?ŽÓ‹=­¸©¾¬p?Þ¦Ø=lÊ"¿Ô¶>?¡mN>œ¥L¿¯w?﨑>¢Ñ¼¾ rm?’y=D y¾™x?õ"=ŸŒ:¾_š{?h\ñ<^2¾ïØ|?Ê?žÊÊ=l¢U¿óW?(ë>y¿¾|lm?·“;¸”|¾x?2þ:‡=¾ï˜{? 0 :iO ¾é×|?jhQ9ã¾[}?“¬¤¸R­.¾t?|?š¢Ö¹çÆd¾r‡y?@;~ºzn²¾Oóo?–<»ë*¿‰”>?²á¸»ßEY¿gC?(íµ<ð0½¾~qm?-òf½Î}y¾[x?èÞ½¾º:¾Hš{?´¢è¼¦C¾çØ|?IKǼ„‡¾Q}?ڜü\W,¾¯@|?eEÞ¼¦a¾‰y?T½ð¯¾Høo?xóh½ú…(¿¯ >?iØá½:uW¿ZN?Ñ$ã½Í¶¾Æ{m?jîè½ p¾x?~Zš½Ï›3¾þœ{?e-h½L/¾ÜÚ|?CÓE½Ž–¾& }?iA½ªž%¾C|?¥Z½+ÈX¾€y?!ž½~ú¨¾dp?†á½mâ!¿Z·>?ú€Y¾\:P¿ol?ôÊw¾AŠª¾Ž…m?Çù+¾•¹`¾–"x?å}ã½½(¾ŽŸ{?¤Ëª½Sb¾´Ü|?FA‘½† ¾ã }?3r½©Ù¾-E|?ÆiŸ½·žJ¾+‘y?›–ѽýã¾× p?^4$¾ÜF¿’Ì>?ž¾<ÛC¿ÒŠ?Xª»¾@ñš¾#Œm?è^¾ L¾œ%x?ôH¾™›¾B¡{?gôܽÁ8¾ðÝ|?ƒ»»½Kuú½ }?ï¶½§m ¾›F|?¬–ͽ­7¾’“y?Áÿ¾>¾Zp?ßFS¾È ¿™Ú>?9˾ôÅ2¿Q ?zmö¾6¹‡¾€Žm?ì †¾²2¾±&x?‘$1¾Æ¾Ý¡{?m̾câ½[Þ|?–†á½RÛ½o }?þ;Û½’wõ½G|?ý£ö½s ¾_”y?Y×!¾+Ñy¾*p?î}¾Ã8ï¾Oß>?6Ió¾Vs¿o©?@x¿Îb¾qŒm?P›™¾Â¾¸%x?ªÉJ¾-µÞ½M¡{?õö¾c¼½ôÝ|? û¾\¶½ }? ¦ú½?̽•F|?yë ¾¡h¾y“y?—×8¾Ã‰O¾p?·p¾b‘ƾTÚ>?& ¿Z¿¥?f,¿ð0¾%†m?W~©¾mç½Ñ"x?ý³_¾Ô¼¬½¡Ÿ{?Å–'¾âú‘½ºÜ|?2¾A)½â }?n ¾2í½E|?~<¾Êνþy?ňK¾= ¾H p?öž¾Ì똾-Ì>?™¿,éϾ§“?î¦>¿~Ÿñ½|m?w_µ¾È–ž½^x?®Vo¾Œ[l½{?#B3¾ÍbG½æÚ|?Ä ¾Sj@½$ }?©£¾|ÎV½îB|?Wã%¾ŽÓ‹½Gy?­jY¾Þ¦Ø½¬p?­¸©¾¡mN¾Ô¶>?lÊ"¿ï¨‘¾¯w?œ¥L¿’y½ rm?¢Ñ¼¾Ãµ"½™x?D y¾h\ñ¼_š{?ŸŒ:¾Ê¼ïØ|?^2¾×T¼M}?S޾)—×¼œ@|?{,¾Ij ½]‰y?ôùa¾Â]V½¯÷o?©Q°¾žÊʽ1 >?¦û(¿(ë¾óW?l¢U¿·“»|lm?y¿¾2þºx?¸”|¾±0 ºï˜{?‡=¾jhQ¹é×|?iO ¾ ¬¤8[}?㾚¢Ö9t?|?R­.¾/;~:r‡y?çÆd¾–<;Oóo?zn²¾²á¸;‰”>?ë*¿(íµ¼gC?ßEY¿-òf=~qm?ð0½¾èÞ=[x?Î}y¾´¢è?ú…(¿Ñ$ã=ZN?:uW¿jîè=Æ{m?Ͷ¾~Zš=x? p¾e-h=þœ{?Ï›3¾CÓE=ÜÚ|?L/¾iA=& }?Ž–¾¥Z=C|?ªž%¾!ž=€y?+ÈX¾†á=dp?~ú¨¾ú€Y>Z·>?mâ!¿ôÊw>ol?\:P¿Çù+>Ž…m?AŠª¾å}ã=–"x?•¹`¾²Ëª=ŽŸ{?½(¾FA‘=´Ü|?Sb¾3r=ã }?† ¾ÆiŸ=-E|?©Ù¾›–Ñ=+‘y?·žJ¾^4$>× p?ý㾞>’Ì>?ÜF¿Xª»>ÒŠ?<ÛC¿è^>#Œm?@ñš¾ôH>œ%x? L¾gôÜ=B¡{?™›¾ƒ»»=ðÝ|?Á8¾ï¶= }?Kuú½¬–Í=›F|?§m ¾Áÿ>’“y?­7¾ßFS>Zp?>¾9Ë>™Ú>?È ¿zmö>Q ?ôÅ2¿ì †>€Žm?6¹‡¾‘$1>±&x?²2¾mÌ>Ý¡{?ƾ–†á=[Þ|?câ½þ;Û=o }?RÛ½ý£ö=G|?’wõ½Y×!>_”y?s ¾î}>*p?+Ñy¾6Ió>Oß>?Ã8ï¾@x?o©?Vs¿P›™>qŒm?Îb¾ªÉJ>¸%x?¾õö>M¡{?-µÞ½ û>ôÝ|?c¼½ ¦ú= }?\¶½yë >•F|??̽—×8>y“y?¡h¾·p>p?ÉO¾& ?TÚ>?b‘ƾf,?¥?Z¿W~©>%†m?ð0¾ý³_>Ñ"x?mç½Å–'>¡Ÿ{?Ô¼¬½2>ºÜ|?âú‘½n >â }?A)½~<>E|?2í½ÅˆK>þy?Êνöž>H p?= ¾™?-Ì>?Ì똾î¦>?§“?,éϾw_µ>|m?~Ÿñ½®Vo>^x?»–ž½#B3>{?q[l½Ä >æÚ|?ÍbG½©£>$ }?Sj@½Wã%>îB|?|ÎV½­jY>Gy?ŽÓ‹½­¸©>¬p?ަؽlÊ"?Ô¶>?¡mN¾œ¥L?¯w?﨑¾¢Ñ¼> rm?’y½D y>™x?õ"½ŸŒ:>_š{?h\ñ¼^2>ïØ|?ʼSŽ>M}?×T¼{,>œ@|?)—×¼ôùa>]‰y?Ij ½©Q°>¯÷o?Ý]V½¦û(?1 >?¬Êʽl¢U?óW?(ë¾              !! !""" # # #!!$!!$""%##&##&##&$$'$$'$$'%%(%%(%%(&&)&&)&&*''+''+''+((,((,  ((,))-))-! !))-**.**."!"**.++/++/ #" #++/,,0,,0!!$ #""%--1..2..2##&""%##&..2//3//3$$'##&$$'//3004004%%($$'%%(004115115&&)%%(&&*116227227''+&&*''+227338338((,''+((,338449449))-((,))-44955:55:**.))-**.55:66;66;++/**.++/66;77<77<,,0++/--188=99>99>..2--1..299>::?::?//3..2//3::?;;@;;@004//3004;;@<<A<<A115004116<<B==C==C227116227==C>>D>>D338227338>>D??E??E449338449??E@@F@@F55:44955:@@FAAGAAG66;55:66;AAGBBHBBH77<66;88=CCIDDJDDJ99>88=99>DDJEEKEEK::?99>::?EEKFFLFFL;;@::?;;@FFLGGMGGM<<A;;@<<BGGNHHOHHO==C<<B==CHHOIIPIIP>>D==C>>DIIPJJQJJQ??E>>D??EJJQKKRKKR@@F??E@@FKKRLLSLLSAAG@@FAAGLLSMMTMMTBBHAAGCCINNUOOVOOVDDJCCIDDJOOVPPWPPWEEKDDJEEKPPWQQXQQXFFLEEKFFLQQXRRYRRYGGMFFLGGNRRZSS[SS[HHOGGNHHOSS[TT\TT\IIPHHOIIPTT\UU]UU]JJQIIPJJQUU]VV^VV^KKRJJQKKRVV^WW_WW_LLSKKRLLSWW_XX`XX`MMTLLSNNUYYaZZbZZbOOVNNUOOVZZb[[c[[cPPWOOVPPW[[c\\d\\dQQXPPWQQX\\d]]e]]eRRYQQXRRZ]]f^^g^^gSS[RRZSS[^^g__h__hTT\SS[TT\__h``i``iUU]TT\UU]``iaajaajVV^UU]VV^aajbbkbbkWW_VV^WW_bbkcclcclXX`WW_YYaddmeeneenZZbYYaZZbeenffoffo[[cZZb[[cffoggpggp\\d[[c\\dggphhqhhq]]e\\d]]fhhriisiis^^g]]f^^giisjjtjjt__h^^g__hjjtkkukku``i__h``ikkullvllvaaj``iaajllvmmwmmwbbkaajbbkmmwnnxnnxcclbbkddmooyppzppzeenddmeenppzqq{qq{ffoeenffoqq{rr|rr|ggpffoggprr|ss}ss}hhqggphhrss~ttttiishhriisttuu€uu€jjtiisjjtuu€vvvvkkujjtkkuvvww‚ww‚llvkkullvww‚xxƒxxƒmmwllvmmwxxƒyy„yy„nnxmmwooyzz…{{†{{†ppzooyppz{{†||‡||‡qq{ppzqq{||‡}}ˆ}}ˆrr|qq{rr|}}ˆ~~‰~~‰ss}rr|ss~~~Š‹‹ttss~tt‹€€Œ€€Œuu€ttuu€€€Œvvuu€vv‚‚Ž‚‚Žww‚vvww‚‚‚Žƒƒƒƒxxƒww‚xxƒƒƒ„„„„yy„xxƒzz………‘††’††’{{†zz…{{†††’‡‡“‡‡“||‡{{†||‡‡‡“ˆˆ”ˆˆ”}}ˆ||‡}}ˆˆˆ”‰‰•‰‰•~~‰}}ˆ~~Љ‰–ŠŠ—ŠŠ—‹~~Š‹ŠŠ—‹‹˜‹‹˜€€Œ‹€€Œ‹‹˜ŒŒ™ŒŒ™€€ŒŒŒ™šš‚‚Ž‚‚ŽšŽŽ›ŽŽ›ƒƒ‚‚ŽƒƒŽŽ›œœ„„ƒƒ……‘‘‘ž‘‘ž††’……‘††’‘‘ž’’Ÿ’’Ÿ‡‡“††’‡‡“’’Ÿ““ ““ ˆˆ”‡‡“ˆˆ”““ ””¡””¡‰‰•ˆˆ”‰‰–””¢••£••£ŠŠ—‰‰–ŠŠ—••£––¤––¤‹‹˜ŠŠ—‹‹˜––¤——¥——¥ŒŒ™‹‹˜ŒŒ™——¥˜˜¦˜˜¦šŒŒ™š˜˜¦™™§™™§ŽŽ›šŽŽ›™™§šš¨šš¨œŽŽ›››©œœªœœª‘‘ž‘‘žœœª««’’Ÿ‘‘ž’’Ÿ«žž¬žž¬““ ’’Ÿ““ žž¬ŸŸ­ŸŸ­””¡““ ””¢ŸŸ®  ¯  ¯••£””¢••£  ¯¡¡°¡¡°––¤••£––¤¡¡°¢¢±¢¢±——¥––¤——¥¢¢±££²££²˜˜¦——¥˜˜¦££²¤¤³¤¤³™™§˜˜¦™™§¤¤³¥¥´¥¥´šš¨™™§››©¦¦µ§§¶§§¶œœª››©œœª§§¶¨¨·¨¨·«œœª«¨¨·©©¸©©¸žž¬«žž¬©©¸ªª¹ªª¹ŸŸ­žž¬ŸŸ®ªªº««»««»  ¯ŸŸ®  ¯««»¬¬¼¬¬¼¡¡°  ¯¡¡°¬¬¼­­½­­½¢¢±¡¡°¢¢±­­½®®¾®®¾££²¢¢±££²®®¾¯¯¿¯¯¿¤¤³££²¤¤³¯¯¿°°À°°À¥¥´¤¤³¦¦µ±±Á²²Â²²Â§§¶¦¦µ§§¶²²Â³³Ã³³Ã¨¨·§§¶¨¨·³³Ã´´Ä´´Ä©©¸¨¨·©©¸´´ÄµµÅµµÅªª¹©©¸ªªºµµÆ¶¶Ç¶¶Ç««»ªªº««»¶¶Ç··È··È¬¬¼««»¬¬¼··È¸¸É¸¸É­­½¬¬¼­­½¸¸É¹¹Ê¹¹Ê®®¾­­½®®¾¹¹ÊººËººË¯¯¿®®¾¯¯¿ººË»»Ì»»Ì°°À¯¯¿±±Á¼¼Í½½Î½½Î²²Â±±Á²²Â½½Î¾¾Ï¾¾Ï³³Ã²²Â³³Ã¾¾Ï¿¿Ð¿¿Ð´´Ä³³Ã´´Ä¿¿ÐÀÀÑÀÀѵµÅ´´ÄµµÆÀÀÒÁÁÓÁÁÓ¶¶ÇµµÆ¶¶ÇÁÁÓÂÂÔÂÂÔ··È¶¶Ç··ÈÂÂÔÃÃÕÃÃÕ¸¸É··È¸¸ÉÃÃÕÄÄÖÄÄÖ¹¹Ê¸¸É¹¹ÊÄÄÖÅÅ×ÅÅ׺ºË¹¹ÊººËÅÅ×ÆÆØÆÆØ»»ÌººË¼¼ÍÇÇÙÈÈÚÈÈÚ½½Î¼¼Í½½ÎÈÈÚÉÉÛÉÉÛ¾¾Ï½½Î¾¾ÏÉÉÛÊÊÜÊÊÜ¿¿Ð¾¾Ï¿¿ÐÊÊÜËËÝËËÝÀÀÑ¿¿ÐÀÀÒËËÞÌÌßÌÌßÁÁÓÀÀÒÁÁÓÌÌßÍÍàÍÍàÂÂÔÁÁÓÂÂÔÍÍàÎÎáÎÎáÃÃÕÂÂÔÃÃÕÎÎáÏÏâÏÏâÄÄÖÃÃÕÄÄÖÏÏâÐÐãÐÐãÅÅ×ÄÄÖÅÅ×ÐÐãÑÑäÑÑ䯯ØÅÅ×ÇÇÙÒÒåÓÓæÓÓæÈÈÚÇÇÙÈÈÚÓÓæÔÔçÔÔçÉÉÛÈÈÚÉÉÛÔÔçÕÕèÕÕèÊÊÜÉÉÛÊÊÜÕÕèÖÖéÖÖéËËÝÊÊÜËËÞÖÖê××ë××ëÌÌßËËÞÌÌß××ëØØìØØìÍÍàÌÌßÍÍàØØìÙÙíÙÙíÎÎáÍÍàÎÎáÙÙíÚÚîÚÚîÏÏâÎÎáÏÏâÚÚîÛÛïÛÛïÐÐãÏÏâÐÐãÛÛïÜÜðÜÜðÑÑäÐÐãÒÒåÝÝñÞÞòÞÞòÓÓæÒÒåÓÓæÞÞòßßóßßóÔÔçÓÓæÔÔçßßóààôààôÕÕèÔÔçÕÕèààôááõááõÖÖéÕÕèÖÖêááöââ÷ââ÷××ëÖÖê××ëââ÷ããøããøØØì××ëØØìããøääùääùÙÙíØØìÙÙíääùååúååúÚÚîÙÙíÚÚîååúææûææûÛÛïÚÚîÛÛïææûççüççüÜÜðÛÛïÝÝñèèýééþééþÞÞòÝÝñÞÞòééþêêÿêêÿßßóÞÞòßßóêêÿëëëëààôßßóààôëëììììááõààôááöììííííââ÷ááöââ÷ííîîîîããøââ÷ããøîîïïïïääùããøääùïïððððååúääùååúððññññææûååúææûññòòòòççüææûèèýóó ôô ôô ééþèèýééþôô õõ õõ êêÿééþêêÿõõ öö öö ëëêêÿëëöö ÷÷ ÷÷ ììëëìì÷÷øøøøííììííøøùùùùîîííîîùùúúúúïïîîïïúúûûûûððïïððûûüüüüññððññüüýýýýòòññóó þþÿÿÿÿôô óó ôô ÿÿõõ ôô õõ öö õõ öö ÷÷ öö ÷÷øø÷÷øøùùøøùùúúùùúúûûúúûûüüûûüü  ýýüüþþ  !  "  "ÿÿþþÿÿ  "  #  #ÿÿ  #  $  $  $  %  %  &'''((()))***+++,,   !-..  "  !  ".//  #  "  #/00  $  #  $011  %  $  &233'  &'344('(455)()566*)*677+*+788,+-9  :  :.-.  :!!;!!;/./!!;""<""<0/0""<##=##=102##>$$?$$?323$$?%%@%%@434%%@&&A&&A545&&A''B''B656''B((C((C767((C))D))D879**E++F++F  :9  :++F,,G,,G!!;  :!!;,,G--H--H""<!!;""<--H..I..I##=""<##>..J//K//K$$?##>$$?//K00L00L%%@$$?%%@00L11M11M&&A%%@&&A11M22N22N''B&&A''B22N33O33O((C''B((C33O44P44P))D((C**E55Q66R66R++F**E++F66R77S77S,,G++F,,G77S88T88T--H,,G--H88T99U99U..I--H..J99V::W::W//K..J//K::W;;X;;X00L//K00L;;X<<Y<<Y11M00L11M<<Y==Z==Z22N11M22N==Z>>[>>[33O22N33O>>[??\??\44P33O55Q@@]AA^AA^66R55Q66RAA^BB_BB_77S66R77SBB_CC`CC`88T77S88TCC`DDaDDa99U88T99VDDbEEcEEc::W99V::WEEcFFdFFd;;X::W;;XFFdGGeGGe<<Y;;X<<YGGeHHfHHf==Z<<Y==ZHHfIIgIIg>>[==Z>>[IIgJJhJJh??\>>[@@]KKiLLjLLjAA^@@]AA^LLjMMkMMkBB_AA^BB_MMkNNlNNlCC`BB_CC`NNlOOmOOmDDaCC`DDbOOnPPoPPoEEcDDbEEcPPoQQpQQpFFdEEcFFdQQpRRqRRqGGeFFdGGeRRqSSrSSrHHfGGeHHfSSrTTsTTsIIgHHfIIgTTsUUtUUtJJhIIgKKiVVuWWvWWvLLjKKiLLjWWvXXwXXwMMkLLjMMkXXwYYxYYxNNlMMkNNlYYxZZyZZyOOmNNlOOnZZz[[{[[{PPoOOnPPo[[{\\|\\|QQpPPoQQp\\|]]}]]}RRqQQpRRq]]}^^~^^~SSrRRqSSr^^~____TTsSSrTTs__``€``€UUtTTsVVuaabb‚bb‚WWvVVuWWvbb‚ccƒccƒXXwWWvXXwccƒdd„dd„YYxXXwYYxdd„ee…ee…ZZyYYxZZzee†ff‡ff‡[[{ZZz[[{ff‡ggˆggˆ\\|[[{\\|ggˆhh‰hh‰]]}\\|]]}hh‰iiŠiiŠ^^~]]}^^~iiŠjj‹jj‹__^^~__jj‹kkŒkkŒ``€__aallmmŽmmŽbb‚aabb‚mmŽnnnnccƒbb‚ccƒnnoooodd„ccƒdd„oopp‘pp‘ee…dd„ee†pp’qq“qq“ff‡ee†ff‡qq“rr”rr”ggˆff‡ggˆrr”ss•ss•hh‰ggˆhh‰ss•tt–tt–iiŠhh‰iiŠtt–uu—uu—jj‹iiŠjj‹uu—vv˜vv˜kkŒjj‹llww™xxšxxšmmŽllmmŽxxšyy›yy›nnmmŽnnyy›zzœzzœoonnoozzœ{{{{pp‘oopp’{{ž||Ÿ||Ÿqq“pp’qq“||Ÿ}} }} rr”qq“rr”}} ~~¡~~¡ss•rr”ss•~~¡¢¢tt–ss•tt–¢€€£€€£uu—tt–uu—€€£¤¤vv˜uu—ww™‚‚¥ƒƒ¦ƒƒ¦xxšww™xxšƒƒ¦„„§„„§yy›xxšyy›„„§……¨……¨zzœyy›zzœ……¨††©††©{{zzœ{{ž††ª‡‡«‡‡«||Ÿ{{ž||Ÿ‡‡«ˆˆ¬ˆˆ¬}} ||Ÿ}} ˆˆ¬‰‰­‰‰­~~¡}} ~~¡‰‰­ŠŠ®ŠŠ®¢~~¡¢ŠŠ®‹‹¯‹‹¯€€£¢€€£‹‹¯ŒŒ°ŒŒ°¤€€£‚‚¥±ŽŽ²ŽŽ²ƒƒ¦‚‚¥ƒƒ¦ŽŽ²³³„„§ƒƒ¦„„§³´´……¨„„§……¨´‘‘µ‘‘µ††©……¨††ª‘‘¶’’·’’·‡‡«††ª‡‡«’’·““¸““¸ˆˆ¬‡‡«ˆˆ¬““¸””¹””¹‰‰­ˆˆ¬‰‰­””¹••º••ºŠŠ®‰‰­ŠŠ®••º––»––»‹‹¯ŠŠ®‹‹¯––»——¼——¼ŒŒ°‹‹¯±˜˜½™™¾™™¾ŽŽ²±ŽŽ²™™¾šš¿šš¿³ŽŽ²³šš¿››À››À´³´››ÀœœÁœœÁ‘‘µ´‘‘¶œœÂÃÃ’’·‘‘¶’’·ÃžžÄžžÄ““¸’’·““¸žžÄŸŸÅŸŸÅ””¹““¸””¹ŸŸÅ  Æ  Æ••º””¹••º  Æ¡¡Ç¡¡Ç––»••º––»¡¡Ç¢¢È¢¢È——¼––»˜˜½££É¤¤Ê¤¤Ê™™¾˜˜½™™¾¤¤Ê¥¥Ë¥¥Ëšš¿™™¾šš¿¥¥Ë¦¦Ì¦¦Ì››Àšš¿››À¦¦Ì§§Í§§ÍœœÁ››ÀœœÂ§§Î¨¨Ï¨¨ÏÜœÂè¨Ï©©Ð©©ÐžžÄޞĩ©ÐªªÑªªÑŸŸÅžžÄŸŸÅªªÑ««Ò««Ò  ÆŸŸÅ  Æ««Ò¬¬Ó¬¬Ó¡¡Ç  Æ¡¡Ç¬¬Ó­­Ô­­Ô¢¢È¡¡Ç££É®®Õ¯¯Ö¯¯Ö¤¤Ê££É¤¤Ê¯¯Ö°°×°°×¥¥Ë¤¤Ê¥¥Ë°°×±±Ø±±Ø¦¦Ì¥¥Ë¦¦Ì±±Ø²²Ù²²Ù§§Í¦¦Ì§§Î²²Ú³³Û³³Û¨¨Ï§§Î¨¨Ï³³Û´´Ü´´Ü©©Ð¨¨Ï©©Ð´´ÜµµÝµµÝªªÑ©©ÐªªÑµµÝ¶¶Þ¶¶Þ««ÒªªÑ««Ò¶¶Þ··ß··ß¬¬Ó««Ò¬¬Ó··ß¸¸à¸¸à­­Ô¬¬Ó®®Õ¯¯Ö®®Õ¯¯Ö°°×¯¯Ö°°×±±Ø°°×±±Ø ²²Ù±±Ø²²Ú  ³³Û²²Ú³³Û ´´Ü³³Û´´ÜµµÝ´´ÜµµÝ¶¶ÞµµÝ¶¶Þ··ß¶¶Þ··ß¸¸à··ß¹¹á¹¹áººâººâ¹¹á»»ã»»ã¼¼äººâ¼¼ä»»ã½½å½½å¾¾æ¼¼ä¾¾æ½½å¿¿ç¿¿çÀÀè¾¾æÀÀè¿¿çÁÁéÁÁéÂÂêÀÀèÂÂêÁÁéÃÃëÃÃëÄÄìÂÂêÄÄìÃÃëÅÅíÅÅíÆÆîÄÄìÆÆîÅÅíÇÇïÇÇïÈÈðÆÆîÈÈðÇÇïÉÉñÉÉñÊÊòÈÈðÊÊòÉÉñËËóËËóÌÌôÊÊò!!$ÍÍõÍÍõ¹¹á¹¹áÍÍõÎÎöÎÎö»»ã¹¹á»»ãÎÎöÏÏ÷ÏÏ÷½½å»»ã½½åÏÏ÷ÐÐøÐÐø¿¿ç½½å¿¿çÐÐøÑÑùÑÑùÁÁé¿¿çÁÁéÑÑùÒÒúÒÒúÃÃëÁÁéÃÃëÒÒúÓÓûÓÓûÅÅíÃÃëÅÅíÓÓûÔÔüÔÔüÇÇïÅÅíÇÇïÔÔüÕÕýÕÕýÉÉñÇÇïÉÉñÕÕýÖÖþÖÖþËËóÉÉñ!!$,,0××ÿ××ÿÍÍõ!!$ÍÍõ××ÿØØØØÎÎöÍÍõÎÎöØØÙÙÙÙÏÏ÷ÎÎöÏÏ÷ÙÙÚÚÚÚÐÐøÏÏ÷ÐÐøÚÚÛÛÛÛÑÑùÐÐøÑÑùÛÛÜÜÜÜÒÒúÑÑùÒÒúÜÜÝÝÝÝÓÓûÒÒúÓÓûÝÝÞÞÞÞÔÔüÓÓûÔÔüÞÞßßßßÕÕýÔÔüÕÕýßßààààÖÖþÕÕý,,077<áá áá ××ÿ,,0××ÿáá ââ ââ ØØ××ÿØØââ ãã ãã ÙÙØØÙÙãã ää ää ÚÚÙÙÚÚää åå åå ÛÛÚÚÛÛåå ææææÜÜÛÛÜÜææççççÝÝÜÜÝÝççèèèèÞÞÝÝÞÞèèééééßßÞÞßßééêêêêààßß77<BBHëëëëáá 77<áá ëëììììââ áá ââ ììííííãã ââ ãã ííîîîîää ãã ää îîïïïïåå ää åå ïïððððææåå ææððññññççææççññòòòòèèççèèòòóóóóééèèééóóôôôôêêééBBHMMTõõõõëëBBHëëõõööööììëëììöö÷÷÷÷ííììíí÷÷øø øø îîííîîøø ùù!ùù!ïïîîïïùù!úú"úú"ððïïððúú"ûû#ûû#ññððññûû#üü$üü$òòññòòüü$ýý%ýý%óóòòóóýý%þþ&þþ&ôôóóMMTXX`ÿÿ'ÿÿ'õõMMTõõÿÿ'((ööõõöö())÷÷öö÷÷)**øø ÷÷øø *++ùù!øø ùù!+,,úú"ùù!úú",--ûû#úú"ûû#-..üü$ûû#üü$.//ýý%üü$ýý%/00þþ&ýý%XX`ccl  1  1ÿÿ'XX`ÿÿ'  1  2  2(ÿÿ'(  2  3  3)()  3  4  4*)*  4  5  5+*+  566,+,677-,-788.-.899/./9::0/cclnnx;;  1ccl  1;<<  2  1  2<==  3  2  3=>>  4  3  4>??  5  4  5?@@6  56@AA767ABB878BCC989CDD:9nnxyy„EE;nnx;EFF<;<FGG=<=G  H  H>=>  H!!I!!I?>?!!I""J""J@?@""J##K##KA@A##K$$L$$LBAB$$L%%M%%MCBC%%M&&N&&NDCyy„„„''O''OEyy„E''O((P((PFEF((P))Q))QGFG))Q**R**R  HG  H**R++S++S!!I  H!!I++S,,T,,T""J!!I""J,,T--U--U##K""J##K--U..V..V$$L##K$$L..V//W//W%%M$$L%%M//W00X00X&&N%%M„„œ11Y11Y''O„„''O11Y22Z22Z((P''O((P22Z33[33[))Q((P))Q33[44\44\**R))Q**R44\55]55]++S**R++S55]66^66^,,T++S,,T66^77_77_--U,,T--U77_88`88`..V--U..V88`99a99a//W..V//W99a::b::b00X//Wœšš¨;;c;;c11Yœ11Y;;c<<d<<d22Z11Y22Z<<d==e==e33[22Z33[==e>>f>>f44\33[44\>>f??g??g55]44\55]??g@@h@@h66^55]66^@@hAAiAAi77_66^77_AAiBBjBBj88`77_88`BBjCCkCCk99a88`99aCCkDDlDDl::b99ašš¨¥¥´EEmEEm;;cšš¨;;cEEmFFnFFn<<d;;c<<dFFnGGoGGo==e<<d==eGGoHHpHHp>>f==e>>fHHpIIqIIq??g>>f??gIIqJJrJJr@@h??g@@hJJrKKsKKsAAi@@hAAiKKsLLtLLtBBjAAiBBjLLtMMuMMuCCkBBjCCkMMuNNvNNvDDlCCk¥¥´°°ÀOOwOOwEEm¥¥´EEmOOwPPxPPxFFnEEmFFnPPxQQyQQyGGoFFnGGoQQyRRzRRzHHpGGoHHpRRzSS{SS{IIqHHpIIqSS{TT|TT|JJrIIqJJrTT|UU}UU}KKsJJrKKsUU}VV~VV~LLtKKsLLtVV~WWWWMMuLLtMMuWWXX€XX€NNvMMu°°À»»ÌYYYYOOw°°ÀOOwYYZZ‚ZZ‚PPxOOwPPxZZ‚[[ƒ[[ƒQQyPPxQQy[[ƒ\\„\\„RRzQQyRRz\\„]]…]]…SS{RRzSS{]]…^^†^^†TT|SS{TT|^^†__‡__‡UU}TT|UU}__‡``ˆ``ˆVV~UU}VV~``ˆaa‰aa‰WWVV~WWaa‰bbŠbbŠXX€WW»»ÌÆÆØcc‹cc‹YY»»ÌYYcc‹ddŒddŒZZ‚YYZZ‚ddŒeeee[[ƒZZ‚[[ƒeeffŽffŽ\\„[[ƒ\\„ffŽgggg]]…\\„]]…gghhhh^^†]]…^^†hhii‘ii‘__‡^^†__‡ii‘jj’jj’``ˆ__‡``ˆjj’kk“kk“aa‰``ˆaa‰kk“ll”ll”bbŠaa‰ÆÆØÑÑämm•mm•cc‹ÆÆØcc‹mm•nn–nn–ddŒcc‹ddŒnn–oo—oo—eeddŒeeoo—pp˜pp˜ffŽeeffŽpp˜qq™qq™ggffŽggqq™rršrršhhgghhrršss›ss›ii‘hhii‘ss›ttœttœjj’ii‘jj’ttœuuuukk“jj’kk“uuvvžvvžll”kk“ÑÑäÜÜðwwŸwwŸmm•ÑÑämm•wwŸxx xx nn–mm•nn–xx yy¡yy¡oo—nn–oo—yy¡zz¢zz¢pp˜oo—pp˜zz¢{{£{{£qq™pp˜qq™{{£||¤||¤rršqq™rrš||¤}}¥}}¥ss›rršss›}}¥~~¦~~¦ttœss›ttœ~~¦§§uuttœuu§€€¨€€¨vvžuuÜÜðççü©©wwŸÜÜðwwŸ©‚‚ª‚‚ªxx wwŸxx ‚‚ªƒƒ«ƒƒ«yy¡xx yy¡ƒƒ«„„¬„„¬zz¢yy¡zz¢„„¬……­……­{{£zz¢{{£……­††®††®||¤{{£||¤††®‡‡¯‡‡¯}}¥||¤}}¥‡‡¯ˆˆ°ˆˆ°~~¦}}¥~~¦ˆˆ°‰‰±‰‰±§~~¦§‰‰±ŠŠ²ŠŠ²€€¨§ççüòò‹‹³‹‹³©ççü©‹‹³ŒŒ´ŒŒ´‚‚ª©‚‚ªŒŒ´µµƒƒ«‚‚ªƒƒ«µŽŽ¶ŽŽ¶„„¬ƒƒ«„„¬ŽŽ¶··……­„„¬……­·¸¸††®……­††®¸‘‘¹‘‘¹‡‡¯††®‡‡¯‘‘¹’’º’’ºˆˆ°‡‡¯ˆˆ°’’º““»““»‰‰±ˆˆ°‰‰±““»””¼””¼ŠŠ²‰‰±òòýý••½••½‹‹³òò‹‹³••½––¾––¾ŒŒ´‹‹³ŒŒ´––¾——¿——¿µŒŒ´µ——¿˜˜À˜˜ÀŽŽ¶µŽŽ¶˜˜À™™Á™™Á·ŽŽ¶·™™ÁššÂššÂ¸·¸ššÂ››Ã››Ã‘‘¹¸‘‘¹››ÃœœÄœœÄ’’º‘‘¹’’ºœœÄÅÅ““»’’º““»ÅžžÆžžÆ””¼““»ýý ŸŸÇŸŸÇ••½ýý••½ŸŸÇ  È  È––¾••½––¾  È¡¡É¡¡É——¿––¾——¿¡¡É¢¢Ê¢¢Ê˜˜À——¿˜˜À¢¢Ê££Ë££Ë™™Á˜˜À™™Á££Ë¤¤Ì¤¤ÌššÂ™™ÁššÂ¤¤Ì¥¥Í¥¥Í››ÃššÂ››Ã¥¥Í¦¦Î¦¦ÎœœÄ››ÃœœÄ¦¦Î§§Ï§§ÏÅœœÄŧ§Ï¨¨Ð¨¨ÐžžÆÅ ,©©Ñ©©ÑŸŸÇ ŸŸÇ©©ÑªªÒªªÒ  ÈŸŸÇ  ÈªªÒ««Ó««Ó¡¡É  È¡¡É««Ó¬¬Ô¬¬Ô¢¢Ê¡¡É¢¢Ê¬¬Ô­­Õ­­Õ££Ë¢¢Ê££Ë­­Õ®®Ö®®Ö¤¤Ì££Ë¤¤Ì®®Ö¯¯×¯¯×¥¥Í¤¤Ì¥¥Í¯¯×°°Ø°°Ø¦¦Î¥¥Í¦¦Î°°Ø±±Ù±±Ù§§Ï¦¦Î§§Ï±±Ù²²Ú²²Ú¨¨Ð§§Ï,8³³Û³³Û©©Ñ,©©Ñ³³Û´´Ü´´ÜªªÒ©©ÑªªÒ´´ÜµµÝµµÝ««ÓªªÒ««ÓµµÝ¶¶Þ¶¶Þ¬¬Ô««Ó¬¬Ô¶¶Þ··ß··ß­­Õ¬¬Ô­­Õ··ß¸¸à¸¸à®®Ö­­Õ®®Ö¸¸à¹¹á¹¹á¯¯×®®Ö¯¯×¹¹áººâººâ°°Ø¯¯×°°Øººâ»»ã»»ã±±Ù°°Ø±±Ù»»ã¼¼ä¼¼ä²²Ú±±Ù8))D½½å½½å³³Û8³³Û½½å¾¾æ¾¾æ´´Ü³³Û´´Ü¾¾æ¿¿ç¿¿çµµÝ´´ÜµµÝ¿¿çÀÀèÀÀè¶¶ÞµµÝ¶¶ÞÀÀèÁÁéÁÁé··ß¶¶Þ··ßÁÁéÂÂêÂÂ길෷߸¸àÂÂêÃÃëÃÃ빹Ḹ๹áÃÃëÄÄìÄÄ캺⹹ẺâÄÄìÅÅíÅÅí»»ãººâ»»ãÅÅíÆÆîÆÆî¼¼ä»»ã))D44PÇÇïÇÇï½½å))D½½åÇÇïÈÈðÈÈð¾¾æ½½å¾¾æÈÈðÉÉñÉÉñ¿¿ç¾¾æ¿¿çÉÉñÊÊòÊÊòÀÀè¿¿çÀÀèÊÊòËËóËËóÁÁéÀÀèÁÁéËËóÌÌôÌÌôÂÂêÁÁéÂÂêÌÌôÍÍõÍÍõÃÃëÂÂêÃÃëÍÍõÎÎöÎÎöÄÄìÃÃëÄÄìÎÎöÏÏ÷ÏÏ÷ÅÅíÄÄìÅÅíÏÏ÷ÐÐøÐÐøÆÆîÅÅí44P??\ÑÑùÑÑùÇÇï44PÇÇïÑÑùÒÒúÒÒúÈÈðÇÇïÈÈðÒÒúÓÓûÓÓûÉÉñÈÈðÉÉñÓÓûÔÔüÔÔüÊÊòÉÉñÊÊòÔÔüÕÕýÕÕýËËóÊÊòËËóÕÕýÖÖþÖÖþÌÌôËËóÌÌôÖÖþ××ÿ××ÿÍÍõÌÌôÍÍõ××ÿØØØØÎÎöÍÍõÎÎöØØÙÙÙÙÏÏ÷ÎÎöÏÏ÷ÙÙÚÚÚÚÐÐøÏÏ÷??\JJhÛÛÛÛÑÑù??\ÑÑùÛÛÜÜÜÜÒÒúÑÑùÒÒúÜÜÝÝÝÝÓÓûÒÒúÓÓûÝÝÞÞÞÞÔÔüÓÓûÔÔüÞÞßßßßÕÕýÔÔüÕÕýßßààààÖÖþÕÕýÖÖþààáá áá ××ÿÖÖþ××ÿáá ââ ââ ØØ××ÿØØââ ãã ãã ÙÙØØÙÙãã ää ää ÚÚÙÙJJhUUtåå åå ÛÛJJhÛÛåå ææææÜÜÛÛÜÜææççççÝÝÜÜÝÝççèèèèÞÞÝÝÞÞèèééééßßÞÞßßééêêêêààßßààêêëëëëáá ààáá ëëììììââ áá ââ ììííííãã ââ ãã ííîîîîää ãã UUt``€ïïïïåå UUtåå ïïððððææåå ææððññññççææççññòòòòèèççèèòòóóóóééèèééóóôôôôêêééêêôôõõõõëëêêëëõõööööììëëììöö÷÷÷÷ííììíí÷÷øø øø îîíí``€kkŒùù!ùù!ïï``€ïïùù!úú"úú"ððïïððúú"ûû#ûû#ññððññûû#üü$üü$òòññòòüü$ýý%ýý%óóòòóóýý%þþ&þþ&ôôóóôôþþ&ÿÿ'ÿÿ'õõôôõõÿÿ'((ööõõöö())÷÷öö÷÷)**øø ÷÷kkŒvv˜++ùù!kkŒùù!+,,úú"ùù!úú",--ûû#úú"ûû#-..üü$ûû#üü$.//ýý%üü$ýý%/00þþ&ýý%þþ&0  1  1ÿÿ'þþ&ÿÿ'  1  2  2(ÿÿ'(  2  3  3)()  3  4  4*)vv˜¤  5  5+vv˜+  566,+,677-,-788.-.899/./9::0/0:;;  10  1;<<  2  1  2<==  3  2  3=>>  4  3¤ŒŒ°??  5¤  5?@@6  56@AA767ABB878BCC989CDD:9:DEE;:;EFF<;<FGG=<=G  H  H>=ŒŒ°——¼!!I!!I?ŒŒ°?!!I""J""J@?@""J##K##KA@A##K$$L$$LBAB$$L%%M%%MCBC%%M&&N&&NDCD&&N''O''OEDE''O((P((PFEF((P))Q))QGFG))Q**R**R  HG——¼¢¢È++S++S!!I——¼!!I++S,,T,,T""J!!I""J,,T--U--U##K""J##K--U..V..V$$L##K$$L..V//W//W%%M$$L%%M//W00X00X&&N%%M&&N00X11Y11Y''O&&N''O11Y22Z22Z((P''O((P22Z33[33[))Q((P))Q33[44\44\**R))Q¢¢È­­Ô55]55]++S¢¢È++S55]66^66^,,T++S,,T66^77_77_--U,,T--U77_88`88`..V--U..V88`99a99a//W..V//W99a::b::b00X//W00X::b;;c;;c11Y00X11Y;;c<<d<<d22Z11Y22Z<<d==e==e33[22Z33[==e>>f>>f44\33[­­Ô¸¸à??g??g55]­­Ô55]??g@@h@@h66^55]66^@@hAAiAAi77_66^77_AAiBBjBBj88`77_88`BBjCCkCCk99a88`99aCCkDDlDDl::b99a::bDDlEEmEEm;;c::b;;cEEmFFnFFn<<d;;c<<dFFnGGoGGo==e<<d==eGGoHHpHHp>>f==e¸¸àººâººâ??g¸¸à??gººâ¼¼ä¼¼ä@@h??g@@h¼¼ä¾¾æ¾¾æAAi@@hAAi¾¾æÀÀèÀÀèBBjAAiBBjÀÀèÂÂêÂÂêCCkBBjCCkÂÂêÄÄìÄÄìDDlCCkDDlÄÄìÆÆîÆÆîEEmDDlEEmÆÆîÈÈðÈÈðFFnEEmFFnÈÈðÊÊòÊÊòGGoFFnGGoÊÊòÌÌôÌÌôHHpGGoÌÌôËËóIIqIIqJJrÌÌôJJrIIqKKsKKsLLtJJrLLtKKsMMuMMuNNvLLtNNvMMuOOwOOwPPxNNvPPxOOwQQyQQyRRzPPxRRzQQySS{SS{TT|RRzTT|SS{UU}UU}VV~TT|VV~UU}WWWWXX€VV~XX€WWYYYYZZ‚XX€ZZ‚YY[ƒ[ƒ\„ZZ‚ËËóÖÖþ][…][…IIqËËóIIq][…^\†^\†KKsIIqKKs^\†_]‡_]‡MMuKKsMMu_]‡`^ˆ`^ˆOOwMMuOOw`^ˆa_‰a_‰QQyOOwQQya_‰b`Šb`ŠSS{QQySS{b`Šca‹ca‹UU}SS{UU}ca‹dbŒdbŒWWUU}WWdbŒececYYWWYYecf!Žf!Ž[ƒYYÖÖþààgdgd][…ÖÖþ][…gdhehe^\†][…^\†heif‘if‘_]‡^\†_]‡if‘jg’jg’`^ˆ_]‡`^ˆjg’kh“kh“a_‰`^ˆa_‰kh“li”li”b`Ša_‰b`Šli”mj•mj•ca‹b`Šca‹mj•nk–nk–dbŒca‹dbŒnk–ol—ol—ecdbŒecol—p,˜p,˜f!Žecààêêqm™qm™gdààgdqm™rnšrnšhegdhernšso›so›if‘heif‘so›tpœtpœjg’if‘jg’tpœuquqkh“jg’kh“uqvržvržli”kh“li”vržwsŸwsŸmj•li”mj•wsŸxt xt nk–mj•nk–xt yu¡yu¡ol—nk–ol—yu¡z7¢z7¢p,˜ol—êêôô{v£{v£qm™êêqm™{v£|w¤|w¤rnšqm™rnš|w¤}x¥}x¥so›rnšso›}x¥~y¦~y¦tpœso›tpœ~y¦z§z§uqtpœuqz§€{¨€{¨vržuqvrž€{¨|©|©wsŸvržwsŸ|©‚}ª‚}ªxt wsŸxt ‚}ªƒ~«ƒ~«yu¡xt yu¡ƒ~«„B¬„B¬z7¢yu¡ôôþþ&…­…­{v£ôô{v£…­†€®†€®|w¤{v£|w¤†€®‡¯‡¯}x¥|w¤}x¥‡¯ˆ‚°ˆ‚°~y¦}x¥~y¦ˆ‚°‰ƒ±‰ƒ±z§~y¦z§‰ƒ±Š„²Š„²€{¨z§€{¨Š„²‹…³‹…³|©€{¨|©‹…³Œ†´Œ†´‚}ª|©‚}ªŒ†´‡µ‡µƒ~«‚}ªƒ~«‡µŽM¶ŽM¶„B¬ƒ~«þþ&0ˆ·ˆ·…­þþ&…­ˆ·‰¸‰¸†€®…­†€®‰¸‘й‘й‡¯†€®‡¯‘й’‹º’‹ºˆ‚°‡¯ˆ‚°’‹º“Œ»“Œ»‰ƒ±ˆ‚°‰ƒ±“Œ»”¼”¼Š„²‰ƒ±Š„²”¼•޽•޽‹…³Š„²‹…³•޽–¾–¾Œ†´‹…³Œ†´–¾—¿—¿‡µŒ†´‡µ—¿˜XÀ˜XÀŽM¶‡µ0:™‘Á™‘Áˆ·0ˆ·™‘Áš’š’‰¸ˆ·‰¸š’›“Û“Ã‘Š¹‰¸‘й›“Ü”Äœ”Ä’‹º‘й’‹ºœ”ĕŕœŒ»’‹º“Œ»•Åž–Æž–Æ”¼“Œ»”¼ž–ÆŸ—ÇŸ—Ç•Ž½”¼•޽Ÿ—Ç ˜È ˜È–¾•޽–¾ ˜È¡™É¡™É—¿–¾—¿¡™É¢cÊ¢cʘXÀ—¿:D£šË£šË™‘Á:™‘Á£šË¤›Ì¤›Ìš’™‘Áš’¤›Ì¥œÍ¥œÍ›“Ú’›“Ã¥œÍ¦Î¦Îœ”Ä›“Ü”ĦΧžÏ§žÏ•Åœ”ĕŧžÏ¨ŸÐ¨ŸÐž–ƕŞ–ƨŸÐ© Ñ© ÑŸ—Çž–ÆŸ—Ç© Ñª¡Òª¡Ò ˜ÈŸ—Ç ˜Èª¡Ò«¢Ó«¢Ó¡™É ˜È¡™É«¢Ó¬nÔ¬nÔ¢cÊ¡™ÉD&&N­£Õ­£Õ£šËD£šË­£Õ®¤Ö®¤Ö¤›Ì£šË¤›Ì®¤Ö¯¥×¯¥×¥œÍ¤›Ì¥œÍ¯¥×°¦Ø°¦Ø¦Î¥œÍ¦Î°¦Ø±§Ù±§Ù§žÏ¦Î§žÏ±§Ù²¨Ú²¨Ú¨ŸÐ§žÏ¨ŸÐ²¨Ú³©Û³©Û© Ñ¨ŸÐ© Ñ³©Û´ªÜ´ªÜª¡Ò© Ñª¡Ò´ªÜµ«Ýµ«Ý«¢Óª¡Ò«¢Óµ«Ý¶yÞ¶yÞ¬nÔ«¢Ó&&N00X·¬ß·¬ß­£Õ&&N­£Õ·¬ß¸­à¸­à®¤Ö­£Õ®¤Ö¸­à¹®á¹®á¯¥×®¤Ö¯¥×¹®áº¯âº¯â°¦Ø¯¥×°¦Øº¯â»°ã»°ã±§Ù°¦Ø±§Ù»°ã¼±ä¼±ä²¨Ú±§Ù²¨Ú¼±ä½²å½²å³©Û²¨Ú³©Û½²å¾³æ¾³æ´ªÜ³©Û´ªÜ¾³æ¿´ç¿´çµ«Ý´ªÜµ«Ý¿´çÀ„èÀ„è¶yÞµ«Ý00X::bÁµéÁµé·¬ß00X·¬ßÁµé¶ê¶긭෬߸­à¶ê÷ë÷빮ḭ๮á÷ëĸìĸ캯⹮ắâĸìŹíÅ¹í»°ãº¯â»°ãÅ¹íÆºîÆºî¼±ä»°ã¼±äÆºîÇ»ïǻイ弱佲åÇ»ïȼðÈ¼ð¾³æ½²å¾³æÈ¼ðɽñɽñ¿´ç¾³æ¿´çɽñÊòÊòÀ„è¿´ç::bDDl˾ó˾óÁµé::bÁµé˾óÌ¿ôÌ¿ô¶êÁµé¶êÌ¿ôÍÀõÍÀõ÷ë¶ê÷ëÍÀõÎÁöÎÁöĸì÷ëĸìÎÁöÏÂ÷ÏÂ÷ŹíĸìŹíÏÂ÷ÐÃøÐÃøÆºîÅ¹íÆºîÐÃøÑÄùÑÄùÇ»ïÆºîÇ»ïÑÄùÒÅúÒÅúȼðÇ»ïȼðÒÅúÓÆûÓÆûɽñȼðɽñÓÆûÔšüÔšüÊòɽñDDlNNvÕÇýÕÇý˾óDDl˾óÕÇýÖÈþÖÈþÌ¿ô˾óÌ¿ôÖÈþ×Éÿ×ÉÿÍÀõÌ¿ôÍÀõ×ÉÿØÊØÊÎÁöÍÀõÎÁöØÊÙËÙËÏÂ÷ÎÁöÏÂ÷ÙËÚÌÚÌÐÃøÏÂ÷ÐÃøÚÌÛÍÛÍÑÄùÐÃøÑÄùÛÍÜÎÜÎÒÅúÑÄùÒÅúÜÎÝÏÝÏÓÆûÒÅúÓÆûÝÏÞ¥Þ¥ÔšüÓÆûNNvXX€ßÐßÐÕÇýNNvÕÇýßÐàÑàÑÖÈþÕÇýÖÈþàÑáÒ áÒ ×ÉÿÖÈþ×ÉÿáÒ âÓ âÓ ØÊ×ÉÿØÊâÓ ãÔ ãÔ ÙËØÊÙËãÔ äÕ äÕ ÚÌÙËÚÌäÕ åÖ åÖ ÛÍÚÌÛÍåÖ æ×æ×ÜÎÛÍÜÎæ×çØçØÝÏÜÎÝÏçØè°è°Þ¥ÝÏXX€bbŠéÙéÙßÐXX€ßÐéÙêÚêÚàÑßÐàÑêÚëÛëÛáÒ àÑáÒ ëÛìÜìÜâÓ áÒ âÓ ìÜíÝíÝãÔ âÓ ãÔ íÝîÞîÞäÕ ãÔ äÕ îÞïßïßåÖ äÕ åÖ ïßðàðàæ×åÖ æ×ðàñáñáçØæ×çØñáò»ò»è°çØbbŠll”óâóâéÙbbŠéÙóâôãôãêÚéÙêÚôãõäõäëÛêÚëÛõäöåöåìÜëÛìÜöå÷æ÷æíÝìÜíÝ÷æøç øç îÞíÝîÞøç ùè!ùè!ïßîÞïßùè!úé"úé"ðàïßðàúé"ûê#ûê#ñáðàñáûê#üÆ$üÆ$ò»ñáll”vvžýë%ýë%óâll”óâýë%þì&þì&ôãóâôãþì&ÿí'ÿí'õäôãõäÿí'î(î(öåõäöåî(ï)ï)÷æöå÷æï)ð*ð*øç ÷æøç ð*ñ+ñ+ùè!øç ùè!ñ+ò,ò,úé"ùè!úé"ò,ó-ó-ûê#úé"ûê#ó-Ñ.Ñ.üÆ$ûê#vvž€€¨ô/ô/ýë%vvžýë%ô/õ0õ0þì&ýë%þì&õ0 ö1 ö1ÿí'þì&ÿí' ö1 ÷2 ÷2î(ÿí'î( ÷2 ø3 ø3ï)î(ï) ø3 ù4 ù4ð*ï)ð* ù4 ú5 ú5ñ+ð*ñ+ ú5û6û6ò,ñ+ò,û6ü7ü7ó-ò,ó-ü7Ü8Ü8Ñ.ó-€€¨ŠŠ²ý9ý9ô/€€¨ô/ý9þ:þ:õ0ô/õ0þ:ÿ;ÿ; ö1õ0 ö1ÿ;<< ÷2 ö1 ÷2<== ø3 ÷2 ø3=>> ù4 ø3 ù4>?? ú5 ù4 ú5?@@û6 ú5û6@AAü7û6ü7AçBçBÜ8ü7ŠŠ²””¼CCý9ŠŠ²ý9CDDþ:ý9þ:DEEÿ;þ:ÿ;E F F<ÿ;< F G G=<= G  H  H>=>  H! I! I?>?! I" J" J@?@" J#K#KA@A#K$òL$òLçBA””¼žžÆ%M%MC””¼C%M&N&NDCD&N'O'OEDE'O(P(P FE F(P)Q)Q G F G)Q*R*R  H G  H*R+S+S! I  H! I+S,T,T" J! I" J,T-U-U#K" J#K-U.ýV.ýV$òL#KžžÆ¨¨Ð/W/W%MžžÆ%M/W0X0X&N%M&N0X1Y1Y'O&N'O1Y2Z2Z(P'O(P2Z3[3[)Q(P)Q3[4\4\*R)Q*R4\5]5]+S*R+S5]6^6^,T+S,T6^7 _7 _-U,T-U7 _8`8`.ýV-U¨¨Ð²²Ú9!a9!a/W¨¨Ð/W9!a:"b:"b0X/W0X:"b;#c;#c1Y0X1Y;#c<$d<$d2Z1Y2Z<$d=%e=%e3[2Z3[=%e>&f>&f4\3[4\>&f?'g?'g5]4\5]?'g@(h@(h6^5]6^@(hA)iA)i7 _6^7 _A)iBjBj8`7 _²²Ú¼¼äC*kC*k9!a²²Ú9!aC*kD+lD+l:"b9!a:"bD+lE,mE,m;#c:"b;#cE,mF-nF-n<$d;#c<$dF-nG.oG.o=%e<$d=%eG.oH/pH/p>&f=%e>&fH/pI0qI0q?'g>&f?'gI0qJ1rJ1r@(h?'g@(hJ1rK2sK2sA)i@(hA)iK2sLtLtBjA)i¼¼äÆÆîM3uM3uC*k¼¼äC*kM3uN4vN4vD+lC*kD+lN4vO5wO5wE,mD+lE,mO5wP6xP6xF-nE,mF-nP6xQ7yQ7yG.oF-nG.oQ7yR8zR8zH/pG.oH/pR8zS9{S9{I0qH/pI0qS9{T:|T:|J1rI0qJ1rT:|U;}U;}K2sJ1rK2sU;}V)~V)~LtK2sÆÆîÐÐøW<W<M3uÆÆîM3uW<X=€X=€N4vM3uN4vX=€Y>Y>O5wN4vO5wY>Z?‚Z?‚P6xO5wP6xZ?‚[@ƒ[@ƒQ7yP6xQ7y[@ƒ\A„\A„R8zQ7yR8z\A„]B…]B…S9{R8zS9{]B…^C†^C†T:|S9{T:|^C†_D‡_D‡U;}T:|U;}_D‡`4ˆ`4ˆV)~U;}ÐÐøÚÚaE‰aE‰W<ÐÐøW<aE‰bFŠbFŠX=€W<X=€bFŠcG‹cG‹Y>X=€Y>cG‹dHŒdHŒZ?‚Y>Z?‚dHŒeIeI[@ƒZ?‚[@ƒeIfJŽfJŽ\A„[@ƒ\A„fJŽgKgK]B…\A„]B…gKhLhL^C†]B…^C†hLiM‘iM‘_D‡^C†_D‡iM‘j?’j?’`4ˆ_D‡ÚÚää kN“kN“aE‰ÚÚaE‰kN“lO”lO”bFŠaE‰bFŠlO”mP•mP•cG‹bFŠcG‹mP•nQ–nQ–dHŒcG‹dHŒnQ–oR—oR—eIdHŒeIoR—pS˜pS˜fJŽeIfJŽpS˜qT™qT™gKfJŽgKqT™rUšrUšhLgKhLrUšsV›sV›iM‘hLiM‘sV›tJœtJœj?’iM‘ää îîuWuWkN“ää kN“uWvXžvXžlO”kN“lO”vXžwYŸwYŸmP•lO”mP•wYŸxZ xZ nQ–mP•nQ–xZ y[¡y[¡oR—nQ–oR—y[¡z\¢z\¢pS˜oR—pS˜z\¢{]£{]£qT™pS˜qT™{]£|^¤|^¤rUšqT™rUš|^¤}_¥}_¥sV›rUšsV›}_¥~U¦~U¦tJœsV›îîøø `§`§uWîîuW`§€a¨€a¨vXžuWvXž€a¨b©b©wYŸvXžwYŸb©‚cª‚cªxZ wYŸxZ ‚cªƒd«ƒd«y[¡xZ y[¡ƒd«„e¬„e¬z\¢y[¡z\¢„e¬…f­…f­{]£z\¢{]£…f­†g®†g®|^¤{]£|^¤†g®‡h¯‡h¯}_¥|^¤}_¥‡h¯ˆ`°ˆ`°~U¦}_¥øø *‰i±‰i±`§øø `§‰i±Šj²Šj²€a¨`§€a¨Šj²‹k³‹k³b©€a¨b©‹k³Œl´Œl´‚cªb©‚cªŒl´mµmµƒd«‚cªƒd«mµŽn¶Žn¶„e¬ƒd«„e¬Žn¶o·o·…f­„e¬…f­o·p¸p¸†g®…f­†g®p¸‘q¹‘q¹‡h¯†g®‡h¯‘q¹’kº’kºˆ`°‡h¯*  4“r»“r»‰i±*‰i±“r»”s¼”s¼Šj²‰i±Šj²”s¼•t½•t½‹k³Šj²‹k³•t½–u¾–u¾Œl´‹k³Œl´–u¾—v¿—v¿mµŒl´mµ—v¿˜wÀ˜wÀŽn¶mµŽn¶˜wÀ™xÁ™xÁo·Žn¶o·™xÁšyšyÂp¸o·p¸šy›zÛzÑq¹p¸‘q¹›zÜvÄœvÄ’kº‘q¹  4>{Å{Å“r»  4“r»{Åž|Æž|Æ”s¼“r»”s¼ž|ÆŸ}ÇŸ}Ç•t½”s¼•t½Ÿ}Ç ~È ~È–u¾•t½–u¾ ~ȡɡɗv¿–u¾—v¿¡É¢€Ê¢€Ê˜wÀ—v¿˜wÀ¢€Ê£Ë£Ë™xÁ˜wÀ™xÁ£Ë¤‚̤‚Ìšy™xÁšy¤‚Ì¥ƒÍ¥ƒÍ›zÚy›zÃ¥ƒÍ¦Î¦ÎœvÄ›zÃ>  H§„ϧ„Ï{Å>{ŧ„Ϩ…Ш…О|Æ{Åž|ƨ…Щ†Ñ©†ÑŸ}Çž|ÆŸ}Ç©†Ñª‡Òª‡Ò ~ÈŸ}Ç ~Ȫ‡Ò«ˆÓ«ˆÓ¡É ~ȡɫˆÓ¬‰Ô¬‰Ô¢€Ê¡É¢€Ê¬‰Ô­ŠÕ­ŠÕ£Ë¢€Ê£Ë­ŠÕ®‹Ö®‹Ö¤‚̣ˤ‚Ì®‹Ö¯Œ×¯Œ×¥ƒÍ¤‚Ì¥ƒÍ¯Œ×°ŒØ°ŒØ¦Î¥ƒÍ  H**R±Ù±Ù§„Ï  H§„ϱٲŽÚ²ŽÚ¨…Ч„Ϩ…вŽÚ³Û³Û©†Ñ¨…Щ†Ñ³Û´Ü´Üª‡Ò©†Ñª‡Ò´Üµ‘ݵ‘Ý«ˆÓª‡Ò«ˆÓµ‘ݶ’Þ¶’Þ¬‰Ô«ˆÓ¬‰Ô¶’Þ·“ß·“ß­ŠÕ¬‰Ô­ŠÕ·“߸”ด஋֭ŠÕ®‹Ö¸”๕ṕᯌ׮‹Ö¯Œ×¹•ẗ⺗ⰌدŒ×**R44\»–ã»–ã±Ù**R±Ù»–㼗众䲎ڱٲŽÚ¼—佘彘å³Û²ŽÚ³Û½˜å¾™æ¾™æ´Ü³Û´Ü¾™æ¿šç¿šçµ‘ݴܵ‘Ý¿šçÀ›èÀ›è¶’Þµ‘ݶ’ÞÀ›èÁœéÁœé·“ß¶’Þ·“ßÁœéÂêÂ긔ී߸”àÂêÞëÞ빕Ḕ๕áÞëÄ¢ìĢ캗⹕á44\>>fÅŸíÅŸí»–ã44\»–ãÅŸíÆ îÆ î¼—ä»–ã¼—äÆ îÇ¡ïǡx弗佘åÇ¡ïÈ¢ðÈ¢ð¾™æ½˜å¾™æÈ¢ðÉ£ñÉ£ñ¿šç¾™æ¿šçÉ£ñʤòʤòÀ›è¿šçÀ›èʤòË¥óË¥óÁœéÀ›èÁœéË¥ó̦ô̦ôÂêÁœéÂê̦ôͧõͧõÞëÂêÞëͧõέöέöÄ¢ìÞë>>fHHpϨ÷Ϩ÷ÅŸí>>fÅŸíϨ÷ЩøÐ©øÆ îÅŸíÆ îЩøÑªùѪùÇ¡ïÆ îÇ¡ïѪùÒ«úÒ«úÈ¢ðÇ¡ïÈ¢ðÒ«úÓ¬ûÓ¬ûÉ£ñÈ¢ðÉ£ñÓ¬ûÔ­üÔ­üʤòÉ£ñʤòÔ­üÕ®ýÕ®ýË¥óʤòË¥óÕ®ýÖ¯þÖ¯þ̦ôË¥ó̦ôÖ¯þ×°ÿ×°ÿͧõ̦ôͧõ×°ÿظظέöͧõHHpÌÌôJJrJJrϨ÷HHpϨ÷JJrLLtLLtЩøÏ¨÷ЩøLLtNNvNNvѪùЩøÑªùNNvPPxPPxÒ«úѪùÒ«úPPxRRzRRzÓ¬ûÒ«úÓ¬ûRRzTT|TT|Ô­üÓ¬ûÔ­üTT|VV~VV~Õ®ýÔ­üÕ®ýVV~XX€XX€Ö¯þÕ®ýÖ¯þXX€ZZ‚ZZ‚×°ÿÖ¯þ×°ÿZZ‚\„\„ظװÿ\„[ƒÙ±Ù±Ú²\„ڲٱ۳۳ܴڲܴ۳ݵݵ޶ܴ޶ݵ߷߷à¸Þ¶à¸ß·á¹ Ṡ⺠à¸âº á¹ ã» ã» ä¼ âº ä¼ ã» å½ å½ æ¾ä¼ æ¾å½ ç¿ç¿èÀæ¾èÀç¿éÁéÁêÂèÀêÂéÁëÃëÃëÃêÂ[ƒf!ŽìÄìÄÙ±[ƒÙ±ìÄíÅíÅÛ³Ù±Û³íÅîÆîÆÝµÛ³ÝµîÆïÇïÇ߷ݵ߷ïÇðÈðÈá¹ ß·á¹ ðÈñÉñÉã» á¹ ã» ñÉòÊòÊå½ ã» å½ òÊóËóËç¿å½ ç¿óËôÌôÌéÁç¿éÁôÌëÃëÃëÃéÁf!Žp,˜õÍõÍìÄf!ŽìÄõÍöÎöÎíÅìÄíÅöÎ÷Ï ÷Ï îÆíÅîÆ÷Ï øÐ!øÐ!ïÇîÆïÇøÐ!ùÑ"ùÑ"ðÈïÇðÈùÑ"úÒ#úÒ#ñÉðÈñÉúÒ#ûÓ$ûÓ$òÊñÉòÊûÓ$üÔ%üÔ%óËòÊóËüÔ%ýÕ&ýÕ&ôÌóËôÌýÕ&ëÃëÃëÃôÌp,˜z7¢þÖ'þÖ'õÍp,˜õÍþÖ'ÿ×(ÿ×(öÎõÍöÎÿ×(Ø)Ø)÷Ï öÎ÷Ï Ø)Ù*Ù*øÐ!÷Ï øÐ!Ù*Ú+Ú+ùÑ"øÐ!ùÑ"Ú+Û,Û,úÒ#ùÑ"úÒ#Û,Ü-Ü-ûÓ$úÒ#ûÓ$Ü-Ý.Ý.üÔ%ûÓ$üÔ%Ý.Þ/Þ/ýÕ&üÔ%ýÕ&Þ/ëÃëÃëÃýÕz7¢„B¬ß0ß0þÖ'z7¢þÖ'ß0à1à1ÿ×(þÖ'ÿ×(à1 á2 á2Ø)ÿ×(Ø) á2 â3 â3Ù*Ø)Ù* â3 ã4 ã4Ú+Ù*Ú+ ã4 ä5 ä5Û,Ú+Û, ä5 å6 å6Ü-Û,Ü- å6æ7æ7Ý.Ü-Ý.æ7ç8ç8Þ/Ý.Þ/ç8ëÃëÃëÃÞ„B¬ŽM¶è9è9ß0„B¬ß0è9é:é:à1ß0à1é:ê;ê; á2à1 á2ê;ë<ë< â3 á2 â3ë<ì=ì= ã4 â3 ã4ì=í>í> ä5 ã4 ä5í>î?î? å6 ä5 å6î?ï@ï@æ7 å6æ7ï@ðAðAç8æ7ç8ðAëÃëÃëÃçŽM¶˜XÀñBñBè9ŽM¶è9ñBòCòCé:è9é:òCóDóDê;é:ê;óDôEôEë<ê;ë<ôEõFõFì=ë<ì=õFöGöGí>ì=í>öG÷H÷Hî?í>î?÷H øI øIï@î?ï@ øI!ùJ!ùJðAï@ðA!ùJëÃëÃëÃð˜XÀ¢cÊ"úK"úKñB˜XÀñB"úK#ûL#ûLòCñBòC#ûL$üM$üMóDòCóD$üM%ýN%ýNôEóDôE%ýN&þO&þOõFôEõF&þO'ÿP'ÿPöGõFöG'ÿP(Q(Q÷HöG÷H(Q)R)R øI÷H øI)R*S*S!ùJ øI!ùJ*SëÃëÃëÃ!ù¢cʬnÔ+T+T"úK¢cÊ"úK+T,U,U#ûL"úK#ûL,U-V-V$üM#ûL$üM-V.W.W%ýN$üM%ýN.W/X/X&þO%ýN&þO/X0Y0Y'ÿP&þO'ÿP0Y1 Z1 Z(Q'ÿP(Q1 Z2 [2 [)R(Q)R2 [3 \3 \*S)R*S3 \ëÃëÃëÃ*¬nÔ¶yÞ4 ]4 ]+T¬nÔ+T4 ]5 ^5 ^,U+T,U5 ^6_6_-V,U-V6_7`7`.W-V.W7`8a8a/X.W/X8a9b9b0Y/X0Y9b:c:c1 Z0Y1 Z:c;d;d2 [1 Z2 [;d<e<e3 \2 [3 \<eëÃëÃëÃ3 ¶yÞÀ„è=f=f4 ]¶yÞ4 ]=f>g>g5 ^4 ]5 ^>g?h?h6_5 ^6_?h@i@i7`6_7`@iAjAj8a7`8aAjBkBk9b8a9bBkClCl:c9b:cClDmDm;d:c;dDmEnEn<e;d<eEnëÃëÃëÃ<À„èÊòFoFo=fÀ„è=fFoGpGp>g=f>gGpH qH q?h>g?hH qI!rI!r@i?h@iI!rJ"sJ"sAj@iAjJ"sK#tK#tBkAjBkK#tL$uL$uClBkClL$uM%vM%vDmClDmM%vN&wN&wEnDmEnN&wëÃëÃëÃEÊòÔšüO'xO'xFoÊòFoO'xP(yP(yGpFoGpP(yQ)zQ)zH qGpH qQ)zR*{R*{I!rH qI!rR*{S+|S+|J"sI!rJ"sS+|T,}T,}K#tJ"sK#tT,}U-~U-~L$uK#tL$uU-~V.V.M%vL$uM%vV.W/€W/€N&wM%vN&wW/€ëÃëÃëÃN&ÔšüÞ¥X0X0O'xÔšüO'xX0Y1‚Y1‚P(yO'xP(yY1‚Z2ƒZ2ƒQ)zP(yQ)zZ2ƒ[3„[3„R*{Q)zR*{[3„\4…\4…S+|R*{S+|\4…]5†]5†T,}S+|T,}]5†^6‡^6‡U-~T,}U-~^6‡_7ˆ_7ˆV.U-~V._7ˆ`8‰`8‰W/€V.W/€`8‰ëÃëÃëÃW/Þ¥è°a9Ša9ŠX0Þ¥X0a9Šb:‹b:‹Y1‚X0Y1‚b:‹c;Œc;ŒZ2ƒY1‚Z2ƒc;Œd<d<[3„Z2ƒ[3„d<e=Že=Ž\4…[3„\4…e=Žf>f>]5†\4…]5†f>g?g?^6‡]5†^6‡g?h@‘h@‘_7ˆ^6‡_7ˆh@‘iA’iA’`8‰_7ˆ`8‰iA’ëÃëÃëÃ`8è°ò»jB“jB“a9Šè°a9ŠjB“kC”kC”b:‹a9Šb:‹kC”lD•lD•c;Œb:‹c;ŒlD•mE–mE–d<c;Œd<mE–nF—nF—e=Žd<e=ŽnF—oG˜oG˜f>e=Žf>oG˜pH™pH™g?f>g?pH™qIšqIšh@‘g?h@‘qIšrJ›rJ›iA’h@‘iA’rJ›ëÃëÃëÃiAò»üÆ$sKœsKœjB“ò»jB“sKœtLtLkC”jB“kC”tLuMžuMžlD•kC”lD•uMžvNŸvNŸmE–lD•mE–vNŸwO wO nF—mE–nF—wO xP¡xP¡oG˜nF—oG˜xP¡yQ¢yQ¢pH™oG˜pH™yQ¢zR£zR£qIšpH™qIšzR£{S¤{S¤rJ›qIšrJ›{S¤ëÃëÃëÃrJüÆ$Ñ.|T¥|T¥sKœüÆ$sKœ|T¥}U¦}U¦tLsKœtL}U¦~V§~V§uMžtLuMž~V§W¨W¨vNŸuMžvNŸW¨€X©€X©wO vNŸwO €X©YªYªxP¡wO xP¡Yª‚Z«‚Z«yQ¢xP¡yQ¢‚Z«ƒ[¬ƒ[¬zR£yQ¢zR£ƒ[¬„\­„\­{S¤zR£{S¤„\­ëÃëÃëÃ{SÑ.Ü8…]®…]®|T¥Ñ.|T¥…]®†^¯†^¯}U¦|T¥}U¦†^¯‡_°‡_°~V§}U¦~V§‡_°ˆ`±ˆ`±W¨~V§W¨ˆ`±‰a²‰a²€X©W¨€X©‰a²Šb³Šb³Yª€X©YªŠb³‹c´‹c´‚Z«Yª‚Z«‹c´ŒdµŒdµƒ[¬‚Z«ƒ[¬Œdµe¶e¶„\­ƒ[¬„\­e¶ëÃëÃëÄ\Ü8çBŽf·Žf·…]®Ü8…]®Žf·g¸g¸†^¯…]®†^¯g¸h¹h¹‡_°†^¯‡_°h¹‘iº‘iºˆ`±‡_°ˆ`±‘iº’j»’j»‰a²ˆ`±‰a²’j»“k¼“k¼Šb³‰a²Šb³“k¼”l½”l½‹c´Šb³‹c´”l½•m¾•m¾Œdµ‹c´Œdµ•m¾–n¿–n¿e¶Œdµe¶–n¿ëÃëÃëÃeçB$òL—oÀ—oÀŽf·çBŽf·—oÀ˜pÁ˜pÁg¸Žf·g¸˜pÁ™q™qÂh¹g¸h¹™qšrÚrÑiºh¹‘iºšrÛsÄ›sÄ’j»‘iº’j»›sÄœtÅœtÅ“k¼’j»“k¼œtÅuÆuÆ”l½“k¼”l½uÆžvÇžvÇ•m¾”l½•m¾žvÇŸwÈŸwÈ–n¿•m¾–n¿ŸwÈëÃëÃëÖn$òL.ýV xÉ xÉ—oÀ$òL—oÀ xÉ¡yÊ¡yʘpÁ—oÀ˜pÁ¡yÊ¢zË¢zË™q˜pÁ™q¢zË£{Ì£{ÌšrÙqšrã{̤|ͤ|Í›sÄšrÛsĤ|Í¥}Î¥}ΜtÅ›sÄœtÅ¥}Φ~Ϧ~ÏuÆœtÅuƦ~ϧЧОvÇuÆžvǧШ€Ñ¨€ÑŸwÈžvÇŸwȨ€ÑëÃëÃëßw.ýV8`©Ò©Ò xÉ.ýV xɩҪ‚Óª‚Ó¡yÊ xÉ¡yʪ‚Ó«ƒÔ«ƒÔ¢zË¡yÊ¢zË«ƒÔ¬„Õ¬„Õ£{Ì¢zË£{̬„Õ­…Ö­…Ö¤|Í£{̤|Í­…Ö®†×®†×¥}Τ|Í¥}ή†×¯‡Ø¯‡Ø¦~Ï¥}Φ~ϯ‡Ø°ˆÙ°ˆÙ§Ð¦~ϧаˆÙ±‰Ú±‰Ú¨€Ñ§Ð¨€Ñ±‰ÚëÃëÃëè€8`Bj²ŠÛ²ŠÛ©Ò8`©Ò²ŠÛ³‹Ü³‹Üª‚Ó©Òª‚Ó³‹Ü´ŒÝ´ŒÝ«ƒÔª‚Ó«ƒÔ´ŒÝµÞµÞ¬„Õ«ƒÔ¬„ÕµÞ¶Žß¶Žß­…Ö¬„Õ­…Ö¶Žß·à·à®†×­…Ö®†×·à¸á¸á¯‡Ø®†×¯‡Ø¸á¹‘⹑Ⰸٯ‡Ø°ˆÙ¹‘⺒㺒㱉ڰˆÙ±‰Úº’ãëÃëÃëñ‰BjLt»“仓䲊ÛBj²ŠÛ»“伔弔峋ܲŠÛ³‹Ü¼”录潕洌ݳ‹Ü´ŒÝ½•æ¾–ç¾–çµÞ´ŒÝµÞ¾–ç¿—è¿—è¶ŽßµÞ¶Žß¿—èÀ˜éÀ˜é·à¶Žß·àÀ˜éÁ™êÁ™ê¸á·à¸áÁ™êšëš빑â¸á¹‘âšëÛìÛ캒㹑⺒ãÛìëÃëÃëú’LtV)~ÄœíÄœí»“äLt»“äÄœíÅîÅ廓伔åÅîÆžïÆžï½•æ¼”å½•æÆžïÇŸðÇŸð¾–罕澖çÇŸðÈ ñÈ ñ¿—è¾–ç¿—èÈ ñÉ¡òÉ¡òÀ˜é¿—èÀ˜éÉ¡òÊ¢óÊ¢óÁ™êÀ˜éÁ™êÊ¢óË£ôË£ôšëÁ™êšëË£ô̤õ̤õÛìšëÛì̤õëÃëÃëÃÛV)~`4ˆÍ¥öÍ¥öÄœíV)~ÄœíÍ¥öΦ÷Φ÷ÅîÄœíÅîΦ÷ϧøÏ§øÆžïÅîÆžïϧøÐ¨ùШùÇŸðÆžïÇŸðШùÑ©úÑ©úÈ ñÇŸðÈ ñÑ©úÒªûÒªûÉ¡òÈ ñÉ¡òÒªûÓ«üÓ«üÊ¢óÉ¡òÊ¢óÓ«üÔ¬ýÔ¬ýË£ôÊ¢óË£ôÔ¬ýÕ­þÕ­þ̤õË£ô̤õÕ­þëÃëÃëÃ̤`4ˆj?’Ö®ÿÖ®ÿÍ¥ö`4ˆÍ¥öÖ®ÿׯׯΦ÷Í¥öΦ÷ׯذذϧøÎ¦÷ϧøØ°Ù±Ù±Ð¨ùϧøÐ¨ùٱڲڲѩúШùÑ©úÚ²Û³Û³ÒªûÑ©úÒªû۳ܴܴӫüÒªûÓ«üܴݵݵԬýÓ«üÔ¬ýݵ޶޶խþÔ¬ýÕ­þÞ¶ëÃëÃëÃÕ­j?’tJœß·ß·Ö®ÿj?’Ö®ÿ߷ภภׯ֮ÿ×¯à¸ á¹ á¹ Ø°×¯Ø°á¹ âº âº Ù±Ø°Ù±âº ã» ã» Ú²Ù±Ú²ã» ä¼ ä¼ Û³Ú²Û³ä¼ å½å½Ü´Û³Ü´å½æ¾æ¾ÝµÜ´Ýµæ¾ç¿ç¿Þ¶ÝµÞ¶ç¿ëÃëÃëÃÞ¶tJœ~U¦èÀèÀß·tJœß·èÀéÁéÁภ߷ภéÁêÂêÂṠภṠêÂëÃëÃ⺠Ṡ⺠ëÃìÄìÄ㻠⺠㻠ìÄíÅíÅä¼ ã» ä¼ íÅîÆîÆå½ä¼ å½îÆïÇïÇæ¾å½æ¾ïÇðÈðÈç¿æ¾ç¿ðÈëÃëÃëÃç¿~U¦ˆ`°ñÉñÉèÀ~U¦èÀñÉòÊòÊéÁèÀéÁòÊóËóËêÂéÁêÂóËôÌôÌëÃêÂëÃôÌõÍõÍìÄëÃìÄõÍöÎöÎíÅìÄíÅöÎ÷Ï ÷Ï îÆíÅîÆ÷Ï øÐ!øÐ!ïÇîÆïÇøÐ!ùÑ"ùÑ"ðÈïÇðÈùÑ"ëÃëÃëÃðȈ`°’kºúÒ#úÒ#ñɈ`°ñÉúÒ#ûÓ$ûÓ$òÊñÉòÊûÓ$üÔ%üÔ%óËòÊóËüÔ%ýÕ&ýÕ&ôÌóËôÌýÕ&þÖ'þÖ'õÍôÌõÍþÖ'ÿ×(ÿ×(öÎõÍöÎÿ×(Ø)Ø)÷Ï öÎ÷Ï Ø)Ù*Ù*øÐ!÷Ï øÐ!Ù*Ú+Ú+ùÑ"øÐ!ùÑ"Ú+ëÃëÃëÃùÑ’kºœvÄÛ,Û,úÒ#’kºúÒ#Û,Ü-Ü-ûÓ$úÒ#ûÓ$Ü-Ý.Ý.üÔ%ûÓ$üÔ%Ý.Þ/Þ/ýÕ&üÔ%ýÕ&Þ/ß0ß0þÖ'ýÕ&þÖ'ß0à1à1ÿ×(þÖ'ÿ×(à1 á2 á2Ø)ÿ×(Ø) á2 â3 â3Ù*Ø)Ù* â3 ã4 ã4Ú+Ù*Ú+ ã4ëÃëÃëÃÚœvĦΠä5 ä5Û,œvÄÛ, ä5 å6 å6Ü-Û,Ü- å6æ7æ7Ý.Ü-Ý.æ7ç8ç8Þ/Ý.Þ/ç8è9è9ß0Þ/ß0è9é:é:à1ß0à1é:ê;ê; á2à1 á2ê;ë<ë< â3 á2 â3ë<ì=ì= ã4 â3 ã4ì=ëÃëÃëà ã¦Î°ŒØí>í> ä5¦Î ä5í>î?î? å6 ä5 å6î?ï@ï@æ7 å6æ7ï@ðAðAç8æ7ç8ðAñBñBè9ç8è9ñBòCòCé:è9é:òCóDóDê;é:ê;óDôEôEë<ê;ë<ôEõFõFì=ë<ì=õFëÃëÃëÃ찌غ—âöGöGí>°ŒØí>öG÷H÷Hî?í>î?÷H øI øIï@î?ï@ øI!ùJ!ùJðAï@ðA!ùJ"úK"úKñBðAñB"úK#ûL#ûLòCñBòC#ûL$üM$üMóDòCóD$üM%ýN%ýNôEóDôE%ýN&þO&þOõFôEõF&þOëÃëÃëÃõº—âÄ¢ì'ÿP'ÿPöGº—âöG'ÿP(Q(Q÷HöG÷H(Q)R)R øI÷H øI)R*S*S!ùJ øI!ùJ*S+T+T"úK!ùJ"úK+T,U,U#ûL"úK#ûL,U-V-V$üM#ûL$üM-V.W.W%ýN$üM%ýN.W/X/X&þO%ýN&þO/XëÃëÃëÃ&þÄ¢ìέö0Y0Y'ÿPÄ¢ì'ÿP0Y1 Z1 Z(Q'ÿP(Q1 Z2 [2 [)R(Q)R2 [3 \3 \*S)R*S3 \4 ]4 ]+T*S+T4 ]5 ^5 ^,U+T,U5 ^6_6_-V,U-V6_7`7`.W-V.W7`8a8a/X.W/X8aëÃëÃëÃ/έöظ9b9b0Yέö0Y9b:c:c1 Z0Y1 Z:c;d;d2 [1 Z2 [;d<e<e3 \2 [3 \<e=f=f4 ]3 \4 ]=f>g>g5 ^4 ]5 ^>g?h?h6_5 ^6_?h@i@i7`6_7`@iAjAj8a7`8aAjëÃëÃëÃ8ظ\„Ú²Ú²9bظ9bڲܴܴ:c9b:cÜ´Þ¶Þ¶;d:c;dÞ¶à¸à¸<e;d<eà¸âº ⺠=f<e=fâº ä¼ ä¼ >g=f>gä¼ æ¾æ¾?h>g?hæ¾èÀèÀ@i?h@ièÀêÂêÂAj@iAjêÂëÃëÃëÃABkClDmDmEnBkEnDmFoFoGpEnGpFoH qH qI!rGpI!rH qJ"sJ"sK#tI!rK#tJ"sL$uL$uM%vK#tM%vL$uN&wN&wO'xM%vO'xN&wP(yP(yQ)zO'xQ)zP(yR*{R*{S+|Q)zS+|R*{T,}T,}U-~S+|U-~T,}V.V.W/€U-~ClX0Y1‚Y1‚DmClDmY1‚Z2ƒZ2ƒFoDmFoZ2ƒ[3„[3„H qFoH q[3„\4…\4…J"sH qJ"s\4…]5†]5†L$uJ"sL$u]5†^6‡^6‡N&wL$uN&w^6‡_7ˆ_7ˆP(yN&wP(y_7ˆ`8‰`8‰R*{P(yR*{`8‰a9Ša9ŠT,}R*{T,}a9Šb:‹b:‹V.T,}X0c;Œd<d<Y1‚X0Y1‚d<e=Že=ŽZ2ƒY1‚Z2ƒe=Žf>f>[3„Z2ƒ[3„f>g?g?\4…[3„\4…g?h@‘h@‘]5†\4…]5†h@‘iA’iA’^6‡]5†^6‡iA’jB“jB“_7ˆ^6‡_7ˆjB“kC”kC”`8‰_7ˆ`8‰kC”lD•lD•a9Š`8‰a9ŠlD•mE–mE–b:‹a9Šc;ŒnF—oG˜oG˜d<c;Œd<oG˜pH™pH™e=Žd<e=ŽpH™qIšqIšf>e=Žf>qIšrJ›rJ›g?f>g?rJ›sKœsKœh@‘g?h@‘sKœtLtLiA’h@‘iA’tLuMžuMžjB“iA’jB“uMžvNŸvNŸkC”jB“kC”vNŸwO wO lD•kC”lD•wO xP¡xP¡mE–lD•nF—yQ¢zR£zR£oG˜nF—oG˜zR£{S¤{S¤pH™oG˜pH™{S¤|T¥|T¥qIšpH™qIš|T¥}U¦}U¦rJ›qIšrJ›}U¦~V§~V§sKœrJ›sKœ~V§W¨W¨tLsKœtLW¨€X©€X©uMžtLuMž€X©YªYªvNŸuMžvNŸYª‚Z«‚Z«wO vNŸwO ‚Z«ƒ[¬ƒ[¬xP¡wO yQ¢„\­…]®…]®zR£yQ¢zR£…]®†^¯†^¯{S¤zR£{S¤†^¯‡_°‡_°|T¥{S¤|T¥‡_°ˆ`±ˆ`±}U¦|T¥}U¦ˆ`±‰a²‰a²~V§}U¦~V§‰a²Šb³Šb³W¨~V§W¨Šb³‹c´‹c´€X©W¨€X©‹c´ŒdµŒdµYª€X©YªŒdµe¶e¶‚Z«Yª‚Z«e¶Žf·Žf·ƒ[¬‚Z«„\­g¸h¹h¹…]®„\­…]®h¹‘iº‘iº†^¯…]®†^¯‘iº’j»’j»‡_°†^¯‡_°’j»“k¼“k¼ˆ`±‡_°ˆ`±“k¼”l½”l½‰a²ˆ`±‰a²”l½•m¾•m¾Šb³‰a²Šb³•m¾–n¿–n¿‹c´Šb³‹c´–n¿—oÀ—oÀŒdµ‹c´Œdµ—oÀ˜pÁ˜pÁe¶Œdµe¶˜pÁ™q™qÂŽf·e¶g¸šrÛsÄ›sÄh¹g¸h¹›sÄœtÅœtÅ‘iºh¹‘iºœtÅuÆuÆ’j»‘iº’j»uÆžvÇžvÇ“k¼’j»“k¼žvÇŸwÈŸwÈ”l½“k¼”l½ŸwÈ xÉ xÉ•m¾”l½•m¾ xÉ¡yÊ¡yÊ–n¿•m¾–n¿¡yÊ¢zË¢zË—oÀ–n¿—oÀ¢zË£{Ì£{̘pÁ—oÀ˜pÁ£{̤|ͤ|Í™q˜pÁšrÃ¥}Φ~Ϧ~Ï›sÄšrÛsĦ~ϧЧМtÅ›sÄœtŧШ€Ñ¨€ÑuÆœtÅuƨ€Ñ©Ò©ÒžvÇuÆžvǩҪ‚Óª‚ÓŸwÈžvÇŸwȪ‚Ó«ƒÔ«ƒÔ xÉŸwÈ xÉ«ƒÔ¬„Õ¬„Õ¡yÊ xÉ¡yʬ„Õ­…Ö­…Ö¢zË¡yÊ¢zË­…Ö®†×®†×£{Ì¢zË£{Ì®†×¯‡Ø¯‡Ø¤|Í£{Ì¥}ΰˆÙ±‰Ú±‰Ú¦~Ï¥}Φ~ϱ‰Ú²ŠÛ²ŠÛ§Ð¦~ϧвŠÛ³‹Ü³‹Ü¨€Ñ§Ð¨€Ñ³‹Ü´ŒÝ´ŒÝ©Ò¨€Ñ©Ò´ŒÝµÞµÞª‚Ó©Òª‚ÓµÞ¶Žß¶Žß«ƒÔª‚Ó«ƒÔ¶Žß·à·à¬„Õ«ƒÔ¬„Õ·à¸á¸á­…Ö¬„Õ­…ָṑ⹑⮆׭…Ö®†×¹‘⺒㺒㯇خ†×°ˆÙ»“伔弔屉ڰˆÙ±‰Ú¼”录潕沊۱‰Ú²ŠÛ½•澖羖糋ܲŠÛ³‹Ü¾–翗迗贌ݳ‹Ü´ŒÝ¿—èÀ˜éÀ˜éµÞ´ŒÝµÞÀ˜éÁ™êÁ™ê¶ŽßµÞ¶ŽßÁ™êšëšë·à¶Žß·àšëÛìÛì¸á·à¸áÛìÄœíÄœí¹‘â¸á¹‘âÄœíÅîÅîº’ã¹‘â»“äÆžïÇŸðÇŸð¼”廓伔åÇŸðÈ ñÈ ñ½•演录æÈ ñÉ¡òÉ¡ò¾–罕澖çÉ¡òÊ¢óÊ¢ó¿—è¾–ç¿—èÊ¢óË£ôË£ôÀ˜é¿—èÀ˜éË£ô̤õ̤õÁ™êÀ˜éÁ™ê̤õÍ¥öÍ¥öšëÁ™êšëÍ¥öΦ÷Φ÷ÛìšëÛìΦ÷ϧøÏ§øÄœíÛìÄœíϧøÐ¨ùШùÅîÄœíÆžïÑ©úÒªûÒªûÇŸðÆžïÇŸðÒªûÓ«üÓ«üÈ ñÇŸðÈ ñÓ«üÔ¬ýÔ¬ýÉ¡òÈ ñÉ¡òÔ¬ýÕ­þÕ­þÊ¢óÉ¡òÊ¢óÕ­þÖ®ÿÖ®ÿË£ôÊ¢óË£ôÖ®ÿׯׯ̤õË£ô̤õׯذذͥö̤õÍ¥öذٱٱΦ÷Í¥öΦ÷ٱڲڲϧøÎ¦÷ϧøÚ²Û³Û³Ð¨ùϧøÑ©úܴݵݵҪûÑ©úÒªûݵ޶޶ӫüÒªûÓ«üÞ¶ß·ß·Ô¬ýÓ«üÔ¬ý߷ภภխþÔ¬ýÕ­þภṠṠ֮ÿÕ­þÖ®ÿṠ⺠⺠ׯ֮ÿ×¯âº ã» ã» Ø°×¯Ø°ã» ä¼ ä¼ Ù±Ø°Ù±ä¼ å½å½Ú²Ù±Ú²å½æ¾æ¾Û³Ú²Ü´ç¿èÀèÀݵܴݵèÀéÁéÁ޶ݵ޶éÁêÂêÂß·Þ¶ß·êÂëÃëÃภ߷ภëÃìÄìÄṠภṠìÄíÅíÅ⺠Ṡ⺠íÅîÆîÆã» ⺠㻠îÆïÇïÇä¼ ã» ä¼ ïÇðÈðÈå½ä¼ å½ðÈñÉñÉæ¾å½ç¿òÊóËóËèÀç¿èÀóËôÌôÌéÁèÀéÁôÌõÍõÍêÂéÁêÂõÍöÎöÎëÃêÂëÃöÎ÷Ï ÷Ï ìÄëÃìÄ÷Ï øÐ!øÐ!íÅìÄíÅøÐ!ùÑ"ùÑ"îÆíÅîÆùÑ"úÒ#úÒ#ïÇîÆïÇúÒ#ûÓ$ûÓ$ðÈïÇðÈûÓ$üÔ%üÔ%ñÉðÈòÊýÕ&þÖ'þÖ'óËòÊóËþÖ'ÿ×(ÿ×(ôÌóËôÌÿ×(Ø)Ø)õÍôÌõÍØ)Ù*Ù*öÎõÍöÎÙ*Ú+Ú+÷Ï öÎ÷Ï Ú+Û,Û,øÐ!÷Ï øÐ!Û,Ü-Ü-ùÑ"øÐ!ùÑ"Ü-Ý.Ý.úÒ#ùÑ"úÒ#Ý.Þ/Þ/ûÓ$úÒ#ûÓ$Þ/ß0ß0üÔ%ûÓ$ýÕ&à1 á2 á2þÖ'ýÕ&þÖ' á2 â3 â3ÿ×(þÖ'ÿ×( â3 ã4 ã4Ø)ÿ×(Ø) ã4 ä5 ä5Ù*Ø)Ù* ä5 å6 å6Ú+Ù*Ú+ å6æ7æ7Û,Ú+Û,æ7ç8ç8Ü-Û,Ü-ç8è9è9Ý.Ü-Ý.è9é:é:Þ/Ý.Þ/é:ê;ê;ß0Þ/à1ë<ì=ì= á2à1 á2ì=í>í> â3 á2 â3í>î?î? ã4 â3 ã4î?ï@ï@ ä5 ã4 ä5ï@ðAðA å6 ä5 å6ðAñBñBæ7 å6æ7ñBòCòCç8æ7ç8òCóDóDè9ç8è9óDôEôEé:è9é:ôEõFõFê;é:ë<BkEnEnì=ë<ì=EnGpGpí>ì=í>GpI!rI!rî?í>î?I!rK#tK#tï@î?ï@K#tM%vM%vðAï@ðAM%vO'xO'xñBðAñBO'xQ)zQ)zòCñBòCQ)zS+|S+|óDòCóDS+|U-~U-~ôEóDôEU-~W/€W/€õFôEW/€V.öGöG÷HW/€÷HöG øI øI!ùJ÷H!ùJ øI"úK"úK#ûL!ùJ#ûL"úK$üM$üM%ýN#ûL%ýN$üM&þO&þO'ÿP%ýN'ÿP&þO(Q(Q)R'ÿP)R(Q*S*S+T)R+T*S,U,U-V+T-V,U.W.W/X-V/X.W0Y0Y1 Z/XV.b:‹2 [2 [öGV.öG2 [3 \3 \ øIöG øI3 \4 ]4 ]"úK øI"úK4 ]5 ^5 ^$üM"úK$üM5 ^6_6_&þO$üM&þO6_7`7`(Q&þO(Q7`8a8a*S(Q*S8a9b9b,U*S,U9b:c:c.W,U.W:c;d;d0Y.Wb:‹mE–<e<e2 [b:‹2 [<e=f=f3 \2 [3 \=f>g>g4 ]3 \4 ]>g?h?h5 ^4 ]5 ^?h@i@i6_5 ^6_@iAjAj7`6_7`AjBkBk8a7`8aBkClCl9b8a9bClDmDm:c9b:cDmEnEn;d:cmE–xP¡FoFo<emE–<eFoGpGp=f<e=fGpH qH q>g=f>gH qI!rI!r?h>g?hI!rJ"sJ"s@i?h@iJ"sK#tK#tAj@iAjK#tL$uL$uBkAjBkL$uM%vM%vClBkClM%vN&wN&wDmClDmN&wO'xO'xEnDmxP¡ƒ[¬P(yP(yFoxP¡FoP(yQ)zQ)zGpFoGpQ)zR*{R*{H qGpH qR*{S+|S+|I!rH qI!rS+|T,}T,}J"sI!rJ"sT,}U-~U-~K#tJ"sK#tU-~V.V.L$uK#tL$uV.W/€W/€M%vL$uM%vW/€X0X0N&wM%vN&wX0Y1‚Y1‚O'xN&wƒ[¬Žf·Z2ƒZ2ƒP(yƒ[¬P(yZ2ƒ[3„[3„Q)zP(yQ)z[3„\4…\4…R*{Q)zR*{\4…]5†]5†S+|R*{S+|]5†^6‡^6‡T,}S+|T,}^6‡_7ˆ_7ˆU-~T,}U-~_7ˆ`8‰`8‰V.U-~V.`8‰a9Ša9ŠW/€V.W/€a9Šb:‹b:‹X0W/€X0b:‹c;Œc;ŒY1‚X0Žf·™qÂd<d<Z2ƒŽf·Z2ƒd<e=Že=Ž[3„Z2ƒ[3„e=Žf>f>\4…[3„\4…f>g?g?]5†\4…]5†g?h@‘h@‘^6‡]5†^6‡h@‘iA’iA’_7ˆ^6‡_7ˆiA’jB“jB“`8‰_7ˆ`8‰jB“kC”kC”a9Š`8‰a9ŠkC”lD•lD•b:‹a9Šb:‹lD•mE–mE–c;Œb:‹™q¤|ÍnF—nF—d<™qÂd<nF—oG˜oG˜e=Žd<e=ŽoG˜pH™pH™f>e=Žf>pH™qIšqIšg?f>g?qIšrJ›rJ›h@‘g?h@‘rJ›sKœsKœiA’h@‘iA’sKœtLtLjB“iA’jB“tLuMžuMžkC”jB“kC”uMžvNŸvNŸlD•kC”lD•vNŸwO wO mE–lD•¤|ͯ‡ØxP¡xP¡nF—¤|ÍnF—xP¡yQ¢yQ¢oG˜nF—oG˜yQ¢zR£zR£pH™oG˜pH™zR£{S¤{S¤qIšpH™qIš{S¤|T¥|T¥rJ›qIšrJ›|T¥}U¦}U¦sKœrJ›sKœ}U¦~V§~V§tLsKœtL~V§W¨W¨uMžtLuMžW¨€X©€X©vNŸuMžvNŸ€X©YªYªwO vNŸ¯‡Øº’ã‚Z«‚Z«xP¡¯‡ØxP¡‚Z«ƒ[¬ƒ[¬yQ¢xP¡yQ¢ƒ[¬„\­„\­zR£yQ¢zR£„\­…]®…]®{S¤zR£{S¤…]®†^¯†^¯|T¥{S¤|T¥†^¯‡_°‡_°}U¦|T¥}U¦‡_°ˆ`±ˆ`±~V§}U¦~V§ˆ`±‰a²‰a²W¨~V§W¨‰a²Šb³Šb³€X©W¨€X©Šb³‹c´‹c´Yª€X©º’ãÅîŒdµŒdµ‚Z«º’ã‚Z«Œdµe¶e¶ƒ[¬‚Z«ƒ[¬e¶Žf·Žf·„\­ƒ[¬„\­Žf·g¸g¸…]®„\­…]®g¸h¹h¹†^¯…]®†^¯h¹‘iº‘iº‡_°†^¯‡_°‘iº’j»’j»ˆ`±‡_°ˆ`±’j»“k¼“k¼‰a²ˆ`±‰a²“k¼”l½”l½Šb³‰a²Šb³”l½•m¾•m¾‹c´Šb³ÅîШù–n¿–n¿ŒdµÅîŒdµ–n¿—oÀ—oÀe¶Œdµe¶—oÀ˜pÁ˜pÁŽf·e¶Žf·˜pÁ™q™qÂg¸Žf·g¸™qšrÚrÃh¹g¸h¹šrÛsÄ›sÄ‘iºh¹‘iº›sÄœtÅœtÅ’j»‘iº’j»œtÅuÆuÆ“k¼’j»“k¼uÆžvÇžvÇ”l½“k¼”l½žvÇŸwÈŸwÈ•m¾”l½Ð¨ùÛ³ xÉ xÉ–n¿Ð¨ù–n¿ xÉ¡yÊ¡yÊ—oÀ–n¿—oÀ¡yÊ¢zË¢z˘pÁ—oÀ˜pÁ¢zË£{Ì£{Ì™q˜pÁ™q£{̤|ͤ|ÍšrÙqšrä|Í¥}Î¥}ΛsÄšrÛsÄ¥}Φ~Ϧ~ÏœtÅ›sÄœtŦ~ϧЧÐuÆœtÅuƧШ€Ñ¨€ÑžvÇuÆžvǨ€Ñ©Ò©ÒŸwÈžvÇ۳澪‚Óª‚Ó xÉÛ³ xɪ‚Ó«ƒÔ«ƒÔ¡yÊ xÉ¡yÊ«ƒÔ¬„Õ¬„Õ¢zË¡yÊ¢zˬ„Õ­…Ö­…Ö£{Ì¢zË£{Ì­…Ö®†×®†×¤|Í£{̤|Í®†×¯‡Ø¯‡Ø¥}Τ|Í¥}ί‡Ø°ˆÙ°ˆÙ¦~Ï¥}Φ~ϰˆÙ±‰Ú±‰Ú§Ð¦~ϧб‰Ú²ŠÛ²ŠÛ¨€Ñ§Ð¨€Ñ²ŠÛ³‹Ü³‹Ü©Ò¨€Ñæ¾ñÉ´ŒÝ´ŒÝª‚Óæ¾ª‚Ó´ŒÝµÞµÞ«ƒÔª‚Ó«ƒÔµÞ¶Žß¶Žß¬„Õ«ƒÔ¬„Õ¶Žß·à·à­…Ö¬„Õ­…Ö·à¸á¸á®†×­…Ö®†×¸á¹‘⹑⯇خ†×¯‡Ø¹‘⺒㺒㰈ٯ‡Ø°ˆÙº’㻓仓䱉ڰˆÙ±‰Ú»“伔弔岊۱‰Ú²ŠÛ¼”录潕泋ܲŠÛñÉüÔ%¾–ç¾–ç´ŒÝñÉ´ŒÝ¾–ç¿—è¿—èµÞ´ŒÝµÞ¿—èÀ˜éÀ˜é¶ŽßµÞ¶ŽßÀ˜éÁ™êÁ™ê·à¶Žß·àÁ™êšëšë¸á·à¸ášëÛìÛ칑â¸á¹‘âÛìÄœíÄœíº’ã¹‘âº’ãÄœíÅîÅ互㻓äÅîÆžïÆžï¼”å»“ä¼”åÆžïÇŸðÇŸð½•æ¼”åüÔ%ß0È ñÈ ñ¾–çüÔ%¾–çÈ ñÉ¡òÉ¡ò¿—è¾–ç¿—èÉ¡òÊ¢óÊ¢óÀ˜é¿—èÀ˜éÊ¢óË£ôË£ôÁ™êÀ˜éÁ™êË£ô̤õ̤õšëÁ™êšë̤õÍ¥öÍ¥öÛìšëÛìÍ¥öΦ÷Φ÷ÄœíÛìÄœíΦ÷ϧøÏ§øÅîÄœíÅîϧøÐ¨ùШùÆžïÅîÆžïШùÑ©úÑ©úÇŸðÆžïß0ê;ÒªûÒªûÈ ñß0È ñÒªûÓ«üÓ«üÉ¡òÈ ñÉ¡òÓ«üÔ¬ýÔ¬ýÊ¢óÉ¡òÊ¢óÔ¬ýÕ­þÕ­þË£ôÊ¢óË£ôÕ­þÖ®ÿÖ®ÿ̤õË£ô̤õÖ®ÿׯׯͥö̤õÍ¥öׯذذΦ÷Í¥öΦ÷ذٱٱϧøÎ¦÷ϧøÙ±Ú²Ú²Ð¨ùϧøÐ¨ùڲ۳۳ѩúШùê;õFÜ´Ü´Òªûê;ÒªûܴݵݵӫüÒªûÓ«üݵ޶޶ԬýÓ«üÔ¬ýÞ¶ß·ß·Õ­þÔ¬ýÕ­þ߷ภภ֮ÿÕ­þÖ®ÿภṠṠׯ֮ÿ×¯á¹ âº âº Ø°×¯Ø°âº ã» ã» Ù±Ø°Ù±ã» ä¼ ä¼ Ú²Ù±Ú²ä¼ å½å½Û³Ú²õFW/€÷H÷HÜ´õFÜ´÷H!ùJ!ùJݵܴݵ!ùJ#ûL#ûL޶ݵ޶#ûL%ýN%ýNß·Þ¶ß·%ýN'ÿP'ÿPภ߷ภ'ÿP)R)RṠภṠ)R+T+T⺠Ṡ⺠+T-V-V㻠⺠㻠-V/X/Xä¼ ã» ä¼ /X1 Z1 Zå½ä¼ æ¾ç¿èÀèÀéÁæ¾éÁèÀêÂêÂëÃéÁëÃêÂìÄìÄíÅëÃíÅìÄîÆîÆïÇíÅïÇîÆðÈðÈñÉïÇñÉðÈòÊòÊóËñÉóËòÊôÌôÌõÍóËõÍôÌöÎöÎ÷Ï õÍ÷Ï öÎøÐ!øÐ!ùÑ"÷Ï ùÑ"øÐ!úÒ#úÒ#ûÓ$ùÑ"ç¿üÔ%ýÕ&ýÕ&èÀç¿èÀýÕ&þÖ'þÖ'êÂèÀêÂþÖ'ÿ×(ÿ×(ìÄêÂìÄÿ×(Ø)Ø)îÆìÄîÆØ)Ù*Ù*ðÈîÆðÈÙ*Ú+Ú+òÊðÈòÊÚ+Û,Û,ôÌòÊôÌÛ,Ü-Ü-öÎôÌöÎÜ-Ý.Ý.øÐ!öÎøÐ!Ý.Þ/Þ/úÒ#øÐ!üÔ%ß0à1à1ýÕ&üÔ%ýÕ&à1 á2 á2þÖ'ýÕ&þÖ' á2 â3 â3ÿ×(þÖ'ÿ×( â3 ã4 ã4Ø)ÿ×(Ø) ã4 ä5 ä5Ù*Ø)Ù* ä5 å6 å6Ú+Ù*Ú+ å6æ7æ7Û,Ú+Û,æ7ç8ç8Ü-Û,Ü-ç8è9è9Ý.Ü-Ý.è9é:é:Þ/Ý.ß0ê;ë<ë<à1ß0à1ë<ì=ì= á2à1 á2ì=í>í> â3 á2 â3í>î?î? ã4 â3 ã4î?ï@ï@ ä5 ã4 ä5ï@ðAðA å6 ä5 å6ðAñBñBæ7 å6æ7ñBòCòCç8æ7ç8òCóDóDè9ç8è9óDôEôEé:è9ê;õFöGöGë<ê;ë<öG÷H÷Hì=ë<ì=÷H øI øIí>ì=í> øI!ùJ!ùJî?í>î?!ùJ"úK"úKï@î?ï@"úK#ûL#ûLðAï@ðA#ûL$üM$üMñBðAñB$üM%ýN%ýNòCñBòC%ýN&þO&þOóDòCóD&þO'ÿP'ÿPôEóDõF(êQ)R)RöGõFöG)R*S*S÷HöG÷H*S+T+T øI÷H øI+T,U,U!ùJ øI!ùJ,U-V-V"úK!ùJ"úK-V.W.W#ûL"úK#ûL.W/X/X$üM#ûL$üM/X0Y0Y%ýN$üM%ýN0Y1Z1Z&þO%ýN&þO1Z2 [2 ['ÿP&þO(êQ3ß\4 ]4 ])R(êQ)R4 ]5 ^5 ^*S)R*S5 ^6 _6 _+T*S+T6 _7 `7 `,U+T,U7 `8a8a-V,U-V8a9b9b.W-V.W9b:c:c/X.W/X:c;d;d0Y/X0Y;d<e<e1Z0Y1Z<e=f=f2 [1Z3ß\>Ôg?h?h4 ]3ß\4 ]?h@i@i5 ^4 ]5 ^@iAjAj6 _5 ^6 _AjBkBk7 `6 _7 `BkClCl8a7 `8aClDmDm9b8a9bDmEnEn:c9b:cEnFoFo;d:c;dFoGpGp<e;d<eGpHqHq=f<e>ÔgI¿rJsJs?h>Ôg?hJsKtKt@i?h@iKtL uL uAj@iAjL uM!vM!vBkAjBkM!vN"wN"wClBkClN"wO#xO#xDmClDmO#xP$yP$yEnDmEnP$yQ%zQ%zFoEnFoQ%zR&{R&{GpFoGpR&{S'|S'|HqGpI¿rT(}U)~U)~JsI¿rJsU)~V*V*KtJsKtV*W+€W+€L uKtL uW+€X,X,M!vL uM!vX,Y-‚Y-‚N"wM!vN"wY-‚Z.ƒZ.ƒO#xN"wO#xZ.ƒ[/„[/„P$yO#xP$y[/„\0…\0…Q%zP$yQ%z\0…]1†]1†R&{Q%zR&{]1†^2‡^2‡S'|R&{T(}_3ˆ`4‰`4‰U)~T(}U)~`4‰a5Ša5ŠV*U)~V*a5Šb6‹b6‹W+€V*W+€b6‹c7Œc7ŒX,W+€X,c7Œd8d8Y-‚X,Y-‚d8e9Že9ŽZ.ƒY-‚Z.ƒe9Žf:f:[/„Z.ƒ[/„f:g;g;\0…[/„\0…g;h<‘h<‘]1†\0…]1†h<‘i=’i=’^2‡]1†_3ˆj>“k?”k?”`4‰_3ˆ`4‰k?”l@•l@•a5Š`4‰a5Šl@•mA–mA–b6‹a5Šb6‹mA–nB—nB—c7Œb6‹c7ŒnB—oC˜oC˜d8c7Œd8oC˜pD™pD™e9Žd8e9ŽpD™qEšqEšf:e9Žf:qEšrF›rF›g;f:g;rF›sGœsGœh<‘g;h<‘sGœtHtHi=’h<‘j>“uIžvJŸvJŸk?”j>“k?”vJŸwK wK l@•k?”l@•wK xL¡xL¡mA–l@•mA–xL¡yM¢yM¢nB—mA–nB—yM¢zN£zN£oC˜nB—oC˜zN£{O¤{O¤pD™oC˜pD™{O¤|P¥|P¥qEšpD™qEš|P¥}Q¦}Q¦rF›qEšrF›}Q¦~R§~R§sGœrF›sGœ~R§S¨S¨tHsGœuIž€T©UªUªvJŸuIžvJŸUª‚V«‚V«wK vJŸwK ‚V«ƒW¬ƒW¬xL¡wK xL¡ƒW¬„X­„X­yM¢xL¡yM¢„X­…Y®…Y®zN£yM¢zN£…Y®†Z¯†Z¯{O¤zN£{O¤†Z¯‡[°‡[°|P¥{O¤|P¥‡[°ˆ\±ˆ\±}Q¦|P¥}Q¦ˆ\±‰]²‰]²~R§}Q¦~R§‰]²Š^³Š^³S¨~R§€T©‹_´Œ`µŒ`µUª€T©UªŒ`µa¶a¶‚V«Uª‚V«a¶Žb·Žb·ƒW¬‚V«ƒW¬Žb·c¸c¸„X­ƒW¬„X­c¸d¹d¹…Y®„X­…Y®d¹‘eº‘eº†Z¯…Y®†Z¯‘eº’f»’f»‡[°†Z¯‡[°’f»“g¼“g¼ˆ\±‡[°ˆ\±“g¼”h½”h½‰]²ˆ\±‰]²”h½•i¾•i¾Š^³‰]²‹_´–T¿—jÀ—jÀŒ`µ‹_´Œ`µ—jÀ˜kÁ˜kÁa¶Œ`µa¶˜kÁ™l™lÂŽb·a¶Žb·™lšmÚmÃc¸Žb·c¸šmÛnÄ›nÄd¹c¸d¹›nÄœoÅœoÅ‘eºd¹‘eºœoÅpÆpÆ’f»‘eº’f»pÆžqÇžqÇ“g¼’f»“g¼žqÇŸrÈŸrÈ”h½“g¼”h½ŸrÈ sÉ sÉ•i¾”h½–T¿¡IÊ¢tË¢tË—jÀ–T¿—jÀ¢tË£uÌ£u̘kÁ—jÀ˜kÁ£ṳvͤvÍ™l˜kÁ™l¤vÍ¥wÎ¥wΚmÙlšmÃ¥wΦxϦxÏ›nÄšmÛnĦxϧyЧyМoÅ›nÄœoŧyШzѨzÑpÆœoÅpƨzÑ©{Ò©{ÒžqÇpÆžqÇ©{Òª|Óª|ÓŸrÈžqÇŸrȪ|Ó«}Ô«}Ô sÉŸrÈ¡Iʬ>Õ­~Ö­~Ö¢tË¡IÊ¢tË­~Ö®×®×£uÌ¢tË£u̮ׯ€Ø¯€Ø¤vÍ£ṳvͯ€Ø°Ù°Ù¥wΤvÍ¥wΰٱ‚Ú±‚Ú¦xÏ¥wΦxϱ‚Ú²ƒÛ²ƒÛ§yЦxϧyвƒÛ³„ܳ„ܨzѧyШzѳ„Ü´…Ý´…Ý©{Ò¨zÑ©{Ò´…ݵ†Þµ†Þª|Ó©{Òª|Óµ†Þ¶‡ß¶‡ß«}Ôª|Ó¬>Õ·ˆà¸‰á¸‰á­~Ö¬>Õ­~Ö¸‰á¹Šâ¹Šâ®×­~Ö®×¹Šâº‹ãº‹ã¯€Ø®×¯€Øº‹ã»Œä»Œä°Ù¯€Ø°Ù»Œä¼å¼å±‚Ú°Ù±‚ڼ彎潎沃۱‚Ú²ƒÛ½Žæ¾ç¾ç³„ܲƒÛ³„ܾç¿è¿è´…ݳ„Ü´…Ý¿èÀ‘éÀ‘鵆޴…ݵ†ÞÀ‘éÁ’êÁ’궇ߵ†Þ·ˆàæ¾éÁéÁ¸‰á·ˆà¸‰áéÁëÃëùŠâ¸‰á¹ŠâëÃíÅíź‹ã¹Šâº‹ãíÅïÇïÇ»Œäº‹ã»ŒäïÇñÉñɼ廌ä¼åñÉóËóË½Žæ¼å½ŽæóËõÍõ;罎æ¾çõÍ÷Ï ÷Ï ¿è¾ç¿è÷Ï ùÑ"ùÑ"À‘é¿èÀ‘éùÑ"ûÓ$ûÓ$Á’êÀ‘éûÓ$úÒ#“ë“ëÔìûÓ$Ôì“ëÄ•íÄ•íÅ–îÔìÅ–îÄ•íÆ—ïÆ—ïǘðÅ–îǘðÆ—ïÈ™ñÈ™ñÉšòǘðÉšòÈ™ñÊ›óÊ›óËœôÉšòËœôÊ›óÌõÌõÍžöËœôÍž÷ÌøÎŸùΟùÏ úÍž÷Ï ûΟüСýСýÑ¢þÏ ûÑ¢þСýÒ£ÿÒ£ÿÓ¤ Ñ¢þÓ¤ Ò£ÿÔ¥ Ô¥ Õ¦ Ó¤ úÒ#Þ/Ö§ Ö§ “ëúÒ#“ëÖ§ ר ר Ä•í“ëÄ•íר Ø© Ø© Æ—ïÄ•íÆ—ïØ© Ùª Ùª È™ñÆ—ïÈ™ñÙª Ú« Ú« Ê›óÈ™ñÊ›óÚ« Û¬ Û¬ ÌõÊ›óÌøÛ¬ Ü­ Ü­ ΟùÌøÎŸüÜ­ Ý® Ý® СýΟüСýÝ® Þ¯ Þ¯ Ò£ÿСýÒ£ÿÞ¯ ß° ß° Ô¥ Ò£ÿÞ/é:à± à± Ö§ Þ/Ö§ ౠᲠᲠר Ö§ ר á² â³ â³ Ø© ר Ø© â³ ã´ ã´ Ùª Ø© Ùª ã´ äµ äµ Ú« Ùª Ú« äµ å¶ å¶ Û¬ Ú« Û¬ å¶ æ· æ· Ü­ Û¬ Ü­ æ· ç¸ ç¸ Ý® Ü­ Ý® ç¸ è¹ è¹ Þ¯ Ý® Þ¯ è¹ éº éº ß° Þ¯ é:ôEê» ê» à± é:à± ê» ë¼ ë¼ á² à± á² ë¼ ì½ ì½ â³ á² â³ ì½ í¾ í¾ ã´ â³ ã´ í¾ î¿ î¿ äµ ã´ äµ î¿ ïÀ ïÀ å¶ äµ å¶ ïÀ ðÁ ðÁ æ· å¶ æ· ðÁ ñ ñÂ ç¸ æ· ç¸ ñ òà òÃ è¹ ç¸ è¹ òà óÄ óÄ éº è¹ ôE'ÿPôÅ! ôÅ! ê» ôEê» ôÅ! õÆ" õÆ" ë¼ ê» ë¼ õÆ" öÇ# öÇ# ì½ ë¼ ì½ öÇ# ÷È$ ÷È$ í¾ ì½ í¾ ÷È$ øÉ% øÉ% î¿ í¾ î¿ øÉ% ùÊ& ùÊ& ïÀ î¿ ïÀ ùÊ& úË' úË' ðÁ ïÀ ðÁ úË' ûÌ( ûÌ( ñ ðÁ ñ ûÌ( üÍ) üÍ) òà ñ òà üÍ) ýÎ* ýÎ* óÄ òà 'ÿP2 [þÏ+ þÏ+ ôÅ! 'ÿPôÅ! þÏ+ ÿÐ, ÿÐ, õÆ" ôÅ! õÆ" ÿÐ, Ñ- Ñ- öÇ# õÆ" öÇ# Ñ-  Ò.  Ò. ÷È$ öÇ# ÷È$  Ò.  Ó/  Ó/ øÉ% ÷È$ øÉ%  Ó/  Ô0  Ô0 ùÊ& øÉ% ùÊ&  Ô0  Õ1  Õ1 úË' ùÊ& úË'  Õ1  Ö2  Ö2 ûÌ( úË' ûÌ(  Ö2  ×3  ×3 üÍ) ûÌ( üÍ)  ×3  Ø4  Ø4 ýÎ* üÍ) 2 [=f Ù5  Ù5 þÏ+ 2 [þÏ+  Ù5 Ú6 Ú6 ÿÐ, þÏ+ ÿÐ, Ú6 Û7 Û7 Ñ- ÿÐ, Ñ- Û7 Ü8 Ü8  Ò. Ñ-  Ò. Ü8 Ý9 Ý9  Ó/  Ò.  Ó/ Ý9 Þ: Þ:  Ô0  Ó/  Ô0 Þ:  ß;  ß;  Õ1  Ô0  Õ1  ß;  à<  à<  Ö2  Õ1  Ö2  à<  á=  á=  ×3  Ö2  ×3  á=  â>  â>  Ø4  ×3 =fHq ã?  ã?  Ù5 =f Ù5  ã?  ä@  ä@ Ú6  Ù5 Ú6  ä@  åA  åA Û7 Ú6 Û7  åA  æB  æB Ü8 Û7 Ü8  æC  çD  çD Ý9 Ü8 Ý9  çD  èE  èE Þ: Ý9 Þ:  èE  éF  éF  ß; Þ:  ß;  éF  êG  êG  à<  ß;  à<  êG  ëH  ëH  á=  à<  á=  ëH  ìI  ìI  â>  á= HqS'| íJ  íJ  ã? Hq ã?  íJ  îK  îK  ä@  ã?  ä@  îK  ïL  ïL  åA  ä@  åA  ïL  ðM  ðM  æB  åA  æC  ðN ñO ñO  çD  æC  çD ñO ! òP ! òP  èE  çD  èE ! òP " óQ " óQ  éF  èE  éF " óQ # ôR # ôR  êG  éF  êG # ôR $ õS $ õS  ëH  êG  ëH $ õS % öT % öT  ìI  ëH S'|^2‡& ÷U & ÷U  íJ S'| íJ & ÷U ' øV ' øV  îK  íJ  îK ' øV ( ùW ( ùW  ïL  îK  ïL ( ùW ) úX ) úX  ðM  ïL  ðN ) úY * ûZ * ûZ ñO  ðN ñO * û[ + ü\ + ü\ ! òP ñO ! òP + ü\ , ý] , ý] " óQ ! òP " óQ , ý] - þ^ - þ^ # ôR " óQ # ôR - þ^ . ÿ_ . ÿ_ $ õS # ôR $ õS . ÿ_ / ` / ` % öT $ õS ^2‡i=’0  a 0  a & ÷U ^2‡& ÷U 0  a 1  b 1  b ' øV & ÷U ' øV 1  b 2  c 2  c ( ùW ' øV ( ùW 2  c 3  d 3  d ) úX ( ùW ) úY 3  e 4  f 4  f * ûZ ) úY * û[ 4  g 5  h 5  h + ü\ * û[ + ü\ 5  h 6  i 6  i , ý] + ü\ , ý] 6  i 7  j 7  j - þ^ , ý] - þ^ 7  j 8 k 8 k . ÿ_ - þ^ . ÿ_ 8 k 9 l 9 l / ` . ÿ_ i=’tH: m : m 0  a i=’0  a : m ; n ; n 1  b 0  a 1  b ; n < o < o 2  c 1  b 2  c < o =  p =  p 3  d 2  c 3  e =  q >  r >  r 4  f 3  e 4  g >  r ?  s ?  s 5  h 4  g 5  h ?  s @  t @  t 6  i 5  h 6  i @  t A  u A  u 7  j 6  i 7  j A  u B  v B  v 8 k 7  j 8 k B  v C  w C  w 9 l 8 k tHS¨D  x D  x : m tH: m D  x E  y E  y ; n : m ; n E  y F  z F  z < o ; n < o F  z G  { G  { =  p < o =  q G  | H  } H  } >  r =  q >  r H  } I  ~ I  ~ ?  s >  r ?  s I  ~ J   J   @  t ?  s @  t J   K  € K  € A  u @  t A  u K  € L  L  B  v A  u B  v L  M  ‚ M  ‚ C  w B  v S¨Š^³N  ƒ N  ƒ D  x S¨D  x N  ƒ O „ O „ E  y D  x E  y O „ P ! … P ! … F  z E  y F  z P ! … Q " † Q " † G  { F  z G  | Q " † R # ‡ R # ‡ H  } G  | H  } R # ‡ S $ ˆ S $ ˆ I  ~ H  } I  ~ S $ ˆ T % ‰ T % ‰ J   I  ~ J   T % ‰ U & Š U & Š K  € J   K  € U & Š V ' ‹ V ' ‹ L  K  € L  V ' ‹ W ( Œ W ( Œ M  ‚ L  Š^³•i¾X ) X ) N  ƒ Š^³N  ƒ X ) Y * Ž Y * Ž O „ N  ƒ O „ Y * Ž Z + Z + P ! … O „ P ! … Z + [ , [ , Q " † P ! … Q " † [ , \ - ‘ \ - ‘ R # ‡ Q " † R # ‡ \ - ‘ ] . ’ ] . ’ S $ ˆ R # ‡ S $ ˆ ] . ’ ^ / “ ^ / “ T % ‰ S $ ˆ T % ‰ ^ / “ _ 0 ” _ 0 ” U & Š T % ‰ U & Š _ 0 ” ` 1 • ` 1 • V ' ‹ U & Š V ' ‹ ` 1 • a 2 – a 2 – W ( Œ V ' ‹ •i¾ sÉb 3 — b 3 — X ) •i¾X ) b 3 — c 4 ˜ c 4 ˜ Y * Ž X ) Y * Ž c 4 ˜ d 5 ™ d 5 ™ Z + Y * Ž Z + d 5 ™ e 6 š e 6 š [ , Z + [ , e 6 š f 7 › f 7 › \ - ‘ [ , \ - ‘ f 7 › g 8 œ g 8 œ ] . ’ \ - ‘ ] . ’ g 8 œ h 9 h 9 ^ / “ ] . ’ ^ / “ h 9 i : ž i : ž _ 0 ” ^ / “ _ 0 ” i : ž j ; Ÿ j ; Ÿ ` 1 • _ 0 ” ` 1 • j ; Ÿ k <   k <   a 2 – ` 1 •  sÉ«}Ôl = ¡ l = ¡ b 3 —  sÉb 3 — l = ¡ m > ¢ m > ¢ c 4 ˜ b 3 — c 4 ˜ m > ¢ n ? £ n ? £ d 5 ™ c 4 ˜ d 5 ™ n ? £ o @ ¤ o @ ¤ e 6 š d 5 ™ e 6 š o @ ¤ p A ¥ p A ¥ f 7 › e 6 š f 7 › p A ¥ q B ¦ q B ¦ g 8 œ f 7 › g 8 œ q B ¦ r C § r C § h 9 g 8 œ h 9 r C § s D ¨ s D ¨ i : ž h 9 i : ž s D ¨ t E © t E © j ; Ÿ i : ž j ; Ÿ t E © u F ª u F ª k <   j ; Ÿ «}Ô¶‡ßv G « v G « l = ¡ «}Ôl = ¡ v G « w H ¬ w H ¬ m > ¢ l = ¡ m > ¢ w H ¬ x I ­ x I ­ n ? £ m > ¢ n ? £ x I ­ y J ® y J ® o @ ¤ n ? £ o @ ¤ y J ® z K ¯ z K ¯ p A ¥ o @ ¤ p A ¥ z K ¯ { L ° { L ° q B ¦ p A ¥ q B ¦ { L ° | M ± | M ± r C § q B ¦ r C § | M ± } N ² } N ² s D ¨ r C § s D ¨ } N ² ~ O ³ ~ O ³ t E © s D ¨ t E © ~ O ³  P ´  P ´ u F ª t E © ¶‡ßÁ’ê€ Q µ € Q µ v G « ¶‡ßv G « € Q µ R ¶ R ¶ w H ¬ v G « w H ¬ R ¶ ‚ S · ‚ S · x I ­ w H ¬ x I ­ ‚ S · ƒ T ¸ ƒ T ¸ y J ® x I ­ y J ® ƒ T ¸ „ U ¹ „ U ¹ z K ¯ y J ® z K ¯ „ U ¹ … V º … V º { L ° z K ¯ { L ° … V » † W ¼ † W ¼ | M ± { L ° | M ± † W ½ ‡ X ¾ ‡ X ¾ } N ² | M ± } N ² ‡ X ¾ ˆ Y ¿ ˆ Y ¿ ~ O ³ } N ² ~ O ³ ˆ Y ¿ ‰ Z À ‰ Z À  P ´ ~ O ³ Á’êûÓ$ÔìÃ”ì€ Q µ Á’ê€ Q µ ÔìÅ–îÅ–î R ¶ € Q µ R ¶ Å–îǘðǘð‚ S · R ¶ ‚ S · ǘðÉšòÉšòƒ T ¸ ‚ S · ƒ T ¸ ÉšòËœôËœô„ U ¹ ƒ T ¸ „ U ¹ ËœôÍžöÍžö… V º „ U ¹ … V » Íž÷Ï úÏ ú† W ¼ … V » † W ½ Ï ûÑ¢þÑ¢þ‡ X ¾ † W ½ ‡ X ¾ Ñ¢þÓ¤ Ó¤ ˆ Y ¿ ‡ X ¾ ˆ Y ¿ Ó¤ Õ¦ Õ¦ ‰ Z À ˆ Y ¿ Š ÃÁ Š ÃÁ ‹ [  ‹ [  Œ \ Ã Š ÃÁ Œ \ à ‹ [  ] Ä ] Ä Ž ^ Å Œ \ Ã Ž ^ Å ] Ä _ Æ _ Æ ` Ç Ž ^ Å ` È _ É ‘ a Ê ‘ a Ê ’ b Ë ` È ’ b Ë ‘ a Ê “ c Ì “ c Ì ” d Í ’ b Ë ” d Í “ c Ì • e Î • e Î – f Ï ” d Í – f Ï • e Î — g Ð — g Ð ˜ h Ñ – f Ï ˜ h Ñ — g Ð ™ i Ò ™ i Ò š j Ó ˜ h Ñ š j Ó ™ i Ò › k Ô › k Ô œ l Õ š j Ó œ l Ö › k × m Ø m Ø ž n Ù œ l Ö Š ÃÁ Š ÃÁ Ÿ o Ú Ÿ o Ú ‹ [ Â Š ÃÁ ‹ [  Ÿ o Ú   p Û   p Û ] Ä ‹ [  ] Ä   p Û ¡ q Ü ¡ q Ü _ Æ ] Ä _ É ¡ q Ý ¢ r Þ ¢ r Þ ‘ a Ê _ É ‘ a Ê ¢ r Þ £ s ß £ s ß “ c Ì ‘ a Ê “ c Ì £ s ß ¤ t à ¤ t à • e Î “ c Ì • e Î ¤ t à ¥ u á ¥ u á — g Ð • e Î — g Ð ¥ u á ¦ v â ¦ v â ™ i Ò — g Ð ™ i Ò ¦ v â § w ã § w ã › k Ô ™ i Ò › k × § w ä ¨ x å ¨ x å m Ø › k × Š ÃÁ Š ÃÁ © y æ © y æ Ÿ o Ú Š ÃÁ Ÿ o Ú © y æ ª z ç ª z ç   p Û Ÿ o Ú   p Û ª z ç « { è « { è ¡ q Ü   p Û ¡ q Ý « { é ¬ | ê ¬ | ê ¢ r Þ ¡ q Ý ¢ r Þ ¬ | ê ­ } ë ­ } ë £ s ß ¢ r Þ £ s ß ­ } ë ® ~ ì ® ~ ì ¤ t à £ s ß ¤ t à ® ~ ì ¯  í ¯  í ¥ u á ¤ t à ¥ u á ¯  í ° € î ° € î ¦ v â ¥ u á ¦ v â ° € î ± ï ± ï § w ã ¦ v â § w ä ± ð ² ‚ ñ ² ‚ ñ ¨ x å § w ä Š ÃÁ Š ÃÁ ³ ƒ ò ³ ƒ ò © y æ Š ÃÁ © y æ ³ ƒ ò ´ „ ó ´ „ ó ª z ç © y æ ª z ç ´ „ ó µ … ô µ … ô « { è ª z ç « { é µ … õ ¶ † ö ¶ † ö ¬ | ê « { é ¬ | ê ¶ † ö · ‡ ÷ · ‡ ÷ ­ } ë ¬ | ê ­ } ë · ‡ ÷ ¸ ˆ ø ¸ ˆ ø ® ~ ì ­ } ë ® ~ ì ¸ ˆ ø ¹ ‰ ù ¹ ‰ ù ¯  í ® ~ ì ¯  í ¹ ‰ ù º Š ú º Š ú ° € î ¯  í ° € î º Š ú » ‹ û » ‹ û ± ï ° € î ± ð » ‹ ü ¼ Œ ý ¼ Œ ý ² ‚ ñ ± ð Š ÃÁ Š ÃÁ ½ þ ½ þ ³ ƒ ò Š ÃÁ ³ ƒ ò ½ þ ¾ Ž ÿ ¾ Ž ÿ ´ „ ó ³ ƒ ò ´ „ ó ¾ Ž ÿ ¿ ¿ µ … ô ´ „ ó µ … õ ¿  À  À  ¶ † ö µ … õ ¶ † ö À  Á ‘  Á ‘  · ‡ ÷ ¶ † ö · ‡ ÷ Á ‘   ’   ’  ¸ ˆ ø · ‡ ÷ ¸ ˆ ø  ’  à “  à “  ¹ ‰ ù ¸ ˆ ø ¹ ‰ ù à “  Ä ”  Ä ”  º Š ú ¹ ‰ ù º Š ú Ä ”  Å •  Å •  » ‹ û º Š ú » ‹ ü Å •  Æ – Æ – ¼ Œ ý » ‹ ü Š ÃÁ Š ÃÁ Ç — Ç — ½ þ Š ÃÁ ½ þ Ç — È ˜ È ˜ ¾ Ž ÿ ½ þ ¾ Ž ÿ È ˜ É ™ É ™ ¿ ¾ Ž ÿ ¿  É ™ Ê š  Ê š  À  ¿  À  Ê š  Ë ›  Ë ›  Á ‘  À  Á ‘  Ë ›  Ì œ  Ì œ   ’  Á ‘   ’  Ì œ  Í  Í  à “   ’  à “  Í  Î ž  Î ž  Ä ”  à “  Ä ”  Î ž  Ï Ÿ  Ï Ÿ  Å •  Ä ”  Å •  Ï Ÿ  Ð    Ð    Æ – Å •  Š ÃÁ Š ÃÁ Ñ ¡  Ñ ¡  Ç — Š ÃÁ Ç — Ñ ¡  Ò ¢  Ò ¢  È ˜ Ç — È ˜ Ò ¢  Ó £  Ó £  É ™ È ˜ É ™ Ó £  Ô ¤  Ô ¤  Ê š  É ™ Ê š  Ô ¤  Õ ¥  Õ ¥  Ë ›  Ê š  Ë ›  Õ ¥  Ö ¦  Ö ¦  Ì œ  Ë ›  Ì œ  Ö ¦  × §  × §  Í  Ì œ  Í  × §  Ø ¨  Ø ¨  Î ž  Í  Î ž  Ø ¨  Ù ©  Ù ©  Ï Ÿ  Î ž  Ï Ÿ  Ù © Ú ª ! Ú ª ! Ð    Ï Ÿ  Š ÃÁ Š ÃÁ Û « " Û « " Ñ ¡  Š ÃÁ Ñ ¡  Û « " Ü ¬ # Ü ¬ # Ò ¢  Ñ ¡  Ò ¢  Ü ¬ # Ý ­ $ Ý ­ $ Ó £  Ò ¢  Ó £  Ý ­ % Þ ® & Þ ® & Ô ¤  Ó £  Ô ¤  Þ ® & ß ¯ ' ß ¯ ' Õ ¥  Ô ¤  Õ ¥  ß ¯ ' à ° ( à ° ( Ö ¦  Õ ¥  Ö ¦  à ° ( á ± ) á ± ) × §  Ö ¦  × §  á ± ) â ² * â ² * Ø ¨  × §  Ø ¨  â ² * ã ³ + ã ³ + Ù ©  Ø ¨  Ù © ã ³ , ä ´ - ä ´ - Ú ª ! Ù © Š ÃÁ Š ÃÁ å µ . å µ . Û « " Š ÃÁ Û « " å µ . æ ¶ / æ ¶ / Ü ¬ # Û « " Ü ¬ # æ ¶ / ç · 0 ç · 0 Ý ­ $ Ü ¬ # Ý ­ % ç · 1 è ¸ 2 è ¸ 2 Þ ® & Ý ­ % Þ ® & è ¸ 2 é ¹ 3 é ¹ 3 ß ¯ ' Þ ® & ß ¯ ' é ¹ 3 ê º 4 ê º 4 à ° ( ß ¯ ' à ° ( ê º 4 ë » 5 ë » 5 á ± ) à ° ( á ± ) ë » 5 ì ¼ 6 ì ¼ 6 â ² * á ± ) â ² * ì ¼ 6 í ½ 7 í ½ 7 ã ³ + â ² * ã ³ , í ½ 8 î ¾ 9 î ¾ 9 ä ´ - ã ³ , Š ÃÁ Š ÃÁ ï ¿ : ï ¿ : å µ . Š ÃÁ å µ . ï ¿ : ð À ; ð À ; æ ¶ / å µ . æ ¶ / ð À ; ñ Á < ñ Á < ç · 0 æ ¶ / ç · 1 ñ Á = ò  > ò  > è ¸ 2 ç · 1 è ¸ 2 ò  > ó à ? ó à ? é ¹ 3 è ¸ 2 é ¹ 3 ó à ? ô Ä @ ô Ä @ ê º 4 é ¹ 3 ê º 4 ô Ä @ õ Å A õ Å A ë » 5 ê º 4 ë » 5 õ Å A ö Æ B ö Æ B ì ¼ 6 ë » 5 ì ¼ 6 ö Æ B ÷ Ç C ÷ Ç C í ½ 7 ì ¼ 6 í ½ 8 ÷ Ç D ø È E ø È E î ¾ 9 í ½ 8 Š ÃÁ Š ÃÁ ù É F ù É F ï ¿ : Š ÃÁ ï ¿ : ù É F ú Ê G ú Ê G ð À ; ï ¿ : ð À ; ú Ê G û Ë H û Ë H ñ Á < ð À ; ñ Á = û Ë I ü Ì J ü Ì J ò  > ñ Á = ò  > ü Ì J ý Í K ý Í K ó à ? ò  > ó à ? ý Í K þ Î L þ Î L ô Ä @ ó à ? ô Ä @ þ Î L ÿ Ï M ÿ Ï M õ Å A ô Ä @ õ Å A ÿ Ï M Ð N Ð N ö Æ B õ Å A ö Æ B Ð N  Ñ O  Ñ O ÷ Ç C ö Æ B ÷ Ç D  Ñ P  Ò Q  Ò Q ø È E ÷ Ç D Š ÃÁ Š ÃÁ  Ó R  Ó R ù É F Š ÃÁ ù É F  Ó R  Ô S  Ô S ú Ê G ù É F ú Ê G  Ô S  Õ T  Õ T û Ë H ú Ê G û Ë I  Õ U  Ö V  Ö V ü Ì J û Ë I ü Ì J  Ö V  × W  × W ý Í K ü Ì J ý Í K  × W  Ø X  Ø X þ Î L ý Í K þ Î L  Ø X Ù Y Ù Y ÿ Ï M þ Î L ÿ Ï M Ù Y Ú Z Ú Z Ð N ÿ Ï M Ð N Ú Z Û [ Û [  Ñ O Ð N  Ñ P Û \ Ü ] Ü ]  Ò Q  Ñ P Š ÃÁ Š ÃÁ Ý ^ Ý ^  Ó R Š ÃÁ  Ó R Ý ^  Þ _  Þ _  Ô S  Ó R  Ô S  Þ _  ß `  ß `  Õ T  Ô S  Õ U  ß a  à b  à b  Ö V  Õ U  Ö V  à b  á c  á c  × W  Ö V  × W  á c  â d  â d  Ø X  × W  Ø X  â d  ã e  ã e Ù Y  Ø X Ù Y  ã e  ä f  ä f Ú Z Ù Y Ú Z  ä f  å g  å g Û [ Ú Z Û \  å h  æ i  æ i Ü ] Û \ Š ÃÁ Š ÃÁ  ç j  ç j Ý ^ Š ÃÁ Ý ^  ç j  è k  è k  Þ _ Ý ^  Þ _  è k  é l  é l  ß `  Þ _  ß a  é m  ê n  ê n  à b  ß a  à b  ê n  ë o  ë o  á c  à b  á c  ë o  ì p  ì p  â d  á c  â d  ì p  í q  í q  ã e  â d  ã e  í q  î r  î r  ä f  ã e  ä f  î r  ï s  ï s  å g  ä f  å h  ï t ð u ð u  æ i  å h Š ÃÁ Š ÃÁ ! ñ v ! ñ v  ç j Š ÃÁ  ç j ! ñ v " ò w " ò w  è k  ç j  è k " ò w # ó x # ó x  é l  è k  é m # ó y $ ô z $ ô z  ê n  é m  ê n $ ô z % õ { % õ {  ë o  ê n  ë o % õ { & ö | & ö |  ì p  ë o  ì p & ö | ' ÷ } ' ÷ }  í q  ì p  í q ' ÷ } ( ø ~ ( ø ~  î r  í q  î r ( ø ~ ) ù  ) ù   ï s  î r  ï t ) ù € * ú * ú ð u  ï t Š ÃÁ Š ÃÁ + û ‚ + û ‚ ! ñ v Š ÃÁ ! ñ v + û ‚ , ü ƒ , ü ƒ " ò w ! ñ v " ò w , ü ƒ - ý „ - ý „ # ó x " ò w # ó y - ý … . þ † . þ † $ ô z # ó y $ ô z . þ † / ÿ ‡ / ÿ ‡ % õ { $ ô z % õ { / ÿ ‡ 0 ˆ 0 ˆ & ö | % õ { & ö | 0 ˆ 1  ‰ 1  ‰ ' ÷ } & ö | ' ÷ } 1  ‰ 2  Š 2  Š ( ø ~ ' ÷ } ( ø ~ 2  Š 3  ‹ 3  ‹ ) ù  ( ø ~ ) ù € 3  Œ 4  4  * ú ) ù € Š ÃÁ Š ÃÁ 5  Ž 5  Ž + û ‚ Š ÃÁ + û ‚ 5  Ž 6  6  , ü ƒ + û ‚ , ü ƒ 6  7  7  - ý „ , ü ƒ - ý … 7  ‘ 8  ’ 8  ’ . þ † - ý … . þ † 8  ’ 9 “ 9 “ / ÿ ‡ . þ † / ÿ ‡ 9 “ : ” : ” 0 ˆ / ÿ ‡ 0 ˆ : ” ; • ; • 1  ‰ 0 ˆ 1  ‰ ; • < – < – 2  Š 1  ‰ 2  Š < – = — = — 3  ‹ 2  Š 3  Œ = ˜ >  ™ >  ™ 4  3  Œ Š ÃÁ Š ÃÁ ?  š ?  š 5  Ž Š ÃÁ 5  Ž ?  š @  › @  › 6  5  Ž 6  @  › A  œ A  œ 7  6  7  ‘ A  B  ž B  ž 8  ’ 7  ‘ 8  ’ B  ž C  Ÿ C  Ÿ 9 “ 8  ’ 9 “ C  Ÿ D    D    : ” 9 “ : ” D    E  ¡ E  ¡ ; • : ” ; • E  ¡ F  ¢ F  ¢ < – ; • < – F  ¢ G  £ G  £ = — < – = ˜ G  ¤ H  ¥ H  ¥ >  ™ = ˜ Š ÃÁ Š ÃÁ I  ¦ I  ¦ ?  š Š ÃÁ ?  š I  ¦ J  § J  § @  › ?  š @  › J  § K  ¨ K  ¨ A  œ @  › A  K  © L  ª L  ª B  ž A  B  ž L  ª M  « M  « C  Ÿ B  ž C  Ÿ M  « N  ¬ N  ¬ D    C  Ÿ D    N  ¬ O  ­ O  ­ E  ¡ D    E  ¡ O  ­ P ® P ® F  ¢ E  ¡ F  ¢ P ® Q ! ¯ Q ! ¯ G  £ F  ¢ G  ¤ Q ! ° R " ± R " ± H  ¥ G  ¤ Š ÃÁ Š ÃÁ S # ² S # ² I  ¦ Š ÃÁ I  ¦ S # ² T $ ³ T $ ³ J  § I  ¦ J  § T $ ³ U % ´ U % ´ K  ¨ J  § K  © U % µ V & ¶ V & ¶ L  ª K  © L  ª V & ¶ W ' · W ' · M  « L  ª M  « W ' · X ( ¸ X ( ¸ N  ¬ M  « N  ¬ X ( ¸ Y ) ¹ Y ) ¹ O  ­ N  ¬ O  ­ Y ) ¹ Z * º Z * º P ® O  ­ P ® Z * º [ + » [ + » Q ! ¯ P ® Q ! ° [ + ¼ \ , ½ \ , ½ R " ± Q ! ° Š ÃÁ Š ÃÁ ] - ¾ ] - ¾ S # ² Š ÃÁ S # ² ] - ¾ ^ . ¿ ^ . ¿ T $ ³ S # ² T $ ³ ^ . ¿ _ / À _ / À U % ´ T $ ³ U % µ _ / Á ` 0  ` 0  V & ¶ U % µ V & ¶ ` 0  a 1 à a 1 à W ' · V & ¶ W ' · a 1 à b 2 Ä b 2 Ä X ( ¸ W ' · X ( ¸ b 2 Ä c 3 Å c 3 Å Y ) ¹ X ( ¸ Y ) ¹ c 3 Å d 4 Æ d 4 Æ Z * º Y ) ¹ Z * º d 4 Æ e 5 Ç e 5 Ç [ + » Z * º [ + ¼ e 5 È f 6 É f 6 É \ , ½ [ + ¼ Š ÃÁ Š ÃÁ g 7 Ê g 7 Ê ] - ¾ Š ÃÁ ] - ¾ g 7 Ê h 8 Ë h 8 Ë ^ . ¿ ] - ¾ ^ . ¿ h 8 Ë i 9 Ì i 9 Ì _ / À ^ . ¿ _ / Á i 9 Í j : Î j : Î ` 0  _ / Á ` 0  j : Î k ; Ï k ; Ï a 1 à ` 0  a 1 à k ; Ï l < Ð l < Ð b 2 Ä a 1 à b 2 Ä l < Ð m = Ñ m = Ñ c 3 Å b 2 Ä c 3 Å m = Ñ n > Ò n > Ò d 4 Æ c 3 Å d 4 Æ n > Ò o ? Ó o ? Ó e 5 Ç d 4 Æ e 5 È o ? Ô p @ Õ p @ Õ f 6 É e 5 È Š ÃÁ Š ÃÁ q A Ö q A Ö g 7 Ê Š ÃÁ g 7 Ê q A Ö r B × r B × h 8 Ë g 7 Ê h 8 Ë r B × s C Ø s C Ø i 9 Ì h 8 Ë i 9 Í s C Ù t D Ú t D Ú j : Î i 9 Í j : Î t D Ú u E Û u E Û k ; Ï j : Î k ; Ï u E Û v F Ü v F Ü l < Ð k ; Ï l < Ð v F Ü w G Ý w G Ý m = Ñ l < Ð m = Ñ w G Ý x H Þ x H Þ n > Ò m = Ñ n > Ò x H Þ y I ß y I ß o ? Ó n > Ò o ? Ô y I à z J á z J á p @ Õ o ? Ô Š ÃÁ Š ÃÁ { K â { K â q A Ö Š ÃÁ q A Ö { K â | L ã | L ã r B × q A Ö r B × | L ã } M ä } M ä s C Ø r B × s C Ù } M å ~ N æ ~ N æ t D Ú s C Ù t D Ú ~ N æ  O ç  O ç u E Û t D Ú u E Û  O ç € P è € P è v F Ü u E Û v F Ü € P è Q é Q é w G Ý v F Ü w G Ý Q é ‚ R ê ‚ R ê x H Þ w G Ý x H Þ ‚ R ê ƒ S ë ƒ S ë y I ß x H Þ y I à ƒ S ì „ T í „ T í z J á y I à Š ÃÁ Š ÃÁ … U î … U î { K â Š ÃÁ { K â … U î † V ï † V ï | L ã { K â | L ã † V ï ‡ W ð ‡ W ð } M ä | L ã } M å ‡ W ñ ˆ X ò ˆ X ò ~ N æ } M å ~ N æ ˆ X ò ‰ Y ó ‰ Y ó  O ç ~ N æ  O ç ‰ Y ó Š Z ô Š Z ô € P è  O ç € P è Š Z ô ‹ [ õ ‹ [ õ Q é € P è Q é ‹ [ õ Œ \ ö Œ \ ö ‚ R ê Q é ‚ R ê Œ \ ö ] ÷ ] ÷ ƒ S ë ‚ R ê ƒ S ì ] ø Ž ^ ù Ž ^ ù „ T í ƒ S ì Š ÃÁ Š ÃÁ _ ú _ ú … U î Š ÃÁ … U î _ ú ` û ` û † V ï … U î † V ï ` û ‘ a ü ‘ a ü ‡ W ð † V ï ‡ W ñ ‘ a ý ’ b þ ’ b þ ˆ X ò ‡ W ñ ˆ X ò ’ b þ “ c ÿ “ c ÿ ‰ Y ó ˆ X ò ‰ Y ó “ c ÿ ” d ” d Š Z ô ‰ Y ó Š Z ô ” d • e  • e  ‹ [ õ Š Z ô ‹ [ õ • e  – f  – f  Œ \ ö ‹ [ õ Œ \ ö – f  — g  — g  ] ÷ Œ \ ö ] ø — g  ˜ h  ˜ h  Ž ^ ù ] ø Š ÃÁ Š ÃÁ ™ i  ™ i  _ ú Š ÃÁ _ ú ™ i  š j  š j  ` û _ ú ` û š j  › k  › k  ‘ a ü ` û ‘ a ý › k œ l œ l ’ b þ ‘ a ý ’ b þ œ l m m “ c ÿ ’ b þ “ c ÿ m ž n ž n ” d “ c ÿ ” d ž n Ÿ o Ÿ o • e  ” d • e  Ÿ o   p    p  – f  • e  – f    p  ¡ q  ¡ q  — g  – f  — g  ¡ q  ¢ r  ¢ r  ˜ h  — g  Š ÃÁ Š ÃÁ £ s  £ s  ™ i  Š ÃÁ ™ i  £ s  ¤ t  ¤ t  š j  ™ i  š j  ¤ t  ¥ u  ¥ u  › k  š j  › k ¥ u  ¦ v  ¦ v  œ l › k œ l ¦ v  § w  § w  m œ l m § w  ¨ x  ¨ x  ž n m ž n ¨ x  © y  © y  Ÿ o ž n Ÿ o © y  ª z  ª z    p  Ÿ o   p  ª z  « {  « {  ¡ q    p  ¡ q  « {  ¬ |  ¬ |  ¢ r  ¡ q  Š ÃÁ Š ÃÁ ­ }  ­ }  £ s  Š ÃÁ £ s  ­ }  ® ~  ® ~  ¤ t  £ s  ¤ t  ® ~  ¯  ¯  ¥ u  ¤ t  ¥ u  ¯  ! ° € " ° € " ¦ v  ¥ u  ¦ v  ° € " ± # ± # § w  ¦ v  § w  ± # ² ‚ $ ² ‚ $ ¨ x  § w  ¨ x  ² ‚ $ ³ ƒ % ³ ƒ % © y  ¨ x  © y  ³ ƒ % ´ „ & ´ „ & ª z  © y  ª z  ´ „ & µ … ' µ … ' « {  ª z  « {  µ … ( ¶ † ) ¶ † ) ¬ |  « {  Š ÃÁ Š ÃÁ · ‡ * · ‡ * ­ }  Š ÃÁ ­ }  · ‡ * ¸ ˆ + ¸ ˆ + ® ~  ­ }  ® ~  ¸ ˆ + ¹ ‰ , ¹ ‰ , ¯  ® ~  ¯  ! ¹ ‰ - º Š . º Š . ° € " ¯  ! ° € " º Š . » ‹ / » ‹ / ± # ° € " ± # » ‹ / ¼ Œ 0 ¼ Œ 0 ² ‚ $ ± # ² ‚ $ ¼ Œ 0 ½ 1 ½ 1 ³ ƒ % ² ‚ $ ³ ƒ % ½ 1 ¾ Ž 2 ¾ Ž 2 ´ „ & ³ ƒ % ´ „ & ¾ Ž 2 ¿ 3 ¿ 3 µ … ' ´ „ & µ … ( ¿ 4 À 5 À 5 ¶ † ) µ … ( Š ÃÁ Š ÃÁ Á ‘ 6 Á ‘ 6 · ‡ * Š ÃÁ · ‡ * Á ‘ 6  ’ 7  ’ 7 ¸ ˆ + · ‡ * ¸ ˆ +  ’ 7 à “ 8 à “ 8 ¹ ‰ , ¸ ˆ + ¹ ‰ - à “ 9 Ä ” : Ä ” : º Š . ¹ ‰ - º Š . Ä ” : Å • ; Å • ; » ‹ / º Š . » ‹ / Å • ; Æ – < Æ – < ¼ Œ 0 » ‹ / ¼ Œ 0 Æ – < Ç — = Ç — = ½ 1 ¼ Œ 0 ½ 1 Ç — = È ˜ > È ˜ > ¾ Ž 2 ½ 1 ¾ Ž 2 È ˜ > É ™ ? É ™ ? ¿ 3 ¾ Ž 2 ¿ 4 É ™ @ Ê š A Ê š A À 5 ¿ 4 Š ÃÁ Š ÃÁ Ë › B Ë › B Á ‘ 6 Š ÃÁ Á ‘ 6 Ë › B Ì œ C Ì œ C  ’ 7 Á ‘ 6  ’ 7 Ì œ C Í D Í D à “ 8  ’ 7 à “ 9 Í E Î ž F Î ž F Ä ” : à “ 9 Ä ” : Î ž F Ï Ÿ G Ï Ÿ G Å • ; Ä ” : Å • ; Ï Ÿ G Ð   H Ð   H Æ – < Å • ; Æ – < Ð   H Ñ ¡ I Ñ ¡ I Ç — = Æ – < Ç — = Ñ ¡ I Ò ¢ J Ò ¢ J È ˜ > Ç — = È ˜ > Ò ¢ J Ó £ K Ó £ K É ™ ? È ˜ > É ™ @ Ó £ L Ô ¤ M Ô ¤ M Ê š A É ™ @ Š ÃÁ Š ÃÁ Õ ¥ N Õ ¥ N Ë › B Š ÃÁ Ë › B Õ ¥ N Ö ¦ O Ö ¦ O Ì œ C Ë › B Ì œ C Ö ¦ O × § P × § P Í D Ì œ C Í E × § Q Ø ¨ R Ø ¨ R Î ž F Í E Î ž F Ø ¨ R Ù © S Ù © S Ï Ÿ G Î ž F Ï Ÿ G Ù © S Ú ª T Ú ª T Ð   H Ï Ÿ G Ð   H Ú ª T Û « U Û « U Ñ ¡ I Ð   H Ñ ¡ I Û « U Ü ¬ V Ü ¬ V Ò ¢ J Ñ ¡ I Ò ¢ J Ü ¬ V Ý ­ W Ý ­ W Ó £ K Ò ¢ J Ó £ L Ý ­ X Þ ® Y Þ ® Y Ô ¤ M Ó £ L Š ÃÁ Š ÃÁ ß ¯ Z ß ¯ Z Õ ¥ N Š ÃÁ Õ ¥ N ß ¯ Z à ° [ à ° [ Ö ¦ O Õ ¥ N Ö ¦ O à ° [ á ± \ á ± \ × § P Ö ¦ O × § Q á ± ] â ² ^ â ² ^ Ø ¨ R × § Q Ø ¨ R â ² ^ ã ³ _ ã ³ _ Ù © S Ø ¨ R Ù © S ã ³ _ ä ´ ` ä ´ ` Ú ª T Ù © S Ú ª T ä ´ ` å µ a å µ a Û « U Ú ª T Û « U å µ a æ ¶ b æ ¶ b Ü ¬ V Û « U Ü ¬ V æ ¶ b ç · c ç · c Ý ­ W Ü ¬ V Ý ­ X ç · d è ¸ e è ¸ e Þ ® Y Ý ­ X Š ÃÁ Š ÃÁ é ¹ f é ¹ f ß ¯ Z Š ÃÁ ß ¯ Z é ¹ f ê º g ê º g à ° [ ß ¯ Z à ° [ ê º g ë » h ë » h á ± \ à ° [ á ± ] ë » i ì ¼ j ì ¼ j â ² ^ á ± ] â ² ^ ì ¼ j í ½ k í ½ k ã ³ _ â ² ^ ã ³ _ í ½ k î ¾ l î ¾ l ä ´ ` ã ³ _ ä ´ ` î ¾ l ï ¿ m ï ¿ m å µ a ä ´ ` å µ a ï ¿ m ð À n ð À n æ ¶ b å µ a æ ¶ b ð À n ñ Á o ñ Á o ç · c æ ¶ b ç · d ñ Á p ò  q ò  q è ¸ e ç · d Š ÃÁ Š ÃÁ ó à r ó à r é ¹ f Š ÃÁ é ¹ f ó à r ô Ä s ô Ä s ê º g é ¹ f ê º g ô Ä s õ Å t õ Å t ë » h ê º g ë » i õ Å u ö Æ v ö Æ v ì ¼ j ë » i ì ¼ j ö Æ v ÷ Ç w ÷ Ç w í ½ k ì ¼ j í ½ k ÷ Ç w ø È x ø È x î ¾ l í ½ k î ¾ l ø È x ù É y ù É y ï ¿ m î ¾ l ï ¿ m ù É y ú Ê z ú Ê z ð À n ï ¿ m ð À n ú Ê z û Ë { û Ë { ñ Á o ð À n ñ Á p û Ë | ü Ì } ü Ì } ò  q ñ Á p Š ÃÁ Š ÃÁ ý Í ~ ý Í ~ ó à r Š ÃÁ ó à r ý Í ~ þ Î  þ Î  ô Ä s ó à r ô Ä s þ Î  ÿ Ï € ÿ Ï € õ Å t ô Ä s õ Å u ÿ Ï Ð ‚ Ð ‚ ö Æ v õ Å u ö Æ v Ð ‚  Ñ ƒ  Ñ ƒ ÷ Ç w ö Æ v ÷ Ç w  Ñ ƒ  Ò „  Ò „ ø È x ÷ Ç w ø È x  Ò „  Ó …  Ó … ù É y ø È x ù É y  Ó …  Ô †  Ô † ú Ê z ù É y ú Ê z  Ô †  Õ ‡  Õ ‡ û Ë { ú Ê z û Ë |  Õ ˆ  Ö ‰  Ö ‰ ü Ì } û Ë | Š ÃÁ Š ÃÁ  × Š  × Š ý Í ~ Š ÃÁ ý Í ~  × Š  Ø ‹  Ø ‹ þ Î  ý Í ~ þ Î   Ø ‹ Ù Œ Ù Œ ÿ Ï € þ Î  ÿ Ï Ù Ú Ž Ú Ž Ð ‚ ÿ Ï Ð ‚ Ú Ž Û Û  Ñ ƒ Ð ‚  Ñ ƒ Û Ü Ü  Ò „  Ñ ƒ  Ò „ Ü Ý ‘ Ý ‘  Ó …  Ò „  Ó … Ý ‘  Þ ’  Þ ’  Ô †  Ó …  Ô †  Þ ’  ß “  ß “  Õ ‡  Ô †  Õ ˆ  ß ”  à •  à •  Ö ‰  Õ ˆ Š ÃÁ Š ÃÁ  á –  á –  × Š Š ÃÁ  × Š  á –  â —  â —  Ø ‹  × Š  Ø ‹  â —  ã ˜  ã ˜ Ù Œ  Ø ‹ Ù  ã ™  ä š  ä š Ú Ž Ù Ú Ž  ä š  å ›  å › Û Ú Ž Û  å ›  æ œ  æ œ Ü Û Ü  æ œ  ç  ç Ý ‘ Ü Ý ‘  ç  è ž  è ž  Þ ’ Ý ‘  Þ ’  è ž  é Ÿ  é Ÿ  ß “  Þ ’  ß ”  é    ê ¡  ê ¡  à •  ß ” Š ÃÁ Š ÃÁ Œ \ à Œ \ à  á – Š ÃÁ  á – Œ \ Ã Ž ^ Å Ž ^ Å  â —  á –  â — Ž ^ Å ` Ç ` Ç  ã ˜  â —  ã ™ ` È ’ b Ë ’ b Ë  ä š  ã ™  ä š ’ b Ë ” d Í ” d Í  å ›  ä š  å › ” d Í – f Ï – f Ï  æ œ  å ›  æ œ – f Ï ˜ h Ñ ˜ h Ñ  ç  æ œ  ç ˜ h Ñ š j Ó š j Ó  è ž  ç  è ž š j Ó œ l Õ œ l Õ  é Ÿ  è ž  é   œ l Ö ž n Ù ž n Ù  ê ¡  é   ž n Ù m Ø  ë ¢  ë ¢  ì £ ž n Ù  ì £  ë ¢  í ¤  í ¤  î ¥  ì £  î ¥  í ¤  ï ¦  ï ¦ ð §  î ¥ ð §  ï ¦ ! ñ ¨ ! ñ ¨ " ò © ð § " ò © ! ñ ¨ # ó ª # ó ª $ ô « " ò © $ ô « # ó ª % õ ¬ % õ ¬ & ö ­ $ ô « & ö ­ % õ ¬ ' ÷ ® ' ÷ ® ( ø ¯ & ö ­ ( ø ¯ ' ÷ ® ) ù ° ) ù ° * ú ± ( ø ¯ * ú ± ) ù ° + û ² + û ² , ü ³ * ú ± , ü ³ + û ² - ý ´ - ý ´ . þ µ , ü ³ m Ø ¨ x å / ÿ ¶ / ÿ ¶  ë ¢ m Ø  ë ¢ / ÿ ¶ 0 · 0 ·  í ¤  ë ¢  í ¤ 0 · 1  ¸ 1  ¸  ï ¦  í ¤  ï ¦ 1  ¸ 2  ¹ 2  ¹ ! ñ ¨  ï ¦ ! ñ ¨ 2  ¹ 3  º 3  º # ó ª ! ñ ¨ # ó ª 3  º 4  » 4  » % õ ¬ # ó ª % õ ¬ 4  » 5  ¼ 5  ¼ ' ÷ ® % õ ¬ ' ÷ ® 5  ¼ 6  ½ 6  ½ ) ù ° ' ÷ ® ) ù ° 6  ½ 7  ¾ 7  ¾ + û ² ) ù ° + û ² 7  ¾ 8  ¿ 8  ¿ - ý ´ + û ² ¨ x å ² ‚ ñ 9 À 9 À / ÿ ¶ ¨ x å / ÿ ¶ 9 À : Á : Á 0 · / ÿ ¶ 0 · : Á ;  ;  1  ¸ 0 · 1  ¸ ;  < à < à 2  ¹ 1  ¸ 2  ¹ < à = Ä = Ä 3  º 2  ¹ 3  º = Ä >  Å >  Å 4  » 3  º 4  » >  Å ?  Æ ?  Æ 5  ¼ 4  » 5  ¼ ?  Æ @  Ç @  Ç 6  ½ 5  ¼ 6  ½ @  Ç A  È A  È 7  ¾ 6  ½ 7  ¾ A  È B  É B  É 8  ¿ 7  ¾ ² ‚ ñ ¼ Œ ý C  Ê C  Ê 9 À ² ‚ ñ 9 À C  Ê D  Ë D  Ë : Á 9 À : Á D  Ë E  Ì E  Ì ;  : Á ;  E  Ì F  Í F  Í < à ;  < à F  Í G  Î G  Î = Ä < à = Ä G  Î H  Ï H  Ï >  Å = Ä >  Å H  Ï I  Ð I  Ð ?  Æ >  Å ?  Æ I  Ð J  Ñ J  Ñ @  Ç ?  Æ @  Ç J  Ñ K  Ò K  Ò A  È @  Ç A  È K  Ò L  Ó L  Ó B  É A  È ¼ Œ ý Æ – M  Ô M  Ô C  Ê ¼ Œ ý C  Ê M  Ô N  Õ N  Õ D  Ë C  Ê D  Ë N  Õ O  Ö O  Ö E  Ì D  Ë E  Ì O  Ö P × P × F  Í E  Ì F  Í P × Q ! Ø Q ! Ø G  Î F  Í G  Î Q ! Ø R " Ù R " Ù H  Ï G  Î H  Ï R " Ù S # Ú S # Ú I  Ð H  Ï I  Ð S # Ú T $ Û T $ Û J  Ñ I  Ð J  Ñ T $ Û U % Ü U % Ü K  Ò J  Ñ K  Ò U % Ü V & Ý V & Ý L  Ó K  Ò Æ – Ð    W ' Þ W ' Þ M  Ô Æ – M  Ô W ' Þ X ( ß X ( ß N  Õ M  Ô N  Õ X ( ß Y ) à Y ) à O  Ö N  Õ O  Ö Y ) à Z * á Z * á P × O  Ö P × Z * á [ + â [ + â Q ! Ø P × Q ! Ø [ + â \ , ã \ , ã R " Ù Q ! Ø R " Ù \ , ã ] - ä ] - ä S # Ú R " Ù S # Ú ] - ä ^ . å ^ . å T $ Û S # Ú T $ Û ^ . å _ / æ _ / æ U % Ü T $ Û U % Ü _ / æ ` 0 ç ` 0 ç V & Ý U % Ü Ð    Ú ª ! a 1 è a 1 è W ' Þ Ð    W ' Þ a 1 è b 2 é b 2 é X ( ß W ' Þ X ( ß b 2 é c 3 ê c 3 ê Y ) à X ( ß Y ) à c 3 ê d 4 ë d 4 ë Z * á Y ) à Z * á d 4 ë e 5 ì e 5 ì [ + â Z * á [ + â e 5 ì f 6 í f 6 í \ , ã [ + â \ , ã f 6 í g 7 î g 7 î ] - ä \ , ã ] - ä g 7 î h 8 ï h 8 ï ^ . å ] - ä ^ . å h 8 ï i 9 ð i 9 ð _ / æ ^ . å _ / æ i 9 ð j : ñ j : ñ ` 0 ç _ / æ Ú ª ! ä ´ - k ; ò k ; ò a 1 è Ú ª ! a 1 è k ; ò l < ó l < ó b 2 é a 1 è b 2 é l < ó m = ô m = ô c 3 ê b 2 é c 3 ê m = ô n > õ n > õ d 4 ë c 3 ê d 4 ë n > õ o ? ö o ? ö e 5 ì d 4 ë e 5 ì o ? ö p @ ÷ p @ ÷ f 6 í e 5 ì f 6 í p @ ÷ q A ø q A ø g 7 î f 6 í g 7 î q A ø r B ù r B ù h 8 ï g 7 î h 8 ï r B ù s C ú s C ú i 9 ð h 8 ï i 9 ð s C ú t D û t D û j : ñ i 9 ð ä ´ - î ¾ 9 u E ü u E ü k ; ò ä ´ - k ; ò u E ü v F ý v F ý l < ó k ; ò l < ó v F ý w G þ w G þ m = ô l < ó m = ô w G þ x H ÿ x H ÿ n > õ m = ô n > õ x H ÿ y I y I o ? ö n > õ o ? ö y I z J  z J  p @ ÷ o ? ö p @ ÷ z J  { K  { K  q A ø p @ ÷ q A ø { K  | L  | L  r B ù q A ø r B ù | L  } M  } M  s C ú r B ù s C ú } M  ~ N  ~ N  t D û s C ú î ¾ 9 ø È E  O   O  u E ü î ¾ 9 u E ü  O  € P  € P  v F ý u E ü v F ý € P  Q  Q  w G þ v F ý w G þ Q  ‚ R ‚ R x H ÿ w G þ x H ÿ ‚ R ƒ S ƒ S y I x H ÿ y I ƒ S „ T „ T z J  y I z J  „ T … U … U { K  z J  { K  … U † V † V | L  { K  | L  † V ‡ W  ‡ W  } M  | L  } M  ‡ W  ˆ X  ˆ X  ~ N  } M  ø È E  Ò Q ‰ Y  ‰ Y   O  ø È E  O  ‰ Y  Š Z  Š Z  € P   O  € P  Š Z  ‹ [  ‹ [  Q  € P  Q  ‹ [  Œ \  Œ \  ‚ R Q  ‚ R Œ \  ]  ]  ƒ S ‚ R ƒ S ]  Ž ^  Ž ^  „ T ƒ S „ T Ž ^  _  _  … U „ T … U _  `  `  † V … U † V `  ‘ a  ‘ a  ‡ W  † V ‡ W  ‘ a  ’ b  ’ b  ˆ X  ‡ W   Ò Q Ü ] “ c  “ c  ‰ Y   Ò Q ‰ Y  “ c  ” d  ” d  Š Z  ‰ Y  Š Z  ” d  • e  • e  ‹ [  Š Z  ‹ [  • e  – f  – f  Œ \  ‹ [  Œ \  – f  — g  — g  ]  Œ \  ]  — g  ˜ h  ˜ h  Ž ^  ]  Ž ^  ˜ h  ™ i ™ i _  Ž ^  _  ™ i š j ! š j ! `  _  `  š j ! › k " › k " ‘ a  `  ‘ a  › k " œ l # œ l # ’ b  ‘ a  Ü ]  æ i m $ m $ “ c  Ü ] “ c  m $ ž n % ž n % ” d  “ c  ” d  ž n % Ÿ o & Ÿ o & • e  ” d  • e  Ÿ o &   p '   p ' – f  • e  – f    p ' ¡ q ( ¡ q ( — g  – f  — g  ¡ q ( ¢ r ) ¢ r ) ˜ h  — g  ˜ h  ¢ r ) £ s * £ s * ™ i ˜ h  ™ i £ s * ¤ t + ¤ t + š j ! ™ i š j ! ¤ t + ¥ u , ¥ u , › k " š j ! › k " ¥ u , ¦ v - ¦ v - œ l # › k "  æ i ð u § w . § w . m $  æ i m $ § w . ¨ x / ¨ x / ž n % m $ ž n % ¨ x / © y 0 © y 0 Ÿ o & ž n % Ÿ o & © y 0 ª z 1 ª z 1   p ' Ÿ o &   p ' ª z 1 « { 2 « { 2 ¡ q (   p ' ¡ q ( « { 2 ¬ | 3 ¬ | 3 ¢ r ) ¡ q ( ¢ r ) ¬ | 3 ­ } 4 ­ } 4 £ s * ¢ r ) £ s * ­ } 4 ® ~ 5 ® ~ 5 ¤ t + £ s * ¤ t + ® ~ 5 ¯  6 ¯  6 ¥ u , ¤ t + ¥ u , ¯  6 ° € 7 ° € 7 ¦ v - ¥ u , ð u * ú ± 8 ± 8 § w . ð u § w . ± 8 ² ‚ 9 ² ‚ 9 ¨ x / § w . ¨ x / ² ‚ 9 ³ ƒ : ³ ƒ : © y 0 ¨ x / © y 0 ³ ƒ : ´ „ ; ´ „ ; ª z 1 © y 0 ª z 1 ´ „ ; µ … < µ … < « { 2 ª z 1 « { 2 µ … < ¶ † = ¶ † = ¬ | 3 « { 2 ¬ | 3 ¶ † = · ‡ > · ‡ > ­ } 4 ¬ | 3 ­ } 4 · ‡ > ¸ ˆ ? ¸ ˆ ? ® ~ 5 ­ } 4 ® ~ 5 ¸ ˆ ? ¹ ‰ @ ¹ ‰ @ ¯  6 ® ~ 5 ¯  6 ¹ ‰ @ º Š A º Š A ° € 7 ¯  6 * ú 4  » ‹ B » ‹ B ± 8 * ú ± 8 » ‹ B ¼ Œ C ¼ Œ C ² ‚ 9 ± 8 ² ‚ 9 ¼ Œ C ½ D ½ D ³ ƒ : ² ‚ 9 ³ ƒ : ½ D ¾ Ž E ¾ Ž E ´ „ ; ³ ƒ : ´ „ ; ¾ Ž E ¿ F ¿ F µ … < ´ „ ; µ … < ¿ F À G À G ¶ † = µ … < ¶ † = À G Á ‘ H Á ‘ H · ‡ > ¶ † = · ‡ > Á ‘ H  ’ I  ’ I ¸ ˆ ? · ‡ > ¸ ˆ ?  ’ I à “ J à “ J ¹ ‰ @ ¸ ˆ ? ¹ ‰ @ à “ J Ä ” K Ä ” K º Š A ¹ ‰ @ 4  >  ™ Å • L Å • L » ‹ B 4  » ‹ B Å • L Æ – M Æ – M ¼ Œ C » ‹ B ¼ Œ C Æ – M Ç — N Ç — N ½ D ¼ Œ C ½ D Ç — N È ˜ O È ˜ O ¾ Ž E ½ D ¾ Ž E È ˜ O É ™ P É ™ P ¿ F ¾ Ž E ¿ F É ™ P Ê š Q Ê š Q À G ¿ F À G Ê š Q Ë › R Ë › R Á ‘ H À G Á ‘ H Ë › R Ì œ S Ì œ S  ’ I Á ‘ H  ’ I Ì œ S Í T Í T à “ J  ’ I à “ J Í T Î ž U Î ž U Ä ” K à “ J >  ™ H  ¥ Ï Ÿ V Ï Ÿ V Å • L >  ™ Å • L Ï Ÿ V Ð   W Ð   W Æ – M Å • L Æ – M Ð   W Ñ ¡ X Ñ ¡ X Ç — N Æ – M Ç — N Ñ ¡ X Ò ¢ Y Ò ¢ Y È ˜ O Ç — N È ˜ O Ò ¢ Y Ó £ Z Ó £ Z É ™ P È ˜ O É ™ P Ó £ Z Ô ¤ [ Ô ¤ [ Ê š Q É ™ P Ê š Q Ô ¤ [ Õ ¥ \ Õ ¥ \ Ë › R Ê š Q Ë › R Õ ¥ \ Ö ¦ ] Ö ¦ ] Ì œ S Ë › R Ì œ S Ö ¦ ] × § ^ × § ^ Í T Ì œ S Í T × § ^ Ø ¨ _ Ø ¨ _ Î ž U Í T H  ¥ R " ± Ù © ` Ù © ` Ï Ÿ V H  ¥ Ï Ÿ V Ù © ` Ú ª a Ú ª a Ð   W Ï Ÿ V Ð   W Ú ª a Û « b Û « b Ñ ¡ X Ð   W Ñ ¡ X Û « b Ü ¬ c Ü ¬ c Ò ¢ Y Ñ ¡ X Ò ¢ Y Ü ¬ c Ý ­ d Ý ­ d Ó £ Z Ò ¢ Y Ó £ Z Ý ­ d Þ ® e Þ ® e Ô ¤ [ Ó £ Z Ô ¤ [ Þ ® e ß ¯ f ß ¯ f Õ ¥ \ Ô ¤ [ Õ ¥ \ ß ¯ f à ° g à ° g Ö ¦ ] Õ ¥ \ Ö ¦ ] à ° g á ± h á ± h × § ^ Ö ¦ ] × § ^ á ± h â ² i â ² i Ø ¨ _ × § ^ R " ± \ , ½ ã ³ j ã ³ j Ù © ` R " ± Ù © ` ã ³ j ä ´ k ä ´ k Ú ª a Ù © ` Ú ª a ä ´ k å µ l å µ l Û « b Ú ª a Û « b å µ l æ ¶ m æ ¶ m Ü ¬ c Û « b Ü ¬ c æ ¶ m ç · n ç · n Ý ­ d Ü ¬ c Ý ­ d ç · n è ¸ o è ¸ o Þ ® e Ý ­ d Þ ® e è ¸ o é ¹ p é ¹ p ß ¯ f Þ ® e ß ¯ f é ¹ p ê º q ê º q à ° g ß ¯ f à ° g ê º q ë » r ë » r á ± h à ° g á ± h ë » r ì ¼ s ì ¼ s â ² i á ± h \ , ½ f 6 É í ½ t í ½ t ã ³ j \ , ½ ã ³ j í ½ t î ¾ u î ¾ u ä ´ k ã ³ j ä ´ k î ¾ u ï ¿ v ï ¿ v å µ l ä ´ k å µ l ï ¿ v ð À w ð À w æ ¶ m å µ l æ ¶ m ð À w ñ Á x ñ Á x ç · n æ ¶ m ç · n ñ Á x ò  y ò  y è ¸ o ç · n è ¸ o ò  y ó à z ó à z é ¹ p è ¸ o é ¹ p ó à z ô Ä { ô Ä { ê º q é ¹ p ê º q ô Ä { õ Å | õ Å | ë » r ê º q ë » r õ Å | ö Æ } ö Æ } ì ¼ s ë » r f 6 É p @ Õ ÷ Ç ~ ÷ Ç ~ í ½ t f 6 É í ½ t ÷ Ç ~ ø È  ø È  î ¾ u í ½ t î ¾ u ø È  ù É € ù É € ï ¿ v î ¾ u ï ¿ v ù É € ú Ê ú Ê ð À w ï ¿ v ð À w ú Ê û Ë ‚ û Ë ‚ ñ Á x ð À w ñ Á x û Ë ‚ ü Ì ƒ ü Ì ƒ ò  y ñ Á x ò  y ü Ì ƒ ý Í „ ý Í „ ó à z ò  y ó à z ý Í „ þ Î … þ Î … ô Ä { ó à z ô Ä { þ Î … ÿ Ï † ÿ Ï † õ Å | ô Ä { õ Å | ÿ Ï † Ð ‡ Ð ‡ ö Æ } õ Å | p @ Õ z J á  Ñ ˆ  Ñ ˆ ÷ Ç ~ p @ Õ ÷ Ç ~  Ñ ˆ  Ò ‰  Ò ‰ ø È  ÷ Ç ~ ø È   Ò ‰  Ó Š  Ó Š ù É € ø È  ù É €  Ó Š  Ô ‹  Ô ‹ ú Ê ù É € ú Ê  Ô ‹  Õ Œ  Õ Œ û Ë ‚ ú Ê û Ë ‚  Õ Œ  Ö  Ö ü Ì ƒ û Ë ‚ ü Ì ƒ  Ö  × Ž  × Ž ý Í „ ü Ì ƒ ý Í „  × Ž  Ø  Ø þ Î … ý Í „ þ Î …  Ø Ù Ù ÿ Ï † þ Î … ÿ Ï † Ù Ú ‘ Ú ‘ Ð ‡ ÿ Ï † z J á „ T í Û ’ Û ’  Ñ ˆ z J á  Ñ ˆ Û ’ Ü “ Ü “  Ò ‰  Ñ ˆ  Ò ‰ Ü “ Ý ” Ý ”  Ó Š  Ò ‰  Ó Š Ý ”  Þ •  Þ •  Ô ‹  Ó Š  Ô ‹  Þ •  ß –  ß –  Õ Œ  Ô ‹  Õ Œ  ß –  à —  à —  Ö  Õ Œ  Ö  à —  á ˜  á ˜  × Ž  Ö  × Ž  á ˜  â ™  â ™  Ø  × Ž  Ø  â ™  ã š  ã š Ù  Ø Ù  ã š  ä ›  ä › Ú ‘ Ù „ T í Ž ^ ù  å œ  å œ Û ’ „ T í Û ’  å œ  æ  æ Ü “ Û ’ Ü “  æ  ç ž  ç ž Ý ” Ü “ Ý ”  ç ž  è Ÿ  è Ÿ  Þ • Ý ”  Þ •  è Ÿ  é    é    ß –  Þ •  ß –  é    ê ¡  ê ¡  à —  ß –  à —  ê ¡  ë ¢  ë ¢  á ˜  à —  á ˜  ë ¢  ì £  ì £  â ™  á ˜  â ™  ì £  í ¤  í ¤  ã š  â ™  ã š  í ¤  î ¥  î ¥  ä ›  ã š Ž ^ ù ˜ h   ï ¦  ï ¦  å œ Ž ^ ù  å œ  ï ¦ ð § ð §  æ  å œ  æ ð § ! ñ ¨ ! ñ ¨  ç ž  æ  ç ž ! ñ ¨ " ò © " ò ©  è Ÿ  ç ž  è Ÿ " ò © # ó ª # ó ª  é    è Ÿ  é   # ó ª $ ô « $ ô «  ê ¡  é    ê ¡ $ ô « % õ ¬ % õ ¬  ë ¢  ê ¡  ë ¢ % õ ¬ & ö ­ & ö ­  ì £  ë ¢  ì £ & ö ­ ' ÷ ® ' ÷ ®  í ¤  ì £  í ¤ ' ÷ ® ( ø ¯ ( ø ¯  î ¥  í ¤ ˜ h  ¢ r  ) ù ° ) ù °  ï ¦ ˜ h   ï ¦ ) ù ° * ú ± * ú ± ð §  ï ¦ ð § * ú ± + û ² + û ² ! ñ ¨ ð § ! ñ ¨ + û ² , ü ³ , ü ³ " ò © ! ñ ¨ " ò © , ü ³ - ý ´ - ý ´ # ó ª " ò © # ó ª - ý ´ . þ µ . þ µ $ ô « # ó ª $ ô « . þ µ / ÿ ¶ / ÿ ¶ % õ ¬ $ ô « % õ ¬ / ÿ ¶ 0 · 0 · & ö ­ % õ ¬ & ö ­ 0 · 1  ¸ 1  ¸ ' ÷ ® & ö ­ ' ÷ ® 1  ¸ 2  ¹ 2  ¹ ( ø ¯ ' ÷ ® ¢ r  ¬ |  3  º 3  º ) ù ° ¢ r  ) ù ° 3  º 4  » 4  » * ú ± ) ù ° * ú ± 4  » 5  ¼ 5  ¼ + û ² * ú ± + û ² 5  ¼ 6  ½ 6  ½ , ü ³ + û ² , ü ³ 6  ½ 7  ¾ 7  ¾ - ý ´ , ü ³ - ý ´ 7  ¾ 8  ¿ 8  ¿ . þ µ - ý ´ . þ µ 8  ¿ 9 À 9 À / ÿ ¶ . þ µ / ÿ ¶ 9 À : Á : Á 0 · / ÿ ¶ 0 · : Á ;  ;  1  ¸ 0 · 1  ¸ ;  < à < à 2  ¹ 1  ¸ ¬ |  ¶ † ) = Ä = Ä 3  º ¬ |  3  º = Ä >  Å >  Å 4  » 3  º 4  » >  Å ?  Æ ?  Æ 5  ¼ 4  » 5  ¼ ?  Æ @  Ç @  Ç 6  ½ 5  ¼ 6  ½ @  Ç A  È A  È 7  ¾ 6  ½ 7  ¾ A  È B  É B  É 8  ¿ 7  ¾ 8  ¿ B  É C  Ê C  Ê 9 À 8  ¿ 9 À C  Ê D  Ë D  Ë : Á 9 À : Á D  Ë E  Ì E  Ì ;  : Á ;  E  Ì F  Í F  Í < à ;  ¶ † ) À 5 G  Î G  Î = Ä ¶ † ) = Ä G  Î H  Ï H  Ï >  Å = Ä >  Å H  Ï I  Ð I  Ð ?  Æ >  Å ?  Æ I  Ð J  Ñ J  Ñ @  Ç ?  Æ @  Ç J  Ñ K  Ò K  Ò A  È @  Ç A  È K  Ò L  Ó L  Ó B  É A  È B  É L  Ó M  Ô M  Ô C  Ê B  É C  Ê M  Ô N  Õ N  Õ D  Ë C  Ê D  Ë N  Õ O  Ö O  Ö E  Ì D  Ë E  Ì O  Ö P × P × F  Í E  Ì À 5 Ê š A Q ! Ø Q ! Ø G  Î À 5 G  Î Q ! Ø R " Ù R " Ù H  Ï G  Î H  Ï R " Ù S # Ú S # Ú I  Ð H  Ï I  Ð S # Ú T $ Û T $ Û J  Ñ I  Ð J  Ñ T $ Û U % Ü U % Ü K  Ò J  Ñ K  Ò U % Ü V & Ý V & Ý L  Ó K  Ò L  Ó V & Ý W ' Þ W ' Þ M  Ô L  Ó M  Ô W ' Þ X ( ß X ( ß N  Õ M  Ô N  Õ X ( ß Y ) à Y ) à O  Ö N  Õ O  Ö Y ) à Z * á Z * á P × O  Ö Ê š A Ô ¤ M [ + â [ + â Q ! Ø Ê š A Q ! Ø [ + â \ , ã \ , ã R " Ù Q ! Ø R " Ù \ , ã ] - ä ] - ä S # Ú R " Ù S # Ú ] - ä ^ . å ^ . å T $ Û S # Ú T $ Û ^ . å _ / æ _ / æ U % Ü T $ Û U % Ü _ / æ ` 0 ç ` 0 ç V & Ý U % Ü V & Ý ` 0 ç a 1 è a 1 è W ' Þ V & Ý W ' Þ a 1 è b 2 é b 2 é X ( ß W ' Þ X ( ß b 2 é c 3 ê c 3 ê Y ) à X ( ß Y ) à c 3 ê d 4 ë d 4 ë Z * á Y ) à Ô ¤ M Þ ® Y e 5 ì e 5 ì [ + â Ô ¤ M [ + â e 5 ì f 6 í f 6 í \ , ã [ + â \ , ã f 6 í g 7 î g 7 î ] - ä \ , ã ] - ä g 7 î h 8 ï h 8 ï ^ . å ] - ä ^ . å h 8 ï i 9 ð i 9 ð _ / æ ^ . å _ / æ i 9 ð j : ñ j : ñ ` 0 ç _ / æ ` 0 ç j : ñ k ; ò k ; ò a 1 è ` 0 ç a 1 è k ; ò l < ó l < ó b 2 é a 1 è b 2 é l < ó m = ô m = ô c 3 ê b 2 é c 3 ê m = ô n > õ n > õ d 4 ë c 3 ê Þ ® Y è ¸ e o ? ö o ? ö e 5 ì Þ ® Y e 5 ì o ? ö p @ ÷ p @ ÷ f 6 í e 5 ì f 6 í p @ ÷ q A ø q A ø g 7 î f 6 í g 7 î q A ø r B ù r B ù h 8 ï g 7 î h 8 ï r B ù s C ú s C ú i 9 ð h 8 ï i 9 ð s C ú t D û t D û j : ñ i 9 ð j : ñ t D û u E ü u E ü k ; ò j : ñ k ; ò u E ü v F ý v F ý l < ó k ; ò l < ó v F ý w G þ w G þ m = ô l < ó m = ô w G þ x H ÿ x H ÿ n > õ m = ô è ¸ e ò  q y I y I o ? ö è ¸ e o ? ö y I z J  z J  p @ ÷ o ? ö p @ ÷ z J  { K  { K  q A ø p @ ÷ q A ø { K  | L  | L  r B ù q A ø r B ù | L  } M  } M  s C ú r B ù s C ú } M  ~ N  ~ N  t D û s C ú t D û ~ N   O   O  u E ü t D û u E ü  O  € P  € P  v F ý u E ü v F ý € P  Q  Q  w G þ v F ý w G þ Q  ‚ R ‚ R x H ÿ w G þ ò  q ü Ì } ƒ S ƒ S y I ò  q y I ƒ S „ T „ T z J  y I z J  „ T … U … U { K  z J  { K  … U † V † V | L  { K  | L  † V ‡ W  ‡ W  } M  | L  } M  ‡ W  ˆ X  ˆ X  ~ N  } M  ~ N  ˆ X  ‰ Y  ‰ Y   O  ~ N   O  ‰ Y  Š Z  Š Z  € P   O  € P  Š Z  ‹ [  ‹ [  Q  € P  Q  ‹ [  Œ \  Œ \  ‚ R Q  ü Ì }  Ö ‰ ]  ]  ƒ S ü Ì } ƒ S ]  Ž ^  Ž ^  „ T ƒ S „ T Ž ^  _  _  … U „ T … U _  `  `  † V … U † V `  ‘ a  ‘ a  ‡ W  † V ‡ W  ‘ a  ’ b  ’ b  ˆ X  ‡ W  ˆ X  ’ b  “ c  “ c  ‰ Y  ˆ X  ‰ Y  “ c  ” d  ” d  Š Z  ‰ Y  Š Z  ” d  • e  • e  ‹ [  Š Z  ‹ [  • e  – f  – f  Œ \  ‹ [   Ö ‰  à • — g  — g  ]   Ö ‰ ]  — g  ˜ h  ˜ h  Ž ^  ]  Ž ^  ˜ h  ™ i ™ i _  Ž ^  _  ™ i š j ! š j ! `  _  `  š j ! › k " › k " ‘ a  `  ‘ a  › k " œ l # œ l # ’ b  ‘ a  ’ b  œ l # m $ m $ “ c  ’ b  “ c  m $ ž n % ž n % ” d  “ c  ” d  ž n % Ÿ o & Ÿ o & • e  ” d  • e  Ÿ o &   p '   p ' – f  • e   à •  ê ¡ ¡ q ( ¡ q ( — g   à • — g  ¡ q ( ¢ r ) ¢ r ) ˜ h  — g  ˜ h  ¢ r ) £ s * £ s * ™ i ˜ h  ™ i £ s * ¤ t + ¤ t + š j ! ™ i š j ! ¤ t + ¥ u , ¥ u , › k " š j ! › k " ¥ u , ¦ v - ¦ v - œ l # › k " œ l # ¦ v - § w . § w . m $ œ l # m $ § w . ¨ x / ¨ x / ž n % m $ ž n % ¨ x / © y 0 © y 0 Ÿ o & ž n % Ÿ o & © y 0 ª z 1 ª z 1   p ' Ÿ o &  ê ¡ ž n Ù  ì £  ì £ ¡ q (  ê ¡ ¡ q (  ì £  î ¥  î ¥ ¢ r ) ¡ q ( ¢ r )  î ¥ ð § ð § £ s * ¢ r ) £ s * ð § " ò © " ò © ¤ t + £ s * ¤ t + " ò © $ ô « $ ô « ¥ u , ¤ t + ¥ u , $ ô « & ö ­ & ö ­ ¦ v - ¥ u , ¦ v - & ö ­ ( ø ¯ ( ø ¯ § w . ¦ v - § w . ( ø ¯ * ú ± * ú ± ¨ x / § w . ¨ x / * ú ± , ü ³ , ü ³ © y 0 ¨ x / © y 0 , ü ³ . þ µ . þ µ ª z 1 © y 0 userland/host_applications/linux/apps/hello_pi/hello_teapot/triangle.c000066400000000000000000000337471421703157200267630ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd Copyright (c) 2012, OtherCrashOverride 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 name of the copyright holder nor the names of its 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. */ // A rotating cube rendered with OpenGL|ES. Three images used as textures on the cube faces. #include #include #include #include #include #include #include "bcm_host.h" #include "GLES/gl.h" #include "EGL/egl.h" #include "EGL/eglext.h" #include "cube_texture_and_coords.h" #include "models.h" #include "triangle.h" #include #include #include "revision.h" #define PATH "./" #define IMAGE_SIZE_WIDTH 1920 #define IMAGE_SIZE_HEIGHT 1080 #ifndef M_PI #define M_PI 3.141592654 #endif typedef struct { uint32_t screen_width; uint32_t screen_height; // OpenGL|ES objects EGLDisplay display; EGLSurface surface; EGLContext context; GLuint tex; // model rotation vector and direction GLfloat rot_angle_x_inc; GLfloat rot_angle_y_inc; GLfloat rot_angle_z_inc; // current model rotation angles GLfloat rot_angle_x; GLfloat rot_angle_y; GLfloat rot_angle_z; // current distance from camera GLfloat distance; GLfloat distance_inc; MODEL_T model; } CUBE_STATE_T; static void init_ogl(CUBE_STATE_T *state); static void init_model_proj(CUBE_STATE_T *state); static void reset_model(CUBE_STATE_T *state); static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc); static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc); static void redraw_scene(CUBE_STATE_T *state); static void update_model(CUBE_STATE_T *state); static void init_textures(CUBE_STATE_T *state); static void exit_func(void); static volatile int terminate; static CUBE_STATE_T _state, *state=&_state; static void* eglImage = 0; static pthread_t thread1; extern int thread_run; /*********************************************************** * Name: init_ogl * * Arguments: * CUBE_STATE_T *state - holds OGLES model info * * Description: Sets the display, OpenGL|ES context and screen stuff * * Returns: void * ***********************************************************/ static void init_ogl(CUBE_STATE_T *state) { int32_t success = 0; EGLBoolean result; EGLint num_config; static EGL_DISPMANX_WINDOW_T nativewindow; DISPMANX_ELEMENT_HANDLE_T dispman_element; DISPMANX_DISPLAY_HANDLE_T dispman_display; DISPMANX_UPDATE_HANDLE_T dispman_update; VC_RECT_T dst_rect; VC_RECT_T src_rect; static const EGLint attribute_list[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 16, EGL_SAMPLES, 4, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; EGLConfig config; // get an EGL display connection state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); assert(state->display!=EGL_NO_DISPLAY); // initialize the EGL display connection result = eglInitialize(state->display, NULL, NULL); assert(EGL_FALSE != result); // get an appropriate EGL frame buffer configuration // this uses a BRCM extension that gets the closest match, rather than standard which returns anything that matches result = eglSaneChooseConfigBRCM(state->display, attribute_list, &config, 1, &num_config); assert(EGL_FALSE != result); // create an EGL rendering context state->context = eglCreateContext(state->display, config, EGL_NO_CONTEXT, NULL); assert(state->context!=EGL_NO_CONTEXT); // create an EGL window surface success = graphics_get_display_size(0 /* LCD */, &state->screen_width, &state->screen_height); assert( success >= 0 ); dst_rect.x = 0; dst_rect.y = 0; dst_rect.width = state->screen_width; dst_rect.height = state->screen_height; src_rect.x = 0; src_rect.y = 0; src_rect.width = state->screen_width << 16; src_rect.height = state->screen_height << 16; dispman_display = vc_dispmanx_display_open( 0 /* LCD */); dispman_update = vc_dispmanx_update_start( 0 ); dispman_element = vc_dispmanx_element_add ( dispman_update, dispman_display, 0/*layer*/, &dst_rect, 0/*src*/, &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, 0/*transform*/); nativewindow.element = dispman_element; nativewindow.width = state->screen_width; nativewindow.height = state->screen_height; vc_dispmanx_update_submit_sync( dispman_update ); state->surface = eglCreateWindowSurface( state->display, config, &nativewindow, NULL ); assert(state->surface != EGL_NO_SURFACE); // connect the context to the surface result = eglMakeCurrent(state->display, state->surface, state->surface, state->context); assert(EGL_FALSE != result); // Set background color and clear buffers glClearColor((0.3922f+7*0.5f)/8, (0.1176f+7*0.5f)/8, (0.5882f+7*0.5f)/8, 1.0f); // Enable back face culling. glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glClearDepthf(1.0); glDepthFunc(GL_LEQUAL); float noAmbient[] = {1.0f, 1.0f, 1.0f, 1.0f}; glLightfv(GL_LIGHT0, GL_AMBIENT, noAmbient); glEnable(GL_LIGHT0); glEnable(GL_LIGHTING); } /*********************************************************** * Name: init_model_proj * * Arguments: * CUBE_STATE_T *state - holds OGLES model info * * Description: Sets the OpenGL|ES model to default values * * Returns: void * ***********************************************************/ static void init_model_proj(CUBE_STATE_T *state) { float nearp = 0.1f; float farp = 500.0f; float hht; float hwd; glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); glViewport(0, 0, (GLsizei)state->screen_width, (GLsizei)state->screen_height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); hht = nearp * (float)tan(45.0 / 2.0 / 180.0 * M_PI); hwd = hht * (float)state->screen_width / (float)state->screen_height; glFrustumf(-hwd, hwd, -hht, hht, nearp, farp); glEnableClientState( GL_VERTEX_ARRAY ); reset_model(state); } /*********************************************************** * Name: reset_model * * Arguments: * CUBE_STATE_T *state - holds OGLES model info * * Description: Resets the Model projection and rotation direction * * Returns: void * ***********************************************************/ static void reset_model(CUBE_STATE_T *state) { // reset model position glMatrixMode(GL_MODELVIEW); // reset model rotation state->rot_angle_x = 45.f; state->rot_angle_y = 30.f; state->rot_angle_z = 0.f; state->rot_angle_x_inc = 0.5f; state->rot_angle_y_inc = 0.5f; state->rot_angle_z_inc = 0.f; state->distance = 1.2f*1.5f; } /*********************************************************** * Name: update_model * * Arguments: * CUBE_STATE_T *state - holds OGLES model info * * Description: Updates model projection to current position/rotation * * Returns: void * ***********************************************************/ static void update_model(CUBE_STATE_T *state) { // update position state->rot_angle_x = inc_and_wrap_angle(state->rot_angle_x, state->rot_angle_x_inc); state->rot_angle_y = inc_and_wrap_angle(state->rot_angle_y, state->rot_angle_y_inc); state->rot_angle_z = inc_and_wrap_angle(state->rot_angle_z, state->rot_angle_z_inc); state->distance = inc_and_clip_distance(state->distance, state->distance_inc); glLoadIdentity(); // move camera back to see the cube glTranslatef(0.f, 0.f, -state->distance); // Rotate model to new position glRotatef(state->rot_angle_x, 1.f, 0.f, 0.f); glRotatef(state->rot_angle_y, 0.f, 1.f, 0.f); glRotatef(state->rot_angle_z, 0.f, 0.f, 1.f); } /*********************************************************** * Name: inc_and_wrap_angle * * Arguments: * GLfloat angle current angle * GLfloat angle_inc angle increment * * Description: Increments or decrements angle by angle_inc degrees * Wraps to 0 at 360 deg. * * Returns: new value of angle * ***********************************************************/ static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc) { angle += angle_inc; if (angle >= 360.0) angle -= 360.f; else if (angle <=0) angle += 360.f; return angle; } /*********************************************************** * Name: inc_and_clip_distance * * Arguments: * GLfloat distance current distance * GLfloat distance_inc distance increment * * Description: Increments or decrements distance by distance_inc units * Clips to range * * Returns: new value of angle * ***********************************************************/ static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc) { distance += distance_inc; if (distance >= 10.0f) distance = 10.f; else if (distance <= 1.0f) distance = 1.0f; return distance; } /*********************************************************** * Name: redraw_scene * * Arguments: * CUBE_STATE_T *state - holds OGLES model info * * Description: Draws the model and calls eglSwapBuffers * to render to screen * * Returns: void * ***********************************************************/ static void redraw_scene(CUBE_STATE_T *state) { // Start with a clear screen glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); draw_wavefront(state->model, state->tex); eglSwapBuffers(state->display, state->surface); } /*********************************************************** * Name: init_textures * * Arguments: * CUBE_STATE_T *state - holds OGLES model info * * Description: Initialise OGL|ES texture surfaces to use image * buffers * * Returns: void * ***********************************************************/ static void init_textures(CUBE_STATE_T *state) { // the texture containing the video glGenTextures(1, &state->tex); glBindTexture(GL_TEXTURE_2D, state->tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMAGE_SIZE_WIDTH, IMAGE_SIZE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); /* Create EGL Image */ eglImage = eglCreateImageKHR( state->display, state->context, EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)state->tex, 0); if (eglImage == EGL_NO_IMAGE_KHR) { printf("eglCreateImageKHR failed.\n"); exit(1); } // Start rendering pthread_create(&thread1, NULL, video_decode_test, eglImage); // setup overall texture environment glTexCoordPointer(2, GL_FLOAT, 0, texCoords); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnable(GL_TEXTURE_2D); // Bind texture surface to current vertices glBindTexture(GL_TEXTURE_2D, state->tex); } //------------------------------------------------------------------------------ static void exit_func(void) // Function to be passed to atexit(). { thread_run = 0; pthread_join(thread1, NULL); if (eglImage != 0) { if (!eglDestroyImageKHR(state->display, (EGLImageKHR) eglImage)) printf("eglDestroyImageKHR failed."); } // clear screen glClear( GL_COLOR_BUFFER_BIT ); eglSwapBuffers(state->display, state->surface); // Release OpenGL resources eglMakeCurrent( state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); eglDestroySurface( state->display, state->surface ); eglDestroyContext( state->display, state->context ); eglTerminate( state->display ); printf("\ncube closed\n"); } // exit_func() void sig_handler(int signo) { terminate = 1; } //============================================================================== int main () { bcm_host_init(); if (get_processor_id() == PROCESSOR_BCM2838) { puts("This demo application is not available on the Pi4\n\n"); exit(0); } printf("Note: ensure you have sufficient gpu_mem configured\n"); signal(SIGINT, sig_handler); // Clear application state memset( state, 0, sizeof( *state ) ); // Start OGLES init_ogl(state); // Setup the model world init_model_proj(state); // initialise the OGLES texture(s) init_textures(state); //state->model = cube_wavefront(); state->model = load_wavefront("/opt/vc/src/hello_pi/hello_teapot/teapot.obj.dat", NULL); while (!terminate) { update_model(state); redraw_scene(state); } exit_func(); return 0; } userland/host_applications/linux/apps/hello_pi/hello_teapot/triangle.h000066400000000000000000000030571421703157200267570ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ #pragma once void* video_decode_test(void* arg); userland/host_applications/linux/apps/hello_pi/hello_teapot/video.c000066400000000000000000000231231421703157200262470ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd Copyright (c) 2012, OtherCrashOverride 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 name of the copyright holder nor the names of its 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. */ // Video decode demo using OpenMAX IL though the ilcient helper library #include #include #include #include "bcm_host.h" #include "ilclient.h" static OMX_BUFFERHEADERTYPE* eglBuffer = NULL; static COMPONENT_T* egl_render = NULL; static void* eglImage = 0; int thread_run = 0; void my_fill_buffer_done(void* data, COMPONENT_T* comp) { OMX_STATETYPE state; if (OMX_GetState(ILC_GET_HANDLE(egl_render), &state) != OMX_ErrorNone) { printf("OMX_FillThisBuffer failed while getting egl_render component state\n"); return; } if (state != OMX_StateExecuting) return; if (OMX_FillThisBuffer(ilclient_get_handle(egl_render), eglBuffer) != OMX_ErrorNone) printf("OMX_FillThisBuffer failed in callback\n"); } // Modified function prototype to work with pthreads void *video_decode_test(void* arg) { const char* filename = "/opt/vc/src/hello_pi/hello_video/test.h264"; eglImage = arg; if (eglImage == 0) { printf("eglImage is null.\n"); exit(1); } OMX_VIDEO_PARAM_PORTFORMATTYPE format; OMX_TIME_CONFIG_CLOCKSTATETYPE cstate; OMX_ERRORTYPE omx_err; COMPONENT_T *video_decode = NULL, *video_scheduler = NULL, *clock = NULL; COMPONENT_T *list[5]; TUNNEL_T tunnel[4]; ILCLIENT_T *client; FILE *in; int status = 0; unsigned int data_len = 0; thread_run = 1; memset(list, 0, sizeof(list)); memset(tunnel, 0, sizeof(tunnel)); if((in = fopen(filename, "rb")) == NULL) return (void *)-2; if((client = ilclient_init()) == NULL) { fclose(in); return (void *)-3; } if(OMX_Init() != OMX_ErrorNone) { ilclient_destroy(client); fclose(in); return (void *)-4; } // callback ilclient_set_fill_buffer_done_callback(client, my_fill_buffer_done, 0); // create video_decode if(ilclient_create_component(client, &video_decode, "video_decode", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0) status = -14; list[0] = video_decode; // create egl_render if(status == 0 && ilclient_create_component(client, &egl_render, "egl_render", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_OUTPUT_BUFFERS) != 0) status = -14; list[1] = egl_render; // create clock if(status == 0 && ilclient_create_component(client, &clock, "clock", ILCLIENT_DISABLE_ALL_PORTS) != 0) status = -14; list[2] = clock; memset(&cstate, 0, sizeof(cstate)); cstate.nSize = sizeof(cstate); cstate.nVersion.nVersion = OMX_VERSION; cstate.eState = OMX_TIME_ClockStateWaitingForStartTime; cstate.nWaitMask = 1; if(clock != NULL && OMX_SetParameter(ILC_GET_HANDLE(clock), OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone) status = -13; // create video_scheduler if(status == 0 && ilclient_create_component(client, &video_scheduler, "video_scheduler", ILCLIENT_DISABLE_ALL_PORTS) != 0) status = -14; list[3] = video_scheduler; set_tunnel(tunnel, video_decode, 131, video_scheduler, 10); set_tunnel(tunnel+1, video_scheduler, 11, egl_render, 220); set_tunnel(tunnel+2, clock, 80, video_scheduler, 12); // setup clock tunnel first if(status == 0 && ilclient_setup_tunnel(tunnel+2, 0, 0) != 0) status = -15; else ilclient_change_component_state(clock, OMX_StateExecuting); if(status == 0) ilclient_change_component_state(video_decode, OMX_StateIdle); memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE)); format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE); format.nVersion.nVersion = OMX_VERSION; format.nPortIndex = 130; format.eCompressionFormat = OMX_VIDEO_CodingAVC; if(status == 0 && OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamVideoPortFormat, &format) == OMX_ErrorNone && ilclient_enable_port_buffers(video_decode, 130, NULL, NULL, NULL) == 0) { OMX_BUFFERHEADERTYPE *buf; int port_settings_changed = 0; int first_packet = 1; ilclient_change_component_state(video_decode, OMX_StateExecuting); while((buf = ilclient_get_input_buffer(video_decode, 130, 1)) != NULL) { // feed data and wait until we get port settings changed unsigned char *dest = buf->pBuffer; // loop if at end if (feof(in)) rewind(in); data_len += fread(dest, 1, buf->nAllocLen-data_len, in); if(port_settings_changed == 0 && ((data_len > 0 && ilclient_remove_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1) == 0) || (data_len == 0 && ilclient_wait_for_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1, ILCLIENT_EVENT_ERROR | ILCLIENT_PARAMETER_CHANGED, 10000) == 0))) { port_settings_changed = 1; if(ilclient_setup_tunnel(tunnel, 0, 0) != 0) { status = -7; break; } ilclient_change_component_state(video_scheduler, OMX_StateExecuting); // now setup tunnel to egl_render if(ilclient_setup_tunnel(tunnel+1, 0, 1000) != 0) { status = -12; break; } // Set egl_render to idle ilclient_change_component_state(egl_render, OMX_StateIdle); // Enable the output port and tell egl_render to use the texture as a buffer //ilclient_enable_port(egl_render, 221); THIS BLOCKS SO CAN'T BE USED if (OMX_SendCommand(ILC_GET_HANDLE(egl_render), OMX_CommandPortEnable, 221, NULL) != OMX_ErrorNone) { printf("OMX_CommandPortEnable failed.\n"); exit(1); } if (OMX_UseEGLImage(ILC_GET_HANDLE(egl_render), &eglBuffer, 221, NULL, eglImage) != OMX_ErrorNone) { printf("OMX_UseEGLImage failed.\n"); exit(1); } // Set egl_render to executing ilclient_change_component_state(egl_render, OMX_StateExecuting); // Request egl_render to write data to the texture buffer if(OMX_FillThisBuffer(ILC_GET_HANDLE(egl_render), eglBuffer) != OMX_ErrorNone) { printf("OMX_FillThisBuffer failed.\n"); exit(1); } } if(!data_len) break; if(!thread_run) break; buf->nFilledLen = data_len; data_len = 0; buf->nOffset = 0; if(first_packet) { buf->nFlags = OMX_BUFFERFLAG_STARTTIME; first_packet = 0; } else buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) { status = -6; break; } } buf->nFilledLen = 0; buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS; if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) status = -20; // need to flush the renderer to allow video_decode to disable its input port ilclient_flush_tunnels(tunnel, 0); ilclient_disable_port_buffers(video_decode, 130, NULL, NULL, NULL); } fclose(in); ilclient_disable_tunnel(tunnel); ilclient_disable_tunnel(tunnel+1); ilclient_disable_tunnel(tunnel+2); ilclient_teardown_tunnels(tunnel); ilclient_state_transition(list, OMX_StateIdle); /* * To cleanup egl_render, we need to first disable its output port, then * free the output eglBuffer, and finally request the state transition * from to Loaded. */ omx_err = OMX_SendCommand(ILC_GET_HANDLE(egl_render), OMX_CommandPortDisable, 221, NULL); if (omx_err != OMX_ErrorNone) printf("Failed OMX_SendCommand\n"); omx_err = OMX_FreeBuffer(ILC_GET_HANDLE(egl_render), 221, eglBuffer); if(omx_err != OMX_ErrorNone) printf("OMX_FreeBuffer failed\n"); ilclient_state_transition(list, OMX_StateLoaded); ilclient_cleanup_components(list); OMX_Deinit(); ilclient_destroy(client); return (void *)status; } userland/host_applications/linux/apps/hello_pi/hello_tiger/000077500000000000000000000000001421703157200246125ustar00rootroot00000000000000userland/host_applications/linux/apps/hello_pi/hello_tiger/CMakeLists.txt000066400000000000000000000003411421703157200273500ustar00rootroot00000000000000set(EXEC hello_tiger.bin) set(SRCS main.c tiger.c) add_definitions(-D__RASPBERRYPI__) add_executable(${EXEC} ${SRCS}) target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) install(TARGETS ${EXEC} RUNTIME DESTINATION bin) userland/host_applications/linux/apps/hello_pi/hello_tiger/Makefile000077500000000000000000000001661421703157200262600ustar00rootroot00000000000000OBJS=main.o tiger.o BIN=hello_tiger.bin LDFLAGS+= -lrevision CFLAGS+=-D__RASPBERRYPI__ include ../Makefile.include userland/host_applications/linux/apps/hello_pi/hello_tiger/license.txt000066400000000000000000000047641421703157200270100ustar00rootroot00000000000000OpenVG 1.1 Reference Implementation ----------------------------------- Copyright (c) 2007 The Khronos Group Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and /or associated documentation files (the "Materials "), to deal in the Materials without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Materials, and to permit persons to whom the Materials are furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Materials. THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. Path data for the Tiger sample program has been extracted from Ghostscript's tiger.eps example file distributed under GNU General Public License. Ghostscript's License document: " The files in the src, lib, toolbin, examples, doc and man directories (folders) and any subdirectories (sub-folders) thereof are part of GPL Ghostscript. The files in the Resource directory and any subdirectories thereof are also part of GPL Ghostscript, with the explicit exception of the files in the CMap subdirectory. The CMap files are copyright Adobe Systems Incorporated and covered by a separate license which permits only verbatim distribution. GPL Ghostscript is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. GPL Ghostscript 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 so you can know your rights and responsibilities. It should be in a file named doc/COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place Suite 330, Boston, MA 02111-1307, USA." userland/host_applications/linux/apps/hello_pi/hello_tiger/main.c000066400000000000000000000314021421703157200257020ustar00rootroot00000000000000/*------------------------------------------------------------------------ * * OpenVG 1.0.1 Reference Implementation sample code * ------------------------------------------------- * * Copyright (c) 2007 The Khronos Group Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and /or associated documentation files * (the "Materials "), to deal in the Materials without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Materials, * and to permit persons to whom the Materials are furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Materials. * * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR * THE USE OR OTHER DEALINGS IN THE MATERIALS. * *//** * \file * \brief Tiger sample application. Resizing the application window * rerenders the tiger in the new resolution. Pressing 1,2,3 * or 4 sets pixel zoom factor, mouse moves inside the zoomed * image (mouse move works on OpenGL >= 1.2). * \note *//*-------------------------------------------------------------------*/ #include #include #include #include #include #define UNREF(X) ((void)(X)) #ifdef HG_FLAT_INCLUDES # include "openvg.h" # include "vgu.h" # include "egl.h" #else # include "VG/openvg.h" # include "VG/vgu.h" # include "EGL/egl.h" #endif #include "revision.h" #include "tiger.h" /*--------------------------------------------------------------*/ #ifdef __RASPBERRYPI__ static float rotateN = 0.0f; #endif const float aspectRatio = 612.0f / 792.0f; int renderWidth = 0; int renderHeight = 0; EGLDisplay egldisplay; EGLConfig eglconfig; EGLSurface eglsurface; EGLContext eglcontext; /*--------------------------------------------------------------*/ typedef struct { VGFillRule m_fillRule; VGPaintMode m_paintMode; VGCapStyle m_capStyle; VGJoinStyle m_joinStyle; float m_miterLimit; float m_strokeWidth; VGPaint m_fillPaint; VGPaint m_strokePaint; VGPath m_path; } PathData; typedef struct { PathData* m_paths; int m_numPaths; } PS; PS* PS_construct(const char* commands, int commandCount, const float* points, int pointCount) { PS* ps = (PS*)malloc(sizeof(PS)); int p = 0; int c = 0; int i = 0; int paths = 0; int maxElements = 0; unsigned char* cmd; UNREF(pointCount); while(c < commandCount) { int elements, e; c += 4; p += 8; elements = (int)points[p++]; assert(elements > 0); if(elements > maxElements) maxElements = elements; for(e=0;em_numPaths = paths; ps->m_paths = (PathData*)malloc(paths * sizeof(PathData)); cmd = (unsigned char*)malloc(maxElements); i = 0; p = 0; c = 0; while(c < commandCount) { int elements, startp, e; float color[4]; //fill type int paintMode = 0; ps->m_paths[i].m_fillRule = VG_NON_ZERO; switch( commands[c] ) { case 'N': break; case 'F': ps->m_paths[i].m_fillRule = VG_NON_ZERO; paintMode |= VG_FILL_PATH; break; case 'E': ps->m_paths[i].m_fillRule = VG_EVEN_ODD; paintMode |= VG_FILL_PATH; break; default: assert(0); //unknown command } c++; //stroke switch( commands[c] ) { case 'N': break; case 'S': paintMode |= VG_STROKE_PATH; break; default: assert(0); //unknown command } ps->m_paths[i].m_paintMode = (VGPaintMode)paintMode; c++; //line cap switch( commands[c] ) { case 'B': ps->m_paths[i].m_capStyle = VG_CAP_BUTT; break; case 'R': ps->m_paths[i].m_capStyle = VG_CAP_ROUND; break; case 'S': ps->m_paths[i].m_capStyle = VG_CAP_SQUARE; break; default: assert(0); //unknown command } c++; //line join switch( commands[c] ) { case 'M': ps->m_paths[i].m_joinStyle = VG_JOIN_MITER; break; case 'R': ps->m_paths[i].m_joinStyle = VG_JOIN_ROUND; break; case 'B': ps->m_paths[i].m_joinStyle = VG_JOIN_BEVEL; break; default: assert(0); //unknown command } c++; //the rest of stroke attributes ps->m_paths[i].m_miterLimit = points[p++]; ps->m_paths[i].m_strokeWidth = points[p++]; //paints color[0] = points[p++]; color[1] = points[p++]; color[2] = points[p++]; color[3] = 1.0f; ps->m_paths[i].m_strokePaint = vgCreatePaint(); vgSetParameteri(ps->m_paths[i].m_strokePaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); vgSetParameterfv(ps->m_paths[i].m_strokePaint, VG_PAINT_COLOR, 4, color); color[0] = points[p++]; color[1] = points[p++]; color[2] = points[p++]; color[3] = 1.0f; ps->m_paths[i].m_fillPaint = vgCreatePaint(); vgSetParameteri(ps->m_paths[i].m_fillPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); vgSetParameterfv(ps->m_paths[i].m_fillPaint, VG_PAINT_COLOR, 4, color); //read number of elements elements = (int)points[p++]; assert(elements > 0); startp = p; for(e=0;em_paths[i].m_path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, (unsigned int)VG_PATH_CAPABILITY_ALL); vgAppendPathData(ps->m_paths[i].m_path, elements, cmd, points + startp); i++; } free(cmd); return ps; } void PS_destruct(PS* ps) { int i; assert(ps); for(i=0;im_numPaths;i++) { vgDestroyPaint(ps->m_paths[i].m_fillPaint); vgDestroyPaint(ps->m_paths[i].m_strokePaint); vgDestroyPath(ps->m_paths[i].m_path); } free(ps->m_paths); free(ps); } void PS_render(PS* ps) { int i; assert(ps); vgSeti(VG_BLEND_MODE, VG_BLEND_SRC_OVER); for(i=0;im_numPaths;i++) { vgSeti(VG_FILL_RULE, ps->m_paths[i].m_fillRule); vgSetPaint(ps->m_paths[i].m_fillPaint, VG_FILL_PATH); if(ps->m_paths[i].m_paintMode & VG_STROKE_PATH) { vgSetf(VG_STROKE_LINE_WIDTH, ps->m_paths[i].m_strokeWidth); vgSeti(VG_STROKE_CAP_STYLE, ps->m_paths[i].m_capStyle); vgSeti(VG_STROKE_JOIN_STYLE, ps->m_paths[i].m_joinStyle); vgSetf(VG_STROKE_MITER_LIMIT, ps->m_paths[i].m_miterLimit); vgSetPaint(ps->m_paths[i].m_strokePaint, VG_STROKE_PATH); } vgDrawPath(ps->m_paths[i].m_path, ps->m_paths[i].m_paintMode); } assert(vgGetError() == VG_NO_ERROR); } PS* tiger = NULL; /*--------------------------------------------------------------*/ void render(int w, int h) { #ifndef __RASPBERRYPI__ if(renderWidth != w || renderHeight != h) #endif { float clearColor[4] = {1,1,1,1}; float scale = w / (tigerMaxX - tigerMinX); eglSwapBuffers(egldisplay, eglsurface); //force EGL to recognize resize vgSetfv(VG_CLEAR_COLOR, 4, clearColor); vgClear(0, 0, w, h); vgLoadIdentity(); #ifdef __RASPBERRYPI__ vgTranslate(w * 0.5f, h * 0.5f); vgRotate(rotateN); vgTranslate(-w * 0.5f, -h * 0.5f); #endif vgScale(scale, scale); vgTranslate(-tigerMinX, -tigerMinY + 0.5f * (h / scale - (tigerMaxY - tigerMinY))); PS_render(tiger); assert(vgGetError() == VG_NO_ERROR); renderWidth = w; renderHeight = h; } #ifndef __RASPBERRYPI__ eglSwapBuffers(egldisplay, eglsurface); assert(eglGetError() == EGL_SUCCESS); #endif } /*--------------------------------------------------------------*/ void init(NativeWindowType window) { static const EGLint s_configAttribs[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_LUMINANCE_SIZE, EGL_DONT_CARE, //EGL_DONT_CARE EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_SAMPLES, 1, EGL_NONE }; EGLint numconfigs; egldisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(egldisplay, NULL, NULL); assert(eglGetError() == EGL_SUCCESS); eglBindAPI(EGL_OPENVG_API); eglChooseConfig(egldisplay, s_configAttribs, &eglconfig, 1, &numconfigs); assert(eglGetError() == EGL_SUCCESS); assert(numconfigs == 1); eglsurface = eglCreateWindowSurface(egldisplay, eglconfig, window, NULL); assert(eglGetError() == EGL_SUCCESS); eglcontext = eglCreateContext(egldisplay, eglconfig, NULL, NULL); assert(eglGetError() == EGL_SUCCESS); eglMakeCurrent(egldisplay, eglsurface, eglsurface, eglcontext); assert(eglGetError() == EGL_SUCCESS); tiger = PS_construct(tigerCommands, tigerCommandCount, tigerPoints, tigerPointCount); } /*--------------------------------------------------------------*/ void deinit(void) { PS_destruct(tiger); eglMakeCurrent(egldisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); assert(eglGetError() == EGL_SUCCESS); eglTerminate(egldisplay); assert(eglGetError() == EGL_SUCCESS); eglReleaseThread(); } /*--------------------------------------------------------------*/ #ifdef WIN32 #pragma warning(disable:4115) /* named type definition in parentheses (this comes from a visual studio include file) */ #include static LONG WINAPI windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CLOSE: case WM_DESTROY: PostQuitMessage(0); return 0; case WM_PAINT: { RECT rect; InvalidateRect(hWnd, NULL, 0); GetClientRect(hWnd, &rect); render(rect.right - rect.left, rect.bottom - rect.top); return 0; } default: break; } return DefWindowProc(hWnd, uMsg, wParam, lParam); } /*--------------------------------------------------------------*/ int main(void) { HWND window; { WNDCLASS wndclass; wndclass.style = 0; wndclass.lpfnWndProc = windowProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = (HINSTANCE)GetModuleHandle(NULL); wndclass.hIcon = LoadIcon(wndclass.hInstance, MAKEINTRESOURCE(101)); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = "MainWndClass"; if (!wndclass.hIcon) wndclass.hIcon = LoadIcon(NULL, IDI_EXCLAMATION); RegisterClass(&wndclass); } window = CreateWindow( "MainWndClass", "OpenVG Tiger sample (rendering, please wait)", WS_OVERLAPPEDWINDOW, 200, 200, 400, (int)(400.0f / aspectRatio), NULL, NULL, (HINSTANCE)GetModuleHandle(NULL), NULL); if (!window) return -1; init((NativeWindowType)window); { MSG msg; ShowWindow(window, SW_SHOW); while (GetMessage(&msg, NULL, 0, 0)) { DispatchMessage(&msg); if (msg.message == WM_QUIT) break; } } deinit(); DestroyWindow(window); return 0; } /*--------------------------------------------------------------*/ #elif defined __APPLE__ /*--------------------------------------------------------------*/ #include //TODO #elif defined __RASPBERRYPI__ #include "bcm_host.h" int main(void) { uint32_t width, height; bcm_host_init(); if (get_processor_id() == PROCESSOR_BCM2838) { puts("This demo application is not available on the Pi4\n\n"); exit(0); } int s; static EGL_DISPMANX_WINDOW_T nativewindow; DISPMANX_ELEMENT_HANDLE_T dispman_element; DISPMANX_DISPLAY_HANDLE_T dispman_display; DISPMANX_UPDATE_HANDLE_T dispman_update; VC_RECT_T dst_rect; VC_RECT_T src_rect; s = graphics_get_display_size(0 /* LCD */, &width, &height); assert( s >= 0 ); dst_rect.x = 0; dst_rect.y = 0; dst_rect.width = width; dst_rect.height = height; src_rect.x = 0; src_rect.y = 0; src_rect.width = width << 16; src_rect.height = height << 16; dispman_display = vc_dispmanx_display_open( 0 /* LCD */); dispman_update = vc_dispmanx_update_start( 0 ); dispman_element = vc_dispmanx_element_add ( dispman_update, dispman_display, 1/*layer*/, &dst_rect, 0/*src*/, &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, 0/*transform*/); nativewindow.element = dispman_element; nativewindow.width = width; nativewindow.height = height; vc_dispmanx_update_submit_sync( dispman_update ); init(&nativewindow); while (1) { render(width, height); rotateN += 1.0f; } deinit(); return 0; } #endif userland/host_applications/linux/apps/hello_pi/hello_tiger/readme.txt000066400000000000000000000323561421703157200266210ustar00rootroot00000000000000OpenVG 1.1 Reference Implementation ----------------------------------- Copyright (c) 2007 The Khronos Group Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and /or associated documentation files (the "Materials "), to deal in the Materials without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Materials, and to permit persons to whom the Materials are furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Materials. THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. Version ------- Official RI for OpenVG 1.1 Released: May 13, 2008 Release Notes ------------- This release is based on OpenVG 1.1 and EGL 1.3 specifications. This release is Windows-only, although the source code compiles at least on Mac OS X 10.5 and Cygwin. Project files are provided for MSVC 6. This archive contains sources for OpenVG RI, VGU and EGL. There's also a precompiled libOpenVG.dll that contains OpenVG and EGL implementations. Package Structure ----------------- bin win32 libOpenVG.dll OpenVG Windows .dll tiger.exe Windows executable of the Tiger sample lib libOpenVG.lib MSVC 6 dll import library ri openvg_ri.dsp MSVC 6 project file for libOpenVG.dll src .cpp and .h -files of the reference implementation win32 Windows backend for EGL macosx Mac OS X backend for EGL null null backend for EGL include Public OpenVG and EGL headers EGL egl.h VG openvg.h vgu.h samples samples.dsw MSVC 6 workspace file for tiger sample and libOpenVG.dll samples.dsp MSVC 6 project file for tiger.exe tiger main.c tiger.c tiger.h readme.txt license.txt Samples ------- Tiger The release contains a sample application that renders an image of a tiger. Note that the sample doesn't start immediately, since it takes a few seconds to render the image. Resizing the window rerenders the image in the new resolution. Known Issues ------------ -EGL functionality is incomplete (some functions lack proper error checking, some attribs may not be processed, etc.) -When opening samples.dsw, MSVC may complain about missing Perforce connection. Just ignore that. Changes ------- Nov 25, 2008 -Clamp color transform scale to [-127, 127] and bias to [-1, 1] May 13, 2008 - Changed 8 sample MSAA configs into 32 sample configs - Changed max gaussian std deviation to 16 (was 128) - VG_DRAW_IMAGE_MULTIPLY converts luminance to RGB if either paint or image color is RGB - Fixes A40102 by storing input floats as is February 12, 2008 - fixed arc transformation. - fixed point along path corner cases. - partially fixed A40102 by not filtering invalid float input. December 12, 2007 - fixed an overflow bug in vgFillMaskLayer error handling. - increased accuracy for Gaussian blur. The new code avoids an infinite loop with a very small std dev. - fixed a bug in Font::find that caused deleted fonts to be returned. November 20, 2007 - reimplemented 1,2 & 4 bits per pixel images - fixed vgGetParameter for paint - fixed https://cvs.khronos.org/bugzilla/show_bug.cgi?id=1095 RI handling of child images with shared storage -vgGetParent: return closest valid ancestor, or image itself if no ancestor was found. - EGL refactoring & clean up - divided OS native parts of EGL into separate files that should be included in that platform's build - added a generic OS backend for EGL (not thread safe, no window rendering) - fixed https://cvs.khronos.org/bugzilla/show_bug.cgi?id=1943 RI does not handle channel mask correctly for lL/sL/BW1 - removed EGL_IMAGE_IN_USE_ERROR from vgDrawGlyph(s) - added configs without an alpha mask to facilitate CTS reference image creation - implemented more accurate stroking by rendering each stroke part into a coverage buffer and compositing from there -fixes https://cvs.khronos.org/bugzilla/show_bug.cgi?id=2221 RI errors at end of curved strokes. - bugfix: joins used path midpoints, interpolateStroke and caps didn't => seams (visible in some G60101 cases) - vgCreateMaskLayer returns VG_INVALID_HANDLE if the current surface doesn't have a mask layer - vgRenderToMask bugfix: temp buffer is now cleared between fill and stroke - bugfix: vgCreateImage returns an error if allowedQuality is zero - bugfix: vgSetPaint returns an error if paintModes is zero - bugfix: vgCreateFont doesn't return an error if capacityHint is zero - bugfix: writeFilteredPixel writes also into luminance formats October 12, 2007 -Upgrade to OpenVG 1.1, including -Implemented MSAA, added 4 and 8 sample EGLConfigs -Implemented VG_A_4 and VG_A_1 image formats and EGLConfigs -Implemented Glyph API -Implemented new masking functions -Implemented color transform -Implemented native EGL backends for Windows & Mac OS X => Fix for bugzilla 1376 RI uses non-standard EGL implementation *RI now works with CTS generation's native_w32.c (native_ri.c can be removed). *dependency on GLUT has been removed. GLUT code is still included and can be compiled in instead of native by defining RI_USE_GLUT. -16 bit EGLConfigs now expose 4 mask bits, 8 bit configs 8, 4 bit configs 4, and 1 bit configs 1. MSAA configs expose one bit per sample. -EGL now works with any display, not just EGL_DEFAULT_DISPLAY -Simplification: removed code to handle less than 8 bits per pixel. Smaller bit depths always allocate 8 bits per pixel. -Changed rasterizer data types to RScalar and RVector2 so that it's possible to alter RIfloat precision without affecting rasterization. -Accuracy: increased circularLerp precision -Bugfix: matrix inversion now checks if the input matrix is affine and forces the inverted matrix to be affine as well -Bugfix: fixed eglCopyBuffers (copied from dst to dst) -Bugfix: fixed eglCreatePixmapSurface (allowed only VG_sRGBA_8888, didn't give an error if config had more than one sample per pixel) -Bugfix: bugzilla 2465: RI asserts when setting maximum amount of gradient stops February 27, 2007 -changed to MIT open source license. -bugfix, bugzilla 820: RGB and luminance are now treated as different color spaces. -bugfix, bugzilla 1094/1095: vgGetParent now returns the input image in case its parent is already destroyed. December 1, 2006 -bugfix, bugzilla 649: allowed image quality is now taken into account when deciding resampling filter. -bugfix, bugzilla 650, bad stroking accuracy reported by TK Chan and Mika Tuomi: curve tessellation is now increased from 64 to 256. RI_MAX_EDGES has been increased from 100000 to 262144 to facilitate the increased number of edges. -bugfix, reported by Chris Wynn, affects I30206: degenerate gradients in repeat mode now render the first stop color instead of the last one. -changed float to RIfloat, added an option to compile RIfloat into a class to test reduced precision float ops September 6, 2006 -bugfix, bugzilla 591: CLOSE_PATH followed by a MOVE_TO doesn't produce an extra end cap anymore -abs values of arc axis lengths are taken only just before rendering -undefined bits of bitfields are now ignored in the API September 1, 2006 -changed colorToInt to use mathematical round-to-nearest as recommended by new language in section 3.4.4. -implemented VG_PAINT_COLOR_RAMP_PREMULTIPLIED -implemented VG_STROKE_DASH_PHASE_RESET -implemented new language for filter channelMasks (section 11.2) -tangents returned by vgPointAlongPath are now normalized -implemented VG_MAX_GAUSSIAN_STD_DEVIATION, rewrote Gaussian blur code -vgGetString: if no context, return NULL. VG_VERSION returns the spec version (1.0). -bugfix, bugzilla 542: vgSeparableConvolve now convolves the edge color with the horizontal kernel and uses that as the edge color for the vertical pass -ellipse rh and rv are replaced by their absolute values whenever read for processing, the absolute values are not written into the path data August 18, 2006 -bugfix, M30301: the arguments for vguComputeWarpQuadToQuad were the wrong way around, destination should come before source. -bugfix, M10102: check for degeneracy in vguComputeWarpSquareToQuad is done before the affinity check so that degenerate affine matrices also produce the bad warp error -bugfix, bugzilla 491: Chris Wynn's vgComputeWarpSquareToQuad -case. There was a wrong mapping between vertices, (1,1) was mapped to (dx2,dy2) -bugfix, bugzilla 519: vguPolygon wrong error check. vguPolygon didn't have an error check for the count argument -bugfix, bugzilla 518: vgGetParameterfv/iv wrong errors. vgGetParametrtfv/iv error checking was for count < 0 instead of count <= 0. -bugfix, bugzilla 517: wrong cap flag checked in vgPathTransformedBounds -bugfix, bugzilla 494: egl.h has wrong values for OPENVG_BIT and OPENGL_ES_BIT. Copied the enumerations from the 1.3 egl.h on the Khronos site (OpenKode/egl/egl.h) -bugfix, bugzilla 492: gradient filter window was biased -bugfix: when appending paths, there was a loop over coordinates to replace arc axis lengths by their absolute values. However, if the path wasn't empty, the loop accessed wrong coordinates. Fixes: Qingping Zhang's cases 2&3. -bugfix: image filter write mask was ignored when writing to VG_A_8 images. Fixes: Qingping Zhang's case 13. -bugfix: if image filter processing format is premultiplied, color channels are clamped to alpha before conversion to destination format -bugfix: in eglReleaseThread the EGL instance was freed when its reference count reached zero, but the pointer wasn't made NULL, causing the use of uninitialized instance. -bugfix: vgClearImage didn't clamp the clear color to [0,1] range -bugfix: a zero-length dash at a path vertex produces a join -bugfix: vgSetParameter now checks paramType for all object types -bugfix: convolution filters incorrectly restricted the area read from the source image to the intersection of source and destination image sizes -bugfix: EGL surface creation now defaults correctly to EGL_COLOR_SPACE_sRGB -antialiasing is done in the linear color space as the spec recommends. -image filters clamp the result to [0,1] range. -Color::pack and Color::convert assert that their input is in [0,1] range -in case a projective transform is used, VGImageMode is always VG_DRAW_IMAGE_NORMAL -the default value for VG_FILTER_FORMAT_LINEAR is now VG_FALSE -added Matrix::isAffine for easy affinity check -Color::clamp clamps color channels to alpha for premultiplied colors -VG_BLEND_LIGHTEN: color channels cannot exceed alpha anymore -RI now supports flexible pixel formats. Any bit depth for RGBA is now supported. -eglGetProcAddress is now properly implemented, it returns a function pointer for eglSetConfigPreferenceHG extension -eglQueryString now returns "eglSetConfigPreferenceHG" for EGL_EXTENSIONS -location of egl.h in RI. use EGL/egl.h, VG/openvg.h, VG/vgu.h -OpenVG 1.0.1 spec changes +use the latest openvg.h +2.8: AA happens in linear space +3.4: alpha channel depth of zero results in alpha=1 when read +4.1: return VG_NO_CONTEXT_ERROR from vgGetError in case no context is current +5.1: VG_SCREEN_LAYOUT (default = screen layout of the display) +5.2, 5.3: vgSet, vgGet, vgSetParameter, vgGetParameter: handling of invalid values of count +5.2.1: new default for VG_FILTER_FORMAT_LINEAR is VG_FALSE +8.5.3: get rid of VG_PATH_DATATYPE_INVALID and VG_IMAGE_FORMAT_INVALID enums +10.2: get rid of old extension image formats, add the official ones +10.5: when reading/writing pixels, clamp color channels to [0, alpha] +10.8: when a projective transform is used, always use VG_DRAW_IMAGE_NORMAL mode +10.8: VG_DRAW_IMAGE_MULTIPLY: if color spaces of paint and image don't match, no conversion takes place, result is in image color space +12.4: clamp the result of additive blend to [0,1] October 20, 2005 -Gradients are filtered to avoid aliasing -Subpaths that ended with a close path segment were capped and joined incorrectly. Fixed. -Alpha mask was allocated per context, not per EGL surface. Fixed. August 22, 2005 -Updated to spec amendment -Fixed bugs -Implemented eglChooseConfig and eglReleaseThread July 22, 2005 -Updated to 18th July 2005 version of the OpenVG 1.0 spec. -Updated to 20th July 2005 version of the EGL 1.2 spec. -Fixed bugs. -openvg.h, vgu.h and egl.h are now contained in include/vg directory. May 4, 2005 -Updated to April 26th 2005 version of the OpenVG 1.0 spec. -Can share images, paths, and paint between contexts. -Fixed path tangent computation. -Implemented image filters. -Fixed bugs. -Changed directory structure a bit. March 29, 2005 -Updated to March 28th 2005 version of the OpenVG 1.0 spec. -Changed rasterizer to use 32 samples per pixel in the high quality mode (renders faster at the expense of some aliasing). -EGL allocates sRGB rendering surfaces. -Includes GLUT dll against which tiger.exe was linked. March 24, 2005 -Initial release. userland/host_applications/linux/apps/hello_pi/hello_tiger/tiger.c000066400000000000000000005213511421703157200260770ustar00rootroot00000000000000/*------------------------------------------------------------------------ * * OpenVG 1.0.1 Reference Implementation sample code * ------------------------------------------------- * * Copyright (c) 2007 The Khronos Group Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and /or associated documentation files * (the "Materials "), to deal in the Materials without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Materials, * and to permit persons to whom the Materials are furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Materials. * * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR * THE USE OR OTHER DEALINGS IN THE MATERIALS. * *//** * \file * \brief Path and paint data for Tiger image. * \note *//*-------------------------------------------------------------------*/ const int tigerCommandCount = 4151; const char tigerCommands[4151] = { 'F', 'N', 'B', 'M', 'M', 'L', 'L', 'L', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'L', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'L', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'L', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'L', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'L', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'L', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'L', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'N', 'S', 'B', 'M', 'M', 'C', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'L', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'L', 'L', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'L', 'L', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'L', 'L', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'L', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'L', 'L', 'L', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'L', 'C', 'C', 'C', 'C', 'L', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'F', 'N', 'B', 'M', 'M', 'C', 'L', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'L', 'L', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'L', 'C', 'L', 'N', 'S', 'R', 'M', 'M', 'L', 'N', 'S', 'R', 'M', 'M', 'C', 'N', 'S', 'R', 'M', 'M', 'C', 'N', 'S', 'R', 'M', 'M', 'C'}; const float tigerMinX = 0.0f; const float tigerMaxX = 612.0f; const float tigerMinY = 0.0f; const float tigerMaxY = 792.0f; const int tigerPointCount = 17005; const float tigerPoints[17005] = { 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 0, 792, 0, 0, 612, 0, 612, 792, 10, 0, 1, 1, 1, 1, 1, 1, 5, 85.25f, 487.75f, 85.25f, 487.75f, 85.5742f, 485.199f, 84.25f, 484.746f, 83.7617f, 485.242f, 65.6641f, 538.125f, 43.2461f, 535.746f, 43.2422f, 535.746f, 62.6445f, 543.746f, 85.25f, 487.75f, 10, 0.1892f, 0, 0, 0, 0, 0, 0, 5, 85.25f, 487.75f, 85.25f, 487.75f, 85.5742f, 485.199f, 84.25f, 484.746f, 83.7617f, 485.242f, 65.6641f, 538.125f, 43.2461f, 535.746f, 43.2422f, 535.746f, 62.6445f, 543.746f, 85.25f, 487.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 89.2461f, 490.75f, 89.2461f, 490.75f, 88.7422f, 488.613f, 88.2461f, 488.746f, 87.0508f, 489.27f, 88.0234f, 545.156f, 66.2461f, 550.746f, 66.2461f, 550.742f, 87.0977f, 551.469f, 89.2461f, 490.75f, 10, 0.1892f, 0, 0, 0, 0, 0, 0, 5, 89.2461f, 490.75f, 89.2461f, 490.75f, 88.7422f, 488.613f, 88.2461f, 488.746f, 87.0508f, 489.27f, 88.0234f, 545.156f, 66.2461f, 550.746f, 66.2461f, 550.742f, 87.0977f, 551.469f, 89.2461f, 490.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 119.25f, 443.75f, 119.25f, 443.75f, 121.387f, 442.992f, 121.246f, 442.746f, 120.352f, 441.504f, 66.2578f, 455.586f, 56.25f, 435.75f, 56.25f, 435.75f, 59.9062f, 456.168f, 119.25f, 443.75f, 10, 0.1892f, 0, 0, 0, 0, 0, 0, 5, 119.25f, 443.75f, 119.25f, 443.75f, 121.387f, 442.992f, 121.246f, 442.746f, 120.352f, 441.504f, 66.2578f, 455.586f, 56.25f, 435.75f, 56.25f, 435.75f, 59.9062f, 456.168f, 119.25f, 443.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 116.246f, 432.75f, 116.246f, 432.75f, 118.539f, 432.383f, 118.25f, 431.746f, 118.023f, 430.641f, 62.25f, 426.965f, 58.25f, 404.75f, 58.25f, 404.75f, 56.0391f, 425.516f, 116.246f, 432.75f, 10, 0.1892f, 0, 0, 0, 0, 0, 0, 5, 116.246f, 432.75f, 116.246f, 432.75f, 118.539f, 432.383f, 118.25f, 431.746f, 118.023f, 430.641f, 62.25f, 426.965f, 58.25f, 404.75f, 58.25f, 404.75f, 56.0391f, 425.516f, 116.246f, 432.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 112.25f, 438.746f, 112.25f, 438.742f, 113.82f, 438.164f, 113.25f, 437.75f, 113.059f, 436.52f, 57.3437f, 441.016f, 50.2461f, 419.75f, 50.2461f, 419.75f, 50.9883f, 440.492f, 112.25f, 438.746f, 10, 0.1892f, 0, 0, 0, 0, 0, 0, 5, 112.25f, 438.746f, 112.25f, 438.742f, 113.82f, 438.164f, 113.25f, 437.75f, 113.059f, 436.52f, 57.3437f, 441.016f, 50.2461f, 419.75f, 50.2461f, 419.75f, 50.9883f, 440.492f, 112.25f, 438.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 100.246f, 458.746f, 100.246f, 458.746f, 101.527f, 457.406f, 101.25f, 456.746f, 100.121f, 456.262f, 52.0039f, 484.699f, 36.25f, 467.746f, 36.25f, 467.746f, 46.0586f, 487.012f, 100.246f, 458.746f, 10, 0.1892f, 0, 0, 0, 0, 0, 0, 5, 100.246f, 458.746f, 100.246f, 458.746f, 101.527f, 457.406f, 101.25f, 456.746f, 100.121f, 456.262f, 52.0039f, 484.699f, 36.25f, 467.746f, 36.25f, 467.746f, 46.0586f, 487.012f, 100.246f, 458.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 92.2461f, 454.75f, 92.2422f, 454.75f, 93.3906f, 452.969f, 93.2461f, 452.75f, 92.125f, 451.672f, 41.0976f, 474.484f, 27.25f, 456.746f, 27.25f, 456.746f, 34.9258f, 476.105f, 92.2461f, 454.75f, 10, 0.1892f, 0, 0, 0, 0, 0, 0, 5, 92.2461f, 454.75f, 92.2422f, 454.75f, 93.3906f, 452.969f, 93.2461f, 452.75f, 92.125f, 451.672f, 41.0976f, 474.484f, 27.25f, 456.746f, 27.25f, 456.746f, 34.9258f, 476.105f, 92.2461f, 454.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 89.2461f, 449.746f, 89.2461f, 449.742f, 90.6992f, 448.723f, 90.25f, 447.746f, 89.6211f, 447.262f, 35.9609f, 462.906f, 25.25f, 442.746f, 25.25f, 442.742f, 29.625f, 463.676f, 89.2461f, 449.746f, 10, 0.1892f, 0, 0, 0, 0, 0, 0, 5, 89.2461f, 449.746f, 89.2461f, 449.742f, 90.6992f, 448.723f, 90.25f, 447.746f, 89.6211f, 447.262f, 35.9609f, 462.906f, 25.25f, 442.746f, 25.25f, 442.742f, 29.625f, 463.676f, 89.2461f, 449.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 100.246f, 448.75f, 100.246f, 448.75f, 101.969f, 447.469f, 101.25f, 446.75f, 100.43f, 446.512f, 56.3516f, 480.887f, 39.2461f, 466.746f, 39.2461f, 466.742f, 50.75f, 483.941f, 100.246f, 448.75f, 10, 0.1892f, 0, 0, 0, 0, 0, 0, 5, 100.246f, 448.75f, 100.246f, 448.75f, 101.969f, 447.469f, 101.25f, 446.75f, 100.43f, 446.512f, 56.3516f, 480.887f, 39.2461f, 466.746f, 39.2461f, 466.742f, 50.75f, 483.941f, 100.246f, 448.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 79.25f, 480.746f, 79.25f, 480.746f, 79.6367f, 479.02f, 79.25f, 478.746f, 77.8789f, 478.578f, 46.418f, 524.777f, 25.25f, 516.75f, 25.25f, 516.75f, 42.0195f, 529.398f, 79.25f, 480.746f, 10, 0.1892f, 0, 0, 0, 0, 0, 0, 5, 79.25f, 480.746f, 79.25f, 480.746f, 79.6367f, 479.02f, 79.25f, 478.746f, 77.8789f, 478.578f, 46.418f, 524.777f, 25.25f, 516.75f, 25.25f, 516.75f, 42.0195f, 529.398f, 79.25f, 480.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 79.25f, 473.746f, 79.25f, 473.742f, 80.8164f, 471.527f, 80.25f, 470.75f, 79.1914f, 470.723f, 38.5078f, 509.051f, 19.25f, 496.75f, 19.25f, 496.75f, 33.2148f, 512.609f, 79.25f, 473.746f, 10, 0.1892f, 0, 0, 0, 0, 0, 0, 5, 79.25f, 473.746f, 79.25f, 473.742f, 80.8164f, 471.527f, 80.25f, 470.75f, 79.1914f, 470.723f, 38.5078f, 509.051f, 19.25f, 496.75f, 19.25f, 496.75f, 33.2148f, 512.609f, 79.25f, 473.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 79.25f, 468.75f, 79.25f, 468.75f, 80.8516f, 466.828f, 80.25f, 466.746f, 79.3086f, 465.875f, 35.2305f, 500.246f, 17.25f, 485.75f, 17.25f, 485.75f, 29.6289f, 503.301f, 79.25f, 468.75f, 10, 0.1892f, 0, 0, 0, 0, 0, 0, 5, 79.25f, 468.75f, 79.25f, 468.75f, 80.8516f, 466.828f, 80.25f, 466.746f, 79.3086f, 465.875f, 35.2305f, 500.246f, 17.25f, 485.75f, 17.25f, 485.75f, 29.6289f, 503.301f, 79.25f, 468.75f, 10, 0, 1, 1, 1, 1, 1, 1, 88, 77.2461f, 466.746f, 77.7383f, 459.973f, 78.8242f, 452.746f, 80.25f, 449.746f, 80.25f, 449.742f, 76.7773f, 435.676f, 86.25f, 420.746f, 86.25f, 420.742f, 86.0195f, 413.238f, 88.2461f, 409.746f, 88.2461f, 409.742f, 92.1797f, 400.477f, 97.25f, 399.75f, 101.73f, 398.887f, 111.324f, 395.508f, 122.246f, 393.75f, 122.246f, 393.75f, 141.02f, 378.477f, 137.246f, 364.75f, 137.242f, 364.75f, 137.059f, 346.355f, 133.246f, 344.75f, 133.246f, 344.75f, 145.859f, 356.918f, 135.25f, 338.75f, 130.25f, 317.746f, 130.25f, 317.742f, 158.617f, 341.516f, 141.25f, 321.746f, 130.25f, 292.75f, 130.25f, 292.75f, 152.02f, 312.918f, 144.246f, 303.75f, 140.25f, 293.746f, 140.25f, 293.746f, 188.098f, 323.918f, 154.246f, 291.746f, 154.242f, 291.746f, 163.02f, 295.316f, 168.25f, 291.746f, 168.25f, 291.746f, 175.34f, 293.559f, 174.25f, 291.746f, 174.25f, 291.746f, 151.578f, 280.355f, 147.25f, 259.746f, 147.25f, 259.746f, 156.859f, 271.117f, 153.246f, 258.746f, 154.246f, 246.746f, 154.242f, 246.746f, 158.18f, 270.238f, 157.25f, 228.746f, 157.25f, 228.742f, 178.859f, 248.676f, 166.246f, 225.746f, 166.246f, 207.75f, 166.246f, 207.75f, 182.816f, 225.355f, 176.246f, 211.75f, 176.246f, 211.75f, 186.777f, 220.957f, 182.25f, 203.746f, 182.25f, 203.746f, 181.5f, 192.797f, 186.25f, 204.746f, 186.25f, 204.746f, 203.938f, 238.777f, 197.25f, 209.75f, 197.25f, 209.75f, 196.457f, 188.836f, 201.246f, 204.746f, 201.246f, 204.746f, 202.18f, 193.676f, 212.246f, 185.75f, 212.246f, 185.75f, 210.977f, 241.637f, 225.25f, 201.746f, 229.25f, 183.746f, 229.25f, 183.742f, 232.539f, 194.117f, 232.246f, 199.75f, 232.246f, 199.75f, 248.379f, 217.879f, 241.25f, 190.746f, 241.25f, 190.746f, 257.617f, 216.117f, 254.246f, 201.746f, 254.246f, 201.746f, 245.738f, 183.996f, 247.246f, 178.75f, 247.242f, 178.75f, 265.977f, 216.996f, 267.246f, 218.75f, 267.246f, 218.75f, 265.098f, 172.117f, 277.246f, 211.75f, 277.246f, 211.75f, 283.137f, 198.516f, 280.246f, 193.746f, 280.242f, 193.746f, 288.859f, 202.477f, 288.246f, 205.746f, 288.246f, 205.742f, 293.039f, 215.016f, 296.25f, 199.75f, 296.25f, 199.75f, 298.098f, 189.719f, 300.246f, 192.746f, 300.246f, 192.746f, 304.258f, 166.836f, 305.25f, 191.746f, 305.25f, 191.746f, 307.34f, 206.879f, 299.246f, 219.75f, 299.246f, 219.75f, 300.297f, 223.156f, 297.246f, 227.746f, 297.246f, 227.742f, 312.18f, 203.797f, 304.25f, 235.746f, 304.25f, 235.746f, 316.578f, 226.676f, 318.25f, 226.746f, 318.25f, 226.746f, 302.937f, 252.195f, 312.246f, 246.746f, 312.242f, 246.746f, 306.898f, 258.355f, 326.25f, 244.75f, 326.25f, 244.75f, 309.098f, 262.758f, 328.25f, 251.75f, 328.25f, 251.75f, 337.258f, 245.156f, 329.25f, 255.75f, 329.25f, 255.75f, 313.059f, 273.758f, 337.25f, 253.75f, 337.25f, 253.75f, 350.02f, 235.918f, 351.25f, 232.75f, 351.25f, 232.75f, 339.898f, 264.957f, 335.246f, 267.75f, 335.242f, 267.75f, 344.301f, 308.078f, 389.246f, 290.75f, 389.246f, 290.75f, 397.098f, 271.996f, 402.246f, 291.746f, 402.242f, 291.746f, 416.02f, 299.277f, 428.25f, 268.75f, 428.25f, 268.75f, 432.738f, 283.879f, 432.246f, 286.746f, 432.246f, 286.742f, 439.34f, 285.637f, 438.25f, 286.746f, 438.25f, 286.742f, 452.98f, 282.117f, 454.246f, 282.746f, 454.246f, 282.746f, 461.777f, 275.516f, 462.246f, 279.75f, 462.242f, 279.75f, 472.34f, 276.398f, 470.25f, 280.746f, 470.25f, 280.746f, 479.82f, 263.195f, 480.246f, 258.746f, 483.25f, 274.75f, 485.25f, 271.746f, 485.25f, 271.746f, 486.859f, 279.918f, 486.25f, 280.746f, 485.098f, 282.559f, 507.98f, 273.758f, 513.246f, 250.746f, 515.246f, 241.75f, 515.242f, 241.75f, 522.059f, 257.918f, 520.246f, 262.75f, 520.246f, 262.75f, 526.02f, 261.438f, 526.246f, 256.746f, 526.242f, 256.746f, 530.859f, 282.117f, 525.25f, 288.746f, 525.25f, 288.742f, 530.418f, 289.598f, 531.246f, 285.75f, 531.246f, 293.746f, 531.246f, 293.746f, 539.66f, 292.676f, 539.246f, 295.746f, 539.242f, 295.742f, 544.5f, 299.719f, 546.246f, 294.75f, 546.242f, 294.75f, 533.059f, 333.156f, 553.246f, 311.75f, 553.246f, 311.75f, 561.219f, 300.156f, 557.246f, 320.75f, 553.301f, 341.516f, 548.898f, 343.277f, 554.25f, 343.746f, 554.25f, 343.742f, 555.059f, 347.676f, 553.246f, 349.75f, 550.66f, 351.195f, 554.25f, 349.75f, 554.25f, 349.75f, 554.25f, 349.75f, 559.461f, 345.035f, 553.246f, 368.746f, 553.246f, 368.746f, 560.777f, 367.477f, 547.25f, 399.75f, 547.25f, 399.75f, 550.66f, 402.238f, 546.246f, 411.746f, 546.242f, 411.742f, 555.059f, 406.637f, 558.25f, 408.75f, 558.25f, 408.75f, 557.699f, 410.156f, 554.25f, 414.746f, 554.25f, 414.746f, 530.418f, 474.84f, 553.246f, 450.75f, 553.246f, 450.75f, 565.895f, 435.73f, 559.246f, 460.746f, 559.242f, 460.742f, 548.832f, 487.223f, 549.25f, 491.746f, 77.2461f, 466.746f, 10, 1.1f, 0, 0, 0, 0, 0, 0, 88, 77.2461f, 466.746f, 77.7383f, 459.973f, 78.8242f, 452.746f, 80.25f, 449.746f, 80.25f, 449.742f, 76.7773f, 435.676f, 86.25f, 420.746f, 86.25f, 420.742f, 86.0195f, 413.238f, 88.2461f, 409.746f, 88.2461f, 409.742f, 92.1797f, 400.477f, 97.25f, 399.75f, 101.73f, 398.887f, 111.324f, 395.508f, 122.246f, 393.75f, 122.246f, 393.75f, 141.02f, 378.477f, 137.246f, 364.75f, 137.242f, 364.75f, 137.059f, 346.355f, 133.246f, 344.75f, 133.246f, 344.75f, 145.859f, 356.918f, 135.25f, 338.75f, 130.25f, 317.746f, 130.25f, 317.742f, 158.617f, 341.516f, 141.25f, 321.746f, 130.25f, 292.75f, 130.25f, 292.75f, 152.02f, 312.918f, 144.246f, 303.75f, 140.25f, 293.746f, 140.25f, 293.746f, 188.098f, 323.918f, 154.246f, 291.746f, 154.242f, 291.746f, 163.02f, 295.316f, 168.25f, 291.746f, 168.25f, 291.746f, 175.34f, 293.559f, 174.25f, 291.746f, 174.25f, 291.746f, 151.578f, 280.355f, 147.25f, 259.746f, 147.25f, 259.746f, 156.859f, 271.117f, 153.246f, 258.746f, 154.246f, 246.746f, 154.242f, 246.746f, 158.18f, 270.238f, 157.25f, 228.746f, 157.25f, 228.742f, 178.859f, 248.676f, 166.246f, 225.746f, 166.246f, 207.75f, 166.246f, 207.75f, 182.816f, 225.355f, 176.246f, 211.75f, 176.246f, 211.75f, 186.777f, 220.957f, 182.25f, 203.746f, 182.25f, 203.746f, 181.5f, 192.797f, 186.25f, 204.746f, 186.25f, 204.746f, 203.938f, 238.777f, 197.25f, 209.75f, 197.25f, 209.75f, 196.457f, 188.836f, 201.246f, 204.746f, 201.246f, 204.746f, 202.18f, 193.676f, 212.246f, 185.75f, 212.246f, 185.75f, 210.977f, 241.637f, 225.25f, 201.746f, 229.25f, 183.746f, 229.25f, 183.742f, 232.539f, 194.117f, 232.246f, 199.75f, 232.246f, 199.75f, 248.379f, 217.879f, 241.25f, 190.746f, 241.25f, 190.746f, 257.617f, 216.117f, 254.246f, 201.746f, 254.246f, 201.746f, 245.738f, 183.996f, 247.246f, 178.75f, 247.242f, 178.75f, 265.977f, 216.996f, 267.246f, 218.75f, 267.246f, 218.75f, 265.098f, 172.117f, 277.246f, 211.75f, 277.246f, 211.75f, 283.137f, 198.516f, 280.246f, 193.746f, 280.242f, 193.746f, 288.859f, 202.477f, 288.246f, 205.746f, 288.246f, 205.742f, 293.039f, 215.016f, 296.25f, 199.75f, 296.25f, 199.75f, 298.098f, 189.719f, 300.246f, 192.746f, 300.246f, 192.746f, 304.258f, 166.836f, 305.25f, 191.746f, 305.25f, 191.746f, 307.34f, 206.879f, 299.246f, 219.75f, 299.246f, 219.75f, 300.297f, 223.156f, 297.246f, 227.746f, 297.246f, 227.742f, 312.18f, 203.797f, 304.25f, 235.746f, 304.25f, 235.746f, 316.578f, 226.676f, 318.25f, 226.746f, 318.25f, 226.746f, 302.937f, 252.195f, 312.246f, 246.746f, 312.242f, 246.746f, 306.898f, 258.355f, 326.25f, 244.75f, 326.25f, 244.75f, 309.098f, 262.758f, 328.25f, 251.75f, 328.25f, 251.75f, 337.258f, 245.156f, 329.25f, 255.75f, 329.25f, 255.75f, 313.059f, 273.758f, 337.25f, 253.75f, 337.25f, 253.75f, 350.02f, 235.918f, 351.25f, 232.75f, 351.25f, 232.75f, 339.898f, 264.957f, 335.246f, 267.75f, 335.242f, 267.75f, 344.301f, 308.078f, 389.246f, 290.75f, 389.246f, 290.75f, 397.098f, 271.996f, 402.246f, 291.746f, 402.242f, 291.746f, 416.02f, 299.277f, 428.25f, 268.75f, 428.25f, 268.75f, 432.738f, 283.879f, 432.246f, 286.746f, 432.246f, 286.742f, 439.34f, 285.637f, 438.25f, 286.746f, 438.25f, 286.742f, 452.98f, 282.117f, 454.246f, 282.746f, 454.246f, 282.746f, 461.777f, 275.516f, 462.246f, 279.75f, 462.242f, 279.75f, 472.34f, 276.398f, 470.25f, 280.746f, 470.25f, 280.746f, 479.82f, 263.195f, 480.246f, 258.746f, 483.25f, 274.75f, 485.25f, 271.746f, 485.25f, 271.746f, 486.859f, 279.918f, 486.25f, 280.746f, 485.098f, 282.559f, 507.98f, 273.758f, 513.246f, 250.746f, 515.246f, 241.75f, 515.242f, 241.75f, 522.059f, 257.918f, 520.246f, 262.75f, 520.246f, 262.75f, 526.02f, 261.438f, 526.246f, 256.746f, 526.242f, 256.746f, 530.859f, 282.117f, 525.25f, 288.746f, 525.25f, 288.742f, 530.418f, 289.598f, 531.246f, 285.75f, 531.246f, 293.746f, 531.246f, 293.746f, 539.66f, 292.676f, 539.246f, 295.746f, 539.242f, 295.742f, 544.5f, 299.719f, 546.246f, 294.75f, 546.242f, 294.75f, 533.059f, 333.156f, 553.246f, 311.75f, 553.246f, 311.75f, 561.219f, 300.156f, 557.246f, 320.75f, 553.301f, 341.516f, 548.898f, 343.277f, 554.25f, 343.746f, 554.25f, 343.742f, 555.059f, 347.676f, 553.246f, 349.75f, 550.66f, 351.195f, 554.25f, 349.75f, 554.25f, 349.75f, 554.25f, 349.75f, 559.461f, 345.035f, 553.246f, 368.746f, 553.246f, 368.746f, 560.777f, 367.477f, 547.25f, 399.75f, 547.25f, 399.75f, 550.66f, 402.238f, 546.246f, 411.746f, 546.242f, 411.742f, 555.059f, 406.637f, 558.25f, 408.75f, 558.25f, 408.75f, 557.699f, 410.156f, 554.25f, 414.746f, 554.25f, 414.746f, 530.418f, 474.84f, 553.246f, 450.75f, 553.246f, 450.75f, 565.895f, 435.73f, 559.246f, 460.746f, 559.242f, 460.742f, 548.832f, 487.223f, 549.25f, 491.746f, 77.2461f, 466.746f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 44, 549.25f, 491.746f, 550.379f, 491.531f, 552.805f, 490.293f, 554.25f, 488.746f, 554.25f, 488.742f, 561.66f, 476.598f, 556.25f, 496.75f, 556.25f, 496.75f, 545.82f, 528.52f, 555.246f, 515.746f, 555.246f, 515.742f, 562.098f, 508.277f, 558.25f, 522.746f, 554.328f, 541.309f, 551.25f, 548.746f, 551.25f, 548.746f, 551.25f, 548.746f, 564.301f, 543.039f, 535.246f, 586.75f, 544.246f, 582.75f, 544.246f, 582.75f, 522.938f, 626.199f, 499.25f, 631.746f, 490.25f, 638.746f, 490.25f, 638.742f, 532.621f, 680.316f, 518.25f, 720.75f, 518.25f, 720.75f, 511.059f, 726.52f, 500.246f, 716.75f, 500.246f, 716.75f, 493.461f, 711.117f, 487.246f, 712.75f, 487.246f, 712.75f, 452.98f, 711.559f, 451.25f, 711.746f, 448.578f, 711.559f, 410.301f, 752.477f, 338.25f, 732.746f, 338.25f, 732.742f, 332.418f, 730.918f, 327.25f, 731.75f, 327.25f, 731.75f, 307.34f, 749.84f, 253.246f, 724.746f, 253.246f, 724.746f, 242.656f, 722.559f, 241.25f, 722.746f, 239.137f, 722.559f, 236.059f, 722.559f, 227.25f, 715.746f, 218.457f, 708.477f, 218.02f, 707.598f, 216.25f, 705.75f, 216.25f, 705.75f, 197.777f, 693.52f, 192.25f, 692.75f, 192.25f, 692.75f, 179.738f, 685.598f, 175.25f, 674.75f, 171.246f, 673.746f, 171.246f, 673.742f, 169.18f, 665.359f, 168.25f, 663.75f, 168.25f, 663.75f, 163.457f, 660.078f, 162.25f, 653.746f, 162.25f, 653.742f, 152.898f, 647.316f, 153.246f, 642.746f, 153.242f, 642.742f, 151.578f, 636.758f, 150.246f, 631.746f, 150.246f, 631.742f, 142.777f, 626.199f, 143.246f, 622.75f, 143.242f, 622.75f, 135.297f, 607.719f, 136.25f, 599.75f, 136.25f, 599.75f, 129.578f, 600.68f, 126.246f, 597.75f, 126.242f, 597.75f, 125.617f, 592.758f, 124.25f, 592.746f, 124.25f, 592.746f, 120.777f, 591, 123.25f, 586.75f, 123.25f, 586.75f, 121.656f, 583.52f, 121.246f, 581.746f, 121.246f, 581.746f, 122.098f, 578.68f, 117.25f, 572.746f, 117.25f, 572.742f, 110.219f, 551.84f, 112.25f, 545.75f, 112.25f, 545.75f, 112.859f, 540.84f, 110.246f, 538.75f, 110.246f, 538.75f, 105.816f, 539.52f, 115.246f, 526.746f, 115.242f, 526.742f, 115.938f, 525, 112.25f, 522.746f, 112.25f, 522.746f, 93.5f, 518.398f, 91.25f, 500.746f, 91.25f, 500.746f, 75.8984f, 484.078f, 76.2461f, 478.746f, 75.8984f, 475.824f, 76.1953f, 472.359f, 77.2461f, 467.746f, 77.2461f, 467.746f, 76.3398f, 458.117f, 106.25f, 456.746f, 137.059f, 456.355f, 549.25f, 491.746f, 549.25f, 491.746f, 10, 1.1f, 0, 0, 0, 0, 0, 0, 44, 549.25f, 491.746f, 550.379f, 491.531f, 552.805f, 490.293f, 554.25f, 488.746f, 554.25f, 488.742f, 561.66f, 476.598f, 556.25f, 496.75f, 556.25f, 496.75f, 545.82f, 528.52f, 555.246f, 515.746f, 555.246f, 515.742f, 562.098f, 508.277f, 558.25f, 522.746f, 554.328f, 541.309f, 551.25f, 548.746f, 551.25f, 548.746f, 551.25f, 548.746f, 564.301f, 543.039f, 535.246f, 586.75f, 544.246f, 582.75f, 544.246f, 582.75f, 522.938f, 626.199f, 499.25f, 631.746f, 490.25f, 638.746f, 490.25f, 638.742f, 532.621f, 680.316f, 518.25f, 720.75f, 518.25f, 720.75f, 511.059f, 726.52f, 500.246f, 716.75f, 500.246f, 716.75f, 493.461f, 711.117f, 487.246f, 712.75f, 487.246f, 712.75f, 452.98f, 711.559f, 451.25f, 711.746f, 448.578f, 711.559f, 410.301f, 752.477f, 338.25f, 732.746f, 338.25f, 732.742f, 332.418f, 730.918f, 327.25f, 731.75f, 327.25f, 731.75f, 307.34f, 749.84f, 253.246f, 724.746f, 253.246f, 724.746f, 242.656f, 722.559f, 241.25f, 722.746f, 239.137f, 722.559f, 236.059f, 722.559f, 227.25f, 715.746f, 218.457f, 708.477f, 218.02f, 707.598f, 216.25f, 705.75f, 216.25f, 705.75f, 197.777f, 693.52f, 192.25f, 692.75f, 192.25f, 692.75f, 179.738f, 685.598f, 175.25f, 674.75f, 171.246f, 673.746f, 171.246f, 673.742f, 169.18f, 665.359f, 168.25f, 663.75f, 168.25f, 663.75f, 163.457f, 660.078f, 162.25f, 653.746f, 162.25f, 653.742f, 152.898f, 647.316f, 153.246f, 642.746f, 153.242f, 642.742f, 151.578f, 636.758f, 150.246f, 631.746f, 150.246f, 631.742f, 142.777f, 626.199f, 143.246f, 622.75f, 143.242f, 622.75f, 135.297f, 607.719f, 136.25f, 599.75f, 136.25f, 599.75f, 129.578f, 600.68f, 126.246f, 597.75f, 126.242f, 597.75f, 125.617f, 592.758f, 124.25f, 592.746f, 124.25f, 592.746f, 120.777f, 591, 123.25f, 586.75f, 123.25f, 586.75f, 121.656f, 583.52f, 121.246f, 581.746f, 121.246f, 581.746f, 122.098f, 578.68f, 117.25f, 572.746f, 117.25f, 572.742f, 110.219f, 551.84f, 112.25f, 545.75f, 112.25f, 545.75f, 112.859f, 540.84f, 110.246f, 538.75f, 110.246f, 538.75f, 105.816f, 539.52f, 115.246f, 526.746f, 115.242f, 526.742f, 115.938f, 525, 112.25f, 522.746f, 112.25f, 522.746f, 93.5f, 518.398f, 91.25f, 500.746f, 91.25f, 500.746f, 75.8984f, 484.078f, 76.2461f, 478.746f, 75.8984f, 475.824f, 76.1953f, 472.359f, 77.2461f, 467.746f, 77.2461f, 467.746f, 76.3398f, 458.117f, 106.25f, 456.746f, 137.059f, 456.355f, 549.25f, 491.746f, 549.25f, 491.746f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 18, 93.2461f, 466.746f, 65.3398f, 510.477f, 81.2461f, 448.75f, 81.2461f, 448.75f, 90.8594f, 410.598f, 233.246f, 451.746f, 233.246f, 451.746f, 233.246f, 451.742f, 419.098f, 485.398f, 431.246f, 489.746f, 443.738f, 494.199f, 548.246f, 486.746f, 548.246f, 486.746f, 542.246f, 505.75f, 471.02f, 556.68f, 449.898f, 531.156f, 435.246f, 535.746f, 419.98f, 539.957f, 422.621f, 529.398f, 419.246f, 528.746f, 415.578f, 527.637f, 372.461f, 554.918f, 365.246f, 553.75f, 358.379f, 553.156f, 330.504f, 579.285f, 347.25f, 544.746f, 364.539f, 506.957f, 282.699f, 501.238f, 264.246f, 513.746f, 245.738f, 525.879f, 272.25f, 493.746f, 272.25f, 493.746f, 292.379f, 471.316f, 254.246f, 489.746f, 254.246f, 489.746f, 216.699f, 503.879f, 190.297f, 475.719f, 187.246f, 474.75f, 183.258f, 473.957f, 177.977f, 470.438f, 177.246f, 477.746f, 176.219f, 484.52f, 167.957f, 502.891f, 133.246f, 473.746f, 111.098f, 455.695f, 96.25f, 479.75f, 96.25f, 479.75f, 93.2461f, 466.746f, 10, 0, 0.91f, 0.5f, 0.228f, 0.91f, 0.5f, 0.228f, 19, 367.246f, 551.75f, 359.82f, 551.238f, 331.914f, 577.352f, 348.25f, 542.75f, 366.641f, 503.719f, 284.141f, 499.316f, 265.246f, 511.746f, 247.18f, 523.957f, 273.25f, 491.746f, 273.25f, 491.746f, 293.82f, 469.398f, 256.246f, 487.75f, 256.246f, 487.75f, 218.137f, 501.957f, 191.738f, 473.797f, 188.246f, 472.75f, 184.699f, 472.039f, 179.418f, 468.516f, 178.246f, 475.746f, 177.656f, 482.598f, 169.543f, 500.785f, 134.25f, 471.746f, 111.18f, 452.957f, 96.25f, 476.75f, 96.25f, 476.75f, 93.2461f, 465.75f, 65.3164f, 509.219f, 82.2461f, 444.746f, 82.2461f, 444.746f, 91.5781f, 407.238f, 235.246f, 449.746f, 235.246f, 449.746f, 235.242f, 449.742f, 420.539f, 483.477f, 433.246f, 487.75f, 445.18f, 492.277f, 549.25f, 485.75f, 549.25f, 485.75f, 543.25f, 504.746f, 471.578f, 555.398f, 451.34f, 529.238f, 436.25f, 533.746f, 421.418f, 538.039f, 424.059f, 527.477f, 420.246f, 526.746f, 417.02f, 525.719f, 373.898f, 552.996f, 367.246f, 551.75f, 10, 0, 0.919f, 0.55f, 0.305f, 0.919f, 0.55f, 0.305f, 19, 368.246f, 549.75f, 361.258f, 549.316f, 334.051f, 575.75f, 350.25f, 540.75f, 367.641f, 500.695f, 285.578f, 497.398f, 267.246f, 509.75f, 248.617f, 522.035f, 275.246f, 489.746f, 275.246f, 489.746f, 295.258f, 467.477f, 257.246f, 485.75f, 257.246f, 485.75f, 219.578f, 500.035f, 193.18f, 471.875f, 189.246f, 470.75f, 186.137f, 470.117f, 180.859f, 466.598f, 180.246f, 473.746f, 179.098f, 480.676f, 171.125f, 498.68f, 136.25f, 469.746f, 111.258f, 450.215f, 97.25f, 472.75f, 97.25f, 472.75f, 93.2461f, 463.75f, 66.6172f, 506.637f, 82.2461f, 441.75f, 82.2461f, 441.75f, 92.2969f, 403.879f, 236.25f, 447.746f, 236.25f, 447.746f, 236.25f, 447.746f, 421.98f, 481.559f, 434.246f, 485.75f, 446.617f, 490.355f, 549.25f, 483.75f, 549.25f, 483.75f, 543.25f, 502.746f, 472.141f, 554.117f, 452.777f, 527.316f, 438.25f, 531.75f, 422.859f, 536.117f, 425.5f, 525.559f, 422.246f, 524.746f, 418.457f, 523.797f, 375.34f, 551.078f, 368.246f, 549.75f, 10, 0, 0.928f, 0.6f, 0.382f, 0.928f, 0.6f, 0.382f, 19, 369.25f, 548.746f, 362.699f, 547.398f, 335.496f, 573.832f, 351.25f, 538.75f, 369.738f, 497.285f, 286.43f, 495.867f, 268.246f, 507.75f, 250.059f, 520.117f, 276.246f, 487.75f, 276.246f, 487.75f, 296.699f, 465.559f, 259.25f, 483.75f, 259.25f, 483.75f, 221.02f, 498.117f, 194.617f, 469.957f, 191.246f, 468.75f, 187.578f, 468.199f, 182.301f, 464.676f, 181.25f, 471.746f, 180.539f, 478.758f, 172.711f, 496.574f, 137.246f, 467.746f, 111.336f, 447.477f, 97.25f, 469.746f, 97.25f, 469.746f, 93.2461f, 461.75f, 68.7969f, 502.516f, 83.2461f, 438.746f, 83.2461f, 438.746f, 93.0195f, 400.516f, 237.25f, 445.746f, 237.25f, 445.746f, 237.25f, 445.746f, 423.418f, 479.637f, 435.246f, 483.75f, 448.059f, 488.438f, 550.246f, 481.75f, 550.246f, 481.75f, 544.246f, 501.75f, 472.699f, 552.84f, 454.219f, 525.398f, 439.25f, 529.75f, 424.301f, 534.199f, 426.938f, 523.637f, 423.246f, 522.746f, 419.898f, 521.879f, 376.777f, 549.156f, 369.25f, 548.746f, 10, 0, 0.937f, 0.65f, 0.46f, 0.937f, 0.65f, 0.46f, 19, 371.25f, 546.746f, 364.141f, 545.477f, 337.492f, 572.156f, 352.25f, 536.75f, 371.18f, 493.559f, 288.457f, 493.559f, 270.25f, 505.75f, 251.5f, 518.195f, 278.246f, 485.75f, 278.246f, 485.75f, 298.141f, 463.637f, 260.25f, 481.75f, 260.25f, 481.75f, 222.457f, 496.195f, 196.059f, 468.035f, 192.25f, 466.746f, 189.02f, 466.277f, 183.738f, 462.758f, 183.25f, 469.746f, 181.98f, 476.836f, 174.297f, 494.473f, 139.246f, 466.746f, 111.418f, 444.738f, 97.25f, 466.746f, 97.25f, 466.746f, 93.2461f, 460.746f, 70.9766f, 498.617f, 84.25f, 434.746f, 84.25f, 434.746f, 93.7383f, 397.156f, 239.25f, 444.746f, 239.25f, 444.746f, 239.25f, 444.742f, 424.859f, 477.715f, 437.25f, 481.75f, 449.5f, 486.516f, 550.246f, 479.75f, 550.246f, 479.75f, 544.246f, 500.746f, 473.262f, 551.559f, 455.66f, 523.477f, 440.25f, 527.75f, 425.738f, 532.277f, 428.379f, 521.715f, 425.25f, 520.75f, 421.34f, 519.957f, 378.219f, 547.238f, 371.25f, 546.746f, 10, 0, 0.946f, 0.7f, 0.537f, 0.946f, 0.7f, 0.537f, 19, 372.25f, 544.746f, 365.578f, 543.559f, 337.02f, 569.352f, 354.246f, 534.75f, 375.258f, 492.078f, 289.898f, 491.637f, 271.25f, 503.75f, 252.938f, 516.277f, 279.246f, 483.75f, 279.246f, 483.75f, 299.578f, 461.719f, 261.25f, 479.75f, 261.25f, 479.75f, 223.898f, 494.277f, 197.5f, 466.117f, 194.25f, 464.746f, 190.457f, 464.355f, 185.18f, 460.836f, 184.25f, 467.746f, 183.418f, 474.918f, 175.879f, 492.367f, 140.25f, 464.746f, 111.5f, 441.996f, 98.2461f, 462.746f, 98.2461f, 462.746f, 92.2461f, 458.746f, 72.9375f, 495.156f, 85.25f, 431.746f, 85.25f, 431.746f, 94.457f, 393.797f, 240.25f, 442.746f, 240.25f, 442.746f, 240.25f, 442.742f, 426.301f, 475.797f, 438.25f, 479.75f, 450.941f, 484.598f, 551.25f, 477.746f, 551.25f, 477.746f, 545.25f, 498.75f, 473.82f, 550.277f, 457.102f, 521.559f, 442.246f, 525.75f, 427.18f, 530.355f, 429.82f, 519.797f, 426.25f, 518.75f, 422.781f, 518.039f, 379.66f, 545.316f, 372.25f, 544.746f, 10, 0, 0.955f, 0.75f, 0.614f, 0.955f, 0.75f, 0.614f, 19, 374.25f, 542.75f, 367.02f, 541.637f, 338.043f, 567.223f, 355.246f, 532.746f, 378.02f, 488.836f, 291.34f, 489.715f, 273.25f, 501.75f, 254.379f, 514.355f, 281.25f, 481.75f, 281.25f, 481.75f, 301.02f, 459.797f, 263.25f, 478.746f, 263.25f, 478.746f, 225.34f, 492.355f, 198.938f, 464.195f, 195.25f, 463.75f, 191.898f, 462.438f, 186.617f, 458.918f, 185.25f, 465.75f, 184.859f, 472.996f, 177.465f, 490.262f, 141.25f, 462.746f, 111.578f, 439.258f, 98.2461f, 459.75f, 98.2461f, 459.75f, 92.2461f, 456.746f, 75.1172f, 490.156f, 85.25f, 428.75f, 85.25f, 428.75f, 95.1797f, 390.438f, 242.246f, 440.746f, 242.246f, 440.746f, 242.246f, 440.742f, 427.738f, 473.875f, 440.25f, 478.746f, 452.379f, 482.676f, 551.25f, 475.746f, 551.25f, 475.746f, 545.25f, 497.746f, 474.379f, 548.996f, 458.539f, 519.637f, 443.246f, 523.75f, 428.621f, 528.438f, 431.258f, 517.875f, 427.25f, 516.75f, 424.219f, 516.117f, 381.102f, 543.398f, 374.25f, 542.75f, 10, 0, 0.964f, 0.8f, 0.691f, 0.964f, 0.8f, 0.691f, 19, 375.246f, 540.75f, 368.461f, 539.719f, 338.273f, 564.66f, 357.246f, 530.746f, 381.219f, 487.355f, 292.777f, 487.797f, 274.25f, 499.746f, 255.82f, 512.438f, 282.25f, 479.75f, 282.25f, 479.75f, 302.457f, 457.879f, 264.246f, 476.75f, 264.246f, 476.75f, 226.777f, 490.438f, 200.379f, 462.277f, 197.25f, 461.75f, 193.34f, 460.516f, 188.059f, 456.996f, 187.246f, 463.75f, 186.297f, 471.078f, 179.047f, 488.156f, 143.246f, 460.746f, 111.656f, 436.516f, 99.2461f, 456.746f, 99.2461f, 456.746f, 92.2461f, 454.75f, 76.8555f, 486.477f, 86.25f, 424.75f, 86.25f, 424.75f, 95.8984f, 387.074f, 243.246f, 438.746f, 243.246f, 438.746f, 243.246f, 438.742f, 429.18f, 471.957f, 441.246f, 476.75f, 453.82f, 480.758f, 552.25f, 474.75f, 552.25f, 474.75f, 546.246f, 496.75f, 474.941f, 547.719f, 459.98f, 517.719f, 445.246f, 521.746f, 430.059f, 526.516f, 432.699f, 515.957f, 429.25f, 514.75f, 425.66f, 514.195f, 382.539f, 541.477f, 375.246f, 540.75f, 10, 0, 0.973f, 0.85f, 0.769f, 0.973f, 0.85f, 0.769f, 19, 377.246f, 538.75f, 369.898f, 537.797f, 339.715f, 562.738f, 358.25f, 528.746f, 382.66f, 485.437f, 294.219f, 485.875f, 275.246f, 497.746f, 257.258f, 510.516f, 283.25f, 477.746f, 283.25f, 477.746f, 303.898f, 455.957f, 266.246f, 474.75f, 266.246f, 474.75f, 228.219f, 488.516f, 201.816f, 460.355f, 198.246f, 459.75f, 194.777f, 458.598f, 189.5f, 455.078f, 188.246f, 461.75f, 187.738f, 469.156f, 180.633f, 486.051f, 144.246f, 458.746f, 111.738f, 433.777f, 99.2461f, 452.75f, 99.2461f, 452.75f, 92.2461f, 453.746f, 77.7188f, 482.578f, 87.2461f, 421.75f, 87.2461f, 421.75f, 96.6172f, 383.715f, 245.246f, 436.746f, 245.246f, 436.746f, 245.246f, 436.746f, 430.621f, 470.035f, 443.246f, 474.75f, 455.258f, 478.836f, 552.25f, 472.75f, 552.25f, 472.75f, 547.25f, 495.746f, 475.5f, 546.438f, 461.418f, 515.797f, 446.246f, 519.746f, 431.5f, 524.598f, 434.141f, 514.035f, 430.246f, 512.75f, 427.098f, 512.277f, 383.98f, 539.555f, 377.246f, 538.75f, 10, 0, 0.982f, 0.9f, 0.846f, 0.982f, 0.9f, 0.846f, 19, 378.246f, 536.75f, 371.34f, 535.879f, 341.578f, 561.055f, 360.25f, 526.746f, 384.098f, 482.195f, 295.66f, 483.957f, 277.246f, 496.75f, 258.699f, 508.598f, 285.25f, 475.746f, 285.25f, 475.746f, 305.34f, 454.035f, 267.246f, 472.75f, 267.246f, 472.75f, 229.66f, 486.598f, 203.258f, 458.438f, 199.246f, 457.75f, 196.219f, 456.676f, 190.937f, 453.156f, 190.246f, 459.75f, 189.18f, 467.238f, 182.219f, 483.949f, 146.25f, 456.746f, 111.82f, 431.035f, 99.2461f, 449.746f, 99.2461f, 449.746f, 92.2461f, 451.746f, 78.3594f, 478.238f, 87.2461f, 417.75f, 87.2461f, 417.75f, 97.3399f, 380.355f, 246.246f, 434.746f, 246.246f, 434.746f, 246.242f, 434.746f, 432.059f, 468.117f, 444.246f, 472.75f, 456.699f, 476.918f, 553.246f, 470.75f, 553.246f, 470.75f, 547.25f, 493.746f, 476.059f, 545.156f, 462.859f, 513.879f, 448.25f, 518.75f, 432.938f, 522.676f, 435.578f, 512.117f, 432.246f, 510.746f, 428.539f, 510.355f, 385.418f, 537.637f, 378.246f, 536.75f, 10, 0, 0.991f, 0.95f, 0.923f, 0.991f, 0.95f, 0.923f, 19, 380.25f, 534.75f, 372.777f, 533.957f, 344.207f, 559.746f, 361.25f, 524.746f, 384.66f, 478.078f, 297.098f, 482.035f, 278.246f, 494.75f, 260.141f, 506.676f, 286.246f, 473.746f, 286.246f, 473.746f, 306.777f, 452.117f, 269.246f, 470.75f, 269.246f, 470.75f, 231.098f, 484.676f, 204.699f, 456.516f, 201.246f, 455.746f, 197.66f, 454.758f, 192.379f, 451.238f, 191.246f, 458.746f, 190.621f, 465.316f, 183.801f, 481.844f, 147.25f, 454.75f, 111.898f, 428.297f, 100.246f, 446.75f, 100.246f, 446.75f, 92.2461f, 449.746f, 78.5586f, 475.656f, 88.2461f, 414.746f, 88.2461f, 414.746f, 98.0586f, 376.996f, 248.25f, 432.75f, 248.25f, 432.75f, 248.25f, 432.75f, 433.5f, 466.195f, 446.246f, 470.75f, 458.141f, 474.996f, 553.246f, 468.75f, 553.246f, 468.75f, 548.246f, 492.75f, 476.621f, 543.879f, 464.301f, 511.957f, 449.25f, 516.75f, 434.379f, 520.758f, 437.02f, 510.195f, 433.246f, 509.75f, 429.98f, 508.438f, 386.859f, 535.719f, 380.25f, 534.75f, 10, 0, 1, 1, 1, 1, 1, 1, 18, 92.2461f, 448.75f, 78.5391f, 472.637f, 89.2461f, 411.746f, 89.2461f, 411.746f, 98.7773f, 373.637f, 249.25f, 430.75f, 249.25f, 430.75f, 249.25f, 430.75f, 434.938f, 464.277f, 447.25f, 468.75f, 459.578f, 473.078f, 553.246f, 466.746f, 553.246f, 466.746f, 548.246f, 491.746f, 477.18f, 542.598f, 465.738f, 510.039f, 451.25f, 514.75f, 435.82f, 518.84f, 438.461f, 508.277f, 435.246f, 507.75f, 431.418f, 506.52f, 388.301f, 533.797f, 381.25f, 532.746f, 374.219f, 532.039f, 346.477f, 558.227f, 363.25f, 522.746f, 387.23f, 470.762f, 295.941f, 481.848f, 280.246f, 492.75f, 261.578f, 504.758f, 288.246f, 471.746f, 288.246f, 471.746f, 308.219f, 450.195f, 270.25f, 468.75f, 270.25f, 468.75f, 232.539f, 482.758f, 206.137f, 454.598f, 202.246f, 453.746f, 199.098f, 452.836f, 193.816f, 449.316f, 193.25f, 456.746f, 192.059f, 463.398f, 185.387f, 479.738f, 149.246f, 452.75f, 111.977f, 425.559f, 100.246f, 442.746f, 100.246f, 442.746f, 92.2461f, 448.75f, 10, 0, 0, 0, 0, 0, 0, 0, 7, 138.246f, 415.75f, 138.246f, 415.75f, 130.457f, 402.676f, 153.246f, 387.746f, 153.242f, 387.742f, 154.879f, 386.617f, 135.25f, 390.746f, 135.25f, 390.746f, 128.258f, 393.438f, 126.246f, 404.75f, 126.242f, 404.75f, 121.219f, 409.719f, 116.246f, 415.75f, 110.656f, 422.035f, 138.246f, 415.75f, 138.246f, 415.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 8, 292.25f, 467.746f, 292.25f, 467.746f, 311.848f, 438.297f, 311.246f, 432.75f, 309.758f, 421.598f, 309.539f, 411.035f, 313.246f, 406.75f, 316.578f, 402.238f, 326.25f, 365.746f, 326.25f, 365.746f, 326.25f, 365.742f, 325.82f, 364.398f, 339.25f, 405.746f, 339.25f, 405.742f, 352.219f, 423.797f, 330.25f, 443.75f, 330.25f, 443.75f, 291.5f, 475.719f, 292.25f, 467.746f, 10, 0, 0, 0, 0, 0, 0, 0, 15, 160.246f, 385.746f, 160.246f, 385.742f, 172.699f, 378.035f, 157.25f, 343.746f, 164.246f, 346.746f, 164.242f, 346.746f, 163.02f, 334.035f, 159.246f, 331.75f, 167.25f, 334.75f, 167.25f, 334.75f, 172.699f, 326.117f, 168.25f, 320.75f, 168.25f, 320.75f, 186.777f, 312.035f, 186.25f, 304.746f, 186.25f, 304.746f, 192.938f, 313.797f, 188.246f, 320.75f, 184.137f, 327.879f, 176.219f, 323.477f, 177.246f, 343.746f, 167.25f, 339.746f, 167.25f, 339.742f, 173.578f, 349.879f, 173.25f, 356.75f, 165.246f, 354.746f, 165.242f, 354.742f, 181.793f, 383.512f, 170.246f, 384.75f, 163.457f, 385.957f, 160.246f, 385.746f, 160.246f, 385.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 196.25f, 367.75f, 196.25f, 367.75f, 199.098f, 372.316f, 196.25f, 371.75f, 192.938f, 370.559f, 158.617f, 354.277f, 152.25f, 343.746f, 152.25f, 343.742f, 189.859f, 370.559f, 196.25f, 367.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 207.25f, 358.75f, 207.25f, 358.75f, 210.539f, 363.516f, 207.25f, 362.75f, 204.379f, 361.758f, 170.059f, 345.477f, 163.25f, 334.75f, 163.25f, 334.75f, 201.297f, 361.758f, 207.25f, 358.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 222.246f, 375.75f, 222.246f, 375.75f, 225.059f, 380.238f, 222.246f, 379.746f, 218.898f, 378.477f, 184.578f, 362.195f, 178.246f, 351.75f, 178.246f, 351.75f, 215.816f, 378.477f, 222.246f, 375.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 196.25f, 327.75f, 196.25f, 327.75f, 196.457f, 334.035f, 193.25f, 332.746f, 190.297f, 332.277f, 150.699f, 312.918f, 144.246f, 302.746f, 144.246f, 302.746f, 190.297f, 330.516f, 196.25f, 327.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 198.246f, 339.746f, 198.246f, 339.742f, 199.098f, 344.598f, 196.25f, 343.746f, 193.816f, 343.719f, 164.777f, 330.957f, 158.25f, 320.75f, 158.25f, 320.75f, 190.738f, 344.156f, 198.246f, 339.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 24, 182.25f, 286.746f, 171.246f, 278.75f, 171.246f, 278.75f, 182.379f, 286.957f, 186.25f, 285.75f, 186.25f, 285.75f, 178.859f, 273.316f, 178.246f, 267.75f, 178.246f, 267.75f, 189.418f, 281.676f, 195.25f, 280.746f, 195.25f, 280.746f, 203.938f, 280.797f, 204.25f, 268.75f, 204.25f, 268.75f, 210.098f, 280.355f, 213.246f, 279.75f, 213.242f, 279.75f, 214.938f, 272.879f, 213.246f, 265.75f, 213.242f, 265.75f, 218.02f, 273.758f, 222.246f, 271.746f, 222.246f, 271.746f, 229.457f, 274.195f, 228.25f, 261.746f, 228.25f, 261.742f, 228.578f, 249.996f, 227.25f, 246.746f, 227.25f, 246.746f, 233.859f, 275.957f, 236.25f, 276.75f, 236.25f, 276.75f, 245.297f, 277.719f, 250.25f, 267.75f, 250.25f, 267.75f, 246.18f, 276.398f, 251.25f, 273.746f, 251.25f, 273.742f, 263.34f, 272.438f, 267.246f, 264.746f, 267.246f, 264.742f, 259.379f, 278.156f, 265.246f, 274.75f, 265.246f, 274.75f, 273.02f, 274.637f, 274.25f, 267.75f, 274.25f, 267.75f, 283.578f, 244.277f, 286.246f, 242.75f, 286.246f, 242.75f, 277.418f, 266.277f, 279.246f, 266.746f, 279.242f, 266.742f, 276.977f, 279.477f, 282.25f, 262.75f, 282.25f, 262.75f, 279.18f, 278.598f, 285.25f, 277.746f, 291.5f, 276.836f, 296.34f, 265.836f, 305.25f, 268.75f, 305.25f, 268.75f, 316.141f, 262.316f, 318.25f, 338.75f, 182.25f, 286.746f, 10, 0, 0, 0, 0, 0, 0, 0, 15, 187.246f, 388.75f, 187.246f, 388.75f, 203.5f, 395.637f, 247.246f, 388.75f, 247.242f, 388.75f, 255.418f, 388.598f, 263.25f, 398.746f, 270.379f, 407.957f, 299.859f, 415.879f, 307.25f, 413.75f, 317.25f, 406.75f, 318.25f, 405.746f, 318.25f, 405.742f, 331.98f, 393.879f, 332.246f, 385.746f, 332.859f, 377.156f, 316.578f, 324.355f, 306.25f, 306.746f, 295.457f, 289.156f, 284.898f, 275.516f, 264.246f, 277.746f, 264.246f, 277.742f, 240.898f, 282.559f, 212.246f, 277.746f, 212.246f, 277.742f, 180.617f, 279.918f, 177.246f, 288.746f, 174.457f, 297.516f, 190.246f, 313.746f, 190.246f, 313.746f, 190.246f, 313.746f, 194.699f, 323.477f, 193.25f, 339.746f, 192.059f, 355.156f, 192.5f, 385.957f, 187.246f, 388.75f, 10, 0, 0.9f, 0.4f, 0.55f, 0.9f, 0.4f, 0.55f, 8, 211.246f, 386.75f, 220.656f, 366.598f, 188.246f, 294.75f, 188.246f, 294.75f, 185.898f, 293.117f, 202.023f, 286.469f, 213.246f, 288.746f, 225.219f, 292.059f, 269.246f, 287.75f, 269.246f, 287.75f, 295.457f, 304.559f, 309.246f, 353.75f, 309.246f, 353.75f, 309.246f, 353.75f, 320.98f, 379.797f, 301.246f, 383.746f, 282.258f, 386.836f, 211.246f, 386.75f, 211.246f, 386.75f, 10, 0, 0.7f, 0.2f, 0.35f, 0.7f, 0.2f, 0.35f, 6, 209.246f, 352.746f, 212.844f, 366.922f, 214.586f, 379.902f, 211.246f, 386.75f, 211.246f, 386.75f, 280.059f, 379.797f, 292.25f, 402.75f, 297.043f, 411.34f, 313.277f, 377.598f, 313.246f, 366.75f, 313.242f, 366.75f, 243.539f, 351.195f, 227.25f, 363.746f, 209.246f, 352.746f, 10, 0, 0.65f, 0.15f, 0.3f, 0.65f, 0.15f, 0.3f, 13, 214.25f, 334.75f, 214.25f, 334.75f, 216.258f, 326.996f, 213.246f, 322.75f, 213.242f, 322.75f, 211.859f, 321.719f, 210.246f, 321.746f, 210.246f, 321.742f, 211.859f, 317.316f, 218.25f, 315.746f, 218.25f, 315.746f, 220.656f, 310.719f, 223.246f, 310.746f, 225.938f, 309.836f, 231.219f, 303.676f, 235.246f, 304.746f, 240.02f, 306.316f, 252.25f, 310.746f, 252.25f, 310.746f, 252.25f, 310.742f, 258.5f, 314.238f, 268.246f, 310.746f, 268.242f, 310.742f, 270.789f, 311.16f, 271.25f, 315.746f, 271.809f, 320.727f, 275.219f, 324.797f, 277.246f, 326.746f, 279.617f, 329.195f, 290.18f, 343.277f, 289.246f, 343.746f, 287.539f, 344.156f, 214.25f, 334.75f, 214.25f, 334.75f, 10, 0, 1, 0.45f, 0.5f, 1, 0.45f, 0.5f, 12, 209.246f, 387.746f, 209.246f, 387.742f, 206.137f, 363.516f, 209.246f, 354.746f, 213.18f, 345.035f, 212.297f, 342.836f, 211.246f, 338.75f, 210.539f, 334.035f, 215.379f, 323.035f, 221.246f, 316.75f, 234.246f, 314.75f, 234.246f, 314.75f, 251.457f, 318.637f, 261.25f, 315.746f, 261.25f, 315.746f, 271.473f, 314.078f, 275.246f, 330.746f, 275.246f, 330.742f, 280.5f, 337.559f, 288.246f, 340.75f, 296.34f, 343.719f, 304.258f, 389.477f, 300.246f, 398.746f, 295.457f, 407.078f, 279.617f, 411.918f, 262.25f, 394.746f, 244.418f, 377.598f, 242.219f, 396.078f, 209.246f, 387.746f, 10, 1.1f, 0, 0, 0, 0, 0, 0, 12, 209.246f, 387.746f, 209.246f, 387.742f, 206.137f, 363.516f, 209.246f, 354.746f, 213.18f, 345.035f, 212.297f, 342.836f, 211.246f, 338.75f, 210.539f, 334.035f, 215.379f, 323.035f, 221.246f, 316.75f, 234.246f, 314.75f, 234.246f, 314.75f, 251.457f, 318.637f, 261.25f, 315.746f, 261.25f, 315.746f, 271.473f, 314.078f, 275.246f, 330.746f, 275.246f, 330.742f, 280.5f, 337.559f, 288.246f, 340.75f, 296.34f, 343.719f, 304.258f, 389.477f, 300.246f, 398.746f, 295.457f, 407.078f, 279.617f, 411.918f, 262.25f, 394.746f, 244.418f, 377.598f, 242.219f, 396.078f, 209.246f, 387.746f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 7, 211.246f, 305.75f, 211.246f, 305.75f, 210.098f, 308.078f, 205.25f, 308.746f, 205.25f, 308.742f, 180.617f, 312.477f, 171.246f, 325.75f, 171.246f, 325.75f, 163.898f, 332.277f, 168.25f, 319.746f, 168.25f, 319.742f, 180.18f, 297.078f, 187.246f, 293.746f, 187.246f, 293.746f, 205.699f, 289.598f, 211.246f, 305.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 7, 211.246f, 305.75f, 211.246f, 305.75f, 210.098f, 308.078f, 205.25f, 308.746f, 205.25f, 308.742f, 180.617f, 312.477f, 171.246f, 325.75f, 171.246f, 325.75f, 163.898f, 332.277f, 168.25f, 319.746f, 168.25f, 319.742f, 180.18f, 297.078f, 187.246f, 293.746f, 187.246f, 293.746f, 205.699f, 289.598f, 211.246f, 305.75f, 10, 0, 0.8f, 0.25f, 0.3f, 0.8f, 0.25f, 0.3f, 9, 299.246f, 375.75f, 299.641f, 384.941f, 301.789f, 394.418f, 300.246f, 398.746f, 292.766f, 412.461f, 274.098f, 406.535f, 262.25f, 394.746f, 244.418f, 377.598f, 242.219f, 396.078f, 209.246f, 387.746f, 209.246f, 387.742f, 207.297f, 372.797f, 208.25f, 361.746f, 208.25f, 361.742f, 249.258f, 374.516f, 250.25f, 368.746f, 250.25f, 368.746f, 251.898f, 371.879f, 262.25f, 371.75f, 272.137f, 371.879f, 297.152f, 373.168f, 299.246f, 375.75f, 10, 2.2f, 0.65f, 0.1f, 0.15f, 0.65f, 0.1f, 0.15f, 3, 251.25f, 387.746f, 251.25f, 387.742f, 256.738f, 381.996f, 253.246f, 371.75f, 253.246f, 371.75f, 236.938f, 353.836f, 239.25f, 338.75f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 5, 198.246f, 293.746f, 198.246f, 293.746f, 193.816f, 308.078f, 203.25f, 300.75f, 203.25f, 300.75f, 208.777f, 298.398f, 207.25f, 296.75f, 206.137f, 294.879f, 199.977f, 290.477f, 198.246f, 293.746f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 5, 198.246f, 293.746f, 198.246f, 293.746f, 193.816f, 308.078f, 203.25f, 300.75f, 203.25f, 300.75f, 208.777f, 298.398f, 207.25f, 296.75f, 206.137f, 294.879f, 199.977f, 290.477f, 198.246f, 293.746f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 5, 204.25f, 292.75f, 204.25f, 292.75f, 200.328f, 303.941f, 208.25f, 297.746f, 208.25f, 297.742f, 212.937f, 295.266f, 211.246f, 294.75f, 206.227f, 293.383f, 211.242f, 290.566f, 204.25f, 292.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 5, 204.25f, 292.75f, 204.25f, 292.75f, 200.328f, 303.941f, 208.25f, 297.746f, 208.25f, 297.742f, 212.937f, 295.266f, 211.246f, 294.75f, 206.227f, 293.383f, 211.242f, 290.566f, 204.25f, 292.75f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 5, 209.246f, 292.75f, 209.246f, 292.75f, 205.609f, 303.941f, 213.246f, 297.746f, 213.242f, 297.742f, 218.168f, 295.414f, 216.25f, 294.75f, 212.824f, 293.383f, 216.523f, 290.566f, 209.246f, 292.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 5, 209.246f, 292.75f, 209.246f, 292.75f, 205.609f, 303.941f, 213.246f, 297.746f, 213.242f, 297.742f, 218.168f, 295.414f, 216.25f, 294.75f, 212.824f, 293.383f, 216.523f, 290.566f, 209.246f, 292.75f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 5, 216.25f, 292.75f, 216.25f, 292.75f, 212.871f, 303.723f, 220.246f, 297.746f, 220.246f, 297.742f, 225.434f, 295.172f, 224.246f, 294.75f, 220.527f, 293.383f, 223.781f, 290.344f, 216.25f, 292.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 5, 216.25f, 292.75f, 216.25f, 292.75f, 212.871f, 303.723f, 220.246f, 297.746f, 220.246f, 297.742f, 225.434f, 295.172f, 224.246f, 294.75f, 220.527f, 293.383f, 223.781f, 290.344f, 216.25f, 292.75f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 5, 224.246f, 292.75f, 224.242f, 292.75f, 220, 303.809f, 227.25f, 297.746f, 227.25f, 297.742f, 231.969f, 296.066f, 231.246f, 294.75f, 229.855f, 293.25f, 230.91f, 290.434f, 224.246f, 292.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 5, 224.246f, 292.75f, 224.242f, 292.75f, 220, 303.809f, 227.25f, 297.746f, 227.25f, 297.742f, 231.969f, 296.066f, 231.246f, 294.75f, 229.855f, 293.25f, 230.91f, 290.434f, 224.246f, 292.75f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 5, 231.246f, 291.746f, 231.246f, 291.746f, 225.938f, 305.438f, 236.25f, 298.75f, 236.25f, 298.75f, 241.34f, 296.195f, 240.25f, 294.75f, 238.699f, 292.676f, 240.02f, 289.156f, 231.246f, 291.746f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 5, 231.246f, 291.746f, 231.246f, 291.746f, 225.938f, 305.438f, 236.25f, 298.75f, 236.25f, 298.75f, 241.34f, 296.195f, 240.25f, 294.75f, 238.699f, 292.676f, 240.02f, 289.156f, 231.246f, 291.746f, 10, 2.2f, 0.65f, 0.15f, 0.3f, 0.65f, 0.15f, 0.3f, 4, 200.246f, 310.746f, 200.246f, 310.742f, 214.5f, 313.797f, 221.246f, 310.746f, 221.246f, 310.742f, 227.699f, 308.957f, 229.25f, 309.75f, 230.34f, 309.836f, 234.246f, 310.746f, 234.246f, 310.746f, 10, 2.2f, 0.65f, 0.15f, 0.3f, 0.65f, 0.15f, 0.3f, 4, 237.25f, 300.75f, 237.25f, 300.75f, 250.578f, 315.996f, 264.246f, 310.746f, 271.496f, 308.328f, 270.379f, 312.035f, 271.25f, 314.75f, 272.137f, 318.195f, 272.359f, 322.816f, 278.246f, 325.75f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 7, 256.246f, 318.75f, 256.246f, 318.75f, 251.898f, 330.516f, 249.25f, 316.75f, 245.738f, 302.355f, 242.219f, 298.398f, 240.25f, 295.746f, 240.25f, 295.742f, 240.457f, 289.598f, 249.25f, 289.75f, 249.25f, 289.75f, 261.578f, 290.477f, 262.25f, 293.746f, 262.457f, 296.637f, 260.699f, 309.398f, 256.246f, 318.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 7, 256.246f, 318.75f, 256.246f, 318.75f, 251.898f, 330.516f, 249.25f, 316.75f, 245.738f, 302.355f, 242.219f, 298.398f, 240.25f, 295.746f, 240.25f, 295.742f, 240.457f, 289.598f, 249.25f, 289.75f, 249.25f, 289.75f, 261.578f, 290.477f, 262.25f, 293.746f, 262.457f, 296.637f, 260.699f, 309.398f, 256.246f, 318.75f, 10, 2.2f, 0.65f, 0.15f, 0.3f, 0.65f, 0.15f, 0.3f, 2, 271.25f, 310.746f, 271.25f, 310.742f, 275.656f, 313.355f, 278.246f, 311.75f, 10, 2.2f, 0.65f, 0.15f, 0.3f, 0.65f, 0.15f, 0.3f, 2, 279.246f, 328.746f, 279.242f, 328.742f, 282.039f, 334.148f, 287.246f, 334.75f, 10, 0, 0.7f, 0.7f, 0.7f, 0.7f, 0.7f, 0.7f, 6, 191.246f, 288.746f, 191.242f, 288.742f, 211.418f, 284.758f, 216.25f, 286.746f, 216.25f, 286.742f, 225.938f, 286.516f, 216.25f, 284.746f, 216.25f, 284.742f, 202.617f, 284.316f, 194.25f, 285.75f, 194.25f, 285.75f, 181.059f, 291.797f, 191.246f, 288.746f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 7, 207.25f, 390.746f, 207.25f, 390.746f, 226.379f, 390.797f, 228.25f, 389.75f, 228.25f, 389.75f, 236.5f, 356.035f, 232.246f, 347.75f, 232.246f, 347.75f, 231.219f, 344.598f, 228.25f, 350.746f, 228.25f, 350.742f, 207.898f, 386.836f, 204.25f, 388.75f, 200.859f, 391.238f, 205.699f, 390.797f, 207.25f, 390.746f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 7, 207.25f, 390.746f, 207.25f, 390.746f, 226.379f, 390.797f, 228.25f, 389.75f, 228.25f, 389.75f, 236.5f, 356.035f, 232.246f, 347.75f, 232.246f, 347.75f, 231.219f, 344.598f, 228.25f, 350.746f, 228.25f, 350.742f, 207.898f, 386.836f, 204.25f, 388.75f, 200.859f, 391.238f, 205.699f, 390.797f, 207.25f, 390.746f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 7, 122.246f, 393.75f, 122.246f, 393.75f, 132, 391.898f, 146.25f, 388.75f, 146.25f, 388.75f, 151.137f, 364.398f, 154.246f, 358.75f, 158.18f, 353.836f, 154.219f, 353.836f, 150.246f, 356.75f, 146.297f, 359.996f, 130.02f, 375.398f, 128.25f, 379.746f, 125.617f, 385.078f, 122.246f, 393.75f, 122.246f, 393.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 7, 122.246f, 393.75f, 122.246f, 393.75f, 132, 391.898f, 146.25f, 388.75f, 146.25f, 388.75f, 151.137f, 364.398f, 154.246f, 358.75f, 158.18f, 353.836f, 154.219f, 353.836f, 150.246f, 356.75f, 146.297f, 359.996f, 130.02f, 375.398f, 128.25f, 379.746f, 125.617f, 385.078f, 122.246f, 393.75f, 122.246f, 393.75f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 6, 146.25f, 388.75f, 146.25f, 388.75f, 152.637f, 387.094f, 153.246f, 384.75f, 154.855f, 382.223f, 152.25f, 378.75f, 152.25f, 378.75f, 152.25f, 378.75f, 151.324f, 374.961f, 150.246f, 377.75f, 148.68f, 379.719f, 145.52f, 388.145f, 146.25f, 388.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 6, 146.25f, 388.75f, 146.25f, 388.75f, 152.637f, 387.094f, 153.246f, 384.75f, 154.855f, 382.223f, 152.25f, 378.75f, 152.25f, 378.75f, 152.25f, 378.75f, 151.324f, 374.961f, 150.246f, 377.75f, 148.68f, 379.719f, 145.52f, 388.145f, 146.25f, 388.75f, 10, 0, 0, 0, 0, 0, 0, 0, 10, 146.25f, 388.75f, 146.25f, 388.75f, 150.258f, 383.316f, 154.246f, 383.746f, 158.18f, 383.316f, 158.598f, 383.77f, 161.246f, 382.75f, 166.758f, 381.996f, 166.316f, 384.195f, 173.25f, 382.75f, 176.48f, 382.348f, 179.297f, 383.316f, 182.25f, 381.746f, 185.457f, 380.676f, 188.977f, 381.559f, 190.246f, 383.746f, 191.617f, 385.957f, 197.25f, 390.746f, 197.25f, 390.746f, 197.25f, 390.746f, 182.816f, 388.598f, 179.246f, 387.746f, 179.246f, 387.742f, 155.098f, 386.398f, 146.25f, 388.75f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 6, 195.25f, 388.75f, 195.25f, 388.75f, 188.262f, 384.969f, 188.246f, 382.75f, 187.383f, 379.688f, 193.25f, 375.75f, 193.25f, 375.75f, 193.25f, 375.75f, 196.625f, 370.559f, 197.25f, 372.746f, 197.941f, 375.836f, 196.238f, 388.379f, 195.25f, 388.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 6, 195.25f, 388.75f, 195.25f, 388.75f, 188.262f, 384.969f, 188.246f, 382.75f, 187.383f, 379.688f, 193.25f, 375.75f, 193.25f, 375.75f, 193.25f, 375.75f, 196.625f, 370.559f, 197.25f, 372.746f, 197.941f, 375.836f, 196.238f, 388.379f, 195.25f, 388.75f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 5, 154.246f, 382.75f, 154.242f, 382.75f, 161.832f, 370.5f, 162.25f, 382.75f, 162.25f, 382.75f, 162.684f, 384.215f, 160.246f, 383.746f, 154.066f, 384.324f, 155.738f, 388.836f, 154.246f, 382.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 5, 154.246f, 382.75f, 154.242f, 382.75f, 161.832f, 370.5f, 162.25f, 382.75f, 162.25f, 382.75f, 162.684f, 384.215f, 160.246f, 383.746f, 154.066f, 384.324f, 155.738f, 388.836f, 154.246f, 382.75f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 5, 162.25f, 382.75f, 162.25f, 382.75f, 170.734f, 370.227f, 170.246f, 382.75f, 170.242f, 382.75f, 170.043f, 383, 168.25f, 382.75f, 162.891f, 383.625f, 163.27f, 388.594f, 162.25f, 382.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 5, 162.25f, 382.75f, 162.25f, 382.75f, 170.734f, 370.227f, 170.246f, 382.75f, 170.242f, 382.75f, 170.043f, 383, 168.25f, 382.75f, 162.891f, 383.625f, 163.27f, 388.594f, 162.25f, 382.75f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 5, 170.246f, 382.75f, 170.242f, 382.75f, 178.711f, 370.832f, 178.246f, 381.746f, 178.246f, 381.746f, 178.105f, 382.82f, 176.246f, 382.75f, 172.004f, 383.93f, 171.773f, 387.504f, 170.246f, 382.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 5, 170.246f, 382.75f, 170.242f, 382.75f, 178.711f, 370.832f, 178.246f, 381.746f, 178.246f, 381.746f, 178.105f, 382.82f, 176.246f, 382.75f, 172.004f, 383.93f, 171.773f, 387.504f, 170.246f, 382.75f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 5, 177.246f, 382.75f, 177.246f, 382.75f, 186.207f, 369.719f, 186.25f, 380.75f, 186.25f, 380.75f, 188.398f, 381.992f, 186.25f, 381.746f, 180.078f, 383.051f, 180.957f, 387.953f, 177.246f, 382.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 5, 177.246f, 382.75f, 177.246f, 382.75f, 186.207f, 369.719f, 186.25f, 380.75f, 186.25f, 380.75f, 188.398f, 381.992f, 186.25f, 381.746f, 180.078f, 383.051f, 180.957f, 387.953f, 177.246f, 382.75f, 10, 0, 0.9f, 0.9f, 0.7f, 0.9f, 0.9f, 0.7f, 6, 137.246f, 378.75f, 129.25f, 379.746f, 126.441f, 385.738f, 124.25f, 392.746f, 124.25f, 392.746f, 124.25f, 392.746f, 131.117f, 391.402f, 145.25f, 388.75f, 145.25f, 388.75f, 145.832f, 384.672f, 147.25f, 378.75f, 137.246f, 378.75f, 10, 0, 0.9f, 0.9f, 0.7f, 0.9f, 0.9f, 0.7f, 7, 209.246f, 383.746f, 207.469f, 386.437f, 206.02f, 388.371f, 205.25f, 388.75f, 201.992f, 390.891f, 206.547f, 390.477f, 208.25f, 390.746f, 208.25f, 390.746f, 226.02f, 390.477f, 228.25f, 389.75f, 228.25f, 389.75f, 228.668f, 387.18f, 229.25f, 383.746f, 229.25f, 383.742f, 218.32f, 385.66f, 209.246f, 383.746f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 10, 268.246f, 535.746f, 298.758f, 531.289f, 326.832f, 570.492f, 329.25f, 580.75f, 330.703f, 591.789f, 319.25f, 604.75f, 319.25f, 604.75f, 321.023f, 608.246f, 315.699f, 623.734f, 310.246f, 633.75f, 304.082f, 644.063f, 286.594f, 642.992f, 267.246f, 643.75f, 249.875f, 645.031f, 229.547f, 619.379f, 228.25f, 617.75f, 226.641f, 615.508f, 233.418f, 573.398f, 235.246f, 566.75f, 236.32f, 560.813f, 233.246f, 531.75f, 233.246f, 531.75f, 271.082f, 541.781f, 237.773f, 540, 268.246f, 535.746f, 10, 0, 0.92f, 0.56f, 0.32f, 0.92f, 0.56f, 0.32f, 10, 229.25f, 616.746f, 227.469f, 614.828f, 234.121f, 573.484f, 235.246f, 567.75f, 236.973f, 561.129f, 234.246f, 532.746f, 234.246f, 532.746f, 270.063f, 542.387f, 238.398f, 540.695f, 268.246f, 536.75f, 298.273f, 532.141f, 325.836f, 570.633f, 327.25f, 580.75f, 329.637f, 591.543f, 318.25f, 604.75f, 318.25f, 604.75f, 320.133f, 607.699f, 314.906f, 622.906f, 309.246f, 632.75f, 303.504f, 642.867f, 286.332f, 641.813f, 267.246f, 642.746f, 250.277f, 643.816f, 230.32f, 618.629f, 229.25f, 616.746f, 10, 0, 0.94f, 0.67f, 0.49f, 0.94f, 0.67f, 0.49f, 10, 229.25f, 615.75f, 228.297f, 614.152f, 234.824f, 573.574f, 236.25f, 567.75f, 237.625f, 561.445f, 235.246f, 533.746f, 235.246f, 533.746f, 269.371f, 543.539f, 239.023f, 541.391f, 268.246f, 536.75f, 297.793f, 532.996f, 324.844f, 570.773f, 326.25f, 580.75f, 328.574f, 591.297f, 318.25f, 603.746f, 318.25f, 603.746f, 319.246f, 607.156f, 314.113f, 622.078f, 308.246f, 631.746f, 302.922f, 641.668f, 286.066f, 640.637f, 267.246f, 641.75f, 250.684f, 642.602f, 231.094f, 617.883f, 229.25f, 615.75f, 10, 0, 0.96f, 0.78f, 0.66f, 0.96f, 0.78f, 0.66f, 10, 230.25f, 615.75f, 229.125f, 613.473f, 235.531f, 573.66f, 237.25f, 567.75f, 238.277f, 561.762f, 235.246f, 534.75f, 235.246f, 534.75f, 267.91f, 544.25f, 239.648f, 542.086f, 268.246f, 537.746f, 297.309f, 533.852f, 323.848f, 570.914f, 325.25f, 580.75f, 327.508f, 591.051f, 317.25f, 603.746f, 317.25f, 603.746f, 318.355f, 606.609f, 313.324f, 621.254f, 308.246f, 630.75f, 302.34f, 640.473f, 285.805f, 639.457f, 267.246f, 640.746f, 251.09f, 641.387f, 231.871f, 617.133f, 230.25f, 615.75f, 10, 0, 0.98f, 0.89f, 0.83f, 0.98f, 0.89f, 0.83f, 10, 231.246f, 614.746f, 229.949f, 612.797f, 236.234f, 573.75f, 237.25f, 567.75f, 238.926f, 562.082f, 236.25f, 534.75f, 236.25f, 534.75f, 266.891f, 544.965f, 240.273f, 542.781f, 268.246f, 538.75f, 296.824f, 534.703f, 322.855f, 571.055f, 324.246f, 580.75f, 326.445f, 590.805f, 316.25f, 602.75f, 316.25f, 602.75f, 317.469f, 606.063f, 312.531f, 620.426f, 307.25f, 629.746f, 301.762f, 639.273f, 285.543f, 638.281f, 267.246f, 639.75f, 251.492f, 640.172f, 232.645f, 616.387f, 231.246f, 614.746f, 10, 0, 1, 1, 1, 1, 1, 1, 10, 268.246f, 539.746f, 296.34f, 535.559f, 321.859f, 571.199f, 323.246f, 580.75f, 325.379f, 590.559f, 315.25f, 602.75f, 315.25f, 602.75f, 316.578f, 605.52f, 311.738f, 619.598f, 306.25f, 628.75f, 301.18f, 638.078f, 285.277f, 637.102f, 267.246f, 637.75f, 251.898f, 638.957f, 233.418f, 615.637f, 232.246f, 613.75f, 230.777f, 612.117f, 236.938f, 573.84f, 238.25f, 567.75f, 239.578f, 562.398f, 237.25f, 535.746f, 237.25f, 535.746f, 264.988f, 545.457f, 240.898f, 543.477f, 268.246f, 539.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 6, 319.25f, 576.746f, 319.25f, 576.742f, 289.078f, 568.559f, 276.246f, 570.746f, 276.246f, 570.746f, 258.937f, 577.578f, 249.25f, 553.75f, 249.25f, 553.75f, 245.297f, 545.68f, 243.246f, 543.746f, 240.898f, 541.277f, 319.25f, 576.746f, 319.25f, 576.746f, 10, 0, 0, 0, 0, 0, 0, 0, 11, 324.246f, 579.746f, 324.242f, 579.746f, 291.937f, 565.918f, 281.25f, 566.75f, 281.25f, 566.75f, 262.898f, 571.418f, 253.246f, 555.75f, 253.246f, 555.75f, 244.418f, 545.238f, 241.25f, 543.746f, 241.25f, 543.742f, 240.457f, 541.719f, 247.246f, 545.75f, 259.25f, 540.75f, 259.25f, 540.75f, 275.219f, 529.84f, 286.246f, 547.75f, 286.246f, 547.75f, 290.18f, 559.758f, 290.246f, 561.746f, 290.18f, 564.156f, 313.5f, 570.316f, 315.25f, 570.746f, 317.02f, 571.199f, 324.277f, 575.816f, 324.246f, 579.746f, 10, 0, 0.6f, 0.8f, 0.2f, 0.6f, 0.8f, 0.2f, 6, 271.25f, 539.746f, 264.141f, 539.832f, 254.93f, 544.086f, 255.246f, 550.746f, 254.93f, 557.832f, 264.141f, 564.723f, 271.25f, 564.75f, 279.258f, 564.723f, 285.387f, 559.152f, 285.25f, 552.746f, 285.387f, 545.402f, 279.258f, 539.832f, 271.25f, 539.746f, 10, 0, 0.4f, 0.6f, 0, 0.4f, 0.6f, 0, 6, 267.246f, 557.746f, 262.383f, 557.391f, 256.785f, 555.738f, 257.246f, 555.75f, 258.559f, 561.055f, 265.555f, 564.723f, 271.25f, 564.75f, 276.422f, 564.723f, 280.59f, 562.547f, 283.25f, 558.75f, 283.25f, 558.75f, 277.203f, 559.598f, 267.246f, 557.746f, 10, 0, 1, 1, 1, 1, 1, 1, 4, 281.25f, 558.75f, 281.25f, 558.75f, 276.098f, 561.957f, 276.246f, 559.746f, 276.246f, 559.746f, 280.059f, 554.699f, 281.25f, 558.75f, 10, 0, 0, 0, 0, 0, 0, 0, 6, 270.25f, 549.75f, 267.187f, 549.5f, 264.961f, 551.727f, 265.246f, 554.746f, 264.961f, 557.227f, 267.187f, 559.457f, 270.25f, 559.746f, 272.687f, 559.457f, 274.918f, 557.227f, 275.246f, 554.746f, 274.918f, 551.727f, 272.687f, 549.5f, 270.25f, 549.75f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 15, 155.246f, 563.746f, 155.246f, 563.742f, 152.02f, 587.477f, 154.246f, 592.746f, 154.242f, 592.746f, 166.539f, 603.316f, 166.246f, 607.746f, 166.246f, 607.742f, 165.656f, 627.078f, 164.246f, 627.746f, 163.02f, 628.84f, 154.656f, 635.438f, 148.246f, 628.75f, 148.242f, 628.75f, 136.617f, 608.598f, 137.246f, 601.746f, 137.246f, 599.75f, 137.242f, 599.75f, 129.137f, 599.797f, 127.246f, 597.75f, 127.246f, 597.75f, 126.059f, 591.879f, 124.25f, 591.75f, 124.25f, 591.75f, 121.656f, 588.797f, 124.25f, 585.746f, 124.25f, 585.742f, 121.656f, 583.078f, 122.246f, 578.75f, 130.25f, 574.746f, 130.25f, 574.742f, 132.656f, 558.438f, 144.246f, 552.746f, 149.859f, 550.156f, 153.34f, 557.559f, 155.246f, 563.746f, 10, 0, 1, 1, 1, 1, 1, 1, 15, 154.246f, 565.746f, 154.242f, 565.742f, 151.27f, 587.172f, 153.246f, 591.75f, 153.242f, 591.75f, 164.34f, 601.426f, 164.246f, 604.75f, 164.242f, 604.75f, 163.547f, 622.809f, 162.25f, 623.746f, 161.172f, 624.395f, 153.645f, 630.336f, 147.25f, 623.746f, 147.25f, 623.746f, 137.41f, 606.18f, 138.246f, 599.75f, 138.246f, 597.75f, 138.246f, 597.75f, 130.68f, 598.258f, 129.25f, 596.746f, 129.25f, 596.742f, 127.906f, 591.129f, 126.246f, 590.746f, 126.242f, 590.746f, 123.945f, 588.359f, 126.246f, 585.746f, 126.242f, 585.742f, 123.945f, 583.211f, 124.25f, 579.746f, 132.246f, 575.75f, 132.246f, 575.75f, 133.848f, 561.035f, 144.246f, 555.75f, 149.324f, 553.582f, 152.457f, 560.242f, 154.246f, 565.746f, 10, 0, 0.925f, 0.588f, 0.363f, 0.925f, 0.588f, 0.363f, 15, 164.246f, 626.75f, 162.645f, 627.816f, 154.406f, 634.16f, 148.246f, 627.746f, 148.242f, 627.742f, 136.816f, 607.992f, 137.246f, 600.75f, 137.246f, 598.746f, 137.242f, 598.742f, 129.523f, 599.414f, 128.25f, 597.75f, 128.25f, 597.75f, 126.52f, 591.691f, 125.25f, 591.75f, 125.25f, 591.75f, 122.23f, 588.688f, 124.25f, 585.746f, 124.25f, 585.742f, 122.23f, 583.109f, 122.246f, 578.75f, 131.246f, 574.746f, 131.242f, 574.742f, 132.953f, 559.086f, 144.246f, 553.75f, 149.723f, 551.012f, 153.117f, 558.23f, 155.246f, 564.75f, 155.246f, 564.75f, 151.832f, 587.402f, 154.246f, 591.75f, 154.242f, 591.75f, 165.988f, 602.844f, 165.246f, 606.75f, 165.242f, 606.75f, 165.129f, 626.012f, 164.246f, 626.75f, 10, 0, 0.95f, 0.725f, 0.575f, 0.95f, 0.725f, 0.575f, 15, 163.25f, 625.746f, 162.27f, 626.793f, 154.152f, 632.887f, 148.246f, 625.746f, 148.242f, 625.746f, 137.016f, 607.387f, 138.246f, 600.75f, 138.246f, 598.746f, 138.246f, 598.742f, 129.906f, 599.027f, 128.25f, 596.746f, 128.25f, 596.742f, 126.98f, 591.504f, 125.25f, 590.746f, 125.25f, 590.746f, 122.801f, 588.578f, 125.25f, 585.746f, 125.25f, 585.742f, 122.801f, 583.145f, 123.25f, 578.75f, 131.246f, 574.746f, 131.242f, 574.742f, 133.254f, 559.734f, 144.246f, 554.746f, 149.59f, 551.867f, 152.898f, 558.898f, 155.246f, 564.75f, 155.246f, 564.75f, 151.645f, 587.324f, 154.246f, 591.75f, 154.242f, 591.75f, 165.438f, 602.371f, 165.246f, 605.746f, 165.242f, 605.742f, 164.602f, 624.945f, 163.25f, 625.746f, 10, 0, 0.975f, 0.863f, 0.788f, 0.975f, 0.863f, 0.788f, 15, 163.25f, 624.75f, 161.895f, 625.77f, 153.898f, 631.609f, 148.246f, 624.75f, 148.242f, 624.75f, 137.211f, 606.781f, 138.246f, 600.75f, 138.246f, 597.75f, 138.246f, 597.75f, 130.293f, 598.645f, 128.25f, 596.746f, 128.25f, 596.742f, 127.445f, 591.316f, 126.246f, 590.746f, 126.242f, 590.746f, 123.375f, 588.469f, 125.25f, 585.746f, 125.25f, 585.742f, 123.375f, 583.176f, 124.25f, 578.75f, 131.246f, 574.746f, 131.242f, 574.742f, 133.551f, 560.387f, 144.246f, 554.746f, 149.457f, 552.727f, 152.68f, 559.57f, 154.246f, 565.746f, 154.242f, 565.742f, 151.457f, 587.246f, 154.246f, 591.75f, 154.242f, 591.75f, 164.887f, 601.898f, 164.246f, 605.746f, 164.242f, 605.742f, 164.074f, 623.879f, 163.25f, 624.75f, 10, 0, 1, 1, 1, 1, 1, 1, 15, 154.246f, 566.75f, 154.242f, 566.75f, 151.27f, 587.172f, 153.246f, 591.75f, 153.242f, 591.75f, 164.34f, 601.426f, 164.246f, 604.75f, 164.242f, 604.75f, 163.547f, 622.809f, 162.25f, 623.746f, 161.523f, 624.746f, 153.645f, 630.336f, 147.25f, 623.746f, 147.25f, 623.746f, 137.41f, 606.18f, 138.246f, 599.75f, 138.246f, 597.75f, 138.246f, 597.75f, 130.68f, 598.258f, 129.25f, 596.746f, 129.25f, 596.742f, 127.906f, 591.129f, 126.246f, 590.746f, 126.242f, 590.746f, 123.945f, 588.359f, 126.246f, 585.746f, 126.242f, 585.742f, 123.945f, 583.211f, 124.25f, 579.746f, 132.246f, 575.75f, 132.246f, 575.75f, 133.848f, 561.035f, 144.246f, 555.75f, 149.324f, 553.582f, 152.457f, 560.352f, 154.246f, 566.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 151.25f, 572.746f, 151.25f, 572.742f, 127.27f, 584.398f, 126.246f, 585.746f, 126.242f, 585.742f, 136.289f, 576.258f, 137.246f, 576.746f, 138.047f, 576.258f, 151.25f, 572.746f, 151.25f, 572.746f, 10, 0, 0, 0, 0, 0, 0, 0, 5, 132.246f, 579.746f, 132.246f, 579.746f, 152.457f, 576.039f, 152.25f, 570.746f, 152.457f, 567.996f, 152.191f, 553.234f, 146.25f, 554.746f, 137.059f, 557.559f, 141.02f, 573.398f, 132.246f, 579.746f, 10, 0, 0.6f, 0.8f, 0.2f, 0.6f, 0.8f, 0.2f, 5, 141.25f, 575.75f, 141.25f, 575.75f, 151.332f, 574.195f, 152.25f, 570.746f, 153.117f, 569.438f, 153.848f, 560.301f, 148.246f, 558.75f, 142.832f, 558.098f, 140.379f, 569.34f, 141.25f, 575.75f, 10, 0, 0, 0, 0, 0, 0, 0, 44, 236.25f, 528.746f, 235.504f, 530.93f, 236.949f, 530.785f, 239.25f, 531.75f, 241.117f, 532.039f, 254.539f, 536.219f, 255.246f, 538.75f, 256.297f, 541.938f, 271.25f, 536.75f, 271.25f, 536.75f, 272.797f, 536.219f, 277.246f, 533.746f, 277.246f, 533.746f, 282.918f, 532.039f, 290.246f, 531.75f, 290.246f, 531.75f, 292.816f, 530.5f, 296.25f, 527.75f, 296.25f, 527.75f, 312.617f, 516.199f, 326.25f, 523.75f, 326.25f, 523.75f, 348.258f, 531.379f, 341.25f, 550.746f, 341.25f, 550.746f, 338.359f, 560.199f, 342.246f, 563.746f, 342.246f, 563.746f, 342.098f, 568.117f, 350.25f, 560.75f, 350.25f, 560.75f, 352.879f, 556.457f, 354.246f, 550.746f, 354.246f, 550.746f, 362.559f, 538.637f, 359.25f, 557.746f, 359.25f, 557.746f, 359.039f, 559.316f, 355.961f, 563.277f, 356.246f, 564.75f, 355.961f, 565.918f, 354.246f, 569.75f, 354.246f, 569.75f, 350.68f, 573.398f, 353.246f, 580.75f, 353.246f, 580.75f, 355.301f, 596.277f, 353.246f, 594.746f, 353.246f, 594.746f, 351.559f, 596.277f, 341.25f, 585.746f, 341.25f, 585.746f, 339.02f, 581.539f, 332.246f, 579.746f, 332.246f, 579.746f, 329.34f, 577.797f, 325.25f, 579.746f, 325.25f, 579.746f, 322.738f, 579.777f, 316.25f, 571.75f, 316.25f, 571.75f, 319.66f, 572.297f, 322.301f, 567.457f, 325.25f, 566.75f, 327.578f, 567.02f, 329.559f, 569.879f, 331.246f, 570.746f, 333.078f, 571.199f, 336.25f, 564.75f, 336.25f, 564.75f, 336.598f, 561.957f, 330.25f, 556.75f, 330.25f, 556.75f, 330, 551.617f, 328.25f, 553.75f, 328.25f, 553.75f, 324.938f, 554.039f, 323.621f, 549.859f, 322.246f, 544.746f, 321.418f, 539.738f, 317.25f, 539.746f, 317.25f, 539.746f, 315.039f, 531.156f, 313.246f, 534.75f, 313.246f, 534.75f, 313.5f, 540.617f, 307.25f, 533.746f, 307.25f, 533.746f, 305.578f, 532.039f, 300.246f, 534.75f, 300.246f, 534.75f, 293.039f, 536.656f, 295.25f, 538.75f, 295.25f, 538.75f, 297.656f, 541.277f, 310.246f, 538.75f, 310.246f, 538.75f, 312.398f, 540.617f, 303.25f, 544.746f, 303.25f, 544.746f, 302.937f, 547, 304.25f, 551.75f, 304.25f, 551.75f, 305.359f, 555.359f, 313.246f, 561.746f, 313.246f, 561.746f, 323.18f, 562.84f, 320.246f, 564.75f, 320.246f, 564.75f, 313.277f, 570.316f, 307.25f, 561.746f, 307.25f, 561.746f, 304.477f, 555.137f, 285.25f, 538.75f, 285.25f, 538.75f, 280.059f, 534.898f, 282.918f, 542.379f, 278.246f, 538.75f, 274.117f, 534.898f, 251.25f, 544.746f, 251.25f, 544.746f, 238.738f, 546.109f, 235.734f, 528.793f, 232.246f, 531.75f, 232.246f, 531.75f, 237.813f, 522.855f, 236.25f, 528.746f, 10, 0, 0, 0, 0, 0, 0, 0, 12, 450.25f, 711.746f, 450.25f, 711.746f, 422.18f, 703.199f, 419.246f, 682.746f, 419.246f, 682.742f, 416.461f, 657.438f, 439.25f, 637.75f, 439.25f, 637.75f, 439.34f, 631.039f, 441.246f, 627.746f, 441.246f, 627.742f, 439.777f, 622.238f, 460.246f, 630.75f, 490.25f, 639.75f, 490.25f, 639.75f, 497.418f, 642.477f, 503.25f, 651.746f, 508.859f, 661.84f, 525.578f, 682.52f, 521.25f, 709.75f, 521.25f, 709.75f, 522.938f, 722.559f, 516.25f, 722.746f, 516.25f, 722.746f, 507.098f, 724.758f, 499.25f, 716.75f, 499.25f, 716.75f, 491.699f, 712.879f, 489.246f, 713.746f, 450.25f, 711.746f, 10, 0, 0, 0, 0, 0, 0, 0, 8, 510.25f, 712.75f, 510.25f, 712.75f, 512.73f, 722.91f, 507.25f, 717.746f, 507.25f, 717.742f, 499.664f, 711.293f, 491.246f, 711.746f, 491.242f, 711.746f, 475.465f, 708.875f, 470.25f, 694.75f, 470.25f, 694.75f, 466.266f, 664.828f, 475.25f, 658.746f, 475.25f, 658.746f, 480.305f, 650.309f, 488.25f, 657.75f, 495.793f, 664.828f, 512.844f, 698.082f, 510.25f, 712.75f, 10, 0, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 8, 510.25f, 712.75f, 510.25f, 712.75f, 512.309f, 722.313f, 507.25f, 716.75f, 507.25f, 716.75f, 499.48f, 710.906f, 491.246f, 710.746f, 491.242f, 710.742f, 475.719f, 708.531f, 471.246f, 694.75f, 471.242f, 694.75f, 466.691f, 665.289f, 475.25f, 658.746f, 475.25f, 658.746f, 480.469f, 651.031f, 488.25f, 657.75f, 495.68f, 665.289f, 512.387f, 697.961f, 510.25f, 712.75f, 10, 0, 0.4f, 0.4f, 0.4f, 0.4f, 0.4f, 0.4f, 8, 509.246f, 712.75f, 509.246f, 712.75f, 511.887f, 721.715f, 507.25f, 716.75f, 507.25f, 716.75f, 499.293f, 710.52f, 491.246f, 710.746f, 491.242f, 710.742f, 475.973f, 708.188f, 471.246f, 693.746f, 471.242f, 693.742f, 467.113f, 665.746f, 475.25f, 659.75f, 475.25f, 659.75f, 480.637f, 651.754f, 488.25f, 658.746f, 495.563f, 665.746f, 511.93f, 697.84f, 509.246f, 712.75f, 10, 0, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 8, 509.246f, 711.746f, 509.246f, 711.746f, 511.465f, 721.113f, 506.246f, 715.746f, 506.242f, 715.742f, 499.109f, 710.133f, 491.246f, 709.75f, 491.242f, 709.75f, 476.23f, 707.844f, 471.246f, 693.746f, 471.242f, 693.742f, 467.535f, 666.203f, 476.246f, 660.746f, 476.246f, 660.742f, 480.805f, 652.477f, 488.25f, 659.75f, 495.449f, 666.203f, 511.477f, 697.719f, 509.246f, 711.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 8, 509.246f, 711.746f, 509.246f, 711.746f, 511.043f, 720.516f, 506.246f, 715.746f, 506.242f, 715.742f, 498.926f, 709.746f, 491.246f, 709.75f, 491.242f, 709.75f, 476.484f, 707.5f, 472.25f, 693.746f, 472.25f, 693.742f, 467.957f, 666.66f, 476.246f, 660.746f, 476.246f, 660.742f, 480.973f, 653.195f, 488.25f, 659.75f, 495.332f, 666.66f, 511.02f, 697.598f, 509.246f, 711.746f, 10, 0, 1, 1, 1, 1, 1, 1, 8, 508.25f, 710.746f, 508.25f, 710.742f, 510.621f, 719.918f, 506.246f, 714.75f, 506.242f, 714.75f, 498.738f, 709.359f, 491.246f, 709.75f, 491.242f, 709.75f, 476.738f, 707.156f, 472.25f, 693.746f, 472.25f, 693.742f, 468.379f, 667.117f, 476.246f, 661.75f, 476.246f, 661.75f, 481.141f, 653.918f, 488.25f, 660.746f, 495.219f, 667.117f, 510.563f, 697.477f, 508.25f, 710.746f, 10, 0, 0.6f, 0.15f, 0, 0.6f, 0.15f, 0, 24, 275.246f, 487.75f, 275.246f, 487.75f, 253.219f, 508.719f, 244.246f, 509.75f, 244.246f, 509.75f, 206.578f, 514, 190.246f, 493.746f, 190.246f, 493.742f, 209.656f, 516.637f, 240.25f, 510.746f, 240.25f, 510.742f, 216.258f, 515.316f, 202.246f, 511.746f, 202.242f, 511.746f, 184.137f, 511.797f, 173.25f, 496.75f, 170.246f, 490.75f, 170.242f, 490.75f, 174.898f, 507.398f, 195.25f, 513.746f, 195.25f, 513.746f, 220.219f, 519.277f, 232.246f, 513.746f, 232.246f, 513.746f, 208.34f, 521.477f, 197.25f, 519.746f, 197.25f, 519.742f, 163.898f, 521.918f, 150.246f, 492.75f, 150.246f, 492.75f, 154.219f, 508.719f, 170.246f, 516.75f, 170.242f, 516.75f, 185.457f, 526.316f, 208.25f, 522.746f, 208.25f, 522.746f, 223.738f, 519.719f, 229.25f, 516.75f, 235.18f, 514.438f, 233.859f, 517.52f, 224.246f, 522.746f, 224.242f, 522.746f, 218.457f, 533.797f, 203.25f, 533.746f, 203.25f, 533.746f, 155.977f, 529.398f, 144.246f, 515.746f, 144.246f, 515.742f, 159.5f, 528.52f, 171.246f, 531.75f, 171.246f, 531.75f, 195.578f, 540.398f, 205.25f, 539.746f, 205.25f, 539.742f, 232.098f, 538.418f, 240.25f, 542.75f, 240.25f, 542.75f, 228.137f, 537.316f, 231.246f, 533.746f, 235.18f, 530.277f, 242.656f, 521.918f, 242.246f, 520.75f, 242.656f, 519.277f, 269.277f, 494.969f, 273.25f, 489.746f, 275.246f, 487.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 428.25f, 273.746f, 428.25f, 273.742f, 410.848f, 314.348f, 397.246f, 324.746f, 397.246f, 324.746f, 425.699f, 307.199f, 429.25f, 287.75f, 429.25f, 287.75f, 429.547f, 276.398f, 428.25f, 273.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 7, 479.25f, 265.75f, 479.25f, 265.75f, 450.449f, 326.449f, 430.246f, 352.746f, 430.246f, 352.742f, 477.949f, 311.598f, 483.25f, 282.746f, 484.246f, 276.75f, 480.246f, 278.75f, 480.242f, 278.75f, 480.148f, 269.25f, 479.25f, 265.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 542.246f, 319.746f, 542.246f, 319.742f, 473, 384.75f, 471.246f, 387.746f, 471.242f, 387.742f, 537.898f, 314.898f, 541.25f, 306.746f, 541.25f, 306.742f, 539, 316.547f, 542.246f, 319.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 6, 334.246f, 271.746f, 334.246f, 271.746f, 355.848f, 328.648f, 377.246f, 303.75f, 377.246f, 303.75f, 393.25f, 292.898f, 392.25f, 289.75f, 392.25f, 289.75f, 388.297f, 296.75f, 368.246f, 295.746f, 368.242f, 295.742f, 347.598f, 299.5f, 334.246f, 271.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 544.246f, 411.746f, 544.246f, 411.742f, 494.449f, 443.047f, 486.25f, 444.746f, 473.211f, 447.297f, 540.648f, 412.797f, 546.246f, 400.75f, 546.242f, 400.75f, 548.348f, 404, 544.246f, 411.746f, 10, 0, 0, 0, 0, 0, 0, 0, 63, 378.246f, 294.75f, 378.246f, 294.75f, 400.621f, 296.637f, 408.246f, 303.75f, 413.246f, 299.746f, 432.246f, 342.75f, 436.25f, 336.75f, 436.25f, 336.75f, 452.098f, 352.957f, 451.25f, 361.746f, 450.34f, 370.559f, 465.246f, 354.746f, 465.246f, 354.746f, 465.246f, 354.742f, 464.418f, 367.918f, 472.25f, 360.75f, 472.25f, 360.75f, 469.699f, 377.598f, 479.25f, 368.746f, 479.25f, 368.746f, 467.348f, 401.969f, 492.25f, 373.75f, 498.301f, 366.598f, 493.246f, 373.75f, 493.246f, 373.75f, 493.242f, 373.75f, 464.859f, 426.879f, 488.25f, 410.75f, 488.25f, 410.75f, 490.82f, 436.117f, 489.246f, 440.746f, 488.18f, 445.797f, 485.98f, 470.438f, 480.246f, 475.746f, 475.418f, 481.879f, 481.141f, 483.637f, 487.246f, 477.746f, 487.246f, 477.742f, 474.98f, 504.316f, 489.246f, 490.75f, 489.246f, 490.75f, 485.539f, 507.84f, 480.246f, 510.746f, 480.242f, 510.742f, 474.539f, 529.84f, 491.246f, 517.746f, 491.242f, 517.742f, 486.418f, 531.598f, 483.25f, 534.75f, 483.25f, 534.75f, 470.141f, 565.477f, 478.246f, 559.746f, 483.25f, 555.75f, 483.25f, 555.75f, 475.418f, 571.637f, 482.246f, 566.75f, 489.5f, 561.957f, 489.246f, 562.75f, 489.246f, 562.75f, 489.246f, 562.75f, 466.18f, 598.918f, 488.25f, 579.746f, 488.25f, 579.746f, 479.645f, 594.867f, 476.246f, 602.75f, 476.246f, 602.75f, 455.18f, 624.879f, 471.246f, 617.75f, 476.246f, 615.75f, 476.246f, 615.75f, 466.621f, 627.078f, 458.246f, 628.75f, 449.02f, 630.598f, 460.461f, 637.637f, 467.246f, 635.75f, 474.539f, 633.238f, 491.246f, 624.75f, 491.246f, 624.75f, 491.242f, 624.75f, 505.777f, 604.199f, 510.25f, 603.746f, 510.25f, 603.746f, 488.18f, 612.117f, 495.246f, 603.746f, 495.242f, 603.746f, 510.621f, 587.918f, 502.246f, 588.75f, 502.242f, 588.75f, 496.098f, 580.438f, 501.25f, 570.746f, 501.25f, 570.746f, 481.074f, 590.988f, 497.25f, 562.75f, 505.25f, 544.746f, 505.25f, 544.746f, 478.059f, 572.078f, 490.25f, 547.75f, 490.25f, 547.75f, 509.301f, 521.918f, 511.246f, 521.746f, 513.699f, 521.039f, 518.25f, 511.746f, 518.25f, 511.746f, 513.246f, 513.746f, 519.25f, 503.75f, 519.25f, 503.75f, 507.098f, 517.078f, 513.246f, 502.746f, 520.246f, 486.746f, 520.246f, 486.742f, 497.418f, 510.918f, 512.25f, 478.746f, 512.25f, 478.746f, 494.34f, 484.078f, 504.246f, 464.746f, 504.242f, 464.742f, 502.258f, 447.559f, 502.246f, 441.75f, 503.141f, 436.117f, 504.461f, 404.879f, 499.25f, 395.75f, 494.777f, 387.277f, 506.219f, 366.156f, 508.25f, 361.746f, 510.621f, 357.355f, 514.578f, 345.477f, 505.25f, 355.75f, 495.219f, 365.719f, 500.059f, 359.559f, 502.246f, 349.75f, 504.461f, 340.195f, 511.059f, 323.035f, 510.25f, 316.75f, 510.25f, 316.75f, 508.859f, 315.559f, 505.25f, 319.746f, 505.25f, 319.742f, 489.059f, 344.598f, 491.246f, 328.746f, 491.242f, 328.742f, 489.5f, 319.957f, 486.25f, 310.746f, 486.25f, 310.742f, 482.461f, 298.398f, 482.246f, 307.75f, 482.242f, 307.75f, 478.938f, 326.559f, 476.246f, 317.746f, 472.777f, 309.836f, 468.82f, 303.238f, 465.246f, 300.75f, 462.66f, 297.957f, 456.938f, 323.035f, 455.25f, 311.75f, 455.25f, 311.75f, 442.418f, 325.238f, 437.25f, 306.746f, 424.246f, 288.746f, 424.242f, 288.742f, 423.938f, 302.797f, 422.246f, 295.746f, 422.246f, 295.742f, 389.621f, 289.598f, 378.246f, 294.75f, 10, 0, 0, 0, 0, 0, 0, 0, 34, 340.25f, 686.746f, 340.25f, 686.742f, 327.578f, 695.719f, 323.246f, 695.746f, 318.777f, 694.84f, 353.539f, 704.957f, 399.246f, 674.75f, 399.246f, 674.75f, 404.141f, 671.52f, 408.246f, 671.746f, 408.246f, 671.742f, 411.621f, 669.316f, 408.246f, 665.75f, 408.246f, 665.75f, 398.859f, 654.797f, 411.246f, 642.746f, 411.246f, 642.742f, 431.418f, 635, 425.25f, 644.75f, 425.25f, 644.75f, 437.141f, 640.277f, 440.25f, 635.75f, 442.418f, 631.477f, 441.246f, 635.75f, 441.246f, 635.75f, 441.246f, 635.75f, 434.059f, 643.797f, 427.25f, 649.746f, 427.25f, 649.742f, 421.738f, 651.719f, 418.25f, 660.746f, 415.578f, 670.199f, 412.938f, 681.199f, 418.25f, 684.746f, 418.25f, 684.742f, 413.379f, 679.879f, 414.25f, 684.746f, 415.141f, 688.68f, 419.098f, 692.637f, 421.246f, 692.75f, 422.621f, 693.52f, 440.66f, 710.898f, 448.25f, 711.746f, 448.25f, 711.746f, 438.02f, 709.797f, 434.246f, 710.746f, 431.418f, 712, 402.16f, 724.539f, 395.25f, 725.75f, 395.25f, 725.75f, 377.078f, 733.117f, 390.246f, 730.746f, 390.242f, 730.742f, 429.66f, 726.738f, 449.25f, 711.746f, 449.25f, 711.746f, 441.758f, 721.457f, 421.246f, 728.746f, 421.246f, 728.742f, 397.098f, 743.02f, 358.25f, 737.746f, 358.25f, 737.742f, 338.801f, 734, 330.25f, 731.75f, 330.25f, 731.75f, 327.359f, 732.68f, 326.25f, 732.746f, 326.039f, 733.559f, 313.059f, 743.457f, 282.25f, 735.746f, 282.25f, 735.746f, 264, 730.699f, 254.246f, 725.75f, 254.246f, 725.75f, 237.816f, 724.098f, 234.246f, 720.75f, 234.246f, 720.75f, 213.398f, 704.52f, 211.246f, 703.75f, 209, 702.758f, 196.457f, 694.398f, 195.25f, 693.746f, 195.25f, 693.742f, 222.637f, 701.219f, 225.25f, 703.75f, 227.918f, 706.5f, 247.059f, 709.359f, 249.25f, 707.75f, 252.34f, 706.277f, 261.578f, 706.938f, 251.25f, 706.746f, 251.25f, 706.742f, 334.18f, 690, 335.246f, 687.75f, 335.938f, 685.598f, 340.25f, 686.746f, 340.25f, 686.746f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 13, 419.246f, 696.75f, 419.246f, 696.75f, 407.66f, 705.18f, 405.25f, 704.746f, 403.258f, 705.18f, 389.621f, 716.398f, 385.25f, 715.746f, 380.379f, 715.52f, 366.961f, 726.52f, 337.25f, 717.746f, 337.25f, 717.742f, 336.16f, 719.699f, 340.25f, 720.75f, 340.25f, 720.75f, 347.16f, 723, 347.25f, 723.75f, 347.25f, 723.75f, 369.82f, 728.277f, 377.246f, 724.746f, 377.246f, 724.746f, 387.859f, 721.457f, 394.25f, 714.75f, 394.25f, 714.75f, 407, 711.117f, 410.246f, 711.746f, 410.246f, 711.746f, 420.199f, 709.797f, 420.246f, 707.75f, 420.246f, 707.75f, 427.02f, 704.52f, 425.25f, 701.75f, 425.25f, 701.75f, 425.48f, 699.898f, 419.246f, 696.75f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 38, 405.25f, 699.746f, 406.047f, 698.664f, 407.168f, 698.555f, 408.246f, 697.746f, 408.094f, 697.32f, 407.773f, 696.961f, 407.25f, 696.75f, 406.281f, 696.504f, 405.121f, 697.133f, 404.25f, 696.75f, 403.422f, 696.258f, 402.715f, 696.457f, 402.246f, 696.75f, 400.313f, 697.105f, 398.301f, 697.137f, 396.25f, 696.75f, 394.254f, 697.621f, 391.66f, 696.977f, 389.246f, 697.746f, 389.309f, 698.109f, 389.063f, 697.727f, 389.246f, 697.746f, 385.629f, 699.016f, 381.512f, 698.707f, 379.246f, 700.746f, 376.168f, 701.672f, 373.574f, 702.18f, 371.25f, 702.746f, 368.906f, 703.488f, 367.355f, 704.574f, 365.246f, 705.75f, 364.059f, 706.27f, 362.457f, 706.844f, 361.25f, 707.75f, 358.719f, 707.75f, 356.703f, 707.625f, 354.246f, 707.75f, 354.52f, 708.227f, 354.309f, 707.848f, 354.246f, 707.75f, 353.863f, 707.996f, 353.543f, 708.637f, 353.246f, 708.746f, 351.508f, 708.004f, 349.871f, 709.074f, 348.25f, 708.746f, 346.742f, 710.043f, 344.844f, 709.773f, 343.246f, 710.746f, 339.883f, 711.195f, 336.414f, 709.797f, 333.246f, 710.746f, 337.602f, 712.926f, 342.758f, 711.57f, 347.25f, 713.746f, 349.789f, 715.148f, 352.715f, 713.938f, 355.246f, 714.75f, 356.078f, 714.934f, 356.84f, 715.152f, 357.246f, 714.75f, 357.426f, 714.566f, 357.625f, 714.828f, 357.246f, 714.75f, 360.387f, 713.527f, 362.934f, 712.125f, 365.246f, 710.746f, 366.039f, 710.793f, 366.621f, 711.047f, 367.246f, 710.746f, 368.57f, 709.488f, 370.711f, 709.602f, 372.25f, 708.746f, 374.105f, 708.809f, 376.078f, 708.391f, 378.246f, 708.746f, 378.066f, 709.109f, 378.324f, 708.734f, 378.246f, 708.746f, 379.602f, 709.582f, 380.875f, 709.281f, 382.25f, 708.746f, 382.227f, 708.82f, 382.957f, 708.551f, 383.25f, 708.746f, 384.531f, 708.164f, 385.473f, 707.637f, 387.246f, 707.75f, 386.895f, 707.414f, 387.098f, 707.789f, 387.246f, 707.75f, 388.41f, 707.277f, 389.559f, 707.34f, 390.246f, 705.75f, 390.426f, 706.207f, 390.609f, 706.469f, 390.246f, 706.746f, 391.828f, 706.066f, 392.543f, 705.238f, 394.25f, 704.746f, 394.289f, 704.855f, 394.961f, 704.168f, 395.25f, 703.75f, 398.227f, 703.168f, 400.254f, 701.488f, 402.246f, 700.746f, 403.5f, 700.16f, 404.465f, 699.902f, 405.25f, 699.746f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 23, 321.246f, 714.75f, 318.094f, 716.91f, 315.488f, 718.125f, 313.246f, 719.746f, 312.605f, 720.234f, 312.207f, 720.051f, 312.246f, 719.746f, 310.879f, 720.852f, 309.902f, 721.492f, 309.246f, 722.746f, 308.227f, 722.68f, 307.324f, 722.664f, 307.25f, 722.746f, 303.969f, 724.371f, 301.074f, 724.984f, 298.246f, 726.746f, 299.066f, 727, 300.301f, 726.73f, 301.246f, 727.75f, 301.172f, 727.309f, 301.434f, 726.996f, 302.246f, 726.746f, 303.668f, 728.203f, 305.703f, 728.371f, 307.25f, 728.746f, 309.422f, 728.172f, 311.313f, 727.836f, 313.246f, 727.75f, 313.605f, 727.484f, 313.824f, 726.91f, 314.25f, 726.746f, 316.629f, 726.074f, 319.258f, 726.648f, 321.246f, 725.75f, 323.336f, 725.035f, 325.066f, 724.133f, 326.25f, 722.746f, 326.703f, 722.441f, 326.348f, 722.113f, 326.25f, 721.746f, 326.465f, 722.02f, 326.766f, 721.793f, 327.25f, 721.746f, 326.98f, 721.184f, 326.98f, 720.852f, 327.25f, 720.75f, 326.766f, 720.246f, 326.457f, 720.133f, 326.25f, 719.746f, 324.5f, 719.871f, 326.449f, 721.387f, 325.25f, 720.75f, 324.277f, 720, 325.098f, 718.453f, 324.246f, 716.75f, 323.973f, 717.27f, 323.719f, 717.512f, 324.246f, 717.746f, 324.098f, 717.363f, 323.434f, 717.043f, 323.246f, 716.75f, 322.824f, 715.898f, 321.836f, 714.344f, 321.246f, 714.75f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 25, 285.25f, 706.746f, 281.645f, 707.801f, 278.293f, 707.676f, 275.246f, 708.746f, 274.898f, 709.109f, 274.656f, 708.727f, 274.25f, 708.746f, 273.082f, 709.41f, 272.07f, 710.473f, 271.25f, 711.746f, 269.883f, 712.602f, 268.059f, 712.176f, 266.246f, 712.75f, 266.301f, 712.848f, 266.078f, 713.41f, 266.246f, 713.746f, 264.406f, 713.625f, 263.387f, 714.672f, 262.25f, 715.746f, 264.809f, 716.172f, 267.461f, 716.137f, 270.25f, 716.75f, 270.293f, 716.582f, 270.453f, 716.227f, 270.25f, 715.746f, 270.746f, 716.227f, 270.891f, 716.469f, 271.25f, 716.75f, 271.254f, 716.309f, 271.586f, 715.953f, 272.25f, 715.746f, 272.469f, 716.824f, 273.082f, 716.617f, 273.25f, 716.75f, 273.836f, 716.563f, 273.973f, 716.227f, 274.25f, 715.746f, 274.27f, 716.227f, 274.41f, 716.57f, 274.25f, 716.75f, 274.707f, 716.57f, 274.852f, 716.227f, 275.246f, 715.746f, 275.148f, 716.227f, 275.289f, 716.469f, 275.246f, 716.75f, 276.199f, 715.758f, 277.172f, 716.367f, 278.246f, 715.746f, 279.219f, 715.922f, 279.512f, 714.656f, 280.246f, 714.75f, 285.879f, 712.895f, 290.43f, 710.535f, 295.25f, 707.75f, 295.566f, 708.078f, 295.801f, 707.805f, 295.25f, 707.75f, 295.973f, 707.379f, 296.316f, 707.477f, 296.25f, 707.75f, 297.688f, 706.523f, 298.832f, 705.922f, 299.246f, 704.746f, 299.84f, 704.34f, 299.477f, 703.895f, 299.246f, 703.75f, 294.348f, 705.047f, 289.941f, 705.715f, 285.25f, 706.746f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 10, 270.25f, 658.746f, 268.117f, 659.637f, 267.477f, 661.871f, 266.246f, 663.75f, 266, 664.215f, 266.301f, 664.563f, 266.246f, 664.746f, 267.266f, 664.832f, 267.863f, 664.309f, 268.246f, 663.75f, 270.234f, 663.137f, 271.922f, 661.77f, 274.25f, 661.75f, 276.309f, 659.16f, 280.992f, 658.738f, 281.25f, 654.75f, 281, 654.074f, 279.43f, 655.082f, 279.246f, 653.746f, 276.262f, 655.242f, 273.633f, 655.129f, 271.25f, 656.746f, 270.336f, 657.16f, 270.699f, 657.66f, 270.25f, 658.746f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 6, 239.25f, 715.746f, 239.727f, 716.129f, 247.461f, 715.871f, 247.246f, 715.746f, 247.391f, 715.406f, 238.891f, 714.254f, 238.25f, 714.75f, 238.309f, 714.523f, 230.047f, 711.852f, 230.25f, 711.746f, 230.191f, 712.148f, 239.285f, 716.129f, 239.25f, 715.746f, 10, 0, 0, 0, 0, 0, 0, 0, 80, 256.246f, 705.75f, 256.246f, 705.75f, 240.238f, 703.418f, 235.246f, 701.75f, 230.559f, 700.777f, 210.098f, 692.418f, 207.25f, 689.746f, 207.25f, 689.746f, 192.059f, 684.277f, 173.25f, 662.746f, 173.25f, 662.742f, 181.719f, 666.02f, 184.25f, 668.75f, 184.25f, 668.75f, 199.098f, 682.957f, 199.246f, 679.75f, 199.246f, 679.75f, 212.297f, 689.559f, 211.246f, 686.746f, 211.246f, 686.742f, 238.477f, 699.457f, 236.25f, 695.746f, 236.25f, 695.742f, 260.039f, 701, 259.25f, 698.75f, 259.25f, 698.75f, 279.617f, 693.957f, 276.246f, 693.746f, 276.246f, 693.742f, 270.156f, 692.418f, 277.246f, 688.746f, 277.246f, 688.742f, 273.457f, 683.617f, 267.246f, 687.75f, 261.578f, 692.418f, 264.879f, 690, 259.25f, 688.746f, 259.25f, 688.742f, 256.52f, 688.02f, 251.25f, 692.75f, 251.25f, 692.75f, 245.297f, 697.477f, 235.246f, 693.746f, 235.242f, 693.742f, 201.957f, 679.656f, 200.246f, 678.746f, 200.246f, 678.746f, 195.797f, 675.918f, 193.25f, 671.746f, 193.25f, 671.742f, 186.777f, 667.117f, 183.25f, 665.75f, 183.25f, 665.75f, 169.398f, 652.816f, 168.25f, 651.746f, 168.25f, 651.742f, 164.34f, 645.559f, 163.25f, 644.75f, 163.25f, 644.75f, 170.5f, 649.297f, 172.25f, 651.746f, 172.25f, 651.742f, 188.098f, 662.5f, 194.25f, 663.75f, 194.25f, 663.75f, 198.879f, 666.68f, 200.246f, 667.746f, 200.246f, 667.746f, 215.598f, 678.34f, 220.246f, 678.746f, 220.246f, 678.746f, 230.34f, 672.617f, 233.246f, 680.746f, 233.246f, 680.746f, 239.359f, 682.297f, 245.246f, 680.746f, 245.246f, 680.746f, 249.039f, 683.84f, 248.25f, 686.746f, 248.25f, 686.742f, 249.918f, 688.238f, 251.25f, 683.75f, 251.25f, 683.75f, 254.758f, 680.098f, 260.25f, 682.746f, 260.25f, 682.742f, 264.437f, 682.52f, 262.25f, 679.75f, 262.25f, 679.75f, 257.398f, 675.699f, 244.246f, 675.746f, 244.246f, 675.742f, 230.777f, 674.816f, 212.246f, 666.75f, 212.246f, 666.75f, 179.957f, 655.02f, 170.246f, 643.75f, 170.242f, 643.75f, 162.797f, 633.898f, 157.25f, 632.75f, 157.25f, 632.75f, 150.477f, 631.699f, 144.246f, 623.746f, 144.246f, 623.746f, 154.656f, 629.938f, 164.246f, 629.746f, 164.242f, 629.742f, 168.957f, 632.578f, 165.246f, 628.75f, 165.242f, 628.75f, 160.816f, 620.258f, 162.25f, 614.746f, 162.25f, 614.746f, 161.918f, 608.598f, 161.246f, 606.75f, 161.246f, 606.75f, 152.457f, 592.758f, 152.25f, 589.75f, 152.457f, 587.477f, 153.777f, 576.699f, 154.246f, 575.75f, 154.656f, 575.379f, 153.117f, 577.797f, 157.25f, 574.746f, 161.477f, 572.52f, 164.559f, 570.758f, 165.246f, 567.75f, 166.316f, 564.598f, 163.238f, 573.617f, 163.25f, 575.75f, 162.797f, 577.578f, 158.18f, 585.5f, 159.246f, 587.746f, 159.242f, 587.742f, 160.156f, 587.039f, 161.246f, 585.746f, 161.246f, 585.742f, 160.379f, 586.156f, 161.246f, 589.75f, 161.246f, 589.75f, 161.918f, 595.84f, 163.25f, 599.75f, 165, 602.879f, 167.199f, 607.059f, 167.25f, 607.746f, 168.078f, 608.816f, 168.078f, 615.199f, 169.25f, 612.746f, 173.25f, 609.746f, 173.25f, 609.742f, 170.277f, 612.34f, 172.25f, 614.746f, 172.25f, 614.746f, 171.598f, 620.918f, 173.25f, 623.746f, 173.25f, 623.746f, 181.277f, 633.02f, 183.25f, 633.75f, 184.797f, 635.219f, 183.25f, 634.746f, 183.25f, 634.746f, 183.25f, 634.746f, 189.859f, 639.398f, 183.25f, 637.75f, 183.25f, 637.75f, 179.078f, 635.879f, 176.246f, 635.75f, 176.246f, 635.75f, 167.418f, 633.68f, 172.25f, 638.746f, 176.219f, 642.918f, 187.219f, 648.859f, 191.246f, 648.75f, 192.25f, 646.75f, 204.25f, 649.746f, 203.25f, 648.75f, 203.25f, 648.75f, 203.059f, 648.859f, 207.25f, 649.746f, 212.297f, 649.738f, 218.68f, 648.199f, 220.246f, 649.746f, 221.758f, 652.156f, 225.5f, 653.258f, 225.25f, 651.746f, 224.617f, 650.18f, 224.246f, 647.746f, 224.246f, 647.746f, 224.242f, 647.746f, 229.898f, 654.359f, 229.25f, 651.746f, 228.578f, 649.52f, 219.559f, 643.797f, 218.25f, 636.746f, 229.25f, 645.746f, 233.246f, 649.746f, 233.246f, 649.742f, 237.379f, 646.879f, 237.25f, 648.75f, 237.816f, 650.398f, 242.879f, 656.777f, 244.246f, 656.746f, 245.52f, 656.34f, 247.719f, 659.418f, 247.246f, 656.746f, 247.277f, 653.699f, 255.246f, 647.746f, 255.246f, 647.746f, 255.246f, 647.746f, 259.156f, 649.738f, 260.25f, 647.746f, 262.238f, 646.656f, 267.246f, 669.746f, 267.246f, 669.746f, 294.25f, 681.75f, 342.246f, 685.75f, 323.246f, 692.75f, 256.246f, 705.75f, 10, 2.2f, 0.3f, 0, 0, 0.3f, 0, 0, 3, 276.246f, 486.746f, 276.246f, 486.742f, 260.039f, 504.977f, 251.25f, 507.75f, 251.25f, 507.75f, 236.059f, 515.316f, 209.246f, 506.746f, 10, 2.2f, 0.3f, 0, 0, 0.3f, 0, 0, 3, 247.246f, 509.75f, 247.242f, 509.75f, 219.559f, 518.18f, 202.246f, 513.746f, 202.242f, 513.746f, 182.379f, 511.359f, 173.25f, 495.746f, 10, 2.2f, 0.3f, 0, 0, 0.3f, 0, 0, 4, 243.246f, 510.746f, 243.246f, 510.742f, 224.617f, 518.617f, 208.25f, 520.75f, 208.25f, 520.75f, 190.078f, 523.898f, 172.25f, 515.746f, 172.25f, 515.742f, 158.398f, 509.379f, 152.25f, 497.746f, 10, 2.2f, 0.3f, 0, 0, 0.3f, 0, 0, 4, 244.246f, 510.746f, 244.246f, 510.742f, 227.477f, 522.359f, 226.25f, 523.75f, 226.25f, 523.75f, 218.68f, 536, 204.25f, 536.75f, 204.25f, 536.75f, 180.84f, 535.559f, 162.25f, 526.746f, 10, 0, 0, 0, 0, 0, 0, 0, 169, 243.246f, 519.746f, 244.68f, 518.543f, 274.25f, 486.746f, 274.25f, 486.746f, 313.059f, 446.457f, 282.25f, 483.75f, 282.25f, 483.75f, 273.898f, 489.359f, 264.246f, 509.75f, 264.246f, 509.75f, 262.457f, 513.117f, 279.246f, 501.75f, 279.246f, 501.75f, 283.578f, 501.238f, 298.246f, 479.75f, 298.246f, 479.75f, 291.059f, 482.758f, 296.25f, 474.75f, 296.25f, 474.75f, 299.418f, 472.637f, 322.246f, 455.746f, 322.246f, 455.746f, 325.82f, 451.078f, 330.25f, 449.746f, 330.25f, 449.746f, 345.621f, 455.035f, 338.25f, 440.746f, 338.25f, 440.746f, 341.219f, 433.035f, 347.25f, 445.746f, 347.25f, 445.746f, 359.699f, 464.277f, 341.25f, 461.75f, 341.25f, 461.75f, 308.656f, 458.559f, 301.246f, 475.746f, 301.246f, 475.746f, 298.539f, 478.797f, 308.246f, 475.746f, 308.246f, 475.746f, 317.461f, 473.957f, 300.246f, 489.746f, 300.246f, 489.746f, 302.937f, 489.797f, 313.246f, 482.746f, 313.246f, 482.746f, 324.5f, 472.199f, 326.25f, 474.75f, 326.25f, 474.75f, 346.5f, 484.078f, 358.25f, 475.746f, 358.25f, 475.746f, 360.141f, 473.957f, 353.98f, 466.477f, 355.246f, 460.746f, 357.5f, 455.035f, 363.25f, 441.75f, 363.25f, 441.75f, 360.141f, 439.637f, 360.25f, 427.746f, 360.25f, 427.746f, 379.059f, 402.238f, 368.246f, 404.75f, 368.246f, 404.75f, 351.34f, 404.879f, 367.246f, 396.746f, 367.246f, 396.746f, 371.141f, 394.316f, 381.25f, 386.75f, 381.25f, 386.75f, 377.738f, 387.719f, 376.246f, 381.746f, 376.246f, 381.746f, 381.258f, 377.598f, 378.246f, 372.746f, 378.246f, 372.746f, 371.578f, 370.996f, 370.25f, 366.75f, 370.25f, 366.75f, 377.738f, 357.797f, 366.246f, 357.746f, 366.246f, 357.746f, 370.699f, 352.516f, 365.246f, 339.746f, 365.246f, 339.746f, 360.141f, 339.316f, 353.246f, 332.746f, 353.246f, 332.746f, 355.738f, 327.879f, 344.246f, 321.746f, 344.246f, 321.746f, 335.059f, 319.957f, 338.25f, 312.75f, 338.25f, 312.75f, 329.34f, 305.879f, 326.25f, 288.746f, 326.25f, 288.746f, 325.82f, 276.836f, 323.18f, 273.316f, 329.25f, 275.746f, 334.621f, 277.719f, 333.246f, 291.746f, 333.246f, 291.746f, 328.461f, 308.516f, 375.246f, 325.75f, 375.246f, 325.75f, 379.938f, 327.879f, 381.25f, 333.75f, 381.25f, 333.75f, 383.02f, 333.156f, 392.25f, 324.746f, 392.25f, 324.746f, 401.059f, 312.477f, 401.246f, 322.75f, 401.246f, 322.75f, 402.82f, 326.559f, 401.246f, 332.746f, 401.246f, 332.746f, 407.66f, 356.918f, 392.25f, 363.746f, 392.25f, 363.746f, 381.258f, 400.918f, 396.25f, 391.75f, 396.25f, 391.75f, 399.738f, 385.516f, 411.246f, 379.746f, 411.246f, 379.746f, 415.25f, 382.75f, 413.82f, 387.719f, 423.246f, 394.746f, 423.246f, 394.746f, 426.141f, 387.277f, 432.246f, 395.75f, 432.246f, 395.75f, 436.699f, 422.918f, 450.25f, 406.75f, 450.25f, 406.75f, 454.738f, 405.758f, 456.246f, 412.746f, 456.246f, 412.746f, 460.02f, 424.676f, 456.246f, 439.75f, 456.246f, 439.75f, 460.02f, 440.078f, 470.25f, 433.746f, 470.25f, 433.746f, 473.66f, 437.438f, 463.539f, 455.918f, 468.25f, 453.746f, 472.34f, 450.637f, 477.25f, 448.75f, 477.25f, 448.75f, 478.059f, 451.078f, 467.246f, 464.746f, 467.246f, 464.746f, 462.219f, 467.797f, 456.246f, 489.746f, 456.246f, 489.746f, 464.418f, 486.277f, 453.25f, 502.746f, 453.25f, 502.746f, 453.418f, 506.52f, 460.246f, 518.75f, 460.246f, 518.75f, 459.141f, 526.316f, 460.246f, 525.75f, 460.246f, 525.75f, 463.098f, 524.559f, 471.898f, 522.797f, 464.25f, 529.75f, 456.938f, 536.879f, 465.246f, 541.746f, 465.246f, 541.746f, 470.141f, 545.238f, 455.25f, 544.746f, 455.25f, 544.746f, 449.461f, 549.637f, 450.25f, 553.75f, 450.25f, 553.75f, 458.699f, 551.84f, 442.859f, 567.68f, 440.25f, 571.75f, 437.578f, 575.598f, 448.25f, 581.746f, 448.25f, 581.746f, 462.66f, 585.277f, 450.25f, 588.75f, 450.25f, 588.75f, 428.34f, 588.359f, 440.25f, 599.75f, 440.25f, 599.75f, 446.82f, 599.797f, 445.246f, 602.75f, 445.246f, 602.75f, 439.34f, 603.758f, 429.25f, 610.75f, 429.25f, 610.75f, 424.379f, 614.758f, 428.25f, 613.75f, 428.25f, 613.75f, 446.82f, 612.559f, 415.25f, 624.75f, 415.25f, 624.75f, 423.938f, 624.879f, 404.25f, 636.746f, 404.25f, 636.746f, 401.938f, 638.078f, 398.246f, 646.75f, 398.246f, 646.75f, 391.82f, 652.156f, 386.246f, 659.75f, 386.246f, 659.75f, 386.098f, 664.477f, 381.25f, 669.746f, 381.25f, 669.746f, 368.059f, 684.719f, 362.25f, 684.746f, 362.25f, 684.746f, 345.621f, 688.238f, 340.25f, 687.75f, 340.25f, 687.75f, 282.25f, 682.746f, 252.777f, 668.438f, 261.25f, 645.746f, 261.25f, 645.746f, 268.398f, 636.098f, 278.246f, 640.746f, 278.246f, 640.746f, 283.578f, 647.098f, 296.25f, 644.75f, 296.25f, 644.75f, 318.777f, 641.156f, 316.25f, 644.75f, 316.25f, 644.75f, 313.277f, 650.18f, 295.457f, 657, 295.25f, 657.75f, 295.02f, 658.316f, 285.25f, 661.75f, 285.25f, 661.75f, 282.039f, 663.379f, 277.246f, 673.746f, 277.246f, 673.746f, 273.68f, 677.238f, 291.246f, 670.75f, 291.246f, 670.75f, 289.738f, 669.758f, 298.246f, 665.75f, 298.246f, 665.75f, 317.02f, 666.457f, 328.25f, 654.75f, 328.25f, 654.75f, 340.559f, 636.316f, 341.25f, 645.746f, 341.25f, 645.746f, 343.859f, 655.68f, 331.246f, 678.746f, 331.246f, 678.746f, 331.32f, 681.199f, 340.25f, 673.746f, 340.25f, 673.746f, 341.879f, 676.137f, 343.246f, 669.746f, 343.246f, 669.746f, 342.98f, 667.117f, 347.25f, 658.746f, 347.25f, 658.746f, 350.238f, 644.02f, 354.246f, 651.746f, 354.246f, 651.746f, 359.25f, 641.75f, 360.801f, 638.957f, 354.246f, 630.75f, 354.246f, 630.75f, 353.98f, 627.52f, 354.859f, 627.738f, 348.25f, 619.75f, 342.539f, 611.02f, 346.246f, 605.746f, 346.246f, 605.746f, 344.738f, 598.918f, 354.246f, 599.75f, 354.246f, 599.75f, 357.277f, 596.938f, 361.25f, 596.746f, 361.25f, 596.746f, 363, 594.738f, 365.246f, 595.75f, 365.246f, 595.75f, 367.398f, 599.578f, 374.25f, 597.75f, 374.25f, 597.75f, 375.758f, 600.02f, 385.25f, 600.75f, 385.25f, 600.75f, 385.879f, 603.316f, 386.32f, 605.078f, 390.246f, 605.746f, 393.801f, 606.398f, 366.246f, 653.746f, 366.246f, 653.746f, 373.777f, 654.578f, 365.246f, 667.746f, 365.246f, 667.746f, 362.34f, 675.477f, 374.879f, 659.418f, 377.246f, 657.75f, 379.719f, 656.34f, 380.82f, 653.918f, 379.246f, 653.746f, 377.301f, 654.359f, 375.32f, 651.938f, 377.246f, 651.746f, 378.398f, 651.5f, 392.699f, 635, 396.25f, 623.746f, 400.18f, 612.559f, 406.777f, 608.156f, 413.246f, 601.746f, 420.418f, 594.957f, 419.246f, 568.746f, 419.246f, 568.746f, 419.098f, 558.656f, 425.25f, 546.746f, 425.25f, 546.746f, 427.898f, 542.816f, 423.246f, 522.746f, 423.246f, 522.746f, 421.078f, 520.379f, 422.246f, 519.746f, 422.246f, 519.746f, 423.719f, 518.18f, 431.246f, 503.75f, 431.246f, 503.75f, 429.219f, 503.879f, 433.246f, 499.746f, 433.246f, 499.746f, 438.898f, 493.316f, 432.246f, 496.75f, 432.246f, 496.75f, 425.258f, 498.379f, 433.246f, 487.75f, 433.246f, 487.75f, 434.277f, 485.617f, 424.246f, 490.75f, 424.246f, 490.75f, 414.258f, 491.34f, 427.25f, 483.75f, 427.25f, 483.75f, 436.48f, 475.5f, 424.246f, 480.746f, 424.246f, 480.746f, 418.879f, 482.316f, 422.246f, 474.75f, 422.246f, 474.75f, 425.918f, 473.078f, 445.246f, 465.75f, 445.246f, 465.75f, 445.277f, 461.195f, 442.246f, 455.746f, 442.246f, 455.746f, 442.418f, 451.297f, 440.25f, 447.746f, 440.25f, 447.746f, 438.68f, 438.535f, 438.25f, 437.75f, 438.25f, 437.75f, 433.398f, 437.438f, 425.25f, 422.746f, 425.25f, 422.746f, 423.277f, 419.398f, 412.246f, 405.746f, 412.246f, 405.746f, 409.859f, 398.496f, 390.246f, 406.75f, 390.246f, 406.75f, 382.801f, 402.676f, 385.25f, 406.75f, 385.25f, 406.75f, 384.559f, 408.836f, 390.246f, 415.75f, 390.246f, 415.75f, 397.539f, 418.297f, 394.25f, 429.746f, 394.25f, 429.746f, 399.078f, 431.719f, 386.758f, 434.797f, 387.246f, 435.75f, 387.199f, 437.438f, 393.25f, 438.746f, 393.25f, 438.746f, 402.379f, 441.176f, 397.246f, 443.75f, 397.246f, 443.75f, 396.879f, 448.219f, 400.246f, 454.75f, 400.246f, 454.75f, 412.938f, 455.258f, 400.246f, 472.75f, 400.246f, 472.75f, 388.301f, 481.438f, 387.246f, 487.75f, 387.246f, 487.75f, 401.059f, 496.84f, 392.039f, 510.477f, 392.25f, 514.75f, 392.48f, 518.398f, 394.25f, 541.746f, 394.25f, 541.746f, 391.598f, 548.977f, 388.246f, 563.746f, 388.246f, 563.746f, 390.719f, 569.656f, 399.246f, 583.746f, 399.246f, 583.746f, 401.938f, 588.137f, 411.621f, 593.418f, 409.246f, 596.746f, 406.777f, 600.02f, 398.246f, 597.75f, 398.246f, 597.75f, 389.621f, 599.578f, 390.246f, 593.75f, 390.246f, 593.75f, 388.52f, 592.758f, 387.246f, 587.746f, 387.246f, 587.746f, 386.848f, 578.531f, 377.246f, 571.75f, 377.246f, 571.75f, 364.758f, 564.816f, 375.246f, 560.75f, 375.246f, 560.75f, 381.48f, 553.156f, 370.25f, 552.746f, 370.25f, 552.746f, 358.598f, 554.918f, 367.246f, 543.746f, 367.246f, 543.746f, 379.5f, 529.617f, 376.246f, 526.746f, 376.246f, 526.746f, 364.98f, 525.438f, 379.246f, 515.746f, 379.246f, 515.746f, 379.242f, 515.742f, 377.961f, 517.52f, 378.246f, 515.746f, 378.398f, 513.559f, 381.699f, 508.938f, 382.25f, 506.746f, 383.461f, 504.539f, 379.246f, 504.746f, 379.246f, 504.746f, 379.719f, 493.758f, 363.25f, 498.75f, 363.25f, 498.75f, 363.25f, 498.75f, 362.777f, 498.379f, 361.25f, 497.746f, 359.258f, 497.938f, 346.938f, 498.816f, 340.25f, 500.746f, 334.18f, 503.656f, 326.25f, 503.75f, 326.25f, 503.75f, 326.25f, 503.75f, 322.301f, 501.68f, 314.25f, 501.75f, 305.578f, 502.117f, 297.246f, 498.75f, 297.246f, 498.75f, 291.937f, 499.477f, 301.398f, 504.316f, 301.246f, 503.75f, 301.84f, 503.879f, 308, 510.039f, 299.246f, 509.75f, 275.223f, 507.578f, 263.25f, 518.75f, 263.25f, 518.75f, 261.137f, 520.379f, 258.246f, 523.75f, 258.246f, 523.75f, 247.277f, 525.656f, 260.25f, 509.75f, 260.25f, 509.75f, 261.137f, 508.277f, 259.25f, 506.746f, 259.25f, 506.746f, 258.719f, 508.938f, 250.25f, 514.75f, 250.25f, 514.75f, 247.047f, 515.949f, 245.547f, 517.414f, 243.246f, 519.746f, 10, 0, 0.3f, 0, 0, 0.3f, 0, 0, 7, 216.25f, 532.746f, 216.25f, 532.742f, 229.457f, 526.758f, 232.246f, 523.75f, 235.18f, 520.598f, 250.25f, 507.75f, 250.25f, 507.75f, 250.25f, 507.75f, 244.637f, 510.258f, 242.246f, 511.746f, 238.918f, 514.219f, 227.25f, 522.746f, 227.25f, 522.746f, 227.25f, 522.746f, 222.859f, 529.84f, 216.25f, 532.746f, 10, 0, 0.6f, 0.8f, 0.2f, 0.6f, 0.8f, 0.2f, 6, 153.246f, 566.75f, 153.258f, 567.398f, 152.684f, 570.379f, 152.25f, 570.746f, 151.332f, 573.977f, 141.25f, 575.75f, 141.25f, 575.75f, 141.207f, 574.098f, 141.148f, 572.34f, 141.25f, 570.746f, 141.25f, 570.746f, 146.621f, 564.469f, 153.246f, 566.75f, 10, 0, 0.4f, 0.6f, 0, 0.4f, 0.6f, 0, 6, 153.246f, 567.75f, 152.395f, 567.281f, 152.871f, 570.461f, 152.25f, 570.746f, 151.555f, 573.977f, 141.25f, 575.75f, 141.25f, 575.75f, 141.207f, 574.207f, 141.148f, 572.449f, 141.25f, 570.746f, 141.25f, 570.746f, 145.961f, 565.02f, 153.246f, 567.75f, 10, 0, 0, 0, 0, 0, 0, 0, 6, 148.246f, 567.75f, 147.371f, 567.297f, 146.812f, 568.551f, 147.25f, 569.75f, 146.812f, 571.645f, 147.371f, 572.898f, 148.246f, 572.746f, 148.746f, 572.898f, 149.305f, 571.645f, 149.246f, 569.75f, 149.305f, 568.551f, 148.746f, 567.297f, 148.246f, 567.75f, 10, 0, 0, 0, 0, 0, 0, 0, 17, 98.2461f, 459.75f, 98.2422f, 459.75f, 91.7383f, 448.438f, 119.25f, 454.75f, 119.25f, 454.75f, 134.418f, 456.355f, 137.246f, 458.746f, 138.379f, 458.117f, 147.582f, 454.891f, 150.246f, 453.746f, 158.18f, 452.398f, 167.25f, 463.75f, 167.25f, 463.75f, 167.25f, 463.75f, 172.477f, 474.949f, 175.25f, 474.75f, 178.637f, 474.949f, 175.25f, 472.75f, 175.25f, 472.75f, 175.25f, 472.75f, 167.859f, 462.078f, 168.25f, 460.746f, 168.25f, 460.742f, 162.578f, 438.316f, 145.25f, 437.75f, 145.25f, 437.75f, 127.215f, 436.391f, 128.25f, 429.746f, 128.25f, 429.742f, 138.379f, 432.598f, 141.25f, 429.746f, 141.25f, 429.742f, 152.898f, 430.398f, 144.246f, 423.746f, 136.25f, 410.75f, 136.25f, 410.75f, 136.773f, 406.289f, 125.25f, 409.746f, 114.84f, 413.898f, 103.25f, 427.746f, 103.25f, 427.746f, 103.25f, 427.742f, 85.9648f, 444.094f, 98.2461f, 459.75f, 10, 0, 0.9f, 0.6f, 0.6f, 0.9f, 0.6f, 0.6f, 14, 96.25f, 454.75f, 96.25f, 454.75f, 94.3789f, 444.477f, 135.25f, 455.746f, 135.25f, 455.742f, 139.699f, 455.918f, 142.246f, 454.75f, 144.977f, 454.156f, 158.18f, 451.078f, 160.246f, 452.75f, 160.246f, 452.75f, 152.457f, 437.438f, 139.246f, 438.746f, 139.246f, 438.742f, 125.18f, 437.438f, 125.25f, 431.746f, 125.25f, 431.742f, 130.02f, 424.238f, 135.25f, 421.75f, 135.25f, 421.75f, 138.379f, 418.957f, 138.246f, 415.75f, 137.5f, 411.918f, 134.418f, 410.156f, 132.246f, 409.746f, 130.02f, 408.398f, 126.5f, 411.918f, 124.25f, 411.746f, 122.977f, 411.918f, 113.738f, 418.957f, 109.246f, 423.746f, 104.059f, 429.516f, 94.8164f, 442.719f, 95.25f, 445.746f, 95.6992f, 448.879f, 96.25f, 454.75f, 96.25f, 454.75f, 10, 0, 0.7f, 0.4f, 0.4f, 0.7f, 0.4f, 0.4f, 13, 100.246f, 435.75f, 102.957f, 431.496f, 106.477f, 426.879f, 109.246f, 423.746f, 113.738f, 418.957f, 122.977f, 411.918f, 124.25f, 411.746f, 126.5f, 411.918f, 130.02f, 408.398f, 132.246f, 409.746f, 134.418f, 410.156f, 137.5f, 411.918f, 138.246f, 415.75f, 138.379f, 418.957f, 135.25f, 421.75f, 135.25f, 421.75f, 131.926f, 423.285f, 128.91f, 427.125f, 127.246f, 429.746f, 127.246f, 429.742f, 127.379f, 426.879f, 121.246f, 427.746f, 115.938f, 428.637f, 110.219f, 431.719f, 108.25f, 434.746f, 106.699f, 438.758f, 104.059f, 441.398f, 106.25f, 437.75f, 107.578f, 433.477f, 110.219f, 429.516f, 112.25f, 428.75f, 113.738f, 428.637f, 113.297f, 427.316f, 110.246f, 427.746f, 108.02f, 428.195f, 104.938f, 428.637f, 100.246f, 434.746f, 10, 0, 0.6f, 0.15f, 0, 0.6f, 0.15f, 0, 20, 97.25f, 458.746f, 97.25f, 458.746f, 99.2187f, 473.957f, 100.246f, 478.746f, 100.246f, 478.746f, 99.6563f, 485.84f, 102.25f, 490.75f, 104.938f, 495.078f, 107.137f, 501.898f, 110.246f, 507.75f, 113.738f, 513.777f, 113.957f, 518.18f, 118.25f, 519.746f, 122.758f, 521.699f, 129.359f, 531.156f, 132.246f, 532.746f, 135.52f, 533.359f, 135.25f, 532.746f, 135.25f, 532.746f, 135.25f, 532.742f, 142.777f, 548.758f, 157.25f, 544.746f, 157.25f, 544.746f, 139.918f, 547.438f, 157.25f, 557.746f, 157.25f, 557.746f, 152.02f, 556.566f, 155.246f, 564.75f, 158.07f, 569.402f, 157.52f, 561.957f, 145.25f, 548.746f, 145.25f, 548.746f, 139.918f, 539.52f, 134.25f, 535.746f, 128.477f, 532.918f, 115.277f, 525.219f, 114.25f, 520.75f, 112.637f, 516.859f, 109.117f, 510.477f, 107.25f, 508.746f, 104.719f, 506.957f, 101.637f, 502.34f, 101.25f, 498.75f, 101.25f, 498.75f, 99.8789f, 494.199f, 98.2461f, 492.75f, 96.7969f, 491.559f, 96.5781f, 488.039f, 96.25f, 485.75f, 96.5781f, 483.637f, 94.3789f, 480.559f, 94.2461f, 477.746f, 94.2461f, 477.742f, 95.4766f, 457.016f, 95.25f, 454.75f, 97.25f, 458.746f, 10, 0, 1, 1, 1, 1, 1, 1, 6, 88.2461f, 453.746f, 88.2461f, 453.742f, 85.5781f, 455.477f, 80.25f, 448.75f, 80.25f, 448.75f, 88.7695f, 412.578f, 89.2461f, 410.75f, 89.2461f, 410.75f, 89.9766f, 413.348f, 88.2461f, 421.75f, 87.1172f, 429.188f, 86.25f, 442.746f, 86.25f, 442.746f, 88.2461f, 453.746f, 10, 0, 0.6f, 0.15f, 0, 0.6f, 0.15f, 0, 13, 111.246f, 520.75f, 111.246f, 520.75f, 92.1797f, 517.078f, 92.2461f, 484.746f, 91.25f, 457.75f, 91.25f, 457.75f, 90.418f, 485.84f, 89.2461f, 487.75f, 87.7773f, 489.359f, 92.1797f, 501.68f, 88.2461f, 494.75f, 88.2461f, 494.75f, 73.2578f, 479.68f, 82.2461f, 456.746f, 82.2422f, 456.746f, 83.707f, 452.727f, 80.25f, 457.75f, 80.25f, 457.75f, 75.3477f, 471.648f, 76.2461f, 478.746f, 76.2422f, 478.746f, 76.7773f, 481.109f, 79.25f, 483.75f, 79.25f, 483.75f, 88.3281f, 497.059f, 91.25f, 499.746f, 91.25f, 499.742f, 93.2773f, 515.43f, 110.246f, 520.75f, 110.246f, 520.75f, 116.488f, 523.68f, 111.246f, 520.75f, 10, 0, 0, 0, 0, 0, 0, 0, 28, 265.246f, 593.75f, 265.605f, 593.809f, 265.594f, 594.875f, 266.246f, 594.746f, 267.496f, 595.441f, 267.676f, 596.617f, 268.246f, 597.75f, 269.207f, 598.93f, 269.418f, 600.617f, 270.25f, 602.75f, 270.359f, 603.027f, 270.387f, 604.078f, 270.25f, 604.75f, 268.754f, 607.531f, 267.98f, 610.227f, 266.246f, 612.746f, 266.098f, 613.391f, 265.812f, 614.262f, 265.246f, 614.746f, 265.082f, 616.441f, 263.699f, 617.535f, 263.25f, 618.746f, 262.434f, 619.469f, 263.012f, 620.488f, 262.25f, 620.746f, 261.238f, 620.695f, 259.645f, 621.332f, 259.25f, 619.75f, 258.742f, 617.359f, 259.852f, 614.586f, 261.25f, 611.75f, 260.059f, 611.137f, 260.426f, 610.125f, 260.25f, 609.746f, 261.375f, 605.313f, 260.055f, 601.625f, 259.25f, 597.75f, 259.191f, 597.691f, 259.57f, 597.473f, 259.25f, 597.75f, 258.195f, 594.449f, 256.598f, 591.762f, 254.246f, 588.75f, 253.762f, 588.051f, 252.805f, 587.043f, 252.25f, 585.746f, 251.852f, 585.008f, 251.402f, 583.945f, 251.25f, 582.75f, 247.898f, 579.801f, 245.426f, 575.57f, 242.246f, 571.75f, 242.043f, 570.594f, 242.363f, 569.262f, 243.246f, 568.746f, 243.863f, 568.527f, 244.918f, 569.656f, 245.246f, 570.746f, 245.859f, 571.352f, 246.25f, 572.066f, 247.246f, 572.746f, 246.937f, 572.969f, 246.738f, 573.43f, 247.246f, 573.75f, 249.785f, 576.145f, 251.621f, 579.375f, 254.246f, 581.746f, 256.465f, 582.34f, 258.152f, 583.438f, 260.25f, 584.75f, 260.414f, 584.75f, 260.992f, 584.477f, 261.25f, 584.75f, 263.238f, 585.984f, 263.238f, 588.223f, 263.25f, 590.746f, 263.41f, 591.297f, 263.625f, 592.746f, 265.246f, 593.75f, 10, 0, 0, 0, 0, 0, 0, 0, 19, 255.246f, 598.746f, 255.289f, 598.414f, 255.117f, 598.879f, 255.246f, 598.746f, 255.418f, 599.477f, 255.859f, 599.68f, 256.246f, 599.75f, 256.16f, 600.277f, 255.98f, 600.695f, 256.246f, 600.75f, 258.695f, 603.543f, 258.977f, 606.867f, 258.246f, 609.746f, 258.965f, 610.82f, 259.031f, 612.207f, 258.246f, 612.746f, 257.625f, 615.012f, 257.414f, 617.129f, 256.246f, 618.746f, 255.457f, 620.223f, 253.723f, 621.59f, 252.25f, 619.75f, 251.75f, 619.719f, 251.398f, 618.852f, 251.25f, 617.75f, 251.773f, 617.887f, 252.09f, 617.727f, 252.25f, 617.75f, 251.941f, 617.281f, 251.34f, 617.035f, 251.25f, 616.746f, 251.301f, 615.09f, 250.25f, 613.43f, 251.25f, 611.75f, 251.793f, 610.176f, 252.695f, 608.133f, 253.246f, 605.746f, 252.082f, 603.852f, 253.219f, 601.156f, 251.25f, 598.746f, 251.141f, 598.93f, 251.148f, 598.508f, 251.25f, 598.746f, 251.605f, 597.75f, 252.051f, 597.305f, 252.25f, 596.746f, 252.809f, 596.848f, 253.191f, 596.848f, 253.246f, 596.746f, 254.047f, 597.383f, 254.484f, 597.918f, 255.246f, 598.746f, 10, 0, 0, 0, 0, 0, 0, 0, 21, 324.246f, 609.746f, 325.773f, 607.703f, 326.094f, 604.629f, 324.246f, 602.75f, 324.445f, 599.457f, 328.129f, 601.637f, 330.25f, 601.746f, 330.32f, 602.645f, 330.57f, 603.023f, 331.246f, 602.75f, 332.043f, 603.047f, 332.789f, 604.18f, 334.246f, 603.746f, 334.438f, 605.691f, 336.238f, 606.465f, 337.25f, 607.746f, 338.844f, 612.047f, 338.195f, 616.746f, 335.246f, 620.746f, 335.129f, 620.598f, 335.371f, 621.164f, 335.246f, 621.75f, 334.402f, 623.996f, 332.125f, 624.34f, 330.25f, 624.75f, 328.703f, 629.359f, 327.977f, 633.793f, 326.25f, 637.75f, 324.266f, 638.133f, 323.496f, 640.047f, 322.246f, 640.746f, 320.559f, 641.629f, 319.934f, 639.891f, 320.246f, 638.746f, 319.988f, 638.516f, 320.484f, 638.27f, 320.246f, 637.75f, 320.215f, 637.688f, 319.926f, 637.566f, 320.246f, 637.75f, 319.93f, 637.27f, 320.172f, 637.125f, 320.246f, 636.746f, 319.309f, 636.074f, 317.742f, 635.551f, 317.25f, 634.746f, 316.371f, 630.211f, 319.199f, 626.773f, 321.246f, 623.746f, 321.684f, 622.004f, 320.875f, 620.605f, 320.246f, 619.75f, 319.559f, 618.512f, 319.676f, 617.273f, 320.246f, 616.746f, 320.809f, 613.766f, 322.559f, 611.855f, 324.246f, 609.746f, 10, 0, 0, 0, 0, 0, 0, 0, 73, 283.25f, 589.75f, 281.734f, 587.41f, 277.98f, 584.586f, 281.25f, 582.75f, 281.402f, 582.324f, 281.809f, 582.32f, 282.25f, 582.75f, 284.223f, 584.188f, 286.426f, 585.18f, 289.246f, 585.746f, 289.242f, 585.852f, 289.543f, 585.34f, 290.246f, 585.746f, 291.727f, 586.289f, 293.938f, 586.227f, 295.25f, 587.746f, 299.383f, 587.453f, 303.305f, 588.68f, 307.25f, 589.75f, 308.309f, 590.609f, 309.707f, 591.227f, 311.246f, 591.75f, 312.543f, 592.41f, 313.867f, 593.434f, 315.25f, 594.746f, 315.234f, 594.836f, 315.625f, 594.738f, 316.25f, 594.746f, 315.875f, 595.688f, 316.934f, 595.828f, 317.25f, 596.746f, 317.305f, 596.766f, 317.141f, 597.203f, 317.25f, 597.75f, 319.641f, 599.105f, 320.652f, 601.328f, 319.25f, 603.746f, 319.051f, 604.578f, 318.777f, 605.258f, 318.25f, 605.746f, 316.961f, 606.781f, 315.75f, 605.844f, 314.25f, 605.746f, 314.422f, 605.488f, 313.621f, 605.676f, 313.246f, 605.746f, 312.25f, 604.977f, 310.785f, 605.621f, 310.246f, 604.75f, 308.344f, 604.371f, 306.977f, 604.188f, 305.25f, 603.746f, 305.07f, 603.684f, 304.215f, 603.789f, 304.25f, 602.75f, 303.891f, 603.246f, 303.727f, 603.504f, 303.25f, 603.746f, 301.512f, 603.043f, 300.125f, 602.809f, 298.246f, 600.75f, 298.582f, 600.801f, 298.098f, 600.996f, 298.246f, 600.75f, 296.867f, 599.961f, 296.422f, 598.602f, 295.25f, 597.75f, 294.992f, 597.727f, 294.602f, 597.914f, 294.25f, 597.75f, 293.68f, 597.297f, 293.277f, 596.59f, 292.25f, 595.75f, 292.207f, 595.848f, 291.766f, 596.207f, 292.25f, 596.746f, 292.07f, 598.629f, 292.789f, 600.594f, 292.25f, 602.75f, 294.441f, 605.43f, 297.211f, 607.574f, 299.246f, 610.75f, 299.215f, 612.961f, 299.977f, 615.32f, 300.246f, 617.75f, 299.84f, 617.816f, 299.523f, 618.625f, 299.246f, 618.746f, 299.043f, 619.945f, 300.039f, 621.117f, 299.246f, 621.75f, 297.566f, 623.238f, 296.145f, 622.273f, 295.25f, 620.746f, 293.215f, 620.27f, 290.945f, 619.508f, 289.246f, 620.746f, 288.102f, 621.73f, 287.465f, 622.727f, 286.246f, 623.746f, 285.504f, 625.32f, 285.871f, 626.898f, 286.246f, 628.75f, 285.953f, 628.762f, 285.609f, 628.91f, 285.25f, 628.75f, 285.609f, 629.207f, 285.852f, 629.352f, 286.246f, 629.746f, 285.223f, 630.188f, 284.918f, 631.352f, 284.25f, 631.746f, 284.133f, 632.898f, 283.391f, 633.871f, 282.25f, 633.75f, 280.238f, 634.965f, 278.395f, 632.859f, 276.246f, 632.75f, 275.746f, 632.758f, 275.23f, 633.902f, 274.25f, 634.746f, 274.043f, 634.496f, 273.27f, 634.531f, 273.25f, 633.75f, 272.113f, 633.688f, 271.465f, 633.559f, 270.25f, 633.75f, 268.852f, 632.855f, 267.449f, 631.652f, 266.246f, 630.75f, 264.188f, 629.77f, 263.137f, 628.188f, 262.25f, 626.75f, 260.914f, 625.469f, 260.762f, 622.813f, 262.25f, 622.75f, 264.352f, 621.547f, 265.785f, 624.523f, 268.246f, 623.746f, 268.293f, 624.105f, 268.52f, 623.766f, 268.246f, 623.746f, 268.824f, 623.219f, 269.066f, 623.469f, 269.246f, 623.746f, 270.223f, 622.656f, 271.504f, 622.285f, 272.25f, 621.75f, 273.602f, 620.332f, 275.523f, 620.793f, 276.246f, 619.75f, 278.32f, 618.043f, 277.707f, 615.094f, 280.246f, 613.75f, 279.195f, 612.215f, 278.527f, 610.809f, 278.246f, 608.75f, 277.848f, 607.914f, 278.941f, 606.598f, 280.246f, 606.75f, 281.656f, 606.801f, 281.945f, 607.633f, 282.25f, 608.75f, 282.773f, 608.523f, 283.289f, 608.199f, 283.25f, 607.746f, 282.738f, 605.336f, 281.609f, 603.141f, 281.25f, 600.75f, 281.043f, 600.121f, 280.707f, 599.898f, 280.246f, 599.75f, 279.762f, 595.453f, 275.305f, 592.82f, 272.25f, 589.75f, 272.063f, 588.785f, 272.059f, 587.414f, 272.25f, 586.75f, 274.051f, 585.445f, 276.207f, 587.145f, 278.246f, 587.746f, 278.313f, 589.023f, 279.258f, 590.063f, 280.246f, 589.75f, 281.004f, 589.988f, 281.262f, 590.586f, 281.25f, 590.746f, 282, 590.879f, 282.555f, 590.633f, 283.25f, 590.746f, 284.77f, 592.164f, 286.32f, 593.383f, 288.246f, 594.746f, 288.441f, 594.832f, 288.82f, 594.66f, 289.246f, 594.746f, 289.414f, 594.957f, 289.621f, 595.383f, 290.246f, 595.75f, 290.359f, 595.805f, 290.625f, 595.484f, 291.246f, 594.746f, 290.129f, 594.793f, 290.125f, 593.742f, 289.246f, 593.75f, 288.629f, 593.223f, 288.012f, 592.66f, 287.246f, 591.75f, 286.949f, 591.957f, 286.227f, 592.23f, 286.246f, 591.75f, 285.453f, 590.902f, 284.152f, 590.418f, 283.25f, 589.75f, 10, 0, 0, 0, 0, 0, 0, 0, 30, 222.246f, 643.75f, 222.246f, 643.75f, 212.258f, 646.957f, 200.246f, 618.746f, 200.246f, 618.742f, 197.34f, 613, 194.25f, 610.75f, 192.059f, 608.598f, 179.738f, 604.637f, 177.246f, 599.75f, 166.246f, 582.75f, 166.246f, 582.75f, 182.379f, 600.238f, 186.25f, 602.75f, 186.25f, 602.75f, 194.699f, 612.117f, 191.246f, 604.75f, 191.242f, 604.75f, 175.777f, 592.758f, 177.246f, 582.75f, 177.246f, 582.75f, 170.938f, 566.797f, 170.246f, 564.75f, 170.242f, 564.75f, 187.656f, 599.797f, 190.246f, 600.75f, 192.938f, 602.438f, 194.258f, 602.438f, 193.25f, 598.746f, 191.617f, 594.52f, 191.18f, 576.477f, 188.246f, 574.746f, 188.246f, 574.742f, 196.898f, 596.719f, 196.25f, 599.75f, 196.25f, 599.75f, 199.539f, 604.199f, 202.246f, 598.746f, 201.246f, 580.75f, 205.25f, 567.75f, 205.25f, 567.75f, 203.059f, 580, 205.25f, 596.746f, 205.25f, 596.742f, 202.617f, 608.598f, 207.25f, 602.75f, 211.418f, 596.277f, 221.977f, 589.68f, 222.246f, 584.75f, 222.246f, 584.75f, 216.258f, 603.758f, 206.25f, 608.75f, 201.246f, 602.75f, 200.246f, 604.75f, 200.246f, 604.75f, 196.457f, 605.52f, 201.246f, 612.746f, 206.137f, 620.477f, 205.25f, 621.75f, 205.25f, 621.75f, 205.25f, 621.75f, 212.738f, 613.438f, 214.25f, 613.75f, 214.25f, 613.75f, 229.02f, 621.797f, 230.25f, 594.746f, 230.25f, 594.742f, 237.816f, 610.797f, 227.25f, 618.746f, 227.25f, 618.742f, 211.418f, 620.477f, 212.246f, 625.746f, 220.246f, 639.75f, 224.617f, 645.559f, 223.246f, 642.746f, 223.246f, 642.746f, 10, 0, 0, 0, 0, 0, 0, 0, 6, 200.246f, 625.746f, 200.246f, 625.746f, 186.34f, 625.758f, 183.25f, 619.75f, 175.25f, 609.746f, 175.25f, 609.742f, 193.816f, 620.477f, 198.246f, 621.75f, 202.617f, 623.117f, 200.246f, 625.746f, 200.246f, 625.746f, 10, 0, 0, 0, 0, 0, 0, 0, 7, 156.25f, 618.746f, 156.25f, 618.742f, 154.219f, 617.398f, 154.246f, 614.746f, 153.34f, 611.238f, 150.699f, 610.797f, 151.25f, 607.746f, 152.457f, 604.637f, 154.656f, 602, 154.246f, 606.75f, 154.656f, 610.797f, 156.418f, 613, 157.25f, 614.746f, 158.18f, 615.637f, 159.938f, 620.477f, 156.25f, 618.746f, 10, 0, 0, 0, 0, 0, 0, 0, 10, 146.25f, 551.75f, 146.25f, 551.75f, 137.5f, 555.797f, 134.25f, 559.746f, 130.457f, 563.719f, 130.957f, 558.035f, 125.25f, 558.75f, 119.187f, 558.922f, 120.246f, 576.746f, 120.246f, 576.746f, 116.246f, 567.75f, 116.246f, 567.75f, 114.617f, 552.277f, 123.25f, 554.746f, 127.715f, 556.207f, 129.137f, 554.477f, 127.246f, 553.75f, 125.617f, 552.719f, 133.539f, 552.277f, 130.25f, 550.746f, 127.379f, 548.758f, 143.219f, 554.477f, 140.25f, 542.75f, 146.25f, 551.75f, 10, 0, 0, 0, 0, 0, 0, 0, 8, 133.246f, 535.746f, 133.246f, 535.746f, 115.938f, 530.719f, 112.25f, 541.746f, 112.25f, 541.742f, 106.699f, 538.637f, 109.246f, 535.746f, 111.539f, 532.039f, 113.25f, 531.75f, 113.25f, 531.75f, 113.25f, 531.75f, 118.797f, 530.277f, 118.25f, 529.75f, 117.477f, 528.52f, 115.246f, 524.746f, 115.246f, 524.746f, 115.242f, 524.746f, 126.059f, 531.379f, 133.246f, 535.746f, 10, 0, 1, 1, 1, 1, 1, 1, 24, 384.25f, 449.746f, 383.648f, 447.191f, 381.813f, 446.309f, 379.246f, 445.746f, 377.609f, 446.629f, 374.754f, 450.047f, 372.25f, 447.746f, 372.156f, 448.305f, 371.301f, 448.371f, 371.25f, 448.75f, 370.41f, 450.086f, 370.711f, 451.238f, 370.25f, 451.746f, 369.734f, 453.516f, 368.953f, 455.016f, 369.25f, 456.746f, 371.145f, 457.359f, 371.801f, 459.457f, 371.25f, 461.75f, 371.203f, 461.68f, 370.73f, 461.895f, 371.25f, 462.746f, 371.156f, 462.633f, 371.504f, 462.883f, 372.25f, 462.746f, 371.652f, 463.031f, 371.492f, 462.773f, 371.25f, 462.746f, 370.699f, 462.91f, 370.836f, 463.613f, 371.25f, 463.75f, 371.621f, 465.957f, 373.836f, 466.25f, 375.246f, 464.746f, 375.602f, 465.559f, 376.16f, 465.348f, 376.246f, 465.75f, 376.586f, 466.016f, 377.031f, 466.594f, 377.246f, 466.746f, 377.82f, 468.266f, 379.613f, 467.047f, 380.25f, 467.746f, 381.672f, 468.629f, 382.844f, 469.398f, 384.25f, 468.75f, 386.02f, 467.621f, 387.898f, 466.285f, 389.246f, 464.746f, 389.848f, 463.453f, 390.113f, 462.047f, 390.246f, 460.746f, 390.008f, 460.281f, 388.488f, 460.668f, 388.246f, 459.75f, 387.402f, 457.723f, 389.414f, 457.152f, 390.246f, 455.746f, 390.465f, 455.297f, 390.176f, 454.961f, 390.246f, 454.75f, 389.375f, 454.711f, 388.516f, 454.922f, 388.246f, 454.75f, 389.734f, 450.91f, 386.703f, 450.164f, 384.25f, 449.746f, 10, 0, 1, 1, 1, 1, 1, 1, 11, 373.25f, 427.746f, 373.551f, 429.891f, 371.789f, 431.82f, 373.25f, 433.746f, 373.27f, 433.551f, 373.41f, 433.305f, 373.25f, 433.746f, 373.707f, 433.305f, 373.852f, 433.551f, 374.25f, 433.746f, 375.645f, 431.258f, 379.66f, 430.238f, 379.246f, 426.75f, 379.48f, 426.617f, 378.285f, 425.605f, 379.246f, 424.75f, 377.285f, 423.414f, 377.223f, 420.809f, 376.246f, 418.746f, 374.836f, 419.051f, 373.504f, 419.449f, 372.25f, 419.75f, 372.625f, 421.691f, 372.496f, 423.543f, 373.25f, 424.75f, 373.879f, 425.766f, 373.563f, 426.949f, 373.25f, 427.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 15, 190.246f, 437.75f, 190.246f, 437.75f, 172.195f, 426.727f, 187.246f, 443.75f, 197.34f, 454.156f, 208.25f, 460.746f, 208.25f, 460.746f, 208.25f, 460.742f, 219.777f, 465.156f, 223.246f, 466.746f, 227.699f, 467.797f, 244.418f, 473.52f, 248.25f, 473.746f, 251.457f, 474.398f, 262.02f, 478.797f, 269.246f, 474.75f, 276.977f, 470, 286.246f, 464.746f, 286.246f, 464.746f, 286.246f, 464.742f, 267.738f, 474.398f, 264.246f, 471.746f, 259.816f, 469.117f, 251.898f, 469.559f, 245.246f, 465.75f, 245.246f, 465.75f, 229.02f, 461.195f, 225.25f, 458.746f, 221.977f, 456.797f, 210.539f, 444.035f, 209.246f, 444.746f, 207.02f, 445.797f, 209.219f, 446.238f, 210.246f, 449.746f, 211.859f, 452.398f, 209.656f, 454.156f, 201.246f, 446.75f, 192.059f, 440.078f, 190.246f, 437.75f, 190.246f, 437.75f, 10, 0, 0, 0, 0, 0, 0, 0, 11, 199.246f, 444.746f, 199.246f, 444.742f, 200.434f, 458.785f, 210.246f, 456.746f, 210.246f, 456.746f, 218.809f, 461.539f, 222.246f, 463.75f, 222.246f, 463.75f, 230.758f, 465.578f, 232.246f, 466.746f, 252.523f, 475.824f, 268.715f, 470.855f, 269.246f, 471.746f, 269.918f, 473.316f, 291.504f, 465.488f, 295.25f, 460.746f, 295.906f, 460.508f, 284.219f, 467.152f, 273.25f, 468.75f, 264.453f, 471.008f, 240.691f, 468.961f, 228.25f, 462.746f, 225.422f, 461.211f, 215.582f, 454.848f, 213.246f, 454.75f, 210.016f, 455.094f, 199.246f, 444.746f, 199.246f, 444.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 11, 194.25f, 416.746f, 194.25f, 416.742f, 177.977f, 418.957f, 196.25f, 420.746f, 196.25f, 420.742f, 216.258f, 422.918f, 220.246f, 428.75f, 220.246f, 428.75f, 235.617f, 438.758f, 238.25f, 438.746f, 241.777f, 439.637f, 274.777f, 447.559f, 275.246f, 449.746f, 275.656f, 452.836f, 281.816f, 452.836f, 283.25f, 451.746f, 285.34f, 451.078f, 284.457f, 449.758f, 281.25f, 448.75f, 278.297f, 447.996f, 243.977f, 429.957f, 237.25f, 428.75f, 229.898f, 427.316f, 217.137f, 418.957f, 212.246f, 417.75f, 206.578f, 416.316f, 194.25f, 416.746f, 194.25f, 416.746f, 10, 0, 0, 0, 0, 0, 0, 0, 11, 216.25f, 424.75f, 216.25f, 424.75f, 206.73f, 425.367f, 216.25f, 426.75f, 216.25f, 426.75f, 225.887f, 430.035f, 228.25f, 432.75f, 228.25f, 432.75f, 235.801f, 438.145f, 237.25f, 438.746f, 238.957f, 438.594f, 254.313f, 442.652f, 254.246f, 443.75f, 254.762f, 445.355f, 292.234f, 459.191f, 297.246f, 455.746f, 300.301f, 453.371f, 289.406f, 455.219f, 279.246f, 450.75f, 277.32f, 449.684f, 240.082f, 433.637f, 236.25f, 432.75f, 232.871f, 432.285f, 226.34f, 428.008f, 223.246f, 427.746f, 220.934f, 426.656f, 216.25f, 424.75f, 216.25f, 424.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 207.25f, 421.75f, 207.25f, 421.75f, 213.18f, 422.477f, 212.246f, 420.746f, 210.539f, 418.957f, 208.25f, 419.75f, 208.25f, 419.75f, 207.25f, 421.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 199.246f, 420.746f, 199.246f, 420.742f, 205.258f, 420.719f, 204.25f, 418.746f, 202.617f, 417.195f, 200.246f, 417.75f, 200.246f, 417.75f, 199.246f, 420.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 188.246f, 418.746f, 188.246f, 418.742f, 193.816f, 418.957f, 192.25f, 416.746f, 191.18f, 415.438f, 188.246f, 416.746f, 188.246f, 416.746f, 188.246f, 418.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 179.246f, 417.75f, 179.246f, 417.75f, 185.457f, 418.078f, 184.25f, 416.746f, 182.816f, 414.559f, 180.246f, 415.75f, 180.246f, 415.75f, 179.246f, 417.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 222.246f, 460.746f, 222.246f, 460.742f, 226.816f, 461.195f, 225.25f, 459.75f, 224.18f, 457.676f, 220.246f, 457.75f, 220.246f, 457.75f, 222.246f, 460.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 211.246f, 454.75f, 211.246f, 454.75f, 218.133f, 457.391f, 215.25f, 453.746f, 214.059f, 451.957f, 211.246f, 452.75f, 211.246f, 452.75f, 211.246f, 454.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 198.246f, 449.746f, 198.246f, 449.742f, 204.379f, 450.195f, 203.25f, 448.75f, 201.738f, 446.676f, 199.246f, 447.746f, 199.246f, 447.746f, 198.246f, 449.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 190.246f, 442.746f, 190.246f, 442.742f, 196.02f, 443.598f, 194.25f, 441.75f, 193.379f, 440.078f, 190.246f, 440.746f, 190.246f, 440.746f, 190.246f, 442.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 183.25f, 437.75f, 183.25f, 437.75f, 188.539f, 438.316f, 187.246f, 436.746f, 185.898f, 434.797f, 183.25f, 435.75f, 183.25f, 435.75f, 183.25f, 437.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 226.25f, 430.75f, 226.25f, 430.75f, 233.422f, 431.426f, 231.246f, 428.75f, 229.906f, 426.742f, 226.25f, 427.746f, 226.25f, 427.746f, 226.25f, 430.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 237.25f, 435.75f, 237.25f, 435.75f, 244.863f, 436.707f, 243.246f, 434.746f, 241.348f, 432.02f, 238.25f, 432.75f, 238.25f, 432.75f, 237.25f, 435.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 249.25f, 440.746f, 249.25f, 440.742f, 256.742f, 441.547f, 255.246f, 438.746f, 253.227f, 436.859f, 249.25f, 437.75f, 249.25f, 437.75f, 249.25f, 440.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 260.25f, 445.746f, 260.25f, 445.746f, 268.18f, 446.824f, 266.246f, 444.746f, 264.668f, 442.141f, 261.25f, 443.75f, 261.25f, 443.75f, 260.25f, 445.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 230.25f, 465.75f, 230.25f, 465.75f, 237.82f, 466.625f, 236.25f, 464.746f, 234.309f, 461.941f, 230.25f, 461.75f, 230.25f, 461.75f, 230.25f, 465.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 241.25f, 469.746f, 241.25f, 469.746f, 248.82f, 470.145f, 247.246f, 467.746f, 245.309f, 465.461f, 240.25f, 465.75f, 240.25f, 465.75f, 241.25f, 469.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 216.25f, 425.746f, 216.25f, 425.746f, 221.977f, 425.996f, 220.246f, 423.746f, 219.34f, 422.477f, 216.25f, 423.746f, 216.25f, 423.746f, 216.25f, 425.746f, 10, 0, 0.6f, 0.15f, 0, 0.6f, 0.15f, 0, 5, 135.25f, 534.75f, 135.25f, 534.75f, 130.898f, 525, 130.25f, 521.746f, 130.25f, 521.742f, 131.34f, 531.156f, 132.246f, 533.746f, 133.977f, 535.559f, 135.25f, 534.75f, 135.25f, 534.75f, 10, 0, 0.6f, 0.15f, 0, 0.6f, 0.15f, 0, 5, 115.246f, 519.746f, 115.242f, 519.742f, 111.977f, 503.438f, 112.25f, 500.746f, 112.25f, 500.746f, 111.098f, 513.117f, 111.246f, 514.75f, 111.977f, 515.758f, 115.246f, 519.746f, 115.246f, 519.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 6, 138.246f, 601.746f, 138.246f, 597.75f, 135.25f, 597.75f, 135.25f, 597.75f, 151.359f, 583.738f, 152.25f, 575.75f, 152.25f, 575.75f, 152.898f, 584.398f, 138.246f, 601.746f, 10, 0, 0, 0, 0, 0, 0, 0, 28, 143.246f, 599.75f, 142.285f, 600.402f, 142.527f, 601.223f, 142.246f, 601.746f, 141.188f, 602.078f, 143.508f, 602.141f, 143.246f, 602.75f, 142.836f, 604.254f, 143.039f, 604.277f, 143.246f, 605.746f, 142.844f, 606.34f, 143.488f, 608.031f, 144.246f, 608.75f, 145.504f, 610.332f, 144.047f, 613.559f, 146.25f, 615.75f, 146.188f, 615.582f, 146.598f, 616.191f, 147.25f, 616.746f, 147.637f, 617.711f, 148.941f, 618.246f, 150.246f, 618.746f, 150.336f, 619.461f, 150.113f, 620.371f, 150.246f, 620.746f, 151.523f, 620.141f, 152.891f, 620.285f, 153.246f, 619.75f, 152.715f, 617.027f, 151.254f, 615.137f, 150.246f, 613.75f, 150.344f, 612.527f, 149.84f, 611.828f, 149.246f, 610.75f, 148.059f, 608.336f, 148.266f, 605.207f, 148.246f, 601.746f, 148.07f, 601.992f, 147.73f, 601.906f, 147.25f, 601.746f, 148.129f, 599.277f, 148.77f, 596.859f, 149.246f, 594.746f, 150.141f, 593.387f, 150.66f, 592.402f, 151.25f, 591.75f, 150.945f, 590.625f, 151.059f, 589.711f, 150.246f, 588.75f, 152.848f, 585.754f, 151.41f, 582.84f, 152.25f, 578.75f, 152.922f, 578.27f, 154.785f, 576.164f, 154.246f, 576.746f, 151.512f, 577.297f, 151.387f, 577.734f, 151.25f, 578.75f, 151.031f, 579.25f, 150.668f, 580.762f, 150.246f, 581.746f, 150.336f, 581.605f, 150.148f, 583.68f, 150.246f, 583.746f, 148.398f, 586.434f, 149.895f, 586.238f, 148.246f, 588.75f, 146.816f, 589.582f, 145.754f, 590.797f, 144.246f, 591.75f, 144.301f, 592.297f, 145.559f, 593.094f, 145.25f, 593.75f, 144.156f, 594.746f, 142.887f, 595.59f, 143.246f, 596.746f, 143.43f, 597.992f, 143.578f, 599.156f, 143.246f, 599.75f, 10, 0, 0, 0, 0, 0, 0, 0, 11, 139.246f, 597.75f, 139.246f, 597.75f, 139.258f, 590.559f, 142.246f, 588.75f, 144.539f, 587.039f, 143.219f, 587.918f, 139.246f, 588.75f, 136.18f, 590.559f, 137.246f, 591.75f, 137.246f, 591.75f, 137.242f, 591.75f, 134.418f, 591, 137.246f, 588.75f, 139.699f, 586.598f, 143.656f, 583.957f, 142.246f, 583.746f, 140.137f, 583.957f, 131.777f, 588.359f, 132.246f, 591.75f, 131.777f, 594.52f, 130.25f, 598.746f, 130.25f, 598.746f, 130.25f, 598.742f, 131.887f, 599.906f, 137.246f, 599.75f, 137.242f, 599.75f, 138.707f, 599.027f, 139.246f, 597.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 138.246f, 577.75f, 138.246f, 577.75f, 128.566f, 580.648f, 108.25f, 576.746f, 108.25f, 576.742f, 118.172f, 579.203f, 139.246f, 576.746f, 150.148f, 575.324f, 138.246f, 577.75f, 138.246f, 577.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 138.246f, 577.75f, 138.246f, 577.75f, 128.566f, 580.648f, 108.25f, 576.746f, 108.25f, 576.742f, 118.172f, 579.203f, 139.246f, 576.746f, 150.148f, 575.324f, 138.246f, 577.75f, 138.246f, 577.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 140.25f, 577.75f, 140.25f, 577.75f, 131.176f, 581.527f, 110.246f, 579.746f, 110.246f, 579.746f, 120.695f, 580.984f, 141.25f, 576.746f, 152.215f, 574.355f, 140.25f, 577.75f, 140.25f, 577.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 140.25f, 577.75f, 140.25f, 577.75f, 131.176f, 581.527f, 110.246f, 579.746f, 110.246f, 579.746f, 120.695f, 580.984f, 141.25f, 576.746f, 152.215f, 574.355f, 140.25f, 577.75f, 140.25f, 577.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 142.246f, 577.75f, 142.242f, 577.75f, 133.453f, 582.086f, 113.25f, 581.746f, 113.25f, 581.746f, 122.965f, 582.328f, 143.246f, 576.746f, 153.902f, 573.371f, 142.246f, 577.75f, 142.246f, 577.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 142.246f, 577.75f, 142.242f, 577.75f, 133.453f, 582.086f, 113.25f, 581.746f, 113.25f, 581.746f, 122.965f, 582.328f, 143.246f, 576.746f, 153.902f, 573.371f, 142.246f, 577.75f, 142.246f, 577.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 143.246f, 577.75f, 143.242f, 577.75f, 136.102f, 582.047f, 117.25f, 583.746f, 117.25f, 583.742f, 126.715f, 583.066f, 144.246f, 576.746f, 153.77f, 572.66f, 143.246f, 577.75f, 143.246f, 577.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 143.246f, 577.75f, 143.242f, 577.75f, 136.102f, 582.047f, 117.25f, 583.746f, 117.25f, 583.742f, 126.715f, 583.066f, 144.246f, 576.746f, 153.77f, 572.66f, 143.246f, 577.75f, 143.246f, 577.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 270.25f, 565.746f, 270.25f, 565.742f, 269.398f, 565.031f, 269.246f, 566.75f, 269.871f, 567.629f, 300.898f, 582.117f, 305.25f, 581.746f, 305.25f, 581.746f, 271.602f, 567.316f, 270.25f, 565.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 270.25f, 565.746f, 270.25f, 565.742f, 269.398f, 565.031f, 269.246f, 566.75f, 269.871f, 567.629f, 300.898f, 582.117f, 305.25f, 581.746f, 305.25f, 581.746f, 271.602f, 567.316f, 270.25f, 565.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 266.246f, 564.75f, 266.246f, 564.75f, 265.727f, 564.25f, 266.246f, 565.746f, 265.992f, 566.879f, 295.785f, 583.758f, 300.246f, 583.746f, 300.246f, 583.742f, 267.742f, 566.699f, 266.246f, 564.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 266.246f, 564.75f, 266.246f, 564.75f, 265.727f, 564.25f, 266.246f, 565.746f, 265.992f, 566.879f, 295.785f, 583.758f, 300.246f, 583.746f, 300.246f, 583.742f, 267.742f, 566.699f, 266.246f, 564.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 263.25f, 563.746f, 263.25f, 563.742f, 262.164f, 562.676f, 262.25f, 563.746f, 262.254f, 565.316f, 284.055f, 582.363f, 295.25f, 584.75f, 295.25f, 584.75f, 275.016f, 575.484f, 263.25f, 563.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 263.25f, 563.746f, 263.25f, 563.742f, 262.164f, 562.676f, 262.25f, 563.746f, 262.254f, 565.316f, 284.055f, 582.363f, 295.25f, 584.75f, 295.25f, 584.75f, 275.016f, 575.484f, 263.25f, 563.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 260.25f, 561.746f, 260.25f, 561.742f, 259.09f, 560.711f, 259.25f, 561.746f, 259.176f, 563.086f, 278.793f, 578.43f, 288.246f, 580.75f, 288.246f, 580.75f, 270.656f, 572.238f, 260.25f, 561.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 260.25f, 561.746f, 260.25f, 561.742f, 259.09f, 560.711f, 259.25f, 561.746f, 259.176f, 563.086f, 278.793f, 578.43f, 288.246f, 580.75f, 288.246f, 580.75f, 270.656f, 572.238f, 260.25f, 561.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 11, 225.25f, 398.746f, 225.25f, 398.742f, 208.34f, 401.355f, 227.25f, 402.75f, 227.25f, 402.75f, 246.617f, 405.316f, 251.25f, 410.75f, 251.25f, 410.75f, 265.977f, 421.156f, 269.246f, 421.75f, 272.137f, 422.035f, 290.18f, 425.996f, 290.246f, 428.75f, 291.059f, 431.277f, 297.656f, 433.918f, 299.246f, 432.75f, 301.18f, 432.156f, 301.18f, 422.035f, 298.246f, 420.746f, 295.02f, 420.277f, 274.34f, 412.355f, 267.246f, 410.75f, 260.258f, 409.719f, 247.5f, 401.355f, 242.246f, 399.75f, 236.938f, 398.719f, 225.25f, 398.746f, 225.25f, 398.746f, 10, 0, 0, 0, 0, 0, 0, 0, 11, 305.25f, 439.75f, 305.25f, 439.75f, 302.059f, 438.098f, 300.246f, 434.746f, 300.246f, 434.746f, 293.699f, 423.578f, 278.246f, 419.75f, 278.246f, 419.75f, 252.777f, 410.156f, 244.246f, 407.746f, 244.246f, 407.742f, 229.457f, 402.457f, 221.246f, 403.746f, 221.246f, 403.746f, 213.617f, 403.117f, 220.246f, 401.746f, 220.246f, 401.746f, 242.656f, 403.559f, 246.246f, 405.746f, 246.242f, 405.742f, 263.559f, 411.258f, 267.246f, 413.75f, 270.156f, 416.977f, 290.18f, 422.477f, 292.25f, 424.75f, 295.02f, 426.879f, 305.797f, 436.117f, 305.25f, 439.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 241.25f, 404.75f, 241.25f, 404.75f, 246.52f, 405.445f, 245.246f, 403.746f, 243.984f, 402.035f, 241.25f, 402.75f, 241.25f, 402.75f, 241.25f, 404.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 233.246f, 403.746f, 233.246f, 403.746f, 238.598f, 403.957f, 237.25f, 402.75f, 236.063f, 400.547f, 233.246f, 401.746f, 233.246f, 401.746f, 233.246f, 403.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 221.246f, 402.75f, 221.246f, 402.75f, 227.125f, 402.586f, 226.25f, 400.75f, 224.59f, 399.176f, 222.246f, 399.75f, 222.246f, 399.75f, 221.246f, 402.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 213.246f, 401.746f, 213.242f, 401.746f, 218.73f, 401.984f, 217.25f, 400.75f, 216.191f, 398.578f, 213.246f, 399.75f, 213.246f, 399.75f, 213.246f, 401.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 259.25f, 413.75f, 259.25f, 413.75f, 266.609f, 413.664f, 265.246f, 411.746f, 263.234f, 409.129f, 259.25f, 410.75f, 259.25f, 410.75f, 259.25f, 413.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 270.25f, 417.75f, 270.25f, 417.75f, 276.855f, 421.832f, 276.246f, 416.746f, 275.973f, 413.453f, 271.25f, 415.75f, 271.25f, 415.75f, 270.25f, 417.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 280.246f, 421.75f, 280.242f, 421.75f, 288.223f, 425.367f, 286.246f, 419.75f, 285.457f, 416.664f, 281.25f, 418.746f, 281.25f, 418.746f, 280.246f, 421.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 291.246f, 426.75f, 291.242f, 426.75f, 295.605f, 431.996f, 297.246f, 424.75f, 297.227f, 421.875f, 291.246f, 423.746f, 291.246f, 423.746f, 291.246f, 426.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 249.25f, 408.75f, 249.25f, 408.75f, 255.266f, 408.652f, 254.246f, 406.75f, 252.73f, 405.242f, 250.25f, 405.746f, 250.25f, 405.746f, 249.25f, 408.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 288.246f, 541.746f, 288.246f, 541.742f, 287.875f, 541.203f, 288.246f, 542.75f, 287.875f, 543.559f, 307.109f, 558.148f, 317.25f, 559.746f, 317.25f, 559.746f, 299.125f, 552.27f, 288.246f, 541.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 288.246f, 541.746f, 288.246f, 541.742f, 287.875f, 541.203f, 288.246f, 542.75f, 287.875f, 543.559f, 307.109f, 558.148f, 317.25f, 559.746f, 317.25f, 559.746f, 299.125f, 552.27f, 288.246f, 541.746f, 10, 0, 0, 0, 0, 0, 0, 0, 10, 292.25f, 471.746f, 292.25f, 471.742f, 316.141f, 447.117f, 326.25f, 442.746f, 326.25f, 442.742f, 336.379f, 430.836f, 332.246f, 401.746f, 332.246f, 401.746f, 328.461f, 393.879f, 325.25f, 416.746f, 325.25f, 416.742f, 328.461f, 444.477f, 316.25f, 426.75f, 316.25f, 426.75f, 306.898f, 437.766f, 314.25f, 437.75f, 314.25f, 437.75f, 317.461f, 435.238f, 318.25f, 436.746f, 318.34f, 438.758f, 309.539f, 453.719f, 290.246f, 469.746f, 271.699f, 485.398f, 292.25f, 471.746f, 292.25f, 471.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 227.25f, 428.75f, 227.25f, 428.75f, 227.477f, 431.059f, 229.25f, 429.746f, 231.438f, 429.297f, 335.059f, 422.477f, 370.25f, 395.75f, 370.25f, 395.75f, 320.098f, 421.598f, 227.25f, 428.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 227.25f, 428.75f, 227.25f, 428.75f, 227.477f, 431.059f, 229.25f, 429.746f, 231.438f, 429.297f, 335.059f, 422.477f, 370.25f, 395.75f, 370.25f, 395.75f, 320.098f, 421.598f, 227.25f, 428.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 239.25f, 433.746f, 239.25f, 433.742f, 238.918f, 435.898f, 241.25f, 434.746f, 242.879f, 434.137f, 393.141f, 435.238f, 419.246f, 399.75f, 419.246f, 399.75f, 394.898f, 427.316f, 239.25f, 433.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 239.25f, 433.746f, 239.25f, 433.742f, 238.918f, 435.898f, 241.25f, 434.746f, 242.879f, 434.137f, 393.141f, 435.238f, 419.246f, 399.75f, 419.246f, 399.75f, 394.898f, 427.316f, 239.25f, 433.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 252.25f, 438.746f, 252.25f, 438.742f, 251.68f, 440.297f, 253.246f, 439.75f, 255.637f, 438.535f, 446.379f, 452.836f, 472.25f, 416.746f, 472.25f, 416.742f, 461.777f, 445.355f, 252.25f, 438.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 252.25f, 438.746f, 252.25f, 438.742f, 251.68f, 440.297f, 253.246f, 439.75f, 255.637f, 438.535f, 446.379f, 452.836f, 472.25f, 416.746f, 472.25f, 416.742f, 461.777f, 445.355f, 252.25f, 438.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 263.25f, 443.75f, 263.25f, 443.75f, 262.68f, 445.578f, 264.246f, 444.746f, 266.637f, 443.816f, 401.059f, 486.277f, 427.25f, 450.75f, 427.25f, 450.75f, 412.277f, 477.699f, 263.25f, 443.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 263.25f, 443.75f, 263.25f, 443.75f, 262.68f, 445.578f, 264.246f, 444.746f, 266.637f, 443.816f, 401.059f, 486.277f, 427.25f, 450.75f, 427.25f, 450.75f, 412.277f, 477.699f, 263.25f, 443.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 199.246f, 418.746f, 199.246f, 418.742f, 198.879f, 420.496f, 201.246f, 419.75f, 202.84f, 418.738f, 222.418f, 416.316f, 224.246f, 373.75f, 224.242f, 373.75f, 216.699f, 419.836f, 199.246f, 418.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 199.246f, 418.746f, 199.246f, 418.742f, 198.879f, 420.496f, 201.246f, 419.75f, 202.84f, 418.738f, 222.418f, 416.316f, 224.246f, 373.75f, 224.242f, 373.75f, 216.699f, 419.836f, 199.246f, 418.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 189.246f, 416.746f, 189.246f, 416.742f, 189.199f, 418.738f, 191.246f, 417.75f, 193.156f, 416.977f, 208.777f, 422.035f, 205.25f, 379.746f, 205.25f, 379.746f, 207.02f, 418.078f, 189.246f, 416.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 189.246f, 416.746f, 189.246f, 416.742f, 189.199f, 418.738f, 191.246f, 417.75f, 193.156f, 416.977f, 208.777f, 422.035f, 205.25f, 379.746f, 205.25f, 379.746f, 207.02f, 418.078f, 189.246f, 416.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 180.246f, 416.746f, 180.242f, 416.742f, 180.398f, 418.297f, 182.25f, 417.75f, 184.359f, 416.535f, 201.297f, 415.879f, 187.246f, 390.746f, 187.246f, 390.746f, 198.219f, 417.637f, 180.246f, 416.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 180.246f, 416.746f, 180.242f, 416.742f, 180.398f, 418.297f, 182.25f, 417.75f, 184.359f, 416.535f, 201.297f, 415.879f, 187.246f, 390.746f, 187.246f, 390.746f, 198.219f, 417.637f, 180.246f, 416.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 232.246f, 464.746f, 232.246f, 464.742f, 232.187f, 462.887f, 234.246f, 463.75f, 251.566f, 478.113f, 287.254f, 542.906f, 348.25f, 548.746f, 348.25f, 548.746f, 306.367f, 562.426f, 232.246f, 464.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 232.246f, 464.746f, 232.246f, 464.742f, 232.187f, 462.887f, 234.246f, 463.75f, 251.566f, 478.113f, 287.254f, 542.906f, 348.25f, 548.746f, 348.25f, 548.746f, 306.367f, 562.426f, 232.246f, 464.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 245.246f, 466.746f, 245.246f, 466.742f, 243.496f, 468.379f, 245.246f, 468.75f, 247.605f, 469.754f, 371.293f, 549.508f, 414.25f, 540.75f, 414.25f, 540.75f, 384.688f, 549.004f, 245.246f, 466.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 245.246f, 466.746f, 245.246f, 466.742f, 243.496f, 468.379f, 245.246f, 468.75f, 247.605f, 469.754f, 371.293f, 549.508f, 414.25f, 540.75f, 414.25f, 540.75f, 384.688f, 549.004f, 245.246f, 466.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 222.246f, 460.746f, 222.246f, 460.742f, 221.512f, 458.594f, 223.246f, 459.75f, 233.266f, 465.301f, 237.242f, 528.234f, 285.25f, 529.75f, 285.25f, 529.75f, 249.523f, 545.801f, 222.246f, 460.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 222.246f, 460.746f, 222.246f, 460.742f, 221.512f, 458.594f, 223.246f, 459.75f, 233.266f, 465.301f, 237.242f, 528.234f, 285.25f, 529.75f, 285.25f, 529.75f, 249.523f, 545.801f, 222.246f, 460.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 200.246f, 447.746f, 200.246f, 447.746f, 198.973f, 446.812f, 201.246f, 446.75f, 212.391f, 448.555f, 235.937f, 493.953f, 285.25f, 488.746f, 285.25f, 488.742f, 249.656f, 504.148f, 200.246f, 447.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 200.246f, 447.746f, 200.246f, 447.746f, 198.973f, 446.812f, 201.246f, 446.75f, 212.391f, 448.555f, 235.937f, 493.953f, 285.25f, 488.746f, 285.25f, 488.742f, 249.656f, 504.148f, 200.246f, 447.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 212.246f, 454.75f, 212.246f, 454.75f, 211.625f, 453.348f, 213.246f, 453.746f, 224.461f, 457.637f, 238.852f, 506.711f, 288.246f, 510.746f, 288.246f, 510.742f, 250.363f, 519.348f, 212.246f, 454.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 212.246f, 454.75f, 212.246f, 454.75f, 211.625f, 453.348f, 213.246f, 453.746f, 224.461f, 457.637f, 238.852f, 506.711f, 288.246f, 510.746f, 288.246f, 510.742f, 250.363f, 519.348f, 212.246f, 454.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 192.25f, 442.746f, 192.25f, 442.742f, 191.453f, 441.449f, 193.25f, 441.75f, 202.32f, 442.863f, 221.395f, 479.633f, 261.25f, 474.75f, 261.25f, 474.75f, 232.508f, 487.891f, 192.25f, 442.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 192.25f, 442.746f, 192.25f, 442.742f, 191.453f, 441.449f, 193.25f, 441.75f, 202.32f, 442.863f, 221.395f, 479.633f, 261.25f, 474.75f, 261.25f, 474.75f, 232.508f, 487.891f, 192.25f, 442.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 184.25f, 435.75f, 184.25f, 435.75f, 182.949f, 434.945f, 184.25f, 434.746f, 189.281f, 435.414f, 222.984f, 471.801f, 243.246f, 454.75f, 243.246f, 454.75f, 230.082f, 475.344f, 184.25f, 435.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 184.25f, 435.75f, 184.25f, 435.75f, 182.949f, 434.945f, 184.25f, 434.746f, 189.281f, 435.414f, 222.984f, 471.801f, 243.246f, 454.75f, 243.246f, 454.75f, 230.082f, 475.344f, 184.25f, 435.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 260.25f, 470.75f, 260.25f, 470.75f, 259.219f, 472.699f, 261.25f, 472.75f, 263.469f, 473.547f, 396.242f, 537.031f, 438.25f, 522.746f, 438.25f, 522.746f, 409.465f, 534.84f, 260.25f, 470.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 260.25f, 470.75f, 260.25f, 470.75f, 259.219f, 472.699f, 261.25f, 472.75f, 263.469f, 473.547f, 396.242f, 537.031f, 438.25f, 522.746f, 438.25f, 522.746f, 409.465f, 534.84f, 260.25f, 470.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 223.246f, 400.75f, 223.246f, 400.75f, 223.52f, 402.457f, 225.25f, 401.746f, 227.477f, 400.695f, 244.418f, 400.035f, 231.246f, 375.75f, 231.246f, 375.75f, 241.34f, 401.797f, 223.246f, 400.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 223.246f, 400.75f, 223.246f, 400.75f, 223.52f, 402.457f, 225.25f, 401.746f, 227.477f, 400.695f, 244.418f, 400.035f, 231.246f, 375.75f, 231.246f, 375.75f, 241.34f, 401.797f, 223.246f, 400.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 235.246f, 401.746f, 235.242f, 401.746f, 234.957f, 404.219f, 237.25f, 403.746f, 238.918f, 402.457f, 258.5f, 400.035f, 260.25f, 357.746f, 260.25f, 357.746f, 252.777f, 403.559f, 235.246f, 401.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 235.246f, 401.746f, 235.242f, 401.746f, 234.957f, 404.219f, 237.25f, 403.746f, 238.918f, 402.457f, 258.5f, 400.035f, 260.25f, 357.746f, 260.25f, 357.746f, 252.777f, 403.559f, 235.246f, 401.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 242.246f, 403.746f, 242.246f, 403.746f, 242.437f, 405.977f, 244.246f, 404.75f, 246.398f, 404.219f, 273.457f, 400.477f, 299.246f, 364.75f, 299.246f, 364.75f, 260.258f, 405.316f, 242.246f, 403.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 242.246f, 403.746f, 242.246f, 403.746f, 242.437f, 405.977f, 244.246f, 404.75f, 246.398f, 404.219f, 273.457f, 400.477f, 299.246f, 364.75f, 299.246f, 364.75f, 260.258f, 405.316f, 242.246f, 403.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 251.25f, 405.746f, 251.25f, 405.742f, 250.566f, 408.164f, 252.25f, 407.746f, 254.723f, 406.945f, 277.199f, 409.031f, 319.25f, 371.75f, 319.25f, 371.75f, 268.316f, 409.875f, 251.25f, 405.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 251.25f, 405.746f, 251.25f, 405.742f, 250.566f, 408.164f, 252.25f, 407.746f, 254.723f, 406.945f, 277.199f, 409.031f, 319.25f, 371.75f, 319.25f, 371.75f, 268.316f, 409.875f, 251.25f, 405.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 262.25f, 410.75f, 262.25f, 410.75f, 262.004f, 413.004f, 264.246f, 412.746f, 266.164f, 411.785f, 304.48f, 406.832f, 361.25f, 368.746f, 361.25f, 368.746f, 279.754f, 414.715f, 262.25f, 410.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 262.25f, 410.75f, 262.25f, 410.75f, 262.004f, 413.004f, 264.246f, 412.746f, 266.164f, 411.785f, 304.48f, 406.832f, 361.25f, 368.746f, 361.25f, 368.746f, 279.754f, 414.715f, 262.25f, 410.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 218.25f, 423.746f, 218.25f, 423.746f, 217.797f, 425.777f, 220.246f, 424.75f, 221.758f, 424.016f, 280.5f, 421.156f, 314.25f, 391.75f, 314.25f, 391.75f, 275.547f, 418.93f, 218.25f, 423.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 218.25f, 423.746f, 218.25f, 423.746f, 217.797f, 425.777f, 220.246f, 424.75f, 221.758f, 424.016f, 280.5f, 421.156f, 314.25f, 391.75f, 314.25f, 391.75f, 275.547f, 418.93f, 218.25f, 423.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 207.25f, 419.75f, 207.25f, 419.75f, 206.797f, 421.379f, 209.246f, 420.746f, 210.758f, 419.617f, 237.816f, 415.879f, 264.246f, 379.746f, 264.246f, 379.746f, 224.617f, 420.719f, 207.25f, 419.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 207.25f, 419.75f, 207.25f, 419.75f, 206.797f, 421.379f, 209.246f, 420.746f, 210.758f, 419.617f, 237.816f, 415.879f, 264.246f, 379.746f, 264.246f, 379.746f, 224.617f, 420.719f, 207.25f, 419.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 274.25f, 415.75f, 274.25f, 415.75f, 273.828f, 418.031f, 276.246f, 417.75f, 278.066f, 417.125f, 316.645f, 414.992f, 376.246f, 380.75f, 376.246f, 380.75f, 290.746f, 418.625f, 274.25f, 415.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 274.25f, 415.75f, 274.25f, 415.75f, 273.828f, 418.031f, 276.246f, 417.75f, 278.066f, 417.125f, 316.645f, 414.992f, 376.246f, 380.75f, 376.246f, 380.75f, 290.746f, 418.625f, 274.25f, 415.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 283.25f, 418.746f, 283.25f, 418.742f, 283.07f, 420.672f, 285.25f, 419.75f, 287.309f, 419.762f, 325.883f, 417.633f, 385.25f, 383.746f, 385.25f, 383.742f, 300.648f, 421.703f, 283.25f, 418.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 283.25f, 418.746f, 283.25f, 418.742f, 283.07f, 420.672f, 285.25f, 419.75f, 287.309f, 419.762f, 325.883f, 417.633f, 385.25f, 383.746f, 385.25f, 383.742f, 300.648f, 421.703f, 283.25f, 418.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, 294.25f, 424.75f, 294.25f, 424.75f, 293.629f, 426.172f, 296.25f, 425.746f, 297.867f, 425.262f, 345.242f, 420.492f, 444.246f, 382.75f, 444.246f, 382.75f, 311.207f, 427.203f, 294.25f, 424.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 294.25f, 424.75f, 294.25f, 424.75f, 293.629f, 426.172f, 296.25f, 425.746f, 297.867f, 425.262f, 345.242f, 420.492f, 444.246f, 382.75f, 444.246f, 382.75f, 311.207f, 427.203f, 294.25f, 424.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 172.25f, 416.746f, 172.25f, 416.742f, 177.539f, 417.195f, 176.246f, 415.75f, 174.898f, 413.676f, 172.25f, 414.746f, 172.25f, 414.746f, 172.25f, 416.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 205.25f, 401.746f, 205.25f, 401.746f, 211.418f, 401.797f, 210.246f, 399.75f, 208.777f, 398.277f, 206.25f, 398.746f, 206.25f, 398.746f, 205.25f, 401.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 196.25f, 401.746f, 196.25f, 401.746f, 201.738f, 402.238f, 200.246f, 400.75f, 199.098f, 398.719f, 196.25f, 399.75f, 196.25f, 399.75f, 196.25f, 401.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 91.25f, 414.746f, 91.25f, 414.746f, 96.6602f, 413.344f, 95.25f, 411.746f, 93.0156f, 410.879f, 91.25f, 412.746f, 91.25f, 412.746f, 91.25f, 414.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 93.2461f, 425.746f, 93.2422f, 425.746f, 98.8633f, 423.902f, 97.25f, 422.746f, 95.2148f, 421.441f, 93.2461f, 422.746f, 93.2461f, 422.746f, 93.2461f, 425.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, 85.25f, 429.746f, 85.25f, 429.742f, 90.9414f, 428.742f, 89.2461f, 427.746f, 87.2969f, 426.281f, 85.25f, 427.746f, 85.25f, 427.746f, 85.25f, 429.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 173.25f, 347.75f, 173.25f, 347.75f, 171.379f, 347.676f, 167.25f, 345.75f, 164.777f, 345.477f, 152.457f, 341.516f, 146.25f, 330.746f, 146.25f, 330.742f, 159.938f, 341.078f, 173.25f, 347.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 9, 269.246f, 245.746f, 269.781f, 245.484f, 269.84f, 245.02f, 270.25f, 244.75f, 270.887f, 244.957f, 272.242f, 244.625f, 272.25f, 245.746f, 271.172f, 250.063f, 270.211f, 255.492f, 265.246f, 257.746f, 264.961f, 257.789f, 263.375f, 257.332f, 263.25f, 256.746f, 263.156f, 254.684f, 263.027f, 253.203f, 263.25f, 251.75f, 263.695f, 250.027f, 266.07f, 250.016f, 267.246f, 251.75f, 268.109f, 249.699f, 268.582f, 247.672f, 269.246f, 245.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 9, 257.246f, 240.75f, 258.262f, 239.004f, 258.121f, 236.961f, 259.25f, 236.746f, 260.492f, 236.016f, 262.527f, 237.09f, 262.25f, 238.746f, 261.188f, 240.539f, 260.758f, 243, 259.25f, 244.75f, 259.012f, 245.281f, 259.277f, 245.867f, 259.25f, 245.746f, 258.445f, 247.57f, 257.188f, 248.379f, 255.246f, 247.746f, 254.41f, 245.594f, 255.676f, 243.25f, 257.246f, 241.75f, 257.5f, 241.203f, 257.316f, 240.793f, 257.246f, 240.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 11, 214.25f, 246.746f, 213.758f, 246.684f, 213.719f, 247.191f, 214.25f, 247.746f, 214.484f, 248.68f, 215.355f, 249.914f, 215.25f, 250.746f, 214.602f, 252.203f, 213.375f, 252, 212.246f, 251.75f, 211.41f, 250.281f, 211.355f, 248.273f, 210.246f, 246.746f, 210.379f, 246.355f, 210.438f, 245.723f, 210.246f, 245.746f, 209.43f, 244.832f, 208.945f, 243.152f, 209.246f, 242.75f, 209.109f, 242.18f, 208.91f, 231.281f, 209.246f, 231.75f, 209.836f, 232.379f, 213.188f, 243.086f, 213.246f, 243.75f, 213.328f, 244.871f, 214.133f, 245.383f, 214.25f, 246.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 7, 185.25f, 253.75f, 188.574f, 256.488f, 191.641f, 259.746f, 191.246f, 263.75f, 191.027f, 264.902f, 189.074f, 264.32f, 189.246f, 263.75f, 187.988f, 259.402f, 185.746f, 256.477f, 183.25f, 253.75f, 180.504f, 251.594f, 178.457f, 244.617f, 178.246f, 243.75f, 182.266f, 249.84f, 184.746f, 252.859f, 185.25f, 253.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 10, 170.246f, 260.746f, 171.32f, 260.707f, 170.988f, 261.246f, 171.246f, 261.746f, 172.273f, 263.215f, 173.707f, 264.586f, 173.25f, 266.746f, 173.73f, 266.805f, 173.313f, 267.145f, 173.25f, 266.746f, 172.641f, 266.695f, 172.262f, 266.551f, 172.25f, 266.746f, 169.91f, 263.715f, 168.371f, 260.777f, 167.25f, 257.746f, 166.582f, 257.289f, 165.324f, 252.352f, 165.246f, 251.75f, 165.934f, 252.133f, 167.824f, 256.734f, 168.25f, 256.746f, 169.445f, 257.613f, 169.457f, 259.391f, 170.246f, 260.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 6, 189.246f, 238.746f, 189.641f, 239.758f, 191.371f, 241.078f, 191.246f, 241.75f, 191.117f, 243.078f, 191.633f, 244.664f, 190.246f, 243.75f, 189.246f, 242.867f, 185.453f, 241.383f, 185.25f, 234.746f, 185.129f, 234.363f, 188.398f, 237.328f, 189.246f, 238.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 19, 205.25f, 257.746f, 205.477f, 258.434f, 206.258f, 257.91f, 207.25f, 257.746f, 207.473f, 258.609f, 208.148f, 259.223f, 208.25f, 259.746f, 209.535f, 262.301f, 211.48f, 264.305f, 211.246f, 266.746f, 209.996f, 268.484f, 209.25f, 266.238f, 208.25f, 264.746f, 207.102f, 266.988f, 206.004f, 264.926f, 204.25f, 264.746f, 204.496f, 264.324f, 204.262f, 264.707f, 204.25f, 264.746f, 202.887f, 264.195f, 202.137f, 263.004f, 201.246f, 261.746f, 200.852f, 261.996f, 200.406f, 262.195f, 200.246f, 261.746f, 199.527f, 261.383f, 198.457f, 261.027f, 198.246f, 260.746f, 196.93f, 257.297f, 193.473f, 254.992f, 191.246f, 246.746f, 191.816f, 245.695f, 196.359f, 254.004f, 197.25f, 254.75f, 197.816f, 256.086f, 197.945f, 252.945f, 199.246f, 253.75f, 199.406f, 253.707f, 199.609f, 253.445f, 200.246f, 253.75f, 199.973f, 253.605f, 200.211f, 253.855f, 200.246f, 253.75f, 200.637f, 254.176f, 200.492f, 254.789f, 200.246f, 254.75f, 202.074f, 256.039f, 201.98f, 257.215f, 203.25f, 258.746f, 203.344f, 257.711f, 204.508f, 258.5f, 205.25f, 257.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 6, 271.25f, 197.75f, 271.25f, 197.75f, 278.957f, 220.297f, 274.25f, 232.75f, 274.25f, 232.75f, 286.656f, 208.855f, 281.25f, 196.75f, 281.25f, 196.75f, 281.156f, 207.977f, 277.246f, 213.746f, 277.246f, 213.746f, 272.359f, 199.398f, 271.25f, 197.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 254.246f, 200.75f, 254.246f, 200.75f, 260.477f, 210.398f, 251.25f, 230.75f, 251.25f, 230.75f, 250.797f, 208.195f, 243.246f, 195.746f, 243.246f, 195.742f, 258.937f, 218.316f, 254.246f, 200.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 243.246f, 202.746f, 243.246f, 202.746f, 243.316f, 224.918f, 244.246f, 227.746f, 244.246f, 227.742f, 239.578f, 209.957f, 228.25f, 199.75f, 228.25f, 199.75f, 244.199f, 212.598f, 243.246f, 202.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 233.246f, 237.746f, 233.246f, 237.746f, 239.578f, 223.156f, 228.25f, 202.746f, 228.25f, 202.746f, 235.617f, 216.336f, 230.25f, 223.746f, 230.25f, 223.746f, 233.199f, 227.777f, 233.246f, 237.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 8, 212.246f, 203.746f, 212.246f, 203.746f, 210.758f, 220.516f, 212.246f, 222.75f, 212.246f, 222.75f, 212.957f, 229.977f, 212.246f, 230.75f, 212.246f, 230.75f, 216.918f, 237.898f, 217.25f, 229.75f, 217.25f, 229.75f, 218.68f, 221.176f, 222.246f, 215.746f, 222.246f, 215.746f, 225.719f, 210.176f, 225.25f, 202.746f, 225.25f, 202.746f, 214.5f, 236.355f, 212.246f, 203.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 208.25f, 233.75f, 208.25f, 233.75f, 200.637f, 221.836f, 198.246f, 200.75f, 198.246f, 200.75f, 197.117f, 207.758f, 201.246f, 223.746f, 201.246f, 223.746f, 205.918f, 240.535f, 208.25f, 233.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 6, 184.25f, 211.75f, 184.25f, 211.75f, 189.418f, 217.879f, 191.246f, 223.746f, 191.242f, 223.746f, 194.918f, 240.758f, 188.246f, 231.75f, 188.246f, 231.75f, 188.098f, 222.496f, 179.246f, 214.746f, 179.246f, 214.746f, 184.359f, 216.996f, 184.25f, 211.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 6, 177.246f, 217.746f, 177.246f, 217.742f, 181.277f, 236.578f, 182.25f, 237.746f, 182.25f, 237.746f, 184.137f, 241.195f, 181.25f, 237.746f, 181.25f, 237.746f, 171.379f, 216.559f, 167.25f, 209.75f, 167.25f, 209.75f, 175.777f, 219.418f, 177.246f, 217.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 4, 171.246f, 235.746f, 171.246f, 235.746f, 183.918f, 260.336f, 160.246f, 231.75f, 160.246f, 231.75f, 172.039f, 242.738f, 171.246f, 235.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 6, 154.246f, 251.75f, 154.242f, 251.75f, 159.5f, 272.438f, 162.25f, 271.746f, 162.25f, 271.746f, 171.379f, 282.117f, 164.246f, 270.75f, 164.242f, 270.75f, 157.52f, 259.898f, 158.25f, 248.746f, 158.25f, 248.746f, 157.52f, 259.676f, 154.246f, 251.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 517.246f, 264.746f, 517.242f, 264.742f, 504.348f, 275.297f, 501.25f, 278.75f, 501.25f, 278.75f, 516.449f, 258.797f, 516.25f, 250.746f, 516.25f, 250.742f, 519.199f, 259.348f, 517.246f, 264.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 523.25f, 288.746f, 523.25f, 288.742f, 500.5f, 305, 496.25f, 312.75f, 496.25f, 312.75f, 525.797f, 280.797f, 526.246f, 275.746f, 526.242f, 275.742f, 526.348f, 285.75f, 523.25f, 288.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 542.246f, 457.75f, 542.246f, 457.75f, 529.098f, 466.699f, 527.25f, 464.746f, 527.25f, 464.742f, 539, 457.348f, 542.246f, 447.746f, 542.246f, 447.746f, 540.098f, 457.898f, 542.246f, 457.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 4, 551.25f, 369.75f, 532.25f, 382.75f, 532.25f, 382.75f, 553.297f, 363.848f, 554.25f, 359.746f, 551.25f, 369.75f, 10, 1.1f, 0, 0, 0, 0, 0, 0, 2, 122.246f, 393.75f, 146.25f, 388.75f, 10, 1.1f, 0, 0, 0, 0, 0, 0, 2, 177.246f, 215.746f, 177.246f, 215.746f, 176.547f, 219.75f, 166.246f, 207.75f, 10, 1.1f, 0, 0, 0, 0, 0, 0, 2, 183.25f, 210.75f, 183.25f, 210.75f, 185.348f, 217.547f, 178.246f, 212.746f, 10, 1.1f, 0, 0, 0, 0, 0, 0, 2, 242.246f, 200.75f, 242.246f, 200.75f, 244.199f, 213.148f, 231.246f, 198.75f}; userland/host_applications/linux/apps/hello_pi/hello_tiger/tiger.h000066400000000000000000000034161421703157200261010ustar00rootroot00000000000000#ifndef __TIGER_H #define __TIGER_H /*------------------------------------------------------------------------ * * OpenVG 1.0.1 Reference Implementation sample code * ------------------------------------------------- * * Copyright (c) 2007 The Khronos Group Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and /or associated documentation files * (the "Materials "), to deal in the Materials without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Materials, * and to permit persons to whom the Materials are furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Materials. * * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR * THE USE OR OTHER DEALINGS IN THE MATERIALS. * *//** * \file * \brief Header for including the Tiger image data. * \note *//*-------------------------------------------------------------------*/ extern const int tigerCommandCount; extern const char tigerCommands[]; extern const float tigerMinX; extern const float tigerMaxX; extern const float tigerMinY; extern const float tigerMaxY; extern const int tigerPointCount; extern const float tigerPoints[]; #endif /* __TIGER_H */ userland/host_applications/linux/apps/hello_pi/hello_triangle/000077500000000000000000000000001421703157200253055ustar00rootroot00000000000000userland/host_applications/linux/apps/hello_pi/hello_triangle/CMakeLists.txt000066400000000000000000000002751421703157200300510ustar00rootroot00000000000000set(EXEC hello_triangle.bin) set(SRCS triangle.c) add_executable(${EXEC} ${SRCS}) target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) install(TARGETS ${EXEC} RUNTIME DESTINATION bin) userland/host_applications/linux/apps/hello_pi/hello_triangle/Djenne_128_128.raw000077500000000000000000001400001421703157200302050ustar00rootroot00000000000000öÖ¿÷×À÷×ÀøØÁúÚÃúÚÃúÚÃúÚÃùÙÂùÙÂùÙÂùÙÂùÙÂùÙÂùÙÂùÙÂûÙÂûÙÂûÙÂûÙÂúØÁúØÁúØÁúØÁøÖ¿ù×Àù×ÀúØÁúØÁúØÁúØÁúØÁù×Àù×Àù×Àù×À÷Õ¾øÖ¿ù×Àù×À÷Õ¾÷Õ¾÷Õ¾÷Õ¾÷Õ¾÷Õ¾÷Õ¾÷Õ¾÷Õ¾÷Õ¾öÔ½öÔ½÷Õ¾÷Õ¾÷Õ¾÷Õ¾õÓ¼öÔ½öÔ½õÓ¼ôÒ»ôÒ»ôÒ»ôÒ»õкõкõкõкõкõкõкõкôиõѹôиôиòζóÏ·óÏ·õѹñ͵óÏ·óÏ·ñ͵òζñ͵ñ͵ñ͵ñ͵ñ͵ðÌ´ðÌ´ðÌ´ï˳ï˳ï˳î˱î˱î˱î˱î˱î˱î˱íʰïʰïʰîɯîɯíÈ®ìÇ­ìÇ­ìÇ­ëÆ¬ëÆ¬ëÆ¬êÅ«êÅ«êÅ«ëÆ¬ëÆ¬èééĪéĪèéèéèéèéç¨ùÙÂúÚÃúÚÃûÛÄüÜÅüÜÅüÜÅüÜÅûÛÄûÛÄûÛÄûÛÄûÛÄûÛÄûÛÄûÛÄýÛÄýÛÄýÛÄýÛÄýÛÄýÛÄýÛÄýÛÄüÚÃüÚÃüÚÃüÚÃüÚÃüÚÃüÚÃüÚÃüÚÃüÚÃüÚÃüÚÃûÙÂûÙÂûÙÂûÙÂúØÁúØÁúØÁúØÁù×Àù×Àù×Àù×ÀúØÁù×Àù×Àù×ÀøÖ¿øÖ¿øÖ¿øÖ¿øÖ¿øÖ¿øÖ¿÷Õ¾÷Õ¾÷Õ¾÷Õ¾÷Õ¾øÓ½øÓ½øÓ½øÓ½÷Ò¼÷Ò¼÷Ò¼÷Ò¼öÓº÷Ó»÷Ó»÷Ó»õѹöÒºöÒºöÒºôиõѹõѹôиôиôиôиóÏ·ôиóÏ·óÏ·óÏ·óÏ·òζòζòζñδñδñδñδñδñδðͳðͳòͳñ̲ñ̲ñ̲ð˱ïʰïʰïʰîɯîɯîɯíÈ®îɯíÈ®íÈ®ìÇ­ìÇ­íÈ®íÈ®ìÇ­ëÆ¬ëÆ¬êÅ«êÅ«ûÛÄûÛÄûÛÄûÛÄüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅûÛÄûÛÄûÛÄûÛÄýÛÄýÛÄüÚÃüÚÃüÚÃüÚÃüÚÃüÚÃüÚÃüÚÃüÚÃüÚÃüÚÃüÚÃûÙÂúØÁûÙÂûÙÂûÙÂûÙÂúØÁúØÁúØÁúØÁúØÁúØÁù×Àù×ÀøÖ¿øÖ¿øÖ¿øÖ¿øÖ¿ù×ÀøÖ¿÷Õ¾÷Õ¾÷Õ¾÷Õ¾÷Õ¾øÓ½øÓ½øÓ½øÓ½÷Ò¼÷Ò¼÷Ò¼÷Ò¼öÓº÷Ó»÷Ó»öÒºöÒºöÒºöÒºöÒºõѹõѹõѹõѹõѹõѹôиôиôиôиôиóÏ·óÏ·óÏ·òζòζñδñδñδñδñδñδðͳðͳòͳñ̲ñ̲ñ̲ð˱ð˱ïʰïʰïʰîɯîɯîɯïʰîɯíÈ®ìÇ­ìÇ­ìÇ­ìÇ­ëÆ¬ìÇ­ëÆ¬ëÆ¬ëÆ¬üÜÅüÜÅüÜÅüÜÅýÝÆýÝÆýÝÆýÝÆüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅûÛÄûÛÄûÛÄûÛÄüÚÃýÛÄýÛÄýÛÄüÚÃüÚÃüÚÃüÚÃüÚÃüÚÃüÚÃüÚÃûÙÂûÙÂûÙÂûÙÂûÙÂûÙÂûÙÂûÙÂúØÁúØÁúØÁúØÁúØÁúØÁù×Àù×Àù×Àù×Àù×Àù×ÀøÖ¿ù×ÀøÖ¿÷Õ¾÷Õ¾÷Õ¾÷Õ¾÷Õ¾øÓ½øÓ½øÓ½øÓ½÷Ò¼÷Ò¼÷Ò¼÷Ò¼öÓº÷Ó»÷Ó»öÒº÷Ó»÷Ó»öÒºöÒºõѹôиõѹöÒºõѹõѹôиôиõѹôиôиôиóÏ·óÏ·óÏ·òζòϵòϵòϵòϵñδñδðͳðͳòͳñ̲ñ̲ñ̲ð˱ð˱ð˱ïʰïʰïʰîɯîɯîɯîɯîɯîɯíÈ®îɯîɯíÈ®ìÇ­ìÇ­ëÆ¬ëÆ¬ûÜÅüÝÆýÞÇüÝÆüÝÆüÝÆüÝÆüÝÆûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅûÛÄûÛÄûÛÄûÛÄüÜÅüÜÅüÜÅüÜÅûÛÄûÛÄûÛÄûÛÄúÚÃûÛÄûÛÄûÛÄýÛÃýÛÃýÛÃýÛÃüÚÂüÚÂüÚÂüÚÂüÚÃüÚÃüÚÃüÚÃûÙÂûÙÂûÙÂûÙÂúØÀúØÀúØÀù׿÷Õ½úØÀúØÀù׿øÖ¿øÖ¿øÖ¿÷Õ¾÷Õ¾ù×Àù×ÀöÔ¾øÔ¼øÔ¼ùÕ½úÖ¾õѹøÔ¼ùÕ½ùÕ½÷Ó»÷Ó»÷Ó»÷Ó»öÒºöÒºöÒºöÒºõѹõѹõѹõѹõѹõѹõѹõѹõѹõѹõѹõѹôиôиóÏ·óÏ·óжòϵòϵòϵñδñδðͳðͳñ̲ñ̲ñ̲ñ̲ñ̲ñ̲ð˱ð˱ð˱ð˱ð˱ïʰïʰîɯîɯîɯïʰîɯîɯîɯìÇ­ìÇ­ìÇ­ëÆ¬ûÜÅüÝÆýÞÇýÞÇüÝÆüÝÆüÝÆüÝÆûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅûÛÄûÛÄüÜÅûÛÄýÛÃýÛÃýÛÃýÛÃýÛÃýÛÃýÛÃýÛÃýÛÄýÛÄýÛÄýÛÄüÚÃüÚÃüÚÃüÚÃüÚÂüÚÂüÚÂûÙÁúØÀúØÀúØÀù׿ù×Àù×Àù×ÀøÖ¿ù×Àù×Àù×À÷Õ¿úÖ¾úÖ¾û׿û׿øÔ¼ùÕ½ùÕ½øÔ¼ùÕ½ùÕ½ùÕ½ùÕ½øÔ¼øÔ¼øÔ¼øÔ¼÷Ó»÷Ó»÷Ó»÷Ó»÷Ó»÷Ó»÷Ó»÷Ó»õѹõѹõѹõѹõѹôиôиôиóжóжóжòϵóжóжòϵòϵóδóδóδóδòͳñ̲ñ̲ñ̲ñ̲ñ̲ð˱ð˱ð˱ïʰïʰïʰïʰïʰîɯîɯîɯîɯîɯíÈ®üÝÆýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆûÜÅûÜÅûÜÅûÜÅýÝÆýÝÆýÝÆýÝÆûÛÄüÜÅüÜÅüÜÅþÜÄþÜÄþÜÄþÜÄýÛÃýÛÃýÛÃýÛÃýÛÄýÛÄýÛÄýÛÄýÛÄýÛÄýÛÄýÛÄüÚÂüÚÂüÚÂûÙÁüÚÂûÙÁûÙÁúØÀûÙÂûÙÂúØÁúØÁúØÁúØÁù×Àù×Àû׿û׿û׿û׿û׿úÖ¾ùÕ½ùÕ½ùÕ½ùÕ½ùÕ½ùÕ½øÔ¼øÔ¼øÔ¼øÔ¼øÔ¼øÔ¼øÔ¼øÔ¼÷Ó»÷Ó»÷Ó»÷Ó»÷Ó»÷Ó»÷Ó»÷Ó»öÒºöÒºõѹõѹõÒ¸ôÑ·ôÑ·ôÑ·óжóжòϵòϵõжôϵôϵôϵóδóδòͳòͳòͳòͳòͳñ̲ñ̲ñ̲ð˱ð˱ð˱ð˱ð˱ïʰïʰïʰîɯíÈ®üÝÆýÞÇþßÈýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆýÝÆýÝÆýÝÆýÝÆüÜÅýÝÆýÝÆüÜÅÿÝÅÿÝÅÿÝÅÿÝÅþÜÄþÜÄþÜÄþÜÄþÜÅþÜÅþÜÅþÜÅýÛÄýÛÄýÛÄýÛÄþÜÄýÛÃýÛÃýÛÃüÚÂûÙÁûÙÁýÛÃüÚÃüÚÃüÚÃûÙÂüÚÃûÙÂúØÁûÙÂýÙÀüØÀüØÀû׿üØÀúÖ¾úÖ¾û׿úÖ¾úÖ¾úÖ¾úÖ¾ùÕ½ùÕ½ùÕ½ùÕ½ùÕ½ùÕ½ùÕ½ùÕ½øÔ¼øÔ¼øÔ¼øÔ¼øÔ¼øÔ¼øÔ¼øÔ¼÷Ó»÷Ó»öÒºöÒºöÓ¹õÒ¸õÒ¸õÒ¸ôÑ·ôÑ·ôÑ·óжõжõжôϵóδôϵôϵóδóδóδóδóδòͳòͳòͳòͳñ̲ñ̲ñ̲ñ̲ð˱ð˱ñ̲ð˱ïʰýÞÇýÞÇýÞÇýÞÇüÝÆüÝÆüÝÆüÝÆýÞÇýÞÇýÞÇýÞÇüÝÆýÞÇýÞÇýÞÇþÞÇþÞÇþÞÇþÞÇþÞÇþÞÇþÞÇþÞÇýÞÇýÞÇýÞÇýÞÇüÝÆüÝÆüÝÆüÝÆýÝÆýÝÆýÝÆýÝÆüÜÅüÜÅüÜÅüÜÅûÜÂüÝÄüÝÄûÜÃûÜÃûÜÃûÜÃûÜÃýÜÂýÜÂýÜÂýÜÂýÜÂýÜÂýÜÂýÜÂüÚÂüÚÂüÚÂüÚÂüÚÂüÚÂüÚÂüÚÂúÙ¿úÙ¿úÙ¿úÙ¿úÙ¿úÙ¿úÙ¿ùؾúØÁúØÁúØÁúØÁøÖ¿øÖ¿øÖ¿øÖ¿øÖ¾÷Õ½÷Õ½÷Õ½÷Õ½÷Õ½÷Õ½÷Õ½öÔ¼÷Õ½öÔ¼õÓ»õÓ»õÓ»õÓ»ôÒºóѹôÒºóѹòиóѹòиòиòиôиóÏ·óÏ·óÏ·ôиôиôиôиñÏ·ñÏ·ñÏ·ðζðζðζï͵ï͵ðͳðͳðͳï̲ï̲ï̲î˱î˱þßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈýÞÇýÞÇþßÈýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÝÆýÝÆýÝÆýÝÆüÜÅüÜÅüÜÅüÜÅûÜÃüÝÄüÝÄüÝÄüÝÄüÝÄüÝÄüÝÄþÝÃþÝÃþÝÃþÝÃýÜÂýÜÂýÜÂýÜÂýÛÃýÛÃýÛÃýÛÃýÛÃýÛÃýÛÃýÛÃüÛÁüÛÁüÛÁüÛÁýÜÂýÜÂüÛÁüÜÁúØÁúØÁúØÁúØÁúØÁúØÁúØÁúØÁúØÀúØÀù׿ù׿øÖ¾øÖ¾øÖ¾øÖ¾÷Õ½øÖ¾÷Õ½öÔ¼øÖ¾÷Õ½÷Õ½öÔ¼öÔ¼öÔ¼õÓ»ôÒºõÓ»õÓ»ôÒºôÒºöÒºõѹõѹõѹóÏ·óÏ·óÏ·óÏ·ñÏ·ñÏ·ðζðζñÏ·ðζðζðζòϵòϵòϵñδñδñδðͳðͳþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈÿàÉþßÈýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇüÝÆüÝÆüÝÆüÝÆþÞÇþÞÇþÞÇþÞÇýÝÆýÝÆýÝÆýÝÆüÝÄýÞÅýÞÅüÝÄýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅüÝÄüÝÄüÝÄüÝÄûÜÃûÜÃûÜÃûÜÃûÜÃûÜÃûÜÃûÜÃûÜÃûÜÃûÜÃûÜÃûÜÃûÜÃûÜÃûÜÃúÚÃúÚÃúÚÃúÚÃùÙÂùÙÂùÙÂùÙÂøÚÀøÙÀøÙÀøÙÀ÷Ø¿÷Ø¿÷Ø¿÷Ø¿ù׿ù׿øÖ¾øÖ¾øÖ¾øÖ¾÷Õ½÷Õ½÷Õ½÷Õ½öÔ¼õÓ»õÓ»õÓ»õÓ»õÓ»öÒºöÒºõѹõѹôиôиôиôиòиòиñÏ·ñÏ·òиòиñÏ·ñÏ·òϵòϵòϵñδñδñδðͳðͳüàÈüàÈüàÈüàÈýáÉýáÉýáÉýáÉüàÈüàÈüàÈüàÈüàÈýáÉýáÉýáÉþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈýÞÇýÞÇýÞÇýÞÇÿßÈÿßÈÿßÈÿßÈþÞÇþÞÇþÞÇþÞÇýÞÅýÞÅþ߯ýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅüÝÄüÝÄüÝÄüÝÄüÝÄüÝÄüÝÄüÝÄüÝÄüÝÄüÝÄüÝÄûÜÃûÜÃüÝÄüÝÄûÛÄûÛÄûÛÄûÛÄúÚÃúÚÃúÚÃúÚÃúÛÂúÛÂúÛÂùÚÁùÚÁùÚÁùÚÁùÚÁúØÀúØÀúØÀù׿úØÀù׿ù׿ù׿ù׿ù׿øÖ¾÷Õ½÷Õ½÷Õ½öÔ¼öÔ¼öÔ¼õÓ»õÓ»õÓ»ôÒºôÒºôÒºôÒºôÒºôÒºóѹóѹóѹóѹòиòиòÑ·ñжñжñжðϵðϵðϵïδüàÈüàÈýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉÿàÉÿàÉÿàÉÿàÉÿàÉÿàÉÿàÉÿàÉþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈýÞÇþßÈþßÈýÞÇýÞÇýÞÇýÞÇýÞÇüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅüÝÄüÝÄüÝÄüÝÄüÝÄüÝÄüÝÄüÝÄüÝÄüÝÄýÞÅýÞÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅýÜÆùÚÁùÚÁùÚÁùÚÁùÚÁùÚÁùÚÁùÚÁùÙÂùÙÂùÙÂùÙÂùÙÂùÙÂùÙÂùÙÂ÷Ø¿÷Ø¿÷Ø¿÷Ø¿ö×¾ö×¾ö×¾õÖ½øÖ¾÷Õ½÷Õ½÷Õ½öÔ¼öÔ¼öÔ¼÷Ô¼òÓºòÓºòÓºòÓºòÓºòÓºñÒ¹ñÒ¹òиòиñÏ·ñÏ·ñÏ·ñÏ·ñÏ·ðζüàÈýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈýÞÇþßÈþßÈýÞÇþßÈþßÈþßÈþßÈÿàÉÿàÉÿàÉÿàÉýÞÇýÞÇýÞÇýÞÇýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅýÞÅüÝÄýÞÅýÞÅýÞÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÝÄüÝÄüÝÄüÝÄûÜÃûÜÃûÜÃûÜÃûÛÄûÛÄûÛÄûÛÄúÚÃúÚÃúÚÃúÚÃùÚÁùÚÁøÙÀøÙÀùÚÁøÙÀøÙÀøÙÀù׿øÖ¾øÖ¾øÖ¾÷Õ½÷Õ½÷Õ½öÔ¼ôÕ¼ôÕ¼ôÕ¼ôÕ¼óÔ»óÔ»óÔ»òÓºôÒºóѹóѹóѹòиòиñÏ·ñÏ·ûáÉûáÉûáÉüâÊûáÉûáÉûáÉûáÉûáÉûáÉûáÉûáÉûáÉûáÉûáÉûáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉÿàÉÿàÉÿàÉÿàÉÿàÉÿàÉÿàÉÿàÉýÞÇþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇý߯ý߯ý߯ý߯üÞÅüÞÅüÞÅüÞÅüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅûÝÄûÝÄûÝÄûÝÄûÝÄûÝÄûÝÄûÝÄüÜÅüÜÅüÜÅüÜÅûÛÄûÛÄûÛÄûÛÄûÜÃúÛÂúÛÂúÛÂùÚÁùÚÁøÙÀøÙÀúØÀúØÀù׿ù׿ù׿ù׿øÖ¾øÖ¾ö×¾ö×¾ö×¾õÖ½õÖ½õÖ½ôÕ¼ôÕ¼õÓ»õÓ»ôÒºôÒºóѹóѹóѹòиûáÉûáÉüâÊüâÊüâÊüâÊüâÊüâÊüâÊüâÊüâÊüâÊüâÊüâÊüâÊüâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊÿàÉÿàÉÿàÉÿàÉÿàÉÿàÉÿàÉÿàÉþßÈÿàÉÿàÉþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈý߯ý߯ý߯ý߯üÞÅüÞÅüÞÅüÞÅüÝÆüÝÆüÝÆüÝÆýÞÇýÞÇüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆûÝÄûÝÄûÝÄûÝÄûÝÄûÝÄûÝÄûÝÄüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅüÜÅûÜÃúÛÂúÛÂúÛÂûÜÃúÛÂúÛÂúÛÂúÛÂùÚÁùÚÁùÚÁøÙÀøÙÀøÙÀ÷Ø¿÷Ø¿÷Ø¿ö×¾õÖ½õÖ½õÖ½ôÕ¼ôÕ¼óÔ»óÔ»óÔ»òÓºòÓºòÓºòÓºñÒ¹ùâÈùâÈùáÉùáÉüáÌüáÌüáÍüáÍûáÉûáÉûáÉûáÉüâÊüâÊüâÊüâÊüâÊüâÊüâÊþâÊýáÉýáÉýáÉÿàÉýãÈýâÈýâÈýâÈýâÈýâÈýâÈýâÈÿáÈÿáÈÿáÈÿáÈÿáÈÿáÈÿáÈÿáÈþáÀúàÆøÞÊþÞÇÿãÄÿâÃûÝÃöÚÆúÞÅÿãÈúÝÂ÷äÊùÝÎÿÛÎÿÞÈñåÃÿÞÉÿßÈÿßÈÿßÈþÞÇþÞÇþÞÇþÞÇþÞÇþÞÇþÞÇþÞÇþÞÇþÞÇþÞÇþÞÇþÞÇþÞÇþÞÇþÞÇýÝÆýÝÆýÝÆýÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆûÜÅûÜÅûÜÅûÜÅúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄúÛÅøÙÂ÷ØÁ÷ØÁ÷ØÁ÷Ø¿÷Ø¿÷Ø¿ö×¾õÖ½ö×¾õÖ½ôÕ¼ôÕ¼ôÕ¼ôÕ¼óÔ»óÔ»óÔ»òÓºòÓºùãÊúâÊúâÊúâÌüáÌüáÌüáÍüáÍüâÊüâÊüâÊüâÊüâÊüâÊüâÊüâÊüâÊüâÊüâÊüâÊýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉÿàÉÿàÉÿàÉÿàÉÿàÉÿàÉÿàÉÿàÉÿâÄýâÈûàÌÿàÉÿÞÅÿæÈúÜÃöàÍþéÖÿãÉûÝÁùæÊúßËÿÛÎÿÞÈõäÃÿÞÉþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆûÜÅûÜÅûÜÅûÜÅüÝÆüÝÆüÝÆüÝÆûÜÅûÜÅûÜÅûÜÅúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄøÙÂøÙÂ÷ØÁ÷ØÁøÙÀøÙÀ÷Ø¿÷Ø¿ö×¾ö×¾õÖ½õÖ½õÖ½ôÕ¼ôÕ¼ôÕ¼óÔ»óÔ»óÔ»óÔ»úäËúäËûãÍûãÍûãÍýâÍýâÎýâÎýãËýãËýãËýãËüâÊüâÊüâÊüâÊüâÊüâÊüâÊüâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊÿàÉÿàÉÿàÉÿàÉÿàÉÿàÉÿàÉÿàÉþâÃùàÈúßÊþßÈþàÆÿãÇ÷ßÌìÜÑóãÒÿãÏÿáÅøâÈûàÍÿÞÌÿàÉöäÇÿàÉÿàÉÿàÉÿàÉþßÈþßÈþßÈþßÈÿàÉÿàÉÿàÉÿàÉýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇüÝÆüÝÆüÝÆüÝÆýÞÇýÞÇýÞÇýÞÇüÝÆüÝÆüÝÆüÝÆûÜÅûÜÅûÜÅûÜÅúÛÄúÛÄúÛÄúÛÄùÚÃùÚÃøÙÂøÙÂùÚÁøÙÀøÙÀøÙÀ÷Ø¿ö×¾ö×¾ö×¾ö×¾õÖ½õÖ½õÖ½ôÕ¼ôÕ¼ôÕ¼óÔ»úãÍúãÍúãÍûãÍüäÎüäÎþãÏþãÏûãÍûãÍûãÍûãÍûãÍûãÍûãÍûãÍûãÍûãÍýâÍýâÍüáÌþáÌþáÌþáÌþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊýâÇûáÉûàÌÿàÉÿåÉøÝÅúíÞnhcòëÞøÜËÿäÊøàÅúßËÿÞÌÿßËøäÆÿàÉÿàÉÿàÉÿàÉþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈþßÈýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇüÝÆüÝÆüÝÆüÝÆûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅúÛÄùÚÃùÚÃùÚÃùÚÁùÚÁùÚÁøÙÀøÙÀö×¾ö×¾÷Ø¿ö×¾ö×¾õÖ½õÖ½õÖ½õÖ½ôÕ¼ôÕ¼úâÎúâÎúâÎúâÎúáÍüãÏüäÎûãÍüäÎüäÎüäÎüäÎûãÍûãÍûãÍûãÍûãÍûãÍûãÍýâÍýâÍýâÍýâÍÿâÍüâÊüâÊüâÊüâÊûáÉüâÊýãËûâÊýáÉýáÉþâÊÿãËþâÊþâÊþâÊþâÊþâÊüáÍüáÌÿãÉýàÄñÝÇÚÖÌ]eg•˜ŽòÛÌÿÜÄÿæÊøáÉÿàËÿßËúâÈÿàÉÿàÉÿàÉÿàÉÿàÉÿàÉÿàÉÿàÉþßÈþßÈþßÈþßÈýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇþßÈþßÈþßÈþßÈýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇþßÈþßÈýÞÇýÞÇýÞÇýÞÇüÝÆüÝÆüÝÆüÝÆûÜÅûÜÅûÜÅûÜÅúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄùÚÃùÚÃùÚÁùÚÁøÙÀøÙÀøÙÀøÙÀ÷Ø¿÷Ø¿ö×¾ö×¾õÖ½õÖ½ôÕ¼ôÕ¼ôÕ¼ôÕ¼üæÔûåÓüäÐúâÎûãÏüãÏýåÏüäÎüäÎüäÎüäÎüäÎûãÍûãÍûãÍûãÍûãÍûãÍûãÍûãÍýâÍýâÍýâÍýâÍýâÍýâÍýâÍýâÍýâÍþãÎþãÎýâÍþáÌþáÌþáÌþáÌþáÌþáÌþáÌþáÌûáÌüáÌüáÌÿâÉùàÅôæÓ¬­«UdlYheÿìáÿäÐþáÅùäÉþáÍÿàÌþâÊýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉþâÊþâÊþâÊþâÊýáÉýáÉýáÉýáÉüàÈüàÈüàÈüàÈüàÈüàÈüàÈûàÈþßÈþßÈþßÈþßÈþßÈþßÈýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄúÛÄùÚÃúÛÂúÛÂúÛÂùÚÁøÙÀøÙÀøÙÀøÙÀ÷Ø¿ö×¾ö×¾ö×¾ö×¾õÖ½õÖ½ôÕ¼ùãÑùãÑùãÑûãÏûãÏûãÏûãÍûãÍüãÏüãÏüãÏüãÏüãÏüãÏüãÏüãÏûãÏûãÏüãÏüãÏüãÏþãÏþãÏþãÏýâÍýâÍýâÍýâÍýâÍýâÍýâÍýâÍýâÍýâÍüáÌüáÌüáÌüáÌüáÌüáÌüáÌüáÌüâÊþãÉÿèÍîâЊŠ@Vg:PMÿõîÿÞÉÿäÆùåÈüáÍÿáÌÿáÍýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉüàÈüàÈüàÈüàÈýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉÿàÉÿàÉÿàÉÿàÉÿàÉþßÈýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇüÝÆüÝÆüÝÆüÝÆûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅúÛÄúÛÄúÛÄûÜÃûÜÃúÛÂúÛÂùÚÁùÚÁùÚÁøÙÀ÷Ø¿÷Ø¿÷Ø¿ö×¾øÙÀ÷Ø¿ö×¾õÖ½þç×þç×þèÖÿçÖÿçÓýåÑûäÎýåÏüäÐüäÐüäÐüäÐûãÏûãÏûãÏûãÏûãÏûãÏüãÏüãÏüãÏþãÏþãÏþãÏýâÍýâÍýâÍýâÍþãÎýâÍýâÍþãÎüáÌýâÍýâÍýâÍýâÍýâÍýâÍýâÍýâÎýâÎýãËÿäÉøàÆäÚÉlyzB[n>VWIJ¬ÿÞÉÿ寸äÈùâÊÿßÎÿáÌþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉýáÉþáÉýáÉýáÉýáÉþßÈþßÈþßÈþßÈþßÈþßÈÿàÉÿàÉþßÈþßÈþßÈþßÈýÞÇýÞÇýÞÇýÞÇüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆüÝÆûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅúÛÄúÛÄúÛÂúÛÂúÛÂúÛÂúÛÂúÛÂùÚÁùÚÁøÙÀøÙÀ÷Ø¿÷Ø¿ö×¾ö×¾ö×¾ö×¾úêÒýëÚþëÜùçÖõåÎøçÏûåÓüá×øãÎùäÏùåÐùåÏüäÐüäÐüäÐüäÐüäÎüäÎýåÏýåÏüäÎüäÎüäÎüäÎüãÏüãÏüãÏüãÏüãÏüãÏüãÏüãÏüåÎüäÎüäÎüäÎûãÍûãÍûãÍúãÍÿáÈõâÎöâÍÿåÃÿèÏA;=?LWI]`AU[Ž’“ôãÛúÝÍÿãÈüãÉûâÉþßÎýãÇýâÈýâÈýâÈýãÈþãÉÿäÊÿäÊþãÉþãÉþãÉþãÉýâÈýâÈýâÈýâÇøßËïæÉÿàÂÿäËÙáÈøãÒÿØÉðèÈýàÈüàÈüàÈüàÈüàÈüàÈüàÈüàÈûßÇûßÇûßÇûßÇûßÇûßÇûßÇúßÇýÞÇýÞÇýÞÇýÞÇûÜÅüÝÆýÞÇýÞÇüÝÆüÝÆûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅúÛÄúÛÄùÚÃùÚÃùÚÃùÚÃùÚÃøÙÂùÙÂøØÁøØÁøØÁ÷×À÷×À÷×ÀöÖ¿ùéÙ÷ç×ö娸è×øêÔûêÕýéØýçÛÿñÛÿîÙÿëÖýèÓûãÏûãÏûãÏûãÏüäÎüäÎüäÎüäÎüäÎüäÎüäÎüäÎýäÐýäÐýäÐýäÐüãÏüãÏüãÏüãÏüäÎüäÎüäÎüäÎûãÍûãÍûãÍûãÍÿäÈ÷ãÏ÷ãÍÿæÆüåÏÉÇÅGV`=QV@TY.23D2+óØÆÿãËüãÉüâÊþßÎÿãËÿãËÿãËÿãËÿãËÿãËþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊþßÈõçËþÛÄüâÑÝç×îÚÏÿßÌõåÄüßÈüàÈüàÈüàÈüàÈüàÈüàÈüàÈüàÈüàÈüàÈüàÈûßÇûßÇûßÇûßÇýÞÇýÞÇýÞÇýÞÇýÞÇüÝÆüÝÆüÝÆüÝÆüÝÆûÜÅûÜÅûÜÅûÜÅûÜÅûÜÅúÛÄúÛÄúÛÄùÚÃúÛÄùÚÃùÚÃùÚÃøÙÂøÙÂ÷ØÁ÷ØÁ÷ØÁ÷ØÁö×Àö×Àúîâùîâùíß÷ëÙøêØúêÙùéÚøèÛúåÏùäÏùäÏúåÐüäÐüäÐüäÐüäÐýåÏýåÏüäÎüäÎüäÎüäÎüäÎüäÎüãÏüãÏüãÏüãÏüãÏüãÏüãÏüãÏüäÎüäÎüäÎüäÎüäÎüäÎüäÎüäÎÿäÈúâÑúâÎÿäÈÃ°š¶¸»_pzGV]@T\MRS¡“Š÷ÞËýâÊüäÉüáÍüáÎüâÉüâÊüâÊûâÊýãËýãËüâÊûáÉþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâÊÿáÅùáÈýâÒêÝÙhwvñäàÿÝÌùãÁýáÊýáÉýáÉýáÉýáÉýáÉýáÉýáÉüàÈüàÈüàÈüàÈûßÇûßÇûßÇûßÇýÞÇýÞÇýÞÇýÞÇýÞÇýÞÇüÝÆüÝÆýÞÇüÝÆüÝÆüÝÆûÜÅûÜÅûÜÅûÜÅûÜÅúÛÄúÛÄúÛÄúÛÄúÛÄùÚÃùÚÃùÚÃùÚÃøÙÂøÙÂøÙÂ÷ØÁ÷ØÁ÷ØÁõíéùðæúòáýñßÿðàþîáûëßùêÚüçÒúåÐùäÏûæÑÿèÔÿèÔÿèÔÿèÔÿçÑÿçÑþæÐýåÏýåÏýåÏýåÏýåÏüãÏüãÏüãÏüãÏýäÐýäÐýäÐýäÐüäÎüäÎüäÎüäÎüäÎüäÎüäÎüäÎÿåÈùâÑûáÑÿåÉìßÍ›\m|M[d:T\LSVÿñëöÝÍüäÊûãËûãÍýâÎýãËýãËýãËýãËüâÊüâÊýãËýãËþâÊþâÊþâÊþâÊþâÊþâÊþâÊþâËÿãÅÿàÊöâÚ¦¯³Viu¯©¯ýÞÐþå¿ýáÊýáÉýáÉýáÉýáÉýáÉýáÉýáÉüàÈüàÈüàÈüàÈüàÈüàÈüàÈüàÈüàÈüàÈüàÈüàÈúßÇûßÇûßÇûßÇûßÇûßÇúÞÆúÞÆúÞÆúÞÆúÞÆúÞÆùÝÅùÝÅøÜÄøÜÄøÜÄøÜÄøÜÄ÷ÛÃúÛÄùÚÃùÚÃùÚÃøÙÂøÙÂøÙÂ÷ØÁõìïôìåøñÞùîÚøéÙúèÞùêÝøê×þèÓÿëÖÿì×þéÔýäÐýåÑýåÑýåÑûãÐüäÐüäÐüäÐüäÐüäÐüäÐüäÐýäÐýäÐýäÐýäÐüãÏüãÏüãÏüãÏüãÏüãÏüãÏüãÏüãÏüãÏüãÏüãÐõâÄûäÓþãÕþáË¢šŒ /5G\nJS_M_BSg=L`KXhFO]OU]»ÁÉþåÌúåÏùåÏüåÏüäÐüäÐýäÐýäÐûäÎûäÎýãÏüãÏþãÏþãÎÿäÌÿåËþéÄΰŸª½Ãs‘§KRhL]h#>:ÿôôûçÌûåÌüäËÿãÏüáÌýáÌúâÌùáÎùãÊùãÊúâÊúâÊùâÊüâÊüâÊüâÊùáËùáËùáËùáËùáËùáËùáËùáÌøàÈøàÈøàÈøàÈøàÈøàÈ÷ßÇöÞÆöÞÆöÞÆöÞÆöÞÆ÷ßÇ÷ßÇ÷ßÇ÷ßÇ÷ÝÅ÷ÝÅ÷ÝÅ÷ÝÅ÷ÝÅöÜÄöÜÄ÷ÜÄJM[çÙÓÿñÝøäÓøæÕþçÑþèÏöæÖýæÔýæÔüæÔüæÔþæÔþæÔþæÔÿçÖüçÒüçÒüçÒüçÒüçÒüçÒüçÒüçÒüçÒüçÒüçÒüçÒüçÒüçÒüçÒþèÓôäÝÿæÑÿèÎÿæÒþêÕøêÓ ¨¥v–«}™µy•¯x“®|˜°s¥ƒ¡²…£­^‡^gv@Q]>N^N\BPEJ]ÿÿÿóßÍýéØùåÒüæÔüçÒüçÒüçÒûçÒûæÏýæÐÿèÑôåÑöéÚðëঠ“‰¨¼Œ¤¹y¢ºSu…qm|]pt:dfNU`^mvZcdÅûæÙÉúåÎþåËýäÌúäÎüãÏüãÏüãÏûâÏûçÒñÜÇüèÒéÕ¿ýäÍûãÍúãÍúãÍùâÌùâÌùâÌùâÌùâÌùâÌùâÌúâÌøáËøáËøáËøàËøáËøáËøáËøáËøáËøàÊ÷àÊ÷àÊ÷àÊ÷àÊößÉößÉößÉößÉõÞÈôÝÇ?TXÃÎÍçåÝùë×ÿèÑüèÓõç×ñè×çÞ˽ÇÄZp~XdrîßÞþàÑÿßËÿíÝúë¼êãâäâí÷èÔÿæÇüàÒúäÚýìËîêÍêè×ïêÖÿíÏýçÈùèÓùêÑûéÁûæÉÿéØèçänŽ•{¢µˆœ¶œ¶—ªuŒ¡sŠŸxŒ¡n„•e|Œ7N]>R^=Q_8MR@U\@V]@S[9MU/6;MVU`@Vc:P\AT[@SZ@RZEX`?OYGXaBS\DT^9WRHX[”‘’ÿñéýçÑÿèÎùçÒõæ×òæÖûçÑýçÑøæÖùãÍÿéËâàÔ“§¸…©½oŒ¢—°Åh|DTdfv‚DU^9ITIa[HQb¢¨£ÚàÑF>HôéèöêÊÿäÔøäÒøäÒ÷åÐüæÍüèËõåÐÆÂ¼fpz¼À·ïéÛìÚÅþäËûâÎøâÐõãÎøäËöäÍöäÍöäÍöäÍõãÌõãÌõãÌõãÌ÷âÌ÷âÌöáËöáËöáËöáËöáËöáËõàÊõàÊõàÊõàÊõàÊõáËöáËõàÊ9JXT\@S[ƒˆ†åßÒïêÖèæÍæéÔ}ƒ‡U\i‰‰‚òçÑøäÓöåÙíçÓƒŒZ]höïñüåÕüæ×îéé²¶¹ÓÕÖñäÍöêßÍÎІ¤«ˆ¦¹}‘¦o”uŽŸx£uŒ| au„e|‡8KV@S_F[bFV_>OXIZcFW`GU^GV_ET]ES\7Y^G[c@FJÿüõõçÕõêÎõèÒïèÖøæÐÿçÌþçÍôäÔöéÖõåÑÐÎÇ•¨¸~ž´{š­ž¸ÉLdt=P]_q|?RY;NWCZYWarœšÒÝÏ_^lÏËÍõêÐýäÕ÷äÒøäÒ÷åÐüæÌþéÏîáͧ¨¦Ubl•š–ûöêýíØüèÒúáÍöâÐöãÎøäÊöãÎöãÎöãÎöãÎöãÎöãÎõâÍõâÍøãÍ÷âÌ÷áËöáËöáËöáËöáËöáËöáËöáËöáËöáËõàÊôßÉôßÉõàÊ4DTGQ[IHMòçä÷æÙûçÕûèÓúéÒÂÌÎ`ruBZbAT\]adîåÝðâÑöëÔÀÔËdpxKXeZcgöñæùåÓñå×ÑÒÄlz‹O[k§¥©ùèÙóâѧ¬®XjvAS\ìèÛêãߟ¥©Š§µž¯^o… 1C!SZ1FN-:)5=JW!.;1?LJUaFR^NXd:XbBUaBDMðèæôçÖöëÒöêÑóè×õçÕÿêÍûèÐêæÜëèåÙÚÓÅÎЕ¯Æ¼ÌŒ¨¹™©Lcr9P[OdoI\cKZCU^çëåöäÒóéÛ˜Tjx>R__egÿøæãßÏ~ŽD`lA^eglfäåç—¡“¯¾ ²’¥·kz‹x”¡Tj|y‘ ‚•¦BWfG^i?R]@U\9NV0?L7GS0>J#1=6DP#/%1"-9"6F2PWDWX;F\)8;¨½·GRgŒ—íèÒüäÖùäÏ÷ãÒôäÏøæÏöåÌÿöè©­°Ug{T]]ÛÚÔíßËþèÒ÷äÏõäÑõäÑøäÏóãÎôäÏôãÎôãÎóâÍóâÍóâÍóâÍõâÍõâÍõâÍõâÍôáÌõâÍõâÍóàËöáÌöáÌöáÌöáÌõàËõàËõàËõàË,9G!,:7=H #,/.3›šš¹»²ÀÁ¸a|ˆJbh?UZ>MTEDS¬ ªýæäüáÒj™HV^;IU:P`mtsúçÔïâ×r‡ŒUnt4OYGVXááÕÓØÌgz~>[e9TZ7EIÝäîxƒ’8I8IŽ¢}Œv“¡ˆ µz’¤cz‰qˆ—QitLPCRP8KY:NR>RR=PW?M\BIVôôöøóèe™IRULU_/J]WaeÿîÚìàÓx“£I_c@[h8LXqxw¦®®q„‘LdtIZ`2LTHQ`’¡²‘¯ÀNj{.?Pet„^|†rŠ n‡œx¢oˆ˜`{‰8P\=Ua'%,;1;J6FN2DN3FR2ES1DP8IS7HR1?L?P];P_5KX@P\>P^J`rq…•ft€BX`9MW=P]7K[Ž¢²Zr|Kbk4MT7PZ.KV…¡²w¤n‰Ÿkˆžh…™f‚”ua}ŠB]jXs€d{Š@Tc?P`=P_8CQ*7+’“îçäøîà÷èÐÿëÌêëÙõäÕòäÜ£«©yŒ›v†¢s†¥s”¯i}^s…asƒ;N\I[f@QZ;NU6JP;M\@RV>HQ,3@-7@CEJ¶±¬ùéÔÞáÖüêØöß×ÎÑÝ¥ÄÕ¡ÄÏ’®½“«Í†¢´lŒ·¿ÄçâÛòâÔóåÎóåÎñåÎóäÑóäÑóäÑóäÑñâÏñâÏòãÐòãÐñâÐñâÐñâÐñâÐðâÏñâÏñâÏïáÎóâÍóâÍóâÍóâÍòáÌòáÌòáÌòáÌ4DK)9BEOY2;D4=G"0)2?1=I6EN:IQ5EQ7GT8IV9IU5CL(.?L5ER6GTQa:M\H[h6JU6KR2IQ2JV`wˆd}d|z“§a|ŒqŒhŽE6CK8BL6BN4AP5AO5BP:GUDP\8BK1BO5ER8ES8ES2?M8BP)7.8F0EP6HR;KYCS`%6C$5B2BM5EQ7JR=MV&6C+;F/<]lyM^k9IV-8CMn‚”Thy0?9LYEWb$4=)0&8?*9B2=C*.sƒJW^CKPòñéõçÙþëÎúæÌçàÙ|¦Kh‰j‚•\mw`t†[n{@R\OY^_^[îèÞïæÔóåÑôåÒóåÓóåÓóåÓóåÓòäÒóåÓóåÓóåÓñãÐñãÐñãÐòäÐñãÐñãÐñãÐðâÏñâÏñâÏñâÏñâÏñâÏñâÏñâÏñâÏ3BS3@P5?M.7D/8F7AJ6BL(9?-6D)5A3@N1=N2>O#0>'/7E7HU8HU6BQ7DRAN\5?M9DR>HV.?L2BP3FP:LW0?L)9E-:H/G #$-(1(1-=F9AGO\`?KSAPY7ITBUbXp€Tk|1N^@MZ²ºÄq‹•g†•j{7DP\qsK_q,@R`r9LYEWb:KT8JPCV\BP]?JR4?C +.1@G2O7FV)6C0:H?LW;KX:JW3DQ1AN0MU1?L1;I,6H4GS;IU5?H'0:#'3 )3#0<,:&31>J:ERDQ^DS`@R^?S^9MY1DK6GP+9B +5&2<&1;0@H)207=$(.$17'-:MT?R_J_n,L[CS_z„~™¨g‡˜{£‰—¨by|[n“§¸M`o7KX?Q\>OXL%2@%3?.:I1AQ/?O1AQ/>N00=I8FS$0&.=.6C;GQ:KQ;MT3FMM.>N,O2DP3CO2@H)4=8DO5=K4;M(O&7C->K8HU8IV>O\GWe;LY6FT3CQ8HU+4C#18@O?FUCQ_%3'0G6DX6GS1ANQa3P^@JUEKX6Qb'J_‰ž¼dt‰ &2Pcu/A1@AUbEWb>OX'9@L^c?NdGXf@PWDX^8P]H]fS\`ÕÔΖš­^fm…œž]{fw“}€šªeŽ©U^jNYdAPVWehU]XöõìîäÖúéØñãÕñäÓòåÓóäÓòãÓòäÓñäÔñäÕðäÒðäÒðäÒðäÒðäÒðäÒðäÒñäÒðãÒðãÒðãÒðãÒïãÑïãÑïãÑïãÑ0=I0>J(13J0=K1>M*8D,=I'8E+>M.BN2DN3EM3CI1@M/?K1AN4DQ6EV,;K+9L/P/?Q)9I*7E-*8D+KW4?K4?KCP\Š‚ìä×íæ×ïëÆòèÝìçìÝãÛàçØæäÚîä×öæÍìåÔíåÔíåÔíåÔëãÒëãÒëãÒëãÒìãÒìãÒìãÒíãÒìâÑìâÑìâÑìâÑ,N0AN5EQ3BK1BH3CP4DP->I7GT6FS2AR1@P0@P0@L0@L1BO7FW6EV+:K+8F*6B,>K/?L'4B#.<)1>#/;'3?)7C/;J0@M1AN4ER6JW;N[.EQ/EQ/EP-@K+@K8JUHP)15BJ,4Q4@K7BP0G->J3CP:JW2?K-8B09C0AN2>L)4B(0?)1>%-;)1?*6B/9H0=K2?M3CP:JW=N[8HV7JX7KV5JU7HS7IT:JV1AM6DP;IU5GR4DQ3AM=IU8DP.:FFT`6GR3BS3@N@LX"+5.8B'09*6B0;F2?M0=L2AL8EQES`7FOCR[APYBFŠçèçÞÜ×âò妪°XW]êéÛêçÒìåÖìãÚìåÖíåÔíåÔíåÔíåÔíåÔíåÔíåÔíåÔìäÓìäÓìäÓìäÓëäÓëäÓëäÓëäÓ5GX-=M9IV)6E*7E*9I)9J(;-"0.;K2?N3@P-:H!.$-7".8%0:$09*6@=IR.9F%1=5AM8EL0N/?L&3A,9G2BR-=N!3F08F0:K9GV5@S.;J/7?L)1>',9#)6 &0)2<+3=-6E.7E'2@*4BMV>IT@IS1>H7BL6EN2CL=IX8ES&5@&1>2=D$+2"'4;<(/?$409F7@M?LX7CM&1<&6?1BI8IOMYaGT\VckN[cMZb;IQFQW;JOHU]CS[DR^4DRgx„6GYJT\R^dCOU>JPEOWIS[bltˆ‘˜N;KXL7DT+9K)8K,7E.7I/:M0?Q/=P6CS/;F/;G+8F&3A,:F)7C/=I,:F+;C$3< ,6(4@&5A-:F*6$+$$(+8)5A)'5*#"-)3%+9)7"08DR;LY:KX6GT!1=&2/;(4)7C*9E.&6>/;E.8B5?I+4>9EO.=F3DM:HT3@N0=L.J.=N)9I+:J%6B*;H0@L&6C*:G.8G&3A*6D'8E/=I'2=(-"-0&4A+7C/7D(1%.#-".5$.6"-<%0>$2@,8F1BN9JW:KX:KX*7C1>J0=I,9E1?K-;G9JV%6B/:E(2<+5@(2<(1;9EO.@I6HQ5AJ04@LAO\AR[N,9J)8H*:K*=N-?P2BR,=J0?K-;O+8K(7G)8I5EQ2CP%5A*:F&3A.;K&6F0?O1AN(7?!.6%16+K6DP4BN3AM3@L2BN3CP7BI7@H;EMELU6AH3@G+?E3GM19C1>J5AP,OX7KSKSJTV^h?IM9EGQZY6>APY]KSYR`VJR]EJ[^fdÖÝÍÞÝÔòèàñåÑéãÖéãÖéãÖéãÖçáÔçâÔçâÔçâÔéâÑéâÑéâÑèáÐéâÑéâÑéâÑéâÑ$@Q$J7?L/;D,=H/@M3CT0=T-J7AI+:B/=H,8F,9I1>O3?M.29B'-63;E2>J-;G@P\IQ>KP>IO:EL3O-:Q0?R5DT3AM/J-N3DP(6B(1;!-4*:L.?K'5A!,3)1!,4%08'3=%0;,8B%4=1>K5ER6HS5ES8KX3EP8JU+=H0CM9IU.>J7GS3CO2IP5HP6IQAR\0@H,8B*4>6?I.=I'3?2>J(2<%/9 )"..6>#,!++7C3@LDI.49?BGJ>LX1?K@NZ5CL;JS=MUEPZ?KU/;E1:D9BL/>J,7C6BO+5>%/9"+5(-9+2;*1:(1;(4@0>J:JV:KW>MV5EL:FR8DP8FS,8@&07)3:,4;"*2-380386:?DLSHRYAMT6BF.:>+7;5BC=FIANNX``LQQPQMÏÑÇààÖèåÛáßÑÝâÖÝâÖÝâÖÝâÖàâÖàâÖßâÖàâÖÜà×ÞàØßàØßà×ÝßÕÝßÕàßÕáà×5DM6AM=FS,5C/7E-7I/:L"0C&2@,F(7@-9-9H'3D.;K&2@"/;%.&-!+5&2>3?J ,!+"+$*(9J*;G%1 *2$, '/$0;"'5&/<'3%2A:FT7GT'7C!-%5A#/-9#1=$0*6B!- +5 * *#-+5.:D,9C5AK4CO)5A6BN4=G&0:$-7(.9*1:.4=*3=(4@0>J5EQ@P\@OX=NT:GV@NZ;KW9IU9IV>NWFR\8CM:CL;EL4?F8@G-2:3:?8KMBKN@GJ?DEHMLTXWããÝáÞÖæäÛääØàâÖàâÖàâÖàâÖàâÖßâÖàâÖßâÕÜßÚßàØÞà×ßà×ááÕÞßÓáàÒâáÓ2>H'2>&/<+4B2;I6@Q.'0<)3B%3@7DRJ0@L2?K-;G0?K2>J1=I6BN4@J.:D7BM8DN6BL($.1=G&5A)4@6BN7@J%/9#,6-3>+2;-4=/7A(4@/=I7HT?O\IXaCTZ?KZAR^HS;CMEL=FM06>O.>N3N.>M-=M+;K2CO+;H.>K4EM.>K3CP,;L2AQ9IV*:F,=F4=G*7C&4@4AO7EQ-8B#+4",3+%0?+8F,8F5FR!-$0+7C"+ *4&0)/:+2;.5>.7@(4@-;G:KWDTa(7@"28)5+77GTEV_+=F+:C:GO)4<'0:)18.6=.4;#*126:,/41389AHCMT:FL0<@-9=.:>3@B*46;>B:?@=ACIRQØßÜØÞÙÛßÙÜßÖæâÚßÜÔàÝÕãàØÖÒÊåæÝÝÞÕãäÛäâÖßÜÑâßÔáÞÓÛÚËææ×§¥—¹¼¬3CS+;K/?O&3A/J->F/:G.;J'8E0?P8IU+9E%.8)1+NZ1GN;MU8LS=NX;JS0;F&/9;EN)7D&2>:GS0:C%/(!&2(/8(.71:D/;G*8D>NZBR_?MVJP7BH8?H:BI:BI8>E(+326;/271386>EHRYKW]JVZ@LPAMRJVX>HKIKOHLM?IINYW»ÆÃ¾ÈÂÐ×ÒÕÝÖ×ÛÕâæàÓÚÓÞåÞ×Þ׻¼×ߨÕÝÖÛÜÓßßÙÝßÙÕÙÓäëäÚáܬ¶°€Šƒ2EW4EW8GX&6C)5D.=H0AM,>F37M4=P1@P/AM0AM8IV,=J.=N,>F.>K.>K(7H6FV5FS-=J&8@+8G):G$3D/@Q6EV+8F'4@'2*#1A$1@$6C'8E):K1AR3GV5GW9NY:OZ6KV:MX5HS5HS1AM:JW5OV3LT4JR7KS;JT1=G%0:4=G6ER*5B6BN2NTBPVKV\?IO;AK;CJ74L-F4BN)6D0G+6=&06,9F7DQ:DK$.4#*1%,3*3<)2<06>/8@-7@*6?.LU=GRH5BL%2;5AK5?I&/8"+4&-5%,5$-6,5?,4A-9E@MYBR^EV_9MTBRYGW^=MTAQX>NU>OVEV]:LS5?F=GN3=@E9@B;CGFQRizwaupn‡~b{rl^ui‡d}|[rp[ljcqk}Š}m|cvw^uv”«°t‹‘Š¡§wŽ’|•.JT1JT2FQ/?K1@K4@L -8*"+,9/?L,-6"-&0%4@5CN'5A)"*!(#4B0>J#,6'0%+$.%.7#,6'2(0:*3&3$2>6FR3DQ)=K,4!* /8$-!,6*5)3'1%/",&0$/+7B%/#03> )&02>H*3>'/$-6%+4&,5"+4(1;*3@/;G3AM@Q]CT]6JQ&6=-4,IMIVXCQSbwt\sp_tsUthpŠ`|rczucvt[libpn}Їy…Ž‹›¡˜«¶„—¤Šªzš{˜n€‹4JV8KXL+6D5?M(4C!-<,N-L,;F(5$3<(3=-;H"0<.?L1>J*5?(/ &&7E-;G%/"+ $(1&0)*4=&.<"-,9E:JV2CP(8F$5==NW$5>3AJ/>G.>G2=G'2<3CL-'3?1?K=MZ8HQ(;B7FM?OVGW^BRYBRY=MT/?F0AH4=F@IR5=D@HO(-5.59,/41498=DFNUFPX?KN>NPAMQBNRK.>K5BP/9G/:H0=K.?L(9E.;H1AM3DP,@O+>M7GW/?L+9D1@H)7C+7F"/=1=K7DR2@L-2@LAQ^EV_BU\BRY@PW?OVAQX@PWAQXCSZCT[2>EAJS+5>DLS05<+1947<48<1:@@JQEQWESZFVXBRUAMQF>MVv†’w‡•l€r…–cvz^qnXkbRdV\nay‡„cqxw“cp~z†—v„—{‰}‹Ÿfwˆhwˆdq‚(7D*71>K1M(;J7GT8HT)9F.>J-=I.>K&5E4CS7GT3DO,K/=I(8E4DP8HU8FR(4>(0(:G3DQ7CL&18"+ )1%0:'0>'2@+6D*8F0P[8KS8KS9LT9LT6GP5FO:KT>LU5IQ9LT8NU9NV7KR5FO+53RYJY`GW^=MTAQX?OVHX_AQXGW^/:BDOW6>H@IR-6<16=/6=45>/6??HQIU[GU[DRXBRTEQU=IM=JP=IN6BJJ3@L->K$4A-=MK0@M9IVGPLV_ESXCTZCQWCQX?JN=JP9EK6CK;FP;IVUfs[kxSbtcv}kz}bnn†ŽŒ‹“‘{‚…qwƒ‹–_otv‰arzp„‹bs|ex~lz€brt2IX0ET6IX-M.>K8HX2ET+>M)'4?)9I+:J,;K/?K,1:C=FOHU]BSZFV\CSYESY@LR>GOBKS3?ELW_ERZ>ITM]fZirŒ ¥aqw¥°·†‰‘xz‚{~ƒ‚‰Œ{„‡^mv€‘™FVbƒ• n~Šx‡{ŠDPV0M[0LZ7M]1EU0AQ2BR4DT.AO0?L4CT2ET*>P+AP4HT;OU,=E3CO3CP5CT+:J2AQ:IY2CO/?K8FR3CP(8I1AQ9IY7HT2>J)3/@Q1AN:HT(4>$.%.8(0>(5@(5J+9L-(7C?O\HYb:NUBRYEU\CRY;KRFV]IX_:JQGW^1=H>HR.8CAIS09C.5>04>49@/8A>IQER[FW^DTZBRXDRX?JQ@KN=HKK2AQ/?P.?S/CO.AN3FU.>O*:K5EU9JX0@M2BO4DQ4DQ-=J2BO8IU5ER0@N4BN0@M"2?1AR8HT6DP.:C",6+N.=N:JW8KV9MT7JR;NVQYBU]=LUDU^>RZ>PWDV]CV]@RYEX_FX_ASZEY`9EKO8IVJ8ES4CS/?P.?R3FR2ER6IX0@Q,K/=I2BO 0=1AR?O\7DP0N9IU;MX>RY:MU9LT8KS7JR@S[@S[>QYCV^=PX>QY=PX:MU:MU2AJ ,61;D;JV*8D6BN8DN*4>",6%.8&/8%1;)2<+5?,6@7@JAPYGXa-@H7GM2AH+;B1AHCSZ#3:$4;%5<)/4AG09@@KQ08?.7>27>39@9>G:BIIU[ESYCSZCSVBSV9JMO.>O7GW9KW.>K4DQ6FS7GT0@M/?L9IV6FS-=J1?K-=J#3@/?P7GT8FR0;E!+50AN9IU5FO0?H&0!.7&/<,5C+;C,:F):G-;L,;K3CP7IT:MU8KS9LT=PX?RZ9LT=PX@S[CV^AT\?RZ=PX8KS,6@3NW>OX!5=)9@%5<-=DAQXAQX4DK>NU?OV9DJBNT2?DBLS29@/8?27>5;B3:D6=DHTZESY@QW@QTCQT6EHETZJ3CS3EV3FS6IV3FU.>O,K4DQ8HU4DQ/?L2@L3CQ%5B1ARIYf/=I3>H!*42CP6FR0AJ-QYBU]=PX?RZ:NU4CL&2<1;D4BO/=I2=J7DM/8B$.8)2<$-6*6@+5?-7A-7A5>HDBKR+5<19@*3:4:A29B0:AFRX@NTFSZDTVBRT8CGFVXBMR2?A=FEMQRBGG:AD9>CCOSP\^forIUWKV\‡–£§»Ì³Ìá®ÀËy…fm|t|Štƒ{šlz…\er4K[1FT:NZ.?L.>K3CO1AQ4CU/BO3FS6IX/?P*:K4DT6HT1AN1AN3CP7GT0@M0@M8HU7GT/?L2@L4ER3CP+;L3CP4BN3?I)3=->KQY4CL*6@09C6EQ3BN+7CDFRX-8?,3:.6=-6<3:C.8?EQV@OU>MSCNRFRV6@E@PSIVX0<>>FECLKGMP',1/7>?JNEQRNWZ?KM>KR\mx]t„[t‰_tƒ@O_LVgw„”x‹šr‡–‚’¢Ž—¨2J]4L\>P`/@M5EQ5ER2BS5EU2ER3FS1DS-=N)9J5EU7IU2AN3CP2BO4DQ.>K3CP9IV7GT.>K=KW.?L"2?3CS1AN1?K3?H,5?,=K9IU6GP-QY?RZ=PX=PXQY:MU?RZ:MU6EN-9C,6?4CO2@L*6B?KU7AK!+5*3=)1:)5@.8B-7A.8B3F>KS(0 )&4:0>D7EK:FL/9@&18+3:/7>18A.8?COU>JP>MS@LP>JN4<@AMQIUY1>?:BFILhtzx‚Š>IPDPTN[\?KM8GJ4GNp‡–n‰šh€”ƒ“¦Ž ”£´“ªºŽ§¶“§¹‰–¬0HZ4JZH1;E.7B$5>,5>43B12A0RW>PWAS\*:C(6<>HMJWT>RW9MR6FL5FL8GM:GL?IP=GN9HK7FI;GK>IM@HLINS:@E=?D4AG?KQANTFPXCMT=GNBIP@FM7BH?JPGV\FTZJV_HX_JZ`P`fN]hO^jM\hO^jM\hL[gM\hN^j*K.>J3CS6JX=Ra*BNG]i5HUQ8IZ2AS/CUtŒœ€›¦;V`9R\4EP6BN07F0?K8JV7JR2@I.4B2;IBRc‰¤´OfkEYaEWb1AN5AO;GVQY:MU>QY>QY>PX3SW9NVAMYAP^‰Ÿ«TnwEYd!3;5AH;HP4AI7GP7CM4AK8BL8AK-AH1RW:OSASZ?PY?MW@PV:LOERT@TYHO=FN;@E@LP=IMDOS>IMBMQ>JMP§¾Í{–¤8S]?U`7IT4?K27G5CO6JQ7JR4BK09F5=KScsqŒœLglH_fCT_+;I1=L8FQ;LU:KTQY;NV>QY=PX=PX7RXMZ*:G¨ÁÌl‡“I`j>MZ1?H:IR8GP5@J8DN7CM;GRAIT0FJ:HN>GPMS@LR=JPBJR=LN8HJ8CG4AD0;>+7:6?B=FJ6JNAMQGVYFUWIW]JZ`HX^HX^P[cR]eS^fVahU`hWbjYdkYdl:T`5K[4JY1CT5GXQ^/@M%6D3DQ6IV2ER1=K3@N1AN-=J.>K9IV7GW/?P,AW¦¾Ðˆ¢°TlxMY(8D4CP;LU;LU;NVPX2AMFXh±ÌÚy•¢CXgL[h6JP5FO9HQ;GQ8EO=GO>KS?KT6ML:LNBLTAOWJcn°ÊÚi–DWnDU[ASZ?QW>QX@SZ@TY?TY@UZHX^EU[?PUGU[APU@LQ7CI7CI?MS=KQ?NS@KQ:FL.:@8AI#.58<@>BF9>CAJMDOS;GJ@OR@OQFRVEPUJY[JY\HV\BRXIY^Ufl_hq`ir^gp`hqajsbjt`hr^gp0KWKdp1GV-AO1CT0AT1DZGZosŒ•Rgr7KV6HS+=H1CN5IT/CN.:I5BP4DQ2BO1AO8HU3DQ3DQ1I`›³Ç†Ÿ¯Nfq=S^:LW.;H9DP,=F6HQ2IN7HQ?JV,8I¯ÇÖUt…F]hG]hDXc9KW*;C3DM=NW6IQ8KSQY8QW>PX>N\Ym|˜¶Ät‘ H_oETdH\g1CO:HQ:EP>IQ>IQKQ9PP;MOBMU=NYi‚‘°Áq‰@UnCV]BU]BS\FX^BT[CV]>PWI]cEV]HY`IV^IV^ANVCPXGT]KX`NZcVckUbjTbjWdlCPXLW_S_g\]fX^dPX_XbiS_eP^dP]cL\cQ]aUadTchYgmWejXhoXio\mt_jq`ksbmtcnualtbmt`ks^iqŸµÁ3GS4ER1AQ4DU7H[3I[|‘¦ƒœ¥Lbm>R]L1AN2BO6FR>NZ6HS3EPOh‚˜°Æq‰›Net?S]BT_7GS8FR5HN4KQ2HN4GO7DQO^n’«½o‘¤IaqAXgEXcDW_(9B5EN:MU;NVN[ˆŸ¯¯ÀmŸOgyt€•_z‚:NY:JW@KUALT?IR=LQ?OU?SUAQWOY@R^EV_BT\FW]CPY-4IV`=JRCPXERZFS[KX`MZbR_gUbjScjRbiPagT`hUbjZgnZgoWdl\enYaj]enWaiZgoZfnWgnVfm\go\goXemYgn\gq\ks\ks]mubmw`lv]irZeo]isalv]hrVak_qx0?HDR_8IV0@P.BS6L^³Ë݆Ÿ©Ndo=P[8JU5GR6HS7KV2FQ0J:LR2IN.GM5GO)9Feu…‘­¾eˆ›]u…D[jGZeGZb/BH:MT6IQ9LT;NV=PXQY;PX=NZ(;G¾×ç~ ¯mŽ¡Hav¯¾Ñp‹—>T_5ER>GQDMVBNSCQWFV\ARWERZCMW6JU£ÀΑ³Ár¢BWm:MZ?Q\8IRARXHUZXaj bepSaiYfnYfn[hpWdlZgo\iq]iqYhrWfoYhq[ktUdm[jsZir[js[jr]ktZjrZirZir[js[js]lu\fo^hr]ir_kw^jv\kv\iwYftWgsQamO_kRbnM]iM]iM]iN^jgsy=IQDOY:HT6FS4IX/HX¡ºÌ…ž¨BWc6JU8JU:LW7IT6KV4IS0&;B(9A)KR9LM@MVAITHQ^1CO²ÐÝ‘´Â`}ŽBWj@QaNbmYjsYhm]gmbjr€‹41@gu|gt|cpxanvcpxamvZkqSdkZhpWfn[irZhq]ktXiqWhq[ltZlxYlvaq}`p|^o{ao{_ny^lxem{fm{amxbl{_hv\huZeuR]nL_lL_lL_lL_lJ^kI\iG[hFYgOjp2IN;LU8FR6FS3HW/JZŒ¬»v’>V\8MU3HP7IT4ER1AN1AN18;MX]ozey„k€Šj}‹h~ŠPfsE]iG\gJ[eO_l^n~ƒš¬‚ž³¤¹Œª»¯…¤µŸ½Ñ–µÉ“®Ãƒž³rŒ¢wŒ–OfvI`pG^n{’¢˜©Ž¨¶”®º•²»¨µ†›ª ²ˆœ®Ž¥µ€š«ƒ¦²‡¡°¦ÄÒ°ÎßšºÐ€Ÿ´ž³Ur‡¸Óçv›¨Gam>LT?HPESXAQWHV[HSY[ggO^cZp{d~¬ËÞ˜²Çd@Yi7R^FakXkr`pyvŠ,1>kp~3YhpWgo\gr_ls]jqanu_ltbntWkyWkx[o{Xmx[ny^p{]oz\nz[nx^nz`p|aq}dqdqer€er€crz^nz[jvVgsPcrL`rKcsNfvOeqNepRerWguVfrUeqZgtWfoPjp;PV5HO;KW@P]2ET˜±ÁœºÉYqw:PU6KS4IQ:LW5FSN^kbr~t‡’\nyRdoL`kM`mK^k>T`>T`4LX=S_\lyZkx>Raaxˆ‚ž³t“©ƒ¡±Ž¬Àˆ¦¹®Áˆ§º…Ÿ´w’¦w’§h~Œ\s‚Yqƒ~–§ƒ›­‚­’«»«¹\t€€˜¤Œ¢²¨º™°Á“­½“¬½•³Âš¸Æ¦ÁÒ–´Æ®Â†¥¼t”«y–ª§Áצ²SZFXcQ]jK\aN`jXq€¼×뜺ό¨¼i“CZi=Vb9P[Ymt`kuYclX`mt{Š06Ebs€_o|_o{_o{`nz^lxao{_lx[kxUfsTenTenSdmTfoTfoZirZjv]mxYiuScpScpYiuZhxVduTfpXhtUhuSfuSftRgxThyThzRhtPcpPdqSepXhuZjv\ktXgp\v|=T[3EP5DQ8HUL`l—®¾p‹™G]b7LT5JR6GS9KV8IV8HU:IY7JT9KV>P[MZ\myy𛍋¤³ž°Â¸ÅªÅÖ¥ÃÖ°Ð虵΃œ°^t†DYh=T`9MXWhqbnxCLWHS\DMZ%,;as€^o|]n{Zkx_p}YjwQboYjx]jq]jr^ksbowiv~dqybowhu|ev‚jz‡jz‡fwƒ`o€[m|TixPetPfuOfuQetNbsOctRduPbsM_qOcnPdoUfr_q}XiqRclRakQ`i^wCYd/DN6HR8IV6GTlH^j>QY9LT5IP6IT5FS8IU8HY:JZ7JT;MW8JU9LY6IV7JV7L[7L[4N]:Q`®ÂÒ@S`BUb=Ra7Qasžx¦|˜­oŠŸ… ´x“§‚ž®}œ­k‰šy’£d}‘h„™[v‹|–«t’§Š¨»Œ«¼DP`AUf‚Ÿ°„¦º¤»|³†Ÿ·”ªÀ˜¶Ê”´Ë­Æ†¤À{™µz˜±Š§¾‹¤½u–£B\l?Q`Pdu–°Á’±Ã–²Ë—¬Ç‰¬¿ž¾Ö«Ìã¦ÆÝš¸Ít¡]q‚GVgP[9KV7HQ6GP7GS2BN7HU7GW9IX5EU8JT:KX8IV3FS6IV;M]5JY4IX3K]E^n•«ºRhs7MY6K[VfCYhBTbFWaBWg>Q^>OXiv~v‚‡}‰$/7S_h…‰‹„‡Šz€‚{€„v}ƒw‡x€‰xˆn}…mz†iy„]lxWgsN`oObpM^oNasJ_rI_qG_pG_sHbuJcwFbvPfxNdwShy[o~`q~euYiu]luK\eJ]dHZaEX_FV\AQWAPWBRXI`pNeu9N^=P]T`?QbQ^u‚”«…µ~Ÿ¶‡­Â„£º¡¹}Ÿ·x›³x—°}´®Èݦ»c{‹IXgETd8KZUp~ƒ¡¯e}’L^tCXjEYl=OaG[h@R]DUa=RZAV]8O`8N[=QY[gox„Šjt|":CMclsgsxis{lw€iw‚`o{[kwZjvUhwQcrQftLcrG^pG_sG`sG^uF`pFaqHauIbvIcvKcyMe|Nf}Xi}[n~aq‚ZkxXhu[luN`gK[bL\cFV]M\dGV\CSYDTZ=PS;NQLctAXh=Ra9KXCT^DQYEQXIRZDO[6CO-:F,9G$+;-M^BM\ANZNTCTYCSYASU;OR@SVf‹D_h6P\6P\5O[>YgB]kHcqTjtATaq‚‘¡o~ŽyŒš•¤t‹šr‡‘—¢Š›¦œ‘žx†¡®‘¬º£ÅÚ¥ÃÖ™©Vm|BXdATa?Rb8KZ>TXBZcF\i?ZjF`tk‡œp¦m‰£w’žsŸj†–g‚–}—­­Â—µÈ‰¨¼ƒ—¡7MY=P_.BSx£¬Á„¤»¤»ƒ£Â‚£½y—³{š³‚ž·”±Çƒž²d€–FX^>QY@R]=OZ7HU=NZ9IY;K[;J[ZoA]rB^q9Xr>Yt>XqBZsF^tBZqC[qF`sM_tK_tG_rB_tFcwFbzMgSiƒNk„Yt‰Lg}^yŠWqWo~WlxLanHfkQjoEX_IY`IU]GW^ASZBT[=VV?TV>NTDOWEPXDOUCQT>OR_x„Piu7Q]4N\5O]:TbE^nOhx˜¦¤²…˜©Šž®€“¥“©º„œ®œ¶Æ€š§‡ ®‡¬“¡‹ž¯”«º ¹É™³ÃŸÁÕ•²Å…ž®Vm|F\gI[h@Rb;N]DXXCV[K^fNaoVi|F`pvŽ¡}—¨|™§z—¥o‰žqŒ¡v“¨´Ñæ»Î•³ÇXpxC[g@Ud9M^tŠ›’ªÁx•®„¤»‹©Ã…¥¼€¶{–¬d€•Up€F]q?YjBT[BU]>P\:LW;LZ8JV:JZ9IZ4EQ6GT8IVNUBMUFPXAMS?NQ=OPl†”Sm{6P^2K[5O^:ScC[mLdul…˜š®™­vŽ¢ƒš°~–«~™®m‹Ÿq¥lŠ}—«{“§‡ ´‹¥µŒ¨¹©¹˜¹Î–¶Év‘¢k…’?Sb@Ud@Ud=PbKZ^N]dTemZlvK^kYo{Umyw˜~œ«z–¦{–ªš¯¡¿ÓŸ¼Ñ™·É„¢¶_{…?Ye>Tc.EUwŸ…š°‡Ÿµ™°`yŽk†–;Sf@Wg>Vf0EUNT9KUThwLcuGbuKfzMgzLezHeyEauLh{Kf~Hg}Gf|IfKl†NoˆSuKk‚Po‡Sp…Ok€jƒ—Œ ¶y¡}‘£p†–atI]hN_kCV^CT^BQZ@OX?NW?NW>OX:MU9MS@QZ>NV@LVAMW=KQ?MP=OPz’£Xq5N^6N`1I[;SeAYmH`ti†žz–®„µXsˆuާg‚œWuŽqª‚¡º{š³|›²r‘¨}˜±Œ©¾‹¨½‘®Â¥ÆÚ›»Î‘¬½n‡—Xf5Q^9Rb>Uem€’?TfBUl:P^>Td?R_BVc9KU>P[5GR:IVQY8LT?MN@OU;MS7JUE^mJg{Mj„JhˆPm…PrˆXz“Vz“Tx”JoŠVz˜l«zž¼£Â‡©ÇŽ±ÉŽ¯Æ–¶Ì•°Äˆ¥¹{Ž¡]q‚UgyPdsL]jHYf=OZBU`AR[DU^>LV?MVAPY?PZ;KUMVAQY;IU>IVBNXAOUBSV@RSr‹›_wŠ5M_/GY1I[7OcC[oNfzr«v”­w“«b”Ie~=\u6Tp=^{Xwc‚›l‹¥kŠ£}ž´ˆ§¾žµ‰¤¼ž¿Öš¹Î|˜¨pŠšC]k;Uc:ScWkE]qC^r‡¡¹„ ¸}š® ²†¨¶€š¯€Ÿ±‹¨½§ÃÛ¨ÄÜ”³È‘±Ä}ž®VsB`oOX>OV>PX4GNASZ7JQ:LSASZ>QXCQY;GQ7FOBQZ@NX@PV?PS=OPšªd}>Wg0HZ2J\7OaBZoNfzn‰¤uªmˆXs‡9Ti5Qj$A]"@]">RJh}s’¨q‘©u–­w—®„¡»‡¥¾ŸÀÖ‰¨½‚ ³VpIcsPY>PX:RW5MR5MQ6OS4JO;OT9LR;LRMT9KS=OVOS|–¤k„”D]m3L\4M]7OaBZlOgynˆ¢x”©s\u…Ict-H\,Gb4S4GX,CS™³Ä‹©½z˜®€ ·}›´…£¼ž¾Õˆ¦»ƒ ³c~Jdt;Ue6Sb3O`7Ug@_rIf|Lh‡¢¼ƒ¢»ƒ¢ºv–«‚Ÿ´…¡º“¯Ç­Ç¼Ó‰¨½‰ª½q”£Wn€>VhRYPX:OW8MU=SY9NTEdcDdc8WV+JI1MM2KK=OP:ILCQMDRP9KL@SV=QV>RV>NT=LO{•¢sš=We7O_6O_7P`B[kPhzqˆ z‘¥i‚‘Xn}=Wd0IZ,G_8T7EO/AN{–£Œ¨¸Š«¾z˜±„¡»…£¼Ÿ¿×¯Ä‰§ºu‘¡[y‡9Vd7Tc2Pa;V`A[iLeuTm‚𝋣¸z“¦uŽ pŒ¥€›³|–°‘¯È®Å‡¦»‹¬¿y¬h|PP:IK=KHBNO=MR6J[2Kd3Qq8Sx8Vw4Uu2Vo9\uMj[pƒ]l~QhtJ\gQ_kJV_T\fO\cDSZ=OU=PX:MU2IP;QX7NT7MT9MT#5>UuuCgfMqpRrsPmnF[].15>CMXPLYUburF[\DY^ATZ?NUN[bs‘¤eƒ–7Sd0JZ3L\6Pa@YmMh|x˜¯}š¯}–ª\wŒ7Wj3Sf.F\'5O;R[7M_r‹¦†¤¾„¥¹€¢±ƒ¢·Œ¦Á™¼ËŒ­½~œ¯k…š^yMV6EN8GP7KS5HO9LS5IM6JO9JLP[?Q\9KV4HO-;G7CO4@L2BIAWZ40SyqDyk?uf:oaH|jF{iPpPqN€mL…sU{p.A>{‚ƒENQ0BA9SO4OKu“¦b€“?Yj1JZ4M];SeB[pKg{}žµtŽ£n‡›h„•4Ue3Sf+CY%3N5LU,BTr‹¦€ž·†¨»†¨¸‚¡¶¨ÃŸÂщªºqŽ¢hƒ—Jcw?[o>Yn3Pe5Wf@]nHcwTk‚d|”i‚u«v“¯w–ªs’©¶’°É‡¥¿Œ«Âv•ªr“¦k„”Ics:Sc5L\8L]1CT6EV5DV3HR3GR3GR2ES2CP0AN2CP4DR3DM4EN8IR9KT>MV;JS:IR:HR6IP6HO9LS9IPPZXi5N^8Qa;SeB[oLeys•¨l‡œv£Uq‚<^m0Qd.DZ 0G/FP2H[Œ¥À ¹†¨»}Ÿ¯|›°†¡¼§ÈÚ­À{™¬rŠž_xŒD]r:Sh7Sd7Vi@^qGbwOf}Wo…t¦t¨t”«y˜­w–­†¤½‰§À‰§ÁŸµŠ©¾w—ªr‹›Rl|:Sc6M]7K\2CT3EV5DV4HR2FQ1FQ4FQ3EP5HS9IU8GT;MT4FM3FMMR>MSCNT;OS;NS7IP9KRL]fJZfO_k`p|]mzTdq[kwUdpOalHYeGZb>RXBS`=N[AR_;LY7HU9JW:KX4FS1AM6EP6>L!+2;EBNRFXWf|xTwJunN|rM}tExnG|rHqKƒv?€pG†wC€ui®¥qÀµuºr¹®†Ä»~œ¯f•B\m?Xh7P`?WiHauIbvt“¨r¢o‡›[u†8Zj5Sf)?V!.EE\f?Ugš´Î…£¼‚£·~ ¯q¥Š¥Àš¿Ð‰ª½v”§w‘¡¥·Zq€>Wh4M]:UkC^sHcxNh}Xsˆw”©}š¯v“¨†¥º~´‰§À‰§ÀЍÁ†¥¼ž³~ž±ržUn~KSKS>KSANV:GPALTHP2EK9KR:MSFW`N`hRbnP`lO_kP]kKWfJ[fGWcBT^?S^DYcR$1IAXb8Na£½×‰§Àt•©£²Ÿµ‘«ÆÁÔˆ¨»}›®§¸Wn~L`oKR;LUEV_^ow[ltO_kTdpQamO`lKXdIWbBR_@P]?O\>Q^:NZ;N[;KX7GT3DP1AN7GS9IV3CP5FR0=K.:F1;E")35;@JOR"'*"''46-/ $"!=:Xurs‚~rŒ†ZŠ‚:€u^¯¤o¸±M~zg„…y”¨oˆœNfx?VfAXhF\nf~’€˜¬t”©~—«q‰›f€=\k/M^(=R!.E1HRAWi©Â݉§À}ž²£³ž³”®ÉœÀÓ„¥¸­ÀŠ£²^s‚3FS7IT3FM9RhE^rIdyPm‚ZyŽkŠ¡{–®{—¯¡µ‡¦¾ƒ¡º®Ç‡¥¾Š©À{š¯~ž±|–«fƒ“?Yj2K[6M]3HW1FU4GS4ER6GT7IT6FR8IR1DK6FM;KR;KR=MT7HN?LT>KSCEJM&)- #(-/527:48;7>A!!"1-&FAc‡r™—sއ•šsŽ¢uŽ¢^vˆRiyj‘p†˜y’¦ƒš®{™¬ˆ¡µŒ¢´x’¢:Zi0N_(=R!-C4>1GYœ¶Ðy˜±w™¬{¬†¥º©Ä›ÁÔˆ©½«ÉÜn‡–ZlyJ\g:KT/AG5K_C[mFavKjSr‰j‡¡r‘©u«v–ªƒ¢¹ƒ¡º”²Ë«Ä…¤»‰¨½ ²w•¨l†›NU9IN=MSMS>JP:FL6CL8DM7HO;;LR9GM9EJ/;A5AG7EJ=MT?OUAR[WgpZlwM_jXjuQaoM]kJ\gFXcAS^>P[:LW\o=Wf7Q_;Q]6JU7HS1FQ4HT6KS9KS7IP6FM;IO>IO:PR7LM:MU3EM1AN%7@=KU;KQ?QX:LSWip[msQbkRclVgpPajCV^J]e@S[N.;K$/='4#,!&!%/45#"#(' .,,&!"+"O93D($gRQqgj~„r£j…™rŒ¡nˆw’§mˆ~˜°v¨|›«€›¬i…–^xˆOhx@WgCWfMap‡¡²›µÆ‰¥¶›¯™¸Ê’²Ç•´É›½Ð•¶É‚¢µ~š«azŠEZi6FSARZ8GP/BM/CRJ_u\ty•¯s§u”©w•§~·‚£¹°Ç¦ÆÝˆ§¾~²~²r‘¦†¥º{œ¯:Xk5N^9Tb:O[9MW3GR4FQ7IT9LT9MU6IO9IPF:FP1>J@LX?KULWbSfm\nuQcjQcjL]fHYbK\eFW`GZb>QY:MT>P[7IT:LW6GT7HT6FV9IY8HX6FV7GW8HX7GW7GW3CS3CS1AQ3BR/>N,G19FAJWZbp\fpfnyNahFX_GY_@QZHYbEV_;LTFUbDW_:MT;MX5GR:LV3CQ3DQ4FR3CT5EU4DT3CS6FV6FV5EU4DT1BR/?O/>N.=M-P[2DO6GT2CP6GT3EQ3CS3CS4DT7GW4DT5EU3CS->N2AQ.=M0?O.=M.;K+8H2?O#1A"0*& &6<=%%% ebdLNLAIB&!)"&¦È‹±Ñ€£Äv˜¶„¤ÁŒªÅ…£¼{–°m‰¡s§Ws‹s’©–µÌ¬Â¯Æ‚¡¸¤¿„§Á…©Âž¿Ù“°Ëž»Ö¬Äœ¶‹«Â ¿Ó©¹l†”OdoDVa;KW8ER7FO/@N*>Quޤ‰¤¿‰§À†¦½“´È‡¦À›»ÒŸ¿Ö¡Áخņ¥ºŽ­Â†¥º„¥·mŠRq„@[l=Vh9Rb:Qa6M]:MS9JS5DM:FP:DM4@L3AM:JV?U\-AL·Êו§´Ÿ¶ÂmƒRhsLdkCT]HYb?PY>PX:JWL3DQ4ER4ER3CS3CS2BR3CS1AQ4DT6FV4DT/?O1@P2AQ.=M1>N0=M-;K2(6"/#-$$)*  %!&XWY@ECCJE87.\RKgXRz¢Åj“´j°xœº€ ½t’­l‡¡t¨{—¯f…œo¦Œ«Ä‚¡º‹¬Æv—±yœ¶£Áa…£†§Ä}Ÿ¼‰©ÆŽ®ËªÅŒªÅ‘°É•´ÉŒ©·[t€Kak;MX9HT8ES4DJ2BO.CTBZq…¡»Œ«Ã’°ÊŒ®ÅŒ«Ä¤ÅÛ ÀׯƎ¬ÄŒ«Àt“¨n¢|¯v”§`~‘B]n=Wh:Te9Qa1K[4HO6IQ6DM2=J,8D0>JDVa9KX‘®¹”®¹›©LguwNfr=Va3/ub]B)%bŒ±h³q—¹vš¸q‘®v”­‚œ´v‘¦m‹¤pާ ¹ƒ¥¿ˆ«Å}Ÿ¼~£¿{ ¼¤Åƒ¦Çv—¸†¨Æ„¦Ä}Ÿ¼Ž°Í€¢¿®ÇŽ­Â|˜§j…ŽE\d@RZ:JW6BS9FL/?L/AS0H^“®É†¥¾³Ë’´Ê®Ç¯Æ£ÃÚŸ¶‹ªÁ…¤¹z™®s’§{™«w•¨g†™NX:KT9JS7GS2BN:JU6FS9HV2EO.?L5FS1BO0AM1AQ1AQ1AQ2BR2BR2BR2BR1AQ3CS4DT3CS2AQ0=M0=M/K[9FM3CP*Ym=Yj:Te8Rc6Pb1KQ/FO9IR>KX…“Ÿœ­º}–¢^x†[vƒf}ŒOct:L]BQbAN\;GSDMW;MV5GO1AM5EQ5EQ5ER9IV3BO1BO/@M3DQ/@L3CS/?O0@P2BR0@P0@P2BR3CS0@P1AQ1AQ1AQ,9I0=M1>N'4D+5F1;L'1B'1C"-;!*7$.$*&+/&*+"'((,-".+%-,#"%$#!!,+'/+*=98%-&userland/host_applications/linux/apps/hello_pi/hello_triangle/Gaudi_128_128.raw000077500000000000000000001400001421703157200300330ustar00rootroot00000000000000ÙÙÙÙÙÙÙÙÙÙÙÙÙ€Ø{ÓÙÙÙÙÙ‰á†Þه߇߇߇߇߇߇߇߇ߌߋߋà‰ë‰êŠé‹ß‹Þ‰ì‰ê‰è‹Þ‹ß‹ß‹ß‹ß‹ß‹ßŠâ‰ê‰êŠæ‹ßŒÜƒë„礎,šF5„N |J*-8)0 " 1Ew±Ø‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ßŠç‰ê‰ê‰ê‰ê‰ê‰ê‰êŠá‹ß‹Þ‰é‰ê‰ê‰êŽï‰ê‰ê‰ê‰êîñ‰ê‰ê‰ê‰ê‰ê‰ê‰ê‰ê‰ê‰êîŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŒù‡ô‡ôÙÙÙÙÙÙه߀ØÙ‡ß‡ß‡ß‡ß‡ß‡ß‡ß‡ß‡ß‡ß‡ß‡ß‡ß‡ß‡ß‡ß‡ß‡ß‡ß‡ß‡ß‡ßŒß‹ß‹à‰ë‰êŠé‹ß‹Þ‰ì‰ê‰è‹Þ‹ß‹ß‹ß‹ß‹ß‹ßŠâ‰ê‰êŠæ‹ßŒÜƒë ‰ì€‰Q0O_ %žw*ßÖ‘,Occž‹Ö‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ßëðˆéîŠë‰ê‰êîŒâ‹ß‹ÞíŽïŽïŽïŠëŽïñ‰ê‰êîñˆéŽïŽïñ‰ê‰êîŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŒùŒùŒùÙÙه߇߇߇߇߇߇߇߇߇߇߇߇߇߇߇߇߇߇߇߇߇߇߇ߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߊá‰ê‰ê‰êˆé…æ‰ê‰ê‰ê‹ìí—ö ·+ˆh(k#K Zˆa ä÷b§ÛŠ“Ès)My- €¿’ç‹ß‹ß‹ß‹ß‹ß‹ß‹ßŠç‰ê‰ê‰ê‰ê‰ê‰ê‰ê‰êîðˆéîŠì‰ê‰êîŠë‰ê‰êŽïŽïñˆéŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïÙÙ‡ßه߇߇߇߇߇߇߇߇߇߇߇߇߇߇߇߇߇߇߇߇߇߇ߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߊá‰ê‰ê‰êŠëîˆéðŽïŠëˆðW¡­€½wf—(-X $8h‘\£ìæ¿æ¸¾èÀn¦xS)|¸‰î‹ß‹ß‹ß‹ß‹ß‹ß‹ßŠçîŽïŽïŠëîŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïÙه߇߇߇߇߇ߌߋߋ߇߇߇ߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߊá‰ë‰ë‰ë‰ë‰ë‰ë‰ë‰ë‰ê‰ê‰êŠëŽñí‰Ö‰Ñ—öÇWÎKeÂCLž—N`‚XGœŒ£óÙƒÌhnºh^²)I›06‰6l%){u…ê ˆüðŽðé‘åŠÞêŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïåããããߎïŽïäãâŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽï”õ”õ”õ”õÙه߇߇߇߇߇ߋߋߋ߇߇߇ߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߋߌâŽïŽïˆéŠëŽïŽïŽïŽïŽïŽïŽïŽïŽîï‹òöC©ÌA±mW³Lfµ4E6ÿ´V™O;¯FU®^ºøÞh³WO ?š.„)i!N+{²ŽïŽïŽïèääìŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïæääääáŽïŽïåäãŽïŽïŽïŽïŽïŽïŽïŽïŽï •ö –÷ŽïŽï •ö”õ”õ”õ”õ”õˆàˆà‡ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹à‰ì‰ë‰ë‰ìò‡ê‰ìŽñŽñŽñŽñŽïŽïððŽïŽïŽïŽïŽïî‡é’ë„ñfª‘¶Â$ƒ©dp¶r1¢m¡w£.KFãe ”+O‘`:\›Rj‹F‹(_4J%= 5[•‰ßñŽïŽðŽðŽðŽðŽïŽñâãìŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽðŽðŽðŽðŽðŽñŽïŽïŽðŽðŽðŽïŽïŽïŽïŽïŽïŽïŽï •öŽïîŽïŽï”õ”õ”õ”õ”õ”õ‡ß‡ß‡ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹à‰ëñŽï‰êŽïððŽïŽïî‰êŽïŽïŽïŽïŽïŽïŽïŽïñ –ö Žù ŽôÅx¿ß2ŠË55¤LE§?%bYj#^Š,šnë”s-':z4t]˜G–ä°3}Cc \G"8QˆôŒïŽïŽïŽïŽïŽïŽïŽñãäìŽïŽïŽï”õ –÷ŽïŽïŽïŽïŽïŽï”õ –÷î”õ •ö —øŽïŽïŽïŽïŽïŽïŽïŒí •ö •ö •ö —øî •ö •ö —øŽï”õ •ö –÷Žï •ö”õ”õ”õ”õ”õ”õ‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹ß‹à‰ì‰ì‹Ü‹ß‹ß‰ìôñ‘åŠÞ÷ŽòŽòðŽïŽïðŽïŽïŽïŽïŽïŽïðŽïŽïŽïŽïð –÷ •ö •ö •ö”õ”÷&›ÕÐ#«Í“­!)žbo(«ˆ£e.éºqêžK‘z<\\x8.0"& *(dxz{{k—‘=˜Z*çªwõµt‘OýÖŽî¦FyxDEºÿÈ!öÁÅ©^>nœ”õ ’õ”õ”õ”õ”õ”õ ”ö “õ “õ”õ”õ “õ “õ “õ”õ”õ”õ”õ”õ “õ “õ “õ”õ”õ”õ “õ “õ”õ”õ ”õ˜õ˜õ™õ”õ”õ—õ˜õ˜õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õšû •ö¢ÿ ™úŽó”ú”ú‹ß‹ß‹ßðŽïŽïðŽïŽïŽïŽïŽïððŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽï‹ì‰êŽïŽïŽïð •ö •ö •ö“ôŒí •ö •ö •ö ’ôõõ‘ö–û˜û†þL„Ég4nl äÌ”ãÀ”í¨×’\æ¸ué¼xè¶tÿÜž½H´ŸhÿÙWÿ¾àÆ2àÃ,É»pr/‹æ–ù”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ ”õ˜õ˜õ™õ”õ”õ—õ˜õ˜õ”õ”õ”õ”õ”õ”õ’ó›ü”õ •ö¥ÿ —ø ™úšû ˜ù ™ú”ù•ú•ú‹ß‹ß‘åŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽï —ø –÷ •ö –÷Œíð”õ”õ”õ”õ •ö”õŽõõõõô’í–ÿ_‹ÜÿÔ7‚]Ä èÄ¡‹‰¦Ÿ{Ú…LùšGö½yò֟ѱ|ȧaúà•üé£úÈ‚×u’Òižß r²ˆÐl‘ÒW‹Ér¿Lr´KU­DCš=#Œ|•ö šùšû ™ú›ü“ô ˜ùšû›ü“ô ˜ùœý ™úšûšûšûšû›ü •öŽï‘òšû •ö ˜ù ™úšûšû ™ú›ü“ôšûšûšûšûšûšûœý“ô ™ú •ö •ö›ûšû”õ’ó ™ú ™úšû›ü ™ú ÿ–ú•ú•ú—ïžöžö‘å‘åäŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïŽïð •öîŽïð •ö •ö •ö •ö”õ”õ”õ”õ”õ”õ –÷ •ö”õ”õ”õ”õ”õ”õ—ü–û•ù’ë’ì"“ñrØÑÏÇ·± ì²tóдܟnð¥oü±rð¼‰úžwÿ¾¡ã´yεÿìºõ·si­XŠÅnfÊ›šõÊ]¸g_²ZL %6•).Š* c(‰ÿ˜ÿ ›ó ™ù›üšû ™ú ™úšûšû ™ú ™ú ™ú ™ú ™ú ™ú ™ú ˜ù›üšû ™úšû ™ú ™ú ™ú ™ú ™ú ™ú›ü“ô ˜ù›ü’ó ™ú ™ú ™ú›ü ™ú ™ú ›û”ú”úšû›ü ™úžÿ“ô’õ—ì‘èžóŸôŸô&˜ö%—õ%—õää‘åŽï –÷ •ö •öŽï —ø •ö •ö •öŒíŽïŽï –÷ •ö •ö”õ –÷ •ö •ö”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ“ôŒíþ šú •ö›ü”ú•ú”ø‘î&›÷$˜øB‹í¨­>¨Ý´@ÿѫèš]ìŸZÿÏî²ðµƒêÈ‹þЊí®kõ¢UÊ¢9w·GtÃJ„Är²sp²6kª*W’6u(lJ7…Úÿ šø ™ú ™ú ™ú ™ú ™ú ™ú”õ —ø ™ú ™ú ™ú ™ú ™ú ™ú ™ú ™ú ™ú ™ú ™ú ™ú ™ú ™ú ™ú ™ú ™ú ™úšû ™ú ™ú ˜ù ™ú ™ú ™ú ™ú ™ú ™ú šú•ú•ú“ô‘ò ™ú ˜ùŸÿŸÿŸ÷õõõõ#˜õ#˜õ#˜õŽòŽòŽñ –÷”õ”õ”õ –÷”õ”õ”õ”õ –÷ –÷ –÷”õ”õ“ôî •ö”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ •ö›üŒóö•û”ú”ú•û‘÷”û™é—ì—î%š÷#˜õ$—ö"Žÿd|ƘTà ‘õÊô—QóYâ‡?÷¤Wÿ¶rÿ—äÒgðª‡ÿç®á±Yµˆ-cÂ1–À(X¿$k³ ‰«@O£L9Ž/,€'1M&K>°(œÿøŸõ–ë—ìœñŸõ–ë–ì˜ö ™ú ™û ›ï ›ì íæ–ë›ì šë¨ù ›ì ›ì›ëŸôžôœî ›ì ›ì ›ì ›ì™ì£ì¢ì œì ›ì šë£óœí ›ì œë¤ôóŸô—ì—ì•ì•ê•íõõõõ#˜õ#˜õ#˜õŽïŽï –÷”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ –÷”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ”õ šú•ü–û•ú•ú•ú•ú•û•û—ì˜ðö#˜õ#˜õ)œüPЧÆÍ³—PüàÁñ§aãKû§eåLþÄ‹ôºïÅŽï¿mÿÍ’ã}BÿÒŸÑ¥W€¤z¥_“h¾e¢Á„bgGKpz9ty:%VA…Ö$œþžïžö–îœôžöõŸ÷™ñ˜÷ ™ú ™û ›ñ¡õ£÷õžöœð šî•éñ¡õ£÷õõ›ð ™í¢ö¨ü¢ö£÷ ÷¡÷¢÷£÷£÷¡õœð ™í£÷¡õžõõŸ÷Ÿ÷–î&¨ÿŸ÷õõõõ#˜õ#˜õ#˜õŽïŽï”õ”õ”õ”õ ˜ù”õ”õ”õ‘ò”õ”õ”õ”õ”õîŽï”õ”õœýšûšû”õŽõõ•û›ûšû™û•û“ú•ú•ú•ú•ú•ú–ø˜í ô)—÷&˜ö%”ø žë/„ÿ •àÙkàÐAÿº¡ß‹I÷¶uìÂ}ÿÂmó¯]òŽOÞµbïÒÔéŠ=ÿÊ—ÞÌà˱éд§i•š6D-Y?oV cFM30Y?;zÄœúŸðõŸ÷žöõõõžö$¤ú˜îŸôöœõ›õõ ¢úœôöž÷öœõ›õõõö÷›õöœõ›õõõ—ð•íœõœõö÷œõœõõõõõœôžöõõ"¤ü#¥ý"¥ý+ ý+ ý+ ýŽïŽï”õ”õ”õ”õ •ö”õ”õ”õ”õ”õ”õ”õ”õ”õ“ô“ô”õ”õ›ü ™ú ™ú”ô—ý–û•ú ™ú ™ú˜ú•ú•ú•ú•ú•ú•ú•ú–ù˜ðžõ%—õ#˜õ)–÷8œøH“ìÍõWʱ"ð»\ñ¶î£hÿ¾zöÆ‚üÃqìœGð²Ž÷Å{ûÚ’ÿصéјü¾ŒÿÈùÊóªE…h]OiGÿëuþÕ=üÉã´#=%-A6DC)2lŽœóõõõõõõõœôžöõõõõõ›óõõõõõœô™ñ ¢úœôõõõõõõõœôœôõ¡ùõõõõõõœô"¥üžöõõ"¥üœô›ó$¦þ)žû)žû)žûŽïŽï”õ”õ”õ”õ”õ›ü•û•ûõ”õ”õ”õ›ü›ü”ü•ü•ü•üöö”ú•ü•ú•ú•ú“ú”ú”ø—í—ì+™õ&˜ó&˜ó&˜ó&˜ô&˜ô&˜ö%–õ#˜õ#˜õ+•÷/÷M Þѯ׭õ©eø¦WΗHøØxú×hÛ¶tÚ³€áÔšïÑ•ÿ¸Yþ¼…åIéë‹â°RöÀpî¹X‘µP_FÁîÊ\ÿÊ^ÿÕ5ൠrOQFBŠFR†.2+CEj?’è#¥ü'šñ%–õ%–õ%—õ%–õ%–õ%–õ$–ô+œû&˜÷'•õõ!£ûžöõœô"¤üžöŸ÷—ï ¢úžö›ó#¥ý#¥ýžö›ó#¥ý#¥ýõõô+žû-žý/œý"¥ý#¥ý#¥ý#¥ý"¥ý+û,žü,žý-žý+œû,žý,Ÿý,ü) î) î) î –÷ –÷”õ”õ”õ”õ”õ’ó•û•ú–û”õ›üšû ™ú ™ú”ú•ú‘÷”ú–û•û•ú•ú•ú•ú•ú•ú•ú–ùŸ÷Ÿ÷%–õ#˜õ#˜õ#˜õ#˜õ#˜õ#˜õ#˜õ#˜õ$™ö,þ2œôBóv¨dÆ“=ý¯þ°eó¸kÖ¤Xý¸xê¦Vÿ¼s÷ÚÁûÓ¨÷³ŽÿוﰃùŸ}ç›_ó¹ué£GðÆÿãhÿØ:ËäcÇõx¨(\“Z”6;rS.V€C[n@%Jn’!£ü%¢ý+ ý,¡þ"—ô)žû+ ý+ ý.£ÿ'œù*Ÿü,žý"¥ý!£û"¤ü"¥ý#¥ý!£û"¤ü#¥ý!¤û!£û"¤ý#¥ý!£û!£û"¤ý#¥ý!¤ü$¦ÿ"¤ü"¥ý"¥ý)žû)žû+œû!£û!£û!£û!£û!¤ü$™ö)žû)žû)žû)žû)žû)žû)žû) ð) ð) ð”õ”õ”õ•ü•ü•ü•ü•ü•ú•ú•ú•ü“ú•ú”ú’÷—è—í˜í—í—í˜íŸóŸóŸóŸóŸóŸóŸóžô%–õ%–õ#˜õ#˜õ#˜õ#˜õ#˜õ#˜õ$™ö* ý8™ý5šü4š÷&Ÿú4™ôšŸÑð§jðÌ㶈õÄ‘úÖœý®\ÿ«Oü¥Qð²vúåªÿÛ§øèºÿá«ùÊø»qü¹kå(ûÀuðáKŸÜ‡J©Xl·Tt¼dX£HL›B<’)J‡%=uW.Sš’Ì"©ã-õ)žû)žû+ ý*Ÿü)žû)žû)žû)žû)žû)žþ+ í+ î+ù+œû+œû+œû+œû+œû+œû+œû+Ÿï+ î+ î+ î+ î+ î+ î+Ÿí+ î+ î+ î) î) î) î+ î+ î+ î+ î+ î( í–â+£ð) î) î) î) î) í/¦ö) ð) ðŒí ™ú”õ•ú•ú•ú•ú•ú•ú•ú•ú•ú•ú•ú•ú•ú—í˜ðŸ÷Ÿ÷Ÿ÷žöõõõõõõõõ#˜õ#˜õ#˜õ#˜õ#˜õ"—ô,¡þ+ ý*Ÿü(žû4™û2šü1›û3œï’˜³ý£wì®rñ¾zé±}÷Ü¥ðÌ™¼‹Yÿ½{ÿÊ…Õ’TíʼnÿÂsÖ2ïµtì·€è¢\ò´iü¥Bð¦Y©Æfs³awà™ØÉpµ…p¯]w¯Nn¦Ee¤LY”W?f^?^†1Ö šó)žû(žü)žû)žû)žû)žû)žû)žû)žû)žþ) ï) ð)žù)žû)žû)žû)žû)žû)žû)žû)Ÿò) ð) ð) ð) ð) ð) ð) ð) ð) ð) ð) ð) ð) ð) ð) ð) ð) ð) ð) ð.¥õ,£ó) ð) ð/¦ö0§÷0§÷.¥õ/§ö/§÷ –÷“ô›ü•ú•ú•ú—í—ì—í˜íŸóŸó•ë—í—íŸôŸ÷žöõõõœõ%–õ%–õ%–õ%–õ%–õ%–õ%–õ$–õ#˜õ+ ý+ ý)žû$™ö+ ý)žû+ù4œî5œí'›ÿE™Ô9¥Ïo•Õÿ½c˵}öÓ¥ÔÁxúÃpüœYîÍ~úÔµÿçÉá˜iï“2à—1à©6ú„ý­RíŒHú¤bØOØŸsìÆtE¬J\¯Ng¶eq¸^ŒÃ<šÇ;šÊ1‘Å™¾>„«$w¢_“ ;f9Z§•ûK›Ó8¢÷;¢ó.¡ï(Ÿí0¨ô*¡ï-¥ò0§ó0§÷*¡ñ.¦ó1¨õ(Ÿí.¦ò*¡î) î) î) î) ï) ð(Ÿï/¦ö0§÷1¨ø(Ÿï/¦ö*¡ñ) ð(Ÿï/¦ö*¡ñ'žî0§÷0§÷*¡ñ'žî0§÷0§÷.¥õ.¥õ0§÷0§÷.¥õ.¥õ.¥õ9¡õ9¡õ9¡õ›ü›ü ™ú•ú•ú•ú—ïŸ÷Ÿ÷Ÿ÷õõŸ÷Ÿ÷Ÿ÷õõõõõõœõ#˜õ#˜õ#˜õ#˜õ#˜õ#˜õ-¢ÿ+ ý*Ÿü)žû)žû)žü*Ÿý)žû)žû*žú2œð0œñLê'¢þ§Ôþ³Zó‘SøÇ—ñÑŠæ•Eý¦iå¶dî¸dó¤@ç—4Ùƒ&ö®TüÀ`à­E÷­rãŽ9ý¹wóšWé«hÿ¶hý„3M*m¨7f±8|ÊK™ÔU©Öa™ÆJ“Ã1„À4€º.g¡[‘$_?AlB;?<Þ4¡ö3ñ1¥ö0§÷.¥õ/¦ö.¥õ.¥õ.¥õ/¦ö.¥õ.¥õ0§÷.¥õ/¦ö0§÷0§÷0§÷0§÷0§÷0§÷.¥õ) ð&í0§÷.¥õ/¦ö0§÷0§÷.¥õ/¦ö0§÷.¥õ.¥õ/¦ö0§÷.¥õ.¥õ.¥õ.¥õ.¥õ.¥õ.¥õ.¥õ-¥õ7¡õ7¡õ7¡õ•ú•ú”úŸôŸôŸôŸ÷õ'–õ%—õ%—õõõœõ%—õ%—õŸõõõ%—õ%—õ%—õ#˜õ+ ý+ ý+ ý+ ý+ ý)žû)žû)žû)žü7›ê4œî4œî4œî4œî5›î=˜ñC÷2 âj”áÿ­Ní¨]ë´hõ¤Vü®Fõ‹.ÿ·Fû¼ÿÌã–Vÿï§ÕÑoì¦G÷’@âÜ`ÿØzñ²jð¸]þ®aö‘Iè¦Br† aˆ:º5‘ÅE“Öi¬ÈJš’ob_\ea¢¦¨ý?©ý?©ý=§û?©ý?©ý˜çŸó'˜ó%—õ%—õ%—õ%—õ%—õ#˜õ#˜õ#˜õ%—õ%—õ$—õ*Ÿü*Ÿü,žü,žü,žü)žû+ ý*Ÿü)žû)žû)žû)žû)žú) í) í* í4œí4›í9¤ø8¢ö9¡öBöAœôDœì¥ÿ’›Öó±|ô³fûÁnú°UòÏgõÙsÿ×£öÇÃòÃAùÁSÿÊjî»bÿÒyö¸^ôŽ:ä™>ý²/óªYð¼uø­Gÿ­g÷FÿÁyʨuôy.¥Š-w¶†º&_V`5|MŸ{¿e ŒXc3wV hx:N*omD ù¼iæŸPû¡]å¼|ðё֎Hÿ»mû”Cå°[£Ÿ š PH˜|(ÿÛlüÔSôÏ8ýÊ.“a’s,cr-D4x–lV]R:$CT7 XˆHªó4®ñ:ªú8¢ö7Ÿó5­ý/¦ö-¥õ-¥õ-¥õ-¥õ-¥õ-¥õ-¤ô4«û4¬ü4¬ü4¬ü4¬ü4¬ü4¬ü4¬ü4¬ü4¬ü4¬ü4¬ü4¬ü4¬ü4¬ü4¬ü4¬ü<¨ü=§û=§û=§û=§û=§û=§û=§û<©ñ<©ñ<©ñC°ø=©ñ<©ñœõõžõ#˜õ#˜õ#˜õ*Ÿü)žû)žû)žû)žû)žû)žû)žû)žû)žû) ë) î) î) í) í* î9¡ô4›í&¢í) í* î;¡ö9 õ9¡õ7¡õ7¡õ7¡õ7¡õ9¡ó>ª÷3 ÿ‹§ÖîÊ¡ìÓžù¿NõÁ\ñºYòº]ð¸X÷ÇkÞÏ“úÄeý¨8þ²Bò¢Köð¡ÐµPþ¥ï»Uä£@÷®Fæ¤9æÈxÿÔ•ñYÿ˜í˜R¿OçŽI㵄ͨ-Å j?º;Ûé`àßZòÆqò¾5ÿ”7Љ8¨ü=§û=§û=§û=§û=§û=§û=§û?¦û3«û3«û;©ò=©ñ=©ñ=©ñ=©ñ=©ñ<©ñ<©ñC¯÷E²ú<¨ðC°ø>ªò;§ïD°øD°øD°øD°øD°øD°øD°øD°øC¯÷C°øK«÷K«÷K«÷K«÷K«÷K«÷$—õ$—õ,žü)žû)žû)žû3œî3œî3›í3›í3œí( í) í* í3›í3›í;£ø:¡ö:¡ö8 õ8 õ8¡õ7¡õ7¡õ9 õ8 õ8 õ7¡õ7¡õ7¡õ>¨ü>¨üD¢þM ô,¤ÿr«âÿƃó×ÅÿÁQô¨RðØ}ú½nú³gúÃiøÁhñ¶\ÿÓuî³]î¼7÷ÄFøÁU÷·tò³lùÄ|Ò¢IøÄlèÔjÿâ|ݾ]õÉuчFù¥fì—EïœMèœ0ö¤[µ§dï³n¹*³É8¯À(µÊ.°¹1¢¯#¨*s–_sHLTwXS\L7JJ(‘[@´FÀdZ‡¸I£üD¡ÿ>§þ=©ï=©ï=©ï=©ï<¨ïC¯õD°öF²ø=¨îF¯öD°øD°øD°øD°øD°øA±øN«øN«øL«÷Lª÷N«øLª÷N«øN¬øLª÷Lª÷Lª÷Lª÷Lª÷Lª÷Lª÷L«÷Lª÷Lª÷K«÷JªöQ±ýQ±ýK«÷K«÷#˜õ#˜õ)žû)žü(žû)žû2œð2œð9£÷8¢ö8¢ö.¦ö.¦ö/¥ö8¢ö8¢ö7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ6 ô@ªþ>¨ü=§û;¨ü\œå1§þ‘¨ÈùÌŒâ¡]ý̉æ¡9æ£MçÀiî°gì¦\ì¹]þÊqê®WߣKô¼eןAùÂfúÅhï¸Wô½\ô·[øµcõ¨Uåƒ;å‰>ÿËŠøÁ†ø¸]÷¨Hå’Aÿ©YÞ³d÷˜Jø«SغP±Ï;ªÑ2ŸÍ.›É'–Ã0šÅ3”·.³…¤*\QKXˆS[[wmJœ…DZ%YN,IES@`…?šÌ#²ñ>¨ñB¯÷C°øC°øD°øC¯÷C¯÷B¯÷D±ø>ªòC°øC¯÷C¯÷C¯÷B¯÷@°÷K«÷K«÷K«÷K«÷K«÷K«÷K«÷K«÷K«÷K«÷K«÷K«÷K«÷K«÷K«÷I©õP±ýP±ýP±ýP±ýP°üP°ýP±ýP±ý*Ÿü*Ÿü)žû3›î:¢ô:¡ô8¢ö8¢ö7¡õ7¡õ7¡õ8 õ8 õ8 õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ@ªþ>¨ü>¨ü>¨ü=§û>§úF¥ðG¤ñ5¶¿~’éêÖ•û¼rÞØŸÿëÕ÷Îoõµ`ÿÑ¥÷ÑjõÎlø˜^ûÓqÙ´>òØ}÷µ7å¦KòªFý¹Wð©Iû³Tí¦Gï£Lÿ˜LñÃRñ¤Sÿ¸yì™]ó­íÆ”å ^é­kô¬VöÀTÔ­+ÂÂ/»É3ªÒ0žÑ'§ÕO˜ÄI‘Á0„º.Žº5Š£ „DoF`Wn[NfG0HzUB›=vUp‡ŠygŽjƒš<´ýG°íM«óLª÷Lª÷Lª÷Lª÷Lª÷Lª÷M«øLª÷Lª÷Lª÷Lª÷Q°üT²ÿK«÷K«÷P°üQ±ýQ±ýQ±ýQ±ýQ±ýQ±ýQ±ýQ±ýQ±ýQ±ýQ±ýQ±ýO²ýZ«üZ«üZ«üZ«üZ«üZ®ïZ®ïZ®ï)žý)žý)ý8¢õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ>¨ü<§û=§û=§û=§û=§û=§úF¥ñJ®úzœÚÖÍ|ÿ§ƒèÖ ôÞ—ðµzÿŀ待ä´?ÿâÔÿÚ“ùÀwà4ö©Lòµˆþ¡Vù¾\÷¸Wû·Uò²PÿÃbÿÐwî«fî¢hø˜8ÿׂôÏüÓ—öž?ÿ›7ò˜Bõ”>ð«`ø›AÉË=²È0£Ð/¨Õ_âûöµÔ°–ÀZ…°9u¨%{§Ф …˜2_*RMF]Z]F_zIhˆ™ªŠ`(S2iV¶¼—x©àW¨óJ«ùL«÷K«÷K«÷K«÷K«÷K«÷K«÷P°üQ±ýQ±ýQ±ýP°ýP°üQ±ýQ±ýP°ýP°ýP°ýP°ýP°ýP°ýP°ýP°ýP°ýP°ýP°ýP°ýP°ýN°ýY¬ýY¬ýY¬ýY¬ýY¬ýY®ñY®ñY®ñ*£á)¢à/¨å7›ÿ7Ÿÿ6¡ñ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ7¡õ>¨ü>¨ü>¨ü>¨ü=§ûH¤ìF¥ðF¥ðF¤ïF¤ïH¦í>®ÿv•ëÿÃEþÌ‹úͳÿÊá¤iüÑN÷¬@ÿÃP理íÞ¢í±8ö @ú´SãÀ†ôâyòºnÿ Fò¶_ç´aï¥Xð¨ZøÃiöØ›ãÏlþœ=ú‚EôµhëªGðÑcÒµEÿÅbé•:óªGê¬?½ÍYÈ9›Á6š¿P˜À\¾.‹¸:¬/†¢‚—q‰dzaf"q–‡<L5TNXuoF|rcÁ²ˆn?x+ŒƒQÈ®Œ©ÂI°úMªòMªóP°üQ±ýQ±ýQ±ýP°üP°üP°ýP²ðP²ïP²ïP²ïP²ïP²ïP²ïP²ïP²ïP²ïP²ïP²ïP²ïP²ïP²ïP²ïP²ïP²ïN³ïY®ïY®ïY®ïX®î`µö`µøY®ñY®ñ¤e °q!°r.ª-˜ÇE®ÿ6¡ú7¡õ7¡ô7¡ô6¡õ7¡ö@©þ=§ü7¡õ>¨ü>¨ü>¨ü>¨ü=§ü=§ü=§ü=§ü=§üF¤îE¥ñE¥ñM¬ùK¬øG¬út«Ùÿâ±ò¸”ä°yú̓ÞËzÿâÌÔ±tâ­ZýÑ~ó¶pñá´ÿՌؘIÿÓ†ÝÌ–ä¤Jé˜Kñͪì¶q÷¸TÿÀOú·HÿÈbò¸ZúÀsùÝÞÎeîºOêªHð“:ÿÃfã£Aÿ½`æ•Aö¹)°¼«Ã ®Ä ›¾ Çb•½Zй2¬(u“zŽexYo S].yŽ8LhgDB€=a9_cTGYw §Ÿ½º‹ª{4vK%rbX\‘»O²ÿQ±üP°üP°üP°ýP°ýP°ýP°ýP°ýP²òO²ñO²ñP²ñO²ñO²ñO²ñP²ñO²ñO²ñO²ñO²ñO²ñO²ñO²ñO²ñO²ñO²ñM²ñX­ð`µø`µø`µø_´÷_´÷`µø`µø6»`5º^4¹^!CŽiS#6“x"˜Ð;¤ý@©ÿ=¨ý7£é>«ñ=©ñ>¨ü=§û=§û=§û=§û=©ï=©ð=©ð=©ð<©ïP¬úL¬øK­÷;³õX£þ‡¼âò×âó×ÍôÎÿñË÷Ô´àáÖÙÅ¥ÿâ¾ÿÛ•ÿ¼cÝÝÅÞŸRÿ°Qÿͦ÷ÆzõÑoðÏpæ³Cç±Bì³I÷Áhõ¾XòÁ@î±jöºYõÀ?ü²Yÿ¶VÏŸ5ÿÕlå5ÿ°Xó Eÿ²WéËsè˜.ɰîµµ޾JÌw±×¦q‹5mˆ8‹Š9š•@y1bVVe?s|`A@MN\–rw¢hN3~IzvUj¤µØª®Œz‹0Êe¾ÿfªÝS¯øP°üP°ÿP²ïP²ïP²ïP²ïO²ïY®ñZ­ñZ­ñY¬ða´ø[®òZ­ñY­ða´ø[®òZ­ñZ­ñZ­ñ`³÷a´øbµøZ­ña´÷a´ø`µø_´÷_´÷_´÷_´÷_´÷_´÷_´÷2¹Y2¹Y5¼\˜=“t`vX`/e$‰r"™°;©ø>¬õ=¨ò=§ü=§ü=§ü=§ü=§û=©ð=©ñ=©ñ<©ñD±øM©ùLªöI¬öM³è˜¯áíÞÌôÔ¹íúìöÅtêÅÚ¶sÛáöÏžã¨lòÃ|í LùÇþÂvôÃmçØsöØ÷ìªóÎ|þ½”üÅSÿÕmùÉbþÆhô¹ZùÈRô»[öÄLú»\ø³Uô¨Jå™;ÿÄaê©Gõ£Gù®U÷¥6ùÇZÉ—ª ®¶!“…$e8ž]‰K-ŒO,]- H'B8yl8nd`URW*[D*KODqnf¿¨¥•`?‰V wJ©šaáÔªÿåÖqƒ˜<ŸÜQ¶ÿQ°ýO°ÿP²ïP²ñO²ñM°ïVºø_µø`µø`µø`µø_´÷`µø`µø`µø_´÷`µø`µø`µø`µø_´÷_´÷_´÷`µø_´÷_´÷_´÷_´÷^´÷^´÷^´÷^´÷_´÷_´÷8ºz8ºz8»{(²C’f”OƒStIcSbOXD>7p¥4¹í=©ð=©ð<©ï=©ï=©ï<¨ñF²úD¯øM«øLªøM³É/¶ÿ[¨í³ÃëÆªhÿÍœïϤÿÏ‘äÁ›øâÅñçÙùèØýÚŸôѾóñ¬Ø®GÞ¹…ù¿zïĉüÝ…ÿòžÅ«`ë±KôÁZÛÂkü¾†þ¸@êÁ[öÜÿ¹_ûÆnÖžGÿÊyìÀü¼Rü®QÿÉeüÇgø™Oø»dïÃ÷ªVèŒ͈–Y†RÿóúädúÔMýÆ0ÿ´^9?,4#of0oQ…LY*W? I$K*W:ƒpJqYkf\D“\0x?¨‰3ܽKÓ‘hlF$}Tzn^]fM'S5 kM'oZ9¤‹•×Ï¡ùº§å¯†ÃzP],sbVzV4wW,³¼t£Äe³ð^µø_´÷_´÷_´÷_´÷_´÷_´÷_´÷_´÷_´÷_´÷_´÷_´÷]²õdºþdºýdºýdºýdºýdºýdºýdºýdºýdºýl´ýl´ýl´ýl´ýmµýmµý3¶t3¶t3µt(¢1šk¥a œY…ZrFZ5Y; iQ e//³ÜM«øM«øLª÷Lª÷Lª÷Lª÷Lª÷J«ö]¢÷L±ÿh©ÍóËéòòáíçÙâéº÷ÿ¼ñå’èØ½éáõ±€ìØ“õæ£ØÇŠøá¤ä°Rô ;ãÁ‹ñΗôÞ±ýÊ`üÊeöÁdà‹Nò¦VúÈDþÂ?Ü 5ÿ¼^ö®9þÑ{ä±Bÿ¾kí³XÿÒuôÀgø«Uñ˜FüËÿÁaóÂYìºAëÃpã¥TñœBÑ—1¹¬AÿàfùÔ`ñØ=ûÑ+òÀ–^U'WY7y\TŽJZ2 p?(aTyxj…b9mOƒ_R‹UŒÑÉÐ廯᧚י†n,YI8†“¤UhyK{Î_¶ü]´÷_´÷^³öd¹üeºýeºýeºýeºýeºýeºýeºýeºýeºýcºÿm·ïm·ïm·ïm·ïm·ïm·ïm·ïm·ïm·ïm·ïl·ïl·ïl·ïl·ïl·ïl·ï$«J$«J£C"£™P¬H£EŒEe@_=S9t2O*1­ÿK«÷K«÷K«÷K«÷K«÷Kª÷Kª÷L«øJ³÷l¬ÚÿáæèöõáÈЦ|õׯÿþñæÐÅúêìÞÍ}òªxõá ×Ÿhí¸€øÊÿòÅùÕ ø«9ýÉjóÖ‹üÿÜôïÉι‹îž=ñ¥/ïþÌVô¢9ûÆeî®>æ˜Jø¿XåËiêªRø¿fîÁlüÇ„ó²qøÝˆùªBò¾{ÿÅ|ýÄiò©;ý2ÿ¤1åÇFàÓRÙËKÙÏIàÂ0î¶©pˆ`4mN*[etS=_oKN.bJ!pdc…ihdFM9~R)Ô¾žóÐÒìÆÀêÉ®Ž`$Z5ª’d·ÁË·ñ^·þbºþf¹ÿd¹ýd¹ýd¹ýc¹ýc¹ýd¹ýd¹ýd¹ýd¹ýd¹ýb¹ÿl·ñl·ñl·ñm·ñm·ñl·ñl·ñl·ñl·ñl·ñl·ñl·ñl·ñl·ñl·ñl·ñ'µ‰'µ‰-ºŽ7¥H!½•/¦yŸY“J xLe( hk`*3°ÿK«÷K«÷JªöK«÷K«öNµðNµñM³ñ}²ïúïúîòëþìÛÌ¬ŠøÜÅøÛ¦üߣïââóØÆè½rêã­ëæ¾ûÓ•öÑ•òȈøÇå¨Nä› å¬HèÂo¾ŠFÿÔÿÄKÿ»eû¹KÔŽ/èˆ6øŽIÿ·pñ™:ç¤*ëÈr÷ÆŒýÖtíÂdóÌs÷Ù†æÌxîÑräÁVòÃZç*ݺIñµZÞ¼O³ÌM•ËB–ÖJ‚Á/e­QX¡B,7q}9¡i(U7 mO0‚Zanh*bF['C6%fHs^7WV:bM@i;jH¼ŸuÚ¾”º‰y…‰Tc7ƳŒÿÏ̼æTÆÝc¼íd»ïb½ïm·ïm·ïeºïd»ïd»ïd»ïd»ïb½ïl¶ñl·ñc»ñb»ñb»ñk·ñl·ñl·ñl·ñl·ñr½÷s¾øs¾øs¾øl·ñl·ñ*¶†*µ†,·ˆ=®K"¼Ž9´+©|$¢wdr: f eO;¹ÿK«÷K«÷R³ÿP±þP±ýc©ï[¤ìk¶÷ëêñúëèääãæÐÃóÙÁû÷öÿ÷ØÐ¼Œÿâ¢éΟÿÑšùÆœöÚ¹ëÆŠîÇéÊ幖êÈyÿ»•öÄyüÕnó¬Sß®yß³KÛ®=÷Ñwÿ£2þ«6Û’ÿ³1ù¶Gî—=ïÁ‘ñ¼yüÒsüÏrüËké²NýÇc÷ÌbðÈ\ÿÕjùÒ¬ò¶c¡Ç>œÝO›âRŸáQ˜ÔG‰Ã5p·8c¨'e™,C…/VqOfJ?9fEvb`vP6\1X5[I7_MI`]fS`z`^[jUF³™\w]>$iQ*bmy®‹’µŽ[šb/e2Vxt`±þd¼ób»ñ_½ól¶ðl·ñdºñc»ñbºðjÂød¼ò_ºïs¾øs¾økÂøjÂøjÂøs¾øs¾øs¾øs¾øs¾ør½÷r½÷r½÷r½÷s¾øs¾ø«|·‰%¿Q¬L*·”=²(³%²hE•9dv)u9J;R¿ôQ±ýQ±ý\­ìZ®ïZ­ïN«ÿwºõÿéÝôëàùóêüýÖãÓ¥Þºæ¡[çíäÕþÿéâÐÿóÎú´iÿÆ€òªeú¹}ûåœïÖƒøÊô̘ûÎ>Ó3ê³lþÓ›ï¹có·•ɇBé®Vä›D÷Ø—ù1ì.þ¬dÎ};ÿ½]ÿÊgê°[÷ÌnÜ»Zæ»_ôÈmùØ‚öÑzé²I߬xÁ/f®I C‹Ar4UbS„TB51U2†fS„WKi?v@)jJCTLGV^eZ_a^einUvtY^gW R-hT ‰žuЮ¡®“|˜^0pC }uoj­ã¿ô…¸år¾øs¾øt½øt½øt½øs¼÷t½øu½ør½÷r½÷s¼÷s¼÷s¼÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷*Æz(Ãy%¾y2³X4¼~`¯T_ºM^½9L¶#“"{:e%F6O¾úO°ýO°ý\¬íY¯ñ^µø‚¹ôÿôñѽ¾ýô벦œâÞßûéÕõÔ²úÏ–ÿã½àÍ´áàÒ÷ÿÿÅŠLÿé¹àºˆþö½ðÕ¡ÿܱûÇéÁ_ð°lÿ¿ý¿šö»bùÂXñµ€íƘçÄyÿ¹bò΋èõ’5ê±2ΡÿÊhþ¹SøØƒùÌjþÛ{ÿÜ€þÏt÷Ðzé¼aù¶RÁ;ÜP¥æM¶ß^ðû¾îöÍ½à…‡Æ,xÀ2g¯I¡6…Fv"]9s`?^H M68€iM~dkTB"^9mR'ne:t_TxdWmhfigh\\]y`wK&t0s]Ã¥…âʻ˷¨ˆpZu•¯sºô\Ãÿs½ös½ör½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷q½÷q½÷q½÷se…N—' ¢,®0º UÈ3]Ï?oÍXÁFj(X+g0C¶ÃZ­ïZ­ïKÃöa¬ÿ°êÿéÓÛÔÌýõêîçàÿüòêÔ ðÿÿñݺè¾ÿÐè¨\é¬V»ŽEùé¿æ¼Œþà¬ãŃÿǶëèÌðÒ–éŸOÛ²yå£Fï³Båʆÿé¦ß¿fï´ îÒ›ò¿•ð Jü«/íðŠûš'ð©UþÀ†ú¿JòÁŒéÉrà¿_ïÑpòÕj÷Óî©9‚ÑÊ4 ÚR¼ì†àïÛãþë¡Õ|˜Ò7g±"d¬dœ@u.oIh>t‚¡WY|@ S52kK#ƒ\\fTSaN@C#@2hX*zrX{u_|w\mYoJL]n•š¿¦˜­…HtT»œtêÃÀ‹¿‡[¶Éu»í]Âÿt½õr½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷r½÷q¼öq½ö|·ö|·ö|·öw|"}E‹'V 89 u 2Š;v bµ*E¯ GR9i¶ÿ_µø`µ÷UÁ슪õûâÞÿ÷îßÕÌàÔÏüñò³­ŒÖÁÎᨉÿýûíñååӸèˆèàØóáÐ÷ߌԭmãµsü¿•ùÌ‚á’JóÓ›òì¿úýÿ廋ô¯KûÃmó·\â¤Cþ­ ê¶AóÊ¡÷Æ{é±[ýÉyï?üËlé¾fôÉië³0ùÕˆüÜ~öÔ‡ûÖˆíÅtþօܹ5Œ×+‹Ç2ÛHŒáQšØh–Ïg|¹?s²%\¨W Sž ?sY] ui>r`†s3qW5oBN'0T>a.pWCP`djKTG2A'cHw]eV@VVbHXˆ]Vnž‰}Û¶™ˆg@=$pW@}xr•OÈŽ^„—¨q¾ùr¼÷r¼÷q¼÷q¼÷q¼÷q¼÷q¼÷q¼÷q¼÷q¼÷q¼÷q¼÷q¼÷q¼÷q¼÷xÃþyÄÿyÄÿ‚¿ÿ‚¿ÿ‚¿ÿ€LzT&qt7BSi=}w@UJfZYh%_i,…Œ@G oGN¾üb¨õh¬ñˆ®ÐãîæüæÒÙÒÎý÷òÿúõÐÆ½×ÎÅ®§‰þþìîåÊê»xöÌŽÞ¹|Ö·¥óêÔ×¢†ÿóŽÿïÊÿå¤á»eßø»îßžÿÂÀ™5ø×vÌ.ñ¶WÒ«pÿéâÇ—TÿÿÍòÆ_üѦÿÕ}óºTá¬IüÄgò¼\ç¸Aÿ§4ûËráÁqþË„÷ÍŽî¼DíÝ þ¢'ŒÇ-„Â+·1¯¼?¾— ©ƒƒm¦Š‰¡,^ˆ GoLd[hqba_@U.“ppkO1O;ZAa;<[CI_XbUYrcgb]`e]bHPI!I,_Y3nfZt^gMZ|Qa}KtsŒœ„“r;v8dI2®ËÙµÞSÔyÉõvÈòvÈòyÆòyÆòyÆòyÆòyÆòyÆòyÆòyÆòyÆòyÆòyÆòyÆòyÆòxÅòxÅòxÅòÀòÀòÀò ‰êÉ͹§]nX²ŒŠzueüõÚíæËŒm›¥‡ž¨‰G04J'¤ÅèëÿýñãÍÿâÍûäÎä˵ÿúàÇ£‰ðѸõîåÿþöéçÒ¸³ ôïÚíÏ“çÆŠÿâ¡Þ«ŽÿêÌä¸_íÎ¬âØ‘âå®ÿë¥øÝ¼óÊëÓ“ÿÿÝêÏ•öÌÿå¤òÃCÿÊsñÄuï³[ÿÑ;õ´Pì±3öÀ]ÿÊdã«Bæ´KÿÇlõ¼bÿÅpå®_êÁnóÆoöÄ€ô’@úŸ?Õ¼ ¡Œ^ER.DR#c0QA=*VYUUsbgnqRYLH-•tcH)J- dG €Q:xUKwabjkllklb^ZrbVqM.eE'R6iJfe7qhfzn{̾˷«¼Ð½œ›‰bzi=¬ ‹²ÍÖ€¼ó‚¾ùŒ»òyÅóxÅóxÅóxÅóxÅóxÅóxÅóxÅóxÅóxÅóxÅówÅówÅówÅówÅówÆóÀóÀóÀóÿÿòâüÝÿíäÿÿùž™„ÿÿôÿÿðÿøæçèÁÙØ³¼¼˜÷ðíõéçíæÞýøãñêÕóïêßÕÌóèßÿùóãÝÖÿÿúÜÚÏÛ¢{ðÖ˜ÿ÷ÉìæÃÿÚ会ȓ~òΚàá¾Ò²–ëåÒÿÔæ¢>üУÿáÉóУðÙŠþÓ“é¬jï½mùÌcå´xúÔTíÌ\ÿÜ¥Üé©øÍ ø¹Jì·Dö¼`ñ¾gÿÙnëÈ[û¾yøÉUúËüÌoîÈDõ¹eô¿dí©%±vpB_-¡r"ÿÿœüæŽÿÙtÿÌ\à™N <5YQ$ZA…dMbPwrcR0U5…b^jI;M.=1[MuZCjrgincik{iosSbFkZRw``u`phJX8|iWé×ÄñÙÛÒÒ®°©”¸©ª¯ž„³žx涢渜|ÂêwÅóxÅóxÅóxÅóxÅóxÅóxÅóxÅóxÅóxÅóuÆò‚¿ó‚¿ó‚¿ó‚¿ó‚¿ò‡ÆùÀóÀóïåÒÜïÓþÛо·§Æ¿ªÿÿîþùäêãÍÍÀºóæÞÚÎÅíäÛöìãúðåâÚÂõîØîâÜúóìéàØüöðÜÒÉË¿¶ÿïÝË¿‚ûê¸Ñ×öô×èÒÄçÓ¬íØ¨ÿæ·ÿþºä—VÿÿóþîÎ䱂ëΓØÄö»eåžcÿðqóÔbóÉZùÍ[ýàøÒ¯ÿåî¾~Ý¢`ÿÙ˜î¾8üÆQÿÔxô¶eò°Oþ¹Söºwó½Iò¸q÷Í}ùÎeúÒRôÈwòÆïµI`5Fÿûôß^õáqóÏNøÅ>Õ—a:97 @9gM mWEcB*Jlm’jP<\=€eEgD,[I,q`BxfWzdnu^_qYW°œš¥|€]LV'…\"xQ†b"g^J—¼ÔǨ»´ž‹±¬z†^‚i>zÅðwÅòvÅóxÅóxÅóxÅóxÅóxÄówÅó|Ëø}Íú¿òÀóÀó€¿ò‡Æù†Åø‡Æù‡ÆùÛÓ¾ÞÕÀ¬¨‘Æ´hïÛÇçÏàÝÑÊôéáïöçÛßÑÊÎÀÝÑÈüöíéßÕàÍÚûéÖúûìÿÿêÈ¿§öñɽ³¥ÛÓÓÙÖºÁ aÿðÆÎǨ÷ÿýíÑÍÿöÑߨ£úüãðÍ•÷Ï­úÀwÏš<õæ«ðëµöãªêã´ÿÿÛ²+ä´gð¿p÷ÍtùÌTÿÕHö¹Žð¸AýÚˆÿÓ§îµiõ½nëÃ[ø® ùÀVðÉsÿÍKôµ|ð¾Xê¼aôÊzîÍfõ©MöÄKïµã¨c˜}ÿû†ñä[÷åWÿÜLç¹ìœ|I* C0iQRi\a|kX`/|p@r_H;"t;q_;bPO`H8YD%Q6:H\0|P}[;zY=ʦ‡Ò¿¯àͺàÓ¥¤˜k½°ŠJ%YG*UV4×ÑËÅ¿¹´°îõ¦œ‹¦Ð€Èÿ…ÆûÃõ~Ëù~Ëù~Ëù~Ëù}ËùˆÅù‡Äø‡Äø‡Æù‡Æù‡Æù‡Æù†Åø†Åø†Åø†ÅøñèØ© Ž÷ïÙÿÿøåä×çèÙòæÞæÚÒ÷ûïðóçùüðñçàìäÚ÷íãýõÜìÍ­ßÝÅÌ­ÿþèÎͪÿÿúçÚàÿÿÿüáãÿýÒÿë¸ýÛ—ëéŸÕÉœÿøÖÇνðÙ­ÿ´eúÊ…ÿý¾ùÌzÛ«\üÕ‰ÿʉڿvÿ؈àµgôËzöÍnõ̆œQôÄ\÷ÆKÚµ1üÝ¡þÌrã±?óÇwÿµPó²IûÚ~ùÁEúÇì¸RóÅlúÒúÌSñ¢AãÓvû¬Rá²4ÿø”öäróæ^öäYø×HìÂ#Ú… „W>*E5yc`iVqdp‰Z`I9mXb]K^@RYNX^oZZZIS=iF-fIO)jAmDkI/tT:›yYÜǹÚŶáÌ¼ÛÆµÐ¹§Á¼³š…k’iC‚bG¦„jÝÙ¾ãÝÑÚÈÓ´¿ÉÀê~Æÿ{Ë÷}Êø|Êø|Êø|Ëø†Åø†Åø†Åø†Åø†Åø†Åø†Åø†Åø†Åø†Åø†ÅøÕÔ§¢uãÚÌêá×ÿÿöéÞÖÚâÈÕÜÈéàÅÆ¼¦âØÂÿúìáÔÓÚÎÔÜØ±øõÚÿÿóæÝÈÿÿíÿûîùòÙùéÀã»ê´{ú߈âë×Ö²“ÿöÅçÈ“ûá¬ýðÆïÇ‹ÌÅŠþí°ìÑ‘ÿÖ‡ùÇsñ½j濃úñ·áÒ}òÑsÝ´GÞ­=ùÂoÓ‘Nê¹|ñÒŒ÷Û‘óÎç½Jÿ²+õ¾D÷ÏgóÊdþΕñÆkðÇO÷΃ñÇmõÌqð¹_ý¹Vù´Iýΰí½bÿùm÷çXõåY÷ßVÿÍ=ýšµs)½t­m zJyX+vlPzsg‚pyliSP7Šh|hV7jS1‡ibmaj[f‚kOPcN4zT!oDzNeGƒbM}Ta²ž„½©‘ÝȵãϽ×ñ‡pZÁ¬™¹¨—¼©™ØÅµX.‰],ȹ™ÜâÔèͨ¿Ç€ÅüÉø†Åø‡Äø‡Äø†Åø†Åø†Åø†Åø†Åø†Åø†Åø†Åø†Åø†Åø†ÅøâÓãüöòÁ»žÿÿÿãÚÒÙÏÆÿðÈм—òïÖêäÍòíÛÎʧÝÖ²êãºèã»û÷ÝýùàÝ×ÁÞÔ¿ÞÒÀ½ª¦Õ¿¹øÛµà«rÞºdÿÿøûæÃòíÇÙÑ«âܽøã¶úÕ›ûÑz¾‹9øÉuñÏyíå¶ôÿäïß·÷ä¼ßÊ¥úë±þÊŽô®„÷ºuë²dÿΑïЊñ¶éª]þµkÿ¯Eí´;þΛúÍyæ¿Bñ½€óÉTôËjñÌRöËVîËQáÑNÚÉGÊÅ(²Ä%®Â'ºÅ1¨³ ¦³$ÓÃ6Å“´kµo´ Œ[2oN ‚Exl`d{§€oquXPY@*‡l{iW7L'eF%vfEck[__hq[\t[Wzd`mO4M2ZAoZRr]SäÐÀë×ÇÞʺ¬™‰Ã®Ê³ Ã®¼§–³¨“Ñ»šyL+L€„}™h#“ÄÎ}Êö…Æù†Åø†Åø†Åø†Åø†Åø†Åø†Åø†Åø†Åø†Åø…Ä÷†Åø†ÅøøíäûðëæÝÚ¾®v÷âÉúåàÓÑ®ÞÛ¶òæãàÔÍæáÚôßäÉ¿•ÿþÿÿÿïÚÒ¾ëæÕÿÿíôîÜá×ËÊ®gß±¡úß©ùç¹üåÍíâ¾üâÁçÝÞúêüúõÉæÇ™ôà“ÓŸhÿä¡ÿÒò£qÿÎaÜÈkú×ùÕŒéØíÓtº“=þÇfûÄiæ¬XêÁ\ò¿wåÏ{øÙ–òÓ—éµKû¾6êÆRû¿búÔmï¾WóÃ^ê¬Kü¿aÒÍD»Õ.ºÛ&¶×$²Ô¬Ì ¯Ì¢¾Ÿ»™°|’x› ¯z¬i*¦wnD¢rU’i †pixS3jFO^:"gWL`jjppwrXkwXB_GxWD_AO5T3¬‰qòÒº»•xl\ÅÇÈк©Êµ¤Å°’Á¯‘µ¨‹Ü¸¢VJsbÞ¼Œ‘—­„Èú†Åø†Åø…Ä÷†Åø†Åø…Ä÷†Åø…Ä÷…Ä÷…Ä÷Ìÿ†Åø…Ä÷ÓÒ°ÆÀšÇÁœÿ÷íèܵλˆÿÿÿÚÏÈýòèõëãÿ÷ðÿ÷Üÿêêǰ}ÑȳïçÓü฽™qÏ´‘ȧrûîð̘޽…úØÿñÈýÔ¨úá³ùðÂÿüóÛ× ÿÿòÿ÷ÿãÚ¬ÿòÀ·ŠLùæ¹ÿÈzä¿‹üé±èØŸú¥wôºIþЕð¶XùÌrýËiúÙJúٌؠ2è´]î»zó·kï¸JùÔ|ÿÏqë´5ë®Ná¥DÝ¿M»Ø=Äá0Âà(Âá*Ãá0¿ß·×¨É ¥ÆªË ¢À£}†Ikx­x$’l,|f_xi=mhCkW9L#Šrtkfup^YV?)YM›t^€QBetAD&E<hT*‚U.€ZW}ZZ}ZY`TnP-P6@%\B }Y~^;wH=Û×ÃØÒ¿ß˼տ°Ì¸¨Ñ¯¢yYM±™”šW1X/k¬Ã„ÌîƒÑÿŒËþÌÿÌÿŒÍñÎòŒÍòŒÍòŒÍòŒÍòŒÍòŒÍò¢˜ˆÂ¹¦ÐȯìâäàÛ¶ÕËÚöóßÚÓ½ÖÉÎàÖų¬„ÈÄ´Ö©}LÿÿèéûúùñçÁ}ÃÝê ÌØ‹ÎÓ‡Ø×„ЄÐÓζθ‹È²¿¡„¶™y«x®’u°–¯„†£‚“š}¶¼„ÖÆ˜æÒŸôºfõ¶}ÿÏsóÃsûÐtýÏTöÎá¾üÊLûÄnøÊ{øÌzýË€ý¿vûÂxöºcõºj÷ÂTõÀcÖÊDÆÜ@Çß=ÎçCßð^ýÿ—àé[Çٽϱ ª¹ ›ª¨{–pxjz\7œ€Y³ƒ~üØËÿÞ»zeSk[o[Kɶ¨ÕÀ²á³¬Ó¤ˆV(\W!‰P%yf§Œ—¼Ÿª«›Õ–~ˆ™ÔüŽÌñŠÎõŒÍóŒÍóŒÍóŒÍóŒÍó‹Ìò‹Ìò‹Ìò¾™oÖ¿šÚÊ©çÓ·ÿÿÿ·«kæÍÍçÚÍÄ6­€>ßÊ•ÿþÿܱ‹ã²p¨Î¢’àÙݼëÐ’ÞÅ’áÛ¥áѱà¿ÅÒ¡ßÛ‚÷ävóãtç×eáÒQòâbéÜ]±ÂbŸÄwŸ‰f¡yd°~b €YšxNlaggyp™”6ÿ½…í¹eýÍ8óÃ3ê«Xç«JïÆUõÅ…óÍtóÏcïÅjø¸Lú¹?þÑWí±Nùßr¸¤~U\*©|7›`7ÃŒKbL„V £¥W{LlLYA@<=PIš€ X7z^-z[?uYPv\Wx]Rk]umebx^QnN2B$xcC‚lLvae<O#~ZI“qg§„oFsJpDfAe6ŧzóÊ•ãœUkM‰gM¿šÚǧÚǧھ¡ÞÀ¦ÜÀ­x;lI ve%}iX¼½¼Î¬Ï¿¨¾iK?hqlÚÿŒËíŒÍó‹ÌòŒÍóŒÍó‹Ìò“Ôú“Ôú“ÔúÌÖÖèѶ±r2˜f$ÉÒÐÿçáÿëͺ¦ƒ÷Ö¼ÿÿõÿÿÿ·žÅßÃÓ« ìâ˜ßÒ¢èÊ‹àÆ’èÙÐ׊ËÓ5Á¬tϺJçÁ'ÔÁ'̵ÖÁÄË2ÐÕ;ÔÚ@ÖÐ;ïÌ<êØ0ÜÓU™¤RK—ƒT™uT˜qNŒha„j0q}¤_PÿÁ‡î´^ÿÉvÿ¹œò¯jåÆ|ì½GÿÐ{þÜ’çÙñÊnâ°CüËRð³V̵=qE}PÿÔOÿÁEöÀJô¼Bÿ© ¶€AXX‚#QP 961.:9 qe#y\eD=nVz_[v_]u\]kbRqbqz\U~_@qTb9 uK†\'‚T9vN]*n9I •fM{M8|M=uI }Z}8d'mIy\FaJðȰßϰßÍ®èÉ®íγֶšcK:²„dEtPØÃ”ãϾֿ¿Ç¨Ë[[IbX:}Æê‘Óú“ÓùŒÍó‹Ìò“Ôú’Óù’Óù’Óùœ|4ñâ³ýßÏÿÿôȱžbÿÿúƒiWÒͱÛ̪¶¦ƒÛÆ•·ÛÄ£íåèÖ•îÜtîßäÝžéÍ*Ó»Ô·%»‘F˜i oBf2e2d2i/e,u@†n,ª”M¡ÉhÇÐeÔÉMÐÏRg„aX‰F„{;„Zqrf;b[Á–MýÈ_ò¼Uð¿]öÔkÿ±OùÐRýÖ@í±[ÿÒqüÊløÄgü´KÿÂAȘ MTÿ¤NþAù&÷œ ú’Ë€—EGgV%TGyn5&>+D1 _H}Pj;TO.C6ž{Q‰hL\a`{|bNl\ÿÏcê²Pò¿]þÓQ÷ØvÓÖ±¶Òä™Ý¢͹‘¼©›¿®¥¾˜¹€à³~ë.âd)ï„2ø5ñ‘òf »B x&P ƒs=\L6$6$C3 ^Gd3ˆT=[OH\G2:…dKŸ|g ~eµ’z£…—¤ˆ†ƒˆ_GsGpF™zc‹gh‹c5ƒY,]K~[V€_X„bVyXPÞ¨§sÙɬãÓµ½µ¤…]Ì’VÊ®…ÿéÂïÎÄðÎÅîÍÃéʰ»ƒÖº¡Ó´œÝ×·ÿîÃg>®•tÔ—Fi}vvˆ14$¢ÁÏ“Óø’Óù’Óù’Óù’Óù’Óùïÿÿó÷ÿéÖÐôôÿìÌ›ÿÐxæÅ»ìϤß~&þúÞûøÞÎïÝîú¤ëÔêø¾îµé¾$̺ ‰k{8„Du6{N rE{Bšb›d’d"¨y4”d-¯*©€Ìu8Ì’4ÃÑÅ9ÂÇ9¬Â(TzY&n~Z’v|Rdf{ª¨pÿÉhóÇ{ªÏÁs×߀ÙÖ‚ÙÏ…ØÖyÑÊ{Ö¹rʤyÆ«y©KÄ«i©€©¨Qó¹@ý ðcÏYÔlu9Y&0bS://'/"aE }C‚M hBRSYJ=UdJE>,THsK¡„\¯–‡¨‰¦ŒƒnhyhLrg6J/’tS›xxnatY/pK‹a,œkP’sX­uÓ²™É§ŽêȯãÀ§Ô´œÛ¼®Ç¬y®~;«‚Gä¼òűôɸ¸Šw×¢ŽÃ£‰Ê§ŽÉšÂ“{¤{AOwhSp‚‰¨ÃêŠÚÿ’Óø’Óù’Óù’Óù’Óù’Óù´}VÿåÎøøööíæÿÿ‚üøÜ¼Š;éœDÿÿìàÔº¨á€ûþöØ“éõÓÛßÈ*«•_8 ‡Fv:s67ɈSÿ—8ÿ¡Ný‘=ð”*êŽ%ý©Bø¸Wö´9ÿ·Aê±KüØ‚ÞÍVÅÅQ½ÃC ±*d`vk{yrŒˆTGod®”X½¿–‚æÞsëÙ‹Ü݈ÜÙÖÕ}ÒÏ}Õ»}Ó±|ȯs¾¦lŦh¯œR’Hg¯€?¥@Ç^ªR"^_.›c!~C‰\B®fF‘T[5 ^a'‚\r^‘?ˆ< ƒV?kTRUT`SCO]CM^BR]J6,hU;p[L‹vg· ˜ƒisb‚~ih‹fBL!c5íÄŽÅ•‘žp_xM(U,N xG;™lQÊ iº™}øØ¿æÊ±ê¼§ê½ªâ»’Ô­€›vDcB tMª{6àÜçÅ·çȲßÀ¦Å¨ˆ}\P´”‰˜xwoS 챛˟§¡ØÝŠ×þ‘Òø’Óù’ÓùÓt"Åw:³€TÒšJÁ}'ÇŠ)¯l_ÿîöÕ†kèÿÿÃëÛ¡æÜŒëØxæÎ¦ê¯ÛÄ-Õ©…=›;@ÿ¥Vú‘8ü…úˆó‡ô‡ó†ñƒæyæzÑrÑ`¤|ï¸Rè¯LþÁYì×fÉÞ&³¹JQpBghG[kPx‰q‚mr¡yOŽZ8ˆ€Rw~„âÁsƸÓ¼xÛ¿}×¼‚Ô´€Ë£wÛlº‘b°‡M¦‚Qe[xrCbƒ§E žN ¸i/Í}«Y‡JkB b/•En\6ŒO0„L.„aAteFY]L]MPZ>HbGPmOZL5,'Q;+kUG‚sOžŠ~€g_}gh¶f¢yGg> qG²D¢zHaI~T%f@kIvQ V2ŠiQ²’{÷ÚÂýϹõDZ콪㴡µ†s¿”uÑ£ƒn9&~d)øß¯ÿßÊýÛÃ÷չ⾲¦†|»•…´’z¦rGÕQņo„Ûê˜Øÿ’Òú‘ÒúÿúØÔ¹VïºkÿíéÔÉ Ó–qáÔœýïß÷èÚ¯‘c¤Ä¾èÖŽåÔåÖÄÛ¤Û½$ž£&>~<ÿ’*û}ü‚ÿŠô” Õ¥VÙ¡KÓ˜@â‚ èsæ`ãfäil:š`ç´2èºOãáÄàL´CVx?8a.buI€W†Qh–kJŒgp„S=j^•‰Wÿß0ÜÀ5Ë¿aƒ¹xnÆ™nÉ­kŦo¹¡c¯–WƒQƒe|†^cpV‡FC°fß~ºn’[ œk*zF~?|H |UvT_}Za^<}_Hi]TXOQNCG[NKRTtYIBC/$C2pA9 ‰5|e‡{c[•~°ˆt°†ai)~V#dE“aScLƒg=†_3zQ$¤{I˜yQ’uMçÌø×ÍöÖËíÌÁåĸ׸­Ÿn\¸œ‚ÑǨÙÉ¡†b>aQèØ¯÷â×ëÚÆë±“§—²¸ Ÿ²e”J$Xo›Ñø—Úó˜Ûó˜ÛóÒ·´ôà ³vMûØÄøõÿÄ LñåÐéòÐÐŒ`ÿìÿ|Ï“}ÖÇ‚ØÈ„ÚÇÃîƒÕº Õ€¡4vQ&ޝ±|ËäaËóX´÷H®ñR¸æO¶èD¨Ùk¯ÉeªÃd©Âm›˜ƒlœE~5—UñÌ^ÿÙ[îÖ›°^B$“sZ‰gLƒeN[PEPSSFUm^V_rQOyVG]>< |^5”tK‰hLƒ_ZŸ}s»›³‘Š‚h1B%gG;˜zN˜{RË¢¿”sqR{Q#e; M‘w>Ù¿ýìÝùëÝòâÕë͵­pbÁÒ§Í»›Óª”Ȳ‰Ñ°ŠÚž„M3hP-ˆhWŸ~cÊ©¤ÅœÆuQ`X.m£©àÿÙÿߎ;±½¶ÛŸeºœ{öùâÀ–JzP ‹fâÀ ÿÿáýÿùi¹«vÔ¹fαX¼åK¹ÿLÀõF¾ï<ºç?¸ÿ=·ü>¹ý4»û6½ü2·ö2¸÷+²ñ$¸î!¯á&°Þ-©â;Ÿ×J›·cœ®~›”‹QÿêWóÍ t‘:Nb<_j4rg;xc7._;el#xotkcuO±$`m`˜A p0mDÛÉF¿ˆu >”p7|P#b65\99M3ka$³O[:}:o:'jL:vE$E"‰8•8žL €U*A& 9{\C•hDŒhKr_NcSXKThA]‡`doeG]>I,S4‘x9˜zWƒaUά¢È¦œ²‘…~`9\D6P4÷ϯþÔ´êÁž¼—i|T)€SR8G1‚T)¢pIÿѰæ³\¹`A•xÒÀ¤Ø®šÌ¶ŒÓ±ŠÎ“s´‘…jM"dJ f.çǦԲ­Í©«~Ycq7UNF ÂÛÜÅÌ÷ÿÿÿÿøñøÿãíâ³Â©ðÿõÿÿÿÿÿÛäʸÿûüƒÍÕM¹ãHÌÿSÄðSÄð?Á÷DÁöFÂ÷FÂ÷FÂöEÁöQÌ÷IÄô/·ù2¸÷+±ð-²ñ,²ó,²ñ:®Û6¨Ô-°ã+›Õ(ŒÐŽ»`ƒz¦™m]HMZ#QW$]g3IQJZR17PCc7g{AoQœ”chKw8P$t-¡—pW<@%^#H4`*C\`o"€3b&”l>j4Œ\ zL(’N/GoY¨[¢e+[JeC#O/3N/†gNaI—aFi[`UQgR`‰yRUi?2a>5W5.D# aA)–v\Û»¡Ù¹ŸÆ¦ŒŸ~gy[L•k9h>X&¡k8ñºžö®‹Ê™RsJO…[-rL £i1€B„EZ‚^0‚S8È©‚ÞÁ™à³žß±žã¶¢¨ˆn¿Ÿ‡ž€oW-[G)÷ؼðѵ˫¥†}¬‰}S.½~eäÓÍÚ¬tåÍŽÚ½¢ÿÿÿóÿÿùöÐõ©oÑáÎRÉìYÈþ_ÈúaÇõVÆôSÃñGÇÿHÆýCÁøEÁ÷EÁ÷EÁ÷FÀ÷@ºñ0·ù3·÷3¸÷,°ï.³ò-²ñ%¬î%­ï'¦×4 ÛA™Ø-“·!—É ŠÃO’ª„pY4D CNGS:MK.)G4]1Pc+Q_)¦žrpP*e D3 ÏÂ-y` A+(HCO.?Q']Y SL+,aA \V7^>#>…RL’NLb(›D§Z]!gUzX6dD+M-9;pK,`T[lQMœsnZZ{`VpxSKhD9iE=Y6?;¨‡oáÁ«âìϰ˜sV¤th¼–P¦z$i9‚OŸW1“cƒ]T’bQb<«†X}Y*˜qFYQ‚yAwUxY3íÏ©óƯõƲðÀ¬vU<Ä¤Šº”{±§Š˜p>\V°‡Wä`ƒqn{”²©åÿðšDî«d½—kØÊ²ºŽHȈÿØ”ŸwS¤ëÿ…Õù^ÈþbÅöaÅ÷_Æ÷YÉ÷RÂðTÃðTÃðTÃñEÂøEÁ÷EÂ÷<Å÷6¿ð%»ø*½ø*½ø+¾ù«å¯ê°ë«å"©Ù©Ú£Õ" Ø$¡×“ƈˆ´u|}KGZ7+@9(>DS1Kb']V0gn.ŒLZ(61)ZS@*83INLFWS%3&W&˜[0pQ>qKVX8A’P3”H#™n5»n@žkBŽiI„fNrR:^>%H2-+!¸s:¤yS¸”tº›•‹yiZlXGaJ6ZSOU;%A 9†m6â¹’Þ±§yN€_V£…hÁžW„`h<†_5ŽbQ–a_¢ƒxÒµ”ݳ‘À”sڭܾ€c2nQqKÞÀšòÓ¸ôÔº÷׿ܻ¡¹˜~·š{Æ”¿”vÑ—og0‚Q …glˆx‡ÔÐéÉÞÚľ«Á˜j¹¯wøðÈѶ¥Î¿¯«ØåXÍÿ]ÅûcÃõ`Äø`Äø_ÄøXÊ÷YÊøRÁïSÃñSÃñEÂøEÁ÷EÂ÷<Æ÷6¿ñ,Ãÿ#·ñ#·ò#µð&¸ó%·ò¬ç«æ$¨Ö"©Ú¢Ô&ŸÖ–Ì"–Ê\‹’0~ t¸7o¦`]2?$(>DW6Ie(Q7n`+–>q$K =]VC- /)=CHGMKi_/‰f6”V3r^jBRyH;c7,#OC¿t`•‹gšX,–oQ†gP‚bImM4[E(C20)!ª…Lä§lÑ›p¶•zœ†xs_QwaNXNHlQ9b<-J) H-U1 ƒ]‚\Xv—sq³ˆe™p;hŽk&ŸyJ¤uc”zNª‰~缚뾚ƗvÚ¹¤Ô³žË©™ŽtO¨Žjä©›þÀ´ÿǼøÅ›˜^3Þ›qÊ¡yÉ™–­ž—–lI¢JeA ‡p2èÛ©ÿÿÿýñ׸‹Yíâ°ûñúúòï»áÞuÎöjÏñhÍògÎóhÎóhÎógÍóaÄöbÅ÷NÂîRÂðSÃñKÈÿDÀöDÀöFÁ÷FÁ÷;Âÿ2¶ö2¸÷$·ñ­è¯ë°êªä±î¤à¥àœÍ—ɘʎ˳VŠ„eg ;)A76J.HPDIK_ŸO ¥SŸJXR nRX:*=MA\< tT"}Y(…[.…]+tPEeO\bQeZDBG3 @*ºrTr[5†hBŒlNŒmS}]D}\CfE,P0=:…_:Ê ~¿“q¢ƒNŒnF„\Fe_WrSRYW([N@oT{UwQ†\ —{N…dQ«…”Ç¥‚k5Æ”e˜h#nD•b«†gþÛÙýÜÌó×®Ö¶œß¿¥Ï¶œÙ¡†è´Í™iŽZ/‘_4¨S–f0ˆT׫ŠÐ¥Å©ŒÍ¥à©žÐyJc3 ÿíËÅ{1ÙâìÿÿÿïÿÍçÆ¶âìÊêrÉñdÎòfÎóeÍòeÍòeÍòeÍóhÌÿaÄ÷VËùYÈøTÁòIÆþKÆþJÅýEÁ÷EÁ÷5Àÿ9¿ÿ4¸ø$·ò­è ¯ç4«Ð-¤È©ì ڤߠӖɖɎ¸‰Ÿv³D~‘_£kx87 çÇžôÖ¼÷ؾâÄ«µ‰hگŞ|Ç‘gÊ”k†h&S&žWD£T‹MôŹŒ?ï¿f¹¸¥bÊóSÒÿ}ÇØgÍógÍóxÏùtÍ÷vÏùƒÎÿ’Îï ÑèªàîÃàÖ»ùÿšÚòƒÏôdÃøUÍø>Æí=Åö>Æö<Ãÿ2·ö4·÷#·ï®ç°é¦Ù!«Þ Ô£ÖЛÀ ”º •»‡¯kœ]sa|c…@br£zJXosQ.g2Z6P,{T'|YrQsChnIK[<V6gG0cQ!WG:V^nE {Q,e@ˆZ;‰]6pN`M@dKOqN[mM/hG:V61\2„V3„U3}[3aI…]]€nj—mo‚X)‡^.´ŒZ»Žw´Š\ŒhŸ€5JG%s\|±ž}µ›¡ˆlP~a;ƒgA‘uT‹il—kI«°¶¥bd{~T®L…f^³‡Õ¥ Ç˜ƒÎ¦tÉt¾D_¡l?ÿêÈÿãÈÿãÈôӸ”lLÈŸÞ¨|ÔžsÄž¤uKq5B Q0Ƶq·fÕƒJgÇØmÓùhÍòeÍõgÍófÌòqÈò|Õÿ|ÕÿvÔîšâÿ˜Óú³åôêóýÿîèÍìöyÎíNÑúQÏøEÄî>Ãÿ6¼ý6µö+¾ù Äù«ó±ø®ó%£Ï" Ì§É¥ÉŸÂ™µ’®‘® ~Ÿr“\„_†Sy`{„‚}©ffZ@`CZ?sS?|^omddbN zTHqOG]="qP?oQ*fKsT4eB>rQ(Z-xK*b?IxWIŠ`Di5iSB`PndT8nL-rU,fI!6 )]L-kQ0pR.†iB‹hZŸ{~„\=ˆ]< tT´‹^«‚T¤zRY­€YÖ¼¤¾§–µ Ž¯…ea@–t×uÁ•q±Šk®zµ­ªuZAeG‹~'­~gŠqgÔ·ŸÏ°—±‘zÎ¶Ž¾“tÐ…Ü WÍ”/˜‚<ÿìÇÿáîíÜÏÆ~F°ŠkÕ«Šâ¸—Ë­ŽÖŸzÚwPÂj6](C( á­d¿®¥Z¶ÇgÏôfËñhÌóhÌónÒùxÍøtÍ÷sÏ÷vÕîŽÕøšÒø¡ÎÜììñËòðˆÙéMÔþIÈìGÆì;»â/´õ1µõ4®î&·ð ±å²î²í±í¦ä§çš¶œ¿ŸÁ‘¬‘¬ ‹§xšq’iŽiŽY3y”|tpµoj_4L%(V<X8 bGMpc‚z[Qd?#oLKiJ*vObtSEiK(c@>g>[oEJqI R$X<dB®…YµŒ^ª‚M™mY]!S*Óm“d<ƒW6›pP™oO㶔ɟ}›~´–‚´«¥„cbyYDhO*€V›…_óÔ»èÆ­ß¼¥¿¤{Ù¬‰Ü›€ÎŒaÜŸUÿÊrÕ5šVM•8cP?ޱ꼛ßÁ¢Ø¡|À]3ÿ …ýSÙhüèìÔóÿaËávÌ÷_ËïWÒð^ÑóeØùcÓõoÎö~È÷„Ñÿ}Ìú€ÏüÑöwÌðlÄù\Ãí\Õ÷>ºÿ?»ÿ5³ö'ºñ!³îºÿ¹ý¶ù´÷®ñ ã œ× ÛŸÐžÑ”ƹ¹„­q“v—jže‰\r"fw–|‡[‹~_Q1V4,U2(T2#mGIkey^"\<`=%Z9(kDZlJ@hCDgCBfBGbA6mRD&C# b@9`;LrQ:ƒo-jKieOI~P-{P.€P+Z26  4 >,\79oN;“lI†[9Œa@‘g<¹bµa£mBšd;’h-Z37 nEtEžqA¦Ù¦†É¡wĨ†Ä»§®‘w‚bIoY•wViO s^7ë×·ëØ¸í˱ά‘γšÕ£{Ò zÒ¡nÝ«x^,|M a+ŽW-qN)×½”îɬôÅŸÀ‚Mê¡qá†^ÐdCùñÃÊèåT´òzÌ÷dÎñ]ÕóaÑò\ÍîPÞô[ÕòmÒöjÔ÷hÒõZÄè\ÄúT·íM¹ú7·ñ³ç"Àü'Åþ²òµ÷±ò·ÿ§ó¬ú­ù¤ìªì  Û™ÕйÁ‡» µ‰²|¤v—i^€]q [f?Z4Nc8GKzkJeA'S2(a?3Z9*R.2nJE\6‚E1{> WA\;3O0d@AgCBf=Ph@JL-\?< ;P+$b;@¹n±ƒ^‚MS{Q,{O/zH&pG*E1"5"" (A M4vQ(‚V6ŒaAŽd9³Š\²Œ^µ€T k?£u\pD#¹”KlCT+wI›Yï»›ð¬‹Û ŠÁ˜Î®—oV¢‚d¨†¢h°‚O¡s?½’_ð¶Öq¤c/çѳ਎՚€«pV¿„h‚L‡XmLíÓ«ÇÄŸæÖ¤ô͉¥Z+ö—që}^½n›ÛìXÕäRÒû]ÜÿPÏøOÎ÷SÒûBÕ÷NÎ÷cÑÿUÆôPÀïI¼ë7ÃýD²ôlÔÿ’ëÿ‰Ïìkª¯e¤¬j¦¦{ ©w§Â€:òæ¿éïØ¡pq¡„›ÂS¨ÂD—°›Ú½ } ~¡Wzb‰WybneY[["PW>AIlV5T/ww_L7)fC6Y7+cA5Z8+];/^4.x?4t:a: e5*rM%lJ;c>EeLWcJkV0+B%P2 +4"3"ŒeG´|_¬sUtM~U'|^'sX(Y8F.7$5"7%9e:tK‰`2…\.f8¸’`¸cªrUœgqwN ±ˆ\®…Z¨Qg?DºjíáëÁžÜ°ŒÓ²™Å¥Œ}^B¸–Šª‹k˜‚až|So; tG‡X# f@•pãϾֹ¬Ö¸«Ð²¦œ}aϵ„–€Eƒ` Œg#þÇ­îÁ´Ô¾Àã¦uÕ‰JøžTþÛ¯“ÔãOÉÚQÏøOÍöPÎ÷PÎ÷PÎ÷BØöNÐöVÇòQÃñG¿ñlãÿ¥Çhu]_DdV.Vbn2r4|T e4a'sV%lL2SF@H!FEjlOq_6eO9^FB]DOZAKT8<\9-Q/#jH@_'_% L: 47CA*%0!*.GS @W91 :PMXy0Zhfjb[|o@—wbUd €a;€a?D1,=0iK7lI7yOEtq8¢‚ZÌt¤sSpW0¯|SÁŠaÆŒd¶Ž`­„V·€f½‡[¦uR,~Y'—uAškKgW¼¢x¾ž…¬†™«n¡ƒbœ~Z«€f¢U!¥R¢r4ØãÈïëêðÝÌåŨ׭ŽÏ¡Æ{]ªŒRç“XÎ_-ŸDhÌ®yæ±j;´H¹ÙOÐþ9ÇþBÊý9Áô^ÄïtÁÖFvn?hCPJ*Œ=]+RO MF%$'#)".3"//.20180R4 %0 *BNIG[R\K:T9KO8-A65TN__CTX?QT:YI*hD(sF`]BUMBRV@Nf>;_1&Y7+T2&V3+S4›vM¥oE¤nC•c8xS@dLZZECeB5`2oE#†_(G)wZ/€_KrR9tT;tT;pP7‘qYyNeX\OYiyW4„j2m6YC3 (K9^P*hAFgF4¼ŠmÒ›}žeH‡\2µŒb»“h¸a¼“e¶Œh¶‹j½’sX9O15y\2“tMÍ­”Ĥ‹ºš¸˜¤„k°z—zR¦Šeº£‘γ^…UbFŽ`:‰I/ŠVŤ|׬’Øœ{¨^,ê’iî–kÕ{QËÕÅ·†E}¶ÊO¿àFÈ÷=ÌÿBËüDÍþ~Îë0w|DbNiK:B(X!S +20N A:MK , 4 -!)!$)77 d\ {zfwYfd8XX>HUEOT:>C*OL2|N:‘F>H/Q<6LIq?DdV@L_=:R/!W3&]:4\=e>­uF¡lBšjC†dW]L]nRu{LZs7Ea,.xK2mKU8? yY@}^C^B[@{V}[GŒ_7ZBk229 %W7e?BzC.Иz¹„f]R—gZО—À–b—f½“nÀ•s½‘o†g?­h”vLO26¾Ÿ…Ç©”Ô¶ Õ¸ Ä§œ|iµœt¨Šdœ”¥~dÕ›`m,tN †J|IlK#Á¯ŠÈ¨yÊ™Xåˆ]çaì•hÕ½†ÑÔ±ÚõåJ¤¶XÈëIÇøH¿ô6­áO€tb]P\R%m7PVKL.N+N ‚6Õ*ï‹ÿ™ ðŠ üš û– ë‡éƒòy ¹X¥TlD(+ +:#'E9 ³}’Y` 54kf:FP5[Q% ƒT-ŒV%†Q"žb@x^ZaTda^EZG"pG%cJ)G0 Že20E%\HHSGO`Scv\]l`qmKt~eN&ƒdA…gAtW/K66#-!>+ T3 ¯€h¶pKqQuX2¾ž‹Å¦¿ ˆË¢‚Ä™w³‡e‹jQ³“z¨ˆo²’y°wwN,i@!Ä›cÔ©†àµ’ä¼™¦mO²j¼›…¨ˆo¤†o…[.£zMY3‹W!pO'—‹k̳˜Þ°ŸÎ˜oÖ‰gã…iéõòñûúúòáŠU™ÀWÇÿ^¼ÜY¾äuKJ=c2z>p>QPD.SÍxþ¡ÿ—ú–ÿƒýƒ ð–èˆà‚å†á‚â~¼n®hªL”E.-)4"9& 8?d›”{’ ^f2TN@2RXTO@(D?+|vb’v[~]EtV4zZ?B&!" h,)ŒV5SF)I,`_> ];-UB=MGGl=%•\8S\?‚g@cH{wZf|T-WKANMLM55oW_F @?QEN;Jl’™²ŸžžxrSjlM+y[5~`9hU6C2:* 8'5$ 2Iƒ6wUnP+lJ<ĤŠÇ§ŽÏ¢€Ë›xÊ™v­‹tsS<¾‡¥…l¦†m®{‚–gL‡e xK1ФˆÃy–ZBcD¼„¼œ†¹š…ª‚Q±ˆY§‚T‘\#R4ÖÉ«ÝÂªó¾¯æÁŒÀŒ_ÌŒfâ¿„ã­|ýüÿíýôD“’:®¼?·Ün½ÄPqXhY3p2c0IOZäˆû§ø¤òŸ ñžø ý‹ ó„ô„ñ çuçvÐkÔmÉc¤T”W e;720&{8_¯ÇŸ¬bfOX"( ZH'@"e8#`B&X8R/-Y:=\H+Q=$K( †Q*|GˆR(ŠgEnQ[zNNqGxYTb[FG5/T6 qX-7#]D7I@IK`‰™n¯ŠjkI#{]7…gA`:iJ$S5 T6jOT6> ,W8}_=„gB¼œ„ή•ı“Á°’Ƶ˜¾¡{ˆjC°“hµ•}¦†m˜y^—w^”tbµ‘L{Tj@oI i>pP+·™r»žwÁ–u›pO¬€_£xW–jHzd;M8íÚ¯åÖ¸Þ«‡§W*ð¬£çûüùúÛüûÞÍòñHž®)£Æ\¬²DbPh[7€?X)Z%JÉ™E÷œ4õ˜ è„ ý£(óš ñ‚ÿ‡ûŠûŠü†ð|êwÜw ÓmÈ`¶G˜?z2R D ­b-X£¼¨ÃvŒIO%& $)$<3  F$ƒ91}k?„dZuVZm]:jZ7EE$#R4Q8N7=\FGM@> 4o4u>•ašjI\QFvR/V{QMGZbC@!F:UI5$" N3,s+Ni]IiC)vS-vX/}_6ƒe?c=kM"Z<g+P2eGY<3 0 D|\BÒ²™Ï¹žÌ¸È´šÐ³Ž¦ˆbkM#Æ¥®Žwœ~hŸj–zaŸnt¡tVrK}WxL)nQ+£ˆ`²—p—uÓ¨†¶‹i¤yW§|Z¡nO²~_\=c?7 :ôýÿýüñðåÆóáÓúêÜÕÀ³EªOž¤\d2q`+f4e#c"j'ø˜Eÿª*ñ³LüÅWù¹Kö¹?ø¦÷™ ô˜ñ•ä…ß~à|ÔnÈbËf·\«I€Eh4=  tÐâ›Åx†+0,1lT0vU>S( (<d8 €S?UM€]R‚_TjL>" 59D$X6*_9CW6@`GC'W- i8„WB‡YC…WEyM/Œa*`JBbJHH5=* 4!8!,'L+^AfF'qR9rR:xZ7¤†`¨Šd˜zTqLP1T4Z:"iI/F& =»›}×ÁšÜÈžÛÇØ»•Ì®ˆuW1ŸZ®j±‡e¬^¡uS¢wT¶‹i`4ˆ_1€X)U*¸dÉžxÇœzØvÆ›yµŠh§|ZŸ[§Šdž[Ϥ…8 ‘'üÙ_ßÁŠñìÿõÿÿðöüÎâñ¹³‹[woT(zG‰; ]g&g$ÿ«fù©ÿÁVö´Bý½Lï´9ù§î–ý¿Ó”ÿ’âÝ|ÕnËfÁ\¹]¬Jw?c1J*  G1J-»M[%„ZCŒB ~`FTLC_ IT&vNHpUHyXJš{kq_L6" 2$ H'C-R2AZDBbIF Db2oB*aL†[;|P!f[+bIXM<#C2D5\6b1~IeO nO5nK9nL4lI2tS.oG®Žf´–p›}W_?$V6Z:!iJ3dF/Y:"A&7I ò¿£ç´—Ôº•²šu}fAvX2¥‡a¿–t«€^´‰g uS©~\tI*d6•p@˜u2\…cÅ™|Ù®ŽÌ¡}·Žn‘hH¤ˆb¡†_›X†uaà¦à…iìÿþïýþøòçèÒ›ÚÑ¡Ñ6ÿëÅØÅ©qDec$Ck+]n*ìœÿžkðµ/ò³2ý¿>ù¦3ð¢á—ü¬0ï èœÞ~íwÐlÓmÅa°T¬Kƒ8g'a2  $ &$ +$gP‚Z+~G uK)~dPYI84 "Ol7nD?tRQ‘nbyUKF1 2<: H&nKCP. *Q) c4&•jKvMa8€\DfB7U2'O,"R3cFA#„e>ƒe?r\›d8ŒnHc=À–tÀ•s´‰gµŠh¡vTrE'g9œ]7šb:™c:˜a:“n.a7ªf»†[j5 €T1Ä™w¼n”|i¥{YØ¡uÿúÿþöûûÝ¥çȸ×ÌÔÖ›jó׬ç°v»k~^{<n, d#r/íŸÿrõµ^ü¹]ù·[ÿ’%òŠöî‚óˆ Ó˜çŠéu×uÎlÑo·[ªIBq4P(  ( "‡o3zU#{FzS1p^Nni`QD+D#9+f.z=5rTH[T‡yT(% %; E xW+'  0 O'rG+eKP•V.QIqNA`:)\8pQ,K17C%fR2ueIveJtbEr_?zjE±”l¸šs¥…qhG4D( ²fQµeP±aO®{M±|O™n@sFe;3fQp^&}`3qS+¹“oº’o¸m·Œj«€^«}_Z2¼yT¥mE£nD£mEžy6‰_2X.t9{@qJ*q@!ÈŸxÈÁ¿Å®žÎ§ŒîÛ¶ãÇ|÷ßöãËò¨bÿ¦Ú›lè†+Åb­«wB{>Tf!å&ó°Hø±ZüÎyÿ»eþ”4ý™$ïŒ æ…ëŠô†í‚ãyàqásÒf¸[·U ‡G}2ƒ7 $2œrO‰Q4‡C*|E'‰Q3U:hdRXG). )" %K c*p@(pQ4‘[7zY./M4!cJ9/%, H)P2 ."=)]+f9–m?†c(iICUEYUGñŽìŠðŠóÛwÔwÍnü§6¹l­\R¨4‰AO,<<"`CQ1+6‘\-ƒN$’]3†P(oD0€e`\H1_M%F/pC!b6`9> -V#^-g7xM*{U‡^*qK@aJaf@CdB3X9!gH+=!K&‰Z%OqKX ”f/”oBdaNOQPDNiq7;dI jL&rN-_B+E92+$bA"^LMgXRpXEU4U6T(®re²nTºw]Ä‹mµ~`°wYQ0ŠjQ‘t]qOdC­}f•f1ÆqÍ–xЖ|ŘkŠ]0iF!Å•tÌ…m³ˆf´‰g—jI¤†`ŸZ‹b@šoMeD_,»“iÓ³“ú÷ÿôܿ޳Zäz*Ò·qìhÿ4ÔmÇy •µ«JÎÿsFn!<&ÿ­Pï¬Nùždö£Xêš>öª:û·€Þ¤â„Úè}ôŠ çsôhå[á]­]±cMª3y5‚EN94ŠL-hBnMŸ“`™o,y:òóèþϤÏkÎtÒ”"øgϦiÿç¯óº†¼Ÿ@ F³g>ê˜4ÿž@ú¦Xù¥X÷¦Pïž9ÿˆóŒä£?¶¬Œ‹´¤I¶åG·æ9ºô&¯ö¨ð!©ï:´ì,™Øvzžp›^~jD< {Nm,t<$cF%S$@) %[;"L,Q0Q5 “r@˜o@‘d9qe^F.e?:k>)sF2iC,F% I(' B' nQ2tV6~aA]4–rWOJBZBKoL%tT&qS,kL$L>/;1(=439)jJ pQ< T9^>"W7R8 ¡kM½qZÉzdÎŽs´Œj­€[W9!‹v‘qX“sZ]DŽfT”~|—†yˆhOKwHi:\1a6»n¹Žl¼‘o¿”r¤yW‰]; ‚\ž€Zž€Z—z]ƒX,‰SèØ¦ò³b÷|î§^ñÕl=ö­Vñ€ ûqæyÿkü·N÷œRþ“ú•=ú¤Rÿ£7ý•Kþ¸÷ÜÀ€¾ÄWàû>ÂâSÈÞ`¿ç4ªÜ á›ÏšÏ˜ÎÇ•à  Ÿ•Á†Õ ŽÖ…À8¤À1p2+o(n7&E,* Y8 yX†^C”P-…E"ˆM,lB e;0zYBrXBlK-eF)Y6-X2=V4%D{P~T›iF^+xK&e8(f9%kF-`@'Z52<!"N%!`>:^D-„cE^@zmInG7mJ,wX3~_8[QEB=8C@;I51…\KnA”xF‘uBX8F& X;&\#p·dLdži»–o—r|^H1'% }d’tZ›jS’jZ‘z{Š}l¥„g„J'–€EK9a2xF&Ìšz•r»“o¶”mÆž~±ˆh¥Šd›€Z°•l¥ƒƒ§rcÈ„gã”.Ò,ø´Tû>íˆü™ñ‹éƒÆe ÔfõÖ‘ë‘2ÿ– úŒ"ç—8Ý×½àþüÙÝÛÿþâ¿ã¾€¯©v¨°‘‘‘˜˜™š‹t‹€mˆ}kxzŠpr€ot„S~˜9p† ­%¥%„¨„¦yœ޹–Ê –®e\‘A$$x0<$ U&˜d.œsUŒa@zO-zO-e;vU1^;qQCƒcQeD0]=#T4W7 <W8!kAwN#‚Z*yP$sF/mF2fF-gG.[:!R2'>!G/cLFY;\o^:iHoM(nO*rKŽmQvVd^.,!PX ]"Š(Àp[y58+  v`®{U¢}`”ˆ|©Š¬‡z”mJgY:•kV^?]>”aD–w.XߎUÈ”nÖm`8c:žw[½{s½zr±ph½qÿ®]èƒÿŃÚsìsþœ1ç)µ™U܉Hþº]ëÅ¢ÖÒÉ—®¹Y«ÔD¨á¿á#»â´Û!­ì¦å§å!§æ"§æ­ç«æ£Þ¡Ò¤Õ¢Õ-Ë9™¹g’¨^’©WŒ¢OŠŒ`‰~d„pQwl@qnEj]*jqs˜x®!WLm  Y!e3_D“hF’gE{O,pJ.u_O,^A~^EuU•wQ‹jCxfGmWO…hIa;rL ‚\£…_Ÿ[‡iC}_9lN.4?L'\5P0O/³‰h³ˆf´†c°šˆ­˜‡—„uN.pWeD”iGŠ`=‡ZC uJƒ_&F{?i:qB¶‡SΓjΓkĉaè‰gä™åÿ Jè‡ø~ñ¯8ÊyþóÿÿÙÏ·‡ÅÕX¶á4¥ãJ´Ù0±ç+¤ò-§ñ,¨ñ ´å¹ì¶ê#¦æ#¦æ­ê«é«è$«Ü¡Ó¢Ø¡ã+œÎ.¢³/¡·.¡º/žÐ7°@§?‹¡aŠ„?x„Ank:gX6]N$Ze(hc,  4 X1#ˆT.Ž\2œkM}\?xaLLr|*O' ]9nM-zX8eD,l>":0 1S2S&i;‹`6pRNnMGfQ8PA?0?2NA I2)C0fI tS+eEdE qM‘pK}mNua\tX4y[1ŠkFŒnH ‚\’tNrLoIj0iSoX?9 ?8 ?:O,N,·Šj¿p¼Œi¯œŠ«™ˆ ‘©‰miI1£zY—lJe@“dl¨|n¡wR?)t…RC+/°–t÷›ê“yߊsý¼|÷Œ(îv ÎŒÙf×})Èw=ÿ÷»Ä©w†  R§ßWµà=½ñ"µï#¶ñ$¶ñ´é´ëµì­ä¶í³êªå«æ#©Ø!©Û!©Û¢Ô£Õ¥×¦Ø¢Õ™À›Ä›Ä›Ä”½•¾—À–À–¿‡µ w¥grSh'QeF5(? C;%TK>mY7`H‰dXx[Bre[, K0+S<;eR@zT[cB2L8& ,0;U$‚X4uI'vQ|g@šmIÄR̓Eÿ‹÷Á…ö©YÚ?Ýfÿ£aä¸u†¥Î[²ÞW¿á<Â×(¾í3·ÿ9²ÿ"±í&µò²è³ê¹ð³é´é³è ¯ë©æ%¨Õ#©Ø"¨Ø¡Ó£Õ£Ö¤ÙžÓ›Ã›Æ›Æ›Æ›ÆœÇ’»—À!yŠ q‰|m‡j`ƒ1PW7! 62VCTtH9’B™pA‰gIUP;"+" 2C'E+Ja87]N‚Z9V3  8QR&i;{TDlJBiG:N,$P0iI`T$B4=-%4"a=4g>;f<>qO/yX9Šd\“oasR/qG•wNsI¢„^ž€ZrPbAœnO t?F, LG0N< XG*gL ¸›n¸œl¶”ŠÆ¤›Ä¢—¼™‘®kT*˜mK¡wUžgL¡jNeL“iH}j[¬x\£pQ“cAubQŒgGסqÿÊþƃû¡Eœmõ³k¹Åì‚ÔÑLËëPº÷FÃú1Äó*¼÷!³î"µðºñºñºñºñºñ ¶ô ·õ ·ô´ë¯æªç¬ç«æ"©Ú¡Ó¢ÓœÅ›Ä(˜¸&˜º&˜º'™»'™»&™¼"šÀ“º•³–µ° Š´„­w rŽet Qi(?;   3*C;&WU@t6%|O:fStM>3" 7B,&K6#c>({Q%eE,kK2?) 6#8%!<8R97V>:XA)XF)S;d@hBoU3ˆmIu4~d!|`7`GmM4tU8iI0jJ1dP2H89) +$ "#>""?#;P6DcM@eF3ˆgRŠjT‰lB‘sJ¥‡`¦ˆb£…_ŽdB”iGV0 `"`"M2U> E- ‚p¾¡Ê­žÈª¾ „§{Z^0¨{Zµ€T·‚U‹\¹Žl”iGfO&œxS§pO¥kQ§nQ©rS×áÃÿ°­h¾ÖL¿ï?Âÿ;»ð;»ñ.Ãÿ0Âý,¾ù À÷·î¹ð¹ð²éºÿ·ú²õ¹ö ·õ ·ô´ë´ë ¬ù«ç#¨×œÅ—À%£Ê  ½žº(–Á&˜À'—¾0—¯.–¯/”¬)†—4 •µ)±6±E…žL§B€•)…•¼k”i.g9T\622  !;)N-c1c)“^6vK*! &3E% _>&dE(S5R5oKmIpP$uX+‚e8sV+‡gLlHDgG,Y9 kK2cC*T4>'5 /  $1<)`G„f@_M‰iP„dKŽpK™{U ƒ]¨~\žsQ‚Z7X![$[$[$[$‡O¹€J¶KшVÊ‚QiG Z<U9¿ˆj¹‚dÅ‹nÇœz»n¬|T ™užÖºœl¥{^´tWÿv{©}a¯Þ>ÄÿEÀüKÂõ)Æï,Éñ+»ø*¼÷!³î Âø Â÷Á÷Á÷¸ï·ÿ·ý²÷ ½û¶õ ·õ´ë¶ì¬ÿ©æ&©×,§Ð˜Ä›Ç!¤À$§Ã'›¥%œª'œ«)ª1˜±0—®/ˆ›,ˆ™Cˆ™+–œ” ‘“ ‘’ ŒHvJ”” nÑo¨ oŒ6[oDUb/QK %AP`%j3 |Jd?A. O=!WF+I1fG/tU=rPCb<8\42,!5(jQ:|\GhH4pS-{\Bˆdc}]BkK2dD+_?&nN4G1 5"8"'&,)/%/$/%ƒm5‰mI…f[ˆhNŽnUŽqJrIqGª~]´†f®ƒbj0\"X#n3€S@§V+®\0½]1xa0A lS,gO*oY3ÌrÊlÊ’sÇž|É ~ÀZ€t»¾ö§ÊÓµœŠÁvN„¢³8¹ý4Âç7¿ñ=Å÷1ºì/Áü0Âý*¼÷+½ø"´ï&¸ó(ºõ)»ö)»ö-¿ú»ò¼ó"Áø,½ø"´ï"µð²é®å­Ù$¸æ«Ù¦Ë š±+˜«%—¹ˆª)¦¼!𳕭*‘ª)‘ª)’¨'‘ž&(“ ˆ–$ž‹ „˜ ‡š‹šzˆ6jyIˆŽ2{~2p…dcnNYM>@3 5%4@K'j>j<> V8\>ZK7MKHUSQbUJkVEaL=N;O<3P4&bC-hH/bB)}]D…eLnN5_?&bB)_?&V6M-D$ J)Z3 zQ#Z1pGAa6…eN„dKƒcJ€`GŽnV”uV†hH‹pRˆ];]2MpUv–š¦L5®T*­T*¬U'©R3oD£ZdQJ9ƒXѦë­‹Ò›}Ù¢„Ф—ÍŽ``V>¹Ã¸¤„kÁ{Y6ÌìT½àS¯ö7¿ï7¿ï7¿ð/Áü)»ö-Âÿ.Áý4Èÿ5Èÿ,Àý.Áþ/Âÿ+¾ûÄûÁù'Êÿ/Áþ±î!·ó¾ø·ñ*¾êªÙ ¤Ó Ç!š²+˜©-ŸÃ'˜»Ç¨Ð¡Ç0Ž£)“«%“¨*”Ÿ)“Ÿ)‘ ˆ”‹—‘¡„–ƒ‹y¢Âj_yswsQ‘tny~vX[PDLDg9LF!!!&K,i9P3\>G)>tL*‹cA‡kTeRBiUHiV4gU5<6$ U1^:!cA&xVÆø=ÅùBËþ<ÆøAËþBËÿHÑÿDÍÿ=Äõ2ºë>Æ÷:Áó8Àñ8¾í;­Ï1£Å'¨Ó+§Î-©Ï ©Û¸ê¦Ô+¡³%›ª¦à¤Ö™Ë¥Ì §°©§)‹¤+’©”¨¤’¨•³¡“£Ž«…¤‹•t†l~"p}rnqsua†}fwfQ4rj"qP3ƒcJ\<#Y8%W5+];3aB!Z:!H:"LN6+?Y9q_OtaOvaThRFYC2?5"% " /A #M(3\6DpP5dF }_6gF1aA*TF-XC"jA‹U(xBtD‰:$o9A4F;#7B#G.p2.w$i(\=$g<,_1"\0)c1|=yZ$ⵆðª†Ý¨~ݨ~ݨ~ÔsЙnØ­~T9>9QgIÆ‘u=Å÷=Å÷7¿ñ7¿ñ7¿ñ7¿ñ2Àñ6ÄôYËöUËöNÅñNÍõGÅî@¾ç?½æDÂëDÆõFÇõJÌù@Áð;¼ê<¼è;­Ï1£Å4¦Ç&©Ôªá¢Ó#«Ý™É0¢Ä,¾¬â ¨Ú¢Ó™Á%£Ì!¢Èž¹!’µ%›¬Ž¢£‰ ±¸ˆ¥‡¥›q’fŒh;a_]bXsslloRQf[PR7"I)jJ1wW>wW>`A%^;8cB4V5%V4(^;06  -=K)X?'wcJu`QlX?n[<*(7D# U6iK%cErT-wY3eF+fF-gH/e;€U3wO,r:m6w?!f8W@P9[CH8m7 n:€4v7&^?9{C3m4#n2#j=)lL?zC8ŒS=Ïšn㮄ܧ}ߨ|ÕªˆÕªˆÞ¦‰d9ž‡^’†ivL¦~J<Å÷<Å÷=Åø7¿ñ7¿ñ7¿ñQÆÿQÆÿgÀöeÂ÷eÂ÷SÕýTÓýIÉóNÎöPÏ÷^ÈâQ¿ÜZÈåXÆâJ·ÔK¹Ö8ªÌ@³ÕV­¸;¨Ä(§Ò©Ý!©Û#¨×3£Ä.½¤Û¤Ö"©Û–¾šÃ›Ášµ!’µ(­'š­#•§£ ² šÃ ‰¦†£œp’e‡j\t8_slkchkM;NDZ[@ZH', 1B!hJ/nO8hK$nKE_8#]†/'t8(l2"m3%b5[as‹ZSt<%‰T(¦qGÛ§}︌دŽà¸–⅒fE­Šv¯yW®l?JÈñJÈñEÃìBÂ÷OÏÿMÍþZÊøYÉ÷iÍógÍógÍórÏøyÖÿxÕþtÒÿlÉödÍñ[Áç\ÂèV¼â]ÃéO¶ÛCµ×<®ÐO±ÙJ°Ö?¥Ê=§Á/¬Ö'®Þ+¡Æ)ŸÄ›Ä›ÄœÅ ¢Ê(ªÒ&¥Ì&˜º'˜º˜³˜µ ª ª ª ª Œ©‡¤šv˜]€gbƒ1a`Ec^jvd;^FKXG^V@C0?++>R.c5 e8#tN7xX?Z:!X7%^<0W0%(69"!M3:V@2_Q#lJ3E% P:?+&B!\6W2aB)gG.gG.pP7wW>‹kR|\C^A*ˆ\:zO-q<r;s<o8w@"€=$]X&?9=V'tCmbAka@YY5p?+p>*uRQrLCk9 R.†W1Š^8¡eEÆ‹jî·™â«× ‚Y©‹eŸ[JÈñJÈñJÈñW¿üS»ø`ÈÿYÉ÷YÉ÷iÍógÍótÙÿŽÓÿ“Ùÿ’Øþ’ÜùŽØõfÓülÒøgÍó\ÂèTºàJ±×K½ßCµ×G©ÑE«ÑA¦Ê[±º5ž¸3£Å= µ9›¯›ÊœÅ$ Ê1šÊ0šÊ.™Ç&˜º&—º ¤¿—´—´’¯ Œ© ª Œ©ˆ¥y”s•a„cˆfˆ,\[A_[JUCAdLN\KbXQR;0Q@J<)'HT'P*Y9 eE,kJ8X6*Z8,_>1,,   ,3?*H2.kJ0Z:!E.WD&<) 5'3&8R2^>%_?&mM4mM4`@'{]FoC!yN,‚M.‡P2q:r;q:yB‰PFH=‚dPjO:\C-A N}=/nT9bH-3^YXgcc?7oS?x]Hw^I‚[J}VEZ=fHÙ¡ƒÑ³›}W¨Šduserland/host_applications/linux/apps/hello_pi/hello_triangle/Lucca_128_128.raw000077500000000000000000001400001421703157200300310ustar00rootroot00000000000000»€•À~“¾€“»‚—Á…˜À…—Æ—€˜Â˜Å~›Ã†–Á„‚›ÃŠ›Á‡šÅ…›Â‡›Ã…œÅ‰œÄˆžÇ‡Æ‰¡ÇÄ‹ŸÉ‰¡ÉŠ È‰¡ÇŠ¢ÈŠ¡Ê£Ê¥Ìˆ¤Ê¥É’¥ËŒ¤ÉŽ¤Ë“¤Ì¨Í‘§Ì¨Ì‘§Ì©ÍªÍ“©Í‘§Ï“«Ì•ªÏ“ªÑ“«Î•©Î—ªÐ•ªÌ™­Ñ“¬Ð™¬Ð•­Ñ—«Ñ—­Ñ™¬Ñ˜¯Ò—¯Òš¯Ò—¯Ñ˜°Ô—¯Ñš±Ô›±Ô—±Ó›°Ô›±Ô™¯Óš°Õž±Ñ›°Ö›³Ô™±Ö›²Óœ³ÕŸ³Õ™³Ö´Õœ³Ö´Õµ×ž³Ö°Ôž´Ô¡µÙ‰“xx}rqv}{}MLLA41jif>44]RT.-+834 IF@ZTUidd675MGLK=4YQPMHG[OE;7.œ‚”~} ’‚˜…r—ƒœˆ —š}.)!.!"x…Ž“ª¯¡]ZFPR€“½”¿–Àƒ–À‚™ÂƒšÄ€™Á„šÁ„œÂ‡œÅžÅ†œÆ‚›Ã‡ŸÅ…œÅˆ›Ã‡Ä‡žÇ‰ŸÆ‰¡ÇŠ Ç‰¢Êˆ¢Ç‰£È‰¢ÊŠ£È¥Ê¥ËŒ£Ê¤Ë¤Ê§Ë¦Ë¦Í’¨ËªÍ©Ï’§Ì’ªÏ‘©Í‘ªÎ”¨Í—¬Ð“ªÐ‘­Ñ–¬Ò–¬Ñ•ªÏ”®Ñ–¯Ó–¯Ò—¬Ò•®Ò—®Ò—°Ó—°Ò—®Ó˜¯Ò—¯Óš°Ò˜°Ò—¯Ó™³Õš°Ó™²Ö™°Óš±Õœ²Ô›³×š²Ô´Öš±Óš´ÖµØœ´×Ÿ´×›´ÖµØœ´×µØœ´Öžµ×¶ØŸ¶×žµ×žµ×ž¶Øµ×Ÿ¶Ù ®Ëyxz€~SMO988`_a532YRP„„eccWGC 864HICNCHVWSRGDH>:VNK\WVZOTPN@C;>ƒ‚YNLC??VLOHC8K8@\XQ@B:TI@745D=<‹˜š¡’|š’~§™†¥ž˜“wm~qZŒ…œŠˆ5FH‘’œ*1$~”½—ØÂ˜Á|™Áƒ›Á‚™Æ‡Æ‰šÃ„šÃ‚œÅ†œÅ„ƃćžÇ…œÇ†Æ…›Äˆ¡É… Ë† É…¢Éˆ¡Èˆ£ÇŒ£Ê‹¡É¥ÊŽ¥ÌŒ¢ÉŒ§Ì¥Ì¦Ë§ÌŒ§Ê¦Ë’¨ÐŽ§Ï‘¨Ñ‘ªÏ¨Î’©Í‘§Í“©Î’¬ÎªÐ•ªÏ”­Ò’ªÏ“ªÏ–¯Ô—¬Ñ•­Ó—®Ñ•­Ò•¬Ñ—®Ò•¯Ò—®Ò˜¯Õ–±Ñ—°Ô›¯Ô–°Óš²Ô—²Öš±Õ›±Öš´Ö™±Ó³Ô™²Ö™²Ö›³×œ²Øœ³×œ¶Ùœ³Ö¶Ú›µØ›µ×ž¸Üž³×š¶Øœ·Û¢¶Ùœ¶Ø·ÛœµÖžµØµÙ¥·ÙaZ_xqtPNLA<@F@;XUT.(*SPU/5254/bhhRKP[NEUTNM>7_TQUKE@7=K@@VURG<<0+0‘”ŒÆ¾½¦“„‹‰zŸ™Œn^\M,j[NKiqw„;<$%|“¿˜Â˜Â˜Á|˜¾~šÂšÃ€šÂœÅ‚™Â™Â„œÄƒšÃ…œÅ†œÃ†Æ…ņŸÇ…žÆ‰¢Ç‰¡Ç‡ ÈŒ£Ì‰¡É‹¥ËŠ¢Ç‰£ÈФɋ¥Ê‹¤ÌŽ§ÍŽ£È¥Ë§Í©Î¨Í¨Í‘©Í©ÎªÏ“ªÍŽªÎªÏ“ªÎªÏ’«Î•¬Ð“®Ò”¬Ð”ªÏ’­Ð–­Ð•­Ñ’®Ð–®Ò™±Õ”¬Ï–±Ô–¯Ò—®Ñ–°Õ—°Ó™°Ò˜¯Ò˜±Ô˜±Ô–²Óš²Ôœ³Õ˜°Ô™°Ô™´Ö™°Ôš³Õ›³Õ™µØš²Ö™´Ôš³Öœ´Ö›¶×´Öœ·Ù´Öœ¶Ø›¶Ö™¶Ù›µØœµØ›µÖ¢¾ãibb}~ƒVXZ320NFDx{~>:;./4:82{wwlljC<5\SJWLDMHETOLWF=PB5B@9SNJTU[B>@*&"64,,0+,PQohY­³´žŽƒž}HQI£©®$~—Àƒ•À™Á{˜À•À…•À€™Âƒ™Æ…›Á€šÃ…›Åƒ™Â…›Ä…œÇ†ÇˆÆ‡žÇ‡œÃ‡ŸÂŠ¡Ê‡ŸÆ‡ŸÈ‡ É‡£Ê‡ ÈŽ¡ÆŒ£ÌŠ£Ë‹£É‹¥Ê‹¤ÊŽ¤Ë‹¦ÍŒ¦Î§Ë§ÍŽ©Í©Ì©ÎŒ§Î’©Î’¦ÌªÏ”¨Ï‘©Ê“ªÓ“«Ñ‘­Ò‘¬Ð“¬Î–­Ô’­Ñ“«Ð”°Õ–­Ð“­Ñ–­Ò–®Ô–®Ò™¯Ó˜°Ô—±Ô˜°Ô—°Ô™°Ô˜¯Ô™°Õ—²Ô™³Ôš±Öš±Ò™²Ù™³Õš³Öœ³Õ¶Ø™³Õ³Øœ³Õ¶Ù›³×Ÿµ×š·Ùœ³Ö™µ×Ÿµ×œ¶Úµ×™¶Øœµ×­Ëð`\e†?:7 TPJb_[901 &! " MMKUJEMB9OC:MK@^SHPNKB;7MICMFDC>9@9?GHE+& ,*-}pezkhƒ|j~F6A046#&416†ƒ\=*ipt¶«œ|”À}–¿{˜Àƒ–¾™Â€™Á}šÂ—ƒšÃ›Ã€œÃœÈ‚›ÀŸÇ„ƄƅƃŸÇ…ƈ¢Ç‡ŸÅˆ¡Ç…¡È‹£ÉŠ¥Éˆ¢Ç‹¤É‹¤È¦Ë‹¥É‹¦Ë‹§Ì§Í§ËŒ¦Î©Í‘§Ì©ÌŒ¨Í©ÍªÏŽ©Î’¨Ì’«Î‘«Î’¬Ï’«Ï”«Ð¯Ò–®Ó•­Ñ‘«Ï”­Ò•­Ñ•®Ò”¯Ó—®Ò—±Õ˜¯Ñ˜¯Ó—²Ô—±Ò˜±Ô—°Ó™²Öš²Õš±Ö˜±Ô˜¯Ô˜³Ô™±Ôœ±Ó›´Ø™²Õ›´Ö™²Ó™´×˜´×˜µÖµØœ³Õ™¶Ö´Öµ×¶Ø›µÖœµ×›¶×œ´Ù›µÖœ´Ø„‘®~~XUV?:7FA<\SO/+)+$$ 11.CEDQHGNBDLGCWRP‡‡‰450RJCMIBIB>A;7KIHGGE.)"AB8xqe›†zŠ‹ƒ¢•„–k—„h\=/k@2<91/!#6+!–À˜Ã—Ä}›Ã˜Ä}™Á—ăœÅ„›ÅšÂƒšÅ…œÅ„Å„œÄžÈ†ŸÈ†ŸÇ† Æ‡ŸÈ‚ŸÆ‰¡Ê‰¡Ç…ŸÇ‰£ÈŒ¤Ê‹¢Í¥É£È‰£ÈŠ¤ÌŒ¦Ì¥ËŒ§Ì¦Ì§Í§ÎŠ§Ë«Î¦Ì¦ÌŽ§Ì©ÎªÌ‘©Í“ªÐŽ¨Ë“«Ñ”«Ï•­Ð“­Ó‘©Î®Ó“­Ñ•¬Ï•¬Ñ”°Ô—®Ñ—¯Ò–±Ô•®Òš°Ó™°Ñ™°Ó—°Ó˜±Ó˜±Õ—±Ô—³Ö˜±Ô˜³Õ™²Ô˜³Ö›³Õš±Õ˜´Ø›²ÔµÚ›´Ôœ¶Ù›³Öœ¶Øœ³ÕœµØ›¶×š¶Õœ¸Ø›µÙžµ×¶Ù·×ž¶Ø¾ã…‚…GDB2-)SJG74.YOL932" !9;6@C>_TSFE=LFF{}|ŠŠEB;@;@QJEVMACE=K@F,./0+(<<5746 •…¦“‚‹ƒz¥Ÿ‹•€v¯·¢yufŸ|]™œ‘`C?~•¾€—À•¾{—¿™Á™Âƒ™Á‚™Â~˜¿šÂœÃ‚™ÂăšÃ„œÄ…ÆƄ Æ‡ È†¡Éˆ¢Å‰¢Çˆ¡É„ ÅФɋ£É¥ËŠ¥Ç‰¦ËŒ¤Ê¥ÉŒ§Ë¦ÉŠ¤ÊŽ¦ÌŽ©Ì¨Î’©Í¨Í©Í‘«Ð‘©Î«Í”ªÐ¨Í’©Í’­Ï‘ªÎ’ªÑ–®Ò”¬Ð”­Ñ“®Ð–®Ð”®Ñ—­Ò•¯Ð–¯Ó–¯Ð•°Ó—±Ò–¯Ô˜°Ò•°Ò—°Ò–°Ó™°Ò–²Ô˜²Õœ²Ó™²Ô™°Òš´×–´Ô™´Õ™±Õ›³Ô™²Ó™³Ö›³Õ›´Öœ³Ôµ×š³Ö˜³Ó›¹ÙµÙ™µ×œµ×›´Ö›´Õ›µ×ZZgJGF?;;41-PKHSOPOCB$#%*&"KKMf]aYWX??7{uqyx|VIGODCIIDJA9GA,PII84-.-':7/;=3D>;¦±®ÎÆË¨”ƒÑóð8+!{Ô¬•¯¢™|‘‰–¿€—Õ¿‚—À€™Á–Á™Ã‚˜Ã‚œÃƒš½‚œÅ~›ÃœÄ~žÆ‚şňžÃ… Æˆ Æ„ Ç‡ŸÅ‡ŸÈ…¢È‰ ÇŠ ÉŠ ÈŽ¤ÈŠ£Ëˆ¢Ë‹¤ÌŽ§ÍŒ£Ë‹§ÎŽ§Ì¥Í¤È¦É’¨Í§Í”©ÏŒ©Í“¨Î©ÎªÍŽ©Ð’¬ÎªÏ”¬Ð’­Ñ”­Ð“¬Ñ”®Ñ•¬Ð’­Ò•¬Ð”¯Ó•¯Ò•®Ô–®Ò”¬Ð—¯Ñ˜¯Ó™¯Ó•°Ò™²Õ–±Ó˜±Óš°Ô˜°Õ›´Õ˜²Õ™²Öš³Õœ´×™²Ô›´Õš´Ö›´Ø˜µØšµÖ—³×š³×™µÙ›´Ù›´Ø˜µÕš²Õ´ØšµÖµ×µ×›µØ‡Œ¬=86EABA>@7.-_^[\M=2-/8:8>?>IIDSFD?<78-+>;5&C>>493VTP=4.QK?1-,&)$FAAHI@=! 6.3212`MH736F:>5%š‰|”yšŒ†¯¦£  Š`WM…t~[W}”½€—Á”¿‚šÃ|™Á€˜Â‚™Â|™Á›Ã‚Ä‚šÁƒœÃ€œÂžÄ„žÄ‡ Å Ç‡ŸÇ†¡È‰¡Ç‰ŸÆ‹¢Èˆ£É…¢Çˆ¢Ê‰£È‹¥Ê‹¥ÊŠ¦Ë§ÌŽ¦Ë‰¥Ì¥ËŒ¦ÊŽ¨ÌŒ¦ÊŽ§Ë§ÍªÌ¨Ë‘ªÍ’«Ï’ªÎ«Ð‘«Î“­Ñ”ªÎ’¬Ñ‘®Ï‘ªÎ“¬Ï”­Ñ«Ñ“®Ï—®Ñ•®Ò“¯Ð”®Ñ•­Ñ”°Ò—¯Ñ—°Ò•¯Ó—¯Ñ–¯Ñ—±Õ—±Ò™±Ô—²Ñ—±Ô–²Ôš´Õš²Ó™°Óš´Öš³Ô™´Ö›´×š³Öš´×š²Ôš¶×š³Óœ¶Ø™´Ô™³Ô—´Õ™³Ô›¶×›³Öœ´Õœ¶Õ˜µÕbi‡EB?OKJ302JF<_WV(*":97x€~QF?UROWOOz|3*0533BB@egb& $41251/x‚ˆ^a^4/.ACB858Š„|•‹}¢‘}š‘}¯ud¹¶ ’Œ|¹­š”¾˜Á–¿z•™–Á€™Â~›ÆƒšÃ™Ã†›Ä†œÅœÅ‚œÆ‡œÈ‡Å†œÆƒžÆˆŸÅ†¡Èˆ¢Ç† ÈŠ¡Ê‰¢Æ‰¡Ê‰ È‹¥ÉŠ§ÌŠ¦Ë‹¦ÍŽ¦Ì¤Ë‹¥Ê§ÌŒ§Ë¨ÍŽ¨Ì¨Í¦Ì¨Í¨ÍªÌ«Ð’©Ï‘©Î‘«Î‘«Ì‘­Ñ’«Ð“¬Î’«Ï‘¬Ï”¬Î”¬Ð‘®Ð”­Ð˜°Ô“°Ñ•¯Ô—¯Ó–¯Ó—°Ò—±Ô˜°Ò—¯Ó—°Ó˜±Ò—³Õ—³Õ˜±Ô›µÔš²Õš´Õ™²Õš¶Öš³Õ±Óš²Ô™µÓš´Öš³Öš´Ó—µ×›³Ô´Ö˜´×›´Õš¶ÙžµØš´Õ›¶×š´Ø›µÖªÄëB;?LGG423I@@(# :=9ihdDBFHD;RNGzyxˆ‡ŽC6;& C?>THH!j]d‰‰‡KF8>>8:64C=.HBB,20´œŒ›‰uŸŠ~{xvd–‹i¢‹n•¿}–¿€–¿{˜¾šÃ˜¿~˜¿ƒ›Â~›Ã€™Á‚šÂ„Å„žÂƒžÃ„œÂˆžÅ‚žÄ…ƆžÄˆŸÅˆ¡É†žÃ‰£ÈˆŸÅ‹¤ÈŒ£Ç‰¤ËŒ§Í¥ÎŠ§ÌŒ¦ÊŒ§ÌŒ¤ÊŽ¦É¨Î¨ËŒ©Î§Ë¨Ì§Ì«ÍŽ©Ê©ÍªÏ‘ªÏ«Í¨Í‘«Í‘«Î”¬Ð‘«Ï’«Ð”­Ð”­Ð”¯Ò”¬Í”¬Ï–°Ó”­Ñ”¯Ò•¯Ó™®Ñ–®Ò”°Ñ™²Ô—°Ñ˜±Ó•±Ñ˜±Ô˜²Ó˜±Ô˜°Ò™²Ô™²Óš²Ôš±Ô›´Ö›´×›´Õ—²Òš³Ô›´×š´Ôœ³Ö˜´Ôš´Õ™²Ò›µÖ™¶Ù—µÔœµÖš´Õ›´Õ–³×hgoD==IDE672-)&;<CD>¡•…|ai¢•h_N¡—{|“¾~–¾˜Á˜À{˜À›Ã‚šÂ„œÅ›Â„›‚ћąžÇ„ŸÇƒžÆ‡žÇ„ Ç† Ä„ Çˆ¢È‰¢Ç‡¡ÆŠ¢È…¢É‹£ÉŠ¥ÊŒ¥ÉŒ¦ËŠ¥ÉŒ¥Ë¦É‰¦Ê‰¤ÉŒ¨ÌŽ©Ì‘¨ËŽ¨ËªÎ“ªÍ§ÊªÍ‘«Í‘©Í’ªÏªÍ’¬ÏŽªÏ“­Ð’«Í’¬Î‘­Ò“¬Ï“­Ð”­Ñ“­Î—²Ô”­Ï—¯Ò•¯Ñ”¯Ð˜°Ó•®Ï—±Ò•°Ò—±Ô–°Ò™±Ó—°Òš³Ó˜±Óš´Õ—°Ó›´Ö›²Ô—³Õ˜´Öš³Ô˜³Ô™³Ô™±Õš´×›³Ô™²ÖšµÓš²Õ›´Õ™´Õ™¶Ö—´Ö™³ÔœµÖšµÖœ¶Õ¨Çé@76A<;%%#,&&MKGSRQOLFMG<—# 2//&! ),*.*&2/*56/tss.+(<3.H===96<12IIEIB:£–y¥šŽ¢“r—‰sª¹£€”‚—Á‚—Å}™Â{—ÙÁ‚˜Á˜À€žÆ…›Å„›Ä‡œÅ‡œÃ„œÅ…ƈăŸÆˆžÈ†žÅƒ¡ÇˆÇ‰ ÇŠŸÈ†¢ÉŠ¢È‹£É‰¦Íˆ¥ÇŒ¥Êˆ¥É¦ÊŒ¦ËŒ§ÍŠ§ÍŽ¤ËŒ§É¦Ë¨ÊŽ¦Ê§Ë’¨Í«Ï‘©Î©Î’ªÏªÐ‘«Î©Í“¬Ï’¬Î‘«Î”«Í“®Ñ“¬Ï“­Ð–®Ð•®Ð”®Ñ”°Ò”°Ó—¯Ñ—°Ó—®Ô–®Ð—±Ó˜±Òš±Ôš®Ð—³Ô˜±Ò˜±Ò˜³Õ™³Ô˜±Ô˜³Õ™±Óš³Õ–²Ó³Õœ´Õœ³Õ™³×š´Ô›´Ö›³Ö–´Öš³Ôœ´Ö˜´Ô›´Ø™µÕ˜µØš³Ö—¶Ö›·Ù_]fSKB40.A??vxzGC=VKCKA9––›'!$+$&21+"!!#&$73330-‚…z|I>D?9/=94G?@MH,i~|ˆp†~p ’…8-(£„j•¾€–À–¿€—¿~—¿€˜ÁƒšÃ~›ÁœÃƒšÃ€Å„žÃ†Æ‚žÅ…†œÃ…¡Ç„ Åˆ¡É†ŸÄ‡¢Æ‰¡Æ‰¢È‹¢ÇХɉ¤É‹¤Èˆ£È‰¦Ê‹¦ÌŒ¥ÊŒ¦ÈŒ§ËŒ§ËŒ§Ì¨ËŽ¨ÍŽ©ÌŒ©Í‘ªÌŽªÍ¨ÌŽ©Ì’«Î«Ì“­Ð‘«Î”¬Î’¬Ï‘ªÍ“­Ð¬Ï”¬Î“­Î’¬Ð”¯Ï‘¯Ñ•®Ñ”®Ò”¬Ï—°Ó–­Î–®Ò˜²Ô•®Ò–¯Ò˜²Ó–±Ó–±Ô™¯Ò—±Ó—±Ò˜±Óš±Ó™´Ôš´Óš²Ô™³Ò™´Õœ´Ö›³Õš´×˜²Óš³ÖšµÕš³Õ›´Ö˜´Ö˜µÕ›µÖš´ÔœµØš´×›µÙš´Ödd|IFI1,+;<92-)RE:742L>4Œ“30- +.% 886ŠŽ6.&]VP<5+OJI;=7LHF:66<>1OLJB75 ‡uœ‰uzw‰„o‘‰t{—€šÂ€–¿€œÄ˜Á€›¿€œÄ™Á‚˜Áƒ›Àƒ›Ã†šÁœÃ†œÆ„Å„›Å…žÆ‚ŸÇˆžÄ† Å‰ Ç…¡Æ‹¢Éˆ¡Éˆ£Ç‡¤ËŠ¤Ë‹£ÉŒ¤Ç§Ë§ÌŠ¦ÊŒ§Ì¦ÌŒ¦È¦Ê¨Ì©Í’§Ì©Ï«ÍŽ§Ë¨Î¨ÌªÐŽ©Ï‘©Ë‘ªÐ’«Ð“«Í¬Î‘­Ð“¬Ð“­Î”­Ð’®Ò“¬Ï”°Ò•®Ò–®Ñ—°Ò•¯Ò–¯Ò—®Ð•±Ô—±Ò–°Ñ–°Ñ—²Ò˜²Õ˜°Ò—²Ô™±Ó™²Óš²Õ™²Ñš²Ö™²Óš´Õ˜³Ôš³Õš³Ö™²Õ›²Ó™³×™´Õ–´Ö™²Ó–¶Ø›µ×›´Õš´Õ—´Ö˜µ×™³Ö­Æì 643ghgWUO;0'RKB<72wst$!"$##.*'*&&21/0,)hji%($<66A?:=7+NL<==9:2.OL9B<3fz€‘‡p¢—†ƒo¥m•¿~—À}™Á‚šÂ~™À€˜ÀšÂ˜¿šÂ~šÃ€žÆƒœÄ‚„›ÄƒŸÇ„ Çƒ Ç„ Äˆ¢È‡ Çˆ¡Æ‡£Èˆ£Ê‡¢É‹£É‰£ÉŒ¦ËŒ¤ÉŒ¦Ê¤È‹¦ËŠ¦ËŒ¦Ë¦É¦ËŽ¦Ê¦ËŒ¨ÎªÎŽªÍŽ¨Ì©Ì¨ÍŒ©Ì‘©ÌªÍ‘ªÍŽ¬Ì‘©Í’¬Ð“ªÏ«Î’«Ï¬Ð“®Ï’®Ï”®Ñ”®Ñ”®Ï•®Ð”­Ð—¯Ñ”®Ð–°Ð”¯Ð–¯Ñ—°Ñ˜¯Ð—±Ô—³Ô˜±Ò—²Õ™±Óš²Ñ—±Ôš²Ô–²Ó™²Ô—±Ò™²Ó˜´Òœ²Ö™³Ö˜µÖ—³Õ–³Ôš´Õ™³Ö™´Õš´Ö˜´Ö˜²Õ™´Õ—´Ö˜´×¼Þ>:BSSOghg432[RHLICFA8KGH*%'./+&&!$%%<;811)d[\E@:2..K>:70EGAOC=>9671/:70B:.9/.?>:LC7‚dšrkU;–x|•¾|˜À€—Á|™À}—¿~˜À˜À„›ÁƒœÄ„ÂÅ…œÃƒžÃ…›Ä…žÆƒ›Ä†Ä‡ Æ†Æ‡¡Å‡ É‡ŸÅ‡¡Ç…¡Æ‰£È‰¤ÉŠ¥ËŒ¦Ë‰¦ÉŒ¥ÊŒ¤ÉŒ¥Ê¦ÍŒ§Ê§ÍªÌ§Ì¨Ì‘©Ì¦Ë¨Ì‘¨Í‘«Î’«Ð©Í‘ªÍ‘ªÎªÎ‘­Ï’ªÍ”­Ñ’¬Î¬Í‘­Ï”­Ï“­Ï“®Ð”®Ð”®Ð‘®Ð–¯Ò–®Ï•®Ð—°Ñ–¯Ò—°Ò—°Ñ—±Ó—²Ó—°Ò˜°Ò–¯Ò—²Ò›²Ô•±Ñ™±Ó˜³Õ˜±Ñ—²Ô™²Ô—²Ô˜´Ô˜±Ö›³Ò—³Ó—´Ô™²Õ™±Ó›´Öš³Ô˜´Ô™´Õ˜³Ó›´Õ˜´×š·ÖÄÑ÷766D<>>=:/=<3>92LMKJC1?=>5/&@@=640'<1)2.+C??‚yk“†|«Ž~–À}–¿~–½€šÁ}—¿|˜À€™Ã€›Â€œÄ‚›Ã‚›Ä…œÅƒÅƒœÄ…›Ä…ŸÇ…žÄˆ¡Ç‡ ÆƒŸÅˆ¡Å‡ŸÇ…¡È‰£È‹¢È‰£ÇŒ¦ÈŠ¦Ê¥É‹§ÊŒ¦ÊŒ§ËŠ§Ì¦Ê¨Í§ËŒ§ËŽ¨Ë§ËŒ§Í¨ËªÎrƒ–›•“ŽŽŽkeaXQMGBBQID1/([[X£»‚—¯…º”®Í–­Ñ—®Ñ”­Ñ•­Ð•­Î“­Ï”¯Ð”­Ï•®Ð”¯Ï•±Ò–¯Ð–®Ð–±Ò™°Ð—±Ó˜°Ò™²Ò—°Ñ˜°Ò—°Ñ˜²Ò™±Ó–±Ñš´Ô˜²Ó˜°Ò—³Óš³Ô™³Õ–´Õ›²Ô™³Ô˜´Õš³Ö˜³Ô˜³Ô›´Õ›³Õ•²Òš³Õš´ÖOP\TKGQPR731H<1UPO>:;C?JC=WVS;/4=;2<5*==782.BA9@@;96(<76NE7ž‹x£|”½z–¾}˜À–À˜Á™Â€›Ã€šÃ›ÂœÄƒšÃˆ™ÃƒžÅƒ›Ä…žÇ…ÄDžŸÄƒ Ç†ŸÇ‰žÇ…¡É…¡Æˆ¢É…¡ÈŠ¢Ç‡£É¥Ë‰¢È¦ËŒ¤È¦ÌŽ¦ËŽ¨Í§ÌŽ©Ë¦ÊŽ§Ê¦É‘©Í©Î©Íeqx}ynkd_;85~~vŠ…ƒfd_€}~€|Œ‰Šˆ||[OK;64LFA'*+–›ƒ™®ƒ—µŒ¨Ä–±Ô–¯Ó—¯Ò”­Ð—¯Ò–¯Ñ}—¶š´Õ•²Ô—¯Ò˜±Ó—°Ò”±Ò—±Ò™²Ó˜±Ö—±Ò–±Ô›³Ô—±Õ–²Ò›²Ô˜³Õ–²Ó™²Õ˜°Õ˜³Ô•²Ô—´Ó˜²Õ™³Ö˜³Ô™´Ôš´Ö—´Ô—³Ô¦²ÖA>9HED865CA7KECC?:IIFFC@'%%1//=;8c[[;30IB8MJDGDG[YYA?8G:+:94@<7=6(75*3,.A5*@:2G>4¬­›·ª |“½|•¾z—¾}™Á€—À~—¿›Ã‚šÃ‚›À~›Ã™Á›Â€œÃ„Å…žÂ„ŃžÅƒ Ç‡ŸÅ„ Çƒ¢È†¡Çˆ£Ç† Åˆ¤É‹£ÈŠ¤ÉŠ¤ÉŠ¤Ê‰¦È‰¦ÍŒ¥ÊŒ§Ë§Ê‹¨Ê§ÊŽ¨Ë‹¨Í©Ì¨Ì¨Ë•±Ö e\TIS]\]\<7:elfmjcFB=xtm~}xŠ„}ŽŒ††WUT€~| ¡ tssid_;-/JFAMI@MY`ŠŸ·„˜²†ž»ªÄã•°Ñ™²Õ™°Ó—°Ñ–°Ñ˜±Ò—°Ñ˜¯Ò—±Ðš±Ó˜°Ò—±Ò™²Ó—°Ò™³Ò™³Ô˜±Ò™²Ó˜²Ò™³Ôš³Ö•²Ò—²Ô™³Õ–´Ó˜´Õš³Õ–³Ô›´Õ¤Ãæ?=ASOJ@<>?<9PKHVRK@??I:;502542BB>lkjHE@VUTMFAE?ATNC=87B;*D@DECA<325-0;96,)$=2+F@5G<0®¤Š€’½}”¾|˜¿€™Ã}˜Ã|˜À„›Ä€šÃ…›Á„›ÀćÛÁ‚Å‚›ÃƒžÃ€Å„ŸÇ…ŸÆˆ¡ÇŠŸÆ† Ç…£É‰¡ÇŠ¡Ç‹£Éˆ£ÈУɋ¥Ê¥ÉŽ¦ÊŒ¦ÊŽ§Ì¦Ë‹§ÉŽ¦ÌŒ¥Ê§Ì‹¦ËŽ¦ËŽ©Ì“°ÓJ@<_TMˆŠ‹—š™|}|–˜™VX[tuuqsy\]a7<:HHK\^ZgffZXMec_‡„xed_€y}{vŽŽ‰sus Šƒred<83MG@<82i|†”«y‘°–¬Æ˜²×œ´×™±Ó˜±Ó™²Ó—°Ó˜²Ñ™±Ô˜³Ô˜²Ô˜±Ôš±Ó›´Õ—²Ó™²Ô•²Ò—²Ó™²Ó›´Ö™³Ô˜³Ô™²Ò—²Ô–²ÔXZg^_\IC;;:>20,FC>?:7JH@%";8<@@@^_^KIHGA6haUNOD662GCF8:5SLFC@2<7-36,F=1782>7-H@4=;4¤Ÿ‘—À•Â|•Á—À|˜ÀœÃ™ÁšÂ~›Ã‚›Ä„œÅ€ÅśÃœÄ‚ŸÆ…žÅƒŸÆ†ŸÈ†¡Æ†¢È‰¤Êˆ¢ÈŠ¢ÆŠ¢ÇŒ¥Ëˆ¤Éˆ£ÈŒ¤È¥Ê‹¦Ë§ÌŽ¨ÌŽ¨ÌŒ§ÌŽ¦Ë§Ì¨ÌŽ¨Í§ÊªÎ‘®Ñ<40bYVƒyxÿÿþ»¶²ÿÿþñìïäæß¨Ÿ¡ÅÇĆ„…°±³¦¯µŽ”—ŽŒ‘w|Z`allrIIJWZWjjba_^omfwup‚x}|{‰ˆ‡‘‘’pqn‘Šzlm=54F?8*&&_xo‘ƒ˜³…Ÿ»œ´Ó•´Õ›¶Ù—¯Í–³Ó˜±Ó˜±Ó™±Ò™´Ô˜³Ô›³Ô™´Ö—µÕ—³Õ˜³Õ•³Õš²Ô•³Ô‹§HDE\XY3.0IFAD@>B@>ED@334==9@;:KCD51!HCC=@<;+A901..352aca+(%7/.F=1=<6?><Ž“—4-*LQHLFC57-PB4JF6WYRNJALHB75)4,)93-HA2}‘½–¿|™À~—À~˜¿~˜À}œÃš¿„Ä‚™Â‚šÃ€›ÂƒœÀ›Ã„ňžÅ†œÃ‡ŸÅ‰ Ç‡ŸÇ…¢Èˆ¢ÆŠ¤È†¡Æ‡¡ÉŠ¤Èˆ¤Ë‹¥È‡¤Ê‹¥ÊŒ¤ÈŠ¥ÉŒ¤É¥ËŒ¥È§Ì§ÊŽ§ÊŒ¨ÌªÌ§Ìf~˜TKBoheÅÆÃÑÍÄÐÏɳµ³C@7‡~q?E=¨¬¯¦ žÝÝÛôôôonkމ…Ž‹ŒgcaÅÇÇÿÿÿÚÜØòïëâãàÐÉÊ€†…ž¥¥³µ¸rtxŸœ¤ginloo]bg798_c^_ZVRRFPLGyvpZXR‹‰kmktqnœš“ŽK@@53/D<8+.„œ¬}’«~’¬œ»Ú˜²Ó—³Õš³Ô™²Ó—±Ô‘¯Ï`blDA>PG:443GC=PMG"<6731-fehC@9EAC,.$;9hffu~€mpsk_`HEEF;?KH>>@8^ZY@6/IAKD9.<71JD=MG;B;3}“¿•¿~šÂ€˜À~–¿}—¾›Â˜Á›Ä~›ÁƒšÃ€ÅÄ…ŸÆƒÅ… ÄƒÅ† ÇˆŸÇ‡ È…¢È„¢Èˆ¡Å‰¢Ç‡¡Ç‰¤Ë‰¤Éˆ£Ç‹¥Ê‰£ÉŠ¤ÈŒ¦Ë¦Ì§Ê¥ÉªÌŒ¦ÈŽ¨ÍŽ§Ê§É§Ëk‚––ŠˆqpoTWUtsr‚€y;.(<10?1(F33LF@¤££°¯­þþüfaY?;2id[]XM…€tœ ÊËÆÈÆÆÿÿÿвµ°‹‹‰ ›š½¾½ØÖÒïñîgcaІ…a^Yµ¹´ÿÿÿÿý÷ÔÐʹ·¹¹»¾¹µºŠ†‰¦¤§JSXJMOFHJ574RRRRRJVWT[WWkifME@¤Ãä—´Ó˜±Ó—³Ô–²Ó–±Ò¢¿æ112LEGMHG;86.)*3.,<>=<54<<6OOG;/HE:ckk-(( 9.-9-+:73NC:RK@LF>@;5MDA,()641+))F;4H==|”¿x–Á˜Ã~—Àƒ™Ã}˜Ã€–¿€™Á}›Â‚šÃƒšÂÄ‚›Ã…›ÄƒÁ„œÅ…ńŃ Çˆ Å‰ È†žÅ† Ç‡¡Èˆ¤É‡¤È‹£ÈŠ£ËŠ¤É¦Ì‰¥ÊŒ¤É¥ÊŠ¤ÉŒ¥ÊŠ¦ÍŽ¨Ì¦ÉŽ¦ÊŽ§Ê¦Ë€Ÿxo™•Ÿd_]v|xÍËÌ%<-)<.+?1)<*&}|b\V€{v:)!E8/E4-F4*OGB¥¥¤‡ƒ€Çƺ_YWGB:xncVRK«§£Žùùõ«©§êëã°º·äÚÙpxxnjg÷õïþûü¯´®PMJad\ÄÅÀüþõÿöò°°²ˆ†‰‹‘ÆÅÊz{|[Zd>BCGHIP^g¶Ø–°Ó˜±Ò—±Óš±Óš¶Õ•±ÒYY`E?9DDA;89%#%>:9ppq-*%20-/,-E976;8~~ƒ%" #! ""E51D=>YJJUTO`^[3.(31-554<<5FC/(A/'†y`d\¼º»ÁÁ½@71mgYOGESQCÎÈÊŠ‰Š«©¨ÉÉÉæáÖ¼²´ƒ‡†tkgÑÓÏûõ󾾺Žw}xÚÙÏýüöÕÕÔÄÁÄ}Œ¨˜²Ò—°Ó™²Ó™°Ó—±Ò•°Ò˜±Ò`cxDEDVQM&('-*,CA@RQS0/,B<:5.*?;ME@6*!yqk¤œ“މ1%!D4*<1-C4*9,(WNG”‘jge5*';.*B1*?-)-ÎÎÎ]^\éìë%@.)>/'PD<&/*éäåÌÎŇƒ€QJ=oeY'%¥¨§xyzõô툇‡{x«¥¨aih_^YÅÆÂÝÜØ¸ÆÚš²Ó—²Õ™¯Ò˜³Ô•±Ô—°Ó–²Ò£¾ã@>5F;-;.*dWSkh]ÔÓÐ::72.ccceag#  TLILIEPKHJB?9453.1GFC9:0E=2>84@;6€“¼•Â~™Â}”¿€—Á€šÃ}™Á}™Ãƒ—ƒ™Â‚šÀ„šÄƒœÅ„œÄƒœÆ… Ç…ƒŸÆ‚Å…žÆ‡žÇ… Ç‡ Æ‡¢Æ‡¡Èˆ¢Åˆ£È‰¥ÊŠ¡ÈŒ¥ÉŠ¢È‹¤ÊŒ¤È‰¥ÊŒ¥Ê¦Ê§Ë¥ÊŽ¦Ë‹¦É¥É¦Ê¨Í‹‡„lgfÝÚÒÏÏÉ445555@AEWWVNJH‹€pqka»»ªe`]pg_d\OZJH?5'„|s§§¥ààßD<:LD7JB9G80,d]W¬¨¢›”‘3$8,"6*!4&#!Œ‹|xzuw+;+$0#!+ lol¬¨£ÿÿÿ<+,8-)3'$pi\‚‚wÊÇÊÁµ¤—±Ó™±Ò˜±Ñ™²Ô—²Ò—²Ò–±Ò–°ÐXWm/,.?:35;5H@>$&%:2/>74<82TLGOaaXVX576" @C@TTQQQN50.1,,92"C;*==8;56==4G@:z’»}—Â{—€™Â˜Á{™À›Ä}›Ã€œÄ~›Â€›Â€™ÁœÄƒÅƒŸÅœÃƒ¡È†žÅˆŸÇ…žÇ‡¡Ç†žÆˆ¡Æ‰¡Çˆ¡ÆŠ£Æ‡£ÈФɋ¢ÆŠ¤ÉŒ£Ç‹¤ÉŒ¥ÉŒ£ÈŒ¥ÆŒ¤ÉŒ¥Ç¦ËŒ§É¦ÉŽ¨Ë¨Ì«Ï|ymdaðíâÜÚÔŽŒŒ‹‹‡ˆDC@„xh‘Š‚ª©™,-/066?CC_^\A?9–Š{³«¥´°¯qojrlhl`ZZSK:( }q{yt³°¯I>3I=6I;59,'+#{wjŸšš±­¢+6)%1"."!mig~yx|wQBA5%&2("B8.iid‘™œ®±¯¯¾×˜±Ôš±Ò–±Ó”±Ñ—¯Ò˜°Ô•±Ó³Êî$!#>=<4;;F=>3/)B<67))@78@540/,:4..)*!!9:=Z]bD8<:72">76812GC>7T[\dfdecW‡yfa`U¨¤˜nlgmdZaUGQD>SLI…{lÚÝ܉~C;5F<44&%"gb\c[O¡”‘ojd/&@0-MF>C:/2;6F;;>9; # [[d<73550KF===/77484/B<4?><|“¾~•¾˜Á{™À{—€—Á}›Á}˜¿ƒ™Á™Àš¿ƒ›Ã„œÃƒÅ…ƃžÂ„Â…Ä„ŸÆ‡ŸÆˆŸÇ†ŸÄ‡žÆ‡¢Åˆ¤Çˆ¡È‰¢ÆŠ¢Ç‰£ÈŒ¤É‹£È‹¥É‹¤ÇŒ£ÆŒ¤Ç‹¦ÉŒ¥É‹¤È‹§ÉŒ§ÉŽ¨Ë¨Ë‘­Ðˆ‡…ŒŠÒÐËÙÖÒ‰‹‘‘‰ˆ‰oon?C@zs®°¡qmfˆ††zzz€€€~~~th_xnãá᎑”––›š™|zw^XR‚wftndæçØ!!595W[\UPFnj]‚rªª¦©¤œjf^[SKYQB}vl‚|péëî7-$83,1'"PH>`^Zilhš˜«»–°Ó—±Ñ˜¯Ñ˜±Ó•±Ó–°Ó™²Ò—¯Ò[_oQLL772?>B0,+;95363IC>QHF0*'FB>876330&:0'('"51370$/+2.)B>88<1;:7@=)D65}”À—Á|“½•Á|˜Á€›Ãƒ™Â˜Á}˜ÂšÃƒ™ÃšÄšÂ‚œÄ…šÂ„›Ä†žÇ†ŸÅ†žÇ… Æ†ŸÆ‡ŸÅˆ¢È†žÃˆžÅˆ£ÉˆŸÆ‡¢ÆŠ¡Ç‰¢È‰¢ÇŽ¤ÉŠ¤È‹£Ç‹£ÇŒ¦ÉŠ¥Ç¥ÊŽ¥È¤Él‚›AIWFT^tuyfa`ÖÓÌâÞÑv€}†‹‰wxyƒ„‚ˆ†‚988„‚~ïòä€vRKFFGA™˜™ˆ‡„‰†ƒzzzÿÿüЉ‹””•–•—€‚xqLI8~wkÂô‘’”‘ŽŽ›˜—MKIƒyi‰‚vZZSœ——096dffOMMaaX†{iwl[¿Å»_ZPXPJG>8OI?onfmlhÊÉȹ¼Ê—¯Ð˜±Ó”±Ñ”±Ó”¯Ð—±Ó“°Ð•±Ñ¾Îõ@<=>?=534.;;9888-"!;NJU@9 <:886,6+)63/;9;985HG8EB6SNF~’¼~–¿z•À—À€˜Á|•¾~™Ã›Â€›Á€›Â€œÄœÁœÃ‚œÂ„žÆ…ÜÄÄ…ŸÄ…žÆ‡ŸÆ† Æ†žÂˆ ÆˆŸÅ‡¢Ç† ÆŠ¡Æˆ¡ÅŠ£Éˆ¡Ç‰£Æ‰£ÆŠ¢ÇŒ£Æ‰£Å§Ê‹¤ÈŽ¦ÇŒ¥Ég{ˆ""%?<={{~UQOÿÿÿáÙÑifb‹qopHHGxxvKHAtqiÖØÊvocHE5”ކzvnuwx˜™˜•–—¸¹¶›š›ššuus585†ySQHŠ„€·»«ptv•””‰‰Šƒƒ‚f_RreUYZYÿÿÿŒ’‘oli_XN„yf€ui•ž“GEE\_]XYOVVSKE<^XLš“—¼ÈÜ–°Ð—°Ò—±Ó–±Ô–±Ó˜±Ò•±Ð–±ÏœºÜFEJA@>102;;;=4*<77763:61ME;B=:GGB=951-*…p\'(!//-400<:6>94BA7666GI?C>;EB?~”¿{•¿~”Á~šÃ~—Ã|™Â„šÂ›Ã€˜Á˜ÄšÂ€œÃ‡›Ç‚›ÄŃœÄ†Ä…œÃ†Æ„žÇ…ŸÄˆŸÅ‹¢È…ŸÅˆ Æˆ¢Ç‰¡Äˆ ÆŠ¢ÆŠ¢ÆŒ¥ÈŠ¢ÉŒ¢ÇŠ£ÆŽ£Ç‹£È’§Î©Í‘ªÑ˜°Ñkˆ001_XX``a˜•—ÿÿøçàÛöôòîë祦§rrtSTS”ŠLMDßáÕ€yo--.~sgMOMW[S:>JHGXYTTN=46/&&$l]K&&+*(20)@<8C=9:82=}”¿~˜À~˜Á}–Á€–Á~šÄ™Á…›Ä—‚›ÄœÃƒšÃ…Å„šÃ…›Äƒ ÇƒŸÆ… Æ…ŸÂ‡ Å†žÄˆ Å† Äˆ Åˆ Æ‡ Æ‰¢Å‹¡Å‡¡Ç‹¥Ì¦Î‹¥É¥¿q—yˆ}…‹TZ^BA=>72MGCd[[YWQLE>HFBIDBÿÿÿß×Ñ÷ôïËÉé©¢©§§yyzqom„‡‰ÞßЖ—™sm`[[XOPLŽŒŠ¤¤£èá×··¸öòî€xbbaB@<Š„v&)$‘†}adY²¯¦…‚€yzz|x|yŸ ¢¦¦ª“”{€xj+/+ƒygC>8mqohjg¸¸©w{~oopXSLD@7•Ÿ¨’‘Žÿÿÿš°Ñ—´Ò–±Ó™±Ó˜¯Ñ–°Ð—°Ñ”°Ñ”°Ñ¿Îó,,,723@BC=76?<152/WRR_Y^;76RJ7504%&$)'(&'$1+(544;>8892C?4@<('%3.,C>;MIJWZZID==50?53D?B##%71--+(,(%5426385-,><:A>5;74531FD@–Á|˜Â}—Â|—¿}—À}˜À˜Â™Â–Á‚™ÁšÃ€œÁ€›Â…¢Ë‰£Ì‰¡Ìˆœ¿o… o~—w…›mx}567:41B=4SPL`^[YVWpqr‡…‚Šˆ†……ƒlkhhgf‡†‡fec}{zyxoifdlhfAA;JGBlgajf\~yvlgd‘Œqom£Ÿ†„€š•’rolÎÎÉÁÀ¾ƒƒ€ÿÿÿ€tq…‚€qohÉÅ¿ÇÂÅÈ¿¹”ŒßßÝþºspjŽ‘jppŒŽ’€~€åãÔvtu£¤©¥¦¨ðê襧ŸØÑͧ¤£ZVN__S€{q65-€|w…‚¯µ£Zbdnsx]`_^``Ïʾ•”™çæà²­«•±Ó˜¯Ðš±Ò–±Ð–°Ñ•°Ð—°Ñ”¯Ñ•­ÎST[;:<8331,'.' D=<894IDIHGF<4-C@=100%$#60//.,)(&1/-56.5+(<>8JGDB;;:5.DCB~”¾{”¿~˜Ä€—À~˜Âz—ăœÉ„¢Ð…ŸÌ†›·Vj‚gwbk‡89:2,&HD@XSPTWRXSS‡†ƒŽŒŽ žŸž’ŒŽ}ywqplЇˆ‰‡ƒ‘ŽŒ†€xssXVP~}{zyyqqt‚„†‘“~€ˆˆŠ’‘ŠŽ‘€‚psoomipnlrphsplif_trh•‘Š‹‡ZVR~xqpjZUSsrm‹…‘‹””’›–”»¼¼¼¼··´±ÂÔÚÚ׃{xpoj×ÕŬ¨¥Ÿšœ–¦¤¤ÍËÉâáÛ–™–ÿÿÿpol}~~d\_nok‡x150hpk»¹´_^aa]_U\\}voš˜ÑÕÐñêã«Ï•°Ñ—²Ñ–®Ð–°Ñ—°Ï“®Ñ–¯Ð—®Ðœ¤Á787@=7:8732,>:8GFLD;0E69<41@;?0,'-*)//*)'%))%64*6011/->:4=:6HB;=:8HB:Zl‰^nˆ\j‹dsar‹x€’KNM,&%3,)67/^^[RPJkhd}}„ƒ‚ˆ‡„bb_|uwvwtRQPsig‹prkvus[ZQsnn[YWxxtƒy{x„††ƒ„†€€ƒ‹•~~…}}‚ˆŠlqqˆŠŠ²±³”–—‘’’‘–®®­ÒÐÉÅÁ¿¾½¹¿¾¸ÄÅĤ¡¡lpr‚€‚ƒ…edejkmffhplhpmjpkdqofiaZ…€u~zu.)!ŒŠ‡‚~w›™•JG@ligcd`ÂÄÀ“•“¾¹·˜•’°®«ÃÂÅ  êä䪢œÁ¿½dde–™˜„„†„€LMJole–›”úüñ¨¨®–—™„ØÖ̯ºØ˜—“ÝßÙž­Î™±Ó—¯Ð˜¯Ô—°Ð—±Ò–±Ó”®Ï”¯ÑºáA=>967)))-**/+*B<=860B<6GA5@=>)&)0++'&'1-+,)).).0.-1./:65C?;C=4B>8FB9KHGJFHSQSXOO\SUmccojk{|w‘„…‡vjm…ƒ€“—œ…„„‰‚{orqkge~yrZXU‚€zusr{{~|~€}€‰ŠŽ–oottu{“—š}|‚ÖÔÕÒÕÙ±°´RRMÍÉÈÔÏËïîèÂÀ»žž—qqknidÚ×ÒÙÖÎû÷óÁÂÁéèæßÛÚàßÚÿÿÿÿÿÿûøðÕÔËÿÿÿðí蹸·»½º½¼¹¨¤ ¦¥ª•šœ††tvwNOOgb_UQKmg`NHDpni†wzrge]wtoKG?b^XihfŒ‰bbY¡¢ËÊÇ››ÜÕÔrplŸ¢™’’ÄÅÀxncÖÚÇ¥¦¥™˜œ“ËÈÂÿýïÌÓÜÎËǯÄÛ–¯Ï—°Ð•¯Ð–®Ð–°Ò•°Ð–®Ð”¯Ò®ÐZ\f521722+)%DFC0/0:95?=8FB?40,&%$210(!"-+& "*)'.)+@=>><><<6RJD>8070)—šœrooYWZg_^nhc|}}~z`ifMKMŠˆ†nf]ˆˆƒpkgyvsspsorrurv‡‹Ž”[biž¡¦~…‰šž¡~‹‰º¿Á–˜“’”“ìíçüüùsngÀÀÅ‹Œ‚ÝÞÙàà×ÆÈÆóóòèèä}|±²²‹±´¸@@EŒˆÐÐÎ×ÕÓåäáãàÜwtlzyofe\½»¶ÇÆÁóñìÕÓÏëêåòòòÚ×ÒãåÝñðíûúóÒÔÐø÷ñùõñ¬ª¬ÔÕÒº½¼¡Ÿƒ……ˆŒ’__dTVVe`dYVSSLEb`aLH9`XS†ƒz~xthh^Žˆ„fbY"ŽŠ†/-)ÀÁÀPOLURQª¨¢¦ŸœéçáÔÐʲ²µæåâÿÿÿ™´ØiyŽq‚—fw““±Ò¡¼Þ“¯Ò–­Ð”®Îsu8033+)NFK?:<13,0)*HBDF=5('(433)''0..+''10-./(2/-74->96A>;NJCYRK43/{z„„…xut]ZUd_[cYYtph|xzfdekhg€€y„’˜][j†Š‹‘މ©¨®š¡ÓÎÑãâßéãÜÎÏÏ¢¡ ¶³®ÞÙØÓ×Õö÷ôzywssq´µ²khh¿º¾ÿÿÿèéç™—”óóïIKETOLš~<;:«©¬‰‡ƒïíéîí쮬¬ØÖÒPTVÐÒÒx{|dc^ºº·úú÷ÿþùòòñ£Ÿœ]\X|woއÇĵ³¯ÝÝÛÿÿÿ¹¸µÇÉÆÁÀ¼ÛØÔìé⬭¤ÿÿþýºœœ™ËÊË šŸ¢ |€{{cadJMR@B?MNMpmc`[PJID=8-c_Rid[xtqa]YxtoXVN–’‰ooo_]\§¡Ÿkg_VPOIHE?6+n““±Íœ»Ýºá322983;<9*+(52'885-)&()),)*)('667.,*0,,<27571430@<9A=4:<:!FC?d`^dhjcbgtszonq‹“hsvˆ–Ž“”]WbAFH§«®ÃÎØ‰„ÚÜØñïùØÍºÂÅ®¨¦Ÿôïëµ¶²ec_zyu~||·°§­«¥\ZWÐÌÅ”’õõô\_`smfrrostu¢Ÿèåá÷õðBDB_UNXIAM>4[MJ‰€qso˜––ÐÐǺº³²­¨10+‘‚k" ÙÖÓ‡‡ˆØÖÓ÷øô¥££ÆÇÆuvr¹²ª[^_›™—•”ÙÖÕ»º´ÔÒÇ€{w€zv[XSІ{àÞÜÝÜÙèèäêê祧¢ÃǾÃÅÄÿÿÿÞÜ×ôõöÿûðÓÎÍÆÈËÁ¿ÁÇÊˈˆ†vu|…ŒijlMOL__^`[V]YW1*+::363,MIDC@=lfb`\U~}ˆ…„`_YD94 )]s…HEQLLL--,200>3)764**, '&'..,++**& %##CDD880E;;1/.7:4D86KD;LE:kfo“Ž¢Œ˜©qlu©¬»ˆ„ÎÊÖ×ÁËæåèÁÀÀÎÎ϶½Å~{xvoq¢¢§ÿÿÿñýþplmýýÿ‡‚…ÊËÎRRS™˜v~y”’”©§¥õô÷ÿÿþŒŒ€|zB<=ukeke]dc`ÜÚÚnno¹¶­þÿÿ>3.QGDSE?VI@RA>UF>¡˜‘‘’çâßÎÏÐ_UFG5-7+(C5(€xo±®«xwyþýúÿÿþ‹†~ULEzrb&”Ž„¾¿Á²²¯ÿÿÿ~}w³²¬ØÕÎ}xrv{ztph¿¾¹ÙÖÓÆÅÆª¨£OIHog_B=.½¿¸Â¿¶ÜÞÛËǽðòêµ³ª—‘îîëÿÿÿýûöÿÿûÿþú§¦£ÃÃÄ­¬¯·º¸ kmnlmm;>@OPMUPLMMH]^T^Y[GJ>wroD@?K@<(#$,-*C@>10.31/.**)%&)((''%367.' ,,*%$!41.9<:668++'@?9794>77D?;ÏÑÊëêçÿùõÆÆÅÅÅÂÑÎζ²²ÚÔÌÔÚØµ»»ŽŽVaX³¸½‹–fgd«§¦÷ùüÛÕÍŽŠýþúqkiYWRˆƒy@?;îìëˆŽŽ—•™óòíÇÊÌ=63SDBPE3K?8A7/JD>ñóïÍÎË‹†|>62I?4D5-L;0B3%hc\ÞÞÝ×ÔÍ££œD7/@2*E4,L:0‚umzxpƒƒƒ×ÕÒרÔ51*^QM>5-KB9ÝÛÚvtrÕÔÔÆÁ½çèç=?5¬¥¡[Z\‰‡Š†yÿþüììéâÝÙ\XRid\JF;~yÚÖÑ·³²zzwûøñ¸¸µ¥§¢ÊÎÌèèèÿÿÿº¿Å˜Ÿ¡‡sw}lmnknrRRMVOJSPE^\]VXPnlfspj643## ,*(%#"212.--)*(+$$,0,*'&**'.-/401<75565A<7;95==ypnŒŠ¤¡¤çååHMIG@=J>>MA>NBGA<ÿÿÿÅÁÀïòîB=8UMHQHFHB:J=8K;3YTRtqmŽˆƒ`\O:/$=/(;.+B3):3-NF@·µ´ÿÿÿÏÊÂ1$<-*C:/A1%+!YTMèêêVQGÆÀ¾3&#;-$:+&=1'peWOPLÇÆÄÞÜש£ @7/ZPFNJ>Ÿ™ˆ›œ¢››—õóñ¼¼·ÆÆ¸³±¦—ˆwxzNMGâåãÿÿü¾½EDCƒ~u=:4nnh÷øô­«ŸÖÑÑÙØÒzumggh†’jvV[]nuyNLMIKBWN?A;79;3cbaF@=764$"(%&+))% $"!,+)334++&DD9MNG52.=9-:86FDCOKHÒÍÑmih¤œŽœŸPMQoqlåÞãó÷öÅÉÃhzs‚‚}cc\]VPsqk{uª²±…„èææïîî3,'F>:NA74SGDJE>KD?OD@QEB>88ÅÄÁÝÞÚÉÃÀIC=`ZWUTNOLCOJFG@>VIF§¥¢˜˜”‹‡‚E=5J@7K>2?5)>0# ¥¤ žœ—ˆƒ&:,!G<5:-&6("6/*ÏÍËÿÿÿÿÿÿ4$!4% 2'"žž–†„‚ÿÿÿ 8)%6'.!iaUPQM‘ŒÎËÇ¿½µUH=4/'SM=ñóóYYRþû÷ççå–‘’;5)plf™œ¡JHEù÷÷ÿÿÿjc_’’pmg>=:çåÝÔÓÑŠfsvabhkkpUSNC<4F77NJJEHE#"",,*!!!--.+,,.+,752' #02/964++*@>;HFG___@;8xqvwr‡~˜£±œŒ”ÒËÈôòö::::/$I=5J;9P@?N;B•ޤ§ÃÀ¾˜”‘D>:J>AF<9K>=F=8@97ÿÿÿêèéÿÿÿ662NHFRMK^TU[VRYWNRDB‘ŒxtuŸ™lhdrqqnjfolijbW_VPdXS’‹…~~yƒ}rXUJWQBQLFRI=LD7%“Ь­©´±®:0&GB7E<1?1)1'!(—”g`_‹†{(5.,."," $DA;¹¸ºÄÄÁöðî%7& 'MF5îèä­ª¦ÍËÌ++ *!5,(%%˜•”¡¡œ¬£¤70&MD<<82^TDÀÀÀŒ[UPÜÛÓ:9*E>8iwu{snŸŸœáÝÜÊɾœ¡z‹qww[ZbSTROPH;91B<>PTIGDF>:5)***+++*(11-/*%46-*-*LF>B>;CA=_W[L;2E0'A/%YKGld^È»¹÷ÿÿÕÙÚ4+&G=3K<7J@4G91LD7iPUûÿÿâàÞéêçD;MHDPILNGDE=:¼¸¸xqr‰ˆ†=A?\ZY[\UWUSJHEFDGB<9Œˆééê{uqrnsrsssruut}|xtoo€xoœ–‘ØØÖqmjpmhtrksmihd]WPFmf[zslŽ…jec]XQb\UVOISK@=4*?56qi`®ª¨ÒÊÃ.*&?5,=.(TMI}wrgd]xu) -#"+! ~}ÔÑÑ€}tŠ‚€, *%ðñéìêéÄ¿ºN;30","!@<0‘ޏ¸¶?41) +$#h^S‰†HKJ¬ª¤ÿýû=>;PIAZYSw|}-;@fkpYX[EC:?:8:7./3*+(*//+*))))*HID4)%(%&962953FC?;901(!8.*B1.G9.H93?53›¨­ôðó¾ÅÀ2+1@3-C<4H?@G=AA;8íàåÏËȸ´´ìîîQNG\TJ`VXYTRYSTVOP•Œ‘˜–™Žˆ‹fecomlussvuuzwxvqqtpo„Ž‹‹ƒƒ~}€xxywwxjjk\\_ƒz´¬¤ÿÿÿHHHssuwxvyxvlkgjfepicˆv¾¼¾_\X{wuzwrtqlie`bYSSICncZ„tsphTKGZWNOG;GA=&VVQ‚znÌÊÅlge:4-6-'6.)qh_midmieRKE+$ 0!# ppme^W†€p;,,/$" EC;¬¦¢<<2ÞâßH45,$873™œ•ÆÏÌURShnsmop55:bg_rppvs~lgl…‡‹kpx^ddPLK62+(#,(%10/<8263.20)A83200;=7A>3)?+*?1&>.+?2&ÿÿÿéíæùúøÓÕ×>1.EA?HDCQJGMJCLFH¦’ƒ…„‚~§—•a_]njfqkimjkjgfjddŠ„}ØÖÕ€~zxxx~ˆˆ‡„„…‚€|}{{zvœŽ†íæá—†™˜˜””“ŽŽ‡…„yzv†u­¥—ÿÿÿƒƒ‚}|}{|z{z{ppmztg‰oÊÅÊQPO^`]kjjiggYVTxrg‚ugÓÑι·±rqnyurkih\YO9/+‰}smfU­¬©QHAXSKNHFJB4/(#*'$qj]€|{b[UKA9?80=3.ohfmjaa`]e^X*!* mmkK??mjg1'& —£ „ÔÒÈ-'(61-&'"nnhOURSVY[^ejor;F>FMR*8;jkqCFH)()300..//.+DD61/(763310;<;9*#4*$5*.8.1?//lqsÌÇʬ¥ªÇ×áD@CRONOPIQRPRNPSOM]UQ¹®¯ª¥Ÿœvssyvx}|}€€€}{|ttw‹…¬ª°žž›z||‰ˆ‰ˆ‰‹‹Š‹„„„†„†|zy–‚ÖÍÈŒ†zž Ÿž š›ž’”“••–„‚ƒvm‡€ÿÿÿŠ‰ŠŸš›š––˜Ž~wo`zqfÒÏÐÅÀ½ššš™˜™‹opqplnrdqaŠuznCDC545?>=HGGX\Y€pcxoc­žž}xxssqrqlha`]VPGEArgQ~yuoke\YN_ZOE@:D;2/-$sh_TJ@_TNOI?>51A3&qj\¡ƒwUPL(())§ ¡ÄËÁ YRL3@78HA?HG8FA;HCBØÔÛ–™¼²±µÄÀWWU__\eddbb_TSRQOPibb¿»»‚†…¥™•nlotuuyz{zyz„„…||}Œƒ{ðìæª¨œ…†‰—–™˜—™”“•‘‰‰Š€~Šƒwëáß|ys›š› ¡¢››››››•”“‘‘ŽŒ›—ÿÿÿ’’¡¡¢ŸŸ£–—™“„ƒ{vnf®ª«Â¿»““”“””‰‰‰‚‚€i_R`TB½¸µwx––˜™š™‘’“‰‰‡g`_uiXqcQ›•™–Œc`\SQR;<;230zqf‘„rie[ÿÿÿQQQggdXVRYWSJJBueY^UAÍÍË_YV\YXKFA0'%STT[QGlk_–>83:3/! bb`‹’¥§¡üññOKJ€ˆ„‹qpq!"QPU24NMJ_feC9<4,.54271/"! ('#><7\^_TQL__]cb`b^]\ZWvkaº´µ{soÓÌÎqqr{{}~€‚‚ƒ|w{~{|tuo ›Ÿ¿¾½—€‚„†‡ŠŒŒŒ‚ƒƒŠ‰Š‚‚šŒˆÎÌÉ’…tuy~}‚twx‚ƒ†}}€||{~~ˆ‚åÝÖ‹‘˜—šŸ¡¡£¡ £œœš››™—–¦££ÿÿÿ™™š¨¨ª¥¥¥¦¦¦¡¡¡š›š???zyw²¬­—“Œ‡‚’€ƒŒŒŒ‰‰‰roo{tpÃÀ»Ðʼ’’“š›œ‹~}~kg`l`NrgQvu¤  “’“‡‡…ˆ‡†lik}q^‰ykXM<¨¤žTTVmnjebcRPRjbU}r\eXL§¤¡b^_de_VRO;42leZuiSi`[øøð[XVUSR53,!+)cZTzvn€zk*.*Xgk{~_Ya/*(EHIJLM^ls%&"++*432  -+,BE>ZXXihjpppstuttrkkmk\`„„š›˜±£ yzx‚…‰ŠŠ‡†ˆ‡……„„…}{}½µ‘”‡¡˜’–•——˜œ––˜““’…„„„€zôóïkhb¡¢£Ÿ¡£§¨©šœŸ’““’“ŒŒŒ•“•ÞÕÎÙ×ÏÁ¿¹¢¡ ¡£“”•ŽŽ†ˆ‰‹‹—––ÿÿÿ††‹¡¢£žŸœŸž‘‘“––•rqqWUN§¥  ‘„wlmi^HJI’‘Ž‚ƒ‚ŠŠ†²«¡ÝØÌ•”—œ›žŸ pokec`RNL[b]Šƒ€ œš“••——–…ƒe`Uk_RonkÉù¢¢ƒ„„lmm]P|z“– œmmm[YRldSmfYZWPËÎË|x:==&')`]Xo_KooccZV,%&"]a`~„H]TI/,&¿¶³VVT?@>)$ PVS@@7LKM337  NJK"-.=97%''2-*1.%"$PWP_ZZ~}……ˆrrvnmsjhlžŒŽÕÏÏj\SrlgŽ“”•š——œ˜—‘—wuyÁ¸¸’™™œšš–˜™šž˜˜ž’~YST±®¨ZWVåâßùõòçé䧦ª¤¥©£¢¦¤¤¨«©¬Ä¼¹üû÷öôóòñíú÷ò‘’“™šœ  ¡¹¸¸ÚÙÒÿþÿðíéøöòõôïÜÜÚ­®²^^[wuv[XQ˜”ñêäpkclh`†~IIDNPNŒŽÍÌȽ¶ÿûïÙÕÉRSSvwv<=:lgb12-š“ˆa\VÅ¿½yrjœ›]_`]^_fiklnlrsz€~íãØ>@9dYR0+"meZ41-geUXUVyuq•™œ”•—ƒ„…~ˆˆ‡€zyxìíá‚€‚xz|\RKZMESNAolkosmspoWWXGBAYSMIIAYYW+%"!! CLMOOS(&!4/'+,,OSX("!‡†Ž€‚…ttzopnûñôÝÜÕnrl{|}ˆŠ‰Š‹‹‹”‰‰Œ…ƒ†„ƒ„vihÍö©ªßÙ×ÔÓÊ˜šœ“”˜‘’”ˆ†‡}|z…y~ÿÿÿQSR½º³‹Š‡ÿÿÿ¡¢¤¢£¦ž¤ ¡¦ÃÄÁÑÇ¿ýúù÷öóõôðú÷ó´´²¯°±¯®²½¹¹ÔÒÎÿÿÿóóîüúùõóïþþý¢¥«XXW™š™¦§¥îã܉‹Šˆ‡‚kjd20.¤¡ž¢£¦èæâ¨¢–ÿÿÿæâÞÿÿû_a`§£ >=6vqj{uh'$Àº²§œ˜>A<;JLL@@@Ÿ˜•LOP?ACVRN?432/+ddePNUHGG<>?')(1/.0/*=9>++*V\\UST-.-63/Œ†‹yˆŠŠ”˜Ÿ¨¬LKJ±³·ÇÆÇldhice¡ Ÿ³­´ÃÄÉàÜàÎÑÒââßÉÇÆ½¹º´±³êåèüýþñóîäéæŒ“—¡£¤WRSøóðÛÙÖii_ú÷õåäÛÝÙÔŒŽŽ†‡ˆŽŽ¥¢ ÌƼðìæéåßðìçêæáÏÎË¡ ¢ Ÿ¥’’ùôﺺ´ÿÿÿóòìðíêñíéÖÕÔ«­°_\X¡—`\JaXSîæàjea]OFrrp““’{o_‰‹¿½¸‡|wulhÔÌÑ…{–Œ~uwy š—™—¯¯²tuv¢ž•Š€u››œ¼º¼š›¡šœ¢×ÖÒÿüøÿÿÿ‡sÃÅ¿uvwrqnIOPzoa/,* nonÁÀ¹´²´‘•‘‘•µ¼¹óîæéç៞ÿÿÿ……‹††ŠghjmopqstRPM«¥ cdbNPP5/,**#04.;C>}|‚.0/**+>C?22/566OLL('$RY\SUV/.*((&150‰Š‚}ux‰d]WRQNsms@78ŒŠˆ\OO•’Ї‡}‚}}ƒ‚‘ghkTNT~z|‡…‡‹Œ«¥©uot™›™««¨‰…„ˆ„ŠÄÁÅëìéõñô²±¯ÿÿþÝÜØ›š›ÅÄÊîíïÿÿÿüúðóîèøöóûùöÿÿûáßÜ¡¡¤¤¤¨°¯°÷õïÆÂ»ÊžúöõéâØèäÝ–™•˜—™ƒ†ðìã=:2uokÛÑÎŽ€{„q–”‘‡x‚zk“Œ}ni–‡uvu›ž™š›•——XWR–“‘njbµ®«ÿÿÿ—™œŸ¢âæäôòîïìæ”Œñòñ•”—€„koqvzzxx|w}€LJI}}}kjmBC;4/,:=9HNN`^`[^`TSS:;9$" #"#CCG(*'Y]dPJJ<;9kwˆp}‚zy‚v|ˆ}~„‰‹Žgpu‚€rutosndgh{zukhdpprebe}~|xx~}zkkjwutxuu—˜—nli——–{yxMFG‰‡„Žˆ†ƒ„‚~yuwsr——–||zsrt§§§žŸ Ãĺº¼RKG­«­½¼ºèêæŽŠ‹ÿÿÿÿÿþÑÐÏ´°²ÓÑÍðëíÌËÉÛÛÛ§ª§ÿÿÿtrtïï鋇;=@c[WppqYZX‡ˆˆóîêæãÞŠ…wúõêÑÑΗ˜–š‘…@CG<83ÿÿÿ'),645Ź´i`Ljnn—šŸ…‰ÂÁÀÔÔÎûøöÆÁ»´¦ž=3#‚eYX`UCa_[³®¢rdb­¬§¦£›–˜™‘”˜ÙÝÜëæáñíçÿü÷ÈÊʬ­²Ž’uwx{{€pqtŠ‹LKI‹‹mqr,**983=:9TUWuz\^`XZ^525!"#66>&&%W`c?@<>83†}{ÖÓÕ¡£§¾ÁÉ—“š¶»Â²µ½¼ÃÇž¤¦‹‘˜“˜¢‡ˆ”{…•‚ƒŒ}Švy~suw|€‚yzvx{rrsyxzz||Šˆ…€~|khf}z\YUyvtmee}|xž˜–b_\nkiˆ‰†lljgde}|v’‘ŽijhSRO……‚‹‹qtlMLJ~|w›—–ppk•’z|yˆ‰„ebc™—™srn°¬¤‰ƒfb_ïîê°®¥ÆÃºwvt±­£¬©«ÖÒÏÿüûôöñýýø˜š•¨¨¯--2979>1(79B^YUàáÔ†‚00*ttxÁ¾¯ëåàäàצ¥ ÿÿû//4gd_XZW(,.“†}wnXNK?—‘ŒÒÐÌ”“—‹ŒÏÑËêèâùõîÿÿþ¦¤¢ÔÌË””–†‡‹†„‡„‚„ˆPLN›šŒ~}€USRVYYV`\‚ƒˆY]`SUU!!& +,.46;C@<.1'++* EA?ÉÊÍĽÄÔäáäáܲ¶µìöø¨Ÿ›—–’ÜÚÙÓÌΨ¥›èíê¶­­ÿÿÿÜ×Ñæè匋ÍÊ˹¾º†ŽÈÉÊÓÒÖ­µ¸ž ¢Š’ˆ˜|€Ššž¤v†|†~€ˆ…‰Ž‡}~€……†„ƒ‚‚ƒ|{ysux…†xvxxvsUTRomj†„|]WV—‘okgŒˆ‚vomrmh##jedurdqkg‚x‰†‚’”’xtonmfhgbidb——”fb\ÎÌÏyuq]YW^][èççUSEIDE˜—–…ƒ€éæàÀ½¸óïñµµ¸mgZÙÖÝÚÚÙÿÿúóïæïì쑊‚ýùéa^cSN=QOO-,6C?+‹‡‰9:4~yuÊÄ·xz|ƒÈ¿²ÒɽáÛÎŽŠ‹ÿÿÿutxssvÊÁ½rtyrptb]dƒ‡‡caa'*)*)'WY\^``||€cff\^`?@A*.1*((300EEF++'/+)AD;;>,ÌÆÈÓÕÙåââóôõàåáöïðëççêéë×äàŒ…„ÒÛØ±µ«°¤¨†ƒÞÛÞæáàéäèÿÿúñëäúúóœÌÌÎùøðËÉÉîêëÏÑÎÿÿþÀ¿½ÿÿÿÜÚÛññî‚€yÎÐÒÔÔØÔ×ÖØÚÞ¤¥¥ËÍÆËÌϲµ¶–™”ŠŒ™š¡‰Ž•†—Ž“›ƒ…‹‚‰||}z~ggdqpnhfbqpk‚pnpwvqmmffdXok`>94[UOtohf`X`]T‰€c_[]ZRVSNLKH‰‡€mleœ››wws~|USPrmkiif``Y˜“~zuZWUsus¼¹¹£¢Ÿ[[U•‘‘®¬«òïèÖ×Ò¶²¬ÆÆ¼¼¼´³¯­åâàëéëÒÑÈÕÔÎõòæóîæJ?:ììãÚØÓ‚ƒ„ÆÄ´tswdhl`lsdcUÇû%"*)++EHK??EtuwQRU?EL77=!! ('#&((OX^58;+($%$#D<7PH@]Z^–’Ýãã¾ÂÅÍÉǤ¨ªÕËÑœ™›Œ‰‰‰Š…“«°²ÿÿÿÁÁÀÐÏËÐÏ˦§¤ÅÄÃðìîŒ‡Ž˜—çèçãÜß¿¿½úùú¡žËÊÅÙÙØ¶¶¶¬­¢ÿÿýæääìììðíéþþýÚ××ÿÿÿÜÚÜüüùÞÛÙâÝÛäååÜÛÍøõô¶²²¿¾»ÝÞ×ÐÏËÄÇÄÐÎÉÙÙÕŸžÔÒɦ¦¥ÃÄÂÊËÉœžmln~€ƒ‰Ž—ŠšŒ’š…‹rtw~„{{|^^Ya]^|xw_\Wvusca_C@dddNNMmlldc`KGHpifb``ƒ‚}®©§@>B–“•†·¶¯’Œ²¬­³¶²Ž‰z¡™ž÷ï𤠸º¯]baV\_bhemqtZ\aMT]/1,+)*-.-,50MTRIKO:E;D=?C@/Ž œvrz‹ƒ†ÿÿÿ°®ªÿÿÿ„ˆ†m~~ˆˆ„ßãᜢ£oqs‚ƒïêé°³¬Œ•èè䑎opnŠŠˆ‚‡Š†€ˆ‰øøøÝÝÜÀ¿¿ÍËÍðñîÐÍÏÁ¿»³³³ œ›Ÿ™“ÿÿÿûú÷íëਦ›µ¶¯ÿÿÿÎÍÇôòîÚÕÕÆÄÂýûöàßÜäãßÁ¿¼ûúøççâÿÿùýúòÜÚÚüüùÁÀ»ž›—¡Ÿ—»»µÿÿÿ°¯£ÿÿýüøóÆÄ½}xüûôýüñììäÛÙÒÅû­­¨×ØÙqnm»»»¸»½xzuchhƒˆŽswx_`a[ab†‰Z^`dhiinmXWUrqrosnDFAurmWXSTURa`X42/:84OLG^YOC=5dcXXXOVRKmjc<;:iha>;9LMHSQH@DBpoppqnHGJ@@=EJE6:8,..POSQQQ<581/3$#%!%!IGK78:243163?@C8<;ED:==4M@Dö÷þ––žÂ½½ÙÎОš™üö÷®ª¯<9;ytmœ˜™|}zž¡§ŒŒÏÔÙÕÐË‹Š‰ÈÌʇ®¤°®©³‡žšbaaŒ‰ŠàÝßøþúóóî——–„‚}holž¡ ¡ _fc’ŠŠ·²²¸¸³ýüýÚØÖÏÏÊ›–ˆ…Š‹}‚{sŒ†×ÔÔÿÿþÿÿÿñòéÑÎÆöóó¢–¥Ÿ™™”ÖÓÔÿÿÿ¦¢”ÿÿÿÆÂ½áàÜêêæÿÿôèäæÿÿÿÏÍȺ·ªÊÇÁŽ«åàÏÜÙÙõóîÿÿü“”’­ª§ïìæààÚàÝÚ¾½ºÿÿÿ~zzúùðñíçððëÃÂÅ‹Œ‹ÈÉÉ›ž›ÑÑÔ  ¢®°²ŽŽ™lpveklotwtx~_imuw~TY]SUV]aaSUV<<>TSPC==456;3+CGD:=9CA@=<5A=5.'':4/WUS<79,+'585661//,#!!1,0'# (*&*%%,.-@:3DA:>2-xrp´¹¤™žÈÊΘ‹…¨°°uyvGC¾»½Ž•£Ÿ¬«ªÆÉÆ¥¤¤ÿÿÿgdUÀ¶©¹³­{zsëîï““‘ú÷õèææîîèˆÏÑĹ¹®ëëîÛàÛY]\~yÆÂÀîî먤›íê燉‚QTWÀďޑŽ]ZW’ŒÁ¿¿ÃÃÀÿÿú»¹±ÉÃÁyuq†„~vqmˆÅ½ˆƒ|ÝØÍÛØÎðîéÿÿýwno¹´±~yoíêëïîíÙØÒáàß¼»¸üýöðñ𷵬¡¢›Ù×ÓáàÝ—•‘úùøÐÐÍêèäÏÍÈÄÀº±®ªðïçÏÌÇìíç¾½¶öóïáâà·´­ ¼»¹°±¨‰Šˆ•˜’·¹´·¾»ÀÀÁ“’˜pst_jlVZbRKS).1DPXJFMBJKLHJ#%598556-.+(,'0-3//-('&##./+;83A1,G0-èèæÿüÿçàà¾××KIK>.(@7-C83F:4E;8|po“•õíí¨ª¢wzz~wwG;5H;7H90M=4„yvíëíztxüÿøÁÀ¼Ü×Ölie?:5bWSbTLVUQ‰‰ÐÕÖ“äåäÄ¿úúö“‘Ž[XL‹ƒxpnd‹…}ÜÞÛ€}{ÜÖÓÉǵ´°ÿÿÿNOG ‡©—YUMÔ×Õ™––®¬¬ÿÿÿéèç››™{}téêî÷öøx{y{›˜—ÅÁ¸ØÓ̾¼¾‰‚€ac`ބދZWNsokâàÝîìåÐÏÉÿÿÿ±¨ªdZSpiayun·µ²÷øïÞÛÙÅÁ¿ÿüýÞÙ×¾»¿—’“‡Šêêç¾»¹æäçèçè««£àÞ×¶²²ÊÅÀÑÏÃÍÊÇýûøÂ½»ßÝÝÙ×ÒééàÄýÞÓÏÏÏÎêîîÙÕÍÚÐËS\`XdoX[g‚€„UV]ENX68=]gk,2;TPXL[cHJN!:<6,.&72*7.-ÿüÿnceÿÿÿy€‡2)&=0/7-+>1*B24K=5;53ÿÿÿÜØÙûø÷DD?3.+D71G99H>8I:2L;7Ÿœ—Zb[ÿýý¿Å¶bgcMB?B70D5/J:2D:-rgbµ±­X][ÿÿÿ²³ Š‰SF8J;0J8,O>2k]T”Љ”™Ÿš›ÜÛÖüøëžš’7,'aVHl]KVOE¤›ÏÐÒ«©§ÎÎÍÈÄÀíêæ\ZP‹‚sˆy10'ôòñ’“’ÆÅ´Á¼½heaÿÿøXTP¸®£¥Ÿ”~‚~ŒŒŒ‘ŠŽ‹»¼¸‰‡lsjàäàÿÿÿvwvXVOž›çèäØÙÖØÓÑC@@]b`xxvZ\Y{wqÄÅÁ·º²ÍÌÅÙÔÓh]_YXFTSI}x©ª§ÓÕ϶¸´äàÝÿÿÿ†u~qnk¼¸¹ðçወêãßÌÌÆ–’šTabSdf^epEJQc`d66;!14beidoriktEZ^Ncd6/1:2-NC?RHH†ˆ„Áµ¸ÇÃÈ-?F+#%:/-FA?GE@JEH=7E6.E87ùýþÿÿÿóôõ/'$;1,>2,>2)A4,J;3@3,\YWùúúÿÿÿÿÿÿ72+G8*E6.D7*J8,H6.j]W¤£“І‹ÿþø—”E6.A1%B/)F6'D-$}qd‡€z€|ÿÿÿ•“†šŽ@-G3'C/!I;/˜‘ˆÍÊÊ]_\ÿÿÿèåÞ³¨¬;4(O=3@84MK=úõò‡ˆ‡ßßÛ¹µ²ÉËlj}feQujY&$õõógdaËÊÈÿÿÿorqÿÿÿŒ‡³«¡E@@Šdc_ÏÍÊÿÿÿuvv£ž~x€©£¬Z^[xsoÎÑÌôñóÖÓÌB95P[ViomKHFw~uçèáÖØÕ±©¨SCF751HDA481w„Ž‹Tirfjo431636I[^b_gT`e7.,:84WLIUNI¡˜¯³²œ—’-'";21MDFSNFXOLRNGTOEf^Z¦¢£þÿÿ£™Ÿ%!BA7LICTHCQG?NGCPEAk[P×ÛÚÃÁ¿êîë5(%B;6LE=NDBPG=J?7@1(J?<ÿÿÿýüù¬­°;*"E:/E;4C6.I=1C1%5*"¬¨¥ÉÉÆÿÿÿ»´±?3#;.$B5$>-"@/$7&G@8ÿÿÿçäæõóñ#B3(:*!?.";+"G7/stm»»³æåã®­¦+=("=) 9&:(#{skމqqrÕÓ豬4#0 . ,!kdXéåáVWSííêööðODI<4+H=356,yp®®ªwxyÍÎÇÿÿÿ6-.7-"D>;45'ôôôa`\ÆÇß•–îõî}wi·«¨*53{~xwyÒÛÕ¯£ž/99SOZQMTnmp@>:gvzjnu8;9bv}st{ &$2;6U\_.*-33+`_^pY_•–Œ”•ÐÒÑSPLWXVc_^ccXa_\a\XaZ[‘Ž”‹‹º³µ=;7ROMWTQ[VTURG[RK^VVYOO¶¬±Ÿ›žÇÅÉI?6PGEQKDVNISMISMFOE@I<3ˆ‰†’–›˜—.'NE?JC6PF:IF9MA8?,#pijÿÿÿ¥¢ –—‰I=/@;,PE:HA.B2)>,#92+ÿÿÿÚÙϸ¶¯F;/D9/A4*<2#B/%3'#OGAéè纸³ÏÊÄ  5(46#5# :/)@>;ÈÈÄÿÿÿukc5"5%/."0& $!«®©×ØÙ©£ž&(&$]ZRjb`>A@ëëíshg'&* 3+(c^X¯®ª‚ƒƒ¸·­_NW7*(']UF‘Œznt›££°¹´H>D##!"SKH€ˆmsuD>Dxv"++_\bFJJONT($#b\]a^\xyz§«¨ƒƒFTUZ[Zdeekkjmklmmijf`eeX|qr•””†‚€UUSfdcffakiijd_gc`b\^a]V°²¯Ÿž›§¢š^VPa\W^\Ue^^daY^XLPND`WS{||yup””PF?ZTMe[Q^UKYLHUJ>JC3RQG™•”«­©pngYO>ZPFXM>VL?PG8=1('ªª¦Ö४LH=LB8KD6E=6I>43&#ÐÑÑÎÄ®©¦©£ŸF=3?6+>1).&FF?îì餢›ª¨œ@6.=0)1 +(2/%ßÝÙmihÿÿÿ#+ $ -+)ØÙÔ’’“hZ\%#&$-*$%'%”””ÈÆÁTKEtj_HONmmmÆÉ¼<;58662@2 ™˜—zus‘ŠaXQMC;IA8F>/C3. LFD‡†„ÿÿÿàÜ×>4/I@5J@66*"›œ›ÿÿþÔÕÓbY\50!<2.5 ‰‘âßÜóñîNA=5-')!"\WWøöñtto96/!#NSO°¬³ ›—qw}U]d<45  )4612/!!"„„‡}z~Š‘’­œ Š~lnosqsx}zƒnll~z|t|xhgj““ÿÿÿ ™”SVWaccbbbplmjjjcbcb^_]ZY˜–—ÎÉÇrolWZ[gggonnpppjjkhhhba_a^]_^[}€‚KEFghhpmmz{z„„„wvutrob^Z‚x©££À½¹ÝÝÚywl|wq|xtvrnskbb]Ud`X’‹„Ž‘…€|d_]tokmh`daZbZSOI99-$‘ŒŠ‡€GFE¢ž˜]YS^XNSOHKD;,$ GA8¦Ÿš’mmf}zpZSGOH@H@5.%kli“‹Ž†ƒ~y|GC7?;3A8/ mnnponóìîôñí92-=52&! jfh¢©ªkcclfc1!hll•ÞÜÐýÿò2*' WVSnu|XV[RKN EGI]]cCOV~~|yzÕÎÕØãá}qqslpˆŒ’’–‘•–}||Ž‘‡‰‰€€âÚÜûúú†x€‚ŽŒ~}{„††||zyyzqpl°«²èçæzql|{}qqrfgd~€~ourecdba`__\‡ƒ€ÆÈÅpkj^bbfgfbaeXXXhfdccb_]\™–‹yuo€€€ËÇÆmllkkhcbbjgg_YY_\W`ZPnklˆ„¾½ad_bc``\[a^\QLGNIC^YVwpmfbZUHvsttpmifefa\\XTLF@^YRicYnj^¥¤YVUomm[UPVQLNG? „†ˆÌÊÅzz|™˜ZXTWRKFD;,""‚„„wrurlhŽŒTRLLG>B:3†‚¬¤¢\]^WRS;71  {xy3/.ePM582 PNQjih:8:6/7 ;;>IFF9;=€€‚yz}¢¤¥ÈÇÈtupŒ‡‰ŒŒŽ“‹’ˆˆŒ„†‰‚†…‡ˆŸ˜Ÿ•Œphd’‘’’‘•˜ˆŠ‡ŽŽ‘†‡ŠƒƒƒwuqľÃÓÑÒxl‘’””—–—–𔓖‡‡‡ƒ}¡ž–ÏÎË…‚{“””™œ›„ƒz}}}ŒŒŒ…†ƒuvuƒq¯­­íéîsql‡ˆ‡ŠŠ‡Š‹yzqvuldgcŽ…v‹{…††€}ycc^ihejhgba_RPKMNIwug†}pmnm¥Ÿ›fec\\Y_^]^]ZPKF-,(€xowf’Žcc`cd`dc`\[X]WUAD@WSNa]YVQHš‘mkjcc_hca_]ZRLAIHCsmjtof½»´„}sefc``\MJD"mpcZTSLF>œ™š\TUGKA>;7cecnjb„z~kghA@>312iml0+(8;;DEF%($ COR6/6" €ƒ‡†‡‹ûøþíù÷……‹“”•™’—Ž’“Œ•‰Œ‘‚ˆŠŽ£–™ßæé‡†…šŸ–šž—˜š—›—–𔕖ˆˆˆ||{Ý××ÿÿÿ„„‚œ ›œ ™šž™›ž’’”ŒŒŒ€„wts­¥¢´°°€’”™˜—™’‘‘‰‹Ž‹ŒŒƒƒ„}}wl^…ƒÿÿÿ€|•”“ž—˜‘Ž‹‹Œ…‚…‡~i„t´µ¶ÝÜÚ••–––—˜˜–ŒŒ‰‡ˆ||}‚vf†ycŒˆ…óð瀃ƒ„xxs||}srrgeavn\zm\ŸŸšÿÿÿbc`jihccbWVTHB@wtl„xjyzohllŽŒŒfecbbaSOP<:9ba\ƒweUNBeb_loqRRPGEG>??@E>xrm`ZRbf^dedWVVIFH784>A>urqNHEZ\[okh???432&UZUCB@DDD?78T^`213*('#''NQW"#"?:<Œ‹“””™èç奸º“‘•”–”šŸ‘•ž‘”›Ž’œ‘”Š‘‡ŠÏÓËÛÜÙŽŽŽ™›Ÿ’—ž™—”žŸ—––ca]sqs„„ƒú÷öÿÿÿ}}†‰„Œ‹‘žŸ£›¡—˜œ’“”ŒŒ}}ÒÏ˼»ˆˆˆœœœœž›œ ——š“”—‹‹Œ††ˆwuk³³¬ÿÿÿ}~~‘™›˜””–‘‹‡‡‡„‚ojZg^NÔÔÕ¾»¶’’•••”•–……†yxxe\G_S@‡…zÿÿÿ„„…’’€|………}}}rnjq^€r_‘ˆÆÅÄ…††ŽŽŽŽ………qtsrl\|m_nfR{wv•–•‚…ƒ„ƒ‚~XXTg\S~vh€}vz~zš˜“x}zpsq`]^``[qiTwlZ^[X‹Œ‹e^ehjkPPP`a][QBRM@RTMghjFDF499062RKJ.-(1-Saf:8:%#%!!"=@>EEF.46–”›Â·¼ÙÙ×››—šž›Ÿ§›ž¥›Ÿ¥—›£–™•—Ÿ˜˜Ÿ„„Œøýýßçä”–Ÿž¡§™œ¢—œ¡˜š j|}WSWpm_ˆ„…ÏÉËÊÊÉh`Rimb6+›˜”pne®¬¯““—‹‹Žƒ…„|{|dZPQH1wvsƒª§§‘‘Œ€mlkVN;LC2qut“’èáß~……ƒˆuvuWPBQE?;3+wwylttŒ‹Š{z~___WQKUH>GD8^dg§¨¨lkkhfjIIH-. -*&44>FGEklrDIKJOS)+%##$%Ž—±¯¯äàß““šËÌÎÌÚØ“™¤ž §¡£­šž§šŸ¤™¥˜›ŸÜÛÛ¶½À”›ž¡£©¥¨­›¨©›˜˜{~{EK?nf_tokçåæÞâÞ‚yt350[POŒŸŸ¤Ÿ¢ž¢› —˜˜ÿÿýœœ¢£¥¨¦¦«¢¢¥žŸ¡ ™—™•–˜Žµ³²ÍÎÆ™››žž ŸŸž—˜—”“އˆ„‡…“‘Œÿÿÿ’Ÿ £œŸ˜˜™‘‘ˆ‡ˆ}}|yvp˜—•ÿÿÿ••— ¡¤œžŸvxy zysLD5*)*vugÿÿû•™™›Ÿ¢‘’‹„„…yyvnkc€~}|ÿÿÿ’““’“—Š‹‹~€gddff`~ŒŽÔÔÑ„ˆ‡ˆ‹…„†rrt`ZLjjbwy}§¦ìàä{€†wy}a`a=9-IKDbeiknløµjqx]da-3+##784ACH`mpSQYPSZ)(("!‡„Œµ™¢¬¬®ÖÓÓÈÇÇÔÐÑäâß¶½Á{‡wxŒ‰Š–}„ÿÿÿ¼¶»çäáàãâÂÈÍt}€ttp‘Š$& NJH]YWÿÿÿ¯¸³Š}w)) ²±­—’§¦¬«­¯£¦ª£¥©šž |~¬¯¬’”— Ÿ¤«ª¬ž ££¢§¡ ¤Ÿ ¡œŸ·¹µÿþû‘‘’œ›››™—–šš—’’“‘’’’‘‘‘¼¸·àÞÙšž¡  £¢¢¦žžŸšš›Ž‰ˆ‰‰‡†êìéÊÊÌ£¤¤¤¤¦nmo542d`Xˆ€,/+Š…t~zvŠ“•’œŸž”•—…†‡ƒ‚‘‘‹ŽŽÿÿÿ‘”—–—œŽ‘„„„{zxutt‚€‚ˆ‰Šåã嬯±‰‰„…‰vuvggennluw_ddÙÔφ„ˆx}kkpUTPFEEd`ffehÌÍÌ88=ƒ‡‹€qvxÔÓÍêâá`bb  ¢ŒŒˆŠsrtljnsty|„zˆˆ˜‰!'("&#KLM]TYDUYMX_EIN$#"–šŸÿÿÿ¹§¬çæçìììéççííëíììèé랥¯™œ¢œž¢Šª¥¦ôóòññîòñðññîëìèDQR353{|zŽ’–•–ÙÝÖ™™˜––™{ƒ€qsrDIDŒˆˆ£¥¨©ª¯ž ¦ªª¯|‚}æåàýýûøöóôðíôòîÇÉÈ©¨¬ª««¨§«¡¢¡ëêäÞãÚÁ¿¹÷õò÷÷òöõíš›¦§¨¤£¨¨¨©ÏÎʱ¯©ØÔÕôóîôóîüúõ ¡ ©ª«¥¥§œžžÈÈÂÿþøÂ¿Ãöõòÿÿù˜™‰QUPyuqfdX}yssrl=5'°¬©øúögaWCGCDGGŠ‹suxoruÛ×ËçàÚ®°¯óðí–•‘w{~xy{srsfijØÑÇÑÉÀ™——¶¹·30(=6+pszhmmž¤×ÎÅÜÕÒºÅÅÛÒÕqorMMK`^aYW[[]_]\]szxÜ×Ò::9873 )))750mpw:AF>>I;8?/.1&#!¤ ©äáãäìíææçìëëæççåãáèãàéæí¥¤­¤¤¬¥¥¬íàãÚßÜóóôâÓÛöøøðòñðéèz}nwx𢇆‹“Ž•ìöõäà㌘™œJO7Ÿ ¢¤¦¬£ª¯¡¤¬»º¿xrsÁ¿¼ññìòñîóñîðïìØÕÓ¬­³¨¨¬©§¬ËÊÈðïëèäç©§¤ø÷òöôñøõñÙ×Õ¨©­¦¦©ŸŸ¤úøõááÞÙØØÿüù÷öóø÷óÒÑÑ®¯²¨©ª¨¨«ÒÒÎÿü÷µ´´ûûøøõñÿÿÿTTTkihrtr“”’‚„‚_ggŒ‡¶²¯WUS ”™™¤¤¨¥¥©™œžþúóÿÿú®­®ñòìûúõ²±µ˜—žŠŽƒ†ˆ÷òæ÷óì¹¶·­°®ID8EE>„„ƒ„ˆÁÌÉëçÜéä×qnrìêã–›¡ƒŠw}zv||oqsuuyihfppmHNG/-.0*,)7612- !^`c(5;S[cOU[9==+&$Á¹½õñõÏÆÇèççéééãäãâßÛèã௸ÀŸ¤¯ ©˜¢§¦ª£úøøäåäwxrˆ…‹ˆ€Œ‰¡›™”•™‘PBD’‘õûûˆŒ‚zqˆ‰uww¶¦¬³¬®xwy˜—œzuqÂÄÇœž¸°¬û÷÷õõòóóðôôðòñï ¢§¨¨®¨¨­ÉÉÈÝÜÚŸž™õòï÷öóõóï÷õñÜÚÓ¨©­£¤¥¨¨­þýùľÙ×Õÿÿûùùõø÷õÿÿþž¡¤¨©®««­ÌÍËþýø‹‰ˆÿÿÿøøóÿÿÿjmk{{x‡†‡x{{‘ŠŠˆƒ€}ÌÌÉ£¤‘’‘Ÿ £¤¦ª—š™üúöóðæ––•ª©©÷õñôô”“˜ƒ‡‹öóéõñê§¡œ×ÓÒqchaaZŠŠŠ‰ŒÓÛ×éäÙìëè™”“¦©ªòíꉒƒ‹š¤¡wvzz|€ux~®ª©(!DDB*&'RSW]XeWagX`gLRU§«°¹¹¸ãâãççççççâááääâåäâž¡« ¢®¡¤®¦¤­ñóñÚÙØîëì—œ˜ÝÞá«­¦tyupnieggvww//0KEHœ¦©¤--1¤Ÿ–‘…†lhd{tr†ƒ‚‰†‡Ž‘ÃÁƲ­­ÝÚÑÿÿÿø÷÷ñðïòñîÞÞÞ§¨¬¤¤¨¤£©·µ´ÉÆÁÌÎÆxspîîìòñðïíçÐÎΧ§¬££¨œœ¡ÿýøÑÓÍÄÃÂÿÿÿúùöùøõÿÿý©¨®­¯°««®ÑÑÎÿÿýª¨¦º»¸’“ľ»Í¾¾’”k\G‰ˆŒœœ›}wtpmf´²¯‰‚ÊÌÈš‹ˆˆŒ‰‡ŒšŸÿüùñí⺹¶ÛÙÙêéåÿÿû›¡“˜›‰‹ôñèðëáéãß½ÂÁ¤›˜\[Sb`Z’™™ñêãíêáýóî„|ööñ’’œ‰‹‘{„…~}ƒwzz‹‡óÚß#$#&! 132TZ_ejmPZaZYdNV];9:ÜÍÑõøùÕÔÕÝÝÜÐÏÐÔÔÕÖÔÕÕÓÔ‹Ÿœ§ Ÿ¢©¥«ðòïäáàóôôúøùùúùýüþäãÞ‚ƒQKM@@ITMNÛÜÛÅÊËIHL~pjmhfƒ‚ƒtqtª­²§­²¯°³ÿÿÿŒŠ‹ËÍÈýüùõôñððîóðìêë쨩®¦ª°ž ¦ÿÿþÉÇÁÈÉÄúúôòòïõñîöó𬬰§«­¦¦ªÉÊÊõóð¼¹¶ÁÁÀüýøúúøúù÷ÿÿþ­®´±³¸¤¥©ÿÿü÷÷óâãáêêë^XP¤£“`YM\WViopFA;“V\fOON††„ßÛÔ…ypd^Rjkj|{wÕÕØùøôöõñŒ‹ˆÜÙÛüýùÿÿû¥¦«¦¦ª•˜™÷õíóïêÿý÷ÙÙÒÿÿÿ`]Yld[†‹pxzìåÞíìæþþ÷±³èéç’–ž‘‘—{˜™‹…†}ƒ†>88yupãäà6DB_dl2)*Ycd@?A1CIVWeBDJÍÐÑïîîÔÓÓÓÑÒÚÖ×ÜÔÕÖÖÓÎÌÍ–„‰Ž‹‰’À½ºÝÌÕ¥—›ßßÞåààáßÞßÚØèãॢ©‰‹%"!CCJ`Z[““’ÞÞàBAH8--‚}°±¶y|£¢§žŸ¥§¥«ùñÊÇÄñëìòîíôñîôî맦¬ Ÿ¤ª¦­ÃÁÆôððÀ¼º»··ýùõññîòïéùúõ¨§­­®¶­®²»¼¾ùõñááÝØ×ÙÀ¼·ýýúúúøÝÜÝ·¹½²²¹¬¬°ûùöøøõÏÎËääâÿÿùñóë¡¢›ska]epWVVÛÕθ½Ãjyƒ´°«Ç¿¿\URrsk£¥ªš¡ÛßÜ÷õòøöñþõóÖØ×ððíÿÿý§©®££¨¥¬¬÷óìõóîÿýñ ŸŸæäå_YVrle‚ƒØÛÓåâÝîìæïï쉉…ÑÌÆŽŒ’ŒŒ’‹ ¢³«¥vz~ou}vusÎËÅ626+()!%(&NQUFMTWYeWZfQV_ìêêõõõåååÞàÞãäãáàÞêéêìëëäê盥Ÿ¥®éäæÿýþæåäççæëéèíìçéè裮¯•–˜‰“@9=HDHŠ…š›˜µ¼¼MLOPOPŠŽŒŒvxx“–š˜œŸ¡¢¢èæà¯¯®À´±ÑÐÌÚ×ÖßÙÔÐÐÊ‰Š’…ˆ‰­¬«ÞÖÒÎÌÇŒŽ…þûøßÚÔãáÜãÝ׎‘™™šœ»¸¶îê忹¿½¼ÙØÔïìçíéâÁÀ½•”—›œž˜šæâÝíê庸´ÍÍÍóóïçâà¿¿¾‰Š@=A9>A‰opxŒ:@F Ÿ–íïêwumˆˆ…›¡§‘•™ÊËÇãßÙåâÝûøî¦¤¦ÿÿýïê厎“‚†‰„„‰Û×ÏÙÔÍãÞÚ³°¬æêæ„€{yyltvpuvzäÞÚåàÕØÔК†˜§~}ƒoqvdciƾ±SWVZekZ_bŽT;<#! :<:EED]ce5:@>>@BE,+/cah@BDEKV@JVéçèççæìëëêêéæææéçèìëëëëë´Áɪ«°¦«­ÑØÑýûüËÌËíëìíííïïììêêàæä—žƒ†ŠPMStxËËÊÐÖÖñóó\X\STX»²°«®®ïèçºÁÁ©ª°ýúúööò»º¹úûùùùùø÷õõôñððïãäâåäæ­®²ÄÄÄóïëÖÕÐÿÿÿüûûôóñððíøõñýüú­®°«¬­ÒÑÐõôñËÈÅÒÑÏõóñýüú÷ööðñìöõò¬­²¯¯²þýúùøôüûùÖÔÔùúöùøôÙÙ×”“•acdŒŽ—ÅÀ»¢¦¯’ž°–›“Þ×Û¡¢›˜™˜Ú×Ч¦«¼¿¿ëêãèæáóóí²²±ÿÿÿòñëãäá½½½™šžþüøöõñùøôùôî¿Á¾þþÿZWPƒ~s†‡ˆêåßîëæíéäóó罹 öîæ•“vu|Ü×Ô†ƒ‚qswnrwt~xÔÌÆ,,+" &=CFGRZDJMOOVUUaRS`üûýþúÿãùÿ¹ÅÅz®“œàÓÙîåéééìÎÍÍÚÞâöòöéæèÿúýÿÿÿððìñðíòñòåââ‰ŠŽ‹‰“C<@‡‚ôòóïêìåèçVQSeac¹¹¹»ºþ÷ú¦§¬°³··¸ºöôó´¯­ÝÙÛÿÿÿôóóõõóõõõõóôààá°²¶ÈÆÆðïêýûûÀ¸·ÊÅÄø÷õóôïöõòôñíÕÔÕ§§©¹¹ºõòïöôóÎÈÇÞÖÖ÷÷òìéæôòðþýú°°´ªª®þüøöòïÿÿÿ¼¿·öõìø÷ôìëê´µµrqw‰‰‹±¯«ÇÄÈ©°¼¤£°´´­Ìį±±ÄÁ¼´µ¶˜˜œ÷ñîêêåóòíýº²²ªýþ÷÷öòÔÓϪ©®ÝÝÙù÷ôööñÿÿÿ·¹†„{ÙÚΗ“Š‹‰æäÛéæßÿÿÿòï슊„¶¬¯ðêæƒ†‰ÐËDZ¥¥rsxpox|wÇŹ¶¥”%""02698=366PPVQT[EL\¦˜¨¦¥±ËÎËãà{stD@;~y{ÕòöÿÿÿüÀ_TVñùóßáè‹w~ÙßáæÄÐïãÛÿÿÿÿþÿ¤ÿÿÿÎÃÁ“ˆ§°³’˜†‰”‰†ŽŸœ¡Ã«ºogo›‘•ÑÃÌÐÄÇøíòÿÿþþûûöòóíâèüùùÿûøŸ›šÿÿÿôñóÿþÿìëíÿÿÿõ÷õÌÆÅêäçÚÛØíèéððíúø÷èæâÖÛÕßàØßÙÚûùõÚÖÐÿþþçÛàÿüüúöó÷îïÿÿþææáøïòîèçîéâûúùüøóóïíñêìÞÞÛÀÅ»ÎÑ౩ãâá³´¹‡ˆ‚ÿÿÿûøóÃļßÜÚÃÄž¤¥ðëèæäáïîéàßÒòðêýüòúòñùìîÍÍÏØÒÐâÚÛÿÿÿÿÿÿïíèäæáîðíÐÅÃÐÑËéãåéÎÕ¹¼®ÿûýã×ÜãØÖõ¶™˜˜¥˜¡mfghokBAIP9A–›”†’N28 Ÿbff?AB9FG''#=98&,.svH@>404XabRQJJ\X]ce}Ž‘ˆ‡£•›”ŠŠUFLŸ˜–vwu‹zNRPRTWž–RNJŸ—™¤ž¢Ýßä’‰–“jpn¦ŸŽ|uvl.)(ÏÒÑ~uvŒŠ‡†~ƒÏÈÌibg¤›œÎÊ˘’´²¬·¨­xuo{yx\YWKML”””PHE½²¸ztv€zyWVLü½××Ñ™’•‡‰‚ŸŸžzrr‘ŒŽ®­ª……‚–ŒŒïáè„|‡„“‹d[_ž›”ˆˆ¡™š–ŽˆmeaŽˆ…†ƒ‚„€}ÄÈÁÝÐÑ>77•‹‹œŽ’¬§£«¤hcb‹‚†.+*£¡Ÿ†~…‹ŠYXQÂÊÄÅÈÆ‡ˆÆÁ¿Š†…WMQ§¨§ÍÍÉÒËÍ_`X/&!"“˜—FHL™¦¦¶­¯…~~vtsE>:hkgmac_ZW/0*YPM’‡Š0+(WROv{}rrsˆ‚ƒ_\ZUHHYSK>>?%)&?=:9<;541&$%--!f_cgikhtshmh~yuca_Ž’Š\YVjeezpPMQQBDwqowuq2,'YXRqogmlhcbbljjmusg\U]XRD>=hbU+)(YXVURA^]RWUOULH:94]WR]UMKKDUH;F>6C=8XMFCGF21.10,&$ &!$#"# #"~Š‚€~†~”heknnt†uox{Œ|ˆ„zˆŒ˜~}†q~€xrtz„qyzhihonrlplptufcespo€ipkedadhhhojVWVljh^_[b`_rvrehesppxvqnml^^Wffc}~wmmknsousnfg`stovsknoiqmhc`Z[UPng`kc[vlge\URLG_WR_UNYON@5-lc]ogbykdg`^[VPjd`ZPQYRP^WTVOG^XTXPR_ZTfY[`ZNZRO=7980/>5;^_ThdasnmXWPPM4(.:(QBC¯¶­áÚÜäÞàÛÚÙ×רÒÐÔÝÚÕØ×ÑÛÛÕÑÓÈÜØ×v}‰iisacmYckìíïííïììîîîðîîðëëíçæç×ÑÔ“››rmhipmƒ€}jfb…ˆˆ‘—™ž¢––Ÿ¡¥{‚ƒ‚„¡¦­ ¦¬©µ½Ÿ§®¨«°šŸ‚†œŸ ”˜›Ž‘•‡ŠvzxTKJ‰†€984ïÛághgÛÔÖìéæöööðñðíííëëëëêéìêèÃÄÕ’”xthƽ¿IH=üúòñðíôóôîîìéæãÆÇćƒ‚jkcROPĺ¾îêæèçâèçåðîîòññðììüüúûùùlb^jcYmmkƒ……–™œ¢¯­‘‘ÜåßíðëÚÖÐïìæôòíðíêôôîõóïÏÏÌôôïäñîÌËÊÏÚܤ©­~…‚‹Š‘}~gh`jcbeeb½À¿ÝÙÚÜ×ÖÙÙÖäâᤨ­ÃÇÁ¢¥¦x||z|z†‰6+*21"/) )!:5*xyzÈÈÂÖÕÏÚØÕÖÔÕÕÒÐ×Õ×ÔÕÑßãßïî‡Ÿ•—K]X/65\gqYNUíëîîîïîîðìíìêééÒËÏãëìXJFrpp{€i`a‘“‘‘›ž¥¡§ ¡¨¥ª¯š¦§òø÷™¡ÑÐÐ¥¯úüþ¦°ºþÿÿ¨µ¼ÕØÖŒ“›§«”—œž¡ª”— Ž’šŒ‘~b[Xtkm‰}ƒëê鄇÷õõÇÇÆúúöýÿüÞÝÞÞß×szucg_œ™zssKKMúøìææãììéîïçëéäÁÀ¿hhh™™UTNÖËÍêæãììéïëíñðñêèèÿýýïéæzqpjf[މ‰ŒjmoÀÃÃéëàôñèõóîýùöÒÏËòïçôòîôôòñíéøõõÐÍÉ÷ôïööñöôõòðìóïìàãÚ‹Ž”Š‘“‘˜žŠŽ’z{|hgefe_rni„}~òóîèææŸŸ©ãåä§£¥ˆŒ“x||{‚nifVNKNFF:7/./&KLCVKEÒÓÔÛ×ÔÛÖ֨רÏÐ̽¶¹°³°RHDOKHEDH;83;6:823.)*ëëðëìððñó××Ö°¯®eefvjhhifooqw{x{y|¨§¯§ª°ûõ÷¡®¯ôòóìñòõõöãçæ×ÒÏóôóõôõóôôùùùôôôíìêÍÎËöóóÓäá–œ¡Žž¢££¬œž¥—stty}|XPL^]VžŠ”ÚÙÚÿÿÿíìêüûûìíêòïí››Š‹Œpkjws«¤¢îëèëëêïêêíîêôï댎†…‰rqmh^Z€~zíìçîíëïíìòñòôòïÌÀÆÒÒÎeb^‘““”–›ÿÿýƒ‡ˆçäáñîèñíéòðìù÷óÞÙÕÿòú€bwvgbl^Wd^Yi][qcgŠhxÿÿÿóõíñðíáÝÙêéãûû÷Ÿ±¶–š¢“šhnq`_]jddrxs‘’éçä–¡©èçãïêꄉŒw||Z]^ƒƒ~lnlcbdNMK<9/8SKÚâÞÍÁ¼ãÙÞØÐÎ×ÝÚsmfJKHF7532,6==QUYMKPQPR221843ÎÏÏÅÁÅÞãæÃ¹½{cfsossw{}{“£™ £½»·øôõòòðööõöööôôôñññôôóíéêÖÕÔòòòóñòõõõö÷øõôõððîÛ×ÓòòíïíìïñïðñÛßå|}†x}€˜–ž~‚ƒooirrl¨œ¡­««ÒÅËôññòðîöõô‰‡”ikkKFBÜèãòìëîíìíìêìçäõóñˆŠŽŠ‹vtvigc¶®­ëçäíîêóòðåáÞÿÿÿiaZ`\Z’“ÆÈÆåîëù÷õåßßÁ¿ºòñíðñ鼤°rbb,***&!,-*54+8GBB<6OLC-.()%"9&'UPHzqg¹¤­øùôóíëõôï¿ÀŒš¥{~†yz~uvxha[]XTçîãâÞØ¿·¶ÐÍÆqpsmvw=99zpXXYIMN>=;021';:µ’›^>K±®­ÙÖÏYOQW@D<99575TRVV\eeeqehngfk@JOT[]ÁÁÁÝÜÝŽ’rqs|{zNOP„‚‡”›°®¬ ª¬Á¿ºÖËÊöööõóô÷ôöòòïéçèñññíïî×ÔÓïíðõõøø÷öøöùööôÒÕÑáÝÛïíïòñïðîïîîîññòîòñÅÁ¼œ¢­Ÿ¡©˜›¢‹‹“}…ƒspfvvyÎØÐÛÎÖ}mkÿÿÿˆ–‹•wumd[QÇûòïíðïììëèëêåöòð„‰Ž“mli]XQ»º³÷óòöõóôòñÄÅÄ–‰ca\wwv˜  Ó×ÓòñïôóñúùùÄÀ¼”‚,&&-.%RTNtro685ƒ‚€[WUnih@F?IHGbb_AC?rroTVN880%"!bWR`QUÿÿûïîîõõô•‘“ƒ‹“„‡qrumgc¾±¸uináäݼ¸~~^\_½º±_FRk€ELJyvt\ge¡œ™ewypquˆyy‘ˆ‰¼”›GUSbafSTWb`j}nxjmqbiqhpx>IQ[bjåæë»éðfuvfde`ab—‘™¡êçèëãåõòôÍÐÒÊÎÊõõõðîïôòóøä톀|~umvpl[YMe\U\PPYNKcKIcVOOG=~pg…~z¢‡“îííêìéöööêêéÕÐÎíòîÜÛÛœ¢©‘––ˆ‹Œwsodba×ÑÏ†Šˆ¶´µ™— uqxwno640ý²ÔÏÎÓÑÐý½ÄûŸš—prwkhnjfi±ª¢ÕÏÌ®¨¢ùô÷ïççssnˆŒ‰|~~þúöíêçôóïôóò¹²´2,,EF@INJab`feeSKEPNESIA=64@@3H@7==8P>9XXHjhffd`jjiegeIJG)(&YLBœ‹“÷÷øÉÅÁíë袦¬‚„ˆrtwjjeihfÂÆÀèçæîì쉉ŽIJL70-=?9’ˆ‹T[_NLN44*5-+#¼âãèÖÓKIO]^bÂÔÖy¡­¸·ªÛÕѲ¨¥hfqfktCJS_dh’—›bY]zzzbjl“‘¤¤åãäçåæëëëñóòêïðÇÊÁììï}z{a``<,/51,;<7GJJRVT`_YYZSdcaffeeha`faUQL<=66/0M?@wll—…‹çéèÝÜÚóôóñïïíî럤¯™œ¥šš¡ƒ…‰kfcunrohiÎÑÖmlrrqvtuva`^ËÆ¼ÌËÅÐÌÈÃÁºÂ¼º¥£¢„‡‹X_a]\_KOKÔÏž¸·þÿûspnpol‘“”ÆÈÇÕÏÈÒËÃòñíÿÿÿXDM,.(LOHgghRIIZLD*+'[]Ynpntwuyyyt{y|€x}€uvxcddehbTSDXPJ]XKnjfegcGIF/)%^]Uµ´´îî룦¨q~†‚†Žnmtorlmd_ÅÂÀìéæ‘‹‡_Z]ge[NIK547:53IE@NMI?2.mstœ¡™<9:5VRK[\XJUU./-UOHàØÜáâßãáß™›¡{…X]\khfy{ééåfcc[aVZ_Z731W[^A@ #include #include #include #include #include #include "bcm_host.h" #include "GLES/gl.h" #include "EGL/egl.h" #include "EGL/eglext.h" #include "cube_texture_and_coords.h" #include "revision.h" #define PATH "./" #define IMAGE_SIZE 128 #ifndef M_PI #define M_PI 3.141592654 #endif typedef struct { uint32_t screen_width; uint32_t screen_height; // OpenGL|ES objects DISPMANX_DISPLAY_HANDLE_T dispman_display; DISPMANX_ELEMENT_HANDLE_T dispman_element; EGLDisplay display; EGLSurface surface; EGLContext context; GLuint tex[6]; // model rotation vector and direction GLfloat rot_angle_x_inc; GLfloat rot_angle_y_inc; GLfloat rot_angle_z_inc; // current model rotation angles GLfloat rot_angle_x; GLfloat rot_angle_y; GLfloat rot_angle_z; // current distance from camera GLfloat distance; GLfloat distance_inc; // pointers to texture buffers char *tex_buf1; char *tex_buf2; char *tex_buf3; } CUBE_STATE_T; static void init_ogl(CUBE_STATE_T *state); static void init_model_proj(CUBE_STATE_T *state); static void reset_model(CUBE_STATE_T *state); static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc); static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc); static void redraw_scene(CUBE_STATE_T *state); static void update_model(CUBE_STATE_T *state); static void init_textures(CUBE_STATE_T *state); static void load_tex_images(CUBE_STATE_T *state); static void exit_func(void); static volatile int terminate; static CUBE_STATE_T _state, *state=&_state; /*********************************************************** * Name: init_ogl * * Arguments: * CUBE_STATE_T *state - holds OGLES model info * * Description: Sets the display, OpenGL|ES context and screen stuff * * Returns: void * ***********************************************************/ static void init_ogl(CUBE_STATE_T *state) { int32_t success = 0; EGLBoolean result; EGLint num_config; static EGL_DISPMANX_WINDOW_T nativewindow; DISPMANX_UPDATE_HANDLE_T dispman_update; VC_RECT_T dst_rect; VC_RECT_T src_rect; static const EGLint attribute_list[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; EGLConfig config; // get an EGL display connection state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); assert(state->display!=EGL_NO_DISPLAY); // initialize the EGL display connection result = eglInitialize(state->display, NULL, NULL); assert(EGL_FALSE != result); // get an appropriate EGL frame buffer configuration result = eglChooseConfig(state->display, attribute_list, &config, 1, &num_config); assert(EGL_FALSE != result); // create an EGL rendering context state->context = eglCreateContext(state->display, config, EGL_NO_CONTEXT, NULL); assert(state->context!=EGL_NO_CONTEXT); // create an EGL window surface success = graphics_get_display_size(0 /* LCD */, &state->screen_width, &state->screen_height); assert( success >= 0 ); dst_rect.x = 0; dst_rect.y = 0; dst_rect.width = state->screen_width; dst_rect.height = state->screen_height; src_rect.x = 0; src_rect.y = 0; src_rect.width = state->screen_width << 16; src_rect.height = state->screen_height << 16; state->dispman_display = vc_dispmanx_display_open( 0 /* LCD */); dispman_update = vc_dispmanx_update_start( 0 ); state->dispman_element = vc_dispmanx_element_add ( dispman_update, state->dispman_display, 0/*layer*/, &dst_rect, 0/*src*/, &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, 0/*transform*/); nativewindow.element = state->dispman_element; nativewindow.width = state->screen_width; nativewindow.height = state->screen_height; vc_dispmanx_update_submit_sync( dispman_update ); state->surface = eglCreateWindowSurface( state->display, config, &nativewindow, NULL ); assert(state->surface != EGL_NO_SURFACE); // connect the context to the surface result = eglMakeCurrent(state->display, state->surface, state->surface, state->context); assert(EGL_FALSE != result); // Set background color and clear buffers glClearColor(0.15f, 0.25f, 0.35f, 1.0f); // Enable back face culling. glEnable(GL_CULL_FACE); glMatrixMode(GL_MODELVIEW); } /*********************************************************** * Name: init_model_proj * * Arguments: * CUBE_STATE_T *state - holds OGLES model info * * Description: Sets the OpenGL|ES model to default values * * Returns: void * ***********************************************************/ static void init_model_proj(CUBE_STATE_T *state) { float nearp = 1.0f; float farp = 500.0f; float hht; float hwd; glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); glViewport(0, 0, (GLsizei)state->screen_width, (GLsizei)state->screen_height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); hht = nearp * (float)tan(45.0 / 2.0 / 180.0 * M_PI); hwd = hht * (float)state->screen_width / (float)state->screen_height; glFrustumf(-hwd, hwd, -hht, hht, nearp, farp); glEnableClientState( GL_VERTEX_ARRAY ); glVertexPointer( 3, GL_BYTE, 0, quadx ); reset_model(state); } /*********************************************************** * Name: reset_model * * Arguments: * CUBE_STATE_T *state - holds OGLES model info * * Description: Resets the Model projection and rotation direction * * Returns: void * ***********************************************************/ static void reset_model(CUBE_STATE_T *state) { // reset model position glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.f, 0.f, -50.f); // reset model rotation state->rot_angle_x = 45.f; state->rot_angle_y = 30.f; state->rot_angle_z = 0.f; state->rot_angle_x_inc = 0.5f; state->rot_angle_y_inc = 0.5f; state->rot_angle_z_inc = 0.f; state->distance = 40.f; } /*********************************************************** * Name: update_model * * Arguments: * CUBE_STATE_T *state - holds OGLES model info * * Description: Updates model projection to current position/rotation * * Returns: void * ***********************************************************/ static void update_model(CUBE_STATE_T *state) { // update position state->rot_angle_x = inc_and_wrap_angle(state->rot_angle_x, state->rot_angle_x_inc); state->rot_angle_y = inc_and_wrap_angle(state->rot_angle_y, state->rot_angle_y_inc); state->rot_angle_z = inc_and_wrap_angle(state->rot_angle_z, state->rot_angle_z_inc); state->distance = inc_and_clip_distance(state->distance, state->distance_inc); glLoadIdentity(); // move camera back to see the cube glTranslatef(0.f, 0.f, -state->distance); // Rotate model to new position glRotatef(state->rot_angle_x, 1.f, 0.f, 0.f); glRotatef(state->rot_angle_y, 0.f, 1.f, 0.f); glRotatef(state->rot_angle_z, 0.f, 0.f, 1.f); } /*********************************************************** * Name: inc_and_wrap_angle * * Arguments: * GLfloat angle current angle * GLfloat angle_inc angle increment * * Description: Increments or decrements angle by angle_inc degrees * Wraps to 0 at 360 deg. * * Returns: new value of angle * ***********************************************************/ static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc) { angle += angle_inc; if (angle >= 360.0) angle -= 360.f; else if (angle <=0) angle += 360.f; return angle; } /*********************************************************** * Name: inc_and_clip_distance * * Arguments: * GLfloat distance current distance * GLfloat distance_inc distance increment * * Description: Increments or decrements distance by distance_inc units * Clips to range * * Returns: new value of angle * ***********************************************************/ static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc) { distance += distance_inc; if (distance >= 120.0f) distance = 120.f; else if (distance <= 40.0f) distance = 40.0f; return distance; } /*********************************************************** * Name: redraw_scene * * Arguments: * CUBE_STATE_T *state - holds OGLES model info * * Description: Draws the model and calls eglSwapBuffers * to render to screen * * Returns: void * ***********************************************************/ static void redraw_scene(CUBE_STATE_T *state) { // Start with a clear screen glClear( GL_COLOR_BUFFER_BIT ); // Draw first (front) face: // Bind texture surface to current vertices glBindTexture(GL_TEXTURE_2D, state->tex[0]); // Need to rotate textures - do this by rotating each cube face glRotatef(270.f, 0.f, 0.f, 1.f ); // front face normal along z axis // draw first 4 vertices glDrawArrays( GL_TRIANGLE_STRIP, 0, 4); // same pattern for other 5 faces - rotation chosen to make image orientation 'nice' glBindTexture(GL_TEXTURE_2D, state->tex[1]); glRotatef(90.f, 0.f, 0.f, 1.f ); // back face normal along z axis glDrawArrays( GL_TRIANGLE_STRIP, 4, 4); glBindTexture(GL_TEXTURE_2D, state->tex[2]); glRotatef(90.f, 1.f, 0.f, 0.f ); // left face normal along x axis glDrawArrays( GL_TRIANGLE_STRIP, 8, 4); glBindTexture(GL_TEXTURE_2D, state->tex[3]); glRotatef(90.f, 1.f, 0.f, 0.f ); // right face normal along x axis glDrawArrays( GL_TRIANGLE_STRIP, 12, 4); glBindTexture(GL_TEXTURE_2D, state->tex[4]); glRotatef(270.f, 0.f, 1.f, 0.f ); // top face normal along y axis glDrawArrays( GL_TRIANGLE_STRIP, 16, 4); glBindTexture(GL_TEXTURE_2D, state->tex[5]); glRotatef(90.f, 0.f, 1.f, 0.f ); // bottom face normal along y axis glDrawArrays( GL_TRIANGLE_STRIP, 20, 4); eglSwapBuffers(state->display, state->surface); } /*********************************************************** * Name: init_textures * * Arguments: * CUBE_STATE_T *state - holds OGLES model info * * Description: Initialise OGL|ES texture surfaces to use image * buffers * * Returns: void * ***********************************************************/ static void init_textures(CUBE_STATE_T *state) { // load three texture buffers but use them on six OGL|ES texture surfaces load_tex_images(state); glGenTextures(6, &state->tex[0]); // setup first texture glBindTexture(GL_TEXTURE_2D, state->tex[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf1); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); // setup second texture - reuse first image glBindTexture(GL_TEXTURE_2D, state->tex[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf1); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); // third texture glBindTexture(GL_TEXTURE_2D, state->tex[2]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf2); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); // fourth texture - reuse second image glBindTexture(GL_TEXTURE_2D, state->tex[3]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf2); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); //fifth texture glBindTexture(GL_TEXTURE_2D, state->tex[4]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf3); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); // sixth texture - reuse third image glBindTexture(GL_TEXTURE_2D, state->tex[5]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf3); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); // setup overall texture environment glTexCoordPointer(2, GL_FLOAT, 0, texCoords); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnable(GL_TEXTURE_2D); } /*********************************************************** * Name: load_tex_images * * Arguments: * void * * Description: Loads three raw images to use as textures on faces * * Returns: void * ***********************************************************/ static void load_tex_images(CUBE_STATE_T *state) { FILE *tex_file1 = NULL, *tex_file2=NULL, *tex_file3 = NULL; int bytes_read, image_sz = IMAGE_SIZE*IMAGE_SIZE*3; state->tex_buf1 = malloc(image_sz); state->tex_buf2 = malloc(image_sz); state->tex_buf3 = malloc(image_sz); tex_file1 = fopen(PATH "Lucca_128_128.raw", "rb"); if (tex_file1 && state->tex_buf1) { bytes_read=fread(state->tex_buf1, 1, image_sz, tex_file1); assert(bytes_read == image_sz); // some problem with file? fclose(tex_file1); } tex_file2 = fopen(PATH "Djenne_128_128.raw", "rb"); if (tex_file2 && state->tex_buf2) { bytes_read=fread(state->tex_buf2, 1, image_sz, tex_file2); assert(bytes_read == image_sz); // some problem with file? fclose(tex_file2); } tex_file3 = fopen(PATH "Gaudi_128_128.raw", "rb"); if (tex_file3 && state->tex_buf3) { bytes_read=fread(state->tex_buf3, 1, image_sz, tex_file3); assert(bytes_read == image_sz); // some problem with file? fclose(tex_file3); } } //------------------------------------------------------------------------------ static void exit_func(void) // Function to be passed to atexit(). { DISPMANX_UPDATE_HANDLE_T dispman_update; int s; // clear screen glClear( GL_COLOR_BUFFER_BIT ); eglSwapBuffers(state->display, state->surface); glDeleteTextures(6, state->tex); eglDestroySurface( state->display, state->surface ); dispman_update = vc_dispmanx_update_start( 0 ); s = vc_dispmanx_element_remove(dispman_update, state->dispman_element); assert(s == 0); vc_dispmanx_update_submit_sync( dispman_update ); s = vc_dispmanx_display_close(state->dispman_display); assert (s == 0); // Release OpenGL resources eglMakeCurrent( state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); eglDestroyContext( state->display, state->context ); eglTerminate( state->display ); // release texture buffers free(state->tex_buf1); free(state->tex_buf2); free(state->tex_buf3); printf("\ncube closed\n"); } // exit_func() //============================================================================== int main () { bcm_host_init(); if (get_processor_id() == PROCESSOR_BCM2838) { puts("This demo application is not available on the Pi4\n\n"); exit(0); } // Clear application state memset( state, 0, sizeof( *state ) ); // Start OGLES init_ogl(state); // Setup the model world init_model_proj(state); // initialise the OGLES texture(s) init_textures(state); while (!terminate) { update_model(state); redraw_scene(state); } exit_func(); return 0; } userland/host_applications/linux/apps/hello_pi/hello_triangle2/000077500000000000000000000000001421703157200253675ustar00rootroot00000000000000userland/host_applications/linux/apps/hello_pi/hello_triangle2/CMakeLists.txt000066400000000000000000000002771421703157200301350ustar00rootroot00000000000000set(EXEC hello_triangle2.bin) set(SRCS triangle2.c) add_executable(${EXEC} ${SRCS}) target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) install(TARGETS ${EXEC} RUNTIME DESTINATION bin) userland/host_applications/linux/apps/hello_pi/hello_triangle2/Makefile000077500000000000000000000001351421703157200270310ustar00rootroot00000000000000OBJS=triangle2.o BIN=hello_triangle2.bin LDFLAGS+= -lrevision include ../Makefile.include userland/host_applications/linux/apps/hello_pi/hello_triangle2/triangle2.c000066400000000000000000000363421421703157200274320ustar00rootroot00000000000000/* Copyright (c) 2012, Broadcom Europe Ltd 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 name of the copyright holder nor the names of its 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. */ // OpenGL|ES 2 demo using shader to compute mandelbrot/julia sets // Thanks to Peter de Rivas for original Python code #include #include #include #include #include #include #include #include "bcm_host.h" #include "GLES2/gl2.h" #include "EGL/egl.h" #include "EGL/eglext.h" #include "revision.h" typedef struct { uint32_t screen_width; uint32_t screen_height; // OpenGL|ES objects EGLDisplay display; EGLSurface surface; EGLContext context; GLuint verbose; GLuint vshader; GLuint fshader; GLuint mshader; GLuint program; GLuint program2; GLuint tex_fb; GLuint tex; GLuint buf; // julia attribs GLuint unif_color, attr_vertex, unif_scale, unif_offset, unif_tex, unif_centre; // mandelbrot attribs GLuint attr_vertex2, unif_scale2, unif_offset2, unif_centre2; } CUBE_STATE_T; static CUBE_STATE_T _state, *state=&_state; #define check() assert(glGetError() == 0) static void showlog(GLint shader) { // Prints the compile log for a shader char log[1024]; glGetShaderInfoLog(shader,sizeof log,NULL,log); printf("%d:shader:\n%s\n", shader, log); } static void showprogramlog(GLint shader) { // Prints the information log for a program object char log[1024]; glGetProgramInfoLog(shader,sizeof log,NULL,log); printf("%d:program:\n%s\n", shader, log); } /*********************************************************** * Name: init_ogl * * Arguments: * CUBE_STATE_T *state - holds OGLES model info * * Description: Sets the display, OpenGL|ES context and screen stuff * * Returns: void * ***********************************************************/ static void init_ogl(CUBE_STATE_T *state) { int32_t success = 0; EGLBoolean result; EGLint num_config; static EGL_DISPMANX_WINDOW_T nativewindow; DISPMANX_ELEMENT_HANDLE_T dispman_element; DISPMANX_DISPLAY_HANDLE_T dispman_display; DISPMANX_UPDATE_HANDLE_T dispman_update; VC_RECT_T dst_rect; VC_RECT_T src_rect; static const EGLint attribute_list[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; static const EGLint context_attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; EGLConfig config; // get an EGL display connection state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); assert(state->display!=EGL_NO_DISPLAY); check(); // initialize the EGL display connection result = eglInitialize(state->display, NULL, NULL); assert(EGL_FALSE != result); check(); // get an appropriate EGL frame buffer configuration result = eglChooseConfig(state->display, attribute_list, &config, 1, &num_config); assert(EGL_FALSE != result); check(); // get an appropriate EGL frame buffer configuration result = eglBindAPI(EGL_OPENGL_ES_API); assert(EGL_FALSE != result); check(); // create an EGL rendering context state->context = eglCreateContext(state->display, config, EGL_NO_CONTEXT, context_attributes); assert(state->context!=EGL_NO_CONTEXT); check(); // create an EGL window surface success = graphics_get_display_size(0 /* LCD */, &state->screen_width, &state->screen_height); assert( success >= 0 ); dst_rect.x = 0; dst_rect.y = 0; dst_rect.width = state->screen_width; dst_rect.height = state->screen_height; src_rect.x = 0; src_rect.y = 0; src_rect.width = state->screen_width << 16; src_rect.height = state->screen_height << 16; dispman_display = vc_dispmanx_display_open( 0 /* LCD */); dispman_update = vc_dispmanx_update_start( 0 ); dispman_element = vc_dispmanx_element_add ( dispman_update, dispman_display, 0/*layer*/, &dst_rect, 0/*src*/, &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, 0/*transform*/); nativewindow.element = dispman_element; nativewindow.width = state->screen_width; nativewindow.height = state->screen_height; vc_dispmanx_update_submit_sync( dispman_update ); check(); state->surface = eglCreateWindowSurface( state->display, config, &nativewindow, NULL ); assert(state->surface != EGL_NO_SURFACE); check(); // connect the context to the surface result = eglMakeCurrent(state->display, state->surface, state->surface, state->context); assert(EGL_FALSE != result); check(); // Set background color and clear buffers glClearColor(0.15f, 0.25f, 0.35f, 1.0f); glClear( GL_COLOR_BUFFER_BIT ); check(); } static void init_shaders(CUBE_STATE_T *state) { static const GLfloat vertex_data[] = { -1.0,-1.0,1.0,1.0, 1.0,-1.0,1.0,1.0, 1.0,1.0,1.0,1.0, -1.0,1.0,1.0,1.0 }; const GLchar *vshader_source = "attribute vec4 vertex;" "varying vec2 tcoord;" "void main(void) {" " vec4 pos = vertex;" " gl_Position = pos;" " tcoord = vertex.xy*0.5+0.5;" "}"; //Mandelbrot const GLchar *mandelbrot_fshader_source = "uniform vec4 color;" "uniform vec2 scale;" "uniform vec2 centre;" "varying vec2 tcoord;" "void main(void) {" " float intensity;" " vec4 color2;" " float cr=(gl_FragCoord.x-centre.x)*scale.x;" " float ci=(gl_FragCoord.y-centre.y)*scale.y;" " float ar=cr;" " float ai=ci;" " float tr,ti;" " float col=0.0;" " float p=0.0;" " int i=0;" " for(int i2=1;i2<16;i2++)" " {" " tr=ar*ar-ai*ai+cr;" " ti=2.0*ar*ai+ci;" " p=tr*tr+ti*ti;" " ar=tr;" " ai=ti;" " if (p>16.0)" " {" " i=i2;" " break;" " }" " }" " color2 = vec4(float(i)*0.0625,0,0,1);" " gl_FragColor = color2;" "}"; // Julia const GLchar *julia_fshader_source = "uniform vec4 color;" "uniform vec2 scale;" "uniform vec2 centre;" "uniform vec2 offset;" "varying vec2 tcoord;" "uniform sampler2D tex;" "void main(void) {" " float intensity;" " vec4 color2;" " float ar=(gl_FragCoord.x-centre.x)*scale.x;" " float ai=(gl_FragCoord.y-centre.y)*scale.y;" " float cr=(offset.x-centre.x)*scale.x;" " float ci=(offset.y-centre.y)*scale.y;" " float tr,ti;" " float col=0.0;" " float p=0.0;" " int i=0;" " vec2 t2;" " t2.x=tcoord.x+(offset.x-centre.x)*(0.5/centre.y);" " t2.y=tcoord.y+(offset.y-centre.y)*(0.5/centre.x);" " for(int i2=1;i2<16;i2++)" " {" " tr=ar*ar-ai*ai+cr;" " ti=2.0*ar*ai+ci;" " p=tr*tr+ti*ti;" " ar=tr;" " ai=ti;" " if (p>16.0)" " {" " i=i2;" " break;" " }" " }" " color2 = vec4(0,float(i)*0.0625,0,1);" " color2 = color2+texture2D(tex,t2);" " gl_FragColor = color2;" "}"; state->vshader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(state->vshader, 1, &vshader_source, 0); glCompileShader(state->vshader); check(); if (state->verbose) showlog(state->vshader); state->fshader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(state->fshader, 1, &julia_fshader_source, 0); glCompileShader(state->fshader); check(); if (state->verbose) showlog(state->fshader); state->mshader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(state->mshader, 1, &mandelbrot_fshader_source, 0); glCompileShader(state->mshader); check(); if (state->verbose) showlog(state->mshader); // julia state->program = glCreateProgram(); glAttachShader(state->program, state->vshader); glAttachShader(state->program, state->fshader); glLinkProgram(state->program); check(); if (state->verbose) showprogramlog(state->program); state->attr_vertex = glGetAttribLocation(state->program, "vertex"); state->unif_color = glGetUniformLocation(state->program, "color"); state->unif_scale = glGetUniformLocation(state->program, "scale"); state->unif_offset = glGetUniformLocation(state->program, "offset"); state->unif_tex = glGetUniformLocation(state->program, "tex"); state->unif_centre = glGetUniformLocation(state->program, "centre"); // mandelbrot state->program2 = glCreateProgram(); glAttachShader(state->program2, state->vshader); glAttachShader(state->program2, state->mshader); glLinkProgram(state->program2); check(); if (state->verbose) showprogramlog(state->program2); state->attr_vertex2 = glGetAttribLocation(state->program2, "vertex"); state->unif_scale2 = glGetUniformLocation(state->program2, "scale"); state->unif_offset2 = glGetUniformLocation(state->program2, "offset"); state->unif_centre2 = glGetUniformLocation(state->program2, "centre"); check(); glClearColor ( 0.0, 1.0, 1.0, 1.0 ); glGenBuffers(1, &state->buf); check(); // Prepare a texture image glGenTextures(1, &state->tex); check(); glBindTexture(GL_TEXTURE_2D,state->tex); check(); // glActiveTexture(0) glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,state->screen_width,state->screen_height,0,GL_RGB,GL_UNSIGNED_SHORT_5_6_5,0); check(); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); check(); // Prepare a framebuffer for rendering glGenFramebuffers(1,&state->tex_fb); check(); glBindFramebuffer(GL_FRAMEBUFFER,state->tex_fb); check(); glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,state->tex,0); check(); glBindFramebuffer(GL_FRAMEBUFFER,0); check(); // Prepare viewport glViewport ( 0, 0, state->screen_width, state->screen_height ); check(); // Upload vertex data to a buffer glBindBuffer(GL_ARRAY_BUFFER, state->buf); glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW); glVertexAttribPointer(state->attr_vertex, 4, GL_FLOAT, 0, 16, 0); glEnableVertexAttribArray(state->attr_vertex); glVertexAttribPointer(state->attr_vertex2, 4, GL_FLOAT, 0, 16, 0); glEnableVertexAttribArray(state->attr_vertex2); check(); } static void draw_mandelbrot_to_texture(CUBE_STATE_T *state, GLfloat cx, GLfloat cy, GLfloat scale) { // Draw the mandelbrot to a texture glBindFramebuffer(GL_FRAMEBUFFER,state->tex_fb); check(); glBindBuffer(GL_ARRAY_BUFFER, state->buf); glUseProgram ( state->program2 ); check(); glUniform2f(state->unif_scale2, scale, scale); glUniform2f(state->unif_centre2, cx, cy); check(); glDrawArrays ( GL_TRIANGLE_FAN, 0, 4 ); check(); glFlush(); glFinish(); check(); } static void draw_triangles(CUBE_STATE_T *state, GLfloat cx, GLfloat cy, GLfloat scale, GLfloat x, GLfloat y) { // Now render to the main frame buffer glBindFramebuffer(GL_FRAMEBUFFER,0); // Clear the background (not really necessary I suppose) glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); check(); glBindBuffer(GL_ARRAY_BUFFER, state->buf); check(); glUseProgram ( state->program ); check(); glBindTexture(GL_TEXTURE_2D,state->tex); check(); glUniform4f(state->unif_color, 0.5, 0.5, 0.8, 1.0); glUniform2f(state->unif_scale, scale, scale); glUniform2f(state->unif_offset, x, y); glUniform2f(state->unif_centre, cx, cy); glUniform1i(state->unif_tex, 0); // I don't really understand this part, perhaps it relates to active texture? check(); glDrawArrays ( GL_TRIANGLE_FAN, 0, 4 ); check(); glBindBuffer(GL_ARRAY_BUFFER, 0); glFlush(); glFinish(); check(); eglSwapBuffers(state->display, state->surface); check(); } static int get_mouse(CUBE_STATE_T *state, int *outx, int *outy) { static int fd = -1; const int width=state->screen_width, height=state->screen_height; static int x=800, y=400; const int XSIGN = 1<<4, YSIGN = 1<<5; if (fd<0) { fd = open("/dev/input/mouse0",O_RDONLY|O_NONBLOCK); } if (fd>=0) { struct {char buttons, dx, dy; } m; while (1) { int bytes = read(fd, &m, sizeof m); if (bytes < (int)sizeof m) goto _exit; if (m.buttons&8) { break; // This bit should always be set } read(fd, &m, 1); // Try to sync up again } if (m.buttons&3) return m.buttons&3; x+=m.dx; y+=m.dy; if (m.buttons&XSIGN) x-=256; if (m.buttons&YSIGN) y-=256; if (x<0) x=0; if (y<0) y=0; if (x>width) x=width; if (y>height) y=height; } _exit: if (outx) *outx = x; if (outy) *outy = y; return 0; } //============================================================================== int main () { int terminate = 0; GLfloat cx, cy; bcm_host_init(); if (get_processor_id() == PROCESSOR_BCM2838) { puts("This demo application is not available on the Pi4\n\n"); exit(0); } // Clear application state memset( state, 0, sizeof( *state ) ); // Start OGLES init_ogl(state); init_shaders(state); cx = state->screen_width/2; cy = state->screen_height/2; draw_mandelbrot_to_texture(state, cx, cy, 0.003); while (!terminate) { int x, y, b; b = get_mouse(state, &x, &y); if (b) break; draw_triangles(state, cx, cy, 0.003, x, y); } return 0; } userland/host_applications/linux/apps/hello_pi/hello_video/000077500000000000000000000000001421703157200246065ustar00rootroot00000000000000userland/host_applications/linux/apps/hello_pi/hello_video/CMakeLists.txt000066400000000000000000000002671421703157200273530ustar00rootroot00000000000000set(EXEC hello_video.bin) set(SRCS video.c) add_executable(${EXEC} ${SRCS}) target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) install(TARGETS ${EXEC} RUNTIME DESTINATION bin) userland/host_applications/linux/apps/hello_pi/hello_video/Makefile000077500000000000000000000001231421703157200262450ustar00rootroot00000000000000OBJS=video.o BIN=hello_video.bin LDFLAGS+=-lilclient include ../Makefile.include userland/host_applications/linux/apps/hello_pi/hello_video/README000077500000000000000000000001321421703157200254650ustar00rootroot00000000000000The video clip test.h264 is (c) copyright 2008, Blender Foundation / www.bigbuckbunny.org userland/host_applications/linux/apps/hello_pi/hello_video/test.h264000077500000000000000001662237311421703157200262220ustar00rootroot00000000000000'M@)©Dü¸Pl+^÷À@(Þ È‡ôNÍ KÜ¡”:ÃÔ›€%¸ Gÿ'Ž ‰ÃÀ MLDuO`ül„Œ°ZfKë :*,g=jw`òÕåsêg- ‚Ü€‚hÐ Cýéþ3éœM cmÜ¥RDð0#ÐÍ¿5sÕ(õ-f$ @w«A*éjo_¿~Й­„À E3o9É¡Ðæk Ð_áÈ]מiåòÛØÿëÚX7‚E kDßÝÈóM‰Ëwi²å¦ÿY¸ÂÓ.™U!.ñÞ™³sÕ`ª•ò¡É°}pxS‘‡þ›Œˆž:8°AÁ“1jo ÷A×|fÆS//óÈž`©g íäë}Cý…¯lý`"{Ñ ÃÉ´£s h;‘@€Éìc€2”x¦ü«ä*+߯Á ðZ%¿<2[ÜD”EQš¦/ÄÁ_îCš DÈäYâ 1Ç1‘D˜Ç Éü‚:Ä¿l²½ó§"ãô‚55÷€K å6–ˆ}%NQ½¿8íwÇ̱þÝí½ds`JUÀþ±Ø!##Ýð-£ÕT ò d‡ù6³H0}`Ô]BßR[|¨ÇÐÌQdu ûY›qÿ÷&Lyx _ýKÎç¬ è°„ˆÀ™ûÒØä &É ‚æ¼Ó ¡Û¥OoŸ]Pþ4X´ àZ€Ý÷ õDH¶ u °Ÿ“¹h'ªmHYs?ÀŽPîAÖÂË*6€J¡=‰|5"«âè€)²j«3J >"š¸¢¢Ö± ¶Zñ*“TlZùç;F×ÃàMqµ0b(' fާNNÆB?GÀ¢0`,:Y(Ð:]õåIö×ïhËN¯·§ÉlÆåBn9C5» ÍBQxkE«"Ñ7„œÜžÀÁ¾°&àO¬:Ò ¢[˜µŽ0/„óFbâªdM%)ņ$ðpÖ½kNp`”ç&à;'ÁMXlÓϨ…}>«ÖÜ åu— ‰^« ·i9ðYþh<ÄüÚ匷S %v°šÈ@ È$h©Oò-ÑŽÇ+Ü$¬À§«<´wÒÅcêª0VTT6›LŸ“ÞŒ.«øéF8³¦HGç((x7hœo*å­°<²ÀÏw4£üjž%:r5Ñ­þ€À*0³Œ-1É©R>_«¸]Š6h=Î'{ô䘟#M±k”BSw™Ô7¤ä‰CSy%â“DåÒÆ¸àdØ5vF!ìÃÅ“0®”FJ8ÃÀ€§=¹ÍƒØëé •™ ø+‡ ò ¾5b¢HðôŽø–X«Ò`¨wîÁŠ2j¥Ê t0@Ð⬌?ºg$šhœà NÚïxBâ·R0ÉN5;­-e@RvÐ g§ô®²Jc]0I0!PCÍœ#² i¾ôíD¤Ð8ÂèƒÉ¸Ìœ„ÆÔa«(¹ùù԰u¥ÀB A\`IñÜ3öQ¯³ßýC{e°= H ‹cç·Û’8rU×i0'ñf çwÇ1ñ€¯˜{ÛèüÛYTaZˆ3;vÞ@(¹yrÍ"8vžZÄSpxPçž ŸÄƒÓ,W¾üXTGЀ9ÁñE`±>2½ ‰µøa,ÛØÚJà-F™´>h€pð鈰޺‘¨žuüþ²³e;KscY/íö¡WvbOð]ùÓ¦EñÜ'„eH›b¯×üA à!ï| SLõåî:‚• <ƒr–l‚~þÜоarƒ9ÁŒj€`ÊÎ#ÅHH?8§Yj­|q˜¿ÀÈf:7¹„5Û” ½ÿí„-íào@2¸ûh<£uÍ#p1ñÞ¢jÔ vC)R­¥œ±C•Ñ4æoæ6†¥Áýp'7À"¼‹ä°ÝÐâ„¥E~Fc¶ ÖIÊ ãKÅõƒt@댹G_‘O°’¾Á†ÀÔˆþjá|í¡ Ëó‹5 %î÷›/Õà·üU‰=€æ‚~_‚06±1Õ´J)ù‡ÈB¢C\žŒ[`VØ!‹ aCå™W èz5:ŸÖq©`KÃßoîÂ.ç/ûøˆÂèP(]”JF¡ QýÐáà¦È%†€P’Ã=ƒœÀW2M ylKf8OEÉvXDAnÙÆ;—ñþ[è„%퉜gÂýçï»ù^1ß(T+ªÿÿÁGwwòˆñ"ÿÿÁSïÿXubŸ*ý4ù?Ó¿w»ßw2O-³cÿýV»ðt²©×ýg¦š.³Wuüš—ÿ\Ö·ûæÄ‹úI%_“ˆ¥ào’hŽt¸zೃ¡¨å < øËt7B¦Äߎ¯ê£Qâ§>#á0ªÁ«–F*ƒvp"èC¥p/ñÛZ<[”’ˆ4sz`9Ѝ82# úQ]·Y‹ÏTyUGÛ¤+eèÓ-²/@’à^¡ï¹øÆIuõ±š¬Òåj2F‡¯áGÚ|ëüR îpààp!b2jkÙÌÒu_ 3WÍÞw¸úáq¸™„ ¢‹†="^Iù3…º‘«_YË&7„‘a^ ö8Û,&xZˆAðƒ‚£—lܲÕoÛ­ÛÜn"XˆÝ]ubáÖ@^SêÐî:·Ê¶c ”5%0Ï(O°iøQלœ·ÐrŒjÍQ‘F–ªS÷jû¸ˆó‹öàiŸ >5ášn%‹H1µÒÌl>f)A©1)_ªlAÛjngP4C`ïërOõ¸S%Ò'9z¡ø9‰B«IiwJPPª^Çäb9𡱥°X!ptÀ˜Ëg›íCd­–Aˆ u“¥m¶šhã3Ôbæ_— v˟ƒjœâ㊼2=™r­ùÂ@+~w¶Ø…Q||¡Es† ñU)ÎüFú¯ÓØáíP¾cJ L$ƒxãpà¸>óŧ" ‘žÓqûmÝ?ë3 õN ¹D,|%©E)LÆR Œ+tŒk-,V9APDR/Q¿ÿüTž£êìì:(r:2*»!«ªû6²`÷À°ø*šÈX7C‰= æ‚õ*0Cë§Äâ£üÔýŸAt´¢ŽA¹,KEé;¡V[±“2†Ñ—%0> Üc\Ýf¬ušÆr3ä¹ ²‡°«Jó݇ËÈòµÂÆ© >ЃŠd܇‰{$_·Ñ£t Þ’,²dò ÇƇÔDãþ*€ ¹½KóSÂ2Õ6›r…oâ"- Í#¿#KÃ#ˆŒa]5ü`âfx(ªô€ü– m‰¡¾NK€ã<ÏÁ/ù-*¤È~[zé!ÌÈÚ»C ‚t`ËC)"TûûÍÌqO³—´1ê¹J‘‰süäȉê@÷3›(f@ÜZ£3‘cÕÙ‰\síÿŒꪦju£~âg•”eìh'áUÆ@!ûxÒ)ùŠeRœ ,ØóË‘·Æ`+*;´Âw.bÁ_Œ;¢óRS5ø±.rA@3ᙦY. Á¿w˜AÜ›A픟ûù(ˆÅöc2ÉCЧ †PünH „d[ŸükÔòÏäZ=¢ˆ«IFHð-”¦Ã3vËwcË*$l™/‡90ŸlN¶Ìq°«#/´è-Ü%#OÝéÿÐp) *-€µ¡cA½‘¢µ°r ¦²'¸‡ ûw»°*.¡Ë<—Å¿Á†Å{vóèW—z_MÂÄ«àÀÏŠÚNHÿ3yRå?Ðu @ÄÙ4bÜæ9'ÎtŠ(<íTm”½ä]hsÞHu”O°•mä™S‰ Lw¿rU¿“#ªdŸ…‰t¢†~0¿J9 iiøÛ3cõ…˜[²%èÙ'j87jBïN{rÑ}ï4DgÚbuXp1W‚}ÑI÷CÀÒˆç€F†‰Úòù©áŒ >3ïJ «Y³ ¦d#Z‹²p)ÆkbgÆØÑ^пQmåöºÃ.\‰§uÿ‘ɤ›X¢5X¿!»¡°°xê.›4Ý5%?µ Ã¥pXo¸)Œ:m¨¹öô„¡˜ÀÔz /ƒiû¡>K†gãâiôNA$[ôE‰æê߉¬ûçÂü;(†Üž UMTŒ€¾o-ÖlÎÎ~ãØð@!n†õçç'Ò}ž…½4\:ª…ÂJ@?“@ÛÙ$ïa†ÊR6íK@¬–œWëoDaèãjˆ4Æ%mYFE -Tn;X]þæQM)ñô<¢Â E”y4º iæ(†iW]¿‘€zµ‘¹½!…fâä´÷±÷š†1ê)8Ç~à7ÄÒDaÄ×xã@!As„#ÀªV:KP b­õXHˆwœâ’ÊòIsV>á.¤ìœÒ:þMЙã—ejÿ b WÐVAN;n;(I£‰0„RÓ€¢x2pA ¼…©…’òi£.ƒIë:þêd]ãqªqÛí¤‹Ã2›o!èÐ^%jäö ¤ð_Ãë'[rlAÁ­ÀhÏ•aš[‘dl°sóZ g¬×ÿÌq¢;D|òÀÂ,%”3þyBt0•…´3Hò,I»Ææ&£‘£=-‹Ä2œôƒá·ž¿¿ºC‹úàÕ$«°åï¹Tà$,Ô¬ìzšêiæä*m±d4‘¦ýÔÖÐÈ;²æíPÊx]@Ù"`Ÿ:îḭ́ÊR“W_v!‚±Mýרl¬‹5 phI¦"V,ƒ˜¡µÑ€ÇrΈ“ÿnàâ8x·á$BÊ‹ŠHÚ…¤ÒP”3Wœˆò×ô˜ŒÉ— ¢˜ 7Çá|pÕߎ#V] ¾ oÞ¡m5cµ¬IŽduÍÅ?ˆ4öîÛmµñ[‹ºy†ÏÄ`O®°¹™u¥’ÁqKÝkve´çVÖ„ ’P®-›Öÿ_ÄG\ÌĽugË£i—IÊp9ûC¥„Žxž±Û£B¬½c@²¿ïùüÜd?;{NÀ ?a¹¨ZŸ€¹&’¸§0¢ l;ÍóFƒHíõ/CÖßþìRS‹.F“ŠsVQú0ø²­vþ@[íÈF…rQåb[ëÕPÊ0wûn¿]I£é-°Ócƒ‘[ö@gr¤fÆ&®A±—A#ÅF]8™ìsÈ[É¥w ˆz R@õ×”`{ûÁ +Ç­t#Åz>7Rú/Ô¬¸E(ö ˆir¤V5%ÓŽµ§×+T˜q#˜÷…^ˆAOÛÌ‚ <׬ûÉ/ÿ×Uf 2ß÷œÇþ,(5?†±/(”Œ››ÿø,É÷<Ü8®© Ý†Îæ9;` ’d:ßýóa$rÃYpü&-$3§. è¿Ù>°NÑ<_-  9áÇ•Ä÷šRá¡7éCÝmÏJù YÙO~‘\ Ö4i†ÃMY«épX¿Ë…<²rõco±Û)’b¶pçdNûÈŠEñïÿ %‹ vè8%*BtÎo.ÌIOcÇ€ç0WB­‘`GRAå‰p•÷ò-Iyögç *oìÍÆ0Z‡eO;ÌD 2.—¦•¶ÐCnf”‡ të’8{­2PÚ5µdßîƒsµ Ã,«7+dE,'…š*kªU ÜKY|= ݱZ˜òIiÈj=š¼F 40#]tJæ¿ôˆÛÇ ³G¹²xÁÝ[ƒîæyåÁ qÚiªÿþEqìê¡7Ÿø=ò㙸x œ.¼ø-©©¡ƒtDÐ…Føÿ—ArßáÝ*jÚÚï×^HQyXÚðÚ.À¼N÷ó}­0ênËŸýÜ;@¯E•9˜åûLþ-xvÇÈ‘Wt-Á=N´uaª7©¶zTéÊlŸüHvH¼†o X¨Cñç8xT•M¬WšŠN%Î µ¢µš ÌžÇÞ&Rê ­º?ܾ€g1:-lˆ­3x'=Z7 rzéŒDèÂPÈAû=ç|–Ž"—ØbÜ £ ”ŸWÊÚÉÜ¢•÷¾Šú!.\/*°¨è’*,;‚Ýš‰šƒbQ¦Úü`9DlpsȑւM b€.–P~z‡€gsMÒŒ‡ÞŸ;¬©~½º†–—’Áˆ‰iÝu3âû<È G”—ëa´£ù‰¨l ±G䜳ÅK‰áì6Þ?Ð@VIÙ„b‹ìÔX5-±€¸ŽHþ‹5j‡³[dÈ,Íš /Åx•AÐÑKWÿÆìD,)ÁXyéP%0è A¬s3;w>Ƴ$±V„©ÑD(A,gƒÆ×äÅH !™ÔeÎf8˜:I·ãh1OóRµ³6õ=oš¬âžàÕ}˜*üW_ÌÅÝHÄ;¢Âº¯£55·¥šÃ- d¶©qàp'Mùoñ[ÀÒaó¸xX¢¡PS‚œ*?¯HnµtAXO¨õ©´Å°ÓC;ê“õd1Ù¾è?ÞûÁ ’Ü9š¢t‚¢¬Zt“ŽOŒËÀ8¶§oQ"?d@h#Ž€éô*FÃë Ø|·¯/r¸hïÒ#¬‰ÉPVÖÙÜP!©qC r‚_…±9FŠõ¯ÌJyA*Ò¸.÷«ZðïPLtöä^»hú[ÒS¿Ó{¢´ÙÌsCž` ¡ù¨œðÓÄ \$aÌÎyta)ÖÚ{,Û>àëÞþ-ø•¡ºD‡kOé@’'˜ëfÝ$TÐ_GøZâ}jËø˜OÇdxYkÒα‡È¾æUãǯää,QXnÐF÷>8ê¿K”rªüg0à4ÍQöRƒ¸Ùt••±ç=+V…ȘEQŒ“Ƨ|þêÿp–´u¡aÂë¯Õ6„2TÙ š9¾!rÅÔ¡ª^1ÐÔ¢×',úv†ÀË\è<ê÷ð‰“x8ÔQžl¤¸óÔ¼y. »wM“šØ2 .ÌŒu•y X§–Ä€­#P +÷Ÿë1 gAÑZ)ðnñìHö,€á¬0úãŸzáJùÆŽò²¦·‰¬ãáÛ4¾äýEo$½w.e20‚@ãˆçÂäR=ÆŸs𠵕 ï-…˜eÑ×Pºˆ/)ŽØs×ÝWê~Ÿð£^R €•Ô K‚¢oÅF =ºÁˆàåÇ,hhµT0HŠ ‰@Ó]„ã Ì0ÁÌ‚è=Ï]ß!ˆRœ55NÓ›W‚@ïu€ÃI$*s¢t˜Þ)f蚤zöH̦kë4/!µIMõƒ °eûàÀXtÌgò*Psé=©>Äb9ŒXˆ0»âé8b:õ ûŸÚÒ°Qž¼Ì8â}±äË ¢½ì†™Ôú·ˆ×Ô]ëãÇ'(SsÀä–t.À3´ÄŽ;©ËáCq–Gê€àȽMHòcϬð@EœPÿ¡+> ½œ,åÚ«©½Üw¢•Î(Ó1ŽAðT§‚ˆV¸¨£¤C‹ÈMøàL…ƒú¾Q†0ÔÛŽ¢Ê”Cܰ —ø§¦ùdЀ>&!V‰h@dXt£zͱÁ—HíN";§:¿H¡Gw¸8Unâ‘«e- 6b9#º–QêºYQŸ@æÐç©H†nçî:0=¡œ¡þ X›A€9b·óÀå¦`÷V ™2¹F 9AÔú¯Ò’ߘåfdЇ«™”’ B ¨Ò;+­ FôÚ…Qöfc!©9ò}%£_™©ˆ¬œÛ–ðxÊПH¾DS饌 ¿ùŒò.:ðúŒ4F-ù¼†ÍÊITbG``bFëÅÒa„è ^w•–æ8‹ÿœþxgå”Rízó%²9Ì@&²x¹\Ëx¦|m~ü^}ĦrŽlÛf•Ô1=ã3ZƒÿïÆàí ¨‡óÂ(b"´q”J¯}’qE ©÷~³kMQÿÍq‘t(¾£F<.vž%HU†…Û<`FyTtZåÓñRÖ-l6çfEE涆ž6Û'ttm”ô}Ri+QÕå`‘î#ú&<˜Ö1,L$qÃÕ~, Á_²ß„àrž å›$°SHw¦%Mo=õž+I™ßZñ<î|£už}Æ5åsµŠªÀ‹Ò‰dF²Š‚Wñ=_ð»{m>Ï»tñ‰ƒXp8ºˆEoŠˆ!ógCUsvÌb¸Ç,°ó¢¨¢ ý1“+R^p ©æBhç }t­‡Ÿ>¬–ÄÃfˆ\Žœ8B,&*†Éƒõoï´ÍÏ@i# cÎXC‹"phÊ §´ B…ò‘¶»ug˜/k¯jÍ‘'UF" Þmˆµçx­¨öS«¯\äI‰ûf ‚øEñŒì ŽÖ$¬:Oqv_ÏÜW[`ØÃìeÇs½7Pè1€0Jq§Ds@öíÌ÷£‡³‚âa‘f,q¬¸•”êäùðò‘wJPÃè;•ààç ´î-Ô°¤ÈÄ( mÏ´pä|¿”dÀì7„`žÇHǨÀWÿ¬`ÞÌl/¿‘¸o{ˆ)( 5%¼,O¼‚Ãä3ұƒ(°zä* ‹HÜÄ€¹‘xB}PIÛ~]ÉœHIÐÊÿÿý·ÿó{± é01\³¨÷€!^@¿AÏ#xw2Ÿ >ê#s\ÜÌRxÐhLOóú¹;“ÅÐÙµ€qbGQ¦*Š-AZÏùáGµP]8Ûõt^+£"T ?¨) BŽå ¸h®ÂÂA±` &¢föúâ9òÌbGùJC1È5Óww˜â ·a‹¶Ãû¸{„c)ãWÐB\²\r_ÿ¦"R£Ø"º‚=à æ±€: ¹Â`:­ºó¶ðc¸'뇇*ð$ï¡pO$ÿâ8G¯­þçÇ âGP}Ø ÕáröUo °õl™»z¸ß ä×¹ç¸NhK´¼Q÷ ¾‡ýË”± I¦Ì‘ÿɰ ŽRЃ$SAE‰ÒŒ„P¢Ó¦à\ÿ"ŽP/*«# D[ÊÃÃár0s D½ìð£2 2¦UÅAÏüK`|èoì‘9݃‰±‡Ú?ןéúê¥Í}€0¼[bsäÂS´ýýÁÞúÏß™xü¢Þ¡€*âÄÔ††ÒÇ)g€rþF§ÀfXÿ_×Ái^|È ‘Ň ;B¥"íÆF×`÷ÿ? E™SæÉ+ÿÿÅ÷ßݿńÔácT.7š¢~´ñt×ÿé Óõ[ò5^Û|Á¾U†ÒV¥ÔÛ>qâ»ÿÿì>ûÿÿàŸ»¿;…œU¨‰š¥ôÿÿðä û¿k˜Ö®ø¡ÆÖB@C¯$þ©×MÝMë=½hÊýšÍêBìJB]-ÉŽ8oB³2€ð/a ¯¢²*ºBJaUØcº?Zé ¼wäìƒhä0‚ì‹5.ûÚ¢*Bj@§"n2 ±Jp+ –58iÖÕñ :ZE¡w‘*xTø,&~ÁÈ-ÒÐL ¾w@Š ש+¨‰æRš›$;6—û¹=Ù €«¢€H ¸÷UÈvñ’yXꈠ*aY{¹N‚Ðã²K`F¾^¢Õ0ħ0>«}Ÿ9¯M5ýJZîî?ÓÃâÄß©wp_‘T~Ф€r@q*¤u¢Ú–óAzÆ0Õ‡} åý úÌJ£+åöãÇĪ!p‰qPrÑmø´¤éGŠÆÞ4Hé𪠺ٶôÃ%¬çMVnšz®â/ÌÖ¨,PjÄ5á}“ú‚¢+‚h„sÆ€)æN6-¤«xÖÐæK‰å4Þb/óK¾=c Œ¨†ÞÃoà³Tj誋ÍG0´v-ì%#r¬‚œÚ» µ¸<|Âõñ¦ªDã7¤‚»jñÆeC% Ù¹Y€ÌÒO0.Æï9û°‘s¬Þ‰¢Ëu~b>(eAÈ÷â¶.RÔ£4 µXÙÑ-èYá:ÄÔ<ÚPpS*ºV¦©‡Ç2èì¼¶â‹ùO¾þ²¡/ŸpuÐDNsG38ú}©#ÕÎ>“0¦f„8/D ‡ E°ë>Ý¿0‘ªrä߬7 þT¿€™élðuÙFi<£3ãÑI„«'/L¬pµ Å€ú˜vÓ¢¢[?S¿ÇQˆ+‚“~³#íp|»òEyb1n/;“ƒÀÑ;ráù8™âù`Å©z!&‹â‹5ú©Jâ5 €C4<çCÁ4è1Õ~2ÒÆÉ¢¼›9´ÏÈ“îÙÙ›šœ<}Ëá‰:ìµÈf2j(†JxfÐhÇ(g|ÀÞ˜I²Ò¡Âõ]xßu•Á¡ï2P£äK÷ú« *ß©Lšf$ÆÖLåæPVf³Ês¹mAï¡å×/vAå!ñFœc7Å¢½­Ž>Óü”•b'¨"‡Èh#¥sÑSÊÈÌÌhvù(aåµ$?¿ó_ŒA‘Ù¥¢»s}³@a*5O8øÇåüÔÞJìÕ30& 5ºRÂÛ l™RÈ.Ü%, QÄbÛ“]•JMJÏ!d´´jcŸÉÁÓ™h{3šOKäëGÄNŠ­Ã­çjnͤùŒO+&·žœ^%æ_\½\ïꓤi²E9±:2]¨íã†GLJÃP;QÑó"`Í|©Öêw³ ¥vÀs1Š2ÔE2~²=j$^cs6Òd;ó£4o`B%ùt”µ±(HQsÂEvQ-U3@“Tâ4Þ|ab7@/‹Õ·Æ|ý4ó!T99YêC°¸FªêJr¡>dôó’7ûïÕ ÿ¤×µKô.ùUYô®µF–´HÐ}I޲FhöÛ¸%ÐÖù'¡ç•û«&÷¸k-†@¡üµK Áç—ž`ÀŒ$~)0‚Å.L»âú‡Ñºð«¸jv™'„ËX:?ewŒïÖ4î€t–Fµ½HPW]‡|aMÂ¥õŸÆ$)5k&>àÖ5V‡/-cxV ‘Âï²oäÌÇÕ!$ TÝÏ4»¶blámK;yŸÉ©àÁK|l xÙðpCJGi­6ŒWckÉÃBð¹›Uê^ª mV·h¾„^•Àúp%ÉSœÛçtp¨t%°òn vØ«ŽèËñCÛ3B&è0(6^Z9ʱœ1ßLS/7Rœ -=‰(T3z†ŽÈô Q8 þ Õ,ÿÔ{XÁÇGiÖ©æñRmøÑ(hÂE@¼YÑS6açUág¶Jh¿…³)›¼ca4¡$6z¿¤*QnÂï×çÅB¢èÉ¿v_ô¡EÐÂò:²Žz]y,ƒJð­a Ù¨rG³G‘¶’¨­å¬ª¿ƒOýJšaýw•I-wð‘7I:ê;S pN1¥ã)†,‹LÎx“‹þõS‘_ÃDM‘¦føí–}ª|çx+é¹ÞÐfÊÑüÝGÅeòdb}„År.T´ìPG䋳1«°ÔåA¬ˆ]>ðÜ.ÑË}iÅ¢nÔöIñ¬ãaPU¸lB bzAbntƒ˜wOÊx“6€[¨ÒÃyqÖL™‡‡'û²/Y^ 6¤ÇÀ"^g²Pƒçc;Fd7ㄾƒ[Ê¿8“o'œJsHA‘Ö 7»·™¼ˆ=¡—ñse‘:r°ØOº$9xàË0ò€½ËòAÚM±NQÇ  GjS}ÎΕʹohnx<™Š$ ¬÷¬:٠̈”çËé?`T°´¬&eÀêTX2 ‚ú+AºCŽ)„rBH«´Ry$ËL³f™ˆˆ€böD•*Ë)+ÀŸZ÷OKöåaˆ“¸¯½JuŽ2çŠ>ŸQ·{â¢7ŀ©j¸‘·H–ÿc&í HSþÙ„(R%¿‹ ìtR-ni«Ò«,%rÈïvÈlQ¦µ²SV4ž…Ж£ÅÝ4ñа‘±ˆòZTd¡BHŽhIÜ*ù€Ù²÷û#sûÝ:Æocü°@üŸÿ0ù€”Ê9ŽØ(–ØZÜÜÀ(>¯Nð\F¢ëÔbRý.Ç= [¸òÑ°æÆµÿa&@ѱê„iÌ©v|G®?jµ y&¤3¤²Û ’¡dÒQZÙ ³jV?ž lÖc1¢´€ ©l¨ù`Q>Tžp8öFõ}¥leð¦•xgÎ*Àcp5wCj&ÆaþÇ Fa-ÓÛ¬¡Ã‘+G@ÇIì›u¶&lã ´½Ö”Š ߣyZÈí·oä,Z!b%³G»’Æh²ò¶k.ºÕ\¤BÀÛ`yZ\7|èIØÑn*YºRGÚ¶@Øgõ©‰ÔAîÈ ²RˆQXìâ´¶kÄöÙ:Â&1Ý’.]E­ZçlåŠg@ 4ZÚÎû8PÁX[¸“™s-%¨[•=_ô¡ų¨ßA×5(ˆ^=ó€k®®Ïï•ùFk´åsny6Caö;™×Ds ¥Fmåq[QZZkv¤É̽‹U62!À:ñ<“ªˆ¶·wœƒKDy5âDÉÍó ¢QBssœ²ýÝ->È‘M”(ËÔšârHá·ØÃsSdÛmy@ظ¾ëÎ÷!ü±÷nô¸÷ŸÿÆŒ‘`Qc¾Ç!ïŠÂ|Š$̽7Xáx6USŒ( ÑcõñX‚gàM ûc åúË‹÷ŽpæËÜ­µè% z4Ô¥ò°²0ì"¶2H8g$n'tЃü7ņrAãoý^$gNZ ¾t‰ÉMuæ¡}]bäœæ(J¢½©q5#qKYë{4eYW˜q6›‡Ž@ä=±Æòæc"Úi艀† ê B³ÅfvH˜„¥J²’·«£Ù R4©¡z‘vÚu¯‰‰Ä,–Û££z#‘9[!«Î’êß¼kÉ¡cf’Ì~çQÖG¸Ý[nðfŒ6±‹(ŠÖÀ+IŽN넲ÏC†œtÝ×ÉXšFÝ‚äï%·`Z©ÊAàÏÿAÌÊ,º3LA‡P¿Þ—6³ŠºÙ8j~¢O›ýrœgšOÆu¤ÌWƒÖñ¤G‹é›pî ò}kÔšµâÀªiåeBz.'ɆíG#»t<8Õ.§glÿûƒT¡ lq}ñ {NÛ-ÁãÏ'7±+¦¥Mšê½üÀCHR5zl’ °zÿknÄ2'ŠH›z—PÒü¶ŽÝu™u·»O÷©£ðú 5-*©–:¶Ùœ‰éq„H•yЋø.Û‰X2ƒP^”®}Å&Ì|j=e,º)3a-® Ù[…؆iñ 0fFÖ‚èéÿEµ'£IÃS¶•'š`cz}û,V ùz‘^’° JM­ÎÊ”ÉM8ÃUŸ³¬³µè­@¢ÇD¶s2­¤©“–¨¦Ê-ýîÇmAüé© ì…[á[ï`/yIÝE³¹ëF›fØ@Y‚Âüµ•J„K?Erx¼%M£ÎfÃì[Šî%´¡-tÙ$õø>#ÊÉS•¿"©Ìû qž5“Gµ¾žè~ê/ |$Äò„>œ˜òA`º2£ 97V&è6¤^´ØÌ e`fö0½¢høÝ“•ž1fX‡cÛ …j¶©üÊÌåbO'x.½ëpRjH© ÖH¤¹ÖE»q)Î³ÌÆ5J4…§Ìz b,h¾ŒÄ» 98,àr†ÎË@ Õ*ËøÜË·ÕÓçÿØÈ¬Æ\”ë]àv(€€#½€|É Ë¨¶.î¸6Œc¤áPUÖ}’Yàb½4ýVÎç¹HÇu…Á®»¬CÊ©5j3f±¹]Ë—…hB1=öCh— Çô#؉ódj…Ì æDi Õ>#U†Vi½JvÐÁª‡íi¡Ht<áSiuï¥a@ª‚ÔÍ€àTZ Yn ôA«è?°×‹ùlFå,1äjS@ε¿0'DøahKE-‘ÉÓýdÚ¥^h:ÖE£Œ·¾ˆþ‰hf‹¾Ä:–f" ,1l[n!ŽÇª(Øh‰àIè+ÅxÔÊià²=â#I0J ËnçÉd>Ëÿç´€Ùúy#ðÉWØHv±îØS(y~|#ì,û/è9?Aœ5ÒzÙÁYäÖ*š†'Ç»5„1è‚ß;ê÷m X¬èx|;Õ ZƒÚkK˜œB¥$$ß‘ÖI ç;UbXkÏÖ' s¯&º(À7M+‹áFàLÛཬ|Hëoy)w.­ÉµB!!Ä Èßeâ5 @žÏq _<Ê~×üEäX%,ÔdÑ4Ž~hZf DO‚pW³ð7ɰDŸc”–V†JAj€.2\(©ìªX2ÅØ wú(*È¿¿Xª©ª*sf3RnîJ¥èV=EÐiDl9³+!«:áopì<ì89EbAÉAîº\ù*¯‘ÊÞTæ~þ›HYæœæ<*gÊãà2A$öïŒ2§¿o‡h]µëèzØ4ô‡«µš?§ºXö'u!Åùc¸À•…n³ñ,‡ÕÉ?as5­‹?^û©.„ŽÄŒ¬k-!¨¢¬L¶šaö•û¤y…åfÊ?ÍĤT[÷•%®¼d‚?Íh N#šÞ>=hpmà—ŒéJÀiÂÎŽ¦º£¢n~¾3p4xaµ8¿}5–Mÿ2æÂí]ç½Uüãlž@{ŽBTw@ š×º㦕§;©Iš©Œ žãh°¬¶"GºÂ8!ÄBø]>#±}HW9SÍôƒ|¹ÖÓüïÊ“&¡/h.tÌñCHìNU ]NÛk‰æ™ýeŽ£¥ {N[{`é£ãª˜ ´?<0%N =Ï”dÀHS9i vºQRdG0 Œ%ªF@Æ]~ùóAìõ8˜£”/M¬Àï©dZòm}‡aQÍLÏc\8C3ßÊÜ+pâ©ü y“¼äÑn?$4¤äK©éAÂF¨Õ  ×¹¹ihVÌgµ×¶¨EÞÅ™À– õÈr%Ž hƒÔß&4áZ°Ÿnˆgvµp?<|Ó„› ¹aØÎ¥Ù$T4G¯2t©·ø=D±¡œKªê‹\ºà ¯_#að­ )ðUuÔiβ9³$Ü&€K×£>4iY®D¶@œ7#Ú æqt·žŒ±ã*O€wSì<\ EËYH É ÊN Yõ&°LU i³0ÚçhûiŒ+xH6|þí“I½PÎB xŠëXxeK§:ö"È©p|"h,x~vh(щûìC+‘>¤jxñŽó#\Êý¬ä¹)7Çâö<7ËpÔD  ¯œÞ 6ßÊk^`¶6€ÞÀÂâT\ZÏ‚|†«èÝ28"›KÕ->ØÌ¢Y˜ßÃ)J¾ØA¼É¯ - ǤÖb(w¬~ZÓP­>ÆìIи([ì{@¹^Ç¡¨4Oнˀ¹!¤2"Â>rt4 ¦r^ô5'rÚ™IGK¬7 ƒnEvF$áéT<çèHq› 5Cmà15 vѳﴄhμSøŒ{ë8`m£»D-âú"fzⳡFØH*ÙÿCÿ½’´­98 #{ÁR„©2‹c9,AOË\‚ÈŸÕVÓ™ÖaÈ]épð`*[ÃGÁµ§ÌIîîâ¡7uñË ½LT2‰õö|ïrAvFÄ\qtñíˆÃ9¨õ቗æÞlÐPl‚šæö˜{°¿°¹É ò´˜¨\õ%ÃBöû`åÐI Ìò/0ùP.xµÛ âyaI hÆÊn?ÂNÀ.G‰&8 —°¬ö±‘ÆÉ7ãu&3öÅQ¼ àj)ˆá¥LL`„âYí>¨œO(}pÄ’Å€(ù.ëŽ-0Oø¢>3Þ§ ñ>‹8­ 3h|rõ] '›l¸)4ß!bn-o ׀ظøk€\$qÙœïs¼ôÈp@ªaå)P$Žå 9Øhò¿‡[ÿ.[`ýµÏü²"‘‹`Õ¨Êø*Po€1ç‰àD§M1ÄÐ).¾øSY½œ‡3à(éNÀñç?~E/ÇŒiDR›ìä~ï ’gLÛÿ´ÿÿ“î$÷Ýß ø£JƒØ//d8<ÖÕ<y€)cL[á !"÷Žð.``Ü@o9†¼Qâ ƒãd.SªëöÇgÑð`b: Íå[?½V$¨·Te÷ÇÁüðDW@ìâÂúIõm?¢ör3± ¨QeöÒÿÀÌóãÖÇYoýüø$ÝäGSÇÿŒ-®û»çpDæ€l4·P¹ZµAËüagü~&‰ðaZû£¿fÿãκ¬1K~a¯¡= Â"D¤ûß/êzh‹‰l!v†É¢Â:A­×µ@d"¬™¨fXa¼ †XZ× Ä·89lÛñ«FI\Ÿ2¯¯»ø‰@t•²NUU`é„.… á©/€ ËY=­EqÇ’ä2à5»0­ÃÆ([§¬çýMÅ÷Dõô!‚ËÔÄ7èP‰UëŽ9Ó.- ÎBK0«Ô °p"¬Gp€¼ì´)ORzØT;è÷¤ËHà3ýã{ôõZ*µÜ–f)‚ù»x¾šš)U棪L¡š¨Ý3ÎT'빓ã¹Æíj¥ŸKòýûŠºÙ´¥µææðÃÐeá6ÜñH]¨c ù~CE<ƒˆ¦,€ñ㉠šÊX ‚É ›í d"Iÿ‚(¤Íj5ÒþBK€¬„„œ8¤šÑÄ^J]õÍc5Gb¸,&ù‰åhhŒ×àÔT:¶j‰îÀ¶/Çz‚n¤Xœa¤ÙבSf÷?î7qåy4ÙÑYI¸ƒÐjÈn@£Ý Ç"jÞx¤.n'3ØJþ4ª@zÛÿXÛCR?WwÜ™ÍU Jbµ +ñ óÀ¡x‘Ý6Xò~UjD²¥ddAòìAná*«x7°´Dœ¯bës Â䊺+O¸ÏÖQge£¨@™¾ùØ9nÄ–%*Bœõ)Äo±`Ùºãm'ôPì¦'Çuáh…(pŠ+ lÈoð;íY®Äê뉟ʹàzuxL™þV8¶ITn<ÿèðØÆÓ.Wy_`ð2ê Qlâ™ûêä_ŠŒÌ}CX¸óHM1v;:Ic7Eg]ëOŒä['€’20›¤^œúšÉÈþTS”4[@¼–-UðUU-ø»]¿¬MF«×ÁÓGFƒð>Ý lOªkÎÆúõl’Ã|Øóš¾dd²¿áé“öâHÚaÆî[(î¿Î0À10ê|½†ƒMÂüªH¤3Æ~výÃúb%ðsè³ôÉÌä¡õÓQ}#Ç"ÒF%œÔõ¥ÇIÙÙ/#Á¥§xLŠÌš‘ýæßŸœû”ÖkxD •a³ršÛ¯šÒ ƒHëHn8eB°ëã¿az±‰lU†¾hÏj"ÚQ:PEP$"f}/LˆÏU^F ]‰Žnˆ]™£C¦¨Ú’«Xo!ÀŠÕ Kp e-næÃ’ÜNñYŠùªÚ CÙe¨C¥L’ö Ÿš™™Š‚GZEØÑƪD˜OI©“-ªÿIÿ¸?XºZèMWÔEC±tø˜t¸­3òÃùà¥úÕrÈ'ËCT 3@ÆX±.†Wu¬»&ÃÙPWëEºŒ 0%º¾;ho𑝅ýÿ&W³Èd壖Á˜rÓ8÷0$Ѩ¥ƒiÉYi½§ ÐQW‡Y9ùKäÊ,°¸èÓÃb‰©éÒÂ7H‰áQ´àäº]»£ôãà†¥#„5íâù†uF64eXðÆP¡ŠðÍC*Â7_„Ìk„L&Np¥âäûcY°[„DèmtÕ £àìå£Àùø¦šÍ1ºi²hHÎÒi’¡ÄݬK…%5÷Ú5°âFÚNR}&"êÆ ˆ~˜}ªÓø–œ›¶bìË#Ø#ê|L¬Ëš™ÏŠ®½),o'ÐnÒÇ| kžNê­ïઉS0SìÆ4CÕìjLÇ+âÙA{D̶¹å¼UI4âJ¡ƒà+BÛD‘ìï}ܬºJ øh“@E”HfB_âÒ”¾ñ0)šsŒ¡wÆ&pUÂq 59¼§NɘÔã±¹fYLé¸ñígS¦šü!ºTÍ›7~åæ 16œØ—´ gþ*ç9â–¼ r?Î +¿Eñ‘Bf·J#XWÄKŠ‡ÊøñÆ$Á¨åþam3ÿ-RŒ˜ù¹˜¬¡.4ñôZª#úÄί:xÌ57`+`Ò_Dø¡c¬FµâÛJ¼F’K/híòŠAÒ&°Ö.Ê‘a0B¢\<Z#ÏF8ÛyÃbt"ê}¥ÐÔ…CúaRœ6ßSz dàL†á«!¸w,}(9q2g¯1ÂVîk´÷Ãdñz`ƒd(z÷ þfV¥&öTѪj#2„£ÁJLw­èœÈ)l5Idlv ú«k5ž!‘Šÿ¾%¥…Šò–æ,ø["5_«JI&j™5ç¡ë·Y#/à›øo nÛ‡®ôd´äN¯QIÙtCÌíþ ãHS“Ú-ôMÛ`HדÃH ñ£Ü‹‡¬8¾iioÐl¾5b~Dò´%4qGI·!C¸ÚòV|®z@o£˜h€±•×0LGH0IÓ!ú¸7MBâJ€“Pˆg“’˜m‡ùhn–­”' Ü;wG%“Fغîþˆ²_ØÁ…ì(äö ¬/Ö\Â0àlÿ¤Ì"Ö +”¼Ÿœs "6$¼Äc¥J!ì‹Q&Û6ïz9F^àóKU4+`(؇ç:Í|(^KFÔf›E8(P<7yÁò0}*?¸X åCU¢þRw¹‰òËö›ä¼&ÏÞ¥ðø {8œ™qáÜ´ AÉŠAIQbjeŒ9ëpNŠu¥Äl¥,§ˆð2ŸýV:NÀ€×ø«mW¼ï‰¯Sæ­´' ­•f¬›Àãî ÔY²2)±±1RÑvXjÜC ¨–P“«Ä žT‘ªö& ¢~Þ­‚0>u4Löœd ˜¤Ðà<³“&Ûÿ"ÁŒ™ ˆ>i6«ÊçL»¤õÚ¸úS’ †r»žÝ·gu’ä(8Ö‘ê²`\ˆ˜('x)ù“b µ…è(7nŒ®‰ùzX‡Ü—¶–tËgI¸ÌÒ\lø‘ÐʱÎ÷ÿ8Õ‘÷]þSÌA膥xqÄ1rz5r`d„)ʻѩ_®*+ü ûdÞ·zÿˆ‰Hdä€Ô$¶Ã‚ʺí¬ÛG¡ZœÅmEÈj9Ôš´Õ5è‡räêÝVCý™„¹°jÅ¥;Ô¼Ä ©JQw\ 8ÆÐè¸<¸L‡È]ÎÂøg+•¿Æ‘ܵ›äïx ølm!䂾5µ+Ñ”B1Å4çÅ·Ñ’jˆÀ;¨¸Ù&àö=ñðû:ÌõgûvÈ jOYio0NÛ¦ðßþw´ )=i4³ÌlxoªÇÕ-Ķƒ±Öuâ\§G²1²Zp»ÄЈB*R|XŸX«­I ãfBu®a™W ÖXKT'¹ÐCHGaµD®ø—µKj¾1‘åqO‚󉫦¦¸nb·^ùˆ2fÍ>VÛë½+!äß·z!À¹Ñ ¡øýŠßÐ@q3M?ˆ÷Rƒ†Öq†×å!‹9äzÊß®1/²8P´®+]'.fåÆNX^<*œ-¿ñf=¤PBZ7¤ˆ†D´Iýœ¿¶¥G[’Ê<lÔÁ“$¼Çl‘Û X3çÿà«.|6Ø· :’BhkýðIY€úÊäïCü‰Á¼ÞZ³NT¹3F2¶<óxhÆ:V_qõ|ÑYm+Õã”V¨ÅÑcÖÝèœËXü• êfhéø¢):£eEÙ­¶w±âÏ@©H…‡ÎR»ä×r¬¯`÷¸ÙLJ ŠÛM4D©œÈéÛoÃ$@k‡íB¥Ø>‘Ï–Ú9ó2LØ‚9{·r ôá¬I86$ÜnÁ/¨†¤j‘¶ÿˆ‘ƒX”Éù¯•5Ê–z±Î¤îq¦äü/xlF X™s6D^%(4^Ȭ.=Ak±À›`víõ°*tŽ™³•"I>ÜËI­$Fà n 1«{·á…¬Wb(îv2bhÆð¥*£ 8Ža¤? n³f±B3s³†GÓnJp"ˆÂ4ËþzÒhZ×­ƒº»"¯êˆé6¶ª™¼.%¤NÝÏ‹âS·uÌzÛǦJ}Ä&ÆAè è‚øtpâAY¥7|¦ ³T]-’ÀrLÂÐÐ6GÌ&v¡¤Ap‰І(XG:=D7-hK6ÂÑmè/Mt·î릮2–­h)±Í|z3ý“ÈæAm»Éx.¡:Iûj¨%Ka׸ÌÇ-Œ*@þá–Ô䵆ªu›ür=¹‘YQÀÉÑFWJ dÄ(EQº[ ©äYzÉžwÁ”Z4ê¨Y¡ },Aù™£¤*²Ñªžˆ›—Iøy¸ª•`°_Áµ‘­gJ¢ý#Ž [„èÀ¸2aá ¨¬òƒ‰€öÁ TÁ¥¾:."Ï ïÑ Îx¾EþoÄ„™Bô Þ-ÆH…åÉ£ýÕ¡¨ÃU8+ߊ“i,Û4Ã[ú>ð…EBf·#þv¿(a)Šª­Zy›¿%i\żÌÛ="ÎØÈ³¼‘#Ä.;›Í4`‡¦ßܘ¤,š¬‚õ/Ê®œëgx¨8°Dn½%@íxîÑé;èáËd0´ìÄ}ÀÕ1¿ØÌ€àÌç7ž:zñ“Ö9æ.I›…Zz¼”ÖaPÈn[’Â!°¹ÊÂÕÌÚf¯ÌMöƒˆ—ܺEqmo³ªª¨·ÁêÉ5h¦Á»qÚE§. ý87c]uNŸ2h XRŽKË\öÛòçƒó”QxwïÀ9¾À0+GÑk¢Œå‘Ôz±%Òlàaräæc¡˜5ÝÎÍiiYáÀ596:A£ö .ýBþa‹| ù?Ð:´³ÆÊt‰]ßAG”Ûp•âž!bî-œ *Sh¨A½d}RŒõyäù¤Ìó‚0%–ö€r‡Ú{»X °ÿ…Mcê_TÔ èØVÙ”,‚';¬?Æ\¶ì".bš…þN¨Í ̈š *ÀòBÒzAñp|„`¸n–AãOY‡( \ª„) o‚>ذ!ñ`APЉ‡„Žx“Ç H¦Fžû‹Æ®TÛnÐ"»F¨9ƒR°ø Äчx@ôˆæ§B…>€(Ž:XPîÑ: UX˜Sëß/"žC•sæÃ3bQó‰Ûã¨=`0§0{Û¸pNU†apXÃ×ùŸPæuDo`jæ=®³]V­h¤+l“Í“.kÜq,íW^@Ù´(iâùÔ\âm8Î^0b‚÷9+|~×X6|Ÿïãæ ˆSê£ÅçÌïÅyš—þÍæ–æ`ÙüÍeV,%-l6`·l¶ Õþ£ ~ЋÈZì-€r†[ |Ÿ Cºèh-bvÁÕ1¸²t$§à`RpM†Ñÿb^Xû«ˆàk»êè9D ÅZ €›‡¸€¶“ˆ˜z7ðÔxq:ƈ2ìn ›m] Dyî³¢ aÒ§ÌAzoð¡æ³ÀBHXV3i« *»³€L‰ú1!TîðŽŸU€Sk Ò“¶2”¤ª7\ ÄI©Í°qp*Ü:$ÌÁϪáŠÎð(Š/ÊŽLS3,ˆx-<Ƹ j¸¹—r!¨” -²KþOIb:xÉ룾ÝÊOàÙXÓ¶eŸÉÄôü:ñ(š ‚¼]bãÆü‚ݸÎÜ'ùÈÏ´Tt(d?|¤›€(]ð@"¿ÁâwR¤ûípX +Ü¥»Ë°ig–A&y>³Kˆ¨"]寑îÕêRÖúcaKnÏ·y@¦CÜO•ˆsPÓ‘â~à6¶{¨ãŒ0£@0udÁ+«sB°³I왂³õ Sñ¿ªñnÿ£ì!”~ÄÅÆfSç0fñ`c’.F,8îi…?d˜K8ZAÒdŽ¿ S^ƒe´óA}ø!6Œø5õÊn[ÀPsø $*‰a\f Ófã aÒûB_­‹É¸›&÷¸‘£«¤‰´í(‘NÝhKÚ'à3õŸ8PßQ©ÿÕŸd3'"9"3t‘·$Ü(¤'¨?aLY}ª4Fßh@†¨PÔI·¼)ô¬ˆ¯Î„ĪV‘ª«¾±c%o .bz.£J FñÒ¨·Ç Q†;ߢE³øû¹•-ùƒóÀœHÉR·RY¨O’Jê $xÅì¶€*‚ÁÌàY‹°"ø¿ÆF„ðíeß5auŠÇr­DËMQɵŠÑ4í»w÷DAÄS=I펈$ðP”/Ìž|Nò00ƒÕ“/Ãy_P©|,ûOâ*h¦À•n) LacAu ãK½»ùWõÙ¿²kœuh,s––uKo=ÀÕ;<y‰õ`w̼@² ÿšŸ)vDKy^’J´j„iFO{…Dö+ªŠÁ`à™O ü… ™,y®­ >/Ž ‘™(lé±^Ú×ÍÎXÏ÷918Âb¥º §íQÁ(-š.œ=`Ýq9ů_JTȈœ4ðX®j=~d¸î„AYÉiÚ'¨‰)áÍ97€7i4, –»3BE 7F0 Ý ”ÄœãÁyaM02ˆFYO2ûÐ2­»Šƒì’&Pö¦¡&ÄÜ#få‘¶vÇ’D8–N”q&ÆaM§^ª/p‡X Ö§¾N…<è3¦¡„ zþUÀ ´ÊN2R|7'}'ja:6ÁÐ9ÈÍnk”TWWaìi4ñDâl¢\y-£¾@ÕšùsØâ¿YMMâ|}¾Ù”:UÏß¾J&îÁéµ×1ºÑSÀ÷]°h¸E?ùËAõ:¬º¬— Á6iþO7¬k®Æ—?þ–x\#,Æùµ½')Ñ5ÏÜb<»[ÀƒZ; •Äv³÷€€ïЊŸð6âhÿI–* Þ𖲆fËyŠzäá‰@C>\Ì3”‘³–­î5û±ÄŠÜr·`IZ3 äдc–Ï v±Oá8 öš˜Ã3Ý0ãùŠ2;à.ÚH=ë<ä¾Ã¥nÅ\ÜYœ´4¦àΖ3Œ\¦GžýXa»)LR@0<Ä ?Û P¸eØæl4ní ̃F¸ØÃqó/¹u_/ KÿÈ÷ïȃ{mùCáþß¾ï}ÝáJ$ø~4ͺU•bpœUc?UÿØCS{¿»»~+Šè’¿ú·¿~Ž'ÅX«ú¸Gþ†ïˆs÷E‹É©ãÔ >’ÿÄ6gƒ7½¨ï’Œ!ˆÃ,’úQGÿÿ…{»¿üé®ù™Ú×zow¯ª|õ þŸö»wôÕV”Ét#WÜH#C({½© ñô—ÇÃÛ5OCˆÇSAåzqŸØ2 ¹ºr4½iÚ=ÑJCø X”¶}ðö!•ô2 š¹±£1$ÆKbΖF˜$§4òê[é?6ÚÕ )Æaÿ_n¼&UŠ™—²P~½í4”Vl’u׋ææù¤Ò):Õc‰ØU1=^t¬ã6hcð_Ñ!Lfò_¬#˜H²¼vïÇí·ª-Éf¢:¬[ «Ï5"P´ÉV‡ÌÙ  ]Asn‘VÒ»›E'VW¼2˜ ÖƒTݧ“ ¿rŒŸGRhä/d1¿Ãaúá`7æÁʃˆ°Ku<$,`£* §ñÂMƒ]±jm ýË–A.;HŒf†ê¾3úq¸¸ï!söâø¢±Jp´°ÃQe§­jẖ{(Œ=²ilB-£‚ÏÆ£æñ }”{0gضÁfu¶©@nziꤥÿ¡í«L\œ„JB}ý·YþM¢ð»4àáPàG^„YfªàxHírMš¬è ÙLT:ÓfÓo..œìÍïŸæ*ñÿ­ Ýà.ÑÒØy½@˜/¤ ’å$PàŠÑ¿3ú•ÁõC²·Ì“\æ¶x»OH ᨊ„í*iE”£lckºýÅ[b*´)™æŒ‚çf™MIâ~ÊŶmª,ܲŒ>Þƒ¢°áO? b㶸v¥gXù#ò“MºÝŠê7Þµ?sNÃçfI@käeŠõCá¡©=²U»œÈnd,—&ÇæuFðrh ê;â®›÷cR\„C )Pãòv o:Ô¶â»v˜lÙp€#ï΢uØ>ƒ_ÙX"º•~tzÅe©‚)¢h˜ ò”uAJú7ó6Ù0‚CR¿,GäsæÜrØ?sÄð·˜&,àS+Ç+¡¡­Bjlú¸JΚÈ¥‚-ž,^$zÀ¾PPÆ0–ðgÒÕs;u/ë!•ÅuÛ4‡î“€g¸¶ JXz¾ðâ¶8“ãÕ÷E"}ÃHw!HªE´™í©”ÔŸ%Žn%œœÔ+|Ê«©;ÛQy³AF,>X–ù•ÄG–§–yÔ$#³=óHOrÈJˆ{àIÊfc_%5ŸÖëß\òž¸ï(ºR¥JÖ}e TÉLÕ;TÒÉ9œ¬Å8QÚ0OØ´Ÿ¶ò? P÷˜ž8OﱡÛ~¹ÐêB#"ÑGVJ|T |É?225È )üÂËõpi&?q2ºF¾óøöCKkf,Šè2œí¨ÈdÓ "ôš ‰{Ñ)`òA¡¢K×@õ øÊôã!+UbýW&Ò5¤°}?›‰.ß´¼8ê±ã°¤é\s *5Ziå­ªjF*«ú\mJ5¬GÎ2(:áÃÓH L¥Â?Ó—t#WØ„åÛÔ”/ÆØ5IA +)™£‡G•8G¾_‰= À#EªT•hßb «e‡ÀÁ9øeÿ±Ð›¿ÉÕ x±\>AV16ä lü‡üb¼Ñ£Áª ¡”kÓ ¶è ‚ýQä{{íkç5IÝ PLö°³p8¦¤DOÁ€í05¾¡iÐø—ÎztŸ¤ŒŒ>›2Æ c3‰” ­×­Um¬rófB7 NšlàÍÕ„vëôú ƒ9ˆr_ ù• £1k6vH„zrÍÊ$ÕlÁ½‘þ ÄÔýÙÐíZª‘mîÀ`y¾ý\Uô-ÐŽNcâ‹Ôº‘°öε¶2ÃâµJÎ3t¤/¥ýTå~Ù×gB2^ÒÞÖš ö†ˆÕz:Ñ~j 35š{:4P5ˆ “ÔBéíÉ¿ÓP·ÏVxöÚâ·ú5bÕGnÛZÜQÃ#eI’™côÓ›ƒÜ™í¬6œ£|¨a-F½ åÈgDBfQw?%XDC m¡/ž^®¬^Ÿh[±s ÛÄX²4j1X­’ÊÅ‘•üŽêLe—[âä/]Öc¿¡¢ŽÝ+ïK°MÐV픓N%Ù}0‹ÐÃ^4CÎ1M›”>’´{lcYŒî’વjüë£m›Í:ç#4~ÒgrÞUe¦Aˆ»¾)R€z¦Ý¢O‚‘ÿ9œMJQo“1€\ì€Õ‡ö°M ½C©»ƒK 9ºD’KÅÐXhŽÀ…ÉÖ© ¡Öh ·í抣7 W/à“æ`cÚ2 yª‹üŒOa~³v„[D:èN(“¾¿ˆdT$iiŒ±£H¨¦>ð_àôÚP‰»'3‡ÅGJ´áå2ùÖ«˜<JV P4-!ĉòœ a§š¯õ =§Bb©5\ýÿ]ŠÒ ô‡º•‰£!&¡ ѹëZØ—Ø9Kˆt&Iwî¨wò{(þØñb/œW¼õ¼¨4É•€³&°) ªÑ"±Å¯ =eyZ×d=\7OñÀ¡.éë`¹°ï§óï8lDë„ Én>h´@UëAþˆÙyò­Âé1Çp(bzïl˜0)0¿X­“ÁÅ®Å()mMÍCS¢kxO+O}šgÆ`£•Ý5ÙF>væ ¼ËJ:ÁÛüPŠM$×d92¦+IH¥.—Rr71 Ï<8!• C.tÑgèIÎjªÙi¢ôM<ÌcäpQ¡ˆÀÉÐýXOªÇè•&‚<ÍKô­h/²ŒiUÒô¬¸–þBÒÜ~CZ[ W¨>ÆBÙ Oþ&fA†3úiúËè7`:Né+Y$´Ê]RbwGž0©äV¹ëõýG¤­ƒÇ*¦# ðÉ dkbI¢0Çßþp`®@Êz o,Ë—¾Þ††6MDÎIe¸~¥?Ìû©óì'zOÿ´¤i]`ÛDûJÁ¾5ˆ ÷_c`¾¯ˆl$Wj°¸­É5¦@"íŽT(rN3”ì·`ØÝÆp_— ã§D6I/d‡,LL}—å'Qe'h(4ô¤sŠ/ÐmŒ>à, ¥ºyÛÎó§1pcIiðJ]® Ø; bÄæ;í÷ï‹0Î"Ñé#yçõŽ¡P(r›g—J4H{`øjgÓ±¼Ð- ñ¦í+T~®…ðk\ŠØg'ÕÕÁÁÓ>ƒËøÏÁÛE¤µPwèpÃÜ:BÔ4ÿZ-:1ÛhÀìØ{ø‚áØª4ÁäŽß£šµ*y}n¤ˆŽá*`œ*ÀýKgÙP?S®zÀŠúÉmÜj‰ Sá ƒ1L€—eÀ¹#è‹ y­$Áü¶yáAF6GÒ;Àq˜ŠDWm«~.Ã)o…õFÝ0ë°âš±V6Ä úûùåϤ…éÔ{]ªlc @Æ5(ÛÆ3Æ… ™”ÂÂΨãÀõŽÆq%Û‘wí«êºt‘ÝÈs€rn¨ûN”0'i€°¨y:–ôHý-J{#þÀÍp–KVž†R@(™Èö^Ø’!0ªU„¦*›ú=|©¤•Óâű8ŽÆêcH%éÜ’^±(qo°ºx÷¬Ð#=nµ~´áT#¡ksßD~‡Ì¯¾t1Þ皇ÎZÐA¿þŠ3J<Œ¯0÷’Q ®Ñ°·L&OÀѱVÓË„i!â^Æ­ùÂ3lbÔ›5Dz?6³¬Í#ų EEU  ”Ñ;“Œ{B~›ì¤Á¬¢Î‰!Ê9+ñkÂã>ACX±ní¥'¦¤h°Ïî^OМ‰j‹ßÓ×fy5·ù§Ïå—g£qÅÚeíÓ '`[¦Š^Îßiá ytA[VÏ93 ]îÄzB–„Ÿ¸öž‹$TlI6¶:œb£5cóg¨Ê­õЀÒ^VD3=œãeñŒ+ü±m׬DHmbXSM𥳀° ]É->L¤9DaÕÕúW«K¾µ/$]X‰ò¢Ò‚¼çôDXÙC÷ÝɽÐ4¤ÆìF¯ÓgSéê'¯æ3s ˜Aã(ç&V}sØ9ŒB©ØÓd vôþ„³aÂ̘Ò6Þï° F'¹QoåÉ\éŠ(Ýdñ+Ò›¥+XR”àĆâ=ª} K¬¬Î@ Ø,·,%°™wÑŸ ½i@ûŸ ©©Æ­>I[Å AQÁÖœ÷ðæQÚ-®ƒW|£Ñé7dt°5—ø’±»ºQ`–zÏè0£9ˆƒöHfHÔ$#½ËÍ÷X©ïU•ÉœÆUfb ñkåuLJ/½`ÊmëðÉ C£ #;ñˆ;“¿³iBëß%†áYµ‰®6: à«ÐH1#EY$™åX’ù•2pQe„ØËjƒ¦J,MRꯔÞÐ[ ¨;v©„´uBÝÑŸÆ%ªuæšàHõóya¨ÛEà¿Çª)ºz¸\Á$áT°2@t['5Rðµ(Ã%l JÎé(æÖW+!6˜÷ÐÞˆ=ÔJ·ÎnÀÎ~£õ0^Ö¡¨cMÑz8}E›mw|h ;ª¸XØIÐ’(ÉOÍízšÂ$|Ù¢Â"êNY,ÑÑ6‹/KáF›0|ÁØß'ío^/÷–ìËûUÞ" \Žˆ_ m§bÛ „ñõñ]†T“B«C _1]òn- ©qpcìÉ”È/×0u?»¸lÁ7H·F=Ò²9f¸‰œ¼p8œê0Xƶ|‰!kl½@4Ìj²J@ñbN&”1Óên   x€£\bš@­‘>ºJ˜+ÉÌú†"˜û#v6•à ¤-! Lsò€” ÎyQ®& vø]ë3gâƒÉ\™égd#ƒ¸Žq. €H°^² þÜþˆ-Þäô¶f†wã5`â ÅÈ)D·YB©± —[õh4Ô„fNA9Í&Án¾4‰ðš=A@ZD‚Õ @±P/‘©7ô°¦®~_¸_Ì”ø÷ÐëØ! ‚‘¶q–hÇáï=lÌ;àˆ›{(Ԩ①‚*ÒÍ', ŠQ0làœ X.ÂpkXLv_68Wãk'kËÝ1mœOhõ?¶ I -.ìO»Q5Í›óä®",‚âPM)è[~“ð[ЍÁgUÀ&¨C$*B‡ÝÝG9²uR?Uþ41lW\…`u¢É@|5m¨Ä6ýÃø2ÝoÑAün2Ç-óY©+ ?ýq¥™D½R»œsà&6áœ`á!q,;WL$:UÁrF¾Ç/Z¾]„ …†$bŸ¼ ‘-Å«ÆÅÁÓsk%œÔÊ¿¯€ëïw";°¦…MHD3‹vŒ'€µlXÂÄa¶Ìa …°y«î™#ñ ÿK ª N„Ó<ø@C˜sfé`™Ÿt ½[i÷ä nbЧõ>ªerRòJH4+v˜:r Æ=àpŒ©P¶°8Öh˜d4ýâ Ø(£¸ÄÅk±ôµ¾ñ¬ìbÐõX%X¾ZÏØ/ˆö½-GŸYІ?YFŒ b49øÓ'ëô[ཀÈßÐ-Žv¿ù›„HÆk—N0ÑÁ!tåò4¸òæ-a¤[aDpS’ª4:æ‹vcJCêBw‚ðÀ@Ç/˜~ù/€]z&X'ÕÓþ 0¤ W“#‰˜Äàï`U·„Æ*ÎLJ¾Â~ +˜VÁíÈÑ1¾U@Ò³1lM`ÂZüF3ÝØ•;Bíf”t©âY1å4dŠoÜ0ægR/#ÒÁ·˜™hÄbt“/Â;æk¶&~ALØ$NNÃrë0ßLŽ#ÀÖ ð‚³óð¡ÈP-y ò“ä ˜jšjº÷~±‘Œ¢LQ®=¬¨k@SL@V_ |zªÓm̶åùæ3¼1à‹k€a gHÙ‡UðvÕ¢ÿùï?£ÚÇ_¢'øË­ÈÆh5Dk–ÊIƒt4¡wÞ¤‘,HsûR¾Æ}¾5­Ck³…Äã1Æ ¸J™~¿—à±̧-Ò„A^˜‹vîxH+‘¯=`§¥:ó D§ÏGDv8J>x-ÑØ'9óT”ÍQ Hœ4/a­vÃ:!„ÕOÖñ,%§Šuþ6…€`añ/ ¯å Fƒ¬J2ª>_xJöÁ æ¯7“Õê–9…%rùF_¸ j p¦ør‰\nò ´H3/^™•ÿ̨­‘ž[okœüƒ'>¶ë¼VâOúMúµ7!ÉÑüZ§}úa?Á•’©àER>˜òµÑ§£ÌŸO;ЗågîÐáïé¿5(à÷ï¢]³þ‘nxX$Ê!ªERyôÂ@x̓æiÿMˆcmÍ÷´â†;ŽÞM"¾µTtäz‹Ÿîljôo«:꜂ñßÃЙàËñ,òø@ÐA‚(î©+ “JoæzDÀ·NŽ”ÈO6Ì€ªN/b8W†Dÿ©(‡ÿªÓ"½ûŸH”À…ƒ ´Þp9„ŸÚ2ý> ¬ûWk·¦”<ø jœ!ÿ®gÖ ÛNEGôè&ÞåÊ fdÂÈÑ©”†/ÀÜH±uÈWT=xØ)(ÊÓBÿ5$Äú ÎÉ\âRQ·ýïž ÷N´Xr Œ_Hwž3C5íŸX4¿OŠgdËé–iQGŽܨ™õV2H`Lq¾Àˆ8vhCЂõà©°Ä‚TXñÎ&† ¯R´°W“™d`@Ö` ðƒº°_ˆnc÷]··@ér‰:ã mnVªýÜ¿áH­“Í]6)&Í#Ȭn¡佤Î_í7žP_ « @;©cGpvú޼|8€`0Rmµ™õ#p`äÌÿÜ™Ø*’{dC¶mbŸI÷Ë$4hµ‹o÷΃A˜‰#m:—9ÊK½¹`GH_bÇé'>Á¢lØ>™33Ó‹AKøÁ‚²$A ïlnø é^k"… ³Ô‹W[\˜ ‰Š¨Ç‘Fõ`ÇqKësàÁp*MãTF”ú!W¡Í=Œ.2mòH€Åõ;5+Ïb œ…NÇÈK=OúNßí=J œ“l€$áÈázïßÿÁ˜{óS…ãyó:£o2 7Ôf:‘È%¼éˆs—·ÀjœÜôiÿ.B1(¨jC\`õ¢w–—åà–êïRjxBüÑx¡QˆFó•![Lˆl¦öK BØH½MÏð ’ bVêâÏU…Í!£{dðò÷«ðÜ›jʤy£™xè?k„dþÕ!è*‚|­ºÿø6Ñú™+¿'Õ`;<³T~t©v;m§ÔOM7™´)Ó‰‹±,B… ûk$mu#ɤ+ºA]—&ÌÓFÞ.ʯÞXm[qÛ¹,®Ñó+ØÚûk1tâÎÚFyÐ"|­áS‚mhn€NEô_¡ Å}­ªÎC+£PŒ•~Ó‘vÕ8þ§‘áO‘c7 Jž*¾±b~ çð&¹oˆ$ÔN€ê:6aeœ”B!„ ¢—croínÞU“ÍIŠÚºvKXP‡‘@ªZcr Í47Ä"Ø4D@µ[D ùËÇDv€jèúè[¡mévsÚÀ 8縔 jù½îãgÕûÙ¬(=ŒAYÀ"Ú|€¨Õ\álò\Ðs‰e°`œþôÓ‡~"Ї»" ÕòÖ­õ݉ÉUŽ ÂÁì®x~ Oåÿì£LÏ÷æü㥉 P¹éሹžÈ–çàc^…e¾!ŒÇ®GÍ(Îä•hRƒÐµçÍ¢oÓ¬³¥Óª“g3°S(ï‘­N•¾Ujuÿé÷Þ[¹«Þˆ¬—6é%r]:½3ù«\B‹²·œ–† ¡µro(^ç ê§ ¶Ç b^0›œætÁ ¸³òLšI©~ qÁî]Ù|fœ„àµ#ާ—‰ÈR–Ádó£…®ÑY6ŠR–+Þd4+'¥S4‡Ã®—rýw£`c ¹ŸÈ^·Ë*âò¼ ¬úG.é(>ž"ðÜ_꺗ÃâÐF‡tMû6‘;ÛAEžbà‹ôDa±?=ÆûÈñ|I†ëÀAJ t!©`Í!½{óÎ3?2ò•¼Õ'àr±Óf r‡rE±Oƒ5Y™jüF'¸TS&ë¤îå¡´Æß®yºÙaæY¼²*ê«ù¾òf%‡4¶qHM„ª0±ŽMiCãN×>俚ÔE)ŒKTBÎËT½dº•ÍY—/ñ°·¦=N¸Ÿî¸jq¢û]'Ð+øëÝ?ü㊥‹³ZÙn<4ãnå‰q¦#—©‡P½¸\ /EvÚpçPmá’§=†Ô?gª [p<$(^{ îBЏ¢ØßR«¯\1yUµ«…ož±æ€ýP;‘ÚêwY`‰•OÓ¬”9ß0ÿ”Õ‘W¤™Ùɽ$Êæ2Ž?RáÖëñ9,Î:’X'B{4C5'"²*tÀLž„f ØAeáËÊM…}…÷×]°¾;VJæZ´ÑŸž[>Á¨³†¤!4.E«ÓÓêF@X¼ù®'·¾VŒ¿Ê”2Ï‚3(6<øÇׯý#ÃG%;˜¹=$vʇÒMá_RÀŒm ,ÝoÌüüêR¾Ãâù;€aª-7Q6áLÙ8O?8Æb"‡0e¯¥õ$”Ö×9Öëjdp^jy†¥bÉà¯!Ð?®´y.ŠÄ¦{ ê,“"":0<ÓB¿ø®ð×?ޏLŒÄɱ þȆæÈþÝÆ¤SËå:²¦[@’vØA!xKp‚ÉãFŸtOÊ^îå?¡š# )’@¹&ÏDÕþèO‚UÚo ð(ÕQ>îsQª·òx¥m¥õªý§LPŒ¦8ÅuÓ`^5xÛ4ò<4°¢Žüƒj³H¦qÔ‘§I¢Ê³>qÈ]5 Óÿ^’‰èÖNc¥4Y˜P^SLW¸SÄÁbÏiU>•ã>Æ/Mëú眒§!Áï1G÷(Äši2yŸ›f³WôÓuøœy¢ºõÌØÀħ#‚o4Án`m2)&@Å£”hŽ  _„û øIÜ.<-¶yæA˜ ”{û:jÚIÁ±Î·C—-Ñ5ý8Ö’ðÊàñ5LØŸU6dg~È Ñ#Hƒ©IÎxóž3¸Æ16É³í¬æÉpÑEh9[‹f¦ÀÈÎ4Gíäêÿ„DôèÈDÐH¤¼jGKD}œÃ¾Öç—@mÐdŽf¦v®[[jÄì•pÿ+ €hÏ`( ƒ›BòmîÍ׿r( nð‚ýgéP‚£>M¿g$Ó)ëÔ&içVèÂuà)Ý ILÚHÀí©?¤òwM@_¯†Ñò繨qEdˆîG É ·*8¹Ã*¨ý}—³á@6³\Û¤Æälà¼õ>Rõànñâ ܼ µHCúKr‘tÒþY˜®,“98†^Ëp>SQ @ÎåP#ëõ–O;GèD`tPú³Ý”yâ¢f°NåòNßSuñG­;øJL=YØÔ=Šϵ>f˜!r™ŽM¢ý¬)6à|Ò×…ÛvE¼øfðòºœ}7eêó4¿*ÍÛùA}Lª©Z~bÌ¥­qFµË´"#O¥Ïš›…âpâJˆEn2Hj„äÈ4CÖm±øF{2OsL÷º2qíäQŒ<ñå×;ÃL§{ø¢¡„Ô4u»0¯ï`û%K¡…ÿÇ·™Ûk>‰Ð(ÅPGÊÞ;ê!hSÛ$>Û¹1e•‰EÇ„ 8[Ec¦’µ¦·EêiV:Ll#›f€-«ÃEù’D"¬ƒ õE•ø˜ßÃ*¨õp ŸI<È b¯Ì‚%Y>è{/svà¿áϼˆ«l‹-k¦Á(Ì®¸¡‰‹+ÎÓ ï„žÆ«`³9)·àÅÑìX)¹,ÿEÞ+MCè}`—G0 ÃV0¯È"EæÇßã©ýÌÕ”f]ÑáQ AE¦TYO|ÒdΪ¨”Ï]UKHAúÔâ[±Sì:ÀRŒp @cMé5˜£ÛO39Œ¦é5ÔRªñ‹"N¥Á ¢|w`Md^†Êg­Í#ÂÊ{û>GŸØ…X)¥v U! üV8*£7Ó@XL¾’p"q- ŸÅ%4>2L¬a„AÔÑ¡4úD5¬ŠXø{JBÇ÷N¼Ë3¿Ïß«ƒÈþ ûå„úi@›µ¯\sÛÂkþé}…ý,`…NfàÏË @Ê’½js  °çkÁ—dYebZ0Ü!¿÷&@ ²´–^à\Ë´bAçuEx ôbòIE`Р‚á³w³Ðg¤ñÎ騑I€}åúyYa̶öVצ±™¹T `°”PX^ ð³Ñºs®Ó×]L‡-ÿØÿ¼4~øÚdtK‚ 3 òI SjÀ¾ DN¢/¥ ÁÇ0 4…M 3®¯Û‚2›zAÅ=A J4÷7ú¶X±yGé€U­,µqcÛ6ãÀæásÓH `‡G"MjäžÂ¨ô¡jÙ~ƒŽKž¦ž|’> H| ¡A#@ÛmÓ´àê ȾGüð­ šªTæ-·÷øNlLkd£p³Á‹~빈Œ,Nô~tÖFcèéš\.F2Ü1 awVǘBLKÎ¥#Ø*AzKGÛ†ÖÚ0Œ±„%¯Ž’!ÇH°Vž_£(xÒ›YZUʼn‘­½°Å¦(|ÊÏV\CÊ ÑŽ¡=ÕŽµæSWñÜ:Là0¬8¼~³È!·d²n å ˜y•8B)Ãß‹¼]ùØÌ4ž<ˆM€Q ‡_§ $@p]»kxl„"bozUÈP,Hú [˜Óã®b4¹ €Âª”;O8,CèÅÞF°†7ÿŠúXÐÞ¡[‚K¾‰” Gö#ƒ“v‚¿ Ø\?aÈϾñå¬)ævÃAEÒÑÞkˆáºÙXÓ_„'ÎÚ€­Ü ž@<߃ ôñÀè[=(ÆEGÝ®ÄzB ¥nž q6¥'ê…íŒÝŸK§|Õ×çùTO (9,?bSZn^/æD‡¬Œ[°ètß\…Õ~Öí#¶58c?ç5Nöü7¿ñ˜l Ç<;.JrééT<µKC ~²O×Y†f™ (Å?‰å÷(œ 7&ý±«wÇ\èlÌëåAwØ·AŸ©Aγú Y6 w€‹Gæ „U‰bÁ½ÎÅÖÞA F¨P8ÜH`( 6|6SjѧúGÏ¿¸Â‹#üøÓRÄÈÐÁjÂ2œ¤ÌTT\£—lŒ•rñŸ&mþ:ñžßd·ìÌ}WÖbåW0bcæu€’ee('+òs=NÜåö% 1T ßP/œh˜¤K—áMœMe‘ÜPö?Ó…™.C5ßõçì¬àä·õÑB4€ @ 4]ë¿Æ/›Œ‹ô€Ág'‡C2™§%G)ÀÝeÌÊQH'ûŽw7Gÿ´×¯“ëå î`ª'„@}”%jñèùøh\eò¯zD×b¿¿ãž·7ƒìTÛiËÏ ¢~‰Ô£B:YD‹dtú³ç‚·'gÕÎiŠ«ÐþçìÌE™òÁ ãfwÞ÷…¿œ[åZ3˜”Õ^ÛæF„Ä ÏÅ?|DÃ"ac]óÑÅr4h›üñˆþm”ÛÙÿPÍEL›ÍO…|ƒ¼}¢é-pш±¬Žì¶d묇Àw¥™ËÌÓ6”Ólj}‡‹éS°”‚2ó-j.GŸIÉHjf=‚O‹ò&fÆÐñ-£ÓµÃP…ø´'‰ôœT‡² ³S—¾ºYn„#!Ã͸»µ‹\{SÆØ¾Å+ÌÌrtaUeÃÂtÿK²tMË®Á,´F{ÜPd1 bòà’Ê¢‹ï#Á¸ Eû>ðä¬aí¢äS`À0™°¼aÕ¢% ?{øw?2µœÈ(ÑÅ…¨=ãM;n'¯`7% E4(¿5ÓCxvƒv(p5¦~ˆðÉÓÅ#£šˆþò3éÞù*ÕÑú,𜶠ôAõëi ØGÆ7:âËôjñ)ù+Èg1)ƒ¼óæ~‰èL1ÀZ8ÁÎX=@ 4,Ÿ‰ Ü2 ¤F´õ¾¦Ô¶Ę)ân¢¸P“9Ø”™ÈÚ©³6 Mü–•ñzÂgHk/'¸Å½àµ ñ_°H¼r¬c ˜jk1˜Ç«Å¿FÝi(¿‚$|ú‹m0´ÝA^F±Æ£LÀH&¨#mðÓoÿý ›S“k$ ‰í±O ýœ­SÍ[¤#9Ϋï&³ð ¼PšZiV4(›Q$ôV#È®ž,௪t[t9[¼î£xŒ‰j𺠪¬ˆ·&ã¤] —ù ÚÔ½‘°"ÚÿºÈMÂÒï7‹YE: €£æ1ÀDw¸á6lþÜ»]f‹(…—]·Ö2òþö€ç êÕ#å„~ÎÓP§hR¥fí˜*ɯ‰:Z«oÌd¬>¾ˆäZX·øÖ^Œ»DmW’œÜ`ÄY–°ã‘ð=S‰tó2WŒèûWÄE ½j0Ê*0ËFâå>®<‰Ñ@ƒ8óJxù73ý­4@#”ç £H÷Ù¬RÈI®ÙÒxr°^â5 šH9'q¥PR?Uï(Š÷ƒx&¬~À¬>éÃÒ†Ö·û¦±,w‰\_ö!Á‘]¯$˜y‚¨º|ùÊ(I<àMìەš06CL=á ”]_pê´ ðwîLB–Â:͘CqØÈ,Gćè¦àýÊg‚£xŒ •@t¾¢í¤ÿo['Øh2ÑxAÀ 9¾1´CH-‡§÷¹¤Z Ea`%Fmà|–Ìgßa¿*Â@4j? y ÂÚ@¸ýgàc3ÑFê {Qž³…ðf:ù2ŽGŸx Pb›è£X{6^^ÚúФÛ|܇!bbÝÏ´w R…ÉËŠ÷2Ý'F%ÅVˆo#ð’¿¢#žGºÞu /Þ]eh·0Ýf¸ D4ËÌsPQÔùjÒàÝf ËÑÉÊ#¨†¨sJ‚+8Øÿß¡‹.wF5›n:~á †cì{0A§cù½°ÉÜÈÐûÿ¿sÒûåÌŽ/÷MÛäS³ä¬A©àîæd]aD”0l%ÒpéàRož¹³DVuîùÄÙ_ÄÓæ‡Ô¦ë„ª”i¡ÏEŠÞ¾6ßÚjª2‚Ôñ¾Î÷r.׋va ¿/oƒtt¦À›ñ®ð©AøÄ&¼Xt¶úxÜzÓ¿í0èPxVØ=QÿôüJ yÔ/R®É!ü~xåßwÂK(XT´L?Ç¿ÿ*qV®îû¾Îû¸ù­1æfÁYË'MB¯ž3¢$@ÿÿ㋽îîîï6xëÊ‘Å_üø&ï¾ÅOÿÆ |k Ñ¹U3–íaI oæ%Bo|3V™/螊"Kõï$>©b5£%Î㺃FnzÍ¢ðm~_  (³1pŸöÿÓ ®ýÖõÿÑ‹ÎÀAT;úÍ”4í}¼ßVâ uIOt±]÷j¸¶$ß»W­ŸçÓsrdÜf[³®JK©£&=ʱ‡|+ë2Uʈ•R®.¸\ ÍM;î{ðyä«×¡cF$ÉJƒŠã.Źç‹À¼˜Và,îÁo@÷+óh~<<ñ\gûW´ † 2ºÃ?¹’ñ¾)Ü2V¤Ó·ž6‘‹/ˆ¤5æ(Qg?äDy–Pj&eÚ,]¤gm‚Ùá{T›? Ds ï¹Èy £ñT´©§ “»Ž÷‹è”h«œ€>¦ ˆ‰ôÁʇeSˆÄ*´X^ê&ÇŒn<Üø”ð”w?Þ Qâ:YOyH™êã°[À ‹š<‹îæq÷ß=L—Ýqîù¸ÿ=A4}ÒHéX´ôé%Ǭ†3›ëæ‚ } qžyÅŠ³‹ĈH5LQá·tRÌâY:qÉïãúº£Ë¹T…XèHÍjü–, Ъ7ç`vŸ5qC܈³‹µ‘·€E¸°¢ê ñV`MI7N¥€„o…É¿°¸¤À1ÊÇ¢× €o\º‘7‘A²9.„ÌéahS©7˜Ö øøyøX·‰ÔRÒ‹iÿxÆ9uáŸvèUÄŒírgÓFÓnB|Óq ’ƒ—;;¬-h«lÚ‹rÓN㿾;®î’Á¾:8©¬k‰wÜðvx BÙo"¶6¶`ü=òAaKfT¬SVžX¸•ã†õ¥È©[©Úoƒ¼ÈnN$îdãä,ö~(Ö:±ã€IŒœ4•-À3›W:¯Ç/#[V5ZSì. LTƒsd"»­Ík+²øj"“ò¹Ÿ²i$?Åñqˆ.¸VÔ4ͰŸãí\Zon«Q [‹MFKœ4ZÿHR< “+©Z ØÒàî}ü¨…h ¡º†ÙôcÂu¿ZÆq!z‰8åcêÆ§ymñV³Y£/H{— rÈlqy )ü°Y­æ¿ž÷C©hT@_ È2c‹EÞW'”2ò€êY]%1K±¸Çç1“×}l=iV’GE¦›Á@/ÉÿãYÞ¤áb­|HC^$Ç;&ׂ¬G9€–1WZ¨’\¿ƒ¥*(E£·6q±oI Ø™qPÚà{vF=M'J°^yÆlËL§¶¥“††…÷µPžÞZsÉkd)~™bš€=ÆÚ£îÝÉà‚}Æ‚kTˆßÐfç¼ÌèÙ/­)ªfÀ´ÂQ#fat .ÛªL¼šª%ITÈj%K¤]G§à’TŠ ŽsÂXîã$ûd†`¸ÄÿËiŠòm§Á„Ol°Œ˜Ä“]ô…·lj"C¤çx1ã`¤€üM{K G¯-©+Aц\²cñ#Õ€8¼¡x"IÖ•G£zÝð3NgŒ«Ãl”°x—Q¡ Ü–+ K¶´Sm‘•“(_ЧŽPI?˜ëSᦠeÒ¼#EÔƒ¢0Ëâo}·1ë5K|TÌê'wX ð‰Uåì”sØåÀ½VÔ„ФFÑ•T“¤nÂd´4¢éA_€ aã›à?¢Ì-¹Kç-ó ”s§Í ³äbí7P™Èì>ù„´fÐI.1Ïôu ¼Ëg0å¯ÉÈÚûUG0¦ÄNŽÂÇI5Ój×y-Ã>îUMû¨^dãUã¿2Ó6™°P²}d?Qj€Â¾‰AÂæ¢aµÅ&úAVñÆ9`Ó˜R0©±4ºÛ02Ý Ïú€SºÃz!…ùÄ€ÓI“娒OÂi¾— ^ÏK'ƒ~¨/—OÂìiƒ.e,Ì÷hr!1Ÿ@mž ÂþÁ Ô"ª§½"Ü ‹­Š¤f8E-óŠz)1}žœÑ–üyJŠ]ü&æpQÝö†ÈžçÌ)KWèj“x›Ÿ¢\0¡žÎbj“ôáŸÈY-&…+s?ù6=Éæ3KÃúdÀÆí@H“' È\䕜zVê,ˆ¡ÌM |ÓL'OGOÿ*¥ÆL¼w?胸l€wJ.ð‘d¦;øDSöÂÂÓqö”ÜÞ$V3ZZG©Pj- hŒVoÝhuàÕˆL¢5Þ—Ìö¬Z`¸Ä³l*¦¿û9Ö%Ïï¤@Í;¦‹CôÍVF•i¤Ì¶¹ÏèÀÛÔç”+àö° øŒôÐ/^ž¼•ÄcÂfBÑcXRœvÿc5ÅX¦Èò!ÏåkdU'5¸ë!TÔ5¡@­A2$¡ÀZ£”µA16!A®œÉfÉo¥cº(rf@©ÈÒÀ¿-”„ÊEûìF¢dF¬Þìíxxc¹âמYLªŒ…°Òp[õpq‚r"ÿUÌSEøIìj A"Ð¥90(7•B«'<•ÔzžÆ=ÀVÃú´ðNÍøjs³“i!ȈƟ-ó)Áª6q&‘ºØe3”6«µÚ0¶¶*‘A¹ñ¾r~Ÿa::Ïq›Šqv4c¬¸ yƒÓý†Ã:“ÿÄáÆñ©…™É>L‡ÆXiÙY –^ø«©*MŸ\69Ýœ×í(Ñ?Òû$6Y·dØ3RŒðHiCOtxæqË"ëbf%l›Ô×QL¸ìÜ­•Y¥$`aËϦԱ«r8+á|¢ÕFpXå(ƒøn§{oÍ”žX>ŒÓwS¡ÑøTn ÖGkÎÜÚR’ôr;wÄÄ?¨&Á!÷ÿ* %+³¿“¬I÷û¨™7{) w^$¥ây«iŸý„ •U‚&Â&ðý¾4!Ù¥øc„~™PT,’`¡„ÿäŒ =UH$-¶³ËR¡Ë^ë׫`”jûBï)å„´ {@ÒeÜ1Œ½ñ¿)Ÿ›â*4䛇kL-ȇ°j•ÙE]ò²¾367HW_8yúŠ2~h zJçÑ'ò> Ð-mŽó{±ÓÕ…´ÏúM Dü5CL#þŒ"¢ö:YÙųjˆOY¸Øn8ŽÜìIƒ,IЍ êNŽkuƒœ´Éq% ‚hÆUñµéüÀJÖ¹Šê¤ûØŽ°Å`Úƒ$u+Fš." â>Œ)¯îz±y‡Å9{ç3j!"9—™`9âÛ`Å`?ûþ.bf„ÛzK4™ªß UÈ@:»$±aJöné6%±ä™…¨ÀcÃ22©ŒJ_$‹ÀHœ 1Ö8HNh ÅÔ,šü%í9ÿl Nw’6ÌW5ŽØIÛá%)ƒ|íÜÇ~K°é“bv`L%®’,-ãÙ©c`W“ˆÃVÿŽ$ÌDÈ&n^OŸæ‚’ïÖ¬ §'ãÜßýµ€Ziä}[éºP“ƒF9=žÞ‘ÞÓmnÙcÃQ€´ ·Dêz{YßÈÚÅd, ½ =›ø·#l:Ò‰XzÃμbpÜun©)éAcëþKrþ! Éšy'šIú»à÷÷DÊatRœ¡£ á4@~Á0GÓ2f&D†«­±ÒK1ª$YtIÎKîjS>ÜñãÙ x@Œ>ÂPVBÏ`ùw¢ïÄ@Yˆ^ÊÄ|Š:žNf¦蔤ì´[<') É:ñлÛY‚z™ç“œŽ@Ð6ûnQ[“ð$RÈüMô´¹Ò2&URß×¶¬§ñ>’²9k‚Z'!™ª’¥ù°= ì^+`þ5RÄè_̉¶™š^° Ü(⊶6}{ªiÈj?Óò(ðí%sö£ö÷‹•jƒü¼$¢¼#CBÁ0™I4l±C ðñƒ;!8~K]®i,ˆCH)Ñ"ÈvlPöHÚi\˜4KŒ—w‚(MZ¸,`g-³ÅOóÆ:Ûd÷û«#AÝ—òx§ø™ðÎjI(ó½¢ lL0Û°èÔaÇe6bÒÐÇ”Jæ¶h¶Kª ܪ†v¤-º/¼‹0ž  k"a­Ù+ÚÊ饳 $¢ ⶆ<³‰ðHWK"=†gTŒS3ë~§$ÍOù|áɤ6ÇF– _â‚ú8&V›7’¾ÊxXxpÑ= ¢ëàœî¬!}…´Ë|ž ‡©ëyÆu=JÂrÑÕØ”~hœieÖ:+£Üaµ‡è%by’3ˆ¦:!dŠœêÓ¤&¬"í³%YV\$%“s.<‰RÎ=fάGQ 5. ,Á.·tNÿ6*PjmrSi…ù@βK‹ô÷ýb‘f Óf»Dmfä\¿[à¤†Ø hé-ƒžo°™¯¡ß3ò™+<†DÈbˆ9cþä%M vÄx—jyävAºUâJ¡çÝoà!ù« 3B_¾7•‰l×·*i¹¤E4ZõP{ë_þ!Æ1êØ-`TéÇ$•À'Ùkc³ùZ;H*XJ)È"d(• (¨¬Ã†”Eÿcjy%á>×𫸨*£J$R7!ãbø$Ï8ÔöˆZ¹«´Èeþã@¾úóÊÄ3®Ý—Eë ölŽ’ `4µeÊfs¬u/—·±iâɬƒÚi*·*%¬Ò´FW%óÜ"cÆU®«¸wJCË’Ž¥=Q0Ö{ðÓ5ÀÎÞÀ¾Xû¬F§¼¶«uØþéÌtÌ:+—®£'1k ø[èfÙé©ó.øó]@àÌ<ÊGQhϤ}'¾™k¢ìW—å=š<þ°oÊ%ß­B]H~T:N­¹^³‘m¡ã`–¨ÿ¨vèˆïEêu´Ûç "—m)ÖbÙ>Íj„A‡Á©ïHÉ{Z%Åⲉç »PÿÑ *b€Îc¢ÇÈ0!ì&óqcõ5S*ß$AWe„Ú‡a‘^=¾}x…BU¼Ãƒpê©eIúë!ošvyDOqQ?E¥)1si2_à96ôa…*W’q O”s½ŠMM)X&–¬ª” ÎÁ»@MUÃâ !‡3°u¦?Èèµßü¢Þ‚s²º-Èj'©d"AV"otlÿ©Àñýd5‰dÔ¹ž ÕìE¸sÇó°ö¿\3~ØÆ´žÚí•– èæœ@Cü•+Ñ(ÑpÅø&£ ¿`ý†©±žÏ†!D‘þ‰ ¥Q*Œø#7±H”Ø!oÖ&™‹•ADhfÐù¢W¿òxšS€PšŒ¨È$ÀÂtNéž-‡­ÐxüS!®€ÐÖ dd¨áB36óî(¡%¯¶6€´'²2LÁ#Í íHÞ\ü~˜È׿ØnÊ£Ç,ĈÈDŽ1y¥C?«ò~ ˜{ã‰Ä!##+ÞÄý³›ï¼©,ò¯tR2ÿ‹œÄЃ‡®¾\ÛNÜöÐÂz5§¥lmêΊ€%XØ~ò É_íŽÆ×T{!9Åö@(' ¿å–»¢ÜL:¹H‚H:PX8g­\:¢þcqy¤s™Lj)5ŠjÚŠoxcþ¹âɥ—íJúöÕmfÙê_f.ìRÅZ©Ú2 -y w š=0ZÛ¡çØ\Xy ˜{'JqákN4ì#©Â¬ð ó0Õ˜ê›Hg׊[üÏÿ ŸFTš+‰ÄDˆ€vJy½G‘ Ê2’ûÈÆ×¨[”‹¾pxü*8ê/Þ@ Ðé95 I¡îaƒfR•¶9b°;|¤yüซT J²ô·6+nXR_+¡Ið]_ê:‹ËÉ% ɦñ7cÝ;iÃLä÷\ÄãšHÝ OIãÕ\dXê°÷“»"Íè )àžîÓ;ö:¥•ÀVHh`§Ó–OõK!ÝQãþ€¹Ã(S%AòóXCcç!°ºýü§xÞ]àÿq‰1:·îaÍ–Áåg±Ä‡ÀQÉCVÍr˜¹g7å¿MÙW…J™ËâåY8Ñ[ÃÜÐ¥ÈôñY± $?ȇ¨Ö¥¿‰Ì²Í|è-xb(*Aö„„†TœÁKV#©c㘄áÛÇì¸ÉÍa@(`&GH½ÇÆNø¨C\²^&>cŒCˆ¯Åh%=1T:Ípo"ÁýF±7þ©Læ¾€Û4áçJ¹CôFA`…”½9§™zI&þq‘È Böl†>¡„ “ %ƒ‘úù%èÿü·óC 9  ¨é'˜rÇaÒßPXJÁãiÉÛØPâÜ›ñz^€/ÒfÿWÈr˜+`ïÿŠ”‹¹`†éäêÀRAÛ›¨ÜÎ’ê²ÓENƒÛœ»4A 0Ñ3R@ ÈT7b‹3™XuÌVb.j}•zI¾¹îœsQ‹ÌD`Â&Gpû …Á²…¡í®©÷1ßêa´.V¤„Y¡Pƒ‘, “†€9õÃS¶&THtb°üšu†4ÅiÛÔüÔ"[ç”Ȥë¢Æ(5G!“Ë6uîè±™èØ×¸ÝoȈ-aÆYîË¢ö©Jr üfÁÎ6(@…X¯ßëˆSÉ N÷̤‘áiFÞJ¸bQ½à'TÇø†‡qs'aûsGn?ì³ÛÒz$ÉÙœþ!9È–dš@ÔúÈ/%.bm xúNù¨©bm2ð¶‚T™x$¯ø,*üDtTÿjÉë[ $×_˜…=,Z›à>Ù?!x5¦Áâ^,ñ®4s„­@*õíP¤ËG÷¾c¨H<7…P¢ÅÀ læ>Òo³5ˆƒ»¾ŠîÆ+±ï%¯ÑÁ+YÅᤰ°“샕‚òÐ$Y¡ºTÁ„‚³8Ó‡2UUÐ* †wLý4ׯ3k˜ÔQŸ¯‹Ø}Èÿ?Zr¨Rc(Ž¢z‘t7¿HŠm5g0îÃÿˆý CR`NÛD†¬:~l³˜ÐÞn™¤­M#}ÈíÙ¤MKÍÏôjñPúå@À¼94MÚsÈŸôõ­M©ƒ•ó„¢CÃDÕØÀ²£tØqˆÖ¦tå,'t~K@Tã`<O[º(±Ý&qé*Ðx »š¸ ¢¶S¬^@¸.ù]ãК‚´`º¿¸€ûÇøŽÚwiεg WÒ©ïZËÁ,èAß"蟤é4Ð/}øáŠN‹þI:ú´"çýŸ >fÕ¨¡0³±û䌦ù› [$2ªxˆ‡ÖVrËéõï3ÑÚ±aƒ±Èpeûá°ý®ÿ´Éž—£`Ü5t‘žÆI‡q8 €Å •™bíéä6ñÿúßÖçà­Þ8šÃŠÏÂŒ'ƒ7 %™©VÙèž:n“fb.‹ îšß€¶ú‚ÝA]åêHÁ¦ÁM›Zýé¿aΩHX—tÛMcAIXFññ-’.€-‰lm‰ÕÀg—€!ŒÔqDp÷¸÷Ƥ}™ÈÅ ˆë‡<³øœ9UçŽ-Ø"È\R…#¨z,U$ÇwEgÌà‚Eñgh)&|v÷ûG¸#ô¥:ŒòX“ð ]ÞGÌØ|ÑP.ÃÜ 3P c­:ŸâÂô4‹`Qµt¿i@Ãx ˆqèbÂi>»¦ŽS²pZ‹NÉOR.þ€`$™åòñ vÜ+AU7´À£\U®Ý•„FJëÒBè%@ÚNˆ¶Zvj4ŠŒ~ ¿_ õ¨±cìÜàÒ¬£•ÎÍ?¢°;ð¬Kn¿ÏzPy3œ°<*¸J); (2´ãml~aø?âÑÄ–Ù½ñ8ÐÁ7Æîa´Šúó«G†úù£67{n¼åë–÷„@ÜòEkxq”^úü\xfˆíD+eË-ìCöäÞH,?vßò!4Á*äÞß®‹/ø_­Wl3ø3’ãn+ÂTú›Mo:¬•ZjŠà}Ù¶x K©ßÔDNkºK‡V(ñ•bL™eÞCnë<áêyš„þ,JMeU¤áÌ>œ«EÄ6 rlÔ¢Élé¢8ýÙ™·SºÙM!Éíiq°ø;Ø›œD4Í'yjâ  Ž@ÓƒUbêÓ%pJ$?Žtè®Æô!ɺˆhFünr>3@Äš°ó¾°o쟅F¿Âyûžî÷úp(ÖÀ‡Ue~#;ÅG² Ïv¿ìŒøufm9!z²E1$….•?ø ^±µóûÑgèx½¿—öjŒîÝÿiæ##è¢\“"é,2òÆX˜bmN2§Âò©‘ Aÿø œwܼ ÖÈ22)wYM¡oæ«\šÀ&0Q–;[¹RK›Ú,ô¡ƒ>håËŠ3õ`”JÃÏöÎ ´òÜ6ýéø¢gU¡á’ó,¡³Rܹ•˜PüÐ'—͵…IXÞ4…uûÿr$ËÕz×ë[šŽy(œ3›^úzÈ?z‚ªÓœc“à‹M³ôåùjÝË_7SËZ®*kS­S·ŠÛ™÷Ñ»´nsîꃨ ›7?³nÙ£Lj«šê.ØQËzb£ ÌßFeNZ»/Q„F’ê¤"H­ßñ‹ÄÆ™ÉmRH ù‰ÁTä¦o‡[ãžf.lî  ÿ ð—´ îG:8ÓË»‰¸=Õ>­âD¢' Ø}¶ÿ¬Ï8sî’»tš#­¡zû ©À¥rRþQTÜ?k="ôw"#C&¨‚Õ,rÈ×Üõ(sYÖO‡\ lWWruÔO ñ1v7²WZ ðñDk¶ÁÌ›à:UÊf½m²ýüî;¹à÷lp=VÁÇRÀ‡°âÊèNÕZ:Bèv«!þ'«æ%ˆá€Ïæ6£œkÍCãà8~0&n“ÇAÞ†I‰·OuŽâÿ° D »Œy*ðÇÒ½J6øxe÷X1e•bz²Õðš)ü4bbÔ†EØ}x&ÉW‘÷ûÝäNJ$Ü–Ã"x˜] sMÛǦÈe„Îo¾)dÁ HÚ‘ 7yæ„ò1‰“@šb(ä>]I¦QŒ ºm·ÜJw{“¤I@¤5ÛPEÕ¸hù ;æX‚p hxÖ×8‚fV1ñk6.N 'µÓ¦ˆ¤Q]²V:·Ý£VUX<îzÚ”;k󘸌„ÚnJÄêžë1l àÁ”f]DOßB?ËÊá%æWÍG‹Eø/€ù}á""ù d2‹Ÿ ;á­E,æ¢vùàÏ-95=,;“äí`Q’AqMÍ#èÆMÒFÿ]Æ×•8HvJ¶c-¸ôš—ä¢íàX7‚pƒÞÃ}Ì) Ö6U=®í‡lÑažN"¢Fú¦;“uw¤¼ç¸)胷UWÖ;ÆÆªI=L‰]„â›õ¾dn Àl±ÒOæM¼Ùj÷Ï–~œqA >›! ¶Ð³ °j£v¿qwQîýÉXà ÷ˆŸîK-ü¶åS)Ê5í0ÇŠh;æ#dWRHêârÝÎ!r¹r ³ÇïÑF‘º½P@:O˜‹LˆbËT•Ž˜87’A‹4Ù¯á†`§˜21”1é¨TËbøâ–d;0•õß:³*ô­‡xAèÈ@\s E#5±´´%VúÇ,Ñö_Sĉºä; FŸ>ˆ0õ/ ¡Ó:^‘¨ÃÜ9<ŠÉÇcln¯ÂÛTÅüTI®k}W±:‡53–&ËFù(Œ‰„íõÌhµØr-BÙàbhv⦠åäÄ'm'FÁSL7u€«S¨ØéÚ™¡§¡™š–ÛœÑ:}·ÂƒþéRëŠ#gl8Ä öœÀåÅm0X5-²ï$aå œ9 èOÐÃDšYV´­ /Eµ¡ 3’‰¹Áг;„ªoN€Nlî©›K¤kè«8¤$¾³4øŒn ø Š’QDU† 7ëgž;Ó¦'5ŠŠˆºWúñRÈLQykh ‹YýÇ´!<4™9ÐeSËDjŒy*Ǧ›Z%`]í8˜aßY‘} ⊗ÞZ_'úÃÐ0{[ÚüL¥˜ž=Œ½O¾-‰n-Îðf™ráØ.NÐL†â%""n¯söv<øeæŠ ë’»€>Òq5 nHŠj7õO$Ñß™‰¾u\1ÂÆ’¶ Þ~²Ë—ñ!—× Ý*f<  ϶ٞÿš{–¤Íâä»^BTa2à ýŒI…jo“€<é#îW0SЈÙDšŽ(œƒAž Ð3—rlùp¾ÁFÁ³Çæšs/ %Ð S¤œ¬|¸ˆÓÒ[F…`Ùl[Aätaž+5rn¦KzUjšáÏ`bCZoƒ3‰z7ÄÖT/ Ó™Ùô\mà5/K† &Ð;ÆúEˆ¦æ npàí¼c`´ñž‚1}÷\ýŠ£[ôróÈú0ÿ6“t8©‘‰™>h¤À²XˆTãž>V8ÛqñCóá9…»ƒËA­Ö‚¶C äBôïËt¥ûOuFër’L7D"m!Cï·ã,c`Å'}ëïñ(Ël6nN>,̾“YdIùñ!mLHºŒHò%„„'Ö9Y,NJì8)ÿyèv„ËQ•5õGÄ3 ŒÀÓT‘w†¯¨8M×€š˜@Ø ·e•³i%…ÈQ8J§—«ÂEù––ÿ!FdMT¥Þ‘}¨‰D¬{÷¶‰€@VéËšdŸqì|бÕ%b]ÅA¶ü 9W"Îÿ†b<¦`>œøt á—¡jMmvÎ+T$ÖRë :¸‡™pt.@A‡fà•0ì¶Dq¹4¤Gl‚‹–¬¯ F]¿S˜<‰Ë&dé à—­Ù„î†\® ´n/ãØ“˜á±#Tô"#ÖO˜ÀqêÖFrp¾Æ“à5·¨õ& »³+£4ÿ ^úŸBµmÃhÑóKˆãœ~.ÓîsøàLV¡Éƒ,•ã¥üŒØ§„½Ö5ÙGˆm"ÈkOy%q.aÿ¢ ‹Ö þ€møF.ýK@¢”·Ü:(<Ú 3µ…ெ–‚;¦²ø,Ÿu&mœšíõ8ôø$WO6Í­ ì¤K:°rònw*í» Ö?b  Àö9<ˆ< Ý qä¨\8ïH?@¶Âj1k¼îçÊJ““—Á£jç0•–R|¥tGo€Ù^dcTw"=H8Æ»< ðÆݤ§7`1©âœ#† =rF³Zä²T…dy?[2Æ0f¯êf¤if˜¤G¿Æ¨í›(˜nCmý"ð“çm{á5¼Ç,03³¦v¨µ'È ‡z£“ÝC¹´O-Ó¾½5åXóÐRÆŠqdÄ‘ý³ A&D/e„äX¯)ePE28FÑßáo½ÿ“Ëí°’*1;lEM7Ä>Ì=LÞS0ûì]Æï­&ëöÍ^5}gï£uÖ®€©\˜X9“ùБõ¸P•‰Ñ¶Ôbn¡n¡ô¼Q,v3×*0Eçÿi aè{ò–sÄb?œ•9I´eŤû'·µ„ˆÀ=¹Y½j}%kiÍ0¸ÃÌŠGV§˜M¾à…×EÈyVGC03ˆ0± Õ·oå¨à s‹~ÿé†ã$ʨbß)¼Ìè/'¡^5·3ŠZ@ã¯*=¸lH=±´ à‚C4*[{/VàkÆ„ŒFÝᲦho¦q;oþ”ìá.Þ °“ZRF}¤ÞU&ÊcQò¯éG/ Ý=ü0=×Ñú*±£ÖNVpÁzn)â: ÎÖqvÜ$±œ—ýçñÏÒŸ¡áLߌ¦LyMðþ¤½xˆE1‘-²¶%žÅàź¢ ÌO±%Ñ^x‹ô‹TÉÏJøW¡=k=еr#§ì1iµ$ÉúšåL —œÒê{`(P/R0L¾¢4ešÛo‘±cOrø2ÆcSD’òÉb3v\&rÎÖÝⓟ,°Ëõo¼ïŒŽ†³ Öža ‘ÄïÈ jnü$.PÃb"b‹QÅèkD²±ýTÒÎ ™.T9Ô¡%K„­™DÁêŠÿÊX|œg‹ •.îßM]öÆ&°úþµ Jè‹Õ¥Â.éa¹ ˆ.‹dð\3‰‰ m wýè¢cÈÀ†.¹B¼2-ÿv@$­W)ƒÝ” ¨Ø'ëˆÁ›”‹J+ I*"º-šýUáú¶ÝÄ¢²µ Á»ÚÖ¢99VèMµ@5m´kŠ)©t´¦'oÒë¼-Ç¿Zô½:#üq‹÷ž¿ó !h,”äAÇcû#mª?áñTç(,nIV ¦:J<šJå˜v­»OÖêÆqèÊΣ0sª1mî¶2zí´X¨lÜÉz-“š„íD‚hÄy-Dž*3@d#‘}”D˰[m©› NÆè“†5ÁOk¯ˆ\\û™›3l4O`rsB@'³ÌWç ©P5aÆ BòeH–Çh°7°.¸´Gv€äGoþdÀø)Œ=O‡ù1|7³.Ã1¿±0©³>÷åh 1¹ª7tÔè´*ÙÄ‚b4? ¼ºú¢Éç» ¦ïÍêß@ÿ½xd§¥µÆ f­„h¤k‰“¾k@œj T¢ ý>8 Žb[Ø!T#.Ã7‡ì¤,pðµÂgnžØrÆëÃkÛfÙ[šrÝ®¹…>õмÿä" ŽLp÷Bo®`.´“ñnš7G–°øk³‚„s¡³Õ¨æG€(mþh"ÆÀ'àéýVà«÷MÚdxŽP¯¹½v†ºÓÍ‹ÊJÓTvш3‰Àv1r Œ,LÕÖ P!pã&ÜŸiÐÉb…‘[Ê*DZegq((Ǽ•ê 7¡oݶ\ˆÞ{@D$[mè_s-ÜrºÝXÜ='.F˜Ïs«žêk“h)2àyŽž­2K[XÆ À°9¸«/‘ 6&„nÖK¨¤w(–'„³®$tf¥Õäö®¥iøÊs/øªp¢Ú5 Ò±ý¸E€¦ÀRV#½0e¤ ö{ž˜¹]†¶ÕžqD]žcZ{¹ ‡[­¢‹¹?“§ù±Uñ‚¼°…=©" úÕןĸNÑ k±cé'½®jEttÙ1Kl Wi·?4wâ;NdU×…Y¡þ•ºSbíFôø÷q³}M†³"fV)ݼwÈõ­p]øˆÎ&ð1 ϨµÌ dÂ6 …&p‰÷_©éd1‚â Ÿæc÷‚ ÷@†9¬Ÿ•ÒÂ¥aS’µ+Â; jÐ+®ž<O…í%÷O°0`“ì6ïûˆr VtÛI" å$.O–a„áU¯X5–3£žì&a':Tœrá¬kSkJGKKEò;)È‹'MTâ^ê’¥ûJ#¹Œ\%P(\$9)¬ZÜ)š¶±y®t…ÖÐÎî:¯éÑ~p °5?Q3¨ÞõÄ1žbù¬'cÚ¤ˆõrŸPYÈ¡³¼¼ÅÎCJU}—FšVOº%* #Á“·L0ÎÕJ?ºòÝ/ÄŒ˜–¤s“«‹­%Ò ZÐíža±^…ZMÑÿÇ)Á^K‰ñ϶ÉHjèÞ}‘ !PœöMØëù ‘HÌÑ'‡®pC,c Ò:ɱ\aúT[i¥'qÉ[ü7Îåç6épôH{8se„ÚjV A˜$6áÏ„Ïjþ{¾® s(lb뼈oòhKÄüÒ¶5ºrÉ,dªm©ÀOe¼t ÿ¸L»È[ÿè–|÷MÙÖD†:Çé)çÃO Ù¤7†IGeqM|÷…nHòy¼îó»€F.Žïîšîs[ô®ó,z?€RÎC–v6o)3ò‘9âS•Ö¨–Á@˜±&ÖƒÓýǧóq0²0îT(iüf?…‘\€ ‡" gá¢ÀvæzaFèø[¥òI½²F&Bdì`Å\›$h(ÿ#”.~‡ 0zA?Δ¢o1‡ÔHLWrÉ6`b™Vþ®•;˜‰UãÌgqÚqÌ"´Íç†à°˜DPÃ<ÕpO'ÆwÀ’ÃëY»Îí <¦2‚ø9jj$v]ÄÙcÚTõG'5œä7Ù(Ô;Œ¶:ækÈ0†±èâ^j§Ñ“†aT?÷¿y‘«‘™Ÿ.±¥˜Ñ0‡Ã V…½`¥àB7îÞ‰ ‡¿>š¤wù¡ààUP©s–ù}c}ÿ5½ʼnˆÅ“Ê¥úòáÆºñMj1_ÏÝâZºvw¥€*P9¾@Ľ‚’ø¦g˜ÐðCT0vO%¸ u• p%Ö^špýe}nÙy:êrÚE„fh }¤7J#«ôÒaîÕP¸Ã(mâÌ„y.IZéÿ1:€æE0„œ!^BÜ-ŸÙ®J¡£o¦>K˜pf¼+Püݘ~CéwW^ü†¹®niè*AÐm¯ÞkDÜGlÊ~sÏtðË×pƒ}°K&v‘ۀø›õ$çßî^ wT¢·mŠú„Tl˜2 ¦XcëS!Ç06è‚Fº8a¨{P¨uü15¨ô~”û!¹ld5ÇnÓ!‰â¤‰›3(5ƒ+èZ™Žx·5»Afø•#ÍyÃÃ(ݲvÌÇUifYºÏXHõ áiããQß׿êïaFÊRHàU1V|ñP ^’TŽa ~â|U¤ÓÄ×j¤| b$×õÄ@¬&2 Åp˜àqÌX@€‡À)€#OXÉF!ý‘8/dò9‚ÝPÔ˜I 7rïƒ`Ä€Ül„”Uojó-všGHX ßéq•}ój€$³˜?yè„OëÙ’Y‘žp@‚’ 6ã%€FØå¿8/écX÷€Ü |?É;ÑÆ.µ˜’F'€°"þ¸,ëIs–c´0 H ÚùcÉ!±#K·Çí£VÜnÿOd®/#1’ o°Õ>ͤÍ餸“ƒ—Ú¸ Gü _ÈKn1`ìbÖˆ,VŸ:Åx/¥Á”!]žxF•‡÷¦Rž"VN\àáE“¤$™žF1¯ÔÁÈ{m•zàUÒb’?wh|™Il"ÐÆ¥Ì|Öá@Øá£ [üôï ©¿Ñø÷­\gØ ¾¾Cã ñhy3g?ùE§Zܵ§e‚šÍ¿@Mï›/ ÈúõASæ\à½7mÀÇ:ìmޱ«;o †%šâ‡ YRq³'ó†ÜTްþ;© ¨ÊhLA`‰èĬ(”Y€ÂèÌoF—¨$f@ՉŠvKU„ú¾T}ÃÏÀÏV » 5ävPvW®òw„á˜ð`hø¯rпr¹Û|^>4ðå;¨™“ëЫ¡½%åÞì˜ 'Âzi‘ì¢*‰¦s¸à¬^íÓË‘‡‡å¸apX;¥±*b‘h !=MÐSƒšŽ>>Mqbði"ç†~²+“tCV€¼®1í‡`ar=‡?Ðû6ÆÀÍuzúÝmg3†Õ ™yæ²CÿɃü tšt¸¿,¶—Q.è: ðŒpW*LÏq‹„p¸fÅQ-þZNxò$à‘,·Êà°·ó(«È3:ÈJ”S£Í„5 è:Á‰•6lÄÁÃlX¾Œº²¬>è„Pº|}±Œ¾NIš<„ ´?)¡á÷@ðÙ(‚;l›EzŸï1ê&*f³OÝî³Åx#ÝÑ®Æ2þšv‹tͧ ­ú‘4iuWcÚ ©ŽÄ‘Ù/>¬?¾ ªƒ/çŽo!)ËÁ¦¶îGZÏ>­Mþ‰Xa©Áµ’žþuñzkz7žˆõîþZ2„-5^ï¸.ÒbiidTºax%“(uå}ù‡ ‚æ¹Ò¶Û. §‚í¥án눎Å|EºÚˆ‰….³B|Ov€ŽHdœ ¤81õCÖ@~¤ÁàÆø×^ù~à»2¯ š3\uñœz_燤Cã²³î¿<Z€°oNùY˜#åãí*PzA­‚馿cÂÙA¿÷Fÿ^»àxïlÁWJ……ÒoAfµ²I†jLü>ÀÁÙ.5°ˆ(Kåƒ0†À±ÃñCÕƒQ¡yQÅâ‘õоæ¢2aØõJ“÷ÜØzKA¾ý= žÛgÈTwUä¶êŽ;0 Mô@ÃMÆ3~R.kž)>@S)³ÈZô{bXþ¿áv\§3Û´›†-,u¡Î™æÝàaFT¢Ëf ôiÞpÃÞaüœê ¸&|ˆ¬Ä–€>v)¤¹’bNìuˆÁÚàZÖæ³ïüßæëV>ð.ý¨OJ å@€Ê´íšËîÛãÖà 0×ÁåmV3.ùï‚NÂt|ï¯-…HÀãHù‰ð]¿ÛÖÎîÛ²8ä²Ó“øÏ;½b&Ròjy«i¿ÄTH*!Àõl”}Ûòâóg)p÷„ÇAqã–ÊFÈç|xd¸TÝ%6:ܬ &ðr€ý h ÍG®%»³ì¿Iœ?ì–i=ƒ4m(ézŠï©ÐŠ`±ÉpÝ&Ü0qÃŽ%·÷iÔÒ8p+"áâ^ÂøÊ® ö]CƒZÃ…øü0=á´œ/óë’5S‘¢ŒEÇ}¥åÄ·ÑHnò| ^Äð—0$aQƒõà[±üŒCÑj鞢î©8œÖdŽ9°´ßWD?퀠S·]åµù>R»ÉäÈ9¨_jNä¾n²¤xáô?ñÕÑpÂCrùânÄ Ôe‚LÿD5ßußüM >Øu…B ï quúäºrµÁòläX+ô.©Š´Æ‰ÃÂóÄí´/ßÐG·3Y‚%Àû}£5¿ÿ¥Ä1,1ºõÓ5ñ'sxÈ¢Dº@…ÙdÆXø™Öê”ÐÒoÁ¸K ˜Qui¬,ƒ¼D;Ðz°¹Ø®["yÀÑC4^ÎËËašê1$qËwaüŽÞZ%AuÒJ!ä,ö¨Çã6T?ÐÂòk—ª–mð@­˜AµÔ¾X—K_âF9IËŒ˜ï%ÁÆ”q2dªåø&¢D:Ó¸î”} þ ÀÎØ¨8ÁÓNÑë”Kà ›bñBÿ†Ä¦xnÍ9ƒ‹ªŒpÞ—™°j¦9jÓN&CöRƱ.‚þç‰æð 1‹½`8òo› ¦Øq9“•ƒ:¡nDùØÄòÑöö›xRdÍ$cQîKóE îÃж‘vÁ]Nb—mù=øûÖnI´šAàèHØ |¥•B:*.œàyñ4I|_› »ZeÓ<ù ‹yi *ˆüN'ó Is3¶Ür‰Ÿ[Ày‰ ±Ÿƒ; ‰a‡4îƒ69¦]ÄØåo-CÐ\<PÆZ)2À(™qe*‰&?úﻈÏT¬vtèc”Àþ\ªaŒ1$!ÌÎJ‡ËÓ#ÆNPñ"Iºr‹©†ôXÆ&?ÞüZh\I“¡þ‡“–ÔBãNô¶%î.. Ð%˜ì2 Éa'oÊ` .•bx© ÅdO2P)í†g„ŒSîH5øQ@ý£¤«ŽÅ'ÿ$8èNq›u‹3ÞŽÉœzo)-DHùÀŽ]V«æi±h…hüJ¼¸ÃʳÁ¡Á|w$ÊÂ3 Of´A‚…¾“–rL -›¯C3·ïh·©m.µ4C"© üyO—›­3Û¼]Äýºoä;ZdvJ°óëiØ‘ˆO'±@w­Ïn c–rª«þêƒÔN‹n ûýÔJǹÙ;üç½ ZÄ›q„ævN*âa¸+Øæ±#«®Z„ÈÒb ‡žV÷V”܆m‡ð²àAÞô£ €²å^¬U\i¶¡Áƒ,b©ƒëÐУKùÊ6v?&¾ |±Ømn'±'Úè#äA9,> ÁrÒ€_¬…ì¼ Ã­%™Ã ý—)hˆ&¬’!õ Án̛ܙÓþ©EêGÌzÁA…ª]2 +R¸Ï}Qj¥ReL»t1Tí–³±ûw¥Ájkª@ž$È1œfµ>ëqº|í»Ý¡ÉÖÜLbšiÑ‚Bh† Xðï‚ë€-º<Ù…Þ¸Ÿ¶"¸5ó¹áÌcµ› rfax¸b¯|5>^PxŒ¹f]¾Ç›²é!GÛc‹‰‡Íè5½0þ˜#."”cÿÀAÌ 9ÇH±ÝÉú/pdGââ‚°ä“âí²#=ì%¬·˜U›ª(ÅQ… ¹+XK’Ÿƒ i†4}™Šm¹Wæ;G Ð"ÿcÚ¼GÎ"ËüÿP³<û[ИØ20íðúŠÉýúp‡‡CèÑ™<ìù6‡#Ùgzc(¨ªÉ;;j¸˜›"g©è„ócØJ´°ýHˆ5âù47ï¡Ð| #t/>S2ßõåŒ_êiŽ€•9Â$ª¾H‰Ì° ƒK¨ €ÕÃ/]þD!gÈ~­åýPí´‚aˆCju|²n q\|~Ád™±Pš‰}v6‹ñ&›žO¼ÇMe¸C% Ò²^kÖ¨GæEdY’å£ Þ"¨?gØäª Õáq¬|4h>ajÔk ü ˆÈB! ,œ¤f“»ÊšÃáÊ=³NäJxN+ÍEγCÃr­BP6…EÑLs=hGJðú@(6ý(´hx8ò wžƒ°¦ÿ‡å€äÉ®÷‹cÙ¼<õ›¤–¶\Ã4<„[B5B;Ù§ørâN}˜_²nT‰‘7LX7”íBW@³Ê«ŽR~ž&ƒ%Âv£ @jy4³ÌfNV¼õ9D±|Å:‹ƒ|É@]o“†/ÂêEÏøµðÔpé°Æy+;_“Wž™Öz!¥ÑµçÂóãEæ®'&)gó B‰¦$sëf.T« ìYyTB;)ÔYBbFÔ¢½Ã3”c^Âc1‡Y„ þ·°ÊîÞã)3„Ì×@@joW»Õ²¸´c n”ËþoS<‰ž¨ÞF×Qà#G~ Ó©±Äp”'&Ø”å £ÝÃò=`¸àÂDzté ZRÈš¦Znù›Ù¤ë‹MìóUÁáMÊd©kI&÷Yô÷p·%ì„ñÄiµùH9Å¢ô i¶þ"H[`lU†ùVu–÷Œ}4¬Í"šAø”µBÞj­ˆhÙóeΓqjXW ¥±Ð5c°d ÄD?ìtAáˆ{”zÍ€Lß=r¥á¼6”ð‰säqꚉyëè–DÅFµ½9•‡þ$$!l¤ "a»>¤À`©xºÖ‰\6^fâIÑŽVÒ’h#¸ô[ämëtE…·Æ-à$Å×l°Ž¯²“ R² ¿Ëî•ÐÛ6ż÷QZíDrˆÕ–ð©1õ¹4§NÛ/ET7.ä2ר Ü âáX^¥‚ ’ÁFõý€ c,É ôdUW ÂøX[lnxr fè 8Ò`#¡I_ºYÖÕ¡õ2Íô>Ê784è_p.Wç†ü|D…v¿\é ªàÀ«’YÈÞ D7è*þ¡ù<Å a¼?5ëCP^g&PZ¶ŸBstyFìJOä^}™sêŒe<ðÙÖÑÍ ?•àØÛ…£Q‘ÇôÓìO—¹+ z9?[…+ë/WeI¤ÃÒiVXów9¥Í°ä]»PÆiŸ½ ³C’T4þÈl† i jXlž,¶üš>¬Hlaêé$¸ÃÒj‚4ɨÎfB7R>Ì,â­€SLßû±X{]–ÒÐÐ~È™ž÷s¡О#½‹2:’ßCx_»©¬¨£ ÿ ¨-“ôöÃè² Ó¸sB; ©j•ûu ÅÆ ë×áy,G°ahe uoøÝq ³s%;7”™é¼áz‹‹-AÓ$j‡yX´’òË»ž.€¦ŒSQ7ïtt¾ÉPÚ-žþ”p €Uâ;\¤-]* Å5 êÐ+šu+zÛžç§@YL-BØ¢vXm£Ìð² à!‰úÝßNžlW„'4Õ`o;Ï#è ºJ2$Õ±ü‡„~Ah¤B]Zñ âU»,SRB]~ž3h¸Ã >‹©&@ìRY¦O‚Y„ƒ¼ h8“¤þ~ûR¶«ŒõuþAA‰è=x.Öº›©Ås4ÿ˜Ä2ë÷Ê'´_*ïÜK—ÛXÅ´%žÝÒ­U%’‚„ä0 s5»&Iv*©ƒëͦärLÍ*@«C ¥nL®¡á>Ü:ÖñƸù±ï´ñœ½®|5 ”V¯aP ËEìÄhÿ®ŠgÎqgÔór.¢ö\œi[§‹ 0úTœÇñ|GÎnB¶fó sÈ$¯ÅÔ7u%¨ÏQ¡ËµÆ1 Sûo lQ°©  SûxLê~Pâ Þ²ÿ§õ®kWR16â!Å.®è±Áê³Ãå®# 3ó¿æùyaQÙ „8Š#o°=îW*–|mŒ¡3Õ/´ç:7TlG='ÉÀwûôor£LÁƒC hÉ‚7å@5Ú/KjÒJ0£årz›€¢Ø.²eÁ@*Ò|*hø¼Ìà–8úV€1o,†Ç¡ÌP~< »Ólb:ÁÐý¢Œ[Gêcí©œoO¼ „ÁsVcW@õÁÞw E’,û ûß7½Œ(xì2ä¯ý·m ¤fÑ*ÊR¶xk2yÐF/?<âñçÊ/øs¨ÿöƒƒäÕ 8-gZŽü†J0h%Œ{“èk†Eý1ØÙO6×|=í¼¾ÿ§àÈÝ‹•´Øè»0³·¶ÄR™’GÿÞ1ÄɤE?ï.mD刉&¤y0pãÙL000J„¥ B_ˆ££GR-0 upcMh}hC•‰DFë+©º¯ }Z(Ë- –DÆý@T,Ù]Q  NÃM‹ UÐðÇÎò²<Ã¥–·šæ?ú>Ã;Ÿî4Qö'M}ü»<ô_Q_Ù QûmÑWZ²×YXCûÓ,ˆà~S8£M²s@ûwD$D =à$/ÈdEã6!RžwNäc"³YËJÀÔbNXtðu?< xýð Ix ˆ ™u˜ž@Õuñî§ÔÜ¢—ÜX\vlÅ…×Ç8@{àÝ@}®´8@Ù‰¹Ót3K±‡Núy¸Ÿ¤LÅk)†¬QóÄOƒ+ —÷ °äTÖAÎJaÃJGцí~Ì •J»¬ß^‘26ÛÒ<UV¬=ì,„&왈 ˆÿ[º'lö›õi1k†¦©ã é¬;‹Ÿýè4àyãþKîš–Ò‘ïŠÎäÞÓ‰@:`ç‚Æÿ¦™µþ%KsT#BD@2uàœ„(…•&ýZ“¨üë\y•½1‹Nƒ–! \©ÓUÙr0ŧ;âÎmÝCR$‹ Žòp–³Uc͉º{àW­á)¦‹×8ä]‚œÃNd7•aÔÈ9,p°ymŽÔØä€QZÆp/L+|xræ ÿ; ôú%“ÃçqÕ]:#¾e5ߟ5’Öc»ä›y!‚î‰Ò^©ßõÿm@Ô‡àt¸lA;Ú`ƒ|¶ƒ8¸ø›#Ø0g]=¢S…ü`!óiôE¨ðcßi çFÓØ)΀xÝæNœ‚§ç2µ­n;œ¸âŽPÍcáö€ßÈ›ÙÆ<·kr ªß²É{0c„/ÖðN@P3I}Ü-¡Z / xiavä9øÛótNÁpXqKžË¨Gy6”"ï!=È´„8&WÉÒÕà컈8¼ùèñsönØe[aqØ«óU*Õb¤é‰þ~µÄ¨¼:ß.À<–Dž%s {/¤0âäÛá:…ÒcåœE uUVΟia¸/t˜h½²|BÕáOÞ»ú´úŽHðˆ‰ » «øTÂÐuέ/yüPYÙedg1¸ÕAz’Á-OÓɵü3z¦œIlRG¨…“ Ø»u ¶ž)36ßóyÙ€:4ÓA¶”­àÆu˱ÑË@Õ‚)î2BNïãN s›`’¨bïØ]ýmBèTQ¡¬hióÞ!bì²iˆñî]Ö*ÍÌ,ÍGäç?ªA9Ä ;Q~û#**|“±‡tu½L-ÒÓ ¥àN“剷ÿ3N.ÄPWÆP1†V(:MF˜#Œ‡(ñµÂY¬µyÕøÃ îýfcL˜‘Ê ¤ivf=áѺÍõhž;hú˜ës8œÁl–þ± rŒFˆªM,ð°JÚz'N} ì®è(ÁRÏðaÏ"Ò(oºDCU*¯ÇC’:ÉPØ ¬åÀÄå–:ž—3ì˜4G.Ù­Iš$|N,ãhêˆ~ˆ¼,ÙˆL/ ÄÀ{8zˆÔi±Ç6ûD¦Àb#4Fzp!_oe ÆB;¯¿µ „ IáO,*}~E÷èÿ‹„x,ãÉhœLƒ€åQœQ]šÏlÓš±éóI“ÏÅàÙñNÝl¹ ”$r ûBè6ôœ3°ÆßBR§$¥]ÿ×"I¨zMõ« JK"€Ôw¸‰§óȤâäò“l–þ—ûØð‰!Ó+k!OÙ¼3J,§g¨ÛY¨>‡Š¾Ñé1è× Í äž"by„X-ð ­Ð@pk !DzðÂß‹øYÎ…KhÛ/ç#0¡¢ 4;Tð<¥) Ì<—q…_Šâjl†Q«ÈàF€)n_&àÐÔœ:Ä¢ü§‡`´T|fµsšIà±Û¤é1æ… Ýº ß¿OÎWÆØvï¦dÑ\ 4É«në#A#í¨J‘3ÌD•pÉx6n™}iÄ-Pfø˜$P³âµ„´kL³Ø¿„ŸŒe]ÝW¤R‡ÒÇç×B8Ð#v:eEHÈÜ/ØÍ¡ºS&(&XŒ¿rÇÂÚ×ú7êî$yç$ƒÞ¥½QÀ¶þŸ2”Øn6JÈ0¶õ‚`uë“ÂnÒ•Âwœ—ßìÆ…p,/;ݾ/Û‘êé¿V&,*0çˆ:ì*Ë&¹& ƒÁëAe PE'ž,3Aù ¨¤æ%Ð…¦æûŽG AŒW»¤Øc•´r¡oƒ1þÅÂÞ+qÙó2ÆYÔ{ð¨‘H ¯ª5‹ÝUÈ¢»…0 ó»î>#6é.þ€Ū!„ƒáµ¸ó›9mhÂïYN«R?êÆT³âÏ•/Âht•pŽ“w`§¿Ÿí‰ÉG¼•¾ïœ”fœ…÷!˜u((ñŸ@Ø'Ž pï@šâ†³œéè¯%˺†õݦmI¬ö„N$ LC̤lf ‘…T ”q$¡´4½ù±&DD7P€ž$*éÃS°|µufcùÊ.ÊŽÃ ’\Ñ<Ô†?ÚSÂþ‰øBžô½ãã‚‚çOÅ|žÀ x´*ØÁÀÈ™Qœ ‡…^PÛß0ý°¼û¡ ûŽ_aûm¡ÿÏÅ[H2¾ªÅ¼··áPn§gÄWiû"ò9/ PaI–K#Ò€l¿þ«Œ×ì¯#+Éu¿âНg©ô3ÖýL¿ÿ­}V“—ev° ©ÿSlél÷EåqcU‡q,B‰}!.P9̆»÷´ žŠKøó€‹I¦]Í_à#5±œù°·²x‚¶ ,ê#æL‚ñ@å1n2EZPA`ý` hÆFƒ„oä'ýq¢’ÿ Êwwߣñ?Ä*©j=à'Qby€”qç’ iOöE«užbÑ iñù²µŸÉ•>¶üb‚R¿ ›tü%[ÿ®D‰Q/H•S#" éaäƒ&ƒÿOçÂ_ŒÖHƽR¨£*¸’B­¤P¢¨Ê̵ý4p¸{µðß“c-“Ï—»ïÌÕ}CÚÌzXìÀZ™îlí ³sÂbyA¬±ù¤ %9 A2U8ÑXšÖ‹«i ³*¬(q‚É9a8!¶HÓ€D\ˆ †Ü¿¢HP..¬ªU¤MvÌLr*ú÷µËžÀ%®þ‚!õ8Ø  Ë >NÉ|jÄò£ù `q¼+â×®"¢*´Õ•J¢[ÇEê.íûã˜9y¨]»×ФzÂÂ¥c_[¶f5“ÜÙÕP&o⻩O®Õ)þÖEzÑšX·¼^¾8ývmû}kªªB+qÄ¢¶KãÇ@€{K‹Zß—yfØî yyØ8ÿ/^øÜ„ˆd ¼Ê¨‚ý…ÕêjP3³­‡äÎr¹›Ú¹f\7U™û|i³î¥rÄ¥ÒÍKdÕvüql*ÄâGµ5\µûMÑýìï-üV¼«˜úmo©9\®·7ÁË|# Õ}hwd= µjãUóü. hÀõ5cªþ2õ›ðBøÎ qQ¬xIš©“úPN,àecð¼ñò~ýâð2Ì•-!oÔ²`\X=Äh({4ˆŒ°’zØy‹qoóËqŸduUÃcÄŽ•y›¾¥¬úwÄcb¼Õëh¨çúAä¶âÜ‚MšÚuIÊù”˜<óÂÀVæÃZ)S¥´lËC}^í>ÁeNÚB¸}#©ä,ø@c³Mr"t+…š]ø‰y ßX÷aHÈ’QŽœqõÏÉ #ÇÒAÕÁ[ñAS™AcÛ8¯jhÉâ€ËǶð3 a%±²Ð•™žšJ~ßäç"È ÁñB ‰…«‡¥p< ³+E”-‰^ÜÛ|Õ.·Ê@ï¤Ü d9ÀO‚MÍ‘¶b/ kŸÄ*X Ä,Áó.Pc·${ŒjÏq=Nò6«µZeÔ7Ri"ÄÆJN©B&6Ž)Û59%.¹ØRª(úâù¾ÙFÝ&Àœ±v¥|çdáTT i»œâM*çž©qž9ˆ­Tv‚hh‹Ï¢2©Z¿ȽÉðÁÿ(€Ã“uraµ[H…‡€}bI@{xL3¥«') ÁS€âQu†E|æ6Ó·ï_´VJ1…¬psÛÄAÍCŸ†!‘¨[Äw£7!»™Ð±=®jg°ÝäŸ}¬{ÃUƒL›aÒ?‚(>û…2ô üEw5{âc¦Nð!ódËFØ^7sŒ1¾kDÚrÔɆñp“âI`!h”TÏ7!0ûüÂTÒ*ÌëFƒºÄly=lŒB‚á:u^¤ZÖ³>ºñ}êc1ˆªµ[ª›EŠîƒPB¦Ê¿\:³ë3yMk…SŠ6 |0CVü©^Š Ô+ƒ¸‰ú»  ýÑéÍšÿ‹‰ŒöŸKBЛh;xhºØt½àáäâÁ‰n1z™dýûtâOt$^Û°óÍx °º0 s³Ñä„¢%1ëgâ\Á:´$ÈâìÂè$nzÂ=ôk‚ò8— w?å ó0ûi&ô;¶˜ÝÈ6®˜}ª,j5ƒ¬É<,6¶UØÚ&œÃ4R8Þk2¼G>ë¯Â#€“æ1]:Ãß$©O .`»ÈxÝðßN¿ß½ÂÆìO¶ýÁ!ýq9“7y¯üÿcDXZ¨™…25[òC˜';«žÛtÑ·Û>ÈBÑJÔw?D“´i™.° i;pà=4ýá„ÖD¿Lª2ýŠ8aˆ-`½)á_„>#Q´Ü𠆑kVÚ|ùÐëÝ‹˜ |ðšÏÚ’Ãt·k°ö䆒´ð— §Á `†ëV=ꕇcº{ˆ;¢˜Ï„À`j)@¦›(!öz#7»ZÖ>}PûƲ&ØWÚµËGlÈØ·§fÓßõ¸Jø7XH¡i…Ø1CÉ¥TaÓ€zf„ÏNkŒ2zƒ”´BmnØbQÖ2)õF3X¥W¾2óÖ¹Ì.¢„ùN »þ¶bþ.ŽQ_µm§¤¢ã¹›jëïxGæÜÒhïÏ0ê4GH–Õ˜e(¥Xï²vIûG1·ˆÎ)Ä›I3~¬6ÊI¥‰ŠÄa`þô€û:ÇüµÈÇzÁ3‹8ñ4^ÿsFšœPqÄOB˜O´ôÒ=£¢%¡| rR¾¼ÁâAÒeͤ{{v5Üé0ý4‚„öW@òKƒ´Ñ4|\9A Å5ÿÈ‚õ…¡ÇC&³<78–ÕK߈½ñÒüA€; Â^Ÿàó1¨4y±/Q¾°–6‡V¸Vü’L–Ã$<'Iª’8M¨í‚8b ÙŸLṇ› #?Ý=¾ŠJqp!í®Røãƒ5‹Ðd{âXîy:Ƚˆli¾‡!*22j!!UÐÑ¥Ê;| ¨6TÄôPXƒ PaÀ1 ÏLdtÛeÂàA?ûÚâÿÄ=è„Ë yäK~"`ÐÒ[$×1¡”©ÍPp¬Ìñma¹N‚á?éé[¡`„7 ”ŠKjÔµÿ7z_h±.‹îp徆Ԛ2´ð€ùï±R±ø¸K©ôì5R#E_¸C¿ÒiFŒ…ÞâvãadÍ?åð›¨5hÞµ'ðõÈ¿t²·¶®×HÙ!µ9)Ç„Ô î‹¡$Ä©#ÿù^ƒÁRñ<Ú¡Pȹød)3ú†b‰Œ·­A2”%eüD@äÊ÷1ˆªcÆa®"ìJz×eB“DÙxŸð?+pPqðI^Za{-"š€åðõÔ¦^é±þ_"6NKg€[àK—üŒúTÇ>iØ É0€¤ÊÒÃWп5ÈÀ½ÜIãªbó&m:^Þ]š¦p6|Ƭ izõ¤îÏ $ >G:ùá4»7„©ÛbÑhzV@Žy˜Áî£:ý¬“{×Ud êštn#¸ÎãCŸÚJÌÇTnûàS:¸—’<ôþÿ¢Íò}2¶kLÈ÷Y#ý¶nPÝ\L…â$HƒÝЕÜ{[fž{;¦r¾ëxÈwìÔV#°¢3z ¿)ªów$ʬ‘O‹Ã´ØÇb„^:\zêö¢ÀrP¨@P&@¦²ìÝÈE“„Ÿâø8ՆР:»ŒÆC$!çÄΔù¥4 Š«p?Æ a) €lñœ±¤R§¬U|:xØK¹åaúšéA€>:¦²(¥{øeËmnÙèêEøØ gYÑ6ïC ì&=®±EŠÜüÔi0¨pn€gëpú6ÈîC’©÷·á$¨X=sÿ‚ÑÍÒ!ʳöbè*kžØZ]Ú°GQ6íÕ¶ŸÃ]c`° íÿ¿ùðÀ » „ð7˜Þ'Ð v^5†’ÎÀò_{aNÓÕ joåýªKHYÛlÿü}M œEêØºh…¼ýo®y‰16á¹¾”]ŽãfµÑб÷cÆ ø’ñÔøR(ÿà„m—ì¹@ÔÙ7ov‡ñi·ÄOGµ1Â{‡@×7mùœâ,u•?ü é[x Ôc=8Ý7¡KÀ§p =eYµ‹ý%Å©dŽA媠¥ŽÊÃbÅ6edȹêKi˜hb«Ø{M~L2r"2 ìÀ>€kH‚‚¿Ó†&ÒïÏD:XY;à ­ƒ5Ø'ªcð¢&~4\÷“Dùc¶ÍKr„ºjb¶í¦ìŸdT(µî¼F+MmJ-6ŠßâBbqÖ'Ù‹ÓÎT¯¥«3Ðò Ò[#‘8KbæHYy«„ÍV-.í7Á˜Û±u¢Oÿƒh›·!€TV€áÁŒdQ5y'Ý}PL N÷UÇ »êY¬!ÜÅéòëàÔç¶;t\[šŸíÒÝQ+A’O/óŽ9+[…ŠëÝ %&÷Õ jo9diCPI“K¼Îð!ÜAÒåR¨Ò~ÀXûJ£2͊ʼzöÝeè^sÚ/ë}-©*ŸÍp<ÍÀ„Ìn€5.bJâ¦jàk½0’JW®kX›m1J¼Í¾o¥—&2 ¬ÏÄcÉ¿ÿÿ´èZ ¬eĤâsk-ÀB$¬l$“`¤IÂÅ#Y¥Ó ¦÷lrzݱšé/yaÙ“×ÓV¯}þVpZ§Kíñ%×öT;A&ÜÔ4$ÐÆHÑÆÍw†™’CžtÉ9ލæ J©R=ôk<ݦôµZáõ'ˆ™Íu¶¸:mÑ’ùˆr‹S2IE–‘G¡µ³_ õ Oº40¿z>þä&ä{’R¤U‹Ã 8a ÔKŸÀÕqë—GÖHhóUê"z>GAÄæò‘°î¬•cWaö,ÛŸóY+‡Ù$kFH¡lP ;Ü< AE ˆg\@á‚‡ï‰Æuº°Ê|²"èl0mBŽ>äC7ÅÕW {o$¶jÐâ…˜r¼të ‘aõvÅhj'OÞ9ùFÆäU.iÄhÿ«™ƒ}H DëöV^”"B¬Š(+¹ý‡j6+ñ6@GÔ¡¡@2Î6£}û¥c\Ó¨ÛÌ]Üo7_³ÐµZò¢aõŸš=7ç(k˜ zï¾pŒo“q]jÕåÝÞWÜ…ÍËö»7‚q¸º™o¢ªÁ{¾eŒ­ ½‚vîƒQÑG˶ m£à[A˜esjÂl"î “¨3õ3`´éú,Àà ƒlbdhETs jËÅä¼ aLÓÈÄŸûË” $Í'ϰ34hÒ!ݘ<˜"ÜO… Ì™‚ɰg—ë47ù9x­À:h(ÒÂôÛ ÇÙ ysÞ¥=¼.ä¹ÍA%÷¸à d° Ã|xQî¡Û§…l2PÀaà Àè½p >íŒ è ’T¢\ÏnIòoPkŒ,Rön .ƒÒëø Lá—s)ßáÄN2êù2W–Áïy†DDœËEŒ&Dð)°.¦ÏDGÉtÐTyÚ„T—v³µ1´ ¶ gí\СšBc–ØEd}Ýǰ-âAAÙÌ›6ˆ³!.<•P9FQäЈŸn ¾ÕÀ# ï©t4 š†¦k'®³Nh ðzC)ƒ%ÏŹg MµÊ7¯Î?QuŒS°ÕÂ\ðÃICè ÒngH\;ôîî§Ó¤K@á_ âļ~Ål u/ q?ˆ<’ÉCÉ)µ…Ú¦yäî-Æì‚Ž7U $¡Šç#“CDæOœ¼ù¶¼eñÁ¢¢o6*Ó¯À"&?=„ h ÓD„¿ÖŸ¤A þ6ð•Î2d ·|Úb–â=‚ÿ8‚á-Ⴄ°2ʱ7îóÆ !Ž|)ø˜¼UTUH¼n‰ð£¡a¡C!æ/B±,žš „›Ÿ€Úä|;™‘F’% ù#²³ó1Kóæv{l¦àÚ2QöÑö^>¿ÔX‘]…<ò¨çCÄtòÀ”+† zƒ{â€O$b­Õ#JÅUêÖ èÚ$òJš!›æ ¬l.áügå·îC"÷(bÜÆ¿qûV=ÏšXYÞ ¬%\_» Ä3œÃ¼+Ú?'ÛƒpB9jü|È -: Њ0 f 'ãPŠó!ÔkxÚFâzÅ7‹œ gŒEÉÐ8øüf¾ÈÖÂÁ\|þþ6W0Á1$áèÊeØz{>z*f8©Y6@+Ô2ùøR¥Çl/@¦†ƒE¢i6`ÿ#Þ…°S¤\¨0"ò6†6„ezŠ ½:îQÏ3ÚÙ“Ìì=– ½Ûß­—ÕìQ6rrØ<{0«¡f°dÚ?ôQ>_ /ð72ÎlKÓžù7#Ée†qŸõàG&D{ŸÍzÃt–%µ`Å׊•½FÆ¯ÙÆ/ØŒ©?ÞFÿèv€ 0•òàK ÏâZ~¨|5Î4ºˆœCv hÏ«’ ½ÄÄNª"ª·¢B“*R;zMyÚ¢¼Ï ¸ÚTIbâå¹ÓSº™¼)º}qã0¥º·ê¢jÜÏZК²nÆ‹´86„xøJIøO=0D!æC}DIòe©SÄÝìé ´uÒÉòãùêµzÀþ“LP:„¶Õ/àÄÂ&¿á©˜_nÃPqD2£¿£˜ÝôDû€1¤¹Æ#Þñ)k¬OC5<¬€yA$$¶¦Vm/GwÞ°Ú\MþGÝ0¨™OëW uéZÀaóÒb?uÁ¶iP:YÄÆÞü“°3s~ Ç ¶óŠðE Ìô<[ÿš ¹ˆ/hR©lI§¢½ö›ïoèp·›ŸñÛ,AL¼}È[µB‘{û>ß{yÀÁ HV•z%”¦‘ Ïßvôκiøë¸¼f3Rˆ'eçœÜôŸôf.SRuP«‡ûÉIT2˜㥼9œJJÙ 0¸!0+éB–#F”·`Ö{Ó½š­2ù/„©ö ñ Q¤-’M ;JÞº¼·ç2Ä[+x@^ÖiÏÀASYÄì ¨Áq›Ù­«Ï•Õ­ €»¿pUø`tÊ‹ÐÃ…söhÇzk-f(êQýÎqÎŒ¾³p<ÖžÖ¡{~F‡ cþ/~+Oƒ3CR èÛ°–¸}•2Ü2>ßרma_;Š™™ƒjÇ[„0çco5Óã ûƒVøµÁÜÊK•3:µ5묟­Û~‚!ÐO<)j""‡ÿ´4‰@ñà €(ž¿Õ[ÄÛ½@Ôo™ø8Þ Æ.K_ÿ:T€eÚ¢œp)æ÷@ *—ˆVÈ“ø·cÌ+Â7ãqëP⇃[ØDŠ?†ƒ‘~*¥Ä_AÉŽò^:%7ø`0põª¬I¢ C#¦X¤VPîƒÕßÿþ%”ƒÐjÀL¼¦Y!hÀMæÀݤõú(žÄEvÑn1°ðÄoM‚HñöZ„È)žŽ8x`FkVyó«uûQR`Òîa°ø(ú>øøæY8b™åx,ÍSx(ñÒ—Ÿ„¢è…$É?¹{ 3š(~,À@j°±8iý#êÌ{ ñ‚ødÌÙ.«¨ÏX(§€BSˆc×ÁàËÈШñý€l£ßކ•¢¦|‚yÈkkz_€Ì/œÇ4KÉz@ãI+°n´|‹$P܉`P8â€Éž`ŒÞè7G™\ ü&K.3£«w¹JR“voŸd7gä5¢Ü"@øyZг“Tðß%{Ñ-G¿ÉO}BØ·¬uÂÖ ]°H»Ûa5á_‰ ?à€S¡T0Û6H„»H —ù84ªžT[fï¾PÜ£ˆGÌUP"f Z,sÅ–ô6kÍòž/ö;6ǵ$GYª©v–Œ•”Òœºn”Ûêñb!®b‚ò’Eió:t"ìì !>©lkcBÐÔR‡@g‹Ç&¨ÎîÉ1ÔTa!e~h77ƒŒÓ H}­æCE$f®äbM'ê×õ˜ÛŒÚ`ìs„61ä„O ^Éþ(]Žäz›Y±!Ü…€öãL fÅÏc‚—×ödŠå-«`îØô‚Á øÐ€½S'D«­È€L®Ôr‹§AÚêø›•ŒV/ñÞ5ëË3³“5ù0Rù°Â†í7ÑÇzˆìF¨BD&é¨&˜(ë‹ò’m® âо*Øð¢6®}~œÆ©Àž‰ kÔÏÅ”ø¢@éwÒ>8ûÑâ ;Q×Vuóý{µëõÞæwŽ˜ºà\9UA¿Æ,²Akhç ^Ìû¤h)AK0õ‘þŸÿφuî®õcÂçø×/ÃÑÛ\G $óT NÁóÀòÜ%$>Âü÷-–ÇÆˆþÆJ?/–]ðàZÓ¸÷䳞—È»;ƘÕoqøcËKë~ßð£»ßpžCÿÓÓÇøŒ<»O„Pÿþ}^êïèéÃ`Ñÿùñ>ïëŸ@ÐZ#@<, ù¸ø¿F‡Ÿ¢RÄõÈ1p2¡€À¾ :X¡aH IyÒ³Wo»c¸›Ï9ªªRnì:/Pœ ²-…ƒ¹*6!üxï…ghz€=UûóH{Á!] }ušèµ”áewlVÿݾµÑH»ííÍýæ`£‰¬IÃù÷“ãs7.Ž{_9µ3º ï‹–«É<°rö5y-ȳ¥*Í‘k­½¬G´,NÅ%½¬cP5ðiñâWî +S:óç¥þMq­W£Ôkl¨­ÉE:œkº·/ªÏ›MÅ0bƒþÞ›øßwGu˜gŸ%2ŽK7f€4ô w¸¤bpxÁ†NK÷úòDÖ´lÂÇÜiò:TMœ7ŠÃ1û–/݆ú3`HPßʾUùŸÅC0,¸Õ„›Ï¾f_bäR’ƒ+99Õƒ¡{ ý²-×Ü¢Ûk6¾£‰/-Øo¬8}è ¤2µ+¢¯¾ž]ÑŒ2¯R”§Nï|w›–ÓžV4q¼O3Ó‚ç܂˧0@Õö‰F-¬` =ƒí®0w3ú*? ª™IÝYÄžêb,ÞÀ±ðW#…™#/ûœsx¾¤ÁôÇÌF¥”yóŸnY8²ä.ئ=Þ.t›Á0…p ЇÂA*Äð87I™tX1¤lâøŒæGâ(U(ò'ÛÖgöè,™ Ô™hè/€Ù‹$È…ú& Uv>ià p´uÊN.ïQ¥Á{Cø2!|‚Pœ¢œÁ>Y+ÍO—Kغuÿw¦²¨¦@Z^[Ë¢Erâ^ÇŽÂ*ììXLÔ×:>Å#N ´Rjé­  ¦Åƒ«AHÀFéEÒè‡'e·ÍT…LòBq3çBqBX#Ø%–Àì4•™F€}]xßÀ*‹Ãàã|c"ÃælZ~âÍTV稗S%…ój²©ï0¬Ò_±²É[§þ‰ºœ•®82ÿ@ØL†ò$ª½ä™êd&v@ôó-M¤ÞÆá.@j`ÆÈ?éMYÓ1†>©‘:dL Ù Òâ£ø^äáp1Y䆓l1¥ép?˜âQd!s&Þz £NÆ£r‡¹!é‰k…´5íÐ3½ÇTÉ¢Á’òÄ£èFÅ{дv§ØçPt|ù…gÿÌÎc"Ÿ&¾ —Gà¡ü<ÖÞ)q…¾6M?—æ™2Ąߢ·( "Ä(ï Äéi‡â ûùÄ[SRƒžá©Í²„öX}…K„«*í´½<8‹OG@LÔÁ?ÈtC F¬1£´Ðñ¾F4¯‘Û7o"‡Ê¾kÃÎi«Ù`y¾Óa˜´>RñJäzÜÉ8ÝM‹´çM'ºè'ÈÁî'e8})„ƒ]j·O;~†.USI¤¤ªNž†ÖòTNO±; F~a›ÀãóF/0.Ý~úŒªtöÁ‡üÁq`½ôwg÷|}‰Õ$v.Me¶áŠúƒ½8ƒ½ûr‰m‡š°ò® IºÃiïþv¡f½'¶váiÔ6¬ÇÑÔàác ƒZ”f·Ó2;Ÿ\GB¾X–Ý`Û(ƒÃóÍ]q9ëáÃMϳ†%w¡ƒu´m68Œ7ë³[·¤ˆÈ& Iòp®ã飵?BrŠŒû']ÄÝóƒ—J^6¶-öΚx´í&˜Ejx8¸³F±/=ûOî¹Iìv…®ÞiÐ瞾ܖvßÅsG—ŸOÂ̧–¯ó4`1R3ª,çßSêGäöG#{O‹P"¯u=u”l~Wl äo>é¼™–ª0Vš2+r>ˆYë•g½ÒÄ* +äAÃ5ÛVÜÄè lœ•ì´±’¬¦À¸iüè– BpèÐH^uÃùÞVü\•µ െäh»Ä\‘¯–‡ñó0ÖVåÕ0[ç [n> Ê“oùF§ªˆqØãzHÖêÈcéÍ‘z&Т?¶fÎF(N ïK–‘‘òng³˜ªíuÚ1:¨Kæ´“tò;x$|˜2^´.âeØPújUé{6H%v…ͯ‰ÈÜ“³Ö¨r§ÕísD »nÇØT³…¶ç¡º,‡°ˆæ±C–ŠãÂÍ3÷ä?ØiæøŒ³‘¯ìzK,•ô £ªØnpqeeýÚ%Ò”þl6ZJ#æâ8¤e‹à(1™ŠRòn” ô©,n^pÒ(˜ÃJü‰çCí M ò@ Å겺…Br…ešÃ­­¤J&bª¡2ãˆk»Š(üÎká²X,*1:5I§¸ú PÌ£dê?î“n†–©‘ž)Áò¼U8ŽÌ_ú@§ZìX‡¿'z,WWÇ¢-`ÒeHÕ"Œi2Çneœ#)«3^Z—|†JÀ'!eM‡Š¥É‡!AíÃteXH“ÁºX2†ž=tç—±Y3ìZÀ%»k¤â- œ±Æup‘ÓÓgh¸Z ©'ÏÔ%ðìX >6ê°×qmYe7ƒÃ_ñ0¢7&ú_2þé°?óDye@ÐE ð5˜¤%¸J¼ýã%¦PbÓVAôÈ‚P„ü^¦ƒ"Þ£–q’ö„OšL7ƒŒx“Ðüðã"Á™’Ü-´Ý˜ñÀ0Èf2`ê!e•(/ýR"¡Ò,fpq‹K Ó4‰Ø ºîQÊ™ÙÑâë¤KFKÛ€;#9K:Ïw¼C,XòŸ [Ä6aa£1<Æã•¶Õb6ü7p«Ü&ÇšÏrÙM kD ïlPZÚ膤µpwDt>öUxÁ¾¤0ãK ôeÒiUo°c¨‹¹´þŒÊA‰9%›:Þ²ñÒ± ´mb«¼Í¹:!ry×KòèVŠ‹Ñ¶ˆD¤Íiæ›+¤âXîEæ…B‚š÷(cÕñOqhó(‰ÿú¢.㯗Ÿñ’-Pxž‘w‰¸aä 4-íÿ u)ÄLÒHÓsAX~J ²pŽMï÷©§õ0 â%˜‚Fçåx»ŽÃ耫Ô»±1X¼¬'›âi*¸×ƒíÞþ®Ö¿Ò¬þÛÔH~v¬ˆ¼2^oà§óš³ãqnÉS=±2¿$E›ß^qoÊï ÁXká<Êõ¤lüD­ýL8(ùÆŠR*ßb ÆÜ€(êDÉk5öL¹c[Å×ytέE/¤¢ã1,øëØÚAá邟(0°6I[‚HÊBâã 9.ÇÁB"4pLœáÒJ¸xÂY$“ÃD5äßQö:äY-‘»v¢‚H-ý^­¾®ÉSÖpÏ÷ÝÙ.]JH{ðÛÚ|cˆ_=6‹ÜIq+ß±l2°ôTƒØ¨‡áj_…`7C‘[¢¡°Ø>2 C»`³êrGѾ‡ú¶°¦Y÷¡$‘Ý9HXˆÿü‡ù 8™s¥£í ZÁV,^ƒ®Ü(>‡HÅñ¥Ý¶º TT7dÉnJ±Ü«— )øö‡Ñƒ ÁCZD ‹}S×þFeÄÃîÒKRÄŸoŒÆBå±7uòL; œj±°ï(†9%oÆr2CR“lH~ þçoÊù¢W ¨c‹µ¹%²fñozþ%nî<½Ñ…%…AER¹˜#ijÏ¡9‘º„<¥Û$ª „‡ …A{ü?®0Â/¥ûjb\ð0mœ*a8†ö2¦4ɘJw4pˆ~y|QÕ Þ°·Ò1 «?ø³nò ¤;¹:àíì5/å´®2T›ë2|°ÏªvÍØ°aÒ ËIw['h=‡y2|à]&¿°, [ [ž(~]’\‰M MÒ)¬çrÕ»B›eZLð~Ððþ"!áæ¬·M ù+ùµƒ Ío׉ù&GF^4kÃ4IÁ¿a(ìó¦ JJf?³K<¿'4H"ŒÒ/‚¸èØÞ?Éb·žžÕ;‰&†Åø‹ í¡'пŽya¸-õmp€«Ë)0éhþLk&)ZPÊ kùœÁmãm]¢ öEØy•i$z'dÊÛ“^åÐZ’…+)¿Õ‘c‘5¿.GýöÞž’3|lÉØô¤ž;M«'æ@t’Ç*D2FÈç +Eù1¶”2—"Ðô#‹YJÇ'æÏ ûý4£ŠiXä bŸš±Û'7e‡ž¬_Q_¨<È5àÀBs2æ¨ï•¿Õp“ÅÃlpoT ÒAGùY½Ï~r+•½¸^ŒÖ´®'Dχ?ö97(‘r&Ó˪Œ½Û£ˆ~3þï$vÕÒÀ@¨oi¤÷ž.ór¼­ÁØ#{¨§«–¿`Ž"ÄÌD€MÚ—Aæ;S!ƒz?ò%wë\µé$‚;YزúÆ&ñ¾0$Yá&c"\pÝ%"þ¡å¦QAh‰“ ³QL#Û6Ðû¦‘åƒÂÍKs†àË a6·/@3s{5^Sàm°ÉÚëø ý“±ŸjjN}©æ †Á>®%F‰ŒõçZˆÃ*r;Œ·HLuˆ=î ©Ê%þ· œ_ˆ‚î3E]2†|µòPÊÙ›¹)ª~‡Ù-9za[Q²p²ÈÇý+Óm·Éâ à‰4b°Ïç¶!¬±€ŸšrUŒ!£’£]àb|޶€’w¯é±¾ÍÝÁˆÔ ¡¨I=ÆÕ7"ز¯ˆh±8^·ˆ‘Éϛɷ@Pªuá…CÅÐúqmA>dð¨åeZ­ƒ°ÿ~Ôâ$ cœ&~6dŸ-ÙÆ{1£a(YHóJ`³Õ±ó8µ 0LpX)üQíÿ(Øô$_ZÀGõ·–ôüŸœè ²ìšÇ)øtú·:]„bi…©ÏXëÞÜÚìš4Úœ˜Ee)”:KÑÿÛì„5ƒÒèà–d#€·š^-§ù‹/}Kr*Ö‡ ö®8î|HÙ_C×€‡ÈJÈxƒ—ðï2¹ÜL+’hdFŠð žÖæŽkÇùÌ! `ä3ÂìZ`‡vZànôæj5CsU‹q ÜÀÐ @µçÉoðîúqý’†M:SÆgM«üwn†á»~Bñº0«à×"ãì)îèRÁŒ4ÚŸþò±Ht´—‰ p3‘×!Õ˜ vŸŽ•fìØYï’2gWæÁû ‹Œf"ÿ;dó°#øb¤©K3„.†^ó5]ó3s©Œ²ìê8],ßê‚ rÏà=4;ñ€½Ñœ»ûYÈP—ÏŒÕå "`œ4›`Hþ«ž?²2‘õã5Ëò×P]—â»™°NˆÔ C Z¥ö¨:q¸5î!t!¦[&Y¹lðÐÊ\Š‚¼)!ðtÂá7œVœ»{߸ Ã/û¿S÷dbΰ0MAþo ÄGÔÞéw¼lŠsmä'Q<„¢{Ld˜½ÆÀc¹;*z ©î¾ù#oaÀ¡£^‚eáâаh?drÀpx&R!Äkó~6¶Vü|äesÆ‘DŒkd¡¤FØÃÆÂ²³a@8 ¬Ä0ˆ!{÷‰~eop‘ZÀ+Gkvú[À )Ó® À£ Bãkà¿f0…Š´W¡¹°Ž5;eÙ6ǧÓÒ×­•L/ëߣÝçÝÒþJ€ÏsÓíâ‚Ù–Ò9¿˜97Ký0“ 8 Á'B­ ˆyEÿ$ÕÜxãL£KîB RÇpJ¾Ænƒ¡ÔCˆ÷îYÚž‚è‹à‘´ÂnZ/ºÔ /u/>¦ #¡ØI½„•Ž.çM™.RM´À¹ìZÀ„ªÀbPÉà(îÛ¡å’ââÃh…äˆ'» Âêd«ÍÅ0`Qu“ô¢&âWñØgÆÎ¨ü6þ@ç”nø~ð Ël4 ºÇÊÍ0¼æ£há0wˆÃpJZZ‚Ý!©€à`zn…þ|œþm¥&üòº0’{­üõ\W׈r¨­¹BŸça)ÓÍ*,,€6m.À`H7ÿ-Ðôk0Z¡†žšž?8ÂTÊÏûƒÍBòÌK‡Ž‹ IᤠÍ7¨9娧˜NõÏ!èvYãö¾Y+/1SŒMU“QPú}§ˆ6DÔ 6déÕ‘ˆ++ÓÓ_oæAwÙV ²úÔ¦aB·yë<īݷÛPÏ%ósÖ»#ÙáøFjZÿ¹K=~‚ FÝcß·v8õL3Ö4q,¡Äï„æEºù gUçøËa?é'À½AÝï>ïÄo¸ñKìkÁÓXB†ÕÄü;d8Â6ÙßÐæ#ï“Lú ËóÖ"ëñ<·ø³yr,Wõä/úú«jaÀ&òê„*uêÅ{vá …‹aEd Úò;ø³Ÿ:½f‘ì“í¤Xué`³\Æ~¸Pú:¨˜–Ød8ÂÞ•ÀÔT|."Ð:Ë‚ )£&Ë#VV<¥o"6Z¤x¦üir1{i}é2J¢ÒŸ'úóþ÷™ä@ƒg¨®¡7\NxaèŠÄAa}Òqª€o 6¿(ØL„Ý­œp3þæÿ*'Æ3«tEJ0øuX~+ì3œ1ž7&9Y€–®«!øÖý4¬lçk /Á1[~p±bù[X"'þa P™òœÍœ+4ŠX$Q›ãŒC¶¥fAÆÜ̇ëèH®e&‚», e[Âl¨=c–ä¶w]ß\‘ÒÍÝíu½993)d!SÐ.j\ÉPÌü(H‡õž’—¦­vŠþ%¬™{m…J6„ÛZØD™‹+n+¾ê _â«#;àë­u“œs4iF•:ˆ÷SŒQÆÅJ‡ƒ­„PZýÖ¤†éñªÈ±6Õ†PQHÆxÔ›½§JLu>!äèVb€8|à)«¬jEÚSqÈÇlõšíC©éôWŽò5éÇTV¨íevچώõ¤¤Æ³cNŽ*ô‹³WOÊ€3ÖÓ:) m¼Üc/"ž!6ùûÁswÛ7n4Ù~‰jàaê¡¿ª!¾f: — ¨‰ú*kósôü6 ãÜ ÌþDàoÂX¸Ð0"¾ó7îtÂÿifXŒçQdì^³…5ñE5hÞF4P <œ–Ð,¦O<`œf˜©îÄ-#$ä`FAB·8{Nxi­!ž=^;¶˜PÅiñ/w=9é§! ;ÐK4ƒpõ™/ñÐ8$ê€ÊÄIfqšLÌήY㛞‘ÁÜzÌO”ð°H·W6au¡‚§§†o/ÅH2aêGmö{íQ_"~IíªzÒnïD¢K\çÔð{ Ïò]sÝfÉTjŸa3Ž ‡a´aª*à3òrØsЖgg¬•v`è2ÀCÄæ]Ddz¶™9³þ» Ú.MÄíFyÀóLø42¬oAñgªåÈŸñ?K§CQË>OÝáMUFkæ&¼ä±?øÝ}k7³ï–¢J:™ƒÈ7a=S&—'ÈS„ÛœG`9€À­&*Ó(8ÆŠt»cÓñî\ ™ÕBQJ`$¼_ÐJûðµðö‚<>—–:7\î¸õ˜Í¸h¢1‘€£ˆã~x °%/H·~BdÝ[ˆ1´˜˜äP½aœv.Ñ厭%Çè/À›á]cTP»I²U8;\tܾPÁ©7ôÜiä òfBÛü+ ¨.?ÓÇ| /Áw3eB1Aò„˹fPè20Hó³6µ;êZv%.ØCM/1âxe€Ž`b¿¢ü £pÄ\kÜ<ŒN}GL´˜²™poƒb§Â7$x.‚‹Þ÷/ä`²Œz è|ìÁ” ‰Á $‰šÖç˜Ug-Ï‹Õ3¶g«÷徉æk5f<DñR¢c; Çá^­ƒ‚aDýí¿¯æcwªVÀ\@²]z0Œhä7Ï#‰OojÂe+÷Á@µê÷”‚`Œ¦?eÖkÚÏàHžæOŸjÄ8@™ûXY)àq>ˆ…;AiO Z†G˜3#“ƒI›%dÉ*rŤY" ;>Ó–W À¸!Áwë=æZÿT-r@(þ>€“Fб‰À]+3” {8§·ÇçÈ’) €,ŒU¬ñÁ̲[ž59ð‡=ÀH·;h׿ÑЦJÐ7ÕÍÌIâ› 0§)b¿³ãÿ#ïŽ}ð¸$Ó±|.Ï!"ž?ák ´GKÈþ¾¿ëxY€HY : %RÊ{£€ƒºÐÞ9eaî¾ÀÏxPØ |Æå,ß Û¯¹Â(_ûÏ?š-ÿž\omBªTlv ÌlXÅÝÔ´fe¦”­ Ð^‹ó]Ò›¸CBTе gü|€uG Âx˜ÕÖc)Ìþ×n…,v»î4RÆüwŸÚØ0‡i˘3d<¾€\ûá˜i‚#bƒp9;ÄJVð%Ñ©}k·ÆzïF%‹¬øåÇoà¥"!$»BÒÁnüÊÕܯG˜ÀØD/·N2†ŠGHÇâp3’¨ á¹Á±YN"æóY£u®Hð;òŠ, ΖbX˜›œ]½`©Úð[«¶˜n¦žÄt‰lÃŒïð1ÜßT£ +5P2ÅwŸïûð õó;<´^æ¯sBÚb(ó„îÇHí ªÐ*Ó²kvå ‡•½ò^²O°‚´c?Ž(ˆÛ‹#Ëà‡ZXp(òÁZ_¾O¬@þJAóm ßH›”(W A\Ò8› Àñø@‡ñ€­ê„Û0–Ê@’ ÷òèÆgÀôeLa&Ã-%>Æ+w|ü¦ütÎ _ B‚ýè ·€c þå™_¼$C{À+øä0DLø¨ÆEà~6¬p¥üï€=ÇÇ cPð_kÒù‘»ïÔ$J`?…ÁãÛä€ §k<üµ¶áFP@`u“ÿþiMAB [ôÿï9 ”ÒiœV‘SËÿóÝ÷ÝÞõd‰LùþºëúÔ¼¸Eêi¯wø^µK{øÿAÜÙ³ïý4ÿõ®+›åOé§ÿ×è4÷¿ùý¶áÁ0´œ‰7Y(±gÕ &øà.cTTÒØxräÚ$|¢ã‹L×ãäʬ‘¯ß™]„å[ÉgéäÞb•áâÖýí{é ô«¨óÌUjA øXU_½rÕ8¼gÔcÕÍžj‘íâíቒŒUâ5+Ò¤jäÇÓ© 0®:›’ª=ïÒæ¸¾é¾_m·ͨªÍþäÏu.q•Û‹þo\Îfdµ# 2èºÜ¿b3JÒ¶¬±l¡aÁrÛ½ƒ¢eýÔEÇ<~¥¦Î·'~˜½_nÛsÑí¡ÐÅc}nª¼ÌNÓÓùâ)T’â¤Ç”®ÿSwJÿj©q|Ìi^Û|§4ÌFB^ºz¬ñ£o÷  š¬ "¾í‹gØðrxÈO—eÐ;ðÝEª1­`ÿ“iS6Wj}n*bÚC¢´Ðh!iÐCrW…ë·²/dYm¿‘T€h L*G™_î Z½vÙÜC¡7/œ-Ò5*Ž %1sQ»ÃüvÐC.»s+Tü…÷¹b¤dLüœ3¶€,>JÊMÇ¢AÙI¡~»*Í”«.…ho»!ŸP± ¯:JšÇ¶6jŸÇ-ÜX®= $>YE—Öà–(ФÌ7$ñRyA2|³Ò`Í15 õ„vlã3š(«d©4›ŠY¤gÀL4ŽYºŸY…–)‡fKˆYÝÅ>Û¨7´V‰Ã‹®Ö—²¾öæVÌ–"#ÛxöÈMUÎD‹¤ÁÎy°!—€À‰LºÉX’FYîöÚ™t8­çQª™uÑÒºÓ“`úB}‚†Œó|C³qïFÐÚBJÆyÅñ(á,˜+»Ÿ´Âl•÷<·iøt–΀´…r"Q6ì!Xø–ÁUp±C¬ü\j±©ˆÚl ¯Í Hqk?â+ZQU@4¨“°$¡ íu®ÛE#?!ÿCê?i-ÎmCù…_@"¿äª:´a&Ý5öÖ=wÚߺƒòu8J–P‚ÜZÜýÅèë…îIl†N) Fª¸‰:øˆ­˜ƒÇ üÎó!¦ùíG:è… s?Ç`sÙpAšç—Nªб~ ¬€ÍÛ¡þ@tÆ@ B:=d ÃL&½Eñú¡PDÐÑ„À‰i¨öìC”ÃÀ4`Éu!‰ÐOI#Œ9$º~jÁ25ÿø}“N‡1Â]rFCQ.ØÉ™ÐÓäãxñ¾I6ûš'6m×ÚppùìC"\ÎäêÓ€ÚØl)4nqÓ>úV·äeZºš¡7W C‘ªÖÐγr4…©¡B¥µ„ ¤š# ÄÏÏÊ£Íð,«z[vüͦ“RìMµLÂ/y’q@È®—+`ÿ¤êtÑ' ÄÌÑëå¿o®õº‰E»OŒç‚o#YäÝ…l´îèËÆµ¢¥•y½ )*!ß~c±N¦ó_ž9ó3¨JdÓ¤¡„ ÍWI¨Š¼ä-eL³³òÀ̧‰zLïPš¬ìÅž×é½0z¼åÄ#†v9Æ=‰O1½„9i@,¹[¯Þa±³&ðÉ `w,¦·5€Ë4‚S¬·å]£^‡›VfÅ¥`s¼Ì¬t¦ íDJNð?êC@ùNåñ†énÚ.8†¶yÝ@¢ªÄÑ4½ iø¨ ½ c„`þsBÍÔM·jywåáÜ`¬z;FWí…šñhõÓ0“Ø}x¯·–©fQòJ·_%$­…ÀÓzcË#V Å]ÆtÄ  2ñKa‚hçÔíô`JfÙ°êJÖ²‚šzTP]ª\³Z܉|à'´»08þ†|Š 7¡©ˆ|D7Ñ'''HÛ·Kq‚O K©9õ%¸È^&p~äñh‚)âN«7ïîˆCÚÊ|Ù€x u^'sօ܆“㥣ˆ¶|ˆò"":R[ ¯ä^/ßššA¥‘È®üÍ:µ‡ÁõB‹x¿Dó³µç¨I".´*Túö[–}06{oqÑOâf7(WH´² coYlò ^ ì¤öÊ! b£ Ê냔ì°úÍÃðvâ¬2pÜN}}'Db·ÄnúG^!l !^äƒý,uq]ÿ£¡ì%Þ2<Ú¬ù»ŽOñæÑ¤µ:ЀÝÁäX´ç·3 cñ-áÑ{p{,UÒ=(kæb²if 7HØUÏÖŸ!:£³¢]Å®_­\2( PÔšŒê묹ðÙ©Ûé¾ï. i!ô'צSñäÕ„~]ÞŒ`r‰…¡b6Žñ,_ŠÍø¥´w‹†Ø &Þ(ú(ìæ§Ì§ô{OPCá–‡usF¡ˆ\‘ø5¬dX µÜˆ`Ûíáoq {ëQ‰Øn}¤„Ãè­Ia;Pl;¼s˜3yÌSCµw9$áíMaF|/)¼ü‚6Ÿâ ¨º9ס©r¿x€µ®‰Ã!]wãg$ôëâžGQ^¸ òèý†žªÐçà Ÿ MlCbx=ü8E=§RxýÇ¡¬­'“Õ~¹%`þ†ªkYäY@^Òɲß[ých¶ÒdýÂ×¾noB4Ùî’‰FtÇnÙ]m ‘ÖøNɤ!t =£¶§'ͶG¹Ä3Á9­®ðëË K¹: 5‚”A\wkC´µ såÎd[8y÷±òS?§îwâ—œ«t¾25n]ö|ß?AKn!I*ÝèäLª¿ìÑœ­Ö€£P´Õ}Äø§pÇ–l1¬~ $›Ü#@u™™I£ÑW¶_ï¨<øXñ‘ ±£Akl%àÜåá-ää?d'“ˆs„0îheYÀ 'ü49ú÷bø‚}ãà\÷ïŠÿ¡jKÀYÅ[YšRÕo›ßàBeóÂf¢6¦[fDš˜Ô¦QÔÄS"9yÚ~7ƒ¤=ðãõ"©HÇ8¦ÁS˜‰»^$ R®ô×y7E§ÄÜÏF®Y "]|€Â2£? pMã7ÏšÎð aç²hÆ)Öá´˜SÜî{ráà–)Ä3¢,Mœ‚À}¼ð&’÷ÉçK Ó-v¸Æ¥ùôÊo)¤³Êꌵ¼7íÝçî©s}ÔUÓRÄñz†Ä†´¡vn!ÇhNX°´²Äð,C¦ZdLç–ÄfdÉXºI`žBG'bÎîå“õÒï#Ð]e%ÓR 㚀 ák¡Íg«}‘<ѺÏA µäù’ö|`ûŠ(¾Oô M7!ŸÅ×÷ð {skî–SähÑC«¾…Z1•y:xTQωå÷×½‰£Ýú8ç`™KOôª_…iíì#Â}±8ö8­Ql€x¬s S `“X€#@ñÝ_û†á¤5y ìµ؈3À¥”ÜLûÆ…y—q*…•DsÊÁ¼ÜŠ)LíÖÑ·­ç†xž›z¿Ôˆ·‰qh!§Èí7ì¡Ò9(L¦Y®Ö(ÚÒmöÉg àu› ¸/»ÜŒ5R†¢Ç¸$¤`ÓÝx_Ýõ?ÛW>ò-Ô³]"š‹ßuU½ `T0ìµ_†Ñêf"~Ò"b%¢è3q[#äw‘‹k#«¯°Îcî–±owR´jã„ÉãcðÉ ©ÈÅ+a7ÏVr(‹f‰ÑcaŠ#s š9ÔœvÕ#8±h«9?Ñ Ad´”½/R‡àáËBÊ=ðaÁÙ¬qé-ÌÜ,û%'î€j@ÆÍ0F¯½KÿÍÙýŽAC+]–úË–V4ІÆÝãÍŠ*é¿%ÛgMiµ¿‹ÒØ¥+>Š¿ ’ ¡Á ݃Z¯ž}¡¼”ñ§¸Ï©!À¼^Ñy½bö4?Xø »!6Ú$s{ý­~nÜ”U#˦£Ž{ØMØ–yTgZWk9šé°+ ˜}Ž®+²Ñ5—Ry%-íÿز³@/7yv¬t¢Õ7Ÿ3)yÑéoò`˜]Ž7íÐh ï†É£FÛÂÌDë·¬f®„”¶Iä–¦X¿í ÉÆÆJ•À~ˆáµ®ßÅÍ@«¥¿¬k³ Û%ÌS5‡º "Šf¹“,©E‡Z£Ž”í÷‘GÅ"ý:à¦!åàì\xŒ ¼[cÙ™ü7 ãœ—7’O§¿é£D'#DðÕí¤ûòïU?ÿ³¦¿°Ñ=VŒ?l¸™ÙQb ö!iŒ\›ˆ5ÆSÊÀâFbËjV¥W9.<7¼2ÎH¼m¼úËmˆÒç[bfS½’5º Ñ^ ®„Rÿ椶&ÚÂÌN“œn ìbnü–´»X£íø@¼§ùÙXÌqoŒ0çX”DŒË ÏFu¾Vm©¿¬q# K:š©”‚ͤÌ@a(7nŒèȽªŸ±ýe}›*ª‘\nú²¡b£hú'0HdØÁÚçrw¿Å˜‘‰×R&øJè=Q9P;O©ô—”†<)3PÎG6ƒ„Ú.±¢X…HaáŠ<æ#ý¡ýzhòö³-Üî8„ENËî!ZR•\öÚYAŠ+¥LæÑSkÒW¨“wm£—”D®a>ÊlOu×Ëð¼f¿®ÓY ¡×|q×ËïñòP}œgÐŽ_¥2OÐHȶkžœò—ãlO˃kn?lýˆÉˆdøk–Êßêsùºâ#dW˜a#àÃ[Óf‹Ê1Ï27óù1R“ð3á^´”¼U8Z´ù¼É“påeœJ}‹Ä¡`¡‰Ã0Õï?mG$mlž$rÅ÷…± ‡VPøLjé2×¾?è;ÅLt"²)?’"`hŸ§ ¦TÜm¨Ís8’²Ö$•e´Ú^–t”8AÑqîð1:!6û•Gþ,šÅdÓj`b~q¯.Wû’m¦AÏQ°”˜?xê;ˆ¦å´@N¤W#®Qðq Ȉ$GëÑ*öàTÆ=`i'U‡gˆ1û0?ŸÄ,NØ2ºAäž.ˆ›(‹/q? ýx³ÔqÅw5År ®˜V8e³OÞF3ô±£X#^Ö¥"Á´^‹±<&BºVÂMäLXé1ÕáËa’¾³‡â3’L®ôºó÷ô€$HÁ¡ëKúp«¤@VÝ{8Ó0g/SVÅ"°rÂìÿnÆÕ¾ÿÏ4¡‚¢uH}úv]d½–€|¦5AyÀ¢/öÔl’jÐý÷&=. Ù}÷9ÿÚìåÞ<åý;Jöî),|A®AÌ®ºÚ'i`ñå‹]%FŠ"3³jÈÐ]A§Ð×`U\…“Ž Ö3ÄvøÙ¼…4bs¶¾6Cš?þ @u‘]Oß²ü}«ŸÌÑ(hÄ_8¬Ú±Õh£Ï“!ÁàŸMk1Ãóá3#ï™vâ½8ħÏ?ZÔV(qqî´[>}v‹;°Î]§ ï¢Ã´ÿ|BJ… }‘V€æ}Cî; _x¡Òoµ8õc#kpÏ_…eøÌb×1œCQ‡` l é~¶ºS¤ž»½à°õqZº€—{ì8‘m…j–œRö­æÜ ǰ,_ÃGAˆäÝA‹‹Ñéˆ,¬$ê9åöë§?DÜõwõ`(*+<¶C|çàÈQÚÁ‰X© °bÁ Ý/˜ +¸øÿ¢¬7‚ñ¢M?#Kn>½óŒ®÷L\ó¸{¡%– |Å Y¡þF­SÆ×zÿZ9Ãr(r `|ÔuŒ_;ì†\Â+è»é°2ÌîF>ÓÉ‘…JÿÃ<Ó$€‚=”LX‰–j9Ã^3^ƻ߱AÖNún2YŠŠè[«æS ¨Ü©…ýH¯W!Á‹À%øTí/HNC`RahìUìr÷`2¸ƒJLÙéƒaÍq=¨&ŸšÙÎÑ®Qµ€ÊCB´Äª@þá°PeW‚òRyNæû€“>4á#‡û‹œBgÔªs:µ£¢¾æÖDÁ„³,A&Iôì%kÈ#lÁ“Ì “”,bWpŽ®³C·‚€Œ}³»Ä) ñ@µv:Æ` èáÚaŸƒ­?ç5¬KQìZ¨/¡ÝDÁP!ÕWH=‚mM€nÒ#û‰¨O´22Û6€°2Y</-ñí¬.±Óäûì 8œã¹?S ˜9kè2M!½ù™€ “ƒ„D8êƒïå&ìäÑ6âý”1<9xqÿèN¤(@-6T ?Åë>̉‡!Ì^˜&BhtÔy€gж0ÌbW¸HxœoçfœŒs€í®4 !3t­d.)%c,˜¾µŒýN³”¢Z@›*o=Þí*ZG‰ƒ×ÛIÍgV¥ÉãÕTjÈÀJµ˜û\ƒm¬¦î+ d`;Q'}1,{·›ÍSeaÀÒ¥ò–á¥(ü…P8sÿPôÎ ïvÒ->Éæ÷?gGÌÏPôA™–¶(ÆLèeµ2}dÈ©ú‘¤8Mã")·­„Ü¿»ðr)PŸÕŽrC\­#ïkŠ”ú²õ¹Ÿühh®YÁR™;—Íç{~ãž$ᥠ{ÄŠ,ê¶L‘VA`j’(43@Áþßq¾{ÅØÛß0Ptl„MÂmñÈâ*¶´Ú 5D–š0ýûýÛ‚iÇ–ÙgUw£(ú’¼5¦1Å(ø}±±‚Ù eg,\ 5¬”#üImÝü2 ^;¿ˆ Išþ6%f>àiäòÓ` W^F ‡ë#äv=æ9½¬ž ò:B#„à5òkɪЇ陚z>–…™n¨(ü¦Á´zø³9nµÍa(ßÕWZ "¹#uèš ¯{<ºÂáGV—ìGœ-üôcƒtß&ƒ"ADÈ&ùãŽm1ô5 Á‡w:Ùà3ÑÕ.GÐ$t ï?ˆâ;»#CnæoXÀTŽˆM)~Í8Ç¿^¦ÛzAج£,›pÝÙŸŠ­ Ûç×iþ ¥÷ŸÁ*mîÏÃL8ê© ®.ÏÐIùù¶Ø—“—iò› ‘ØÊäê¥.… żõ{ ‰Yº €Åš" ©HÐ1(Sï´7Ôƒ•Ÿ@ƒÑ.Ó±IÄÁ$ëòaz> Õ¯rv4•^´NHÇ‚˜Q±Þ›ô×eÆ ¾@à¶€zEόѿ…µ¼ln¿ ¿bP¤Ý.¯Ðb@‘4ï“ôâ0L Ò¡œ«ÆGw¨ñtzªb>nKú“Yðã´œeÍ©2 äÒ<»Ó´Çy‘Ö„‡Âýú\Ã+<Ü‚¬rPi\è‚¡Ç&®ôžldÿÌO´~søˆp8z¾Bé"Y#³[<[¯Ù_Š—ëÆË(ÊÉgü3ÑœãÙ1ô!wÇPŒ)­"‹%–n±­pV‚Ó£†æöüCw¬Ä°Üý¿Nüé›àÝJdeg»BäÊQ×ô᪽T+Rì(§ð´Iu0)Áe‘ÃÁÈáe½‘²fi5¶/Ó9æÀ*^˜M8=9ÏüƧ-Ìu#¯­çøAø¸$ (ÎjÂëQ± !@ÆJß¾z0·F"Ø,Ña. _Ù e}ù—ùËÌ1‘+ßóa­V3ÉÃÂY0k…!#Ná–3 ùà8Gâ?¨i18Rúmj4n£H‘z6ìï|Ó×2÷°‹ ÁíZ1*ž#lúi†ÁAè;ºlF{>ϸÿn!P?º÷ï!€Þpm­ÀØMˆ%Êt‚ÆÛóà€º,=‡XiàwŠê4p…éHèØ…GMÓ$nv–65½¡zÚÓŸy‚'¼Þ kÝã$I0÷äkó!k›í+A¯™…,ð, œ,”Ü"ÑÂ88Ó²'p¾L4¡¬w7¦ÜUÙwãŽ$ˆ2*>"­þ£Ÿ¸/œQMŽ\™ªDµ#[LAï=]À‡~ÜN›[ð¥yˆ®¦ü‰šIßãh‚À Ô@`ªß'#Cè9I´x=ߤƒà„„(B`ði¸¯i\ˆ´z+äF3÷µéìÅ˸^zª"ˆý… ˆaÀyç¾)˜€ì¨s%`/µ°´óf.È€z+*²Ð R5çØl’-™,, Ø hÍT¶¾Ó¬UGÁíö‚äÉŠs#Cøc×(ƒ’2Q®Ï¿ü÷„—B˜¹¯ÐD[I0W×ô²Ó~uÜB™ÜÁcÀ&fÙàˆaÐ`É´ç–…&|î˜cyƒN@“°ˆZÒŠb¸I ÍÝîMr=É0×*õëy0åVÜóÒ]õ/Y:‡µxr x|5ÛÐ;ŠC—©Ñ»!«ÕlÅ=k½ïêûå")ÊNªyÄ!p*®7@°éÀŒf;sXåeEú­Ö¼…/*o¤ãžèŸ¬eßÀˆ7Š—u PR•Ý)®#Díàó0h—£uUþÇħ€ÒîäÈê=9Lhoã±-Œ¬ŽÆFæãd0#/˜£g“¡è¶éô+Oî„¢dĨwq}š>§JùâAØù¬ûô(hå<UZ9jxÝÉZ@Ö©V0¿mµûôÁí,‹`÷@€CˆcRƒt]¢ü¹¯‹ 'À_­ÍL_èQ=Èw"O&Îòb+¶üÃlÖ%9÷Ò𠆶lٱϛ,~#í}ªz½ÊÅ º/¡½Aî ¶Û <âPçd!…9~"Hýõ|‡§*À˜:À+0ˆæa«2qGQ°¾}vaÿù8»–ŸÏawD{eäÎ=.¬k[!Ro’®«¼-Rð×ûsF'¤8Цãä1½î$yx[èË•¾Ã0€9®yƒ=@ѿ¯P€>µq)‚ab¨™k¯þ(µïñütpg Aq<‰æ%Òʶ0>a)>9/‚dSMÃÌʤ갽B€ÔU‘·FʨBzžS*z Úi\O’‰›õ4ÑèiªŽ"ªO¸óØ6›Žž¥äM\51ÈïÎnI ƒ‘áÃÞ_‹hf¦†«ÄžÚ;”ç#›öªa’6MëEG†.âZ8°V`]0­Ï(¦¢XvjØÎjŒÂmÅE’`÷œ32§ ³ /-…Æ7áÇZÄ~Å6ŒvíŒÀTå(ÎËqôêW/i¼`ëÅ)—ú×  ³È=´Ýº òíõšŠ.g ¥Â9°¦éî^ ÌÍþ᫆5oð##0å‰yª & ¯âÖliɈt7ýÎÀÀÔáz¨¾wþ¢£ÕULV.Œêt œs ßh,Ù‚4ˆ„ wHaö‰vòI¡»MÿJh(ît¡½Ì=ñU›õ¦ÃW­¨G×â0+Ÿ€HChûç6%ÝÁºi“^&¨¢¥Q^¤v®PÍc‹†Nü)ãe=iÀÔ|§7üL¬AgV?æ[8™VÏ@Ùì&:æÁb诫ؖËËg¡=ÍHY]¾äݱ&÷}&ĹBrš~°3b%2?“X½6G†…t]S2q+¤üßÂæ‡š :ÓQXßÜ7E˪ÜÒ¨¥˜Aä´Ô :}4aÄ1e·žÁ\î Ų0ÓùÕóÍ-iiP“øÇá·Æ ¤Õo5^{°¸ .ZJ PW“|_ŽAô@DCñåÏrfÜŽõ§ºôKµ˜é±7е1¼ãp"†‹E (¯düùÖº¢tôå8`§a[CvB_’~éËuä £„é´²{Ë ˜USšz,üW¶ž¸ck¦|õ s ˆ;-2Èæ¸Œh&YôŸcéÊoúEìÛ ÍG~å»Î˜„wô,ÿ€¥MŽcÏÕ\ÈÏ÷7µŒM Oñgv+ºd°E» C*È»p‡ y˜-‡ÓµøhÅ”Cm¸r >¯°wô%Þ C’5¤¬ËK˜Åjm(ð¢ ôÎsñœšmßøâSò]¢ÈjÑ {õÌ× —h´Gmf[>½ï\wLKm!î%‚1~Æoí—€~ShŒ~~u² È(PðŒÔo©CK6À…ÿÃè“ TÊtOàìGÊ 3@¥iÀ廊 ¿ £:Œ#ŽSp_|Nã£Éxž24zä+ ºw–y<T¦›Ì16ŸŽÁ±G£F Á“ÂéµN—ÄD+Ђ ¥'!ÂÁ¿m,OdôtçÎ1S!v£ì¾ØæL>=ñ'•„¶yÝ´~€Ñ©äÉ,jÌnðQ.iõ!¤Ò=À7:}K ,}AJèÖ’PofÆ{¶ûH~ƒ3r€çeÁö.Úø*fñ*êè?bÙ:ç4üåÐJÞ×ãAšg!凥Éc­ ïQÎÑäü·CtgtºbdŸ_ åƒøgJ×kƒÃ/aüÿ2oçÉÆ0‡5p•7Ç’ÏjÀGª9†©Lø&?¾±~–oCú²ZìÊ#O†f.«Ôk C‚5 ÉþÇV¦iw©gÇ¥i&QÚcJ†'SaIä++¹¶µûˆ Ê”"sâô!æ÷Çã_g„]«F•Eµ]ç¢s ]’ÖÑ ø;2¿«ù5© 1ïšT"ì ì=y lAýIX2Zžo@U²N€¡¡‚*ЂA3òÞòÁìD¸ ´ý©¥×Qä¾#Ðô|5Æef¡I9zh`VÑÎ÷^yš|JèÝÈ•"O!‡ŒŒ¢gv_&En Ú²J“+#°úªõÓà‘±¢ŠÖÞ0€ˆ·‘Oåa.lDí™o±¼„_ƒÜa¡„Õr}§‘ Rš}×ï(:2i@mß®âÙùóGÓ;¬4k UyzÐðn-,—/õ£‚ô´†öÝ2 xšþ3Ûñû&ʶkÕw‘pÁ¢AÛÈ+¥¬Œ'Ÿ€È;ëÅ¡“PœÑHñþ1½ÄïÖ£Ø S( ‡r §ø“ç“ IdÐ]­Ö½mAÔv‹cTlé¾8¼dÍ9µwäå¼V d3Ñë›Ô±Ý4Y§k“¼º±BÛÝ€ýls!þ‰Äd@ÞRgc´K­™»]‚[üÁß)YP˜}G,L!†©ŒýÏ:]CEàL6m6'Lä”±eL]¨Ùy f:f»±˜ jºô£°p©´o$1ö 1 7„õ¸¤‘Úû«Hþ¤¹{¸Y<ÅöóS,:~[Zr¢ë¯î¤ª¶/® Åbso¨f=Ððþ)m—9ÏI ŽúMõšKmKâ~íU´‹“Í4Í¡îÊCÛ0 ßé5Ë7½!ì^jJäe22Ä”œ Y4+ÌØ¡»ýþâùuòzc¡ºô=jà:†}¬xþ‹¨á‚$Ø„5ÎU@ç1v©¬„u½›ÖZtu:e«ÛAì]­ŸRåí’Ѧæõqyæ¥%ªâ"ó+˸ÉR0ö²ö± ¡©ÜÈ6q¿ ˜˜«$OBÀö…ŽÐ‘dÅI`°ŒkyG¬^øÓý¸ ’H²ié|-}âu©GJüUµÅ¡Ø¶?Z0u4P$“§°Ü$Pùü4}aÚjO®"#¹Ü9ù^ˆ”Ë%=’rZXÅì„ Ê‰ã ™‹2cû@Iñ1n=b›ÖÔVùyi4-kjĤS%OËåpØÃ(u‘¨2>Z Kä+Ī&¬’5ÙpgC»ô¦kdÂE4ÊÔ“«<äçYú„{;…6†ˆÛ­bþo0ÞHÖX[»_W‚»PçDtº5/yP›JõÐû„¬î¶aisȯ]²gEÂ@<×À4¥ºTû@FÀ‹ÕBáI;á` ݆kD¯u T!CFÙ3Í7„çZ}̦"Q£^l@C|JŠ( ­JkœôÌøl–Ã8›ÝÀ£´(KÅk6%ΠwŒý¥Aw«îëîÁü¡ÍìX°,”ÄŽ^‹<ën?ÂiÅz#â'+!pur»vÊÝGûbs1”UþbLUÁˆf;*¢Ñö–ÝÖâæ&ªò|^gý¯­™—°ÎO ‘᪵þL„¹Ùˆ”‹\Ägãêå$ ÅRõd5ð^¬òaLœuv¥ÐnøoA»±B„ÂÖ2÷ Ý,çÀØí®†ñØ0%˜;°°Ãùæm÷q­©êZ“:q¤Ç}9˜BÆ+B{ÒMçÕˆ™¥Áÿz ,>·Q€Ù­œ"-Ðæã[‰,‚Ë îA¤™U.6òD"cÜ,`Š¿]“d”þåZÖ)1ÜaÎu¯OòÍ©DÌ–± «Œöv.ÀݽY8(2"Žë²ðëQ. h‹Ý6^¥q%lM`ã,1íg–"˜è—[ðÇkÂA +Á 74KŠTŒaðêñ ÊÀGMáw‡,E $!AËd£Òø?¸2ÿo†_<daáýÞ”¬€¨R²¡µ«­Ê]tbêÏâm’ð ì½a¾²dåÌÆ l"DêŒEüÐ}`ª/½ Ü&Ž“lV@Z–kƒ"ttÏÙwû @5^enLHFTr ÑßÊwÀ“›MÏ|p)5.LM[8–2L¦-9OxéU†/ý˜t¬½3›<ògØ„@r‘>ú8žÀ2…-SÄû ÝXvpTlX¯D9HD8p ØÿÌýæé윘 UkÖ’ÍaEï—n/ †åÚ>úÅûiE1->/“ϪT®kœµA™è©œõ(³þ‘aÛT£íP«—~üÐ$‡óú+ã–…i(ÂÛTdD®$Íw ø”Mî-78Ý@C­Òy&g¨×y…r_–¡|ÊxÒù¦óž“šJë2ДÙ4€š¢cà¢.7Oà[¬Oá¦âð:»ÔݶºäO™0hÓø¯Ìq&‹™ÖÅï'B ÿ<_H‘W_-»k TÆê\ Ng“ÎZSÐi3ê™0Ï®¡Ã…Uç‡C;ü¬@®—œ]jµ®}acò\iAì9‚ê>¸åó€,‚À䀔*=ªXæI3…: HJöúxå¡)î5â‹ m¥Õ²5 Ȫ@ŠQ1Øþ×+~šu"R)ØN¦(pÎb‚’]ŠÈ]+k–â÷pùoQÆ'Zw/ƒ1JŽò>öÿ~¼4“—Q™žm=1o#æGÝs3§|ÊLÕ Ù¬þ$\æ’ÞÏü6|?BÿÄçm`Ö™8–—3Õ¼¦غ©];ƒçüeœ^#N´Ï¢4‰c› £Nžs«&DÓ÷O—PÕ¼à9².žUƒáú@Ž,ââU'f_cïŠñ²à‡°6ˆ·ÐI]Ëø7a‘ð't'3^y§Øný «õ³¶XÇc‰î‹/nSjÆ`øãÀF²‚­°r;‘ÀSop…áÈÔ Nƒy;à89~ «˜!QK ¡„wŽêânAŽ &dd¢Düt†ÙŸKúâ|”~í›C°iÎZ¬r2UÒÀ$V*› g[Á–÷¡•¦-cúD@¨¶£T8ùû£kv(<31䚇w„;%‚3†‰3ä ³µ‚“¾F 7;¶\u›ªÚk! ­ö3€o+ÅÇÑ<’A9: ~˜§o4”/Az€+‡K[ÀÏöáóÿ@Ñö)¶Å;„;¸š¬€÷0•z!Þ8››ë‚O€TäD&¢›Æ%æ¡JäÙÉ(td*†®aÈ¿w|PÔú^"*`r‚ eD…zA­`œžA¨½ HXØí *<„>“é p³h¯ÌÆ,.#©RûjDsË3ÕùŒÃŠÈìh 0 ¥°qÍê ­6¹øÌa¿ƒìH8£¶Àçø Ò1ÚF+ˆ?– šú–Z€DvEøÖëÕš uxßeÁô) [r§@Ža6™AfTgô¥ë®<§BAPF ê¾sNpˆæ®Õ/ª¼¾&¾¢¨ ú˜.¹éD" 7%#Z™¬R÷Ès rù°£9±Ý!Ò3@¬‚„z/ÖF;œÌJU: 59{»&„u°wáÁaf_W«§3òRöž3/æ5tˆ6­š¹T1êùÍ$;þ!²v;åÞm±Œ0Š'÷ûTt½?'…ƒ¤å¦Ür:²™ù•ÿ¢8Z©¾°•ýLÏ^$¢B…›ñ‹s|’u„‰€¥°n€&Ô4…] M?8mùÓÍ\{ –VÂ@HYUü©tâË<®škJÄ÷ y¨´ùo^ zpûœ{a|Œ~[P@RÞ–‹ª†ï‚UnóQ1×U)vo"t…[O‘TH1ˆAã…AÅhv³²‰H´ÔÄîÕ6¼¦ «t;ÓÈE­"ÉX%ÞÀ3(¦ *˜ÀdÑçlXþà ~ZŽ!°ÐxùL±!žÒ_LÚic5 |Ƽ‹ô÷ÀMÁTë©ILJ]>[8ÐæÎÚ2€6q§j!rTø¶V ? rò[ìýžMkS_¤J­ðhúŠ<4+:XG7ü,ñ†ìxŒñã(!ýš7~$À©¡Ä§‘wdóŠs>ùÁšSœŠ®WÀ6ù1üÊî‡[ÊQ…Ó„R)ˆž`!ý¾~Ÿ_:ÈäpØAläË*úe#ôKØÚÁ_ ^eè8!já2\2ÆM¸j ¢hÒ{èmÙ ——½è&]ÃOhŒA¦½±+ªd‚aþJ”¤ cX/Ê=¿Xl!ìÄ¥^m…°v6¢ªIzˆ!îë‰$Àeò`”T‰á -'œšÉ8Nä=äÆy“g%¢6/Èž™„·ƒ%¹‹^Q·Úrndgäêë÷®úm ÞºtÍ6 n‡„Aò9 ! ›?È[Õî¦^·TBgø XI*ç¢/á ç⸢[íó&f»±`+MBuÊÏ€–ñ‚#È^/ãd“/R/ñ å‚3)< VôáKq¶­aܘ~†}ë°­L¾Ac$[ðÅ¥p/8_ÞÁ†ª9ÿS£`^k'`M½iþÇ&bUÕP´$$nQ@só,¬ÃäÓè3ø)'‘€+ÄðäýcÅ›%+åyŒaOøÒðš×žó‡»”Žma[í&Æb,Uâ)#Öš8$—X …†œåâÍ×9†|è™.Heäaül›Åoèã(|G¿]2t„XÛARÇj„±á¹*¬òâÀÒQñK6j†"00$¡Ô:ý…£5¸­‚¿Ó]YìÁï­TAôaQì*äºnäz#¦Ó­mnþŸâ…{Î-+ (U#Þûåvìm%È“Ó[ /šhjY€J„êv¬ˆ…XT‰sZÜxœ†Ý5’ðEÛ"nÏwž*8lÎóŸ¹1Ohwa)eP†(h\7—8˜&£":ig ˜ 6×8Ïâ7uµ'¥m‘ ç ˆ¿ñÖ3©Ý«Î›cÀDê®÷«õ’´Wv݃%ñ=í#ÖI6hé°¬LG‘…‹l þ  íþÁÊó² 5”ídФHð`ƒ§$5(xDçLx;FÖµÚÄÁbvh3‘7ý„þƒ¦§Y…£ì*¹èîØåßà”H¢°97G¶µ8 zƒŠ!|Á½@o°-ï–ÐfUU ?A™¤ãþæF7ÿÑÅ m„R£ÂQ@ òƒ#ærB0R•û½ÐÉ.îÊÞ}„%ϧ̀³xv€Üj :!ö~¡P‹˜*7íIœ;ü5æD˜^• ¹ëÝPX·ài0ɘ$4 !-úÃ(îrOª‰1'+‡öèJW£Ìñf&‘å Úó'&ê16X(Ë©‡RYã#ÃRàeÏÞ½,(RQÑë GÐ%XË]ÌY|R0”SÖiý9ÍÝâ|Šè§ý@` "j…¦'oÿQ,‹±òïÏWÆÔPºˆ$‰½¤Öû}‹›ùµ-˜f®\™³‰,sz¬ì2¬¼X¼`Ôéîý¡PjÂèÁˆga#w°|þûáòùý€™·m!Ý"Dˆ$´‚QeìÀûÙõÁY»IÏ'ZR 6Ï)ƒZl;2÷H-ë(öò€f#Èœ€$–1É'~ƒ(™¡ÿÖÀªâêeà-ý®Tèj6-`&b‰Y¼h£÷^ÎÀG*ô¬Š,) ÐE}ÑC&¶ÊSA„M`f3È,yèœ\xÎð6±0«@xrlÛX}°0€˜r¦=ý?|Hè‘ÜtÁ¥àù÷ɛݎ™]¨ABè¨{âŸÊø*Ô:Z¿Íœ&°–Êð]ÌÆ´‡ãÂì0à"ÊLc¨I[³]ï8E|êŒʘÌêV¯àiÀ<…eMÄŒe`äñ¾êl,3ÑXx‡çÙ>õ=ø"-)J‚;.#0gHÿJ7É|ïç"²h>×7‰6·’–±‡­~E aFm¼*ç¼§ã€Ý<+ÒªˆS  ¿âàFË Ó9VBe„­«`Ô IHk4eP—¶~‹÷±…;œ0ÅÌl±Œ@9V3 ÙáS/&åPq ¼; ‘˜ŠºÉ⽬H@ûúÀJÜ(á!æÿ>üøø*8pÞ ýL#H7ù³º-aHª‹ã+ºÍelË%ªA¤.ˆ?>ï§ûÿ·pƒº„}Œ’˜:7‡\‹Þ^¥®=úº‘\IÀÜ:Ùýþ ¥Ø ;æÙÄ!(áÜÚò—Œ}Åu#ýq^6ÿºÔùk"-ÓOÿáçš¿´=ín&¡IAeËýºu”¾¢7Å_ë»úPƒ ø‡ííèyüý ¬Ùª©~òºÌä uû¿þš~?ÿ¥~ѱ.÷¾>ñßÿAÌù¾ªšÇñèw]/—6ßÿþ>ªÖ×¼éª‰Š®Ytº ZÅo»é¸€UÜ"šh„ V9®E\NË2à¥CFÚ„pc}×1±±8¹8³Ýwç|Ïwþ7Ò‚cd¡@Êí÷Ö:»Ë—{ˆqÏûÿu?Ô×ìuˆ†qç2P‰í0iâ$¼N[¿nö“Á™[m¿ÿþ2*(¢`,.!Ú4Óúwb·»¾.ßu¸cÐèÐôà˜d†÷®K0rè¤fÉÙÐ?»þgÁ”¶ÚiüGrS8*¨¯+ÔŸÁíHc¤;ìŸ-63Œ¡ãùö[ÆÔxyUm¿ÑÅPoQ|ƒm­I@©1„zÎAHìBdO­í|ä]±WñSIæˆ÷Y¾š‡bZŒB%”%*7SM„ÕÁ®GæœoBùŽñdK3³<Ùk—Ü(¯èƒÂZ̃ëyqd±6KäI|¿ç¥¢*8€Á½ŸÓÿEÝzë—3Yˆ˜ŸSœÞø¹®ïA31WAâ1ÈZR³“ÔO*jЦn†jj¢ŠàöF?®µmV Ž̾üß#ª¸H”O-*˃ÜIZO|™NëuU>KÔ»=Ú¶)E³­µúøà5"1ê÷7å“Wk¯S'öÛ¸¹ÓY¥+ËÍžãʹfeϘÅGCÒƒŒX¥î^}óÉ©)^ß1êC7ŒDîX¬"7e7ël¢)u^l0·ïl{ŠXNµ­Gž j×WQ ,¥¸âêÅjÐeÿoâ®fH:­á×§Ø÷õaÍ!Y!Û"¯Fw³\­Ì¬ˆÆà2Q…VQ–¢S(s©ø…áx»¦e´Oàl-Jä›äC á##4GTŸ BÈ),=| ±ögo~Êé»PnB¡j¢á¹ÙeAßužOx¬€´8´ ?ú5ÖX7Ý)°mm†yãªDk~fÉ€SðQ@ÁqJKpÔv'¦VÊçÆâ$ë3H7±"}d4ÙE®j‰N¶µ~f÷0Šf%~st„`ÿniÁùg#¶ÄÍ!*ÿ¢Q1ç¦(:àw @îÅÇ9”s1sÍþøÉâ7uchü¼>¤ÎÕ 8î’äRÊa9.\…` ËŽ, DWY¢ÓÃ)¸û†7áµq¾µ}À¢œòÁÞéUúzkõ²’|~†¾ô/ÝxaÄVT D!wsáÀðŽp¨ö b†¢ËÈJrþΘ•»U|bpÁƾdŸ‰f 0ötÐ#‰‡3NýV²%KTÄÆJ1/$@‡·@>öê*Ä8E´¶bÁŸÔ«lï9è’ѽvå>\ºú îä3cN`qXMÏð8«¿!ò(;F£Ì9W‡àLÜW?1 ÝCôϵ˜¤ì$•$Oä옓2¤A`VãžÇ†3+üøž€2=ú /"§J§´>È(á,ö5],¤Ìó°õi6¹Èö.¸ÔØ!³©œ†wm)TG115“»¹~‚ÏÄ2r° ”¡ üáY7?† ûÇ uRKmp–ˆY aî_6}'=¥‰×#§G$ŽÄˆ»qšX^ëejE…ýEÆzT¸¢ù˜uÌäÓ“á*Ïm-„ý>ýóüAGóÔ¾Ÿ”~0Ÿ^nó0'ÍûHÓç°äíf°Íõ°ÅÞÎáTJ~ièædÎëžgî#àlêßö:ó, ½^×U}ý¨…´Óïçü¦f%‰´ƒÉOvë¶Š dã#¬%RuÁ‘¼âûi?Á*z“bò#ÙtrQ´=„9Sað{¾€RVFèÑò¸è”혼a~ÉNé£ànzFYƒ6¹•ÙvâÝÏÚ÷_²^è÷æÓ"”ZÞã`=ËØóÒn8ØTÍhîa¦Ex0äÊüÏ­OïT©”$AÛ²p}ö{Æ+%¯_†Åú¯_æ* Àƒð߬¿4»>Ñ+4ÑÅ8Tðc‘·²GQÒ_÷,Ýö ¡Îɲ1¦_k… ·T!q6ÈjÏÎI¤[4SMé2*\ÞcþuTÆP™Í¡GÏÓàȘKä÷Qp„Ú t 0uGè²x1HlEŠÏ¢"P1D0&|rÛmc—"»²Y3Ç'3eõ¾Ü߯­2×ä®2ˆ™H.Íøôtù¨ƒä?ÈÐÆ¢ [9“i¿Ð~ts`Ê2}V™Ê1ç‰䋦¬töŠÒD!zZÈ©<¦€$äÀ±Þ¦*†—Ÿá0¶|© J+ʧh”ÄþKçöâ¹ɽýrÏl®ŒWÀQ,.FX *+^o Òò#nœä¬g€C_ú[ˆ< é¯Ïʯ–Vû´§Ì„H ˜}@j¦; dq~‡vw0\^ˆz“§Q\$qt[]A…°I³ô0¦©Þ㢈ރË'¡âvõÔ×`RÛ| Ѹ̗$•GîùÓÙͬ&üѤ`Ÿ‰†ÂÓµïbañ¡†páì ï ºxÓQžxd³¡‚J2T¨ Ó…d¥ Ƈ¤Á¨Øék…Áàm¹V6HÎMp£§¼ið”ð#%ŠÓ¶ 'z¬-Cœ¾>€‹«Ÿ”ïÃx[qlêi{˜0lÃ%ÖÈÉÕ,ÓÒóá:Õ߯°n%éMaŽTVǶ¿±V9lŽ<“ Þ_»Æ4Õ‰ÿɬÇròíô?ʱR›3¸ßY⬩ÄóBBÍ[3å¶’jÒúÆMoF"P7ÛQ˜`+€é}2¿:¡œ}f'T“ H`Ž}AøÛÉ|åë=dÂE˜å±vÀ¿~O׋8)×òE–?¢Ø”‘WìÂ’B¨)jø™Ú ™ƒÒfd˜ß“}K`ýQÅÔ#≟žLlc°c8÷r,­iãlý‹DÖg¥öç Tœm–ø'‰x=¬‰‚@šwI. 5óƒýU¯ß¡‚“eÃO¥EÜ"ËA(l¬•ËÄ–ùž€Ä¡À¶ÜÈdkFM"•Rµä`I-×ü—ZG!%ËK*{ýÙôP2Æ¢w©ÇpBäMã·Æ(ò`w7çkúöÀâ8¥á*ÆÄÂÇ3M&ŸvDßF^¼K¸ÜKþn5€]\î°!f Jhg ŽnÛÄÔ¤ÂhoyýÐŽ"žÆ hþù½­•|Ñà]‚Li<’LhÔW-F:dleÿ_µÿ¶ÑMzÀp‘v°u÷¦01Ÿ™ø‡`̘½yq³d$äÛŠÚÏ'ÞêШ¶¢ö ôg`[­„ô5ä\3’ªµÐ–}qŒ¥\|ÛAÏHœ ½‚i ¾Ûu¾€ÇFÑOX)Å÷ßj7±qv'¢ƒpå-µåíÝžõ‘Ê?ø^ÀÚaUzz]²—!L=`½­+Ñp5ÒˆO¥9¹=géø%˜DíüK&Ÿ–dêOÅæ´RnÇFFïé’¤¦®š_)Cè‹’‚$ &ÞzAݘä–ÕÆ¹ Uq.^f-í¢ûþÀ[3ƒDÊíIÇ÷\@ïÓÛ—o¿®ÏÂ?XcÅèa÷â1gѹVA"ïJ5п霧֙_ã4£ýˆí`hÏ&°H:ND‘ ÍzôT˜#œy-`M»w‰§µñôp•œIb®-biZ»l¥ÔµÁ ïÿb¹³òüÁ~7®â'p<·´³"û¦­€¾ßEYE—@†ü“Òfâ}2÷Éž2š½7øçÛßYF›˜òzL‘RéºÀëïéR±%ø­z/+N›*fºe "IcÜIÔAI(´™\åÉt*2ÕcT¹¡jƒ© –Ò#ºhû”Ñ;[D޶€"U4+ò–,'PZão•ÀUMixiœÛpà°#(ù ·èàU ûÍ›¢½ð+ØQ7 Zàæ–9ŠÞAan9.pÈZË[µ´b;ΦLJì"2©ô–5¸'œËm%8C{S_®.Ê„Ê@‚–¡*èFµ‘]³ŽÂž…Zj,£NªF)W¸:ß»×XÆ$LÇ,›î©„ÚÊ/0˜È VmöÖ!á)à ðœ®)2ì'Òcà \!#ßT'ìšA©³(þépBHE¯ma6ÄúKí&nŒÛ°“wÀ¶z »ÁѬ Á|­ö½üHä{ã¸L&@|Jîáu·à«~ Ctb«8¤ µ¹'ëà“›M…¢À=Œƒ€ºñ/¶ÏOUùúÈ'¼8:ýé„dðh ‘FmÄŸm|ö¨øEEóßò`m‘1›L¸7Ú“«if–NòóK£L 1Š‹ƒa@Æî›©Ó»=2v_¼"Á˜auõÒ+N3l²þ–ÏgWÜ’‚$¨Ü/9@`z0`@&€Ä¨@02¦ŒOãç€7OqX&b(ýÆKƒ‹° ¡Z i¨Ó!’B¥Ð;ùM`@(Ã/ c®“ê|»Àç( ¤GוšØS¸“¹!šˆ ç9<0$Žø¥ˆ 'ÍX‚ßç3oyË(‹ÈФòÝ¢uMU«-DoíèÒÅRÈ-iwQÀ©ãÕsCkÔËÃÐ Êw×Pü“Û†‘*kTÐÐ’áÁ~DIAe ÅØ‹907©’[iIðmtó~†ù¬5MŽŸŸä‡É­ I(&ü]_š“„AgÒjK2·IªCØŒ4aâ%Rñ%åB&AÜÁâèÄN3Ñ 9>²…EÉ¢‹ŒDã ì»7„¶Dódן•‹í$£Hh—/FRnñE-ïù)ÃÌQ)£‘Z8mÀCÿ¥òÍ$9†ú$ñ<“^]²bìr˜.Á\L¶Â1gIU!5|4°16¿ ºã¬`2FzA.!ÔH2§ìfœfÍŽW—‹Óv¤íúÏ' DÈÛCuHÑŽ : eC4ÃPŠYû20+EšÊƒK¤M$b?Œ”R¶ 4€b[,·º]#Ħ›7ÀÍuˆ6]ëqþ$—qÚ4£ª,!@BÖ;ÙÑQ:ò’éΈÁ$êS^ÉÚáX›ÿÙŒ`§çOǑݬ·Zž„›C ަå)°’bxÉø*ÒÃØÊ§f?CDyL)U¾+–7`¥_4Z–3ªµ_ Š^¼e/‘ˆz£t„ ¤p2µl¯Öê®=kˆŽ1¸â"IÚXBûÿ™GZxAõÝF¢§j<a.SºQ¨:V8e V¢y0/Ó¯ûB×…YÅcZÐ` ô’‰ôààÀˆ ”‡å ƒ”µäÒ€{âo…jFª!ç­iTã'cYãýP¬´Þůä­5ó’M®…Å©tIÄ-âGõ¾8…hEÁXk!&ô¥¢÷4k%óþÛìÍÈ m€Z=õ‘ÀÕ=Nk‹©‹H×ÿjL©6Ñ´äv»øúbnþÞ‰-pu‰œ}°‡mãÑSÅ’½çÁÆíuÁƒ;¡ÛlÅG=á®&»çoH@_”·öPí°Æ´8 ŽÞ¨” ¤*’Hݽ6®ü(;uxÖ;葦OÂØ=ò[59P<{ÊÚÓR(˜DBƧS¼Œ}ü&€k¥}Ov©?ÉÓv¥äêI°òƒð tª¸:DNìÓ6$YþƒC°ª=X¯rÙèe%³KæÈJ=±˜ü[Oï>º#äPn/o^Ro&gž¸SƒKÃÄŽ¯‡òS8{¨$ êè  îT[` \žC÷Hº$W'Û Òdentàt4ê !‡wl×!ŽPªR?I’HçŠ;lzÃ3aËlú°4yà^&Œzh#çdÕ~ÐNÛ©ÕŸ‘øW,aA–RÃù!CØ(Ö÷ÀQC¶êsõëS-u³oüý—X„"¨ Õjfÿ%÷sõHYüFÁPs§ÁQ)œy D…‚¦ÊøZ­’†¬Þ@ ”°V·€-^C<Þ’^šÙAŒö€nÌÇÚwˆù†.€=ñzµ©0?þ` €.†Þ±ûT­®úûöžÖD )ûNÇ6±¿ÿØL¹ ðcÇ´×hF\G×o~…ϳÂÅCƒß¿`"å]¿¤Ý×Jg …5SüËŸP#3€8\ž„ Ù¶ÆÄ0hüÈèQúQÿOãà-' [Âb“‡ïì]kDÉ`"½ (Ñ¡G@Â"dÃrÏ{«¯…lmûƆõhu‰l¦Ó ~ßÓŠõ_ýÃB:¡g€„_øÍgZƒŽ8dò6/°UÎ(M­²~±Ú5wÀ¥ê“'¤rÏEÎkÔÆ  üÙ-D±°ª€T„nU³ËÊX.à7E˜ A¾ß ›ƒ˜tÖ9n·"‰1WÓ&D–+Y=SX~aÖ<âLÇÚÇŠܰ“ÇÏô9û|à–ÿþIà—kˆ%PšØ¹^!\/8}£¶I‚Ø%&ÜP<°êØ’ƒ9tž¼±L¦nÀ«’¤¯—GÇVu)ðLò¾üu/“ŠÍŸ,Àô7J ¶š¥ûÇãßv•…ðb×›!’8`—¬# =åïŠ8øZÙÞ‹@é}X ã+¡àQ#ªÄ&Lƒ‚ø1‹fDÒ ¥°I›ÉEË6—A‰aýT-êùÜ”$§Kž©mìJ¬À„+±Å®F!Ø;´>DÞ a_lPvø%©v ò]ªÞdų[O?.\èm`ˆ5‰PÀ-Ê·Yeظ"ç²¨Ê (õGº>,~Ç΀žGåu cçï= ¡è-¡X"XÊŽçnÒ$yÃA¬º<ˆ9=G„v !®©(Çšj z +r›Ý©M…ÈÕfhü‰;‹$´± ‡ª$š¶‹©wLQD°©HÒxÛ6 mY#‡JIJ[WÇ)ƒê"Ä3€$1Û2šf×+îŸäû £ ªâSt‘P“HˆÖ:üD±‰ãý0\°$í¦û2ZÇ“[@5t›§—cžâsùƒÄ-id¬D†·Ï7ì¸y“˜c1ÁN1ê~ÖÁÔÿ 16àFŽ5*êA]Èß±”±“©,Òî{4ê‚ 9†4$Ò<Æ´(£w )´ Vǵ¢ÉafmLE]ÖôC_¼ÙU¸8² Ÿëæ{’ØyóüQtùŸU—~ |D,VÿGZÚÝÙOív}ñ8y9“tq0삳7 Ɔ€åN&éyÂ?ÌçŸ/À4œT«ë£ßXù„\Ÿ{ÜL€‰g<¬yEyV¬ Œu¸‘£¹ @ñaÙ0ô¥`2Œ­î&Î/˜Pß`±§3xàH@±¸m™¢°ãêlȃ\…U`'Ä&Ì‰Ô ˆˆ¼’TÔŸÃ0oDWgû&â­©ï~Y}óÀBoIk`G×?k'×2rý­WíÂ+NK }†¬˜ô_ecJÿØÑê8V®œðŒôðˆ< %T"ò$x3ø3¿O”Ö-@ G`$Cpž©Ý|ÈÄ›wwC/Œ¾`õ0 ‡'RÅx9$ÄCÊ\1y°Ñšc SÈ@uˆP*È-ïpÓÓ?ÙËùÐ&=ùMy¡_€q`1 ÊLÎ2h>/@Î7õß_ZÓIãQDÏ(¹°øwÞêSòõÊí°s:~fˆhP×Öðaq™¡ÅËž£d>gA€kD¤;{pð‚@)bVO'ƒ¾!ú~7ÆÝ2'cÄ‹âï¸ÝîïÆ>€­ÿÁZ»3ËÏeVÝ¿?þ.(ÿ2©;ýÊ­˜üó æn>øç H„ÎîX=·Mø—ÆxdS‡âŒcæ¾ +"¿‹îí_s/b«U:Ç×¾ýäçöÛñýq¬oô‡qó-Ò}o¯ß¨…Ò¨0죃{â<^ú[÷9#*EJHp¾4¤•Kß&rówwrû´Äi»oþ U *óaÍžÔú}úÇí3øŸòô'Oîû¿‚®_j5Ë]ïq¹»€;üµÍŸí?}ÞekýaZÿ¢ßÜWwíŠÖÔô9-Åz!Y¬b"ÚÔLJ¼V…_ø Pî(gDÓ”/ŠmR7C úô•÷;ˆûøc…1âD]Bl„4€kdƒ·D^Q.w2Ú·¦c!:¨Õv¥À˜¾ßji¬Y…TW,{êl µ&…\‡_Œ­s@WÑÜ‹Ü1bÆxÖ§6$ü½ÅfÞ¢˜Å&Ÿ'Oõ3ê44*È»ú´½©´Ö+ŠþU÷rûËþ™hZï~ÒÝú]¥çñŒ=/ÁÞTšÐÕ.î‘à\»ô ÏÍÅ_“^+ã®V_yy`+-j–qBñW­Üu8q] ¥ËŠíòóc_F!û·X¨¨©Ñêk[}EQw®Ý¾#Õî#I#.­VîøœoÉq8æ8©Öíô³á¿—&—7“?ÛnùˆùœDX‹Z‰åEKìwQIŠ|!÷±ß—Ó‰äœD—{þ"ä3®é"zši“‡Kw VÖ9Ymý\®äOª8ö,‚·‡Œ·W}l' 7Pæ‚‚@p„¶É³ãîu8[,!†}¤Pe.鯿ܕ*féñ‘AÆ%r&% ÊUȳÞ'{=zW_ÔaÝrÄ7!:¡é2"‹,,αïšÂ”@±l8µ<Q ^kÃ'Å•Ú á.Lu{I‰Hôñ_|© ÷œ¾TÐ]éÉî #rw’ÆG3P=px/tÔé@u°?A©y 2]ºT4By ˜“¹[âGøÐ+èB@îJ(ôP¼$”êyަ(˜Ÿ¹œ¶ÙNš—ÑŽ.É‹‹óɯ18PU¡:N…¹“ù5ªb®B´Éd ÈzÂõ#ASHÖîî©i &¦ @8ÀRòd«|®”E«aûC²øÄxù ûf¼Maô¢K7üˆB­ó…¬ŒI)ePéª.s˜Ù³=cü!PoZe0E=âÞNéëJ P4°='`ä­†n¸¤8¥ÆÜg\a"pbù½‰¡ù c2 š"HC™ Í| Ç…|&'ÞÔnÈXLOÇ1¿›“Eákl ¼,Æ|ÉØÕ#…ˆ‰v"¿ÀØÖ, æžNâ%õFO¡ÏúŸhÒ퇊ӛ©'&'·b~#ýCS™B‚g~F™ë°…‰¸ýŽMŽ4ä Ó%QUó‚µç·È.®¼f ߪȮ-G­gP1ðÞY;»B¬ ­µ%ˆ˜v êª¨õ;ñL¿Æð ¤—³`“ʦ£Ÿ¯U3ýëÅqRB-”h¦p<åö„ž˜¡¿#¶;zœê:ôº1Ø:ÀR9-ˆUŸæB˹xùþ3Q1Qr^:eá_+Uw^ |ÜLÚjX#¤"¥j m2»Øë£…YÛŒÞ} ]Ê&-q}õIã …V•ZÁhgŽ4€`‡a SÝOè½/¶È·Ð±Ñ4‚½ 3wCÐ¥Ðâ¡RiVçÁu!·«nt”æï5$48\éµ1ŸñV—KöD¢“DhøëÌÄ>£þ‚‘ñø0Qçà) *ñ H`u´æ£¡/šÕAÔ符Eãc?QŽ&: ôà§ÜisT÷øñ ˜ß:VWWÚÇDÃ(GÀ¹2ŠÜ¢&˜>òz?HÎ*E~#õ»ßIÆÆ€·i?Ùé$6`–QÂÊŸæ_~¯«®§êù ‚œNfKWHôñx@Øx¡ç|·É5›r[ò4† âöwá.§MÚgå.µOXÚ_oÏ>zf<©oÓ¶|BÝè5烯ø't¤é°xRó‡UÔDQbÕí­ÎO噬«å¤LK’Þ”äu£[/£ ΡˆÖ(Û\—DÂáº|;áHÊ÷›ªAO¥w-CË\e$©ACR (Îf¯ƒ›ü‰Ñ )¦ÖJ^¹æ‡æd"is*Ëãåæîl€Ùn‚^cO(­믅À#O“Ï‚7â(ÌÂNVðØ(ˆ`ê4ƒb ®3“ÙgßaIÊÉ AŠÊq‚ºYe׊‰¥^™Ï»ÿ|onBcëi-¼àÜQDÓÁás0ßGÀJÙÂ%Ú¿°€'x-ù‰râÄÖÚz-¤}2®¤ÇÙX‰€²#œ{~ÚAèíÿ“à=9`– UPz}Z O[\”ÈÛ(FØàú7¹ŠOÓü b1³pö½^êËÌ$Ìs} »Äl•@þ›C¶ßC쌩»ŸT WîTÈšù1q%¤á©ÈÕDpb#¦þ:ð†……:a7dâ} |h€ú¸nKo½˜4‰c&u¦³§ˆÃ©.RùSƒSCЖ/_Ÿy «êÿ!ƒƒâ=p7u3ùX†]9bÉ(ÝÛÐ|*]AU°“§¨m t¼ñÂ4›*Ó<€7[!: j+3güî÷ ᕜüwˆËѪFHŠÞìž>ÌQÛkI ÐÙ™I·"Î=àF.ä ëë 4NÜàra–2í²EY:ÙAµTÜ7ï4Þ¬®Z6*c¸ûõŸêÑa²ˆÅ 9 ˆ ·úOþ¨äÂ󦙜?ˆ)݈c&\}•†ET}o8Âg’*&„N©@eŠ%—¶F5=ê$8úI‡ì à5yK¾„nÜ;³­Í.—\ýÀ‡/""ùaÁ©ˆ 5Ož¾!í¬ ø¿~z(»“á&ý&¶†þ GÿBpÃâ$ØíŒAå\;z“Ÿ¯.æ6LÒ¨!@¬bûê‚þÈå N‚D©y•ɰ„£¡a¸hcN†â†Nì5H:ÏYïÇÀpqˆD” v¸YGBƒÞ⪩«r”[H}®ÚCËíAú½RƒsQiÛD%÷£Èüµ«=PÙ/Ä~ d7¢š#ŸMe¯)ñ,¸ý¯yª·jhßž7u3AÕ)ƒÿsy âßò: ­Gnà.[|4vZ ØI*rH1™Óo=17MO¨ŸE» ‚“¬‡®´iŒ52ôpxuHä±ktÛÞêY(þÌX‰[,ôbÄ×9.×–å –ÀÛLi%í¥,¯3B"ë±—+Î~OîO@ 󅈛?ƒÙ ú8•ÈR" ¤h:Ùÿz6R°Ì#ÔüwÝcgRíhd-@%J)Á^é²·¿<0òÚÄ¿|60ÿ⹑9 lÃûI·äºÊ|œÜ„ƒ½cÔÿÿ1§æŒÀEz¬Ԭ͂®™¼Ñ‘ºMŽqÓÐúmFÊ3h7«/š\^¯t:¨Â8äñ(¶#ÆØ~l†L¨ Qå™ûDfüG³µó8Æô’wWæ?iwò‘ÂÖÿ¥%± L{r(é:žæõè4¤Úþ"€8÷EUU°jê‰-“´0›ãÂuç¤;¥0=4ž²Ì>GSíô—æÈé Â(?’‹®]1ó€@¼Á£‹3+nÃèE~_#yôöÓ°r1”…™í#--¥Šö•(„Ãb­1ì㛼c£AW¦:JÆW¡ºZÊŠá›æ?¦¨}+­=C‰ê:ÏÌÊv™’sÔcÎlù|O9”ÂqŽ}Ž–4pžh4Ûbøqoïw° ¦(£ß&+VÃÿèŒáɃtëLHþÍ.6WþÌ”û`K²L_M\jn—äÀcÞÖøð¯Y)/Ë¡ð¬Æ ­åÙÙLV·*¡? –ð€ÞåÓOæˆ`z›äÑöûCT{K†I¤Û{¶Û6ÃÊ W£Nô71ØtM¬6´L™Ÿu•4þ)@2!#rtÒ(óùØÜ,]—º4Ó]U;˜Ð%+x(OARNcï –™nE™†ËÙ´1 ý`íh¥)RXX|#¦(qÑþZw  j?rŸM½цÙÎQLáË蔲ý”rOS‚#ÒÀcx8GøÉÁ#Q§Â@„½&6ÄBbçä3ôÆ=8D-•ÕżN òpRj¬[§ìŸäx¤°’öFãÔqß¡"ÒTUçúõsƒy¡ûÐ×m¨cAYo–t=7ƒ§Q %:Izõ$ðèvµöÕ¾%€B†ìbÓã· Ý‘˜ƒõ3jš\6¨ŸQ,-¢X¡0ˆ‹åôàL³ '`‡êë!Ð7B°+ÓRñ+¾†’1(ßsMg89vÐB&Á°£×PË!ó~N§Làø:o'ÉeÿXøPç,Z¸Ù"¸Éðu‹k¸ò>TÊ8†­¿¾ €årIñ–·!ÖÉ­­Ë܆ ’``E(šºä)æ²ÿ`Ù3î þÏ5X¬ï8Düb «}ÅÍ×Óu¿ˆQ£ÏTŠ^Å—¶ø_¨$go$[ëzêØèŠûчÒÍROu)#ôo¸0 eɈŒßÇßÿ‡—Àö÷Î £K*„þ´nañ~ɲ\„mþâQ‘îaùÝŠúÞÙ"þÀ³KËäòØ [j29 GFÝt‰…‹HÞÙ&´”­Œ5ûà|>7ŠÇGW ”ÒWý<ȽÇ(Ϙ_-RŒ?8ñýH}¾ Ï—÷ìô[HXDHj1áüCŽ‚EI[ÉË÷¿»àÃ|£!§Þ3¤È“Í´MóÇŃ׿°?˜‚¶M(¹)rd d§]ÍLPmjô%32ïÊ –‘m]΀¿ÎEm¥1Äv'þÝd Å¿¤ò¬Ì¸ têð;€Çe¯íÆwú·kǙދ“àV8£ûD"Yÿ(UÛdG¢»(Ð;—¼8ˆÉMZÁÎ0›þÀÑtÓ.ýX¦[– Gýð(Ù’>Ò)>Z%Ê‚ñ æld{(e<6¹¬²JŠOùk’˜ñ,hûcþ`ä, §‹ç…zÆN98YPÕ·0'¿×L¬Æ£çm.lÐÕÁ‚K51ÒL.¡Í G_7G°ÕozBTûãtð嫯:èÃnrm–95—í;AƒU(hÛ´ûx  ‚³!¹Ñ f9Od¨ï m ì’Å–þd÷¾:.Ê0R¦ú0ðùñ¯w™_ä§š†Àzè¡¡ó©Íþ‰å¾’ýðÿô«@¢hÑ– u7n˜†T¬òSëÎÞè.À°´E•7’·¶l±M˜só§4 ¢ÿB ÖÊñ;öø3§CqyüÃ)§5­áðÝå×l%7Ô|±Ç¢8Vø³ê xó¡Ð¿ µÛnÃq}!h¤`аQDŒüH~Úh3·ó‡Ÿç¶Ö3CEï°ØX"ÂÖbvš&ëv{pCKOÑßXâJÖ„x;•Esè ‡îöþÒ´f5Â]<³$t:©?äé=ÃyÚ(ðé­Æµ€tdbxhÆAáЭHÜ;@~³´ØÌŠ|IIAë"Ží—œà˜Û·/SkË@æ 2üã^x¶ŒèÖô^x¼K™“BÏ>ÿ’/mÿ¾›³"FáŠ?«È¢”f_Ýÿ]¡Cq¸„ Dé€IÞˆo/¼Ú¤ Z½¼¼‹v"°³o DöÁ7ZP‡î¼À¿§Â­•UÿÄF¬÷Þú´Â l_¯ãï¹ÿŸë`×sã\ß/äн_¯=z>þ޵Pcꓱ#ÿïŒoÒßì3ä+Cl©@5t˜èð-j³õkôµì•RÂðâY~ˆIÖ8s„ÛÌз÷‚»›ïÂÕK<Ú“Ü:¨ó«á„ÒÏD®Bik®fÄÛ ÛÇÔ¬åtÑ®DdÜ(Z݈5„¡v„u €" ÎÝTÚPU(L,Ô…öŒ„™Œ…¥úi!+ÆŒ Ūñ˜Jò'‡û²QnˆƒÉXÞ¤î˜1CËÍ‹0§oÁÖ/øLµ8GæÁ²0ù ÷Îk1†œŽRçÁ*wž ‹{Ôã‚ .8XªäêI”Å´ÖÉ$X"u`5ý5 8²&f98™11;Ð<ØÀL7Â`@ßÐXJåŽðéAº4JçÖkŸ,'ÊkÕ)*LÿBcº&ÁGŒÐ“Ï7ÜŒ¯#ä‘›%èPSƒ¼äsÒqß¼¥èö€‚$ 4¹üi8ô«zK¥ Ä>£†Ô¤;ïÍæï'ÎUÇÖoìþ™ˆª+Â\€øècäQûb#,Ë4{œx‡i´äûmô›Àæ˜ëùƒ|€j'Ãpf8 M¶ëÉéºOSÑ_˜ |Q ÄGžuš0·ÎÕÞ“÷˜‡’5zV±…ÒV 5N¦=pA»1ßn–:-pP2ƒ–ønâg‡Çg ,Á¥Äo”Âv1\ûË5„ÅKdVä•cÐ&°eòˆª¬þ‰Ñ!€€$$Ø#¯ïy0ŽØ|q¹WööªÛ é§“±x–µÃçSýûwâã‹z¡ †Aêíßá—ø±ñ\ Aë-rÃé'Šôæ‚+ºªŠÔ+ê^ÒOµM÷iÇ7E.¥\×Z•H“Þh/[HÙQ´ì¨9(ñ]5l´îþê\˜Lp祒„ŠBxÎ3^9¸^¼êjY˜®-Èño)Tª®_*$œ·£îâê½xÖ fŽ·Ü€[ô×~Kø‡¾¯«>ŽTG¾OãL1"²à@R2ÿù ¸žã5Ø/·ønéæj ¬™q¦¨T%¡–Y%ͪeÒ)ŸrÀ&3‚‡‹äP„¥¾•WÑ¿[Toñ]DG5IPk’ð¶2F:D¬/&55oÅ`ƒüû øg‚øU¸?Y#~•€&`'„TÀ¥kvƒ Œ*eð5òÇoAžšõäĞ)Öè˜s8m02T2!“Éù(×ýÉi:u]T}ðñéQT ‚…’WóBRhaY3€Ù‰ã¨¾n“ÁÕñAÛ•¾!¸Cþ«œÜÛŠÕ$€-dqÚ5C™xèè¹'¥_KmÅÏ£ìAYßë7 ŸêÔÍ„[ ¨~ß<¡~xÂÍAý@Ÿ:P÷Q`2këýÍL®8®¨) AK¤hL˜Â€å¬ ¦ÊÊú ³ÎƒÔ‹âüfp„»ž zH'ê—1ðLZÚëD†ú˜|%&Ì'¦!!”Ê@ ®|Âø  n€¹I¨5€•q¿Ï%©ú î€¾4îîääãܬyFl_äXŠÜ Oþ˜ªîÊþ¬!K½ó䌳gB|Qà…]ù Æ¶Xµ5O=â÷ÀYvÂÑ‚%ØApl-ÎXß+|H ø.6 ‡9E‘ŒVëÜsj/Š¿Xµ„îKˉ*‹ý1‡‰ BJAÜ#GNós¹N7»Ec­,6 8&|áÑ-ÕtÃӭ冘ÚA®à°iŽš2#V!ÇRmà*Ð^˜ùO$Š-10° Òï“›_S%Dvø‡G-¨g A–Tºªô.!pß„¾µBÊ™¼88ÁK@z«#qáüš°fÓT‚]†õÔöâ*Fæ&o”ýl7œ'¶¾œRªnÔP}ß:mdfbvDw*/˜¦¬‰øÀ}’Ÿ¶å~¥51þæIž‹C³¡á·™úgð’ägk’†Ï8 ,…œ…á_ 0þ‹9k/ƶÎKF×mð› ;×S }pëo"žØaqƼ}©!·³yÜ>Ÿµc K'xfÌE¾ø=‹DÞƒ¤Ž»ÿÙ4]•Ó­gïŸZÉ›¡L mU+ð¡[ßV§8Ÿ¿R&ÃéúË/¤N¨M<טõiù&óV·¦SуuŒ T¦=ƒB..âHÅ™U WôÞ:hqYl í£›˜¬ýwa˜»Í,Ù‹—+ˆý íÁÔ´q‚ÿ׌‡“UCe „L’$2v×nOJ|Àu%ƒS a®’V¿,ùFõß <>,nƒ, ¢®ôSƒÕ’ðà¤ÂQ°„L›†?9Fpq¤»¯€% ÎæFOcoVÓ~ky}øö&4²0€ø™¥?ŒÇTŃSÀÂ÷$\K¿bž®‚'Ê€¢Pìì*Ôq‘чÓx"Cx¬®cö5HüŽ_[X ,Ö Ë™?aG˜Eˆ»* ìõ²äWƒwG™ôŠ^@,‰tþhs_ÎdȰY‘Ät/˜žäl°Ì4´ðü µÓœÃÜZ¤,xâð\¸ƒZ=ï âh@•@æ,ó—•a3º“·Ç"³*b`M’¥M¿vn Ä· ãÊ"1 pÜ E9ëÂ6OC˜¥ ´ŒkåB–‚àà{””„5´‡ÁÈ'³œª$­ ƒ¢oKÂôRWÍË?&ãäŒ@`g¡/¬#Y4`øI+;cvÌ–~qB)¨«Y³qÚÆmOvøDàZE²f§ç¿*T“¨CÔ±¬ÒºbGHcî_°ûË4ÚÀŽñWAhØ©Äÿò‘;J"Æ$>\ר)Jñ$`꩜ÿ’¥¶f•2ÓìP·­ƒ’×AJÁЉãäÕÆðß2Ü0+üV5€£E5ê³ÿ8ZñGä}aT¾Ö±]Te4Il¬´®õ›MÙµ÷ fBNé̠͌@Ì!g­£A EÄ¥1AmÑ)ð/I 5£¯Ý=D}U^£‰“ÖúM)¢C{$0Ù>تèwꎜ|aýN²€Óöòe’ ¦¦IªCfB| Gœ1>5š}x ²íå°}ÔÎ\qÒèDTG.Z£2äùÎÿî%Ð×{ f{‰Ô§Ýg$k<­ïëЀaì3cF–ó!ü¿  ­c<%„d‘Ž߬—˳f¾k™rV¨4h ‚(UR¢ˆ¼N(VB@‘J›-z²=*ø&Wi >|̤}ß¡ ™Ø‡O³º?úæfä)oŠà[WÍ6½qSU'¿éê¸XkßO»ËC™>hyõÿø¸÷È€ö:‡¹:¦ó#ƒe­†k1c¶ Å»Èç%ÌvA¥èþ”À˜E–îÿÌLÑóoi-,ÂÕdpx:­Í7[Ž‹wqΰÜ5¹I`(‰™Ý´*¡m:}¨H+æ[[NË?¼¦!óÒ³Ì#M‚mNál¹i¥ `ÞÚfÅ[·_ãÔ“`ÌíãÄÄk_"C“árW¾e‡ðVè®/hw‡(Ç=_Åš+ùƒD‚^ pÌÆSá…ÍÌöŸˆžÈX19ÈúJR®>¤*eœd\¸G—ºm°\€ônW«œb†¥,­ÞL^ìÚ*ø*Ä„=ãì+-+cGF£Ø€ÖÖš0 ’áX$ÑÈ0éÍñ™ê‚f»"±Ívu(*ÂþIv½ð"ñéŽÏWïûCîá…›+ ó[Š"Ñ=°á·™­µ±&Å0%Úüð¨o`ƒ}ç”y%ƒøE{†Ã¾`—£# ‚{Ý{E‚¹^J¢ü‰»101Þ ð^Ñ ñ¾ˆéŠ™ž5Œ{Œ*Ö$Óøàƒ $[×’ýôùˆRÎíC¶ðÚ̃Œ ñà²Öâûá:©l5d|:MÙ¶nŠú‰3ÙÞdÁü‡Ý…pCpM>üYø0Ž&bÆ·ù?õŒ¶$âcЊ¡VÿF ¿89Zr¡¬°¿eºÓ.õxƒíú!`ìË[”×HlˆQÜíi7ö›…:Ø£HÞ/ÊÞÌßÀ!„ ¬;}@‰@lÖÊØ¹þÁr¿þ 9´Ý±°1ƒ²˜j¨ÝíÃx> nRÓ]˜š…y~=ç!É“rÞ÷•—Ž—ªD´Ã|S‚j~ƒ‘:su›•·Û—i᡺?ByúÆØ2Ò²ø´µ–g¦¬p'˜ ï`†Ü?ŽAŒ¸”"Ö,d-nDâ ÙOrZ×"”ïíøÈݤræðǃ3þ²y‚ú²1ËäjšŸlÊfr-­pžS+9x¨¾ˆhB®5>ÔiÈP—úg Vc¡ðj¥oIr –‰×‰½N`zÊçäÏ]“FB\òK'ðÑÂ¥™P‰“--Øðæ 3GàEj½ñ`ňΤJÂ_Ë>ÂC\6ßþ™µB+õ€~ˆ-Bºévò7ð۪ຠñ¶É}Fé p-ï‰ó'ñ~|O³fÆì™½Ü)§‡,ÿèæ´Á(M0í©6b·MTlOr`ã·Ë¡³¤Ý¦YL@ª™ŠØ1,Ë]ZÑRèi?hà´ ‡e8–¶ÄåΨúÁ'¡JÓ'EA\Rÿ°K-¡šÜßm%KÁ}¢ø—<™‚Þ±òš|X=ßEë”cOð˜[À–àʼ…hÓ§^Œ‡¦wV#Œ#fU/SðCêC&°‡ø+½hãþ=°Ý óîp•w0?<Œê!ù—kœÎº–œºàh9ú ±#2iETZò?Z ð'=4d³Ñˆäà'Þê hëcÊs:e!kÅ·ÏËlí'Ñò?4T2¡—šV‘¼þÙt¹s1ĸõv12Ë«•@À÷.NtQ»å;áHº±à Ÿ«FVÓ«ß#ÿëx±KÕļ Õ*Iò^ŽÛ©¸cüÂðD„T2¦ö×-ZáÕ/ Esi¥SôeT©¿¤-m￈¨Ø›ŒÔûiÁŠì =ý5i'ÂBÇAÏ%„Ÿè;„6d«ÎF©Q„½±Q^©æ"ä7x™¿˜°Jè(`{æE çŒ,7 _„tÖµyúkDl¸!ƒ|»/‡²ˆæ’€ìuš3KàT ¢ E²sðeLòðÌúÊb %×—ˆm×e8ñ’Z¨ q•¼º8ƒZ×iz'*B è ‚“4uÈc3¶gv´°fæ¿(C ø£!”©yæiÍNÙ£‚ùæÆÌ\ï%Úˆ‘8m³ýVÿвJÄqÒâ!:›0 Wâ Ë¬Í?£Éˆ¶‘)9;fî ‚ò o ¯2(k¬Íñà%œWƒ™Î¯k¨nE:™L~½†=¬GŽúÕ–ºpòÛOýÀª®¨œÛ«Æ• >¸paV?À깪¨ƒ11º¼€,DÝ,P¯ëÿX‘À dl–üºPþ… +_Om R $÷ÔáBÑFÓ#eú+^7CÏyWˆ™ìIJÂ[ž¸œ2íð¾Ó³Ä7‰¸¡F XLþŽ5?Øc5ù~lñ´ WýLßBPÀ”ƒæ éÀÖ>즢ѹÓ jyfµ´€=Cåm"qqþWÄÛ36oqøÆtqP6DÄæ²Qé% h;ç'_s§Íí]†2˜-p­¡;+CÙb8ø 覰`‹ Øé𻿶ãôÏjËO»Së뜗ð@ £" b)˜îÄg…Gvbäû`·Tt‘¿/è%OsR£ $oÒ‘–:pIjº´šÏ¸äòu·n×D6…oc´¾l'ãûù8ÎDE.…“L‹*@pÑÁ¨¨vmŸaTä È€°F? `«`ú­6èÿSdà²UÚöˆkŒ Â`¤¶±®h«›ŒÏ=:4‹NçâpêéÞ›Á^ƒÃÐÁÐMl`â”튑>&!ô  ¡2Aæ^Œ¥QbR0CVHy ™¨2ñqÐÒNó,"ð“ô^–€Çˆtƒ}ÎК€ÑFÕQšg¥´D¯½Ír#™­8ee^Ë]äÍ9 iüØ˜ÙØ«¦ û®œö>¸ ÌVAYªhã> ‘1,K?´à5€û_u¨Ç.'0éáSS㜺n¥CXˆÄ/bº öåØBpå >‰F Eil_¦ªØVú¦"¦†'^ð¿’T¬UåÑ„| 6ÊEH ÞW2À¸Ùq’Úä° `Á«#a k@OŸ¥æ˜m»S"Xk@šÊ<ѾåjAä-–MáÁ¬n‡F ¶¹Z_¨_Ã’]ïŒóÖÂn7a»¶oE~`½+yÚYá/½ùÓÆ$»¢HÄKLxÜÅ ÒÒ1²­UÂ1…Æ’HÝ\ü!zBN¾¸¯•¤"­~Ë?‚:½+¨Hçñ–‘ë 霓99že½Õt Swø”]£`­w#If8ç‡öà+He{ÄòÕYmúÑõ$oû@Àq'âk:Ü$ûqåÍï¢@=’‡§•1˜K½—ýäuF›Qê‚ϛlj놺”+‘шifB0ã`­%¾f7ü3„VÙÚ-œÆ!oŸ¬C<Æó;ÃHöÁ{°4’ã4‘J›¤…isó9Q̳jÄY“Ái·øEU¶€èº'¨¼§Ó¦ƒJRÿ*K NÁŒ{J×´ÉáÍýüæJ„ª&Cü“"ô"%ÆÐðÑh~‘Åk]GGì[«&oˆ°œƒé]Â_A†À š-uê?ñÇÄß7m™!ò3ˆè*B„Ü©‚ÁZ‡÷ÈäAâ¿*|#léÚÔß2è;7=r2#q»ó÷Ï.{H„þ³ , ÈJÎD¦÷6ðj%ÍÐÇçgÞJ?yUnØÓ-¯M ™Æ§‘éí¥p•¨ÌTdi>#@NªLðìžÃ×ö…kãikzã±ätd´\#N_Ø_düÖìº#¦D5Km|óÀPÞ›&B\÷—ÖOòÈÀá“ šcvÎzyXWuèÊ4¿ñútnTÞ.mi¦¨"™tX@‰`jÒO0áHâî¡c581âàC_iÀV¼ý» įvÛo ïè¦/Xû±‹­Òÿ;HȪ‚€uˆãÃÅF÷ðdã”M0¢t0'ÇŸXÆg&ŸéSòü\`Ô¶ªO!ÏŒÛDñg«X4yXƒ†úÌúé¸É«ƒ³0+Eà ’dá 'ôaš” {9^:zˆe‰·]yÚÿŒo Dg¥èD_‰dOšŒ;d.b€å–Á¦rK 2-HÛ}’õbzÐýéì—žcK k#Ä”qæä£7hÊ­]ƒ¦Ú­y<µß†¡›‚“ûYàÐ"*Ÿÿö·sH¿ú•Vßsiù½c•€Å…RÃ;瀗±µ'Ø Íèêg`Ñ%­¼{bcåÄÀ¡C–£Ó9›ÉüŸP7µ9þ‹‘ž.GIþKœsÉ–ÒDÖJ[—ÀÈÙ ¨Gh H+<¹Å/\Aè4„Ú_|T鲃 ­}kŽÙ€*„ü-2â{|žK$Pû üÓçk¿€qöå`S‹ŠÀø1”ßͤ³|'®T3aý ]N2Ïrâj|mò½ŸbÚkóäÒ¢êØm!ºŸàQŠÀ¯MÚ-=cÇ,µ~M¢f9rû¤kþ}€Öy Áñ®øÁYÊ‹ôdAÄAU\_âb›Ðd…ô|Ú‚Jê¿p#_¡;âŒûòüh§Û¬j“Qðù8ĉg ‡¯š®Ã^é¾Àf91Õæ@d[eRý¸€ëS*lIh%à 2Ù¼º3¾Ò“F˜2•i°´#÷ÔíS‚©·)ë)çjÁbb"2ÝPên… Ïb¤Rf¢¼Å]ýøìl»+OÔ1²+aÄeu${눙aVŠQÉâ%³†1È‹iÏ´ôKsã@œŽU {·èQ™¬}°°T¡S Ù 4åb‰“ ú£¥䟓N©>XU'm°%û†OɰvVVÐü]’zÛL.Üu¼  âÑöjÐàhK~"8LÖ;²Ðéc•r×@6¯ÔD`×hÞ¾v±À©ÄœIAš¤gÌ·Ê ãX˜IØó Á½úOòIVG>|èYI<ùF«msá+,©Ïcà®N¯CÜŽˆô¤AÙã¤Ð6'À!¥œ¹ÍEÁ¾`ûf¿ ¬ÖçÍŒEÿ¾-”ßâ˜9ïÁ ,Uk< &¯Êç)ެ¸ËñúLl7râˆñ0ö<°®À¶”x²7¤0³ôåGaÑôZ€Yìñ"ticäÚ¹kÌW£¥Ä}ªW4ûãèŒH»}èKÐè’ð.»Ê´E´¯V¸‚ƒ™DZz;Èi c¥Áézâ(4D}…¸ l]7º÷aŒ±VÈònMaP8ضmn ·ÂjªkAIVŒ ¥å‡ `¹Z 05„¿™Ø¿€×Ým€x´½_ìw¥1Ê· ÿ9¹z&;)üÖ:!¨€X›âÈEdî¶µ#"ÊÏZÊXƒ36΢1ÌÍd­ãÄ-ú-÷P]—U óˆ!“õÔJÞ@ j•‹Ù´%óÑû!…ëª\¼J1ŒXþKá œ]äë+ÝÌåÀ¡\ >+hz»‹À±c;±4í?=­øYͶ w }+¨‘ó;3¾Ä@®z/SçízA¨x÷¨:Ê›¸”Íÿ÷&?å „8%ÃÌg%¶[B–‡]ÊC¿ŠÎP[_  ŒÞSÈ0œ¬ñX `’ð¾c÷kŽÂÕOnÄ™¹-~#\‚86€’Ñ›44sŸ¥ùu?ãØ|’Ä×_O€,<[jÏH`y Öcq²ÏTY5…¼Eœ¯Q¥ {r$!:m?Âlx;•XŠóx¬—«cˆ [!XWŰJH›'‹ÇP65ŸiÀÀTÈ,È‚äò͈³|‡àé/HßÓ=⊯}ÜqòmèÌ F{’L!@—@ßÊÑ.7Òþ¿ú/»¿®¾ª_šÎC¡ÑùûJÍEŠ$Júš®/ñŒ!Ë‘ZQ<Š;j†åFò%þ‚TärÉ"¾ŸÔ ³¡,ÊÔŸ#=¼‚‹ø±{IËÂ{÷vª`LfJ âåPùÄü–*Cœ¡Öà,Û!ĸ+×pq·ƒ²"ªï¼‡'8 ‰.ˆþkÿÖÊMTáàµúá4 f¤(;¬œ †_Ú•yÆ_ŸM¶çdEjŽB½˜oØU^ôQnS,5Eä˜ *ën0c÷ꨚõ'™ÓDÎ/Ë<*¥OñzJ1´d æ3!øsÿ›ÍR†J„ÑGWõ«îøÀ[Ί¾'ØœŠïµŽ®“ã.ñ»"M³ƒÞä¯ +}¡Ü‘Šk¶ªOSÎcJ¶ÛøU°½ü›Ñ~ÿÿä2HŽ€îæ4ÞNN³½T]J_¹¸TE3A¸Œ»ÿ»|±‚.¨hj¶ZýW¸%ÈDY•ÍŠœÝìéúÑÓç2 D©{³Å/6.^¿‹s3U[à÷‚ÒºC88yn©¹ûmçUªÖ¨»…Ww™z™›ŠšªÿT™Š÷¾+OV>ݽ§–1]m=ÞÕÝ ”šÂ]¬‹{¬Ò„uËWg÷ß\O2nìZÞñ¹Òm—£åQËŽØÇ\Ó[KõZøæ" ±©¬q‹ÇuräúH²¹ “bŠQ&CY,œPšÇÛcÚN5·•b ÚmÓ˜Ñù’µñ†[åX.í©r8Cì{/¼GAÜ GÓ+¤iÓa&GïwÄ÷%O3‘19Â?êšA΄=8È&ÁZ› ƒÊ¼q(e¤Â°šêr<ÀX${v´¢‹ÉA¹g†ò âÔ…º®a;ÏûZ?s¾DWäÒ²§G –,gœÃdtÀƃvß Ú |œÂ0àÜü¯Ë‰»a‡[/Åv´gc¢é=:™nIÙÜ[…­ÌtɜΜ:­Š`d%J`{§s—{Çÿ7öä´n 3©œ¾JGæ’P1§Ê¹ËáŽHîæ–g…ˇ¢YïÎ4rÒîp½§t_Òhª°s 4‡³Þg}ÿzŽ*ïêËôØ»@µ8Š i†§/®buJJÝ0vb”pÈJ4Þ ¦ªŽäwxñ¹¬˜÷ü ¦°ï `6kpíq—I4DT_$Z×˱A¿ˆB¿zB&J|='²ñ0íÙ'Ðtm¡ÙðC–¼N`×&¨285½Õ\þ%+f^"ýŠ5•ýEë:¥DÒÞ¨»R™~#c€©fÊZØä -&Ÿ›»c"\„s”nä`ó¹T|Øw°ÄTît•_im ¾ˆéÈ‹Éþ£‰Õ¹êUøü„¶¹~ßž ×ñQ¶`‹ð8®cC¸Š0õô7ž|C¦?y……ÖÞìa—òí¡¥ oí¯ˆësg´SÅcWV…牧â]ð_b ˆÂüLëM ¥8¹Òï-Íü:ÑL˜•€›÷1VÛqgêîêÀ“Þü?[·x¡W%ôB· }3Õ?Å×m½²ó©T|l„þw¸òBÁÆ~a®Ùván:b”?LÁâAJ*QÔ…Ùà,pü,Do@ÁÃï4a œú¸øãLBF¸Êr(|-Ãúà»?ìÐ‹ÕŒÄ´ÜØšËÕî²míÁÓƒËx‰3u|ŠÑæ"úm–E ô{òÄ>ËÃsß~Ì@kþ»€+ß¶Yo/½x©2üx@’")pVxSÃ2¸§Î¶`ì%~ùú[‘°½•RåjLyyì>©ìý‚L_½$ÆH°Þ5 <$ZÀy–£Q`ÖD'dúÌ¥<´ï^>+\Â5†,‚J¬}5±L Œq°–#ïä;§Æ ç ðr ¬ç–Ní®”g«ëË´¹’†è¼Ý¦–MŸŠD2búxÍ”"ÉäÉK+¢™K~dõxys’óÎ&‰¬oF°\Ú.N’Ò ù© oÃm+?Ý@édÖie ß «ßX,ŠÀËRQ)‹£"·„—1Òµ0¥·˜ëÓ3£B‡¦R_amÄ"Ý­iM™¾Ü-„>v ²ßr#pC¥Ãhõ &+¸Èû‚ÇR?¨>ªpÖMÐ5Ã;±ÿ_m™ ©nèq?@¡i0úgÓÜEƒÙ¨‚Å¡|RAÉärËí"ø,Wcó:ǰ°Oó,{"|³10éõËÞ-Ýt-õ‡»½¶zPÞ+Zc²;Ûï^ÇÒ§y¼óî©ÊçÃÖÉg"Òž»ƒ?í#ò.1‡¼÷àÄNJÝ`FÈzÜtˆ(ä1T™4¤Dî1I†±¢õÔ.•E-=,u¦¹’Î1ò’x× î-Ùºõ R°çU®Å”òÿŸÑBݽyVàüÀÇŠóØÑ”©4–©iÔŸ¯øRÌÛ³1Ü×VáÕ,öNÒ‹Äçc]Uaäü"å$$"(Xê<ý•céAÒÂ=õ:žx^CJfb œä}ú[4,ù{¨ p`Út6³îþȾ¸%£müÓKOš7#Úc—©¨îéãÑå²}ËÞ6´a¤>3o ‰;„@n»?qÿ‘§žÿ÷f&Zãi)ß®#{KõƒÕ0Ý1í¡z,L &8lC¤³ÐÀãX4ßÜ œ¾ë²›JZŒvEŠ`uÞ·Ô¨ìž9›a'íUæÉ¾Óo‚—T\~^¢"ÅØ+b(gYÄSÉ\Æž,¶ ÆwÑû`vCq^èð²d[ű2Ø­3ì U#¦ U}fë¬Õ*°^z€ íqL„¹oÁPQ0iSw´¿W‰–H|Áƒ0±°ÄÈ]Ý5FÈP¥Öš°[$ûþDú4¢óEgŠIOjš´¡D‘0vÚ{…;é­×!K+ñ=óõb•þ.Wÿ„‘àHôØq¡AD#ws´BD¯Ä¶–ѧÆÄàV®UHI[Ê ]‘(ë½í—‘+üŒ?ÜRºTð¥÷w®èÈ£a!òÑ¡£ñ0‹úò /H8fôVŸ Ðc¦Û0ש„„‹Å_ä B|Ñ÷hG_â]eËx8·ŠR`r+J(=`ËíˆO˜noøœWEK}šLÓ¡ª‡jK [`ƒô=g®Àû¤P+¢/ùÕ$ËÚ¼_'µûUP+1›£Ã‹zÅtsº.p‘2,Ф‰}CÝ Já¥÷4¥ÚÏ^ » €_æãVWèt0Nf?’‰œb¤Ô·qÆp°xÎ–Žæ±‰ù9ÐÒôjÝô»¸ré>UJ™&¶ønœIÁF£vÀô‹òtÕ"_î„fš¼œ5L<5‘õi©Ú¦îm/b Ë”r`¢ö°þ™½N|ŒÕJSåÖTt“(åÔ칎ÜU±ù½ý3¨¯æ¾g9²|%¯/ÄÞÉ·Ü/mòJmYÂ¥ˆº]ïY6B|͉j ÅÁ eS/\,¸ËÒQóö'NæKh)1Ø ‹çØ%o¬gýRކ(…Ì̇¨nå9=˼þs”@g”Tùøw–’v²{ ˾ޑƩ†TÛ^ÒòñL3\$O—%R~¤Ý·ñ¾6±^‰ñÈå2Ž$E^ fƒBÿ 6`ôK\Î"›hiÚ·µògz¼ BG¼Ç¶z……èXO$hx;Îü h©2Ù78о Ã#01Gdê’ŒªÑöµnÁ䑘›c‹·Xv²k]3Øé±ª´+S{Æ9ܨ& É=*רa×~fr8jU45¶G ÆŸ[W"K·×Ú z`›-o×!ÿ„LÏç\æ ­Ý·5ˆÂ³%÷Ø–6­È µÑ¼ðK°iDÙrLD¿%í P¬Ì†.\÷Ó‡¬ÉÙþR !g3*Ú 9‘n_©†ì ÁØî #F 3ê„Gמ³.¶]ÿPßžƒHJpmÇ´fY½·K­U¶/8wfØe@ÛOÆ¢D‰ˆðúB¶â± èš¿ù†$dî‡*wyƒö\¦€Y+gÿ椂žäS,•[Þþ ZÙU/F<¥ `6íØ‘»t§¨[™é‚h¹s»Rr<ô°3®çñ[´L¹H“Ã;þ^¦W)$‹Õ-Ópˆ6S”IúJ¹´“|1)äÍÝ©Lâõi ¹0£WÈ´ ù6e&Ÿ£bhà·¯ÚJcæÀ‡Ó·/ ¸½W†á˜Iz†ó$¢Ë‡Ù{ÂÝQ'Œ°Fü‚£\d!ÁüºMw)ª“bêMšŸ¿O¿Šþxê µôsßâ1SG @€;$Ø14PŒGX«´Î?à=òB–EÂ%BjAÉ‹ŽCQìª ~³ ‡¥âb–4h_¿©=~¢u‡‡G\€båk"rzÖS7„¢}×F4Ê(±.˜€b°0LopÞKÞÿ~;ÁŸ‡`L­Dõ'ÔˆäE?׉ޣKƒµqà¹nNGa£([¶Rçð(Pgæx5 HzdáùõQÁ!Ù×W qQ „P²GàÆ8™ËI¦ŠcšãeËuŒE ßä• Ëœex>é×i\+f3çøÛðRekRV2=ÄTë—Æü84EÚÌÓ$FäÎÍyö'rQ‡`zË SóXuÀXV²|#?¤˜a«¯?ƒ†¥rÚDwÆßøZÆR gÞ˜°ܳÉLÀ~m‰…=œ¹\³ãBðÖ„z¥©±YÄ^O_‡†i`C•ê͇ºFÚâ³Í×Rº;.lËäQ¿PÇ Ð`5ëÈ-$¼’ “¡´MsVÜ~“È'v©lIö$ùLk>¼‹¿Þl Ñî`) ¤n<"Æ÷“=µ — `Pvl‹ÂsS}6P„º–žÐ„ßüÇŠIi¥àmÆ› NY{ÕkZÍ™óA24$®Ä{Œ)àWßBµÃ!ñ:¿JMlÞÁ|¬IÌ3S¦Ç1‰œŠ=ÛH¿ã. «£Ù+ƒåh>tÐ7,¹ÝÝú¿¤ÐˆLI¢ê|Aõ Û£ß~΃Y¨ýq¾k „8¾Ûà$C´Ù—*þP©šøÇÃ~DùáÃ\ô;L>Ð5‹cÒôQD†­ÐGdW…±ˆoÕ‡1þÍ€2³­­p—ïÇ9”:•Ç¥ m+Æ4“8G g)*t⨨¯[ýœ0{ä_ÓXJ¬hOSO –’­ïyG‡¬#Éà`Ê, þÔ¨b_ªÒæÿš%“ý^‘>À¢vso vêc[WE€á Û¢ƒ†èHú„­*¼Î£0òÁ®™þ "Ý•€N›ƒŠÎ~¹€F:7X¥ð¼ø÷H;:AMÏöÀÝ\8›“¹"ׯ›Qó6æAH̓>XlòyÂwÐ]ÐqˆÙ_âXâ˜oÒèòã^ÍÐvßTf|øÆË²VÔÏ0²÷Îýæ—˜|ØÐJ¨€%w'á†Ú9k úyF’Ò|Õ#İ2x’gG'=·QÂà{IóÚÆY ªó`Æ ‡´¢ÛI“0áêãgåiÿ^søÆŸ‰šç¹ÿ¹È¬AY?¦yd5÷–Û1ÓÄÄ«M¾ŽbÖa>B2O²‡WÕLì‰*Þ¼œ;ûN9Ø~öƒ¨LöC§-alX§¿Ú*Œâuð…¼¶yŽŒ§?ÃK.öö0cÛ&«šb¾Ésã™0K="“é^`iÿ¹?a8IGUì ééþ½®5=2{ˆëVå=…¶'Á Å7¼²·õ§ld‰O:t¡*@‚`Çýå}Zi‘õéâC"Й?-ÏÞÊÕneW÷uB¡V„mÓæU†õei¢ÀÝEÛÅ7HÇ4.&Ö ÕÜ×7‚+ä™8¹|h”gÿz,dÑëW—Ó&£õWñ±znû'N:ª/š´bz‘/r68£»•†=eq¿Ü+­šçiÀ€ âÇÎÖ:N¸qâÔvudUHÒþ•ïW•(…æ8†)*º Ú³¡ÌJí8þñé^ƒ#…eñàîý€£EÕ mº¯ÒVG¿ÀÙü;A¾ô騔h»ÅrÁNNr|í Õ"¡±îœÔ,‘ëúHñë}`6±ò[É¡‰òܾ ³;zã¾s‡¸s°dBÃLýv×LäíRÿ,ƒ¸k 2:IÛhÍï—ЫÆÃKàÔzfZzÙ ï²ï5^OÀ$zëÈÕAp.c¬±£WˆŒ ôr°8_2k?¶ó»^løÇ+×ÜŽ³È³~+ûÆL!—c0[sYýEHƒYGãÌÉÙ?[ñý[í4Ø*«~¬Y %¬l!ëd›c¶X ÛÆm…]IІS[f ßkuæJB”…ØÀ% Ûvá½R&ùÔÃÕ¾…Ý8¶žS¸^ݧÃ.ŠY¹:íæ-mÀ K'ËXw2ð'½±ðL&×wO8rh‡‹ ò[âÄÀë<8´$›ÇˆÕ<ˆz‘Ç@)ÇÄËvÞyô9'€'Òé*™!±:ïùðYQ˜€,lK”æÊóÿÊFtŒA¶EžÅ»L!3ãÖŸ‚Ö,þñĵ‘ À€ã¢.9èÒ”;†éÇ͵{©È‘S±…ÃøÂLÚZ톨‚c÷AD¿Ÿ¿l£ä³öµÈ˜š¹1Bo11©Fwð§· «ýú¬L#üöcë‘2µíÑ*›Îÿ w»JÝÏ @L?†Bƒ½§7·ˆ›ó² °¬×ã qé+L”À9€ÖuŽÀE`úØ‹ÌÕÆ—â8>k–ùcvÞR‘%\°DܶJ0fN˜]oC*<Š"kÏ$ œ *æC³ÿJ(ã{²8Óà­‡LúÕõã"56J@#N}x†¢6lÍ‘ðK=l’]{í¥ScÖ=¥!zª† dƒ×ÄZ=ùÑÍG æÇwì/ÛîÊ|NâäSÿ[üȹ þ6,Æ¡Žýχóßw?ÉDÀ“8">’9IªÐwªù$óp‹ûœ«ð4½eéLè Æ¿ö§HÕí÷hâ-m?U°Ýù.¶å Ú¹ üe¸CÛÏÆ@€íÃÐm[Mr±˜Y®ß¬dÍãME‡ ´æøˆ€Ð¯»®¤R’ŽÓ$·éœp!ú),0`ÅñyI’cF.ðx&1 )7€Ç”¤YÝv`F´9…)ŸŽÀH:ð̰].ƒ¢Ð˜j\™ œ:)ãïFÞ ù'J©…ìâBMý%výüˆOX¼IY2µƒ‹§KX ™‚Ï@p^¢qïe"7g âÜ¥ÔÔRÐþÅôÁ¬ò~@ˆÙ gõ„@2 0Áé_ I¦è`¯ˆ P>5¦Ûž&è»’*ñ§sâ—ÍOþ#3!‚-ßÇ6Oᶸ=áÎTË©¼ÌË=¶'­s‚›º_H1ðO³Bà=evàzëÅ·~V×ÊKéˆ{¨+GäÌ…±Œä !iiôù«Ëà´B}ö*ô$m2]h2Yæ’ò;auHuÂÄ`¥(DºªSÙËTv½Â̳Eß<&}bãÀO*Š'ƒ0(×yƒ1¶Ò“|v ̽=zÖ!9ãÚÝË#´SY8ÖcÿÇ9O`¿2 ǪÂõ…³Tœ©»˜â«”n}3,{¹…ÏîJ¾´U´±|Þ+òêX–ƒ½Û„w&„Aù„ìÄ"³5LÙÖhÜ:¨÷6†îp†åCŒÝcN× rØæ»¹?QïU„*R—¿Þ·Ð0ÌeVü¬ò°Up ušùl"¢PÓX^‘¹b·/ý^ô|ed˜K´›ò,À…u'™'eûM‘òûËăpÓ,H*àU¹©òÝÄ¡°f©—^Fp™>˼8â<õ¿dØ`·û_¿º½WTsC¥'Äï÷Ä8TËùwË„ŠOÅÞîüCòãçÛ@wè°û¶žñÁÇ!êlÍ‚›æÊb˜!üãA$¯y»·M¿¾vÈüÿ-u÷.o~NˆØiñ1©_?þƒýbT.Qö#_½îÿ€˜øŽ‚V“®º¬OÏE[K[ZØó?K7­où¥†qñbi‚ŒWÉ kb¾Tj©‘èJXŠð«#…V¤ñMpÀºÍº "’ƒ_‚Ï ]—Y@‡ÓF¨* ²˜(ˆMöáúo7—ÕEIÞéæ‘ëþ=Âk0)ˆÉ•ø(t™ŸÉ Ht™W`e1½% ·m Ø΀s³ƒ¯™€ xÁ2CŸqƃæƒ03H;ë`&ü¿>â,Ó'çx8¸[¡¥T”LÄ4Ü‚PŽxD„F D4ÂG¡p4`H_¬°> 7Íýð„0ì ¬q앚F˜Áƒ+<bÀ¡j&ˆ~ìw4pTT#Zt G‡` D¥fÖ ×H3nlQ¸`òT~1¨z ɈƒîP“¢ø)¼b–ä¢"xFw3 CK¶­¶ö°´ç<¸Ê˜¨÷Þ ¾l+*_æõrÐyDî³ÿ·m¾«x­~²ARAåÊœÉÙ‰ä—íûZM4þÇ]Groó/Â[X£7ÚÄñ®F ý4ã]ü«Y{m4nNV®ÆÐ¯!’@¿à@ÒØWr|^+Sß+ðgŒ•óä‹pÉ '† _^õ­ÛÍÞD¸Ô=]S*‰:ˆCû«¦·ÁfH›—:FãÔÌ7D5ÿ_Xà‚fEjòã¦î£«uÛ W\g1òà®í\VmÚK´ˆ¯¶ßz±]UVYn·6 ô’Ð@Ÿ[yÅTâµÿmdðóeràÚվΟþnÝÒÅPZÖŠî}æïl.Ù6ã™'òôÿ°ˆX~‰üÜ/Êm‰ðž²ôR͵–àÓå{küð´C j¿ÄæT{¹J“àfÖÅR4‹Wðû„Îïž|¦~ÆûÅ~EáÐþ"¡¡Ë.•Îá¬Qð§Æ‡-$ë,>ÅÄ{g\¬kˆ0yV £ô§ÆpÞ*þ .¸Žò¼júVàÿD“x"!TuÄIEì™AÌRz'a±­¤X/C3CMé„ò÷1˜['‚Eû ¿ ±S?K°.§±7ÿÓú.¦ô¹†3Ü:W°»¢ó;ÿ‰ÆãÉö)d&]yÐð]~ñ´…5 J™¸GÜ¥þ? š… eäp_³MÜ_ñ0Jz–Š©…x¬G¨ ã…ÿ祔LB ЍøšÔk—_ýì=Éß~;8òvMµüσK½aQ3$|ëƒV¡o@bžx[ýþ:rÓá–@vI¤>QA®a+ÑWÍõáçÎ,W5h­îa 21ýrãT§ÕŒq>Zm4æbºÜJ¥ºœqŒ´{X…ÂàÒ*p4oa¼¦ËT6ÍŒ"zVdøtêÙ‹KG=ƒeŸˆ68 8&¡>='!Šƒ˜LQ3B û*ì) þ£õÕ –ÒΦ(lU}ÄÅø§s®d¦:s5±fáûççØÄL¡Úà[+¢j"øÁÕ’¥œQ— !ïÔ¯ ¼]8 XÒžv#xÞ"SE["öK…þ3™’š©™IÏ86ªn^¹cßîæ’fª¸÷LÞ¡3±+ ³ïúâÃ|­¿ƒ·¤G4|Mì 4@ÄHtàˆ°óA³‡õáCÃÕ~HÏ|e£#O„`²fº<²Eû_6I‰œ€ê@‘˜÷pêf‡èØ|]Ì¥h(ê¿¡Ï)Oß“&¸~Š<%÷( ¯!>XE “âGˆê³Å÷ƒ4á®mEó倵pѨmån8ÙÅåìâßXUÙµ‡œ`@üGq3±Nò,e&_¨¤_C©;á’í+€Q–ÐÛ7zgôõ^I_WQû,ÎG1‹­þù¶YýÄvSäÑ™¾ªP².d,*_È!yÖ/™!ðò3cw°. ì邽[¤¨4Z¹ËÈøM׋ë÷“FÇà.bR!ŒyK¦ÿ?¯%xÁ‹ây?žfG˜n¥EФ‹„ëÍŒà+œ2㌠"ýjõ°P, ?ðið¡×c¡ë§ødÿ“á‡rÍ)Ði ßÉ¥0hïI@½D8t{Óþ¿[Kž‚ï#¾Ó¸­ =€@¨Ig…¡Â Œµ„dnG¿vôg»újj™ Ö˜ìÄ?º¢Ê0Ø wÏ΂9rÛª:qZ,âk€ ä~n•«FAD ¼§tYqQ„>8kšixÉÄÊ$pÈwv),²ÌQd,§ø¤–vÚñ\Õ ÜxŒø†\qÁÍ!d%ÓA ÙúÎohݤ,¯B™ 7$àa?RqÂìÂÇ«#Ø«ˆÞ'Ha-#>oøœ£-Wó78 :Œýâ·ç³OÉñ”DÚ„zaGßê¸çDÍ ñLÍû–IC,EÕìhÔVcu\jÈfsßÖn1IÂMs5È›—2jÿÖG:Éw±×œÌ'9¹fîŸèãŽüØär1‹¬õ/aò 4oáÓŠzfÉøKIÙ‘êCñÄ!^·ìæÄˆõÙy=…JPœÍc˜$©”xÌ@ÒûQ¨×ÿ)xç1Ãa|Ò/+é+|´KÈ æÉôÅ™N„LKh‡nAa1z,ä“V0 ´{ذ÷äí˜ð¤ÜÚ °Ç_Gß]F±ß¡8‡öÍNÌprXð[iã¼­ýÿÓž*hÐ ÏÒ\ ¼ÙW%ÜŠ´`üèBaf¥‰FÂÐlj6@šJ)‡¶‰Mîí[’±Y3@Ä«\4*Cx†PÒ`òÃð¨Øü‘ V™‰¥¡ëÙR•åÅ"ŽCÁƒè3†ˆÑ˜Cöäì#R¸Hdw{º÷áÈ÷¿#ãµóÏB”tb«Q†Àj¨Ic´¶¦¯8˜m8\‚¾é¤"¢Ýp~ó5#½ÙÍ ÔÖ~ÙX-UõØ×g1Sä¡— Ž€1 cFñ„È‹¤~J“ô{ôûцDdäN ‡CæÃ M•dM~)Ãám’óÚUÒMe¢^ +¿Öø1 gA˜l@禯úßý8­äG/ýðsï£{TßJ%ò`;¥¼1}À¤Ø_Ь:×}ô‹@L)¥á+sxeé„ó âÃpi«gÑ#½a £.ýž>¸ïrGˆ¼`£ºÏ€Åç,ã=Î+ìò‰Fãy”/5 u~i^óX¢(Þn ÄÍÄâÐÌ{1+Þ3´¢®+£˜jÌ€¼'°F!V06îîɸí;sºÛªZ%,œ3¢^GßF½Æ;âìÒuñô…_n^7tý‰wx;F ˜ïȰîx)P8y4:Þ²~»|a{«#ÉSDù$f†6YÃ¥ ÷0ö/ƒãW>A2;–ŸŠ¡á=ÔÈœ¾cŸmW6Š//BO²ª¼Ç\„½RšÚi;º®ô–cÆ$I„LX”;-p••}Ú_8cÄ4¸ º'@–«–êô@ûkøPj‹ewûGõ3R¾6Ã;÷O›xFÒHNd@dš¥¿9`š VBË:c~¨ªfÞÍû¬ÆßÖö¯R¦Ê3×H£¯…DÑ“TöD8{&rŸ|^]“'u/XL7I¿ý…¢w³öÏ+¨€ûÂã "‡îW­\su¿Saç7YÈ>¹P5–¸8üÞÔ/&½ÿÇÔ9ò/>]§m.Òb¸[\Z–]–4²g08XŒ¯+ˬIn)ί )²)·AÞ‰Þ–ì1•î7¿™Ú‚ŸÄ$s ¡!qñ¬äo=‘?÷”]ö¨sâÃ9ˆØ3Þ¾³ãÿ>ÖìÀà‡žóo+@7lÁ>˜² $4€ÇŒ…®ËÐ*«v¼þà¡-¯‚vh!ÊèqîSK>I”ïA€ãAMîPÔ àkKŒ`Ì…©ÐëJ§çûÝ–ÛÀ“ž¸ZŒkiw˜õÌMvœ'ôO낃|Æöï`z7@å¡fçàv®=˜H£Vߌª b4RU«5Ç%–HŸš ?ú½Ö¸(²pÁqݲ‚t§ü51©n»T?—‘ÃF8¡TÍx,“Óï6w0?JÒ²·‡,͘Ê6°ÌŠáɹ֯8-> Jn  gEB©s€Ý½?Ö'‘+ #‹ÐÌ_"268°³M“8Ð]gÖ= ÒÀÕ¤"Ž«Û½vHoþ­QÌ<Œ[·hŠÉ3êàè t]—W«ÀƆ›p¾”9È`ëÈë¦} —ñŠh‡õøß¨ ndEÖƒvܪ ¡vŠÄ9í&­D•‰ÝÈ›{7¼¾¿Ç¼ʆö}ЃúÓ}uh‰Pl4m²ØEf ya–[ Ò¤`í‡SáL !‘…• ïö«ù,Dª.•c+˜³}±8ò Ô/L©;k‹“$©Âãφö2Q!'\ Äw"m´È /i?Ó <=±;£3”T‰ªÉñ@e„’"ñA#ÐÁð\"8¸}°R™ßß™’:Ù<±¬ÄÖÌÆ}<ÝûV¿½^_}ß‘ÊÃ}—ZÈŒ:Pã p´ 쒾ƥ±iò܈¡¨T æ9ïe¨Ä/¦îÅ»®Åv)IÎD$ÛœÆ!öf}…•‡¾øVÞÏ{pÐwv€­f5>ØoÌ?ºîe YIë)VÈ.Ö´‘Ñ„%‡¸Æœ<Æ·:Ú ]<ÄŽì ¨"À’",OÕ8!åG¤¦¸yb½ ˜WÿAjžCšÁõ¾ÂCÈÆÇSœK7Öº“á‹´’ë´9?3û¤ê@©²Ë5ƒ½¬~+ºcÊ¿Ë,:¹r¬¯'ü›Y+9ÕDòýJÞ–oø`܇ B}›ÇmLçGy‰‘ '¤P¿hôñB›ÄL±êC¨ø+  ¢¤e×¾åo,/y›`l[ÄÜ:fë*¹AAMÀbÙ±bûS[§çÉðûÁÚ i2#.Œ×íÑ\æShë±£»œÉ)È·ï!,ÅíCŸõ’!ÙìûÒ(Dµ‚d%´ià¯Z'±'R=ÂÄ5›‰ËùÉr÷Ž,ûHÏÁuZm|ÓôýÇAþ?—`%?Ë‚~óåÌÒ\Ì€V€Àµ5†ˆpvÐä[²ù³<¬ñ‰òÖM ð‹ìKí›"ˆ½Q4J/Ê&šB¤ô¸s…oÉ2êã½Ì í› ñØàÐ kCf$ïŒ#¿¸•MþüÐÑš5._`n¾qÇ6ø1Ù) ~Am‘‚äþV®BžºúÆ’k%üqT¢å>™§f¨M[¬ÑK„aæviK€éF<8L†-š=Ó¨ëøÄî”x„{‹h”¥¶ÜwçDÅ‹þÊp^DG€¥HL¢a¸ze˜pëdKÒiTo=‰ðš«xÁ£~K›²Úƒ˜$UqE«÷hÅ–j`u”`MÒR"³‘_Dv¤h™Q9ƒF†¸Þ2ÐÈܲdýC¡öGbüe: ®”†»éFÆ ¿šó¤Œ¿qغÐ@’–­i<³iÖu8ïÑ€Gè^ÍmóLJV¦ç¹Ré‰~¨é”is}xœÂúÍ%Ì@—ICD=¾˜Ý.¿¼ãçBé¤XÞ¹AOåP7bs–¨‚ê‡ê]ÁÎòìÊ8a3áë‰=¸]©/=§h„_s©ê&hÌuAm€2Eo’JÀiZähÁ?±Œý—+6ÃUüt a¡7E&¾¼ ÖÝÿ‰Ž½Îšp&+±©n6¤»ÓE@MÒÕÁ’ÄÕ·m%<Š‡Ç´âi$^û…v೪­ž2§S¨ýÈûž¥#óp׊„ܳ¢°½Å W!±«j`J³ªïˆcöâ&9Õè"l¨ÑN ÌÓÁ$- KhC(7æÿ½>ô˜…°–s;}hУößasí` ”á»LÑüsôÐ+3H¬­›î»È–ž¹Æ °KZ%ÀÚÅ?湨Îa>+ƒT:2_î’{²âA =Íú""Ð’­pŸ¼×\®÷ú%¸®k©pÏaÅB[èâY¿÷÷%ã‰Åü&Á-Õ <_ÖyÆGmÛñÔ¹_á×hAÉvÓ¢›°…yZ˃)OÀqŽDËé¶ \dÚ¼¶H@噇ЮXb^½gù|€|1žý’ Þ"fZdà?Ф@¶†ß‰³Mó^ô…µ‘/(ͬ×`ó™‰í³ìí­üJS¯W¿§Âìj%i4OÔìE‚6q¹˜}# -ÐÓ³I+ÃAæQ똴ðÚñ!Ä$YÛD³¿ý8ž™÷ ZÜÃüÌÙlUòÂÂpåñnƒ2o èªîxNé:P…‡·. z9¥ð‘T’hËå¾S{¤#ºù;1^ Ü %d4Wóϯ >ý Ý鑤¡&K v;f­aæN—Ꞇ±,pdzè j dÀÊ.þ=˜jë¸ò¨:¢0Þ† [ˆfÁ„œ"õÑñ^j&HΜ‡Ÿ„9F¤˜0 :·½†<ØšíK*-GŽLifˤð€~î±³A'²ëc¹ ¿î›¨bŠzÉ6dáÞÐW±¨NCÞyP / S_gm*r¦V ¹8xØr×0%v~/©ªL6[hû€‰k”ÀüEÕ„Ê 6AKÏ‘ æÊª1wWL©)ö>îÚmª¶4‘Šbè>aÆG«/ƒ!$Œgi?3³þ’ƒøEòV’ñU¨¹ï†&‡=÷XB­[Œ~UsªÖ€0N†9”áá±Ê7Ò…rËÇûϤÓCä$KËmߢ>eV¶BVÉà-óp)NßÔ¦é_' 4ÛÞ ­œd5r‚yËå–™¦Ç!ïaÇ'Ïáü0MÑoWŸ+À[úí‚Ch¦K=Àx@ox™.mÄùvwó¿íƒNfÛ豄XbþPìiyƒ344!õ †€| ™Á$ˆh~ŸUà¶É{Çóx€ÝéòÈ8•€ïG¨=Š%LiIa‡¡uÐ+”Y´±“Ò²†Zj¥DÆ÷gÉó‰šOÝH·¦á²Іud³ÜK†ž Za[>ˆÓ^‚*j=‚î”™šñ“ô0V<‘ ÖtÌöÅÏšB—wäQÕ+€ÃA ÌIJ Éâ’‹¸û^ˆt*K Èã6m„“¨÷âF±¿`3w­µL^ºµ*ìÒ±eûä†2™ºh(aCáµ^#B÷ÇÐpREÓèàJ£Twî#?ÔÂDýÓ…&ÚøˆAñ‰ø`®ÓiøÛ‘“+o]”€•ãÛ“‰ÿ õ™® rÙ9e/:»!òÜÌQ‘UO­=™f›”ѤÎGj§ ®"«VÃã ­R&ò@#;~À%êLÈ¡–@Å'øcÖrà )H›ÁÃÒ†'Ùt ;ËP x²s.æðÖàâfÀ&ú=p=[Ï ðUL‹DÃÆ5¦jŽkŸÿê!r½c5!Yã‹Æ`„ ÉÛ\bC$Õ6lÑ ×âöb)XÀ-ñƒy…ÁNƒú#jà5P1? '¡!)Q±ª@G3„[¿²k€yÃtk5rô,ÙæB} ê©üžjH‹#Ô\ƒrXGª7OS5¯¼7dÍ'ö€68òe±ð¥ÐhÃEù%xToLzxŠON…J×Zˆ³·eS²}‚Vœ6óvÅr 1± [˰„í„ xÑZ߈ff†„¤7š®¹nïàE$Ò«kåÓaÉ~üž1ëÜŽpfEÃ<^®]ú‰|ë´ìê|/‰Uy^×Í—~Ðê<ôõ~SáXƒŠª01Ûÿ°<} Íäj+€@»#cȺ©ºôO2N´ÙÕÏUCÏùŽÕÎêA$oú¯Ø?cy³ûÞÓ‡?$ÜæYæç/±ªZãì +‘}‘zÌ2ˆÌd,:z;M¼ PBIS.䯆TbUýɆ¹{ÔŒ…m¡»¤!a=–ÖjÂ^Q&O¤®*æ'ü;v]åé»™™úPÀJTTu‘_Ê€Uµè(‡GÇ…K}©¯øF[ÉãÌ Yã=“ŽRE¤®—Ñ|¾øääùÉÔZ¢J¸ÃìŽm< ,ÃWfý-y‹Ã·Âèº3©g?”xÊ™³ÐZN˜’´"‹Ö—Ãv­9/¢Yà–J’DæbÝFß`À|Ó{ÈK°Ë‚÷¿ŸÆE¸ð 6Vlþ‰Þ61”‡]“áD銣¢Ì»G«ÍHMWAùG@ÁÄ`çrCñíƒá'ã ïÓú¶Y4´™KR¹òlÀ]êeXBY$ÙÉh ÐÓ+)›ˆÓ!ý~UÉ>òòbκ¢K%Dœï¦âÝb$¦0ïòZ@A ¶¿ËØ´âV»YÀÛé” otêÝŠ^I»u)r‹ª¾U¢èцæJ ø)è»vu,á½L`Ìõe@~1,*Ñ^5{y\>OKJ •ïWRoÞÒ6ýÄ6`”£ß@Ï’ITñM0Ú  K\Oˆ’N’ è¤(O°ÀÐyº@©ýT"Ý`jeô‰ˆ ëB$˜K`Õ=9(–?Ú)±P”qöOœßlg¯6Oìgª_q}=d­†ú"¾ï”Ð/‡¦ Kâè [`÷›¼™ð«¤æŒŸ´>áEð@"Š™˜ O”4\® ¥³ºÐ†€Ö)Ñ{\²\³Ý_/}ïȨÉÃ'ÈOJ;¬)ËÞ6Øä)ÝÄȽx¥žþæ°œžeˆèt‡eˆƒIZ äüO_ÿ¹êSòX&Õ³Ð/gþﻫˆ¯ã]æ[»Ö%³7á >ÂÌnužëÎ/ëÏÁ/¬0KÙ!Åà¶¼O/?‘ù¯½¿2â±óI™Ù`¯¦ðD½Àþ¤!Uꉙ@}æÀ ¬K¶y¸¯Kà:DI•>¡ÞPªË —œÇÖ$À H¤0Áªÿ‹…NÁÞªx\^2±‘¬Ä«{L'ú´ŽãÊ cÜ(elú†Þ”sèßWâÿÇ*Ë’e}Ò»µ4Ûžš.ü_ðýé¸QóEº×¸ø&Ö™·ÅV*°]5V’k—‚¢ëü gšnÿˆî=÷«U¯y°þ;ðþpR…ÏM6ÛM=Ü]Þð–2§8Ù;E;ýñ2«¼û|5Øî¼§{¸¬bA* Ø=4[¿¯øÊD`˜@Z¨‚Có3ŸžA`U¹¹ÿ<agžSÓ-W_»‚™ÿø(zêñØÿášýÝ:>¿ÿøÞï»®¿ÿù·{÷í…ÍQ{~Pƒ5Ä|h8´ Kx÷—¡ô;ŠÈ2ߢ»ÄfúKΚÆlб+ð=ò¶=„eˆ`x¼·rG,%XË|sAb±¾˜ÍéÿaÎý°AxáN}(áÂìgôæ`—!2¯I0ŽÑäxSÓ@…%þG=p–eX>5Ïñyºqé§ž±Y¢ë]€qð{L~˜„p_~Ó§0à§Æbr´„÷ŽWL9ˆ˜½ñÆ«°£ÓŽéı‹7‚àÐ* ,+Õ\åÃÚ×@ŒeÉ63•M½GëÜ{EÔ€ Ø;úÿéÕBPi€A(¶T·d̰3p˜ 7Ç`l8+8Ê»W2Zñq›óŸ-q0«áoZ\O!z® Q‘}¼ižÂq½ØƒY`x`?Q‹mƒ«þ#ˆ­¶¨š¼@óøÙ+r_Yÿ­Q,tbi³Ž~±¨ãþª«åâ~*Ý¥79Á¸jj  è” â)ÁÛÁ»©×QÍWPáÜ%9î†p¶€-•b]56Ü# ,T·~ö°OûñGNÊód@õ³aÿ^œv5_ÁUF?œó–9ç”D½÷ËãB„¿Íþ«‰×9Š¢R³2bÙ¦¢/l¾Ëïs°P?Óm»ž§UêÝ,Ót–ä$ù]pºïÑZüŸ˜Ô À?l Íܸ äó²÷a'6"ãÜ=}h'ˆRÎÓÅ ÑÄJ{îòg"Æ:ûÙ~Xˆ¼Þ‹ð ¨$+ü5.Øÿ‰È/„"{õõPÿÕ3 låEñ9I|fv‰¯©ü‚>Tܦdåà1@ú6”–&.þ~¤csÜŠ.Î:QÄñ¡ ±ùG•Qœ -Ôz‰·dÉÓ0‹ÈÀM7ã}Æé×+Nã”ëêçtà‰Ø]*ÀKo󉨦ÞT¹ÓJkœGÇCšQ ë‡"­1ow‰þõ¸ðY–i`ÅkÞ„òLØ·3P5ª a˜[¤Ã|0Ï’DZjÓ¬Ç <Eð3Ô°‡¾!Rs_.½)Z’1V› ý‹ížÎ‘vdãt¾»ºÞãÍÜë=0S![¸„¼WÕAs¿Ýd@ßr´C œLóÌ¡Ic¬CK©½Eh(iðÔTWmÇÂj0vY‰Å…bÔ r›P‹¶ Gô›I=0[tàSËnĤÎ#Æ{¼ûn9¿Š`‡…ÔI’Ö,D±`ßš»`¬K韓r‚÷üäË×aûî(ڎ݃Mž´\¸Ç?sªV:‡jÐI ÖIý.è¾ÜÖ¼ø¸nðE''­}á™D>aIb = r#,ăAçùˆ´¢¡Cf¨V*o‡T$å(²±8–E‹Ì‘G±ªˆîL7‚’z< ¸ŽÔ"¿p±‚z3³½?òI>ƒûô9Ê9ìB—í^œi wËÀé£äßÂýX˜v[ÅÒÀ÷H68ê…‘*SÄ|ž ¨Ü6Øqz dˆÆBõQÑ*èðK¾4ÙC

g»@ÖP~Y*ËNMd@#ðP”dÚ“ùÔ% `I>'ìI¯[;w¬\¡Ç5MEðO®:ëŽx2›!Ç"b¾øÃd<*p»d¢shðÁ&:lZ Q¿&ƇØéä¯L‚®X0Äæÿ Ž;«ôGg¦} ˜gÿ”":M?Χˣ R[3!^oàï31œy`úUP_íà Kêc¦C‘¬w=¦‡~­œƒãœcs ‹bÃÚª·_,µÂ­h¢G¾Œóé©®tq4H½v‘WMÔGúº‹I[¾ƒgûSw¶ à@Ú[ô¼'Üw”_\n¤ž¨1ÃÌ^ *S.¥æÜDÎÀ«\.o[¥ÇEšHó9,)ãÔ–5ÆŽ¿x’‘ï!Š;ã,™ƒcjÃÌ1'è†. 2„B/{ÀÌßïUøU­ãO¸= Œ¤p˜_ÈG<¨ ;•åf@2#ïÊ âQ‡ì3cðhÌtÌtÃsì€{ÝÐQG‰€}z¯°èd‚‡úÃÕ'æQåûM芺¢Tnü"Á®!öýA€ì k„,žÀ•‰qTfÅ3I–ß~J· hìÆÊïÏܯ—æÊ£QÑh{ÈéÈ*¥Hî(øÒË”ðÀ¬ËºÖœ)‚Œ•fbì_q×!h;LE‰)´œu5r W{|üäÀX‡vb‹Æ Àwãï÷36ä/Râ7Û¹#*‘‡}Þå–׿žHäCƒƒZ7‰pÀö‚"g/ˆ]þ+%æ3}+ÍXÂôxœ×¦ZŒÿØÙÐ,þ²mȼŒ|ÝF°zG÷I¥úû¨! Pˆ+8~5Ò:ãg_"-° 6Mü66ðÈUé 3F«÷_ˆR§ às¼~´†>U¨CËJ¢Zû•Záü4àõªÙG ŒµÝNW(;[ûìµ'cIÎ^9Q²«FEg¹÷¨ ñ[ƒ"à AE+?®MÊ:¶×KÙ´R¸¢ªÜç¶ÍbcV<‹T°5—%…‡°ã6Fw*‚š-•ž4B:shc» ÑÕûu¤;ø Òo(!•ÂJ¯.:ã&˜3†,”cÛCc¨Š=Ÿ…k` Aq vm%°—~L?‰ cŽV÷Ø Æìг?py±½6Aì0 ¤`Ûƒ58”ñzžeä–Pzß,ä™9ë0BÓ2x!#¹ën^ü ±ÃçËÖ g~•†þ‚éÀ©Éþ²QÓËpBâ@Ôjy=,Åþ¾x0 ¯¥4Q© F‡2Ø*âó&†8—“@\¨*‰îàu±ôok9Æ#Nã¿‹^Æ(ì³Ò‚ ß¹Àc£IYÁ¬Ê3‰û䈶‘ûZõ³cüLíPÊùËЀ¸èe›/ñÐl°yfˇÅÎM€-³ÂU«g–/–Ã&b“n¿f „õÀ_ØÀçÓMVâ€è;žR_ DgöL8‚ -·Ä³àÂ:÷ß…à{/Ç’´wÚ\^u‚“(àYa&™mL“¢.5·}œÄo9îÙ³­„‰Xtîè7L9B!„cÇWƒn§¾Ð[°&«ÀRASáu¶;Öx‘÷ôt^ÏÁRǶÕiê`ðêxot>;]•·*Pÿÿä^²ãxÝ­»aŸø1-„&2¯²0ðüv‘’¼Õ¹>Ÿ¾f¤agËŒEýasáEð‘Ê`iL¤¨Êûú€7¿Ý1JÉ€ê,OL7¶Ä?æ`>WE|çæ@±C)Y¬àßV§‚D?þö ¸j¾°ñžƒˆ.ƒÛ›63ƒ$ŸMGˆ@D!BìÌ—/N{‰õ%W<0±øÈb"B"÷B"r}—šƒ¹) =̉oौßÙ­‰ÜÂ3V³H½¸†Ð‚:«cÿÈ3Ä~§ïU÷úRå(¯¤¢ŒÄ9Tg&ÈÂòÉ<‡`e“m¨ÿò<°•¯=E3EÎ;<¢1Z"㡾`p <…t(¸¡¢ë7ý D ± 9L„pnÄfkŒzLRVù:¡B !.²x¢³ÞXðb͘t ò´öÀ°µöS~@"½ötd5Ä‘t ;j+‚u(nÙøðuÊîn6›º»¦¼HL5ùÐ>±paÏ e}0Ír R%ïÀ_‘H+1;ü³·xˆ@½à=°¿\•Ù6ýK^—o( Œ¨U¦Ã…ŸOŠK‘&A€‰[l:†[|¤¥çëFßãWÃ¥Á:1Þ"aU›ôL²}‡‘N£ß„ÒÃÌ6£wç œZwçB Ï<¦Þçsm·pˆj÷’1o^0:Ô Š`©ˆ8b ÍR¿y¾ÄÓ×—ôD˜D Db ‡.Ï<ì’8 ¥™'i tÛ´†=ÇAÿ™ã7û:>¸ªUL(µÂ€ påð”}Œòÿˆˆ°Çf46mÏ ,‚ߢòÌà²×l:Ð!š^®Ð@AGËá'æ=òÎÍÆRË4cĘò±5€Qö°~ÞxV©0óeZ ‘0lÈrK“ê:O`êHÏ”dS!)´ç¥<¤{~L†^ͬ:"ö‰@.½í»-“$áîÜ¡åÕ¸“SžC¦‹t¥v½íx‡)ïúlÍ<âm-Ôu§öú¨à³¸™Y§Á ™ç2ÆŠ¢ý*Xª¸ÉFÞf 'IÅ`¾ß ÂkVˆ¼A…ÿx·²—1_k©Û\ѽVNà›tñÎëÙä},ôIQLQ>û;o5bïþd¸æÉžº*8#Ôõœ\’#®Æ‚°wë "ðªàÖi@Š^)P‰NÀÅ8^dÑš ‹’ßâ ¯D?Pz¶ÑL†­Žxè²vcY£= À°X,Ù‚LŒ¥Ì¬éæ„&“ÇfNn˜"6'ÅksC©_¼ùQ›õú,´ðÛ…®™É1´Ô/6«ÑÃo±2ñÄøz®bûÕ]ô4<8Íåfø°0yú!TU†yðã“}r°ˆ)EàÌ—Ý{_öÄÎb»Å úi'ÄQgãVæv'l¾ëã6ý}z3]¿IÒ¸á»[ó’¿ %؉&µ0ä±ß'àidÀz4šéb^¼¤(/4È‚)•#‡•`;¼™•°ÀIÉD z´áö{%X¿fW)Ï›ÂcJˆ9ùà™isçш‹%2;«LBáOÁä‰Єß[8R(]ÚgÇ.¾›”ÕåA‚iúààKxî±àÙta£K+ øèLUÂ3Å´;üi ÂË{ þ«¤ð âÅ UþPH¦š&úÄ¢r…É”þüùâÒà?ßò÷-Ç8Áy\jpÄö¬Ÿ‹ÂEU ÷ŒÛýøð¦Š7ß.*gXÔ ‘õ?¼¨çܵÉÁ<ÎÈ|è>¨9šÚU´fºÙ›'ñ\E‡ Bi'º_‹ƒÍ¢=?ƒeÍ•¤8Ë[Ãl×¾ðóÅè…‚QAAÖ€ž“H¯Zâ5kEÛ‚°0–Ôp#3癜ÊsOGŒ8Ë`/~mæí–ý~’)’‹¡¹M‰SÁm´’¿àäë–Îæmð2¯©3¶úÈ>S‰O/R·µ¤Û7ÑfŸýA4¸…žïø^]¡²÷É×?Óù¦’ü„à¼'âä’7‚&†|hl‰LËâW¼,Ü×£NyÀbÀÇhŽŽtú€aM͉l—Q”û‡‘ìø•* ú‰Gb¿€»²ˆRã#nŒ€GNtÙŒfè¨Þé3X|®O)OFÞ»÷èn%®kƒÑŸFêûg»y,-jiÃH¶µ~n°CK¨ÖíÀl­öæð.7/Ì%29ɶtÿ9·ìÜ(@3²·Œ£"Mèf›Ìz¹˜UÔ1ü.1+„×téi¸M÷`3«ˆäÀú«\Ošo˜rò7±ƒ[zîš>=™ÝšÒá'ˆ“U™býǛĿF‡DÁƒj2î³’aD0ÝÍ_dË^TÊ èuâßôlÜBxÞ Æ‹N_0 >I."a(T¨o‡¤úYØ/¸=¸È;kŽhl¡ÝKB|‡¸ŒFO eQcxxYçÉú!pD~~›ñ#$E:VhÁRÒï¯þ!Úd.ý­R€L9ñXÜ)cQþج ¦äqàÖ=l­fh¸Çu°k&ØÑ—Û›8à—¿#`+.3´táâ~n€6U®² }cj¥°0a…Øv‚¸y©žj|Sâ!‰.°Zç>qèu¤Jþjß©›þÆ8½ÐµLaœ©`Á‹Da7L#x†¶Ãª(໢m"žYKûÆ2 xô4çÍ"Ñ;L:‘[µ¡¯G™Óvç‡<52ì–jÒéPÊHÔŸv®|ºËãX©­A!øÇº¬ âpô}$x] ê ê&>2}j~hç=ߘ`îÍfÌú>ˆ6 6‹ ,¢P^%ÊS/ŽÍ8›À=¿ÀýæS·!Á—3-¤¤ü¶ïÂëö;ßš[‚²¥ÉéZˆ–9‰ˆÕå|±pZ턹þ˜ž"‰­X<„ IuzÒ˜i¬—l…,™¡ÀSÛ /ãvÉ tÏX{òZfÎøM@õ¨"£KÈø¼´¦ÎßÄœdSR´2^†Lϱ|ÝšÅF=lòô6þÈf§ŒG³Yeª`Z7þ-*²3ú´ø}#n' OYZ¶·ŒT2¼yã}S9ÿclm“p`Ο‚;Ô“o ~ê]«Óð¶9­7ïPpm~“ûS6tìYTý|Ù–‚ñjúO«óäv,"ÍeF Þ LØó¦/µëÞ|2|pJ¼ôbÛ4fÓÔÕ¹ù&¢vÆð„±™æ7Ó‚-“~ÏË:øE¡a<·RˆòŽÂb¶ú+¹Ù½7'ø3ùü¢íËø¸ŸØZ/"~iT/²-½ÚNzíGè!ñˆ‘•è—€­°D²ü2)£x¦ýÑeÆÇ«ùdIÿû™€ÒmŽ„û†2 :ÐÅ3á~õ©üK¥gŸˆëÙ8Ëh1zçø&C–>îCÕP°Ãrû?Û—Ãz‘z«ýÿÿ^dWÕ׫¿hÓ©ï•ܤúEßùÄ7óƒõ4í¦É÷þ:ÎqrÉy÷{Ò4ÓÍ©ñøТúziÿð®6$ó}ñ³—ö]%þþï°ðÇù9/wâË’àΟý˜¤{D nû¶SgllÆOOâñr)©ÅòQjöÃÇùÿëqqÝ!tj¯?OoR+ã×¢â³çó­S¤²&ý’¼â«åùM«psÆf£ÝΈK¬TÍÆÁ¿!ñª# b[ šçZÇÒUytÇ‹Ÿ~šš.^hq/y¬ö‰µ§îr?fÿÔ‘•t* i*×Ch}4aýY×nQÜ;…¿MEUãøº,*y=Go››­dæ ì]4“1(m†!¦ÃŒqAÇŒ,˜Ï•œ*’֛͌‹—xƒÅŒÕ*QKò͇h ºÅåiy)ÓI©õRqâ#m(#qæ²<¤ƒ.RdÓýs:fBBÿ\|Au@Æ”¡ P¶í°³A¿zò«‡¡ð µfì„)´ÒÜáç‹Â1Å€FÈäíÔ‘þ¥£Ò¿á]Ë Z.¹®±‚š…â^*[ š ÏÕ0Þ œ$3³ÛB”¢3†× ùö™8™º›cÝuC!U¡Ÿ_N©MtÛK(‰EŒ0%ép”íÈ1åúÕñÏuÂ3 wv<µÌÄæÇU ¹R÷Øï~ëË™€ƒ‹Ö™}îoº­ÍµDÙ1ZL„64J»»W/óMI¥þüqoŒ€Š!ö_£xPÓœú…ïôÖsê±A5DUËÞß½+ó|ß©)šÖ9‘Rž .)rŠ·n+ïy‘uSìz–"}±e^L¾ÈB-/ŽBQã:lBó<Òö¾!6ùê?‚ûpYK<(Ÿ@ò„15(ýãxŸæà’þĤdĉ¶+êS.ÃQX¡”ÌÔRqêE,nâ w\V’麷ì€~0>F°{Ì"ê§_7TqºxÔGUO…=ÆèÛ8 O⸎ñçä£uÊ:LSà?Ë 7‘ßñ"Ç™8 ¯¸ƒ X‡Æàv,‚Œ>’˜(²ÏX!Ê Ýj ‘ŰVg+ºBte˜ÈôI6\ÆG¥Ù¹›Hzßí?Ú?ØµŽºüA´°Õ}i èÄ?Æ´Ð'Fg6ÃJ/lÝ9¯ÄL§çˆ¶ËOÎL¹#Õ1IN,Uç8àŒTE«Þíd½)fôv(“¸Œ– ©ÆÔQ)üöÀëL^Ô³¾5EØì<é<¨˜xË}bO%¦ÅMd½A?ÕÚœè :6 M<‚äãÒß»öæŠþ}È3ìâ¼'Ú™J¢v¼¦‰ Ρ—. Õ!±…µòo€‚C¦_V 4ú€¨ëÑ!f¹8¥í~#߸‚›70~œqS$×”Ewyæp‡£þÏòXµ<™DåÏ1¨ q…ŠF¨–Ï0Ñ!>/ỡus‚ô2ýe+†ÛäåøTÎȃ†ÓÜ×Ѐß…6;'Åp-¤ölv>+€¬&Û¡{Å‘-jñ×QS°ÍK;Ko4¦A²ý}!Ø…îý’Q±5Ì9öm¦_ú™=ó[K@L“Éöp?¡&@õñŒà¶±’h9XÇšÃÑD;¤ÅÌ\ž‡WNb„µæçáöÛÿ BEs}©£/`¨ƒÏ"ã4Y$McÅaÐÆ4«7‘:@€7nä¥Ãˆc.# u!? ¼—{Ó«èäù Vƒò98@èæ+¢’è#:eJì繋^òÍÓC©8 ádÊ–â´ãRØ¿ô S25ÑöòÂ5€B!-š:6ìT6š“X4ôYP£¤¸B”kñG`ìÚCÍ9wn‚4O›¨Û¤·€ }Aá¨:ù±D¢w#§ý4%ùP˜äÒ ¹ÜÄŽ™v”{CT‘Qq ŽX6S+û‡ðj“PGe›udy¨Mµ f€’xÍÇœ$à`R”ûÛõï-"ïèsÀb-AÕï2bFüLˆ„À#ü—«Ö>þ[2=»]ƒÈÙÀ¦Ò•eÉWþîE{®ÿŠSüK "²’ïôkhèÞSàÂÂ~•¨‚êo)͉çÇá¤8éþƼåášâïÞ ¢¶}³‚„R@mWÑræ6ò±„Ÿ´@˧“…æ:ö+€‹ÕC6!ïÊ®§ØkFÀ¦ôÝ Ý<3]«g´ ¤«\BÅO±s÷é®® ÉÃwôÛEgdVºìù•Õñ"¤V·€{Òi+šAU7kxQ¬6¬…xé¶ ¹‰•“4øL*ÜV™ùXL y•CÂ×`‚ª@áw`gi§)=‘4|×.>·ÉÍZÿ¤Ýù¡é4åšyÒÝ·«Û.Ç…U”N¶Qe˜aÄÁíä[ÂN­0ÓÛYÿFå‹Wœãb‡ì’„€ZC¥gE^´ðàeV‚³ç‚ë¬í¹²<åðìS_xÒ%¦á;ë"Ø¡ûJ~IÁ‡ âŠVLa8ä1°êÈ…Âí›Ê‡‡R€@o$JйÑ!ùðÒsÓ°Är³WÙ€Z)?€¯´ò›ûˆtßfƒõ>86Öåç=sÖJa h€±i³Ž£^T¨ù}¦a×:^ðîDþ×{Dº÷…Npr1ëc ÞÀ¾â±ÿ¶º³ñÑg6r¦P{o-^ÜQÿ½ÿßÖÄMÎîg×ã¶Ø@ÒÆÛNDÌá¡-> ÍA÷ì’à…ñGО$T ÊÃý¢ªWúxàÁZ¢Tº\ŽÉ@±K_ƒŒòP×ó §Žÿö‚8¹W?ù…x¾±µ¿ê\îgn®ð9Xâʈþûòa>o™ì̸‘ï8üˆÀ4ŨQ0M„îdÞ™F ­,5x³ö„ƒ˜2×1ëc|fKȹí÷¼ô±èA½’ 74½f2ö9QÿøMOØH÷ï;|“¨m‘+§9 šH®ÿÓ³ r.èiø¡ #Nx™áË =ÀÆ(“cvWÞ ]ìOݨÂÓŸB1dG†ƒ>LÖŒ{êqv&‚ëμÙ,‰b¦Ü——°—ùÆ C«U<¸7@© ùb>v4“›ÖU”€†ó‰UãÉ…óD ï5ô.BâÿD†åZ•—–!iE³ó–˜t¤À^âîw/+ó2N†U`v÷¸T0YÕE\ò™7h̹C…üËk§˜qö šûK`b«È R ]›z«%_ç+O ÒÉ z‚…«g¸Lƒ€ÖÌ[ˆ…q›6Ý4ÎŒb¯úÛ¼=:’1öt#„•'•«nî>Ʊ/õ5ý0QÉ¡b X®:}÷Q[}=±äÝòîA4#»6tv‡\y@z¶òWÈU~pDzE)v:'hàKã²ÿÁõäQì~>-mêŒd!ÿoÖä_å}ìã,P÷½Õ÷h$êý>FCCÿkòãÊ4zýÊã ’B ]ï‹t©FëbKypÃx§vÀÉ¿GÀY-AÞáê çRG‘ €×Âèj ävÜcNÍÓÒ"f:=PHÐXz9 Þµ¨¸dÊÆd𕤉—µb¬+8;7‡@”ÎTÑÆf °Xí| FXŸt Ö |Xsc3[!ÔM6tÜAQ˜¨K­ÞºŒi7ˆ¶FO¨ÁéÐoÄ$ÜüQLTM,°H?b_áÌ´™1H†Q²n@…á¬O¤4moöÖ"`’¾Øîi†Ñà,`X`"4PsЩȌ jSþÐk3$Ì@ à© —ˆŸâö*ܯg&ñªÁA¥%„B‚.Z#„­ u,I€ #ÂQxÇY¯Ã[¬ ŠH²Ãu€±}þ7ǽð“0‘_€“°ÕdQsægà±·ZíœÑŒGF±žÄuøiAØËÊÔ «`ÄPŒÆ¯Ûif t¦?§M_A ËãxÝ3TŒ Sé[¹PHcõï™ßÀˆ2â¢6lþ¦µƒV©õ3Ÿ Ïi~,šOYC8“è±køW=5>yº¡6€')¢€ÙôYc0aöX¯«>Þ¹XBâëGEg‹ëÔuyþ÷±H¬ÄÙzH•ù˜ÃY·Ký aj``xñå |0W¶Q¸Úœa"¿~;cפyæùøïâ@æZDxÓ=eñ±Äxd³là!ŽCíU÷7K$™,Øivo Ùh¸â .^¯š’ØöÐêÑt¦Ïü‘ïx3LŽÉ’Èà´Ìm~kÀ&XDußีŸœà™,j½³¢¶Í_òÐæèb|I„¯Ü¡+ü›EP ª:¿À1˜–N- }_]sZ,% ® Þø¼Üd‡MoÔ;„ÇÛŒø¤iüͨ옸Íã"§dôMîë ËÌÃÐ[”Ñ_™ªRb °qÂ…™ ÈÎg­p7pІnS ×UÉ î5€¦Ø ½Š¦ãÌf¥¾ÍÆL¨ò>šðâÊÃ"ó+@aØçðÈßz{ÿMQ_ÝH(ÓË£ïÐ0‡Àp¬49 \a{îô¡k=ÔM<È:pq~GÃeþê2E Ü3èâ¶2%0Ç Ô1«)×ÂW–„¬Eêä–‘¹„54׎‹0aÖfí…{¦À‘ðTÒ—÷Op~ X‹Øq˜­^]«ÃVæ’A/á:9üðÌ;?ÌòÕ§å¶EQ¬@ n–TM&¦­eYaj€•0rÖ,iÙM/%;ÅÑ"â´#ããÑšë„­ÑÔÓPhô”©ULqÁÅyk;ñ6\ŽyœÅ[8Q.ްw3œáhˆþÅEÂBÛ•+Ü.+* 5q%&r@ÜfG=vÀ'ûÈø‹\´–:ƒR€Ë«ðJ@æÈëq‘ÕYäÈžÁÐùÌ KA@üpÛ²-¦oû|Ð1BÁÄýsXb¥êÄœÀœÅ`OÓO…'Îy/8Ÿ²!¬ª™?rÅ@3~ÖlLSNì=šÄÍ-ô¸²Q•ù R3M>/ŠX»ù@¬ù”Щ¾E¶Åû»æzÎ;u+ݢ뼣v+°î†›÷X_¦Éþ|?+}*¸|½x-²AQäû¡ß€´qw©ö2êˆÜšfžá>ÁóÅ£B[L©<Ï<ŒçDûg&5¹dL3lɧùܱ(N ¬aÃ(ŠZ¢^s`k ±¶yéÍMsS~ÒñÖáÖW˜;è· Ž§9Ññ;ü!»¯'}¥ã>´ÒíqxÍùšoùûŒ¡,(l…4¯Æöh=<ƒE‹R²¦dŒsZYÏ , ¤xÞ.6ZW:á±²Eè4º*qÐÙ|E§êJK¡tV-ɱ£=†L*ë¡•üB'– ·˜¼òWŽI64·dázih'aæ¶—BMž”&F¶œÒQ\Fy¨EY - >@!m+½zü!?Æ~èIá˜W‚] £MW_ã¿@IgFÂ5î~Ž†Ò H3KÁËX0ç7NB¤îPŸ–ñòÈ#¯«Ç«.õL*µ®ÍQ ‘SÕëã²a¼7ÁI¦È­6²\yºìüœp…¦éðÈZXƒxó@–cºN‘>Gcßp5êc’¥_2¤ÞM­ñG{ÅñRÙ¾¶ ðÕíéacÅQ NHr¼8K(é«pùšlî—x(/zÀþX̨½Ð†¡)§1þ,«KKq;ù÷RÌ ´ñ.„º ÈŠ,Ý$àT Œi„9³ó EeB#æ6ïFbEôr§¦Ñ°/fÕ§Ý#ÄmÚa:)à ‘¿¸"ÜNÿøv°yNÖa P¿Ac4l {º3Q¬ôÆ(àŒÝë3÷s cVK7Áer¢3¯v8B¿@›xo„K˜¡”´îAÄã΂à4©Ýž€Ÿ~§"~mRƒMå ã’‰ƒ!%ÌöÞ^m@Íc*d‡†ÎóOcò<šç¼ôêïRÿäßuê'®î«¦9+Útr6(¼BÆbD#K‹÷Üa€¨of$š 5Û?íÿÀÙS ú ¡¹eÁ9›^öYçÉ£óï~8®óëxã^­·6è$¨¤2Ñ nc;õ›¸LoýH¾ŠýB¥9×ÿÏöË󊟧 ÁýÑü5~‰.Fe„—ŸV['<”û˜C"À÷ùûüÏ·oŒ5Ô ÐR›>ÿYÛjÅ|{ø2ŠîfòU¸*Îܲ>ÕÆï—Yæc€î¦)D÷ö†ŒÒîPUÛèÛ³ -†ì…ý¿Ù9Òñ<…éajúT#Ž¢Ÿí£u Šà^ˆ¥&ˆ&ÙwÝò‰–”Ò#ÑW 8…å¸OÏ_YѦS E•ÂQúzòŽfÕ@Ú74÷&Û¸-Q{Þßïæ}Â%NFn“,Ӣޜ֦>âTV“m7ùM.¶¾Eø6 ËÝéΊ&%ÛLhÜc*sÆAëûªZJ`3PéØ W«Ñœk½¬»­š4’A62R|œòj‰ø-LJ«~Žø¸ûüêØIïÌiü\ÊA`¯Yê X4·M·’b+R]„–ÚÇ¡w¢KjiÈÔ3Vo™փl×7¯ðÁs\¸¨º0;ÈD צ€~3-3õç®ù¨ôäÑœ,›5*Iþ„KHg•a®+ÃfÀ8ƒp/·ƒ—£ƒª'5@:cèyd•mŸ 3—-0È5G¶—+ÚjrŠ~*¼—ÀýQ¸*öAË?Ÿªa¨k¹®Ò¯ãSÃëaž½C}#´ ¼IœØÔ)1 »²CëÛÌ}qxkG؇yäOôä–Éî*óošÏÚ•‘ÎWrÀ ËËbÚ·ÙŽK{;Þ÷àQåá¶1ú:¥­óž ×¹ÐËl/jÝ£@/N{ñ#ÚÈcÒvå&°Œè—£Þåø6*øå?ã+1ØÅ̉‡¾E‚E†) Ÿ1dƒIowä¾÷Óx–º¯;!B¥G ‡/ Z[ gn¨Øªø:æ±Oœ±”j|Íð$j„D í.aLg UékŸ “B š3’¥Bÿ.bqÔ¢…i×(¦Ç»J+ˆLÍKÒ]LMÁhÁÇÎAìÉ1¬ ‘còD `ÑUk@ß­þ¤tëûU‹²ü ÏdР‘³Þ€¢ïö’FùL5æ†ÞKÉ7t5ZÆ ö ÛøÇˆþpdËÉïŸÈŠÜårœ’6}1Þ$½á©SÍkðN V]A ‘A$:2Ô45À ¯|«»ç£ ¢‡X+FB ©æq<¼œ–s[~‹Ú¹d%\À:ãqtbl“8<@_wÉk€“Ò%]¾\ À™Þ¬TOèoÎ.ADãŸÜ&U^R‘€ýðRuð @YjsÁcâØôa»C˜Z€Ò«ÆÕ#Ъ¦qTš¥(:"*dñs‡-¨Ë`~T3òHß³Øá @X àœ>®HŽÛ'T ºìÄàpØ]J“eDkÇÖ5²sR³Ràð„ùÑ2±ù½ ù@¨&¡áNq¤2vCÑü6˜„3 ÀgègQ>2‘\Þ„§âSØl,Åb3 Êù£dioB ެæ%!dF‰Ñd•!]`„‡RÑ›øEóªÎRørT†Z° ^+àp‚ƒ(Ã:+mwÿ1Ó3@4‡…°nö‚Af:ýö4©ÍÓIÌ{i e»áåZ`Ã?ŠnÂßÖ#PÙŸ ,T!Uû‰ g° àc¶lT¸ÜIaíKÖq°Rƒ´ƒy™¬ø –-d`å ¯¿Ü4dÞÈ=™€qbY 'IN x…õm!Ë MAâ¼H5rO͈²<Á‚¨?²²ÖŠúFÕžN0I7JYiŽS0$ÿ¢^4WâÑ9@'Ú×K$žL4»Ïü ;Hˆ¹‚Ž´GìJäÈÐâ†qo~r èo„V$nÚÝu‚‹¿^LZ‰%†ãNS4ƒ5›È¿ì“nÙ°° G;Ÿàh$bÑØ8†žÒXÔ¶ºdxíW:Ší¶ÚǺL 1£…DQ™è'%VÙH }>ÖÛŸú8³§"V`_IgRie[,ª`x 1ÒN©‰§¶A‰ò3ÐÂg ŒõÎ~±úgº’øLÊÐ0+ó¹Z«†Kœ Õ?K6ÀÈ¿¬©¼ZŒùŠÁïèÕlxóy<qÈÓ\¼S)ÞÆt´1ÆäÍàTÖý0GXq„ Œé* ,éz¸Úó!0d@3‰o°¼4W¬1ybpŸñÜôF‚h¨céƒ!’ +ߟg0Bª.w†Ì?²ÙƒhÖC‘…7“Q]D òëÈKß3V 5Xíòz7='èäÑ#&.…=´Y1òÓ€%Ã’q‚r‹õ:¹3[b,W¨›ª‡Âëih¦°Ä¶}q—±±<ý~½ÚCøú'±DˆºÒv^W4˜EÇwçóéê4 ¿85Qµe•Ä–:5©{¢—›5‘ȃÃݵVÜÞ%4©ÍŠÍ/O¸º-¾Šóµ;¤Ëazææ% Ö ¾ÓI¶†À2$ÝK<4/l; =-Zàf»Ñíò9„¡0¹¦ ñ,ü‚J÷’qð÷Gå³n>„Q†d4töéˆÛÇþ’Š, –Ï’±¸ÅñÊq-"…ôŸð8£5†µƒŠz=P?ñWãý Ïú”X6ñ®uïƒÂÂÒÉÖ·à‰ÜãAÖãס&£Ž\Óº»ÍLƽž@¿ðËJ:ª‘çP­rÔk>X0}<“ò=1¾éÄú{ñ~¥Æ>*i¹: Ñ´¨>»–(»\ïN^iÀ¨=ƒ¸¨¨L#P[vÞI &ÖHX€‹ŽáG;Õ hIøMy†•ãav…žYy)Ì®¶4ÿ‰§9*Ý.‡òpò5µý Dʧ7t%œO¢ÒØÉ˜™šyÀÁ—G¢hj¥-Ù#T«£~aÑÌq D/Zñ\l•r6S“¤P”Îu M–G¼é×·çð[º<ª g>:ù]Ðwy ¹$²¬¼¯B5dJõG‰~G_¶T“'Õ§ô ÁãÊ¥æîîK{â”ï4‚E’khW4gFC»fÛm¤Mr;’âh=ah;seÒŽ<Ÿ[=•ü*´£p›!óƒJ3”OÌÊè‘$–FqÉI7îÚ€F¨?¶ß}÷-øVÔ»¼†ÑjýþZìÁ4jß“þj±é†R`*4¢<¸PrÒ’´ùQ_ÁWÃÞýw.³ž‹~ÝçºÞq/zäÁbâ…ÊÞRzø÷Éù<˜Üœ]ò‡î<2ÔÛ ;Ÿ•®›‚h»ôÓáp/¥?åäÏwwã¤DÕR¡PÇR{IlþkåýÁ}Zm7­W×Ö±éü˜·ÿ"é‹{ö,jSÓwϾ° ÷6 ò,öÿù ÛýWçL m|äA!DØ•Oò‘8sbà°ø¾<ªû„õ?õnzCdZþµß#Dnr­n«-}QÜwÎþo…0XoÒÕêþ,¯_Óùœv4§óg‚d +ö=þ”0žKѧ“JsÚ>K@H²‰¿¯Y™ÕcL7@Ô$˜.wæûK@;üÚðáÃÕZl4AX¬õïµ»P˜l1R{ªߌT Û5 wôÒ |ñC½BÓ˜¸ ô¢Q¨1,±eØÀnÖizÁI˜+V52õ%œæy‰¯× X úDQâïçØÁÓf«Ô9ÿö& 3pÊå!YàÂ8èU¼vVÆ€ nwëÚÓ°ú›´6kjKZÆj/T(ü=DhÇãAq“Î@T-ß©±[°ki—ˆˆàh±Æ¼¤ˆ„¦° ¨7R¬µŸb\9í9xvõ<„ä¨A®Írgþد®+NÛæU(… #š¦{% ýV¤0.*’ƒÆ \»ÖïÔMHpUXÈ™³6­mTøŸÚ n‡9½u©ù³¾ú’ÚæÚ~þ*úƳZªàþæÍ»OñLN€3¦ŸÕbëQpV0»ÜØÓý©>îVêºU¯Õ-ê·š¨y“K¸‡Þû»»}¶ú”L„Õhki|*˜[ñ[ðâZƽ`¸ _·lÞbûŒë?ð.fí€máOoh.~n¢§òŽÔÌPÏ9dß Ðèñ{O73_·nî[ŽEy2¦óÊ7ã)S>çwm>X°Á·E¶ ¢ð½H2­œS‚Ó'‡1˜¶<¡•¸ÓÒOàÄ:^iQ.hì“.Ïâ¹ ÝÇÑ]‘õ ÆüdªMnÔù‡€ö© àÛš`ýEmÖÈc š!b¤lLmÍ1>nÕ6µaèÄ••ñÌ>”†ª]™†yL²â6òìqÔ]ß¿@4r½_®c5 <˜g¦äg(Fÿ*82myMûÔW£¹ÆÈÁ8ñ+™ä°áò WʈìäxYPc\ r˜õ8ñµ65°Aßä ŽZ æ*…ç@fGN¶KŠËáÊõýÌÜEøë5Á_˜³$yÄázÒ³‰|à¿ÀR:-‡‘lT€%'µÌB-å8Æœ…#F¹_w7–U KË;~ᣤҰ¯(G(ß¿s›Z³=P 5d=VÑÞx1  L÷8Õ–£ÃKcˆü(hÕyú@îG‘<&*¢Fþà¬Úš 2G¬Ò?ó!ò檄錗¥O*‡aUܘàgÛãà˜Ü†&6˜Ò@ªDzR˜¬ª$%é§¹Õ7Ë1ƒg 0¬_¯Ðx™õ9†zL÷=l™K-'ìFÀìžBOyðCKˆš”ØõU·+D$`‚NT’ÂáßwPŠ”®U¨dþ|Rø)˜’jRÅX‚ÜŠ”.>vQœhre:ñ./ùWYš½¦_ I»ñ)¸pe"Äßbþ¤°¼…gNž“=ðµ HW™àtåHiwËŒSê„’ «'LbS¦d§(QE8{®ù©Ôš|ñ.Cï#.8úø ­4ÎÃ0Ês¬L~°¼Ó+ƒ_Y¢c„4‹ïÖŠ‡c¸Fpzî<ÌÔjÖ™C–,®ö„AÁ’?ª®a¸}ÞŸ}`·RW²(gºˆ æ6g%€¤Çî N¦€Ô3÷´§Íü/þ[dJÒЭ ¸Jmïò¸0ciâMlP¦Ž¦ÖYÈé g‡ëƒ¾H¯\ iÜS›‡ô² àăz(S€N]D§ù@6hxp@#ʇf@*3k]Ü&€ ÓŸÀ: C‰F˜ÃàVF(ƒ&Aâö»€qw X%,»ö½g×î”<¶-òú²Ú„Dù¾à§¹XÝAlqq€·–Šm7kruGÞ«kÚãºíNæ¦â:×”%}œ‡sûžÚv.Mr¶Ü‰Õ¼FÄà˜R#moC’à(¨À³!ËqDòìú‰ã'ô硽¦óNvk’Y[î÷ÿ˜òÆT ÐR­n©pl£jç#ëIJ f’ÛA hÖø4V`¾íÕhƒy2°5¦™Ù,£õØ#5¡|o(Ù#¢PvÌöÓL¿ ifjFO>Jhr…‰”?ê?ö³ò¬eá邼5•ð#ê2Øüÿ§µ®î¶Ž³uj=uRw½È;¨{ ê³hC \ñéË»ëNmÔæŠã.ÈkļÇy½ˆ [Éð>‰¬Þ9Ð 1IŽ<¡Ó8dç‡Ë¼Ü'“õ\P‹VË®æx˜,‡Öá-‰ÙI#aµŒõ) Û•z½ôò1ýhnœhG9 Gœ †íÀ÷"ñKyPÿ9Íû¨-ÙW;cíôAš Y-Ì×àl®žÚÆd¢*Š+”päÐñ ßÚ²¾‚úÎ{û œŒ5‰ oFN«0=¸²Øí5û»NÿñÐHÀd΀¸·KŠÖ‘ø%¿£+y2|*Q¸§ÛM/ÆHëœE /„#=D¡3hØ;MIlEn_–>×]…¾ÙYi« n€7z”[ŽŽ¢ šé|'áLJ2‰2—©’p`Mî³Ê*#Z‹ ¤ð cÂ>@E,Ð[¥b1}ÐUßBj B‡‰òVÐÀ):š‡ÈÈBÛÆ›®5` „þ)LÕ°°0&,ÚÊêf{6Ýe›½â`¦;Úû$à Ni&Œ†ŸÊK1íd".tlo¥ýñî5å’hãÃV$>ü&(Ó~"…ã({ÈÜ À¦½hAõµbˆëèÅhlD¹``øÃ_w‘KŸËðÎ tœÛ8¿çï÷E&é±J ’û¦º P­…![”ϼö i†kdj~5br‚IwPs‘`¯„}á\›Üu_°ö°Á-²ì-BbÉj¸Nzc 1H”ÇÍß$?zeI;¹ÝT d1å8ïO<"9š‡yúk3üPo $ÞãCµÂƒÌ‘6QZýÊ^\6îlbH ·*}ÝH”á£Öà îzvô«@ƒÄ˜¾VšDŽd$ߘs!~I}b` €,Àý¯ â ç+,‡€£Ïòeµ¨lFkÒ¹,r) ¾ŸËrp $0 û þ{ºÙz‘0K$X˜¡Å–6DL”¨³V•ƒh¢ž7í –~Z †ñ b×ÁÖÆ¶—{`f#”jp8 ß$é%zÖJ’…[ ýs >ô6+˜öð¶·×ç%Ü' [ Pd~¦ W»¥-ô!¹«ÿ›2&hkëfjYzû%©’ˆŽÒ2"uaÛÚæfOûLf¢¸6€YS&C Š/—ˆAg_´—µæ%£äG´˜Aî ~Iûvˆ!A4ÓyMú’©22Ÿ'ÏìhR/ŽFúÌe YÄÝ‘¾Ò÷Ä›–¬(· T±t2³÷øÂ9&qðmYüPýOÏ1bÀT£{+Oh=ÄlìÁ÷4vÆÏˆ-SÉpP_±»Œ¾†Ò‘ÈO´ÖËÏn`V›Cð*"×)ÔõM"¢`x³ÇGó:DÆ6Ÿ7V3+ü¢}0­ÈÜC´›ŸøMÉ`}„‰ ÄlUæ{‚E ?<õüÝ˽ÇÏôÓPvX !b0 Œ‹P]½# >J¢ü¼¹PrJævfŠd™ äÿÈuRï쌩‡Ë@÷Â$¿Uÿ¥Nôý.8lòÀ‚ÐPhàÜ¥ÛÌÖÀŠYÖ‚– GŸŽ;ˆÆ<Ñ[1yMù9w-Ð`QNVå7úC,öH_âôˆIÉDÏ̈¿P ×F ƒí¿oÖ “ÑÔm€$žg—=<àµ6„rÕ|±-L÷¹ÖÙccò÷ëŒW\]BQé°ö$Œ@ã̃bt\*ºM®tÞ@U™6•áà´Îý‘DàÑ~oøo§ Ã|ÃÜ µt1 ¢d¿*ëÜ©­ÀòA ®ÓK¥Øj+Üý=4]<¢¼ù0Ö©¬”iú- ‹¥‘xñC‹‡ÏOHÃ^4ÈIëy±]dŒ-ÙÔ˨'êÄòÜ ÒMxÆÀ9 _56ƒ$x6¾˜>Å7HXB¶aõcö†ö„¨9FçuúCO?æ¶XOÑï¬`À]Eê-t+»8˜©H|ÿywž£BÜ 3ªQ§&LÞÇå¤rç¾yȶ8<„O)Y 6¤Lç©ów køVþJîÀ–ö„tí2™Øå´¾|HZ†µ· ª895—€e€CfêjóøÌÍ) '¸‘bîíþ3MÒM7ÀM—s q÷Ö».e¾+¹xI)–¸ŠøÙ‘Èäÿ&´œ¥œ'oXrü‹7CòÏÈÊ xN}/HÈžàChÈvâ²µŠ÷‚ß‘çÍ,MþÎsfÚ°ÑcHæËÄ·z"»У¸õZ.“¼pVû«Xßßñ:8UTÍjÖ"•ç M3<1TÎ{ìÀ¬-žMd鸽K¾yd¬U·ÁŽP%«­¶8MgÊ@[ˆ>Y!SC»% eÀÊѯn¡o]ZÊKA–òÕöHŸ‰¹ç+ûþ‰ ÿ_ôÊ;l‘”òyÓ“Õ=bu¹§ŒÄ…!èW”N\Î ËÏl,sþJd»…³æàÑéÁa …2#ì*ÖuÖÜ:š>yé Ó¢Ihû´Òf”2Ó>>rB‹Âä—ƒo+q`ú nb´5:i"%@û£š³†Í4þÊI§MR†Æ£îŸVYk ¹°â‚“DÎ`ƒÌDÓ,úeä²Sß­:RCÚ~„â‹j…X,iˆeÅ­ó ¼¿5»¥p6s4¼¤åÖÝhŠ[§Øk½‚à£ë…ÁP$Ö\RDû’†<â~5¸$iÃa{^¬ìPÝjB>šÖ•­oÑÏ "Œ½ÜßàçiBÒMûÀ€~ ?ö&À˜ ¾; ¿þ|ßùùȲ•‘¹úr{S[ÿ²è}`š0¡'¡ò~±­ ë2vÙ #bº¼œÃÞ…!MáØ•ç2@tÀGJ¸k›º û*îjà;ýL$ B( x©|nSaKBMkÁ2~F& ïyðk™ZojûËÒC]ýT 2B‘1z¼<(z%[óñTfú¥ Ζ'ÁšÙ¢Øi ®ŽX¥M…—HåÅZZϬ5.…ü4„ ãp§OÈxiGIç=.AÁ ¢±mAÿR†!ãÓI•.b à[ghVÞØ3“ã¿èÕËA;ܬ ™’¢q.­@n"§ RcD·H†¨”x=¹QXwC±!’´Ø¶ 8§˜õ¨ÀŽÆðZ¸ÏŒôŽaEr2ÇëJ»²"—ɰꌦY1l÷þš +ðqï(žg^:Ü*u°Áù·%…ÂäÑ[‡ ì‚ Êa{})ÿ-‡$6H¼TŽ v9¸œJX?õûcƒ0S~ÚUHŒº%oôúvcS„Ê œ4§!yŽÖ1›ØJÀ¤_á™DåuíõæZ-9>:~jh  µ%¶øÇeËO“†&)p˜@ÚƒcŠúi1^ÿƒ*4Ôf{<™§Ð¹ƒ²#$œ÷š}öž¬í œÓÄ:¬vêÄ£AÜ›&ú~ Áê0?eµÿ¿[CZIž§§Rî=L ®ÕúÆ=Íê§È?ˆƒ–P^Èê ò¤1¯…µÿ? 'ŒJäIü-tÇÌÌŸÃq1“-Q”ëÒ>A(:OÃ%l1\…¤±œˆÊÜȽòˆ¾AADY½@³Øu†×4!RBbÀü+ô}&>zkŸâ<ö=m'­ê8ðâü6y5dIŠÖÊŠÐ/œ±2qEDgŽÖàG+àš )Ö¯ÖÂ2LŽ{Ó4Œ‰·ÖëP+~úQõ…’™8|±Êpa «yqæ›C<Pj”=Ž,š¢"YzG$ɸhúìq36ÔõLrÎuçMpÛ÷CDZaBK 6^ó‰ä/àoÜð8¾ýÙ´lNEî(Á¢ÛpSl” Üj™Œû!„nE!©2’=€(Â%ph“KÔðbÔz Md2ißmºcŒÕ3Ðþ¶Ð€° Dä‡Jaó¢3/"Ö cBf“PËS<B {Çù-1QY¼³Æ!rêé.`"_wôöû±ÄñÏÿn,Cë‰ñÑ 4—ê´A¤%†›Ëá²ó~ñ"a$âÈhÁPÑ%¸“F¶.ÊVy)W[Æ9Ð_{8ÆxïƒÍÈ&òîVr•IMY8T‘ÙÎŒqp˜©frÛŒ%ÚbVB™ 6¾‡ÙtécÉoÔõuÁ›÷¥õàÊ $¿Rö à­ Ñv«‰#aú 4þ*±ÄVDU1“ðõZy[éçŸrÄU³mmÿ´+HÛýÜV A•ºq 9³cà—‡ëÊ„àæö´| ©vYÚnvØŠ ½€‡ˆeø«H¤) åjKíÜKúïõe¹ƒ ÐSqÒ)JùNaù:1œ'ë=†‘l)«ûèVPŠÅóÑ\5ÍîÆÁ¤#34盕÷Û8ï&l–xÞ‹èù¸H-°ÜÕžߨ17Šìj°«˜ÛRþQ }6'–dÅ[&‘éºyûMFÁô„¥²2˜j®“o°Òot¼e=Ûâty{I©í-²->ƒ¬–CY>)Õ[\”a 3î aBdn¶/qaVrÞ¾IŒ!ù‘Õó`tÉu;ؕŊœš fQ㛽øa/ª™Ûi =ûžÐ¾Ñ¥½÷'AQô ùB båC¼¥^“YƵü:R?BoÐøÚ@­Ï›ñ³&\)¬N½|êÀ½ËÄä¿ÄìÉ uµ§dK”ÀÔ m@w=%3d®Å|E® †ŸoÃÞQˆsÿ ˜<¾E¤Ò»©+ PZ¤à¨Æ–:¥Î±Ø˜O¤Šôq¢†:‘™GÐ÷†°FWÉ0Æ1L~òëÔ’è¡3?v ˜…© “ÚóÄ@¦zÂl™¥Cþd°±“M0 u ¢ëQ ù$'ø·ãÜh,µôKÉ[ôØh.ªÁtâ70rÉz"ÃÂêV¬;¨uHuéT†H1«×%·,wþŸåcK;IÙmAŒl\c ê‹™¥WsjWé7b)Kõè–2iԵŀ¯&„úªILñ©Óµ« 1–ò2ÿP|1–dK'äµÍPª©‚¸dJ­ ä:H æÜÓ[MÆrBç¯>ÃÍìòeÃY²v˜ÈŽ×Þ¦ËUBÿ6—|‚ô·è²¾M¹:çè%]ë~$8P’Ï´ìT/„h íí‹·)Jù«Á9øÃJOf v'p£ñ¬X+Æ€³‹I$+ÆåÐJpÅñרÜêÜ)~ò¯®à•ÃVä{`ÀŒ@ÀH9ò¯J„ŸDRн²bž( áÅ,qŒ¡»3¿c髺®˜1¼‘žhÝ ¼AH´“Ë<÷¬ª*5Áœºg¨Gÿ´ÙçýD¤yR6n'kÙxa1‰:ƒEE»)y7¹ý½ºã³×xðQd‘·pÍaRÀq·¹tŽ2<@)Ñõqׯ>@¹_½“_=FˆP碫ï»öÑ2¥áŒÍî÷wËîþøŠ×þ«ÿ'w}û»îñ8Gêdßñï‘.û¹s÷s!µ"‘//ãÞýȆÄÿÓ#t€S¢äïˆp°Õ¡Ù”?8ÇK»½¡qåø§w**ᅦÈ]ª©³ÞûÇ?ù¤õõïw¿. U%>¾úù:çq9Ϊ6u¿Ü«×Zñ?÷¶Ò‰åõœ2¦-ÿ]N«Yƪšh¼]æÅRïºÃ‘‚šËÓ®„ª}F³úĻ֘*‹ÁÝ(æóœ <.Õo.ż+—WOê½7_½#øì½Ät0Am7›ãìß4ûß7wQ<ÔØ1²úüºª“ÛÛõ'œÆ40 w}i×ÿü0ü߃ 10ýTPc]îâßœDÕo¼Öªt”w+±¬’qmï g ëûnò c \oq¤8¾$£Áó êþ>Xú(ÅX1MO}9cþ¶ètª# *"UcÈå|uRêÞÜ5@IÎÂÅ¢ð¯v44`bm_åü"Wêß5®½p^Aa[`¶ Á•ùà ý„#¬éÆàek,צóDüTq*‹2‹5âÎÊ4„IÛ*)Z̺2•„º¢ÍÝzâ“ÎO ¾Q³þચ0uà2ü¾W®—§TH<GI̯JäRwù (K„Z€H X‹Ç§†Cš9ó ¶býì ÙWÎ-Æe À”´‡K3xªÉIÀ´Và~: MÐcj4 A ÔÁ„_ë›bsY=>@`†÷röè]øê´(½NB vƒ¶Â2²úñ?-aõнôXÍ4Ðze^/$t¢”¹¹{a„V A¬3¦yÀÃ>ÑŠºAÆs7"x^½m¤$È>%ÿÁÀ€mˆ#ŒCØ€ÀÛaéñ ÌÓÊ{æJ0¬bÈtÌ•’ž†*¨„|Ö Â$ŒÍà )צ·7Э™I~ÖÚaã0AçŸÊÊkc*à«0-B)¨og¨mMÐàÄ4"ŽïëMELáÏn¶ð;¥f¶„ü˜”t›Æÿ¡ækœÔäL]õ¼˜›QO›f'ѳ±K]2÷éÔÔÔ³"‰ÂEŸsas²n?¼NcÓõ.ÜJË…òû¼œdªS’ž.¦C$¹q ³ÔGm¿œìjNìB¦FJµ0˜ÞÜ= ݼ‰PåžÅÀP´¸w¹×ÿõúï97•¬ücyS'°ë§")¹±Ü—¤Ë쀵µÙ^w‹ý^ð@_gR ô„aGQ½PÈŽ‡ß ¬Sôœd{5oâüŸ"g7XÌÜûQÍ‹‡"á=c xH2«š€ÿè PäIÊœ› üû†ÈÚö[Ì-ñz­6ƒ±}ž®z/yéÈÜK•®hi‘Ÿãîbú9¾]-©¢é‰%´é ‚”áb•±ËèìK|ð߈øF5 `¼YGy8Ót¿÷Kx”Hï÷7d̺¥‘…¢0P‡ÛŬøäP&9¥ y{ƒ,pi6 ‡µK0¸«»†ÐÝÌ{£¯Çî Æ“žA™G 5›f*‹ 7-˜ªc]]÷;g™³äN«Ö:j§-Í ï“†Sa™¬†¯“„œŒÏ=Wè«%[‘—ŒíÇÂHdJàƒŸÈ é|?è^uLJ[fª•–[Rr C]t5 R$L~ýS¦B0Þ§cÞŒl–=4Ž@×6ŽsÈR³ ÁÜê#KsÙáœUcaËííyŸßtMæGü@eTý;‡­‰ÒF"~Êó³™àγñ 8Ã˼ÀÁpŒ©&ˆ3 Ž®˜¬ÁwÑXhH \ÊKHÈ ‹@×5½;`OOcn ±þsOÃh\uì\B·¤CæKoš+¾(k¥6i›w¡J˜?ÄÉ:Cȃžd'÷Ñ‘9 Bwˆ$¦G{p±kûêÅåÉÉILÆ3Q?lLí|¦·¾V·-¬29=V;èÚ††'ÓJ±ªÙ1\h>S¾€aÙD,†S“@µ¶9dJ&äVE¡ª°œ€:±U!êV~ž¼Ê…BRò9ð†ÉÌ2ç‡mëvm·K1YœÜ—8=³K»8' “%¼*a žÛ*Jìt À™I §ß/>‡óvPdjŽÇ)?¤óІ r¯RA+oÝZÑj†;sÚfòÃà&’ä.ð®í”=AF8½Y±ßsÆX.²|BZ#†ü_ O‰m—HÝ š jÕtk@ÿ‰O.þþ°¨$;8oU¼ %÷ò tW¢‹&sñ4J> aÍ%â‰ñ H«…°©ÒŽ?B:¡¨F—§Ð?åýè£é"Í4©R (Ò·£ÉipR=I¡‚X˜v[³Ó^—‘í}ÊSi ÌÔb$—Åô²'3xr·Çw ågÞ°G z]að/á½1¾¤.Çyƒ\;öFì­çMJW8ØŒ9\ŒYe? ¾Ý¨%Û[B¶^jñ¥&&³R«žI)œË•ÉÔÌìX*gtÀSü„vCkÛóX6° ¯Ï2 æµiÚY@pGÈ’3‰‡Æ€쿇è5Z£Kzø”X"ú0š¹?µòHˆ_ïøÂ<éh3œ‚‹E—þÿ!{ ЍÌô‹ؼР'¤˜dÌ } »™ÒžÐ羚N›Æ/Ž]¸—g”ØïŒÃB_ôƒüW)?ä¿Õ¡þÉg”ìÌÀFtEñ¦¹‡¹reËZ^¹¢Óé‰Èöë‘Ó¦O¯Ü¼®Šš¥ÍÌ´ƒGˆÌ¡žqà3 ¼¤0^nñµ`7dŠÌȲÄ„¤óó–µZëÄ™«j·–—5­¹¡?ä-lê=×Qðõê@ú¨záá‰Ûõyrìßê6‡†³ØÙŒËåª}v‡ݧuW[š«óZœí›y&Êi˜‰Ñ2Àˆ, õØXŠPzæøRçÍ®wŸ¥;qyŽÓË!U{fïBÆÌ²æe0MXnW'lÇq6[yù¢í3“RÎ$Ѥiô¿ÅøE'M_…8û2”(d@ çzÅ?ÃÌV—†¢!Õ‡Ä ò#äëÏ—Ö¶ÔFL/ÒÅ]VÍ‘fIô¬œó`&_8ú@ G͉á׃ê1¯<Êy6¿ü*ÄEÉŠäM½4ÐR¾A»VògbXn.å»gÖVÙ*Ád·žÐÆÜ&|€e­fÖùÀÙáúrnH¢t´Û:EöLïp@—Õrªˆh[ß›½Mê%:Ú3ïrŒ4Å€P8iB;ÞáAÒG*-5‹RE&$„Ð +¢<Æ#˜ŒzqQ(·2 ãö醋) ïà5‰öÔYY¶2²¼Y—þݽ‰æEîG=ãÚˆL(µýTž¶‰ Ôa¦qíºˆJ<ÑÑ Sø¦†»„\ñŠy]Ýö‰$Ö"¤êÀ¢jpOfÁ[ÛÖÄךLûpQã”éb±77l¸gùxlFV7ÿ€1Ôg‡råo¥˜ÛÜý¬ba7]˜ç– RåŸÍ*º9p3+YG®,pNÓ…IDÚÆ–Õxœ8ü3­þ‚(ôU©x”„+Òh¾îÜ\ªšÖ ¹ÀD\_ÝOÚø:,‰°ç,¯‡YÚH\ëCÿÆ,{¤2 ¨C0øà1 OØó²@ ~ v„ߨ˱\dW1äê—ŒJtØ”2_¨‹B0ëð¬FJ· }êôh˜Ž0Ó•_˜Æ` K^ü@qÅa POÃŽ:f]“à ÜaÈ€â*ßY†bGøöO°wöè?KD0òû¼œ]òmáÚÓŸŒX ûª5fÅ—¼’Àrüh꤅…,`o¼Éñ_KÂ5EÿÂ̘'ÖðÀ÷L;é÷µÓyž¤±Ï3Y‡cðž!ü©€Mˆ eq‚k_’±põŸï/ë]-1­§;3|BÒØjÝ%Þ¨ìá?%´0ö­˜ øt8üIå¥ ÃáÎV¨ü;Âä˜ ¦‚3`yÞJØ(*\—TPß P{8`z?mAÖF™iÑd…ÚŠsÐÃxÈÓ¯üd¨‡ÆKú÷0È£¼a4ضtYêˆ}&­U¶Câ åU=lí'(Ãã휑Ãßð&턵J´¢\,Sà¤)ð &6€d Á®¬Éˆ%õ‚[ݲ? #d š+þyyÄí¡¼m(/I²>0ÿ:áó*­SÈÒµ»—JØú× Æ&c<¸î öÊY©¬á® gWÝM¬Ic¿ü|§8äÂFu™ÔÔ•±ä˜0‡¬êØ‚LB¸Y}-š°øßR‰C*o†}›ç“C~Û  šmméÎ%c-¨Éö c­2ô;¢› Ø ØVi0™Þi£õl¦nìÛ°ôà^F%=ÕV°¥*å9‚/Ö™gl!S¶ÓÒ$+ÖOö@ÛYcî?»'žk[ÚÎæj^{/^DXoìØ–-œ<\ý×ô‚ÉŠú§ºø ÕìšL¦J9“Ãéš{}™¼Dï– kŽÎ’û áYÁt3ˆ™ænÓÀäüc —´H-¡O˜Ëw=7ð5–%³{w/$HN6éªHÇž£y©gf>ňf//”òúŠöô\£°‹‡òC4€œšÚr+Ū© øÅç)-¬$GÓX[ýå¯ã¥"ï­ÚZÔ³Í6Øêœú¡½BA›8 ºáN!n¤;ýðD.z$Õ;ÑSjÜçxçŸÙ·¦+-+¹ã‡uY¨‚kk.àØ4ÕŸ‘ÚÒ`eWâ>å ‚Aô/AD VNà?7}é>E Çôv†h€R³*-ÿÉ˸á5ø¸Ðuñêïýî/7+}ùxGïGéRT}†Â/á‚ìiæ/%"¦ ·€ÜO<q¾o|ÿhâN©&”F8Z–_€!ÚkJàÄ5öùNˆÒÞK¿µ,p'5´($ó c°˜*®ÓÔ=ZGQfI^<ð¾g_7ôȉ:Éj‡ÍGä¿Ä*ßwv ú`CÅØó¤ªW7àÐW‡Ä¹1A8¦WãÞúÛ;Ò`H»“¥¦Q Õ£¢u .JénIBMž!¢NŽš£0õ;,g÷Å"ª´¤å9¦©áŠ ã‹ŸšÏ¥g<â0¾ó> à^U^|»¦&wH6/I‹üÓæ6±@Ç_‡[ZZ`Ë0 öݒݾ¬ HYÓQ¥¢~e„°%Ôc`’óç©Néä2pÜ&ÿÕ {æ$ÚÆwNš¤¥:PÈÕŒ—¼dû`$X蹄è½þÆÈä^àJÈãœx©Í\¯€Út±à—š6kÝô úÜ<Õ®þ½Íw™|Ó4g+é‘´l…÷ªzˆ[C¹»«m°+•¼¶žÈXßx G¸Éi‡Ø"=” ÷÷õ#Nû•›°_¤ÄMÿ£ šyt9×÷:O¢FÐÜÎTI¦…3Ó¯Ø^w\5r@vs¡YwÝÿ÷;-nbÁ$ºÜŽMJ>èljËñé|Ä=`²pLÐwܵ4ø6è4µ\AKUVl1¾­”Äᯞì;°G6Ã^¶²{<ΣgØnŒ§ÙVѳ°-JœŽM%3u …É­ï(_~ˆŒFEd´aÏæ¡sZŠÂ½2oÓ@=V¸%|%0ÃòSê•ñ‘]—¤ô.|û34>~Å Gm`ÔŸ$¬/%Ø­`ºÿÐNÜ÷\8ÌEl-ÓÐA-#ÉÑ'æÈìmeL£4Ä#h¯ÏsäbØÍÐt×ìhôòöxMŒˆ¶ñgÀÐ ,Äç3¦HÉz G†ƒ†þS(€:a'âp^<7‡âéÀR;¯ºK!TµÛÿùDL=wø š<'>Ps3ª“ý m«ÈÃøª¨–™ðÀÌØð…¦Oý>,DZj*NÏY­X“–ó°,%*+@tS–8¿ßÇæ*IÇGìX%v Ù¶8¡¿„¤˜óüý6T“ƒŸ)\¡bV¬ä àyƒ¦‹Bå;F4JF(äÖña ‚Îèùx>’<ûêF¨päW*à”¾èp©tçsþÖ$ÖÿÅÿš5>T2wI™…jªˆòS“» Üú4¶Œ‡&‡<¯È@à´ê8X^`Àã~Z5—³›@û¹ &—|X@FI7È¡ü4FèÍêoÑ•‚‹ùëÀ­QƒA¢à™&̠࢔•.?Ù‡ï/vZÏ®.†Ó¨ÍF:!Ó§¡«gx\ÿò õpÄ&>ÁM×-hW§H_ƒ^zJ‡ûÜŒÊy-(~‰Êi7ùð,+ÞR×ù ²4=ÜŸ*x›hë:¦bƒüÌ•GÈ’ÊB¦ôL“÷Õ&Ú#Ÿ­–/vÖ.)OZÈ‘ªÑ<2Z$G¥‰ð/iµ'ðÞüÅ0Ê(Éûã¥/vP i¦ÉyôžÊ¤´Ë ;5‡ ‹ƒ¢$:à  E‘2ζí±L›ØðíêØ—…ãg©ÿL«˜ÑyjÛ„›iàr³½ÌI_¶²DvÍOß¶°·Om#±ïŠ"¼GÀæäKy]sÏ‚]𣑠cúáJÝÑ4Ȇ¤N±Ü¶µºlŠ^ÀUd&‘‡ è½QÝà#©uréì0Àˆ ´¤A®ÄA3 'P¬Q³!Ȉ† Ìgo»…4‰¶ÆøV úŸ´#8r°öìKI>Ú³¨­/øŒL¤ÄXmí ÓfC*YÆe´Öy¶ËVõ‡&Ã'‘ÊR)ïþIn£Jõ<_ z[âžçñìi!q°o â…Ènm€Ý ¡|¸þÅ ëF+1l$î7›íŠw¯)›ÐÝPõ Œlq|ÖFgct„9Ç.þðeM¶é¶ß°J±3Ï«cÒ^]ýâNY¬:ŽÙ!pe$9ë˜ò îû÷I/M?§õYª*×ô½Þîã¥ÌÙd«5áoWøX”£–ØÚ¬Ï=ZøTd½Øn~ ( À s÷é# Q·áæ\ÕãëÐëž'¸eòÖ§Ì7h$ŒCªÎìû•G| ½DGènKÅ3u—g3ˆ- ÖŸ€îz¢ùxª ;’¨.fŸ8ŸqÚÆ™w'öiÁÍÒë>0c÷5Ô±l3‡#*ððb òÒ–#Ï-Œ"!þ©³§4ðÝF•¶ßÂDÌrü—ß“ékÿþx‰¾ýEë_2Æ[1|€¢¯õþ4¤¿èíóbÏÆU" ¤èD Yéÿø,éÓ#ðùÔÍ1'¦„˜A%.y½ò¤Þ¿¢*© Šç>)ì')Ds‰â¼Þ/B´Ù¬ˆÉ e•›“ñx†éiõŸŠ¡¯cSœ$«R~lˆ E%×zßÌÕ€ŽJmø1¹]Ç4š+Ü&P”ˆ…Y" €Ü矤p0¤wHì,OÆê,ªIÔoçS(­j·îx´ .2…ØY´è²BÍ7ñ! 5cž2jSÁ?o×+VÿãXs·¨ˆê§%˜©3©›Å‰=‰Aã-bÒ mœe¯Éh†í2:#ùA×pÁ˜v:ÑyÀ1­êÀ3DD±æ0$s ß 0¥UpÎ2¤q³Ó<^j­UÈøÑ#gá´ùbQ G&¥…È sr `×xÇ¡øÊÛß´“5V‡ÖÂÔÓxèL@;BÀÀh“'ù›pÕ‡…Ljƒö)õ¨Ø×¶ œqN?ñ1X/íþ ¸7DÜÔ‡´ê:ÔFnxÄøÆhqùÙdqþ Wµdcn‡.Ûƒîš×è”å÷x’dC\ðªÝÚ©q˜Ö&«ebÔçBW-£)x$«7¦¨Ä¡^qžÿÜÑRˆLÌDNÐ,\p2ǵÓ<´¶, ºæÁW¶/Ý Ø}M‡Æ»j_§ÆE‰AÕ!rŒ¹÷UvI‘ „VPÏÁ ¹k/æhº@~Å´K¡}§Î?e¢šs©öUhþ_»ïýRã:ˆrö4aæøìƒóS)箢JËŽƒd"ƒTä\v·Œ"ñ ঽx¦þÑÛî÷+Ï3]+^a¹êŒ£8åtcfúaguÆ€H{cUƒ>S×É_÷¼í§±:О‘ЧÿÙŽ¶ïT¸ÝK;¿%Òj`é„o(Ä=NËX8fÚ¼Ü ½ÀÍI}éëƒÇ¿ Ë01©?Åžâð1ƒ>öíŽN“ "#î9pèHÜ5Mó5=Íê|q 4šðMi¨[È=å'#è5C×T?N– =S4Ù¿ÌÝÐÍ]4ä¶â>Ü' ¬áWû‚¶i€±Ï†}6§b±âkÛêÒ)èq´;fCŠ”&ðjósþ•ŽÌßÎ…*Ø g!±Èè ·A¦m¨‘ Cy[À*ˆ- «øEÀ{{HxÛK¤qÐ1CCš;¡™lÁ—•gLvw6¾~úÖú,MÅüQÝÚ˜g¾ŒÍ Ó[S È„2CO d ¦ýuê»qÿ¿â}E'6î«Ö*¤ ç_¶Mµ ,£#¸Ð•@T¹Ø!ÆŸ¼8 +‘ÃZñ»!…[AóDB´ãÇ‹è¤Êžx§Jþã×K• Å´ø…âUKêOLý² {yMÜŸ²í®³Õ /+ûBì‡(e¶[úôqý”³ã0ÄR„¥ä#S˜50§ubVzÊ^mƒÝïgÆáý©b]QË”h ’g&@?«‚¶! î·3u(^²c=µÞËvìqÿZ»¡ òŒc32®Ãñþ”G°"š‘ïÓºkƒppé‡Û EÚþb˜7F@;ž´ÒÇV° K<ï‘Uvê'Ã˲{´?Êø&ýŸ.‡úâå ª ŒÀa4@¾FkkQ®õ¬ZÄ8• ãa[oµAâ‡8žðãá8pGzí(%€W&«wsµýtå?HæYè73 ëæ_õ¥Ä¬¢‚j¼Ä2Y"5I{³l9!ÀG"Xx—éûR`¶w°*/|õ‰Cªg‡ì…H𨡱/­‡ (Ó€@õƒ#œÐá™vMañÐzhîfx¤ Çþ ¼¤]|du_B¢ÖR4à£Ø. ¬ûïlH![†1|G¯˜òØRer{R÷Üà~„rSK Ë:2.‚Ëù¤1Ä’0Ø`fp”Ì„[(ÕøBšö¸z‘Äcw"Ãã3 ÖâMKÔdm}z!ê]¶·£¡-±jiN`! Œ$®"u\Ôø›fÛ'‡ò!õ=€ÕÈXofàîÙzÊÙÅ ѬvFµ›©æ#Þ'_ª$»aΗ}€ ¡ª>4æp5#ª7KØ~àʼV‚ú?*¹é=þvlOÀÃÈŒz æ·Nؾ9”}Œç~VƒÇøí+}c»ˆà´h+&0÷ï|»M¶L%hÂ>Œ=<@­õˆRËSvŠ}¦‡ü£¦w½ÐÙSd‹úÀM §Ð£G¯/x{TÀ)¹¬jåíHOÇ éå¤{íG¬˜O?m­ÿ­"ÈQìÃÔ2 ƒ{ì~£@0–’1²l2Ø-éûã¡cXãüA°Ão|`´~\ÿ'nRË%æ# <Í 6ñ„‹¶f‘ù¬f¥ÚIX<[5mé¼Úé“çþè¶med{š`GZ ¾”¢D=P´†[vüî4 À:áŠE$ô!¼ãâ÷G ¼‘é¹àÛcFIèǘ -[pà°×DúeŒŒèNø® 9@œ¾Ì‡Í=$k9œ·:Ô Wž™;«*éÕ&þdlí9ü¶…™hlÖÐ'™½Ýí+*™phžOW²ÒØAõ2ûh?íäFâ˜u£¡jÞÓó8Ô¦ª~-Ê77Û ãPÎ$,ež–Qæ†%a6$¤:®äw°þ9ˆ(ÿ õצ~Ñ·Dxpu}eýZ+ïó#`gæÃ=¦¿H==ö¦‡›u¦»br§‡ø:'<üé«B@`è™§½yp¹ÞŽQ"ÊGbëzû‘ˆÝPUöN×w„¦U‘ÛyDHÓáš‘ð,ŸWEe§fw‡}ÓfBR´L|OÑèÌc}žsÓ5=ÉLav%çÁ¹,‹=?Ý–ý¥í2öã‚íY ÷µ˜Î7»ºF5¨ÆSªõÅ##ýëMÖZ9²ø.U,g‚°•;qFÔ1ål÷Ü0 çØ·߯¼8)î ­` ÖûÜt^B‘ø)uüá'È(NÜb?òóŒÈ*¥C˜®Í.&wø/®¸&ÂÔ¸e&5Ÿ0WXÙ\¥ÙšÌäuDeºÅóG×DG¨ôXÿ–XcÒKß>¸“â–5 ·Œ±˜ 2Ðd„àéF³ŠÏ0omT+K Q_f߆à€Õ–ÿ‚BÅ*°S,#bXO‚L´âÈùdî°/ ìý îvRW mû ™u¢VäaÕ>rZÈ+x2}ãГp’ë‚Ç|Çà ^—w= þ}E =KGö‘ûoßþ7‰\§ñÑÞtÕÙ~¯”Ä®¸R¢€ÔDðhù¡Î…·“š¿Ü¯Écõ† ãÁ\ÊLn%xsñ Rº½ÿ›~ÅF þ zF$Mæáu}á $Ya<ŸC˜ÿoê¾@üüAì† îƒ¹Se°“„è‘4ÔŽîÎi9°yrÍ*½ ò/w¼ÊéÙ|“ë\ü?”ßns‚aÄʲc^lƒÕ_Þ…‚eSâ|Þ.[o‚†¬Tºš_øn¹",Ì€EÌH47 ®¡®‚Q> T;¥œ#þÀMBkkÖÊ<›Ã dœ  ?H yQo¼/ ÛíðÚ  ”À=`Ë †3þ+A[஀$ú"\| t¬i>Ö÷»OtKíÁ©ÞR(ÍjJÏyŒè†…áÞ®ô†9D¤Ž§¹=þ€RbÔá3ë˜PM@öûž º˜èuÂüLwáU‚˜ïÆÙðÐ/ºJüL€þï¸Ï“¸ˆR-Í]/”m®x@*’ÿñQU6F`ZP©’UIy¨‹sLj m`ê„ýR!g–oO»’c‡……µšL èÅàO/ ]‡Á¤i|UUJQÓ?Âgn@…N nÀ·ÊŒ,æ¼ÔvSï˜2PìUÛü35øòR(²ëWÿ;Ñv@nÈ‚°OaU5ËZ;†Ä)‚Pi­Àí]b“Àà׌¯­à2ÆKÇó_[˜P`={ËÅu] ËeºÀ˜Ë&_­È$´é_ ›Ç5ÝVH*¶Wøû ’žuÄÌóÖÌóúx¯á øL™Ø2U—Ì€tr)’Í¥4kpfpðò›Ùž,=9¯æ±¤3Õ¿bÙ‚«a»·´¡“…òø²Vž@Ü’ürñ8ÔpaÒ´œAöΓ©¨N¾œD7šV€‡<ˆ¬ló_Ï耜 ;ŸÇö™û˜*»šôm&Qòa%„‰–𙓙$‰{Q®ƒÓ!/O¶ êm1YåM=~ñ?‚ÿ: “ £©UŸü!º±F*Í)ÙæÿBC2ŠHÅÕ5y¬¬9ðãÆä£BV>Sû ‚LNûA ߯Z¯ëä~‹5 …ÿ\4Š¢Ò>æ›L^¬v¾[ì.ÜQ®žSý%Wkv\îÿw¿¼”mdä¥BáGÀ;åÚó3$Ôã4gÈü):OˆŠ=vÚðOÇ_Õ×·=Yb}õ…o¨Àe‹T=fkNÒaŠ‹ë”½O!·:»êsD6šbbLÝZN©KÙFÇœzá.¸^ᮇ„JÍ”t •q[ù¾…#Kv&¸6u“zOïø(—§éýÂ(—_ª‹6.Éâ`&Çû +Þl€£¶80]H¸¤Ò$ÿ¶˜pAL7ñ‰·eØE™±÷ÌMoƒX#0b=õ„ ŒŒ-ið>"Ó6Œ!•w¢½w_«# "ø]Wï‡'šåƒ¢±ôx/+&í« 1]à)ÌnÀ?•hI5 û&%„ Q=ëçMØ(—…|挙*A (×¢ëÏ£^ÿX-cƒ]}%øèDmH··_žŸ»‚ÑŒüƒäNx)ËùÀìúÃ`ù¶ÆøÈbo <Р‚´[žªgv ©]s”c Ý®T¦ìƒ’QpÒ¨vÑóÈEW§‰‹‹#31åhDøû§r¦åcNË€ ¯F/ÞÜŸ7ynŽdµÊ7 ¿$~þòüß*Ÿ (ߨé)ýErAýÕ‘› äf¤þõ´L}©î9/î/L˜>ئíú×°2Ï|5IÁ¯¿Í©%‰ÌJvÐøäÒÀÖBi“ ŸŽ‹µÒ÷ÃÜk2¢¿‘8¾“6 ÀÔ0ÜÀ‚ôyØÆžýè‡>5Yž4’n²_+ÚÿÊF FŠLA’‰nš¿6dêÐwµÕj˜Ž“÷aÉâê}¯‚±7ÎíËn¹„h `_¤ÄÌí ¨5ÈÖæg¥ª;ß°DOáYMê®÷€“ž¾1¿°èoõô»¢À{ò¯(;²ÉŽû¡ÌB¿w@Š…0¤[ºn˜Ð±¿Û•0wú´ ›U-†ïá—¢ç’×Å5fdt44€£‹¾Šö R©ÆA¶î4âS^=3W¥ÂÀ‘£–žé’[m ^gf^oOcí4?ù}`Õ1cSHC:(rš-Ãñ¸cyŠ{û¨?èI•"¹7pW6Jô$«ÄìÉQJT@k…û[Š/ï SÎ?SËÿ] †ƒB\Á ŽAÀofÈ{DÍŠ[6lŒq²@òsç(–ùp¯ÚÔÖ¢ ¶ÕH~¦üãYÙ|7n˜øu VüaÁ*L ÑÅÙ+cA.y?~B&îÈ`™©×ÙŠXqJ^4‰€˜!N¾«PA|Ùg6ð|ýdTÎ5ž;98¿ËÚJ͉tÍB¶KÔX¬o˜1Évß‚P6¢ÿù:}·Š,I¡¨ ,,‰Ñ u>\C.{±ð0·¶ªã¬ÿçÿˆÑSX ¤-\Éh#4 ³âü8ì‚íqV%zíþ¹Ø Ê’)‰€Emp&Š?ûIXGb$½–™ÿ¡å´kb[ÐTyü‰0àL-O±NOdªTB…×øx¤—#LœfÛ·ô¶ @Q3ÌÈ~ü¸ú~¬ãZ òlpÂIëG³ðeêØmü|¾hP¼ в 0«íZŽlR×8‹ÊÆ“Œ/ीݺsì¼¾}÷ÌÂ6¹ë?_ F›Êüí¯Ãû’'IugÞ¼nápC:"ŠHîx*įì´mˆ…vH9Q£iŸ ÿÉn݆àGÀ¿•ÔÖºŠðà Kûèl߈šÍRMÉ$x±å3Єù7ý"¤«sù?ŒÌÐó<ϲ0fIÎ8õWÈ(mé;RòŒje´ô5í’— neÂºÝÆA‘¤7¶JSzz¤F¶ÚJ"Cõ{µÎ“6¦á¥^š]%¹)·N¿I‘œÍ j‰ß¹íxVéU‹î‰6$´§ÊNyÙ75h=áððåÇÆ%ÊÎy«ã6˜6K‘–iª½jCTÔoÎ;|}øþ<êpõon_°]‹攀 ¢õZÑ7ù¹gÇ3a ñK5*ô®”Ó@ÏãÍ£Ã\¨pМ÷xKÒ_ß•/ÈÔa‰4Á·6$PݺX2÷/eãá°"" &îi©ÚÄ´clæ­Þ†!ù–Ïð·§QütŒê±ó”è¦ WXÓ°Ý„÷Ï¡eô”̼i0ؘ²ƒçÑfb$!ÙOqv*fëY†Vü¸Q äGNKš9³tæ8øZ”ö‡> 'á¨ß‘t-ñó] ¼ !è;ô@š‡NÍ©sl eØÒÕžäG"a¢T\<‰Dܼ_¯|d¾ˆ8‡øëƒxäl…[ a<¹‰|!-'ûá-$Üfœ€¬CÌ‚O‚÷A¡ž”!DÓ€U2“´ à‹R5=•–'æL„À؃H%Ç[|&'¥y?v!„0»ì—5`J¸‡Ø÷I3CD+)eixþÀÙxpO{[ès$Æj‚Ìcîi8_h$J¬!,„l8õØÇ—œ D™‰®ç,FóBK“œŠ†±ø áãðx¬Ü¡ð?›t„¾ËÖW-í’²§›¾õì~}ö°Ýí*@}§Ú—.€ ƒ¦Ç´èi|°7>pNÓࡌw ÜÅëªWcZ|Ü­BpÁˆ•GèšóUä aX]1…˜ Ïk 33% >ØpÊt³êU“ÂHJ›þ/Š|pÒ8[|†óˆºrRAÊPÎ6J<¢ §[„ézç„ÉpÕìF¥³†1ß0urÏ‚xFdÖ«N—ò°ŽX4[í}‚cûðša¶Ó?ê¤? Úñ¸§‡æ$bþO 0ìª$0P8~Ù…tΈ@€  Úš>ÞŠýæÚ©"Kq­ †šá’ s}:½B—’ô³%9¦¹yˆ®È®qSƒãòŽ¸ÌŸ¯M8д$’¾ÔTKƳ§þ‚@¼ýÛ!›}±cZ+M _ÿÎì±â:ñ c l†Jv4%^±p°Jr ØzÕÚ2"çVmm}å`vü—èˆ ’• <Æàÿ<Ü5ˆ²~ePzJš'Yå˜câÕ ï†í–f«OŸ¥Ú…[av–ØXzÅÊ`3g“µÿ8Ì2™ŸU™©ôþEéÃôïÏEž8Þ’¼¿•MÞß@Éz÷5 ·þ¼^àQÞŽ[Ïf&AÚãh…Ëà–†Õ©r»ˆ%D­ÿqÿÄk¯^œ—œÑÄl^ä•Ñ j‡ÁNÆÍ@þáD€7ÕZ=+ÖÕÿ‚"*µ™8õó ˆ'] "Å7—`{rÏ·‚BÛàr>º«O‹@6#$Œ=Y *à |F»hÖª³R¨Óˆ±þÐ’¾„•®ÓÙ0þ çLx{Ím‹ãð˜—(I³Ú‹4•‡ýVOÈO[?Ÿj‰š®#'f°Q­0F†©‡R§nŸk@éî4ýÿš«îë]ƒâ<s;O4ôæME—ÇØt–zisëÂ<šñrX2r(Ò¾ê%áМ/$õrõ2vqt׊?¤š5@·Ëò—µŠX·ßF5ÆQÎ_¸õª"R®Eáùšëšu½ÒòÆãP8Spwhûƒ2Ð$¬K„&a³3%ȉ‘?öZäÆt m‘a/Ý -'éÿ»³äÛ%ã­ÚëàÝ­Ô¹zqX6©÷ž¸0Ÿð6IC¿ìñKíµHÕ´ÕêXVyL„/uЦl õ Ù€t§˜¢_Cö-á­Å"÷ä¥(Ã%Fm8Vèsp’!-ç3ñn™z|ÚOIü‡p´èü)§³Ë9iþE»® ÖBÈ]tÀ‡:ë:  O;z.ÇЪT†XBÁIPc0Ý!øCüLpG×ZÁ“f  È–æ¼—EkW@aÁF'8AòГ^+¹(h¤ÅxÓÆ±G–íÉ5‰Ú@í ¸,L="læ'S¯ƒÆ#@`Á¯ bXj°ûù  6 5Œ,,*‹eï ´½ËoBÒ¥Ðý YïÉwl)+¼`€Ç£Z?(öêN†Š>5 €ÃI#è d†G‰Hà@Ä쮦Ãפ©¢Üßïú3Dû>Gãq‘ÌÖºd ±Y `ȉ%lkƒÓc„Ãñ#ˆ!öéÕàû[§±|0z5.I@kÄh!û£Öcþž,¯¾îîÀF¢L»Í@~”tN:à1%—°±Î›ØÛ_Ž<ž‚ ÞÀ<ñNvT?Ñ‘g <( Í2'IÂõ?5ë@pð@Ç[=LÌž²³ÂóÔüž‘ðÛûÆTOðÕÒSÔÏà½!§.§ÌµšèyâÈöA&q¦$Q°Ö{6Äÿ«§Óp¼)·Æ>5?ÂØ‰rጔuоÓü©NY_®ShBØ!«ˆŽ%J Äâjä“Pþ“.N˜~ì øù*cæSˆn¶,>„ë*}°åúõ…O ­{ëÕ€Ö¯©¹G”YŒ\™QQD®Xâ[É+ÏÕâ.˜*$ƒ¥½ºw+äƒyW‘ØÏ§÷Ï.Ü`ûŒû`é¡É kÛçñhUÿcÝWÌLú-ù{üÍOL4l¶¬-Í,ä‡CÁDΪ¬žTMCU0ú$8Ÿ™X°.Nå°Î¾-&°½W9¶¨Œò‘žoÀŸ’•ç•«’;ÿü$í%6ê覎ö‡º•I&àÛEç’7H>÷Š¥R7ÒN°pô+©¹˜Å KgÃ-²Šn­–‰0í_ËÅy˜¶ÚÒ7t6]h¤N7$—<ja…e¢s& ¦üñ?â4 C_]`—a'/áh¸Õ~'Öê÷Ý„*þD7‚ÊË×Oë^'3UÊw|•[ßï¤L¿…Dn$ ï¾¥#>õ:†R¿ÿÅYoWwùr¿NU%u¸½WÖ*Žþ›z¢Ï8Thzß\¸¯iÜ ª9¸iØÖ«ŠŠîüæžÁkn!`2ðêÕþ ¤e©xg颀)¥OMZ€ñ{%é ®Â½OC€`Õš »›a‡r( Šªðxx÷åö€Á×3 ²µåJ<žm´Ó­ÓNE—Àñ=»î[Éž?•iGÆ+Yð© kg6ý¿m¿c°¦ %wýkéüùZYèF!Îlôã^oî8š {¥ôŠøøÿ‚–ïÖäH—Íb"šN¸’¥ñ¥úúÔY¿þ‡©1²ÿ[ëwÈjiÿR€‰è^Zòâï\ˆ¸W\fš Ôàdï^®Áþø­»XÔØ'Õ~ª8sŠ®Äù}.Ú¦..ûˆš2ø;}?O¨—Uïód`ðùW›:“†«¿êóT”®­MfOvbS DöŠÈOƒü?“'ÊÚý¼ œ·l»&Ž·z¸ñ¬‰Ö­‚ãîÊݰš0­¡Â««TpôÅ0)‹À J~àÈ|ZBª” uÌo876 á¢ìèïcS¯KÝR\˜ßÎϾ<ÝùtTL ?4¤g ¸CŠ=QV!ÿ°Ãza¿†¥6SË8MaçñÆÄ—ä²!¨c‚‚÷Ô­ßø3ŽªI09z‹ëWjq…U,˜*úb0cí¤HXj$¢\pøûr{*AþFžL¶;ñY±ÈbbúdÊë—ö.N^ü0(N‚ÀhÖ„ý0ÈuÎâ¶1EpªŠ¨™DȈ§>æmÔ™vi¼ªÇ;ìH3w¿»›·eREø¤W7g¯wÌ*ÂñÞHBæicèÛí}{=&ež×XcþÔ›íÒf o‚Ö#XÜ×5µ{ˆ[ÿ2[v2'›˜L” stØ"{DêÕ´w[kÇå¶ã­l™~C!’‰´m=V3‹ß÷À:´+Ô™øõ0°Õˆìl×#_®'[''r,ØGý+CCŸ2ŒÞz -k‹~xrùíÐ"¨f¨Øö‚),yPVÞ)¶æ&˺Q°œ º>^¶>”_ï’‡‘s&îSa¢¯ >ßç|L Ö†q—ðÝx†@ƒ¯0n2lÞ¾´˜Z8xƒY¹•ਠŽM/ÿ¸âö`2ê VXȇ×m_C@Üy•Þäñ©Ù1©>œ < AH÷6:@Ü* €iãÁÑ)¼®$-ãÿ‡Éù8ÆFA•:r¯Ü»Ñ+ŒŒÈóY‘øI\Yåk‹gQD^íepe8`ómø(¾*ÿQˆ€ “;¬Jw/c@nä!O(˜ÜœÆÁrÌÁ\YÓåŸ@‡ˆ,ZT÷hÖF@ÏqÞœïPmþàºeˆyöº,ðëYÌ:² /<‰ôßXÀœ½ðñ"€µ‚êlª;µ§ v¤9¤} ã‹ÐÆAJG®þò ÔÄ#Bë?|ÉMMÞ`§<›p& ZØÚ" 5l¦évžÂ@ ÂbªÈ=Ô±ªu󌧺¸€\jAÔG tmŠ!J;«¤9vÈìAz‡Ñ©íƒJÝñzœk£H¬TË¡;þüök•FÂRtDù*½þn‘UR±”¬ÿ¾ƒ —…ö^6S?p‘¸¼,1y +á©?¤¯iŒ H¬.þ-‡ªˆÄq…Uæ¼bfeÿ¥@Ú¬QÜdl¾Úrv“ c8n ‹Nö²üóp&±‡Þê€Å_e!€×-™Y–5E‹¼ã”e0~Ÿ×r«nð~åÌà(9ëmuËF A¼‡Ÿ6õð‰ÕT`Ì#~äQòq 8굟x"_8¨Íœî`?K^Œ\‰T´‡}%Ž% ÆÃtŽ s”ÈJš¸ºÜ9Ly$¥>.ÙM†!é–÷»cðÝ“ÜN^ûŽ—¨ÝŠ+$å:Ž®!]9«e/àÊݺ«awçâýõVM4†kª†ùÒ\Û H‘Çðp²¬i%P‚aEÐ/M¦LfЦJOÑm#W<8›þëë¤9 €êbŽ~-:Œ]óaª²œö¶‰3 v`ñÐÌ–ê“ù§ílŽÍ“sXíé<ÿÔ &Áƒ™Èïl±«°$ÞB'ßv·i8cÄIæÞ}Æán?qã­ ‘|öûR'$=AaK•*Û qnE÷W„a¨F0ÓNøx‰ö€ùú`JÉk\R¤lKšçé¤Ze3H-ý¢-Çà0ø+å ­úl6AŒª²d‚'2³yxuÃßq\¡yØ}Þò ¢lDsßöü«™Ñ]N˜Ç呉¬ q!“ïl·¿£HÄãÐlĆõ:`7üÒØ¿½=0–SÐRN?‚°a4xbÑþø#¾#™›[n¸0k¿(ì~2FzC‰ØŸV(#˜°›‹¯þi†»6 y$÷Öfè©®X+2skIØG5r[7߆­Eàc_Ýu½®rx&gÉæÎ ºæçG|TÜ9N2”5#÷Í!lë'0Áb‘0;åOÆO8f3¨›èæ G<h½ƒÙU¯M,ÏÕ÷“6ÀL m7㣠º8$¨Pñ8Qv(!í7ãxá¯é%ܘsãh47zÆE‘­Q¿žŸ NNúJ“d±ð1(l{’Ý5ǹpßy™0{½=}@ : û?-ùÃгÎÏi¬ð©Æ°Øä† 0GZbŽX400a#j²þí´éôð êZñÃ{ÅK൹Óv· )‘+zûà $s÷¤ì‚–‹{•úô7s²Ã(ü@ÂãJ* 8&H †mö²`>\4›Ä*üÔ|Ž*€=1›=Ì:â‰WæÓo¯Þõ+.anq9ƒ¼B)ÓDuÎÌJ%7¥2é™N¢1qá"®£q0kƒœ*Ü]Ì.M(’‰WÐwLËvVn368èÆýä ef&X¤kNóUñë_oýnËÍŒRÖŽ$øo5–ÁWeÄú7Ò³›»½i’l¼§Ï™TO?K³T'½ì/l‚,õ;3šÎ$ £\÷‡u}¾ y÷†íKÃÜ ’´ßk*¦SŒ6âàÁ %b§Öú”%ÂOžiBc§s†™eÏ@åoR˜ŽãV´97SM•GI´X ©Û8ºýÀïØ=ˆ¡{gQ5>•13Ob鳂4“Ø9:¶É“Lô…^ú†×ÔJ>¸ ÃOUÁûÍ—3Lõs¢ÉnTÿ€‘šŸä£D¬G’õhr”–N¶ó1Ç!ú¨Î‰AÃ|FKB=Fÿ¤Ãã-yì3UßëÓîZؿȳ’©=s´¾z1B!K0ŒáßY;òÒ‹F HìR8cãÚ0b\¹R2¶€Ø”(©Œ+E¡$ÖÔG/(Ä¢ñ‹˜yœÊF‰-¾sôõ}¹°ë:Å>¼$@ZþÖõã…ìŸûõ} =€,}ÆWä$ý©þ·v½Žøžü…9oš_ëOÇИ–MÜ$–˜»ƒŽ•é‰ÒãR+±¼Fyˆ™Íqõëê[ßEVÖq I=®Ã† $9‰ ŠZ›±`Ñ£îë…Ö|Kþ#) ÞQ…¦Ÿ’Þ7þ='€¾ÔB<Ô÷"Μ<õ9™–‡™úðfõ•±‰ÌE2E6ÅÉÓ/K÷¤!˜­²Âç"n2íÏ=àx‹I mXEÙuýõ#cbã<ðhףɖsG¼ô3#I„ç?oCÓ“ænäm]dÅ=é÷v‡6 šhuŽÃ@ ^gxÀ(ÈÙÖM5Û1רZFg2.[î[¨5\TÁé2ö2¯’!z~ìSPc+ÉÐ#–›|°¨÷ ¨nYx-×gþ³Ü/’œj\)G9/çëƒ)C<Æã2?ÍÎC\µ@¼Þ‡Ô_û“vÃemMI“ ¹ñÁ_ÿÀ]rNßl¯N8Ðï{mó÷—"À“‰·¤}¹X§…HÚl!vÐþ¬ò ÀÜM!Ô^z1£:9¹Oï‘É‹¢ýZ[ªÌíØgéã#¥ù¢¤ÀfùææÍ9á D0 ªJAf•8žÃ&õà ÔÆ*f­+*`e{ïÄ$xl'þ°ë¾¨4ûÜê(ÚJè£x•l†Äâ6ëÛ@;®®¥ÊRAÌJ”’‚m¬é&¡{20F}ÇËq÷Äqö´‡Ò'áz¦æ:g[æ`£0£ëðÜ9òü’®ô*Önïp7Po‚,$ í¤À³}Ý~ö5á*úø Êhã%¢>B´Xøzßጩ×Òì~—ÐÄQð5wü¦ò*™œD×26—MÕ¼ßÖ,oW²‚&AX¶¥*bö¼ŒÛR¨÷ü©äBú]Ý^VMq|yl¼½vÚa^%ïy…iœ¤Þ!lù¶¸#“€Å³‚G C~½{mˆ†ç@ãN0”‰ftÞ=oRK!ÍýÚ $ÒYÓU:Ý}@Ðø[xùÕý † cÁ›-Iý%®¹‡? |§2Ÿìd)8¨ÌÑÛ&œônuO߬¾k1/ûlÉ’mn`\èó‘ÍúüJ5ÓŒ‡ÞôQ0ÏîÉ“«mU£Ú®¿ô—Sd{–“üK¡ÿS+Š)à€EqðH˜ÖzÃZQé=D·Ñiîsi¯ö'™ Ýú/Øl©yQléþ¥ähÒÅEk*a~W©Á£¼úõ¹X}Çìí]ð_ªqêZÜæS0|é¬d°&Ì›K»Ö$³Ã³óCßoïâV=‹ùnÀ(¼GÏO³ŒWåÚ¦ÖÁZ‹ß h0=µ×£ Õ¿×3‚L \?ÆžbÂ0lÆN4&BQÎz^úhÇ "ä*ÕLWêþÒ‰=†–‘„Áì¥Û7Ád[²F°tá´ºÓ#sÀ ŽÕ!Žy¬s%ºOFž—$'sÝŠñ­v€P¸öýx¸Å-V A«w×£ÌΓ¢rõºãçåô8i2æ>úë#¤òëÝ·kÀ°øul#‹ %‡RRV‚sŠû¸µA=·÷¤©Ÿÿƒ(q‘†·Å¤ŒBq ²]cWªÿ?YD²™\¬Åwr ~|5_ûX4—Z}ÿp§ÞŽ'áC.º}"‰: ’_}ÁûþSñx‚«G‹ÞKøW dv[Ï›¹J#'W»W`KóLT|à·ÌHà¶Ë ^ß)h¡ –ð¨ó Ô–Us¬b„Ô£Ýþ(r±söÓ ä¬/b‡‡ xûKˆ¤c@MÁ °•Å2‚!‘÷Q]^ä¬s)‰cÚ-ØŒ©cÌdWCX'AY'ÓàÆÂÜU0¸Šq¹<670t†ê4¦“€jE6p§’  1ÊŽ¤?%9–ˆÈÑQ [å*x-wo™UÜDÁÖc0Yª‹F®91gNG²ÜÖôÃù¶Ã W[üŽ0äE°œ.e¿<‚ä>OZ’Þ£zÿ~˜„JÌoµJ*Àý ñ¬MWp0%`¾.pÆán|É?çÆmneŒ]é“V4Äj—ˆ§ŒÞ»Ñ±ŒÝ<‰'râÍÂ!ZàÆ²äèUi›m‡ ÉjøOC„+µ'±y¶Ÿ„<‘¯¾zo:\ŸµßÓ¦¬±|²ÖÔ¤X,$krÉ i~Áf“ìDDµaÝ Ôùoæ4 §µÌÇ4`Y;>OÀT‚‹éÔ AÕá3’×S¿¥?Þ+—­|J)§N+ŸÑ°-Ð`·$›« ÂP¤Á¾ÁŽVRS'ïqMœ½ÿÊ ¥è°‹µKTè¶{s Há¯"˜Âb°–åXoä0·´;‡/hðë@-j„`Þ‡¡°Sò².S”ù{ƒ™à&‘Y›±h¦ S>8¦ŒÌ óºíTþ•JüHÛ=Ãê o´²ƒN#ûY@Ë$¦6^ù¿Ð›ˆ×ü°Ç÷b¶ I#50G{$`pRÒ]„’R±MgÐÏC«„?®£Ômn ìÆ‘Ö£ j(ºQïgŠ«[Eøi¬Ž²<[zgïÈ»J öO²–²ïIÐð± }áD¸¶ý€e(×MÈeÚqš”›'È­>'@÷qð«Þ {Ulð–Ç¢^Wç„ f…€üÒ/b™Ž<±Ø,ò¥&Ê@Ëtaë‰/´ ÌÉFCõj¢ra]¦.¶£)(‡Z’×ÃU ǣ碒º6…aÐàÎlF­ðàUˆŽKåÔeÑ@½6•À¥~µx Dmxým²Žv  X­Õ‚'†yß$lhÔLM ØDÂzÓ]LÎÛÌí’¸Ö¢L2"œǃ\ºíÌ÷ø]A”#ëÊä¸QÒǪ×G´$¦ >'E]²XïÌÿ P™ˆëö§P¯É“u¦r޶9Ã*NùEkeüà/‹ßBß›ïw>ñÛ¿!¤0êCwLËèŒ ÄåL]¨¥Gnô¹;!)Œ¬L‚Q®WÍäBAv­¤¨kÅ †¬  ø7׸-`6&0?Ì*àê"y€„"¶Ä?tâ…¢#*ÜóeƵr#‰èËÈa8T›$4ýXŽoD<–†‘Ÿ À¥v,sª5I²evÍš’ʇJ&½•Þ€vcûâX}Ú2·,[°š²PÑɆ”£ŸîKKÅŸJÆ òh>mbù¦‹‰ƒMJɤ5s!.êæÓ±µ¨úÀUl«œ¿+Ù^{‰ƒD‚L3aœ¤Cd=sY0ƒ…í¦D8ê4þ*Ñ9pt¤5Ö(¿fp¼YØŸ6}Dà[üz‡…°‘€• êTÍãix‹6o(å!=ÔGÖýëÓ·¡ËÎ $L†ÙÜK¸îéH²ãn&fä 0À§µ‹bkV:IvJš”X.òxX›òå“ð›‡(|&[mq¤SÂÓÆ6«ÎIô‰“el»¥ Àhh…º^•CûüþügÞ¢6Ï¿ßÖ¾­ÌÆ.}dÄ¢Ïi*1Ö»*—pS#_뽌m¼ 4 gîš5|ö•­­áÁ[ ü¥²—–à5™±¿­?g±*€@®“3v…"›´ÄÔpÚ3ȇ­6¨sû`!™é‹Å )gƒs-:"öæz¼·ëàæaWøÜkv÷xþ!”uˆÜ™óŸ·¥S¢Róý¤×nÈ´@IćŒvåT­Á/všK$“43,m[åÀ^ò'&M]qg=¥çöBÒÀ´›@С~"HFu¤ïÌRª³qÛô¥hÚ¥ËÇv” Óɱò¢”ÜÀ£¥¼_¥jßJ¸µ²^k3]Æ`,m¯L=)6±½ø'Y¤u^¨@€s¯b©Ÿ¥GuÑÜE4õsÉdÏy91v؇sÿÒ? <­&UéÍÉ—;Õ+ø#ÉdÉúÿª˜ÓõšÀOðŽ›ÀŒô~Ljœß÷ÿŒÕȯ‰± YËõ=€«Âv—e ·åmaPÆIŸO/m¸hÞ (}ÕLôõg YµF§€õŒåéeÞÐ&gÿÿ'«Wêl?—Qr¯Í¿ÿ»¯.nï´Ó’•I2%Zié§ýýýSî÷sc©ÝžE$¿üÒ—?Ëbt]NâäO×¼E†>¡z“ÕÕ8Fï—®râ†nEõ¯ßó.®ìÞ±}u¨±`u]¿ânç#€"c¥}xjÛ#ÿƒYrÿÙ$ ˜$Çê]¶P>q]ø Š„pJ:äTçí ¸œ@ÀBÞ·Œ–üoÄéqÂÕ«ý݇jxx“†A$zŸ3ƒŸ„€;és5PéS¤w¤^”Žb³Ê¤Ò¦ ¥]êQ_üšE&§–bˆäNß› Q,Fc­ág-šÛ.#,ŸÝÏýV†h̪UDõf*°…™3R“×oz,h0Ä:N‰±'KBPäC¯g€óCA=*§æg7ÆCÛ¶%¦ºø ‚Ìa›ýgŸÿþ(CUï½îu™ÕsŠ‘!ÈÙ­úªƒôÒiÿÿUÖ«õò„”+XÿÛýÌÿIó­nûûÅæü yëCJDP[¾þsÖ©LtæëæýF,ù˜! æ(j¤êo‰ájDeÕ¬OºJ:X”~4@¯àÓt³Qžþ„¾+>ˆ¨\;ÞL4~^}Ïi‡@qÖg&R˜ ¢¥Ð„M.0Ëë8ÇApÇ߉‚¥©J‹¶É057-€ÈÒ¹1 =>éñáÛ›Þ?&nŒ6Ío²éóÏvØFS#œ7àf–¾8<:YqÂM1˜­EqùŸÉé¡á^FLj† £… âØ¶ñ:ŒõNbËUT¥r”y‡xõbåÃÚ¶ÆJ”†êãBl9õ5øû?¾Åß‘Ÿž}¾Ü„j *ª "´ÑÃ6:´tˆ²[F›~kÔH8„«ÛU¸Ý¢ÆÙ`T&PT3usÙ´¥°=(lÉí PÈu4ÔVRõ4m‹.|ʬ\ÝåQŸ×¨âæ«Ï‰˜‘×T‰¤9³©xêe¤+Ä=ÊŸZ©Ã%¦½þ_‰üËó@×b/¹²“½>Ðê%W˾ýeà$H1]ºŒ”chM'¬H·GÃS‚DGÜœ™Õ‰U‡ÖDi<š@TÍx' (Ãe$ ÒU]z-‹×ðßù¯dÈhûW¸° &†ièI`çBþ𜯡Ç÷ÑÕ{¼q,¹¶°#¸"‹{„”gNô²kõè 7㬤öÜ~K5áÖ L-©À¡kŽ Qv¼¦„I8B«<,r&#)ƒß\¯Ú‹®/+ôŸ QÒÄ2[sÅ“ên Y±o ïûèv%܈è%ã©læ- bƒë$®Å—&pW(äìqÑ`p¿ÈÈYwB®O=%êH2 1ôþ²Ùà5 ê%!¨8Y£€©ÛÒ†£Q?¶õ€¯ av-ó!Q± ¥ø‘d%öa¼´˜“)9KQ&žAa×OäaöÃÿÊĉyŽ*Ç3ôQ†±Œ"ñM:¶fB~OÌ)Ð òë#[Ýÿ¾qý¼Þjü7k9ê_í'tRd9œn‚4f-±‘pW€ø;HŒø È‹‘¥,S…ä7ÌÞ}d¾ï˜œ8‰´©ä—¡²‡°è<·¥„*Ý×X‡Úªc±€SûM7§à ÛLl€ä èm‹÷™ÎçI„¡ >.Ô$ à˜ôC9ƒÀR'øÀ»8Íâ_çH °˜QQøÖ5G36ܘÁ rФ<ÆJ¨LPG7ê|›dc:ÓLÃå.´e¯Ä!V ”ÌûÚ1ÂÛMKO¸Æâ³èÓ-%ÈFê@*ã“_&bHˆ ê™in,0xë¸>ù˜Eà:jŽèó“(Àö¢a0»È­ã²§ÂHK âÁ Àu?ÌHÁíuP ÙVª_>„"·œ÷‰:•gê¯ÈÈÙüòFCDsèÀ˜ Ý›²RèAÐ^à¸,bý“9ªþAYÃ)Ë^éBBãx†òÂ7D\gŸ?ªþI K1õðô_ç-X–™©Ìi¾HÑ´[>epµ¿âm£'Ù½Íp0D}€±Œ’!éÂ_†,G%ì>V?«dÜk=çþIŽÒÂñGݳ_óç ¦Ò¿eÓŽöF¶ د¹OŽþÄ”c4/ؾ«úÐÑzMÞ©§ä„hò@àEö1n›qG>|?I‹ÂN#ý{à?î¯a:ÚL$"ˆtÖ¯¼Çðe8À€+%âÆ¨¯(Ï*Õûï2ˆtâCÀKË™8gtŸ1 Ly‘¤‡Á°˜Ê±'¼!t!-µ·âƒ¢¢­µwÕ€ ¯ Æ*‰  £È# ’h4!Èÿ–…àí`hMã÷ÂFc©ø›ÔòQ[~õRÚȲÚÖWôiÖ™NtIhT.õÉ™—Aß“M*§Tpz>ÈÇ_¤!7<ògS¥T§ó}GÔó?‹öXËÇ~¸(`šÎ|;žóKö³ÍÁ¶4Õܰ:ÄÈßÒ‘.ðík¥—ýÖÏOxŸüŽ$[G/L–6¢ÀòKÐáý±&c’Œ;©§suZ£NM›aS÷À4„éES‚-` }:¬Þ*¤à¿3)©ÉщI²AR!8IÆX@е3ðOòã_üŽ{ ÎfË/úK‰‘)s_«®CÙLQK~’êHëáÑ%<çË8—¯XEVâ|p4U„Zê$FÀ`ˆ|é…Qç¡41w¹EÀ kEeŠyÕjÒæÛ`x[Cu–o¶ÿ«ºÁVmù¶_ÈÀË%Ȱ*  W‘|E@á£læ‚û)ûÎð™UwÚ»“þ\WŠ–£¾¯Mx5ˆzµtzd‚À¼íœ·BȆ˜¡8ƒÛXyß‘æPú@èŒÆLxf TëŠk°×M’É$înÀa{ é·ü “àY•ì Ý!µñæí±—!vIô GôfbìÒ /„£Œ¥\ •$lZÂ)¤¤lC Mˆª/V%@Ç˼u¿TðÇÖwßå§°€šN±…òªY!™®ßIw d‡ø±{ù4?ü>qÈKT*qwæhW—qÏÉ÷|CSÇ~>Cý>—Æìx¿šƒÖxówñ‡½™ûß@·ñ#¾›R½7¥}åPJÑIòE­ŠÜ¬ƒÆHuAóÇÁJ•²´Ý3¥å­Óõ`ü»«^ÀñzZÜüŽ=µÿœ´ò(ð˜>±n,¨l„IÒò(ê9,m’#ýæ¦-j!)Ît‹aO¬4ÊÖB®mç«JQ±LÁlZ]uÐVx…P¼wLÆaÁý‚¡&%B[e—[„ ¬vʃµ•BG±‘ÉC/ÛQ›£áWaߘŒÿo ¦B:Ãܳڟ`‹_ . ýçÞ8˜úµ‚î„¥PbiœªÇÉòÖ–:xO‘ž“¼i Ä?—Ï tÒƒªt‚tëWC[2—Ä3g íõ{F“0¿—FŒY›r×y”]lÛ‚áÚ2<@8è?Ú 3ùj‡”!YJä’6,±:`m¼¥ÙK’—©þyV—ï/@d¶é¡±&N5½Àר½®+ÓŽA­¡aì ë–ÙcëTH“,hû"e˜A}ÌûÑ'O¢ãm>ö^øM›=>šb2­ÈÄ»j¡Ƶ.ÜPLª„ÆîÖ§¤ë££äF!±Ø¿WD¢pt®‹¯Èç$Í?òΖ¤É¤Z)Š¡kD/.0ŸØšç´_¬À= ß­P2v¸E³‘CtÞøaµ¶!÷u÷“ãÿ±5íñ%¤®š2i5HuzÒ«L‹“íüÍ ¤y~Ø×õ¦Gº/"x1ɰRÛNaŠàçò7Ûhó’~Гn4Å ¿)Ÿ\ÆR=n³ÐGaoÐâG{éòAÇ— s7È]*6åÉ#þ‚hh9*’±N³%çe\Šz `ÒšF·&J¢‹É X¡÷¬P˜+ ­cpñ!œIÚœ »ž·þˆ¾Aµ¶‡zÚºË:‹øÌâ}2Ýž4Žý0‘ÚRdœ˜€Æ`Uš°b8µˆê´©}þ§¸dý{¿UñÇÙ9›}Qý e³8!\îçwàŠÈ×°äU²:fR^¦5:"V¥:.zåµ~ŽŠèò郯SÒ1®`8`k¤á[¥-Jïlšji²rã:¯ª‘¢Ñ;9†r36ÿ(ô¹›`hl~k[R\—ŽV„ˆ±Ãæ¶5øzþxLVŒ9?²Õ÷hO½yÚvuõ—Dk”کɶy‰Úôæ¡îFøšwûDŸéå›çOgqZwbGzÿ °¬¸‰³N#ˆ½%ù­-„‚º&¡¿öÖåxƒ)š@MP Žl|„kËDÖhT÷ÈòÈlEëüîTÅþÓpÒü£C²ÙþÔÅ®ïçlÝ^§5 ­e™|Nü;èóCƒ65õ5>Èæáá•ìš4 ÒÅ‹Ä†ÞØPûà) 6Bû×~¤ˆJ¦ÑÙ«Næ…=MJCExM‘ð5_ù·j<¿‰7ÿ§šäú"M¡ÙµAì!™HóÆcè4 ì’­º…ý~‘Ⱦ AaSÓ[›H„–R »Ž¸£ùÒNÈ’²ì¼Y-wœî.#ÓØöF·ï-ƒOd¶egXÙÇ6;@‚RÃaÎô›SªT &@¡Œîc &þûMq½šÁ¸ˆPßYö³Ù]¼èdf)›ð~å=)G—¶îÐd•ssηó©Úņ¿÷÷?&[“@Nã'µe€­W+y&Wr80iÏuµO(ìÍú‹ž“ô©IÛ…ËÀ¯âwKðFn›Ì€1Z›eº†a‰ðéð@GÈiåÛ*ÔP„ol-û‰øi|Y"¦Io<æÿX&ûjL>t’.ƒ%®…òSõÞüt÷¢a¸Åõºå£¥gDò_^{»«ãF¿Yë¬Â<šC“جv-S[¹Ýøîû!7‘vB[ ÿ™™ K†Låôá¨ÿ î²øÛ£‹ðX¬Þ…”d%B–¨ZVîØ«£QK¢^+íjšÛ •æBè¶”:<R#6}q'¸Nrꥎ—±ôǪzK¯I›¤@0€üš¼X¤'Á-6² ŠRƒÀâ'9ÆåjoÌóHáâg58GþÎ~(  ·ÁÜÒ†à}8å¬Dù¤g"ÞμIdÙ“†8Ë^…ª(Òñö`íÑp¿¸ûï„áJ!­m­’‹rhÕ,cÉ9Ör¶DIóM·"‘òõ1ÈDÅáMä÷#8ž*x¸žLs“ŸO çµ|bÔÑ8¾ˆcÅ€Í>„4š…ÄAíy‡—žú°ÒöPÞìX:Ñs“)†?ð=~LSÄ˦ï(/þ› :>bÿÈâ®Æ®è—’ÿ7"9´§úP‚™ƒöÖŒ™FÕ¢X´@òFë¡•|<$ß|Çv¶èg¨9QДœ‘þî–ɱ1@~ƒ¬í4Ìê³+‘þßë4#I'Z%¤{ŒþiÐÓ“:bR™ú953BÝÏÅÀz‘[25'drŒ\/!“$s"ïÊ5uÃe óíàÔRU]&ù£E‚ (K^$!À³aéÑ1È^ÃÒaáÁŒdCŒd›ü4í¬ª{$êÁI™¾.P5˜E®ÁËM^„¼d¡H€oØ-²áÙ"©ìÌ-Ä5ª-¦pt·ÞIæ—IøÈ†¿ÄÖ—i{ƺ¥ÿª)epu`çj‚: }’@éàØ6ü‘áªhkë5YÏ‚ùð§&5çZ^e¨ZL£ÅTŠ~´8·ù|wÇ””Œ~“ÏlaGr¾†S“ñxðáçKRì0ºÄÒý9æFÇô±ÄQÃü…\¨.é,Ÿ÷yi€.ìútªPyq(¡Ž‰Í‡'}Yåó6v+ÒÜBŽN„ªf’t…CàM1íK°ÈhkФ@ÀÏjzcèà¶Ãħévœ„Ê1rhJÍ,_i<—–¥9xµ‘‡9 §IÆšÒÿSÊy€ÐþVçÓÁ¥ßàÍMù›ôDPh*.¯-ùcá[>M‡ð[FüYkË´ûÅ¡é0¦3ƒõ§ŽUR~Š?þ†Ù*Ûãþ)†ù´aë1Yó;(—Ò§Jº9\œ%¨ßh‰ðVÈä¦ý9µAýJÏT¾óAÜp‘í§å ‡˜ž“˜`¡–ÁÇWµHí»Stñ ðË=d'Ûä24À@Z¹AоÐ,dòO>Jµ†sWþ_AS{è1ŠbLÕ}j*Gi’zàÜþ¬+©<Ñ(¾ "À¦kl"~FO!ã>çGý7,à£6æ¶œ¾éFçok|RHnI‘ :]åB¹ ûµëªõ§ÐàÈø½Œ¿tË9­£U;ÏÁ-`Øa”ž¼¨«ók"ÃKƒè…ZòDÿ ^šÒ³…ªIA!¦ct n>S “2™Dí'±ùž ûZ¯ûŒ¼9?2µ ©+”Á9]µ´“e<ÛÍÄÐhsšf”&cƒ·ŒÊÜS'ù€D’õtåÁÀ P)CÁD”žÑSÁ,ñV>i$Tb°fƒ1 YÍ;vGÓÀ2}±A¸ƒ/ò}Ïut?@X–a±¸+è÷äàèWãfä ·È™£­:ÛÒzFd‰ùÂ'V?IqÇÈžó“_]p1ÕùP†?‹*f–CµJMheê&1'äcM-.}ƒ¹=¿ió=]w3î62µŠžMcLÅÜ®œÂtJ¢ÿ‰œJ@‚”¬à(µ½W±«àRÐ……[œÖ¸_#e°BQ8bé‘K‚­¶’€äÉÈ´‚ÁØ¢ œžYÒJ놮±«øR <äÜ&à £Ьm3„OV0/DÅmU],Â&Ž ÌJ5æ ÐèÚT¼ù²2|S“’ŠO.ϼuþW£35r~Ìá?:|¾/ÇQ‡“<“H ´ßôòÙ^œÙíyöJù{ñhY¶$´‚b¤bð h=`tOÌÈ8¢NËD}¸=èà~šÊ6S@Dä(e=]ªB`ZË“½-.ƒ 8úiwrNV¼ÏÀºqúÑð-ò[°p³óÌG…—Úukþ˜ ˜'¥úá ¹§ä¡›KÆ8 İ=‚u’…žŒM0¦ðql´ ¡æÉ%ðà¼4ìCÊ übü½.\ŠúK#Ĥ53ìxÅo#ßZKKÚiå*ÒŠBy¼K^f³\½<Þ9‘~KLâI./r}o9õ¿ä܃ǀyç>cï°·ÀÜô°PÁ3:ÅplwØM†_ò_F4 Ø¿®?L¨²økWÔˆPGôu¸d/â66j¦2ß‚l×P¶´[v ?WÝD>µ¼ˆ6Öýù¯Zë°_tÜuóC,J„†N/±È-~Û‹<ÛÌ øã Qh%]›†Íù’=öÖ‡H` :iöåq5*bŸ¾Å)YÌš€Z²K ì"¦]h£Xœ²¨¢eØ%C³$ÔGJŸO½Ž ) u$ÇuS…X-Ôç †Çêt¯-J¼° \‚1"Ÿ1bñ˜Ú¨ô®Â«ªË„ƒL• 7xVñÓJ›9§ ðš˜îÕ€Þ$"CpDa;|~o´t >ë ü›öH€ZÌÅÅn#T¬xÓ}¼«bvÙnpOVí¦mÚ‹m>D0µ“8áÔý9„+PTŽ¡¾§Ûݹ¶¸Æò7Hõªù¹”bC·° 9ž•‰š‚º[¢5þƒµÁŸ%ÀWå\ˆ7´ÊÊ@ATÂKýÅÕHoItJ*¢ËŒÏÞ #©ê‰fØÿD–­^Íÿüxn)ŒúhqôÜ¢ì ï"R¶AÊ\ ^’KŽŠ‡ÕcMk‰”ò†Í8£ß4—‰´@—¦¬6òkZÓH˜Ù­ô¹,~Н|95Æ3_6¾^Â)PŸôIõÁmL»˜VýQ$Ï5æ1Á"ö$. îD#ô+³8:³’ñsf>!Ðï§ßþÿÿ `çûäé·í¶ÛÄ8(N[y1ôü´,3ªÖûëñKPFÄ3iÇUÈu߿⠬‚WßÄH7ËŠݹrî¤Wm¿ÌMM0E\V9žr{¸ò¹ÿqØ9u/›oÁШb¢;]Õ;œÚÌ…FâÃ\ܳÔÝRá"§t¤5t°Îs–%ËòR­Áÿ”sŽæª*¯Í˜ßòGÃI–Þ,öŽBdÞŠ:ý U‡31U9Àò@mRÅ{hí#,bµùƒ%d!÷Ö ¼yt4ú"¡ÕcåQví´f”³eùxP| ˜YæäÓÛÒüC#—Sf‚¦ ¨/]SVJ^`%Î!€‹ (Jþ±Ø7’ ³Â°ÈM뢸à=]3BîÏ8Ù¥%ïðRÔ¿ß^A—3áéö&È·èËâÆ âùFçÔæŽö  •<{­±ŠõIGcwÜ¿ô‹®4€s%­Å& g¬©ˆ·^$LÙËóá  ¶YLBhÌm e`ј±y¾°AT)¬sg*ÅÿnÏ2S`m¹­ùF£•y¼wk<èz“‘½§—®Í·î•2ÊBB8˜ŽŠl\·itg"Šã= šÄJ~Xêתù{]Á–-r±\š×’Ç‘ÈËž¸H·[&“ãçœ8Wwÿ^ƒ7èSƒÌJD\̜դñ!nÀIm´ñT1 ™ Bo»E͇@ÇC¶hVC¯°[ºnd±OþeœËó›˜È.dLnêµÒN5³‘p$¾ôÆ?ă¯…l¡| !&žÌeZ&J 7R•Ê…:Í1íßFèQÛI4®h³ •7Œ¡‡øæâïCÓ ºœwzcÜ_,÷Œ´ì©Äé`K ¡nd8³¶¹á!å XGÒþ³Â5±Ënåú\ç=€‹¸S¡à]ÖS _ž«’²Ò/“ѬÀJ18êék°A©„Ñ…¶zêÈyk¼ð±™\†®:‚*ÊG ×’ á]ë€:ò\ûªGNyµÜú¿Ç½ÞÊ'Ò¥ðá ìïYàòZ6"H½wô㙓Ù¹1¿<ü„W ‡ox~J‘ô;“Ò®2â'|£I¦ |.…üØ,’`ê I}·YûeaïYи´Ùîl`d˜¶LúP¿ÈæÄl=þò`çØ[î  :6 ¡Å›¡Ü%ün"šiÛ󪓲¡³5î#`ÆKø}{‡t-)GÙ@R®à9.a¤º‚Õž–Ãkì{+EF»“ØÁü’‚‡q˜}øâš¥bK1Ï—5´L{ÐÀ GÀ¡ëôSnœB?TAü¥pöƒJª·Ñ.Í[Ÿ–Ükîhñ°™MÈëÌdûÉtdtp˜&4(E^Çžöf$yŸŽÖ= *q‘|K»·Rå öoñtÚŽ\ËN±æÚ<óX¿ûcQÊfLB_O~?†¹¦:b°•ž:yFÏÝš’hv%šäüm'ްTyC'™œW‰ÀáQ¯ñïÀ9·Q°,Œ++œÇ ÞEƒªdˆ ŠÊ£iH:uÊÈêPjKš¹Î•¬ì”ov‘þ&@­<"ˆÕÒ s“Ûÿ94p `+è÷„\°Èêž«AlÜá“w•TêBîùAÛ¿€¯Êd+¬e KÙ“ÉΙ5Á¬;)Ïëö¨°6ÅÌÏ¿^,¥ ¿¾ž‡Ô!òqO-r%2ÌÏi–ÑꀀpK€6“®oO8ø2‘Û$§N-’ènŠ“25"Cù,Í Ï !¯ˆw“ù<ä{€‡kÊŸ'ûg>T:<øhÄJOdÈTAÿ:<ô?€U}ŸC`÷þnFãÃ[#¼©7/0=ƒØzÈ[xQ¯ïþp„ Ãø“>œüÐÆ?8ɦÎh ׊7Y!†sA7ÖLäL…‘…ß|‚é+ÃŽ,cдlv1ñˆÜ÷¢«¤ð4æ¡ê'›ßR%Îä{®QÞdÒû2£ˆÈÌPíª°Ùhë‰ñ·Ô’h3‡°ÀÕƒ w†ª}F͆£“DZD!ÄxʾíÃ×CrçEéqqi#)úϯk\Ñ$o@c_±B )²{δ µ…DÈUݽÛhqˆØL 3Ê/NǤüfÚ­²³y×Ü‘|ƒpǫ̂—œPó„®®èÇ‹Î!éÈH¨<ä `í*‘¼,ØK2®Â¯9€SUc'c×07[] Îq'|‘ö]I—N4é6yÌr&kùè¾ê Ôlï,~§Ò.;¿º—®ÐǼ«´ •'%sšµ=úŒ9ù-êßbÆZ×äb€;úPNþ`'G#£nà#©ÆY ‰¯APùηS/]~gFbLì¡å†å|ŠÁ¥cÌCÅ‘ÑÀÚ\ŒaŒ2[eA%o߆ƒÇ%»ì|ä~ æC]ög/†¾òÛƒ ¶ŸŽTÌqë|U­¦€¾X13‘ƒÀøD¦9àî ¸_b@õ0=,~^{Ížg|ë_Ô"íÃjÒ„o#]`Tv=hܨ€ö•€ÿÌj—½àÒ ;õfÀ¾ÛÄ?øq';wÈV$5À>Û'œ% ̃/àCC-“ =§‡È,ø,< º~7pʨä¹pupU. µä0±¼W|y¬r2\²«³€n,©ÔÓNXªˆHByM,¦–²«–Urø/îàÊž·Ê½®©Dí;Rú~”i¤Cˆ¤¼õÁj—/Ëð¦^‘¦ ImV£vfÊauÌ Gˆü ‹‹‡1nn'cÈÄïл/9LäM¿×iAŒ`zªwkåùƒíè߇ýRƒÀ… y>w¹¸u•ïáÖ^9:t¹>N:ù{:Ù[y¸à¤ÚoưØÆ8”aËø ËÉIØéøcÀæzù$kZô|»)hàÐýØJ¥Ñ‰Àç©8Öbai. zoi¹Y|¿ï.‹8ñlãvÁsEt°†{Å‘-Ñbç4¸8cjZCTL¤$N”¢%êí0—Õ J‡s.’¸>Lñ-Ò?ýŸÆ€ªzôŒrç)Ì^QÕhywô[èFfB;±7uN #© åÛ qü‹Â@^"bbp3±¸´µ•d½Ú^ Á”œñ­ÃKEË‹k¼ô‰ôEmíTQ…?½À5ƒSÐVFn¡óN¨0ƒ­Üh„:ÝÍ, ÄÍÕ·}(Ë!©Qæ†z{ÙAÿÖCñ Gz gTF ç$ÊÀàqí7B‚ FõmG¤ŽZÙÅѧú 4Uì7Æ|?Bî³r´ªË S™Ds}À›´Ø3»öл­1)ƒYò}’Ue[šDì0pGj$Ñ l#uÀ®ò=­ï0£¸ÍΡ,^¤À˜\¶êY!±þhWI5_)òIñðKZó qìüsp‘®¥0Nã*ȵLKê÷ºÉ¼Ä7Dò?#LØYq¢EqŠ=²ã·1CN8y°l?äzë0Z…Bãä¼fšþBzBl©tŸøÂ5bêÃwÄ»ÊBönþ§ EÊ|4Ù8&ÇtdHãåÑÎf…>L*I˜bÜ{ø_=2a¿ßäõ'S¡§gøcùÝûð–—ÛvúLÑ"'xÿO)C‰Ãf¸vZq±.¦%s.1'ø9SÄ&ÎßR/8óÐ'<ÀуTî,¢ÿn­±$lFÒÞŠòõ©eìš@wï±$~®›æÐs¬c%0­‹8¦à–pNóÕì`È>€´Óz¶£óÐnî+ !bb.ö7åF[hÿUè"ÇjåÄfa ˆÎ‡}ÕÊŠoP…Ó¤ž½ô?·”ŒöÞ~û”Ó—­®ÜP,YíÆ¥3eÃR#«µý'&§h›ÓžuŒckvÓw ½ ˜Ë¿tušqøú&߆ãZÎŦ¹ˆ†¸xâÐ CÁ`ƃ&÷E­(rd•äü•ýZïz‡ÌmúFª¹!¾];eÜXq)2Yû¶G¯æœ`]Bõ2Ò¦i\ù!Å›Êí£„¯Ü6aë §âOÿúä{ÈܘÆ~éÔOÿÚœHXŠþ° Ø)ä^ Ñ\Óõüh^?wwèMm'7X ŠbF|­ÄxeežAf€óð2•dêé ox—äïH®Se^`F¼Ä²N,°¥–¶@1Î9 Ègv†2ø';LMR¼æGF¶á£S²oðœÈ5ÿ…$h–¤‰Îm”/.KÅcûñ§Ýï‹Â@H¯íáì§Ûë-±šVdðKVû“`ÏVuS—®Âö^\ùÿÔ wœÛüó¦C†BiôïõîˆD uH—…ðÿtˆº …1„W¤o((o¾óOšì‹©Y8‚âÛed\#çL'xq9""@hâåw.´¨ Ãÿ$‚*s$kø{FÔDS/6‚‘ Bxø”Žÿ{RÖ)+Ø×a7Ñs/Mù‚@"’’¼[™¼û™x-<­l6_äVM] k˜ƒ¨¼Éª¾ ŠÙZlÝ*qX6\AôÝ-ƒd:6TféÓJ sÍ·–ø1s<˜öÊBûÖ< jˆ|šÇ·§)Iäì«(àw+’Ó-}ä€r\ÍÍêRâprí`¾€ùYðuˆ~Leo¾$µÂ@eØWhù.,æoð’ûEºþÖÃpû˜Ï¤Á Ð_2ÐI˜qÿc!Õeœ!†MucǺ”]Œf÷Yˋхj·…¦­µÆ¡u?Ç~ÈN¼þMÿ~©(ÒtDæK±BAMòH$ ýƒÃÖ¨VÐrÜ„üüþÃM¸–Ù´….|kCÁÇhòÇCGãè$ÃØö yïG”À((ˆ/ÈØÀSt!•YR…ÜÉ£4¾Cù·ÄÆ¢@?4äþI$“ñhn ¹ÙH9°C& Òñj·†°£`Qíù§ÐãÊ×rHÌÙÆ üŸÃbò¬æ–‘¥€óDTgâÕ•\ùôóBz¬íàœ´qˆ}6yÖæœj=G¿Ñ#ȳ‘^‰N”x¶ÁÕ.송¡G‚ûk£¦O©É¨¦a§?°±¡z{š¸¤ÇÀQ& zN«§6©. –ÆõË´à·N½o¡ôkXXHn® *Î-ÚmÚ`ƒ©…›Èg‡ú*ˆ‰E‰×-;t{¶~÷Ů×]|±bm%õóK@‚0I2Y(kp¤ýèl×#â›Ì™ƒ\¢ì ˆô7ü3%¤7ˆ¢*ÌlÚ³ñ÷–ßyë\*P¡€©0¿É‚Ä¢tÿ*Éh©#ûfÌ‘‰Ç’Nƒ×u¢qd„vDj ¤“Jx¸Œ?#$z}…àÔÎVÊ'fsø*q}Ó}8ãþW?¬òþÄ.ÓèáîÞ¬ášÏ<ùô,R"t¡)>˜–†‰Æ:5µïàÜ!£¥­ë€VVÈÂ^ï|ô/^‰RúpÈ EÂKÊ"íbNf|48E¬4‰DK½¦oõ¤yÄ ®k÷¼gû˜ ˜€„˜ƒ¸èüB¶æ +¯Lø^„^GÐqC»Iº3’®€H‹Ö¹°¡îЬj![0 X׉F<\¼ÝS·XÁeÏF_À1Ã1‚+üÁ O¯^‚áFXzðšüç}¨ÚÔj›B¾üS`çS‰:ŸTÔ*”z0ÉuÁÓQÃhMøO‰pjF7ÏÕ¨SØ=/™¨íµ Ã… ãÚW†ËÁ×-Ai#ؽK¬ŸØ“ÂTLDZO49ø²5¾kq„àH±(‚ÄFºÜeÒXaI˜Ãƒîé+?F”íb7YŒÿêen SùL±}rï⧘ƕ:F–Ó3% Å8ü©!o žÈǺ\õž ïIäqz&$òŸ¡ä j 8©ái2=T¶Å ‘¤…TÜ.÷Ê}»…8-àœ8\‘Â7p½AºÍˆ~ɦÌ8µ09Ðf$Ž×™ ÊJÉZÓ×ä+¨‰cùBÎ6H*—R\ßäB£ÖQû–W]Í€=KºMPŒu^–IPtd\ùûÉ+ñœ.˜B>ìD).¼‰öŸÎŒ.’ºÿ†Ž?Ç–Ì;–ÿƒ‹`Œ+£Í>'Y6³Mäa ‡¦JbZÁ üQSÍoüy°¶Ð;,mÈe‰q)(h=eí*œZpÜ2×6Þ;¡ßC®ÓšÉ‡³{4)§ú†O¨9tsP|ùPF°j(û¼å†…Xo“yX³ª%¼HÑ ÓÚaæÌ¸„~K°Ó¡ ‰7® ï2q^qI…pUuÝ•ðí“RKäÁ£}ÂôÈ;->´ÖÒPßý¦¾6gévÜSî.ñûCúÊ3ÃßPi°å¶¼—aŒá¦°”>Nâ»ô?«cÐè2£%†6j”DOýì•"‰rd3‡"o# ³É%H(|ä‚)%»jrä?ŽSÆŒ>Ù&ŒÂJ….ädu28vg”¿†Ú5«>ÈÉz¿”·™ p¤úl#S[oi+ƒ4µIó{äý¨Ã¦IÆäWxg[ŒK­<çñ6q ªØ¥I€)oál 2£38=›FMNì:Á^(ÄX5$ÔËK@Y8ÃG'—¿†j‡áœíÈ-Hÿ¶e<$)âe±¤Ù Ë™·@_py#gŠ/HÔc‰•PÁìŒô'hªÈy³ÝëlUã嘕»¿¤ñ˜q =åp+¼'¦‘ÚPûO“£2sMþ,¹p»‡›nÒ¾5§ÔÙæpË”Çæ9˜K& áèˆìt´êÕéÏç”@¶Ç+OÝPáŠn¯+¶KCRlBUzjõæaíÁÖˆ÷L† uþøÛë7ù9k-™…Äœ‹myEšf`.6®ÅlKd/ÑM±œ«þ•¹ E4R'‘«Ë<·]²À°ÓíPÜQ·õdF¥ùxÉå|½3úžOPahÙ ”ÃxÚÐJ`öJõx'iŽÚ±…2¡,V² Âàíô‡ˆ:&‘Ï7¸!Õ¢z µ ™À‹’pÆJÙÚCkMÓ~[6VȤʼn¨IYK qÈtÆÅ³ WŠ ¶…¶rM‹Ù{¥-a°°\H~–~¹‰ «t·„Nv˜j6)j³y%[õóyn{›äDíÌ÷zˆÞE¬fi,3"€Ô×QQõ—r  v¥‚[X*Éfø¢vo_C_\È㢀³ýŸûaþdRòíéeåGVï.°,’Ìô˜‰ˆüPÒ–•øá©ýe5i€ˆéŽ…n$‡ ÜI1‘AÙâĸšoÜÂÛ‹.tãÿß=ájæ#,¤¥p(’ Åÿ!†ÈrÑ î &óªÁOÄ}‚QÃbV3Ói±Ù’¨ùKþînõßpÝ3ÊŸ“6Ä}õ"šŒK_ÿñòõZëò^xÿ„‚:û߬%êà€~À¦äSÿük<¿ÿ5W®¹qüû‹~KˆqÞâëÏñÌÝqu￈æþ¯z[×ÝÝâJAfÀ%"ˆH­×‰ò¯ðXA…rS¤CY²¹÷Þ¿æ>á¤×ÞÉ`òÏ«ÊOöÛþæXTô·ä"_‚@šk÷*e«Š¥µLt €”” ³¬¨¢TS‘T¢˜:4 7oÁAC›vÏæ¦³ŸÂ 9KV<~Ù3’›ÈjOÍÜ*X ŽË.uËŽ[9öÿÑEÒÝýßuoõ?5]'>+²ý|_1àêª3]/~Ø9·XßpÖ ŒóLcÓ¤Ó¦ªbÆœ*W’×õÔ¾™˜·Åsá45„*Ò›¤·~ ¦7æÿqhÕhˆKé¡Õ“Îûšøº·ðÓ/wé¯Û Ž£ø“ÅVyäáRe‹¸^£¶VÙVölΚu©D¥ñôøIeûí)¢$®µÑÅ1¥Ävn]†ÊFo_bSϯÿø,ƒŒUÿø¾Tñ£Oÿúú ðyç¹SºšZ@X T.ånP[{|;^kš¨ßƒŠƒa§C†ö°‚ÐÓOOéñÿËõý x Cx-›î~mwHÔ!Ié¯ó)…UGQNN±˜Ž`3ãq-š …G„{Áùð~ëÈÀtn2eOÀÍ‚Ÿ›öãqÌàªo´£[É€Uݹëc­@tšàˆÚ~>?¾2ùOÿbe€H³(`=¶ÿt–ñ)Ö­†f6û Þ»ä—hL€@s½‡wÊHÇ0’F F˜`s7³e—‚f I?ðúÕRR¸‡µœóžöí^*ŽøŸšx­Nõ]ûêž>ÿÚ»J9^ïpòØ1-å. |Ÿ‘æ§¹{e)trL>® ³š+AŒ x8|Ñw­ÓÛÇdg€Á@à¾7ÁTÎ…Øÿ¹nO'w'ð"Á¯pm´{*nð[ÛƒÃ6«nr@x› ±j+|°8ðÞ¿_uFÝèƒ9伇¾hp p{ã°dÑR¹.2=«>îîD^$œëŠBn¢®kü4…À´–rÌî<5…ò5ZE?`ý©¶Âü Ú¶Á¼Äì|g¬8 kì9‚D^näWÐÜ¿”„”DLá#§Ô9O8ãÄ‘$  j Ö­¸I<5&#œ [Ï?´˜SßEG£DŽÜé¤D“ý‰ðNV.¸ã¨&xå0‡ 6ÿ€¿Ú¨ÐÙVbÖÒ'‚wakúÚ´ÈXæÂ­,¥ýމè[Ńþót‡Á ´û_n\Iˆ8µ@a/£X @¹9æGÅïHžÀïh€Œ0€]OÀŒ‹äÇPï½ñÿÒ—2Byã07è#˜¶üÝ ¯º`—!­¯§ú{¥ÜZ¸AîÀð&âè’`Âk‘ÖÞ{*Œ¨÷g¸IÜŠM‰\ÐðäjÜø…lV»,‡ƒÌúi¡»ª¤ MV“'4Œ“°ìèå ;#Xñ…X}ÔÖÁ-û%gNWBíy]Ú2,Cˆ`‘wcwExÑé=êðÝ…îþ-Õ¤ì»Ð¯4àt6¼50lJ«RA„.ˆ‹èÙ¡FOÓòª9¹.“¸,Ô¥«²« 6ÐirŒvÁçÌh@s±Ihòƒ÷K ›¼¥H+|í‡~÷6† ‘þøû@4R”™É$B+ZÜAkÖÊe¹þ7éD00ή ˆÀgSÓ®ü„mqýLf‚õ"èÀ•¨aò&°°¤ˆ˜ÿ6{Óæ±c•ªiÔØ¯• ÚŠ{hÍT§ÛA—e I¤@²¿‘5‚¢Ý ¨J …î@ûø¨†Næ]ßÌF>x5€Ü?@±Ž4?¶à6iª¶gÚÎ(fz¥„íÊ{ï™Þj¤jï>xï¨Æ3™¢2½ùÁ4½@LjvÈl $‡ÛЦh„`þáóÃpŽtpJ–kj«ÑŸ/:˜´ø×ÿÄŽ‰àÀÃ?}x±Ãaã÷ÎÔ:¸†´XòsáÔ %<·¦Ø 3†Ù[’žY¥jÏH²unV«SédV ßß®èî<w*œ9v¯¶‘Ø€Ç;)×<}kóeð#{{ÞÚc®ó–Cl·zmõÙ,(¯ýêʃ[ò±A¯ÒÍpaùa«;V„YּőQàL9Ü767TŒ+ô—þx_ŒÆ›BMÎ sªøÙ3\«Â(„¼ÄM_ hyÈÀ±¹c@(àòCšØ|c_Âqd¯WàN4½€%¼y’挰zPÚ&Î`ø(qŸá iŽï ~­¾Mâ!`„[ä.G#2×,í+ÀO9v˜î×h² ¿4;d°& ]æfú ¾¼¾9ì "NlF‡Ïâ}7^g:†{6yøQÒBA”!Æ¢«2¶ G­*ÆÒ2#b}¿¤\¨#oägÄŸ×B›Ü †`6Íêâ·ÎÙVe\ÀÄt¹ê­ÍºPnèŒó÷ȈeWC(ŽPÈ2Ìq®C§<= E'¤Ç@ž!”5=fÞÃðª‰ì¶ÔúU‡¸‚ÖЙ¼€^r+k£Ø`šÍ9rË”eÒ¦yžä#þ©ö ÁWOHÞJ„NÙôàbQþPb ¡kÕ”Jãcݾ~fÁÅ»¡ÐP†‘DžY8uá‘8·Ó}{LJtïðÙ$æõe ­£ÉVøË˜ô¶Å ¶ L˜–|#‹·6Éš1 6[n\{¶·vhÉ™Ò >æ C6jöï"»|\Ä›O¡THpëÚ·󑕸£ yø‚/§Y˜*ã«:f™U”vo•ýºæ,ÁpÐ1@Ÿq§j Š1.lR^¼âñ`3}‘G¼~$aWÁ‰ô”Z€º:”•€·›À˜Ôh•Vðì8¨4¶Œ·ñC°PqÝïÿw†'-ñYƒ*ÆMȨ̈‹¦Ç”›f_N°Éš‘°/îñÛháàä €d Ÿãa<嬪Éñiβ¿9RkìŠ|é`¾Ã› â+ÁŒ¦d èƒÈᕹ0Òpáà J5ž  !ãàt†ç÷< @,ç/Qè /s^þ‡ëNhñåßzs\a`öes 6+¦ªÒ«(­?‡ƒ(UôZ¨ÏÚ+ÿµÃä˜Ðòç·¡§F׺0œ bi¦I&}ýDk÷®Ãœ”8TE¸…0µwé9[üÒ(ªqØ.ùw郷¿ŒÍجÕèÌ/U99&åØCæ´h“˜Këdk@€¶Ýå1>;}÷ªÈN{ ¾±‹¯Jqã¤VÆ u<ÌϹ&Ñ®žÞ„–a`aãåýµçýâ5SvKÃ#Ž IÀ÷Ytj¸1Lª™ö9á˜Poj#³ðþ|wÝĪ¾Ú¯æL¢ß€ù­>$eýi·“è`ö†å{eæ5@Pþ~¾yæ5õpµ¹´gÊ´üÊýÑ⢃TëÕ,iPô2¼„t3ä€Ö×aÒîW‘XÕGÀ‡û éaznÈ$²†ÆßŒ8½ üN}þùiîh_Å“H‹ÂŽ9…AŒú¸îú£ßqEcã]‹öµÍá 2½!@pHPªîÛ'wñóŒé‰ÕèÇЃ>:/½ú æ›jñ”½·˜ŠôõèõFlgÌb–Ä.;Q¤1:wçBûBq[ ­ ç )[=ˆ<ÙPñºã¹éï¢â,æhÀm^ÿÒe$/ñÑ;°Jæ„V õÖ×"ߨ”bŠâjôe´nïl<`æÀe4¬ŒºÒ w!?ˆ ÃDÌЮ¯=Œ-µ?î+Cº&Zj pÁ÷Ì#­µ7Q®—£"5ñvüT! SÞóD>2›Dv™z‰6¤!²‘9`M¡¿úŸÐ莜DáF8JX`Ô%Ž£X¥LíM¦ÔÊÃOÿDŒÞÞ-8t®‰8‡¹Y¢µÚÀÜ 0”a^blÈ~&í-‡˜Í3 <=Ãú6ÐþýNLÛÁúÊ,[ ðòI°òòð`(X÷†(@ZÅC(Ö46ñóÆ$x1¦×`°-ù6!}%¦q%x’ma¤íæ^¤ñMú >ˆÌ^ö®bæ&©ËQ¬ðM¹+£ÙºÞXuýÏ;o¡ðXêó‹GgkÛŒó“±é/ ëI¸9Ò¤!¸<¸ƶ%¬ †VÓ€µm$nÜtÍôÞàG™úbdE¡9™ðÂè5yÃÍLP½û¼ÉVÒVùŠ[ìéÏç?d×Ç*'Åìe™/(˜”Ô¤öŸ6©¾g] E5NöNn¨û}5KT©’Kd•’†Øž²eœÊŒéd ü`iL›¢@Zpu‰‹&ùÍô´ëUŠš‘ØIÔÍ  à"Ô[}+o¾ü05Æ*ìüṪ¯}Õû·[èú3ÀŽðÓFñßaŒÚ#ÄoÓQ"Úwèâ ª©k€`4 iâ0dÏ¿!Óåç³ù¥±Óƒþæd]ÇÖ¾G_³¶äán;â­&Á×·Ø)Ca Ü\Î|»*”è¿iÒš˜+½¢î«î©_áÖ¤%ï3¬‚”92öù~ Ôh?7€ñ9§W”C˜‡³ñ·ài0m€(ká0¤8»c8Ž1pkZÜž¬ÂîZä`O¿W=á:õD¹Æx|û ç}Áê·’ZoˆSC¤±¼õÉòFë›èÀ!O¼§Dê#0BA‘ óN¡KÄÄc7ç¿ç†Ò)´¹üZ^  ®êÃ*»!åÁ=/-úŠ2»ý™f9Bì&þf¡'Øz«yþª²£ç:6좑Šò/©ß!h/}½ï¹Ô é»ñ›Žoa“ªóáqùΩŠÏÜi‰åÉK~º‹êFN»Ð@Š8ùDÉdæ%ìaü}â&d+EaL¶@Ätà-y†´Õ¢8“ãúC‰s ’EÁ5äµ¶ÆŸž]¿YXÉw@¹‹øa<²¼™p43e£ñRr=hh\ÿáÆ“5ŒIè=ÖäRßßMñ x˜Ž“$P¤ÿ,x®Ú'cø‰³º¢‚¦ï¶ÁØ ÄàNBP¬!r!(±OœÕi¬ü™Èߎy÷¨<в˯óM´ro^øŽCnȧо"шR›0Ÿ°PýQI(VùÆî¹SÕµí4ý8 P)Ó¸Õk€)VkPt ·À¤½eã-iƒXu{Lj hÀfÝ3 2ÎFÏ¿Éq(qtOjl®T+øµ»Ë_ô#1‚-§"ÊåÕø!Øg~cÊ#W¥½¤<È5âNߌ<“ÖIèÛ4‘À»'ý¤™ÆLF¦pÏÙ“ÁÍêÂ22×<¨Çúd‡ûâ’B0áúG¤œï‚¿°ù72p´mêÐrK4L€¶IuDÓ×Í0†)À\IXG¸5TùÕ’ÙƒXù$d:ÂÍ ÿï<´ôY†y¡§Â2öñohYáT¬£’Ú! _B÷ ®d tèäÝ…†—Ê&âåìÙ£+!»`OÃêI§îÁs26Í<ë#©Aµ°ÿ $٥æòvjøózCªO¯F©oh*êROhAð@Õ›”±A5DW¸ šu )\¤h:ÿƒ‰¢{IÙQSãD‚¬ò2d2IÉàû.‰o¢lvÒ2SÁƒG¥”€ÀÃhse^6Ð>]Q¤q‹“©‘Nuñ– ýÆPDúŠêþŽ©¯'Žˆ‚ÉgI[_—Þ§íTÏÆ“# ÀÚÉ"À· ÉS[ëƒòIáiáð‘5‡¿+B û­›gµ‘lO g<¾Ï6‰gZtSÐyj":‚ÿˆ¸Ã]ˆìr·¿{tÍ‘;Hð«~”rý nX±Ï/QLÀŽ×…äaA“ùu9èi;Û(>Éþ•:L”Ä’¤áïû(` œÃŸë_Ï’D öd-°,C'½5+%Dÿï36#có¥bMÒÐw“³-)äl xº¡ÙÒ’³ðSâ«S[äݘH>Bš@Öñ;X“Š”üMuŠ÷{^â+ H`ÁÇ€A”†pÿÎð0™éÚ†“h£¦·CTÌ{ý‚oÛŸ!~ DNDü(Ü7º¸$Ê)ÂÖÓ¯,©ÿ•»“¢XR„ýF0:ý^ˆ¼<Ѐ.‹»5:Ú4ÚöѤœß€}Hc’J‡ÜÓÖÁQö˜´& ã:fÐ’ÂÃóiIŽ\cÎ[¦”ÃRçp?3@A\†°þ[b[(u…j+}`M%]q—ØÎ/X“@3•2Ôk8ñÕZÌ<N‘ =cBù ü}z Žn"³áÀkF m¯óNŒPúh¶ Xª‡R©$ë´ƒ øéªeúHPHâT¹ßL›¹?vµÍøc¡Ó[æC–D~Õj¢7ž¸€Ì`¶#ƒ%z<—«½â™A=£…ÿ¢ß™"@-'Gß@ Ù+ì6Rà…xjÅçXO¶ß©¬Û° 6dµK€!%í:é™ =¡¯Ï¯$7CŒüg×l¤/W¯ÖÐÃô`¼„Ì'XÏ6øq,hóï¡9]^®ŽÃY¤æÀ³(Úd Žú²ÃKY0LäØ=A ‘–ü? [›ç¼ F5tÒÍNÁj ßýIÿ‚Ò²ø2äÚ'Ç“ëRHØ|ò±ZQpÜô‰¿ÉÚh2©5šÇšÐÓº™ƒ|ÿøCb»«·ÏàÏ=q·“Ú úoÏmèîROæŸø-|Gƒ*Å}óÊŸEØKªÓöJ5jãA³¶¿dL|º-Ê qN¹žµ3ïo¶IãË@  µÈàbËÀQÚd?¹,$‰ÜÜâCõ-tkaïUA ¨;lÈ¥ªãî³´A²ÒuîÄ0ûX.ÌóSü{4@öí ¼…g%Ⱦ Ò‘9q `¶%¢ÃÞ#.!î¶Ã#ÖþnÖ‚­E©åKa"Q7ÿåD â‡_ôspYè÷ðfÑŒ–Ú5k‘ž"J´…ãbågðöDF’ÑLÍ:ÎMÂø=“âE0KÕa*É…j2ö°FØÃQ@SÂ.Ì]lSñ¨…K­ÐÖåjAdaâcã2¤©Gm4í,Ü„Y–>ûÔ4€è+¬-p½–€ÀÛ£+ÈàXa4Æ4èØ¹={±Ñ$ãk ‡T ¸ þAYORQªy"‹XØ=G9qDèil“7A“¡ýþà$Ñ÷ÀÃë+7è>BŒírI.é”l÷£^&%«©{^ÔalЖŸZ'(ˆE³LQúZ 3ɯ¬<)Å?¾?#i–O¢Ò"[õ,¦íTò'«JêD4¤Ç>Næ½ÍÄ@ï‡Z‘í¼EyPñ˜ÍïÌ-¶~Œæ}æï" ˆ§ÓF!£”PÄÖjˆ²B^ÿà)šÓI¼k(ö‰Î¶Dtì^ƒ/‰qpmõé a ˆíRºÿ™<ˆ s0àP†Láù2Ê5í¹¾‘È%Ý)ði†Ð·: ðÃrPœì™c»–„ãן |E25'ðc[;-2ÿÀ«¤¤¿ å…2Lbˆk0ß .¿ŸÇܦRbõ”¦£ ÄÆZ°{ïÌ ÄÖ¤c,‡0'yà üŒEï*\¾ÈŽ—¡+–õJ·ø&–Ú˜ÈO“„ÏKž÷@T g}„ ›5_ò,¬ ï»h-öRFHl´«(qB¨Ã܈æðzë”\s)(P}®è¯ÿÐV£¡? ¬iœ©ì?ó.›`³’ ùßÿ°‰kiEúîZ/Ú iÌd«dÍÝ 2zS‰$«ˆgU€çbbÚe9bÔ0x˜óC–òWQ&h\ö6ÍÈð*òáàV$ër÷ÿñåèùM‡E`…,µÛî°’q€a‵7i8ñ–ÔÆcÆ@Í s®_©Á<ó Æ3=4Sºh´{SQ ¦‡(š€U,T Ü",ÃÁ²_³\…ˆl(K#H_´Ð/EèéHEUµƒõöŽ1xciýcíö˜¤£âC@5¶kL¢øièÔ Ýt,`NœG¥†d9ñ‰ð 8µà#ÊÖgGÇŇ–†îW˜#yJ ¬4­”ù`7é tÖ:ƒKBi‡ [‘î³þ§ÉÃÏEJ*?m‘‡¬8°xÃe€$±lS×^>îåp À½X° ™…íƒHÐeüx²ìjè­‡è=œv­q ëþ;í5eÀªËk¦\¨§­†"OÈ{'»· ã>Ÿ‚h‘@åÑ j,ð€G  (aáìá««4·Lãá(:ГñÇùù£¸BPÏ@p0?HEÜáð)nA:zÃøÏãÞC•Â3¶ªìHÎ)<…ƒŽl&àÏJ'gŒc-GÕ}Ȧ<%ÇÎm ~‚¼Æ­“ˆJž'{r‘#SÁUm‘‡þ×áÿn¶ç–¶>Éxi-šÒ å'‰"aþ4²G…® Ò|x¿#Ë×Už’`€êÁXOó<¦>fΈoŸßœÂG`âö :J£Fâ²´‰OÁ`Õ$jb7‰=7¤÷YÁ%^çI>‚̓ÔÖ£b´Éž\¢@ÇP$±ÿ,]¬HmèW‹WÀ”9Œ6âò´=~»dT.>îuâ\ݤnà2~_´hÏúÃÿ:Â)§h”òé†L•¿ú0-‡ïçn÷¨m!‡ñ²t‡ÂÃÈz¤®C[ÚIŽºDpÆ[O³ƒ£i‡¤ûò²I0É—˜„ à¢SèaÓÞ©øÛb|çÚ‰ïâ)·žs=ÉüIˆ€>ªu$DƒÞ1˜QýyÚA?kòBß±WëÅèîR0¿ÉC.?õ[ªr Àº=->ô ÿkzT2äTW?ˆ[ãúÅFZÃ΀˜hìO ‡f¨×†§ƒ*EEs†ƒQeˆ?xEayÑKò‘†»öwìÚ\›ˆáV¾ÁÖ!ZÉaDvÎ’a<èÿ”T[òüž«êн#wÞŒû… ‰-iΧíÿ—Ãü,â½×f_ÿ×?wêWÙÿ«»»â·wwÝÇ7ŒqãÁMÿ|‘ããáÞ1wÝî\®fßÿߟªzi®àâQµ\8€Ñ;]ºÖí¾¸ö~™ý26ýýî÷« r®òë÷ÛJÃ]YnJ €I²·b¾›Zë*+>dw($uüÙ-?uñ¢qFg/qYq¦Ü7ìÓbåÌÏQUC+ÿ…ÊèÏ*À¨‹º‡BâKž$ÐHXúiuÁ¥qÁÞ™úàícY;͘¡2þþù„Éš³ÿéÓæUNªœêª«t—T¶KVåy?ò™™7b¢ˆþ+ªMÐKB†òÙa³êËÿQrqŒ$°i&šg„ÈÏF0dµˆä<뮋›[·«¬‹q14eÃe/oÄÿ—ù°nw7æü¦±$̃ÑgƒÞT)wæÈa]…%HQ³”ü¨ÖÖíõPxû×ÿtÄÑØÄçÍ˲à‡!eVû,eÁˆŒÓA½ˆ‡ ˆÆù—ë‹TjC¢€,çóæ»ˆr!ÉHglUéÆÜX£²$-pjiß›7×p tOßtNíºþëÿÜjJ/È)«\0@H =Cd›—T°=ªLuÙ… 0·¢ã”à-·@Mé÷CÉ!avÚF… ¾í޼j!›ÀÖÆ,Œä7½;)‘Û{y۹æ~0åt™i?&¬•X}0Ž1¦U3á0g¤,fáLñêPðÖ¬æ$aÓûåÍú€`.† £]wzI–p-ú‰‘9(º©m¼VF¼Ø¾¼ÖâÐmÊ;ê6”¥Ô@ÿ“ÿqƒmÜùC/7m¤²Ë¦)z(=z§²!ùP[>BÚ<9aœÔpK%% õçf8CL$9žB–&G)wTO˜èÎüö: «Ì£q¬òSEL}³Ñ(‘@”,žäéªÎaô³¿œ5ªúg2<ºõrò ™¾v`ûÖ¨YÞ‡Ö1Œ†ìk€‘<˜‹`vI]}ªïW%¤)ÖÊinÒ8sÉkM/¡Á ÿ/¬[%ÁË]p§ú1Är˜^â‹JGÇ]R™ËBH¸Õg0dß#mŽX¶H/PÆÎö$Ô«Ì9P ØGa(Ðô`+¸È:ÎçÆDŒ!Ú§ttCW‘Þ Ò+öÔu[®/Rí",ŠßšTÅ–Tû´_ÁM&¢YÀ˸!ûÆfúÿqIYiùbh6ñ"”Ð&fÿñdÓô~@Ô¦Ð@ „‚¥OëŸ`1cÁ—Û{ØýTîQyN[‚bœâŒ|}xܵ’•Vçԕ㑚õöK÷õ«‰¤ØØ¦1;Z©Ê˜žcÓW šSq½Õ[2\FUl&:Å?|ÅÏvCG(›MùÄ;u2ÀzA@鿎™UÑæŸ2bDÌtò1O…fÇeâBª³1ù3TUi·$Må!õ±‰e³Q²0KIã4 Qß“_Îùl/\Þ¡&ãC%ØÄÊi\REúÔk\WZ9ê¼ê».˜•ç›T@š#=‘QãWZlXùf`i¦ :”üݽjZân´r‰½ô€d<ƒ'ù•ÎÙß$„`Ž}ar«Þ¦¸Ð-~À´§!ΤQž£ŽØ”GƒÑ½–2+Cv¶ÀªVƒû%Ž®±žJˇ„ÙHAíº(æcO²a®}“º(U. £V#Ž@ÚÀÈ·¨¼3±º@)ý*À§¶þ‚4‘¨eº‡Ð€ÇQ´uÆÌ±×¡ô™²&¦‰ÅC\nÏ7‰n*ðMw r±!1× aªñje+Åê (ã%ieÁM‰‹ä›d·1­¦™¸c k¹¡ÿÛ><þ†]ψðàÆ´$£ó.‚7Ù/<·oï¼I×J9c,sJ‰5T²¤Ÿ477Œ V dUI·( C7‚,ôë@=££™Â[óèj8ð`7DñC:1¿ú¶h ”|fïFÌà„TFš?î6ï’}æ„&¼-ò;NÌ“â8}·SË÷YÆÙmc°‘6™´å(ä¥~¼ðä&Î]Ó8V†áÒ™p¢LõØ<>e˜meA_Ææã4ò¢SÏ¢XþDv¼lÖ $//ôúÅé'¾GØ×EÈÛoSÛ%öÓ õŠ·Æ—QhÚÍáH‡§C’Ø;‘èW* ·;»˜:œŸ¢&àÛäw§ n|DÑ„Ð˳¶®™€žubiÛåÓNõ-šÙÇl~²F½øºSüGÖúxÚ‘üK3=“YÁöœÐ`Ôc2Šcˆµ–…ð›ÎÒåu—h¸gÑ xê€9ÀÌú­5÷3_‰ÜýLë°]Öp%µ¤¶È$é<°ÔëàÛÙ®ÚzzðOÿr뼸Âêæuß/ï«äh³‡¥ÌþŽ9ðdQ%i“ì¤\æûFƒü–íÁ¨æf"‰®G7Ö'yxá â­çü!Ã/› ÛC/‹ryáfÀJØïËëg÷ä舿$êmUÆ·Ÿ8Sp¨P爓/ú4/Ù±R;?}öðßæxúônj£bÌ:¿œÌêfPJáûe¾±«t½‹™©F5…zêðx£Æ¥>Apeµ±š÷©íý¦IØ_{×”oË`¯j‘·”âs²“ð@c¶ÝÚ¤ª¨ˆ¾’S&ÅZÊD©ã¤àÖ!iÙk@Ê`M{ƒq:kÏq<`>|Õ¿OÌ+ ·†„á8û²Ü²Ïy§á9Ññ>¶¹r¦x7È##µî(fdPH:ߓڠØ@9'®ka\ˆñ  ¼ÄƤgLýLjŽlìbƒU†é¡\+—Ý BéËLN—èÌ8•8¡ ×´k¥cÇ·9å{NíÜ©÷ße?œwòÂßJ_Ú%‚]p @`ƒåÖ”­³dã¹–ïÔ&²¬kGÑžÅ\³Ög˜UØ•ò‚C»Òh÷ÿÎv˜úÌf°—Þ»n{c/áÝÍÛ_Ÿ5,<›LNê3få½<6çS@Rzf¾içèS#@ ©@‰Üe†.u*=|¨³@9u"‚ÑAl¿ã3ÒÒ3Š3ëV½Ö®Zɲƒ—Iâ81.9à>^sNµ¢Ì“†CpÙtλd`ƒÎr2g’ß§ÇÀ>yæàxllëÀ‚ ˜#­ô® .xyüŽ^›®7ž(D‹GƒÉ‹œ¬ Z¾õ¸LAª`>GP•H+隺ژ05<~âñ­¯M?¤••\ëK Ò©i yPÝ[=Xt®™æú6zOL„–ƒ!X¨(ãטÇÔ–œ[zƒô0óµK¶BLL"ŸÛ–%†•¿,© ñáI4¬.i nÃÕ‰N ‚~c Æ…{.'Õ€WÀµL’a¸*ÌŽÚé ´Ö©TðÓ¿ˆ!Yï3„,rä}Pè^©\¼ýhßD‰œþjªÃ£I‹¦“%gý,¯À„{/ˆßسjÆí<%áÁÎÓñB$4°"/ZŽÜ N{ €ë»×¬ˆKásŸg¥ÁÒšÆ/R|0¯:¿á£œ.‚v|XL$Õ}ÅåÛJ-$ÖiÞòp—?Sù9™ EКàê„ø±–mä—£IŠÁÜ’v¼]ó&°‡¯KüW8Å”úî­f$×y:†‰›ª ½©þˆC$‰ˆ™-F¸q­jßÔ¢ðELuº¨áó„‚H<ÑŸÅSH­VÜdymîK‹š<óøg¢`þ`Ö™ö3!z•g‡ƒ¿‰¡$iˆÉÆC—£®‘)r'”­þ—Íe˜–›…´Ïù‰ïŽZ33!XÖ¨•П8¡¢%^ž—Ø¡ŠÖy ÿDÈÍ!¤dépWváE&·ÅÈ<·Q_ÚeT6F*´țUiZ‹N‡"tj >ežtý@R9LŸ’PK¼VF@Œ)~^èòRE¤vÓ2â&=¦ÎU' zIMNcF8øðúâmw9…ͦ±¡ øŠÏük=5Q®Ýº$de.’븦S¯ga…Ö\ûTÊ4b:p<‘a§•¶(ªnò C>ü")A§±ª˜-E¬LÂcB§Ód¾¥püæáAˆ%¶@uxèfÊÍyˆ=4¯M‡ýM²ófšQJ<>ö ê HÜ`HÄü08¥)ÏÖ`Dip%Ɯᣮ8Xƒz¸i¥Oø¯xz*_·‹µ[äuŽ“þnt;vŒIhÝ®Â\H¤1ôpº9Ë5ãFü6Ⱦ^­:HÍNSm«j4†Ü q-l mÚÅúž“¬)òáÓÖzXU+¤‚ز\8æÇ–¦·*ØÔÕòÇ·ˆ¾ül=Ý¡¼HΓŠ~ŠÝ®È$WOZ—d°â¸¤ESV»ñ ®¨ÂáäÄEEMEcÊ‚2ÛÅ •Á™À x?Ž©0»OòP#›ÙºÃÕTPi£[”Є¥ÅyR)p äׂgaÏ•±!û ëÕCÙ½^û­ß èúGÙWž­jTþ#¬+pe$¤j¥X×3å?JرÂqö´©%„Í'¿Üárœ„¡~FÜ fØ_Ó§-ð]þÌ´ŒØSsiHrå“‚F‹oÀðrÃt‡) ˆ¶ºø žšB$Ä˽<Î*¸K8K¸q:&ÐŽžÜq%RŠW'ø-½$J·W©Q¸Ð²4±c£\ši£Dv3a¢ÄßË,œètê.HÁœ ‚+Ñwè}© [©#"#çé•#Œœ¦6_Âí¸çÂs‡¹ø÷ˆ< Å×9,9òiðlúNу23…\‹?SÚÝÖ´µwæ& FÉ$4Bewë(ºÍV@…³v!rkt|Y]Kf6z4÷­‘F¤çÁ]°þOcVˆòÊ8=– c•"ºÉÚ4@üBH@b®¼™ é!¨ï5n<"Þ/—H~ÈØîƒ+Ö˜˜ë±&ã#ܼ&h'm–u)%®ÁªŒ ªƒÑ«‘~’›‰l5‹GôÈLljg¶ð¹Ô„Éòs õ:cøœÁ(Ö§IøÚ#Qñ5PÃÐ[6߆ÐjE3keñ¢ êÖ¶[ ñ­Ë²lØÒ„Ä•1êÌ”Aãøpä*3º6l “™¤¤74žz'á‡K„Š ÿÔL a·—´g*Ì„UÊPðmnµU‰JIÏa£Rcˆ4½d we³O“7EdhZ)| Ĺ}Xí†ÁñõE\I<6‡ß©5» ÆV»ÐÜäïÏ,:ÈIÐ.s=ŽA´;6S¢pU--²7t{1§B“Sɧ#©TTŠâ‹ô×Óì`èÌúýä;Lm[a>ñ~$à÷$ê´ É#Ÿ£öƽ$¨~ŒÜó)ÉY>å‡#̾Рf(‘0êy®œNÙðP73~Ÿ Йñ¼¬pÒÓb;㉰_s7töèxT|†ã#D8m°‹?¥uúHÊ.øÍvPúõ’³ÌÝ«~ÙÑ*ûg±ýømÖèØ ‡+J­£&ûEf¿üZS@;ÅZQ²|æ£Ý§/Çꉄ©Á×"œ_O7Cßµ¥ÂöµÊ¨¨qøîÉ =¥ìA6"eòñ ìêqDnûýÕÞŒ2„§ ×0Á>Â(°”ž”b`‘7¥R ¦±e0ifJCœ Õq[TÍÃø¤Á#ûXÆ”v>ž$Æ–¯iwÀÎI0 ù(F‚¬Ÿ’™$—½ÈíÈõ Ýrñ2a܃¯Ü9•,Å £XH°ñúÆC'Šªé¹×Ó ÅH:ÎKßYÚC÷6˜ö§³•æ’ÍaDê’“ñ/2N††uçõè‘dàôxZ•¨¤vÓc^EtáŒs4 xÙòOdB© Kí«=>S± H•¾ :2cˆºxŽ0 ],û˜™cÒ ‘Kìc x½ï(ÉM%t—àW’Ú$£ßãB,ž¦¹sM1›\À˜ú †@`mG7ïI@¤6½sî%àº:í›%éðWO \‹®y ’?Kª6 jÖ"9­ô5/"+c‘1qÕø’À“Vì YÅJ,„‹ö°°”ySe[ɪ,Œ:0)Sü6˜b\šód¯j"/?ü4Oa˜4ÍáØ ”MŸàœŠ¼I¡ è‹v§ëâ¤2G©0Kö¡¨ž ƒ(¦|Í øÇÙÇOƒ60h_(Ãn!‚ç®65çÏSx •kKÃòGÏZ…vž®xÙr’i#ÌdD¥Íèd@ÓCðˆéü0î}²Š2Iº•® [_ªÄEŽÛYOØûAæ;}n>[ÃíåoÕ¬6MÈ¥M¦iòDÿ‚½oè°§ë kòcâÿîQ„¡TW!9×gò#µ;Ï€« ›äîíF wËa¨>“j<á$]?8ßPB ·jHÝ”ia1ØA/Æf&$ ÞÈ_>š4oon+ ®–MÜñ™*²…þ•X+ÇF¸Yê‘õ‰áÑ98´—‹õù5–dY2Xžý^ίö‘#ׄmw }èõú®‰66 y$Rrˆ:[@‡·ã’2~&Ž€’RŒ°¼¥kÝÐO²°æFÅ03$éP‘80—‰H”(`*¸q¸Nø¹1‘+aÖ¢Ýø68ì&L<±0ìQcäÀ–`G±v2Ò´°÷®*å oS˜¯TD÷åUö;¯½ã­öšT@ÃL^˜ô;Ö.Ó ?F}[:äôsÚ¿ß)Ѥå†cN%eø¼m¡QF»$ZüKëcq»ñÒ©ØRc•Ñž˜¥²‰‡ïwÒHÿ¼··›ßÔ½çv¸Â– •£LÌŠ¹š@¯eØ(·€Tm!àiˆÕÈà Û?6·-ŠÙ6&«ka‡À·Di–(þ­PÑÿQåàH <7úâüP1luéoÅÁ¸O–óŠçè둸 ©ãŒ”VdׇiÚOÓ#Œi¤|°’ƒik@¹¤ˆ?ƒ}1$˜¨ÆYÂão"è\*)Ø’Ìc#¶ŽÖLÌ¡ù j'…øžÒUü©¨@°£ÎšÀ1 À¥òYÞyí¡0KÎfh2gia›1H²-á\ë–y/¶4Ô3qBò§Bd¥0dÌ…Oª4_î‘€-Õeˆåz<8H'™­IXHˆ,¾l…ÎÃÄT:+{†‹o/oû|ÁJæ×ÍBcÚ é Ö íÚ‚Qâ´4 ¼­²ç£:„°ÃK]`2v«„‘‡æa§ð!bñm;Ds ‹=~ûš8TME´ßÿüçŽ?ðK»¼ÇÌø¯wû»»ˆpóB©ê¥ø¢ÏëòtÙ̆Ո¿ímdû­B‚¾±wPQ íÐ~ˆŒuó~^CÇ ¾›ÿ°øæ>h..Žáçÿ N÷wªñ@Q:ê«$͈Q” kowBM|-êù}ä6¼v]°ðrˆ2ìdã.Š §Ìwt#Kª%¹ß&,¨ˆ½ûëä+`/M­3¾Ÿ¾ÌKXxªvcs´Pcð’Ga !¤‰ :ímÑÖMš[£÷ãÀ5]÷p:¢L±Ö£¾<ÉËHWª@Ť¡vÛÛoÌq¢ÏÕUQ–%!ñ`À9ˆ^MBÀPã‰Áø?-YÇr'×j5 fØM˜OÁ=R\–à9$GÏspÆ ½  {hÑ‹w¨ñ^&‹ÅŠ.Ò,gY@4”~0Ð;ï ¡Ìl ¾ÀìÈYI¼{’§øQš³ÌûÀß–>9ÌßøkôTàt+º— UÓˆ²îpš KMÁo‰í“ÕÎ_;cË@ï¡â¿?㇆S]2vlD¢µfÕÉôõ<Ú} V%%æç(žC3T,yäOAóLþi ®ráQ¥«â¤Ú©úS/ø×4üëö¨¤—žö殸¼¯ßMùøØ-xÀß‚‘˜ à·Í<Ò³úÇ/vŸ9á,ðp=8ÔçPíÆ{\T½ˆÿÆcø¸ ~æ8ßÝÕ|ÐNòØ:²4´5Gî$5*±Úƒ˜Uj+Ýòýy·|Þ±-ʺiåÅÞ¦+z®U<òz7©Ç}§ý¸÷áˆi&ÜDH‡¡¶À/Ý"9"é·…í¦ŸõÔÊ1;™*c{PØ´%Ò™öA•-sí‰HÐQMDÊ•ô†B“÷Ïß•“Ò-F½5¹, oõÉœvZ®_ ½È³¦ÄìÔŒ f˜Õ¼<à`ŠX,£©Ô2Q°0¯Ó®ÚeôY­tJöígÈýÔÿ݇5Ûû®ñ·ØAüÉNýUÈ”Èç‹ë‹èzéèÃÄaû…!™s†MøCD©Èø™ÍTÕ§|¼w]JýÍ—Eñò1_ø™ë«²ªZEÂûË—ì4Žà ”BrOú§¾¹oæÞ24*`fÍ¢¿yyqW3TõÍGEie¶¬bùÎR¿$ÍW€±ÙjT «Àÿ)¸ê`Iû”!1Ɇ/0ìÀ5ˆGxzÅøƒÊ€p€U¨…G@0{ã†üßñq9‡À)1Õ%Œ; *{$ýÝ5`s.ñjA…­ÐäÚ~qûP¸ù˜/[æ G×sÒ²¹ÈM„Übñ®à¬ã÷?ÿB¡Š„—²èWÿv7ùP«)[8Œ]$D‰ˆ{´‚ïiÌ=£ãÒ=ï£UþÑ<å¸;p(lD¢Ä⣰&VÏêFK‹rÅÖ£Ìå‰zÛ¤3ª9è#\ˆoç¦*‘RPRß5¸ñx‡¯²‹l6!Àv®OWóèÀæÑè 6ã  x.¨Ôž”œ ‹i4dOs~p¨¤Ã]ôF£´ÛWp òÈìÎQcrÝÊ<úm†÷ȱÑAÖp±Õ¨gb›½ÿ¦ú(±b;¶–7âØ')Šã<ø™¢¶VäF ø(öH æAóŸBµ"Cµî]¼o?Y¨Á§X wóí¤L8@ƒs”¢ú5€ 9& Õ3"jŠÇ ìÛ^¢ˆ?iØo¿r0ɈԕG˜\‹ãyëÃ4–A¡OÔ=¾»;?`a†@ÝWÌ&­Ö?}ÿ„E ÝeÒ™a°5(oóñÞSÀ¤EÚ~§l‘ãüŒ^€ yÎ×ð>ÏÔ²æqoòÉ»l„¾"Ô)F:U÷û§™$@2Ô]EÐf/äåTú2QÔ'£xãëì´˜ÄAVô®½r”8T3±ðZiX5¸Y™g YªônÙßÖý¨¬¸IdqЊ³HGB(±8„¥¤:!šbð!?…׳4N@äu3s¬Ñ(ÝúKÏ"ÌÿëŒÀM¥È¹¾é~pôÄð¯.iõu;:IÐ¥w¬ ÷ïÿßQ6Î4ÊߺºIÖ²\xõ¥™5äœÌD©ýÜg–$Aäyر¾Æ{jB™“Ì1ÇŽÈ=«e‹‚Ų¾™„œÝSמ㜠‚K‘]¨í.§ƒtBô*Å¢‚ˆE€=8wØ_þt éÌšO眀á#Ë ¢Œ„€WÆ4Í)àÖÑ€‚˦Æ3òu¤vU ·ÄòY¥Ýe²êjYŒ³°~G£Ö£m"à[j;b€#¸†düDœÂPn÷̹»dè 0Aão‚× ©¦£š¸´é¤EÙŒ$ ¨TT<ܼÊ%É™Hù‘áÔA&, €“öãÀCyº œ¾æÒ¥ô¾aAꡌ‰†¿•¢û¾üz xÆQhÒ´ˆþúýúÔ×ù Ô¿€íìh¬Ì%²ðÓmþèAÚÐz‡>Z‡ýú¸6M4>®])¸ªJ.ë?ÈP†,÷GÅ£œ‘HŠß ï”$ ¬87„âmëa]0t'!vù=ÜJéÈ¢nD7ЧoaÐ<ØAPxArÃÑè¡òóÍfˆ¡œÁüÿ™ZŽ5ùü!n˜¢_ÑÕËÑúÅs`\n"uÞ»ÔÏ zw‰®õ\9m7KAÊ`kª áa+µÑ•ãÂ[Ó?¾ôò3fx„À@¨GNº×O½ ¬ÌäTç aÊçs¯3Ñ/ªíZnK<½âO”¥’ä©é£DìWYŠõXÐ Ó¼ ÅÏ‹tFJ×É«`vzÓéë5æ…ÏÃú:eå%íªmOá°ŠäÔÎg¾’˜Å$²ÊÀ©2­ºÄáI“^Ð(ÐâQáð^VѪŽN¾ü±Ò^ `èE7A¹‡„¸õÎì$G¾ †×Y×@19€œGË#4šî4Ȱ£wŽo”—­êÜL(ÐÐñ P4•ÒÛ“ !%žRa¨DÇccN=ª0¼†üôiíÜB?®>ÿàÖîON¿rz÷ÁåZ'™ðJ9€¹Øï³J†6h·÷§¢ìJ< ;ív!·7Þ‚€3o¸<1+be†ÞˆÜ×BÉàÖ6%ʳ/pó7é”Ö;-˜„v-ÎXî’NxµxÝ2 (Dœ­åÓŠçδ¸8XNàX ÓZàÜp'#Òž¶%M-…”?µ÷¡wÄxÝ²àžƒ vðKå蘢eÏDUÇAÊ>“3ª¥<ØäÖ5VáPVÁ©MD®_ æ ³o‡ö›ÃY¹Ì$}óÚ¿%~ìÓwÀÍ/Ðuþõ¸äk¬æ¸þÜ—9Žv4–‘3Kÿ5„Å­õ’i® ƒÍtFNSóf@ǹŒŠêÞ\ñÉ/oºVÕ8lƒú527LÝc >‘•pÓJêñ8új ÷$<%Ù×î·ì\ä4Æcùxµt‡ªöIwà)‘:Ÿ©,ªôt‘N†“ ÃŽKÜyºU˵Ψe}éÈ¥nBP –[ˆ|‰[̛۟RÜŽV3O‚(¿Ã’ëTˆ4«&Åt÷#plÑiiZ+/ Á\ŠÉŰzgyíñ“ö÷ýÉ!4.|o@9º “§ß& §,€ða©¶‰X…tEBÇM¥Ÿ…ßÏ$aì"\¢<†ˆ*Ú¦«á ŠásÐ0¥\ÄÁÍçMF‘Eù™b§¥¬¹Â½úÁ5„+Zퟆ’Ö×Ú‡eÛ®eÉë€=„HRt©†½ÉS†#J‰ê¿„‚„}lÖkNœ8pkxdv¶x<ö¹ZÅu;B$5þ¬U›¢‘u)¤ACæ‘’Ÿ Ú ?dQk„¿èˆPº>à$ÕP_ÅNôÃù$?²p–<ä  \”¶ØÆfy ˜ D³ÍÐn³Å¨â̸é,5¹)xÈÈ–Ö%aÌ£Så½&¹ô_ÁMðô1ÓR ÆðP²\m<¬ðøê­3¾½~YÖµ:*¤ sÝ.VÍQ_Ʋ‘; 6x!ük:kM:Y´€W´î°0+î/Õ”æ5<Æ‚kpLÍÓ‚j7¥™uBWˆ£¬Éò9ú‡¥& Ü=ð®±ÜZHª6€~;h†|iPí'°?š1VMèÕÉ|•¡µ -’?ÿ¦T>†\_Éþ ~I"e$aêB3‡[9?Ú Ö¡›*ŠpÔ¸é\ÅßJ« ;R4•žòiFÒXDˆ ­YªßO¶Ôóo‡ulÐ/Ití9hNN$’”–Ù,ì¦Jˆ«%© ŸtGqÎa-†Ìiö¸ÒCšÙ„oƒé¨âÑêŽ#ï; \’hè‘p"ÌGí¼†""PÜQ‚Ú¯/\g JÕÚ(¤2ñåÂ{áj¶pÝ`SY@FxlÛÑ 6Ø•F$ã÷Ák…oÂA¯ zÔTc\*鿵„7§î3*žÙŸiJŸ¼ã›ðÞnеçƒu ™¥pN„²tl2h Ø©In$PçO²O|4¡Ï³ò¡Îå‡K»(Dô ,à1øµôyüŠö4ÿRæ3XÆXŸ=Ëb\ÏÊÏgà:$¨û ÄØ˜‘,ÊöIˆŸA¹¿Ð{Ì:;¯˜%^ÆûO¦ŸfN‡êi–²È!ó˜d€ûzC£¤sùªçUÏϾ;Hמó+Ï´ù¨nŠdlT8fY5.Ë¢Ú}Ô³Šß~;6²÷Ó¦œëÛÙ‚Ka‚šœ\‰Z¶V|¬ì‚>ÑÓ[" Y`\©9Æ»±~4¬Šý(<É0R&éÊzµg§;>+hL‡#%Ò}¶þçeù£˜­†Áàí½“# à0_<g­’ì}®ÌL51ˆÊ@·h ŽÀ¯šKÍ„6»Æçõø0éÌQ¡¦hân%„<Þa !ÉK—€SWÜ ·öµÅjº‚AH¤Ç¸iý¯|%RZP¯*òø—Z6ØÆ¬­Aÿ±¥Ù"{ v™ 1 3¢k ,øÜ„®ÊGðDùÂï`ÔŤ(Ä@¬² 6D¹3²ŒãaóIiIõ€H r#Ëõ6õÂD¼WÍ~x| }„ɤÐb4–C"“ã9À½ýï9þÝ:’¢!¥„x5ƒ ËÜZ<XÖÙÍ _JT’™vq Ð4…òĦNbFµºcÑö‘øD)äS.êp—Ñ3¦»À[?Žëw=ú½Ä€˜™,¨Ô×tÎüxt·bi@û"%þÂ~´ÛÑé@ý̶rhèKæ­ãƒjýjp–¥i‘ã#UaÌsÇçè ä ñ&º5Àï£êªò¡ú(Õµr¦dŠ|šïÙ]:: ’µáå0Ô¶ ,$z9iöé?W~ïŸÀ[ÜXŒÉú’ÆÆ€‡“h,¼šPb‡R³ª ×Qsq.éT 3pÂÖ­nq¥å«÷ŒFA¿WÛ|牰}í©§%Oôˆ›XònU)½«Ë£’¸Bð¡@j&ž†°® ‰™à…:ÛÚr#îMõfžŠR`=læ"[ý9P· ºP8í„\ü/¿Tññ`vŸ”‹-»æ’Lö¼»*N0•Âûè¡€ÐnÂBÑÏó?ºÆšÌþçK`¿€8Œ€hCWÝpß’¹\\‰©&ÌÌfMìGQƒÁJAàƒ£þHy½aÐ4È1•d†²xh|?ÊÙm­?x§°ŽFÑýÏç3Q¸‡šgÈ[ÂX5ºk~†Û6@u±c *%‘ó8Á=²}˜,@(Z‘0ngoEÁäúŠÿÉ¥>ÖAÁ|5ªÒÈi·ŸbŒÇÂ,öµ„—ÆðS!›o±4Klsã#„©¤HC¬÷œí{¨lÌ­I%’Åx%ÿ&b$åb &‡Mµ„w®¦8d·Ð!ú }Ü)uõ¿ÉVÈ[ÞnƒäÄ€nJUþ‘I $*îÑÔŠAy– ’ŒÆ¾øÞŠˆ²êúÔ¼â&Œõ|òB'u“Æ’³c?©ít„Î=’ ¶º§ä·ã{ð¯1¢ÊÚàèâIà¯ÞÞ,w×¶”Ì—L•ÍÛ"6d¢íü™ØèJÈJ5ÈR¾Š1Ô[ë~¾g÷(®C€4­‡yð…²(ðVZ¾[º•eËqÄ?Í™°:ìO1;?>'ù“ÓOò"Ë~\‡êºªë7Æ`¿Í'¬É玟á…×|–K 38R€5ü‚ÿ·Oʟ𯨠»ßß™õÆ}±ªûß^?Þÿ©t.xY{ÂÝ(6u„à;Ž“øóUùUý+5,é¬Èã˜#ó©‘r¥"ûªR"Ÿq_kÄþp„qX!‰M8k‹¼ÌD”ľæ]iêaàËŸ¨1(ÍeÑÑpá´†¸)øñ¢žü3w1t 5ÌÑ>lCû-¿5½StW¾f¨9Å*Ê^+Š»wSiâ ˆ(}ÿ>x¥iXU1_§?7ø39ÈVoóPkí¯—1é2I®ÌiW½«Û­UÓŠ IÉòk®ÕÛÝþÀ(é<"7€ó.#՗׉U†¢±ºÂ3q•ÿÿ ^ù²«ˆXÈÁ?dQIω¥§¬aPÿ@³®½Aèü†SçaO<4v?xB&ºh l&¦!  ßÀ0^PuâYqñ¾xƒî#ÿüp`Cª"ií† ¢G}8#¯bƒ^!¨0±Î&ª«QÕ“ƒgƒ‡œQ·[“¼Éˆ5ÚÏëy”* %wÚæ÷ W[§ôK¼~aß:?d³5©Ë½m'õ8âMtÆgÌ>?òS/ž…'¦ùñãÍZô¥ÉJ0fÇX~>jÛ{úŒxVN [¥§:*¼‰Zy$œ¤tœ­J9¿r™Ï¬¸àz TÓûЏ;!&ú$uJÜÁ=ÁiJ+Nñ¸2 iŽBM! >FbαíèêÆî­¸j\Á„ÎÍ%€¡É:TÇ+ïR'¸9êÍ;ƒ v+*S.ÀkM7¢’Å|QUÔ´MØH…àû(»j2Œ>9»¸¨ѽùÎÕÍvãËÆÂ`y’" ’“æê‚W]8Û6˱…®¢«¾B2mMhzKX 9?ó5_ap ”'‘=SŸ{4‰ Z’$h¸xG4k_ â¥$Òh»ˆù˜õâµY/wí0%E<À0߃âh,3’ø”*‚Áÿb®Ø«·n¯\{®‡êµ¾ ^”л#AfTá£ÍdÊ‚o®a°ÒI2ku»WxíÖP\Ø/ÔÆ"$8vPíõÚqEzã!h—ð¹ vv¼ýPnÜ=e„Èäæªùµ6%+K.¢j1QqõêŽU‹\w<¬v^òÚÁöxŒÒPý'‡·š–òAã;Ç!ƒ_¿û+{†H€ G©¨þ”BÀÿY•àtÚn"ŸÛ+G€ýÒRd³ 驃ÔhU<8Ù{¯ð¯TWRá°?,ÂÿÜ^÷þ}ñù—KÕþÓëÄòWóÿö ìØåýî[n¾À®Wþ¿0D¤TxåП âÆ€+ ¬±H£¹÷ÉÝärÐʇ+UQ´°„ÿzo½¤g’¥¸Qþ ¢î®p$°[n¾oÒÞ‰^§HVOë#<=šðÀÔ®dÛ«»ýÎegØMÁÓ?$PB´9r8¸’  X–ßV ç™Sg :4}¾Ã@'keÒ{|ŒqJø'좈ɖ1xÄp,—J£mq&f"ªÊ%{3ª®@m÷ÉM†OÓûš ¾Ê¥sb#wŠä#J;™Óu”Úf3wVêkÆÏíFâXâ÷÷ž Ÿ+‡Ú–7•xðcîJp&ÆÜ½o 1(ÞÔ1ƒvh9ê W"?ù¡>"õmÒ{qÙÀÄ€´Iq®$Ø÷€¢ÃKfÒ¥"•ÀY$0ñ"4²¸+À Àcð‘÷ßÔb•$rî¡¢ƒ:²íà°¯/äây­¸äf¤öIŽíyìÔ÷Û­2ÿÿWX,‹¢p ä7¨F ͽ|òˆˆÏ9]üâFzéºSV¨è$îí<­„¥ $= ³Ò‘Ü?šÿÁ€hBÕdqAüg*)¤l°&òÙâ`"(Y¯ 4½cýö‹•Á鈳×Ô»ä/æ†Ó<€báÁë _×ìjg넟TV˜”J¿ÌÇA«?7äz2xU©Í°æx¼oŽÐê†ÂòŸ  $†ª£æëM4°a§gÖø÷š«"–6›X$1+‰ya~C‰oøÎÒá;.-p‹®„wh¼{½ŒR|óxÂúÀ<š-ÒÌ·š_,k˜ØûÐÿ‘’¿¾ïôŸ–`Å$D…íxñÁãd ‡3'.ØžŸà Ÿ„µ- (¡Er¡ýÃXôSE<×-ò½)F?ÕÃçÌn†€¸iÊ!¥ íh¦ÌNù*›$€þüï¼ÞÞ¤*LH[¸Hr9¶"@ Ïóø’£ qä8Ý…¥»x¶Í½W–¦KlmG¬IS+"!ÛÞÅ‚ªÎDAåI\/¹—âµS ‹°( rS/ÿÄh#$,•©BFP˜Á« t ô”"í½pNb`Îÿ~ÿ’ëäp¬BO„­w…0[ü‘A« M)R¸Ò¿m½îò1lœW¥Q{Нš[(>í\ü8åTFBÊ–ušŠüŽêØž¨+—¿S×CχR4cÞîcÛUàòrÂC}éô8ø¸HO´ ø)ói±¨äžþÃ+UÕdtolßÌ,ËV’É9>6Ý|,Gb%o»Ï\¸¨rqù \€Ö1W%d°ƒŒ@%ÌÔ˹föFÚ¼òéðå»ðItùÏAy.³„ÚL â´Æ 8º¬æ vè¹§xg%…ß ÓÍGÙK¶{:’ &`Tšü7ob}RË”” ¥HöÿÀº ä!8Awaž”ˆ_GŒ ŽèáC¥€‰ Æ$û‘‰‰ª2‘…¦ÐèÉéŠ: Ö…#ÒI%¾xLfÐÛjŽ`è tQ0\„q™kîèç¿úêòƒ†@—a4ÏËа6àõåé*{)xïüä¾Ç—·ð lë6gðLrˆ :ëðq^zX¼_Çî#œ L ¿L·ªÀéŸÀˆ ²ž°D.Ü51{W’3*µU±Ú?­`+¾’ËÄçbƒ„‡{ÈP„Ap}È¡k Cj”ÜUŒƒ“‚`©rìvd/Ï‚€4‚.=$^x^g,0ÇÞq÷@-Šò…Hb̤û ›÷š,Ô´=w±ðºA œéË@?AÛqóKÏfÒ0Q'gµEÈ\Ì—¬,LV+¨(Ä÷êéЊX<—»ŽïÁ”ð+M!…ÿÞ-ž Ž;Êð $hÎ õvdpœ@¯›û?š?abÁ^ëH4øI"¡®¤ô¾‡ìÏÜ.oC­ýö0‰ÙYXÑœökåò†4~:;À:OdÁ^øðö„¹Ù‚¸ŸùÇ* g»¾Qžø|kÏ5 õ¬ðaU+‘®âÿ, $ŠðÆXzlÙ‚éØ 73¤!|^ì>™Šîö£L[c§@7n ЬÝÚ° Éü*uî¨D*ŸJôK”¹6æÓè7,m¤ SC'°eSFgvÆÿ$ Â÷ Vüü Š]â“”ÏGòŒöaéf'èm6™¿ö£Ñ²“Œêý=|]ÛršÏÐÀa-P¶àß>^çqF†ó‹oG*"š¬~)ä‚‚„¿F‚TAŠŠ·^y7u(±UqO®Ü1tvô_æ½ç¼^E0¼éÜøh`£ò<}DÒÒ˜U> 5sT«é“Ñ&t|9 –)tÙ‹IöÕ·óã ® ÇÖš‚SˆØ¨Däf¸'‹þ¸Ì9θ£’dËdýÚ£ñŒc#•*×4žšÍ?¤"o¸3 w^cú7µÃê2[¬<_‚uu®|²Æ²ÌÊÛãûÏEçÀœ 4&òÖbU+½Ëp¹ðê:¨³v®˜ cÌ7Îø%í„¥¸MÖv“@pêgAgi/”Ì$À [>ÉãzÏ-)gCé7¡¹ž$ â¶ãK¿HêDQívû3Àö‡ãakÜ3„‰Ð|FI9Á >4€ù0«ÉƒÒ½èªŠÞyCûÀÞ¨ 5´Ø†_Õ%˜¢P µ9oÁeZê6uŒcû@ú/ }’)ô=À¿m&°Ã¢YÓa/cŒÉ¥‰ÌâìC”š>QVDZî˰O?œÀ¦`iv4#Yj×GùÉ-Ð^ÁŒL ŽaGõÂòHdŽw›¹¨q|NìcY^àÜòJ@b˜“–Ze¡]¨À¶Û8V leòIáëCœa7²òö£ÙÑŒŠò*Ëþ"æF„+Å"xl˜I¸’'3eu¹æ¼ˆÁû>üÜœ®¹1ôd ;| öºTN‡to´KuÊ1½ ]/IQ¡d. JxÈŒ2Vóÿ8-Ñ ñÆäÒ6ѧB¡²Ú' ›ÚÎyÎGÐí\™í‰¥b­ú.ýÐìdH$âË›üUÛ¯¢Hq>yaƒE87bZJ.í„é+šÀû5¦Œ+É|’Uä·âZâV/Ó·i€cîÛ] À:@Ävb1ѼkÃ[mññÄŽCÑ9Ž*_mqÆÉ‚÷8ñ˜–×Ï D¸–Å8™ j*ʃ­sNaù‹MY‰•Yì4ÂcZêÈÕ*”ÃôÄT¤hjœÕ8åJ†‹öEQC§J"3pÚËíY4·rìO6Ÿ)Zê„6á$øl/ñœðï^WL ôÈš^>'†Ënu9@ßÕ¸U/ZN ‰SFjÓ Z<èw-ʼýK9ô¥rŸ¡Ôk¢ý÷^te,L§ßäh=/#ä;V ô´"¢b£1ÿb¯%éjK›?¼h¥4§íF1Ð Æ!+}3pÁÈ’Å3z137›¶›uÄ›¤@Š„˜w%Ú…ÿ†b€ä%FµŠe‘šÁ52 {1Þm7 Ñ3µûQ«Ø¿ «Ž±ÑÅ g >Òát¨'ˆRðñ?Mq¨EÊ$xoîãŸ2Žy;E"v’%o%º@d=k¢ï ÝÇ3F1Ž2:ÖüNaûk€Ï†³i^«mèa04z4ļ“ÍÙ™—ÃãÜ+[˜:͵tË’”jŒ­ðÌ(àÈgt¤›COÖù*P3‡ÎÓ2Y'òd¯}ö†Ô+Ì ¦P¨“ðR¹e´økg<üôõ¢2=Ü,©!s úËÎhí£Yî)-Gjp„'$'b­¢+‰8t*Aöå¨gigÈÆ[ȼ?„ŠD}ÅôB½ª¤•UOß:"t H”Ô¶$Þ /HK¯û¤ÃJa_f×nÑ«ÄÜÅ"¶ßãÕ|¤Œ–uû!’‘xî<#˜%œˆt‚CÌjä{ÉW&êÑ+¿½bŠq‰ ÒB~µè×3ÜUoê ¤—"›ʦéu¥Ó¥ ¨Hçÿ1¤)D,ýÄKD79Ò7¤¥ê@Ÿ7¢“%e)«WÌ4-J9ˆCûCf=L ú/à§Ï.ÏÜÈb;e?ôìa¾ØˆpÔ6G.²‡Oɸt8m-*Ý”‚zÞºH¸JK2ô«I’©{ZÒ6´4ÿCÙÚ‡k§vX#)Wq 2>„H,…èTynÍa}EYOâô:”@6ÝØl\ä©^‡{x ˆm à¦3NERÅÅŒ;Kçyyh>ñLÑËïÓPOÙ+íúÚÄa ' jí}ÀÌú‹'oh‡+ÀN¸Çs¤d)¦ft‘ºV„1ÁÛ ž•]sÿØõ‡“‹ ¶ôØà"ŠsmÏCÑtDÚ ÝØW•¶F"}“ ÙM.£Â®Öú,èÅñÇò sMTÃ3s‘“D?l½+]R>÷yÝ…ƒƒœf·“i´ |×é]ÀÂJ6RÃPÑ‹Vɽúhd„è,M7>+dË"âYÉÀªZÀÚàíw÷¿ö¢±r×f:Ý·FÎdž\µ{A1XPtd¹œÐ. B5q‘>«põ‚ø F#»WÑ ~áZèj’~ºcãÔíjiŸU“Ø–y¾ÉFŒy†èê˜ÂŠ}æ5U41Lݲ.6 q<=( !¡3 bSÇÇc©5 œ%E=â è«úRxy^TS¯Äè´‹Õò‚&‡ê 8FN¡>©ÿ´Ã‘€YJ¤Aü2Ø£ÿÒž7Bí ÿìc|¤ýHL^Vvs'‹r¥Šüæ$FTq4õ%ÚQ!lÖì•Á‚¶<€®%%Olѧú8ôùöÿêùEsà; Œ†Øa!1o³~µ lO–Zè—D†:î4a*òÄE‘oËc%xH[Ä^æñ:xÎ"e:´è-xbê^ÝL}xúEFB ‡*ZàˆIu'²ÚJ¾ÄM¤Ü{dPC3”#}qxa¡œLÔ3ÂI ¾Mýmô| ž„$3°¯”¦×C ¸O>†äÒg‘PŽWxÙÍ4¯¤\²žì`׫£Â8¦tæ+féQþX¬ùÈDˆ(©ÌÊ0'Cë¹À` œ((LR“¨3>ð°[²D`q±¸D¶Hs§Trã0È™ìV¾dИ«±\å_^¹:`úÑoÕÀ劭œ >±Žf:i¿jwÊ™öA“"«7æ‘‘9ަSáš—zXfJ`û-µ‡y¬¼«LãMÀ=o¢ Ɖ ^š—¡£žAZ¦Øˆ=A-lµØS Ïã±?‡úx¤xm[´†à)ø‹z|ïÞ"“i5˜_zÆF®¡ÿ¢ö&9-!œS éRÔhå¨OŒR„ ä¸ÁÔÒc¬‚ÌDµáÙXê™J-gE`K‚iÛÏ”91ɳ.pÔ :á‰mGŒf,5 ü®ÖaÎÈ5œ,ˆl£:ø$*BHFE=°£áÐ`†ûKñvܹ@½†Ðk%mzNk÷ 6*+£»![Ú DVn ãaôãúœñ:Hš4gNiÓv@)GZaJ«¤"ËU¼0ƒŠ¶Íå¢HÝÀdß!^>8e3¬˜WcQéÜNŠ©ÃDÛ‰¾)ŒÝ~¬G",óŒNÜŽ´\‚¦sh›väÝÏ™1L>ØÒŽbRôÕ/RiÒõ…ª½ËÁ&ÙÍ›‰˜·%¿§ìþañŒ°“¨Ð)Oȇ´H{Kñ){Û¨ê'äëÄÁM¢4½Íüš}™Ø1â[p4Þ…‘YQ”ªÎš#¦ö•䮜9ù qo÷/—bt§%D„غÖ%ñˆBLv}œ˜,é!À®Î¯iU‚Ù6‘;¥Pˆüñ©ôOÄóˆÎRcsÔÓVQr%X]P! jOðl%Û±@#ù`I¶È·÷¿/ƒR·TœCGv½¦Ì„Ô‹Ö»záp6;ÿþÁèhZg#S~$X3yíAúCQÆÅå:å©&Œ`ü ÇçÊp¢U,"vmï‹Ô”{Ään¥Ž<)Àu™á€«[\¨ëZûa®ÄÏqºšÊ}›cF·6ºnZ Ëààôû2–uÑr2hÛ>‘¢Ø/¼!Ó'Úþkßo  g/ c© .4|M,ë _ ŠyË͈éÓ^qñD”JN޶¨Ö¡þ–¤˜£za¹sà¿ói[ÿ´ÂëÚ>4ðJÏÕ©ªI·6t¾7p[M%Ü>5…¾ÍóˆFF,VŠÈùƆ'û ê} :”àîÓ¦Œû5­ lj¶8“LW±È øÏT Þ›ºG€¸2饠ùøê嬳r»ߤ>δ;÷ˆàXgäuh[² çL‚rË@¤ê­!{™éàh=· Ä %S8ìËò”ÒŸŒUBÕÑ!'ÒµA®ÒÛõIDíãV˜e¾’wè‡wÞH˜ä<Ù¡U- OIz5c4Vÿgz‡Ú0³1þx ª6ºÙ7L({ôüHÑŽˆ”I¶PEØ”·­ÎvH#8•0?Ìë¹ìsÌâ¸zãÃö>QÂË?€;|òd–ÅrvA¨;wþÓsCvI²˜ò˜j á’sÞŠ@7·Ò°VoXƒÁIE8“U1:@nã–Ú@ã1¼Æé9?ÖÀŸoµ:ôÕŠU%xNŸ¤ºà¸A»}ö$‡nØòcišR ùðg$ )Ïœï˜ã Þ_Á“ø²evAÔè* a´iü¢G.#9äå‚ ÁÙT¿§ÙSå¼/s`øŒèwÆpŸŠ¤_†W»¨`)ŽÇÂÉaô`£-hoçéVXÑšÐûjxèž"‡Ío„Wÿ!“Á­¦ðx Öš--… íDÝå~cJI‡5Á•ÙÂAä¶–F+°Ñ=Ó2“h;½wÿ ³v&&/g)`÷ õÄ+qZá(®ŒKK§7Ì7}âF#½#˜ûÜá–¡¦³÷o×ÐJÖQÊm™÷бXNrˆä€ýàÌÔíåû±KùĬ„ÌNÎRfÆý'ùYÊ_%à†çW õVw¶Û#¶®‰„N¤ È#UJ9DXÀBR5þ¹Y´ü] Ãèiô)ŒéÑq ×観”¬ÅÂO×í&Ú!C8+•ãÖÃUË9°Â«™b±öÎÚÐq /ÉÛäaôñJ¡¤¸ Â'f AݪÂy$,{œf ´îӜ赾¾ÅÄÿÝj{³5Š( cÂÈ*íæ‘Õ‚`_NÑúÈ"Ò<Õ÷|ÖÁ® ÃÖ"Õ`2ï 3–à“›*e¢Ô€·_µÊ¾àe¿KB;ý›&Å„‰õžæpóVâAÎf q!F½Jâ£qEš¬BìSÚh›9£>j¯ˆù"Ãj¼Ç 𣆿›h•á[®¨N…<©O‰.¿šÿc‘sº¯r2L‡ÍPèT³–´¸Ó‰ã2 fJÐÔP ‚Œ`Ô{H ¼R•‹bˆ{UÒÝÝ4ƒ¦Xa.B:ˆ}LþX¯ð]†=‡ëõáɇtãYÁG¼)SÄÚ›^1‚†³Ma’¥0E¿êÿ§tŒðåÆfü)ƒ :s}=›>¥Z]ñ_¾K´å{Ûsrÿ²—¶+h=«‰CRþï½>¦,‚¿.W(6ËùA‚/!©§s9d´âù“tóȧ9¤=Úëü]Â>Ngiv­BH7á:Ua<’Ø0›nÿÌÜ&%:š “<àæk2!M,ZM®fеc]J}JÑ}ñøŒ~- }ù²nî3³^)†‡ žÿ ŽI%x€œ…¾Iø¢Œ”âÆ¿t‘Ø”<’3š(/‚ªâ ¢2§ €€(°``cñ‚iÙ°I%ă>É”¤Á;sù›‰/w»ßüF8úêþõÿÿ šîûש[ùÿÅ ¿_®+Î?ðϽß^>kŒ²}½eO3<³]‚4¹¨]žxßB%ïï~(NQŸ”¥[ÝÅtNþÝÜ9;Ûîdüɾ½ÇÐÄúFudC¿—Þ÷o*à \,öÃOü÷üý)LÓo·ó¶ ¯3 p%•ëþP"›;”®¹²ˆû=m`Ê SªCy)Ìúã™øgÀÀ,‘êßV'Hmê›O5¡ Œ$åüÿeÌ;;„„ lj?ÿ:Ìm‡üVçÅŽq‡ ëÅ-z¾_ÛÿÁ™¼#µ•ä€Bh;Tΰ>üLp2`ö¥ù~Ja¦Ó ó˜äÔ"|CÚ*ª„J Ž>\1­6ö¢c)lµ»Bó†¨;ûSïZ‰æ`¢ˆŽÞŸõH(š¡GÖ-öîX`ÀÒEø !3¸Œ5}Vø<ä"6æ¨ì_5Ô “Çøl+D2t÷O ‰ó[¦ÀÉE{›¶æÞ·ûnªÑ]eN´™œE¡Ï­îè -¯¬GËÉéÀê‰U&˜Æ¾ñ‹žªµ–¨œ ùºzíŸG™†.Zó¹ çcϵV”Ý\cÖv³¾¿¬FÙhмŸëOØ/cöÚy¹ôäÌ (G•E”8ÜiäaÚâ†@¶“d.ÔŠ”×Yçkl_¼˜ãïàB>!8]‹Iügóa.¼j¬ìz VZlqÆï­¡C‰„· kÙDÒ…4÷õw@3sÏ$},€»ª"WiæO>ð$»Oøô®Ðã´ÓÿõGRq|V´ÔÆãcz=¾Â®{bRŰ?ÔÏdÊ’<Üê02çEÒÂR¿㉭@͏ôm ,ªðôSÚw)Z**¹š¥ë–ä–ËS?ýì›&‹ÏT7u,RÙL Ê´ù³“V™k±PÚŠ”í@ÀÒ8½{Ÿóeåñ™½„²V&3%ørM˜Ž½øøÀ ð@@»^de).î£&š­ØrôFµ¥gøƒ&ùÉ´K\û¸‡ylb1ÅÂ:Kìn‘?³µ{ähV¹D‰ìe%}kN nÁÐV°tnÓIYÄ]ßà:(’ Àê‚ýßàÌŒHPUyÏwk6X¶v:Ž{AYm:‹–q¯»È÷M<Ÿ(©£ê'[dÛ—`í¸ròôÔ÷,Èwäõn覑5 ȉ¹¦ÙTR+¬Úo_ˆ06Zp”Ì"E?]ÖïiÐL»iEæÁO c#]-TOìQÏz7éD$ %Yá_ªú××áb•4÷Ú]ŒÉ#þ‡+øÒ¿Õ~Jvüz ë==Ò/}úÞçbUÀE›Ff­ k÷í§á²0ò7¡Ñ3¥ŽA€;˜°|öSáÌ¿A<Ô•©¯Pßkê=1Zô0‡Æ6ãŽÇ@UGHÄí<ˆõäDMÜK3ûÀ¡Ú„y¡Âi˜ òX·í ¼ùÌH9{p€w_œäkúO‡B>M…ψE‘ˆ&¤ìçЌІ-ø>€º&a)Ý€B btWv í¸€¸Å½ê JD™À™è„éí€K—Œ€æ–+é1U1Jù2A©à6^RŒÀŸ!Šë¦e/ìÂø†ì Ïҫصº|<àØSÆÏ*&Yòñ*éYææ*ÀfJ  úŽçwªÇ~²Ÿb¨_ÿ×ãÏã X僺§ú'á†"™#ë}Q½¿Úû0´jLÆ&OïX$"5ú㤼X@ªy7|)®ƒèÍc+|<*öCÝÉx¢Kƒâ³[¼&#kò ANÄïáßáL±ó|xü¼â.GâÈ‹&Ò̤Wô–Ûi íÈ›¥)»‚f%ÄwÆŒPÛ«B¸aõù12h”ÐËFw`g ~³æ@*ËX„ÌÄ»Å}§1ÉWZôÄþt°²òA ÇÑhÆÌìO5ù{ÊØ­™òµ–† Cª)áÝM5=SßV÷r#1|¦©±‚«Xª v÷,›±iÊ–‹Œ |E`+ÊñžIò¨OÚ褘¬˜ü0i¾6!¾Š— GjÐ`8lfC6h>ÓëjÍï/¸X£íÜ‚…‹S¥>a!ËEpª~Ä ƒ—"Èó.{¬§ùgB•Œ£ñ(z~B,®ß<¨M?b F0í‰,APà|âAF ¬Hå Cã=nÖÌ\}œžØç0~Ù¼Ïÿå÷5C©²“%i§Ýì±çæÄû@Ç“EÝ«oÜó¯{„ C†:¦‘v.Ð;¿þlM=MrvÀYÒíe˜Ã[ÁjôE5zO¯¢øÂŠød… s~„8–£â;²ŒU$?…Ehœ} ñ¶“ Þ`+&ÇÇûq6@x´D¢Ku8‚†¼ô%ü\š?Åë¶¹I*bãXƒWí¿ú¤Lήµ¥p½µ³þQ;. Jœ ;Æ)/ý´!)Ư¥à~XJÝ•f"(Êÿ¯O{†¶wÄáØÜPÑÀå§pq^ØÿŠx·®?ª­*‹2BIeÛ¶ïáÖS»´*šÿ$ó†¨Þ|ijÙk}øC]:†ÕëI8W‚<Ô󫔟×8}†Ñýjøý¡pik‰&MJ3_Ÿÿd¹©›ØøO’¬DGG2ÜŽ3¥ ›Ä\Q WÇD9–á ’Y{K×Á#9ÁŸp޼U% §Çñ•£GÈ4ngè©c Éú:2B´XŒTF¨vr‚¾° ýbxG¯RxZâ™ÁÜìŽ ­is‚êÖäÛåØ,S ¥57œ[ìÁè7ŒvNÿ DRg"`÷–Ö/ôÂéÛcÍ­‚ËUò6íë'dšÝRìxFWº_ ³Ö< & ‚í£nTŽœ*hÎÌ‹ÏB¥ ³œ~gÂÊÂd —Ϻg\lQ~ôÉmÓÓéIó ü)–ë]øE5Gi½Mks(kóÓþx‘!zL¤蟫vI‚ƒ$?%®Vĸ&n× ð!.³3·äy¡Iy£¶h¦=I¢(L¹Éºnb}½´ï˜Ÿ†ß£ê¼£å-JNçÐc‚Κ”œÊ1)ã0¢~ ]@wOÇ»uwH€Ô$ršï @)ÿ,Ö*ýñÏCÁmaú]ÎîÙ€†2hÓÊñj–Ã@ÐJ§©ÜÂû„ȾýKåÃ²Ñ –›g‡±ŸÓN ÃÅ‹¤bYÜî¾÷tÿæ3´¬~™!«hòâAWðJ—_…@¡Þçû&ô~"evK½þW&í‰ÊZ¸‡´xR½f8ê¹¢?&ˆz †g!À].W¦ºù”)C&­=!üîÕ@.”ûmë-„œ)WåÙ6²+SíjC¡‹¤¯kñSN;'ÑB;¾,˜Ð:Ëœ@«%ÅÇ‚[Œi2Q¢9|›ŠÃ#Uy/põ ÔšéCTY€ó¨ÿ2[5 ¼ÁšOeêOà&ðÇsZØ\Q¿(jwÆ F ‚Séð"!(2)éWCv~<‚\÷ªÈxÉK_ñ,ƒ54”ªœÓ¦ÀÿYX o£§ÞÂ"¶›DYéRº¥¡;jÑïÌ ]¿êICûÌìuÕk +.–+áÌH›¸SRš¿´ä'2¢Å1G*G´K 6‚L«Ó“Q섽n`Bh"`$›3}^"_ÔG›Nqý‡‰:€jåæ¡?Α8®Ð;ÕÝòS‚õËEÿ.‚aŒœ+‡ÅÇß´? }^õ æá#)’ ÎköûMFAR»é åǶzlãˆØK%{K”µ·±–˻ͣr LËɤt‡1j¹iÏÉp´’G#l>dA7`DZ‹Ã)©¤ôM­`=`„@†*ˆ´ŽsÂÌ_ë¥9Ý&£‰hÅU¶:g0rÄiQéײá³²>3fbÝda©¨ |ÖC0résá@t€a"5 3­(¾`„d@WMe2`èóiÿÞ?Ó€œQòõ®^mË’ÅÓD™²]z®õsŒÖ[=úß°µ0/™Q=BhOÄL ‘2#/]\¼º:¦@Ãí¬/ žiôUpgH‚Pcç381­g']œk3ä#émÛÕµ¶c¿ .¢m°uíL¢úŠq͆ŒÍ¥ÎÔà1ä¿úrƒ Ä ²5"‰8õåG(ccmª®\Ë´v˜H4ñc O™ájkÉy¡ò)›…BYyÙó9?†Y‹ïÀýJi~¯z S™œèdI";í Ä9„Ë1œ¨4ÝÍhZÓVz¨„Ú®èy]:^¾¼vd^6ÿM~µSf¥8\#ŸjÉ É'ç0<úká"ª'-à¢ò»4>ƒ©Q&OC$FN0´lœoYšv«€¯ÿiGï²"U ‰¥ø ÷£Ff:SCY[Çÿèz$ô$O›Þë¤sWk$$}þQÐ"ÓSßñÏ*nÎmöåbºGñ=¤äjSàY3é Cdp+ÛEÆ—fG…Ç ˆ—=¼DŽ\9þ1…³:G¦À~³¬Áti×µÉIHëyK—_|-ÿÑ6lk…5ÿÈgËnàb~Òòqå±'½ÉbLˆŽ`J,Îø?ÈÀÂ’9©‘u´—ºÇmxHq*ðЗ@J#eM§H gƒ‹ ²5h¶63„eµ8„¡ðñÎ\+øwß >Ô¤@LYà\ƒÒãsñ40ú,I¬,’ÍQ ×ÏÀãÒ]Øs¤yŸŒd¿ôÌy¦HÕgŒv¹km¥ uo“Ø&Ÿ#íR3âå Üm¨šeA$×Û•é ô@·?I›Ôæ5MoÅ]efõ9h-œÿ¶±é­Òjš÷´¹eïØlí0'¤+i`Ô,ë’œ»&AˆiÄÆàÈwÇÇ^F¾Ÿyí—\ßætM`Ì^jȵ§*m¯ÁsÂé6è"¥ w”‡¸v.|r>ÓkÂÆþœæÔõš¡¨tRîÀ`\1u[ù¿i?c 5~`¬'§ZîÖì”÷ÎPYb5[ôeûüÓÒ³¨u0èŒñâ´(òQÚÚ°¶rÙú!™Aõ»¼'AùJ¡~„C¦d ¥Ë¤Äu<õ°ï ¿Û ó@´ß8ïÆ›Úêlšä¯Æ×ù¤§œ/¬*ô]Ä š_‹#1ªdMFZÉÇCüiâ9ÃJ@¶¥Œ¬ÁÜ'¤5!ýÝÐvAEÜòÈöÈC½©Ùd4»Ö©š€Ž¦U*– G¾-ý-ÁËJ  “#ì5?o®¯’pB'º(•„úP¹%Ý@J(Læ\%Òx'œ¡àí#0¯Ï&¸šâ¾\ p°ßÙœ„cÃxÕ0RÅ®=$¯Éã_(èÁGí4äŸ\µàÆŒ¨ÓÁ\uÛHø˜ðŸ8xè–·P€€ÌÙIÓýˆfØI}@åÆ*«µ¬^W/÷“s¡@œØ99râcìêg;‘+™÷':9¹m²¿œÃþ¨ây©òÒ!í,Ÿ ĉÏw›‚¯W¢R·%V€tuúqjGíÖ¯k4Ÿ_ÿfã!½ñfÚüÜ/Óý°Ã%€n‡– ýkˆ%˜½Dª¸rЯ¬µGƒìŒ¼ÿ†Ib°k[´.y±CྃˆD¸gƒ ùîa@¼j%±û1‹hhΆöQ ßN¼¦ï>N …þŽ:~B´#1R<¼d¥¥#z¦ÝG’¹½Ù{ ºzÿ”d% àIOdCתèB«(ÔåOƒ#;kŸ°‡pd"´œ@¯“¢]“še—ýDÂr:|¤± !ga>††ôc£··íUUà¸j˜ó= ¥ Ÿ\¡Ú,JÄÓ¸_ f‡3¤^|ÈÍc¦,­gj²‘3pVbÜ`ekÊ.ë½;zŒ–ìsh¶+…\xòE©Î%Ã{ällÅüºÙ›ßqी@=‡Ýâ ·É¡|–±„Šc ÅÖ®OZ’°ÝvÁú@«êS¤?´˜Ø]äŽxäîc-ø൑N‘•ijЇzœ^ŒŸ†”F=ñºQe^‘*i%vò3Õe|ùÒM/àö>¶.e—!oÿƒÆC²G/»ƒŸTOw²i£âG‘° 2dƒt&¶[Ú]Á¨ÞÑ*¿YL›ƒÁ¦ž·ŠxÊìNòà1ý mï¸áG­ceŸ8ÄÈ+›rCinCFVOåü ¸ר¾þÑ‚#oo?aÈ?”š`°Î}”8Ö'Yz\C¢ÚƒõWÿ¢WŽJ+XCŠ*`² ôä ÆUŽÔa£çèmš'…×Û Ì~¹#sˆd~’€HØ‘½½‡C‰\€GžL˜m«ò  þN=ûœ¸:2A\-(í­”p!zõ‚êfcÿѦ †È$DíId p¾Õ;X‰#êµ^4ÿ„¼Ò¤žó·Tg‰Ñóð«¶×?´¡øÈy§7U§CË5Á‚ÀÏÂo¾iÙtË‘MÄÿör%Q•Ÿø'8ÏÆŽ5›ãÿŒÁfF†»Ðªæïì¾ó4£à¾ Xnî#y,£6Ôz ÆÚNor ¶Gû¨±žà˜h‡DþC¢H€ÇŒÞ$F=CË]ÿ¸‚C·:]¨jy= ³óîîO=¶fÂÏݨ3´>`!̸Î<Ô ©+—x ·XR¼IŒzpè2%Dc8UAÙ/yòùªœhÀô9{´II‘/LÈCÛÏU ja¢o)þA‚ý€1ê•ïš@…€AGI_íÊVq£ûX0DÈ$±32ëV…âN"£‡DX`ÇoŒ…Ðî… † Ú”äj'ž§Z™¼…Ò‚«0¨ÙO–;Б¦Bj!Ö|@?&füëÔZQ-k ’Z*k]‡/Oèt³Iœy>GŒ½äZù)Yí>±m¤p«’.ø6¯Þú ¡Ôm…§$F›‚¢82ô2íªHúP˜Z„ÄÒfƒZ—[ ±|2•R>ŸˆÊF)ÍjIü>•€ñÀä€6“ÄÊé¬òn -"Ô­ô_¢&šõ¿ÉJ6ê² ¶“84$Ô{+Ÿ»Z«†?i:ÁK Ò.ðD·àË jÛY• ë#D­Îxþ^£ø%”Žºˆø·Pqz.ûÿèQËï|SØ>ÇtJ—ƒ“ 5Uv(ÜLÓÜÝR~bl LØ"ð{Љ`ýæ2Ö-ÜNc†îq‘¡þáz§U#1¾xù%‡%ŒÖrÁ伺²®;x}`^¦Ø-³Eä/l7dR·v?$À3¢êÁcêº0k¤Äa÷ö(1 ›ê>ŸbÖ‰ZäúÌ ìkªÑnÚçìRìutFVˆ¢€çˆ3)ÖÖàY«?Ý d„ÖÃçy €Ã0:» –VêÏ`¦ê>l²m×QûMŒäFRò²´VFQ½q¬ò§µ­Æê£¨ úOâé #ZZ hÀŸo…™{^d X˰øU‚"ú¼¶Š‰…0Jú…0̉¡EG‚f£’‹ Ä2€Lüzù“lÄM‘–Ü`1“õ@ÀD{¤Fãìv>7Ìýñ­´.cû ˆ¤qjÙG›ƒ†åC³ì›ØÕ Íé&‰Iãû+–¤·6Ô"Ša²çT^º°,@È.4VuªŒÇ¿ð(ìÄŽ჌&q<¡Øw\õe}Å}É;;3†c!=¶HþÛvì…Ó5ÑTøH¦X`)0×›D 7Õ…8¼tVKOÚZiþDÄR1s7íEdÝ‹Ãöú åR@Þ|éû¨‚²͘Üxªýá$¨­dyFÙ‹éó6]ÍyÐz\Ŷ/*¬Å7¾Öùe©¥ÐfÔ§m—·ÅªíæW8J´z5¦w1 ÈÈñ,7¦}°5!Šbwd$ öBó NYåÑÙ¶c‘ÿ€Â ©ÛFãØO£äN'",t¤Yöîߪ>< ïQ[™Œav#žEÕ¸A—¥“Î`U)œª!Éš¤f62&å„=,·4í›G×"1“n¡Aö©HøN¬(ôذšeØl=Ÿí2‘àȈ PàkŸ 3ÖØCÖBìÒí±ìñûÍ|‚°ÿnJÛ>ǤnôxŒ({fdgjÀÇL[˜“‘[wx¤ Yæa@\Û —yÜp#ÓÎYou¯j'§›ø‘©·©ƒ‘Z%Ý ‚3ÀŒ¢‹Û¡‹P&ûùØÂØžwa3¯MVËQ¤w'µÄ¬då–i…î5H.R·´EÓó<ÐçÆ’M¶ª¿Ú'¢À[B-Ú;ÈK8%üé:Ê!FÝÝhn†Ï#.` ;3!ñ¡ÜYl7»æŽeIü÷5*Œ¥ý‹z[ÍJØjZGÚ†ª~s«€¾;ð ¬\Šçæm}œ|5J¸aí2øßè1´â§vÂ-¸ù‡ØOà¸xŒ5U€3cДPc§õ¥¯a¢ˆÐfÔþÁ–Aƒk›0`‡EXKÑì>»ñ–»ï€GP7+pŸ"k¸ßr=NŒ>ž-eº(…»VùšHçC¢d/®CC]™jáSŸïŽÀ…eøç…@E˜ bw¨£Á°æ,Å3¦càš#•-âÓõ8G)éa»v"Ž 1±(”£×iÂv5mÎt¿—£ªDãú\ïi—¾ù¾\÷EGJnt,nBHcÚ‡?X>’ânHÒ|ŒÁ,… ým† DpCÄK’âù‰‹Y2 LÜùš IÕ<§èX.Pš4Œ¨õÍbüËçÀ]*é ðÌ·eÍ14¯ï„¯OÊR{è<ù†o‘ýöDÅûO¥‰¥Ú Ë@!ej¡÷ñ2M;ì #—>'IËágH8ÅxþÁiùŠ{$n>ñJ¼C¸·˜2bo E (*üMqÄj´Æ2€iä0áߎˆGjpõL$§žR0 Ìwì>¤Àb!@ø„GvzÁ0˜£Ð#îίÁR{Ö*kë?ÁÕ€³S}ØŒýÒndµ<>äÄ]Oõã+Ö}¨©‰ÕQj\Ÿ†Š}{’_ï0¨ØC8ÿ1 ½Gl.pÄ4¢ÿõfçqoàtˆ¦.Ù½KýŸPvî¦Q›‰’î¨Cë§3˜}¼Ë²Q1 Šd]ŸŠm‹f]Ì:Kž}ZŠt7Cù6L F:³–\^©•Lþ''³%/óÚ+ùHÌ0XØiÖê ù4øö–õ¡¸ª»›°êÇŒxõWr·ÂW‹Ù'‡=·NÐ(‚E–ÝÞ*(ªƒdë9õ³õ j»®²!jHG«®gœªK¨Ÿ:Í¢¿¢ÞÿA0ev€Å‹“ŽÛ×Q”j¨”ÃÞ[uÓ˜~ŒžjýTÄ* ´Šÿ|¿I°Y–5‰hþÛܼ¿óÐfª£:|Ú^½ÿkgßÜ¿Jÿ'{¦ûWL¿ü¾žnQƒ¾[ëæ Ò¯ÿû Í“ï0)¸úÿþÜÿýòQÇ™^’Oßo8Éú/^YüЦ™bÐrù™cþA¨ÝžŸ×¼PÀo7Ç©ó”Ë«¾ñtë׿¿m”sò$¼†¤8û­WÙXáu"SÛÒéÜjù;$DŠÀ=îŸSúÞ%¬»²‚ÛRé!>3£²Ü9{EÞ°u˜µø«ì£8¸$ÀEé>á +y@TA.ɰéã &{ZúIݰçG¿Á=Ú`Œðè–*Ó®Õ[´¡¶^$º’™œJ¤$Þ $ãи²²‰´NùÞl*‡¦‚9™ÒG&ÖœïVÜYë¨âç¯ÚÛçqÌœyšî 0¹Ï• ±v˜Æ^ú“Šqqa¼ >¨Á£ò÷¤Oœ{ ÷tòMAýL­ê;Ÿ¨%2.ßøø¬X˜¹:ÉFº„9)Õs`tœjì´#pø,sÌ ƒ=‘î”-¹ÞòÃb:õ² ›_omÑü±ÑŒææB;Éê°Ú¡¿¹£Ê ¢*ö|z¹6Û'QD Ȳ¥Óùt¬‹ –ȵ˜ÎV>^ø‰ÁT¤=xøT†=J~;ÁãÿûbÝ Åˆ·wïÂ’ƒP±gà:ТWýøçv‚ј§©$½3Aâ¯ÀÀy8n˜CæïÉZ:…ÐbUæBEMzôÛÈÐé¯Hš˜ Jn4ޱ`NŠÞÁ¥‰ A9F0ù}bâòl=&{ûŠÖÓŽ$ÐäîÓ³mk¸¿5Q´×]V ÈLظLqè¯ Á¯Èqí¨.…Óù<¨Óñ”7—ºo¬ÌzŒyë]ù‚Ÿ"¹\±F­Që  ÄGiϼüÓr!éò™ªÒ«YW( ¦­ÄϰMF)ž*óz >ÿ‹Ð€›5ð(\ÊÇÅÅN'ã¡ód“8ÄmÛ?_äKq©^JýpŠð‚+tg­C!ßô ûªG"X6K¿BÞÞjXÒä\:³ñß­A`6o“ £ÓjmíË’ê-õ9Ãgvž4ÌâIBOu‚<3ló'·V¼¥÷6tc¢-›ZvL ¡RròyÞ;’÷ê(­ékaNÕHÛœ.ZaÆrÒHpšÜ)­w[ `¸N“›-¢µã?އËMçÄAAt\Ì9¡"jÆÚ&ÆsŸÍ¶1æÖ Q†ŽIÂÙ·;»ƒ¯ì{Awž’ü?̰ [dG‘®F§Ó‰Á¨÷óP«ˆ;®¥¬ “µ~V^í®öŒ4¯©€õ¦ñ‰|Ð\°,sEܵŠòà´Z²§ß„¼~„Gø¡-L&‹.Ž0¯£Êõ†ˆÛ— \ o@ðBÁ¾ó!gèwîXZÅ{¾hgcÈþX KdæNÁaÒ¨)<éGK+¡·¦ãàöÂ]h†€&ÈHŸt3£=W/¬²~K…cÁ!¸eðtßTŸŠ¡86›UúÅÕä—_¾Ü|4ÿÑ-Pþßš qᔟô $Ð!Ëì‹#P7ër£^í\žCd‹öäVâšmu½¶ðnø2ù´ýéD:\ø”“Am\Mø;Ny¨ïý¢&&É>j½îŒM^ &Óéh’e»Ëå|,á ʼ%9}¨ÑÝû @Ù®:6_ч}Öñ 93€T¢p¬gY4P2Tö®Ô ˆ¾#–°°M°0ß ·«ÿ¶ ™øn¦wïߨ rj í žô ÔÂáÖbÀ³¸ÉÛ?8¾`£wÎö(j)­G½Æ‰Ú|Ñ ‹@‘®ÖMGÓore)ö á™!~¯Á®ÿ•]È 6»5²É R ¢´#GÂZö„Ê´Wll(ÕaÄŽÅé±%à5½¾,•_ Îp™¢!r³F§Åeâê’pzÜÒ×äç…HhЧyY~†Ê±íƒÍÛàסî§Ë-Ðç­5f'Êd²i7Ý€žd¨DmMè~üñüU ×/OÁíHkÞèô5Ä–.…KÏÓá#J =Âq©¿è7§öÄt‘=bY†Ä.Š÷iÈÖ«ì¿ÙŒ±8ënü¥b8Sðœ¨ä¸#ŽT‰Y¾²K°{o™ËÅÀ“X4ŒÉl í-TÄšp_ÃN–Éž#Ó? ÚßàìüÓFÿ#Vú ޲‡^{jµ´Kë癩"•ï#ˆ8ûpèRj)½'lgx9p°€ÓH‚O¿ßÍkhË„h;ð5k‘dQŸ Íwá2Á j¦Ó õÉpWc¼­I±Œi$©D+ê4·¨ð‚CûÖLã\²]˜Øºƒ Vn|jý¥ÂÁȊû% `ЍfôlÎ^Þeùú" §ê*×6]¿Xu¦ÜòéÞ. b”ÓÛ¨}§‘}‰Wšö½”*g´z¡ó€Vë–­æ`ò²Ùr!n°ÆTRà õ*"BæK2W4àãÜfMó—RO$ý’g.ð›¨~Ç{¹åD+ÐÂñ¼îÓͤGæ Õô•wÃþ˜c²E¿º£Ôzå©h]ÉBBCSLY^LÐÅPžéïhŸ‡¤Öu’×RúK•A‹‘»ëArÜ–WW~èVïOX{˜¤<‚Ùá¤bv¸ïñ_…Œð,ùÏU©‡èh@‘˜¾Pp78S°=¼œÞ[ÉQPt» ß5ö°lÉ2ÞNÎ3×éêá AÅœhr.á„ú}ù‚¥é°`P-ï2dÊÆø>0¾ú K‚ŸB¡—…rYå‹X´äïnHš8="‡Eƒñ¯sl³M†7Nê¢S2?¡„Nð±`$„ž±<~µá£ ¢O¬F¯$c¢‰ ¦¦ ØÂûK,üÖ{4ËÑ¥„¨XõkÐPŒ¼‚‘]£Gm†A“¬ MQQÛ vWB@5 `dë1rsÕ¦¯Ì5ñšÈú0ÚÁðòUǃR+*¥c]Mÿ 3I¾™ÿú*v1ÐE49Öz?ÚW«ó”/Öï‘0™,ÐÙÉz#ÛÕ_Ðá) ƒ €óŃe þ…ø‹ÑHËÒТXÒ>ǾñYŽýÞèiÃ!PuüÛ ®{ZèÚ›«%DhÅ'm,{|Åû~-kë× O°¬y{CÏÉyd¶qÍ+òi…|*òºhøÄŠŸ#ØŠ[|m‚Buèš×iæiàŒÔØy„ÔÎPØ;;Z$àúJöH!¸DíÖ Ûq@I8)ÆA6ÏXÔ™Xq&r‰Ä‹›ö¤Énq!‚®<¬ˆØÆ}‰Ö±¤v¡%R)Õ$„ÌîŠ%‹£k$ýªâõâ6Ìv²Ó†Ž\'20ÒWO!˜å©Á £i"$y?UˆU{¸kÉoµ=‚L L´[~/&4¯;!c$Ho7Åå­ÿÚL)‰x¿Ùá’N6ZYH&“0hSg""÷ßêˆ\òýæß½r#Ú oø=È´¤¾¡>«ÜùÓDINRvŸ‹2±@nrñmÅ1dcJœZѺ—(û^*iCÆÔ€©©úOÙ!ASó!ä¤âÜÈfÙ@ÝT§¡ QÁ:ž+É(öõ‰áL2HoÈšF@8ÿÅŒiOÿ<¿AìI€FÅ*½+”lY.[¥—4•¸#¶UE- lï¾?®ÌÇàQ“&DŽûè$t)×~‰®þÂ1Ã$É8k»£Ó½0›n‚—Ÿ=þÛÈ ]ÆRvill°—ó@`ìÎê IÝž²T* R¼ ~lHBŲ“‚/ W©.'ìD‚þùýÞ/µ­÷¿§ÑG1ém¡|ÀÙDÏKcm¡éšÁÔl6?¬¼ ØnÉÓG!†NF|‰ÞnL4$Z¥tER ]I6RœW­Ù@e4¸§ôˆÛOœ¦L6¶ÃK@“)jÙѶqÛV'„ƒÈöO*?F‡ÏËQµ‘EOµ»³P¬ÄLãÚv¡‹Ùr»ô"œt÷G…ÚtÉõ:ïÓjÆ´á±Eíñ rJa 1×#M‡ðZÙé»@}uˆ?íØ-™s?R—R@qÝ“"ÇêÀcP(¦pC߇{Ò(÷á°wcE¯t5r&y :x„E¬ºY„ N†ÐRhX¿\"ïvÌöŒAáôcò0Û[©²I4eDÚ¿:všKY_÷Â7,Ȉ°r݆ºÖ¤îƒÂöâï$ûr¸N²|Òúå¦~ꦕžÁ¯¸QàH;ÌÜÙÀÞ‡ñÄåf"díÃñG‰ÜñpÖäøb›°§â¼D2¡oÖ×$¹~²ÝØØÆß£Ë*Žyär·»Õé˜'œv#\Ž–H ð{Ô¡Úé.žÀ‹C,=L¸Gø2)†J™v”`÷¾(ù¦Uœ²4‘¦7÷{cކƒOP.øjølSx üùP Z©ýNÀÊd54ÿ «óxĽò9 ù7D¦¡”cKŠ XŒ‰ÄlϨ‚$äøÂÑ«(ŽÄ´‚_-@î¡·Õ»z)vÉò«š$Pu žO°|_fwW]'hQ‰âëÐò¦.„æ¾lH X‡Iɺ ¤?u °hÕä± E¯°[„É  hiM–’ÞÀ„_eDèM0¤*Œ%{Q¾)夑—ƒ¤ŸùkÜc÷a’Ú9äÛº‚ ¸|†ª–ñSl™nxdC"<ʪ@1yx³É+ Ú9°‡&ºë øƒwí“kÏ2šAo—£òýµo©^qÈýdòqi» /aØÄ1ˆÿ TtOÇöü¯„˜¬•’žtbP3Ã%¿DÚ5§ ¤ò1c¹a˜4–sĉ‘ vT‡îKmˆ@áçHÈÌC$ n+²ä“ÓT̾xNÅ#\eùË vSfЬqq¾‡¿×À'Þß8˜®â#[Ì4ÑPÔŠcI5‰Èyð|¢7F£Ï åºÂ’ &JܽнþÑ# ±RMõè?£Êoót“© ú]]$ž–;kN˜}Köô8.¥+É¡],ŽåDM£ðŠ£Øb¡¸uEú ]¤’ m°V1ê¥Í\ƒoóbŸ"f2gègo€{¨…¹m¤ôëþÄ2à}©†NML£Ó¦Š0·Ì '“S=%G4B!õu«1c‰3#U‹ÒÄé †G­ž`g¢—"!ï&"jöœ5Ž ¶‚å‚ÚÓêŸ/'ohK73ü¾‘Z³(ÃMþwéÒÿ«¯sxQiô¬ÌˆÈê_*"¶ B7gÈž×Û•‡ã ½JGÆåJ&ûHŒò,œ›âþKÄÁ¢´7XT=y¹˜Ò¸–aÂÔYÉWWQkUf\̓ӷKJÛ7ÖjÛìÂ]BQ;[pƒmêÀõÅ‚ŒQÇŸ¡@—àDÖmŒÏø6ÛAÿ—òN üÄjìH†··/u¾Þ87½±xùPEq‚°¬ ë °/Ÿ+!`C,õ³à*ZjÀ¸tê'ãiHX{œ äѱÿlß);*@ø' œÿ€õô³$a€b—¶ S* ì­…rÂ,}©Áû97‰YY‰“Yš;klÿâ …òßM… sÒø†6 ”ÈD}ÄžþKNæãj6q§šu%1Ñ‘«’@neáÌŠ,$øh®kàô®p>Ìí¶*ÿäå}§2&M–x7umê”: , ÆX÷+õòÝ“öH¦²,<êѸ(ƒ,v*ƒyCóaΛaT:Þl 0c ©·0ùVÌ/¯¾2sÐÓR˜Ù.Í”®G`#çƒ(µ$ÖóCÚšxXc}鯩Šá¬[ÁAy|X Œº€ÝBµÁ(NÝÐŒõi˜4ýÃõ…%ŸG_Èexd÷&³6ÒtXð¢h§·Â XÏE3e71µÁb ´ø¿[{ÃI¹?ÀÓŽ þÖ\éÝÕIf¾ÈÕ†‘Íxôj <"©ÃY®ž0 “ŸlJL‹´mBH·Ø‡À¤&y²Ú,Ñ ‘šÙ°7kM·—­@_¨¬sð‘À:Þ™ßC9•⓪؉þLÐ/é*Ç—?xzccïU/Úuá]À:£!²…èbrÑá ˜d}œ¢®k‚GD¾Ø”ˆ¦DÅßý›ûÀ ­ô•÷%#ë}!g¾AÁ7aÚˆX“/¨‡(Wl½Ó¾°ƒrÁ€@ ”2ËUÚ²°büüød0QAG½ép98F&Ùöuw-\Θæ¢^ŒÐ޾îÿVÌÙýÿLþYßzýóõ3ï^¿¼ÁM“î¿Óó1‹‘^ï{íW[‘˜ÂÓÛ @Š.³÷`239ëÔÐg’jŠÝFÉ‹°)zmOoäÄøº`1á&ˆú†œ‹ÝRp!åÏ–!-öO*âIDª Ý#Ö×ám!~ƒ_¼M^ôòyÐçÔJ/ÑöòÌΰ)6†+× IFQ‡ŒÚR3¡5õCªÜUßšò°ÀÄ~ì6F«­Ì ¸ Ž1t£ì†ØôÚªUµA1Åß ½!õ†©#ì²yŒû6Útႈ. UM®Ýú1’"Þ€ôÒÙ•ÑO$½x,på+$ø¯¾ì*›¤rÂõ  MÒ*p>ìªGÞ`.Š(Ù¯£oòë2˜“‰«qÀfzH­”ž¨ó»&ø2p‡ñ,åÝ·¯þ[g¨‘ªàé–‹Àò1¢³L Àc ‡'¾¿8;xmåÉ+ã^œW³lr „ QC±‡­Q¦Y=7WÃ~8iwë-Q‰öüÜ{§oWw!»Í ’.ÞÀ%ö7Êp8‹~!uÆÂ_´;ÑÔ§Ô‘þ±e ë3,ðC’¢º×áú‘ŠWSƱmÍ¥‚T?ÉŠš‹ ŸÝ""ÀôùW,;=Þ±^æÔr06ÐS6t“.€B ¯{q_ÿý†Ðä3ì+cÞà°¶C‹öb‚Nâ³Ãa­L=Ð ´ÃÞUÌ2W2‘\Å£À$~%üÿ>ª´AP3G0QX û#RÆ\LÔ®™yœÒÆQlŸœ hI €?l>ãðfæœ"'õáÒ>oªÉ€BÌä/ˆTÙ@DÃ[A ^¶óüsX·€ôHSáì¸É~?k áËsš~áH> Õ%µ‰vè>GJžíA¥¡’øºä©ð¬^*ªë¤žzÉfGÇžÛN¹¶aPk/÷ŸÙƒ³3x)+­r—eŒFnK·½pâC~”ãÕ÷V"…"Ï_I»"WÝ‚À ~mMÀÚ^)ª«ú(6\]k]&‡W²à÷ï$/žgB57v%ó:2}Šö9CøüŒJ¦/!ˆ¢L°mÛ»*—Ú/¨QRçR¹ŠT ä¤H°ví³ŸA¿::4î¿Jü7ðgöm¢&ÃóöÒbjŽtœ¨°(˜-ø‘ÞÞ÷¿Fܨ¢P[iÜ‚xèKeÂ_(‹¨aრ¤_"oJ|H‘?1QCŸßM=ûÕ)Øb[Úèìeƒ´î©whíÌ—QÌ'6»] 1×Ï,\‹ÒwŽ/QX ¨CMëѵÛûï]¿Æ“&ïÓÿ÷_a—ÒÞ`rÿþ+ZWwÿüžsz„’K… ÿÿ£ì¹ÿ¶ßåäR1ÎF ÷š¹8½~ÏÿùHÍáÇýõÂk×A¤[TÍ£aÌŸ³D!ðs4Þ?ûïÏNn"ei抪¡õ 1î¯æ´Œ $”úHåu£<[@õ94Œm´Ä:Ü }´¸Ê†ïãIŸ²ŸÓÛï Ñ0Vˆ ~cÿ„Ðô#š8B(ˆdÚ1,}•érá š!‡§…Ú3ª •cÀ‰ÄZcmˆ5qí–ýLˉhÒBÃKÊt“1ô ]' æU+ö¿7{¦ýŠ-ÈL,€óQŠt¾-½v|Jç,Þ‘ú˜b‘ïèüÙò4€›[VÒz¦¹ù‰D‹à>^´$,ÜœÀþŒbY™”£!›“‰¼¤*û¡MÍ? Y­eó«ÀÓgQŠÓEá%l¶Fÿ˜b?WÿlBâîŽc­ç¨ÌlŒ7/Tî×áü0Ø&ˆ]OJ3G6§nûHf¶Ge-بÌU)ÿMz$d¼^æ`ÍSTðœ‚S>÷½ èoˆŸ÷½Ïxd®µ±YÞ­’™¡Búã"½JlŽDX_ ZoÎË@¤ª ïf‹ä*¨’àJN“bDl\qÔê+¨Þu•#Èй1GÐg:ZeæC)Ü)ž|"Z1z P@Ì+ñ<Iz`Ü0úSxào9‘­žÆU$Î" 5&=bHÅbø|¹—¾ÉáÎýÈK“ô°ªÿAÀ{ÜïÆÆA£ÿ’ÙäÊ…!ËBòƒœÁLzš5oV54Á_§ãáÙ±Žï¥¶â UKšîQãÊÝ2†·f䙇€¢`"jƒÉ(ªüSßötÃN(>>°ñþ•7 i¤jÛM6!ZÁdz?Eç Âù›é€ªm,H>ù/ËY –S~*¶¦ÿVå2gPÀzRðÁéÈ»À`á+Bo0’À ð)°x²Åœà$Ÿ|£{²,äT’'s¥Ä‹ŠUZ ­À-‘~ÇÝ eau­·ÅÝÂÓëô®É˜rA›–šõ‹ðä“ð"d¨è¨?€tÖ÷•Äiu­¸)å·I?’q‡û‹–…Ñ‚«å«Ûg/NAŪÿß®4Œº&#B9f2•½0ÀÇn¾üX³1ÅÄC¾êüÔc™ Þ%[ À}¶ohÅbàûxîAçðh$kÛÐU[Íox«a1 $Ñ!KÖÎÏ1oÔVx¾)4NgLîè7Î:ÖàY*;“þÏv%}“\ÐOôZ€˜!ˆƒp×EèïDYbŸ¶Ýûq™x…LÓ7æ«™ÀÝŸà—Võ¦ðô¤PŨÆo¡5h4ãÍ!¢#'à™òá°øüdAT|æImvYî Vaç4Æ f––AáiÙBI­³¥ØôÆQæuºj¼…gÄ~“٦Їàt E4Mõÿ’¸`ÃPŠ+Ó5t©4…b÷d½þ›‚pÆt:c`Ž¢© o"úÑó!³u/[éBç:©dèp îÉÂÂçб¸n}IˬnR.X/8¸r ’‘ˆfZ@¼½“oõ_JsЙßmÝVþ²ûÀꓘK±âßToËF£Mi«%úš­t ýOT´R¡-…løJÿpbuÓ/†¾ùPÌ(¹Ošc@”çý%êµ;•¿;ü.ŸQÅÅ.#¥i”*ûÊë¼,=O$èv§”Ë:EEP›SØ÷âpÚ¡"é‡é«—Çët×G¡%¥WF©/[QdA 1Ç‹`kFuÞg"e•è´ÿ'îz!ØÀþ¤CD4D“+³Û5v¡FÀˆã@[A²v™l5§µ¨ÇM--±h‘ï  ҕϵäÏý. üʆUp¬ÕIÜTC¥p*¿¨ÔÚn¤ž#×Aü{þTü±×âÿ%ä3pw:H:*®tþh¨S‘ÖR'WµJ”^Ö‡? {œÊ‘Ë>j»_6»÷Ã;tê¡øYC3œ+ÖÆÏ¨ÚÍ•‡4 ÒP®™õfïj=–ÿèK*ÒÍèœWêY§[Çü¤Cø_낾æÃòOäö“3byϨ¼Œ_¶=÷£"¯83¤QÒlŠ;åúæH¹[§P^@¡5öкæh¾$b¸”vÜ^Š1"é®t_´±õàvS$Ñ£ëä–$¢pä%ƒ¬n …ÛÚ^ÝõBá>;Ÿ©H g0mr»<:MݽŽ- A(9 ¨Ÿ¶€Ô>ŸmÕ gËØ1ƒBœ%…Ù |½Þ °;rBu¶YÀŽÄ/iLµZ+>N39R„äײ?†èçí 8aÖÍtéÀ~ô)&¶{(IÙ.šId:[ªDCR47™#¼‰gÐÓüvÌšPFœ?lXÊ…Õ»+ÿÙ!l?£ÀØ„1¨RcËvJÁ!_Õ³›ô;Kðp5:éWW7HÇÉ–þ¤øDr6ÕXRj$Õ ‚ºj6°KŒ8L_ëQú€ˆTåï=ÏÝÍÏÈõâ 6½´<øT±ò5¡ÑÁ£ø•æƒÃáÙ2$Ú0» µÐ}©™.ä ì Õì ØªH€þX»s:b›êG÷#˜ä‘säÂd$6Éø’; yȾ0C·WþKAðív²à*+ƒéc–.›œÈä ŽÜ©§É› ‚H¸iJCªÇĶ›ï' &¨>k–#a;¤²´ð64m¸”k«Ë€…¤ÚŸ¨ùL–¬ê¾ÖÜ–~[ú“éív!$z‚oÖ4.ÇÊàb”…($é ñ( ¤‡¡˜ uÎ\…¿‘7<š†©’ƒ×%Šp>%¦À¼W’BÆT“0“ônYýߌ¡f`3$"g6ÆŸŸ‹àò:>1ÆDytù…ÒC"[H·ósž˜øÊ?Lb#-i²'¶ùVœˆ"$Ö^¶Ã„@Àþé1͵a\X· ÔÅ`qJOÁ ŽCª—»d43ŠÈú½žf>®ˆš¥Τž0[Oø4oŇ&î6XÔ= ÷9fäˆÅÁþ¬ 8Õ-èF5¨¤G I”@úƒ@ô±a àtr0ÁìLáÅf¦ƒÀÁ6mBÜ ·ÄX{Êå4›! 0ñ;³´™Ù‰›Ó#?¶Ì0hƒ3¬xbº‹ÒOPhg&q©­‡[´—ÞÇ|6~F¾u°†X·’Њmäešõj$ÆDæž¾ƒ£ vb·H8Ê&E—<8}1· 숚 `Æ;ÛßÒa4±­Ó¨û°„Ùh\+Jðž;ÂyÊ#xzôù7*ÉÞ]L<(ÿ¶ãqü'eº*@óCG¹Úư{ôz$Xm› ½èéØãHÉ©z%^„ŸPÂ/l–êàÆnÑr‹h õ#±؇Y­xø¦>“öKìÍ2l ¡“mñÊæüBè9#E°nþŸõÕz„¡Õ¾mT£ºßa²BQñ-<¨FDÒh ¶°z4…z§ÙüSYTt ;Ù|¸Ý†-?£ïÄñ—½œ>"ŠÖ¾§KmF9„›{Mcð¢#}¿ùåÇ?t²0¸¬Q1X ×׉“ °m¦6¡;ß³dhy˜š „Í-’Ï­çË©‰Áȶ$¸Ìt±<Á' \lñÄvä´`GR]Ž¡Ñ9Í$ ƒÛO!4E&uˆ*oÑY>c…£ŽAÍç¹·nôßå_"± †-“H8ª7 h>Ô~‘º`Έ“RjÉfÉÄÙÑD„áéþ™y`;DñB¬~-ÆÍèÊðá¨vá¨8T"4Îk^ë¡V¾Äå•ɘ›,D0s_êµzvàüw©,è8ÇĨŒ„Îûfe‚«É;]ú!A¿¢D½’,QtÄQc+ ZQ=2~‹‰Ä¼S?”I˜¢æÝW%">À˜Ú:8ž?¿3˜•aõpw‡ « ëKP[š;¥1–¤Šlˆ#Õú¡=¡K@µÇƒwWgÿ“X¶ŒÉ³¢ÇØ Ø÷ – ýµ)òXûã0‰ºG\$Ì‹³õØÆŽ³í²{´Óèaòr%ØkÁÜ8o:&Dÿ‘mÈ M,\ÿÊiôÉ&6ÇÌ7b{è¡ê.ôaˆnlï6NÐҌ̭ðÿ+Foi͇L0c}%ìôV~u/š f™c㥼j‹·E¤OùNçw/OPÌ“©!ÝO_ËÊ +Ûûd‘{ŠÅ¾’EIòÉ8Êî.sEcô¼iv§UéÜ Øj&ÕKqa먋wMÚ4EÕG7l ènTB¼ˆGŽ8Þ¦掱ˆÿ±±3j lu·T¢š1ø¹ ˆ´ÄÌðË¢–“d‚^ÒOa¿‹.Ò ’4Ø}ø#â·[H»à“3,¦™åÏ«`C,;‡ÁÀà–ƒLz«Ž!‡1dè’7«:&\È^Î'³šŠ2ï‚»zq#Ü3!óH¨¾$Z‹EŽ@V gÈ `àtÿ¢¾·’ÿ ?aƒë[‹ý3zè ÂOÛ?~+“@Ùýøë•µÚÁ :l  pQ´÷;ˆ«rŽEHåÁʺZÂ}ºPÔ͂缢ÐBÐ1DÂl”ÿ:ïP\¨P£òƒ˜l©2lLW7²•7›º_úwT´hº¯ÒÜ–Ö˜¿Ž‰Y¼F&÷˜¦g€'[ÂhÙžè¢Ë¯ËwK¬%Ø6W‚ÆBÖrfßѸ„ï"Œ×Á_’s<#æ{vf&f;ƒç$Õdáô7Ø¢¡CÉ,ðù'ƒã¤öëÉ@nŽS-†äå½€2YÝ.*þOjV;4BJ}"R‚! d‡1¾ ÅÌ’ƒU“\äÏ›xcñ/85Ù r·ÙîKýnÂ<ÂoÓHõ[ýÏ$5¨nTG‚K/WªhÀò(‡'鬅!V!fgXtâ™JåðÀÄ·±ƒDA¡¹497aü*ap$ç d5äHÇQÿäÕ+ _ûq¸ê£­SúOŸý†©’¦ ½bUŠÞU"‹m©»¶jJÆ#…ÅËqµ`Nçº …ý8ÊÀ±G«¢‰ªÇs—a¢FÖ¦—{M§ë~ôb- ð€€a…¾c·Þ“ðÌlu=ùà¿|-H(‡s÷ݦNgr 7ê_'¸?BãoA«ÐÌœI§ýãåcC¡ü6·=à,·eïÓRÆ…4™.Éc•À{ÚðŒ¨2Ó®]û'ds»›7Ä–à "„ö gBfÇ_ô kŒdÀò[91ªç󄯧LÛt$iÃ=y€ ¿Â(4Œ·o‚;΋Îü¶Ó¦fYšA ¤xK@°cërô:<œ8hC]·üm¶‹àùyá€1ÀYÇf¢HóðÚ‚E|Áft­6Án±š(šâîdb%ù³šÔd ž†ïÀðä:ô"'‡‹'éð™ˆX±O^™tàÖ´¥ù/± *ÿᇠÇÄ€tŠ÷ê¨ d±¯‚oW8 WK8y¸|Á>Áy_޾.JgòÞ‹zü 7ýÿ`¤ÁŠ^3hhµsúÿædÆQ¿óÿîïï××ñ¯â B7º÷»Š³&²™D)ÉÜÚÃsDÀ“9Á<Þê^VzÈÀè 0`À@ñÎSÌ¢PS‹°Å†˜PŸó_Àh•QÒÖ3›í-‹‡l‚.Éq [†DáܰMümœVõ6‚è<ÈyÏ,›zÞè<È5Ê@÷¶`ðA…Xû€«Çð„‹¼l߬8vZ/iñûóp\1‰`eez8çqlˆyÚÏzƒ  =xYì‹Ë‰Ó§§SÙãÜÕXädW}I>Éfk±™iÂYÝÚVr2N¬û{½±¹<†Õ½B{1‚×Iýø7¬·˜¤=f»‘â³@¦øYÕ—Ãe‘6>$Æú²*ŒB`œ”§^º¥¼î:Ïlrì×àYTMê&üD¶Ttƒ ߥûõß‘»É[_.»l9 ÍNa³²‘¦Ÿ_@ΧoôAô= ×¶IûÂdB‹ò*rÏ«v¦‹ïŸÃåÕÀ*h½—gÌU2Œc{¸DP =+n¢™` ñ™¿×þ‡käßzZ‡¼­ÄOŒJfŸ¨MÀ#ÞÚ$æpz¨Û¦—>XX»¯Ê-&ØCi¦@`5­‚DìÇq!¾q`cÌ A:Pžeºpпxæ;‹ºã7 £øäÀCþœ²jpA # ðy…c?€›å_Ss‚^›JÀ=RUt‹õÓÅ¢ë ?±ZZ{ÈáÚG³x¡'Œ ¬I›Àc¢æEäÌN<ب¸ãNÙÛõÇxN‚ÈÜÖËMžȆˆÈ; V=k¼ÐQ0€‰ÖžS=ž¶ë“VªÞ5ài]퇘dµ”öùS(ÏÉÖÂŽ€pO‹D\³Íþ:r0Á½¥èC„WÝ!k4Ò\ÈÒ‹°$ ÖaÓ$L A·@Xb,œ$òÂöËYÈ&çk·Uʼ›·; Šé‰_ôþw‡&7i«ÙZæôøO€1ê4æǽé/97|µ?RËÅxô"‰Œ¡Ax¸áßv@üa¨Pû¹ž˜w®`ÕÝÜLËÿà—e8^ÑTsOøê°Ü ´PH9\ÞìÍ ³ìñ¹|Ó ÆA¾ž¯àJËžÉK«‹¥‰¶ò[2މ7éw )Ÿø¹Y{œ÷ûÎóo`#}Àͱ.AûÑÅnµ¹7Øì”Ëz¿ÂÒ®ÿúäM2³5Éza6²AVÑêÝÊR’<Ú“9üi€ W†ã:Z 0_nÿ°Övsw!;F>èbŠ<½0³Æ¡b¢SÄ¿$ȵ\1×ö¿ÀÕq2ŠÏ¸ŒãâÛ€+í³s È÷ã˜ìj|…h`–& mYý<µÚ|jÑèãê§U0‰‹¹<ž–§­8B5fkˆAUÑZå£Cá\(:ÜyL5A¨Uqg;§ã@@ÊÓŒõZ§ŸŸ¹ŠÑDb&'·“‡Í:v`“Öèy‘ˆkmÜìhn¡W;ªìüC¤Öíæ¡ÉЖ3«´Êu *<ùÎ\q”#@4H.)5=ÓÙç…°È[?‡}¶íÔDÄ GT…âòPnEXt”ºž¢O¤8 ƒu†^oÛ€¨CQª‹Ö.Ë*àõ.”y¤Œø’€nÒ~ h_U¿Á²¡‹Å¬'ÀX°K¸QÖ¦ˆµöÛÁ “˦N0飽l‚Y Ë­ertãV–Y甆Œ¹Œe¥¿æÆ'‚¬lºEÀéQn9nâ‰Â¢ûäcxu¨ªŠ¦–h&‡­TEúýkj5 2@y uam½?ý?ï™f ¶žl¥[ˆÝëY™8 qÍ#´Ëÿ[õ×iÜ7°ôªOÿæ¼™5e7Äúþ‰üñŸÂɇߺ'ÿí0^¡ jh©ÿ`-´h/ó1÷%B³ðE÷ç¼À½â²òrkšÿÊù3fÍH´¡r^ÄÀLà¥G¢äôB³yá|aiÇ\’±]Шxi¨Nʹ3~—8 '!CC¬–…ñ!CgË;9dÎ|®Ær¼fgµl•yÝEýÙ÷’ƒü4ÜäZ¢Éqã–O~v¶•,ð(ÉéÎÁyY©Ø>gMñä¹M|Ç}BÓ‹}&+æ«ÄË¡MÁ0ŠDÁ›Þi`G±pûõÐUËÿ'ZñQ'tc£dð¤Aå$é±Ù}%ê¦Fg›qèϰّþÛ¯?˜ÄÑ~¿ˆF„©69ªìt5ç{ú)œ™™ñ>_äZ†,AdƒÅ ŠISXn+ " …T±5Œ ¹±—$&³¸†£¹‚c¿2:Æ`1 iö€¾¬Í‚n`3†e.†@×£æ°$qa»Eß"Ú‘ã!b D'%W¶À67¿:SÏ'X#.ÍP´FÉhD‹(PîÅ`eéeõ'#–O£¥B™9~‚o{ `á| DÏȆÞúÆûÛ²m:ºé_~±_à«òbê#šÆ ÑÖ6ÐQÛCV™1•`ΙÈût°ûŠLIŸžt?__ýÝÄPïÜt­Ï§oäÿ%îjp¢õ’zø³ä©1dr­&ž¤_pï?&‹ ‹ÈüV4ûþú’ð–ÖP2{dx¹_qëMwcë.ŽÈš€¨‰,*ó ÖùƒSæ *ÇÂ\è äá#ð:C–ÝmKägý>Å®m&â–ã¶S-‰çú¡aåY5ö½10ö$rk©Bã¤5?Ihë5f¬]3öHÃQ‰iɧY ~36@~C$ºÓö‘'äi.+}|à‚$Ñ—¦yÀy̼NÚÔÎ`±ÊäWÍ®8oy,¿®ŠLY‘oèÀrHÖ7ÍhL÷ž’[O™ÆóOÃ0T›)؈‰RÔd泞gmš–yõ…D~+4žãŠe£Õ™?éÏðˆ°'ÎL•vµÔš –-$.JÁÒ8³`…{‡(OO-‡•À|œé½º—@¤.‡ ÖkÖJ–Íõ¡[¤(‡‚Eö4öìÞ åÃM}rJo¿¯½ËÈ]„y£Öx”´uO- Àý\‚¡ A£¬$û ÝQœyà§!º­ `<ƒŸ ãéV•,Ä}a æèȦ`š›Û"ˆN|—"ñ’0ÊÆ‰¬2Ýž¥í—{>¼8VÓœ¬ƒ£ñØÉ>½Ÿv512áäÿ½õ™Xú>úöûÞnI"•ÈØJ@Ä%¤‰Æhàëe;Ïkà‡‰oãÑÌËÌW1ænu“U—ð,j³5b$ŽGcF;Î|ÉŠ@w†>\«¹n¬:§ÀÂ~&¢ºÊ›qˆr…ÿ¸Â³‰C»y„‰¤ ÓHWä:ÙÌ£QÞxùµ% èôÀt¹·hÛd0&™ç2b~ÜÖE_´îL „Áµµjb[J8Ñ­ë †H4Ùù} Cˆ>ów™ÑoÃa,4eÅlÒnr×›…Éuz¡&änà•©¥ƒÙ?¡È ¯r¬r\´ íH¶2x!ÙäQIòk0‰]èV;ü¡7‘Ýé˜'X11ªö6ocS~ç2¸¿óÑ£®“סèÚj²g K¤…:1–«a--±Ô݇­~…¤Í;Þb`ª° j‹$xhs| B羪`ÅÈEWùùM®d"ÒQðÒ¢¶£e‹¨ûˆ—ú» ¦%áH@ŠJÿDÍz•›s3¥I=8ÊBÉ󈳯~È¥æ5A!Ò¢ØÒ|q܃E…Ô'Æ&=²Â¡]M“ê´+PÀÛvñµÂæÕ D/Í9™ƒï!wD¸v†ð÷gÑøÌÀƒµ°:Øzd<í4¿¶HQ\æ}`hж‡:jj,Ê&X@&­ÆJh{È|¢#î·[øRèFŸ0bÔKÓPª»^ú IJœDÑ©ær ŽW¹d©žÞÙ7ÙàÇÙ|\6Z.àmúmíûÆŒpvï@-¼+ /ßL.2R@l/Ö…=1*f[Áh[ßKh$ïÖl…6êghÈ ºB2m æÁ)šì¿éJª V¿d/W¯|Ë‚8x5ªO(VóØ;m3öÔ‘“ÍtÓþC4cç„x¶BGÒK„Ž…î‰´Ã¦–Øf8.Ü;Ë) ¤t…²mMz=_%Ýd+Q¤Üié§#9Í6™“¦vÿ“¶{fÈk‘ÏÈVäc¡®Ø¼PºË³]IÀµçŒkÃè?ÔJên8Ù„­±ÄR®™Ô 1¯êB`µ;!û€UØÇ,UCê&¢KámÔáo,­Æ{†$sC˜ÜÞ Ìv+UI$gè, 5L(a[Ç=ÿ9¨¢AŒ”­ÐÜé96ÜV‹¾¯—+¥pNŠåšWC0ôœk;æ´Íc¤ò=¼‘ÌáÓè>kûÕ ¨ê¥äcT€?3*]ásäÀäØœ8s‡‡ ƒÒ iÁƇ¥˜‚p=f£Ã£¾.ùÐJ¨& 'ÄVéØñÙ°ÈÝ¡‘oð/h(Ük}t~y«fm|î¬#í š}3|GY3î€wbfì÷”p^*4o ™1Ÿ9©1/¥äÖbʹocAÒ½„FÓ[ݶš{©íÑ/4ᡗǯ¤ Ò¬Ä@¹Bòïáw«Göƒ,Gÿ>&¶>p=«”…äŸ'ÂèúëÂù icfÁnß°æ±} …9“±òÆQ%ðåš@pÚCÔĶÔMó†Êó (bÍu‘ˆ0 Ÿ¸ ÈiŽŸ´Tg¤ºÖªü¤Íé˜ íUH„ÄúGgärhJ…]/ƒexU]SàŽôó£PXˆ´'áJϨGš)ßó1ÀwÊ,0 ­ÐéGù‘²™'8?•t¾ŠAø–I@ÍÄ«òÁ‡2mV(œÂ€¶±ˆö@9:ëÁ‚‘Ù>* zÉZH‘RU¦¶òíã‡D2òmþ¨¶¸Ã!­€q"$ÀÎ꘩ƒº'û×Kƒäv~e†$f ¦PÞ+ÎPÊ8ѳré àÙbˆbRýXrK5X_H C=IW·ª"òÕ`ýZ¢„³"Ü_èfÓRvŤ4sbEyD4 .üý gž•aÁN$ýµݑۣ. &’`ì. ·™¨a"FLª ‡çoÙz5€Ëšv‡ƒdP›ö–ÍÅÀ¼´À8à„;[ÀõÔ“é ôžÿ‘X„%3µˆî©šõ‹KKfnq”û0c0¦øåtn°ÿEÏd$òŸ¿QÖ£Ú±Hh¿9Ï žúà«ðlSI¢Åº×LvÑ TëMô—Á¨¬Cp€Öx²M\ ‰ ¯Óصî¦6ý .Ɖ ‡ÎB«8(#zUÆ‹€%Ã@’³LÖüf±°ƒžGè{=3–+Z~EX“£.»0̺rì˜7vSšØÈ ÇÔáÓsÒ•„Ýó(ÄŸ²X²h@<áÄšf¡Ãò>×m°‡-Pmve¡=äqF2v6ä/™”û1rðv2 ¨¸ÇÁ±ô5RHü@¤{ÖiôA& F`ß)M…ÜA•§š43ƒSª8 ê; ð¸éW³ó‘ ¬sÜ%NIzkp¤ñÇ“ì°OXÂÿµ”Á ªA }Ʊ’óÙ™ ›&ƒZÉŠj¼Ï |Þ¼€T‡÷°¿‚U4¨‘3Ä9þš¨Ÿ«ÁðÞ£ 1$¡¯$ÍœÔÜÏMËîH$ddðÙ‰ôd1±ó’håuï®øR@Ö‰<Ž_­ªußX¯´ðAnKR=ÎÆ:( š½1ÛËì6£ŒžQ©Oû¡«;AžGÛ$ۜء‡‚)-#–8Ÿµ][ZëPò€4;· ÁDâ+¦Côˆ$‘3ÕÚO¨¶U‚üH5Ýøl†‚x%¸2jæ`Òæ_z( Ð4z[t×ǰ±Ô|<‘–=ŽL†ŸBa/VÃÕ{ÈÚÿQã&éœø![O0 }a7oÏ‘pQ8é¾*³- NrÌÒ'‚ë} mjÓͱSY¼WYÝƒÇ Oüi04e‚}6_Š8àº4àêòZc(ü5N¿\“‰àeƒ®vÂ*}p>®B°:ÕÏ(8ÏÐàNñ¤ Öhò$vš­çüE¬FÏݶÙh¯q;…§…[é’côJiz'Â4X·FÈÓÏÊzo=Ä¡—#Ùãú˜`qÏr.ØŸ|»+y|6sv¨ú‰OëÅ×±eb1bÒ-¤ñþ„¾#¼a¶o{÷¢ôBÕQŠE¾†Ýãb}±°qJ–GH+>Ic Ó}µ¾•!»I„¡ÀÞ”Nš#¶¬:Cü¼¹1rÚo•ûÃÓbDȢ؂=ðÎڌĺf×ù<ÁäánׇöŠƒ¢äH{gQ.±iD Ï]ØÐÞ–üEBh9þÛĪJá`ϲË&¸ì¾1Ã.uaG£•ÔòæžÀ·»®ª)‹n9ñu†šÛ›%ò@Ù¼éĬ¡æÒáÎWn׿u†Lg€T RS$%î,¬Ý¾¿£1‘—Q# ÿBz0C^TuiÛ¨JŸ{Ü[4—G-'¥ø…')BÈo?ÑâüÒhÊJmRTìvƒ~³}vmÇËn|dÃué#6²Af§å½ I{¢uÆŽ}I„ÌmªÆO»1µ),}À((õR½'Ͼâ;‚éîNqjØnÂèÁ¶Ãö¸gLõ©%.Èv½ 2¸m­¥ôì  ßî$_í¥:þNB@Äææg“nŒÃŒ°´W:5vÂ}Noyù›Òí¤;Ãj«„SE% Ë4\Ï Œ]Ž˜aö:.B=ŸK¦‹ºXUD²>ÞéüLÞ0™3°Õ—$nfƒÑ„jKúœïÉ‘cÒ4)”i&8“CØ=e3wR`ß6ÖÈv*·î¦ëÿØÔ±#%ëlßËÓ‡F—–£ôC1¿;ß‚-h)œî䛢ý>uÂ"„Ò:GoN÷4:”G² X;l›sÝ…· 7? ˆ˜K;“{ß Ókdûþ×|€|]ƒ¬Çë4vÓß ¦W: ö¢²C Cn->ö«B@©Xœ<£(Ô•½=@ ц¡%$[‰Ÿô­Ÿç‘cr2“16žæh>ÉS.;8–Ål%d›º¡å”#Sì8¶ä R °²ÐД"1:À,õ ¶ó6;„€ïyµJdêP°Èv1Ê,]³?èSJ7Øj@cJ£%Ûàï3³Ú-=n;ïöHO§ŽU*~£ûF‚|¡šÁÄßFØ…‚­ô\YÙ˜YùÕhNÑŸÔˆœšD-kÚGô4¡&]4Ùwعû@ˆ®iC‰Ð»%-þshãÄóà-Ü\0#TØxš“é²&°-Ïà_ÊWÊÑPìc*¦ÀüRGq7ÀH˜ß0q#ß4 •<¬y3†³4À´`Ɇþ0‹Ó{ŸC‰¨¾#õQ>×i±¥Çè VßYÈ*Ñ)⺀¥Â7d›ÅSáû ü)“yŸ‘õÿ©úÐÖË~ßô.ÙQ%+ÚÂk^]]öFÒîZ‰vÀÊTCöÑ¡$5µË›¶½Géó'Æ¡*Värr>ÿ‚÷§øD¼{Û–¥6¹¬2çÚ1¬×ÓödCÞlÊmÓYÀdTšS –RV-°¥ûJèït›öQÞd•^œìÀÈ›æÇD)Ã+†J;tN1°˜¦$¶_“îD…  t†^uCíñEùQŸÍ^Ì26"ƒ1“°ÔŠë±¸Jz’žR|“­€¯ÄƒôF{Af ÄvÉgˆÌù ÔHR¬MY|lvŠ}íI.K 8BßÂÙ.Ö OíY5­LÏ-íaío "°h«R´–ÔbpòãX¥©#X ë Ç;|¯•ÀÙ!l\˜fñ$ò}«&ÁFy]&¡ùˆ‡uŸ‚”Q( P„õx¬Ñ {$¯ ä=?ŽÐZNÇg³òZY›fR(茌ðÉDve¤Àg,W¸#Ð>uv0HqÇì ·*êëZð_`€ 2âqQƒÌûOù|c^¼¤FA•±ò0}mŠ)ý :­êã^Ïh& @˜4Ñ©¾Ž(5à3hWíˆ6ž>ÄfܤŒÏUÕÄþËê(Aªd—RÂQ†¢àbmæzX-?•l¢¢ QóY"klûe=‘‹Ôÿ¸,Éñ zI§É§‚²ôìߤJÀFÀ`É1G1çýU5®¸¶5¿GϽžOÏGÖÜ€›ñ†«.8Ge3.Àj!¦Û2×…Áþ› ø6PèüHÀmó ÃÈ‚L“ìÃmÌEçí—l $;'É4(³ÌúŽ»Êáà æ!­‚® áÁXÞj3ÇÕ°Z¡µ^ºHÒв5Jßt|œ¶c'Ü ™9Äc=ú»u0¥ Èb9y#¹Äf3˜táðBˆ1ËBí¨­¸£5<ú”—?}í‡@Š›æNôEm ’ ÀÖBU`†ôÊ Ù(FÓàÛk(¶ÍÏÍâ|þ¬GœÅnKg,Ë1[¹+’˜DÕ€‰|¾Aî/rª¡@pNBEJÁl½Þ½ÒÍ`5mWÃf$^0ÓÇ8®‰Fw´Û@Hçø-–‘þdå?ÕÓüâ—ªô梛x "S% Øi@aQ¿ýät. —öFyÜ0të 'H2q(%“<‘<àîNY•®Y0ð)W¯a›°-Ç5  ˜§qeIå>ùð`›ÓŸ Ûê"l™“<¯Š=’1ež†¿ßÎÍA­*,£ƒÓܰ¿p¤À„@ä—þ¶w`JÏ?^ì¦þÔ«ú±üØ÷ämçÓ©xw!§—ÿð`VŒ‘öÀçíä=Ýõ" ‰ø Ëâß/ñù[-a8¤¨ïÃ&ŽÐW»±W^øŠyŠÜSá"+öÿ\_ö@ILÓØõÿ ÏC+5EæŽGù9๭–™Ÿ"è †cƒÛç†6ŒÕ°¾ýbÔÓ!¼ÁÄ0:!;ÀÐÚU% F ‡Ä,ÎäÔ*’Ãögv—@¬¬S®uDåpN~ ÛDrÒ)ƒ¥íÐ3=”#ºpR3¥cÄÁgô–p,g-ø™0àWÝÒ JMKS¹Ùo$ÒZe¦I98î1FkA•òÿÓçNÜÁGûþÍÔŒ¤ôé3u€ã#µÉKšŒÖÑÅ…!ÄÜ·£XØp ý0Œ~7ÝÌ•ëP.… ¤e3©„IY•Eª´œÂ¹ˆL—5Nþ¢ª±Ö®ADÍET·8ž‡s[_Wën"¾h‘bKƸg1Ð)xªvR#¾Æ{iJyU©–UIÀ9N ㉨J¦©º]F¡Š ÐamÌ¿ˆ™8ƒuI“Òö¤ëLKIØÿT|FF´‰n°{g¬öK)š[÷u!&®ÛÏÓae_r‡Yß a¶™ñħ’98MÖ³–L(îY mÌE“—!Hǵšȯäá£49DH ìÛ™ “Wn+O¾kô•¥-®]X»åÌ‹_¶p€\CgFŒ,ÛžüR7¢Óéßc·ò1ô•{ãlýÝßÕ&ˆ3Ëüð™ºB ·ÎÌpÿ²zôáëרn7~Î=m'‰Qí í Åï*1;îÌNrËnÆ•‚l"pÆ¢Ãeàç£.éü–Y xEº$ÀÿˆÜ™çS^ˆ0âW^Š›šñBT«w;[ÀHa²·_ý$BIßPr–E­û>@1ù¼ ýŸ/ó®£ ¥½é˜ßÙ? o@ÑÉí)Ÿžoù„f„ j„Òãj‰’ô#逷ʼn"Žˆ|I’–E•/&'=¢8± Rhœ"o©>lCN9â%Á·¤:ÀNÖƒ’c—ïc¡ci‘àl†•FA`‚í¿?ˆD#Ùh÷ÏÁ´ÃÛ~öFñ·°› ›LÇwްÙ:£ GØs} ÎÈHüew0a¨o›v°V·(˲éêÿÔÒ- M7›% ºEàg‡Ý¶Ññ 0× Z4ÑŽ-àDïÉihd僊A•¡7K?Ä\cÄtHáC•ñ{ø¿ÿ—ëè2)ÏgÚ£ïÑÜo×éó‘5û²I~½dC+ø§ S2ÛO`e]äÞ0 Èæc4PAö)qû*R ÇÎið,s}? ¡„ˆêLR²6Á‰¦Ì7…ÇÔ;ò\ÞŸH²ät­çë‹Rë/Y ÿ(âþ~Ûý­—#r’F™uR;@】¯»ž&…Z@æÌž>øº±ò9pàS~@I‰íƒq6Ûø÷`HˆsùáZâ?¿­"‰¤]zóø«Yc˜M0h¦T?ÿÚR  ,DàÙäò¬ê¯j¿ÿôx_L>„-ªj)kZÍÔÆ"•–°(§} O1Ý€ÐEÒ8•£ã*½´túyGý¶×'é.¸H…‰0ƒ ïA†éJ(Ûƒ8›‰ï‡¤sÿ>AŸ¾òœy1'ÿà5—ÏÿþDT7zÙè,R·zÛÐX¥`)¸d*¦Õ15 S?ql^€2°“H€ÇÈeß0 áI?4Ÿ/CPvßÊncò•€Ö¡žÚÜ«€ÕÙ¸ .táŠ_ …³^ÃX¬£_s6㿆٠Ù«ª÷^±¾jptÁÔ¡PÒÙ’üKñðèbxµƒâõ*ƒïØ¿ÿGøÆ;ÿøÆÌÁayM£!œŠèp8RŸ†U½$µÀ$SD ÝEë+P+d¡.QO‰mÆSÈ{œÕîKxÈÝB}|€f½²°3…k´ï„^‚¯y>È‘¶%rÛ¶=¢ªLæžÕÀ¢©¤ÒöÒÎ_¨ÐO%`"ú5‡†VW‹÷Üzs‡­ëäß.k¾9]å¥0ÙU.¥ÆÓ9†„íy· kuy|Y ò;?GšL`ŠV.ñ§[I´°è°¹ŠqÔùøáä6 ‡1Å]¢60ÅI+qî€ÀѨ²$Â/s\¶¾B£¡ÅZç„,Yä°Kÿ hÔÆÊ,Œ…ü'54ŠÍ]F ]8‘~³`Ä]¿ˆìôÜ~Ôö¾3N†”„ÈÚ|MRÌ•#êd1]ç6¥îšq‡Ò€b±XK÷gưÁ’™ Z¨zð’èØ'f[¢ù#IOð„˜A{—}gIÛéŠrYá œ™f¢a¨åkã×Âê 3ÙÀ<YŪ ¦)ddÚ€U^ ãýú½¸ÚSJß¾*uÕF90îoE€ï@¶‹yæIóJlƒ°,@@—ÅŽÜÙá7Ø$”²Ï¹!èöTCÃ袾¡-€/ˆ4e5š|ê\Ý‹aÜÜnʃø9Ë€õTàVù@¿äkn¾›¨[w÷†*h¦¥¬–t v¶7Ra,‰vSƒ!#«‘µci3[ùŒ±«£ª Í\k|¯ò.kvÈSks+ŒÑœGY3 Íp¤J»’½V~~8¢Ù ù9äÆºö }\fɯÝ]Ö ¬³ {#„àT¿ ¾AüAíPògýnܰÐ%¬ÞîË‚¬áFãæ‡—ªJãÐÉbCr(¨³{wé9gÊ©¨J¨Qó­ð3¹a„Ký`˜ÊM‚Ÿ>¬ò¥´ä'ÐÂm6­¿ç6?­V‘Öià7æ—ղϛ%¹»pÞ7˜Áw:Žé#{•´ÿæ{h"¸,˜IƒŽ°È;Ru5|À¯yk[ÒãÛH–ÖFGýiH8Â{Ô—’F*‡–u†ÖáÊ¡þ©ï5öð£÷&½aŽo¶:¯À|ÐWÆ?Ó¤v'–M ´çÒ3Gրҙ附œš>ó;ŸOâX6%øq#ð4oäÂÿè¤è=¸\$ÌS‡¹¢ÄÙžxÞ£’K^ÂÿŸyG®Ò[Äöõàj ­¸ÍÐÿÉ—NÅŸküË2CF×ê-ôÒ •åX% ­\;ÐG>í>€R°|ö‚ ¿øÓYÒñ¨ú’µ]¾m-bëç nµt†>®EæÁd –0IŸn¾¹£!Ê/ØrцZCL+-‰í­ crˆÎLðð_)ªnÞ ˜:­?åaÒ’/ýž D)ÖE÷"âf£3^-e‚K7ª€]¨šôÂóðó™ð\~rÓ(LÑʱÛií0‘FªÐ;ÖÁŸ¿ÔôE½~ ©†JõV q[°–L¸yöS2±ì&àÆ,e¢sÊ‚É[²·„\ s¨–íÛ Ùöò¬ÝŸw§è´ù ¸jJÛ«ÜÉ´#œ\øÜ[„ª^FËõ©MmÃS;ô¶ÐœAÅä?E¸ƒ²hnñ–ËijÅ_B G©+ ’ö Ó TÄÏ©ÚKf¾|0ÙC–íÀ–áœL‰·¨•ÜbÚoñn™‡ }a3sy!ð0«Øu^cL#¾¨áØÁ•H¯žÐ­}¿]J½ô“À[xÚCX†K’Ù²_àÇ!Z¤ÚLÕ¬úØÙvÁë˜E†èÙ ÈF·ÑU“0©X­ë´)¬©DV?g0¿È[ö:œEØ3j×ó] ©:¦ÙØ؆5`‹ÒŒê¬85™7ÃrLNw¤®nk°±5+¾ž0¾9ˆå¥qì |ñ’ NP Ø Õ/{ 攂øŒ´Àß·”*2wÆg˜ègõüƒ40LÅcË=@Ô/G¯N|´ÌÚ+ «I´ÌCŸ£jÜg‚.‚ë½)¦4Μˆ™‚Œ[0´£I1[Ž$ú4ýh> Õ"¾+VeœGö©ÅÒáƒéoªæ8ŸÀ^Š"hÃo…ñÁ)Ï`ÇO „ü Ñ„­@£(ÃÔ¾ˆÖmy…÷Ðú†Cv_ޱ Znq?ŸÓ(LÄÜ„= ½wšŽÅm³IƒƒT*’3]-'í3¹÷ãã ˆ„hH^s¨NL´ ú›[b—±•X‡£G䱜bœ÷Þ8Ï$Å+JD[r‘a^<Ñ!r!˜Ñ;$•ËYM‰o¬¾r”‰Ø‘Aëa4Ç9ÞJ â6ǯ¸ìÉ»/Ÿ¤øOo¡üø‘\;P¼Yâà¨ñ€]3¡†Pk\ã5øVÚ Þh<O£ ×YÆÏÅ[2³Trmt`ŒÁxÌI?̤Díñ¦Lè0ÂO1LmaE9åEKy£ŸãCóDlºgˆV ¯~áƒÑr+’,€cCS‰Á µ‡RbúÄD„!ÅiךqU¬º^` b>•<£;àÝÏ­ІÇ£•hmü5èÿ‡8  *·€HP‚Níç\ÝL&~šAA pvCâJÐ òs©n‚ÛAùè©Âu„ÂÌSê$ÁüaúJ £RtÇŸ g®³ÔÇ4Ò¾ðǰ—ñù¨Qdd¯ È—1ëÈa 4¶ŽÀ×5‰ °>`bõ¹å>—P›¡íÛ†ôr7×¶- Î€D(’:ð'@嬮°;>¶}AêisÜà´¢üRº¡Góùå<¾ð÷²‹üšÇàJÏò´²ñò3„4ç(ŸßKã µuâ@&#‡ÈP  ø…î7•ì\;T”x˜Qq`j›ò(tHR;Á@À¼ ©ØS¥çà­xo ¾U°¯‰„ï%I¼”ü†ÿ™{SÇM±ÿ"è;LÚyÛ@íg{vhÆýü.#+9X­Þ¶µÅ©%Òá~(ælÚM„Hu!èpezêë ܆Ôt.î3 Àå – ºxõîôÚDòw8Á¨4ˆW® Çß䢯ŸÂÈŒV/>s™A•¨³\.‰è•FÞ€ÀQôE·‘¤d ×îû»Ÿ¹“#®¸äóÒÆƒâYÊMrŽƒÊÀ7öèÙÃ2C: J€‚qà¶ ò„Ê>0ä=[©4(ÅÐÉAb¦øÔƒ¤e +ç _vx‰à*Ãô%ôª„ØZ}‘«à‘ËbBƒ]èt ýž?Iƒ…`èuÂ^Ÿ‹±´¸ ˆyÉ~žøTëðÄ·µaàx>èÍax]3˜‰´­Qå'´7"0dZ6A;£ »¨ÉaÝ’¿@^Ù="p›²0M¡~§rߣÖ!nþN]è3²!hÁ!æ.ǾîÞ,ŒÝ½rRÜßM{` Ä„f ³FŸÊj—²ub…}jŸ/†µ-íÒHëu@ÐÃ<w‡­£lŒ’€ÇgGçìší?‡`—6òQ:³‰´b6ƶ’º%í NUH×­Qvàip܃æóf‡<±Lìçì±vÂz®A?2bÐR 6€0€Á†`§Þo<€ºÑ:w©\@Ö,¼8Ò̆¶§H{…Ñå¼ÌîÆyÀ@Q/LìG÷v7ƒ‡Þ·ƒ"Dc!­XŽÅ,%t ,ñÜð»`PŒc0L‰ÅWß~6Œ–ô7÷â ¼QG³eÆ×`QTýµ-*€Ž~À׸2dlÝ ´á{’ê[—ÏÞ—­¨Ì¢ ð€†Ö*T%fÞ$«“ Å7(OŽO`”‡š¼ò=#67~åàÅ.Ýp#€ègNáQœ“8¼ñÑ[…ô pAÆCÏÇlQ“Â{`´H á¦à‹n‹=Ú6î`[å–Ìû%‡€¨§ˆ ñ§SEÒ¸s{ú|¨ÿ<±K¤E < Ùœˆ¬‰Wª-)õ ,ÛI[ožXôu÷È¿ïÀՈÈcêGXAÓƒ–úÄS7ž% ã5 C"ÒA`jc^€¼|1ô¿©8ˆ ÖîÃ}/_îÌj‡ÚÁqÌÇ©£Œ ÿÕù¢Àb­L }*oÄê‘3KóLÓ\v¦‹SÚ–äâ^R¥–²nÜråÀ0”ËWL)1_O䊕 pqȰê™NTŸD›G²-‹êJ4îÁþo©ÞòìÛQ˜w ¥i´'x0i‹éÚŠûÈ#É¿Ü`u1Þp “}¦w6’­ÈŸ@sÊlò^1<Ääl;X¸E@`g}¿‚u/\lGPœ„ŒyZ™q"BHÚMpÀŒŸÝÁÜq96à%Jl ðú¥}5qÌcÜy^¦ˆ B5k.â½4¬‚8çÁ5Ì±Û q¹‹:à½Ó0å”D>´Ù1æ]Kw»WÒ·@”zU]0ÐváËÿrŒÕ‹1€I°Fœ!ÎXРiÄÅf>ï±ÏÍͰÀ©@~uÑ1^ÕcûçÐçŒ,6ÂÃ(O@ܳƒ1jFOärÃ" /Ó Ð)¼ù5ô|fxÁqÁNBøºìs­_ƒù)ù»™NDû!ëÂcCã'zS¹½€d6y0Õ$œ©öÛü¢:SŽý_¦ÈR]G´jû“Ñj‚ÁÙa¾>úéÛYÄC¤>š…îà04Á¬"À’ŒÒ±H9às¬Û?ï)£Ñ‘ù‰´DV¨2Þ c‘gSæ`'©†|Œ‚§ñœw„4l–üA(ôð;Äcúî¦ês%SÀpËŒ ‡eÞ'¤Y yr8šîÁÐ i©Î£÷«FÔH#ÿö²&³}½þ7ûf¨§.™|s]A@Žâg·µøa£¯mQ¨ ÔšæzÑŽKŽ.¿¾ìf9 àº!]GnÓþ-‡·aèÖèÞ¢vóÉ*ÓT4IoÍ(Ýö¸Äãï•ä•Ù<#ÊNVOÞÄj¥í6Ðj´cz"†»gbæoÞ<°E—äQQL>ߟiØßÞ®Vû½žÿ#ßJ˜eF-)p¦D± κo{WÄúTaÛáyÉá´€ü¾'Ýÿ._{ÆŠõ¯Ö自Ér8¿"ØŒxB~©bWÅá@ !›­ÚO!ÍÇï·nýÞ@uÂ#N*p€DK0àÅ ñNhHfÚ¤¤JÀ@hÅ-a“ü hW†.–VÔMGK½ãÿìÏí^¾ÓõËÝÜ ý ‚@hâè¹ñÿ ž¥‹¿{®÷â{s©j¨;„´¸¶ËšI¨o$1t]hÀvZ5t™5N†®Òf«µcƒC^hNøC[·ˆ…ënÿÅâóa‰ix„³H䀩¨˜?mñ†Ðk¨}®C: ¢Š¿È$ê)>ƒeþ7ü³P/£Ïû‰'3&©ÂJÑë} ]V\X+ÄmË!^Œ3Á}Îkû–°²1½b3ü½™­3c>(‡VÏ»b«TûÀÇ©¹ŸËŠK4=hs›¼®ÈÓ”FýlÜÍùËJ½¥žì=ÖSBÏxˆ0Ì Tƒ.Ö(Äô&ŸÕüƒ‡#×ã¾îiäbbç©…øaØ·Aðyh±¸wðKõÌû2aûÂ#«@*"ß6U Ã÷?;‰óõ4ÅÊÜÂîþ`º$1qY×doû'w¿cަG\þlt^™'÷<1p¹)+“cãÙÔ}Ùq‰¹ÛåÄn4žÂŽqºâ 2 '‚6úHw7žËÏ5Ýzf·"ÿi–îzô„´­_çóåÝ`^;Ѧq"i}ÀèaM¶Zîäü\B-[`šíùðá÷þÑ ý‹œ{“Oˆd—[ ]G¦’f6Ö¨EZÕÈÛL±f Ëú`ÄuT?b@¾ö~õt‰»—ýç„ÇG äNΨ‡“ Ü*t*)}“ñ¤Oȱ”ôÀ €@-°6!ÓȰ<ˆ ´y¸Ž*/ß—´ýâŒ?­¿â'ˆÿK½}ò嫽|ê(­UK»€ Eíl~ ýúvûg½~åd¼Ã¿c‡~®õÞ¸¾ •H}÷‡Kâ¡pF FxU›"w½±à›ÁðªàHi+N ƒ,jÈrÃñëߨ@ RB$ãLÓ-÷Ü2ÍÑr'’Ž“ãY°G€ÒØ„›I ÐÁÿ€/œŠÛ°ðVÁÖ ø€Å ¯™GøÆEw¾öŸïL¿ånX)Ä=î—hü¤.ÿoÿ1ëÿUWÖîïG|xŸÖÑÄH\È ¼ø CGtj˜ÂV}®¦Ó/׺{žBøL°q¿7â'œEUc¤¦t#‘«(öÜ¢/Ê{•~=Õ×7“þ€ %äþëˆNŸs˜˜…z4ÿ€9A‚q®nöÛæ8b´5«>óÓM7—ä žºÁ¦unÁ,b´ÄöÚiñNZ¡‘05A÷`D­ô±[þ¹ÀñãÄjH}î¤oºw}ûcP…§š N^{–l÷ÈŒPO:«Í±«}ß·fŸ«à.ô@5PlÕrÙ²$ù¿XJ£œiÜQyѨx€ÖÇÐJkX2}Ö‚¥i¤GtôËÝ… *×`Ö=üó ð³pÈ@CO\$Â$)r4ïâ"b&&5FX°T\5ÒÚ¦tƒÈÖšb¿/|ŒËÛô:Ħ¢X•‡ 4cìU“zãñšR+W{Z Ã Ýú3ƒ~:ì(FЂ¿›Úƒ§ " ÓàE¸r _!»3ö²‡°þ, 0ͬghþùã‘ã×hßçõQqîûËŠ¦[o߃8gGŸÍß_)žIª4&eAx=H»ðA “3àv‚åúù0•²iùçø)+¿¾WÿŒ£É–@›mñFÒ@m Ó J¡EzD¶Íë€aÌá)>‚¼à®ñ1r ?€1Øb•}m@ ¿þpwLcS%oòÔ2θć¨ð†Öõ€)ývóýÂ. €H:§\ãü#ÜZhûï >>«!Klï!tŒ à“úòqÈp>?"‰±8 ÓÓ¬ a‰Y*·q—þ˜`€|IŸ4@%A°Øii|²!dJ¸F(µoˆ>Z–ž‡»ñ° ƒ¸Šö¨2ptdŒ¥ö†oþ4·¯^YˆíÈÊ⃥¹~­‹ïnDä)ð¯û?ƒ@Á ié§§ÿZ¬Š‚ €˜ÍÃu9¤@^ìÓ/4+ ç@£¨n8@‚ëÃL¾ FŽzp ê$M¹_…8m^z$5eJ B€»é¼ÖrÑ}œ]•±ë–~”‚‚0 Òb>ƒßm$ƒéÐ0j¶WÀà>ÀŠàŒÅ¢~~À!\R<Øþ¢[vÂð¤«ìúÿü;–¯Š½ú"'þßê«Zû×iåE~ú[¡ò x¦ï-³`Ús?Ù1¥¥ˆ¦þ˜‚Èó#hÅš|‰ jÁãÿ˜Ž-cû|NðZýVàe©N° î!†9ÅèŠÉ‹ƒ …$wXj×Q]“ÞR—w2ÂaJØ3ï]”nà°56]ùXÏÁ΢ÒMä|Ý:\|võ¥ÃâüÅ?˜?\F°Ì ‘–ŠXç+CxÔJé&VúÀÀV!¥·”̨óaÐkƒ”õ ÏOeéUÀYŸ_Ä4 1 ôh÷EFÂ(ÚR]$·¸n]|ðúQínA¬ºeXû“KÇJ¼ "e‘yñƒ#ðý–¶ò!ìs‘Àð [ÍÍ€–P—¬ôªWP68À¹€$L‘ácÝeÍ…ÐÃNµº¶˜&å§´%#_í–[ÿfvÕÌa­03Iú¼I¼O€iGË‘ ða¹‚ìå¸þæÖçˆ÷CõR.ͤ³²/ê”óÀïØ"h#G|9Xiä÷iG½MD°Àsy ¬ì'ƒ™ òÕÖóë$ï¬å+€¤>‡ú ¬ÄH@½µA–?ÎJ€¨(3Àf÷9l]4@qî«1h' ³\Ñxƒòþ¨¬¤ñE‹JÏh¹ÞujEéGÒ+ž -Ù_ä†v9ŒðîQ!QNó0`ÓøAF‰ ÄÞð¿æ_ù§·¸;œ¼þê¼,¸äDÈ\ dÚ²2Kø[Ss©y‚…MH ÒúV F£3%Ú7&ZRù)O¿Zµ©–ËbµØ¯%ŠøHQšW6±_•ÃÍ$€ðÒ÷+gu¦+†Ek·M>ju"¨ù4ƒžIV›¢K¸q¹ZŠX x§ˆ6( ËÅß52n{0ý1jTVg”Ò~8÷Z‹ÊããG–ÙO§8”Ψµ“䎲ÖçÃÛ§p1Ü:¤Ãätþ¥ ³€­œº…k+ ÕHòéÑ“è:Eƒ`Õ–¸AÕr|:8L|Qh€JHfнWCpáÒm&ÂWDd–WQì*Î" ~Ϭ§(Ç=¤êSì‘ò^iYö”lf/Oª«•ÿF1jš*IÐÕ•£ÞbóSàíù-mÿ{Ä×µî ÜcÏó6XÄIJk©d Ç æ³ÙÕ¹Ÿ‰Å†i1&h¡5&UíDx³J½´Ñ Û5ßën 2“ĶÄH5ÏMˆͤ–¬§ó9ÕċתxÀö´åÿr}t––6•·Ð¥ó…} aå ™~ añ/}è¸pSl‚ó@pWNåLÙÔ=v$þ„©äg7ÉÌMwZb×·%í¾ö@ñ}¨½º3ï¼õæå*µËÉÌ€¤N8ÈÖÖ zŽ%´úê3%<š&ö9' Íø'ºëĦ”±¿ýÜb~vœS-‹ŒXÅ\—ë&Á‹Ê“ø­S·´»¡ç¦M —1o§“Wȃþɾþˆó3B)$Ap-µø{ÅI2$·*׬ÖµªÄðŽÎÏéÁÙx¯>n‘\Å)Þ?ç8™’Î ¢±Ö­êã‘W‘}1}*ÝEWdh¹{O”m:þÆm9#ù~Ž"³UböLÎ{‰ z©ˆYðͱ8rà=•¶hS==ÜÏŠ:/®sI©Ô—©töã#ð8þÓ`Ð%ûzû²»LÀB‡E¬Â†Zu{X¡´|=彉óF†ê§Ÿò5îž„ž2W¶ÜÌ_päÀ…‰ëKþàÌK4½»ù)d™š,¯ŒÑÆVûyñ P ¶³D1­³q1I*Çо:û}z䦶ØÁVìÌWmÕëÔö=xcj·¿®Ò…ÖÏ8­ö’Í•H¿wý!˪býÆÍ)óà€hkPÛ@”Xùá°&5p½‘ÐÏÇ“M•šm=F¶WQXøj°ÆìÚT{°§tÅiçÔGâ)l¾Š¼º™G=åM¹†áF9LÚ¦Ìézç}üPâ';üæ4üj$¯sÉ6yxÆë6Ÿ&U™  fuHù5îhŸCh‡ ›Ž²}¿Qßp6â)-Ø1žûW¨Jø~Ø^T ý'ˆ«ï˜C¶½µè¢~fmøLú¸d´ð R8ýòÚ™kK‘nªÏ¦ÇüŒ"ÖðNøk«_›²(5—‰Ï¡¯Ðäâ0Ö€™ÆØ}¯ùÓ&‹€%Ì{žqÝ´BéXOE2Ûšº-hK2oỀN–(uÈ6µ8¾ ¾°ÍŽ)¼Mô& ÝÃÍÜžDIKÉã›ôJ†âBq½>ÛDZZlù\U †üýña=xÇ"€0!’‰ â„ZÓ¥c\,Es' B¶zž2 .à§Iè`oQ³ÃX¢=… Z þ<}r[ò7p¡®ô;9ðõúyP*Ð×§š±qó5–—¾ç…(.X‘3¹¬~›(É;±DÇèiñ+R€.£fVƒ/Y•â‰rNÁ¹0nN åùðEb_E‚~|ÈsX÷8aû”ê]jR•¶=‡Ã YGâ`ŒmV@&V{sOéËÛ=°òŒi"9æBИêNb]Zé§­ô¾ûÍ(K”?‡$wö·|‘hGÍ_ö›b&™ÉKWPJâLøÐÄ‚În¢zô»¶Ô5pÆF"Nœ µ3ìhäiѵ£zóï¨:F5ò¸åL³Gïõ²¸N-J}’xG¡MìIÆø!ÇÁ"^ébÉàÂÃE‘34¨ð½<ÀiíøÑ 7WÕêý/÷a¢xJ\\9ŸÑ’NE…Ý ÑR”q ®§—’#'ãäí)X·Š_Î@Bǘchv_@} ^Δ«4Ùp‰Àh3Ó£Êk ¥Éq\æ³6¶±«Ú’Æ7ˆ~Ok¹ DûMˆÑžÂ _Ê,à$¬³Àj¼×­%-þÁˆÃÏFB(…ÈÞ„Áã+`©ÆøÎ½-1xà8R@Æk³îÛ„Ú(õØPù Øò-RÛüh Õ}ð °Z7úÈ ¿èÊ·#]Ysh°úý1 IVнe}9hŸ«l¨Úe(|ðúÄ¥Jãx' •õ=káx·ÖKNÒA\j_²ƒH³çBQ£=x’£‡L% Ù\KnÈå ë㓘Íí«zÐŒöØjÖ‚*9è–…='“ÃAÍF=%ÆÅhoA ``p?üáuüø›3+³áº0Èrúˆ/"F–JMz…¢7°DŒèkö¼è]í'x…|­QŠhŸfô@øFÎø3» ÌØk<*󛮥Ý^™¾Ì~ÁУË$§â q'̆P‹º?Â+b#‡g'fâ!ÅW*Pެ'aÓtizÁ”aÕ0ˆXT< ˜BPz·ò~_çÚ8·m¶šÖ©-oÀP6#Ÿ¶…Î1ú FˆÖ©ã»z²)pm„.`|'t˜ÿ^‚0åPþÕÔ †è8"§¥c‚æ (1¦–½z°’ÓB„¨´Í,óx}¦6a¹váÈ¢zÐ@Åñ;«€_ì¤1cÖSJ9'¦ °nR}bŽDf+ŠBµ†eø•½#xñHKÖEJjÀÂ]hiË„• ø)M‘å§Í$¹Ñ0SXeëíÖYM»û¬†ªX•c((N¹š à¸kWS"® ¥¢ŸàpÜT»r>ž}[&)TÏMÌ XÈØó?<Óø·å±tp&ÚÏiÇ‚¢¢(òj‰Ê£Óáâf:!3of’éIÖû ô‡7p•Q¿¢H(2Xo=ÄÈfñ4Hš@Ã&u Æd]Cö†ÓvƒW¡kg>Ý%ˆÁ™lñ±€$µ•!E>8”Y+ЇÈ%ƒU$ö×µ]'{G3wHŽi*EÂçƒøâxIÐëX w¡™Pá(¦iO‘0šÜïÀXA$ X®Ö H¢¹ˆSÆ  <@m•7É]¶2’Ýto¬FYÃN1¥£®@¯%nq‡ìϰäÃs9g²NZWþ“Kb§ÑdH™œ°,; X.ÕbÒÍ£úÀLÚŸ`A<êü@«2¨³Æ¡7‚ª"JAû"Ðum=YKÝòFÝ:7ãH ­“e”ØI#r£½ÈÌ™¸$ Ü2Hwö²K¹±:ƒŠ|"Eˆ ;‰ó†Y9<äõ1%P Ø[zOü«Üjmıºb×4¦ÒßAÅÞ…žQ°=o„ê2y¼&âV±å ”»HÝ:á$›ÿkÃÇÓà l¹…7ð°òÒkíõX]gDvÄ^Û3HÚTÒç[Lhªø$~½!çL wífÿDÉ£âh:Ðh,Wðí\ÚÂP¯æô¾Éh§‡‹Kœ@˜”\ø%ÙžÈÍ,]ƒ%´WŒLM:ðÀýÕx$>Ív$Ù·‡ÁÈc6¤‹6Ø _y`ßÿãÞˆÖó™þÌıü=ƒ&ãuú˜,VžJ ß'Öü7êLwÄ~€­@KDH¢Á¢Á×An«a‹MÌÏu*BÁ¦À×®Ð@o#|ƒâœ'F„„Œ2ƒªCÑgÔZ0Ì+o©¾ã+ÉùP‰Àóܳ§õ·>QËíÛ‰_ì.úTàøŽØüktÆÏôö–•‡È*iXL’fȼ%…ï„’½ŸG6Å6ãP…m?HÊ¿IÓŸË'##sº‡Éc6êb<”ï>ƒ8,ZÈÇè×µÈà ¾„…„$¦2žgZh ÇŒç+ÿ&ø >Œa,߀‘ª\[ÓòÛÇMᯘÀ€ÕÕîp¹ªX'r?4ú?hœ¡" bÒ”—–—<|é‡üÝÈ-樑iG¾Öº·FYÂ[túÜ?nƒ± ¨ªÞ·[„»8~¿±Ý¤æäØNYÈ9ŸdÔH2ÈÎÈ$#O.ÄmãØ°O˜Û£úl´ç&±0Z„Äv|÷áÖÏ®P1—”{dú&Þ hmâpÉ„Ó!ƺ'±‘-æ^Çä„ÌhñH߯‰Z¸ó”QiBXS:@Íþèê‹ðjÜ¢Š\#ˆŸhRˆ¿ÀFøBËQuf ®Á —ΦÁÜbX&óMrƒÑsÆp-G¢3S³çÿúüï~ýM…_ò/^j«ZÕTˆ:¡#“,vÞ¼w¯À‡ð ¿ÐØ5áö¸KùÿÄÎb‡ªõÃVP”qÐ`:@4x¡¾·Ù‘ú¤µ}<§‚ÀáƒâÁ¸ˆÿÜ4·IÖ‚Fbøf `i'"RŽJà °ýˆ–QÓp|Õ²O ð)i¸Ýb“& òŒ¹¾÷ài£dj›AØR™°AÓX^òõ »¬ ši€1›7©°M6?èý×¹TIZþ™›ößô7Æ!ªVÞ®;Ó½ää€||æ>÷ pãT¾*­¯ýsËŠ6AÓºpη@8#ƒÓœ>>®úžàMœÐIövÇ—´fV@0˜õ]oûƒ‚óH­(Ü©Ü8Šš¨ƒˆ X`œqÌ€4:Ÿtÿ7c!–Ptõλÿ:yãõõúÄÿÔÔž&Òs:õšé»cƒœ·‘¸–mÿ);øD}óá¬àa˜ðc¬9L¿ü¿šÈ«QSR¾£B5]~ýsˆ7ÝÌÝÍ'´%¦žÀöÊDïçun§¹‚sÒ·è®0iX[8»ô üCµ9Ë­TÍOð´{õúè˜â©sB©y¿¹êmBÇôÒ¯ ±µN½Aÿåžâqä´Q`àLâØÚ½Ô¦Óègýaö®Æn¾-†Þ”…hÅü‘I¾ÎÕÉ‚÷çm´õì’Q\äJ‘ÇM)NïQbâ˜Ø3&ë,‹Xd–p‚Ÿ-@¾YSZŸÈEòµCŒË_@¤"çé¯2˜žø ¾ü¡¡=À»EÝI9‹î™¶Iv‰Ø:SýVŽòN# nzhpîÛK¥?Cm¨µ4E…ºžmö3pg¤ð»5;ãÍ­o¢ž*"¤ÜÉø· ƒÆÔùLI5´ž“ÞÝ‚ÍÕ{}ÔH¦åB£üÖËe…ötC¢›tÍ€tNIä-"¨ 9Ðù/Ãtý[ƒ¸Ëy3Èë á”D’·55rG¬ÖÒU'ô:™QÞÖJ{‹]ÕeF{¾¢â›£N­GáœÅß‹€ ^6ánl r6«·8EÉàl™+çLþ2#Œñw`éD2?c'+ÓÓj˜?ʇÐaGõ?`K,Ç:Õùj%‚ÛNÏ·li õk`'€å=”¡\‰ÊÈ4žé[zîû»‘òšÇ/ñA¼ËHnİódª é›f#è\Óœ˜Ÿ‚Z3Ømof/[ïùü'töÒUdtÙ*ðÉ‘ŽÔØW(ÕŸ…‚¦…rL¤ˆ€ÆÑ F"¶cØÎw»A“ì4pa˜‹Z/Ô‹åÁrä¸Ó% w?jâ ÊÕž¼C~!e•.ëNŸâ1(à€"qt©ßÿÉØÅlŠã"bH ðsàÆiO ³©lBµÀ˜lr·âS9K̯,çs€hü¾ƒiµ+Q&}mî¡þü ÀŠ]hjƒ<í,…p6¸G‰dD{c{’þúìÅí$¦II‘Ø0Ÿî’z„sOÝÐÉ“ÑNi²#›ýÅÓ@üàÑrYáGåûñh½½ìöxàâÒè>lÅü“ÊØÌ³\È!¤÷ÍžœJ®ãõÏ={߸D¼[ï©À²C_þ£<‰]ï»ëÖ(O£’ÿöÛñD’4{ˆø|tVoÿ_Ufšø¨cð9 ÙøªïÏ´ýUè‚þw:lêœH s¿m¿ªRUTúÕ²ŒNÓ¦+k+u¦ B‹Y¯×¤N8X´-KÉ’àb™ XÆ'X~$VóY DR' ÿ™é%ÓTª´1—?ê ¦n»ªä3piñ>lüÞ$<6ïl¨{ÒùŒñ"ª¥½÷ymØp—‡Îf`°T]2%wjòåt'äŸDT”\P&Û†JÀ­ã—ÛÉ®'¶?Ï?§ö'TçÛÞB+að›RÒ‘‚¦‰ñ“ð`=IE3)UMv^•óÍ'ý-p:¼­òÃŽRrÅÙX‹?¥I)BÖ/¡Êìg£|)ªðTWøx*AâœËv÷H*.$†ƒ‚ ñ¹‡±Ó(4iîzd\=Kÿƶ†¯KÛŠŸ1YpVw-ùô¹—øž›~f'„¦†šÐ¢mëúGô<;'iÁêÏyè ™à%÷aýî&fˆ.ÄŽt–UšSá9õëû3èf:/Ëü -†”u ·v0Ê–†#h [²-aA1;¶}•&0<Ø OÑ9[뉠$` óëFK‡HB_ˆ) ÝßÿFµ;dmX°"|Rž¿Ûf& j–µ7x?ÐT¬åxZ¹ÀçŒp&£-â'àÏc¤p“~ô2 ˜þ6?¢9î·cŒËU& þVú¦Íjj¹£æÓ{Å_<¦°æÒCÿ¶B©©Áý¯ã}p…ï=ì8 i4ÓO„xÚ%>¥l”u&¦iVýljè£5Ž#ÓŒ3ýzê—+GF45%°YãøÌ^ÎÚ/­3æ(¼ýp¾ƒÎÉÃy±ÀC謉#¤¼’C²Å íç’Ÿ®©DwÌ‹žÛUü;¹>4iÀÖ¯åþåèÿÿì3[Àe»äݯâcöyþË· ±ÏâÆGÖ•BH‘`аBý°«ƒ]‚úû©\‰Úé:6ÒØcË×¥Mk¿«ÔpåÙ´¬ Ü»Ã&ï_Î3ýÊÊÊËÜiA–Öª´1ö.ñ†©A5*Õ‡á˽h~”†£IýX…æ¡tjº·‚ɰ8C®>ýò}³p‹æE :Žšæ±?£dŠ êgÄúê=s $ÄRe»m`ÿP~ÔÁhZÃëÚÛ©·?ŠïœÔÄ« º¢HÛ,ÐAÎ÷››LÊÆÆñy?ƒœI ÏBî”&S_¾™„~Ü"­w‚ þÑQôçä&z)º±6÷98‡6Ùº¬– ¹f[œ2ß•‰¶ š’âK¨gG8ÖìË• I8Nçë[ –¿€ìÖå*9ü èÓ0Ÿ˜…æ‚&{¹ÎÏÉHgÝŠƒ6YôášÙtïæÕæ‚2CŽ“M§’ã \,¢x;‡¾ øu‡©ªîy]Õ¬Ö,S$‚ºòK¥¶Ö´š|¬K3VƒM.Rh‚rn3É~ƒä&£Òê@?z]å:$Ó¶k±X5JÚu‡ìi-—Ö›»qýú‡ÓQ}Úl N•³àÑ=az’X²˜K›S-Ðb£ÈhŠy¥m¼liŽÜè(¢š 2«2_î«Ý±¯ÏRVnÉãšOsœ/äBÚÙΔ÷†!®ÈcCJ©ªdÄð&¤A§Ù£Öð¤P6ÁµúÂs5¦D̺HÞχHõ—*0¼ÚÁ³~ŒŒwœ¦Â»4C‘îÝïÕA‚ZïOó " ˜šÁi5×O‡«Î.vÉf¹R¤ÍþÒ¢ŽÂáL®?%7Dv°#±üàçT2:–½bà5#Úl)ˆH m¹®£#Óí#«“JÈW0óÉF³ (jŸÅ^B̳͎OÉãkâëTDȼñYi]wœ¹~5pÐj-Ö‹0¥ÃgñTjèDÁ:Øã»Ó:G=ÈÐ_ÏEïøB*%8I±©r!A|k럕 ù"ZFÔ§â¶ÿ €œéß5[hdeî\SH„}! |;ºˆM‡$8’Íà y•wndíù¥m£ÃO“ØÅòŽÃT}:d× PÙTuˆ§2mn0ãt¶éË“£ÔÑdì+¶D“caÏöÉ[ëFÂ?}¤uE¹(»pð9”–ºú3_9Æ:¾%QðG‘Êv„ù³üp&G´ ãÏó8·‡¢OQÆu»œù_Ü,|M”Å®CÒÛ±aá»S—!qt­Šñ“ ¯D+ÌùüÕøBd¦9Òi² ‘•£a`M,XÉp½ƒ_2“Ùâó?&ú<º¶%KÑ%¹ë©?`­¬ò]3HÂF¹"ŸýÐêøµÀÁ¥2a¤Ã ëQ«?Ì€ N æ^ö` BÔSƒOž,øc#ëÜñÙ¡±sdÄ“/#‚äÁ:ò+“_Þ踱†4tº¤Rqíkÿ«SBš©ò² j]V10ÜFÄ &Õ»eôøÁçŽõB RÃCÆ!XÚlÒÙçªÌQç diˆã¡Py„¡ÀÕû¥bý†M4ÜvBƒZÿ”qyß¬Ý 3S—fz3_*«ñ~í\‰F»›ßº¶â£×›ç©€c˜¯ ÆK‡€ïZdÞ6›+O±nlætë—ONý‘BIì6ó$ ÉÐÙ6rŸ…ôß—;š„ÅÌ^ÏŽÓäÒ¼ìT ÐÅ·øí}1Œp¾ †±ÙépŸ†¦fî£(úœ›ˆønµSךàÙ¼¨g‘sÕ&3cVçws¾è_šf2e¨ÃXG­#mmÓƒ«€S0̨‡…ÃÉ© u9ž ^›ƒ²˜WUJC0òdáJßê{þc@$¬tæ÷’ŒÙˆY½,š¸väù—¦: )È£cº*­çI6ËÏ7Ø“ f® ˆî¶ oâÅÐú)x­Slƒ¹¸Ðo5+ÎòìuNz†.ÿ­YäsiUŒ¼gZ@xa«/_ ¤8kÿäh‘–Á²fW|ê]&ê cèbs, ×cYz9X3ZXpÌ5}V°K²Ñ““8{ãhåb±d<‹Aó­Â0ÖÈ*u Çnd¶òßøÎÔnKþ]–EMfrÖP‚Ùgˆé%¥¯¼Ród‰ox7P6‚Öd-Ð3ÏYbµ’ÐÕ€€Yîb½IxdÿVPŽ>ȳWEóHí¢e°î'T8 Ae ”¼m­xñ©BQWPü7Y"cõ¡¥D?t:H‡,QœXübÅ@1Á/Ýð [x4¢¤æ/Õã¤VÝI0kÇænàu¥ºGÈ·@æó¨2õ /ÓÎΜF¹Qi*,xÓòÂA¨³æ*ô†ädB‹]Pêñ-$ÒÖÑ%ïi±ѬÆÈ>T ­Q䀴„ã S ¹ºÌ¬*oü!ûx«Î8ö˜:ütÄ‘¹Ç¿u@áTdÝ=@‚‹ö’Útº$Y¡ ‚"þ4ù Z9ÔÆHÁí v½¬å7 !z¾± ¡ö6ïO„C˜ö“ ˆ ¶)¡Ú¢ Ѩjû)…©†óä —^ë@?̳1õù$=.?0*üNÛŒæBMâË”J²þ”Aò,Má>VOºPì%€Ù¢¡XyhBfÛ¶p`@[5v9rý9­€pÑTaùß 1žâ–›Çû_XŽœÊÍz´•ç4ßЈV]tg{,ÍC­zFQV°ê–ê›Ü°u4Œ NƒøòŠëiFôKÞ¾ý)ƒh+$œ©e)¢á©>Å&Ûàň‹€ÐM»À°/ˆ¿!ŒÜ Ø‚Ò]ÀWê Êd…í¿e¨YhÉ`gJ¬|ÑHÏ \[a@n?X³àÖ?±?†M‘J·,ÊF¿uL¸w cŒ[ÄoÊV€÷àÔ^DZH-®IˆB²²ÌØP2RÁG W  CDÿó|I® Cº×_cô–|ŠÞÅ£üßéNÚõåÁs3)ö)%¶3$‰dx%çs ëu, ìù`.&Dö*¶§EŒf 2djñ”eÂ.µÿш$QæÕï+É^#Ù@l9òʇü&oaK6½Ôr¨ÞØÚ¹å1rEQ* BDÇLÚŠ2è‚M’Ör¦éì6ÿ̈‰Å&\ÖÙ~‡.°;$ÀµCœ}à¹çúå¤/7)3nݺiˆèç•dOÎÐ;‰>Ê–T±†ÉŒ ~߯œÐ¢FžfIb˜ÓÑykûGaŽOâ« rŠÎŠø‡ÄJÃ/k³ *ªc»f…¸gŸËñ|\ÎËd‚çmà>‘ 5ÌCEúƒ\ü_›, xG¤ø;*;ŠM¤ ׯrÞž˜}x']9Iæ aœf5‚oªíˆÀ–¹Ð›GË2ò¡ÇrK, z¿!êÒà²n2‰Ú7¦ª?ØÍE'Ù)ª¸:Ô†Lšî R¥ò„ÏÄê£À+Uñé(Gta9i½²z½.§p`Œ8tØL&³0`W½#ਖ¦]óýƒL,U™´ôMAua“ùÔˆ²<ÉÇH_ü.5ƒf¢E‚„¸"ä¯ÝÛƒ‹)ˆÇ€hÚæèW­@ý‘Âÿ¡sÃÌUYñ€h0õÖ’7s€ã´†*㛟{£@Ö¾EÈ0TVô!ìÇT‚j:ê‰@©£þêÚa²ÕMfð†«0b[á­à…û”€ <‚½eÌ(4àŽ×gAˆGÌh8ÓP^£=×òQׂÜ}LPÇl³xŸú-Bªø ”W…i+ÃZ¢Š<³Êú€Pg¥W&9üå&ˆegæóí±“B\±9z£M…Æ™÷-•|éû&b þÓôjHY˜366·®Ûбb‚4æ2·B‰åóI,Ó¤&.ÂyV‹êª»G޾C‘]” ŒÉµ‹‘,òn'›¯ÜœEéÆÄ|ŽÕvføDua7x…ñR&œ÷Ô+‚jaõRY{mNŠ[G•3ÿ雇!dct: €²€@Tx@ózØíõè_„nªÇ¼IœIɲ"CŒÕ¸UF}÷t¡ð‚g`H¶k¢¥ÙvˆÒH{!KÈIo³4ÞÀL¦Æ$ýœ?hWF”Äæø‰Ak˜Š+ŸºX)Ëô܃’I[NQB¹Ï妑ëºÏå*” ‡óU³ŒûÅ‹ éûõ`^c¨øÿ(§žu$T9j†Î”–õýä‡U>Þ$‚¯ÿ Z·;¦¯oó0z£[§'ÅW1èm%Ží›Æ^ØÀ ÁxXVh™k…_þ¾o±X¬ Öÿ3èö@KÐ"h;öKõlºJ¾¬,ÀÆt¤²ô7öl+$ŒqXÉB˜wÉæo!ãn—ÅáâpD›ôTLÛ¦x¥\ïÝÉOV±YbzË{‰e0÷“ÔÁâ²2AY(¢ ãŒûB,eÿ\]»iúˆžQHìbU¿Èí“Àl †¹A€Å_`kL’ƒûéä_°*®å‹ðt22ušÉäsÚ¶çùþ=Ýù××}Ø‚‡i ¹_Ö¡J‘‡u7f~:SϬH91Œ' >À˜J#hãüOßÜ}xŸ]±°qѾ¬ybªÇot,k‘»’rˆ¿FX°ÐÎLÊ<‚È=‘·tÒhªú ׆ùêÄ7R°@Âò¿ÇþêÀ ]¨d 0rle_ïZZÌùRšÄ‚dhgBt戣ݥÛ×i¸ÒhÁAxÕ&sÄ‚½ÈWå"TÀX ‰_ {¸E~"‚Pl3PK¿¨-˜š˜0ü(ü— sg‹õU”óõ,^´™‡m–+¸fît¬ \š@¾®s|Û Aòéû1ÂiH.±;J…ñ§'³Ö={TiÌúñO˜ ÿ }ö8Ó]Ý$ùx‚Œç¤ÀØm¥kð Ìÿ˜=?™ø¶ó 1ÜN-èøkàãŒÍ÷.wßl⪵8Åâo¶TiOg2°’^‡¦Œ(”–ô~ît %_ä+Qèï¯yýUDTcªå mÑ_Gh)Å>‹Iše´üè¨Úz‘›Ÿú°`c `@Θ”•|ÿÚæTÿþª¢ê1ÿo 4¼ ­À)¾æx å~ª(ÞªŠ‹C:åV::ÂT(Ö ÎüwÉÈ z“ITrݶÁ©Ë¥UTŽº¾ðîJUêJ\D%ž´>F5Ïbï×sÆ÷S“AÕ$RÓíPXŸ†øayõ Ò³5R™Öªµ›þq^¹üi W27€$ÖK2,ÿ´†‚öăJ¡áU ë+Oa< ì¦Ñ†C[“bRÊ”TJI…%Z\íÛ‚úÔ¹&U9:«ÇÖúv¤^U¸»ÃÇ|^Y„g’Á¿KªÐf1…¦F54Z‘žlZé ¢‘Ç‘…N[Ǧ+ÇÄT±Qš¸#9ÝÉŽ6Æq´¡ãq¶°5º”KtªÎë«S xŠM7Ú² rа8‚^B6‚C”DÐ÷Óð‰žqM“Ãûh– ^ªü`[À™É"oÖºi)TJž7ézÓ=Ž­°cÑSŽ!Öo¿’µºz´ÿÓêU]UâæÃ~ù³mÚZ˜4»c^Äìló ”Ëÿùøã8‰‘7Wõv³gô*H- Ío§åòÛŽºýrþl×7þŸ‚6¿(‚D¤^u =QQþÈaoäëJ©©¤áW0{^6WûŒö€ ›ª©ÒlaÇZ°N¦ç7Š:²ó’N>Û*Ú„âÀ4;ŠF«—wÔþ)QÏE©rmüáçµàÁŒÁ›½Bk±œ˜“˜Tà¿ZDºŒÐJ×Íq[ö+¯mSÿư "¤NYͦ—x[ž kQ­Å¦uÝõAzþ J|Èq|´âxc_;kŒŽ€{ah(«VÜâá9*X HäáßkÿºÑTjбëJa©<'œÃ/=­<}®[ÿ–Á3÷Ùç;Ž! ¿¿é†d_®šÐÊ|™<ñÐ¢ßæ7ôL¿²(#¢hìшH°²šÇ³i)7›Dþ’wïòX(*ãóG?õUT°v(Uµ*sݰËÎ|VL¨YäÐ##££M (i©5'Lìþþ¦$€LÍ CwǰΆ"îßKWÇ«l4} )Ï›™Fñtè»6z‡mú|ð0 ‡ƣݤ€ûŸÆ{y" 둌Œ"Ð>½ÿ£!r vT`a%ÃÝü+ Dü;ÊÅp˜1+wˆïÞÈsŒup4˜Þ_šeÊJx6]{%ùnš‘'FDh})$§9%n_/õ&TÝ._ÆÀ¸–é]áY÷~™X„æÀRW )!Ú¦ÊòT ÔWÚ·@UÀªbœ{üâCó:·ƒ@ãñ´)Q›JMZ¡~”¢"'ÝìÄ}Uoyqî0D]…dz×1òÿuXwrnA31a¢ôgŒ°Ð_;• £%P3¼b[´Á‘Ù]¸QvtWt-ÃþFOF 7ºVNiUfÒw"Z‚Ûåfd‚bßìv]º]¿ƒº3øç5ÌxzaÄÉ= ÜØXrË#7Ù¸ž|¿Ž÷éý3Øo½Pˆ#ëJðZA3ø¯øóþ}è¶0C@>ÜI';>yAzêá’ûC‡cбL@ª„n5bÒ‘G+])Zöh´]eÿécÿ`ÍJS|Žb%"ON‚Bú˜v•V6¡³óÔ½un±BƒazÖôòÞ ÞP—zŠv¦Ž˜;EWÜú©¦W2á‹i`´¢H/ðžøšÌ7äC3Êbêhþrå {-~ZÔÓ×»ðÞ|@q¹AéþÃŽm¹Ü´&,½Š8C ŒÁ@¤ lËËWÒ¤‰Ù&~3èÝuYw¯ß`ûj§H”¬6ÝLýEwgSJšA¦Ó8< xa°žÉ9{Xâ¾5aItüª ²>€É4-òØÍ¦€׆·–»Wøz$îºòGÅÓÆ¸ä˜6âÔù& íȮ€é‘v‘ºn‡þøâhœRbë°$X¬Óh8øÚcìƒá^µëÅhpÝæ‡þµ–è±òM±ü¨²^5L–}ž#‹ª«±?¢BܘÛ<\}Ý´Ò:A3µIÏQd¦…ö…¢ çJjì!9ÍY5G{?U"y,·PG®šY©ä‹äÃpy×E³Ê†.L™ªß]¿ƒ¿ô¶ ë1.U+ž)x«i Màõ’ºÖÏh£ú¸À>+ßAå…­ªÿ7MH 3>Õÿ@8·‘ dÈÂÑ?±°²c=Ñkb’ƒ¢ÝÝ bô¶dðhÓøÙEºYáÈІF‰Ò14sÁÖØæQ<~¤È2£58#© ÊüÌñU#4¨£Èc äЉ­Ø>Qs8þèÅæh3³ÔH3{\2-š92xJ+ÄÓû)Õê4†4ê6Q)ç+[ÿÎ``G+?̾µ²Û–àØmâ/ÇÖ±Z«²ÃÛ…S,&Ç”Á­-(&½d»»É$Cç’¼á{xì”»Öu-_QÔÑ ?½¢ƒÔ*}.0ÙNzÆÅÄ0ÃÍš"ü õÂö3t[ iÀ¬ÐU¸p A¿?[Hi4ÀFÔƒ§´kwîÏÕµI!óÖ¸®¥^sì&k=‡Aƒ\ À&‰=Å¡ý”–jžÓ¡YB—¤KÉ(rëU}ôLÀE™¤`)Ñ‚zÓР­µ<=Š~&gΤJ¥è„Á3¦Ln'zPý~cÞN§èÉtôèËsk/’tÔ”MKèòüàKœHù¸&’êo‘˜Ê¿J¨$Š‹N†ë›ã3ª–¾„È¹ŠøUÂf7@q¸æÒÞa³Â5Êh³›®a~ü ¿¤i_ˆß™w4¤>m¹^C²ÆRú•;ˆO{]Þ[ú”fu’ªÓ¦ê²Â9ª>|Èl9`Ì•ÓU®ˆGV|Ûq_Í¡ÙJ)xæ¡·™¨n¾sÔAãe󃿇?—„æ u6$°ˆ 8²‘üDg<Ï“p ¥;²@»Ç2xb‰q“Ø[<&ýzƒKRm7†3YõÖ:½ M÷w£xlÑ’c)à˧5E§ÃÊþ°™ŒÏ'›2î„VG§W‘Oõ^4¶›ý¹lBŸµŠÌ‘]‰9²5“`“Î!ïjW´îÂðйÍeý %žÿ™™HÎ!&ØÐ3_‚n Ý¢³¡á|Dï/~ì„ ‘´K/’W½q±É«æ×±-Ô:­ED»ØU”ª€Û‘£FCÍ_Hûê€5Ïb?T êJR3öÜÖ‘FY¿œ—¾HU÷\NÉ–­ÔŒ+)ޝ'¡1è·ÈÓñÓáD~òŒºˆ¾"{ÎuŽ51JË ¢óQ¿©ÐMLŽó¯ j%ò#íäœX–‘׈òçž_üÅßÊh]“§$ƒgKú†¬¤á•ˆùµ86œGgºlF%ö•¿ÍPDøæL‘ÝÜu®9ýKþÌFŒHr!Ìgßh¤-‘´ÐÙo.L¼RnzµÚB^õ³¡·ÐáÖ‡åÀ²h6N¹ûŠ&޼Ú|þ£uÀ_4¶º(²ƒÉ'a³@Ý”a¬4eB6ÛOÙ8ãcq¤ûVµÈN¬c¯£W_œLQ:€²F‡·X5›þ€wFòiÃk¸ûÍñ‚„bHûJ? gR ¯NÃp[^þ?ɹ$w0Ä8Èö¦rä8RhÙG2Û•~׃×Kj&=`Ô¢’¶6³óB}óùÖzÃÁ›Y/Á¾·Æ­üLÆ’Ûg 2T58ŒÇECC›ÚXгBÛÍÑMôv’‚@8ÏÏes"~ZGʾ´ÉqÅ3—iùYótÒGºD"×- 3ݶGx4Lžššk\)aªsOÒO!\ âÌ<¦wð˪ÿï“L °Ü¤ -7ŠÓ9y]<5ji»á„å‰fåº5¸J®@Æ<Å}{:¦5÷D. A2ß”ì¥ÙD….u1d]CãVv°uÛ5´¹_ž R| @­–D{[ĺ²%ÑüUµ¡û@Èœ€ òm–Æêãª9*ß×IQˆgâ9\«ÐHž.Ê·ÊN/Ir©ÂOµ È¡Od È,Ši e”ä5HìgT„ù÷¿0ÌÇ„]a°Æ“}Ÿƒ3 —@Y¡óâÜÓ“faœ¯Ð&…<_¨B– ýoæp¦·ÑĠר7Ä)^лuVf÷Nåpt'žF(¶›ìˆ¡äŽc{l$„÷*ÎÉÿb£QM¯ü»bш-àÛj¨s8ˆê<`pÁûhÙ zñ0„+CTØÐexlÇ%È’‹Ô± ÆàŠ­*Øn&§ú4aT0Î T TÀਠi(b*Œð|·ÿhHÈ…™ Òn÷Ytþ¢¸ì3ç p‹(È%ƒm!‰N’»û†(èQA2¡¥­îð~<†ZÃ! aŽçj1£ˆË ¬på-%ôž½ÉžR¬¶RÑÎMáDÿì Rg É€F[…ˆãB°rNyÅ{˜YàõˆV<ª­žœ7W™"ß49@î-šÕ°tõˆc¼GÆG*B±‚¹Øtø€àÖÒ1ÒAÌh‚@Õ>ÉÚÁŒ6˜bô,Ä ïIÒ…¶‚#ñZ_Eê80lÏfÔs–¾¨¢á§½3ûJÇ{Ì¡ØÉwñFÇ\&¤M;µø}{bpy‘÷W0ü V昗5¾6y xëö\æÅ!‚òIî6šƒ‚mC„J(•¤ 90Zz™u·Eq¦š}‚D9íYXuÄŒ ÂYÜcFŒ1ÁÇü±êÑÍFyk“-°4¼ˆxÈ-o“`AþXAÎÌ;ÛèšfŸ…,jñ›’möáç+µ¡ßÒ#Ëd´óäñ¹9€ Ûô2Ö¦ƒ9Ä–ªš@‘¦Óݹ‹;@¶ü 7“ƨ’7&+suíp2FO¾k‘ŒöÒÿ†Ö6@¯|îu/”É-/æ\"‰1;p×­=^Ùx@‚ôiš 4‡fê ^¤õÓî›øšG¹œÛPgh ,)Î-ÂÑødÀÇby’ÿK?T}S¹_Aš&åÔ˜DoW®Ññ7éºe¤èÚB1Ðj6e®öÛ2ûL®©V¶ÅØ{³TQTN˜´/—­?ðèçP+X’Ù¢±ŒúÛ‰1Ò!ü,3!±' `a]$}Ù­ÑZG³Ë¾ÁÞ.r/2±boª=„*q_©2Á‚«¥‘Jtæ@‹Žhlž±F$ _²z´KX{\›ÞÆp!83Q!(ˆì¢é=a:M–)ÊÈŠ¶@dü22`~ƒ\ŽËRYU0u+iÒT(ì]É›ˆ3¨‡ŒP·èú>ž«5GÀ0‡’7¬¤ xãùz{JÑ)6Äí•ôè°÷„aBPõYo{ö>›LŒj–¦¯OÏÑExÉX¾À‡Yu&:*¸g‹¨úµ˜Ê>l}õÜ (ªÉ:‰w°¢üøˆ ãž=³•_‰Xîgâ~’1Õòw´Y“Mõ$ ÖiÙ âÎ<¥à c¥.† ®¾è’O‹»)™0j—ïH´½óÛ|)Ž/XÏ ­ú3BšçÑô•®z[¡'¸™~§ZQLI–+½3u2‡F×*¯™ÍF+î©ò4ZC_vçoTÛ?'€žY•’Ç:5ö}ª„µˆ0úÅZœž2Ù{¯{þ½«D‹á[Þ$>ᯥŒ™•ߥŸ=E÷|§¤L)ŸϱÎ`w— ™y»Ñ—3‹¡ìÊÕ’‰ŠÃ[ðaý6±šÓU`Øln¯\n½L&&¾ÌîÔ™ñ Ú’kjÄW_Þa18åéCsˆ-6Ü&ÍLÄŠ ] ï -¢}îÃYè«B¯1Ì}ݬ-èšéðäåI}ZA÷å,#Oä2ff‰*8‰l±1³¹±°a#Э•q›:)HéÁ…èE¶•¦4‚þ¶`Ö%Aÿ‡rïª$ÎÄè&‚ëÓ1Ê0þ¿_‹zQÈmÀÒï™™MQÑs¾GÅÝ!crnúè h˜qcêÆ¦^u.ãfôØhùƒ×û0 Ɇ²þDøoÊ©dEM†rChCo®XÃOân UŸ ÁЙӝ¾øÙDZ¾Ãè3^BäŒ×v‘¥ˆñþÄE.¶-›¾—9µ:BÑ»dê¥tŒ‰£b³°æ( 2É­ ‘šEX4ÙÏßé³,÷ÀœôöAs­†"Þ’@oJÖ°–ä?"fJ[oRC'p—$ðRŸ@”‘H EßõHäL½8ªInÙ ˜ôabÍRP‘4Ö…Dj|zkM9ÿ}åˆÑe@£ò\Þ“Ï}ŠH€„ìEo_‘¢îÔdA‘v`R÷Åñ¤24ûèýÜnb¨qLg‚Û#ÉŒŠÙº&:ìØK¦UÃ:|šr€â;Eö­eËÂØ*ЂüM€÷Mð!4„Xä^ºqšG7ŸõtÆõT Š&Ƥ& PÿqÈ^䌞øÁürÓ(†*-rlŸ †Ú+FÑ´^E #8þ/Nn,g6†åKŸ6Ì[À`ýEvy!“|m:ù©ñŽÄ A&%4&ç/Ü^ª+¬æÁÕºçèµIx­¨LC•^'ƒá4S‰dÍ,A3Ú‹Aœq|Ûä¾×d¯!‹ò(*bº† AiªK^[è"hÑr1¬X¾ÇEôaD7žÆU …w âòÍ•WÇ ñ¾É>ý2þÕôavã[äƒ*ß;ë´ñxv5![¾Ò›§Öò=RX7šô—qÖ!É‚¨›âxÈR`ÁhŸñt&•¶sY·ÖÄ  °¶§ ˆf˘fdÔÁ©;´uÔôuíŒKÆT¶v*TÇÊók’˶j>’ÜÿxŠR´=@‚Kä›xÓŒÕp2%8ÈñsÖªÜWÈ¡fƒ ädÐs,uPû8ÛtÒø5|hÒ»:ð ÈÛk /$~#Åüoê04[rýˆêKÞÚ9°¶‰¡àÀŸVÀÝp'èV­[\ÆŸƒ^.BËQ&À4ÅÄ]…ªq¡Þ¡Ay·‚BýmÍih5ÞòêŽ6¸žÛøp!4c BEQqÛ«PùAÉ A4ƒrm¹„Ry‹h oE¨@Ijã;»y„\‰NÞP·‰¡×—‹Hqk¥°¬ºõxEŒ ýÈ,ÌY¯NŒjq IŠ`Õr.÷ X@¥VÞK«;|¡ëÛ•¾KoùLvwÙŸÞY5¯mô>fO‹°–Þmƒ-¡ÜØÕgÿLû ^‚K>Ëqô}»òè½ô<ö6N‘-¼+s[Š ¦o"“e€ð:ô$`}úüU?‡ÿ†Ýgšxù3Bû‡NRÁ"Ò±cE&™é•ÔÁ˜2[ºé@–ÃQŸ½ªµê² yz‰ŒÁ‚&Bì¹Uí* <1Ò7'Ióñ¹ ®‰/`Ú†RÙ‚!}=oÕ]S}ò8Ÿñ˜ò`}³í-øc Ñ£«hÞDn¢GBàŠâ³–Ù¡è·Ê’ÚKKðFε®FÔ‚@ ^Ñ—8öz…–ò‹7@•C'K×ÇëÆ=•é°ùh$ý•¾Ëà ÔØ„!ã>ß¿ßÈ'â£KJ“}àe‘0•l¬åŸîÒz³2ÈVfQ*Ý'Ýjr­Àï¨l.èÜà%~Þ4%¨"ò3öª7@+µD&¸ü­q¥Ê qh«3’p÷ÑØ$ÖɶV²Î©§ÿÛnéì‡ÂlhÐ¥>¸Ä¬R"¹6@uR[ÌM}1#Àx—´èRA_’òí–pËü²ÉtK@›à¼¯ú<î³î%òßóâKó#@SÈþ0²š4è¨~-á}ø®Xv}~¨ªP³€1ÃCÞ´ª°­Â:¤7]¶u« Ê9EýäD¿{2¦õöt)J)²+½gªðøƒüç)µËö£Ø8ˆóaéN¿iQ>?¨“*¨€ ï½ 1©ƒÜ@ áôˆnŠÉ˜i‚Cü EBmï$¾õ®‡#¼7P½ô¥w#T.\š4T„þ—ckÿ×-7pÁÍçdQ]”¿`¹áù–Ü Ì‚J¡Wè°2À Ð娸 y‹X¦} uÛK|™³ö_ä‹ÚèP˜¤–™iJ¶f°aøUn"rÍ/¦à×­ˆ4wóSÄsQ©zÚEQì‚tÜ(~‘ªŒ¤ô—²½u2³`ðÏBÛìW%ãþåíŒþüNø‡ —cOÿÃ2|uß…Ç‚DÐpÉ{Ë÷|%7ŒD¾òGq,Ú¼Da(OÀHM TzSÌKÁ µˆÝæ †º CÁ:(«~ƒä¬ñóãæå«_Âͨ·ýòþ¦pèT¦È0._¾Ÿë—°Iнk\÷xI˜ .AP$õ†6v¸àÜ´ ùÂÚ?¡j&¼×Œuª> ’­5ú}ªL Êhz*8ظw)»wþte4ž—õ’*[ñïÚP(æ­l¶®OŒÚ1—Xͨ«s° í!r•ÀX(Gϱî‡ÅëŠ,Èu‚“”‘mËa­Z•Û™ôÅ%»2[t°Š¤©j=áÄÎB6‹ÉQ®¿æ`~«:¬!ê!ÊGÉfº…w† ¤gƒ,ÇüÔÒgë —®³‰#”U¦¿þ„'õ6Å—âÿøÕ ÷¼ù¼*Ø€íþ¿÷þÒæÎëëåL0ÅxÍLOs© ‚ó/.ú3ÅÙ‘Nùõ§ýV£<çŽJnŠR W0…AÄû/0™©{NÐhÆ jÆÂöÛ*»0SªŸZÞ&‡¬ó‹¹(Ü›1åzì°·°–ËÊmðc”X¼–ÜK;mÔŸfíFÜ[cJ¨‚UI÷…nÜ·JÓv.Îj „  vxuOòÛ·‹ËêA =UתËB8xÎh?’8LRÜ0 øYÔ€G ‰-Ú‰SÖ¬þàãø fc†¸€@M€ɾ:@Án kÔ ¯SÁ­ k_¸Ý ãÈ öD€Œ$6õC€Z`%Aìë6¦-gÕ?ô#ª›Š/Þ@ä_Æ7‹²‚§ý_š×øz- z?Ñi_ô€,ÂÐüšØ¸ÅbbE£+" ÓGzÓÆƒvCp@äWf)÷çmÝÿ<n"„íœúQóÍórö5–D÷0s.¼'C¬ÖãË $Áˆ£ w^'£8ÔÐÏç]@ 5§OÇË.UZC¡Dg•ø÷‡Ä„ u›†á}ð€® âéc€Ô'Õ­~–n`€©ÍÏ[ψÈiÅ‚¸ôØ Ð!jhÂЙà¡ógà ÏäEàcq®-–úÓH‰ŸÔ+ãõîÛví +̌ӫM’<çÃѰ®=VüðhÒ ašRƒ¸ð A¹õyƒš5%X2tððÀB+Ø>gí²r0g`_1ãndt”I1YZ]Ç}‡¿…T„ ¼rGÌv&è±qÇÁüI³öØ(Á—V5ìµÊÇ}HØé’1Å£úp²p‚ÄQÅð";’bwEàä>O·“‚`\pEäˆ{è½>»4£’;æD눬¦‘õ ö2YˆE˜ð?}¢U µ"‡8x8=H`Rš÷H®Go8ÿüVå’¢;¢õ96`©ˆR•ËR@?™`ùU,xµÒ)ëë醀q íhà^‰H·ÀBÍ𘯂~ôL6Lö‡ýdh2Ä~¢:Öb<,ˬ=ññø$?ä®ðK7Î}41HâHA ¡«s¨Íλ± Ú«ièmr⩊–½T²fþˆ#½åáÓå¿Ê¨ÅHTL”g42Õ€Käj¶¢Ëc¤Þ^`D“ ð [X¬³”ë}0¦˜w•îèká‡ü¥w™h¨É`“ ÆyÑØ¤(C_Ýû©P‡-y’ŒPõÌ^„Oöb¦ºœµ2˜üÎm ØßÙSNn¬B—ªËü×+–ƒk™š©‰[Uµ(аs®îàÓÐÆ%³7®66 ðx²DñøQR`(“ú0 1—À(1‘aÔœñ ³›‰†›Ñl:CA^€¡)ÞÆ“¦§Dr— Ûݪ¸[6Óßgabòe.Ÿó¡äˆ0&hd¥EfLJ é;„RAz­) q©DþŽ·al[Yf¸C8Ó½ºàÂU(¥ÓÿC$:"&¹£þŽL˜s•¸_ ¦ª´ zø:•ÀøáÃx^VŠzLÔ¯õ”,ô<¥Ù=àš=’~BU‡‡}Ô[ûCè€|Ö$ßIDšÇñ)9±îÛ²q£ÖU¬—¼†tkZ8£À».tW—s!f“è›Ð-^¶ˆG9ÝÖ߻łkqC‘GJÑâx2(ï Øn]_fõÊV¼Êé¹"¾ 0ïìm§c¡Ä©-‹P‚þP3}’æ-Ðï}5¥ôRЖ]ÜÒ‘ÑýN0€‘Ì[á¶Ç”?„ÔÁÄ×\Ï X!wPFíúψfp«ŒTà2h¬†¡Se”§ÏúÁ™•[“’|1)™€¡Q8½:DîßAö– o¡vt—žÌ Eîdó;LËÕraVÚVÓ.(&ˆ&Ú‹j´Ð bHð2Sw&~h„P›& t™‘R÷\‘°¥±"‚wƒïK†]6ùcýõ$s)&Cv²ž6þ‘×”ÃøŒz½J²­¤Î¿¨’€Å„ÆLÙˆ¼¼Æ–KŒ6Q|ùÀÚu#Ñá- ¾€¾—ášS'8M©Ó„lÐÙ„wsÖVà ¿MuÍÿU(ï5ï~Ù!üäá:UCI3@•4°²cX£}c-­f¿Ã DÌPVïS δbWe’¢x, @çħðhhÙ4Sc\9¬ Ðl‘Ò랟¬ÕW5!eÒ¶O .‚ëh™tôvðå¡Zo^ ä¸ÙÉ Þ9à°¶ßLbž?Nîn¢u".“௹xyž„¹?S¡TU˜²ð—gmö1f²ƒS“Ô̪0p €ÃïQù!k¬öÇR%¨‹š·Þx»MnìwdxY•«ÁT.Ð`­\ÆÄÎI20I,Ú@­‰`zo)í«s&Y¥¥IÍËIž¤×S¬vDU³¹Ÿsb»5]°l|š ‹Ûpš±ñŸoÒ{ã:ä†@k³â›nz³Xö53¿ò5ÌM£ž"ú¦=rbqÁˆ ±Ðˆ3“©Ù1€Ø™P÷Ø9iPJ˲ ‘¶˜/óÇJýTk×ðS™è¦Kß·8”õÌúñþ˜Úi 1he°{z,UcŸƒ{ð²ÚϦ a¤9ddã¸ï&g!8_÷©‰ÊUÕš)ù‚š¡¥¶ëEeÿ0¦ ]ŽÇ®ãŒŒ—~´Úâ¤/Cü€A¬-‚¥pUöGÉ8eÎÌ+?¦ž=î^Ç{–L,ƈ½LB1_ýLH÷-³N[ç…ÕoY7'éÀ{!ˆè»*¥im–„Œ”(ºõ*k ˆj¹,zpìi§„Vcá"v­5ìSjaä 4“ŽÞɪV/—¾š£Õ¿¢NÛ]&K£gËRõl 0n×§ˆh¿[eGlríCW"0ÝØ’Ô¹žáq:õ ¯äaãÎ*>\ŠƒƒÛ8a:²N¢5ŒŠëL=H °nŒ’dÓ—‡žµÁ³D3Ë’D ~Á»¥~™'ø6Ø?DŠ£Tœ]DÑ-¥¿t ™¨¥~.‡ÁÛÒzhlóŒc×Ò¹Òó‹ĽE0í“õFÀÁ'±Ý~Ó&}–lŒÚ±X8:„¥SºÞIG/üÌ\ÓÑ¡þgšÆ´K°g“{ìLN@–0†‰€É‡'«hÛ™%» &:›ì¶=X›ižÉ3e¥K4Þ/Eö>$g² * -˜'ÆNÒ/îX€0Sí¤m†Hü;‡–Ö`¢}ß­EKË~CÿÚ% ·HʨYjR¾™¢XvHº]›;‹ž =X˜>bªd dj¤8Ö·ÿ™‘´¡²!É’'±MêþÒñ©‰¸­eá!gã5Í`Æza “ùŽÂ­‡¹)ûª` ÇôbØM$)\œ1L» ™Ó«Ò`=·Ä°6¬mù #Û&&µ–‘‡säd³ßxÅ s¥.gˆ× vÁgØoI>ßCèª:å{„)ŸyÈVÕ  yÈÇý…:aÌ1äjP·l)YO¨ø„Y9$&†cŸ$ŠxL…¸É ¼2ÜŽ°Ÿ\¨(ц˜Lp<7ŠbŒìO 㽌$¶>4¾“ëJèNT¡ž8î—æt÷âqb"ÏÖX†´Rp-5"€5²ÉÁžPÕöÒF%é´D/ÛN[ŒDûhÎj„”à,^IWX0<5êkHØ‹.B;¢|øHvtAV£wÕcJøŠYÉ…¼ÙC¡ ˜Cæºo>¨øE‘óESwÇ¢^Ô{ä¬EŠ/úe20æ =ˆA¦s~bdxO¢•ELxO‚°' †d;jB(y?l­³A@>ÓŒ(g` ZñׯM𪜷 s!†ÖK`C8ÈUÎB9ó´%}0ÖÅ_s}ëÅþ½xx~ yŒõ Ê)ñ`ˆs|(L?Ïj2è¼hЉ‚ø'—&…ÓØ~ùÜÓrBÂÇÕ¥ø"i–wŲJf0Ÿ]Œ# l‚wرôòº”»®þÐÃ(×p‚êºÍU0ð‹B{‚ígÌÈ!FbÈÍØ&­Ì£Á3ŸX¶ SgP'ÅLèeðÀš«MžÊî8f‰TÙR+J³¤÷¤®¾Wœe:b¬F>Ÿ³›2,„*ßsººòbguáÃ=븚ïû”`É+É´?zà$ŸXÝsìñ +™:ÄÐ}·áU4¤Ã?[(«>- Ê $ÎE »±>v&r!¸Œ@öòDÅ:O‰&9&Px¶jsì\Xÿ˜Vm,F·ºFë.ëåñ‡FÓ.Pãâ¾CéOÈ‹¯I÷bCÕºÙï»`Öõcøj"¡×јfIykM£Rà÷ùãûà•%þÍXˆfǪa-bÈÖ¦óJĤj„0O”ºøŸLt8½ ÙÀ QŠ0§9§†æÝ ¤ŸXCXçÒ+ÙØ!8€a²øêµy› ÿHƤÆï.°€Á1² ½©>/GT€Õ“jW`V%‘ñŸ‘¨–*Kx3/~0P»O÷мŸbˆ}²PØ›œ›˜ìF²0Ô,‘4à¨ãN8úiÔ•¬d"nL*B}kYô`A4®@hàj$p*·@çñ¥Þ~’3Mm>^D|WªÛ”J–Ê›ÅDåÀH²WJïå9Ì̶n%ÒÿÃÃÍ‘ï ƒ¬§jY'šWuœb ûHAÍñ£k€nK’í"²ýsAD±îK3 9'`Õ™OãoŒ™ŒtB¾MFg “bÖ´”> Óá~Ê{¦‹ÔòÝ‚x õˆ‡#ò7чŠÒz¦d²4D²(Ú‹ÚÆSÿï ³`LÞF¿ ×ËŠiq|{ïÓð-7éƒ4|‚B*V…±)ã€ñQ­È™Ã³Ç4ƒ/ƒF"F’§žˆ†ÓL1_Fóã:æ4–~˜ |0ƒçí9Y—óí’jA‰í7âlch%>ÛÑ*ý‡Fá?Wb‚齉mw#ój ÷<$]ô‡GÁW^tW(ðÎ. i´IQ2ȃù ‰1б¼Ù{RDtà‹Ô­QšØçm’â>m*’ŽîsÇø^¤pÝwP?Éšô-Ö}ÿ¾3½²ãVHBÊæ@û`sIÍa7WÀ\Í€H2Ê–Þaâ6d¤ü’o îâ²2Îr’@Ò@™Ý-LÆ'c‹U4¯6ÞDƒ±±`êÏàz%"h6(Õüÿä &žÐlG'KsBâ¡àB§ìœìµàØ¢3Y#¯ë_Æ‚-žëL'èK®á½F¦ÇÂéà—Ë—íQ4E.G[ìm\¬GôT%Ý'¬aîa@NÌò@v¤M„ºÎY ?Ý*¹/Ž$"™}˜FÈ:H;OÃ@Ù¢£Páe:Ca*¸ ›^x0¢¯Î:Œ´Ã’áŠõ¬6™v…ž³37¼#xÆ·Á*ɦOŽqdIþ t·±$aS<ª6âµxvÐͽ_\Öhû†Í`4myyÕ„éõ&D1/&ƒ™ÇnbB{Ãs¢ CÏÙ½×A&ÒÍTŽ&ê:ÈLjÀ*î1¦–Ñn½ÜË‚|à€Ðd„Ý!)O]§† pË ºé™Õc`H>Žbì¯N‘gÿ¼²fw{É2Á8¬µŠó)³ùl¥4Jë͸v±G†sJãã+8ÄçYê«a[Jk„®'¸ø /!a+±“3F°ù¿P÷Øî™ ³1ѽ’Y¹|žiaƒßI×0\µ³}qJyÙ¢‹<·9<ÁE‘בáá=#‰q5)ª[)¸K ‡Ïš1d r›~)™Ï! ¸EŒáª'i q¤GF^èL©jr‹"A=‰àlÜ+Â:–\æz2(ÜZ+»{Ç"´ÇÈØ€žßçM&Œ¦ù¶×€ÝÒ„`ä‘Ú˜¨³Ù´–‹yu‘f…`ÿ)Ù-µÌL.èL­ER?^ŒÑÏ>r–[6RiŽˆòLt·Îè ”C»"´!ß(*Íq Ÿ´ž¡1GÓØZêe7QA¸f’xâ½wØ»‚>dCiïü`© Ô·ÿzŠÌðä@aÏdcoÐ^Ü¿"]Cy>âÙhÃØn£YPë­ #VuÞ¬>&d#Œ¢ >¾’*ó2cpJbt–ïÉý z}ÕÎZž´aWÙ’ðy†4\(¿&Ú”ªJ¨Ÿ…= ý p‘êêN²ß sÂÈ>1üà@œrR˜1æx{žV^Ä(îD¸Õz³6Ì5qV4ÚxšÁo]7-Ÿ* ß¶ä{ì]›}œËcV8'“Cߢ::Éš5Ø>nèo*Œ܇àݽ ®Ä˜±¥å9ÂýºŸGì®ü:–ªG‘q®œ7¸0¦u¥°Ð[€jx椈þÅ#d( X•r í¢w¼ØØµ­ó5üQ¼D¹°Ä xhMfvãÃ(3’š%¡´¨µ,‚·XW„…f´6ßlŽb·¯k¡"@×ÉtÆþ0m+ S­vAÚ¸Oº¤†VºùÈ-¦ß˾ԱdºÈpwopÍ`Ïœ">)!5Ï:ÕhÇå â‹J73kÑYYáC±›9×ÇQEZR½ÎD~ój°v3Wâ.­ÅöV×n ÷à”¡PÜÇ¢÷¢ ©‡5åVwù»'ÄÇv˜í¤». è™hVŒþÙ¶¼:´ç¶â>À{¤¼ eÿ„«×Bbò,Ì –¸3Š©X|d¨Ù'ýÌGR–cmñÈ’ ÙÆØX¿@ÆàÑ‹U EpÙ¸@#ÞÓЀnçA6FëâöaVßXúhmFõP&ÑìsnßÕ/”¡¬¾KˆöœqÐ×óàZHa´€o@6³ð¢±´aûΜÜG’²åS9ƒ'è:ǵzÿ8¶(Eø"Ðb ¹¦%•˜¼ö’÷a}XéªgC3î1êåî —f0ÆÚ_÷%ÝØ5b+œvžXPnMa`>+ÿ;ƒ­AX•ì~sþ@ã^áƒðü)†ôÕù¹Soðƒ?Åz›…/½Ã=¹>²°rñ×÷Œv4‰ ìÝàF±GeÄÆ'½ÄIÆ*ÿ1Åâ¼Ñ»hȘ‘þ†ð]¶7”î¦PpZèÀ bI¡ÁO¼èâHÙ¨©ìFÆq*(‚» ¥K¶ ª~'3Pp1ÀžÞ“w0¨Ü&Åq(þtÍŽ­q}*?÷…‰ì:ޡЬoðá“ãmú$ú(ÞØ–¤N:¹˜ßNã‡|0e´€„¦Ñê—ÿƒŸBƒ BÞÂX*xA.[Γ ¾+¯ë¡û¤†¤ˆ Üß„,·mvÐfxWþó58ÇSÌ F‘æB̰ÖO“@ 2 e“2â+/ÿë76`1>ì¿Ï^ež° `§C™òì×5JÂŒ·…[àF3[ð$­ðK#±+*}ÉŒé¯ál­ó> ’˜wãÿ§íHB˜R¹‹ŽÒwûïY¼Iæ Éü*H#L7{ÿ§âˆ'â‘ 2.J+Ïÿ^¢èüiLÓÖ®…˜¨Úê‹b^Ré¼,ÝŽ}˜H¡H.MV± -!çhBÌÏ€ à›ð´á:/’%1î×|•ûáåo¼2$L"a giHÐç~ÜÎÒ<õXfÞ³¹·òå/}ÝùÕÿÿ¹·pqÓY3†žúaô0å·2ÄHÐ@IŽ< ÐmÉ>ä:ÕÏæà¶‹÷¯=û~+óÖf!Ö„Gˆó‘þÍ5Š­kÜk„çÓn#8ŒúÃCTðÀ5µ|»*Pš?àž !@¢äâ”ÕŠ­¨"J{V¢’t*³šÃö±4Ä¿¹CRK¡«ç½~ó;-îþþ÷ÁÀ@@’­#`M7‘5̹C,”*¡!‹·ÓÿÃnÏUT™h‚ªMf Ϙ<ѬçX㢼Jf ”_¨rÕŠ3†ÃðÜhÝŽ¿‰ÊuVË­fi ]òÂaÍ {ÊM³·Ôˆsz 1в°*œ›a‘0e@ó€kç`€d4'à ¨Ï@°!õášNuŸS-ÊÞsî¶> €øõ¼þˇ²ß.AÀ ËwgÏõªê¦¶ÉtÇ•œà̬{À¾S·$öø9bÓƒ9ˆÿùȪ©‰¼!®;IÁÁ²MÑöþI=º~ ýâEÄuÄhAUjªÝ×q¿øu†„.^ ºÔ5$=L5È€xÅTj+¸âq^õk&ÂâͰ`¨l(òì(…ji¤ŸÀ>j&i% n*͇õ)” ò.êÖ–¥N{mÓ‘í^¥f áYj1ˆêÿQ[«½E}g³Ë¤éƒu,@Rm /²éð(¼˜^HF¤­<ñøc¬³?µM>§­ÔK!Y³{c'\86q’5h|=æ«K±Ðè¹[À•Ãdã4_è¯GBª-ìàlGæüŠw‹:bTa%êI¯hü8Kp¨1Ä~—¢‡ÁÔÐo¦¸EhCP²Î}#Óï6ŸbQ`ÿ 6Ð8§wÈ/hGÅŠ0Ìç§„HLpCžÙÚ^™„E×ß"(ŽÂw¿ƒŸ+ Eñy ÿêÛâ~—aƒMÂäE•½p¶H¬;p€‚AÀY ³¡A ¼ ‚6ÆO{•ÄðªNßžg±ŒøôÎËo#;˜Œ©84Hêõ¼Vˆs8Hçc: 4³úW#ûâ@!Šaˆ”$d¢M­}FÜ[~"|t+èÉÌ×–î˃kÚÛŽò 6L&<ØP_PÇ =ÍÛ÷Œ/äÎQ¡èuñXQZvž½! >sˆòܬ"‚±0À’4ãéCæÔcî\ýUUŸ±z“ƒ7OoN0¹dqžZý Õ† Ö†$lKÈJìn§›YÜÜ-âuÔ7È"‚WùŸ@ œÖþßÍhøþsSU¬ŒÝ7Qt|K…•~Kµ4tCpv“ÎÏL@ÕaÄÀ†¦ƒ>Þ¥ÚWH‹]‚ S@ ¢ºK@û>étbäw;;Eˆä«%$€uBÆŒ ¤@݆é À»```t†²ô&©QŤx óMÄ€nO#êàÍ7t’!0Óø óˆ6ûð QÆž1cΧêÐ;…MrQ­F«ª«Ü^ÄTÔ{èLX¨­¹Œ™½¬Ù_ý4(e‚ãù‘™Mcÿ*­ ?‹¿ÃŽØz°4ah£5ñof¦„+™¡ë>˜T4Áa트®#¬ÎùKAjë–ËV(¬f퀣émo€ÅÚyŽý$ñœ´K_NµâÐòÜ­·?y¿!DrSðQK‰Ì|YÄÒoOuʾ¨´Iï‡yÒ0öy0Ó„`˜½yÛ°—ÄG=©ò¾ÅÃSF‘W@œDdÜb‹Ð¥ñBKo sóD¡§^uͱni»ÜYкî.9HèR!ôõ­í¯5eþݬG%q^<ÛŠù8í$£bùR((ÌïƒZ5d¿O¼ÐcµÇ] ¤“hî«8’”&I6®¨Ï°{~KZÂ2HM(ÀI]XÙßD0E÷YÛHX#9.á!ÎK!›µËaâçÖžç¼GŸW;‡úFœüÕÿë8Ù^Ú¥ô0™K20èz<í|¸ž0­ú¨ÌÃ[:¤¬ÄéÔ&%bðûçÒÀ‰âOUxõ‚~?IâsEßv±^µµÉ‰õ2F‚g}¢„ DÛ¶<¡Jì¼ S²yÑPñ(?Š~jF¡£æ}< ùÄK6q;€Ûá[Š´Ô-^è†g¯jN qˆ-äÁÓ̰r`g30[“Ðü>Á{mLº@ $‘‘ÓJbŸÐ~dK‡š ä}vT³m!•¢Øá¸ÓRØ \Ö‘¦óœ¤y4× U“~åÌîã€|ÁlJÊ:CH„³HDIP¹Æ„Ý3•C Gùg]ÞHÄÍ÷_5ý2Ç$a“V´Ì~¥ EZÃÉþ­nÑúÅYÔ×ð03Ê)•>[¥±ÿî‚#<ɧSŠ\Õ¿ðß©BY©XÓjÙŠ &1ò’ø1øÀpCy ÞNY²˜xdêLF_TeY~Ktç5š|4‡5îÁ+‡3OMÎÝ|j+¤à´du›+§cq `¶“$=½I‚N*ø;ÐEFIG6-·ó8‡a4?À‹;+lúoþÜ*UØ£Aã-‡Lf¨†qOr„iÉ9éhª¤…ù-Ôð¸ô÷œ´þx)jFÓí!Ã¥¥,\]‡ ôS& Z¡œ1%¦eçÆÇIšÅ Cþh{§³l×Sg‹žó‡ÚȈÁïÔO@«µÔ1d…pk»Rò¸KPJIµÙì):ë'§^‚6• 9¡ÕŒ?“#B=MbªÐØdH|Ÿˆ¸y*V Œ\îh6°Ý2±0žÎaËñŸ$æÞ‚í‰ô»dF&”´´4z䮰倒`\tkd\ÂHdF³1:%%ª@‡90qäט—‡¶½/$#p·lȾ¡É‡u“ù" \ûføJ+àRÞ ¤_C®þíÇ`be±§®¥FØ"PÒàÀ[×cQ10!Íÿ„4Áö]Ž{¾XuÆ"–Æ5f†[¸~Õ8bxjERÓ“`Åvø&Íèk»û ]¤Î2vGª/ºõ›‘5÷~C*êÍœ<¶èsê*ŠK,UOêð9ì·;ü;‘ݱfLÓØºßL‰ 'P^E \*oœ\Šñh*‚•—Z¥gB~qìZ"ÀŒ¹p;IaXv㨂ý‹D÷ÚÍ¿ü Ô's¹7fnþ›ù Õ˜ê8 Ç΄—ûCÈåX²(B‘~ÙÎö>••/ÔŒåÉï×/R×èKsìà“ˆÔîÄÔk½\_¥DÔ.¥É§(éö¯_§KõÉ(m­Ÿ.Ö$L0b˜ÎW̓l–ˆ¸æ™2ld¶¢ŸR™Ò4Fè%²ÆÅ!6øÇkÊ<@s´Ó^ÒÛ™—µÕƒú‚8Ñaý…'‰;Ò¸)qÓ)~÷&÷ÞŸ¸|ÁŠ›H¬D˜îƒfø®‡m umƒ)ð8„Å@ö²ÔD±„oTEu ºt3òå„m^@¢‡º3ûû£•ꌂçmÊHåe`… ¤ï+Ø‹¾àc àÔvqsøÖ?ÞÝŒ¿°Ãk2ߤü̧X«Ò‚~H‚Í'ÈjL¬R¸šHÃÏx˜6åÚl´“€œ¬˜,™ÁG©ðqâ~‚¤vHÓº#çÓd¾A»b”*„(½p²PØœ×d3=wК€Õߘ¿µÜ6h!Ÿ '8÷Ê‘Ê&éĪö|ÍÖâÆÑF”Lc`ÉŽd^ ·iè9–æH^±S¤mˆ”#¤>‰—"Ö‰ƒÁ Ö"˜½a7Ù ê;X}ÙÅáZļ›°ˆ.¸…‚Ôl”xÔ¸îòOò~Js=c5(ÍjF0×j“ºb{_5þŒ/9IiÒm3îZ¡‘AÝ_iãv–M¦pš£!üÐ',8Ö8×öãñ+¯%†`3 AKý8Ìbû6ÅzIÆdÉAÎ’$ô\¬t€ø#Ò—¢I3¿ÓAWãgŒ=L1­“kÈs5vu  d™€6V¼Ž;µ]I²wƒrbfÂŽŸ‘aj“wš¼ÁàWPÅÍhÓz5ùQ8€Êß<&ùÂÀ[a¾øqÅ{Ýܘ1w К˜j-ew-/s£Ãǰd}¬¬ð!K•’¡4õí…×Z\ðôB¿ FÓÔ)úüÛ8·âo٠Ѓë(‘eHâΙ“£Dí®»6"¡6µÿ Kž»mA…RIÚkRô'ÿðâÜ_¡Å…O˜lLBY,<–’Ê£òR‚¢LÔ-ºä“Ú²$²Õp²¦‚Ÿd?↵oz+ˆ!SP‚rÑ•þà"d&&Ï8°„Ó•æ¼å*>UÍQ+ÉÛ™ßf„pzHÓAN fš¶¤ÿ’¢˜þžË\ÖècònÊx“k`c„_TmZñgŒy- ÈØ,V3úW3Eœ%s#×O•úENÈVÍ‘!|°^ª Ô´…Ç‘Î1:ÆVœw]t1Ü ¬s¡Miº_4`ð6í®”bà î GK‚BP˵LX¾‰gx¢ßñ Újð»Ò¦/•ñ<ë¢8¡ÀÐe“ òAÂJ’gà÷Ï`ìPaDõÈ‘Àï¡Ë¶ÖÚ´>´Ä½']dI†´ÐP®P;\±±“œ<•j‘\“cæt<ÔºCãÃb£´7koÒ2ëþŒBA&·xixäyH¢"꣇?^W áâa<.®DÙ¤Ë'æ@¢Å­91ð¯ò¯’Š@8m¤ËõÙ¤UF5qQôx±\[&ïén‹¯ò”ýêLt‹Š–×¾‘©s eØxD}ÂT·‰*JþŸ/úb•dLvpݦSy®ÅhÀ €àz¤Cn6QhÕͪ ?gÈ‹&´–-þm +„–g:=˜¤ÂluBa6ê±#ý›Ê„T pé©f3"xšø~G“)½à´Ü­»ö"Ý?Ë3^U‘} вŝ·l©Åµ#T‡°]vBi3éEÃ_‹“R}…sŽ}˜8–µ*± tñJ'‚·¡eõÊ|>C¢)¶ÅôX2Ë äeàèZ­–7»À艛'¡³ÿ\\ò°b^)òõd‚À>&¡”¢>Dæ’7*P Û†$ÁùDývFÜH‡»ÔJâÀ”7LR©ÊŒÃ™œ¤`´“»Qw­ã,wí³‘K‰Rz|u\ÀMÒ`÷DáŸ÷™[‘Ò¼¨'Ñ®ô²'ä¼¢7¤¹iæÏ*«û&LRH‚Xð*" ™ª:¥ƒïèÐ;)˜—A§pÐÄhyål¸0µá£´´]|²wŒqÄÓz ¦ë€ $é’鮸h{¸ $|)}l[1 “¹V=©|»‡ ê}ì4œçÓÐý7G0Ö¯Ô4T×ZAbŸ!sÖY _ýÜ[NJXµ¤ èÀ¸Ó¡w÷×tìÈV0´;ŽìˆÃ©êuÀÛ'Àþ'·ö­9H•g¦ó Ož:Ãq©Žò&’LÀGa6üÐwUÃ"¸!ÃVƖ‰ø¹¦)æìŒ­q ‘÷™ZÇ*yé0"³„øYUM<´‡ ´‰²Wlõ`_ý0©nBµ¡¼)+6f»á‚ßÇ^ž€éØz ¹rYá*Øc¦)ÐÄnÿœÈÁ¾ê¹ŒOzKû-}*Ö5®Ni|fÉÜ|/ß3mc5ÊvÍo¯™¾ÊgÔN;Uí–_E©€N‘³ƒ¦Žõ4ÜH•¾… h†[·K(ö¾Ëã·ÌÏ[º¹¯%Ëè' Ld š^—Ò:šCÈÓ³*ij6 þÚðh3€9‹XŒt®T&ßÐ/E¢„V*³òNŸÊÞÑ{@ô¶šrlf„ \£M ÁEÙ @ÇL¿oDïa¯, vÓÉÀÜd>0\ ˜“؃ä–ÈÒdi[jQ`' Q§wsº!dP Ümø“ZÀ^_ºlÙÈpw5‹KbN v=áNÒY)ÊÔ5H@ÏYìM³ž*ÙèÜH±’‡ÑMÛ¤q"DŽи#„WJs^†}õÈ?^ ê,•ãô®·Æ6ôß"´¶r2·Ï#£ù ßÌ ŸÜÛ‡k”%Û/›ø|^¬Ì¶“±;Ù£²!@¶ÚáÁ¤#¥…Þ…S×®‡™ñBŨàÙ„Ðcå N3þ/{ÎÈVÒÏï¯ØryzeÒ¸±ÂÖ c†AÌ™&‡BŒùGº Ý·lûɇ•@>jaUÁØ JH®EfËÛvÈs» ql*ȪVÑÂ5P_Sb¸4ÙGUÇEò¶YHM‹¤\{l´Œ)è6uSÈšmýÀ–xAÆhGغ( ”‰h˜è¯\!Ö6…•M/·ª*Îù‡((ž…/ð„Äg×o½Lüì øAà~Qv Ž™3GitDöE `Ÿé“Á‰¡mäx5aÛeÆ·ÑO2¹„»`¶àµ?8iw!}ʰ c[ÆQ>‰Dtd}á£A ràÈB‹XöÕ(AŽ=DêØ:Ÿ•›iLËëÇŸÿKü=ô-ŒÍn5îò~I·D²‰€ÿÿu€zbG·£ 0¼æ ¯p5¯Hˉü%ëGxŠŒÓ”¼ÍÕn|»ŸÝIñ]A(.i&)Ïp'½xÖÕVsdÉŸ&í"üõ,Qk¯æ³8DɪÜ!}+Ý0íáGéH7tÛ¢³¯íªÒòô3ß_‰M Œ˜Ô¬¤Xh£éú}À q‡BÃ>ÞiP‚<œf`Öd´ ÔIÞÁÜù¯¼´l/ÌÞ7‘ÐÞ â⨠ûœÙ„78‡d$Œ‘¬°9P§)—%í Ǽð„'€p4GÌ4Tlàh\þ,{ ÃÖˆ}Ä5TÙn¡˪…våÿÑEfNˆ›¼´/NÄûèBO­M®æéóœu¶^¥¯?š‰«4ŸäW/.§*Á?’f³1‰ŒBŽ–èy¼PYÀgõö}áˆ\€mâÜ¢<¬:.GnÉ’mZ ›ÝúÍfí˜×ÏäÔ¢a”¿ÄžŸÝ?U‰R q ‰ð °©;NêŸdTÑ¡2˜@ü¨JMª–å].BçÏò0V„Žòu ¾¥äj“Ä ¢³’O:x %Øg hþ¾ä#ûQ×ýßÀ+‚*sfÂË2@tœ‰óI([6…C›ÈôŠ-F[ å+mj•H¤‚!²ÏB,‚Õ<˜i¢ÿާI¾xºD#g‘V¢sQcª»±¨¹t »¥Š¡:ãMž;¾¿UTÚKk»S,Iêõ€¯Jo£Mú!T."–X/sŒžr÷š Æ:ú=8t8Ü·mý&‹$h¥¢e¥)^ÿRáÜôåÚ²P™j1Àˆæ/ó~epB˜j ½ ðî‘>R|sÎ`fc?ünC&Z©!1 xJ®&&½TT†`wð™ŸJbTE εìCÆÃóÈ öéðk=®/æÙ’m@ØB5}HV¦}T§~YŒþ¥÷ªMAi©6²†ìüÙŽ;ƒo@:¯]]BŒ_`5;:¡ ͬ Ôì\¿/KPõÇ?7rÿ,h÷žÖ@¯aJðª;A´ž¡ë­GPøã&  äkñ`gˆ{U´F¢È ¯ƒ2¬PƒˆM¤°øñÀEûUq‹ª«@ß´jL &}`ä•þjgW‰“D-j™ØÖ­™8(ý,9É1CQ0Æ1¾©êÐ8¢5ÅI†‰™úUjj%¸DeB²@å¹EçžB}€Ç“›À ÛõkpÍúÒoÝ9àJ†]Ö8ÉL3?ÛÁZpß~¾êµ@n®Â™°©sÿÔ%è]È:Šêš€*³¾÷ÿÆäœg™¯I•™ò¶ 8èªâÉ ãºö˜!÷£EÁˆ:¹À2æâéòÙs¨þrÍcv:­‡R³áž¬xáý€ªü×AtšR‡eâPŸùTXh—¦/]@èê‰ã M— é½¾1ƒõÿüd³3¡Âï¢8.Ò¸¨n«€£„KœÃŸYÅýº¥6]@à¨~³ð#¯õ˜—õNŠý…}ä¤ÂdDГ¯oÿögÔÀŒA\À€7~䢂¸ª²ÖBMËç‚é ¿‡s¾î¡ 0bCÓ9ý~G!5ª*© ˆÖZ!.:)+s°•êt´6å¿Q}o‘†,ŸIüê‰)ï½S:@3þ¿kS|Èxï#@‡´èÿ ŽF!y” Æöœ7pÈü®“—ŒóD pAµˆRÈPÈ9Æ‹¹Aß}®æµû¼<ÐDZ§ Ù蘱ÑÀŸbã  ÷Ûnh;þ«u¼4Oü1½#q¿@š”sAìK¹+ËÿÊÌWýÝî[›Ji9õÈH†d—ø¨J>ÕRñ'zËT£SCNÓAA“¾xà‡€¹´ç£X$Và>ìv¬¸Ù—ú¯èÉ#:DõbhuŽDJÑL…ÏïÛnŒ•3ÚHºdt¨Ie¿™ßYjüÏUHMÕ ¼ÆkLÒ¥«ß¶êÛ*¥•]2ð_CÇ 36 ‰äý±RðJ!**P\˜[q«¯f?év==•z‰L0j¶ Ã+ìç¦Ìé$_™aAžpkåèlÊ亮& ;’¢ Ÿ¤‰ú¹Y$éN¹UrÚÄ¡¸šñg>6Ü)‹JË’.´·®|bÚþ&pH˜‘Ý’+<èéUDT‚÷ZßCŸrÿpbW3Q}:×fЖÎëe“háÅ%.5vÙ#Z¿p1ò-×Çøš‘ EM”3A-[ŸjÞFa^›ÐBTÚÖob˜~c‡Ü‰æY}ñÁ tˆœÞl’졺âͫ㭑?†²6dîõ¾äk M2’ÈçDâ¬Sf[žn$-!q…°XmcÄIEEò5Mz¡¯Bºü{ÊÔú@ò¹F¶klº¶d=[ã'«8åÙ¨[G*6Ž™xhÇÍ Á‡aÙV+”æ>d†r™¦s]k,¢E<Iá$@±(›f]Q­IaäAc:wîèR­ÃcñÞµÒh›[–ÀöFI&sø¨&h‚#“æàèt$[¥!FDÞ´èêd×БÀs©èÁYÜÁ\žÿï4Méh"¡¨‰b˜/ƒ¼#Æ=+ƒŒÚ€ Ûb¬ë#™v¥9Y湚$Ð(M®C¤êû-¥Žó+èx4™Úó“›-`·6™«ê_sÖ§Ë­îeÃ&ÖŽJÃ@<àK š»IÑ ñjeªæÝÌ|0`‰^–í¯&R>9âÈA2ùÌãMm$ÚœÕ>š–åú4ƒn&k°ºŒzÖh@7taö-ئÂDËh‰F~„ÚQPÎ.Wé-X‚9ý•]5'¼ObNÛ…7i\Ù#ë"˜{ IÌØ¶ŽpD»Ãr¯u_ zš3¥q ƒQGw™oÒ€( I$–°†šÌ´ìŒƒn¶g`-‡ù± e¥·ÉK´“ÂûéVmE=×' Ì–Ì‚"³GÁ¿ÿÁ (Ę>¦ýTO!8$¶apƒÀ˜ø°}LÒ@¼êªæ—ÒÞ½j榽&LpÍ 3˜KLdÓAÔAþ&Ä%k–w“6L1¶´lPjÏjã6°÷MÙZưJÅ^-_Xâmé0ÿlx ·`í€!ó<ÌùÒ¿íóÕ=è’”ÿÀ4£µ‰ÐˆÜœ.>¼h UÆ™Þñ@póU6NÈ[ïÜVÿ‰=“ÈJ$GjÊÁ÷Q$?Æ•KÚÎY(}ÌG5'È#vÎUjiŒe‚-$Ö Ã®Ká>@!ìr1Éôˆf äNvË9àh¬ÏAŘR8«WPö¹ÂûHŒU•O>tÆ,ŸRFp·Ä­…Vd¿ÇÙdŠ€¨$€{H×=ª»Ü=šÚ¯€näÎ÷mmY,‚Ë( %†È T›×ááIw±ÿJ@Œ¬È¨lÔ^hàù1'ºÒ€a)£ß ¢Î<·?ÀÌÀQùù)ºzQâ„=‘Ø ÜÂPHÈB²Ñh=Mî6…§l‡d’QƒIBÒÀ;ËÄ׸Yz=Égä0:lÂñ(Ïé éÇEoô ´&ìÔÚxIŒ_|ÝúH&g­£Ó …Ež]]i&6ãºñûøÝvÒË“gˆ´ìƒ6Ä!.C=ØìÔ ,› ~â%µzrÂ’‹3F.»±šÕÃÉŸ …æ½²Òm5Žž#c·nm ÉœX:ÐÕ¨¨%òW$ bkôL°ú”> ýZ'Òº`d. Ý#¬L9Q>„¦ …˜ íIêJýH{C©·Íÿ2ÁZÔŸ˜ñX»¯TåÁ6K v0Æ`7Ð×\Æâ¼Ò„ó"€Æ{¦I0ª3âópÌ(bc~¡†™ï¤åYŒøc–d²V€°|e.ªŸƒÐrŒQqͼõ’®ÁŒZ1C°è12yšÇmpqÅz¶J0kòˆ OVšHg¿ºØúË9ޏsl  Œ”0=ª&LO¥!ƫǜ ­:L’ð7„¬y´¾.ýPkLŒ¢áÅ!(6™‚º±–¤Ê@ztúSÿ„•eWÝ ê›Ý™,c‰r/I#\y9+ð²OÊCX ‘ïÆ½=o¼À ±ø7 —&-€0Ñükdì˜wYbkfõ zß 䜀F9½¶œ)¦¥Ý(Ì&ÆJämÄ‚ÛPÁ€ÿk}Ms´¨h‡`%ÁÁÜÏqŒ.#8-èÓ°ËaÒFdpOˆÃ)@ÛÑ=ñÑØM¼T[Â<½ ±1\3ïé¨ùô¡ ]Èó¦ê‰$¦:õidÀÓžä½LHóÅ»-Û’jãòBCðH´2'Ñ#2i|<ûz–uD´ñ†·È$ª7‘.þ`ž¹$lNMññ@!2e Ó¤›Þ°ñÅ/Q,<Ët†l†GŠrûëè&±ŠB†Hû‹þu åbä%i5´mOgT°+P=å7z®¾Ê™Tê#µ>f†€¢q£t,1]’6Ti§Ô¨­3Á½Æ7W$ʸX`‰¦dÓñh±¸9 - h¸•8|m‡dÁ¹*¹V]À2êJyœd>PÃ6•rá§ßýê¸Ü¾4òW8îØ6¨³2Mì# 2O°ßŠ®°ÐÎ PZÐ"qM€µ£´¬÷!ø—&cEBžÜ#s“P厳ìÐìR6g®? œÇâxdÌ^‚¢ðj¸w`’0ꀬ³ ás%ä#T±=<û ËÁÛz‹ÿsÚßâÇ%€¤?JÒG ¬5~5ÿÓoXX É5†B«ÿ…­NtÀƒÀ‘B²Ö6ÒK‘l§úeŒدJìÒ€jí°¥tg3 aXÖbíÅåôýó&:I„zmþh.yÓ ÛyU½' ã)qZÅ÷÷¥›_t$xöð™Ñ­]£z˜\ƒäÿéNWe·»Ü![ÜuøšIÑ2›~Ö&«êµ(Á ö0ãFC¼Äîøs—^hâÁç2|G 9­]çù›>‹™ÄXƒ™7›>çqÍ3Ù·“Ð^+‹íƒB>t¼ð0¾/¹d³n'ÁdìLèôM±Ää]yt&+È 8Z$ÂøK‡¯I±00ÁŒåG`b¡ŠX‰‡OÚDCR-ÂÓß™ÐðîH£<)ОØ%“Á*aæYXPj†™[霵DNCGöò²ù¨3п˜è´½GîíË²Ž–¨lº2›I™£ƒ;~ˆvuÜâÊ|ŽúNOºÊíš&~@‹\½ÉÃjDtJó€xc¬ÀÀ4˜ãÂMÆ`ð0!ÄF¤óÄâï¹Í¡Y³Á‹ö &´Ë]¤À,E¬NÚS„Ù4MI4…éšz_þQä]¶5A(Í4qÚöÆ“>ÞÜ¢sXô,;PIÕø´ $€Þ ËüžÖqÜHšŠçÓÁ h€ô¬¹t,Ñ W8ç˜ÖEãÝ#'<‚¤:2%Ãú>Ú‚€ëGž $6Óý{,Çžs*o2(‡ºà¤>HY¤ª80ÆøàÙ]¨:IpÒÏGPEM}LT|d?Ø‹!`#ЂOn—¥®¥Ÿoñ­r[bªˆ ¶Ï˜¾Î‰í ª­ Q½ù»bõbâ,^…SðCþãÝG´Vú=ürçæ˜ÆM·Xº™ ‡ÔŠóAq¦£åHG‰1N `Â; ]oá )ú'ÂHYìô‘Új•ƒt9 çÂìºtatRH‚ZŸþG€€lJLäw¶Š©»Û¦˜h“ îmã=.S)« ½Ö…>ËÔ™Ýì'IÀÕ5·QŒ[A]‘Â9!®¬¡ïœ¤O“! mü¡ì‘¼š*÷ÆW=4 Ìέ4ª‹5¢@£ÄÿúØxí\––å)Gÿ`KtcP ~J™M/¢4ö™¸O ³J=4A¾1 :_ˆ$üAÖ_ÝÅË3Qý¨.M”~S=˜UüIóõGDŶ²Æ}‡™ y€6XÝuƒ¿ùX€‰í“l‹mÎ- Á’Óè]& *uPàËX„ = Û„géŽi³†‡LãØvŸ>ì30Ò±—¤/[`fî‚ã+H ×åžÅfm‰’'ël¸6u¢¦CT áÙ¨ûˆDÎ è‚d‹ã\: Ö$9€†,ápGG21šk«>ò`²þðï=%ö›Ÿ´ˆ¸¸äñ«UŽ¢×ÑpÂd1i–U/› ÁàÀ6Ìñcâzö‹Zy,û~D¿È\Qjg€¥Cän¸@Æ 8 ŠEÖ•'w­\ŽŠçeö-å-ü˜tY±cG=b›Jù=PO°JPjcaôÙ× )8ÇnD´24b”³|Š0z¢Œ›Áºøh0P1›C°b¦˜Jn(vȘc’KÃi: êÿùH³T˜À7Z3ÉJ aÙÚ­$ÔÌI £‹*õŸä84ó[}PÁùn•º ³)pf0¦`Ùirø~“o×âjNè&'êGòÂYAIoó$tAÅEðýÃx+bÌZõ‹‚t=ãb'Øý¸Éã.ø9'k€àöA+dBÎwuÐ;Pï•,UŠøº zI£o çì¸r¶ÙD4•7^Ö9²è­ó„üfÄþÛPžÔ†ðÄ ö*ÈMޤM‚1¸M¨› NV¯»Û"J½Ý<ÊG÷A“ÈHú#ëÒÞ„å/B'»ª|9ÿÃñ¶*û¥žU²*‚Ž·inµW_Dþwe^B”«Œ~õùh?ž·WFpñû÷¨’ƒUpao´&Ù©³$þEhq‘}P´M¼-F¤¡«¬!3Œ›p°º½µd d“ûÃ?êé¾Ðìô|_ilgwb-ÌÊ‚$ä‹ê*ñ {ÓBQ PrPÝ‚^oïJ–xh_½%^êËà]¨À7ƒv5 ‡"imÃ.™Ø9öl[±^Œ~¶Ü’@?]ÓnSûA k³xŠžcwM0ʺIS£Ûƒ¥vÓ—{p¸ïBU:•Þ3cð[]LÞ¹™ƒhLá*‰áád§ë ‡€ òæÌ„fR L r25¹YUC8ìþ.9 ]NÇåÿºLÁ[>++üUUëƒH‹Ñ€D›7 ž™cÏžp^Æ?¾\KÂ[¦=ü"EPüôŸ¾Îª£‚óUÓþl·Y}„Åt¹{ú_pò8À¾“l±ËwMÄ Xo#EGùÇ)ï Áøe°ÐÀu®ØŠ˜Žíº1ÆW»Þ’0¸½ä®‡ôÅ5@–šcçhÿ…°]½‘¨§˜§h¶ý÷}]™wèµ-ÐwùÀø ?Ôa¥Èa“(h-Ù™5P ð74_ÚU» =šBŽÚLy²~> ~QñoàM\63_,†5S«“=ƒo/’§J ÿ_į݇ò5dƒôú7‡ó‡Z“ãÜ<åAÖ•`…,J«àd±¶]%dÀK—Ö!䇳Ͻê*ª¯š²[Ö›¾Ñ`ü@úDÏTµàŸ—þoú&¥hEõNÉ 8i K×óå«LϪô1qÌËXb ­Ò‹ªâݼßÏ3˜ÔÕ.œç˜bîk"Ú! Ëœ˜Y èdº`Q™z/¯†E¥­émk# *´+hc9¯‚­Ë(ÀÝ£1¾e  Â×ó6x'_þFÿ§â$€TÃYÅÉ@.¦Ô{{ÏÀ}(´~Pf’^5’!è'R ‚Q—ý{>`è8"^|ßu÷øNÈ6å¬6²Æ¿‚ãÒãÜ<^0rüLò-\[Sœ2 @Ö¡£­t44ËN„¡sÀ2y sô0âRW'‹•n‚Á þà¾‘Ë oWü·“´Ûù°9`(J¸5U¥g¨B’¸‹\"ãŠ}OK|À‚4Q÷¼h± SôR¦m i<™üŒþÌ_¿|ÃâAMTÍæñ’éhGš§P2²!¡pY[Ôí’é,zßzú§ªâ,+€>?ñ'hÏ2QåJ*DÀ”ËØ++ô²“†³‡[áÉQÆ•¦>WÏ…´Îë H(iƒüVÈŸ.¡ÛüµÅbj'Uœ— j|BÚL®²Ö.¹O߃ŒŒ:ÏÇBxÂëþÇYö„ø«ÓýÌÄx"±j?& ­‰ùGèø î5Ôú&XŸ´‚öFïÙtñvxC¦îûZ¯;,êù¬ˆØÌÿÀx"“кs!c­'{.â¡c@+9ºT0;tÑoͦ^Ã)œb\e,«äl„¨ËCøãy:w¤‚—²T–˜‰)cf+úŒÍX:ƒWÐÂèÄí½C¯õþl"ª`W3ë¿KKÔžgLv è¿ÍªEnuæ²:Íf—ž#Ø‹ßæ'}—+} ”⾇ Óòp¸Dµ®ö¡4éãlޱφÍ?üÈçqÌÖ€ÿnBÎyÏ»IÜ‘š±v·2WÎé`4×°]˜ñ¡•ÿ_ë2GžÂ"÷Iv̱çg–ŽøKÄ»Ò$Ö¼MÉ‚áåKTî0!èéFze<ˆþtC‚V¶&x ‘®èVŸä†%ô1·í¸¸V^Mšè”†M±«ZJŸÓÈ&J2ÑßZ¤ê£†Ÿ×ÄW?z ãSO'žm&ºχYBê`ƒÑÒ`4?·.÷lA¹§I±zª…’…#ÅyÂÆÓ%8€Ôjëüt«Â$A™â(¸$½ó—šÚQ‡¥c¼ùŽn!“oå8)ÎSCòa¦ñ‚ªæ9®…ü˜‰wMŸH'ä²Õsà JJ% &L«¦XÙ^b‡T'X}UÃ{ƒF¾ª@sU‚º-ªPià~ƒR–wl«b9ð=¢Ÿ£1fž·'L³ˆ3-#±¤ÚÜ`ÎË=1U‘?»¤¦Œ?ÿ‘ÍBUÛ*´ÂØì“áGR–Ÿ!?† ™ƒF²s7yD˜øØ56è€d×þÞh4ô:ÈmO()¨è3 ÿF Tžu9v+åbÏéѤd2]XºWɇYˆlÇ0iz1ÜÂ7\ŽN¨ôr/"pq¦³•“g¢N:p t3,4Ü=aÇ[OðÍ&pWã§ô«P8Ó⧉fÙ+ÏÖ–=5´ey†*¹9‡©Ø•vú„ô°¨çuÂl"ÇuXÜ©äUE® ޤù?ŸU´ëøé¦c&[r²ÏS.­Ë=Édl’©¿#a˜·T•_¨Jª(ÚzYâ*’è{ÜÚÂТhé@ZÇÐ ‡‡\ Gf&2ÁŽÇíâZe– ÷|è‹;ÑÓÝ”‚›tåd Òÿ·eLioX"$1Jt„{ÿöÙ­3“$¤™Â8ׂÙJ0“C3“$=À¶˜÷­ô>ÜKÑ^«ÐÖŸÄM\z‚òDé`@&kì»X¸Ÿfºw¡ Û‰ˆu Ϊàn”K•¼lõ¶öÛ[Hz©"•™‚J‰?"g ˜< ÅfÉ €<±kôëúRÔ7Bs4MuŒ±Çzϵ’ªŸÒzÕ´R0­°%m£þþ~ œf“:I¯ÊÂÛÂO¦ÿ‘d<®&ë /^Õeâ‹1OÒbÓÇ®þDÚ±ú–+B;g7B#Êœõ)ó,[æf”J.nò–=Žè“ÝŠ£‹ª3ò˜g­?`àê7éHÂVTçÝ’Ñ>ÍíœÉ¯1ýޱS:ù¨FâÎ³ëÆ·Œ\”—Çlu‘y“Â:\tDÈ«ÓÇö/齚Y-—|É} …oPÿz‰žøEsB =§ Pãs ¶“î’܃'{Ø=ª¿Sɵ]’´$°lcîûÖ“rAeÅÜ:in&ž¹Á¿U»Ù‡1ȰüÿÒêQ8¢"ÚF‡ªz´½ÃPŠ(¡~FÌèvóDßi÷6~¼’!ªÇü5“\Äåhø3ǺeôAãöE@ ¢Ù ì´@gíbL-“¾ Gr½ã !}‹$­|.Ñô­W(± Ðà–ðÝX–œ6¥gâÀIðŒ7•꫇[Tqƒ ’e€dœVŸ5œæHúönâ7€qC"u„ #yÙ(ä‘;QZN…„®vC£`Ô0j3K”^QSTÛ­è˜ûH 'TNcÜ씊&…rñ¤ƒ·_©ÊÉgø¢ ‰ð¡¬£ Ño"îî†m!èlc|Û ’Õ.’?‡ßÒH¼I©Ö<ÞHúÊ&%5K (vâ0•×Â7Ç ¼Ç´°û×~ L‡ð™Š‘®ìÏÞ=Îç}7j43¡ f„Í}êõÑh Å$¡¡°8Í´ô«É\cœb4|þdb—‡™CÈÓ#øöò#·+7_R±ð/YB›8¶ââ¶ $†ÀU’0c0õ—-ÏoD¿`B1 ™Ì7ÌPš.|4—ÅÒx—–(PæØ$­7i&Yò› 7DÎ $¢P,Mt£›’ìoèEa–Ù”‘´«]&*#`Ίÿ˜þàø<3Y¤÷Vd.€Ü‹ºÀR)“sÔW‡º&ˆÇSøk0ZKg¹(¤ âØ/DPb| Àƒ·j#)ÃÁP7F_²2…b[4+–ì ‹\ yc-^ìcù–?5ºˆÿ¬$è:”×h;3Þ öMìzI@Ã8 óoÅÓÆj»K6âÞj\ú ž@ÛX™ì0 h™#D::J|Av‘É'e ZÒÒ¡dÏMo [_rAÂ!•z \•œ|#„ÁåŒ5øõRCcN7¶Â½áïs°¶Ýbjɨƒ7ëã9Ç5°×E»Rme‘±ÎÅ9]Á¨ËÈ Â3b[ AQ„Ç´Ð _ÛgPfÒ' ð»à?0ý ;(¾¿Ng¶œÌzüÅÉS®þÔ÷†C%$öáТWWŸ0^šp(¹š²ƒ&-ðéqü"¥}Œƒ!Ì|T•^uÕ ”Áp&tR€Я~ïÄæ6¨+†‚ûŸ•¦ÄdÇTV^Kð¯i+É7ì±âõ3üCzw<Ö¿úKM™—sýâ­÷Dm#æÉÍðnPºX[ržç`ÈuÉÆþ?oú13ÂÝ™l‹§ŠìÑ2Œ‹±ú"2Ž7‰DeqàDæÙ†Z£{åzÀú®’—†…Úå8y¦%…l½éû‡ˆê6ãU>D6VWzð§V|o,Á<‡Hc[Ü#ˆ­ªSì̲ Œb¶·DÃŲÈáãq+ð-/Ö††æ¢rWñBÏW`Y%?*µxƒ_NtA¸qìцù!’<ÞE½F ÀoŒƒl,œ!¬Y—ž’éÎ@žU¾””$ áèQÓ„¬'HjšÃ·ÀaßoôLJ‚&Èç„í„É¡1Übö~¥Zø!ÿìð-r^®iªƒ7fU"&¨¶V?EŽ˜qUF˜l}vM‹‘¾b³$|ÓP?m˜@ZÌ™˜ \yŠœƒÍñ2>–)r sD’Íž!¸‘™[Т,XAYf¹cæGƒø°FÂÊ {O Rɺ[×A®÷K“ 3’aþ—Cn¡É7^z ´ „߆ðà{Åê]zŠYÆ~°Ô]°m@¨Jr@ÏëQ>ÿa á2•#UÆ1çYæ$¨y©P†ëÑÝù'2î"ØÆ…¹µ®†;> @Îçòœ"‘±ÉQ>dS¼€ XîRÍÒò´Ô°ü0‹Té¹8@x¾Ðm6™Ihjf‰²´Ë”’¦g «ñ•6Q uª½ÓŒÈ$ÒcÎMpߘWË”\ƒjm¬òò% dA¼ðÆôL·°hÈ/Ǩõ¶)ÃI¶³¸éÏm¦¿*X/! ¡±,ýVÍ#£õRŸâDí¯ÄˆÌÈä-%ï@4U—»ÃÆ’Èü¹q Èš(-ÏDÁ6`]>KZ;¦JÂZ¾î> ªuU¿¬üÞw97‚eb~k… ¥ŠsŒ‘ëžþ‡Óf ^5G#[ö®/-°lÖHIø« G¦ÙУ(ÅÈïÐhyÒ[sQ÷mD 9‚ ÏYO€ …»=`šzHKC–¶†;qô9–*u­{|€õ+Š{f¥•":ƒ™ *€ÆŒÂW (jkhÂ"빪ò±{ZÁ×›NÐ夸Ñ|rôéHˆ<Ž3ôž„Û‚TðQ*YbiøNÆBºIq5¼¶¼Á«{Z8 ÖGdֹؙ͒«±Göó´d`L~4PŸ£*Yä_ilÖØ&¦¶|!Ö;¶D4¢ÐñH´;ŸàwÍèþÖ&”¢@ÛRJkäô¿ÃG'ø^`ÆŽSGëËZ¾HR_e¯=nŽnï©‚i ò01&“´„­‰4› ¸½rCoÕ4…¥ëÃe zK-­ý|y ~à¸x'Ð׳~“¤?c$PšÄåEàÆK ò{€U¢^Måw7¾,årÉ,1íà8HÌGU‘S\r«ÜH<ðâç½[%« Fd³0ÇL+§cÚ—šÑý BÆBÍb¿9s}‘×Ú4³¶ É07ÐÞÁïÑðpt¬Î¼¿ ˜‚aÊ|/àÉÂp¾2ÅŽ[it CBÇÀC^Œ+6   ÉGOöiÀÇv"cŠÍúP„ª·¨Ô\JsÒà§(=†Ùª×œ“†h:¢ÁÕAb9°:_ÍÜÄ.ÃÇe €È€!ªåïÖûìÌÔ¶(¤þC—2û€Ó9™ôï ¥™¿B(ŠÏÆ,Ån=ÏüþÛIX%ÞÃâtŒ†®åÏKOÖ§ˆé9dçœq3B\Ö¶bäfÂÐÕ÷z–ò5ohÎ$[ˆÑ¬:ºÇ^ß ŠXÃØÅy{Ëmçê8Ó‚>oíÌÜñWò{ 4?ÕQ¾QÃÖ…X)~·Þ`0Ø3&,‹%³¿ÜY‘Æ¥„÷¾lÀI@RGeœxƒ!娀b`&S±ºŒ*¦ÐªkbÐêxyZù0d¥˵ӌß× hB C+«9£AìzôÒ¬¯5¢×P+â¼Mã<›ÎNÕNÍÛRœ`³ó#8“éÿ=‰S4ñº]-$.åLŸ¸Å‰g¸¹ƒ|µ“. ãò·3žG3ûnw&•|ôÆ/]ÀN†Ð€=÷9“€k‰Áƒahðªx+*‚!þO)€…Æôóâx"|Œo|}Y‚i0¼9¤öidysdmÜìÃQDÁÝc$k²Ÿ.à¾h²D€jæd‹Ê¡T_ýK[[‘¿ÌØ>AwY'd×%žžV³'º˜ÁÖÍð;ÜF z†VTêCôÌ£Ù×AÕ†²Åùæc™F[ƤÓ×a©’¬£í àOÔ˪Í+ý¯mXߨ>G1(õq<™xn•°Ýþûˆâ°¼™²4xɃGÅ!Ƹ†©«‰cïaâ°Y*­ ²‘9Ÿtó>$J3xª²P€â¶‹«Ä¥ýÀ I8R¥¶@HÚü¥¥š@ |x³…À›¾'FVšŸdˆ5<Æm-åÙ>ÑÖ EË ‰’}!Þ3«Å° Š>¦ñ÷d´ÑpâgoµÛoÎýã’2e€ß·i2¯K‘Åiš\W³8bvòt…N_Do6¼ßB—·Àúì+?r2g‰+ˆW8D=¿Ï’2}‡Üø4Ä «§hH·ã;Çe×׺rKZª`Ó!Ðpr æZ¥ ZéˆÿƒC#Ég¶øDRÛ ’å'óÕÖÓ厢d `x. ÂTÜ"à™|À¸~J6 Ÿ"fƒµ,º&ËØ½›ûÍ‚êj,lk‡<)ühóº,]ªÁM–ÎYÇÆý½ï@ྠo\-e‚$)­øïÄñë“ý?+ì.ˆb£(Œ=g¼Z\lÌ3~à‚+2n¿ÄV0&9oÚhfê’"Šð8<|ñàwÒhâñöÖá‘HþÉ3†‡>IHÀt¦µVœÆÍÞ¼å¬Imk·âl’ýC“Í'Ç.‡—õ±ñ"¾³Ž‰Ø$aÃ+0k߆FòϺâZlÙ&èÑ™ Ú#À(ä˜{:gvéi¸à± gš¥8ÿ<(5ÁÞ…T'ZµŽÇ1;fz”ZÄEsˆ®¿ìqj/«¶!²—îQ7¤¾¿Äú®¡ëEÔWóä¬â˜b&r¤=͆UÎ+t˜8õÿÕy .®JNæÍ-ªB´€Ñ°1Z†ÅíºP@HGà ´ bµ {ÒÀǦlÒ:‚ã§lÓéüŸ@°©¶>9(÷ÝÊîÜs–ä„ÏÃPżÕäÁÁU 'hƒã¦ÿŽÍÕÈgE|Rƒ¢3ø?&À<‰hÒÅnˆÈ8hJ€I>ƒÁä Œ{í>óðÛŽ9¼w'q"¤…oœ3s˰–[⤀øüÂÛf?edz¡fmN0,A˜Ì&C= Åš5ñ…êáK]w–¬/؉Ö1^N™ ÊNA¬Ù,Ë  ûCá–9rí1W5¼Ö à‰ãÛz÷÷€ª(……Uz’=ùsQBaj{O‘&’çäU%u‹ÄÙJ˜@4([¤²Æf÷åÌÍOG½àíÈ“-ÕUWhIJµâÒéZÙF¨€Jåì+»M²R‚\öM‹” z«Šê¤²!XÙz¿ÖC¥°¿Y= hcæMí+lZÔã7Ü‚Êðz`T!߬Œc L]6T ^ÙJl&j‰°?‡(©BsÔ {ÈÄv7Sßòñ1ô^ÛÃòPý/…nóI$R¯@Ài£B³Œú/`•,žÐF=+¥‚æ5ÓY‘ç3ü ºYØ èUQkø—*ÿè^âóœ<øþA ó>%+ÐÊ@è€kòÔû÷Ø1M wL¾¼ÜÌRx›¶5.«š®¹­Tž(}2ÿÌ^ñ…‘Šòînøy9„èŠ!¾wᇡ©¯¶Úû zÃ`Á@I½Àd˜+€‚q Ù‰Óh3@Ñôõ–Q Afeß3ª3‰»U¥îœ³×!r±”ãzkIåöÆH¸/4úŠÇ—óº‚ö‰àc»¾FPÙùÁІ~!$Ã|ÍWäŽ sÇX(XÁqÛ+Iwb¦”:1—åbµ‰Y##nž0=3SDVqœÿ›±â´™®ŸuªÅʨ(”Uçðîªíb»D*m)F6ë\MâgѦï/¨_Ée½¬\GK0Q†HÂàxýÔ#OaF•‚M;m442&Ûþì IC05Mm”ƒP[ð®tµe0ðëÞn ›OÉGG¿‚ ¦{¤ätYâSkª…5—¿Ñ.Et‹!îÚ['€DÌ5†ñ×}Šô'¤<³}ùO±úX6Œ\Ï9 /3dCµ_Óëö¢à¼5¡¦øbØÙïü1uwnþ0-èX®À&•á…He½k^:AJ 'ðp»ãÞ.æ&ÏÕEdœÆž˜œ>“{^]Ø<7Rr™=ï¶•Ý.@À<Q½ö9r~¼°Öé¥ôX°¢½É1±’@a‰}õµ(☠bøNà !ªÄBQ@îˆ[ÉÉ3ÈœO°OLRñØBÿØçñ¦ÍîAù }Ú²Z.&bv¯¾Ós ˜ÀÜš&=Û9²jºZüƒƒ£H.tñ£eL§3µFçÖ+,Bã8D÷ŒàùLÎì‰ç€Â@ZÇ€Ó*ê”Üì­x‹0’dñlߨ±Š×ª*óëž6F“±'€Íx—!ßûÒô¼Þ·OLÞãÜ"½JK?¿¼ª Œ46Q岌ѯ֓¯ý}–âÅXê[@ÿþGô)D£‘’%Vm•Ùä$ ‘')Gëpl±JòxXÁ†¸ÔwUv•Þ¶¨ïX8#Ä{^i×™üé*²%b‡ÈìZ•Ñ·<íyººqxìÒVá®.°+‡‹\U¦0Ò¿ BÇÿ¬é~.b“Ѝõ&Oƒ©é;Ï’ˆäÏË»CeJÅ÷Lsì¤òëcÌŸü–¯Û1éÔØ®·ÿÁLêDgSÕç ‡"€¶V©jU„Jdžцîó˜å·,žÕÙ¸)•M7â±#5¶µÑ±øk»U bFrÁÎ ÀøXXÈSÌàhe£Ÿ¨$£lèߘÇà"uyúŠÖÔuù|Ë(Õ–°Yô^ªˆ0µ¹sû^K60”Ç©š-¹> T\ÏÒ¢øàm—£ùdq^*}U€XÂörFæxa;&³+9¯–Òê…“L”| Í'/Ó—Í|Ž3§Ñ8srÑÉU,t¾¦¨™5ªý6ßÖh2SÐÿÔÅæv…hÕåæ‚qT`6’’(T€’R’{}5l§vSz‰héå„ߺñ™4¼*Cͬjj›¾ºjšÈú ¦Ë Õ ¿òãûœT‹TÐÀ7§¶œ äÞà0Äx=^†j1y^%M¥OGÑtÆÞt-N.£ ‡ýxäy“&`i£?gaíÖ„ëf¤TJd¢\R?ýŽTRa’Ä«ƒµåa—áY¾j;Ôb}ÏX#óŠk÷…8Dšÿ蔦”B÷,é=DÏ“"ZEøÙï«ØÚB‹PþÀÇYhÍžŠBTŠù‚ÎËâ7ç×䪒臦¬Jós…âˆV®“ÏJ¨ ì‹q8¿²RîFÉö× ?h¯cI¸ŒðKÔÁÅ $Lz… y°»Àv« ƒF™‡ŠÊÙoR-Ì–A}a¦+«|Jí~ìŠÈKóœ¹ÿÔÈ•dù3 MNriPÿå}Yl.¨¶Ä¥6pÇ̾pày‚ð7·Æ¸݇.:'¯-ü‘Q T)šÍì«‹+ƒaGf¬ªNûjì#¼xäò 9Y†D|°LGÅöÎ3†ƒÍËÑ2'Táš*rØÝÚž¥¬¿k„WeH¨)-.ç.Ý4C¥6îõlë]ËfF|' ö™GKö"$éËÙËbU÷ª4™@=¬Ÿ£IþÛž+«j È$ÌÁä¬4QˆòA˜š•†#F÷–d§¶GMaüÛÇÿÑŠâhiB'–̽…ùé…ÚºR(y‹§ì^#“V·#MÛ"c<¹‰ì£Ú&ÝIø1ØR /1­•CÔY)¸’<°}(¥‰¥š6≑2àáG ŒÉdr× ‘æ«Ô|Ì0?aýp¶½â‘¢s™çJ☉z0€;?$dƒ.ˆ˜¥»lÁüÎ.HvÙi‰úJ•%Rë4ŸG1g\ƒô3¡k:S‡ùŠŠ‘ô|²­ò!7–ûL¹YnFœ‘Å.°7ÑDp÷ þ9>ç’N”C¦uÏA/ÞKà·U¡Õyj0n†Ì³]ªÝn‰ï !"Ä Ô?ÁPÃ’ i NuϽ/hž0ÇÉt#ojëj€<„cí®ëP»­Caëkqn?ƾœ>×qn;¥Å‹Y®0ÍiÈÃ$ˆ¢•m€ºà°ùŽœg=ð‰ÞÂ`Sy'epƒš|¯#`» º,­¹ôvVÁêieŸj¬ñ‘¬[~˜Çú!Ž J¨Ù’øþ˜c'TRcÉx'=‰ƒÇ« nØ6Êžf Ž f©"ðY˜¶× Å1àèÎ ¶ÙPˆv€\ÄãI3E‚Þ‘ÒCÆF€i²äËG÷Wøƒ\RŒ¢(Ò,ŸH;iÓ]0š òËMÃéÃgõ쇣Հí 4m¯EÁÀqÎ4_‡Mïÿ $Á îi$=> ÊÈèP°J( ÕgÀØy7ú“>м 8l<ÂX†‚š÷d1U¾”¿5㨼g›1 Ä„{^â*Ú]i•è¯IÇÊÔ<$"FD¨í£«%¤Kmäà%Þ$:ÖtYjÊüɱz7éœúÊôc싊"s’¡?èüb@ŠA)ƒ6{V°ðº±ì&aµb޹öé(bOͳ_bqðkˇz â-øåÇv•ôl< 2¥É)‹aÆC}Fâ›Yà·¢XÉʉ&fÀ÷ÆÄÜÒ°K§%–Cw2±ƒ œ¤RX/·¶ÜÅ‘®•ºoΚ “èíö¡ŠÉhºëã›u÷”@Ý’ó àK¸S)aº'(™ƒê W ëí&ÁfbMµ¾ŸJÌÂLJ:+ÄmLl^‚q¯üp_¬é.%Õ ·{MŠ9,ˆc|©PgŽM/`³ùŽ.’•eì‚ì—ßF™É‘v¯€@Ò1¨¹Ç¢ÌŸå–ÆfÛlŸþaB5£áØß^cDȼäôi©©áh`Y ::Oõ¢ågÌÇbŽð è‡l™ø)f\J* ëb05ÜTW{F þ4Ÿù©4ä€|3­ÜQú6kK«Gý¤v_£YPʾæÃLÆ&$¿àp]¤ç¸åóÂÊ^¸h>à^ Ù–¬ )…÷Ÿ¤Ò€s "fT€JÕuê2uˤvm·Î”d°¡¶ìEæ~Þ3(ÖG—ò$Ä÷õÔüaEìK-ѽ‡z@û ”㈠®˜EÚ”¶’ö2¢~°¥þ G¢z‚ü\ø7§íMŬ;]Ç ab]qA‰½@çq-1R$àë ézLLY3©€qw–ã3)*`iâ‚`Z 'þÙQË\'^’ž—!ôlžˆ„É0Š Cqƒ¡­ÖwPó ÿê/"M©¶5€r—7 -,¿IŒk¾ûIC?µ½q~UÂk 0"K€ÞŠL)z CûɡҖQ+B€ŠFŒ‡’#*˜Í¡ èüpÛ½ G]¤ù?TkЬÖ`¼t©·fƈé2˼3%>?ìI\‘¿eOXù„š€cZpðí„XÖø¦*ÒÂq¡ÅžµÑùü• ÿ‘°òЪÁzrC­N<ŽZœÆÉÕÐ[ù²o öÃBOÇË6¯úDCò/0 ‡Ap¾O7–)ÁUKƒ‘U´ž¡U©7ÞõV”No=4E'ü“Gb:é`…¤º§¥,ãÐ iücFè2nk°rM¿¢”ðaŽ9“W†äŒ ¼÷ÅÃÀêí$depÄ™I‹l'IRí!«Ú¥a‹*£ÚAþ?‡ÂafPk‡zYƒ ýYánòHå`Ñ83¼Œê> ¸£¨ÚÑ<ìÊ Ìà´µzIG¥ÓI ÐêMÁ’9:#-*€Ål#¦¸;CáÇDAù4à'ÓHØ<–¢|“ñHÈ‚™=°÷ά®ñ-à¢l—‘,o2L|ª^ðE ØLÓIP÷I"“Ü®4Ò“÷á§1MÝHÈöWÙø³H¢×â<. ò5U%žë²bBÞúɰ…Óy\wC‘Ä_¥K4©>Ô1BXü˧–)•Ç唆U©=1Kí,*0ë° jŽè ™ö†î 2ÿVT=PT9ãß"~ô9,„= ]Vx“q»¼ÐÑÅèHšnäÖs,À„¶éÄ™ttt8e¡Ž:Y RvîÑe:ÞÆAp)$׈`çã2¼3yްãÚˆ”l/±]}£ú^€l”ãÛ?VþÑø¦;´2KØ`8PÆü¢—L÷Oþ:3Ùó¾ð5ƒCÜŽ¨`.½›Éy`1zveç+õs÷€]F3€©8Eˆwî¼j‘uÈð÷Â`e¥á™ðN £J~Š1­Íþ ¿f0uC3xŒ¬‘Š{AÄÌ^¿L8‹Ùâ~Ô—ªl­úÙ?´4x=Ø—©ÊLÒýE^*Ÿ`;[0W¨ejrï™Sÿh˜ÆS+(ÌnƒÙ/Îuùq"‘¨¬ ZilHŸ=&ÚUM4Ío×p„‰žâ¸"dÂ5>N~ì— ù·cÍfµoÙ²ÑÀŠ{^Ùúœ„˜]JÖ :õÎÁ © Ë§.Ö6ÀbŒ¸˜,G†7µ¨‡’¸Ï.¡¤¨DÌ»6(¨«ŽR©—B,Ó“ßH=Ëm—(A4íúÉH ƒÝ˜™ÈW 0`:ø§ã÷€Ly ú›ûlÃçªÿôʱŸÆ@ƒ­0rü4 zP@¡ã³†‹‹ü\sâ©='‡#à1Ý:EÇ¡Äëðeƒ5ký¬ÐËÙñ\8rSÇ>—Í k@¤ŽæA– &i{„ìßRïÍRhB(ÕÒžËÍ»óJ9´‰­w…~dm¤«i4'ÀgÅ*ÃL®ÁÎ\›èˆÇöY!9ŒCÑ#õ.'ëy YBõ†šb"J‚%=9¨œk…ø% ö¸ G†0q¹–^ü“®o8¸=^/Å@ðóIËÑ%VþXVî Ù k=õâ”Q~8êÑ ˆÚpFï)¢È;qÿ%¯`)1ôgpY4$_Û„¿Ö6K€W—¯"y ( øStTJ™I?ö<)ÿEuM·^HG¶Žƒ{rFš'â‰dí5PŸJ¸¦Aƒ¯r±IJcøŸg!`o%­ÔPÞ^t‹¾Þ:¦ ¥ÑÎfÝöi×[—!JŒ2[? €‡ '#Ô#f•!ää-Òÿ“Ý&Ÿ!‘ˆÉŠŸ‘º•)ó­»‹?b”§ÇÍðÎéX½ø›¶øJÛ#°²ÙMlvÔöL,c5|ÆöN·ÒR¤à‘uç<"äd0`›— ì)¹º:¯w!¹j‡0÷#D>Ð5Ê0MŽl^¢K°ÍÙ½¯8’ˆ;jÕ˜)tmuÂeØ‘âuLÂó7`Ù631€$âŽ>H9€ý Œ0”ŒmQaµ+ý­µÖi’{û6Óùü8Áݤ–äu8LªIZ¦êä€exRëDΖå™qš~‹­?#$§ÓÀäqƒ}ê4žöó¯vz¨ì¤Þ 4³’C\Eõ‹`¬zˆx$=ð·@¯‚µƒ:~FÉ wM>¿°—%úÝhô´ºð`§Â¹ÓD0ÉÓGº‡‹·<‰{>÷ ¨„hSh„Ž×²bÖu¹=ß&Š ·Ì):>f‰âCÈTš8(øåïè*–Ø-i’0íLrXÒkd¼m‚q‘ÜFÒçBœ ª]b½¾ˆ‚á)ƒ}6]ì kTUXÐV_ôḴ8È?4 JÈ€£çÌ‚Å1j74&Bʘíb(†eµ#ì`¶ÆŽÃÊ]÷–kŸ¶ÃÍÛ<™Ãò‰áŒ–ó·[ÜLio„¢€ÿ©â\°Y f‘=€‚^âtœ©6v°O¥ýÝòƒ1ÞXXÉÞóW.ß Ûz­oÛ®i¹“ðöÃ=Ï3!cŠß'¼–ûÑ-H¹‹p¦ãßuºðé´bÚª*éhgCãyö"ˆrcÑàýG ¡“_m­Œ÷Û–€‚cÌÛ0©æ?Ò‰zä߈ÉY«»“¾–mƒW4~v»‰7à݇°vÍ\á?Ì–º-Á™þûn³8’¬ÂÿÞ¤¢üâ WÑñȹßÖZgÛ¼F/÷IÆ< ZÈICÛjà@¥ñâž®…"O¦K³Zh+"D¶ž;êõþû<×iº©ö0´Pþ²YC}@™mÌ|3{çuAŠâÛôý6ýoPe·ÏÀE ½#Ç#JvwöÓPY_:Sêûõ¢ÿÏÀüi‹]ìøtLn–¹…Ùóú¾R(ƒÝS~ØM¯nÑ-ì¨ wØgÜÕ©;ˆ1 Fp‘î7\ãÊ/ë̓³±šz-ž±ö ôb–H:¤h¨4,Ã?Q€–´ß!•MC?J éÃÉ Å¡[P{éêk„…øÄw¯âL¥y'P‰¯•‚ma6¨²¯Åq׃ìõePì$¼!z5A޹½x`‰ð… °OU›)¯ÔwÓ%˜:4æE·…|P›Â´WŽÍÖÍ#&äD*‚läºbƒ,,C*J/ŽÔdj">iõ‚ê6ÿEÐ=r$åE“óhÂG·ø@sʘ½  -´׸jé~|72™° —KÃß°_Ö¼B†zÏ›cƒÿÎôÀ%_§?üÒÊã„Sy~$ž‘¼ž¡Ò€ç¡[ëôC‚—E¹}5…‡Âgþå"lо«jv0A¯’ ­9$T¤Ö¹lb3p@X@°‚b Mþ½«íB›~a[¹/ƒþ˜BôpTµ“)nìÎ2k9þ[†Ÿ¯òèBc$—êíÐH3@›/›oÿp€"–À‚â¨W¨b¢l Š*…ÿlœ˜A†ÚßQo€„„„"GÆ¿|€aŽM€_ª‚LUÛàPѽím€F«qÏ3ÇèVPÞ%‚°N–­Ðk'Ô#€ ˆ!à)Ûz Ï£BcU=禀$ÝAp,®³äÀ X¹áy@ÔjÅ «¶Ûgïÿˆ´4P&Ÿþâÿühº…Ö×[΄Myüa&hîþªú$¤=`$xWî{¡(Œ!B,ÜÚY飠K¸"垤Pv9ÈÚ×Ame¯…èi*¼çÛ’ãÕ–ç>4F _ì5Áëï“ðü&]Py• %{)z‰XjïCyÓý¢§ÅQŠVIÙ>o|{Àð@ !xÑú\ð ïz‡Åp7y·_Ÿýû×wð„âO­ Ù€L¸§É0üÁ“çà=Ið_Ï XîŸýâîñ|‹àHÖíS0$/Ñ!`HPsˆ¤´—9àæ=+ßÊW tQv¿¬ /ŠÌ˜ÛÒ øì‚QÝš=ÔoÏdýÞŽæAqë({“2ô±Ör‡¡žÙÙ¼±ÒwaÈ«ŠÝ'¬ ¾54{¨ßøûÇÞ qy_¦ÌE$ø0-m²ªÜŠîŠL"¼%¨‚>)rFbØ u Ûorx–î¨ÊpöjDjT+]Eúõ°âZð¾jv ù‡`Ê-ª:PR1û‚0÷ ±°¤®Fõ~R{œA¡_~Ùûþ—rS)j¨ò€ôàŒÛÈòÝÁ +û·W¼ÀD³qâÝýE+0úq=ø( 0tPIè¡l…7ô[Áa9Æ|?غKí4ÿþŽ9Sð&;g>{¥‚kîa!¦qbîef…áÑ €Êx€/*ß TS`Àœ_æî#ò}£ïuU}ï䌄Bˆ´À[è†S uc@Ñy>,g L©]`F8F Ó¨ÐH¡­ôµ;žáÔc’À|ædç‚@T4ì6ŸÕÒÉ̸Ž#¿ý£-ІËÍ®ÒÆ¡C9=ßÝÀÀ5Œƒ²–8£QÖùçÆc>#ˆÇÀIÕÖ²#|Jûòb ã¸(Ôn|aØ„ñv<Ð0úxбƒ¡Ùë.øŒG-ÿ!§¹ öµç"›ê1È©u:ü’hŸn b²:`ïÈ#öéyAÁDÀ± $~!báÔåÄZÄŽ¸Ö\N"½ë­:WF¹¬¦æYÔª‹¤l z0Hô20¤Hq¦vü0<*ÊR…N¸Ë ÐqââñŸ&ÓøU¤ã—œÐ;ìkü~²ñÛ²ÒÒæŠf\pdùúËÏ‘!™lè 2«•ik¥í·Çß»Ö[> ;˜Û;ð¶ç0ð§b!C@S‡žbè1YÈ Áa)Êú&ŸUáaxA9¿"ÓÀq‘@Œ£sÀ?ÒöHRôÄw¾äÂîNóV -+U È€XX‡‰½¨pf€GŽYC8h~@¬œTTÆ äŒ•›Å‡œ Ú~!–8š;¶wÔj‡—ggØ«Ä]èP'ä‚€GŽ+¶Þ\à7´røTQ@Ó†y€:*÷j²ªËàðE®b}¿óC#I.ÀxÐÇ“c~ |6"ÔßãuãBûÀM眗`v@Η®=Úui„Àï}ï1‡A:ØpÁàÀ3î¼-qñÓÄãUÈפɕ-ã&Ê¢ú•6ýcC*|j²’M ç[)E ½ñØxóŽç‰ü¸l£ïN¿â~ª…!±ÃÕ •Äq¦Ÿñ®¡Vå ÏâòSeº ¨MÛúÝ——<øïê\êhzÀ!æÙŠ?”@÷&æ“ÜÝø¼ÑM"ÜB®3ÅãÑlZÙç¦ÕI3ÉBd*]ÈɆÜ×÷®èÔ1IIÊQ¶T’ʲö¤?ŸtØŸüð&íØ·"n“‚=¬×ÿLΉ.ë‘EÙE3âULVÑcºäô¦¢^ª¯g'¸›5ù²#^³îJáó6g;,0"Ù|ê?ˆì«4Eiˆr-‰»¶˜©xbT¶\5^DAy12!€J”ÛAÉl’d8† gÞ5ÝМÖjÞ’Æg§bÏ‹ºÜrþg™—2Qíärë–»¶÷Š\æ°¾ãY.CÓæØ·XŠáÅžøà¼›LH9Ò“÷GèãÅw<ÚÅ|ò’œœÎk? ýï#’_e`–6%¢»ãMß[©°ãwļ©9xbŠÂþøØÊ®D¥žZ.wÁŽž-Õ8Ê•<¢æ9z+,iF"Hj5GsÇâJ°Sªõr¼âvûj—æ_Ò"Y³¨è³f84ž9HûÍ04M´ñ;òʘà÷SÈ@4àj.Ü’a µ)+Т"È«´'0’”“ ÜffKF屋HR .4ƒhbË÷Ì1˜ü…€„ÌåQÉ¿ Ø[nh¸UIËWt5´áQ09tƒ7™¨ º%‚"žYù¼)°ŠúT…U+µ‹mcÌ$àpº  ,—±Tº<fðjm‚ê.h ÕM–ÿÆøÌ ¢2¼ãU…Ö†3ÓxᣠtPak2ÐH«*0çšøVËJfÝõôIúØ»Ðÿýcò#E>“©jv4ž‹T€’¸¦g‹£¦«&m—\YZlÝ­o>­îÀÁ–ToGÿ‡ŸŠS©0Ó"ª-µzî~”u/ÇhË®·ö˜ýM7þ‡W"ƒ(Õë®$qÁ¯9¤<¾U¿4l5Ncgõî:¨fz“ÎéñÄ–ÐVa“¢uLHdøý†¬}¸5²bä=1L½ð@L®1qçø^(niÍ$I† \e™°ˆdZ G딋öAeœ}€ö0–—^™Ak ¤kq2NšÒ±xWºÀ}Ђ6íñÿó8¥Oò0ý°>] yÙ­"ÄÒèZ~È/Îí!eÚ | LS¦ó·Jâ$¨n›E EM¨ft9ý…h癘J\àõ *ð•"ámÓp8ª%Ù¾ƒý9o[¿Öe8¹ä eé Íl¨ÍXÏÍhž€žÞ&â8Ä¶àœ¸Î;z1?ž¥(Aħ¿Áɺ²_K·òAõz¦\¨Ñ/àÝÞ…z|fxsç<¾º‘mOŒ74žÉ"ª×‡°jÉAx÷ð+¥™p¸˜–€Wˆ¼`E€#…Ó7zßgˆCXxh¢¡âA]üŽJSGGfˆ-:WúÂ@VŠ¿7 ³¬D7ëÆ¯txŠ«Ñ2ÚÏÆ´Å3Öp'T‹B‘Ì´n»Í“énï"úM¬2Ý)r8]J¨×%J’~’S ÑèŽ'ýÎôŒ˜k§¶ á^q· öU¸_q©—@òª C´/™uú˜)P¨F˜ÐÎÅkptˆ4ª%è!Ç2e òŸÿ˜ÜÇÎ5¤hWpKåÌQž¢´ œR!zª¸gzfvÖ(ñç ú̉Þz§þODS·:¡æá*çÙ/pÆ(pú€j]èGçè#R®p‰¼˜}{Åra €J“Ë*¹X„qGM¦³~"±éü/¤\Ä—ÑÀ0xhqüaÎ4Ï Ð”çuÀ ‹\ ô‘ ÿ e^i ´¯EßDpx-hÀŽö €q â€ù X˜Dÿ¥,Cô‚}M8 tA(š§@ šp¸ž&íSKóSüË€Œäz·†˜ÚL£~¿‰JãɨDŒòjOX‡0P0H÷²­6QüøJf&ų,öº+•HÎ]*!“‚P£Z;mÕyy ñ|²-æLTz€iÄDU;[»\ðGz(sJRˆsâ >A”ëmP@dò6cj`ižÄž)£À2z¾Q_K ¤+ÒÛ¦›†€éÆæH7ëÐ1ÂcT£t?‚pnM€@ðåÀ§Ìé…À^‘(¨lœ@\œ‹ő낞6 ☗Håf\$ÈÁ†o2@.G/€!hÁô)(Hýü˜ZɆŠ 8 è±™Èö(Bõ Am@—>”ãí/ƒ]‰Oü9!ªàø>ÅÓ¤½%¡0rzB[h£AïÞû»Êa›xáH4½'P%´ÇZìÐ&Ò@°8°lg5¾µvçRVÒß%×ÈÅ­³’0õÓÅ ]`¹OO¾1$R1†‰°npuˆQðàÈr!ƒ‹pÄôPávÒ¬?¡•2O‡ÍoþM½P£¢ˆämà¿’|¤TrãhâëÉ€€-`ª«‰âX#[[¿ÅKÚM?cžmý?Ó’¶/|†©2œ­è_Ù0@ñ½„pÀ­¾¦­rW Ö*W«šDl§iMíßgF¤+ñ`ÈÜÐÀ{øh ý IÖ#<Ô’t¾3<(† íRŠ#`g¸OÈ ?Ôþ¤ÏØ^Ä`E=’l롃ÃæŽ-B¼µøÛÍ5XEB™Ð»•äE±tn èšA)‡0…撚V&fk¿w›Ð˜³Ae‡õáŠ0Ùþ rQŽzbÁв ƒÔ\’ÁjzI ˸6‡ÙÞìS•l¶ÕMÛâ–"ë¢S¸,ÀUÅLmPQC:™þ™µ IÎ&¡% b´` föBëÍ…"­ 6×±Ø?ÅßB$µÿQ Ý+ýé,³’Þ2Ã5Þ=o^˜”¸Áo„X~4ähÔ¸KÀûÉÊuU§ÄŒh´Æ†€ý.–ȧšƒYï7äÕLIùN~¦@¾w…~ÐߢÀ,C`SícÂßÁ–±w,¨XäÁ¤Rqóë…÷»PRréñÇôëÍqííúrÓ mdÈױƟïmß‘‚=ƒÛya¯tqª$yæT8)ò-Ì5s&0‰N #&t삄Ú{j2hÎŽÅ öþB·FÅ@0óp¢û„þôAn¤’|¨O%JîÀ݆åVá¯æ æžJMùvD8Q0aÀuš^¢u»F±l ?xž–cþ±D´'iÝŽTÆŽ*þ ]„ú'ÚØr³ÜÙKªb¤ø–­$qz.ÙŽÁªÌ u*1-¿™Ÿ_þ9à   ޶'¸…ØT¹¨QeêH[c”8W'aã0刣‹‰l‘µõQ{“q# i!ÄÄ—ŠÖ)EKþöX±¶Ó Kî.kœ9t™@,ˆ¬ÞCcGñС G¤Ü„;B̘†ŽšŠ÷je¤Øš[©…º¶ãµªžöÆD|“ÅÞ Þ†€ù›¥Pt›Ú·¡o‚ç„0WÒ:R¡Ôèq?w&@zÕb^µPO¢$s„‚PÙ¹€®Ðv©©°êd£•Œ„_äBû kª(ç GÐHu‚@SC¸žÓ…Eð9ë+ÀiNO›î§u gN¦G!‚i|ø‹^AàÿP ©[®€à‘„BðÀ’€$q˜Øj³q„îÁ.æªBbf»Óž†½3~@,–%Ž@/w V”Áð^ÄÀ^£ÃLãÔþ[“ÀŽ¡Ðð1ûP†ˆ³å÷@qzÙÑ´”% \2Áøðâ¶ ô\6š—|(+³Àú&úšL}›… ŽƒéŠ9?½`lÏ',Nʈ”ëd“r½ûÝAµòFÁ_÷ZŒò(»è÷F™ ‰¬i >7‚P@á@:"ÝgdQ£ª×ÓÁ  C¤£¡#2AÅ-¢GþcŒð+Å‹  Éçs°dØ®Ö-ØÆÙb¶PX|]ÜêT)ŽÇEâR±˜ƒWéQ¶«øÄ1„A±æ‹oƒ#ø„ RiöZÙº‘îI܇\xH õ£·õ€N²LíéÄŒw7Î&äÕ‡|:À [æ&ïSÓ¬R$'{Ìp»ƒ³/¬{K£ÈüNâ~XÃ@dpÍxçÚø m*)¤z‡Â-¾vú7Ü`kÀÀä§?QªŽ#2™ýz)fü‰oPñ¤côð~©Æ3ðQ :t 2¿Ò€j D€m¥³]*m d¶ã¦Ñ¬×ŒJA¢ãíjâx” ðÂ~^3ïfWÍ@‡´hŒ3"µ¾þ6}¿>–ÅÅA•&ÿS¯2*VâÛŒôFG²X*&X(BŒaŸ\dQ©M‘µ½OS×)õüñ¼žëãå‹Í ›ä^އ•_{Eª_Sn,fñrŠD€pí2¢­Ñ€Ûé¶ÓA€ø= 1—m’s ¶Îú5ëñÖSóÆRö/ò#>f~kV9óèâ¥ÓSCLÏ´òô´~ƒ Â] xÞ0½Gº6º.‡àˆ‘ö¸öÇ*ƒèÙÂA9SÓ i~²ß°3ˆ ÷DáÃF×ðÑÔ˜èm ±Y¥±°ó‰P`ÿŸo¿Ž|…QœÞ¶Ã–Ú_ÕÚ aø¦§ýIˆ Ì›vè-×DâàŒO5,"*1GüƒÆï‹’)¾ ’ÆÁ·‹~Ü&Û IºnýmƒùE$8äH/"I­;„€¤–òDæµ§mÇ‘•ý7­â44¾4õ\´Üƒ\¡Ó5C–…»bÝ÷€²¬éºÒQ‘¡13o,*É¥å¼ ³N¶vjí \$™TëÙYLñxæâKå ¤Ú2»z9³£€k<]Lû (b0Pá ÞßPíïl„ÅNSˆ!‘dß1Ž)¼dµr<˜h ÓñÃÛÜÞP3PÀ¨{BPÍA‡€å娠­Ì(©c5¿žÛEøÝ7•^i¸M/’×âûŒÔ oAP 5Ìì3ªì¾`nôxøƒœ…Œ)ebSXá›l ö¢ÏÅN‰áð‹”ÛiñOUwÇq%¾|}q—¤´¬G nG;€õ¶9 m¡SV§¸´üˆ m ÚÄ(FYrš&í'l›´×ŸÊlD Ž^Sœ¤C[4€½$5(ë÷Ôv`woäê„ã'û@¤r çÚ>-ü=`$O’ A¥ÃÿKûðéò   eù#XÒÓl:²°SC“Bµ{À:@½ø1Ò ÕÕe¾F%®S•ðÂŽ!sgŠØx™Á•«ggÎM÷â5B îÛ0䌩nOÙaСÓ9`ãÏBäÆ Í1úù8'¾1ÎÅåØò!bC€¡Jc½œ‹p$¶ Šså¶ãíB‹‰•ŒƒpûìÏàÖë>A(°gï#ê ‰&{‰µ˜QôÅÀb‹x†š`H%•RìgPÏ•°w›¤šPÌžÌå{Œë›@} Ñ”´+lKUˆk J$qn Õ„¾Ž¡‰Y†ö<ÅÄÚõ‹„ý èr­dR‚h,ÐXÑñ|Fujv‚Íô»i¤ÚóįFpÞ;ë1—p0¤1 e,wû¹¦}Aa"h‰)]fÇs|‘&z.]îÁ5[ Ýòí{ŘÿňÁ;çò\ Gzcã¡t|Õ)0ýÃ]­ ÌÄg²KY²2;s^£A~…/©ù„Ú@“#Ú@1÷¶,€Ñ=7%UbG0‚#[Ñ$Ò “¶¼Ç»>sæŽÚ“pwŽÞ†uþ–§Ñœ;™Ẇ¼,jüI'Ü+¬T°ž Z­ÅF•~çG S€ä€n.¶1g3Öü@ÆÓˆÌ+õˆS$áâ 8Ù Ô2’ÐÛƒ—ùWÖÌ¡yïË -£`U×cð³sYyx•LÈ’Ót²}ÕþÔü…(K8þÙÀL¿ à 8`ïh¢‡váž´MáQ7~Óãƒ1ÁY^ÒhlÏî¾mžž¹ÿhý¼)è^§ÝŠ‚ÜèÀë PDN8™¡g‰§q %Œ‹¸‚Ñg„xº'áéqo}Ø  !$§ùhD¹÷ÐxV}„›%x@¸c‚`ë–sÙ÷0@ª¯A7#z¯g¼ÐÿÖ·†ÄÙm Cv$Üí’e&®Â“]¼0aðÀ _:‘:MÞÄdí…¾0hF§@ ¿˜Œ°?k7f³‘§Û¬Àd—›µð½p#2Ï †²½„t—àÔm°VpQ¬/÷ß ¯î¯»˜ Él;Þgå=K½gõÎÓe€=UÛ·ñ9ý<5’…¡Ö±Ñ>à=èfE#lì0 L ±h™kjì~HÏÏ®¡Q-¾ü ‹Ö÷DÌÂ÷‘§|*9ÿþÁ-ÝÞÙçëý†oÞªfã¿þß ‡åÇ!ɇI\‘QÕó±e;»;[ïnþÊ/½c‹ŸÌØå xŠ$´Äÿù]3ûÛ¡”À?D`oñõž ô Jy†ã=‰*sVgÀ[įsÖÃò\˔ƷÐ2¹^ß]Û²@õz¯Ž¥f–À§Ë/À«?C¾Ü³Ýâ%S@¥äÌéß”«߆,Ù%n¸2`á󚼚¡*1Â4ÂÐ3L|@\¤e¿˜˜&¸¨&‰ú €ÜmlìQJ#È)£L³E·À§9êuÔG9õŽÉS×:íáS 10ØÀ9ޤÿô{˜ 6Ü÷Gj³3†¨º%ÄåÜ{Òº$à$„ªðN‚ i¹î$@ -‡µíÄòðh˜d“ÐÆÀ“Mج É^x÷èM1ïföØZ®ìY¹¼V^öPIcùåŒ a`“µ±$6¨ ð\Ë/»Îl“¯g‚ ¡ŒMÏhºZÖ ¨n†snSkÞ«,ÝÃSväÀ@}Ë[ñšÖ(5˜Ûi‚Gb! wÿ?äNm1ø;ÿ‹îù¾æQ¿c6°:àš‹‘­Ièðpù~Yo–øŸ¸Þ§Lµ4¬}ãþ#œ]Uû()íÝqnÏY|ƒo‡4ÆiM1–»ÿ?nn]Ü]ñº£òuµßagŒ¯³½ Íå¸4l`~cPBƒSqë.jÀÌœ?àe¦¶ø64Θ½?Ú¥íâ¶.óöÛ›{ÝÌÊ\”{¾À.7°#õu÷àR3øÁÖ@€—B„"Þ(=pQ²ócýרkð°§•Q*ŽüñpYý=4îù—ù:Ä  n/Ã[èÆAÿpoÀ;¼Ø©[ ü‚”„HQÞ.ã<Áù DɧG øþïw‹D@6ƒRPH&5X²ä lºÖšÏË´}oëµ)€ he0Çâ П¿®Œ“ðô à_¨ëU޲ßîþå/7ZŽgdðãqŸ›§Íº‡¹ò‰˜ì?ð:Á(`L;@ÐëœWM…ºÁ”¦_ÏÓñÕ\@æî¼~~LÔ^©A0|Š{`¦- |K2òuh*±ÿ!¤×àhYKV¥GÃwm¸Ä×SñãEæÞÔd‹9L³PŸæûŒÊ 4ØÑ‚ÜÉ…uîlS VÖ+´Ä¶Ck1æ•ÖÃ"“š¬ûz8ßk?ë.Ý/R’6gÉŽÛ,e|¦õ'ËŠÕÚ Y–I7bãpù˯ë=l¶4§ÀÅRb|÷›¯ñY¡ÌÏtOI{í¸œËH-²’ªG´=¹UVH„È”á¨La4Ñ'áÊ„Ó[<\rÛªOÖ>c™JLÄëâdާ…×áÆ Ljkö‹¿ÂØ–²ílQbê‰íñðÌvš ›4d/D3Ñ&>‰¨¥4ä¾j]t–¢ölê©#ý¹m-pň٠μ2ºË{…ÊZÒÒK8¦X'3ç0 6Ö"iok*ã0yy>Ф*®v°éå¶µR•½Ðÿ¦”C4ÐÖ¡¦HœŽ ¡óqÇ»€VRÝõa5™n‘½‘Kbž®õÇ߸±ÄÝÓRØŽ˜1w¢ÛxQÉÔq†¦¯ÄzÐⳇŠN)¸Êîû¡æMÛÍÚs¸¸Ä/‹ž¤WÓk¾6!ÆAôœîf Ì#Ð^Ö¨˜l]µRjªî‰’#:ÄL;æ¬ÄÖ—u·UBð«AËrEÒÁ|U±²¥{Á8°··:ÛÁÔýàveLÛ=5ZšO¬¦¾^_t¦©vd¼Of#®­”L÷ai¬m’нƒ1“ñp|¹ÕfÚ}yçU]'üÓQª|£Âfà÷Òu¢Ä|e4êFøá? Ÿ‡/qêD›t‰/NQ,e8 6Áµ&ŸŒ=`§Tv¢08â"ÈÓ)ƒRð¬.àD éJË&6/'¤ýQU aøém•K»ÅÃ5lZ½³Ã^Á6Ò‚ÛMCx["5¤õ4õ -¥8äæ¿„ƒQ¥ô"š¥Øûû"ØÒsIúÍ\ä[GÀxã Œq™=˜s9‚R³r”Ó›^Õ»SgÆm« *D}"xÓÞ…v´.ä·ò@Å&)‚?ÌjT¿<°6F-þñº:ŸÂ>1î T;Dл÷ ÀÈš’¦Å·Ç"€â"T ˆÞ•hÈ dù誉3D|˜í€A[Њ9È¿µ0_¬Ì dÊ âˆíÂÐæ±ØbSY“ùœÄ7‚ÍP7:mÓHô=™‡þ| €™súÌ…‹ÙË$‚BZ¨ú'AM7ÔLè&v9ÅùCnïÙ»`¿PÙ0ð·ô~è 7£4`VžzxÅeŽ#&¢.³f 2›¾\¼ÞY‰B‘£G'øZ>!„¬á€ØkÊ—þP„ýëµIŸEi±s±?A˜FL²xøG¬ciW:iqÚRD»AoÜŠÞ<øújÖMå·åCEx¢.áŠäÿ úQ‹i%‡¬œÊd™Ð2ž©D¼©/#À*þ•Èð÷DÎÀup’¢²Œ¶éæŽ(=û”8Øv3V{/éŠ0:`ìôÖ|q¨¤‰ç—îÍ8=¢sä[âWJvØ(qD¼Õ´…’[6™í–… ÄØùµ‚D TÖˆK‚Ú}O32 D×®2­÷‡²²í䨹á?ð‚ª)ÔC‡9Ã’b‹©1‰ú²å½è9ÞÀñÀ6i•Ž ÏˆÁ f…HµËtvb°;§¼pÉ:Yw‹`¹ù;Ô1¨Æ1LýVVóèÖ ñàéS@ö0¾k[B‹S‡W+ëµ¶ DªN¸ë5ù9)(;-iѯœëñ>Éß[¦_ôœ€ b83+•¯^XkŒfÁã |¯,~Vð\m£»üþ&>°)à( ÑŠáFÇ2«D¿J«¿4>(;»¯†c~™L å!˜)™|Ç6‡‰VvÐ÷]ͳB´rÉ`ÒÛ%†H« ¼‘ú%³$?[è.A‡úò‚Þšœk ]°ˆòOŒ ?m0 c)»–.õ 03E» ç”=€T([:±ŠïùfI‚»M=—g}ÔÄ´f’–ísäTq.«T  ŽoÄ yu›4±¨¬ÒÒ¼õš<ŸQMÞü(mAc>7ƇÔi‚üåw~˜ªd* úRFâ›náþk)ÿí°ÌÐ_VTõ:„Ê cüŒÑ!X!³-y°fûZy¤i‚¨Öámt»Eub׋u Râ¹]+„’xWmÙÅZ¬|¸Cb3¤GøáZ¤G ®SDc<—&÷¨{6[¢Ôè–ƒš•lîø@¡ÔsȽSOöŽ`$U:àq°– —³ýÐ ¡¹9UþøQÏG%ŒR»´}âEL²Æq–q>Öfµìsúim¼PrvJ9`j\xp)E¯ˆBÇáný%}aMÿ|ïnyÚ|½¶&f,˜…6+ÙË|[8¼…þX uyîK$ð„]™¨rtÑ÷À—m¨•bÇ]­”%fì qIïXˆ¿_îŵ¿9ø÷Ä‘›6ãØ1jiFœsqœX å¾’’•=ÂvŒĸhé4QÖŠñkäê£($t7Wñšâ@$…áxÿPtÑ tGd£382öA[ñªÞ…õ#Ìd0Í”…~ŠÄèHÃøÝlðÔšO\Ó9~–‘Ï ·ê4('ðt99«B_+(áà 0hü§ ¿Äˆs! L®¹GÒ¾©sÐÆ¾L"‡vü4"‘p&Òt —ÎftÖ¦‘/g¡Ï;z·ô8°˜Á3ã5ð©ñ<©Â{%Å­Ë]˜W>X8JÌ9!l–OåJóNE†»½'ªÂ{Ä\Áã‚Yú[w!/tù“3~‚˳Ô+‡­êAÃÝ,ŽÙ&ÊNÃÒ^ªLÒE´ª¾‰Ä€´›Œ‘93Fm‡†©³ZäOZ<‚ 0$Ë‘‡fR$x6#þ–ÂÂè ¿¶ Ší-ôí§ˆÌùonµÉ–JŽõèÇ#]‰€aë®> ´}?û …3"j0”œ®òI …ŽÄØŸ-Ä5êçÁˆÀÁŠ+âw_ò †ž<ANÉQ3é†èI¡KœùawûLmÐBÚæQKnÖ$Ù»ë4Àör’—쉀þ¼£Ñh:ízŽZ‰9Èðº(¦£ûbãÿ”à i ýí'"¯‹ ÐÖœƒLxx~1X¹}á_ÅEW,ÁU‹GOL-%êQ ×L1w¤ºá¢Œ]˜²=‚‚eEŒ­GnN(^•Ò ­ŽñÃix™0ô„¼†ò`1  lœÿ+ ç üÐÏ¥!꙲¡¬ªk¶ç´þ6Z0qn2 ;„püqñ @ÆÂš§oÑ}ö¿A˶ó°É<8\ŸJÂS=¸l3Qžö!’åÈ<FKÕ@¶5Îõ¬(²%nJ…è lÈ{lwa±Fóá5ìõ„?@ÊIGÝuòâ±TƒªðºF 3ý²*ºÑ¬™ªöLÐKY½/RŠí©˜WÙ–8â·ŒÉÚ9–°ÑÆä¤:'ÞÛ”túkôa™¦‚ξdß <¶pfúõ@æÿ<9¯ApQ‹WçÆùgÝä0 ½ñިȎœ¨ˆ„ÖtŽÔ‰gxÔÒÒGèù<ÃÿópJ «•“r<蜌Fx5¤F<à@°¤ä=éöMeŸ% ïœÈ5ß¿‚5¥«ukC Íd9¢ˆÑòBJÍÊ+å Í4 AájAL5rÔú v.*Ôt èëbGÕ¿¥°æHœá.Äy²/Þ£@q¥Éàé bÊ>ï')Çú?Â-v¾ëÄö¥BŽ‚{¹2+Û®©1°° Zý¨âÀ¼EMkúöë§£4Ü€•·¡TKOÉBťσ€„‚ÀåpÝx$RÒ.X]ÔÈÔjÜ>3ºØ{°Cš(è †*í&“1¨í‘[SÀ¢DÉ UsŒ¸ˆð ±M3ZÖûøÕJ­xœ_Žxgèg$»J‹±MA“§Ï õ8â"P£-ÀN°Z¤ F€6làœ¿$%Q‘ñ¤ÅÃÃyþN`à+\zX“MÀët&ÃS}cà&rƒµqe7¾1!,p$’ gÚâ0TæÙæ”BvŠE¦.)öð[s$êuQ$¯Cb¤ Q'¾#sÚÿAéä4Q,}Ò¸‰@ÌÉ&–^Á9sCº@†fp °á~x˜Ñ!ù¼QxXžqz$:ƒ#oÒO×yô1f€ÿÂVc›K€ùWEÖcL1éï>•zò`Ð1Oê=Ø×¿ Èž5¨ï48³E ûPÿ·þ3Ál“÷Jí‘䯽—‹—UÇ"Ú˜9<5%ƒ¨*pˆ[®ðA´þÀ‹ñ1~L:ÿ”UñM¦„/É1ÍÇø¯Óf®EY%1#Gß²`Ÿ Ê}y»‹ÒµÚŹɱÛ'ûüެxÐ̓¥OQXàð÷Ð-%æŽ"‡¶ñŽÜ‚zÄBÕIÃ[ü\œ–ó kÌ4µ,40mF{ÀˆšêtxBoñï< è¥QöçĶSÀÔªoÂ[ìÌ1?FA‘P:Kò"Ò‹c“Òv;Iƒ"ê‘PÔ3²ÇA/<ÐéÃÉØÆZàõ#k#;Üà¸#!ÆÜêØ BX9víPYÄ@¥/€0,AΗikÈÕ,êïƒB2Pë_0¶lûƒÀ"&`IE­^I'bÔB×އîÄ}\˽Œ²n¯ÚÂ_ýp ?Paß&ÅKÊA¤†iÕu«ÁB(«çˆî†q…S\.¸Z˜À%k:ÀëÐ9ðkŽXòÿãñTTônO9!¬fœÐž¹,ûe ,ºx+ÔW^IøRìîD(ØKB´ë1³ß.Uß+ú­ä¹løI©äàÆ,DÞOÚÞ½ÌÕ¹”Ãy™Ê B®ÑtŒj@ïtØMd:ö¤ÏBc8v/¶àdL~‚ù¼°È½¸¶öݳþßòp-ñcú(Þ€¦Ê){ᾨ ‘ÔPcúKìÅ‹=À¢Ì; n˜¦ ìÂÚ`ÏYûb$/‚÷Ø\ÚÌ Ø({DV´ô@ /ä3y' 3µ²Hó¦…N1>`Q“zĞЗðÉD`Ÿ¦úɰ5i)šEâ¶AÀRÄ4¡ª€—âi¨ý§|Åqkœ?™,tv…p¸qBíÆðôÝGWÀ-ÖezD·GHTA¨k‰ú417ÜÝ„E·aF˜òÎ’C¼TøMÓz£¶›õ9ýFJ` Ð :¬#újŒ×?^7æ xò5êùb$ \ ౦MêHaÇ¡`œžs¥Ö£›B,årSÐ#J!Ľ¿ÁáÓÏ:±þ{ñMæk³ïiæ7¡¿_üö`¡ÆÚD8 9(w`ÚWwÁšÙyÔ´5Êrñ ÚÒ±E0—JkOB3š2\ñላf½¢Š8±?@ض¬høGõçèˆ×¢UC|ðn]%áÕÜs(6¨•KyâÄKßËŽE‘>Àþ cÛë~`ެj½ ÉËÈ·xûóBÖŒŒñýM9¿ê¨)9ìêŸbjâ€@ƒB1ìJಟ˜ˆîØ<§H ‘8ññr€4´: …qM{fËkmî&â¶ì2÷¼sIosõ©tŸÀÂUq…}«F«Ûÿ²{JDE܈”l'79î”rÝlñKÿ†ÇnÈßÇ`ú#€ÊIhEe ÉH#½„ÿ÷þÛßîlÐ>f}ŪѠo̹]? ð@U086jâUþ‰Éà)¨]ÞpõëF™õ¾6åóŠùÓæ|ŽÞðÝžÒý??2¾O_÷«ÌtNŸ“¾`aIMæä}Ø@Œ:@r-IaÓõ±(*^0Z÷_¹z[YAmèÕÄ–!Ú‰g‡LH;rµ¸5âLH" a;ü?îK¼v‹S P¯;¾²]@êòŠtP*<ƒ=ÈÒ.ÖKý¿w“VIÿ‘MÕ8$sÛÍÐþúQH÷qÆ^˜k/AÔJ`Ÿ¦$a:Mj˜lôæx R%áÆ¬Ï•´®½´®MBR˜§˜QÚA£t)íú3†xù¼‚ÝÀºŒÇ“¸dã{¥Þã%”šÝ!‰÷:]7á·N€dä{h47sjcÀe‚ iHÞ¿A@)&sãËþÏéƒïqG0‹—úˆCÀ¨Íè" ü¾Ò!åø«I¹ï³6— Lä´†}×th£Àå·ùêƒÏecËmÓù¾KÿÅÀ˜Š¬åày0GÁTŸø?ýÁµüŒŸA Ô 9m߀F¶JÙfn’*† X@Ê|cd¬ôˆÇ¬|ù§æ…²>h½Qȸ†$¹;¼²);øql&Û'¾û— ·8 \‘[&¸ "9mAœQ ábn š8M'ùüĨ —{“±–1ktvøžW·¬°U †â•SŽN¸>ñ¿‡»\‰ß.¼o2˜7ÄÜt>ÑkÔs‹×îAÀ  7&M ?¬X µüöKl½xYÐ}ÄÆ6ø-›cý™8Ý_ø¬%úÁ@l¦4D4ó¿ÎÁÒæ‘Þüß\!€àhÑãQ¼ºŠ€ô¿Å»'²t—ü±ü~C4`ÜŠµC<åþ þàž–Â€ðPA!…ÿ·úéÿ8ä}Ö/_õÈU6|5êí“ä ´$åàÉÔ:ô”h1Åþšˆ0B›,q¨`ðhE·Œ‘KþÉt>€äŒb¨Ó…ŸkMø¹ÊO—ð¨åœ :Ü: ƒÎÆÑó€lF¯8[0C]ÿŽa€GÏ$øh¯ÚãñëㆠûÅþÓÊ­ÌÅQúîÙ™niîH ¶<>9ƒ‰p©õê>^ C—m.zC8ŽÿÀ„xÉšx÷îTtAI8EBW<5.v}¯öã Jtß—¨ö:QÒ€sѦ÷ÿälSðþ9™ {\b ¶ˆåj½ýiDôI·Sz4M襅šÌMݾD»P‰·˜s¬9_À#Í·{­ÿÛL°i[ÀÀ?=¼ô5ß‚FzŒEg»ï®·h÷…û"®Œ1¸3]oçw˜F¹§±çØ:»–MÛv!@pÒ–Üà!¡¡Vݯ4c×ôü×í5¿÷ž;íM@9ÿþ¶"ë·ZÞ0[øÄÐDí5ºëêlo‹coâž’"©ݪ€ ³ñ5AÚa\Èè(s™ü@Œ[oöv¸‘ؾ;æÄÚ-:þDæ£î/u€!AÖRúGÁw_úZ6B<<¦¦5eÀ¢Õðø=ˆÙ¯ÂJ6*Áº6àY± Ô[bI™KGfÀD7 çQü.ªÞ3úh3éFM>xŠŒúŸƒ}Ó+Lj/(ïRdl|cË”$‰¥®.^õÛ-àv óìÍ™wi 5mó C¼‰¼?ÍTSrZ©×V¦an”ÄÀoŠ?öc?쯛%—e–[=Ñ™{P LËHѺŸEÓSï»[càË£^ÍIwÛ”|`+.ôŽžÑü׫ù#•øÙàwOõ²kD÷ ˆýWÇ>ó­ÓecÈŸ“|¹ƒ;I*Ü>A`¯ÁÚKºD?¹ Bzª/vƒÏ Ù [W<ð˜÷ld´ÖŠÏt[6OZ¢mT£û9\’Ö¦ÌÍ׆`VTI`›)†ÀÒ ¿Hñú1Ò ËrœY“õ˜‹Å¹Ò\U@?a…+0òµÑï MMÉ€%Ö|Z£© ZÄYšB2MÝzþ…: "P@ý)dšÏÃzXð±uÞ¨þ°6šô#¤'›£ýWª)èƒòNÈNÍA~éîRŸ8¶Fg‚ =‚DZÀ3Ù£¸C.Mm3ÖøYŽÃÙ¢è}l‡«}]=# г~㙘Ao´¯í‰ $MQ Ô%™z\‰`m´6üàeky3dšd<21À^bFdìÁ_éôп¥°²*:¨Ák<2Û?Ä€Nq6 '>×¹ð Uc±M¸xÛXîb5&¨ ô5¨û=a 1ó:Ï)B§”ßsV„—!¤6ÐÊ`ƒCO.¼ \dcêÈü·5»x†s[X·˜âbE±jYt #œ®ð®L°5¶kf–Ú2·Fcá éóvä"G¯}kfù &Z”á\Qa˜Z#š¨ÓC7ƒp³ÌÆó DM«ë6€¿ÇhCW\šyÂ0mäâ„fЏE±™Fï34 L­Š?pÊ8þ½i˽[@(Ïþ¸,Sûa6»äð1R¢65)p°Jq¸ý–?Í3Ç396=3„•†¶ºoc#‰h—¦í¦W½bµ¡q;e˜%”m̽ª6æ–ô šä0(Ô;Ú~s,.§„%çD5ßçè¨Ók@i¼mÐ!‰ûŠÙà™×òN¤ÉÙàH·f¦ŸìlÌé(è÷o¼²H7š×þ)šì„IÇ^%É”ÙàB™^Ô2Æo`ÔíÈG°hL,_’úLÞt m?‚` Æ o;7b.I±dú–ƒ©.쵔͕b°õBDˆ ‚²aDf/ðO#+∡/sY¡Sñ ;:Cè-JCzl–­“‡‹"QÊKô§³6‰_P7[™9ÿ˜Àd” ržÓÿu ·˜î„¹¯¸/ݳAžäaìAóh%âÏÖ ÜŠû¢wAá|TT/ŠJnqÙ¡ãÔRă‹ûä,††Ÿ/v·,½Ý8)„œ?‹cÖ‘P¦b½Á+Qm‘ƒAÐùŽ!µ´’ÉÊ£W‚b|½æk¢jL¹4ûf@=ÄX‘? ˜ óŒñ` Y-5ð1Ž %U4‹‰ $Uàjb±°LR“ß›10|¨LëðƒôO‚„èø…€U'áVç¤Ê°”(K eÁhXÂÎâ´¥ŽånRïƒư¤Gp»-õq+¬5È7ú8Øü 0Ð˯’ Ì_ô?$Å^*¨ð‘¤ÔWÉ賎 ¸rO¯ƒ!¿@³­¬t {$›‡àò¸ 4vž¿4 }ž¾¥øºw*ØôG¡Z"ä’š°“ªØSCc[òKßö¯<-{)ÅÙ2Y‚ôÆš{€[¬4•zˆLu/àT<Øœ2tD~ˆŠÖ|úlfT–NE7ßR^§ü.åš#Ì×ÍÅZöY+ëˬ EAºÃ<³þC­&ÜQ!d4¦&¢7L#ÀhJû®›±øø°Ufsàäa¹.ZKÃØ* uNiȲhÓò’ô§xJ•U¤ÈTÁ*õdBe”šÙûAö¶àMŠ¢™xQŠÚi†:†òËF˜\ý ×Fgö3×b ­\t$²wÿi¬ú6Ht£¡F ^G’àcVƯ‰”-)Pšœ-Á2iwF£z š‹,±ð`K3“`¯/[ü°1-âÖ”q¨QŒÜà%#KGóØ+Hø'Sf “Æ6?[äHÞI1;¬\["øNÔ<±ô*lßi…u¼% ›ýOÌšÄh² uïäy|¬ïÆc Äë!}ÿÜPªÁZÆk^fKø´‚®JR))ê ït˜jÙ‚?[ z?³­Š%—/å*¼ o /<(¼\wãÖV‚+@Äè–PÞo>ƒÍFÈ먯Ũ=å¨î8·þn0èRh9?â| ,Qc›i{&tƒ×ÝrÕ5”âþ‚ù˜*„jM†ƒý³Ã(Ô¡1,[Dnñd­ˆóÃÒÿ&l6!@&mK:èBB£ •kôâ?rÅ^ UÖZõ¿S­dmc·×I¢*ä‘Ë›œ$£0Õ2ÑÐɾMÐÿ‘‡c š ¸ý£‘Œ†¶Òlð¤ÒH"\Õ `„ðuò¨ y´8Ãåp¶±zÆu:%çK''ê3âIGbjMÜc+-Xà0CZ¥#¤˜$t›>M6F`¼Úlö¡¿¼Î¥¦lbÜ"ð‚»(]©¼¹Žü•¶–âd—§é¹¤+sáì_K[ü,ü‰’`À\8`,:i£ooû$Ù‘§D¼•Ž‚|ùYn’JŽô¡Ð®,þb­Ñ~š(H ó Uæ‰o‹Ð¾TÍ-þ<íøìÓbW@?ÈSÏÎY*" cÔg«püEóÚfáz¶Ë!v‡YcÌ¡šæ¦øÞ2 8¤aÌ#I¥½¿ê ‘ÆäØI.\71®±ÒJÇü¸°â¬KöÃK`ÍŸ,Ɉï}tLõý'~ÃÄÅfœè'kªC ÏTKõ•hŽ+ç1 ö@y Þ<Œkº[Ä/惷lŠÕŒð5ðrÕ¼Qøºìq L·ª¨:[ïÁ2tK ”#ð:ÜäFíI$,`Æ;f( ùV€ €T†ú¬!ä…üv+±1“ ÇÑOpËN9©a€.­tq½@â¬K—Ü}!ÂZx}¤À¸p©ÑÝ ˆ ‚šfb!Ã$ZW€À/y;Sá ýMÖN¾L&—DÀ­/b¸•‰8†¦Š6ÙÌZ§™©Lv5fRqKlÛ+Å'"µ @j8¸‰ð3y1`}Ï:I‚‚{ØfY¸Rh‡Q0ÕT_ðt÷/üaxÚ@{ŠWM9¸(N6 cì;_Rÿ­D0Í…«ÎdVr&÷mø¬[Öç<1Bý«aàØ1½Ø7 å^ŽèAWñÌî—|%1l h¹ç<>FW\%×q ŸÖH¨ð™€·J7<®ˆÇgN¯[È(EïÛ`o•çØA£d±¤ê–ãÔT“‚ÁÔ•ýÃÝFr(ººëÿ«DmŠ1}B¼”ŽœA\[±öä„î¼ãt}1ò:D@™b©@Xù¶½ Æzé7Ô~¼SÑÆ„¶ÏÝQ1Î`äÆgr7|=ž{јÕðŸME¦:Äâ¡n„Å"À¶ùñl×>¡í,ÖhBš`Ó+¢XžÐ%§Òæº|†Lá ­yPa_µþ6 Ÿ6-Ï´¶?£QMmc’b·áWˆXx«ºæƒ•úSc††—2^dJvå¦ÇoêÈ©8%'ç6ýÁQÎ?r:7uŠÈ¶R€i#¡œËGMöG0E¸U'Û˜pâu1#Á Ùåù¯%2ü?Ñ0€ÐÂÎ3…„£}¤œÅ÷ ­­AÉÁ” Q ±œÛÒø&#ÞCW¹%¦lÈœÌ@‹Atœ„i†2ÚŽ/,#†º0’Íä/]™öØ èvÄ&öÓÇ)ÔÅnccVÀá¨uGÑ® ƒŠ•šºŽÜ‘2 {:µñ1xfb`RãQp©ð`ò›¿\'tÿi¼ÆHžt'Â!C¡°eêïú¥!É[¹ ÐÄåþSùHàe¤iD“ì¦UsÚü!ö‚I1k ìuéZí?Á“j¾ŸÙŠF‘—‘÷ ôÞg"CµÐ£.±9Ï%šÿÚÄ=åxV}z˜`ae° ¦¨WÙ¯Pk€ë·ÅÜ7K½`Þx.ƒý¦*„ŸÔÅžé{h“"Ú«ƒ÷ åì¦Báê>­ê }pvy®…J?âÔ‡ é÷¶€L5tó$ŸTÈ¥ç]†§dß‚‡ºú´´]G°5nQÒÌIÐâÅ~½0»ÄÅ UÙ¶ fûͧü,ÆD7»n4ƒ·újÂrÉsh"­1ï­HSHwtf»œ GEÐÑ›ÆUðG*XÙ†}îtº i bd¶ 'œ…)æâ9æðG[à=3MÞVÆCõC`pÀ}錮åF‘:ŽZ\"AÐd­‡Z|r‡äŽO.ê±¾ýÕÄBaz<›0ÞâÅäàŸõ«Ô|ÒÚNÀµž@²—“{ÀF¢ÓÿÓ_¼ß2à$ ˆlf®ï!Î"êh6dmýâ/»‡i⬬B0•Õsmð'Iõqg–H¹±„%ý…AïPíÚÉ GWâZ9X yUX,£˜/(üX_îÀ[PIº…iÝöxT)Â{îä?׌^ú׆¹˜C :‡>®xšÈ¶òØ7¯dèH߇‚-#ÇȃZíe;ìarÖ&•p‰#)š¹ÐÜŒ ¦Kfzg;°©HÝLìÆKk?»q ¼1å€AÌ>2Q1:X4G·€¨ôS ðh*lR4ûÒ·B¹æÏ‘«$†ND̳7Ñ>,H.²Ú’ÑÙé>¡Çd³"³kˆrñïñZe&¶ðwƒ‰™î1ZúØïù5‰˜šL„]2ÈÙ)²yï*2vcaæ ,‘ýÐó3›!YLîs6Ž3äÛéW{`##dá´•D}õ6Ï"Ñ6X{댶%–ЌƬƒŽ·Õã%]#/ûG¹nÅy#oïõt°»»Á¡\øØrçÔûÀH8!àDúB)éë¤`g¸QñáyËNk1òÇF/ —¨"ÀÈÑ èÈ[GÆ…oë€ÕißÅx ÿ2AEù™yò‚Û ’s‚eÏDŠ`˜¯>ÝÆ_ÿ¶‘+œ²<§óhÊNù1s`P& cøÏÕÄlŽI»ÂÊÙXÇ’Ö§WWþÔBãÇÔjCÃcOí ×ø”ü\¡•®²]Ýø^| ¶ý½v÷ƒ¾2U½àíÌ£/Ýì×¶Ðu~6λ¯¬¸ÿ‡ü`PêgàªA”ô<’ð¨Es4󀣕²4»>4[@-<¨½´Á½{ŒóâE‹–ØØŽëvê_»"°EþO“2úO¸E®À°(UŽþ®OÀy¸8c1»ãïÅÓÉ$ƒŸü7 îü,ût¡Ú ÇÜÄð$Z4O8%æUZ|¿á+ña:öxìüjêŸbƒÀ ߟãû¾U%gL5á¸ncΰ(*'Ì:oŽäåŒH0ÿöòzêv=ÕôJt»ÿü~^MÜÍ/ê‡%£9bšÕv€Å#´@ µÄK5tbõ8<ë4snßôHb ›IMáÁ*}/S?aK£+D î¿ „ÄÙ\ì„܉7e5wI2(ù xP§ëš=ñÑ7™Ùï§ ŒÍHرa£Í¦é×£ef“ÜúdHŸÃC(¦@À—{÷ßZê¼*ƒÖþl¿»ÍlÞßæ»+HØ•ÀH´œy¼Ä<­ÈeKtˆ@‹pXèËYxH!–IKÅ xj—´ˆ¬^I@1zLxð X­o|ð˜éU÷š4âðb .å¿ûg±4¥%'ˆR8ÝHUÎGMAºM逹´FaG”:”Sf9àÆWÔ¨B Þ³_iŸ<\}‡€^¶ç÷Ûþ_ÝîÞ÷vÜÜ%CºÿŸ!ð<¤/æ'Åú¿qÇþ?úÁK_À-›©ß¡Eô$tqr‚ábì@„y[¡-ìÄk‡.ª‚Š„€$$†7ÞxôWÁ{ü€«„Ñ| õÿ¾Åµ·;=Iøø}``˜*yBC*Þ5*§ûÃðnè ßçò/þ3'²Ø&]ð½DòdO‹“•¬F¨°!B£f1Ê8/_ò鄯ýøo"ˆòov ‹wzWÛ¾á ãZA‹Am›ƒ¦±ÓŠ·BÖ8¥°a6ý ær ærx›(AQóLb[ß×Szú}›ìCÖ߯ùÔI ×\‡A=W¿Çœxè'}þ#9;‰ÐôþçÅÁ´ eÀ"¾cæ¢B°LØLAØDœÛðǯ¥ŽE) ÉõeH«„Û•‹Ð©_k·žU·÷ñ– ¸–cPðp ÈH› xßÊ¥œŸ¹ ªÀwÝÝ·üZ§. ¬BŒxiÄÌgÕîïq¹^Ù*à|j¥D?ÀÜxmÇ$VZ ã ºƈn€G¢P¨¨F<2(¸#H£Ôˆ¥ÈÃŽ6WVÏ ·õz³Îêæ>¥!p…‰mëÇn€ð2†NM†rƒ`VÏþO=ë‡;[Ø(;ýúâ†u3ùæ4<¢ö§ !4•ÀÔ/b¤5݉®e<îùÕT§•"\[×øÊŽ1Kü¿öû޲ u]ÅÏõNµŒg?Ï@¼†'-n ·vÖí·d€ÿ]—ÅMÁßüf3ªÿ˜Í{£›½¼,áX¢BRÛìƒâ¶[Ûà6-¦/³†&ø8ÖÞ!Âö}އã wø Z?UZk®1ݲk]²d¿ôËšiÇêVɦÀ-þx£¿Ùpø›Ÿã Ì÷øoÆ,D3†³ülpl ›ž %Uu ­ñÇûˆŒ†Õ8sõíX¬*Q„]/òÔ,†Ö•”aà{üÏÀ@1_’þqk¹âƒHLËõ Õ*«ìß6{{E5ƒÚ;+Lß”o;ÄKiãOö$-Ä«ÉR€GSj3eü¸×¼Q‡‘à,S, µ†¤bÞHïP‘E{¶ÿ?ÍŠ õýŸ*ÆCÄÉbÜ•6䕾UÑ>j®R"ƒƒø?¦^Ú5fWãµavkÑ%Êð­× …GîÙ뙾&†ý@ñhf<˜RÎ{Ø]vÛÆsà«Jª…C¥‰ç+ \“Aͬ`už´Ñ4‰kSVÜn!i­‰a\íë}«kĬ«ûeöë.—_ÒhtßOÁûÂ.ÓŠAÿÅx}ÆXÁ·e|²˜lbRpäŒ*hno+YWì£MaÅ"Á™‰Êu©d²e±V¨t¾KcöblcUv\Ôä«ôkùñ)žÝÔyÚ—³Tn€tÅÔ@™90‘œÍMID^Œ}± êFŒ/ž1¤29¡aÈpµ(£r,ÑÙ®Ý<Íã„JÁ*Ä•HäÆƒ§ÚÏØØå"lzåZxÕJ€qäÁE2Àü×ËŸC¶1£ Ö´¡‰B3“‡ž–‰¾ˆJâsä7ù•´´aîìJÔÝþ†“A©¨Õn´ð­‰Ø±–5ö^"÷3’SÄÄYêf-"އ|:å85I†¬®š€©´²%ñM+ÿš0lƒíR r 9báÄÃ!àr¯¾5f«KÖ~f£|þŸ:È;.’š©ŠÌé\8–ÔŽ’VBu„äp¢0y‘°IZØôŽj?àÖR!¤=–€ó^¡/búÿSUˆ¬î¤·@âQ·–.>póÏ×·5 Ë€Ô=âÐø‚yÂ-é¿Pcõ|ºþ¼¨8/óø3êE/Ú qZkžÂÃZ5†ÉE`±ð+äYzÿLÉpE¥)º˜Ÿ’U—ë[ЇAS“ŒÑ£ìL˜p+Pû-}%ì[9gb*F°z²Y‡OJæÃ 4€Í7{!µüѸ…å@¾uÂE0-åœòP‡:'åà£d‘ÒZ%ØsdV:ªÁD <(Ò)ãaôK$øù#eŸ LðS°yh±¥ äŸ}Øj8‚‰’IwþšxÊ`ÒfŒOÙ7gÉâàôòO0”¢¥]"‹ù $»f$Î+ݲ԰í®Ê²¸Éu“©3#Ç+T{ÆyÄÁ 9 “šHŸô ™ÉŒ=Й.|4ÅHBÉŽ f.Éä©ûì òoÖ¥ )c5^Iîºá?DÔ*¬bŸý˜{쀨ÊJ>?L´ó5t2Èì7k.îTbåI:Xg½Ùã(B}Ãbḱ"Ú¡ï`#àÕîâFt?ûÃfcÎf뛃¤ArÝJÆCÕ7…aÕ µ¬_úQDRhÀ M^#*WÃr’¼­óÙ &CÃd½Ä®«Â—SÃû>ÀOªß¯¼g¶ðT¾2`l|Lá88a&´býÆI’E`B%•-¼-W[M´XèŽ?FÉ)34 wW%ê2ûÇíþ »8<1ôuì ·˜‰_°z,RB^/bëQj{!†( b¦sÁ/Aï$¦ <Ûbͱ†„ãÑl¼ñ )¶}‡1S±CÿÌZ*‚©Ù¥-^pY6\ÑRq(Û·FøÂ³+Kˆ¶Røà²ÜÿÁô°zÙüæh ƒ1 Åò2ØùÁe´“3k)7zšu§‰†ulÖWm+uŒÎJ÷Õ@TˆU¬(%öÂæ+hÚ2_2æ•bqsxûÌ ž§¬3´‘ ßÇ#X^+@uŽ“¸’Ù£dÔX»™j/D¶|–?L23D3nþ*Á†DTk…B.uõR z‚Óº¡l…ì}E¸àD.š„zUA´[©I}é•.† óâÌÍ@Oq$V+p¯YE)a¨VœÇ<›h+F=tÓù4äVÈVO?þŒd$‡'ŽŽª¶‰c4èg¨ ÌNYÿ”ÀL¦¿›Kö—HÅü!aĦ5ù]DbÔ‰dš³e?ˆqy[0Nè˜il £0ë$‹¦Î) ±mQ ‡§ÉXÅ#òèÜ—Zèôp^KB¨ º‰ÈË`t…Úg=ŸÌšLߦNTZü^ „®Õ¿„ï]|ÀÃAõ…#‰‰³)âfžE`©à´£ÒèxÔœ+’˜tl>;•¬Pˆ¨ R&Àø&T'¯'¨´âE¾®¯½g~ƒí %Ðêd(öáA…ñ“%W¯]³ž¦óÁ«Vè[M*ù3a`bùØäÜyÕt!~d~º6­ÍEi Œ¡jü%`/Jf¶À ü3íJ>Œ"6Š/·ý Ãlýp¸ðzˆ Eé+”˜Žžª€æ”\œF'F$v´À»_(€±¡{’¹ëà“`f=¶ZísØB)nš†!2úÙÿþˆNŸ˜0Lâ:+Ó½xH“_ô>Q$‡’( 냡SIñP´pÎI˜ÞCÕ$ñû]¾ÑGÄ8PL=4C‡k’Ì7–¥&L~@kÈNâSå½Yvø(ö’UÌ­Y Õ¯§ÁR'÷Q4䲇ù†ºPšN$mA&^À’x˜|F¶•ƒF4Ã^ÆØj µò[2leôà º,(™EÛn_<’£¯Û„ìƇröS+„þN Fâ_Ȉ B0“;óvÄ:š™Goáü«A­/QK2#ÄáWˆ½Ô”ɘ«Œ]‡Á;¡¢0'[­.+t¼ ˆ9a®}£"ª{5‰¡r<·©:'1W-aïlBéUTðR!£![Æà;‹Vuà*j#ZJj#cƒ«0€X^°ø| 1-Ý1T[Ì´Vg/”AÒÕ(¦‡UÛžœ*3šZ f;Hòa¤©ìùà>¹A†˜s»¾‰½´þýˆfðq²ìaŒq:VÀí>¡Q‚ì<¨•Mšvœ’6?F ?å¡ûÿ|)Í1(Å'“ Z”ó~÷Ä«+Ó W.9ØÁ€BE<œšÂÖÃ>¶¡dÙcØš…41êÙ !ø¸¤[0o¶ÜĺMâåmͺSX\¡‘ù_8ª>yÒ Qà^͆„é_ª©o‰nY¥Èj`›#=4º“¤mµV"+!ñÀg»©ƒ ê I˜ÂÌSŒò•DÒŠå¦ÿ2›1“AŠEÛa%yáïÌ™ ˜e öiˆ[Ԝڑpy&½×|O´™6 ÷» Å'(SäÑËÅp¶! £DÁ;¤J¦Ý~ÞY5|8Œ7|‘FËðÎ"¬M™-¢¸1òq¹¯5jKPµ½”/ó%±Gâ2nX)ñVŠÚº-» &ŠŒV±¡é2Œ¾A˜!ÈØ™Ó×¢âW Eˆ˜R”Mp=ë—ååÿ™„©‹ÕÃoaš¶g›T¼rÅÇÚ¹[VÐi)O߉€Ôòœ faŒCIoɤ†mEwHÃT¬/™á!Wÿ%CüGåvH,2Â3´ÖÝ3Ÿ×_ðâžN°Æ³•4ýŒh€k:Åû˜”TjÜ"¯7Ô‘” çéÔ#^dS¼úăl²-âk‚v×d'BƒÒºÃJ(²Iç3}Qh±ÐäÔÃkÀªj»EÍÌœéÉ›ì ¬)†F}D,0Nzµ#{oE¡:„ŠÂ÷) îo‚·©Ü+z‹ƒ2 É­]úÈÍêK)²¼÷4ðµ-èVØ8£K€üo ƒîÀ³î.vB?%?;X/⋨ øÂMŒƒ Õh9ÿè•…KÞˆŽÕߺ¦ŠFˆ”É>p9Á=`N:‹ò‚ƒ;1ÑT‡*î¤ §µ•WwÒfÕ¢VÐE î¡‘‰ëí¯ iV¼0©àº oM »ÿ€â™‚÷²²?/"è‡b҆ţÔCìÝ6ƒ‰?H×p©‡ÃP!Y2'z,|.… É(_v”…´ÚDy„LDàŠ¸™C*5@`àX)¿ƒE D¤`Š…›†Ûؤ©4k·«SÐöS:FŒ‹~ðãüõubÒ£Ïñ”`?ž,báàÛûÛ€ß×="3êô±ø0‰ƒBÌ¥i\¬]>pæú”®£$ÖÏ9š^{ùoƒ;”e§ ÖúL{D.ïàÂDÎ,Ì6åä‚d»Ñ\Ý^~y+¶ú³4‡–Þ¯„`ãkbˆù@ñä‰ncc­ä•$/üÍû¤öžüM¡‡:?IÉ”ÝÝÆ£ù8ö’*\óF?@¹ ÚØhjÁÔ¬B]E{\š@úz@[qBD¦ãµ¢ít³tsÜÞqÄqý¤fMÅ•gOf1RiÄSqOHJí sëûÞ/ûIàÅį 2ª±× ›uÎié:ËNê*Ó†y‡wôäYC⢒Ï[×Ú•Z¨e›ÚkÁk¨ÍÙöm„jì`Ö:¢!¿ÂäÖØ©ä¸Srç½ÂÛuÈÒ.…º¨LÕu×TÑß9.Ã@úÀ; ‹½£ÜŸ¹ ì8»Éƒ£ðˆh/¨ã&ëß¹ð‹>t`G}&Ï ú$`»šuWliNœ°“´[™ˆü'÷b²ÅôáyùO9P,°ÄFal$-ʱñh™ +cñŠïÎDÑzd–VNÌWn`75 ÙÿÂÔ‚pÛLΙ¼J 7¶ï‚+aÎÚ¹Œ`ôÍÍ®ÒËľÈàÙ;ù2$54Ÿ:2`Èîò*lƒ”¾í›gq†¼'õXb?•­—÷Ô äQÐÃñ²¦#ÖiïŸeü{^™~GA± ª]¹ª1ý( ÂÞÙt€B÷€Á)B«i|Ú6Kw¢uÜDˆ"®òj‘RƒìLÖÀ¶%žšOµ+Kèo)ãoö*í‚$â×âµøÓñÈæ0¨夹$ÀFüŒn\HM#…\;‘ì*DW‰va60DU䊟kåÄ“Tx0B)>ÇŒ}%âÜfŘ7þнÿÒñ,XIþ˜ÃXÖ¯ âñ‘僀ÛDë^{­‡e#GœKðe¨À;‚jKoÏý™‡k3`ŒMpp׌'âøõט\X·p:âögäý?Y0IŠ¥É t1¦~ ‹ùKLj7 KöC'œøŒG½NÈ~t«Ó§B“;ç}ÿÙGÐy%F^„½DzxuWPúÊĺ˜!ëí©þõ„eþÔÑ“»·o@Êåƒ àR €0 L²Â">@• Õ²¨¬},ôd–ÿüÉc#¢˜c÷Ï·ä|¦4aø£ïy=7Ð©Ñ ­Q+­ãÃ}½¶•sæKâW%ƒ7òA¿½'*Ý]LzÕódy•K•;_— µ÷½*³Æ*ÀRð °N9ˆÙÇb-Ì.¡{ *B}àdÕCÿöœùãcH‚³g†Ü5êOÌ|âKî†_`ŒççgHÀÉ„=†¬n=L:d¤ÝbÁÞü¤ä?8 œ@Y†ÛE3_©cZ…ey9|ül´ñ¯]ÿwyÚ5‘íoM•çšùÿ•¼ˆ¾ÝNÌk÷öÙPë+r=µº1  )›Ž¯ÌËL­È‹ö­yÜ¿÷Z-r)èOÇ=Sgâoy11ø¯6Ü0zSm„T1§–M‹5­¸(:æijƒ±Ä¶ ßFíl–PúPà›Js=ðÖ,]ì~y³‰z¡â²I Ÿû €U‡ƒò §^R–zz?W=÷ÿºÕ[Á¸“×G……ú¢Ói“qïdÉÃ1ɘo!¾#íQ§Anx?æ=ë|4 >­ó†ëàŒoEµi¸ga€À|9 *‡•ô%+@/*lO£‘ïcíŽgJÙ€Ìóƒú.ÿí¹»dôËù‡9»0çªK“2€TŒOÉÛŠ3¹®á T]Î? ¼ˆ§ùo|¬-w{ùú¶äÁKÆÂ׈Óêå%8 iGÀãÞž)ø»OünRû"pb—x–ClÚ£ÿ§˜íéA=Ð5V bªÖƒ±&ëµäñ8ÕØzŒ3×cåd^ÎZK8HJƒ3ÆÔ¹³)bx0mb:QŠK¸ÐÝÝBn9uR"ßÚQsfãîÜRóå>6²®­4јï!7HzÖ°$¦‘iÂ93p!ôÛ5¿–_9ðQÖ?öÜd¢¨Sc×]ÅÁ‚  @%r˜&óø‘%žÿ[[bf´Úî¹û×åÆèb Hq.€´½PºÄÁ¥é¼^ó5z‰Zc¬æÊåûkcô»¯Ï7|$h!0Aií,³pà{°Ú%îúÆß âi`ÁíûÉküzþ³É÷ß}ð×™X`Ç3ëzC÷"ú q”œŠR¹+abdÜó{™Ð{AÚ¨Hyí’p€˜3 A ö€mÅe¹lÄ!°fWAǨá„Íy\›s¾ïñ╌¯ÀôkË€z FT¶Ø…tAl{?ÏòþAi=àqa+ÙÎV\˜Ç—¦%¬I{Cj²˜o”frr0oßþ9?“Y€ªDlÊ÷Ûúüq]¡Z%)@3fù-÷èÁÃîWÀ•¯«¿ ››ö‹¶¦ã·Ï Ø”€Ÿÿ¶~6€|ÊdN/÷}ú¬ªª¨*£¦ŸÉ¬R ç‚?rÒ¶Ѹ9<+øìM!,ÝåçéRi<¨ ^D²Õ¡ðIþ®Dc×þ7/»#[Dª«Ÿ{Ôáê3µön —hþß³¦Âiç †pu›ü¿³¸¥ñåÿ†¢¦«’«¿ûïœÜõ4Ç% 7ó^˜1“tÀ_±m€"?Œô‘ÇÇÑ"s‡ª<\OÖŠÕ%BÕ’MX÷…ø´ú½¨Go†2 $v~{ŸG‡hƸQä¶ ½ZpކÁÙbÔ0äeÀTÃV,ÞÈ-Òp.Ô}ê:PÏËÐÖ÷ï“~ÈašÙz\uäù‚õ€±WÑ…tDAš˜ OU"ˆøUÿí¤DÄ/:ø›…×8;…Ø~oœ¹ÃŽê«GJldïM äzJü_ê¢v<cIgsCýˆ½¬ØVº¥½`ºJTiÓD\/ jJêMNäMxL ‹‚¶òÂÀOÅŸ‹|® ÍWÁ¿ñ``÷“ôûÿ£ØÑ £Ñ/f•Öï2âE¿TžFaPõ1/ëFÐkSúqФÂR±_Ã8ldNrr$*ÁþKIñqµXðlc2Ý;hyú¬Åx“Õàj'¯a˜ò#T«Rü1ݬ#ìì ¯ý^5dƒFäŸb~VLJ]ƒ­ËbßIÉÍHß¶fŽÝX¤«WÀIlÈÙ40Nâc‘K:žZþ33††> 3„´"ˆmX´É‹dÃI)¦qO|½Ï8K|ÄÀäO Þœ°Ÿ]*OÒI”É\Â!+Üñ#Æ­h8rgÙ×5hw†)e_'EPUP–Û­…¡sÙ|Я±’z½.Tlºà²7a)Yº‡ Æt¼« %µ²ëú}+ܦô ê]R£\!/6ã¯õ!'à†Þ…ŠêG˜sÍ~5Zd.nžFÝRM*Ñôäa8VÃÂå¬r*.ж4ˆ‘Ú}à„À”f6&/Äiò„kÖï×ÓÚ¹!GƒœùË ïìâ7úBî¶©…×2Š Ê€ÈŠDÎ]ö}ØÐ£¤4¼©’iÕP[[ʡۚk?»€.(1A€×çT)æ9ôùÜì`d”™¬ÌÚaáe¯½[Z„žÁ¬J7òÃUÀHf1|Ń,¦EYuÛõÂ×ì1 ÀC£€êkò_Ò)ïCÚP­®= 9m’7mÖubï²!á‹m(1“ƒR§0<Âÿ± ·Dx}ÆØ‡Ä!Ã<²*aJ\†ž²÷–\ª<'¶G$`“ryÀ#YO|ÿêhÙ‰¸\ù©v<ø³BNUĦj¯× ^5Ñdк* É a†³Ö†²›AŒ~ŽNƒÏÁ´8ÒŸÒ^Ôé2jŒUì~°Ž@=´?ƒÙ‹j®K;h?Š,Ç!©™ýïóR®~#ÔúÕ‹í˜*´à‰J/óâ&€'>ƒ‘uW9à¸ðö¾ì~@›õ.3Ô#,Z± Rù3 ¬Ûêeu`žÁ_ý PÂ8ˆN¹í}éȼŽÊ.‘ß57 3¤¹`í&X·#:ò J£ôþ&U\Á½G|EÄvGj‘:Aå)ý1 ‚& }& MÁãl>^!(o–Ÿä…4êJ8_( •`øèÜîŒãA»µÕ'$`Ä/Τy°ë’ÚÇŽ ø´A6i=ZYdû]¦ÉOÞçÃXUË•äÔ,ÞQŸ©úXô¶…÷»d˜{iLûl/ÅW¼o9Ii*´{ÏÑzÆ™ àZÓà{9Ô™â CJÅ2y_eÑöÿ¾®ß|ãœÑ f¼A¹~‚rT;Aì,ë{üЀßç ~¾Ñ)Wj”Šâf׎ÍU¶øm»òX=C·~Ææ½Ðâ³û+ŽòÈ9ʨçü>!Ù€ðÞu£1µâèXgYÖèL–&hÛF™ç 3èqŠÍ+2v•µúÐ*Z˜lÙQk°3 A™§òbÞ>Ï ¶ýEz'+'1ìyà”׆ vÌ6¶5ÛÕý™‡ÞØÁ.’JY<áË7¾*ž#‘“Té¤lŒá¬¬uº©MC"ÑÛDŽVè-ò¶aãž©+WޑɛI×yM-dÂ3º3YÍÅI*ÆÓÆ–ú|¤å` iŸÍ±B‹ÌÖÕ":édƒmnʸ6Ú¿T1ÓW$/Z!‘;rAlUE_‚CžÁpÊ}^ãbœ/ìQÄ‘½íš^¥§Åä  ,ª° &ll9+ÀuY˜|”Æb ž>°=y(Â,®p ë½çÓ¶¨c?5bñOo&yX¤N”“Ñ¸Žº•#ÎZ+#5—Â]Õço8ã‡ä5§º@GA0.ƒç‘_ÈÈ­ Õ™¬H—ÁµHo-I"_Mk~úu›ð¬óï¼"¸‹¹ tL`M¹>Äé`®õ5È:·ÜöÚEÄe@4š3‰¸Â´Er"Ñ‚2šSš™E² aÉy²l*þ¡{Ü>3RvÆÂÚF(v±eÅe”Q*¬ãFœ\ÚûS7C ö2‚',ÆßZ¬#q.—âX¦• 7鱟'd.Á'qÓ¾b[ƒÚÐx<_t‰`ù”ím(WŒ¶MÜc‡ 6‹‰]…^ÁÂôóÜsküy‡n dÒ°½‡r q_R;µ(‰ÝŽ9âÍ>&â šFBо†ÞˆðGŒGQZ¡—B8TÒ÷ío¥X=Ðo£ã ´,À¬ÂdNf=L÷³¤¬¿LK5Iõóç.w1ž)Ü‹OØå¯žR†ÆifÙíyç[¼Â5ß0£¤üŠ¡$R/£‘h‘3ŠpªÑzÒFÖ ðJ£BRo#¤¬û x_vŠì9ÎafQBá †LBÝMˆ•Â^e`˜WŸˆzû0_.ò†[ä‹-ÀÎÉWgÕ÷G¾Su˜“€eÏ£¾i¥©³«%ùC>-E¤G ‡ô=pÓ ÞdR?`ð}h›z„ý¿Ð§m ÞjÊüE¢Ì60ïÌs$1-·@VÂ%¨+ Vé(Oojí%“¥ÀݺR–T(š #*»7Ho‡¶èÆiV;1R rS|‚¼Cœǘ€ê¿7Å\_Q,Yš×ß§;ñ! CŽÓ ‰cìŠNc *Àv¶`¿œ /Œ"äajÍgzËõ`Lý ¢÷‹Ñ¿ÈÌN²Í¦ék½Lƒû¤ÈÇDJ‚E8’ìkô2»{ @¯8 z†ŠÆ ‹_袕‡§‰›AAÃ5€Æ>ËŠNFñ¶:r!ŠjÍðó& H ·ó+ö©ûŒoÈld•Þ:çÖõéÐÖåué¥F ¬¼6$jŠÊ­â>n]¼ô5GûŠ!`²gÁû ƒZ†§0ŠGâ×üZCŠ­¼›/-qÁ5äRæÀ|/#ÇY(Ž›P‚É›Uªöž´E F 8"y/ưd2>zè OÛU¬9R~¡YÐAiãç×V‚8X´<]Xÿo(Ø×CñÏ4÷pº’‰¦¬sg±y¾ÛÍB ^ö‡³œ[W] ïnô7û8h“f¢ çábÀTE´Üð=L¨šÝJŒSa ð (àG±&ýþj19m~ímF[Mð ­¥®'! ‘Ô^ˆ )ý|Ï£pgz„w¯í ,᪹'ßÜÖÁ‚¯­v9ý–[ ÍpâéáˆÜðã Á 6 ¢í`t •ZX-…ºŒuËôVÀ½…šØõž_jèWâ²›Ú«ŒMˆDÁ @z •²Ìkd/Ü>F=bt ‘Âäî0?×Zïïþ[§ÐÚY‰d—xÓ¨^c§ÙµŠ9G`8)3KG‚`¾˜Sê¯CÃþ‡ Š':·Ÿ/}€?² t#Í™€Ä*¦TUå®J¹zñá1CîÛÁ€C/7ÜaZ =QNáÒ˜xºH»ÿLj@n|A훡¶Ÿ­ykñ &5G,flªJ‚`gH ${ÏFsþ8C¿¯h £\»‰(Û áÆp§Áá½³ –@rÊ µž“MØÖ€|`6•Éï|Ô=ù³w6°ÆSf¶lèÌûÀ‚4´’LÀv±MMaá=†Å3‹ZÛ;5¬¨æUG)P]%L貃.¶1¯H‰3ûSB© Ú“€•šÄä ‘u%@~”|ÅÙoÈ)pyp.Ø„§“Ð;„LÁ–÷Êã`þ†*~' PlPg6id™#iPWLߥŽê1nÙöùCuz$Ó·»%»¡n$¨R)çÅAùX6á ¨O`’p;zGŒu_ s­§I,pþ¥Õ%4h2Ü3}šï$[ó5*‰^‘ˆMËé?¤hö®‰fБ ´èkY?ôZù$NänJÁ¤ºÛ"ì/ì"g=ѳSZöþ’7ÀŸ|´Á»X¾£Ú +Â+wÜ­ˆ¤²ÆDRe2[4ßKø7«‘æèñY³púÙdÆÖÒ’ 9í gèÆT:ç Ðà€Ã'r ѵÀ¬ gVA±D10›<àÙ6ÒÚ\YzÀŽÛ¡ È§ö-È0,Q …þ/ ¼Är½žžóÓþ!a X ÜŒ’£²!‚ÄŒà^iF"?Ýé#idÆ~K>@Þ¦k€×µ1â‚(/0™°B°ˆºcbv˜N›~@Xçëéø¶à7Üê°Hz³7€d |7|£ãÁ}!¤Ñ”À[˶7##+íÉ• 7¤åúŽ&Å8o”­î¨‹ï7Ãð zMÑ| ÕsnnâK—=”8#ky&6Gÿ6€ÖA„íŒ|Ë<ÔoŒ ¥v>#ËCz 0 ÉUJx+mí"m´Sù|¶na•øƒ)„~ü·PSQ×ÒÊ‚ÖÄê8Ëš<‰—mIèhì‰PýD¯*Ìûˆ5¥³e¢ÖÄìÀÙvI05áªP't!Pá{¼¬|ÎÑÿrô.ÃÞÎ_kYY›7sJ)¢¬ 6ŽàµŠŽ[î%Ê·úoÝêoàÒ+Kl*ñ²PŒ<‰6kñö¸øc®šL,DÛ4–¢¤?d¢i_R¦ÄÒj[È ]n]†xÁõâGÿ­‰7Ox`u ‘ p]ð;`N@Á°oap f´Ã˜‚’ÁŸ2g›j¿Zìó†›DvpF( gõ.Тh!Uz w–v.J¿µõ9eœˆ 4ÐU¹2w¨qð¨öÐ2ä@5#À'QÅÌU)FŽð+~ˆÚ8n&í¨)¨ÜßÝ¢b¸rù<" X°6¸ˆ ÕÉRÇb¶}Xƒ!$hÃyЦR+ 9FíÂ× ÂkÖ)aÙ1v—ÿ"×g‰ü™HÂéþnÈŠøå›Û”^²¤>¼»¢§t’…Û0Fwɲ1œi9±öë®’¬PÎl«~€H5ýÀ@$ˆsC‹Bˆ®‰°Ø`/û$–6âÙ,VOIGj\³›“íš¾Z…+ù¸:¿=áD?$§60Æ@á 6€³-F4g§Y~Ú÷ –ßze{¹·±‘Œ¨Nˆ/¸Nà0”™¢d¶2í‚à  æÉ¸acŸÂd4RÃ6~D÷19Îîr—φK#Â\µå`dûöm*Ù¿zjÜØEsBq>[ÜuUg•7âè ´ÑÈ£œN! Â ËFÒy¤À‚L#¼í.È„„)šoèTxìžÞe™Ý…ë?çÃÜŽ2Nxð<Ì×ãQ³¼üྯ¦5‚Pà\«ŸlòQàßÃÏx7Âiêf_ÛN@rƒD¬b_¿|^V=yU×ã—Ú_Äuù‡Òpþòª4_ñ"æ^¯»¸Bôs[ž5—»ˆ"I$çHk”<¾%@@Ń 3yû¹!’²QT˜Å:—mâ¤ýʹDÖÁ…€c¥ÎÁã¤\|»™D®·þå+N…·]ﺯ/ÿ²ër;Þ¢¿éÇ3P ¢ß_@WÛÑ4Åê›x[¤ìÊÏ8Q˜âÇ…A`¿:0ç `bˆ&Õ¸DFþàô( ­ÖøGÍ)H~—ôfÐMe²äƒþ¼MoÆÊ-¦Ò×Öö6á8t½,,QÞKê CöõýŽhx±“ÚE)5ÓAòcŸ•øã-´+§†¬`À˜Ë|8ûò¶Á›’³`MÒX#óyX]©^2Š×]ÛÉ>ªÀá!‹#ŠoEû8P Ô©*¤Óð ¬ÀÖ»$Väyy<³4u/ F8ß}ÕòÙòaN!fÄü•áçô¨h.àÔ•†j‘Æ¥‰ê4ä*0c`1 D•Õå2†æÚè ¬4Ò ´ÁK’äM_ÿâ¼…$@g:¯ÎXB÷»ð¸a™Á*ÂdøÇYŸ‹€1ìu™ù9ò¹*XŸ„þ `ÒÇ—lqŸCúÁrì±X@†›<ƒOôU>fÅT®ø!½ÆÆJÀj [ðγ"3C¡ü÷ ?ßL¸=xpÏÃZ†ô6Ûþ)s ™¦ü¶LV墯'iY¿[†ÔõïîM4 € ÜÊ-ù3ƒù[û¾Ñö‡µQip±B§Ø  èÑÿ\շݪ"N’]À‰Lˆi»ˆ¢d¼•Ï©`Z?¸V‚ôQÇ©ú1^í:äËŠâ7’ø"”ÿ]doÊ;f\©=UZ‹úÞó¦cñ]Ï ÕŒ«UŸ{Ýû|Wæœ1@œµûˆÁq7  žï½Äॡ^çèßwþ§þhÄõù, Ëh²ºgª+nÄq ÕXU^NïrH(Àù¯¶Ôb‹5Þšâ(-Ú`KL7™‹ú±N³ EÀ#€¾ùÀ.%C«T~˜+aùÐykÏA•ùå ™‚K‮ˮcyƒRPâ`%x ã„\8ñï %‚ ¯'‡ ÆÆÙ;|Ô?/•ݘ ßaæ©*ô Qû¢/]Ù'!VST¤õU0¤(õ¿àj„Nÿ3'{])™ä¢iy?™w ÑL³fÜ’|.,!3æ±!ÃÛ@Ñâ@¼Ò*êÇ`eÿn_<ìY¬ºJ7—Î%¥ILŒõ7­À<)¸KZ$ê°Ø§ZJÛ$÷™ÎB=ÚX0,öš\|U#Öç™8,\Ö+5¢;¡  ³Ç½Žxü7—‚À“ 'ZÚfÜ/!TõÓ@ í¡ÙŸB]á1u4whÔ5ëñtJ™ùKpEJU··fKÿ:É•Q¼ï`{©<Äç-è Y×Y+ãš×÷0–‚¹6²VÆB‹ ±÷( £âSsDsÔtœé½Õq\¸“òÀ6ÆŽ0Ñ3 ^ÉFB»Öd_Õ_«ð1ô‚ü úFÂÉ-*ÉEaü1*ž2ßbžCô°­ðÙ@+ ~ù’9 ßdÈôþu(u#8ÏiB´[fºÊe4Æ ˆÈˆ€V¢uRèÇ_[GpVÙôH(šI ò¬°”;i‹[@ó-éªMôžÂA\¦E¸üŸ{91“ŸÄ0à’÷ôÑ*IB§ìc¡¡]þJ«ÂÜ)îÓ'=‰Î}o M%Øed¾ù: ÐqH’õé+yF]›¶Ü›w±áòð›9—HQ×Hëº~öq £}ðO4e<àLÚžLÑRš|•Í-u#OÁj­üÔ\oo8p¨äè £j€éÖáøÖ™ŠjvE‚aý??„‚ú6@PèÇ]XEÒ˜{ý8Ûe ©pÿÀeHW¶àe¤Î ¨žÐÕS€ŒÀ(H›Ù‘¯¥þ7¼DÿÕtÖfÊÕŸ©1=à^Ç2põŽï{&ºÛöˆ³î*-¸ügê”Öi]¶ÕË‹hÙôˆÜ`˜³yÓLn½€ÜºM>¾#ƒ'÷œ¥4¬µRW4ÊN›&!†@›­þDŒVs` t¸™†_'~”Ì´lVÖNžM|Èê”›ð<íAy?‹@2ï¹™ ÄÖÄ%Óº €ï‡,iäã»'ßt1xG"ƒÊ㸚LU‚ÒAOJëv L{Q÷}I$õ“æ`ûƒ@@*¸BÀi,b™î(þƒÿ‚˜‰oUaΪ÷Ð{/+ä8RÀ]¬¹°ë½àŠ”ÿD_@—¿ç0Û¶‡ëv‰ŒÙ#äü …´ÀnS<¢Ç\¯…uÂpÐåa 4…z©ãÛà=e Åtfè7 Øþ‘O, fÔq,R ãÈ€âÖ/7JS y¢K<2…¶c*û ÷¥-æê$ ôž@Š]qÖêõ_VÝü‘LOÏèÝ·L›ò ON¾n·Íœîb•ü AƒÖ¡)aû4â|æúD-æÖ®uôþoš&‚l”ÑIäMº’ó‡ z:žþ>ŒD¦Ž'œ’kBì34ÉI@˜22´j­ÿЕðQñ˜Ü ¦±S&DohVâ௻@­!Ç%òUÁã—µúXŸnÂ>‘–ê&dÜ<呜úxv´¿Çø…¬+¥iVÚ†u¦%H…Àª‚ôa¤ûÑž´KpahCõQ3ƒîJ,¬·Hi¹?~ë1mÊ\t™¾Vˆ®“®Ybü1,º7HlhÒÄ3_ÔcÛUF› ΕµÄ3îÑ^H~'‚\Bf6©24¤=ÿ"Ž#˜wÿÎç§®à ŽÂ]HfÒ\ñ>Wl»¦”CÙ)iÐÙZŸ¦ß»åÝ4hx*sE&gäÆ ý„¤^—µbòPNÒQ;ŸZÐè¦Hz.õ›"™¿œ&z‚"4Ö~¯þñ ¡öý_Pcðx;#É-Ø4 å -jøü{ÉFŠÎ*ð äñ ÿÓ±LR"ˆj¬Š–ƒr“ÙR” ªËÄŠmÖ´mì‹_l¨Ko€±›TuðÄ¢Ðh" uEIoêŠËX™2Ü‹åP/òðUÌA¸Ö½ù(öYZÑ+†‡1uq»‰K,H%Hk@ÿb¡_¾¨.¯Zs~ h!¦4&1õ{îh‚y-9&þÕb![Y‰Î $IÓû Z” À%ïHù+í^€‰6.s‡7ªý®>FðàP$x£FZÅšGûá´‡>ë¥ï`\ó`ŸŽà;†£JÄÎu•(óé÷³ÕiÁñç§k«¢%ý'˜úÃQl ·‚5Èÿåð¨q’b«ŽRJ›þz =eÉÐOÅ¥iq|í¹/ݤ˜~4° Ì6‹72EqT]gF@G;|/I`3"ÝßÒ(©ÿƆ,èÒºP“»Ç£¥”W±‰\§ URÛøê€¨ê òxcã°çõqÐÁ‘Ì5ü¯¾Ó2„½c¤'VÈáЕ@‘Â'Ì=©Q4²™e¤¨GN,¹9Q°í™&Òˆç„=AϬCØ…EôfPÙ¥YÕA~Êélj ‡Aw>'*Ö…•¤-G8QxfU½—cÕáÄÝ€^x^e뻲 Qòä T(Y8}t2áˆ+½Žà&çû*ô[BÖÐ#ytƒ ô*%Ñ| Ow ‘Œûy2`XÕÀµ¼3ßF€üžã†X§:Z(F±ù¡Ïì*^qÏðx¢t™=±Ör&NŽ7(«4%:Æù†BZdŸh=ÐÎdÕ•&R+Õô‚À½Öß+Z<œÎ+¬¾ÈkÉ8›,¹t•$=)‚© ±n“È3«.±Z48ŠLaÉ¿×öHÆoÅØ~+$3Þ.-Jãÿ÷MÃ`Pcô®Õ¥‚_m6…îÜ5)¤Ü08Ì¡bÈ ž Ëã2’+ÿÄ M@fДiYñbaQVwpS$€¦Üê`wiH•­$ †:µIµó ¥i¥yÑE·a„*‡Á”æ ƒæÕ ZÊÑ‘4¸ÑJ‚¿F8RWÓ*}N»"aÓ¢ÃÁöÊ’ÙÖ­ ÷œâ¯%ˆ.éÚm¸ØÓȱDE"—¤ZØ#wRçíx{xU*ñtu±¸·ÌD8»*Es,¨†Sêð­Ã·RÏüV.8=Ú‹t5Ú ~‡æ¥Lu–¸gü°ÇâFÚÐÄÉ5þ’&÷+Š–w“DÖT*{°D7Lâ+¹õ…ò“3KFïï 'Â^hú0OulUQ‰ã¥^‚iê“PÛ³ýP+n© án~ˆÈ븶°»qº8²{áºBó²p†Ípþ8'#912ð rɰâDwëxI¹¸Óµ¢TH{ñ#më6 7–R(ðao…XT- H ãÂ÷$WŽ¢8j%„…ú|”}qe4cöb³°5–?M|Èøu0¤”0jR%Çì4Ç‚+‚j˜0HLÙ¼5hóU礣ˆ„/L4GKqì”õg,:ŸŽÅ™[¹TYC#á2`£ú%xþœi c1„q"{ÑèÚW» ]·ÔÖ0uŒX[‚“Ÿ.2™9Ô8¡ÌaÈ€äî)‰ ·ì>Þ$‹kZÒvX6`àÏrä¨= êc|-¼™Ö fæ›týT؇<þ ŒbÈqwþÒ 0>‹ nÉÒ¨‚8nã@bi3>š¨–ç Ò¯©¹kj,{ûÆ¡Ðý¿ðŸpOò}”ÃLŽ´€œ¶ÒÑaÁõ¿i3Å)ƒÌ)w¼YÞnÀ­ÙX$ï= _Y¿æN_”›z˜X(AùmvE2»-˽³`጑‡kùKHÇièICD¸sO ˜¨Ð1©g_šÂ¸3(rÃ-p×-%å¥c%¸Ý<13Р2ǶÊ1u9zá%þ¬ ³Œ“(¹4Сeq[hˆÇ™…L/éy&g7§q›ìà˜ø°º'áM?{’ä^ö:å®>)õ±Ù¿cú¦'H 0)AãLSÚÍ~Ømÿ–þ4ƒ×š-HAÈj™³`ê™V^¡†íNë4D4˨€¡á­ŒfGn 0E/”&øàÄ´¤’«î’ὩӥÕh†.Ðdy™2G–ÇàKÛ=fƒï^תê§ýžÁòl¸¾»Þ‚^³(—ç᩵pç¼à•{ { ñ!å–*k$swž=à­àˆ!Gò{yŸSC »sú:VÔáÑè*¿÷ºhÕyfÑmÏLä|ƒAF g›†€Î¨S<h9Øü4E½Ô0ØÈë~;\Š}-;X$F!ÎZË0«õÏB¬×ëGÃSúöŒG9œé]݈äUU5ÿun›BWÔdj.'‹±—¹Ú3|¯”÷Û|¼‹§9ŽÌÞOtÞxžqÐéT·rãPö‡_C/^ S)¤Ä×ÞžXÀ«‚=øäpPÓR`Vzâ>É,–Eýj}ó Ö™£€²JaסQþ™ŒCHÔ0‡Â]‚O-¶Ætö4ƆvÊ-èïÂ̼MUQþ_s*F ˆ2]LlEË*„"D0õ4‡L–:²áà1±;!ÖøqgÖ ×»vAùÛ„½ŸyAÀ”€WJ°X0(e Ÿû-Hô–CºR‰)÷œà°bd4Þª™'o£o=¥àg÷‰ªïâàQb"×±Ûwòyü…\žTÈɨ™L'š.”֬Ȍ¬ÃT¢ˆ*ÊB˜õ3ˆK¡bbˆ¯•¦«‹¦h§ò•†ÄŒ½Ì9Íu¬äF͵ˆ˜ ­6E“â¨Înžrš<éQxQãÎø2ð-´ÁíÎ7+×ç0Ñd Qk…øúÆöu½oQ¨ $8ÀÅ ^˜ÝJSlɸϦØ$Ø„¥¢F¨6ëè£à¼¦8)¹éÇÞtñ~uâ,Äß2O…Pà?+'oqEüçU"²L‚ô `pc8E5ÞÊ6Ä‘ÓF $w"+´;:ây´8è¢h'ƒU¡C…D'ã¼Ljå ߤq¥'ûMœ1SGÀo¸+ h‰øGÐÄ. <½ @îà^â0shZÅa=4ï: ‚DÝ 3ŒWõÍ„dYäß‚A²Ä-/@–ÔäÀX(Ÿw+‡0‘T.ú͇Y!ïüÒ,—w°Z,ÅðØ0ç ø||ÀíôÎ à4LX´LÔUïè?áMz Y-dð"JÙ4›¿× Gý„»ŒF‚!#ìàn˜Š ôQަq6ò€"-9›%Öà+34GÖ¡,sŽÁKÿdËú1ëÝ2 œ^ÒòÅÞd(ËÐ>Rï>ÌŸ®ãš$R¦îm‘B÷=+«;´¡y}\!¶ÆïöÓÇ®¦sS\*Ï}⬥¯À{%=zA+ÍâÞ"º0/¦£5gí*)F Èì-~Óš*áòÕnf0ʘÅŠöû–«ˆ‘ÀjÉû;¡” ±Ã'¤5EÿÒ¨pÉè°~ßÁä¢+J§P1öf ~RÝ"eôn Buµ¾ý¾@T„€}"“1Æöe¬e°mè4±çà˜X0¬rW”T@oZ=:› `o]¡gÂs~RCì„ÆÌiã—ˆ`Ó·$ ÛWÅn|M(«©“ ŠÌx®ã†´Á.QmkX>’¹gOéÉNͼšÛ’¯MEbjnË}ƒ*Ü„nÀîÊ«¬üŸ§ìÖœ‰®Í%`2C_ LB¬%QI”ËÌ^‚§…°©£ÖLn†‚Fˆãm‹ø(kpçÚxôâðyœO"§„ cŠ’”߬FØ º‡Mcø]¤Ï'2bhŽënÀ¬›êbv9ð“5¯«x±KTOymȲýÏÒ×½Zž«!¾³§7ÄìÜÜÅaà[‹ï/ˆQ†<ê³-ëdC"ßÈš7˜ý"C¥ÿ(å¶Ñ{ÿ}Ö D~€fŠŸU£Ôo»\¢Ä2ocísE’KÂdå‹ >³Á±x©C´Þ¨ Š9ó+0!ƒ…ǯCKÃS¤ í`˜jÈv\c^I»ã0>üLM]ü÷áQ·í-‹õâW©ñ‰<–A„Áù?ßÙu„®«Ü !<ØïF¸÷•}ÍËÛ‹E‚“3àYEP¥ÒÁöšµ,VëÿÝ>ÄG6Œh“>bPü šg æØë1©Â/’X)Á¦ŒŽÊÃî+ÛÁTT*(¬ñ³D”aù¹*JôgªœMÞ?j˜Ñ>¬²D¯,³I|_ô0n˜mü ý¦1sÆŽf-(4pO窸¼'Æ "@8þ °{Cëh{ØÃr{ž½&ï!k£þWî„Þç3à )‚À ¼Ñ ›ß(ЧBeW²é¢ýàéýHY ‘ —HºwY‡f‰tMðº[ípÖ«¾äA$úJ>+<y¯8zàˆ§’ÞX–àÄY7ÖÍâ6áaÃ.#à€µyû0|Zô=£tž—‡ËFÿ8)lŽKÖ<¢+vÞg•Å)j~5áœö@©èSÓcÒ¿èŸ~s½ŒyÃ#kóÓê¹´äçþ:Œ)Ʋf3è©öbWíÉõ–òÿoðWヽ™9„*0*äÏV$ }«"ô+ÓZA°3ÍÐq'Oa$Fƒßý½ŒXÚvvë•Ç'ÜÌå \¾VGQËæÌD´bòbvêT\’X›ã¯=äåM½#?¬rc”øKð;ƒ2®ÏóÀ#BN÷œˆÂ»û~?ª¢®§0"YÏôîéÀÔ,R!ÀjãÖ0XB®_Ž ñ€ßŽ˜‹ýE¼G"ƒ"ùmäãy€½0ö£vÉõNþ¹v{Ø{ßw4àp ƒhd~=‡ëN5UÝÖ5U/ÿìEð'©`& û¼1bÿÿ•ÊûáAQYR`z5ð9 j2·ÃME@gçÿù \„Ÿ·.ôH¨—…Ê! ykÍs@#à +ýøl7-ò"ªŠ“ø$`eòüÍ•ÂJPp€s€@.+þ¾pøê«æ¬ëù„ÜÖ8þ8?†ÓøÏTÂ3&nûú“àDÒ/‰®«$>Î7 ‡#æB8hèì®Îú6°—XSHKšoøv""(dð]׎ }n,ïªÿKDöŸè¨ÇÕ¢ÆØqèŒ:Ø“j½Â§ 8ïXvŠ5ïÃ-€2½°v³¢f!„Ùoîúñ5#h_Þçò "r"%<¸^|~áAT⤜ mdø5•=ÀÌ—lÈ[?ÔŠ?tX†üÿš¿&ƒzfTÏü€æè'Xÿ¹§=¿(ý„ÉÏ\¨G¥ým¼€"ÌHŠPA[Щ@|±L1¡…Œgÿý^•[‘fä°ÄPTµÄ°€¦® .tÐô®[£zÜþƒkÄê7ï¥=W ‡§¶êm8pgUHóq½á‚ó ©áe, ¯Õt”9ƒ´”œÐáD‚Z-1ZWÜCø€rîKÐÒ3Š×ÜW—åÿ/–)&uPEÖ<Š—Íò÷î!ÆÆäÈIüòø‡×®`†Ø .].x/Ï? I÷{¸’^¼¿þÕßp°«îîîîåµUÇ)¯I¸Ñ<@<Ò»»íî«7õüд¾W:¥å¶;èДI’!‚£œw*~#DAÁZuX;H>&ÖS݇$ ÐEN¬dîNïŸ' “V‰$Fƒàh?ɬ\éÛ(äò“½@iû?Æ¢PóQž n´Ì²ÖS5ÏÉ¢\³¨Kx–;oäBkßë?RËø(˜()þQ½*;è-6bË®D´ ÝL°$@x E]”͘e´ƒ¨”!†Û)Á[IBËéþ¸½•h9¼|vq‰“1)fsÁ¶¢PU&°>>ò‘?ÅH5ë/h²èRÒ]b,aÌe¯ö2RZ‰$ò6ÀÍ0ËKüÝ7†žC°Ð—•9)Ë ° !¡(aýÃ`)vÕÿÍVj½N$°KFJ¸j“!H ¯möXTmdgR|ºÝlIðÀ~è³ ÐDÐéYõ_®š Ÿ7ÅüÛNhó µÉ~~u¦1^¨±?ÖŒ‡’¢Ûܳÿ7Ρ%ì†ø9µ$jWCYª×U¶¦mô#U>sk)j:ø:§ ó¹óÁÚ­å”§KŠkp<ìs ‰þ >ªQ=%–Áq}áE|s0C$aáï©?”Ë/á8uñ¹’FóôF£]¸¶Ô3W7UUa¾†R©Ã<Ú!}EŽ!‰°:ü~‰¢ Â((â³ÍÁÕ²êcM87fséÑæèÆ×«A€¢Ò.æ 5¾ /J ïÿÉk¤Òƒ°]È\û  C£ˆ>2ùòã¢e¹tÆ£­zë¯êA!Ô ¬ûtˆÛU‘V^˜nºþ›¶ˆA¾I$yÇ%¾륢M €¦@ýp„¾Ú#µ`@E€ `1žL<ˆaäÒþòZWê<˜Ü,ú‡mɲýš“03fΩí=›!-ðজ”(D®HeÀ’ªðôPW(ÌJDÀuwþàQë++†!¶ÐX»1 ùu¨’IqàÅ, Íi¦§õ=Ïá¤ú£&qgúkr+óÁãÿI 3ÿ Q>+ôƒY^[+hqïg «"fEùú¸!|Òà. rR¦JÝËÁJw)Í(4ÅŸØlÍ‘ð‘b¬‹G¦tÒ’ ˜]M¼§!Âä =”€ioâF`½õµÈ£Ïq-Z8gQùÈÛJÃÕ-ÑE„ »s3}´xÆuwDŸ3½a k…cnŒ¡º…Àj€T¼+eè—éñÜlÔ¦ìmH€8aÞQiˆ(ÚeQ“ø:Ž<‚ö¢WäP'ÿØFPåMm1{ýÍŽÂ?ïÇâì°ù¢–•%àûÝö íXgp7%~@ ÚR(`R5OSd¿V¤S3[3Ià"ã"UƒÉL,xÞO…G¡Ä _Ôé¸wÙS-™€LÀļe,‰æ´²Ymˆ™ýõnyÄHôJÎFº5Ê™ŸäÉYÌðbÔ"Ðb'G÷œw®wõõm¸…Pdõa“„¿òéϘïi¾OúHƒ¸ ËîË:™V ê«&áš3Çð«—Mo†çŸl(™R¦¨ÕÅÚÐ!Ð2-Ç ™!’0íá^¿3r†xàÖ™]À€4à€#Üï.½^c7ù–°’ú§ß„%ãrÝÍA#R}»¨ÚÆg ca ìØQÈâÉÒp½‘ŸAöJ\¬²SÁZ Ù³ Ž ›ÊzH½bq ìሠ0$·ÈqÇ›Ë÷•Ô–sõ)‹ÇŽKÆËJuÕÀâ¤Ý&uï £n‚ò‚aC°­ô…+ £;~ap•QÀ³ ùIø˜(zZ`Y-zËáÒ$Ê (x†×fÐ/46Y¨WÈ0bò£/ŸôZþ@@d‡H!(t8ª{o8È"ƒNö¡~RÔ¿UÔ€äýÒΦňë!ªþ{ͦœp6?⋹a –€2 ÕN…A(^˜Öïž™¥'R9Âoe•ÐUW G$@‰›é>DMwEœbÝy{V×džYŽhÁû !cójÑlçª!¢™¢uŽŽ)lcnÀ£?UW†*µ”ÂUjÑÔvÇd£HZ´Ia¯ÔÁ§c${´›C-ôëIø±d(­5¥“F / U­Øw´'jâ/épÉEK…è1K9X¯[‹o»5ª€?ñÎàøð~ÿE2^7H0u•8e©oÍlÂìÚ6%±¥~™°zCy\!‡@Bµp)!T‹À?À9åå°§üv t™j‰Ž,Òõºô”_>rÐ>jUUY£ôÂ;…ÆÉÔ–x¢­^LviØŒ¥A@´óÓø fa,š*Œyt‰h_¾ û%97I\Ýê9cWÆØ¤VÌE$¹õƒ›½ßñAIšÂd?v¯6?ócž‡Øö[×EÃ8¨¬8.ŒÈÒ'Ü4<Ïr‹Ê§ö쓈ã+² ž.’l~6]oXK0ˆ©¢@· ðäs3&nŠWjÀ5Û‘BÜ]^Jë´N˜‚2jíð°¯Suuõ€{5Þ¶‹²@šXüÆH ÷¤Ç© 8‚õ@¹I3ïˆ nÖ¢‘·V¼%÷˜­þ÷{ÂÃÀ™ÿ·€¦ÁwúXð{Ó§ 묌ú‹Ÿ"óŒdô^“qNCfT’»Äì*º„Oà¶Û(›×MIÿ ¾œÃ4kF†SéÄÀ ï“-%X¾©qÃ˳£ð²^pŽÏ™Q0g1¿'êå(EŒxÆZSþ~Œà2-‹¢”"¶$X D#ÊŠÙ’fìúöÊÉœZ¸Ÿg:1Î"ŒHP‘ü-¿Kž’”LP“ücü›†lgmì?µvÆì€â,9ÃZ@d¬ŠØÍùåÃa’£Ö†øàGÕ%Š?IäK1Ö|dØ À(ÚnâEAfn®óQµ-"šo=ÓrýuŽ”])ÖRÁAù”Uzn5GÂF¡Uei!D¦¹Âd×0(Ÿ™ŸhÉ+ üÏά^bô ·¼Ãp5—?eyÍÿžÝ ´Ö~¢±©VÙ6hh††ó–´ºÅâ¤Ñ†J oCõY ÇAvV˧E/“pû<—8ãù€Ññ´W¢jëßÐÚIR„Þ¸–ЭŽÂøRplz¹À º46S¼Èá²0N· }°‡Âa¥—’—ÿ&ƒ¤ÿ*=ðÆêRÌ:l—ùûðôN)¡0Ÿn!àÕp³ ò4œé’øPº¸lg®ñ@}énˆ%ý#`©Ä«/qIV/ø‹É!#O…vÜÑ*Ëð 2÷éë3»s¼¥Yx!VŠBR¤_g^=d/‹‘ãcQ/[àÕ·û_‰6×x-ÁÈ®V¤:+®þѰN LvfáT‰÷C9Ý-b9|))ò|¼nÂEòÓHƒ}aï6¤WFÀT±]oìÁtg!hbNßû甪ìc¹Pp熦à_ßø«-6ã”à( Y(Û²Q:™ïé¾–E·œ>¬Àv_SHð5Kù…ÑL•­!ì儹ÓYK µ‡°›,¡ pã ïÞ¯í¤; 1"Ô±Jê §ÜV©[³VMÚ¨æ—ÚÒžÉRÓ-E‘T¼ÿ“Çñ®L_È„YͤXEæûjF÷4ÔÞ`…Otáƒ{ÓQ–„Âí‚´à6m'#lðŸr;Ʀ¦‚Î7CÔ¯*â·|½õ»5­Uznf6ŸÁªþ´26†ÏúA$ô=:þ+fj‰ Zî¸ ÏŽ¥ ex@±Ø2”¦Ôg\@sÐKbuè;ˆp¡80Æ‚yÔxŸŽéÔi”]ÕZ-/GØeJtK@L 4òý kd²KAÎ_¨­Añ¤Ü 䂵ë:Ãù´ú×*œq§¢iDEDDM'Ò¼d#ë­t+[p=ü]×H}Åô1öMy»#)°í{ì;ªˆ<ÁÇ‘fr؃þe?¡Ý7Ó#0¤ÒÈí=guT€yhIŽÊÏ¥ê¤üÔ{:lÂOÃPå×.e¬„M¯^I‡Keœžl0Þ¼I—Ü_IüÎ]ØIÁV“Á{Cݳ†jpfÊÌúÐsÚq´q:½‘ " ÚdØ_h Ú ³L¶ÖÀnÙâþ^£S "žalÇ ýÜsVjÙþŠ„«ƒq—%õÖ‡$FÇo¶‚ïcQ…nÛÂm¤¦­rÖ>GAy§2 >8×j÷n7`ÓØLô86cawOê Wÿþ>a”ç$‚a(—Ë-çN74²Ôþè8Gkƒ`û®_CèW™øxpW3RNä!‡A Ãaº Ò¦fOÊæKÕ:b}Êó3#IúP”dó£©O> K¬¤Œ¸¢/€\/ F»pdv(ªŠÑ³>⯠H@æ\¢™:ñpñ© ñíÚp R6KûJü‡¹ÍG©ÏFôm¯"\íDOJQÜ”|v ðGa̹™×E°w%‚ù¨œ écF)Š€'‡¾,:Nœ%/U~IõhDiì¶E2A \•`}²S ÷mƒ@ Ô’ñsádJÑÆ¥º©\~öˆ)¨Fí«åàXPSþ:LKYsçâˆ#:cU˜ ZØLrô5Š˜Òò™…Ä×Úpaî§:ŽÉcYTÏNЦ†P@›//ª5’3{Ñ¢‘p„cãæ…‹‘0moÚ¨w÷èm‘c㟎ŽhƒÂõ!PÁva÷ËgUÎqbjQøE1äõôp8Ê X>¯Ry†‚”¸¥rWKgc³lEàPXå’r uuê`è²)±þß[-a“oG÷R{¤²¶¾"(Ûä´¤!Úè biyÏ#QÛ®Q§O—àÍ ‰Y˜a𫆇°$ï\_ ¼+FE±õüÅ!Ü0ƒä§VJ±‰IM¹ÈØ|òùÖí°=8¯ÓIËFV%ý†dÐ"käÌÁ)†Æt)ÇQŠ­sN(5r •±çnˆFé€âVÐOUÛì;‘­¯â°É˜\‘2—t„¯›<=HPË6帑ôVM¼_I™A «`_è × Oì‡à±\šˆy Â/Ò\„ªÞ l‚¶˜ûU¾µ–TäKÒ‚¨­åžÍFŒ +Vƒ\àŸt&ê –tÇåÔî8Æ}•mÊ MYdØ‹Ò{Hêa‚º¤Ül›f qVO n﵌܋7±;ÄvÃu£í—i6 ™R ý†ë}jLÉK.<>tÎùHD¶"À–]í#”G½vôH/ŸÑÑjF¾§ÀœçÏÆ|x$lH ÷Êë"™Ëìp°jË'÷ä…p©ÛHĨû ùˆ«zCw¦Hír½²Û_c!y½,{Òâì‚' Vy¶2jr&ª9h”ÔsŒ49uÊDfRlE‹×¬eÀø͇dÇša„•—ùj1;=l™Ò·Râ(¤Sù«ÀÀ¸MËÏ—Oô÷ ‚¯# µP*BmM¹j„WºÀ e"„ñ#ÞìÇã1úºÿ³¾ÿáq)r’Ê€j53v‹¢eÛaÞ·@uŒØaÏçpÐ¥ü °ËsýèìÃ>” Ô¥c#݈!™ î6[ÚòònïèìäÐ 3Y*¸ü|tÍ/ãu+ïžWoO÷4~Q°ÎVw~iO¦­~cx_ó ö…’2Yß•FÍ‘1€Ø©>oÐ{S‚'Óqµ~©f"–!­·(浆‰è€MG"´JÈBøvŒÖ¬hþªHÎçZ¡šVsÚã0êƒ K潎L4Åxôaã#LÊÎ%ƒç€V¬ßwîCz%ÆÜrCв5÷ŸæyÏÑ6EŸÔac8$@­ nœ¿zÞ5ôíhnVX¨`p…Að”bÛ’ÕB’||oØ 9³B5_´§Qée™yªˆhźqfL/°ö6puXZ¬ñ`IÊÑ@ã ›'%%ê†sÕ0aõ &瘚Æ÷y 1<ñ¹34•Ÿ+öì[¸ó…ÔÁyÎFPÛÞØÖ.¾ÿžo0Ü–ÅévÎtBÒ'.fÄÕõ$}$¢JVÓ½01›qÕ YÀã3眥.‰\Š‚­eðTÿ¤|õ»ÙAüÛ’«SÚHÝL¼‡<š¸ÍJk0н~M¹åc~³ÿZð³5„>ú!ß\_Év¡©?„t0^ˆí¬=äHÝ® ¼™Ç$û¡"Hd#øá)/Z .xŒÉ9Jh‚Ò@â8îY›ÿÀ•™Vc–³}xÎr3˜Ö ë¤étäP(Ñóÿ·&†Û7rª›ÐÊÉ>§x9Ž®™Z8íýÛà®mlq‡¦Ì^GÌ èäwÐTÃÉ=ÅÔfO"<xPT@÷5ÂÜNð †`b%ßo¬´l‚ˆPcMè¾°*9P"ÿy8“„fwí# ùï´? +#¶Aíç7˜Ê„1„(*ZÆ TÝ´j›¾dúX5¶é7ÙkÿìR¯ì(*å¼ ¡÷°dj䂱Xh;ùïï·Êâ{ÆÒç9wvÀë {ƒQ)°b'àKŒJ×À ÙýKþùÿcxiˆÈˆ‹“Ý€FžEìbDôE†¥ ë@‡¾ýá!I€€_…#m¾Sÿ玕_Š"˜t¥|P˜ßµGñ»àÓΉôFÇæV!Òðâ•cÅã+FbÕùT Ž„†pXã_·n â, ,;ÈŽ¯œ X„Tèâû’IUØ™ê{‚P$øé»BE° >@”¢Žãÿ¾b,° ›Cîÿ>‚×XàROXoŒWë®nâü}\Öqc™;i]â–(ÿ…– ߆¸ÝË& 00mÌJ‘ƒ“b`'—z6CŒ ‰ñÚëäë¼¢ïñ'ƒ0£„¥ü½Æf˜ö—lóà âög d M?ÙGáAwa £ÿ ø×#ý(ÞvÖøåì¬à ¢üB^ ÛÒû,¶äø` „ ð¤fBYàeÛäø4Ôäˆ5¥Ö«¯ã®›G2éƒÞ|À›ã…ý£¥CYŸØþ‡7(‡çBG¹Œ¹  ¬  =kîW@^@¬ø-K|؋БÎxÒ1yŸmø–býCa{þ EVv$K3Ëã{e™ä5öûxc-øOµàÄ«Uà¶\L›ñý9ª"̤˜µù–êsÂo;Ašf œóa¹¯t¶ÏàMs­§Û°J»/«è‰ Üä†×8!k΀[¢ÍÐn^ K0%‚qWÇoM€`ʪ O‹h†"ZÜóæÿ?+È„‰t.Mág åI Á6=Ù¿8~¦ß  ÕRèG­¾wÚ~-ÄD·CÛ;ô£uTa‡Ô ²‡Ÿðn1c<Ê\97{Å­îÇëèö)V«zÞ,fœÂ þß”b‡''g'ýhœQ}‡ñîÀnÉÇÉÙ¡h,Ò .\4ˆK…IáVKÍÙBt½~h/ÑK ¤Ö-–Ýâ·°ÑñÞK¢[ƒßSùrÆ¿x÷7ú+®gº•>òNl>!ϻˈ-J!Ü:$ëU[ùqw|ŒTáE4ªGëqZxŸΧUT\½Sîo6—Måî,@™/ÿZ©Ö³ë ³‰ü+Ñ©víï *ƒþ ýùýTkUþ0‘—9æº_à΢¼Æ¿¥ï¤È’0°›+ÿÿ»ÿZªÕsZ´G×T’›®ë§ÕËÈ‚G…þÙ˜šÓqs²ÁÕé<<÷å‚÷~y}Gñ¡gõ_ß0T|t4˜F¶\‹7UMãqû¸ h~5Å[¿XìmfUÒûí¹ ‡(L뎺R'ÝCdq{S,®z褣×ëX ,m9pÆÈ~µ>Øé-œ?æ‰ '€ÿ,McÁ€Yi68ÿ‘-·ë‚ì  §.Γ"Õ¡)ƒ9ƒXô”–& ý'üXG˜½˜±ê±¢|L@¯XÕ/”ùÈ­“¥e#AÜ ßj°,Ð(>s.õ$l¬UA/Q›lñÿ¤ó,×d~µýfWz.UéŸmA [\ž[hA†S Q? ímTR‰g{‘ÃCÒ%ö'Î…¿úГ·²Ðµ4ðeR¡H:µÀø:„¥Õ±î+ÿ!ûS'ÿʞЃ_¦ìá7wš_:£*õóI÷k¢äÃòZ=JHsY 6+±=éOÅ—+WP-]2í5È¡4"fDû9i®’äêi•8ôÝDŠ}ÿa┎XÞHëÈè4G!úˆR¨il¬AŠÇÀQàÊP4G¢á2bRSn˜A ²ˆ!xDµz3'"›£–ŽÃC)ôŸ—Š5; ‚eÊ?Á°¤Ø|@à‡ šÌÕ•sVH×M³(Låà*aºœ çqÈrNúÜkRÊÑ" ¼ïœÏxƒ^=94ÏëOš1·”„? êt¶åÏÎãnB¢*"³)åKøÍaÎ¡Ž»…>ÖBÏ|V¡nÄl]Ð("-¢q¯î–ð®¬Ùh€ÿ¼³’¿É‘ê–°Ò1X1äÓ­ù£Ç±D¡¬>pÂ8cú@w¼÷TC  m¢ºÁµºCQi9€)BP9{þFÕaìÜ%±X‡s0ê;@LPH&3ñŸX7mxí æäîB·!ö‹›*>8ì ¢Ôò=ßÙ|Sl»v¢Ô[O/÷òoÕ="®ÜÏÖÒ?Ô(ŠˆLÑüfªÎ°¨¦à ‡Îw¥ i+¬TCéMLùInp\|KÀû©›¥dÅÅö3†„ L4G€€‰j:„‰{ßüA,…€Î~áûæ¿ý·äÁŽoCMA'õöa04 )CÅu á|ýùM™kQs×}¬fÅ󦇲jÿí<Ÿ2ÃήD^U!íQRòÎÿ,.ÀÃ`Ç”€ zlPIÐ'€` ¸–¶™³ÆÿÊEä°"(Ïñfn#aqÏÝD^Æ<¢Õ'Oø.õïÂ×zFN› YŸSo¿PÐx˜;oyÀÈ÷ðü›jž‚;à\A›ºZ! ]:bITF´Õí?FºhA–yµ$À€©Á† A/‘ämÂàíÊGL^<WøØÜ·D|ңݢÏñ3××3lDHÈùDT 1$Z¹°?/æÌºOWàõ¥9’ ‡NØ•i´¤´š•Ï_ÔѤƒÿ½E˜à÷·˜ÑÍð03ˆV•±î÷,Y–)mm¼ EöÈûðøk­MÁçÎÍb_ºi©¥ˆ9° I‘Tˆ£ 4;y/¤'E¥ÛDzûX©™(š»°‡Fd¾JX»e„ÕQã§3RÚô à»d·Â´,z€`ûHž‹ü €„$õô·óÉ‹t߉l†â&~ëvˆ9ÂþȯeZéI+§¢Ð¾BaOVÂÎÆç—½¦‚€„‰˜Ö¹sW«¬Òz†o†a $¬•@–$ð)%A½X¦¨ûejàT\b*~aň?ÏAD݉~焈À=5©KJi ™ ÿN¾BÌv)Ù-Hr`.aH4å:I0òÄâ.è(Ái}55øç[•[‰çlIã-†Õÿ±\jJ†îE´˜Ò–Ô’~~8˶+ˆ´z_ Á:±½õý HÄE2wbûx^G•U‡c÷@Ðõ)kT''U“Ñ–¢WÛsí¬Ï[JoÿˆŸ]´D’5lÝŽ‘¹œ-¶Q 5É(©ÄÖšw8³B¶· Æ%˜DLÕ‚Ú´zìC èÞž=Ø‘•×P%,­Q+`pèGà•/Ns G@96¶ÅÒ|47@",¢è>#É$Í2–Á¨€h_F°s’ZOº…<6;K’E‹ª·êè:_ô¥T/ðjá›ã ­ àÜ$S‰>™:N¥äõsB€fâ€P3GÝ „ÖÀv§ž´&„$7¶*p9h®uïÑ?!¦Eû %˜ü=¿Jаó™Îv#Ç_E"TÝ*ë–N¤I*‘04ÑDL+Ó-ù«ÿ%ÇsLBq¨Ý)gÔ@vG˜ ²Ï GBâò(p5XU#g4%¿`>ñÖ5¤²bxû,—ã®› ŠÇ”†+ͱw¶Kpn2½S³ÊIzœ‚´•ðïõÜᥓ],~ÇBí ¶cú!¦Ò`”ÍëT…ãʬ¥9x=÷Ôð¾À½)<ƒ'ÂÓønMa%k[t ‘‚D|x(gdñ—4&èJ%2L®™¡KVu@A¡¼Î!ðöƒìt¨”„ŒFXT˜K˜ðjßÂÑÄ‘Fã£^ð*+XÃÒA1&бz$¿†áx!©‹©òȤԽк|^èi´£¤éÅÆ´®>uŸ}fŒ‚ËÄÙ‹nß&7xãèA1Ž6Ä–fT¯ CZ}e.«  Úã1/MÂÑœkb˜®åtTпpĶ(Y¹Ê& 9ÐýµåƒÛÖþ@}µ8øÞÒªpº­[X?çSŒWÅ&:k°ÃÃ3ÍÎý…Œ¹ Ú&¨é­öE1·ÂÕ†zÑ8ù ) Ü^O…é)æƒÅ³$q¨õ|‘G;(.Q¿"p-0UÜ!õ:±§…¤.ÇÊÓ™H܈šä ¸q¢Ð5eSsFÕj ãä]ì{ÒdM‘k;Ó«ÄLé ˜·‹Õ¦@ÞLzÑ“T<»BÙWŒí²V±r¢•Ö˜£°H*ØÇò 1³~m„Æ2ˆÉÓØôä­±)§"q‚È…Z–gà¾Ã"f66k>XÁa˜1ƒÑÆ Ö­Ý© êDE•Òta-$ƒ‰Z@†ØY ¦Dot\à<"­—`•@å Ÿ}€ùÿ:ª;5;<( ‚Z«ÃÜ[†,ZRA@!P?œmòLš¾„y %ÜÊ£ãüܧ™[„™/y­«­îºh÷b,£bÃÖä!T%LÔ9å ~ _|[мަM˜—ÿÄ–ôzþqmñAýé—M–TÞfI-›’¨8†UÞ˪ê8.fŒÄà¿Ä²Íš‹u&ÿµÿ ÔÑ<·Þ-µ 4–(9ƒª-L”Eâup üg>auˆTg¡J«l9-žÂÝlâa‹à¶_ƒzã÷ G–/½!n)?ôX¶V!5SÆ0C ÌèIî’ÍÁqÞ¦øÀwØ‚¢+» ›¾ÀP‹ocbnZ–L`¬– ¬6þ¢\ !¦eª7:¾&x[chøe0Í‚µ±=‚ÛÒ'^7feiƒÎ\%gZ1? y hófªy²V-$*@4¨ñ»zפ¬Wšˆ½üXL4CFg¸4ä1Á²ÐS3¥‘dx°ÊÒ$áÌ€+?-5Œ]Ñ©N­õQé‰çÑOþ2±=@Û¬ÿ‡”§*M3èÙ±äþ¯€²¦%T‡&GSÿR5Hd&a°/Ö*qå”j„ߌ Ø"3„¸íºÝ‚pÉÎPœˆ:ö‹£”ê/Ñ&€š ÛrŸrã]±¯ÎöÁŒØ¤tþÅ÷¤“ÿ.$9³S°M³“–º´¤Ò¸ø&ˆèÁšdo–ét0³¢ìl¤UÅeèÈÑk7i#ÒÅQ)Ÿ‹–4úÉRù=W«£¼Éƒî#|V¹>™~t”VX£U+p«ˆ9óÿ<à-"5ËÃÜô´pÔl&ÚP†Þ ³k[¾ÜöBNq“1?ƒÄà%]I*’ÂÒvH­b ¹×‘u§ª#‘!€ÓUxh¿!.Œ*ª2Âçèœæš"zN©•'J¬ÉAqáÆëî UlbOŽíºkCïâ7Œðº£âiÙb´àVY‡–#9?S1‚¼PU6ÙbŒ°” ûw›Ž mhˆt2…>3Î#"iEÉ׿éÿg˜C£}0eœ´1×îXa(z>|Î-54âÉf²K·˜øtp²g½è3ìÕZCÂå:R†uÝÁi"€­Ø®ã_ÝeœæÅ}M\ŠÃ’ó¥<Qeç÷ÖAPÆ@‹ÌõŒíÈÍ­ikÙvÖ@À¡bƒ«½ËÏ]ÓÅb«x|1p—²7).4ÑSÑ:¬zýi0«þͲþ•À¬)Ÿ_BØÃ]w'FÄ%´ÏDû7ÚÕÂýÿÁ ‹À)¢+<¯Ù­q± ”CíÒ˜ç<Ø´}©8蕸]qÁ½ÙØl°C °ì"ýš`0Ø™Ÿ¾»ꡃq´ŸäCTüóup¥Ê«Â…54T\b"<Üz:ÑD‹‰,+΃˜Þ¬;Ú}´ Ô’,LR"–v_ñ%ÖšOÀ¼N”Œ¦¶6Û€ôóаÞraÖKó_’ʧ €·8ÏuK¯òòÁ@ú6š:ζDÃiȬN\")†„Ï-°;ÒHÛIØàd³E@(íâ OJ·óó‘ûÀÀA uÅG—¿·Å*Øp*aàzi¾- ù4âž|•åïož€Ö—-Ѿð–ÿ÷y·&l9ÃŽ¡ ¸Ý‡€0Yâc„›€—^XxÇ6‰àé"2fÑ6P5gïÉ¥kÙ‰‰gyH§Zx ±‹È÷\B;ss1eˆóàðÃtÑ=nÔŠÒ[qE4xqàpæ$Ʋ`Ñöµ_ÀŠ›âMWÓ(1ìÖ;ZìÀ¥<_ja^‚(5ã¹6ÿÃÊÂŽœ$³»É,¬KÀ7q¨²O@igjðch¼@wù©™˜¥p“¶&ž¨‹Ù5Â, Ï uŒï-ÙT@þÝåÀZ> ( K.Ö2›‹rÿÙžHÍy12Ø\ˆép­ø*Ü4ˆ^ï²ã…ÄHU]E¶ÁA}\,{ò³1“1‰(bapFb@cÊûúˆa0ŸôSÿý˲;‡Q°ŽëÅF/€ºïž„3Å›ÍǾüG¬£JÜWüt4ÛÞ;«_‰µ=虆˜HŒz&Õ¸Æ ³‰ÀËîS¾Ÿ²ò,ï¹nC "Gêþ$D¾æ^¨ØÙLbœñ<¢=3ØñrÜ1z)´¤Ø;—6V5×4E˜È”xÀŠ-î½`x$wqͱû›ú˜‰´`RVHŸ¶íøWÑc’ºt]ŸŠï:éR¨Ü¢ƒl”/XÒ<^*¡`òÜa™ÉYÒcݮԇZhJ$Ü¡·Á„:Úeé8ÍX™Éþ[Kwr+ì‹@ëPŒe³Ò˜øŒc#溞ýðQ²¸Xʶå‡7e)_ƒàD ìM˜í/À1vF=ãW ^GÁEu L?¢§­¿_ýíN”ǰ„Èïò#¨n'„˜ÞÉßï¼pÎO\~+}Üpqmöqž½/èÃýª6’¸÷|Ÿsm„Úä§#ئÁqàý& ÍƒFü䩞41ޏؤZ°ü­­þ ´OÓù1÷Pûf6jâ–ÁG1mE¸¨³³ªüàÂ~Â]½PÈsfJäVÒPeº°0‚QUw ’À—Ç‹Mû¸÷(¸ßD:]ÌFzX.Õ8wÆt½2“xÑå ßxF]æ©S×cH^J¨ßÀˆ¨`ÞØØQûÏÜ/ŽÞ£@4gÿd´)cîMwÅãŧL±ê\ü\£º€´è =Ü3²Ó°Qbܲ\ÅÔ˜Ààc)¶ÀoÀÚÒxhT¯˜Ä{¨Es¨5ã÷¿ºðîJŽš2µãp2œ¯]‹4'Ê•0JÙ.'XB2Óà2@Á¹˜¤ðYÁ„Œܸ0UNÂ)~]ÇýÜ}rÙæIVäj­K@Ç."‡¾¢~ÃT'å@™C˜Ö¢ø0@j”OJ$“»Æ‚ŸÏhuÂs}÷\>"WÎ%7“þg€8gœ±òãŸÔ¹T éòqÐtˆ6ð¬ÎZ ü‹- ¡Ò‚E20à¨%¨;°‘W†<Ù_,‰:þ0‚|EﮯOéó«Áß Ër1{T\Fù‚W-`¿i{°ö›y“¬ú³oÏÂð€­£:VÁ+õ‘l…‘ã®ø}Ëšë3• ë~$–“²ðhñ XÈqñ°“Ü :0008±G§7û§‚^ŒMé«ú~?ûÜ"( "lƒÚg–C¾fþïÿÄpVÃÉ‘G_ÿ3sR9ýøx@-ücÿÊ1ÿÐ|»½LÄõÐÒC/!2™Âút‹wðñ_Žîó*¸K}Ê×X]ð%â)ÀÙsTӢ˻8s~긖(YÀ܃òêDýW;é…Þõ/9Íê‚f¥Ï˜Y\Q÷¾ôš("CDh‡õ¹{ÅÚŽÞônûß¹«§^`¦µ½.®ï.w‰œõ©EV®*«iwºßÜ]tÓÿªrUAGƒ œ÷c…Ã.T²QWªõÇèv/‚Üe—뮫_ªéÓJk3\JTWžÃ@ ~/hUýG_Ö½eÎ’ŠAŒóì—ÁÝ+¨Kâa\‹OÉ¢ßüê¾ÁkÃ×(ü°€#ø+Çð:õ¬|¹=)¡¢¥Ä«ÔJû¥ý;}U)ÔID+œ8•úƒg’Z£ÏgŒ©ùæ³Òo¼€á?Rþ†æ% ô¸ GðÝ…šaâ@•‚  ”?‡˜˜B¤á­çª j`Dĵ|ι¯Ý‚=Üp ßñ ŠZ¢%äi,G$m¬“ÂŽ&’ áM„‹O‚æö|ñ•±VUáÛ(>Ã`¤d!ƒÿÑQ^€ fÀÇÀK¼wÒÑ©È=|ó,{“^iá¤6ȾJ›[ˆ|öˆŒXŸßiΑӦ}€Ã Ç>¿¹÷Sšë¢ËXkkÏX¤ú*>íVt3°Ä×vJ0Oc,jkѹdŽ@¥37Š/ÝË 1PCÉSC`6ƒáç¤DÏò‰ .ŒmH¶UJ‰ì¹’ TzŒOÍÙšè-l¿)0ÇÛÚDú²?/”´¡%ï|;Àë1‚"Gr‡Ý!ª.I$‘ûòû ÓE2@kÀ!eÝ¢CÇÃVÒ@Êïýy=LžIíº n™="`óÍeóÊQ¤GtÛ Û“©–@5Ñ™ò¤é× ö’‰Yxœø(Ñ®Ž’ßRrø @@Ѐ”`“Cq…ûì«n¼a'±s«^ÒAÿõ‰ ‚ml\á¢øWšÞ1°‡G°Ê3„á£=40>L#:¯é'í¡uÐé"<4A)V€Cê˜ÑG?Œ•1Xå¹7»Ê¹ *€€¨$Hµ[ž”EÞ,¹¨v:Ù¤ëÿ“hèÖ_™ &·ó6¯|6}|Q.FxF«K:¯q"ŒO >Ë»ÓÊ¡äñÐ AаÃ%p¡ï%j‚œrq£@ ðÈx ó\˲§J·güqe@…š¡‘™FÓ2=¡¦¨”3¨{s  (W–>ܲß\kÓÝOØ«*Åw¤äd×D=,nH ›a@aLOqljàjôëÐãT¢+F¶‹ Búß“í’KO6 ®Éf@ÆGn)zNIÄ›7LÉ´¯³8OhÀS})µ">4š{{Ðó2!¦}0„:\¦Z‚kÃÄH½:¿4xu³¬ÓIõÎ,üïÅ0¼ (`/!³ûdXãY.†¦P}Z8S”W¹˜å.¸¨?ŠòFN¢ êS1Öã³C@ë‰ì,P½Kj')ø:£@#Ï5P d.‡Ï÷¶MKCÏÙEoS™á%€ š•»^c-®ÎáÁôe ÕePÉnË¢l Œìò*Ý×þèÄü€žoкMï³  48À†ZcÖ@4êpypf¹&4åeF(ˆÐ$+à¢ÇëÒE l#Òyþ/¡|â‘ZôFÐä‰"ûà´"ÚG¤ñ†LXŠkÞ'ýÁïcë@F.¡!1;Åñ <8ë¬ýšÙå@C;©ˆz.^è €Àà`åœí0¿¾‰x\åê«XÀC©òlDeÉ\@À*_ JÜg,A±NŒW›ÿ•Ét†ïV áUÅdâôx3Àv/X®Ã DˆÆøkniËÄ”" CpÉ(˜.ÉŒU³# :Sb(u2ká`É„œDñdÿðz@;sK`2žL%²DŠr0ÙÛI;IÒÛÁǸ*„‹ÕÕAš)£$^ÀÖ;LŽ/éM%­‹jyÏ‹Jè:(B+ µïÁjÈL8dPð©¼÷Œ1Ç›ÿ u²kìàyÀöõ´9ÂZœ9Zjß]ˆ%hºê¬^†ëÝÞ ¡áç¡àøÒadÉÀ[ˆq-þÝêÂQUôä£aJÙÂUuåp>4ÿk¼°þFji uS=æì)”U¸‡ÅH}Ò7Gw:‹¤ -‚޽zØ« tA¨{[ Aiì Tl{ðb0Wb|ÝÆŒ ¯Ül6nð#\¥‚¹0Ò«Ó ÷· $ó^3|ŸÐÍÒAw,`·þÈ0ú "é¼ÀÆ3© 2Õ±¾›&°4æ¯<*ú =—èð F–1LB¡!Hd.И¿ž”I5YBÂËK£, Îb~Uakr–ÓîÁÒâǨ'N,™jœ÷³ (!çŒ.>”¡ùHB‡/¶êyPƒ€ ¾Ž›º,¿ÐœÔ,_k¶™º³bnèÉ-De·¨Y®²¿?Æ&b`Ü8æ @í;ô+xŸ5 ‹ 2îÔ/.)H³•¬^W£«±Í‡ÁPÙ-÷^Þ‹útØ eA%‡ÆpZ:Ñ}©Jò²G÷áþ{Ïy/ÐÈæ®-c ×“@PÓ#€ñAºý±[hôÐÀ‰+ôd»¤E@¦g)%ް»ÒR¥‡Fa_TGY ²Ø½»`2¢ÄåD¯Kÿ#BúðH‚ô¬¬ÉT¥*?a²ü §IÅ‘zæ7Èó ÄÁÙ~…SKk”²‚âËÖˆ#Tí¤ÊO½¸pxýb‚ÓQx²9KþP°b )0)‰ ƒ™K$Ç„ê€ÁÔÆ“:¥À9^Ë, §NÉ5r@ÁåλyÌËð}šÓ(bÚ£–°PNù%0÷\côÄî&öÜ›ÒUÂÇKCÌäp‹æ¬±“ Ôò´KÅÆû^0\Â@û=ÆU˜)5+xÂi)­o2AÁª…¤¿3ãÓäÿþ»Óþ饥P·˜ .0uúHˆ~àJC™é-€íŒ:d¨h1ØíL¦‹þîîÓ“¸q«,hPè ¾V…¹&«„wnŠ0‡²È?'w½-yØ›0íEŒÞ×PãÀ  3kѨšÝOáüXÈ› ÌÂJ-IJ`rFpÏYQÙºOBaYhÄ}VÒ[Ò¹«Œ†2çƒ7¾oü¢ò~ߊ¡Štdø~)ã(66î \ƒæHÂPâzkIøÙ…fÜkÛÉ8¶ÉΔû-€Ä2j}D±Gßóh ýñZ¾SBÒÎJÿÂ]̽(’lGÚ[}IiX ƒÇ³›-„n`O©`!ÇãµO÷ÉÄYä?@Ã%Å ¡ß«šþÈÆ[ÃVò¦bb˜ v ë×tUû¢~µ¥,6K#Ô¡¨hnˆy¶Où”`@ï±g€¨  ëÞß…‹yÎà”:|ÀÑË„ð”,Z€x4úaº‡OhŒŸÕò¤oê%›£oÔæHÊ¡c?ç£fˆ†>Zv€ïß[VZæd?BÝâIèd…Чa°ä|¥’{Ø_D&CÇnmÄwØ $ûÁãF£ÿ¹nN[” Ûm¬ Ûá–þ_²ÆxsVSPOÁ ,%J —Q&=(O”©òó)ÃËê%Ö1Œl`ô0ˆDªþ2ýÜ]Yd(&` )€Éd<Œ°áñ^aÊ ªÅˆÖ'‚þ„¸®ôÝ‹Š*: >i-ž^¨ ˜X´`¥z%Vö)þÁêן,pôG‹(ø¤|~cüstàÚÍ«žSŒ2Â7®*ÇKËÆŸö¼ˆ‹v²ÖgÏR¡EE"c˜%Û"‚)Dvs±ecö½T=žjÏó,*@ÑöºùØT^ý.CÀ›òŠH}åÝ#0)c¾\PͶqû f{ŒÑ÷ÇFˆµ‘^<è 2ƒ½;¨k4ðhÀO(Ž“÷óæ0gA÷3y è IÖú¥?)Ï97ãú`ãw ¢¹‡N—ašÂ~Ý1ð6‹Ïw›À¸j A]9H+t€w=B¬9°›·/ä,”Ð ¤ö=ƒ7_VnÈ…± µè뺎ÿÑ##CM퇒sL"¥à;ŸÉür½³¦W5XÂ9Ï>…bÜ72·– mÂ.K ï˜/-ÙFº–,´–7æÄuY >=Ps1ÞñÔ %,d¬+#‘3r7 ê!˜}ó&mÔ=øð<ˆØ&Ê1|çØ;9Û® ‡VgÀ3jÌf9+°¿´¿{ãBq-ü'¢ÀL_^”°­LãS±ºaÐNff'›æÕ+òõYAËÇ m%çÑë˜jõ¿ýÞÙÓO§é8™ÂWÃËâ}À1?1Uýž£€Á²ÿ•?°0K–Œr»+âÛÿÌ ‹¶s×myD$1waA#'$FH:‡ò3MÒˆÑ" ¦„Žò°¶„0%C€ ›ô é òœÞÞW៱àˆQ{XEáûd¬“LÓư¬~ yçðÀfß$ùEÝ SOéÐé°½Ó„&€ÉLCË:r v¼T   (XjBtÑÿ7 5§deçÖ8l¢p”ÏûhÄ"Vvƒ0Pð´mžžŒf„wLlô i:£ˆJ íDѲþØÕ‘áúÔ *H‘.±3‘³ž8€JRç8ŠÝ4ï6°ðv} F­îˆ.$áżzXØ­áw/"»v @eØQñ+έ.dÖ‚ŸŽ}ûÇ¡Á~Öé툵L‰ak8ïˆR—­¨B Ñ×;ÙGBðñ÷´eÚ €€涘Ý/„nã¬û>‚l>1®÷àøTQš™!Œ{C£ ©áÇ£F9–iàc„l§¤ÆvÄ ûAÁap @8ÖO€ù·}Ï|=xû¶;0„´6|.[8àám˜…AQÁ(ÔÂx‡T€¾é?ÇÃ= AëÐÁ`þ ÄHƒ6Ã6‚›À‹0aŒ³¿àÔÚÿAįŒé¹^LÈRÃfEðÁ#Gr ç ‚p[a³æ£u³#¬Hfˆg²˜ÆtöHáô´P§5ƒDw7[Óµ· 5`||Ýá € 2a ¤^¾KêWì*õt»ìÌQbõÄ€ïÂjÛÓVÅÖX:Â1ÐPIJœŸÐtòðµÎ3$õþbCydW£i >!ô™0ˆ?¾.Ö“1Çv cà°‹‘zt0¢bŽìón’Z¢8á_¸X¡]œa2^/e˜VôÜ7h8Aj=£`w¨Çù½Éí»<­³Æ2½Ø„EaHC¤Wn#P³TüÁ-žÂƒzx—¹ïÿ`Ù‰˜¡=©Ìf"’†“íŒÖÝ“ ƒxåÇœà€%»h$‹„Â$¡huñé)…a‰’b<£˜‚Nq58ûr‹”cפKOHÔ!İW„·YÉ€Xc‡§mIi÷Ü+‘Ç]ìÜÓ³ŒïF’ˆš›3lÑMcºê¯†t†JB‰â½ØØ1ÿÃÁÛUSÓd.q[Ý8Z¼W,¤‰æšÐÐGb(8Ï PÆ«ãp™Ä…éÞi¤$êüÔ_À$Ñ׳ÂLGïws›•¿éÊ«|-Å-*Œ–GçbñÓ%‚ˆßœ÷€QÜ%®èŽ£Vd€ì(9(È`ÇeZÿ'ãpV] ™SçNöæš5۪ؼ ÃT¢ïqw“»ê"C{SnϹ6ls“È^Õ~ùÍh'b•oTÁìÀlNÁæÄçH‰ÝYù]\w_\@ä—³˜Hºí8¨0ú‚Ö•sµŒÌÎ\n^Œû¬Ü }Ýò7V/ü‰= |ÍNUc|ï„©Áàã4šÙ —<ÝFçô…á×éÅ,ô!øŠ5K‚% ¡„“tH/öE²3Âch؃ÈÜðCЋgŸ‡ø¸…ñ¦¨´9€íƒÂ8¼€Å&?'OÉ…¡ Q=l1·GÑAÈ) ÞÏnyx— ™‡¶R Ÿà·b'CPA)ÂRFΜH ô“`|ø¦aÁc˜{•Öp±ÍXeE:ß§ã÷:$¼ò“w`seÀ<Ÿ¿€“ÿÙÀŒ³Inõ…–HÙë 'k=‘¤À 9€ ÕFo%ÿ¹“$¨k,!#„8"ÊéíŠ<¸ð0(•Ìq›ãë•:}ýö Q³ù·é•‡pÀœÃ¡†4sÒëÓp3xÖjÉJ£¬•SdÐt™ep5W3t (²`j°gUãL!~}g×ï¼>?ôLjdJ {ŸqÒ\Uqñ ãWljµ ŸÅ•‹†·ð[Éš²=H#IàX0@p - Pé ˆXÚǃ âkx’çûG³Þw±K†¸€C€£,=Û`™’´ ð·P($4[È=ï;QAÚûvhWX:Kˆ!Iþ0êT¶‡¬‰‘Tl!píç1ÒÈè (?ÃÇÂ^qåòõìÏ*0³}“€n$J2¾‡€Â4’ &Dó…"qÊfቊ"k·º¿Ø²ƒ6þºÀc˜è¥ZGúßì)¤?G*öÉÂnX6~ñË?Óè£m×ðëÇa„q ŽÒÕ…­¸ä´ÚÄJÁ ¢Ñ‘>BDÎ:”‰ pïß {½Ü(«œ*™eŽ>÷ÄdDDÙ†¬(*åÔ¡U+óã+UÏÐã(Ð0?Ýü™Þ[d]X˜.WaUèÀ,øÃÙ†OSzcxØuÇD›œ}é—ƒ0"Ú„AÔËŒœÅq Ê‹÷(Ç|~X·ô‘ƒñ>°¢¯·eÎÀÜ|¡Eïû_ñäBào‘%˵;Çø]–ÎóààL¤6EœƇoG+gÝä/ƒ½Ü_ ùçrñ'€ â±N`bŒ7€À˜:ÈÄ@ÆúÉ›>&êÒZx—Îp2ƒ†¡ß‚Ë«ô°ázàÓQü]ÝþT!àÜØ°o  ÕH“˜C^~ê֯ű¦„ú/᮹Ý. Ž…Ö CqàaäˆP0bÓ½ì}ͤÓ7úîòÝÞîƒßȺÃ/ÂÉ9Q‚ÂãY€â£žvÌ &`˜é°ê$Ad" ¯º»â˜eLužm¦G»ÝÈYܯ¬!R¤–Éû”€Ü¯2Lˆ¦s#0Øònâ\Eêi‡£ýBÚ!¨‚ÛñQSy\+l kž|»L¶ëïšîä"ò¦òäÿö¼¿!`Za‡Dâ‡qÏߎø\ø QA3vØm\! z†À411hAî^R³°ü›F€ø8d“ÇD¶ ðëJÇ»Ý÷{»üù)Åxˆ=B“¾ä;€T ê(¸Å²˜ ë“‰€"Êàû`†jh°£ïœ]ÀC%\Æ_<|Ñ “D†æ ;M{:EoÁþñ™àßV"DŠy,kÏ"Ê'Ø+ . 0Ê»( Ús€FP‰²cn(Ü–ÑŒ²)+kUÝGá` –=baÖÒøD­÷ ßcÎ6ƒvaòKŠè²¿_ÏÆãàþs'Êvt&‘»†Emæ„xì¯x@‚.ʘÔe£á¼žsè®|ÃßžPjzv[üÿΫÕw}îãCÿñõªúß«¾d;ñéôæ&ð%óUIŒÎûصǾ4>:!§Dœ¼Á‡3ŒäÌP”&—ž4€üì³]F'gbó 1(!\wÑ#EIWrA¡Î€65—cS‡Çá_:’Ù\‹7ø‡Ù½\  8ˆ?…ýªáf3à€¦…:-ïÄ@Xžã•ÔA€Ž~2f¶(Ž?M3{‚Á’07B*FÄ:’—·nƒ¶üD2`â±¶Ù2ÍP*Q–Ù®EÓg4~ÄÈF!˜u¦FÔMF.Ë ‘‡D?CÎ÷y(4(CRÁÐÚ 8ÇEþ"öµ¯C½*†.†Ñ§ë—ëeƒ5ZKEÛÔ¦,Ôkˆ¸Ã5Ô‚Áj¹y¢Z~f‰DCy×MíOxM "ð4Y(ÕºS²p´Rtº×—Pm+‘ƒ„?ŠÀ¯U&ýÀ!Áþu4›D`žÔÍ}h  êT¦ž¯Én0u0_®"öÖ™H°ª@¹Â…³  ®“µŒRÌm 4VðÝl3µ1D"Ù@~F£.ŽJ×þ»åTHض˜¨.eZ¼µpm H†¡¹,jhCüÁzŒ3 Ú_ ž¿Tâ"¨ümÌô›‘!ŠºÒP¢u¬©¼¢²&ê§PDªqþ˜àœ ‘E„ÿëuÒ=Z¬¥#”GE"4ÙkÙ$·'â¯K#²$2ËäÝt0µpØÞÛ ´Ìö¶ìˆ2—^åöd«™‹ÖÊ1н îõÒëZRa©}JPs±Æ “Ý)+âðª> !™…Ì+.€GólgÄ Ñ¦à׌7>ìŸ}ŒfDAuìT‘›6·@¤Yíîš,”¤žñZ}‚¹áCì—@H·ÚÕd2;qgž¹¥­¥1ÔàÒ»Ð0âHüÕŸƒ°¶HnhÒö)‚‰¾‚†±ådVWŠâE&¸¬"éeYUN„Àõú!çß°8—'”€„]S ‰®'Ëm*ncy¶]£3îAëâ£È`tû–6S…µ…Â*‘¶Bø²j¤:iã7Í0 òÀ;ÚËaÉd ^¢IÝ`@ŽÕÀOã¨]#TÄ Á:(0˜Î˜8ÂqÂ$‚}*ǨŸ(CüÈ8Ì$…«ŽYàåï)µ¢WñRnòƒG[ËÒ_%2ï¿FÚyöJÇ„ € ¶„kè-790XI ‘> óUÚ¾¶+tÖȽý9'´EmÊŠ¡(Qzˆ:_ømyrlb "¨Ê"¦€°ÄPŽ'²h7æ©ÀtZM`'7Ti$ükHÔý#Ÿãd6€é;éù‚Ë•¶.*3ßÿ&$00N"ùákYn‚¨Û#„à•½Jˆ ^k(߀Àý|­`Ø/ Ç„F”/ˆ j—hê<„Qß¶i=õ2 ¨5N|ЦU4’f¶†ø¬Mzp^ê` N“`aOç†"tf<ÍרÞÀDƒÇ[׈–4 µY«+ÓÈ ~NÎà MNõµì4;+xï*4¤mk] ‚g+¡`ø¢,¤aƒ;#ZX(bßM]ë^p›t„ͺã;Û™ª[N´ËûV¸#ý3ÿEóOüª29³vÕz\"ÝCGoLx81b†®ƒƒ¡2QAäãPC7 [85„°˜ÀêÛˆñhR½É€„Œ ëD£þ’"_ð­]ßáíºßÔƒ$Û~ÖK2!e®3r•s{bÃØÄ}…O  H\Ë+e ~L&N Ù‡yº¶–ò¿2Ñ»W›ýÚQ'¡ˆhN\1»¨©#Í 6‡À0‡H F‹k:ôŽ9±;„’›ÀC`ËB%Nr/xÐWˆ6 éŠÆuaÉi¢ÑB%oâg“/‰7—ÿž°N$¶ˆ§Š¯c#øÊ®˜îË [*a%˜qÞ±—ÿ ý%˜xW5ý–‰š‘±`îæËž8x£éWÛ¿ª¯×Cˆû=ìåo§÷ìùò8X„¼Åþó• ÚF.'ÃüØ4`%U§‰º8•Dè˜È ˾Y>«lÅ™"Ïû¥=CÖ§!2–×UXÅéŽêöJ5Ñ¥«Þô Cèl@òOä­Jh ü)L©ƒåxÿR®¹¡náÊÂÄ`Â6Æ5Àþ)‚ñKv‡|ôJ=Ã!)wÜ—»©¦Q®²eÛ`>Ò-Ÿat%©€)×1o± \5’+‹u¢#ŠZÌBy¼Ž·ÏH†‘ l Ì„ ÇæÊž«Y…½VzLzÇ›£ª¸NR6ø^¶ ¡”@bcð±~jЄÓÖ  #©ºè;N;!“Ð3¤EdýÝl^VP†¥~ U8D9ô…š=?¢‚|ŠtçŠÜGþš*€L¤ê®p=]>ïÇ‚…­µñš¼k×_Ý…`ü[{g„ÛIO|]’*̤Mç8ä„õ‰8C€p<<­Ð$h±EíbÄBÓHʤX#sŸÛ?ÂÑäWÃ=¢DçÛ÷ã}ヒû°Gú3X¬šŠ.êÊÜÍSÀ³(;âßO‹ Zýxsuü!mæ[|Õ£çDYæ›òúzÀ"¥‘“E©èx£1?ϰ@Áà¶»óôFIzƒ1š}y*¨Ì&Ä-î‚i'1¤ÏË.Aðê>_WQºÿ$’¡¶øù9ô8ò,ÕyLRwY5>²)|=Œ%›´ÞH{S~C H#–ÑÙ¤¨Þ¾ÁhèÞûËЦN ®X=/[è¼Æ‰/ “€þÂÒŽák©nðTR ZI¡Ôpœ`ëÑ¥FQ´>ìjŸRB6 É£?È,&~ÂÅõÑ%’µ¥$(:ÁFf‘å¸ÌAj-–GÒûO‚Û”NSútçy,\Л÷Ò“”-Á¬‚bw½zKyÓ6´ºië¾N¶ŒFWKp2®kÍhMÌ=5):$ကû%:/KFûå ”·rÅæmh>û Ãrmæ»h}QdÔ’Þ¼¼¦óƒø5æÞ˜ö¾htØú¤A_çY|p5tBªÍŒÆá¹ò…}Ý›ç缚o.Ǥ.§*†+-ýþ0Ù¢€ .+4Ç—€åëGX¦àݵ¹—5шɃÂÌëW8*‹T€‘Åà ¯ ³»%¿ XÔጠö þ£B Š ”À&Þ¡êàX}±ÃƒûÑÝÈ\ž$ªƒ*د¤òÒæÅà­‹wQ—Æu|ónþm¾…ö<­·Â 5ì8éK0¨!DZÛ‚{b–0ÝêPèuF ¤«¯[nð8¦\²ò¿<+©r€k(  ÒÌ1læRÃùcãdÂÄ1FÆ@{Ð+Ðûùo%~U¸îØ™†CUÙSg[^ý 8þ`æ¸ÃIç9Jcª“}„žJ`@Œ%§ F¨HÏÿtö¦5­”\ëz tR ÃA÷£8›8ß)A½ëÁÑŽ¥"ˆ=9SC1-‹_yrY ç-å€,`êíàL šÉæ w ¾¦š4¶Ö¨ è<°†4D34ÕÓo7¦³H¼ÈÀLõî“uã *ÀÛ±ø)ƒ™3vz]Àr¡•}°~o\ÜD™LÍ™Ij%™êß@Mè­o à”sæRq"ÙóUÞì=‹ SÀBŽÞ˜ ~ ´Xϑɬ€`· Éÿ𚀣 è?§üÀ[Ö7ž’j¨i¨ÏxÆ{0²lJ.ûF Æ™àÿÁ9M‹$_®9l°²È¥HìàŠ}µˆà¡HWÏC3·ÎXŽÁ¿dÖ,¤Iò˜f0#“°t g§Ç ‰a’²|§w* Ɇkï›~O$w4S)¸«’Ü×@Ä4¬Xá‹ôiãǸ£îû áæ”‡ŒÈ8“Â‹Š¶’»F4ÀÜšáö/ðóU'ŠI§N |9ÕÓé5 @×D"ʼn¤’ª;¤sC\­Ç§ô£4*ÀU’yÓ‡v,{£%Ûá°×Zè©x¨ð9‚k¿`9`“@=£¥Ïëeì(РÚ7•ͱ%–£E¨•/0åëP·~™¯{âêÏxá?°`vMH| k‡Û"ðÌãÁGè4e=Rªð7-e`6‘èk] D»/,µ(ƒt5¯J¸ø–Þ"Ôt¤v@på”ãä/¶àÓt|ð¨³hÌ@ÂÇ&tm¯Xä¹q6b€ƒø[håH„ës<~#×µ Öÿ Þ€íýYC@lF–»X ̦ŒfBN}yÊ¥¬Ý !bZðËî' [½¥qùÿ…r“RØQWHà{Æ+\©Ü³ñK]©Ôx|"¶ÍžzÃ@âøb”ü¤Òay92ÛR?Iœâñɲ¿#jU‘ŒŠ ¥:X‰”°‹- ™mT}4ìhW½ÜÜ€´L96S!ÏpÒž`~ìQ‡*[À)äcPGé~ ¨¹^`VE2 8Î訶4Ù´}㱩ÌÌÏDm]ñ„ % ?ÁàÑt´ü˽O÷ða5s{‘8¹€‚éB–ó¯ %`øþÃ}\IRG´YTo©¹j»$äÝá.¤–ÈgÅ™ù,¼Ça"ìü¦á*䤈(ÎùŸ;ÛGß?©Æ!€wÕÓ£ÚFm >ˆÍ#¶ãÀ,Zº°I1‹¦b·Û‹veH‘…ÐØ;ôZ~ câ™U÷LÀƒ?Ñfií.„j©Ã$l1Úïxò[§€`òÈ™œê™°Î©<ï_A+”_V{M„Ëm…å9·â[&t9Á÷6CAÅfK&å1~j“í¼NBB¤1\ïûtW;ï+Љ‰—!~ëkY¶èd ¼eÖËÓE'Óâ `*Ìw÷¶‰Ô/ò¡M0<‚ªÄ:SÆQ•Ã0éÅ€­¡LGÉeèMa„QƒÂnªÃÄÆíÌWökJPyÖèî$/ •‰¶VKìÐÞû»@jGxB°C’VhF³jl;é\h,¨ZÍgŸ¡gà˜éNÃü‚ @m¬RÁÅžº~/1^?¨«NÜÊ”›Ò ‡ôèð0¾jáòÃMJžÙ3‚·ÁçF±Àä½výfdæÆÁâ\)€Ð˜•Bc{N®+å5Î;ªyaë¹ám1µ¹¡ÎN„ùÄªŽØ—Gµ¡ÌtÛZ¸×X Њ<{vWT²D“×$€¦oð´PlDˆŽŸs©å†4Ísoíû²Q\áÇýŽ¿“ aì]³¨‘ŠŸ5ÏäÛö;EôËLç,罡‹£]kþÍ0‡º2¾£r$¸B£Oޤ£ÏBQXÁ¥=z·ËÈÿ Î5]PÕ‡BÏJÀ( mLó€z1šQtosM¬Ý›¾°•F“£vÑpUMø…‰‡¬|”JS[®ï“Î\¬Ð;0]'|óÇÙúÐàÂØ36ÐìOƒö<¯îþ¸ó¬,”LVT nXaOtÏ”jV7ÿfðûH™Ï¯33w¦6–ùƒ6ùƒR‹ÒØkAÌ¢ÆØça¹.ѺÞçƒÓRþŠç8ÜîÝ‹PX"ýy³ü¶—"ÊÄïo6+r<°ôkµáÖ ÀË_?ö³Ù®—°ÙålV`/ìgØ6µ$³ð8ž;Q8'‘G«¶ ÖňÉïîáÀ0?)¸~›6êò lŒÏ]˜YwØ x‹dÐô Dv «ƒ8VA€Çc¯Ù‚ûûÿ„HL‹kñ¹÷ÙõwJŒ jkƒyÝñ‘i‡Í@€â8ŽLÒhøÜ ¥ ÀL>þ¥z)©Q·¸ãÄŠî÷rŸé‹B;T{° ä]ÔÄãªÃK‹?ÇiÀå]LT-÷Þsd‚• T“Ø 6¬& êÝ'WGúý&w×LϘ ¦æ?ri™ÿç€bãSÑYv–GÇï„p¯iÚp3¿h'(RŒF3Úï>0€@ ’'tWv ¢úþQk… cš-p³™Ù è­1†‹xØÕOѱ }iÿ)wïÝï{¸@H1‰ œ—X:9?paÈ[ïâ[á4䌴Ê!BfÁO»ïQ8d g‚-(+<Ñ€¢(.iTJ~é ÅÝ-o‹cILFÆ Üêlͨ—Šìþª²SÌ×DÑ–Ê©ksç1#ÍaÁŒºOå#\BZ³)¤ƒ°žÿÉkúDÄöP¨T¤׽ـam‚pÿ/œç&tG‚¬3&æ«©˜™ jfªÇõÕK-µÜV\@¯ø³è‘¥'CÄïz¿^ò€úê5™‰/¢c»¼¸îð1˳ã³÷m¶Ûã>+U.¹½Åo-ÇW¥ «/Æa5ÑRmUfÅw^‘ ûûkªª¨Ä¢ƒ|ªOY&~•`%°¯±_ÈyëMRF´)˜¿0QýUYz­N«Ížãó±o–‘Ý<;4²¯ô1[a̎ŦœìÈÀ¼É ä3ˆÌd‘³Øþ9›¡XèÛX~¸‡Ë(ïFèÖx³Õþ0A² ÑMemG•*NL忤á^Åö=ŠéçI³mÛ£i8â }"dgD;^ÃßV¥¥ˆþr aÆ΀~ñ]º¨ž2Á¨¢É¹<ÅaRëõ ÔD˜[®î¢ìIòE@08‰ŸÃcVçÂ_ò|&R Z·CƒŠÐ0ª¤‘özÄY¾Ë¡ð¿­x>µTkM½þˆZ&s 'ÛO>ÏL ’£R_g1 y'ý+óYùp:ÂÜ gꔺyæñ·ç§ÇíD‚Q ¹›¢-Kq”à/zv¦rã¿/sÐmЪ– }^“åŸêjkš©E‘ß‚§x©»SRχرjõšF¥¬U©;«§\%—ì\J ½~,¥æ(s†ÜãéV\0õˆ &ö='‹PÖ†çÏ2Ü?tÕíy^’‰«oï”—ª:ÅMsoÌoNßœcað–·zê eA%°‹¶“Ä•Q÷6U.«uo媢¬(‚”‹Ñû—BbxA’ }qVÞUˆ ‰ó%=lnpÙ#nËoL0*¤°€ ÚÌRDˆ„‡ú2ÊþV‘¼7‡z™o`çÞ¼iJH6·È õø|ÔV è*¢Ö²Åf¥%²Erã$Îf@÷…ÛYFPLÁ %MF ÎÑ„ š|”˜:òSBC»Hú2rkÄ‚SH)˜‰ÍÐþ–ôå‹•ak› 9X´l)Åt€u@OZÕ9žy/í@ ²¸@f¢Mù=.æ‰ßøq•Ú ¼'@¡ér^‰‡^%øÚƶ ²r§®ëƒ°LÊ#ücN›Éõ—³_ö|ÀÑ ¥(Å詬”EL½VA!ÞÔ`ø{ÈWjjêàló,õ!Ó"ù¥Ê2E³w]ûx‘æèÄÌÐ ª‚†LÉf=ž2U½•m{MpBòvh/HÑ«ω$ó%š… Þ²ó´öع)ÀÁ­L5!ÿl/CT7üÎ ”ûôá¦Ðä{V‘†ÕÕ$F^€q­S¸$mV8°@2*šV¯3Ð{Gè"\ž® Z²ÕŽ‹ˆtœó6 kì?Ê,1KbœŠÿ„( 0æ c¹:_ÁŠî†×>ÛÅ ë=˜SO@ãwô, Tb&‡· ÝK°Ž¿u@s`6õ.Tœç (ëˆýxC÷„:#ÙøEBêAþaÆgCîut𨄩câ ‰ž3XþS:¸Y ?„1áa òFýí®ê¢S~Ê-°nƪ»Ô{ßxÁ•¾]Ç×ðÙT¯§9Ä@ÿݾãEœ6KÌ®ŸðÉðÌ?[zðK’[…R²¢dþß½ûN“î¹]ϧ°_bEÛ ÏažÎ 0}¶ÉxÝÖ žÞiøç¹“y茦ãηñ¤îg³² i’½eàh¨7 ñce§@ Èn'n$r7$÷ƒâÌ ÛÀ¬®ã€B85ÎÁ0ŠŠ#^XœZØ<‚Î%¾Ž?r~|áŠçs¶¨Òße_4r H/o< j2€fGM.‡ T=b¶äæ5ζ•Ñ;l\Ý ®`W£èBÁ-.)ÞúîÏÓÎ_i³j’7h ëÇK…a?΂hva&€f៓ç õQ±ª?çxŽÏ$üé3ôŸÒGÒ‚ÝAøj¦Ñj0®ö&Ø&_NÓIb‹Öž‘Wû3pÐ •µ`°(™ºQÌÓ +6#ßÉž¢(:gcx`fW“0ò˜…cf)B©ÃI€&û‰²†Œ•Í„Ž;‘—kê'•kå\~ÐÞŠ²‚†šI~\´ µ¢¢%;êz|Z`ôlú“Éå”õ¦8LUNÍà’'sV´(²ÒQî G*ïŒó³Ë üÈCZ’:¡üÌd&Ð#øeÊ™ðøÞÛOß.þºâþ±k¬BöÌ;kð›êE¦’X( Ê™¾kõ5­ ÙÀ9í)(Å}À`}(øC©©Ö˜*º`[&<8Ü’’p5-»POÁ Õ®z²¶@·þš5¤sÉIª¦ðg¼±5¶]°« ²˜œð÷Š)!æ#AƒÚjn@TSÿÛÄ­ÅUËöCV9\ ß÷À¡¼ÒÁI¨¢k†¿a™ÑdÈ-Køê´®YÂp¥ùvÚØ2m‚8ý.Sk˜ ¸4ÓúW Ô%\e¨¬#¨ßk+;¤¾qwZtÓvPvý*ä)-á“`Æ©Â6(?Öýdýám!°Ék”¦{Œ [ Àubˆø;»çŒxé`Ŭfq7—q&°o5>•ÒÃôâ⯕ð.¬‘3Û䪢?Õ6D ƒºÄöôª«øD$Ó¦|û@dö$æ½,Úè‰Ê9Á˜.ZÀºc»GþÒ 5¢5zȺuÛ|Øè$öÇ$—|,¸1èðõÈ5 õuݨðåJmI+ôm ù%š“ücdF<”¤œ8JÖ¡£„Úd®Ý ]’D¢·«_>ƒt•Ô“CÇC¾ÝeÔE™ ón-gL,©™H®Ê¢kŸ›½›_K®uåT¡‘[Á^C“SàÌGí.ñ­,TaUÜ`Ó¶QbkV€¦”þ‰XšÁXôš³Øa¸IŒzÚXË5`=×£…¨/–ýЦ> Ì@¥‡PpzÂÇá«•9eh‹|”=…A(6’ÐIRKÊï i:*:>¶¢Èx„WÄ3š8‚À4Þ¿&†ÂÎâæÌMâÌ݆@ âX]Á25WÀ¡;Õ¿ôA1ë#•kv|K3ãfQE/C6´Fè2AÕ1÷m?j·&)5òï#Ցسè¦ AÎÉ„ cT¦Ú¹øldhÀñÖ°˜¡c( ´ÙO¹vn.ð`—™†® åÒ"4ÆÐ>É(vÑýª€•¢ŠùX²i§Šqó‘[ ¬wº ÌP᢭ÁìMŒ,Ö]:ÿ‹éah±ŒWcÆuà%ì¸H(‘‚wai­\KkÃ..0C6ðz;UýþÅ”-"k2פ´îÌT@ Ö©yÝ!êIMm© ÷WQØ~Îe+çü ÀLÆ,$O!®ÂÄ%­øÙœÌœ' (…®nÂÌkï“›¼ŒYqüÄwl÷žú/…„„‰â€w&±k<ç\½6‚fCWË^$¼òÄù„qR›øÃSY–o@‘KpI6… ux² 4ÂKè<þG#{̹+n]°vC›c¹̧@ *Ê %DÛ<xÀ+jYˆHJ©’¹Œ U?fP0Lᦲ"Â@ß œÂ.q³.;¹™ÝÅãû$ZÂ2»h:Ï’`Õá— ¾®WXÞŠÀb… Gp!¸tEN¾ YѼŸYî.÷Í.îå[Ã%Y5:À ÊÁ¸E4˜¬3”~àgk\> ™tŠ ` ½O÷¤Ç7÷x÷AòášZĘܣH·4l Æ™«ÉâH ”2I ªß€˜b¬vÈ̈"ᜋ4çÀ•>Àü.‡’×-]Ž)kH€}ÃÔAòcþ S1Òg`B†B#øÞ 37¿Ç]h“âhãU×V«WËV#,rÁ·Ïÿ×è;F¹þNÏÎNBv澃kë`_9Ø“:LW$ŽÙÂq€ ~XÇ7”y‹>z{¬*€díã×3ÓEªÑWÅËzÉE $Yúô3–[;Ùà~†•þnbvØhؤ‹M£ ¾q¡ n„g0¦4“â£ÁÉòªâei‘,ÀЦŽ@„s«¢ïpÐpUgÆ×íjɺxB?`×ëxT. ¼híÁ‹ÙÀúGí4h÷Á#[&x-;l¥žƒOQ8û-7AÓCñƒ3„Ö…£ùôSª©Jâæ`(ÎÆŒö²Ún e³êÌãàfoZu“&ÒnS€jx¥6}k#ýÙçMí2^›!‹;]¤U(÷ìWœl+m-ÂùniI2>ì80Àrƒp¨GW‰£”6`_Ò}üŸO×—ú²‰`VÓà2ÐêΚl(™Ò{zF¦àÒY™‚.ÁõL£V‰Æ 2+–Õ(Lƒ–ÚÃì©?:ªrÒ}3NÓúDU±]_VÂ~»º‹ÓC8eHÝ(-eþiåľ¼`fÌ{Y×§X- †­žÛ’5Âýž,)û¥!ýVŠî=ó£·ª8&û*&ãö(åmeò[//¨Sœòa*µðî'°»ÞâÀÀÂÍÃy6„PðµìIhóf‰*fE0 ácï%ßÙJî aP>Þˆ&Ó¿Ö¶O—ð‚ÈòäþB…;ì aB;2Y8(L€†ö©Y¬kIËŠ©Ã[Ôµ…@Ü(YAX5ø’É•‹ 5£ÚåÕ‚€ªHÛÁ+KŒ+Ú4=€GÑ)`~)µEl4ÚÉö“3‰¹>]H&÷Eù‰c1T7)ÒÍþ3 ý‰á‡ùj>…ô ‰)û$< ¸Ký—}0ä;ÉWJOý3Ša ôçq®ãŽþ(ÆD¢‚ÁÞ-™Û„ßê½] ŠHØÌà4£ü€»Tš–„p葵ÍÚÞäëxë&Æšf“eíMfWïõb×ÔÓ8>\5¢ëoñC~ ±U®ÆØPnå$KÓ¥sEð½xP õ›íæã[MG}Šå!~\šgÝÄ=À„Á(PÊå'$ä$”@!ë\³|ûjù&êä®À4¸N\¹Ó±èž<7{c›®f1]’ÛäîÕø¨»ÉÕ¬» :WÝ ¾³ý*#@b²¢†³ˆãD9…ä1—Ä "BéP#¤ `p‘êj•Ó^ð2ÔšìnnJ÷=ÉA­y¶ v”57œÿ) H.f®û`cЙøY^j#x|Ö"VûŠ[8k94¬QBƒ0ÀkE‡`}”Ò†|}=µFŽ‘%b|Š^,^NÍ#ÄNŒÿpÈ‘â;¶ÿ{)™¢ÄÑAɽ5 —#Ž„®ÕƒÉbw3R±\#¨Ÿ’Ã)zô a€…7M0ýàÜ8ÍðÛ½˜£9„,ÜÃÿ÷ïËß}•Rë…z&áíÛÞ¸WÚ/8$kÀ ¸? ¹¤byóí¸æy`2­ö6AöRÔ)šóø:ùT~Åé,”ñ©RÛÛ€7‚‚´ŽîG7‘t!Þö]@TŠŸð¼>ŠXXÕš%%JE¼Zœ ´Qèƒ+p±ÃÁÔ|€LƉT‘þQå/}qøR±ü59S‡äoø.ã¹;½¥5òrÚ¨P;`Õ†¨Gš~Ž{¾!!a%!‚Ôü¾,Ø#tƒ «§Ó³Ìò.EŽJ÷H€=À@›Ä7E¾¸2<„\¡ƒO樠Òp+®` è8ªâ1€?Ær]ÕB¼){î¸À#b‘-„õ&ØEÒ\¹‘žã±ëEÛœîîîß“8ß„€ƒ˜ÔqixÎ-¿^íeÌàý|L¡’jTC‚„8•Y2ã•ÿÐg¹¼îNP7¹¿Uýz ,CænGïÑÝGTU†¢"åBKèˆï2m±GØÓuÝÿ?9Äç¹ †Ø5­Ž ²›©_p Ÿ`´0ßðîb°L ½¦ŸÂÁ#4_‡ ß7Oúû3ài<¸‡ä‚©âjß çšÅTÛò‘òKP]Âõ”Wj„µVÎsYð+åÛ{ûÇK=ÆÿwéTÑzr^[‹¡ tXîxpV÷Ã`yiL)/ïÕèhžŸNÂd榹šÖ÷€‚ѳ|Þ÷Šžª$&jº6©qZJUpÎqÒšq§õ¯õLz{.SW¢Øú£šÅs.7"|N€Â¿®¹ö=kSŠÎ´ÖJ4*nŸ9šåñm047,ðúõy žd|¸q3_¯éÉP­……Áû¡ŽÂVîb½ã2Òš—Hâð{±›ƒXdv*²1U¿Ë|òÀjC\ní~& †¿ )?¿*/aò•ìaè´:TÖˆ¿›'-rÙ=]…~csçmhÃâ­Æ@€/*ÝtZ-db05èOxóÀ¹Wå'*Z…£ÿÜ?%ùÜÿ],¾J?m2øz2Ó 'Ù¸nÃÿð€vêH(½6$&>u\”}÷/Š@( ¢’Ly#ðVØü[µÀ,I„3e˜¬—€jºË dlJtB&;ý¹àÖˆ\ c*É@à:Ò]B¥àKó*ØÁ;l±(±xûI–1£úR,Ñ]º³iÌCÐ5X'ʪtaPöÔ©kp‚nÒÛà[}?9­"b`£®¸,Að2Ѻ|[ ‡¾ˆW­~,Îpmè»TôÕz—×ÕÊ@zÞ˜‡šDþl‘QİYþR[ýê2?…+LV`gÕ]ÁOb…ùÃï'© þ%!íÓÉËp(®J°”L ES¾ôþ‡‘¹ØP)Y™÷3Àj¤Ö c š€ºíI<õ–9nCn·<9§Ëµ ±CôHŒ&ÔžÝ]úµ#L¹pÈ~VÔ1‘îiÖM.‰’ñ»£Mhà8)8tÆn£!ÿóFg¬s¾™†”^Jt)¢$e«TXw·6K[gÈ0þÃÍRËnGF„1\8 êTÐ8š4*­ºƒ‚&ZÂT‹+À¹-éô^Ö f«† #eÿl˜È™–¹–ÛZaûiÜÉÕžA›ï”ëWbã’"86Gš­5M—–†i82Šî‡õ‰ÛŠæl¢å‡Ëý&IeI6Ô0ê†Îq$‰_y"=ý˜š À’ô­¾Ï=Ô¶ß9œJ=áâG&kY± ,332.)רHï!.Œ ´©Àµî£–;' ’”'rð£(éUJ”B–Zu{> ‚¢ð>4K&¸³¼ 34ö=³èZQÀç[žc2 ÍF,çgW`X>ø–'|v¤pÒã\žrÍ ÃFL0Ä·µŽ=XÑXÉS ü'lmËA2äxûÄl4 @Z€ 0*¨E/3Éþ½9|1{rͶ•Ð3¤(÷ÂI£F©ðd_˜ ÄŸù ¾ôÐÐc¿Ïryšqa¢J@u0sÃãO^úw¹—.DXÝ?gl-RÈ4‹!íê–ŒT8qü0 q³#BŒtƳ]®ž/‡ÖðáCŠ˜!þìaÔ¶¢U ™R2Ãæ¡h²yþPt¤ˆ¿d¸ikõ‹³ ,Ñ»ÊH @ÀpÐ æP÷~ÆŠˆÃ\“»™&|ªí©É7^é›õó6A1‚ê´8d¡÷¨×`Ñ ´Þ¿ÝÕô:!¹ýL˜]êìjg•^Y.:n¤*C¥–JÔ Px@hÄÈwJQÔŽu…v<©Ž!Íúâ鯭Þ1µs4Ì&«(¨êYz–oC1ÄPÒ‹€³Ù4ŽZag,UJ°n‰"(jˆñÕ(\|uâøgóð®QQòا–Šçx.[˜ ‚ªMƒJën:â„“\ä°šœµAÀù€ÃÝ™êã¢3ñ"§­ç½€¡GÕQ£1 ª¼Å¥‚ÿÿä\Ñ´ˆö åî‘&§=ñõ¿,¦Ç¢0HÐ=ô‡¸«%‚0ÿ7°ûº/÷‚°”¬ "û]%oä„í”ñš§±#% ´/³ÉȲäŸì4@ CðÞ^¹W^sÛãß`ªvJmyêÎÃV½aNóÞÚOà>ƒ=¬ñ쥨בÙÛØeÀ D‚e*ŸÇƒðà¹Úc«†ÉÀ;¢Pm¹š^ÐÚ‰˜#FâØ€á2ÖV ¤$´ð‹|)$ÇDŽFi±wIÅJnœå‚"Ð S4àúÃo”@rSÇÂù»Ì™’¾/ËRê²Þ8Ж€oK«§`VÀ2HàáaR *òmœB¢ÇyÁ: tD꣉¡úÝͺ®ovŸ<;v¶º¥Có°åDŽëÕ×Î D»]Âʰ¼ÓÉ­9ÇšR-JQÉõµ’ ã#²±­nÉ` ³X´Pµ2FÔZ·‘~¥@ôÍtd)ê.Y~ˆD"³ŽQ¡ÿCiIB¾‰ÝaÂÎÅ’†T¢Œ¬êQš]/ë S\hó„!|©’U×ßhsá³ÇíÉ›‡y”`€ÙøPm=¸µ«Á''ÓV² ©Î-)bµ(¾¸ŠkÑØ˜WPú‚öOáNi¢ w"§ùã7É!V)¢Á4‚ZÁ¥qQ•Ðk+°b6J€Èz4Eº…äþ¼ƒ¤¾ðá#û1X ‘ÛÁâu¶y®f„}œWV:f˜|í~e ÐuCÆìUÉm›5$ÿ˜âÒ-¥°mûû¸K¡_€¼õˆ˜r=Õ;k"䩯²[pÜ.p1KЬÌ{³ÿw*ˆ®F4“¶Q¬eIð’V­Z{zj”¿}m£ [²¨j*hDYÔÄ”®çâs¥Ùͽœ8rëƒýK0`‘ÂÂäƒã ½ú¶Ù M·ô‚£ η¡¨ÈÀê~¥‰ÍXZZþÌÇ8ưC“ $ì&O'诮\-6¹C].‚köòø\T'è%ËIÆyÿf“7efrwÍÏ2¯¯ ^¤ù›í«"é0i]¨Ê,e6nýÓ‹Õ óÀGÉ5u*ØÏÚDS+-š™øué9År$”¶±rTMk±#2Dû±(ußvý û¬ Ôcföb•Âìã'8$÷ÐY5äÅåhŽ0©l zëîÈÅç’Sä\õX÷E4 ÈæÜÅ™Öþe+µ'VÍûŽdN,OÃí@‡Cv_=YJlÊ•‡ÞÓ›UX$§ã­i‡RQØÄfÛ-ä“)”÷+™¡‚ï~áR8ԥɅð}Æ0]•Æ:(ÿÊ$yàY„­¬=œx²¡þjùië¬h°•\†en£‡ƒk¢’ðQxnòÅ‹ ß•Üw5ƒ½­t©@ÝìÎæ‘³å!pdÍ Z¤ïU+n‚R–1·øbHº!í˜fTÔ¢‡âÁGW$rN¶lZœÛÂh|i*ßÈ^ÂKSH¼Á%y0&¹ü¤£×'‰‹øÈ›Ù0¤côS-­€ C˜á¯ÆØo\\ð)€Õ?Ï\xp¢óW›(—q ù"G¤I¿ºÇØÿ¹“:¿•— ;'«ÿ#*_ànú¡âBfPÃ,³¨5¦çëûÅÊ¢$ÿ¨ aŒ¢Op¡6'Àßö˜GóffØé–h?) dÙOÓZßÀ%«àŒ,x0v@%¾DŒÅ¸Êó0ã.Q—Ž+v6=êuçú›RØXh;¤FG ÁdƒR Dñ¢Ë2ô4VmV-‹¨Œ÷èQwØ£Àk©¯§&ç‚s‚[®Ãáq3¾È…’ˆ!§D)—ª3u\TjcÀÿ@Þ!÷°’hÀ»\Y®½Á‡¢#ò·…´ªÐbÑø)m˜G_sA‡T¡°Øs'q*8< ßLKŽKrÀ?e1"W"x]rŒ=„‰š¾àħ`_Ãò§]y3‘Ì…Ù+‚GÇ0ýn\é‚kÑ; Ž\]–7heCÙ¨2%‚GXY[Ë.Žâ™ -$ÇÑc²‚Š"=²¨%õÒ¥ú ×⟪ªí\| C Ò}ÎSÍ­$]&MM¸:Ô7ZGÚ®¡2¯Ý×ä€Õ3û¸$ püŠþl¼'Dµ‰ä†#€èÍhöƒ1Êïgžø€³IÒæ¤ø*pâû£ÙÉæ¹9îóvMç’6­ã€¢Y¶'h† P+¥E§ÚÿÆ`îE)n˜|Ç~.yCÑéÝ#d±Q¢íZM&Ï8b„+@ª$r¿>°›m û¢}·`Î B©"’°ÁMð`h$HÀD ~#-ðÅÛ »wGSžîΩmléèÃEj[ãN4{gƒ0*;¬fæÕõøÈØÁGÐHü¶àmFá¸pY>ä€M˜ì¬¯êP…~œª2Ä`MÃXDÇ2%4Ã’‡œù nP¹ÿ÷y¼Lc˜:UA(t3vÏØÄždUaìBðV‡ØOζà^<¥€Ô#šñLŘÈÐU&>dv+ŸL©ö@Ð2%<—s^Ä„Òa£ÙŸ 6e¬ÅoiК˲íFÖ©zÓ,ùš~­?`e1•‰gu%˜yêœ0~—_„HÑk]¦>$:V¿³Z Åè‡#4`çâyhNôü,‡"8:Ê=ÖªÁ4™I&ØC›Gš5ü©kí ÷ t4ˆa0šJÜ xFÅ]2¦¹a›ZEiIw{1ÙöGÝÊüžð,}ÀÔPùª™ò,v .Ã?éHìr/Œ‚+\RÇìz´,ÆVùš%«* ¨½˜ÐK}å•4sg‚j¼,ð]ø C#ö%0Ç*¦`b؄ŷjÀ(5˜jK:ÀÏ=ˆÆˆI#5dEÀƒàr† XÀl$wId÷PÈXdp/_gä9HVLRm@ºöpYW` ûvstµ­\œ8F@»ZêjÛxZ#ôXɱŽ{—Ï? Q¸6Í­,?•m^Š=‹r»¾ Ñj´ÀH „bc@ÍÆÞ3ÒÉ@†ãÐ7OÝãhÆ1½`Ôa3Ú á Š#.Ãϯ\Ÿg`€~aŽŒâ›ñeLÕõ)tKPuÿå7^Gã“¡¢è u4à±c¯Ôå$ â _ýP€Ò‡ÃÇ€º*E¥Ãsb%NÀXÇÂà†`™W× NýX¥2µ©\‹ˆ««±AQhRÌù ¯j7Ûe÷a8C0òÛ&VrC1•ŒÎ‡c©ôùë\}*ŒQòË‘©á:"JGkÜu€ð0à]AC,µÖª$­´†/äÛjävðØÀ¶0-}& æ„{: °ü9œo„Êø{4,m÷ëZ°W %MÀ¡`Á2ùGFÏüYŒ‰‚mÿ[Ò+­Á¨s{€B.Aü'"t³° +ƒÍà q8&b?†=¶Ð0üìÌ ;úÌ1ÿîø±(Hùb…Z `9wÇëüX„‹&·ç8<÷å÷æïn9ŽÞþ”‰²›âä®2±_Èé¾cVQ0€Zjˆªv9QŸÜþœ³³‰xÿÍ-^iõ—‘Ði´ÝÍKÙ΂W¬3ießÓî/?í 7À¢;±Â®XÄ7S3/–¾°Àë¬ꦎ3h1Rÿ¸H“xÅôÉ`uÄà*å"¯kŒ¼í ’ìøDv(Edal…¬wðÅ^ƒ÷ÿË}eg PëD'Òy¹„£K÷Ýu"¬–wñÿýjÚY”Pɘ’ß{©õªƒ€L€àh‚¶ò{±`ˆgâö ݈`)ÿýqY¯¤ürxìKeGdLô¢Ç ¸À4Wé]¿q°åòÿ¯óÇIÕ}v¥æ/.ˆ‚}›\øh΃2é–Ѿ¿ùgþºæ³õ³Ó9—ãœÊ'Ò–f¾d$* &¯ÿxó# à—Ïñ©ca)qÇ5©’ëkÂY™p:LBŽ@O"œG¬Ø¹Â²^ÃælFysõJ ¨~D©k< U”dÇËÚ‰ÍÛvÔOZ¹÷üªº¤I½ýûß„—²8‰di4Îj‘ºU¶8(ƒ-ø( â­@X&ÄJ×ëð@w@(æ c™H0I7Ã/ÎçtþæÏU\7îûí~7ÍŽÜC vj¹w ƒi¦mÖõï¯çý…Þûñ´”Œ(øJ‚nê4´5â¦2J¿‰~yîîïÖ/øõŒX%×*&'ÿù¬_Uõ|5 qÅ#Œ¿SP´¡Úý¿¦™—Ÿ›Îqk_Í'äf2$~Ï0ŽWU—–«¿"Ç74擯W„… ±x´4Äêº>Ï7ŠŸˆçM‡| þž0ØyÙ:Ê¥çøåݪí)ÏLÕXAâ‘|;[öÜqðÿ•WuP!ÀÍ3× ø¼+µF•Õ=n~Ø0à©ü#¥ã—Û›Î:ÍWUQíÃø Õ졸URÕµ:„6§> )ÖßÃBbEZ¢-ÞßRýŠÏ·kŒŒ‹vT¸;vêšvm~†J:zXŸ6^’ óK$Äœþj*£‘zH¯&u×ãY-QU+­ '+-B g¹¹p|-§ÅŽ?û-o VÄù’³*$U RÖRûñ•£ûqž;ëES-@ûš»>8«'é3¯/ã>¦÷4¿ÇtóJvÑÓM°3˜y âmÁÏP8Œ!)¥¯OÇ‹"•OÁÃ™æ¹ Än˜8J%ªÄŽ,u‚ hø‡«Ýg42A^Hcº—%”î*¯FJìéÄåÙÇK:ž],.m!{8•Ãþƒ%•bc:V^KÙý0†ƒ÷û‚*®=†=õA— ¡‡{¦Œê¦µba5E&~ËbX)Éë† mYÉü¢¡ ™‡¿:z§7ýªˆ£SZðy%ÔBÂUŠ”JŽÆ²9°,âHÛ—2—ÚGœÒ¤ÝÛ¨iåÏži]ˆyøšÍH‰{4—f©%Mç f%Ú­Î2à!^G÷w<,jŒ„*2vÏÄAžÄµ|T«Ö,ŠHÝ{[ŠÕI€³‰ súËÇ^e˜¥pWôà‡Ar)ã­R@A쑳|VÑâ!Oõë6(Žð¯©ä`Óç0Ͱý¯y@ðx íÓmu´Ë­²Lyn%À“M9eK²u‰‰îc7ÖDˆË&°Z¶ÑcáÆ@La*§Y‚s¼ÿþì*y½¼DNö` ɉP¶`xB£ÇÕô»q\HÔªuB#Ö :Æÿ’ïr‡ —Š“Ís%zá¡Äo*W1ÚLpÄ’º êÕ¦(4p@GàØÙ±Q¾ô UãKäòv5'‰ZeÇNæ¬43 A|l@:Âû2Ƥ‹4ŽÅŠÓmœ:Èb¨’B²ÌCöòe G8’]Í: ñmÞ³Ž4^ ¶'¶p„eÚ^¶ÞàÚá©>ácè ð$Hƒ/E!Çí`>£„PoŒ ›¤’]-±©q±ur5¸ÄÕ,n*džYk}Ç’V‚ÒNññE/’‡†Ñ dlæÒÔÌ¡@vÎ?C-Hîþ•0Ú÷AÐ?ð%ÝêÒêL}ï¶¥Ã6(ï5Ÿ€‘õhšÅ?km¼C˜Ïãùáy 'XÀU@¨Vñ”HÇIáÐ?rõÉ5¢dÐh¶‘ò·®K» ‚ƒ"&ëPÍ\?ÏÕ´߽Ú#_gëÊmÕ@fue»dOŒÙtÝÖ}Î 4“‚ƦXmÓÄ4\] –Ê©ÖßÀ1²¿ÇXS7˜ÆŸµyæ´eÇÊ>¯#ú`óýV<_ÁEò‘ø’¡ïž{4)Ë}šyÈàN/1¯öe†c…[DaË}íÁ=¬HO*OƒS¹ÎlÉ?›@ÙcñIHYx8ip¬U‹g,¹²V¨àBòk÷_3ŠŸ•ßÖÈWÆAV¿(S˜ÿ¸R„`¦-G˜‘Ô mЮ͗½§4 ³­àO½'eKnJ ÁªÂªSåÒqéùÐèìØ§{°å|'5<¡<ñ炱ïŽñ¬¬±†Ubsø‹HÜ©lX®ÇÚî#“|ÈVÕJ„}‹nÜ®9å4^¬‹n·U<°¶^ÝQW°;)WÏ`9}±üJ428 žù;í >7e"q‘ r,â‘«<10PÏÉAŒ$Xôˆ £8T:ü;!`]e§¦Â!´“8é!KÄ}Xw·l‡þ‚xÍ (µTÛ"Ì-`ö'à_ ?ܧµÅxuá\µ¨#¬P‡„j‹»X#É1â²+Ïbûvb¯uPTm·pÊt¤wU#ÁƒO<.Ÿ\ òÕXö #¡YÜ1—²×M4u(lw ó­3ÆæY>ðÜÒ¿®kô3^èڦ춺è@@fí›pàeƒ2Fˆw+¤b¢Ã•wú¿Ø[ÿAüJªØ†*ÓFùcüЀëáb1ü;8Ý ²J6{`W´¹CXï{ÊWÂ4dµHsbk]FΈÿýÜ)Ïiè‹u7#é–ÉâMŒö®´R¢ÝÛn%‚Í&¦~…Ae¨h[r²*Ž=“(Ö u?lˆ®Ä•ÍüÌï¼±×à¥KüPî`XÌ%U]Š¢Úe,2–¨!r9£b{FFì/^ˆR¹ÑH’]q¤Ë ”Ì*ÐŒsáæÎ·+×Wé%“安dSNh,€<& •È ¨.aa-Ñ65Qù(pƒI£è’œÐÐ Ö¯¸aèó‹LËdÓðCÃ’Ì‹t}zÿèŠ$dœKP5è:•_‰„ÊËËnK׉32†¤úƒTT¦~ÿ@Æ0AžÎèžðªtKRÏõÖv¼Œ5p©¦JÒT ‡° ÅÖ¿nÐ 9(ž¥C«“šŒ ÿ¦ÆÑ]—Ç|ÙÏg˜‹nŠ¡¬Wi÷IÚù¡øì%íït_yö1bà¶ó•䪱,4ûÚ8J#¸é£Á|¹s]º¡d}`>ûJ»Uº}èˆ_Íþ0ûzÞZ(Þ¡¥?D¤&„Ç7%X€™6¬É¹yGâŽË2N–ÅhN ÄbqQ%F¸ç– Ö¹Ø[£3¦$¢¬<å…¢ºRˆâÚ£˜W®”Ü[Þ¦©¾ÝIµ¸s–Ò¼#¢AÒÀÌ/L7”`„M ×±wa#Sðû¢O¸2 ÓvééüÕdÚ*ÀXÝîkͯúhläd¸PšúÀÕ ì<²•eáÉÊ  xU¼XÚÁ¨6Š÷66%Ÿé8 Ú=‘ñždt/Ïc”Ð(ŒúÁif«¬‘[N°öÃ`Ò´í“ðH5)ƒQ”ªvXÚ¶°÷Wâ” S€^w“ÞX ¼I)Ufj²Â|WÕºŸÌRëE.U3Dp‹{ï}‘ Ô‰€™½êsUƺŒF¬&ªªs'ؽuÞ8›À™¡þ‰€£,ålʾI]Z÷†6»‘*ŸèdåMQ[h½œ“¦V-+›'‚Í•“æ­BdÖéIòÉëÆÉGÒÙcÉx‡Jy‹ô‚aÓlïË¢<7T!0'Ð¥áE,¦‹ó¸­Ö>ëŠyT³%dœÜ>@ƒ´*¶kæ ÁÄ'PXÐÿÐãÜÐÉ@føÃHSY&Ãcý§ÙÁ«´k¨Ž¯=‡BP)¤¨(äó/U„Y¤#)UÑz× ²•DI'6SÌKCãú¯¡!Ô»ûì!:¬ÜÎ/@»"г3(ó ¾AÆ7ª ØxUÀ¸]nJÿXHm{„NÜ«ÆÂeß0Ì1³HÙBøANŸ¡9HQv™ ùxU ¸Y[i8|w%•Úµþ¹})6m»5[É08Ê›¶¥h-ÓÖ h}N„äиÓKþ,DP´…p& èëpì-ZØÜÖgQþú/8#^G!àÓ‘´3„¦”8-ÙqÁÏ@­žÖ)@jJ¿§ ý¦çåiZ¬Ó“Ǹlâbè‘EÊà’LÂãÊTæg°DBú«©'ºbêÂKë€ ¯å>‰ƒå_ûû10a*ìпÛ+JŸð› ò7(€“/ûWÊ.‘ã6êpå gddÚJ4Ž:6¸öœˆÎÁ™°ÍÉF±4Ó ¹“¤Gw)â—gZ+ zSÂý¨¦ƒVüÎ͆ ê Ÿ£†î¬¾Hîkóbw|¤#&k €÷G°_±gèQM% Ô°À4F#HÑ7J/ ÐA 5tØ÷)VºÎýÌ7´$ÄÙ[È÷¼Šü*œ:Ñ~Xýˆ‚öÑÊC»£ôàrØyØ72ï÷r [·"lN¦u‚ð¸ÉµÎXoÙ€íªÚECn•þÄ·"èç 'ê6:DaÝ|áÎ1p]$7ÅäŠÂ/£ at¤{ æŒEa€?ŽÛÂ$Ôf)º`¬ãˆ$EÝù˜¹k!ÄbÙù<Úk@N‚ ¦·Ï0Ç‘9QœW@6LbušKl»ìd„8‘PƸ,äÐbø/[S¡Š>Á?·š\Î^ù<v vÁa¸Œ°×GñPkï»r?s.¹ %!Š›ãòÏb´|¼°}É÷ø l`«Cîέțdd'3É(ô{ÿìÌÍ‘“µ*ûÞç͆Í@ÅLˆ¥ˆÂÅäøÂ`:àDô2놠+ƒq±¸dIŠLÄNS€Û6DÁÍì5à$~ʼnûÇFá©v‚Tœø·+ãAhW{3wö Џ–dÃüŒ ø°·È ¶?*þ1öjLí•dnà’^5e\î[±Æ€¬ã3G`i"à_úcáß })ï’&N|*982± ¯C‚祲ÛE—8åÓP©=A(¬$Û)€›,9m—ÏrUƉmƒ 2C"ïÍÛ3ÔKêïú±àõå“’‰€°cbÝ9»‘…ª&Ç=žáþ„~’ݧú,ø¢l‘­ø˜ûÐÛKÄÖfšÁÈÁ1~nר9¡cÀÎ…zq\·lm÷U,5Þø¡ r#GûSùÎ ÝÖä‚è”|UfMÌü„‹_¡ÃO‰ZdMµ»4¸xH7#§o€úc²[îDj”£¡ éÆÃE  ŒÛ WB¬–u¢éâ`  f7o‡Þg°Yv†4úPÈŸi ªÌƒ¢Hs9=À‹hân±sÄÁ½~pá ¨ À$óð7†çG²™Xó±å*»“Âaú†cc{–Á.˜0 kPÖH³ZÒF¶iïn+Yv5!—/ïÉO€¶(<†²Ž6ËOÉ<㬀…Úok¶Wºu×Â3© =Á•„òŽ$¯M¸¡0(ts€ü E½¨ž*\²ÈzÔNÓÓ%®G³uÐur×ÊH i¬eàCAÑUE$Ç‹åÈKŠeÉÕ\ëèˆiË^]h ª[ú¨"»…iËä1lï½…‚ÛÆ«ø¶¨«©`›ä‹V"*:Y¤-aدDik.Ô”TC3q<á`nÍr¸i»¤xpÖ¦7ÖVÆk}§6cÚuˆ¢Ön 0È™ùæ"Ä|W˜p&Ôú%é š¯Õ“P™Y8­+e•ø@¼°é’†ÁúDÔi¬t9A„‚ï@Ô v„}p³lÁͪÚþ…æL‘òAçCT¹i(sh8™©QEÇð3IŒÕ«s¼nÙíŠp:ÆÓQ1YæôéfbIœ›e”êöTÈ'ˆÅ´Á¤äaäëÝs=Å'/éÍÂÿr}x`Ö±EŸ×ñ˜b 2þŽõí Ž"ˆ€òR òXÅ©}ŽôÎ$ÙöÔÒ>ObOŒ4°*r#uøý¬ÕüHTÀCH2Ѧ~ƒ¸í ÿb:˜B­q”Ö2†Õnô°Ê°pU Ÿ€²`Egp²Ü!J! ×Ì#h2à+€¥Â«ãkkã“|¼z9±;\ÇÚÛ§U[™‹pè«|,é øàê ß"Gh@.”ÙV4ŠÍ4Ž ð1;ÍÂBÚ 3 CÈH 4Ãråšâ¡‚ ÿ;M&\µÜÅÜ\}ÅKdn ô~Bä .+ù+{ Uw),‚ÑfäüTƯúˆøå©EÏb¢We ½=€Â3`[Ì>q Ëö¯<ÐPc¤£ÉSÊàmÌØÑÁêcÏöꈙ ¥Å™oo„0ĉD -¬á3e»e:5nDŽΦA¦æVA}¦‚ɧ"âWʇ²¢@ªŒÔ>¹‘¾ …Ýoò1ÙZϰÙQˆæoA†(EÒr†¤Žt<ã¢2áYÿ—ƼhoÅ«6œÇ"C’0QŽ‚Ÿëj®€}‰ý‹{É"ŠaV±öCs!B«z¬ꢧ5Œtºœr\$¶öè^CIÀ¬FÃ"ë×*D ùkV:yØ - ¤ïGöñ·«ð«.d>€Ír<ó¡¬AARZ·ç<Ƨ Øúþê‚ëÕº‚ì@ÊB¬$SƒÏ›†sh·*³¶£Ù¸>(p5±(ä†7ýR–hð[š†8EšÊ]¶J¹2ÞÏh #\ƒídNQ‚A±ðo{¥~;ßhz–ž¡wãÏœ8sol¼¨íóì¼°­dÏb&6Ix/òM-ËOnŠ•9`"Œ×jY¬f–È Üº^ÿŠGÎP5€´œ ¾x<‚í¦.¹ Jô‹ÝÊNä†J/À˜ 2˜°Í©‚55 b (•ç~üÊÞkrŠÂç›ïwÄd¦¾ZÜuÆž;÷9c.äŽ<Þîñ•sùéKÙúÛÿŒË×rãŽzð ¨å¨{ÂÛðÕ¨Ðn¶Ú6¶Ûb¯õ¤wwUtŸc~_ê¬!²…€ ä®ø4'¨B 9`ÝÆ øm ‘€]ˆv<í——m¶’U–·øõxÿX«—Dôó¿wY3¬öýª6…Ú[¢d™µ„¶[Md3ÿxý.¢ë&Ky|Ù eª12ÿãõ×ìõ6)²v“I¨(ÿñ?[ýfÃaƒÄè`Á’§¤“S1­»ú¯¯U>CÕW¦$’Ré?í^9ß®+ÿUèÏœgÖ–*7ËúÇ|ôIúûµÛM£ï:ŠÕEg^îÊT§Úû¥n²ÿT„@QÓç¬üsþثתõLUÒ2Q‚-f’mOKbZòc VÜ~-²á ¥…`IÆ£SOtX­ìzßoÏ^9þœÌ—Sq¸À68¹ŽŒ ‰òü¹×9ÆàUZFñÃùŠ DÌ„Âr)ŸØ7Ù,ì…áàVO1†jïí¹² J˜ñ,ŒìG‘/ciäÌËŠ”<oýº]H«ª®[£•øAuÞC$‘ïça8Hyðų‘utHW4Óøþü—ÚÑÐ#Š,²•ôC™Â&à8U yÆ|‘O³ÿðå:už0 ñhð$"`W»Qo1‚óXJxþÕGŒçµT«.v ÔLà1Á;à/zÓÍsõžy*6öÑÉi)f +g Ô³žx+:vÏ[OÛþyê|;∹mc+\&tñJ ì œf~ÞH+$ëóΓ¾àêóÆ´4æueÅý›}ã«{™‰kñl[ªÕWÉQPäC_uÚ„— Ð?ê{ýh :®; „ž§óH3ª)[$y¼y÷T– À¢{`ÿ—{mæˆ0°[îÇ컊ˆ.ûw2eëÜÉMá£Fæà0ÑC·tkZ¢ª 2@öìâ ×ܾƺÕV¥µùXyÍoŸÛAR©F…\$_[ÜùÚ¹eÁˆ{÷1¡©5=Ï­òÜ\©*©É@¢~ Àû˜±YÔ³Äç,joúZ¦–Û\Aâk¬}ÝYl%òäZ¼].é§ð…ƒf“¡ƒ©â&¢àóç\ŒU¶`£­î›Ã‚0²7>ذ£¡`ÆÉ€PÐgh /®ã̪ý Ć&°ë¦”™^¥¶ÑAͯ‘ÍT$w&‘Ö ƒÒÄ9.%àN8¶k^ß=Níå1SëÄ„T"ã…¨?”‡X£étছ·óÖ Ú©ÔÞ(¸ºqÝiAkÒ‡5%G°áÍqÖýÏDÂÂ5Y/%¨Æi5¸ýoФ¦¿ãDYgUyÞvös’F€Ü’Ú?E‡uÃþŠ© /fÀPRê@N€dÉ@a)˜>h1(˜ìªùJaC©Nf-q¢QÐ&³ÛO œq.cQ0À7Nó0&kŠ?‚0ãÓB.§^•Ñ€¸X9ú9` Ð~+燸›‹E•ô€ð{šbçÉBB $0óI!‚Þ¿7°-”êZpoAï™'“Pb*x Aü YJ­l”ð°2L”D&.íófÖð™­~&·‡ ÌuF‚-‡hÀ§ðz¡ÍUU n„áJáÂà`Ó ?ñHà÷Þ¥v*¡#{Tü$Ëﯨ4}¶‰±ˆššÏç=мµy' $Pß©”¯?ò\³xÊpÇx@³`àDéô.%à’`BÄc=dÀþù]Íá‚g†A’‚ìLM‘ýn\EqezWDE’}†ªÓ†‹ÊA£¬ÔÙ{C’Õ\Hˇϛ-_?RJ÷X¶1‘Èmv¡_‚­¡ÜkÀØ}‡É=2,w3Ë0ìi‰Ò`ˆ7\¢"£–0àbvrž›,·fzˆ0ÄÖJsVŽvpQA¯ ŠôâÕçÌ 5.I„³˜´™ØÈ¶¶ÑC«ávÙ!_F M6J'<$ ­—AÔ€@/€(V˜pÆ}AM–hËýöaϽûéùeAV·…Ü[SØœ6ÿçV½ˆýI¥©¨'îý¡ÌœÕûãñrÖe' »Ý.‰Íi³Kvd2‚¨çÇŸƒœŽtž %}dc@ÓŸ¤&ˆF{‹þ}aÕ&ð“ÁƒHâ´”’eæ±$AÖë†Ò(’³3½¾â£dà¢Cü~nÀjÌÌ&÷µOA#}|ߥ?ìStw”îãêMF9ä.ÿÞqÆ*X6­R%t-Z$›šD™º»lìæŸéa¹tø1@ "uvö¸Ù˜"bZë©ÿçêhO´‘+ ðôS7Iœ\T†sD@­–T+ŽI¥xÛaDÁf“Äò­Òwª`kzÀƒÆÀÿ+1\í¨°‘¾ ˆµ×A`zx`Ö©xc'Ö Ä ±±2ìçcKè¿]Fã øñ¢"ÏÝB7™°ÐCÑôìj»JÎ^}4@­mp³ÄY¤òÛlš®,¢ÊdŽu9õŸ ‰yX 5ƒþQ%ƒ®&‚Epa*˜&‘ŸÞöå[Ò -­__@H4¤ÅáZ£·éÈ‚F‡žûÁPïLû0Õ‹í4±¨öÃÎ k [ÁáÌc…ŒcÑx€|m:À@LÃc<2“¸bèsëäÇØfTñ+r·[c’·´ïìqlìŠÌDá‰EÓ] âÅLíi&˜•Š`¬ŽíxÂÀÍ€wñz9xµÿ÷/nû"ƒúä<b (‹ýA¨ã Âi_Ô£é7 äû% TÑߊNlµTFãGËæ™DQ p ŽŒ"¿æ|hÏÀhmüÙVX€²0ò·ôaýñ ¼½gÏl+æ4¯j²À 2ãiy¸º8;è ?“Á@!ó`ݼÿPÃáM‘ÏGÄWXãŒüº‚lfÛšÐQ×]Q caNi Àz®3v{Í9÷NWƒèF|MVÔMª}öy£&Ìæœ“EF¤L‘ŽNžñ®zcÛµ€÷ÓxÛ[lÝ8š8ü>îßÜFôFºÙ¿œÚ}³7ެtB.‡&Ú­ƒ¥Òè*>°„T9ØŸ³áƒ5·ñ[U¢èÔTŽ´ DûÀ†Ž‡È±j ìÌú蕯3_SHy‡0K;½üptÐJ àô Ã/D¦¢÷ìá©iô28 I¨¶D%Õe¢ç€õÄ®–WZ™]‡sDaød‡ÅÃ`¡§Û8E~˜ï ¤@‘_I`Á"†7¾ Ý·Õ`U‘š0??õk,]<™t9§1zÉ‚aeÛç­0&aAôÚ§TDK¦S6û/h"q9¢­ËɼÈÃÙ[Õìòú੆ .þÛâ]ÌR£°gÞcï©'̓¼4§Q°¨:" (ž÷ÁØç ý.e‹T¯xð¼¶0ƒŠ?÷„â(`6²ÕÅcØBQQQ²ZS;ì^ÜÏ ""£ã?ݬ^!Ë€hœ­¶"‚ÔvJ„5„¿æyÐ&¹ûqÂÙìE÷Tª ,,^ ƒ{$Ù9[z‹»ÜÕ€@Ân´£Žš§ý‰ÀÐ(òz—²…P÷‰U¿û„QÛÁöÀ¸vΉN®–liÆÕÆOŽÔƒ­¢ùÁü¿ÇñUpüCy‡»98j²Q¬DNd‰A™ a¼`ˆØ=ˆ¨ p²KBúšFU(§#aÐþD4$%~ÐðVòíºZ„#5N¾ª^¬x~À«sbx¯8Ḭ+)'¶|ìVéØáƒ ÚjIÉÆ9*s€ÿI†²×HšM¨››.Ó¨"à®·¨  þàßËî >õ€Å/6ën’«l!Ïu‚!N1sß[ƒ…C 1Úåéa…D«%&R:LJ¦ñ(©P?ãZ ·T[f;&çг³°Ö¡žx¿€ÓÉ€j†VŒlÃáÄJb5²HüÒ+•ÚœhÍ·ÚÁ@™£‘òfSgÀµ·aªd!þ9ž/‡QIxár–ÔTpîõߜ͙#D1buÀtÁkëé¾ÝhˆA˜&#Ö^dZ¸^q ¦…Ûžp¨ USþ¦²bµ”¨Vž‡ý¿é§ˆ VÜücsËYÕAIfßäbN,X22‘GàU,‘€Û­€ûY"Œ‘ú'¥p©×Ý¿J+!_‘zùÂP¸|¡‘åcŽôË¥Þß; AóÕ>‡VãEc"ÿÒõ#4"j¸J(°¹V%]us¨¢«¦ƒ¥è'K`Þ*:Þ’’˜0Yª!ÃB)ŸŒuíY Çk!¦¾C‚ ™ @šÉ©:ps6qs2u¡¤xƒxÙ-yUJ1‹U¦Pdí$¨w°[Ú*õú$‘‘˜¢wfñjan9*‘ƒžf"8ð²+ÒbqÓ˜!mx%ÛÔÒ÷ è¯Ù>žòTgÛb´ô-K /?dÚ-`àqÞ,d4ÈýCωÄkÛJGù¦³ _™SóŒu ~3YªfY±’±–芓(©c9´©ΟŠJ1‘Lû%ÀÒœaî(+kyd2û!] o!]ÃqÈá5hÛôF®n5›Oü1 °ÁšAðœGP´ØãkCÚk~Aï’Ѽ¶Ë/lÅFª:˜.ñF›È>wS¶8ÃGi-„Ü$E+‰µ¼1JPóôºk5ïƒø:‰b¤C¨çÜf–,©XßF$”±(qðâ€>aûN¾Â×”wûï/æ¨ 59ã«Ýh–’|{ ïvÚ˜Ú<%™ñë‡ÿÉ÷(‚‚—…º.Î-hŽ¡˜>™«ÝGŹ=ãéF“lºðßä×`ÁÐ*Áß%«*ÇzµbÈ7\kö.ª«®ž‘_E÷dÖæ¿´‰öÌÄ•‚—‡øc’Â¥·ìÚoÖäyC­TQZ ¹üCf—Å¿Â$AR+X7Äú«ÍáóÇ êè…E‚ðoI=œ732RÇ1ü ºº ³” $+VÔkZ ã%à4ƒÚXcæ ?RêAç<£ŒªX*9hVý±0f¶<-…à‘ºƒ-ëDˆÿ݉å,d²ŒPAfð•@q¬Ó¶ø˜˜<7תcïb(j /Âj³Îº —P2„ºå…"flúÈÆýÓWöiɸÅ[amïÞCå¾Ã;/ÐM3Æü©x ±ð¨@ñðfÐä﫜¸ÇôääÿðB«4œ gýﱄ5°Õá]ùGØHöÓt’Gʱ[óœ Td"͘õ²VeÇãúÐÅŠG9Kkcšß÷QöJïºú5¬KC~¤Ö ÀBÇnïõÿû кnN\U£³†ÅY$€:E P.H?&1©z€ÜGf‘Àˆ³qñ`E„ÀQƒm5YàÎàe§sÒ#ID¿1¾§ÜPe&=0B8>­}Rp(%Þ‹o‚Œ­GA£ü4ñ¨Â€±C˜åbŒQ°%Õƒ¤~“8F :¨1m˜-ÎÆ}Qvj-z¶ãˆ9˜UU ÊêK§èÎ<åD0ÒÖ’vœ6ƒå¢pd3±œàÝ.d{ÀÜÞ¾gPû „+ »g)p؃5‘ŒÖ=`†•¡šÝò 0…²aÐ¥Ôa,ÒœtvÇC‰¨»U ö ÏF^i"½}0\ÒÐŽ°$?CéG¬iãýlÒŽÍ/è BÍ.…XžF–ñp(µÌ¢ªÙQƒ˜X«´8Ü.õ Ý«LµɃ’¦^‹`†X¦…ûÜ(„ˆŸG 08bw“P8vÇ‚ŒŒ«a@шàß\´ƒá›‚xÅÄzOÿVAòD‚¡KÎ%Ƴ›î'=­>Ö»Œ–G‹²Û„« ˜"þµiàdï ’]æú/A “Ÿ–ßeñ,Jþm%éþ³14· ~OÓü…že°“Èší[]aýæ„¶œ ­aŽîŒ($,tœÁ0HæÓ*ÃÌ_K´›žbJ5$L·(‰ýbŒÔ¿W'³á¸¸qým$ Vˆu £ÄˆŸP\ðSâÉaÏt4¬†“†¤¯׃‡¾È`à€JAïÖ[V¿¥âaáþŽ‘§"vYÍÚCIZ ù¼Æç.5a<.£Æ†EçÇ_ò¢wðƒ®ŽXĨ ²ïe@épÚ8ð [ IôPˆtW5ùIXìI‰Ö?Ê ®½×6àJLhòûœÔØ DɽËB©–¤ç–KRjå9–©H u6"Øn ð þlñ N~øÏ,AÏû¨·›‘IÄ,8! (>fƒ7T(W"•-`fÁJOùpmÒøAXbY¯-KöAäZ[eÙ‰Ò'lL H¥ðˆéä¸ÀhU³·ØèÆyÆ£ý~¥¬BÒ}Ça•"jÄ$Ôäd]¤­ªÔµáo”øúZ½iå´VWî+ þAà6¬Øû;ŽMþé*)›R—àæKß>ÒN&#ÝCú÷q,ìÐò0jQx ïM. íèÿãÀ(2´p«yÐ=#%Eƒœ¾!VÐæS´µh]ðd`ibqjñÒO]*Ƭ¤&—5Û•øûÑP ö?º!†8s'„Õ÷FsƒÄ“w¿cÌ™òÊ_®—?Ƨe_ó—ÅEн…èð øß?þ Öq@CØÄ”ïÖ;fE `0!oé²a¾±ƒ 嚘Þ<à´›TȈ.ÛøTC#é*ˆªú·® µ¢â]í`Z¸uÙ” Ó™K¨Þ â@Waf¦m&ËW–Õ!°Îq¼Ð`ëÌtßÝÂáB`qâ«Bn5ë%ÔqL—,ó·L!`ªòáÀª*ÍõÌ®Èý¸ãpa`Ø( ü Ñõƒ*ü Æ %aãÃ0Ä€é+€2ÄuYú×È'ì±¹w!ˆˆê@ÎBÿÔìkˆˆmMN°¬!$XEñ§ÕuÀMUdÏ"ç\%ÏÿÁÅÄ2u‚{‘ u,­ºs¿PVýOT£?à Ô÷I¡rí„¿ý†ŒâåûôM¿@vë[…¨t"B?Ò}§b”îòÊdÃqÌ’8Dy'Xj„]€7 ä†BY •Eüh)GÐ$§€Ãhj6 >Ü(rÄÔWzÚ.PŸŒFí°õR‰q“cYRoð‹gÛA†9WS1kXWeO9;†Ï 0È5kí'¨q߬-¨èÈÖ³œO${ 4q[>Ð_“\"çn߉Î˲Þ6ÀjDÖÐLHÙ~$⥗u§F e¶ ʬNS0³/»¿ý™¿¢ºÊ˜N¬š ÙÉ%3\Õ ª<–ÑìÄÙENÂÖ¹D£/£*Øà<º(²Nh”š ˆ¹E§qÜ·j}©RzªÁA,ø—’swø¤­Â¿ÝÓ/·amv[îe'‹nPuÂC0À Lq2aƒÿìl圡•%qTãDª8Lõ,”jfËlxÒËCϰtTñ007§&)=\ð2s‘-ZÀ!L ‹P^È2ÜÁ‚Z‰uM|êħoæjsŽ]•›bCm*Kxm°Ü ÆÈf>–PMi†wŒ/õ†‚œ`€~ 2ü*ð0H³*bÓƒÖ°°X°I®µ]ŒµVõ­NžƒVá`µ°Ô×'ý´eÏÍp¸˜ø6$4H $Ƥl<k‚ )|ß_ÈUbµìÅ3vHuå,u*e—¬Æñvða9€ˆW]Aâ ¡``Ýx5—køG(¥TÓ•f«æ÷©y¶Õ¾,À»2€Ö à3’¡ó3óÃÃp8mo9ãÉŸ›k–G,düÅ×VîÉ/$Ò‹PXRX²gKdb¤ÃžŠ±¹.óHÔâµ^‚'„b—ù1ͦ¥¸¶„f߀;fKž²ÛøL~7ÕjrZÕJª†º¡¶—ÄC@ÀHËñ‚é—}#†¹x$pµÄ´°•È g»ow°+ðùÿZÕaÙŽ%R'PÓ‡)K%àS 0™Ð@FAŒ_¥ 5ð6°œd£g€ÚN?ÈžG®Kw{‡YEûš «'›ìCÞ°Àbl¼ã8€„wtÖÛEöÁÖwyD£ ô9÷® öm¿T5š}5ÿ)[µ¤výÊûǮъZÈâ§–Ö·¦¤ÌF½¸…‹~—ûmµÕôÿ&c9µÅUß¡ÁÛŠ½°µÓrÌž¡¤ÐßÃpñõ;oóó3}Ü6{Þ10°d7{’ñaxA´+bè¡ðÅiŒÿ85Kb¨¦|ëžGóÌ^sâ|™Ñ¸ì&rîya^©E´ßíüóŸç<ç5ļ‡hã З©o´€ópR舜ÏOˆyp7ãù¯Oæ!Äb¼OóEÊsZö ê÷}×é§Àf°š)ŠñÃÊ ”«{é)þîäÎ*æã¸2‘P«l¾S/¼Ê¦k1zÔy)QR?óÿ  rg&Û¡c¥‚}Ú]7IËèuÍÿQ©Å÷¡*“†¢£}d£çœŒº&]à …oB.dDå©ê}m4ç$³…ÀÁ–ÄÏÖ>â}z5¸€«zñ©€üeõâ)—þ1¨‰óÅW^’[XIŸÆTškøBfì!*9›^‡2 f•{Í+±kñu RxÊø¼% qÃŒÄNÝlË**ÿJÉ÷ãÕ•\êXñ¨ˆ»° >8d{ J1n;$x“ï%I‰í¯ªÈŒ7ª›Sš5%yu6ì’3F"›±"£¡2m’·‚uÀbø­U·Žÿÿä¸]Ú4 !s¦>‰òšÂXh>_¾˜%î¸?Œí@Ll2 ’œzOë@¬tg^xÅy’ñtx@ ز¼€i”Ž£ò¸TR§¿ï8õêOl$ŽqÅsü.å-E)èÒ`Åy&+2—DLšëøÀ”DÑLø¸úÀ…ê‡æÑWÝÃêäJ´†…¢í|ÜòŠÙµ‚âw“ÌëµÂÒjj9úLPÖÓŽ0à@Ó:E¡s2Iƒ“¹ïþËA ÓÅã}ÑàÖ– ÍÃlr,ãjå9ÿw™7Åf¼„Ϊ]9mÀÄ砑Ô.wÝ :0œ3dš`o„Aü”KXmìLa C#¤GmöÓ9ÃSú°Ý6Î4–¯ P9¤oÐH ÈgÁ'm[aNú:´úàœá#ùÊ&úcòž»©¿áB'ðI‘ž·„Š.d?­c„¦Õ[ó —ô¨Ì@ª{¥yˆ…c¡ñsÌXaUƒ©‰bL5ì‰yp*,¼.{ŠŽTð@@')è™zXÃoçÝOÄ`) ñ¾#¡ÂÖ`ÁÆ$< àøXêÐÅ`HƒFBUa±òÍ6‚ذ 9![.nxZ+9!öA­?a¸ 3j2EO< ·}Óuô4yÄÆTÓ“(?”ÁGTëWJÀ€º‚$Õ¥´°g·ôÓɲ’xábUlG>$È'RDœR-µ˜‡km;ùûC†3)”áu¡œ—{—מ†hŽc{#Àk&äž1d½,Oñ-Dša4½¡-dŸ·£–Ûv2*–Äh°>wk¸¦-!j/>R,é\‚L°ø,¹}B²k¤2@ëßðe¡PŽ@£˜SkJÓèÀ"}xÕŸî²ML—€-3™‹é Ý3Vd„{#Í¡‚ŽÄé°wÔµÚq¯V$¥Ñ!Y¨Øó&£õlÊŽycÙ®g ç‡)Œƒê¬vXNL50Õ‹« å_»X1úÐóYÿù€$Y¹ñ|ªžM<ú5fcÎ#X–É7gâ •0ºï”ÝŸ ôia3Uèo”¿ 1n´öH¨7 ˺uÿCç GGD';ŸÇ@›K?,^lõËô¡Y]¹lki)WZÜbÑ}&¿³°~B Úò/k† ô°GÍ{ Õg—bJ«ÌJC«®’XÀJ› dágÖ41»ÇÑHÐAd²‘ÉøZØÙÁÎõ ­¤èápO@R;Ùkô¿V(þóAÂ'󕄈/“ÛC„”Õ`G ÝRCaŒïæ$[7ªÈÏ/AµUIG²@oLj‚̃W惽Á¡*&üA4 À_ŽˆøÒ…£Hí¾¥pÒ”™`³=w¨áeX†Èû®Aƒa/Ô¶´±–±ÊÐ*`i§¼ ½i+/ZÈvÙêL³ zR5Œ~QçøXøª¥ 7£FeUVºè t´ðW?D`w•“"!jDð`‹äZ ´œ6sV5æYŽrg“"á¨ÏL†àÖܶG£0U$å…4TÜx±•|Ï™ %"HÈO"º¨‰1ÃØÿÉD²¦b êøkdƒJ’R!ª§ñ»õd/ñYíâÃþXÛuÊ說W=#ÃyÆ]Åk<:ÒQ]qU4ÒÅbàÌ kìBN·Ø3J 4BŠ…UÅ›m‹±uJ°yñ•œ|výk‰Ã™6åErUÀ 1»qVPÊúÄœFÀk5d‹¸Et¬Ùy¹%Ì:~JèÎÿã(„H™¼ˆß2šLS†¦ìæ¼<~Çæ¸ý]x=í`Z|XÉF‰… O™É¸|}_ [¶ŽXö8Oìkj6RU›“¿ý<†’ë•­(ôBí‹'òµ˜ɉ¶¸JP;:…î4*Ê,Cç–kŒs‰ç!àëäyÚK>±5è_}€1³O6m$x¤uÇUxG3^}fÅ&¦3ƒ>‹0”T] Øü¦nçõãò"—wÝ„ßç& `èc ÷äŽO8´• k¡.uóéC‡4m992wy‰ÝÍšá C¹½Ð$€¿iYо¯‘IΧ&µÉqÀèù‹?ÿXÌ£[Ð’ÁoËqZ{°UVŸ÷ŽÒ³QÔ!ày˜¦{$4ÂïÖkèp€»ì€¸ânâßæ„>ë½`r[$ ËÛ®ÌÐH y> GðS˶’•&ÆpÑÆ‹?›Wöëg,b9˜Aáœû-@†T¤˜æ­‡ökBÖ´I‚æ¿ÄDà^¼§ é~n\^%¨“ À³XâN›EÙÀäˆÑ$¢Oˆ9A µOHlTÔüò¡JbÀlÍ‘Œ„›ïäõêyÇóo´öéCŽx ‰Sæ‘dV"S°Úó1%N¹é@Ž Å¡iL4¢ >áÕQÿfÛOš¾Ýµ5ã*+ºŠ†6åeƉ»A‚Cà1qnÔGÜ=áE—PQ{N›ÿùK½øx~šÜÔ îe_lHà0Uµ‘ÃÚâ¢Å×ÿi¨\Ópª®¿‚¢Eö9៿ýE ì&?õ#êÿW^VÞbê›òë¬vy¨fK°÷Áô-I6QüËbâwD¬ Qí`5YŒ1—QáÖÏšâ_¹Þà`1sE¨¤YˆÒ/$‰öÞ8ƒ³ªŒÁ‹c%¼Ä#Øó »DØx燜åÀÍĉ͠E[´§¸ŽšË;pi†zM±‰åÁ0Á³mkØáÞꤻü,šUP§ðg”àõéÜœï­KµÔƒÍåŒìÑάEêB>ØŠ±˜¹Z[u•ZðsÁË—–Êr5¿o%kš"a4B@B÷g$X'Ãa–RM=€»V­‹ÊÂnÆc#XFwGùtGÁ+¾Ê…h6Ð*t¹©fdv#Ö«l‹Ãß%¦ð’Œ}“~ìΔܾcÂ¥ˆyli€çû¼ATãCßž¿G“"a“yS÷óBOµ1/‚íÒ^¬ Lûôyç]¾1ûÖ‚Õ¨é]„úŸpžÕ.Í¿šHÙˆ¬\ÉÔiã±* Âýܹ?’¾›…Ø›Ý#•í$‘Ê/ò¸"K9Àlq²‡Z~9PS ™öc5H˜_ÒëÃuõó¸èYé |‰6꘵#Ë1ÁyÁ¾\ùMfôÖÖWý6lZU•›aÿNêšcëñJ€ÐqÌ<1¹ øv ÍC»Ñ¾%ƒw „_Š8tû' ÿÐQ±ü#«öœ°]jn›1`žFÚ•(ºs0¾[´A ¡Õ×5ì,àö6g•Ò|H?ÿ/«lxƒ©(ºž§ÁÿOžZ?v †UN “a;ôß €4$Ð#I¬é$+ƒ´-³[BoØîe6Ø‘¼NñaGc‡òÖo±jɆgļdE9¬×H%òEÂUØ,ä*ÊZCŽ=×DØuv{àÑb“1¹ þòJߨ%k#¤Û7\S@ÛЬ¼eª_50-yIF†íFBH”¤ða|…\¥Ç0 Á(o³„*" 4,ÑÒ''„³LºðÜ¡¢¦Ynü5·R¯øº95‚* ´“&óÑ?ýQÛŽ¿äk_?ÁÌÄéø8¤×ÑÄÒi³Y¶ hÁ÷® ·±[©!…‰ƒÙˆfwSšñ"(ON Rn¹ ƒD†V©Ž§Cê£Vñ?CTXÖ’Á1O›äœ Ía:ö)ŽJ‹~žˆ…uÝÄ6oSW3G0±ëxRâ‡gøBðÇH¯·30Ü9|Ã|r¡†ƒNä]Æo±8Õ8c—"æã7­ã˜ M• MàI?‚èk˜f‚ã‰_=G@³á¼…sì ÀàÕX®2 …Œ)ÀÕæW>¨-û¿Ùo·|™¥-°½ˆdŽßd‰+žÙ˜d¿×dîåîЭzÂAŠÂ±R‡Ô´ø‡G§–/ÿýk^øì+'ŸdÈä­Èî³`ŸŠ,\y¡# S\'ÉEP\”„Åårõ*P©5alÔË¸Š­T~µRè`kÔàƒóa¬¿0Ù$ì  îvôôèsYYÛFpƒ°JÑò@jP+rÁ¢¨lÝB%°¬j(€rÅ h*Ë©1]rYôΪ΀0 Q¢u /6À¨"ÆF`®'zy.ÙrÐA3&®jŠLTžlß'ÐÐY]‚W,Õ$çèurÖ;ÿ•º×^š´ªµîjå¸4# ”Û ÂðŽÐ!¯3à F+¢¡[@jÉ‚ØOƒØ& «™X­ƒ‚‹o†W&Nu*r7dHJR6Ú«JAìi‹!óѶ1Y”ÝR *t./鹬Bâ¨Ç!,4pšîU>8Í@E 1zÐ\1¬ÛH†©YêŠ6“T2KAl+#(7VÚ90Øq–»fÖckL¿Ö|ƒp”}ü7bÛ.€©F†f¤ÌPP4u+ ½‡[½eÁ&2[(j‰¼$¾{VcÇ+ć!Ïr†äŠ ‡Pc­ð¤Ô±ßA4—+D ª›OœØ¦êŒòIW÷ÁC|ý„š9 *pãÀTtAQ!´wú¸G(nì28@¯¾ 7†'}›2ï³fïw0¸»äï´3ñã¨mH}°µ…n~FÒ°u„ÁRŽ‚Ù,ð<Ù‰†áB 9Õ¬` vÄñg|ìôç)WšÝîìDMb'“xd~ƒªB hø‚0QЬ®Ëé>Å0‚d%W˜µËøBHÎÊq_¿*Uð>!-Ø]ö[L³Çň#+-aö Iús¶€ª µl "´éŠçôG´>Il‚s¹Ní‡_›9Æeý ¦»7Ó ŽI=>ž%.ìiFÙúµ35$ÿŠÝñΙWtå)…f˺tªû²¬³˜‡+íà¤<@ƒSßÿB™óÏ%v\÷q]²‚2ÍJ\§{ƒ$ ÖnÿúR©HÌț޵Ðgƒq"òaÊÈ­îZ"f”ª¾³d¹;—Òù¸5îiè\RŠÖ=Pôc&+Oùç!°:Ku$Tñœûì(,l3lØÄ ˜}d²ŠÏZüØ6Ê©-`ØsÖªAúË:ÃCÄj‚ÀPcŒÖß`-nÙH–“ë‘9]ímUsµ"N<ÉçÓ8ÜM!2dülO€7µ’ñl£þ ±ÞC4ïÇn,t,j;…v `:qüñgê9ÿô±u[¨Ô&˜‡„ÀÔr[ˆû|w+¸h}„¨WÎû‚Ç©Ç_„DUj£îî÷—¥¸Êö KeŸà±Æ*qÙ¬ ´Í„4í]ôÑI½²™f[•¹UjÞY)þšzнzûá…?0nà}ÿ¹‘XÓÀ©“ÿ®Çcª›ˆúÁÝ_›m ° âOn&û±¥ÛYþÚ@$+¥ð~:È3þ»ÿÏå?±÷öàì½øDJs/€€i‡£ßõú¬zð±!Tí‚Ñø±ÍhÑ$Jßû­0©zÉ}NkUI rög™à=a-ÌVCgο@€vâ„R¯Ì¡òwþ–‘«bQïÕz©Õj©©lucH.=µÖå7àoLÁM¸¤#D½¿oÁVuQª×è!ds8¬(âPîJ©B¶,%LRÆv ÿPê5 Šû‰RŠ>6âœ÷7n;íÆâ7M6Û7oÌ™*ñJOâû”ZH ŒjÛvT¿:›ŽÈˆ¥‹\+xÅT¦“E¸n±lUÿT‰Öò¡ÉÝæô¥“€ÁT ‚¡—”—b–¿Žî(ýHÑ•K—l·µn@Ý€ûV󟓦Å_ð†‰S¹ p/ô4.³ÿy~s$zƒ×æW[oÕsG^QÐ>ÖýÖlÅêBxïÞ뤢jžd–@#6=xM ՔfH,QrÜv}’!7Ø8(EW®®€$aÀ¯Ëãpâ‹àWx¼ ÉŒÞ[Š åè½<6„¼«}À>'ïîÝ’NW®üÛ䯱óY+jÇ’ÐÎ`ˆáÈÎJ 6Ö« n _©DÂy[níÀ+°3·JÓ†‡îJž ‚r@О}î&éc”[¹b/úŸE8=ë•MeŒï\§¢Sñ…HÆ9±’´à»(X·uƒåV6bª©UW07ºØ§˜Ú àÌ h6ë:x4 âZŠÍ•ÀiD ³‡‚6 „èìRD…æ^=3ÀsõØô£WƒŽpÿìáå™BצNØr À0­þ“üy/¢0ZÖ”j¶«ºß'á£xÇ µÆ/rhGä {õøL:È Ü9b’²pJŸâ0ÁDølR(t¼°Å)’¡à@huݶN3)òšq0”EþõqXê¤Q¿Î„׳»µF~·Ç÷ÉÔ*dn±6ÎJk~h ½ Œ˜à*I1S\›zÎTVcvjxÀü 0ц¢íÖçd™“B`N‹!®Øý¬½¼}ª@­<1™¯%X”(F¡€‰`@B%–ËY¬µbÿôwZ“îã€8ÀŠa¬r‰>ð±]˜5÷•\¹—mye EË<ÿÂOùË$d"N[]jèuÊ‹f’1_DŒå~Âwê•Ò´ŠlTlÌ€wE1/RǧfXZš‰žH$$$PÞ€µ¤:×Ó³*á U R¢ Ÿ$’#©D¸%õw7€ôÍsM¦ÃÍ Ðêd‚?NÏ8•>%µ©×cì#UtFõ¥+gë•oä ëGô50Ôâ€à¢(‚Mÿqïö*æˆ9„ƒ¨ 51ƒ¡‡L”:À¹J¤àXÉÃEt &$¾HŒg Ñd(ú©Qd> xŽÆÉûp¯À#PI"<^rÙãŸXêÉŽhÕ˜×>ð–I|­5ÎdÎ om UÊ3§a ÈÓÍ®SÌ1ÞTòyAò¨ŠÁóíÒÙÖ¿|­Æ]þkCšŒ¿Æ-<«1Ì ćxpÊæ1ã‚ßœ¦Ci9ÕKŒ^ƒmfùÝŸÓ8úe}¬`º‚õÝ€ëUBinËØ^—Ìœ,¤H€v1 m±„­²L#b)dÇù_Ê;ï¡ LÑ4“ãÅÆåôÛÝí(h¾X…†¨^¢w4 åÓD ”5‹”6éþáKNŠŸ[ûo´g ª»¢ÐÔ[´R‘VòÈÜ>Ûñ„@X1ú6Í_ò D4NDÔ[ð÷Å©i,¡EÔähH5V¯éxbÃÁ'´¦n²û•ˆ9êéjßÌ‘ ¢kZžf$y뺗À ý.‘«"Ëa¥hž¹–ñ¬=mXæüÕQF„x¡þ¸Íy;ò *«@'Ì eѲé¨ÉMrfp»}Õ[Ëf<`1¹^‚7u=ñÌ€TÖb‚*êÒ1dDÂÈÌIIã¾eÞÿžØÂHªÈŒ—‡ jeÀ—س)ŽàÓš‚&¦²P®X®|:°£û¯%š\¸­Óx/3ìñ'uRb#€/í¨ÙïúU Ò s^,Í~+Å\Ì…ÃR&¥t­CWU$|-?<°UM¢¯Ľ‰4þ½:Ñ'ûvá¢ITPn†¡[’ŒÎ=¶ÖlªÖ•…ÛpmfV_ô™ÁtBïñÎîÑdxÖÖ´&¾JVÎÔ…Œ9~±C–Ô,@RVøï²öwt((óc½e홑ýBa0JMJnûáz…'õF–§0>©0›ìZbØ«,8påšÃk)“~@rýU?™Ò‡V²ãÉS OÑz«Ä悜¿9€`0Ÿ°VƒJãÜ U^Ω^u†žÃñ¯dKœÔHª:}³ìˆ%=Óê€hftÝõ5FÑ+ý¥eªÛ†ìÎÑŽtÁýHÕ1ÝÚS!*Õi:hÿm`•Ú’ŸÉ!§ÚcÅcÃå3BÖW©ë'ßÄMºí ãI=`²=«ÀÛßé#¤ŽX ò%bÍÝë<ù‰ÞRÕÏK”ô8%œ uå³´/&­b|-øTÙÏ.µù›ÎɪŒ”UáÇq[6oЋÀ 5˜?šÛž£&P?L_RÔU%ê£?rp8ö‹~lWÖf)´ƒ·[´›Á†Œ J®5)z9aØÂ6ä"nk8âH*º…M‚IÅ0®Lm1‡‚Ì'i{ ªœ&/RÂMœ@&oBPõ`ûaLdY‡€T(£_ôo›}$ŒEcÏÅKh~cþ¤èÈ9ÈÀÀlðcöK`.ÿøe öVYUnéòq‹Lšž}T  ½.Q ¢ùìÃü3Cv®©pÖ°„$ ô)@`ÚØ: ŽÃŒC°Q¡~7:@ÙŸ-Á!ÛM{yáhe®°ØSydkE¶âÁBùv±æ1“’Câqˆ ÙÞ¤Wuaz%Çá3‡öQÌ ü‘ünx¦³í|(Æ­_ëþÞ“…½J3­<{t{±°ÀM'©„4²àwÕu+Ã@ûyÂØÔÙl·ZÐÄ‘˜ýŽ£ÏŸaáÒÇœ+<*¡YR³c²–­©PÉÙnØd¤ÜkCµ“âáBû¯¨IÄ€=ðèM[áúðT]vpH{Œ•|ˆ²""H´©•®Yá²úG=Ϥk䛘\÷pdéSë[¥³"> ¹•É@ûÁÑ2ùjEGÁlhÆÝ¬Œ¹]UJ÷[Éþ…RXã…NŸð{´œ2i¤uø}¥O!M_õ@¶} é'ÖŠOùާ1r ®¡ÌcŒpΗÜ[#”lÏß|+È( º<Ö›HÎèIÆ-ápUM[ªgå ö0k®‡ï“¤J–b‘(®3·Ç‘zák Eò*8–ªñhhŒ;«T”UG& ¡¬¢2Àü] ìXôôQ« ùÿöÚ©€t”м©ˆ±TDIyç,ƒ®…=/4c®%d!€”rÓ€4€±¶ÇFã-$y% ñ }iú°7of플Ý1äÓ!1„ˆÛ-þW$“Ò“€r7%Ú°_|Æ2§`I%ìUà½b!Åã"ó\ž’‘<ë`nÓ"f™-þÒœšˆäWh.ÁbÓ¼¥)k„ò𱾓ԼoñzÃÁþWútëoÆcé‹<É/Söá¢EþTeYyÛl* †(¸þèö¾*qTs©uàBžTÙ’M¹õzÜ$%bí²½X3î…«âPøè1úõ¸Úû¦"[v>#5­Ô`¢)ggþD[)Œ‰EØP Í€ÔŠ6‚ä.½íÙOP¾ÊÁ–éøZ„LnÓoN´]_áß³["ƒ2?ŠGjî=¬LÒèñdÝ…ûxnXÄÀÔ8LUØM®Ê‚6] œWòëdí2ñy 4žqÈ*U©®M’©(†xTQZ`^Oa ?qAjŠÁ] øâ uqÎÀL$Ñ|ºB ³,-1=H´ˆæ£U&\y; .f^ÑVá *Õsq$nïaÀHW‚žã„øfžH¤MkpY1îä6­-Ø‚?Þ+›b|žÐõ!ñ`)‚pÕ|fY½`¹ãߨT%~‡ËøÆiÚ„ûñÉ+=ø Û / º^pW¦ Ö­s—4Y³Ë h÷®ˆê<<³¨#ð¾øØ.¬+ x– 8ë#ød-°6ߦãK=Ù’}“0zü´C‹=º^+N]>ØÏÎ|éÅìµ< U1ãC;ÇFL0#Væèf¿ Ø{ã$ÈØd^œtlVÉØ]Þ2LC·¬•(yäÈé,/0ù»<ì©#6¨€)R:Áe‘«ŒŒ6M aÆ“Ds–9Þ@`P8 ë5Å ¤ì³ÜÔ7ÝZò'hýþ"[»(ê$ê/„³Â ¦Ô[ÉFŸ[€À1 © N˜C=¸þˆ‡Km¸Ð¸1FˬµÐ€#g(;]¸FJO¿çë.¡`~¤U ‹D¿çh¾ C7Ù©!´€6h55náÑ@ ƒÀL¯ eÕø‡ÓTszuÊQxŠ·™5cê½j«,œ\M±Û?°ÀJš‚NLì‰t7hüÓ1P}Á›m…@Ö:ø1ô$î2 d"d‰²ÉK'sf ðZËXﯭkUªúîCLÎÄê²ÛA¬;î¥KÀ•Ü ¢ïÒ„h˜‡T†ºâ¤A§-SØXB†ÝiæI°l—X³¸ÖƒE+0«Ôªj¥ßà±ÀÌk‡¡‚À®('ŠaéÂÜ åì¬'ø 1û0Ãì*¼´Ýt}á¯ÀŠ/n RˤM4n”òÅjcN±_…ß2U“]íψ^îvéváªå³æD¤§õ¯ñbž4Jãd0#ñK¯¬åÖ}ô{ßøÁ®l¯måj “¬^8G2 <Ä;tô*“”P»$ªÏÔÿÊjïç‘vÆK3‡ÕSa:®ªœfªJ!Ù7 HñÒ­Ç3à!o¦·÷å1í²šK°W ED'–f¸»ŠG…°ó‘œEYµªëý¦U;¶‘"÷n¤+¬UsM ècél½K 2ewÄ ”65^¥üaqm›Ä¦Ü x­¨{ .yUÓ’P`_;‚(h¬šïpú„ñÎ8¿'ó8*pè6åj£UUUbâµl¹W#+1¹<ÙGc¡¶ÄÇïtù¥ÅØúÆàd a/Œ×Ó†‰§F©ŠUØ„µ–ß„À iª eQ®¹¡ „AvÄÏþöà0ªŠ¨¯F((’C€jtP2ߣäl|…øé©Êx½ß Ôx~MÖa ´˜„£FÆ"CéKò_C1R9Ñf>æƒä/BgL>ìô³ ½E@Ü?²! NÙ¹HI%1ÒLŸ9Êô?^î.Q 5mÎ L}ÜhFN!ºùk¿hH ¤à(¿™A9aH<#v9 í >N°#<¡ ÙD×l=gðKÄf¹€üÕìgª Äà©LždðÏ ?ç¦óÃŽJ}†g…´PìXËÒSmçðçÀ0³¶`ñx½)µí¿¬UȉÆ@@aÇÊ0ŽãÖý›”­Ñïä qóÖËCž9)@èO¸MÉ ¶ S¨iŒÙ³U¢3–Ž©ì=Ø^á&+7 –4±¢õ2®Ç´Å^º9-äð-qx3Hæ¡ØëH(ì&2±T;¡Œ¡« T}‘iYÇdî/$‹´2‘÷nÒwUÀ˜|Ëo€3±FÁM•0t„-’sþ ¼ŠËy“xcI Zx”ñï<Ø™å&h·dv$¡¦Åù%b…T÷ùn üŒ«¡–Í®pÍA›–Žÿ"{þߢ|l¬|ÂÌLVf%Ÿ—Ö¬¹n¹’*\*Š–S¨xЧ>ÀœÑžfl|(è#Ëï±1}¬Y(½rÄ€FIŽG·>àÀ¢¾hÁ|á\/#H(*DêZƒZMó*=rÙ?ØšÌÆÍi“.,ÄKò7—¹ñ}[¼n‚òŒ3u'àMýg¼"ƆÀÇEšûôlnžÃÎÕŸ¦Ï‡¦Y1H€ß!•>B9ð[Û¢ë¿:Òó`âÊ€=M§»IÃÃdyìŠû–Ÿ »!``CÃ"<\Š"1è•QK›ÓÂ5/©Ô_ŸËb±7ç…hž§¯Œ(98Q2K«– ×Òáx‚߃f˧ûÿ ÇÛÏ53ö߇DÊÝÇÔGm©Làt³ü¦ÁÐγ}¡}†¡æ?–rïá  ¤:¶ŽŠ¿Œ—aý~?ùÕxC…¿VŸ'œ‚pàñÅ_ÛDý ÌL6arEt–øäÏTQ—wN\…ää„¢Žtø$yNà!Â>cMbbuMШ à ·$tzòü@¹nI!ïœ/TtµŒŽN³É-w&ªû}ºúåŸ}Åð—þåA>º€xòP:÷{OMãÆ&­Ú NˆYªüçû¿µWËþ¿HŒç4ݺ%…œb¦%ªÛýl¤§D<†¯VèŸõQŒøØbtð øñõ¦VøÖJå°ßG'é‹5˜ˆ˜76%¹`û+¤•n$,Âì—çÆ^1 Ïÿš’+"™fZ§‡¨ßxLËyA¼†ËÝ¿X6ö7;Ý ÑÙOÈåê«ÍG3U‰O< ¨áb²s'¿Þ À, dc’lÈ@@ð@z2'jnÆ#s¬hí‚_P·•à8$l©é˜©ªê÷ÝKˇò f?Hó®ÿ®èy†„lhäz°Á©,˜:cJ4ƒªzp™k:u€g)ÞcêzªÞܲg ljÞrnÁŠSœ²ÆáØ¥ÖØ÷áåñz­Nª½QYm5„½nG¥j¾â˜H¾Nš`·©Ç_’ñõ QâÍá éq¥#!÷GPh:XÐ`’„ýA õ/¸ÍCÑÀm sPi¶ jkºØÖ–ˆÌãìÄÜvȳ1MñYB}Ƀ62ßžáùÿcëSLáÄ]1¯CÛ‹Y˜;uüÉŸ ÈŒÒcR®®C":Q8Žç}o¿¹ê5R¯­-Ûâ÷¾uUñT44¹z23ž‰á×›ºi§ƒ·ﮫñ®8s*Ÿ† °ÒÈb@tN%äDS~(¸7q0ùp áÖ˜=IúØ–€½¿3¢)5d£ë¢ÀÙ‘¥3ˆÊ‰NÇTy€¯ÅaB0ê .7Y2Ç÷¬É`E“+wŸúiÅ ŒŠyCzÚ®Eù/lîGÕ-@² )8‰Ñfܬ1Nìü‰ë,!bB7TYÿt‚au=cB7‚áßÁa…ùtÿP³åâ'Ñ ×N¨]ä'œ±QfåckRæFKa“)¬>n*‘v(|›¥²DÀµ…‹5I'nqâ®ÀÈ8 (pÓù€Â3°•½t|R©(kf'là˜“…è£vè˜j8juqxÖ†ëÔÓr:aæ€À’$riºb•l?Þm:Y+ž4„bðÏJ¯ëŒÃueˆÕ`kŸ»q.™VMˆ'™JGS°ÜcçÌ·Ò±©A‡}à xt:Æ¿;ÜT¤±^³Bâ¶ ¿ÖÜ9¾²KL3p½ƒíÙbaÀÆ û óZµ&±2„$&DÌÌ<`S¥QÝ ]‡þþþ¯ú¶ŸSí2-Tj ÍP›_: Ò€ªjñþ}âT†Ÿ`–=­*È?»\}E~—܃Ù}TDrUù‡ü¾}…Àă@Óâ(Æ?O_eÙý"Þ&•3PMtÌÏš+Q×zÝB Wo’ ¤ò´h ÷!Ϙî[Y+h! ¨‹–‘Ï¥ÀÈÑÔãap´ 14Á£BO9L$Éüí¬ò[4Âú™OJŸ",æûØm¤z|=ôþdìð•i¾W[W¦“ž$SZm,ðà¢c¬’âFÓÀ,°$£ô–âéºÜÂvÊh&I„J•`€"˜p@Jˆ°#ˆý4%•€¸$RÕ"—ÌÓDSœ@ëJ¸1²à‘Z!Ó¸÷irt8-áEkC°ª¢BÞ#Ô{^Ÿg ôÙp5X¦sðŠì_ÆŠZ/¤.ù‡Ž QŽlè*˜j&ݺ•«-|‘ÌÂäÒ”DÅçÕŽm÷¿3H0rœË™M/\­€ÈBVO}‹áq!ò6‡ÄA½—ª0ËD+ÀCÛ§IÈ…;ŸôG§C(,†Hºpö £·Ç¶@µæÛ]'0—øH¹k‰AX(ˆ3êÒnëêÿAJK3NLÑmâyg\|=£ö5Vì„8yÓíÉ€n{2 ¼4zºÖáxDŽìñþ”ó8'÷ÒÎv#¬s8¿‰ ¨§eïÞêÚw'°_8—•T“ “²›küÍ‹à"ýê‰>£ëÝ…fdJ…o!`³<Ñ2î Eý°®õ:”Gľœ•þˆ›9›k(¬dUËо”TL"X=²X÷Ä]I2’¹sù)Ù—“X¹%—ÍlTòú¾²ÈþH”DÐ ä¸Läì·¸±Õ:e¯L„åü@>9]B<ƒ¡Ò´Æh5™³þJ}LV~Eé“Òˆ¢ˆ)Ôxmå_2S–'ŧêIêR î{fñ„Ðú?® qð ˜"_£ÉG½°`!Q,øL<Œ>ð²§Ô­†`Œ‚´èü‘i­2UÙ&:2½Kž5„ ã:(åÊUÿ€4×/ùçüðœ‚(ŸŒe CÒUÁ(lO®´Œ¼D4þ 2œ‚¹ºóχJÞ8ÊÞtM9ù¿éïøéCÐuVK8’Š£V8 Ê:–µj{ÕTí²cpúã;ÿùdń԰*fÍæ;,2Ó@ˆ~Ø™àæ…ëŒ‰¤ˆ=—1 9âzKãU p PÕ¡ˆl/ÑE.é—†›•ž›ŽYekñ¯%U¨M¬^1²ØV?—é°$_°á5EDÂ$ŒÚ‹TÙ4+a 1®+Ãs o›CÇÙ˜¤áÌŠššq´"¯L€BRXI/tQÊ‹#ä|}nÂVQY8ð 5&€ëªÒÇdÚІ‰ùfê2‡½1óè)/E†Di—\JÈŠiaIHöh³Ø«iâA@Afù"túª<§1ƒZÿä•ÁE\-T¾©ˆ1ŽìPl92w‡ú"„W˜êÐW6Èö/gØ,I\Jù1ñ‹táç#˜:²r¤Y2Ql_qœ CI 6Mg½u±~FI02~H;àLå‹umXaÙ¨pIƒ¶<;€+H2g—’¿:‡†Þ麫Ø4®†µìu†ýT¹Ý&j8p˜j,¢X…|Y¡ö8’Áœ$¸Â€ÔfHûÊC©„²-E¼,²JÚ´PB©¿Ä=Rµ©‰ý›!bl<Áië·ðø±LWŠ"¶3V_ÍRœâG…Õ†ÊðLÒ@¸<±kß÷÷½ï"Ui)w…Ï‘(ó’q¥¦ ‘Q{‹}/hÚ|›òp¢©´FHoßñÇÖ8¨ IÔ„š#ñ=¨¡üýÙ€´ý@½ç/…eSüŠ5)©¼~5Ÿ ¿í1ÐV‡(5-g´¢L‡l€Ä—›B?·d\¥›Â³º3Ë`þt_ X°ôš¥ÓÃ=7×bÜ// a8jYÉWÑmt¨U—À¨±xœ>`¢‡`Y>ë……N¨€“¨‡e1›-3]€Š›ID{‡á¢²vSkƒçÜwo‰æ]ÿ1 çkûÐÎÍÙ@XjˆîLàHïàê6¿áƬ¢ù"f(dð%£¡ÇÚã–­º[5Ê,ÐÖ%›(<4ðë,Âd¢å¸DIƳ°}›cc6‰þ3/³_FÚfœ% ò¸ OðŒž™k  ƒ¹"×ÇñB a+Ffqü²]Dè&ä>XUÎH;g38þ °‚ñ M"*‡gÿ¼Û –ÊÀ&hÒª¥Â Aø€ J4£½Cÿ~MC¼Á+ ŒdH E ³­°þòèÌL ÚV@4ÉB ¤p¦9ÙTÂY5?âü,õÍw-Úá-Á ·•¶e^¥uäI¤\<øž~õkÚ>Þ°#¦^ #GZ-ú9l}¤K\ ØË:ºX=9‘S‘„§ð Û1+ÞA³+ *ˆrrÊ>¨.p ˆÈx1,™rcúlø‡ @ëü Èzݦ5°8dVÄ¿BßÝÂ.ÁÚ-]) ±,!0{¥PTDŒ¶Š2œÁ \´Ö$6ƒ¡gÎ ò ÒQq-!#™™ƒêÓ…qô5,M‹‹«÷"†BTâÏê>8Ìf’h®¯ åw[T' -Us`oMKp­©-‹eÚ<Àf:Cžø lPÚgþhÌþ0¹  “ÃkxEØc7©o}в,k@§ƒ¥õ¦Àv•·:ó2`£@2h gcÖ&A `Hx°<–ùŒŸXaS ¾f-ö:ÊQ†åÏÊFI÷¿`ŽÇ·‡¡á_x`¤Å9kðÕᇠ`ÇA*CÝ2úQv™oÝBýfyùiÔÀ)ÆÉl†’dá ÔŒ-ÐÉѶ òåT˜¡3@[),mÎÚw‘Ì”_³,ð ;‘¼7‹¾™ÒîZ4#ð0ð÷ZÓ­Åõà•vÁ YõVŸÔðË ù¤U®+BZj'þ/ù$èÃáj¶A×Ñh$›V<Ã>ð»æ!êíÅ2—ü#€ËMƒ¥j›¨\ëÌwZ‰ÝZ+”ÏUÁAxé/—v Ÿ_±°ñÇpgÇø†¼ºé6j´á9ºv±$0çÇ—øJÌs#ôkIE 5‘çòrpnV‚,M,åšýÅÃú¦hà? >‹%48AyP%-0§»0-”glH¯Z÷\Îxp Ä0tNaJ'œ?‹N€ÁåR¡ã:®ÁA–›ÍV^&S$5‹ÞŒV™ H(D»c8„[œÃÚöÙ¥è` S”U´òôãû¢…;c—Ö"ŸD©(ª€œ¦h}ÑM€’H\ÊÆŽ¡ƒàñ÷îÆ.NÌw^3tÌaÁãQ"`[Œˆqâ¿ ¢@@`5@1œÉG!?Æ0ì7p‘6º*wËÃö€ *Ô-Zx³H&7œ{—™Eé~äüt « *XãÁ¨•ÉêW¸*ʲz¦+a2Æ„<ž.ßwkº3XšÅ¢¢Pñr–§ð&R)ÒòQÑ¿{±šOÆÌ¾øA¦Ð¹2k´=çW_ ™šÐ&â€;ëni)?vb2UÊa0ZEèÌ2p ÈÅ#;m{ÀüKSBúüñ1˜Š …©ý°ß 8Gø²³‹d!( :Œ2Óí ¸0Ö~4s4¤h÷CG8™8¡ ˜.f3Oe³‡òSš]›»À;X‘â°â eÞÐ¸Š³Ô$à+Íëlf剿ÿÞ-ê4ÚR6#Zìl×>ʸeZ7ß‹W&4=JM¬Èx#í¸ò¶b5øë˜¨*ÃTýŽ´ÀŽha:rY?++â–@ÖHó„ê b@ù‘"½"ÂüÃòë±>ønj”Àø›² CU†'¥ÑóÃȬ™’ë!¦Da©0Ôfo€©ÉHA‰Ÿ9 å[×|:¤W<# 锲©¦BÈGJj5×®«©T5‹yìzÇ<eJ:l‚1N/xÒûR'V\è0:ʦÏ:й’b`H–˜@Â/ïnòkD,g·^ÜeB{ªï¥$F›€À4 ,ˆÞI¶Ngóški#EŽäS&®Ô0`Ó^j"çz§T*h.ÑÔ˜a©åqõTHL@9”€!Ryº‚¶²Õ®‚ëò)#M%äH€– ýV€‘i;9ðà 9?eib(H“Š%°Êƒæz¢0Æ¡ßÂý+ú¬¸ZòΘ–Sºô†]Q _ 8Ž’1 §_¯Í7æši/¾ò€ˆ«U¯!‰ÁU#£„î:±n N§8ÞsŒ€8J†*BÏçÏ‚BkJôµh÷z–¡Ö‡ Í+ &.ö7«„K¹[ óØ -€3Ö‰ÇüFiJ",É4‘饎þõ\_0¥+ð^µH¸ó¬{ÈHÊFJÀý@ö_âôO¾úgJ¬ÓÈa6âHÃì{ѳAàCÚšlÂX—s•€íiúj’O,\$TMê)¤5z^ýW@ª*ºV*bTêñ⿈D4z°Yqp\-!ØÏ†œœŠ¦´[T¡àãD,BØ$ũüy‚œÐËoq4€ù«™)y•F"B¸%¤ÌI^u«²ÇlgÚgHÌ­8«@À4$c¸`D=Çü#ÒHzr<#>ߣáD—#L2×1Ot?ƒmh]™júúSH„)vZ*‘i=ÿØEY4L;ƒÆ¤Àv¸¼To,¸{.lMéCN{—ƒÙ@L£Ç×TPš¢ Î>±óîéáXB H)«²£øá±þ •€‘‚n¥¶:?`zšô<´üuò[(ØP1–ß‚tT«NOþ"cƒ;4áYR: |ä{€Ü0džxö_}'U@¬Ù—Aþ ’PTNŠ×ýºš¢O’¨óã«A#ð`ƒI‘ŒºÓ4G•0$Î,©æ¢³)7ôI©è°“ñ³8…¡ÏÓocÖ„±ñ)jð,YÎ?œ cYxp­àÕ$ŽrÈÖÁÄAìL¬ÐýÕ¯ƒ±;CaP*HÕQ”Y¦¬\x³Q‡Çø=rÆrÀª\ô™ÞÀjvÚ0ùÌýá±jW¶‰Eaaüå™ çØŒÕyK7äá¬Í¿ ÷e~¶ó…”B ›pæItkpùÿ‡Jd€‹%GâàO¢ªF–\ÝÎõ¹)ÍiœnÁ| ÀY9_ŠÌºÿ6æYÉÉj£ Á©¹F ¦i'&^ ÅtÅ© ÝÙj*ÜñE‚ÚZÍáO†0 (üÈ¡2äWnø-¬ÕÕ ‚€½ø9î_©MƒÊ‘A¼û·ÉŸ0|›š…HÊGÄÁ>ƒ×ª^ÿ‹ÐÛ¸é˜ èÙDy¥v.63±Rƒá•«Ê¨¶k5`ƕȯŠéÀà¼OEÇUö½¬Å¿!ÇøÉº逭9OÝèÉy ¯ö±l …ÀÄKT áä @Ù ?s0ÕWB€L$%tÈv):`™)°@Ï¥ve2#äk1¤qoÃ) 9mÍužõǸðš¬Î004âÄâÜCD@ÆÛ/¹¿h²Ì ìÕÆÙèK©Ï Ñ ¨æ‚°§îa½ª6`ì…¨¥é1ÆËY穃 ²ÔÂå$4¿¼27t9OÁ3QëŸ-¡uϦôðYÙ°±“Kíi™Nȱ·X¿²A¯4Í¢P¸yór^º”lª„(±, i‰¸3”ZxZhѰpÉ?NXDs!µ6àÙ ¿k`Å ÀPÏÎ`0Ÿ«\ Ñn ç¬:1[…á~8僄•F†æ…p„¤à%Pá‘ÊaðÒ&ß”vÕÕ¡.ckÀ܉µ“ÎFMù<ÍÝì DT‹'òÕmG*E¿¨÷xvÚÇÜõžì[½;ylG&?QÌna ¤ßPBí“,°Bdy¢ "bÁdz):Òü˜!åÀXEŠŠf{a>x/Á ¡t±ðA5È –K×€‚Š nSZt„a-Rl²p5~l·5ëVS|R+`šÙÕÛ ¥fÆd‡±eŽ Hh¨3‚P¬3ÆMbýê—:V8 *BŽSHüVô¡~m'¹IÑ\¡.›µåAÇfvÎÃTr]¤PGh9‰§½üÿkY¡e§­)BZrDEJà*TSÆîýõÒp©Žò…]E7øpä±±€»¤¾xe`öù6•-[ºP^pÀÿ5­éšøN)E'»eœPD öçÖ-`kÒ`aaÒ䯩›ù1cJly5„\õŒ”(ØêØ»!LÏuámb³¨ai’ȿɭoØØ‰ËÈ2j„’Ü»ÞuÌM¼†º±¦^'"KØÌ~ÉaC(ÀdÂS£Z—Þ™;é"Ík8eðþ™û/Ê¿`°õXåRUsbW2êêÿú¯¯_û}¬@á ˜Ð>ÀclôJ)z#À …Vêxþ Ø9áôJÀ Õ…Ã`@±…‹ ´YéѤö„xÚyQiH C¥€ÞÌ6Z­ýÆe‹ˆ@¾ì¼®#Ó×Êu š`1QÛYšˆû®·-p]쉺"bœ®¯¶úÍê§“žøtýÓÿ5SŽ&ÚVõ òD7pž¤åV\¸^w"9ÄwTÎÙ¨Ír_¥võÛª&Qzí7ý(Dt)™CÍ6}®¸‡Ÿ“«ð`Å‘eö_LLæ‡S4ÇV<ºÔ&Šf°àŒxù—Oï¡Úp<êE¢Û¬ÜÝø”¨øÛ‰q_#Nx™­Ö½CázİÈáî!ÈV´íþ(oxÏUËu"ERJSËyrñºØ«M?ˆ=j”ún®¿¸®\vâe¶ßéO šPXâE£;Ä£í¬rË6d(‚äu3Èç„?f¢+ïHà§¶ŸÅ`1ª¯ôCÞ»þ£_(C•üXO!÷ ~”\(¯]‘óN}¥ñÒld j¦:—=Æ4ŽÚÙÐzÚŽA‡ÄáäÞíÃm”YöD/ÍÑ88éh¼Eèâaj’McS¿0¢ ’qwä.NCëO„s5Zª£¼êúùÆ2ÃGIˆ¬ |'Xub¿á>ÑmܰÊ8Ù†4¬ÔÖZ h‘é-ÚØÈ ã*1†Ôµö’ŠŠêY2Òu’Û‡_±çöL3‹`óÌ¡ ¬¡ÄùÌ9Éù}ÁŒÂÏØe U¼É5vQŒj·¥kU¢Ó¯Ï7o „1º jOÿåÈ¢¸Ø «tðYÙ ·ÉBi<ú穚cÚ4s&Nx­±<“„¡#|â¼!7c¬lÆ‹ xV÷¤G}eX/o~” $(¡S|Âr`WÓùˆÌ©›ÿÿÈúÕÿÿ>ïË7K ÇÖîýïÝÜÊ"T:Ä܉Áe •þš.»óƒ½ùˆ¥ÈnžÔošf¢E NjËrÈLa m™aõ—`y‚qä!¸¶ðßUâ+ïñK<<ÒÐ:[‘ L  u”ACÐC@Tƾ”‡"^-:\Òð¦< ÒœfÁ3{îÕÔ‡™cì7aQèØ}'úMFœ²:™}‚sv˜sÒ21š¸…è~‚‹Weç¾É‚AW˜‘î¾`À 0œÃ¾HÇ·ýÃv •üsÖ¢X5%P ÙÐÔ^«.ÈÌhINù‹…AŠpcàaKÒQ*•q(!ÑðÐLŒ—Äi±ÀÊÂÁíp¸RB@ –>T‚>ŒV·êoÖd‹õQeŽ•ø¬% «Õðby3ªL‰(dáÑ÷Œ!f”X/ÂÁë9­ ¦.bˆµŸ×`Î5É ÈÔý÷Àà*G&à ù‚ ª@¾ž` :Ÿñ"b‚À]¤CýB‚@D)ßË¥m²8__&‘Fס¨ZD !­À‹q©4`§…'Ïâè,¡rKIŸj^IÓ:3ñoBÄ4ñXÈ´‘¤¶Ñ‹ M!ìÀKFd/|à@$Cˆ°kp5N´Ðr‚1²&õÈñi<:(™¢!ÿí`Ïw7Sôp}%y?ÂÍ 9ypÃ×(EEÊcië•Nѯ^Oéÿì_‡±ÕÛÈT;cL7IJ2õ ©IÖ:ºb ^ƒÚ˪Ê~–Â)DDéݬ7]_vÐø—‘Á¡( ?š× m:[pjUñ]»2GàfŽB4)Ÿ¾ß2»lØþ£)§z0<ìvídOøÁ®8ø¿ý«Z(Ìþ-a…&‹ õ=ÕË?àÆP0UâYêqçßEŠpÐb@]a Ú}$~h„H ¾·I–Üç¾EÚ0òj™pJ•ð¤*‹ŽqrŽÅ·¨Ö¥ÊjÛÔªu*Ô•/.*S‡{ óXkàìxÑFLè^<ñ§˜Hë L¯æ§#äf"ø¶¢P¬=°ùe.äiI% Ò MN§ë‡ÈÿN_ý®V‡x¨ÍðúOl¼.L•ò£<§y„CüîÙYõ$¨öâÊ"35Õr°Ì?W.•¶Ã(ÍOôF4G±tV¼²,­{˜\ß ’Q#ql5Ú´œ÷ i&Z_ŠdI×ú* LóÎÓ-Ù›Ülζ—Àõ’8ò]ØÞv"g!dcŸ¡RD`g¢V‹0[F)x·S Ùéé^@÷÷¤mP­ç¯Ì Qf‰Cjø(©¨D³5¤yö'Àâ–©‹ž[xª¸ünYN§÷EAƆ¤&f´‚í‘k*ÖÅxË·ªÆŒ6ÈTQ,YQ²Ó·Y˜œÓqwüe˜RˆÖý¬:ãø*©*EýtÆ[Aû•§õ— ºÃ ¢ác`ULh ヵ|g6J]C|+é¯8QêÀSÛÂj>ö,w®„‡‘†©OÉ„ˆ‡Ç9ª!¦WÉR0˜b]hZ'…À}ƒË ˜.&´âò×Ç`š‡µ…5 €;·ÜVÖ( cÓÔj’”b€ùéü°“€¨€ˆ5…ãQešé™0lsÁWïßT8®q$s…&¹ª$T¥LÒFUÆÐB0ÒÎ À8(ý]"%ðî’>´Ð™èxóZ«Í•T®´B³û¶Ïü’±LžMç@å® œ­R }8äKís^Ü2ZLQa^bÑ"¼ªÄ˜‹ùøð§°ØžeyñŠKÀî©nBì§åBÌ\å_vn¹èüºà7[ž`z"!˜x}P SX!ç:í¤Q©mÍ¡Uw$û¬ÐbåqÔ?æÍì CQEà˜8v q¾™™*Mè ó¸¹tÃüCfrÙéÜaaßd&wÁÚ£y#þçb •ò`qÛc¿³PÖ}ó\ýƸíÚŠ|v•@Á Á©œƒ1ÛGôJf)ÄáödàpA)êQWSNÜ|áÅò'ªœ”"…DØn¤à{(z }«+=©m‹ÏÅÞ°o$Á¥/Ýõޱó—µÁdÁk9ÄU T–LÈ·u0sOíñÄ "o`|MRìºî<9p¼^¡A–!ˆÎ+!òÛí;shêÃsÁÉF)±rÅí‡Ù)Cù¬É‰¨¹–ßX*ŠˆA¥ÆÿTH©ÂÀu%MXZ·HÁ©WP$ÃîƒÕìèeJ!bK-mŠÖ‚ª .o‡¬Øê²ŠÖ˼ç´ ¨{ÇP^¯­ÂDN/x>†€£h(#£àgx1› · £Òp¬HøõºrSð6b;ZRQ+’Dâ`­º)—¬çå‚N}ÂÁµŒ›o! ì¬+pµ–]ÈYaÛzü¾4-Ù¤é¡z–Ÿ‹ÒB­ß)$Øyí: ¤Œ€~àûØ-#KK".ìNj¾Ó¶F"÷¼}¶cÁéT Áj©^+§Ó°*´Ô|ï%&¤ÊƒwxdÕáeRäï2)'Ôt=x¶á0ï7«m¬œ([&go6Ü4L>α’­øÊI¬0&¨¼ˆ‹´‹…xÑ`áÞús÷p2+É™iY„‚7€`ý±ý"ÒNÂg‚M¦öÉmß,œÀfÂeÇÜ¿DÝ—8‚‰9j• pͳpœt\ÝŒæSe`Öš°,Ò@Î!d¡›×3¸iOç 'Gpj£`Qº10vÀ‡¿3å1¾œpøe¤åB!† _^h‚έ²"/|ÍÉ?f ´lÍÜÿ (-é‘cú?ŠÓxœ”¬ÀøYJ€±¥ÇçPÄ ë+ŽÙ°MœD)†)Gü¼þ #´Ga0`±.Œš Ýø.2`Ž>{).‚ÊôIÀÑ6_e­Ûè$—-¾¸Z{–×öÍÊÄÑqðδ·!8%#ÀÃ$ì78ƒ+iP¯D-Úe¯ƒ[$=&ˆÌ{ÞŸéh£"OÐd R}™v¬S°sæs¯ v}ïÝô  ¶ãÂÉiòAU†00ó¿Ÿ–þ5ø±JwÉ$Ð[Y”Íì[9kà« IþßÃa¦…\ ÚÁ´>&ã8Óñ‡Z Ú.ÝÓÓì Ÿðþ±®šYaGF·Ðqà• 0xN ŸeDh°àò“5tÓSDg E‡:òÚ!mÝØ˜ Ö®´^ayÜ•:—†ä»í"ÿd°ÜÞ±w¶I¡uh TIÔ}Þþ 7ô€;ða}íЂ¢#"Œn#ÃvTBâ¨Ö "ˆ‚f,§ò7¦8ͦY¡°“™xFTƒ?a3:–§»gì\lõÜÄDÌÆò!œ´.Ñl‘à ü‘ôR(ç1‘& Â&KÑ,§î¨¡²8§y†øïaÛ±·¢ÌÀ H)eyœ³„±,ë ÖëÒÅX­uþ»!P0õå’ÿóü0ÞCÜ!Ù“p6w7óRM¿sê|“äõÂEøÙ¥÷˜˜›¾ œ)aAZêJLÎlœLêÙ×¢É[T¶½ýÞ">C<p·(¯3•{ëÚdÚYÎ[ ’ €Ö¶7/ð1I1 ‡ë`L‰ÆãºÁqÚfyŠªìµ“ï;Š_1ç0m”2p ªtbÃð«ƒ$¡`S\¨`K'Ï?…‘INX®÷_ÿHFj, „Ê/%û6*EíT žXrTL<©qý&AÕ\| 3#zf¡ì?ž/½ª¡Ë5¾»’ßæ:Qòî²ü=ÝhUI&ó;Îò©@3{‡ƒån}ªì>¤«Éx"fßi1txpÍK"to&±XúGI0|Ããäáè–|‚ˆ}ͼ ðìr­‘tè[ŒWŒµœîfÓœòŠ$tNYj¡@fœ*¾O½¨&h»~«†'Ô$¨ä\|;[в6ƒÖ~!ÄN6©Z ÃOXq;Ǫ¸¦jnX–‘Ÿ“G´B¾M€ à#õĺqY¨6B¥vµé.`†q¨AtvéáѰxVºú¿æú«•TµqñÏéÃA^•ÓR`³¾·c'¢`{DŒx Ù×+ŠÆ’Ëú›!ë¦dÖ(ø¨7…o†i”ÜÌáä~°LüëàÂ¥w3Fs†ZëU˜ b™ÅȘâÄBŽDxÛÆðPwŒ6[5œ em²Ž— ¡A‘XÇô7Ÿ¬÷{%Kbž ô‹^x8¿M§üóÄŠe‘¹7䃂ÊyÃc$Pv„&8QÖ„œ¦°âÂðŸaÑ–CÊÃK]±‚tudNïyr±}–5eÖÓ° èõ¢X¥H j÷¿n^äf꠽ʠ#E¹ØoØ' …«¸]zå*-`j’TþÊÖk4±`|‚`8m} YÊêx´ø¥«Î^âd8C‡¬k:F ‘ È……èžó˜MÝþV´YÆäÜ/'àuøm©Òt@‡·aŽWÐ/š†:di>ã4féÆÒdŽƒÏˆ<@ˆ7S3j¢´LïÁ˜Ÿ&/&Í,j†Z·ƒ0ûe-hÀivò•?SuJ€av"Bƒ!Ì’Þë»4±f Ws‚Vô­‡X®Žõø4eDt±÷fK“ o þÀ0q3¹ª6]9ÓE³z5PŽÁ#t‘f{ê ˜ËýâS_x/¿\ -)˜¬s­„Ô ’€kvž{ÁJ« [›•‹Š¼Eîl L„Zn5#ÔJVT H-«aèG¸XÇŠŽ@e T/Ö©Ö¬²Ò yØéBÅt€ììÄÄÖ9>ñê(ø‚Sd$aÁBjlX¼¾šž&À]†BF3bUq>Ñe•jŠ mÕ¾Ãl1©Á#hiÙF°–"…)Áí‡DƒÃÔÈKyŸ<³è•a†¹.6"Å 8ŽJØKq6×ÈÁ@Àb 8‚…÷ùDPÊ Ö\a€ó0®à&Ï£øH‡tÉ•\ ?—³Jzªè½ Ø&©ádh"ò·Ī2mq@èC˜\jé~{Ãiõº¬¤sŸhãõ è¿€5,ÞAçqVƒx÷á†Éb7Û|PâAÉ÷¨yÈQ2æ^ q÷÷ù¿>„]ëIý4ÜVñ~q˜sJ×Ï Ý"ô_,&dÈIf•Á~É…‡~ Æ&Çö-C¹…4B ȃ͹õ¯Ê®1sÄdÁÌ%»ÿ#­Ž•¾W•$[Å{—/„Á%¿Í] Õn"³œ­"û¾t©Daïú=ÔÝn§Þ$S»V“}êúæú¬¾kž‚þD’x?š{š ñÞਨÓõxLa…òÚÃp§Ètg¹ü¢!ý¤»ûŸw=H<豈Š˜ÛÓÂÞ­qfqƒEÄ2Q†¬*žËg8áÅõ æQò$dõ“`B²†[Gñо šcƒ?ISð¼à{æ¢øîñPUÒ…ÿN'®,±{ÁÕ‡éé_ ~"˜—,%=ú¼X:Òø1díw£É2ÿ]„hðy­q‰PpX+ lLOGY‰ÝFW"÷5¡®Nl9 ¨ùj˜2˜€gŽ÷â>\û¿A«B ±˜VçòÓ£à°ÑŃ‚Ž$ ¢Ø[ Óž^yl!q¦4°õK^1UR¡ÔÐ /$P\Ƨ”·7…zµÒ¯ûö‚™ bik‰ÈõÇõ›€Ë©< T†+ösÊïœp˜ g'Ÿ)mˆ€Ýr6EÕ¥œ8ÖÿðU‡^ê:¬ê«¥ùÚ`Y€´Õl¸Gh £¨' Àýõ0(ÀÅã|ÁkZûÇz¯­LŽ ÛïŽéƒH#†%³Íšüq~xú`Àóÿö›÷½Ü»ÝÝï…ÿú=âUˆoçÿϽû»ï.áVŸya÷½qh=«{™f@%VZõLü6~$uŸÆRÐ Šj Ëé‚!3§ïš( I–u9"^²´ 툅8âůphÀÄۀß‚•"@`€wdIÉûVÇ3 5ZŸ8uß ÏJZR8NU^ÂçXâ âÜo=6>«â^“bÄ g<¦ÆD=lC9¥3‚‰0‚ ëût¡ºYãñ~õàªÄˆ#‹:,ý7R€fOÝ çÓ0’Ú-¤Iƒä0-J’ä(–$Èo9Bƒýt7]¨Ü…fÇ&ƒ ¶'O &@FðÆsT•¿FWEî·b} ºÚŠˆº0—7ЏÅ  XY2Nöjƒ¢x@/½Z—Äð¥›y‘A©®¹”AHpSÈB¨Rˆu‡É¾ „y+VU̘ Ú F@Ä*_‚ÖÍt¢¥‹˜ŽÉÌŒœÂ#fhõIâQ[ð ظBVO”I:üjsQ‘!‘(7s¡³KTµ[¾0 Ã4q0P©ÀœyW¶Zøl °ê/rSuEe~5ÍÖ‹ÕŽj©ØLsWá&e…}ôÖßF ø™":­j´Š«‡Ðrcú ¥àµ7‚àvÂ`h»:mjg¡À4Mßoº )"Ë U‚™ä©¦j<6ôAÌ©sbÈ–º^à †A—¢\{Â’³ø´ éscò±33cÿõ.}ô{óü,A±Ë›$&¨öî­’{¿‹ä¶´P÷RZ-•øº¤9OèWW_^HDB |„§D"‘úò¨¨}Mö²³HãV‰g–%#ÝÈ Ô+kÓeüàˆE:5U¨¸ ~.ÍÊAÚË+0ÝmG<ç¸Ã$hU%+*2ŸOðæi:-ì&7°”¬ŒÓi:O«ºš ½¬s7k+=í¶õ5Ìëÿ^! qòÅ×ñ7üÐ!®\ ífnÈ¡§bš®‡öX*š`rÝ4"@UŸ #^Ä»°w°i©¹úb¸ú¶wý’#œ”ääLî„a^ÒX½ÂÕîÖjÿ[Yäž ~yT˜m ú7ý |ˆ§~_Óºzݳ“Ôßé jî_ã]&,D/óœ>Çž&¹]Íî¥'9öÉ£¨²Êˆ*½°õšHd3s]Ç2ÁÙ¿mϰdM! ŒÛ!\’K@`N,²|>†ˆ»-žÞ(L±›ŠAXíýùvÿ3¾§¿ÀQH–tŒú{HLCž–?‹V¦ 8¼ý¥µ¨6ÁrÖW¸DÊyÀMÛkúTIfw©jW,ç+ˆÌËX7åj³=J|È|5ÅÓZe­WÄÛY¯%ج¬‘­&ØáaÈ­ä–i_<4ÑÄ<¬òé~î_ÿ  Sê‘©IIϸí¸gbÜØ‡|˜s˜"}”òzºÝnÊ@CUC«ÕtˆÒp0•ÂJÔ—¸áyvÙ¥#$µ´+D=e5Ð.›½*¢/ ݱM:ÏHþ¾”‰"Þ#&­´¿m±ÆÚVÑ¿ôŒÈ?6Zz0ªÒ³ÿ|ü=ÿ«÷*†Š)拃žE©óQqž•"|„9Û¥—ô@¬^¼dáégX¯±lù ÍHÇúX‹¥ à$›&†_ô©°u Û !¦.ÆÜ0F5*ìðflZai¨¤ßUé:=€¢Õ¢ÇznŸÎD#1 šŒg5§ Kg4D{­7Ø´~;äÕ’¤t,RÕ6Ñû–”ŒSõÇ8SÕõ¢uª¢Åp€áT+ôpÑá2µM€\ g“Ù&ïÎc…܃ Òðvƒ©óqŸf¡+¬¢ø³È|“UÐ|LÒonñÖ “ê D;Ü¢'Tý B_ð ëa¶ ×?°$bÁà‘ØtÜÊñddÉwnP5CvÂðEK\gäH+¡cŽÜ8`¹2 (¢«Šx}hš)ì qí ݱ¦Ö}h^Êe»…OÎ+°/%y5VRT×±ÕM ,ë®é…r·±'nuNb̈(.ë{à ‰Hæ ¶î%Ílç9te ¢ú·ÒÍœcŸÿ)æ"¦Tü¶èßÊCWÌ®±ÝÍj‚¡ðkg޳H»t©¢|x«Š!8U,—”e&n¨ÈÔè%k¯•p‰«$Wƒ3Þñ¿EDêÍ/¦øÎˆ ñ¿Ðo#Cɦ£Ãæ#¨Nk=Šˆ['FKEcÔ¬X-køB¨W¤ç 0«séÐÍ(È þâÑ ùÆ]zæ‚`$É«£?ŠÉðw©t¾Éd¢D ýP o…ÿ|Yƒxüš-¬¦)jaj®ëE(4;2³P±Œsoø¡‹ƒ˜ ²ϳ"4©µ½°£%–ISø6öÕŠå$ƒ‘xä¹™áqš¼;ÝwAÑ:-"Ïh”y% ìF´d5}^÷û‚šopÔæ•¤jŽåvchðûÄÝ\Cg«å‰¢i[0µŸâ“Q5cø·ZÉ Õ!|†«l°ÿJDDp OJc£qmjÔ•QY­Ö³(Ü D$»£8pü§¡_’ÒþD 4¦•c9Á'„ÇJÈ­Ä‹ÒI9…Ê¥I´!ê´~jçø:«GÀÿ¥ûlÍydŽ÷;©Ê˜j¯5ŠÊêÊ&¥&¥¬¢HEcªP¼£Ù²_ÎŽï[•°:CÖñÖ!úQý>¾×ÀÞ%RfX¯ñ°¼]lu\TTåKJ.ÅdèÅ)ðÑi!ôq¾•þ›ÞÅEwÉKÆl’ÀWC­m‰+/¾ֽɅK"e„oìQ˜› ,þ’ eã·J‹Uî'õèKXÌ'‹ì—˜|(ë(Ò·àHQ²Ù!MïÊÃ;]¯l…B¸6³Û À¿É4Dž®½»/ⶤÅnv?Ó/Y2aûŠGÑÀ¨gwŸüËÁdNˆJÆ©ê5„Š×6n°ÈDºÐß³³dÚí+CàM=L0·Í¹ö†lÖ‰¹ 6ó‡)¬%n¬Çr{1¹k6:F‰ ¬byLQ>œ ÑCú½ÉâOye¼'IW›âï[‹ÎáTK]eRÆØ ´ÊÁ¨brrF äÂFŒeÿàäÎb< Lø€¦Ç˜ Ë=Ñ@Øo®Š—…Àî«[&˜AƒW`ÍÉ»CÍæì Ñ¡µu¾¿Vsˆûa‰·SÙDñ^µŸ¦x¸ðï#¬@¡- ò}* aè"êÌ6 òˆHË¥øw±·XÁøºñ«#døbÙ)ão¶¶>&ÇÈÐ"nïé™äDèÎVþÂrÄMv³©ÂHL²kSõ˜8»ÂeÛ-”ø&“¾õa‰¨oO– ­×"Û(n7õ‚÷ ˆªa娚[È‘T)éZ=$®I1a6xÁ4õFk™·tþè Öb ‹¿›Oþ•U׊ÕÖâECK•Á»VÁìEÝG1:ÁûI¯‡1KìL@LÌ,"êÔÍXƒ …ÜÜ©Œ² òì¸W>÷’óðL,©N¸™xŠé‚ͶP¿B«p~¶öÂÃòßP¾’<¤1ab¸ly<Ûªœ­m5Ñòß$YnV bº(Ôrä,ª]”£¾ÒƒyY¬Mó„Ü6+´?¤£›WJ¥fŸ!påº%uªÚ4ôÒZ'%ý!¬Q‰œÇjMN Â[EzñZZ˜ÝîÆÄ¹¸LNÀÒç4£7)ì œÆû#=œ`@ËÈÕ÷9Eàã˜øì%uÝQ’9 É,šýuòr£€›Åþ€»3H˜ó“&Ea¾âþ€^bHŒ ò[Ú+D. Y ™ òx\“E¤÷{OÎt‘ÓfbHÎm£i^V¬Š†máEØ-U ëßb•`©á ö)íœpÓEÿ°í“{¾Õ6¡¡¦flò–þl'JcëûªëÅ·(F¤ƒVÌŠ±'XËçÖ¿F{¹„‚¡W\G1ÀBPÇPøkPP¬CñœiÚÄyMŰgg,F 0áüåøp¯Hˆtƒ!…VP;i<)j§´»““4GÞÐ 0±ú6ÁÛŠ¦Ã0%‚ù†ÉVgtν¨ 7L€ñ|Ó‡ÐfåÁøä:ù¯ËP gNGÃXp£¿ÒÃêÒú=¾y'¹JE³°KzvYGû;Ä´­OËV ü®ªÖ|ªT%ÖXÇ_ÏZþ¶/1BTÌŠÚYЪ-ÞÛ"C0~Ù5óN$,{F² nXb%íÖš¬ÅT.X‹Jø˜niÍ«ï´vWïœJÓõˆþN½}s9N}?Á?)€–À…ß¾9®¡Ù,ÜŒ#*™HŽÆ’i$ýdqšd=Ìp0pClÈÐ≦ܘÁ¤ÔÇoxV+¬§_ÿ 9ü™9¥ðÈ24)F¨«ˆ]ŠÜpV'¡_>Ûz1k…}ˆ&óQÃÅ1š2žÐn;~ýËUÍììÂà8pFË©Í/çæt¡ Àt™r–†ÕÜùC†§@ÀIâ6$·9åI4È/ãˆ3\€Ë7°ð"z `Îc¨¨­AŠÐnI¬ É”>Ñ7Í·øØ¹à*Îê<æ,Î ÌŒÆâ‡ãÙ¾øÒ-ƸÞáÿf ÌÞ‚x‹ ®!†0y6‹$:NËpÂ*‰¦XN"¸%«nÛ’UV:Xh€á9á(#&-!J~¬vbÌN8y±bÇ3É9¬‹Ô#äÜ„Â(B}Aþ/ûôÕ·ˆbWñ1t‡nX’ 4„R"ßS¹Òýµ*ýìÙö,šÏa­¢í¢®ðx?­™) ÉÜŸÁÄ €ñ¶,º°Inkå\ ¡å;Á÷dÿ;jσà«©´Î«U^§Á@fçÙ[‹PK‡‰×“Å ܨ 2 ¨’£&h[‘54ó¦€ í9”wãÁSøSâû˜³0 ³“4»m )=ê<–Ú|7p®jš×DØÏw{K¼C9l‚¨°Q2‚,Y ¤FïxJsõ‘Hqâ×þÂdÜÌ_m`LoDÇ“­/^kÌМpŒ8É ¨»Ÿ¥nR°VËx˜ËE=ú¼‰™Ìßû9+—aÉ|Òâl \’ÃTä7#”N’£€H w ùjòÃ0˜¥>ŒûTÕœ­}‰â 2t 0ý“æ«ËÒõ K6VZÚQ‚çëÀy-kyÀ1…³#Þñ…À%xI¾uß4ÆìÞN¸C3\NpÄmW#Ô×=Êùõ£óÃ%÷\¼¤¿mÙ“ÖÌ&­zIj\þ“™NK¯K%QTë´xTt£îœ­"PV$­"c— ³•2hg0@•”M‰r1Ú(òuª%äâÙc{t"CÞ¹xN+1‚•{É*`¶–FÝ›{Ö8=h58d  §5‘«ç¼¹[ ‰‰šCʼn¹]ƒì‹ð|5öuó!Õ8 ©MgÝ^-ùÈ¡DÂêÙçg Õ1rš‰_ægp K>Öñ-Gññ¯\eä‹?®<]JÁi=ê(-Õý--ÂÔ¯ë·Åìwƒéâ´EDTŸ+í¾Z7 Ù›q,íź‹Ô-·N Ä,ýUxZ›¥Áo¡V"i„x%¶Ä1í¾_âlÕëb_‚³@¨&lMÌÈg΄uSœ-œÀþ™ÜŠÓÁ&7^íˆLÈBÀYÊŸTåÕ`ß„Ø U â)»£ H`{(»çŸ ‡ßþûdm"—ß° _ F°Ì;Š(Ví}`Þ«Ðmsy„i¼ ëi>NÂÇíÆ‡¤hDPþZCgTEÄ%[†xß‘|ÚÙy6…:R¹?Û;¢L5vµáÁ;‰Ûc¾5S™[p€WjäŠi}½SåŒß®x+ FL%ñ:cδB—ûŠAÞ(½?þ ñ*ïRü5oñúyrH —û|?ÿVwcw"䑸‡§)Hh¥õ‡æ çÊ1¥@O9îE{Ûà ˜™Ù`:дLÇÇ=˨ڦ%¢•…bnD¶¥K7˜…T”~Û}¤ÒkŽB¤€Dßgoÿü˜å/Wwçó^”—Å|CÅlâÿu£å ŸSpˆø|oþ¿¨õ#’¸þØYѺE@ï¥fEÎb2/ýjÚß=5!ÄS}x®€ ]ž$¶ä#¥3àÁKŽ À&£a,J5Œ˜¥{À˯tÿçUö6ïÝÝ÷‰Ü*ÏàÏñëà©{ r=:í¤Ç£>üòUÝùY®7 /_Í®`ª(´ˆ)nõNx¸Xd°tT—6b€ãUÞ¯Åðt0’`Z´~İØêü%|1 6HlÂ9àJ«™=ùQ†HmÌùafQk4ËÜ„#}¬˜pí/ÔÁs˜4cTeöñ“p3þí*°n–~ÜM6ŽFà±€RЂ1ÀÞAgØQ$âÅç47Éð“†OîK+ “ÊÕð*ë7Ù­¶ZbÁ°~?Åi‡í8piƒìÜÖ0ìB¶({Á ÝcŸûÏæ€€ˆ@Яc˜P«€÷5BkUª Vù ‚§âõÇ–ˆ~=Ã4° 6tQså[ÕìâÜ72¡“ÐáúÙ”¬…ïèÂS$saý@! µáª‰„Ãý(¨(%°Éþ$÷}ôüPð€¢tðÄ8o¶%eB¶1­(PŽ«}'‡¥®K#€h*…@"-½Nhê×6bœ_(ÿ$¦@¨’ñµN|feÙ¶RHª†È’õ ¨V•Ë#žSÀ•quˆ¦†ìËþ„oQMcüCm¥lÆôô¹²Iƪ)8t‰UµçØa¥šIÜŠ°Nc"Å¡ab¸nÒÿcDH¤ËØxñÕS~ ,+R:DsR^_[ZUn÷³áÞà/Ë#9)ôóJ‚"oÉœ î7Éjíâ^ &@·Uê È•"A[¥JO‚æX¾ûû °G÷É'4CCJmÒÒ‡¼1ˆ>µÞKuY’âËšÙB˻њw?J¡Q"†ÕÊP“‡ýáÃØî {nÄŸÖp±¯.¤·I†Fh¼òâJ¨'œÛKØPU)ɤxTÿž¥k›4€ØVÿžhqH *+Û„´2gv~ˆVŽpáãÃÜÆ³\m‘ÏÞÆ¡ÚSý15îœ4µYíËæäÛ)R*íUɰÐvËׂG^ν"ˆ:å^]µåUˆJn’·£úç8çsº!¹F²gü¿â"¨™Â”ò¿ åêþ­Üáí7šÉ#ßF߇‹ÈEk¸òçìÅÚiß™¤h¯— Ã|«ÓHàqVoÈø?Xt*¾Ð`–,¹é¨©*sþœ+Ï3Bv*ü5“pgøâ.(¬ Ó×GË$QapÔUhÁ¡îØ5!µUDÈ%v­òR‡¹ÑŽùºÊp^MÅ!êôeA€´N­AÙ'0YÑk?ˆ Žó‡ÿù\ Cþˆ‚vºX0<â`–J½8•¼ å/tlg‹lÓ¹5QHåF Ä,Ž” Yoõ>_Ók’3Õ¸Rbç¼;x&pRÓ0(»ˆTòÀÆÌENHÄÑÄ^O~„'ä¥UÖ*Üð85Ò1%Õ«F:×ñJd@Ø>——iòÈ~E J·¡ÁùÊ"¼"9"SIy‘L®’>éÖ@z³ä„> 3'Î%ðWD$XdƒÉ’X8È"S¢K‹Ÿ.Ϩ¨‡4 ŒÒT½FG8Gî ¨sŒ%)1¥¦E#N|8Ýb!“Ölé`ñ/C?±Â'†„Ï<8, á× ØeÊ’ Bzä¥ "›x^_jÄ`ÔQú™šŽÀn*p¨?ÇzOãP‹⤱XÖlüY1‡È¥©xÿë)¬œNX9ˆ_úðSÌ/âÍèÔºlKm‰ºJ L13Q$x\õ€<Á*¼-pn熀égà%ETGê±ðãíq½l”Y€ý¨óÄ RE-f‰•£ ‹²„¢˜õõäþHº]XØkkõçñÅ ýp’DP%?i—~…ðÆÁx¼4õÛqZaR®[®†k‘6³A–ãÚã ¶ZnúI¡V¯=âTK W]"ÑùYÔæ;ë!‚ô±š¯2É uþ€B¦kÔ¤íÇQ!)(ÞO[p¡3T%¹V²p,3*,¥Ñú¬©Í}ô:10²Ö^xñQ¸—v†õ€þ5¯Q5C^'ƒd>s!z=ÀcÒe_ä1l,w¦ ÅuéERÒvœÄóƒQ›”úÊñzÑv—æ\9ªÁ1 <б/Ò7 ÏÙ ,þ—ˆ”=c)‚aºY9B[€âÀU;³¹DQgÙ’EÈÊ ;ºãZIâ6t#²y Ü“þÍèòBm,¶H6µˆ0¼ä ºÜù(qåSý›c!hNÀñkäõã]š›'ÁnÞÌÆ®d{ òUaÃR…FR² 4Ô…eer?³@Ù‰ˆFôÙ®‚NU@MÀh\‚‰0!•‰ì WW&i$Y­bNnÈsè•Ð_@“ɨ-И °çä®—AjƒDÐ^­•àÔ¶¯ÿ ‹H«Dƒ¶eó‚›+kœ”a™6JÔܶз(¿³6Q``$x}Ü'Ý8àût¸@£N\;72|›[®Ñ´Ÿâ@\Ýq˰¿bZ\"£[oü¬ÖKY…Qöâ³Á`Î/\D†¥e‚Fá¹]L»´„vfðì9–²W  EšIú4;BwµìÑbn¬“‘Uhø¿ë^ìœI[NfELEîdÕ°*>q3pM0=Ípã 8fºDl[.j’“Åg>à9¬¢ÌŽðvÌåÕ:yÌÐ]÷a&)Í8«r?-e9wÛ=DpóÑ©9`¶³´F•‘ò¼®+nHÙ$ÄÂÔÑ67ð¬š.&cû)׃áň Û52eL¤µÇ-y ˆ“·Ê‹£Ò—Q ÕM Ñõì ¨F+ü‚ÆF|jf‹ú/QJ%3Ý ¨Ü^Þ}Ô]=[¬qãÎþD@Àl‰ÝÓªâ\f£ºÍŽ”‹{d„u@€h/°3u…g$ZÜt1óä!THÖñãaßçÓع J„é MgÙ`á¾Z8…¹˜ªöý=ð ™áqÒ[G6g1ÉI¾ì¯ºßþçd7ï¢ ÜV—E’­¿c¤YøtÑwR&†Ì…ߤ£ >ŒR¡\º¢~2ûÿ ´²û‘y©7–Ì|ó~%Ò2R‹PÌSÁШêUš/_Ãpr.BŒ`ܹ°ž‹jîm‹(‘ضVk$öàC¶ÓäaعäÄEáoRµüÊBtF YÇY¡‹ð 3ÌÓ"P2Õ )騺€>ÓlžláÁàÎHÜtAI@™»Aq@Ë¥ÌN@±à Í úLÏÕó³3ؤîm)5ÆÌyö'‚ϰh‡æÁz½‚lìÌ¡ãÊ 3z2‰Jºõ€â…'0 59ÿ Rˆ)Žp½Þ®vè Þ¿‚ òÐç0tªÎpŠ‚çíæÿ¬—ô®zn#Fà ¼ÛZ, ÷oÁ— Ö6u^%ÐD-ÍM¬×5÷ßÁ„å,,aΞÃ\`xObç!qàóáwŠ~LÐ +|Y‹sÎŒ=aGµžFƒ×—͆xí}ëú²˜ËÚ?aSF5ÛV¹…E²¾„aƒN¯;ê.Ìp;ÄûðpèÓ.‚·±²Ü§ `34)ð2#÷NÜVvæO##ŠÏ­ÛËí&ðëÕÏêdR+VJÜf¶ßø¾V¾ì&E-—‹—Ä%„»i<+ÿ%0õI¼¾c|0£ ÎCˆ»5§ÜV;6¥3è“U5$ÀÆ5cc¢Në%„ß4dnÇü×î-¦~@YÒàžH°È éT²Î6 1¶˜ á78t xZzӃм$#â„?áÆVhåÈÔtC#Ššê´vïÁgÃîdôCmËsb¤±™ÄÆI=ßàz(ÍH6ÐT 8îÉŒQ·7ÁC±;¯  QýcI ɼué6šo´fwÒª¤¹³¾—ããþG~Œ˜–úN©p¸ÀMš>e+^“̶þ#Çÿ„Zôš»”½¶ÿõÿºô³–*¹i“"ã{›~–çtÿX8Ú'laµuFN»¨L¸lÈa‚$à-J$ðàÌäûúÅýVúßîï“m•+Šè‰›Œò@øÖMÁ ¾—^vYi1–mA? Ajpu1œçJd: |2½/ü=GµJˆCî©Ô¢€ù/O(øç0Õü•”xØàa\{.e*6Þœeª¼4¦ki¬m0M ÔBl¦°rÚE¼ÒBÃ}®C¬¬•»¤046ØÖ|[é¬ZÁ]Jä8h˜š•y÷¿Øu´Y !7Ù¶ é«aø×±ãŵ\`ª¸ç7è»Nò0 0ñZý£D`ˆ‚^þÛ¯ ÃqàV‰t©‚L1 JúQ¦¨péΜ jÉ-áõ4šiJêöUYàßÛ÷0å”j9~·ô›Â&ؼÐ&™[ÐÓ$ÇÒ+¸Iñœ,FJÐ,yOæº=òw%jïP^ƒH‘Ê÷×`1üv¨fR¦ôüHÔ 4¤Î,â$n^Þ©ÓõøåÇ{1#¨jW€A.ô/~Š‚di¡Š…üoè£UJS#ìý²t @ë¿Ö¥”~-|Ý+”Z„I×u¼~¯ÌÆl” vÀÖ*&*4¦¨]Ì™.:­ÒÌbã9ÜVûD+„ŠÑ åÂø…I´]Ç=Ðø¢zfh„oˆ~_‡JÐæ=-lþO èm½dèJÑ9Q-ÖÁd[ú!„öþH«­¯‘¨¢‚ËRFøu‡a*¦N®Pe›}žñ"JfHIŠßIpp›¨*uÛŽÇÙHŽ£žRœ µœ¼ˆõÝ¢ î9ÂVìÕ&YTI5zÉ~ ŒÆxD·¯øDQp= š›{nÐ Ðõ´3«á:˕ˑ+瘦tÅúIûDãXe¬4Q(? "SÂdrcßo?ïíÇ4m>(ÔA-wÙpɤj/ óŒ.¥§ÐRH„Чœý£R­ë߈ëdõOZû/dÜ#É®*C™—µ^'¤ª€&‡ä¡PF¨¼QùJƒzöc«–éN¾ZúF²Iª\ЍåXµ”ú‘šgZòñ2Û¨µsÏU’ÂÝÇ@ÿƒ¿.Ýâ¥×­ÀjiЛöJÃÏ y:pý+éYk=Ñ?Q)Il!eWž’ÍÑFE9mð+ÉÉB'Ê0¾Öº’í+ØÖ´ 8qö¨•6Áß1f¤(5ðšñ¿½§šÚºÃö¬Ò~cü$rÚb á‹›þbH¨LL˜>†¨p¯ã'5\ak ,b 0élôØÿ·!(ëOLåxEk’)4¿DBƒt2ëeº¬q¸ռ~f¶ \½”Ü6Õ\‡³&[ܸ*•CÚúšØŸŸdåreJ»O‘ºDšBÀL)5õãdÓ6F!å€Â£ošÉ2Ä‘jæfV¦XÙHJ®1˜+Qê.u7ù*¬ ò’pyȆ„œÔ§üöìÙ=GIäL³ Z¤„?üp41sì;\^€•ˆ-Õæ!£w…ª3ÔÒS¤:§R®/6~çÒ¦‘>ĬWh9€j® Q'†,jP93Ï~ªë¤+kQ «b§Šµ¤[g†$ç À€ÍT€ö6X ¤Q9 âødŒF ÃÌTòÄ,R‘ ãOD0:ZŽU”À0J "UŒ€ÿZÆ¿IáÄÉ?5ð²È9ÄcšLñý4”80L:˜’…ðÓ1¹w™‘$€„VÚÂÇüáÁ~°3šHŒµÙz/²e> v¦o¶Ñxb™X]eºÛKNiÊ„ŠBý ¸}€±C –±¸”n?a½ýÀœ‡Jld°yó°-ô&VÅôr¿eÊ¡%#ìΡÆagÏÂŒf #%—ùÛViïì•õ‘f Œ]&D®‡ "ˆõ×AFvlºÔD$‚Àvú£òOèœ,RM;ŠG‚b±_­)ÝB EÿCí‹–Gܬu±«ùàfd #’,Ï3ô&v)‰Z-zÑ*Äj]ñÛÅer.Ë£lÓŸجô¸Ê#0<>[ ™É·¶uf?„ðÚÜÉëöýÝG•4fߣa©¡Óå=·paIÖÞÄeh!Š2˜ ©¦4WèöZ"4ÏoQIue¨}+æ%:1¸MÁÃE˜·Ìl¯IQ‚=Ÿû2dÊ‹4J.!ùfì › UH÷¯¯çø7 û*»Z¼K¢Š®HÊCX†h¸_´±…¸™vgØ©˜G)ù1Xv`d'ˆS»8Àbüü6·:AÃX$`Úæ¤[é“öe‡†d•€õµÎÿfÉè¾ÖÉž2p¿}æ‹IïÚ;é_‘A2%*Ör>Å%òòù¬&0ûP´°{0¸Ê¡®I®×÷ æDR+“?iÀ­#<¢Õ ÀÕ–ì=ª ¦g²ô%¦R˜-n¼ LÑKo!iºêù8_™¯Ãhå"#?÷w½À‡ÎEUЛ#ÄŽ¤!ª «5•žÆqz]) ß,¼ §o¦›…d n²íFgà?™ÖWÞ;Î+ýÝ廑ɴREL]J¯éø—ZÈ #1ÕâÉÏ‘³-.P_ߨÛw¯ò0~̆;ä'q”KÑ—oq´(iƒt¡ôö-É(ë/N‹€%LiW+¼vk¡ë÷j ܦâdýZÞ*.íX=Þ†ï)JŸýâð”::Ñé,;%,#«ùƒdà» ·¬¤²Ü);UˬWázîdAÄFŠ ¸XWês\C¤ÿƒ27Cÿ³dÌ(ŠDÕtƇr¨áJPõžŠ ‚PUxK̉¢Gxû^Üæ`)S³fÓ@>öòAZÕÔÀêO¨‘9(Ø ‡ü?·SÎ,Ì羺B‘._ˆÇ1b‚;5ø¿••ä™°%Ÿó*°ª>!/rd(dã,¡*¦¥wnÒ÷< ÏaZþŒG¥£¥ß‡«ÖÞpÛ¤¥õòÈáºEߣ&n“4{ Ì‘,e…©ÑÏÈÀáA36gWÇÃÁ¯€|êzü™5G¢ VPs?a$†<ÝDtæKƾb^"Ü—ÖšSùˆj·ZH²ð£dMð¶X@Òû°¢–m¡R<²Lùmk²³R:Ûû<Í8¸ÁŸ}*ÕÃ&Äg–¦4óa\=%¹É¤ÁÖ&xˆÂþõ.§¸z‹êæ`å8,UŽ7 °IhÒì5!*G˜¼Gz áçìA0·•»¹UhâôÏ}ßÓ´\I©Æ©å€Uáß"ì³°UÂË!Ä´²J¨öû_çR!XµA×g;:ƒ FÈšcaŸ\×þÍ'"cƒ<õ£8a¼XÙg‘‡ÀЭ‡¿-;€éøÇ%: 'èØåYxö¢Î~ ßɘÂ][r+¦?|¶Iñ'?]x»¯'˜½à^……¶¸ö ÌŒ‚šNœ6&"í&·ñ§?GJÿt™vo–k‡8ÞrÆú.…Ò˜7rçëü˜$-TÖä©Ä.žÇFÙE$žþ¢8Eû⥙‡ÞšÙ@âÕ?$Ý_‚›#6rõΉs‹1îµ@êž‘æJ0I* ¸:Ä« ~%á6Ÿ2Ï\*~.ˆ"1Åf<Øïã¶'ÿ'qesÓv*R:?䦮!T\œú_!«¡O°û±d$C(ÀÞå2°U”:W G6Ú:5¦’Óqïy07s]°ZÀ8†ª£Ð%XááAëçþL³®ƒt¦†…[qÌSïÂqMòíM“û›Ò –I…‚)®À§Úçlo†\þ„o}ÃÏ¡F~4ƒ ¶àÍddÃlü3w<ßæSÿÝà65Édl·¤¨,¤~%ƒcë>~|  ¯0 A¡¨l˜¥™(7þÉý¬oB¡wœ°fW58=¿\G„hùiØÐ|¿P¶Ž¸šP/M,g™ZN„D¬‚5Џr*ž¡3d,ÚẮïwŸ¶ã$}uì*C×R[ô¡~UkXþ!ôôù×ù˜³áäß,#PL3Z²NßVa+K½åÒw£ÂÊå¤4­‡X?f¢´>vºkû3`än;îGÙ㌛öП”/bbÁÝ@"a×h:Á à±zƒ+ÁT‚F4“7,LQ7™‘—÷*"*†ÅÌ Gò³*€ÉbŸ{šƒÔ°þcxਸèê2­žÀÿ.UÊõÉU§a6­V™Ã‰áų­ Ô ŽVØžÁœM^%‚Ì͘¤Ñ"ýs~6@øF’ènªÝg Ü£WœˆzâYzÇÀ2ƒ„%Jã7ã˜7X`/V!3’ªê}hÑçžnÆŠ[–ÂØ?` ¸ðX¼}Ð>G‡²šŒ05MàªN}/ËOó$mj!!b”Òl©*D}ï*îö2U¬!ŒÖ€ÝCœ™NCR8'’ƒSp“×°¬¸)ùðHâ`6ꘚÈìeŒ†N‚ À HÕ6¥ƒï5|¾*ó œˆÌ•$vM, Þ-ú‚Áwä|ÿod‹]='øï4 èñ„`œ4ôUK‹nÜ·@e£ £ 1 4Š UǾ™ü𚃫¹Fªí+ý|ç6c-ÝËÁÒñ“}ÿ3çy¼ü€°3 íczã2€¸Û‡eK²nFI£ŒDÙo¯¾@häž¼Mÿoù¶Š&Gޝ±ˆ­|ø=Ám\&è°`L<8X×6Ê$F_ßû„L†SÞGøÖþ><™6þfŸU_yÅ*šA©sÈI/^ÃYf6kø-DoƇZ®*ŒK¢:ªâ{E.²yºZaÀxV±A»Ž,ïé„щ6o«»$É·r±^ µ?»wm¿Œ‹(L ®I›'}Ó|Ò¸emÙΦ˜Ý!p¸Øk¢ ‡˜Â‰T.`ÜzØú'²‹døO]Êúå‚>³ KÔçyœ=’n=çÐëHÔ>‰YñøØt¦ü qlÝYPôAD ãâãžlÌAåñÓ­l¥qo9¡×[”™$¡Ð} Tƒ¤{Î|rpW8áš4Ä97=£Xè±X½iŠ”û<òÿðdüPXÜôD3>iÆãù"`£/›µ1fqPºtÆ[XƒÜ|þ0ˆI%‚\šF°4>}<.-i€¿,RØ\ã|ž„ma5Ùú ð @0yã^G÷(ãó‹X 3݃¿) ^¾: è™L%08vGuoQo¡R?hö ¦gWÔ^‚mcÓ`ægÌ=ó G:‘X= „$8¦xb+©Ö\QúÇÜÅ€E/vÄiòœš7éjšCæ=ƒMÞø—à+^x0㮢žÄ,È}Ð#Çs :‚[XŒSX‘ «øõàÚÄ®7¹Ôܲ[‰åKái’‡ªc¾à3ÓŠ,{¿‹Ž‡3S@gÞ·Ä8d;øÜ@!ëµ.]t„–ïq}¶þ/ßÅf´ž´î“»–ƒim¿þÿI»OIï.æÊœé‹ÿÝ.íR\¨×âÏád<ïãýëu´·Äð˜w$Ù´!ÐÞšiÿÿߤŸ–ód^k×þÿ÷.je³a1”–bæ´©“cFv“Á&tkpöóÞç  i+¦%öâ³… £g°\bc`—ü¾ÿþ8^® Ð4Fï¾3üÔ‘»ÜþeͲvçå!K…Ç™%šÎ€s•3 8ÎÄ`ЋԬÜàrn–49,qEQ˜x$jB*À ¤yð=3 áá>0T×¥èš!*ÊEJNòaZ·êsv}‘Y äm%ÓúàóÎ#k0TDEE_'vÍ-–Ï¥õ£å±Î’¬²k½@ÍhÍmÿDéÑŒþ¼OêT¶ÆßŠ2=ÍÛd4€ÓL¥òS±º?¥'„Q¾jØÓø‰É*‚ë¬UJkC+éU!¶•T Aü×OòYíKˆ‡tÇK ÀÔH©kÁÛÜg<µÎlr…¨v"„ò2ŸµæúæA%{AšÜãâ²àêë•IºÍÀª µ:·‰fßY+%n;þBdpB:ÖZtŽÂ­d–§ç1[†> ™À!ã%?ÏæØOÈ×EÊãþ¨“¬L‡ù«}I…=çúõFŒXú±ø{Ë%k.¬Fáo%ìwô‘OÛ’æ ?Ô´¡#+ùÆ…t¢ÝWQ¢~NëÈDøãË+”[ë&qeÇ6üs?ÿ¤,`hòkT:,^ø•n3­…Ø$‰ü³1ûot 9!·5] ŠV*:Ëo&ä'äëŠ"[–ݤ‰°ЭŸb‚jd±­k|Á}z$ù|U‹|¤´Xö#Œ( Îþ„2¶- ]7zìÖ($"ê[þ½¬™«)yÆí8 ‡0Ž¡öTds† ÈU‚œ™»Ô¢¾5›ßúPœÄý Qxô±]Þ·DO!V )Oø‡BÆqdYXÙáßì"`zX‚­±WY¥žbJ8 ŠC º—e¬Šåú‡lJ‰§„mÿ˜‚ ÕÌ4,CôZÉudÆö¶…‚éDÚš“õÀvd©Ü˜ Š$ë'ø…>s÷›—mïÁð—iíÈ3BE a碌´hЏ(¡+&ë5@ð üæa³ð‘5öW4Jª*"13Û†rËEQvmíxC%4žV·E]BnrÝ«@ü3ÿHÐM¡»:†C‘@zÒ$ƒ³Ð•ä£GÌ€º¸ÊÐzhóŒ¸`ÿã 9@9éŽYPpI&<ÿ Ì@òÙ­ÝT’¯± %66 ¨Ã 6ΧW_`MHÁÍ^ñô;Dµ}Ø)#}ûb Pseq&£ X6º”X.QˆÞ—¼רÆD^óˆ]''K`±×Â(@®æYsàÜö2§µŠqNÆlÍú²æ äªrþï PV†µS·ñ®YÝã†*Wí6Úø£7 ‘Mì‚Õwæ€ÌªŒ,v7˜–y¬MUBxì|”›~Lõ5QøŸç˜æRøt›üš¨GÄÁuðZøR M­Ãc° úÛªR5$©Ìb0LųGè)”œžîõ­ÄXà·Ô$])µX‰u"}Óz¤¬W$£ÍÉ—ÙZ]Îy9ª[Žlo— ^ˆÄR-ŠŒ£XÓZÿ©ÒÛ¡oï’7]fë§ÍÛ¸ÃÓWik¶ Z*€ä^Wêŵ‡^@üögôk”>V—¤ ?Z‹‰ld8æ¸HÝÅó¾HÄ‚øW&ãK‚šñíC£Áü/enÙ°ö‹ä©Ô–„ŽîÝ ¨i½¦®/Ø“D.…‹“=uG ­*ôRÅS>¶ªDFæ|ؼÜJãg®¢RJö=9{Š2»Y'܈Xƒ óãž§í‘y8j€ß¶h>JÊ® “$b\–e³O²þö ¦j˜:ĸn–Y.ˆUJ:èŽt¾£ Âjÿ™;)»“tëFi®yšOÆ l4Y‹!ƒp<~â^oÞ`s˜-ym6ziàÂLS¤f .üÄvû¾DÞwv`ˆi!òPô5 <¸Âr_„Ôb›`ïü…vðfûè¿ùÔ JèxqQ50¤ßyâÍü‡•±ÓíZÄÀYa45'pêDЛ2“'5M*å$óŽEc?ëÖYÏ{4‘Q´ŽH9œCÁ{¸›H«’ÍSeМ»Ü=eØpáTq.*ìïž ¿¶NbY˜7‚´ëTxC °ë‚ÏÆù€eÈ©5¶eo¯öa™s&Ecn}aùÖªbãS‘É!﯇CÞ¾p”÷ŒÆ¥3ö'ý;ü²–Pœñ@³(j(˲à"Ô‰+2àAèÕÛKÿ©¬¢Q„ÅGmxV´µ°f¼º?•ÜIÉæäç%¤t,ÉàXfºrß#©ýJ¼ÙFô@´èqÁ 5Ãý6¨¦›;rb|ˆ¹i§ïpÅiV”ZèJxË[]qGJ˸Ò‘cJѳMCÌ“åÁË&•Ùsâå@lcÀÓ‡ÕÊILÔOš¬O—]ʳ‰ ©Àá Ös‹Ôd.|>ü`7„¿r¥fôb×-YÖÀðu*,°ÌÞ¹ËEß&ÑO#Ú((Z¹-_Išž6µšWÑP]Çì¨r™ä¼œ©ã,”Wf)&—9qÕô_°“¡)©¼{l¤ ùïƒH:UÈi.ņ°„˜ý ÎÁ›ÅàP¦ÉÁÿÿfÀÐ"¤ù1åÉ™¤ÕõŠC) .ü(ú0Ü~¯¥V…Y<ÂÂU¶uú"˜¡Nåx«£-®¸„[S÷¤5º—\Þ.àç#(7ÌÑ(Ä–ÛfSüår2,¼– O½ú•‡ÂÉ,ߥòr§²X?aWúk¨ß™Š±oò@Ic ™!àTݦ är ]„ ”•SÙÌ ü 7ÎØU]yšHÕ¿VNm·Š²^ËŽÝBY˜&p->]Uæé0ä/ B×ïl*W\…ùi"ZÈÄßÌÕ€íÕ‰èUE÷­ÉÊéÏ9Æe䂬xÕyÿö uÂLà}çÂÞôx;M|^M=+s,mÁÈxZÙB­…XcÂêÞ¿ý»¾5•– _ƒ#Pš¹x{¨dÃP}­»°Ä›I à¢Â埩ˆ^o5Ç_ɼ¹ü ™Yn˜ri¡ª!“ˆŒ&JÚkEée’®FÖ@ƒlŪ¼L?Ì4¶·MÞD€ ˆs’ga4‰Á¦oÛXýˆ1íǪŒÒQfœÜ¤=Õ£À°± áâÒ‚Ð À¢ÅøÐï‰öÙ¥ø˜Œ‘F8$ÀBüW†3£DTãírrÈH…€yìQkkªyŒuŠ5Á)0›ƒdjhÎ)+ÒfÑ–}j©ÞItÍ2ŒÕ†mMÓàVw)°»3ÅØœ2Ra1Sjmy#¶aÌøë`¬ÿ‘ ’ïünŽÊ@ž\{×â3*Ú×÷&°Ü´øª²$ð[ÓÙf¶+•¤›v8Iao‘Æý%¸Y:’¬\ºYÇ¿süŸ#6Ägû‹’6ª+ÐKI‚‚ø›%ÊXü@tÅ0Ö¬ƒƒãDŠp5Õ?‡#|Œ Ð%[––Þoÿs?ŸüOd}èþûÜ50æ¢ ä<ÛXÌM^†!Lø5 /^U‹ŽÂ5$l ò ÷øÈ¥Èú]æº"‘rGša£"ñšPO+­¸c Ð}]aA©ÌªÀYÄh "Pëí Ôû¦Œ øµ íShU“1׳VUÌ t‰ňY9‹ðxº[#IoÆ'IE‡Ù' äHÝË 9ÿI`÷Ð<ñô5âU]ö‰2Š,O ×&˜Z‹ èÕÊp„T!ÉI&^EMæB9B<ÀUzÚÆþý±]tàpû$¦¬ ÛaŒ;Þ‹fkH!Ø •žxU07‚D˜gu‰õÖ\<19ç=U›üTu$ÆñþÌâs:ûâËBb ¤ÙLi.´‘Oz1:ùݸò‘¬¤ÖÛ‰JmggÞ<ñXNy¾µÃm2bß›ŒÁä°ÒMS­îœ,‘°X ³… ‚—}ks†P”LÁ}(ᛡô4 d(…ÏËjª?ï'; ªð‘C€pÖ𡳛—ÕP~…òË‚î_ù Y™äMRgChðk\* ÃQ½LÁmó‹õ.mœ´ÈSjoÕaõ“Q¼(x·òx ’V {“VðªÈØJÞ*côZê s¡œË§Ys†ÓøÀ°å”>_ê6Ó¹¸1 …ÚÕ"œ¨^Lë:à¸rÀ¦“øSn“-½bÃf›d¤D»v"g;HI˜5±}rúY17ûd‚bØpÕÆJç1±†®‡ñŒì£'ðÛjm±æ)@½NZj:J8J;™cº¾K$ -§ ñk„Ëh².ø9bø=/[óì“ ƒiFx Êj;ê"’YzÅ\)+ÂA¹·µ£×’X§³ä°/´W=*&†#¨‡Ž~¤ñ’8Mïk$¶b""oã(õäâW%S…ìräáÉ&ç-À]í%×êóPÇÅê“Ü2>­ŽŸ'È,Æå`»QXÒ@ðÈÀù‰&”½|ÑC¯~†¦K»à LAq‘U\:ÇFœ’„ÂØÀ쥜FZ†Ä>ô„ðƒPg*ŒGmëÍYJw&Øy?îP>×Á\Ë…xÔ4©×§ä`p„lÓS„X4É(SЄ[€0‚³”ãàG]°ë5Æ~̰wkÔT!€\Áw@w†Þ[AÿøœzAG•¦õ+Ø×ÍÒ lê » ²)ƒ‘R”]bØZp囂 Ps‰NÒÜ›ÇFÝ,§‘E¸6L)¹ÓD+8©œFkÅI)íØmÊNüB5;{6@|¥bÌ.Lé6ÔJh8¿.ìN(\Â…é Yþêñw23¨BF uµÃ~ÖtÜ|6tûóû_'w'ø¶•s6¨J¹ÐÜIñsPY_<0³-°#N,Xl  fð=íÒfXš†Š¢ÙöÎ~­Y39»xUÓ­Ø™†ÄÛ?Ý+ùÀ3Thçsh,!näE`ûƪu¬µv†*l ‡AS'ù9‚æýcƒsLîê[öájyw w|¸ø…¬ñ§ñ}É޸ɩѶyó‹®"Þ¹}D¹¦Ûzi†IGà;+olÿÓM4ɯãE{møe@oäDÕôôÓM*­¶ ÜACºiÿ¸ÇÆ v«Ò¶ö±[Êë½õ%:GÆ{ï»ÛóhuJ¹hóe簾ՊiwŠÿhÅû> ïÚZâ|_UeU©©aøÿ·ØûÙ”ªdškéë|É7?)^Ȥ°‡×ÿý9HD4¾Bî°>X;*Ò¶âȸPHc*~M[@â X°`ºîV·ÆX¨0—˱ÙóIÔ h„|¨#)˜àˆÑz3Ö[5Aek®vÑIÔ}b¥JJDƒW¡ØÆÌb¤ÈT¥€-Ä&¢K©©0ë”ÓJ‰Œ˜AÔœ<±‰á-A“>©aròÖ™Ø^C¡¹$±Ûi s«s Í4›Í_ö¶çU™¢WÃÔ[6µ,Ìu`¬5®øÍ~"aÈCÀø5ŒL5plE°)lÞßÄŒÕRÐp&܆·Qĸw„×OQå£îe¦ºµqœ‰Œe7XÒ4ÒG CÀP# bœÌŠá1¾A|<SØÚÌh¸nr'rT-²$m§Ç³Àtè`3Ê„7, P©Ê$æPb#èÔu°GPÐác¦KÁAªû“JÂCÁS@*…ˆ3‘ “ŒìŽ:«Æi]‚wË«!±ºjÈ”Õ –¨„†{ŠÄ¡1DöѦ n©úÉŸ†á£8Ñ*™/$‰&¨žä´Æj98öàÅ.ššîGýøÙÖ«b™Ž.ÊÇ‘™’>â?àvCï³K3D±²§B¦’‘ÈáöÕŠ3ñî&¾(+‰y ®/6sÆQ'üê_æª:¬MñÜßP•J{dßS™â]iÎ-ðÅ>aêô`x$ š¦ŸÖ›¡V€óðèVÞº¦Ç§öuÁ.Ù#>±x N4YM¸]oo*ÜÚ˜àfú-)7J¼<ºV©ßIF@ÙâÛÎp9[|`ÙØò/#ODJ'Ð?ƒ[£=`ÇÃÁ©­¹nŒ·–í¹†LùdA?üØ=STÁë)ŠÊ©Iêhá”U‹”K¸LTÀåË¡·2•Oï¢*%ˆïoa¼V<…¸§©‰KŸ©áÀªÈ⻘ÕáÂdÌ{ïˆ2“·e¯Èç4¢H;RAÎÑ‚µb\`ÉÓ”3%zšÞ!Áå¿2¨ô5ª²íüõ‚„QÏŽàÔU Füz‡ÀKɵ–°e!´Ûeí¯†Tpíb.Q„ép¯HbÛz–ÉO>®‡¦n;ðEÅ´þ«Z¨¹nZˆŸÅÇGŠáeN©À«VIF£ÜM\5îŒEw_ßóC‰Ðà•SwªL4SJÊYS’`@ÚR:“šŸóPI1ËÆuu?àx…)¤ƒT(Ò Ìd‚­‰Gàß/êÏ&ª¡(ýp9ÖP¼Äf„¥4­V†´‘âGØ9~°KÕ×Èj…-`+˜÷ X…ô¤ô”Ôˆî7¢½'è„Bz L Ó¯Ù¼tÍÌ•ënóCΕ° ic‘´KXïÞ¹Û…* +ÿ_°ØÕfF¢Îã"æJ½µ->:±Eèl‰)  óÌ”àÕûÁ4ªn˜†Ÿ)Êg;2/²Ê@uè,ó/?ŽŠ"âê.ºãHk|¨«¡œovK¯²SæˆU&55g9·ƒÇ 7Ùíã÷au vºîÞ*ìM€}+­Î?âsDêiÉÖchD+]¯Ýe¨jZߌ˔`·•øÐ<8º°ÛGÓèùþ÷Aðv¦ÚZd^Y5=õqë/'­‘>‹ÊQ39!€”ŠêÊmèÅ~+ÜBÆÀ… !¼Q  ZäÂKB¬‰„[qðñÔµ|Ôö ?~šñíš4¶¥àÓM‘­g*‰ˆ_Gª/\Àó@-‡-ðß4ϧSÛI€šz$óXqO1» ïQ“š®ãÐj~È”µuª¾ÇzV–þÔÇi; >XxpƒÑÖiÄ*Фñ³5VLÔœz.<:ôó8ƒhɨáý3Àà´b8þèL:ŠAb˜%µ™ò+boÛ4ÃY y!ÛOyêÖF´~RQq G^–@í¥9¨á8-‹$µÍž¿0j``Ê»H屯%=ÇI´ ” ÌïQ#ž>ëª$j˜ÍÉòE[éÓõ¥+:P 4:æÐî#æøâ +­Ês‡>jxì™oó5±bÁ´£½gŒû ‡ð$>&© *‚ID8#êÝ‹µC«ìަó‹þæ¢Å;Ų́…y/šÃÐVfé¬AÅÏq…M:²*-S‘Â!JÁ?É’qD& ¦Ú|˜ÉA”¬Œovt>œõ÷3\Âd3Z‰Ê¤á„[eû*$|“Ò€5¸X´‰IÝ ¨gœï¯Ãý6Óî BFfPl·,±\²B±:X#Ž«-%Îl£.,µ«1¬¨eHêßÄ=™=ŸdÓ„•^¤”&K—}Û'¨ÿ¶)XYÈÁúÄ|BL€–´YúÊÍéñ¼Ø­7Lô$zÈ:H>$UÀªXzê3-@øÕuð=û¨$ô=ÿ2³Kì&lK‡šUØHªiú‚äý<©êYa¾Ôí+¢jÓ2¥+hS¨üs¾NX ãÉ~i޵T&rºËÆñ&;1m…*5qÀ·v‡§?ãlœ¤Äº^qò\l |ÌÔ9@Ô»è_Ècl7ˆa+±?ÚjÎá(*¶ÛþâÙ ‚@ãR} `\a$(µܶ ä™È`¹Ã5¤Ï[®º¬Ù9‘1“d¢_·ll8X™3‘âÂÓ"ù‚4“³Öð³Fg(š°<ƒWú¤Ë(~Džd‚w“ºV»0PDåx;ÒÎÏàè°àš®dW¨NtÓE!ø,~—?ûãæ@(y>~î3)I‚2fÇ]Œ+b*$¡ˆû‡ Û@+ªUÙ[À{\O´œ™¹ ÁAÒ>žò`31ìç‹RÌy,¶X/3ÖÆ•>Рæ T‘>¢„ÃÔ¾¸7p"ÈÏÙ-ü¾l¯+U (øÔtL8nàöj2ƒ¬ã8bëYõUF‚~bÀba)— F÷‹ýÄœë›"ÚÈååiå8gýÁ÷O£%ºþLä^ç8W\6VæõMÙy< DEufЕ8ê¥TrQ‹3"í;.kÄßñ«øÄÜw6ê}•%M·ûP6¥K¨6µŠ´Î¥ƒ«k’½R‘œåa‡Ž"~ðƒÃa‡Ï[Vp­Ä”|:Ÿ–Lé+ˆk#S:‘R”Yš0ü¾\~v0´­LX¿%F„Vð•x«ï“24܈Ÿ¹á›#)q§ðÒzÂò<Ãÿ E—«ñ{hÛ_ý Wo™ôd;9Lü'ˆ¸×--HÞl›îßã–ß<óÇ«QÆ6dÏöÙ¦(o±¨™0Æ>Ø‘² SÚ•JèLdÆ 2çŒK{÷ØŠfò ®É­þ:1En«…Ðe:ã*PluÙ£!›yáó¤Íf*Ò0#áeâeb—·Ä»6>/Ö·`'ÎQì'쇻bþjh¾ÔB}Ây±1˜ÎÕdÝ–L.iîr» ‚ªåuíTæ¤Gþ jXˆ4 UÊ‹bÓpáÂENqòj C"·1”1ÎKÀå`4Á«¯Ëû2´2X^.–ZÅʆ™ÒI¼”ËB@©0Ô"1ïÜÂ@͈E%½¿Åê„Öl3&ÍÉ_ÌÀÝËtöH… ©Ò0/CHØœ6ƒáÙü$ HäLÐf[4±’ ÖöS˜Dû¸/<Æ&s€p/!¤°“lBVQóþçòfdl­kÒ·ïâ²F£–Zˆ¶pâ?#V¡(ªb±6y6d #U°$˜OM×>farR³2ÑmY§Îî@̃Ü[ÎEW\$8p›s5IêlÓ1­å‰¿öʼn„HÜßGúÁGÌIMJ(º>s*q5>Ú„j>x÷_;…¼\ÒcÆ’Ì-Ö°i4Dw'VG<\t‡V*dÉ'4™¹°—Õ–çJhöàÐâð?áèj!Å4lŠ)'qûDȸ4…•'¦“½Â(j_ñ œ!X4†×nû™`šCðUO¾#U=ÿ|L FNÕ£ë*Ãݱ§šaEKY¼é)´úJt­©)Oå&’‰IàJÁ%<õ*N!>K¸®‰n†‰T±‹¢¶2ßá­„°äŽdvè¯ù–¼/Ó¬ âýmÈ©¥Ž-¢k„” ›r(¹Õçâ Š5"}áx¨{¯†"…òþÿ¢b64 ]Q$œ«l­Zë ©$(Ê¿FFżZô“֚РàIŸËyz%ù™Ò’üoãwüÄ:Œ! |Ã''¨Í²K¾ŒX‡¤&Ú çWBøÙEf?aŠ{Ö¢×:D9(z VTØ«ZÜáCiÙ Æšq<.¯Ú‰6‰Û2Oàn)Ö õ¢Q”â`~ Ã6-(xHVqfùJngw˜¶Œqý1–Ð=GχzÖÖ le“á»’ ¡+~i°3è7¦”ý0çÑ:d"ìÉ6\^—B‚“>åtJé·`m܇²ó—Ÿ6>~;Á æ°ÌhÀk3„¥öÓáI¢"B$µ …NMj¸²™l×QT\‹âËZG.ÒµòO,¢ÐìpÎO ¬ýe#ø"#0X\1¾]@ÔùÃÔn’j ‚¸YÔrñÍ@ÃR‡p‡+°½Õ0SÅTÎ3ðJ°?…\‰T§Ä@¢ÄQì¸úúo»†áj:\ÕI{­Ž[ŠqÀ”r(…PLŽëƒk¿ bà$ú8ppDîUTmt`ÎOPï``«\,}u¸ ng`ã‰#_‡7˜"-©ŽÂ1E…þÑ–W؉ݟd—ÏÒó°"›ëŠ·Â´ÒUÒÛ†âÄÖÓÎE^ ZeP“ýlÈ.Nw-&‹C(Gâ±å8RÉDmZËs£=íäL°$èèrZÖ¯d"˜·ZdÊ?ÊNBÎâÆÂ¾ Ùàö¿øo;q3tÍci#¨*B-%Àô‡Ë!f¸Xmd™ÞÙÔÛ•äïv!‹wÉïsb,È¿—4³)8ÔÕèÔ«À,£PbÐ!¿éŽÅ(dW…µ&RuÇ÷ÔxöA‹Eö9É’[n½å¬±¿·+#ÀJèÕxŽE-侤5~Pf›é#JC&b‰–ë\˜~šœYº$Š/ Z¼â…+aÛPÃÉUW±¬€; ˆH¾¿X®_U°ªÙØEßQ ŽW˜lÉ~øÀÚm)€ˆQ§Ä¡ Bც˜jƒVp¶ DrIÅìD" äexدŒR‹ƹýœ­ô”ÍÀV®pq&U&! Ã=a¸0X}TŸO^¬¬Oˆå,–8q´ú°Îæ?‹s&À u¶  YÕ ÌrV©®1ŠA&„ìåI0XbNCb¬ì`¯BŒNY)'Ÿ' cQ¿c \vcn%d@Mµ9èð±ÿé=âàInLÜ¥%s¾që£&¥QüxÖbXƒ¬kƒÀëÂܨg6Š?5Ùű37Á€&œá¬ïí;X9ÓœÐÑ;gå2Ó?×Ç0MqÎŽ œ¹£¾à odoµù2júdôÎsG5òzò55:Å`ÏTïÆÀ ¡o;ì2„äØ-ñcœ>ÚÕÿ‰@w˜@è !‰dvoO:ˆÚ ùúc†"!!– LY)`—ô¦(€†ÆP`a)¹…ûys"Þ`hÀxʵìy´!ø #â7|˜1‚9‘ÿ{§0jà;¬Ô”îŒ*¨ÍX˜ªçøtŸLÈßq¨Å>t¿ ù:ÔH¬£ >íÜã&ÎDiå†må©êê?éïn¿š‘èÒžB 5/àU¹Ø…ÑZÖ™ÓI0”›©P™¡@jä°RQLOžIm AÓ6}cÝx*É º„@4€À‰ a‰àO“>½66«ß« )ìY[+¸ÍU`djú éAƈ<õˆ²Q=¬Ó€j£ ö"ôrÛôö1Óa=¶2Æ@~*„5é&FFe/^ƒ8AL mJwºqë­ºnØÃÎw~tú‰¹.MÝáAñ €ãffP”ÝŠ”ê0dÆ$ˆ5øBËAÛ¨CDb;¦6òú²Î• ŸÊˆÂ M1²?: #Ü ˆÖCZ»fZÁ-/*õ£eêN· Þ9¯ÙçLŠÔö(8wœA0ZƒHéâ°Uhp\H‘DàÒ¤€ù†‡8|™4ŒÂ ݘT ð(Øàrh”Ë"Rf{:ÐJŽ­_] õSæNNñ5úÒ@´Æ¢#Äð°àFÈQ0ò€+òcÍÐðifØŒ…b`ð~Ýp5hÈþÑÆs 2Ðå«=;)ÀÞѸõ_M7]Th4µ*ÎP°æ€$µh.\í°t·ˆ Bû6åWé24•9Ôæõ pl7iÇE]ÿý] §Âî=àŸÙ&¹o|s»ãÖÿ?œy$¯!w›¶£;V†Æ=êî²`)V8v?ânð/Sz‹Ü¶ì%©tèè_éÁœ±+òÝbàóÂÞH9C͉ÁÒð©áv`m%»C‹õ¬Î{ëä ðÎmùIÙûЧ…>ã/þj}ÒuC›T«Çºcá«U4ëïñÓ1¤$«[}EÿLÚlÐÁ+â¼Kë_„÷j.«ë.þª-¬V3±¨w>ø¬NfÇ®F·?÷ 2 1gÕrîËËÄÊ(nQ'ÿ…?¤W~ù¿ë‡À8Ô»H1Ý9òì».Šº@က!d?«y° Y`´ØW¶øáÒK¹ô#š) ÒÉø­º`¬"㒪ǵ͡¬´æQ5iç' ŒÉ¾ŽrÄTSý®×H%ã%À: dõÆœ[Jé“¢ê3ÿA®,¯ hî=álU¸A9v½1¾›~A BîÐinKL T>2Á»ß ÉÚ˜AÏ'È`‘§€(£`°Al„) ™^𯚧¦,;ò±¨‰²ÂLþfà‹ÚIˆÀ ki§=ƒõ(Ìy ° ðI˜3ÎC<°âå’tüœLÐ? Ö<ð l˜ üq¢ÄÔ;ì»uêTeÞiTûÆhVOô¨…ø\—v^y¢`Ò£Qd¸ƒþ>ûO>Ÿw’¯ú UG®Uˆ²—ÍÅ| ™wúTA4t§åúÿEJ ÔÞ ½m{KE–ZÀày4:šŽúóGwñ¥A¦ó9¤õ;asë—-õ½“÷ÿ< 8tGœµ€¿Ÿì}.@êyögÞ{>$‰¹÷äÚÐ’áÐj˜*? ‡Ø"ÝÌ$ ©GVw˜Õ9HkãéUË^܆h„ ¤*©&ª³€Y˜­ÿ–›hÄVø×þtw2è5#‹'ÀÔÝ©ž›¨:ÅÊvÒúA.†#ú IÍDá°Jœ2ð/Ø]y ÁŒ‹š-ûñ=)ÿA«¹€:VS5¤¨8Ê': ­1J#B"G Lžd}ºiõ  Ó‰J¢BD¬nŸœÉÎ~¯«UtÏÍÂá€`: *Þhâ?cÇ‹E"ÙM]7&T Mæ&Hl?Ði\= rÌ Phsm£}É5¬<<2$J±Ú?IÙEHŠ'ú ‰­¹ª³j”Meï y:#fï¯ßèL¥ÿA¬Ñf®Ç ËagÅFX-²$”½?ô”ÛÿAeN0C3›$ þgú¶W]8tPÝIÂáÐj§…ôóO,Ò5X¡¯*ŽšÙ¾WÏùÈDçþƒV‡FâQ‚]2ëúÙ­ÚXh <}lßÒJ†Ÿè4ð«pþÒXÐ|îˆpÓ§Ôæ¸Ä»Ÿ_´àŸè4žJ˜%k?8Ç0‹q”Ëÿè°‡Ðj+keýv“9‹èy*cC¬ÒÊI7RA1§qÅ Z•80]Æj 92Z\H…pŸpni¶-T™ðÅ^¦‹±l·4;šÚ1fè4§ü ýž=­2  ÍfrñüdÌÏK{å4$$ L,©™Ï ÁJnMjTí!2?ú >£ø[c7ãØcŽ mÔŸX«èL(ŠPônÏ}ŠÄj¹¨þƒI#œ²‚²­ ¢Éb:ËFÖëü‹"?ô¶Mî†Ü]|}~”>Llj_/ä†g‡è4žvpqúPq³Ãì‘‘Ó»ç8ÚÐj$«A.°ã³N@‡ñ@G•ÆÛsÎ+‹P'‘ÔIϱ![•?ùXˆƒú Uä<¸ñǶ H4¯õSÔ‹ˆ‰¿Ø„€ü: icTÒÖ]"mdŠ‹Z‹A”ssÞÝç  Ô„dP¿I]ñãy‚¼äDBæ s(ÒêOåÚßôÞnK>Xë2x2S’뵑Œ§QÚþ„NÌEþƒ\,˜™4¯ÄmvÏ„¡ó†6Qà-¬ˆâ ¿ÊòfÿA¨S å‘ù ­ú‚w"äI‰l׆aO]óµû GP : 4å«Ë1ñìù¾Ü@*;‚†»£Qø×9™°3fÐi Aµ‚°l¾=ëónvè5Cêue?ÉÚ"éþƒSJHåµ´“„‚ô¨sñByoµp£)é×—É ØŠÙ Õü¯2 ô‘côø4•\¡ÒQîñ«#¦50\È«'eþÍf&ÿA¥£2½&m0œ@^(°PJ£méì y#þ1z² Ñoz‚üÇ`ÿ„Ùlápâô;K±ž–¾_ÚÂÄoþƒJ¡ƒ¥+1I Ža‚¼ – ºdÒ:.Ëñ‰i»ƒ·ú lVóRÈÖrFÏI/“X Ó!ÕÓ‡'!rhð8M®3Ëœ¨–ª%D¿ƒ55ÿA¤å²ó˜Š«,¿¡¬JÈ ¸SCCÇÊQpËû °…¿ÐjJkW™ +kÏX)`¨®""[‚µÑ À’ ƒxtŽÝ’RÊ¥r=L›U'FȬ ò!KJ° ÆsB‰y7c€`: q¨¼”ë È3•’öDt$nôìAZU˜·?³O ÔsëyG$€î’ÀìêÓ”|ÁkT\ðªxØÓ5 MfÚLÄ:C0i‰œ±+™[D"°.ëá b|4xTj߃*_àNÀ4b߇‡÷z1Pàs‘•:£ÿóî 3?ú R,á”z6‹”TARüJPΛ6Gå¶ß€”#ÿA¥Ÿ“feаŽQ w–«Mx&,c*6DìµòÿSp`ÿ Ó ˜ékRã`cªŠÑ#d‘‘Š?a¢M>Æ0þ Ò&tÕ¬r|½”Nv”Îx?kOû 7ú U£Ò’Ú-ÄÛ¤ë’ÑÀ›BƒæÕ™ÿãB6‡è4©ä׊C_)[%%an§37écÚŽ>hûM‚þƒOTÑ‘VRBg…ÇÕÁŽ,Mc¡¨ Œ[t)gÕ1¨¦Ði. žžpŸOš9‘!«˜6±ø/‰ ^²ô_ûAŽÀ 2ó.Ê­ÉèibJ‰+ ˜ €X¥1õ?Ø›xtQª-i,ZÈø%¼8lÌC9‡û!ÅN‰Œ¯ÇúþƒZhšÃgkTLä¸ P^kW"qþÉ\Ð ƒN–y=sQÿ¡l7àÔÊÿòôZÕ¨¹T…ÏûR¢* rà’0¶òž¯Å‰Épeü™É6ÿA¨×%(·ýrª3×ã²£YpÖWóP€„Ë<3Å↤0è5 ålÇ+À0S¹¨óòÚ˜”Òøá£(Uh™K<šè#ëöŦŸè5Ù¨ÇMQ–s q¨¶y#اcôU•~~ÿ cÃÐj¼d̘¨òÕ2 ¥©l¤Ô³Ubµòÿ‹Ÿ‡A­B!èðN´Pò^ò§4™â0¢×ýH© PFýù3YþƒVv øåúíZœû±(Ê3ie×U¡¹¹œšM³7† ÖfnÌ·Sdœ+ eÁæ‹+ŒekçA»b'‘?ú :’«9,7û +Ìœ¨5‡{2a¯ÉšÌ_è4‚ £§„žÖJßÂ<ؘsð\Fúºdß“=¶ÿAªËPý ò…”°u$¢ãºCµŸ~Ÿ~Dá(ÃUÕTçT &–E-» MÄŠÓý¢ÄÐý£˜Î–ëK…6X<I‹¿¥*\~J‰wû7ú AÞ bZ3"btp: â:.bo­œ ÷ØRËerÙË¿sB!ÿÐk/,Ï:nE £‰Å 5åÊæíi[N¬nƒ×¦³k¯•¬Äßè5+£ˆ(ïn"¬ÓÄýPšZãÊP¼ 0 D'2!¨Ê’Zõûü BÐiƒÖ5‹§W-bÔº3a.î/YÜY¼2v_Ì@C@áÐj9ënQºŸ”µeM·§ùLÛý©7µÒzì 0þƒ]Ópb.sÒŸùù> 8{/!¡‰È7X~ƒDdÝϸs´‡¿ "â´EA„Q¨À“Ú[`¦´ Ìe²ÜŒë5D²kŒ§¬Ð> SH Gèc%´a?Ù¾ùHÕ)\ÓÆ†…ÝIH0zº~„¾ÏØ®Z©8©õXÛlá*äKž!¦t ÛðÀ瘟l¼²¯/¤•¥p€pÔ- »ÑlÂeÀSœ}Ë?yƒZÄ5$µž[ Uƒ¨à€#:/ñjŒ² -a“Æü…vDIw:+ôŽ©0 iÀbùƒE0Òyuä%éÔ L,8µˆxéó!÷zTßÍ @ "8 V'CK1•0 „¼:ÎZ³Þª’´éÛTä!ý±Â7^¶éè1`QýÅ-Â`óÅ«+4Ób°îAˆà 5&3-ÎfnØË}a|ìg:ûŽ?ÿÐjì®'ÛþÌ\ð:n©OË@9U@eª« ë~ÕuQÿ ×o;Fß}ú wfZ¿/I¡±k_l*Ú ª: ˆq0‘©@½|`--Øãæ`²Uðè5¶O‘ªW(îq^†P,Œ?,¸‡2ïE IÇ.ƒPpRáþÑÀ£·OdNXx'&2«—TÄÞóÿA¢æ4ÓQ,·qH5BŠlÇåí·0Z?A¬ö͵p3 vk~ülÑú _ˈQêé>Q'e‡YËy4›„Çÿ ÖM.6šWȾ}þsÿ Wn«’/Èd†}Àœ¿ÐjÓ3açÔ©&Çÿ p AªoÕÍ"q;– šC%ìÿíá Öª¦À~Î &€Bà€wàY¡sÊ%…tbn\æ|S?ýÇ÷Ä$†«ÓIºg3ÎgŸþ ÓîJ,ÝŠU;4¾‘K%ÿÿÐY¾ï¹G¨wß,/ÿ‡ôNÍ KÜ¡”:ÃÔ›€!á ¯Åå{ÒWûÕië'ëD‘ ÛøþYßw¸)Lri;iCh46['úXÆ÷ ûáÂñ æ;¡Ä-%*Úø O0Ó7EOeËK€™/òzOjËF„î: *©/ÿÙ^u‡‚,û) ü<°E³–¿ºgÂ2°G’T öx¾’ϰ*ÑÌg®Xj[ý`rïü®«“Ñd”È¢UZ‘ÂujH`Ü!"ï}诡ïÜ ! +,×*Nþ¹C¨m qt-WÝÜÜÁɬÈÚ¿¿ùF¨þ]žÀ(u‡wX×Fx`¥I€P'„8<6ÁŽÙØ¿Ú3 Ò5Ø0I‚ÖÉ$ËŒ‹ÙÁ¿Á5+¹²HbƆ µ;¦\‚fJvP\LxwÌœª 3l8Ã; <¶'€Á@oKú­ã“_'‚PÑÐl8bµÒr)rµÌËMÝgóÁ3-0þôI |p-àQ¶\þF6;Ýr¢æ¤—_¸¶ K‰Öo?®L¦7ßÒì¤Pð™¤Ãü š†|8õn‚~8(z®ªÆÂÀ´. ´3$C,7¡ú­FAòx›‹”¨UÆÁ.6èñ-Z4â·šm …]6°Òÿ. Öaö§0Àø^þ‘øo'FOlì‚Ò½Óî¿.ô‡§õ÷†‚à…‚ZÀ诵n!ܶ ÞcÅül³dG48¤ûƒÆ“ÃCN"¯qÓkêqáS ÔUP!{!æ¹^w¬b_ùšXZT㌆‡Vq¬LÑy=­Ašæ¢çþ”̦ôáé,Ò—ZÆ.}øra þ¿1íwÝwßAÔ §$}ñîyQ­~ùÔƒ¹"ÑR^™š?¦¬0R’(`=á•é/5~¼·¼ÑLiZ ¨Ñ/ h–»IÉþEA’¢`͇%?§fÇXlñnë{th{Ò ¤ÆjÆ d(Úº¤:DÃöxõÉ"$†ÙQÖ–JA£ým‚Æö¬Ž÷ _°ì;pa­Dä‹ÒVð Lkì!Œ&>ä †øAeC^&&Ö›ÏUãÁàB7'»õI“ïèbLnÎb:nªG½ÃIý(™Ün¤$n›róxKÛÊã¹YöF"<2o³rCì®Ã®!bü"¿£Uӥδ臰Ó¸zY{ò¹Ÿatʑˑ·“þà¹_—Ýn:éÜ|aÔ=ÔåÉÿiŽ‚­öÚß¹<†„Ô1O„8“ž¿ý_ÆÐà⦕X'êÌpNŸu}ÒßGdòbJÉ™Td”OÄŽÖ2D„)!„ûhMH«Î“ýùŸŠÒæOðˆ\@‡¨!`nOüþÅ4´ë)š`aÖoCF¿Gce'GÀ³iá*H„Ú¼ÜghûvîØFåñXµôm\¯·ÝÓ¾«FÂêdwJ.ý‹ÙȾ:vÔž±N¡È†övx¬³CÂ4‰s2Çýäô÷w½uOhÖÛjÛ\Ô…´2L<î=²ÅTZB¤:ð¡© "‹'ò}dô“¶‰™pã¥ß†=ˆT¦?¨ãn(ƘµÔ}³äûR[ ¼¶ [K Ai`/ö˜a~å,¨¸r…)™¦ÆÒÂj${CaA6‹b[”JP":%–÷·»Âè ¦±£ })›S"Å=j¡ÇõØ Â+ Rec7C)ö35•®¶æ‚úJƒõtÁëÓ5'ï-5O…OÜ[C·ª}’ó#m]ºÝ´ØþÉÆ¨lLͳÕ=HÞIgåãé°¼<+ßò}'t@g€ ð„†–6­°©a„6ÐcM%óÃȨëÿv'O‡§%i£¡“&e‡ÄÊëš±Ÿ¦ñÆOú²640Õ ŸL$é1Ž<0ŒÛÅ”´?éq‰,SÖ a(ÿ­Í\8‘öK†‡í2¢"wÁ¯AD6s½*sA¾Æ ;\º'l@í0ÌÁñ­Ðè•«Õ—ÐT0’œpã*»7µ×é…R[„ &°ñÆãïcW‘ªÃ“³'äþüT%`èŽb´Æ ¿zc#oŒÈÇF. ³Õì>4ô”Ž·#Õ½‹d”qp„x×h!˜Áð‹ã÷ûš…íô=˜?ßïƒä‚¬íhHÐé¾ó¦Ïþ™QrÚøJZŽ™#€s˜Èzy”*y¿ÂÁF‰ß²Ã9wê׈U›ö I 7Ì?3)£#¼xÆ»db}á#b ƒAàYh˜˜×é`®¡gÚqÕÏvÅ|¿ x”нá¿#(;,§ßŠŠ9“ Q6úwÍÂKYˆæ4¸žXÐÄÐoŒ'0j¢(,È$úüE¾)T[¯ˆHñáñ1®-Ù“ƒU3»ÿЛXøÀÄ?“B;â1Õ±ÁŠ}G8§$,Ñ"'jÕ辉xW±eu†JC'8+5c¡x©eÈtb…¡;0õ?;‰®49v+Ïñ©2áÒs8³©‡¼]Á™{ŽóŽl=)×ÄFçfhçâ.>2}Û“fÆChˆë« [ÎÈíx:¯Cå\†4Pgß3½ü]µ¡Ñ" (‘?«h\¿ÐȽU¨+¡°ÊÃYòì›Å%(½/Eï§r®óíw/¦¹dþô‚è$pºHM½‡7`TaM_ý Âqb9Âôël)l8ä|Ë:¹÷UâËT•Ä|dßïy ¬MÕowÁìl¬mŸ6ƒFB᥾z®“ /C"#£Ç]øcca‚…þ =–çH:¦^’j|'+²)0ÿC2î°Ž¸ˆ7(Õ~E?w„]†-ÐLdïz÷ˆ%r´œ,µìŒñú¿Ó™á#õ¼’wŒ¡ä6Ár~Òã'¡&Š5»yàåó‹&mkuвX€3é³vû¼jˆh)-UãCª•9‰À1[÷1Ĺ‚5·Ó6Îy‹6Öhäö½0ëqäÃÌ_ë!ÊXlz ‰£ý]ñ«ßøJ‘ÄL¥Âá* ˜‘¬]¹<‰Õ±«V5¼f6¼Ü[Ó ìW/ØÀ'^¦ã‹vˆÝãÚ.Ðß?ì²Éù â ZZ1ftŸÚXÅVöqúýÌô *éÿ-ø%Ö ßÐ×ú°èȸ°RÝ%`/†Òˆ×i}*óñ]!‰nÅ÷W©GBûo@®˜í]Έf ´kÇã¨(‘Ht-UŽÿK+ Hhº’iÄj7£Á+öõV‚bÝë/ÿ˜wó%Ç ¿¹¥j/˜dþÊ­· 3š‘ÿÒðì;?wTÍd 4¹¡úƒ#õã]†V<%h~6üjÓõÞásOYì&÷?#&nO+¢!^¨};«Qqñiöø*y ¨o hú'×§¿¢81á.éT YÁIEP=&e‰|iVåþ%L„ï­ÿïh”¿¾û7Q`Ú-X¸Õ££Cã3ˆ¨ÔpIÿëEÏölÌ^_Ð_,ȹ;ðO°EË=)´éö’„5~⤬ ví*ïf-ï„w]s¦Ú’ÜpÓéΤpÏÁf°ÍÚä ?uyÌÀµé++جhükC£(|.»zÁwÖE¯v+‘²¾ôFY-‰8¼Ÿ}‚ä/ɂӊñ‚»'{*†ºÖàñ±:ýr!–“»Ì—â·b©¡¦Wf{gÕg¤šµbãõoJq‚Eøà%®lŒ«PŸˆîT#‘ÞºõÉê¶Ò纄·N©+eõß/¾'vöÛRïI´ %÷A…4°îOÃüåþ«¶4~°KØœ »t¿K…p€'Ë2:7× @¦ôÌ<†ÛÿñÚÒÁ)*yŸo^ö@ÀÙtžyáùq*þ%ô ÍxdFxáÔYÁGÛƒükšÓEɪÿïŒ×¥—'ˆ V»ÿÏåÕz„»íUÇ£ûåÈR³jE¬àOvû×ÜþQ„cR7e+~:Äþ?¨`0fïÄ©¶3’EÁÔhS˜=ÄL°ßÑÈ9<Æ0HI˜G°f…)x p¦<ˆ‹Ÿ6q.D·~F·ZpUT4[‘pŽRâ¹W„à ºõòËïÌrõ[ÌÉw½ ˆDîW×N”°]dzŸ7Öiy»+Ü'FIw½d/–¼¼O‡Å½&@[à¥2xãÈõËOQÍï?¹©¤a¤à}{^ä"ùH2Óö%—Î ´;~júx«¢ªÆæf:$%LmÁÈí@‘>ÙlCȦ.Æu㺺q2oa '5TÍtl6ÖŠ¢½K‰ˆ´ÇwC¤{Bž£Tëš­?<ð…vÒ–o]+@’<“Æ;q¢Æ›ÑV\:¤ÖÚB0®#ªŸý'b ‹+é)‰9Ño»ÇÙ-ÑóŠÁÅË2ŒûÖ¦6ѽ“NÐì^õJx½ÎÅåh'Ý_úÁ¼@UÓ{ÿ³±«è7Ñé7u]G_û|nÜG~ÞŠÝܽÈ{—cEA±…8²±/úÔcˆ#«€‰ü×ÒXÜ'Õý.?øÛò®&O{T‚6ÇD,B£E ÄVŒnC‰à¹ÍŠ|ŽÕXCM.^Ž£êI©LH5&i뇠Ä&ÂŒ@)ö@ ´å,tU¤ÆÁª•¨¬¯EB-Ïþ°™ÒPGÙÄü•=?ªR—ÜOW'ºø/&h–‘ 8&ØêñÑ, †ÔÙê(í‡÷ŒvÙ¯ceÔ€kþwðÐ(&Alª5˜Ÿ°9–ÓÃø„‘ö_¬ü¿¶ë1‚ñwsRõãàþØ",#3o”ßû|­´¬,/¾¤ wûïÁ¥c ‡yƒf¹ŸÝc|¿AäZð£}f¸ºV¯p‰·o*€²#S |Ѿç²k_¨è†®+KÛÃ;ޏu—iã^à dà.¹¾ý êYšx#¬;øÍ„ñGZSs¯Hã u‡},Óç'˜²þ,$ùí0éT¿zôÐμbYÃÜ  óµáÓ9‰‡~° :§ç­FLãDq~ùNr†žx(¬ž4Dgžß7 H8Ã÷¯ÒNÐm¢èØã ‰“ñV:)¤Æi­zqEc+¿òrÐQY’\ÜëààŒ1íd@lhTèØ\ºyȱg¼{ÜÅ—¢sçõÍ!áFhÞ$¡¶8.³†$øï?R3ÿEy½éõe´A‡*ÜñÉ®«2(¼#½292äåÉæ2AS |¹¶#lûŒñxhp^*¿ ÝHÈ ®!®/³ ~p¦/‰å(„@»ÎE:Ë>ØqþH5!«å³¶ö`€b5ˆøu“ž4µ.ªu*þÛxu‹yèÍ)d Šú¨WH%(—ÔæT0pütý*.½ß…ëˆF3£ƒ …`ÜA®Ç°Ñ!á`ØÆßßÿe¡ø*"‹ƒQz޳~JöjË!zJÝ'‚ €Ðˆ@0T3ª i=ß.R"k߈©|Z€9ƒr)—;J«‰„êšÍ™Œ‚úÂV`®bÊÂýû7ùK»ØDHÀÐ'AæˆmÃ$J|õ£Ù?%ñ×ÿçÎM†œ”‘ º÷Ÿ/×$#½ø®â·?œÌ?Dë '’€T9»Þ¡g+ÇF÷ºhžø¾1!ÿ†Ó,4…ÝL¥~FÌã¤Þ‚7Ž"pmQ£8k«U„lÝÌÏø ÃrªÆLgŽˆ†ÊÅ2T),¨Lê©l¼2àÀj'jU1´YQ 1ÚgkWLECêÅ‹ŒtL6~&ÂIêc:lc½«[Bsíñ“lEá #ýŽî‡—•mü$Ðñ‰ñƒýì­oìu¢dúÞÇŽo\ï:/ÏmOS ®û[¿“ßÚ{~zÆbÇݸKÊ߉Ýpcæ6PÀÎrASU Ú—ÊöÔ˜[Cý±÷¤Ç'íÓa6•š´‡kQ¦ÐƒijÍüÅÁØ’¦õógëw.\£[YÔïûi?pS6ä€í4¿4 H¦-êx<ÚñÉqiè:S´Põ{4œ%?ÛLw¸%øìÿÀþkÒ¨‹·_„Ôõ íøº`Eü¸ü`ð~‚Ì>ÁéŠ/#¾ÍúÁ àÍí)¨ÈûÿÉúOcÈcWsf!¼@+t‡K[Uêm ïñj—ÿ`bšá÷}€x+V,MƉŰj:2ÿãP÷KJƒä÷Zí÷l‹Yžá"ÚYþüwËóvMc{4záþszâïdŠM¤Þ(ÝsÿvÓx«·ì,–Ù[»íAnr—Ö–tr#x$x]WeÒ¬c¾QÑpùh^ÜYR1Ü¢kêǹ³ÏtŸßŒV²¾ƒ_­¡5ßv™‹!ûFÈ7,ÊÈ¢J|¿…'­Ö28ÿ.90¡µ˜Èż¦ûiÃã}'íf\‚ ËbQ„zäHÊÇù«<w·@Ñ:ˆC¤æ—X·úôjƒ/¢ú¬kàÉ `åJ[ ÷¯¢¿Jo™‘á m6‰Y6Ñvjsï+Ý—ò‡ÈmÃþ5ÙºJoõJÃyéä ‚Erˆœ˜èèÄ•è)}ò¼Ñϵøbž™úÆÆºá«ü;4°òßé/qC\%EZW¿þP’ô›hcݹ‡˜˜Ù¯ Gw@?¤ð‡pê{¦\SÍ3ì`uy×­¡“#VäјDÒÂÚD‹¨Ð3²ôOr6?¡Á ÊøL+F6­%f¥‹ ÉM•±ºG ¢ö™cK@$—é+Kçpáœ0ym]´/ü;RßrãQÌY£³°þXk¼õ¹L2ÃôÿŠÚŠˆ‹Ã7r éÓVwú91²vQÃBZ#ÜoÈó‹õÝÕ8XiÏû¤òû©D00w¥ú1Lp§Ç8ú†ä?Ìõ5†èÐûÛF;¿®ƒìû(!P|dü>wØ@H&ÁhŒžŽ©Åf [ˆÂSeiø„µ*IÑ~ÚÄ:"[Õ„ü@—s¿Ë,a{=á¸ÄÎ!÷ÓRï÷~ß{X¦ß¶Öý™1­˜0¾OéܨÁÀ{Ä>zÒ¹ŸïV±–µøQbjÐ@°b#}ŸBI½ÑQ[íÛý­ï'ã÷ÀhbpøþaÒ` øÝ€“4°&FqÕK–©çœbuá öpïsXlcFÏ}4·Žß`™s댾ÿp#Íîyôœ=+ÐÁƒ=‡¥áWÑË!<2_uc*š …Ó¸·í ý…jQƒ*‹›zm©à ÍõÞÝtD¨l"$­EIîð‹qøÄ62”©jRQ%9¹½O·':š4;²¶†¼g­qBdÃRŠÓG¼ÜÆ Ó’ß“´ÔÞŸµoxvÕ`÷G´…zü0>6D]oHñÅÛúû–1qY"àL\5¤Jt`utÈ1äFþ5ðhÅÖî"šË}ª4†ƒÀÀ¾ú hÑ¢È8Aé­"#b¹‚~¿éíÌÄ`¤F‘Xš­Ò'ùo!kHjØé)» ~4Ï3r´üŸËZ<;bŠ zx‹æ¿>jH.ËKäúFeLjÀ¶ÿ¯RGZpv¾N¸ý¸lY«t3q’àï1î(òîæÇ0F`È­!ngÇÑgY@w$<ãku©"=„™àþ…Ñ-Œüö5xî«dÆÃIôDöc ™G!нò½q8qÞJe„—ªû«x_OÍ#·þ*ìLl’Æjj†IÞ]÷GZƒßNв`6û¡'³éð޵çÁ¹NÝð´[P;ÊEBRŸÙGXÿgg‹ÃpÛyRP-xɀ݋`ÙÞÒ‹ežã<´ÕÆv¯^v†£› ×síÆ†1jй°~Ÿ;g#‡àÐömŸkáõœªƒþ˜,.ØØ‚0ÉòÏi@¿¿¡ëŽÊHætø×,á¹q7å࿘‚HQb¦"  îP€u?R…DXÑ öm®;f´»ú‚0¥øjºÿòxpJ…Ä„zØ\6 7Çø!f-ï¡@¥}÷toü"9ýsà‹‡E·ê™&-‰&ØLs†–àÔ”3 6'zBmã+öxô²ã%wWê‹.'énÓ ‰‰Û¢¾¬‚`W{4‚ÕÇÙ—ÍÑ ¬hЧ'»ŠÏŒ“´Zµk®Bi‚Tð§âïwËc8ʪïwq/î#î¨kWÝž¯Üîöié'ÝBo±' [UÞÕ:| k ƒOÛˆ`Ü•ce♉‹/š’}ðYŸy+ Nhz¯Á½èŸÕXJŽßÌT¥²„Qb@Nt Ý v`õMi¡³=€ïëÂ;Á¶  za‡»3Â|@«ÆP‡ÁN8ܪùè&1£ã†k``sÕýˆ”7X l\ŠQ—Á N3H|¬lL@ßÜgÄΚhPo?¢ªÙ?Ý Yà xnàè†3¨AQ>—§úøb?<°™SÞ\eÐuÃ0Ê?¿s£/V!:‘áEîü!1›ìeÑÜȨ¼#Ü¡qµ£î±X˜?kdúi¥ÐÞ:ÈØ%æ\¼I'ï"-dúk˜"´Ê¹CA…­Ùþ÷döšé»s[Ø­¥ïwAÓ¢=!z¡òí†#õ?îT´H/Žel×þ+$ÉŠ#.à÷ ÈÕ>ÿfZz¦Ío½Ç‘9­ýáóm©1U;C×Êé´ž|šèîƯøäŽÆjDÐø8 Úu( #_Úÿý[á3Õ”8—ò{n´-¼úz%Æ­ ƒ´ÂLÉú‘t§|v1i$£^›ÃxÉ høvÊeLg¢×S| SÛ·€Ðp¨òòŒ$ÚC:Ù{—Õ¸*vë@$S­ÅZgFÖ˜¶ÔŠC,PK~2Õ ö#*”5ç¹+$­‡=Qâ]„ã‚àtIíˆd!ù[ÎÊ =c§¦f*ù|ù˜mÙàX£F[d;Fxk2©@K5bz‹v‹dNSäåæÎYŠs•¤»ìV™&‰‹„ µ á0íi'ê›C †.Üu¡Ó¦ñ(¨O­Úç –)[f)‚5T¿ @évø@¶´uwÕ³"U¦Rz*.C„Ã,ƒ!xþ­ž‹³ ¬vø÷†)DTƒ€Þü&Ù¸ÉDU”f쟿A•Í~óÆÎUÂq BØ@z—‡=üž›‘”ùs²#Ô$¼á!Û¼¡b©»RÅÅð¦Þ ä÷XÇÏî ‚à5­*Îuc£H»TÑ`¡ÝæÉˆÐˆúÐSí+Ô§ Ç—‡T¸skÍ_Ô…èûE%*X~3²°e™a¶Ë»ô¼‹‚E$òˆs±„ß±Läˆ:…–ù‚âÈ^ÌñXÐë2V•Ô`ÜS÷5†¨ügA‰ù<1`aGó‚’®+¤·Ò~1xX«²©6´üGžë[ãSOräVëH¸ÛlsÍ%ï<­ Ýßzç×{»¶lKó"ÆéŠ=ê´ïÅ|" cÕlÙþõ“Á(%‚â\Çîý_9›šä,¼˜¸b<¾"î5{aúÜd»Ùå·zªø-åò>Ùåñ1›°,^åÎOßÍÍ$¼NƒŒôË~ôÅDçP8þ«´ÿ ³…ŠW4ÛøC Òi85B*êÓ3©¾ëߊ˜K*“üˆ~â´*.”ž½š`K½òìZt zéq¡­Ø¸Cöfî…ZxJÆv$ k/û‚ü×ú(Ÿª² R~`À]çü½ªÑHÌC2UdºS+òzî¡Ý jm ÙC\ Ò}WŽ^a‹X 1€+·bï ~à‡Õc•37ßa¨†-`·8€u:Œè1 7ô×ÜŸ{XÞSÃIsÚþ`4¯ ó~ý¹o)ïѽx~ªúü½cB%/ü΋£Wa:Ê>û¤tŽ|õ ©¿aìN7Üšpzö¦¬ßDEü ô/‚6Ba}.;¹¿¤¦ABSšr¡7¿óVÎÙŸ”†“¯V£8Ùtüâ$±ãß]„út¡ä÷¶%¯ ì“R¡7¿¸`|TM›;Ĥ6ÀTÜá4hn{ºy\6>ÿè*‘¸gV,!楜;Õõòk£¼èôÆÎeWÑ„òúÿ«ƒ Ì1Ïdª{t‚âô½§ÇyñN2©Á 'þècÜÂNiäVýüðdä5¶ ÊœëÀDü΋)°ÊßͰÅTq÷Ë^23gaB0Æm=Fì÷– ÞÐßÊ’?þé$ð–.ï¥Iëã(8?ºÈ7‚—´ñ‰"tx Ï®ŽÈ|i&Àµ5 4´M¢õÄ1m©|ÉïèÑ"èÇÐÀýðѰ@#>ôÉ“ûÈpúyúëDŽFñ¶9Á Æ?£Ÿ(d¯ÅrÐÿÙL¢Ôeƒæ“¾´µwŠ ÚÓ4²ÒTQ›†¹YªÃò|Úž‰<Âc°yX6ôp9©r­1…n£O_Ì,]Óñ–œr•ÐD6à¥ÕCYlü¿|ŸÛ# œglÔgû]j\ë­a EÙ…ÔNOý?·‰"½äðøý‘ƒ u<…A:ä@˜ñ"a5šsßÚSAÁ¨Ÿ‡îßmNŠpå…ćNèáÐ!ˆ³RΈP0”´Š&¬—Q6"Ð-½ 3%ÁÏB˜wÿò8{øÇ\€bLåƒÍx‘ˉ¹ví=¾mÞµrqœPèXŒ‘¥¯ØÜ1†Ý ôºêæÕ[m4þc%©¥çQ'¬GE’@YIZûI[ç- ƒK[Wâ_“§þâÁˆD#ŠÝó]GPm3<ßZ¶K[F„KÅÄýÍ›1EAŒÉ$̲ÿâ¡‹¨ºë¥–^ïŒÈO±âð^2J^ò}²Z„™c4;¥6ëçQ¨ÈnKܹ…‡Kˆ\èêO..¼OÕ©®3qs)Ž ª+M̹G8™^ؽç‘h'07âÒ¡’Œ wh@9ÙÃC-´ÖX¥Œá6C7 ¡ÉØl3RÑf}òÝ»2øgL6 «Þ [¹åÅ9 ’~« ÖŸ¦c­t°—šNQ÷k¯—ß²ñN¤fuû}‡Èv-XHÀùÐ{ü­+­€hɹy$CœwÚ¥ã˜1]IÇ?ýfÀâ“:höƒ!ÚKôO‡ò}õ…RòõX1\‚_Kqt`WêâOÚ©G£æ `ƒ¾É¡q´«eú)¿ÆBU3v¤ûø —À¯`Uîð,„Mî[nöãB]캺Æ4GU„µÇðÎD¹Ú3Sžþíí‚’;Òbjn@Y:¦zùï«F Çð¹Å½ ‰YGPd ßâÞp  /±›…íO>½L ®áÕ ˆ‡Ðã›þïaÝÇì«‘ Ë¼½íé‹Ð40¼HÜIŽ8<‰ï±±ÐDÄñTMmˆü'26‡Yô0F>4ûžôÜå,6¢â)X¿ ŒGñÆ%º?S°$ooƒ²@ʺú\kÙ‡Å"žcy+‡W&f·j·y;ú£kûTX#P=C*„øÿÌøŠ;¤çD¬Å­œøîǾ—ïÆ¦,Ṇ«ªv·}мÿNb‡"l“®,‡µ94²CþíBÙoª:ÎþÑw÷~|žì–P°Ë”@~4$Vz|@YQ vUX6ñL‘!D PV#ÿHÙ6Í´ž0ýÒ4¯L’Å ü_娹B§ò«ÇýKL_(2žLÿ‰,(3?x~ñŒ¤ G%ú9]9÷²K€†¯OûD¯ðHÉX$P'å,}kiÐA½ïÌ€Qþè/~ÿ{Ôi t½ÿ¥…÷ñÇŽÑsãUSŸû@€ˆ\ÅëÈ!gœï®,xÓ¯éJƒýßà_>G«Ÿ3Cö{C%îÚó Ìõpÿäž‘¯Í9‰õìœgïPP[£hl¸ôL|"ó[„ž-ž³¿5«ãU¡ܸù¸ w7å!Gð)u/7ÝGý°]rÙáǺuãíÙæÐߗެ cÀº.¦>˽Ð:ÿ+9wäøÀ‚&þ ›»Ýï[ý‚¼j÷–àU½cjRýy8’SÅ!éâܪ¸Ú¶¥÷Ä~`½R•š"®‡Þ¹pÔì‡øâô²ŠÑu7B½.6â3c§œ2 ”´xì˜!€32ú}n Ö;ë¶{Ñ_p4 y'ÆÆŒÉh 7vŸåt`y\gÌâ¿ÞsËQXQ›÷õ«‰ô_§>pÿ?ÿ¬Ð›› ] QTyó'mƪ$0`^­þ3‰ ¬9 {¶“¿©2××p¶¿‡ãl"¨®Ì.VÈ}È^Òá6‚ñ ùØz™³1}àÓî½-qùX:ÅÿY9V’É—m†õ¨Id˜ãxZ szúÑútèª'>Õ †MKä@^ÔB”ÂK‡1‚V‡¼yÅÀ‚7@‰xÔÀP8Ž­Ÿà†Ž ™óL‚Ú÷Ðî®õh™Ñ}GÿýÞ0¶Â+ÕCÖã›?h:µŽRU°Ø×‡íȼÉԯ–À,7Áüd@qÄ v•öÔÇ ø7hh› Ù¹PìÐë2VÐé¿nŸ°Pi­´ —®ß2FÁµ&ì h¤ö“M ÷¨|@ú1šŠ;˜þ'›ûÜj5cüŸãÊ5 òðÜ6“€s¥ »”¿«ÝÁ”s_è‘Òs$·k¯Ä¶M¸Ø |Óüžf™¡Q7÷Á-áöÿÜ=«ð­:ñ‰;¸­œ €Gv`¸ù|…Í™³Ýï s¸ ;ÝÚúR·ˆw½©àh$äØ'‡ñHv-Ž.<_ÅtAÑúýz}ߌöµm4îüï³äø¿@ W*yƒÞ‘Pâ_~¢Kê,O¥ ß@rzæû 4ïx\1ÖY+?§nb¹(±™1.ð2*@y&—ÜÀ¯ôX\+ƒ”!ÂST,('¸Æï€Çºº$…L›ñkÓ/?ó*Ørº]ƒì. ‹Õç¶µ†çÍ$©#ÖΘ׿3N`\¨ÄkÁ'ûáŠÖ>+ @Jµ¬ÅÓ%Ì¥àü7¿ð¥â¾.¶îé$+x4Q¾ø®ñ£JÏï ›  O5>œ¾Y£Aó,ÇP°ã+kÏár±ãçJf¾°cwåÄ£i§Ü»‹¼½m»EpQ·rp³”E·ÍUõ@©© §Ó|ÒǼ¶Ã®6›9!pVî%k§ ³Ö§ÔŸŒÉtq5ª¬ïAؾ+—UËb·ÔIV•»¿^ºî%$ˆ×»òÖ ·5$ž*âëC†4dGÍÅ»AÂ/Ì"½Ð,`P xG+4lJÐù–í«¶%ÒþN* t Ñ;X¤Ú®Ã›Lò’¯s[‚YGŠ•")T=IÚG>1{zp·ÞD¿]LøK]'–¾ßFpôþ˜{˜O}ìbÌ, w~uëÛ¶q¿¾ÎúÎÂü@;Æyã1œ  @ý!§'Ö%ök™ùó†<}×©ÏÆ,l,¢I Æ®9ãsêw½Ã+έvn¯é.hò°‡‚BÛ9óÄsWˆm³ÕòÕý14½BD“é˜RvFn>„»>„²2S_Á…NÝe¡@uLŽ}–veìj_$ès°‰ýùel5OhýÌ6±”O\_Lœ oy¬« y8ËcÊ€£Ü0;aŽì;B] ò­¬}ÞÁc9kÅß? ¤€¿s¤¼Àñˆþ¯½ ׋Ÿ÷N[ä p@”l:NŒÌMgL´iËZ†üs×ø¯©ÀnòšLnzRc(ûGJL‡ºÄ@L:zú°2.?I&ËPÖŸ{AŽÜ­›ÿüQpÓÿ¸Ï³ÌnÁ e‚¸Òf½ŠSÝF;3ãè"(i’áþpÿ;Bì—˜³­’Õ¸¬÷Q«@Ë —ºÑ¡ ŽÊð@¬ 8éÈ·00ÜÁyðòpMØƒÞ 0–Tçþzˆ.ø=e>ê^É<6kP~õ-ƒg ²½Zgg€M`RÈ9gˆ¤ŠPêtM- ¬ÿüŸ~£~~OM» жMñ/ŠY 2urÞ?ÉCÌ`–Ú=¬]CØ(O4ù}.ûÙïAÜs m­UÿÆË挰7pÀ+º-S-Ô98oß/ §::%ªä•g^êçœoèôàÅ®OñŽ;„/•ý×ñ´ïÐ×0Ø«$lªÄ¼'Û@ÿòômÿž¤žÝûþVd”c_âúWÃüEÊw GøOSþ1eÞÅà¬¬È ŒV2:TˆÐÛC£ñ– h÷P$ZIA.ˆU6z YYÊÑ;†áEÄjÇ”Œ™ÏwO˜øçÌ+\•ª_`5€d‡¼ã2R€«ülŒ{3¨\~Ô·Mó¸`'nºðGk±Ë·áO¸lV‹YؽèÒºÐõE޹F¡§}uá™ã•êuöE«C<ºÏ˜CÉühUíë/Cl¥¿}­ƒ˜w.›¿WiŒ«¼™tÈü2\MüÏ»m¤èi­ùÇ×ë]î4LO8zúŽU¼]ûC[2Â^³£€nðÃr?꿸çbƒÁi=ÀÑ!%(jqÙßÜlȨ†¤{¼3ýo€jljHRМÌvTÁˆñÆ -Œ%°n<¹üt£ÿ'ŒBaæÖ‹Ùí:C@a&÷Ö¼o0,âH{wWéëaPØçt¯¹AR² پƂý æüÏC©÷ÿ{¿2h ¡PQÝÝÞÍÿå)^â.â%”IýšîR)\¹eİ;¥,1W•‹Aу(,VîØŽ7ÛcÇœ¦—÷ ‡¡äøJÒ…¾”Ãê#" ÖÔ¸‘ìy|à~[q÷m¼U·D 'ñ=ìh8pC}ß¡ªØ`‰u_ø‚i´¤VíÞîüŸ t °8ût÷±ãi¼íŽ­_° Æ!û§—P[3µ˜¶‚š“M¤¤ö»wæ áù@^M!åÅó 2‹ˆ XE£,ø«ù€NâPg¿¼ :Wq‘Ïß!™ ® m°^£Ãyõ7á H Äô‰ô°f˜Ö}GÊO#€xâù(GCèƒ,mêYãÖˆq:¡*¿ÍQêº`N§xcUŸâX ˆEl%`hA…âmG[?™1Ð ö±ý×ñìûà›ƒßý(‹nö+­.QŠ÷qYp¶¯¿7{7lÿíœñGŸÏíò6¦Ðoæ>CD?µU‹…Êó³uPŠ Ôæ@QåëÇlØ…Y‘×õ‰9^ÆÆaW=ÂâLˆ®Ä⸀zea±l¡|öËÉUÚŠ=ä‡Ìq~Ÿ€‡q]cê×ÏÕq׿v¯uZ¬q„F’ϘCw½E¤9¨ÕR½'ÁÃ2¨Z&hxÚ¡‘–Ú¹ñêú¦Â Éý:Q¢²b ÂZçáyÇ€P)'çVÑÆqÆÓë8š¯ 6üÀ€*hî¦Ä>ÿg‹Øô6.“bTÁo …8qN6`öA‰w¾…öÊœªÍÞêÚ íàJdõöŒñ8äö­5¦J0&*¡Ž‚Mp¸2z#{A+ƒ °(!•¯Î®ÿ ÃhÚT1!O÷ˆ)qûŸ«ÔÌÑigÕ˜žÀÿT0Éý&£öEGœ:„OqÙ#\ZÿBé㣀m0Dh\Ò{ôáÞ!HAùr˜—4~ýJ·g¨™+`›T³lK¾Xy§¹SL®þOªÚR!î‰%}œú˜µALŸ¸†¥==J°6^‚º§«ò„,]æ¤r›³Éíi¸íNQ±ðò,hD0ÚÚIB #Ê †üà ¼|é¥ 1Ô îz›ý j1-¬¾‚º--M¹ìŸ®âõ8÷$lã¢ùœ`KcÈOý–n&'IlIÑ#èÐþþøýÛ¹u˜?ôÖ!\­ Qï>̼˜âxÌC¯Rù>®ñŽ’¨Ì"€˜#vÂpÁö«'ºÛ‚lSm26˜w=¥­ö2 ¿”Šk ìýw5òV Lß}ƒ­Î’ç­^{ž—ž®¹œ«Š‰Xç~26 @ÑS~¶F¡ˆ2†ÆÁ<ñÊEØœj„#ž«öÌ‹eŽÃ‡ÝPFRu7¤1°%F¸ûö'- W”/Рª±‰" %æ, ´S¼lƒ^%µ::%©•i¤Ü!R"7®:i.G*g¯L•“{ñ–ÆÃ†—Ãušœ:X$dôcåT±²Z+¿ô]l 䄚òPH¦›ùº]$â|oAÀ&"c%¯(×!³6Ávp*À+þwÃ[SïæÄ‡÷á"üO¥À‚· þ¶ý½T­/<5êÀž£¿Aý.5aªÚµ‡8FEà°âDüµçΑL°FBžonžƒMr^UÂÍP¿m ÒxQä £EØ^ç d¢åpjdþød”ÒW0¯/¯Ê8“iãu Yz îúÕ^K/ð6¬•;–¼}á¦<ª3.«„‰”AH¿‡“¨69svòbãðÀtCGâSÃŒƒPK”1_ñ‘”Ô¤^j¼‘À/\K#G™^’´ËuêC¿R )P&Æ"2LÀÊHKÑF¡¥c dÍÃÐ:ÙgBøÖðÀQÓàBÚš¡Ý-þ†¼Iì¢4äû‡£,]È$v q5$Aê ´Úc¬t¿ÃïÃ\pL¦ÐÔÜ0&¯øèÈÀawT–@hoþ>cÈ™h ¡ÈÃüI06´¤ÀR~ 'xÞÁ‡bà—%Tç h ÄZœÒñóÚtÕÌb‚+ Á —Ÿ¹'柱bmÑÐð C~O¢zh*ü ôjL  qñ3)GK)N®dòA'˜½x’{Å‹éÿ Ï—íX°÷ŸÝ'´Ä(…|츥2U_Ø2¡h- O¸heüKù9ðòú! Áž-𲧃—yd'¢ªÁØ@.,âC!’êëÓ …Â`¢òÈU;Š `¯>ã‚à„9ÌvÐFQªiNsürèç iG‡=Eϯõ·@¤~“KøœðxðÙb™9àâüßÈšªœà=æ;âÜžC‚ap /Yï¾\{’VêÞÐ%IM‹mÿÁCx£îçMD-…ðìÖ[wØüÕæ/Þ¥¾Oi°j#È6o|C‚çãOÂAY­ÝìVñ[y:о#ÆW¼nøþ¼‡õ †{I- Á¶–ñÚ½Uk³Hc/5cl*©¹Æ,¸xÇ|b‹hœʵò0X(S:ÅH–ì5€nT­ÏÝ:§×~L¦ÆFž/(Ô5:Jc€>á‡2ôŸºNni÷AÛ´/B5(ó!?ÎùN]€Ké° [ð`“´ˆá€ŽžˉPY‚fÀ¬Cé†êu1ŠSIü?oÉ-7é/o½Wÿýå¥/ø,‰b‹€1ø– è9îvnÁøEd<O«Á»°A‰h~€‡Ê°–¬ €† ÿŒ”`· ø‡Hœ9&oU„DÀ%`H2¥è&Ôoá¤X5`¦ƒðùHpÀ{ÖØÒ¶þ^iX–šÃ¥ ñ ÍZBòg-lâàiñ`l>„¨¡v¤HÅH¦Œ½*ì>×OoêÛ¡Â)ÃÎÁ¢Ñ”0–rô‰vY†4jgGuÞj‡OÊϪdeR@Þ=LWÖáÝ 6ÁÙI¥~«„t!ÓÄ©D?ݸ)ê¢1ˆ‰! 16M}dõé öes—†Ú7¥A«ÿ^>õφ´& T“x‚Œ‰lŒžö¨ä¤òÁéiQp|CŠPµ%hc`ðU‚%&_úÐ 2®×E¨EX‘€A¯Ä½2”&°1\y#ý.Ÿ_àd]ö<˜̪ 1¸Žâ1Ea¹á^X¼¡spâ$OY$ì¡€‹(¤u}­&2>dõëÈ“ùàY˦¤ÁC0o Ç´cç•é–*Õñ—ªrÃ#ppHÍŸ}ïÅ#¸AŒÝõ„h«Œ&R5x)Wã¢N¡ƒºõÌkÐû¯‘˜`µv6(ðòÑ"–P§cÕ” ®@Ìk°#?ïLj X¿@îš2Úøâ¦ŽXÊ@é}ÿÙé"³¯©iã`!qgìgˆ˜(@ôe?7°9—ÑãGX5¥€Æ¾]+&~𯕦« ë§xûÿ³Åî‹H‹‚Mr¼adôn2«}Ѱ»Íý¦HÖâ¼úY²8 Lžî_¼M­Ý$ko÷âµy¢Ëó¡·šé¿F!˜`éâ_îýr÷rÆCîWüŸ<Âv OñhL‚ê5g-éÉ•°øÔßÂ÷“yËWÓ 6‚0ì—v°²b8hÒþ@XÁÞXÂö[ÖØÜ0rx*)È$26ÐYY·RgE¾WÕ0àö¾¾|eT¡ûPþQ>ý¯°ˆrf{âåÿo~`í™(¼ìÎî$Ñ_ÿð制hÖVÞÛZÑ$“OM>ÀíY¤ÛicËÿ ‡„E±]¸­Åb±X¬ùÜ(*X¸d ¡½í-ßD S™Þ/éú<ùWßwÎhD/ÖJ}¨€ªÐÈÀÒÒ÷xÅ61ÅÓ¬1³a‰ž:Ìgü1€pPEmž¬37Ü8 Þa~:©{Ò`KB­%…± Õ“ù0‚4‚Œ ‡ÆŽê*jg*ò0LÄþ*E^‚Qs0‹î¡QX—°ã¿ »ûÃTk¨Zž!øÀP Ò  W@ÑapUPI×?q¥9@Cñ3€ïÁ•á sý”ƒp˜ÔrBÖ‰n îÑ„ Ä6CFà ,[e˜D(‡(Íi0Ñv?¹ ¬ƒ<¨°8ôÀ@…í2¬’Ìïàfáë`ÆØù<0f ËLIÞ*挣6UGŠ¡ y»¹RjpÏíoa¥@Ôº¶.v`Ä 5JŠÝ¿Ñ€º Àˆ5gÂqÒïË@†Ç„PÃï¢"„%/mFó?F^ µW‰fȵਠðñ¹…lµNg‰ à 5Üu[™ÀòǺ*¯Ã+(‚OÀn…N‚#š*WƒQ£§,°süU›Íqâ+êºb\9ùmÅSFìÛ÷SFÇãÁ+wÌîbÞ´ÉàÆ`L€À!½²vÍñà`”K–¶‹é«ùü}›þSQF–íu­Mƒãº©D‚€ÐÅêœ+§JM]v/€…D‚´{óõ?—J¶Ÿ'Ÿ:ßÁeËé;*©²|&£eóeû„­µv ƒ.~*\aåLùð‡Š Û-ÏDóÿ„H'áÔ3» M…¯„(iºÁÜ#y¤B&¦q7m7 +2“0òŠí̼è¾Yª>ôŠÎh(íS =™¾f¹kÏN¤ öê(vhº¨ˆp£kçPSŠ2#ø&üc¤Å=ÅãÐÙõöÁN]ãât¶_«LëLÉûuŽ}Êuu'—ñ‘BªX¶têgzA3#à«G0"ÚátÊèŒvŒí^ Çè˜c‡phhzIÇz¼eXmábmp»ÃY²0ˆÕ8 ®˜r;¾²}ý”pA-ó<üôïç‰ÁaKцà¾BÿbÐi¶^§ PD¿k§õú©I& Þ<öU1x§ó-ž;i1¥Ž ‚f<׎‰¾­™:°íÏþ±ÔOù¯%}NPDúÇæc”+ÆÀkÿ‰1ëuʾfT ÐS7d÷ãeh­E™3‰ì*9Ä/Àgû2xBáï{¦jkóÖ)“Öî†7ÒpaÆe ˆŽà/|p åߵ÷odNLvÎRõ / P¥'ÎÉúb‡ ²  ÖA4¢ªÛcD`n’¿pþ¹é;-0©à:;Ò¾$À¢uCˆM3x ~ÐøÈ†rRbB ¶™·šF¤´ˆ!îCß!ƒù-æ@j§ ;Ÿè@ú/6ƒ,Æeþ"@Us«éîà:mA…»Kþ`ëý}$Rï“Ô©p“’§µ$öê"B ³ƒÑ~ÄO•cªúy>»qèva–¯ùgŽ7:¸+Õ ÁR•y—Úø|_P.!ö™Ç`ÛÍA¤ .3+îÀã0püå`•]ÔxS?fl=áaÄÙ1es °–UËìÐâ ‹õ˜LeÅ/‹®®‹¬2Q¼2Õ^kNA˜ã% ÑÔ´ Ôå0Lh˜ÿs§ì£ãå=;'˜è8 ü©Í :þFḑ™?ï¡¡¡£ #س’ÇAÞ`äbçšO†lßýPþñœOöF~¬2?ÿœ,ˆhKrc ­“¬âÛVkYŠ'Afþ9¹&Å,hc¢GíŽOõ°…ªu[˜¡3@˜€U¨A&öÆS=lŠ—aª<ÍÁ“ÿ£¢b¶F !(O‚¸2V ðALe Á£«ñgXZmËhV&9eH€N—ß…+Ñ8%#Ȳ ÈW'çø¾|È ²ÓÈß{Aöäñj‘$º$M YePyqN»ØDŠ×ø µªÇš”aâ¹@»Ñht%FŒE$S £_ºU‹C_á†,¨¥‰’‘¡ œËô¾€î)^2LÐ^ pý«j‘$Àº…‘]¸Œ¬•f!}Ÿ…@ž;¡ÓVXÜW±˜^V¨üÕQ´ög}LD¥¸ºë¾z «@@Z@ %ǘ˜—/EïÔ™ÈFñ[¹ÊBïzh`A Ï™Éñ€IK™Èê:ß»Àyµ×¼ÝÛüµ›yMG!7éç  ;>Ü;—;p8.Š+>¾2çºr›f`Æ ñÂì SOƒŸ_ýSOãj8ˆ ë‡Ù±Ò1YÄPy¿˜Ìë¥uIÇ'~—ªyØ|l9›âsáöïÄºØØ5 × ‹JjZ]ôÂá Z0‹ß!~›qôžEç¹w†ˆP|¶ˆ>Ýÿù=¶8. "¹#_Ã1\Šc4Š¨ÊŠÒýb\WÅo°vÀSÁÑ)ñØíx¾ð(2eMm¿ÇgÕç},Y<0ଂèÃÆv >~bP‹siþ|ÊRŒ`ï¼½IOü…Æåå·mâ··›Ö´ÀP£U¡ Ã'Öj°Â­¹„I½~J ¦±мj yl¸>ƒ:±ÀâƦ“WœAÄðP¶íÛ˜æ† ÊUFÕB¸Ç†›,,8_)m+$äñ° «­Ô¢ÀøIRÀgH('cP<†? Íãpý,Ñlü&èfB>ìs+Þ`ÐNÕ¨A&y渲ɻ©kiÝ¡„žW‰ûÏ @µànb}ÊèûÏ–“Ê  BÈàªí øZ˜— ¦ÀàƒÿP’üÐ5ð0Žðsp9º±Ó¥ ~ZÉzLÌ.tÁN úQ è? ‚¾XAQn ~®T‚¼PK±P¡b´ÑÓ"Vƒ ]¶¦,ja•óÜae 6Q]ιҊ«Ÿk€Í;2¾Â¼2šØÑTRÇïâ`ÔΠ¤Ë={X-PƒxP’üƒC¥YÑ­Rp*&äcÒpÉ#þLÌQ¯Yç.ãªã¶ª‘%Í“™’j· šÂ¤Whß´.mݾ ³ž)jÍßZºÝqŒÆHQ‰^fvµûÍë¶f`æâ‰‘HŠ?^lQDó™®wâ·ïçVaÀÔ¥_ +~ØcQ]f–é%щSUÕPÕCˆ¤n’ÓkþK4Øe¹?¥Çèߘha˜·g£'Üá,Ýéþ3ëµÆ›ÔvÏd9°ö™övEÔuübþ0»/ d„êy f–ÉH’?‹t=ôø<¨WåÚo“t°ò{úÿ†"{ñÕpøºÈn˜Ä*X¿µõæOV¤O¦öšj‰?ëŽËÁ½3—0ĉ º­ÆAó¢ j§Î =èÎ sÔŸƒ·¸‰{)þV ÀZçKádA¾­æÆÜK0¨Ù?.´L‹ºf‹ÿóº²4®8Ðø,Z!oèç!x—ÀÏôwÎIÄa6‡5 ã¢ñ"º«f z¬ýï.Ô®Õø\+lÝõµ¶3¶¥æCÉy8ˆ~ØŒ¿€—9WrŒóz]ܼ(°ûï­uà}x}A¸)õ¥ܱ‡Á €²øËžÁÛ#zk•–³© ±L2ÊË=]tb«b¤Ü 侟Ÿ Eì‹¥‡DI¹8ƒ³šï¿Â-p@þÆŠ0Ø Ü^èBë±pŸ‰Ï@Ýä= `Þ$!†òYV$¤ÿkÐÞ¾c*;¸Êµÿò¦GÈ–& ¦;´4‡@ªšÊcl9^–^C(Ž6PÞúÓ ð@S+ í8D•Ê‚²)ªÃîZ­õ57ã£b®å(€€Ï@þÚ Ãb_—u®^ËZ!Ø7[i¦lñ–®/¥¼(¾Úº…;ÈW1==Ç|ÿ[ž®Ý|Ð(2JøÄMwµ~äÓ˜‡³¤Éæjc{A ›:ŠvT:K"²Qôí„-F½ Ë$q¸J|úÕ¯éÉóIÒŒíäÿ#Ŧǚۋړ­GÄþeдüí 9æAË™ÛCGÞdS—5‹,ØÒ ý‡ïñâãû¸óÅq:ÁýlȈ…‚]vÀôÑi°ÿpl/xzý ÿô°xù{A-zxJÄ㨈RXAÉ)üO[B „ k’„‰\LžˆöÊæ>0ʧJ›«=^:Óõ/?“î›Á‚££â“úx÷ú À·)Å<ð_­ž¾ÄUi,‡émÕþün Èë¹ðnO>Cãav:Õ¨YIãàeUSÜA2… »"9îü….ðV*pŸóÃÏ…Ž:•d}z-éÌÖž÷Œ|0Àc×pÿŽã.—‡Ž p\(lìdÌ;/]‡ôÛ8ÿ³â.$iì_ÎÞ<$Ÿß…p-á¹7Š ׯófLpHÿø”&ãŒ}¿í£ê¾ïß>ìß4|txü?À Ý__9µîýx?í! V©¦/ä"ü;ÿÑâ˜úo„läÞuû¼@às:4^D$׿·ä6´0˜9K oà­Cî"Ò_^Ü7× €ñ åä>%*š+É šM½ð¯8CAFÛÇá‘è6Õ1ÒR(Žÿüj(ÀŸÿÄã²`Q‚¹!x"RSê%ð{Þ¥y×ÿÛÎt£ kƒ¶vÄNâ Ûäbšø–zÊÇôø·Bl눆¹Ié¿fA”º4i–ûÔdlK× xqk3&U‚¾áˆè‰COZ5çü;ÝØ”ÆÚôúÔ&EÈ{ v¸y}õ ·!ŨõCä|·¼ãá¦zÿ¨Z$ü>Ÿå²˜èŸWU§Sýqñ+RMO]2bÙ¿Y¶¯àÁ RbȨ¾n,}?ñǧãÚäÖGwÚë‰S¡øÊ¢b ¤™Œw]%g†üÁ¸1“>ŸP?xÝŒŠh®èR@C`öp‚Å#À’ÀÀž†¿pi@$Á­D ʆ­C?¥ˆªû 2!»¼1h”^”¤S§–)RPNP8=ML‹¼·Q•ÂU+ÏÚ¬9%í^ðæOgDø7 ¥pX”­T °ºbZ&1oì‰:'íŽá1ÂÿZ êeõÇÌ4!ƒwåÀö¼D.¥Õ‚+ÏC'>eàã.ÚãvнQ¸«þ5u½e‰aæò’š•­¾*¦Þ$ê±i’Y²)áqQ‚ ‘#…÷&b³„:ñ¼^ÝécéXVU¥îbv~…²ŠíõC¹J+'ú8Ûzy÷gIµ#ü/H‹q\ÄÑ‹»R'?s±Ù¸ýÃÓwk›6¼_‚²¬ íÅ~e5ÏóIû¿~ý°H7-â_ADÁA‡´ÿÿ¡ÐpÀh©!öO(𠇀¸©§È¸ÜtYÎ8# ŸãF&ֵٟ¹s»ˆqª·µ1˜ê&ñ€ƒ±Þ!ïõÊõé§ôàðz‹ï6{PÈ= šµÜD´žÃL6 WŠÚuЉx¯ÛÁ Vï{¾îjpV²òQõïê¼´bRê/Ímß»ÀgT€$b“=U@I R“œçZH>€B©áËS؇Œ²¿1H#åÝ5ºù< @mÐFB†¥Ç/·äÐ ?·0¨*¢ŠàÈ#J®–Ÿ¬ QLj7r.µ¬Üž”EÜž`Œà‚ê“äÎßÓàä®r×y¤‡ž6BÀ–˜8y†äù®DüëP:×%q´¤Ó™•UŸ‚a‹ãZ±ˆû"±‹ÀºÓ|ë%H€4™øfPXf—pi¬Ÿ“TÆ+¦•‹rÈãZ„êlõ0Re4Å‘2t@5Pê(¶ÌYY¶@ÕÑRo„³B´{Pp.v6•ÌY?ìòåQû«„‡°Ú_þ6°W ƒÆ0½ßtÎLAÚ¿P$Ð*4pQyÃR„† xc$iVkí´—Û¤qW…öQ—.Äj(B‰5“yõ/RõÆz”xV4vP¤m0l;{R$4Qì¬Ú1š Ø˜¹dMf ^Êç ‘+Ô“ŒŒŒ.‡A¹isÅŒ%å`¡ ÞKÓÑ^†`ÎÊ4Œlaô…½poOÍÏn¾U+)aÕ/ûWD‡ÿU#L“ÕxÑ;›~Ë¿ÄÙX¿†jéu‚CÕÿ'¯ç¯ÂéU?ü.¤4æäk õ5”¯øzÅè^\?ñ³®ˆ¢pÀuaðÿ·ÿ¼ûp!¿Bo“W‡VÒ™(” ÞQ-ûA€?}¢üTq3ùÔ¶½ŒjÓå,Kai'>±ôͶ9?´èeÆZ®µÃPØô€P~¾ÀÑCi6ƒº Ê.C³,9´3†–›- KûŠƒ²Šr)OÜvØiмH¡+„ÑVkÿš/}^3K•uB´aÁ=+ãÂ'8—„¼ÐBZ+—O@XÑå€ëÁRŽõ íJT²WÿðKŒ­ª$‰Á‚Wˆ ÷†¶Æ„åZe_ŠÓ~Ë€ÅevÇd‰\x`6¨O«ÙTY×Á^šñþ=Ø…zÖ¡×{ˆxUŸ¥‘8#¨ûF†þ3úÑœ ü¨:x[¸¯ÕBzÖ.$Šø¤ÂGÖ®44c.+† &ÝW[té¯ÛoË@:€¢à5©S ‘‚Û'ÓOÿ"p2t‡w ëš?QóŠ=pÓ@Ë hÿý4ÿ¶ŒuÕÒº©r&n\¥¬¸/TY¡]LUüxŸ¶ûô€Ç¹f¯­¬ŠÞš2|úôÎõ{¬ßñŽh~Z囿ª¯ÿ`¢ª’§^U7´¦ E¥¤ï¿µ~zöO2Ã,O``ä,˜¯öÃàÜ0"wäøFˆpI½ÙÀ#&РVbòýf®g^"º©<àŸé\+[Ý+xSC duÆ<$}y€@ÀMÏ¡ñV Ü0L_Căïˆ?£9÷8p·Ê¦ß\±·ù©’ÅíCZͬO…«<òZ£×ÎàpGêy†£®ÿüÊÞèª Ÿ&-Cfš@åÉL:DHÖV} ;0–še|Uü4lwo´/-Š¡0 J7V•»¶NËó¥ (cɯ7fd/1ìHU0J"Ó¼B´d; ôàVÖP² 9.íHÕ1y7Ь$m”á"ÃŽiô¨®p" 0!gɧ'ƒJy`˜• ²ÕM³á§ÂU6ê·øŒÜ‹Ñ~ÎihÀ¶À‰à‘<ÑV˜0r1eóP¨e¤q†÷VKyê9õ»™LTÜV«šIt(S‰áY] õe¶Ÿ Dv‡|`­t 'š†”`Ñù¤ ê'ŽÂ­F l`ð6ø™ç »¡;¯0ßUÂjª‹iÉm¦3ù¤$‰bqÞhÁÝËìx²T¡-@C˜”!½¥Å›…&™MYTaS5R")±RÝ4Ñ#·{¥FÔfG#S™–g¥/M;é¦:Gæœ$*¢k V“ÞÖ¿s8Ø|:<à³ÀÅ‹…æ6^.ò®õ'Øò8STÞß•*àϰ`¥¹Y/ÈQ.]˜HáäÀÈ•E›‚G±a© Œû @‚© ήÆGFÔ–”@<Œ4öˆh5¹’ØSã*M”kpăኈGæ±oý‡‘$_[€èyÎzO®°@¾3v>Ä’³@MÊ5üà íy!Üøòˆ1xã_ºÆ,Bp‚¬³ó$«³9·~rÇâß—§ò}}9„L§0(ª_¯G( –ÕYÌדC\D`bEBfë‹v2:ðE‹_¹3ߊ*"†L¸½¨%«ú ¥Ž?óÈ8Ü€Tú]õWc.“çK¶g¥0#Œô·Æér/4׊ÖÚÛGõ¸Ä‚pSÉäM1a«‰pÊì…®@1©†"k,ü$VMïuàêœÿÀ©g/®½„[eJ=†X¨è,wVš a_›‰*ï[ .pÊã½mëâSŸì &pÁ;(TSŽMŽA]®†`e¦‚)Äû;3·z¦y Fü¡8C}3‡éÐäDî„~SÁ2¿Ç‰ŠÎR¯¿u7“/Ž€§}½AUL’³I`ËdËGµ–’QØ–å÷Í€1};wsß°d¦VY"ex^)¼þ?숥°YžŒ!“ÉÝIƬCÕ[BÓtÝßÂåS¸@”#eÀ†E-Â<Å@Fq§üX—eêƒ?3²ö²;ÀÏ]™=oÙ^9ê"/ûïËjÞíg®¯el)p÷Lᤎº¦rU7Ñ…ZjݽÀA+ô¼{Wã#^m¬¦²|Öºì^QËÃÿþHÊ?TÿÌÜ qjzŸ“ú Óº5ôÈMÌÇÁ囸}!€_’ûý@…±úûëTÙ·ôCb 8ˆæ‰x¢P‡‡l¾º ÅŽ_ðìŒ.Ë×-Šrï£Ø×ƒÄ€µV [€»ìû^ÿøyÁ’X ¥yMŒ >DØtË#7°*¡÷¡/þKDE†0³ÑW?Ý4zÆJúM C/Ã}<°d7ˉçêñªü®v‘ Q±zâó<‹˜î)#*@y…=~½Ü¨_d}ØóÚØõw ÙO`}‹àÌ/úîý5'ðû@Z·-ÐÁù–ÿ|úæLêÿxx@ñ&(=rˆ_Gü\3q®àØ?p>ˆ68$Z¨¸Œ°7òé$:°KüÛ ÝÝýã»t ±ž`QvʦÀÛAÛ]À¦à‹›òŒJw[oýBŠ)è×ßü¸¨£÷A꺉Šïߣò¾ÆLúÀ`gÉc Ø e:]åeêEêD†5Â[$ð!'ÞݬcB[CNP »^í¿u, Jy¿ýRþLt¸}=‚j–…ãàï\Áƒ@‚'Ië©ña¥µÿ¦Ê´XŽ´ñº9LÆ›“…ü%èá6'gòzþÜ‚´¡¤¼èذ͚„­ÇÓ-PÃ'€Ú‚šÆ&*¡ 8ê,LTIeGˆ¥• hŸèÙ=e|áØºåXK×Á‘Ñ!­'¸Ð=w©–Ñ1Ì.¤«6¥Iç~xDÐϦÅ4l]‰ŒxBýÂ.ò´ }üªÎ „, "`ÞYBrçaøˆà¢‚­Y®*£iM÷…9U„hRXî%ûæ\É™?‰ñºÔôؤÂù@¸”ó£E£’룈äwSDÆ{ÐÁý”ì0ßð­÷Bÿöƒôé¡¡OpÁ`­¦s7ÌùÁFÄ6ªÌþ¯ ÐlѪ$®ÒCcÞÂ@…>ìZOÐ <À¹ªîæÏ>Ɉ%R¬Š½øžU ’Õß}Õé5Ø„Äoa_¸¯ ~ @ž —&=ÂÏKw½Rè`D:ŽáÜ9ç½3A+Þ[¨*Üß 'žÅÂè*ۄͱ?ž²NßÚH)…+¿QðK“…Os'á3ˆN?PžÎÔî3â¿ãü÷>aJétöÁ‚\¦6®¶rEd+5ã_ó¡¨l;µSdQKRàïf DâɲïlVê$úèüÒq‰g¿fÖ¢x³â·0‚>Æ¿OúUƒ€0â¼ßËèÞýïÞîô@ƒ™3þ ©‚„ëï嘀kª© ŠÉ!°ªJîx²?…‡ÒË€ê¦tCù804ÄœeÌz¢:8‡wΕM†ÎÎ?”Дv©*·ciÅŸUO¿ó@KJ]9|;Äœ íBÀOýóÏßY ¨Þ†Âÿ÷‚ÀP­žL»§¦Õ–0' ±#²xA{Œ(H`Ô OP@érFjÒ€cÜ s\,;ŸPã{Bƒã °¤2ÙoϹݑPpËÿ×Küž Žñá#nÃàbÉ+žYùI@#4Æéûo¼àï tâ:ÛÁt¶a·v`Üv8qR(p#°_Ölã:n¯ÍX©)ôU®A¨ˆQ¶sð«ªèfí3+tÐpk¡Ö‰bàD’ûy¸¨  áJ|p@pM/ *†T2˜8ÍXÁŸÍîÆ[‹äâfGE\Æ‹ ‘ˆ(ž{“ÊM¤0¼s“ɱ”a7($ç ¿¯ÆFxƒ•Ù™'Aý( ¢ð{¡ƒyú˜ØÍûw,ÃbŒ*>~x‡üïŠØÉUL©m¬ÐVkF¬’„ KÍ¿˜Hƪ8¤‚µÚÌ!Xâ=Á8uk…aAø·tAµI󳘩9Å ¥`T¹bа:wòU¾qÐT X„?ŸÁ›êcD1oâåé~²x^˜:ä»v‹h< öÍј;”«üüÅ”’9s©{©Iéö„Çe…J­ÿ'ýcÓ •d»ž­êüJÓyh8Ž5>1 O%´¬ žä›Œ KyÅ,ÖþJ{çõ'W²B¿ŸÝ"né"WNÆ'ãó¦¤)µú÷L=ƒR¼‡McH€¹ ¼Éžê=Ëb;ÝaBè³Ä„üovÄn<Ën|÷Àʧ÷-¿ÿüªêÉÎþ†åßj? þO¶'D©’l?ÃI¨´PhÿÎ"0þH€d‡ÿ÷Wò”—g“öêÄ  ×rÀ Ñi,uêÙ¾3Ÿ‘„]u]¨ÂeÅ#ˆmé;áØ’r™ÃƒBuDñ(-yDÊæ{‚ΞÍZZå µ­QÄ…æ87p’ÂðJv?¬ƒ}„Ûcvkƒ.„ÓM»aæ,Áú]o¦T øáäûJóAG[hÈ3ôK×dÿ¥«FN(Äc±žšU4ÿº°P‡lwô1$<Á ™à>ù^èL…‘£ðOfÚ_Îú +!3Um€‘ærCµÿñ™ØcéJ,l8>=Kb“ø·…G¡ˆ‘©ù5àL$cÆÑx´|§Çóç«°òOÚ\{»72Mù>³¡ ~Pë (ç'•¹—i-»‚ŽĆٹJÔ¬¡ÒÚáçHN÷ø¯»P@ºÂWww@‹ý%•îÓÎAmš/½y›&Cø?ü)>"ó®÷ˆûÕÈ% ÕÞ¢4?üÞ3{}œËá‡ïBn@úŸUZ¹æþÿÎ:Û*c]¸6ij†û®eˆQËgÀŒù)Ÿ±ó'‘ÛÞ‹ýinûïÂÌOæàjþú7wŽÿ™¼X–@!¯®&†œÇ ËïÄCH¸MÁ¶G 0 _¤õû:hTc"[‘uÀ®S¡î1pÄn¤)"y£&×Ý ŸUÉ0móÞßúo´;Ô`©§Á;àE¸Â¶gÂbƒPì3ÑN,?ïðÌ íÊÇ×[•3”3‚)H[¥ºœ¾Z_ãVÄjù{ÿ::Ý ýœÙ ?þnÚÐÐØU¨‰-37jƒüì¾­È7Êßá ghn߃·ЗÈýÿ£ñâÿƒ¡&ŸE†’Ô–¡×}Sûï¿ønw‚ L$÷Ëôk3â^SûdÆÀFáë×Îh`P¿{(:$Òù÷Œï0–Ѐ3[øøèšëüõŸÿñ¨ï!±‚dX=.a2å¼E>ªÆÿ;9/z–¦UûÀÝøù,tK÷þï 7XzWª‘>¥Êç_ä-}BfÜ}ë¿þN7&`ÐȪ2¨Ú ìâÏ6„´NÀý“ÿlNˆu0***±¥'¼¶ˆ6d Ž1ƒDuJvEi· Áõ&l ‚ø]ÃZ5 €Âú|õ†!á¿ð@ˆ?Pòxå^„0˜}I¼\š“Õâ¯Tþºø/xñtÐBØ!¼Pþ”ЀÉï[(Û)½)ÖµÉÂmµcl°oÓ^c@†þOíÚ5póŒMp”õÖQ*ø?  ¢”±v%pÀq`Vø+P8àî¨ë%‘"zíH§Ð)šÖã˜Jåê¶áÕ–Ì•AÅìù| h—Ý((êÙµ'µàŠhÁ Øûk’H¦tj*…lB’PÀJ’|PœÊVˆ Ó'¤Ð;û p;i·(X%ÂЬ4RHâ^ÿ€MZ¡¦†=ñ>à˜ ^™_rÒ ãL|ZngÿUzk×:¨èwCÆ¿­0Ö^ü\ÁPì9†¿ÌnUm‡tÝžƒu]HøvŸ¨mØ»vÁô&³)f¿³V&„& "÷(A(œ/†«>TBÈxõ‹[L>/Ö$"V!3Ž™$á°N+Ãk•™<0 ÃÇ€ð6 “uÿ¦)]ŒÈpÙìæ‚¾ÊzH$Pª–ûQÌ_sö;x B˜§5ˆiF`Hd>±'Öºq‚Šá§ £º_éÿfÃÃ0Ôà™îîõ LÀ¼–,c„¤ëMÁˆ)‰ÝÞ³™ãä â½ù¦Sf&#w*‰ù››2ñ•­!_pJ½!Àˆà©%Ö$⋞p;YÕ*ËŒ4(k<÷Un–ÍÄ1>»éP+A¤;û1ƒÒA*ï ’픽»R74Öf!XÄ`ž‰V ^çãùdŒ³ôŠp%°U'¸e ¹ðâ¶.N2Ä˲0ĮΛ÷·óHvbY¨L,¯¨O>~‡<˼ç Ô«êð* >©Ô¹d÷~/lÔÅgQQ4~lµ]Vú˜58H½Eæ™Ì&Hh×~Ÿßö²1+mîû« ¡±£©ÃNGGJò}/Ž05é"á!]À÷|Xž¼o«ªü5- J}wÐ?×Aæšš°2Œløâ`FŸsë•2úð È|æ`Ifßê¬i8ß0.ÿß`€(ÉÆg»œ0Ž'ˆÏôõ’úƒ´åA©ÏŽ×þÆ …,`‘‘‘Çß½òg†s(Í{© ½À×ü®€%<ùü²OÉ|Ÿ> tüü?5Áõð®òà.à­ºïýÎê…T<,L§Á¿JÞ8,ÕbØ3ê~θÚ+!Åi±xK¹ðÊdsÃÜÈ71¥[„Û‡„QQoZï±äˆŠðÝ?„#Ö·$¢ÃìŒ8:—)Éë~1zÑ]°È€u§—YW+¿‘jè)=^Ôc| %Ú—¶ôAQM¹Û·oÇr“î²F5‡cÂ9çÆŒŸ€××€üåì@WÝYOïÌÚ»Ù†˜htÔ„=ÔxÊên’uêäßêÅ\„ì^™F%õ›qÉ, É쟎ü%Q;N†½Xç!7+c²³£6äÿªëÝ#²ÉMô³`w”‡Vê šXÊöqô{òýs½ï«UËÑ0Å[ø‚$]FQ_OgŒûè41¤Økü÷ûûÁîûŠöZJß‘iÉÿÎd`m§?×&mŠ*ñ²ËL·ü«°›‡ýêØ{¯xÜ}ÃÞ5ƒÞaáŸ8Ô•ÏÞã:ú¾ßð?ÁCÍýÅ)F'¿ù^àÙn Úd´ü2Öâ¿£…­a•¿²{õpN!„4@öUWýÁ¦=Ë7„k^üíÇâ˜àom Åî‹Y:UŸ¿ØEð°'Ûn •†"¢:eR†,(Ù=W¦5¬´„ÞÊÝï{Þß9A@NÿnŸ•‡áŽ@Ó…SjÓ¢{§Œˆø‡Üù"dÛ+±‹@=«Gf¯§Ø?-.p@*®¾ßÿ¥ ?/C'·ø Œƒ‡^ãùEÌÜŽƒ¢ðoÆ¿+±Ì@ƒ/ê[”ÿ}€Ž‡®ïíÿÿÉp7ÝŸîÆ¾ý øð³n2Ê™M‡¥²¦«‡vÓ°}êûïZÿÖ°à§›ÿþ¿ÿ\É̤?ÏK÷@Dðõ3)ó ø âP×®ËÐ/þ2¨=üt¸/ ³8Î$‹Š iÑ›¸}¿ÕãQÀøØ,&|fP òÇÊ, ¢£¸Þ[àK¯x2>%{EêEÎÚ»ÿå‹a|\„¦¹¢P/ãx~/ÝÅ  5£eyáp”àÁb—Wœ6©­ˆËÿá ’® &îK‚¯Gtƒ ­áŒì¡FFU A¤¡Aò“”Ól+´H¼ hˆ}R½\z0DeEÀ‹˜]#K%ï@úóýGNÆÚ Ô¯Â̇V%uƒåÓ:ÒΡpŸ9”ö̸Ž!¢5 Ï€ã›ÁŽAb°34Äüèx†• ÄLÈñl2"0õ´$†tÕtøQpÎü?1=j@Âeý”Àö&WŸ8MŒqE^°ÁˆÔ´öqÒg-2†-®tí9­Æñ \b²¨49H¼‡îÿ D‘iÝŸ¤.\êZ–%£ ôÁ~T ¨ý m†‰‡iQ•ì ýÆ7†^?G CdÞlÚC鮺è½5cƒÆcº‡p`AÝ|tñ w~•%¡¤*oANÂWCՀ݉­Á$Œ‰²Ÿ…ÜŽµ#WC“ƒ!äG:ðV–/Õæ m0¸þ @=RUÞ¹x›,7Ë #.ô½¾ë)ÂæXÉX÷Á?ìø½.v!¼]ädn÷$Gà—ww~-ž„»ÞU’™·­ ( Cª[veÖ}µtŠÈ8 UfY'þØ-ÇQ ”†ºDÆ>ø›ÂèUYšb»ŽÄZ¼þ̑܅ý&íuh*x.Tœ+¸­/«Z;Ç7Ûna%1Q83”s˸òëøÔ¸>Ë|—¶ajÆ UÛoì Ô¢œåE¾D¦ú0À@I=Kò/Ýwù²îм¡dðF`dŽ4)4N”ðÀ˜Ô6Z”bñ˜½Å¹ØM@ 0l£Úbnú»Â´Êi z˜šª‰Sê_ó{K†°÷ÅŒ:#/»:üÁ‚ ™Ž¢j“o[¹»_Á¨/ se°§·z°G6¢²æS&]ŸÇ¨†‚½²xv¿IØãC H"î3p@XÌW .GÍûëd¡í¿ÛÿxÎÃ)ý$úíFÓ@} k!ü6×í1š4)/³±Ølâ\YÝcŠ]AáK&Ó}Éþà›0 OUìß9óòØ1ÛУ¥Ið1kÓökÿ‚«…Úã<ÇÈ¿¬{3ÇÕ4<Ö¯èPè­]øâ{ Í#?ûRÎýþ«Úhƒ 6@…+´ÍÅ,‘Ù;O™Í~Œ6õŒººƒ;Ci+ùD t×f¢ÑàÂÇ ‹ ÚJG‡¬Ÿ~ÆKÑ(õbêy»»g€¶ãÖEd4p¡‡­Éÿð/„c£‹ îrÖfdÁºd@j ´sÌfD ¡1™?fè"à5IšŒh‰Œƒ–ào s7áІ&áÁ*cXd‡)ñäÿÙ…6Bî˜S=¬ÆŒ66É‚já €“è`³$¿ƒÐC•‰ÂËâ¨tÚ—W‰ªµ•àh¢,ÉÿJ20Üp[q4@Nyy:¿>X¤óí¢°î*G˜×a¬‰—#œ£»§~%OsÛ'âGš&4š°­;ÎvÜÞƒÖò•…N3ˆTš1¬ÓÏ+þQ7þ²—åüˆä}Z2Ê}õk½.¿¥áäÓ %Úû:8Ù8c [ø%þ1sp»ƒ[ªtRU¢êÖ;½ÿ‚4“¸ˆ#^;óSx¿FŠF¾èÿk”h–p%jCüvž.äЬc¿çøÞDê¸ëw pdì0‘üÃÅ@§©ë¼1¨D¦C'/‚«¼ûpÃICþÿüj ¢÷è SõQPN¿còבpÛ—ø7}ìQðRãV7뛢´]S©ä~òB.]þj·tÈ@%€ÿí!™¼'ìbÕ%žŽàŸVƒG.ç^7Fí:ƒ@õ–ËrŸÉÄ¿øýçè#:»Žòªaø9kl}t~D mBàKÊ8€t¨›ÙûþE:<ð‹qÿƒmÐ8Ð"G`‰qþv«•5®€Ò^¿ýíþ&6]ëæw*îùxÅð–ÝìC`’°S×q$ö[’AˆŠG̹ÊcVÓÒ1¯§ƒ÷§Ê¢¼mRãB_UaLl#ËŒ'ªÔ̘4>}/ŒÕ“hrX€GRЊ°x î+Ó*A_Wx%~‡’.`@®äsÝKçA¿±hú(6%ú¡üH4 BpÅ Œu2Eè­³æÐƒÝázw‚ÙÃåãÂ}œ†¾øûWpi*.Cƒ/eX¦-ñûŸmc,3©ñ(¯Þ—]¡Ã†QqX¥” mðGÃÔ<¾'\ºhGQzšÃ@Efë°ÄË¡²1¡¶`Š”UÈÿé‚‹b\˜¼ŠH£Y=wÅÓ¤¬Ôƒ™CÔÖ˜¬±—Œ»”ùpFßÄ?3Ó¾ ïMä]ãS†ü üw?þ!Ú¨,ÑâàY›wâÃ<*JÆÇ„Ã'ºWÝíšO&5‹Ír$•/.+òM¤- ¦›*ŽÝe»Mµ¦[mžª€îKyç Æ£(`í¦-¯ñÉàO ì… ¸áôh†þ5Ì/Ç´Ë{jß^Ïô8=aeOs6TØXg0Ú7`XTU^o¿¾D¹<Ÿ ¡@"ÀB¨Bï—†…h¨‡’åð'¹]ש«ÂcÅN•bðϧ¿)B•UUÔSgWïWÅž'ËñÖ!kŒØ»f#I;7ÿÆiw}îþêŽæ_Ð˵]zÆ+ÃT&6`§ïû3øçðGëÙ‡íðc_ÕUVO`} >  D º€ðÞl?âÛT¡õH=‚0`$c«A¿?‚èl_ì #N-Ëp¥nÂâAHª>·ç¤iC@#_1–Ó“À˜@”:@+Àë”>5^¥Ñb*ŒaÖ•Ï€æ5˜sê¥&½Ê€^©¥×sØCÃÇÓP½ü¶¸M_ÒñxöW¦ûiÑ}=R?_“ß|"°ü «2(õ(ÙSãÄI=KÔspÓ,‹³>Œ6¦‚nñüµâQÊÊm|ßÙÕ{£ÒH*;)ð@‰b›EÚ±õåƒgõ(¼ŸàúEÝr.u J 6~úÒ`ÿÁèø/c‰Š%GôIt ‚ªƒÖ¤²¬«á„xò^S èpï‰LWb;øš¡ÁÊH–½â㳇¤‹6u—‰ ï¦w¯|o`A´]äS¹BêÄá@œýP—ááZÖg{á”×’& ÉV@^«°0r=Dz*‹658B‰ï>ÇhÀ!Õ°ãC^$ ¿¢^%±ÁA@!€y2áóõ¨ ”9>^¨WlËÀÐñè"rlmŒ&oRÝŠXö*Iãhmˆu1mc·É0“¯ÁL¦ˆ¢g©8…b{PöÐB;0„¬O`€•J`eh'pjÆå^•G„”C(€ñz–¯,Ì ' #‹§'ÖÖ&¢I‹‡À ™R{„*EÚ´ÏÁSÍÅI–ö¯i41ú³Ï¹EâAúïpBÄ}8)=sÿl¢Yþ1È– Ä6 ü`!ë„$ý¥¡è ̃-é¨:Øpˆà‰Ew|ÑhŽ"9Ž£¹±/dµDôEâ¯L¦}ÿé[Ø€n–_…L/ëÔòéÈT.D_Jp—ð8ÕV¦É• Ý0U†¼l_5ì#©„©C¡`9“zÏS‡—ÃuŠíùTcÃù<6 B²°+oÅìa`¬àéa³‰8šuü—òxº29`³W¾8—ÀÆCS^8ÝØM÷ðê`m2 Fëm½9þGOí·e…B`†µ¸°n<*<%ªëy¢\ú£Rwªê baqµ3kþk* ÷âÓó²Éài È@€ü ¾~o›H‘lÓ Â8Œu 5åÁq?‰ã\ßV©œÂ$ï#súêDŸ2,‹ˆ,÷­ÍÐg@Àºø1»Ðf …œÒ—/ʦD¿ðq<*úSÏT¾Ãh{С5I>çÀ±–£týžƒHÄÛ78ˆ”„¬5p°Ó”ªŸÝ÷{2x|Àmø60V ˆår í4•±3-¢×cV!¾ûbð+L:È œß¨€š¬iN bàøò ư(<¢! ÀJ‰†)ØéÃÀñ äÆùµ¼ÝBãD‰Îƒ0s8±]ÈÀÄzQññ8z&—’¤¬câ`OE‡–;Ë×^#ƒ¨lìž(OÆÒ _H¶:h_.Ðùö“ÄQXÝÝŒ¬T¤np@´W¹$–yí¥xÈ9ù$˶K!oåét ˆIÁ€Ö²L¢l¹F:.«Õ¸ó㌞ÈB=àEo°AçUµ'÷¨âƒ@@R¬£%³kXïBG ÇPЀò ü Ö¡+ ¾ÐÐÛýûc2|ø46kÊþwZW_Ån*~ß]Ö–†#`^mãývæøa5]1¾ù±«v¬Ê{müÜ6'2Ÿf©ÆHç>¡ý.OòÜ"ÆÄ‹ÁÆ#è‹=LÁU;ã}—të½­­ü‡JïÑTØ`I»AÍv*ØJû­Æ[G ɯÅc€Ž€ÕÀiçœx]¹PS(6a“¼ÅŠO{T]!YÃG˜´ƒ[k!D»×^ǯcq#T¸¦×ÀtI'2¤L§Û=ÆT™ÂÕ$Ìp( ÈIú„Ä¢¾öÆF ”)иÂL;ë¢oÂ/[üßC¾Ôãÿ¬§ç[ÛÕ{ûþMëôëGV"Tªö  GèDˆeÐ&ÂÙë êaa[&d~ f<¼¹R‹C&"Çç¹±ˆY°dý?ÁáÊGÚ\žNÞ X͆6(ÞIÈRò‘Á Æ7 Ì©vDð—z²ˆ x¤3¦w jăŸEk0€Ìÿ À1Ý'ùÖ,ÉWd±ç+ Ã䲸õË„¿Âê—É»„>u1w¿ø[£:³\%å.S±Oÿÿí¹+ê5‘ycëíÊ<ì!ÕÚß4{Ò)óéÂ)!½Ð˜z•g©%{1]ŒîûýéC°7ƒN¶›gÑÆÿp›ó¯íŸ°Gø]&ð?ÖJ¡ºM‡L­œÅŸ$3éùá¥ÎÁøAnZÞû±~ø¯Örú²^Àù`±• oñ€èÇå§ÝÕþÀÍþEßþ9„O®u©Œ µ “†h”ô^ Z õ"B ŽxUŒ|Hh¾ÙTb2ãº÷Y9FÀ®†W‘LÔÍò&Çܯ@ÔðMzБ®=1ñ}`ÿþ]ŒdHP?Õ~ÿ«Á-a!Žªo+è³ûÉ>WuwF‹©­ Övø¯†T SGé4°Hè†09ØóH"‡åMë°&™ðk‚"5q‡èž¿ð[:7j„èлä½6U¸î,Q£ÏÄÒ)?ÚÂs¯Øì8ý?äϳ{u­ ñÇG*D¸‘ìC)Œ’gá´–=Íb¦4%ÒäÊ…i¸Û±8´Í£Ib%_yï­êµþ˜»e‡¦X•¢P^] xKö·VI>žêͽü7W`N!]5ÿ.fG5¦*›dQxø‰äþÒý)óÔ¢.yiü÷^'ÿŸŽÅÙþ¢Ë{½ñ¶ŒQ¦Å¨ö««^Zׯ+ŤîaÊØ› °§#®"°Ð…Òß»ƒ3³bÆ +‘˜.´ <Æn4Ù‹Ë ýF1ÿ^Š M8S#>º°ãJ8X%È›(½ñù_óÊ~u6r/ætÂâª*Ç@R‹·ajÛ<Ÿ‚­f<ýœÈþf^/À¼²2ÕƒKRÐFÎîV2ÌRO¨¬NÇÇ‹Ëá°zQö¦µáIßò€iŽo—ß5]z‹¯ˆìØq¸õAˆÒži:ô™jÈѨ8 ×"PIç4+½-‰˜i¯aé–-´É{#v8ÂÊÔçýóî :ªÆZ¼Vv"þ ™¯2 xý Ó M¹üir·ZW—õ|ƆÃfà‘6FáÜK,ë¤I7Pô@ßÁµÒ‰uƒ`ìÁ·¯Üg¿ÑOmýAüîÌÌEè™ýbqIv,XºíŒdZÊM·î;ã¶áÀ„݇gîû6kV¾nÍiA§`T°H-²®¨j‘ì† ÕU¹Y/­ÃYkÆK¤l¼¶Ä?AÙsÖŒ º›Gj¶³RÄ<¿.OÌñá¥Dš3â?÷râ+¹™3ͧÒ#½:io¥{»÷_E¾OWdQDàÙÁíåþ¨’ Çó–^ö†ÕËx"¹ÿè?¿ô¸L´ óC?ðIjŒùÙ>´±U$áÆ."nƒzOWIÆsµ%FKÌuöŒ\˜ÈD³öü"y›å„ù_‡hVž$Ý'â“úÚtv?fÉö¨]€ÇaíªoßóbKíW­e¿êÛ¾—Á ϺßPÔm7jÄw±ønÜ2>:¿#}J—Ðè&ün~›äeäõ§¡Ï¢ÑÑr¬x0¼ùöÊøÆ_ÎÞ£¦†<ñÓ&(þ HQðŒŠÈD¢ Ÿz‚Ë¥A¦ÌûÃmIýx‹PY1¨ê Ai«l!ißÎsDœ/7£nà{×ÉûéÒ%-`QÎOÅØSu©Ã“ÚVÓYÇE¬ EËB´ ÚUÀ1E‚–Ð@Æ[‰êW‡ù ée¨6]Íœ®“©o1³h<¥P=^.¤ŽqAÔè€ôðîGGJ›€Ô®‘\,°üfw(vê—¬3Á½ß@cHlt;ÐÛsေjip*š·øî:Ö…„ÃkÛ6¸­)}OYïÈ »o[@.ž7ˆi–Syñl nY°ÆG\·Šëkßô‚³>‰õàÖ¼ìÍÄÖ2/ÿéîPÙmïæD¿¨CWUb!èÞ»Çç¬Ïýq;#cÂéó¨¯ ð➸0,l'¯$^£ô¼¡¡vAâzï'ªî*À‘:°%]j&u\ËÆTÙU²Œ”J`há׌a`€‡}‘\! W¯uI Q±«•m@Ö£Âܹ]©¥Â M2¨ALRÔÆd- ‰!æÖ/ÏJ“h'BqÑí*a a™,1Î^`Át7žïqIÌœ!:§xu1åa¸ºÉzp"aÊìQ4›Çc›ÂŽô:–« Ø?ãNïêúþY•gUHVñþ†îÚ…w‹¶¨AõF ±ÎlþdÏ8ÍÔ8úì(7%Î$_ŠÅbœ=5Ðbëå%GÓðH»dÖ'iØ ÿß‘­¬¨·zÿÞv %x ??ÍïãxÔ†õãM@^ר¤fÐbâÕ¾OÀëN@G 0íIr9‰1ðáO›¬ó‡œìÔ† L+©°Ïâ‚S(L$,[ƪLͶf* ¨Òj²\("÷4Ш± ’Ú¡Áø?‹Ä¿Y5J[!çbßù‡ï(Þ2màÓä=a(ìcrÃü>e±ªÄðçàœ6]—<ÔàF8W1a—‡Îœ[ÛoL[“ÃìžH¨ð‰p7çð;^õ¿J£¸=ÍýÏöšÄ>Sߪ SÓ·^ÜŠ1@|K×óú†ðp6Ûƒ¼©Ä»ú8˜àÕ\jd:ÉÆ¿“ÃÁa@#°pŠ–lK¼ù¤ÿyÆÅow¯½^'†Â©ÓOæT3SW D2d!väЙžþ%^žóЬ™$qö'hiòˆê¾=Ó²p*S™<>`€DP`@Xo)Q°f÷4PW‘ä+ÿÛ ;ËîÙJ¹ó ÑP6жçùÏA—›…4ÀxÍh`/äð¸T[ `>ðçá£pŸ,nà"/Åå ü³:œ¡˜GåAGp~c”@4Œà«S"¥»å[ê¾J¦î3Hä^¶d÷ÝÚ48 ð4©Ñô ‡à£66yñXZ§ã+!°Ÿ.V.¸Ó„HñÒÍÍÇLŒ]Å Š¡ø#9¯uãN5aåÔõ×nÿ¾ºÌ¾þ«§¥oüno°à»§²ì–˜e¤,ÄÇ~†,??^ÖïÒ/l™SØ%´Â”»ßíÐ…Ç–Q´Œ­ÊsGýCßé K4³ñäWXᙡo´-!p ·³­ï5Ø7j’±ž¡Ñ}ÙžG@€‡Y.›PF$~'Y½f+â”´9ì0…¥™×Ò+yðrq.—ÚÚð=ò Þ¬ßñò{'¸*zÊÐÞÝÂÆUˆÃ/Ù?öÂ’FaµöíÈ"wèÌ©=ÛG –ˆ3Ç l÷ªß˜¨žnø+°aúlbðc¢Ñƒ¼z@€>êÝóÓ­˜A(³ŒéBÀnËÌõÏ$Ê@*ߤ›\hiäìáÃŧA(©0mú£þð"ž w÷^Æj9 U»DA}c”œ@l¶!ïV¸ ¸sëÅòÌïÚ*udCcùp )Á ½×ÊOç@}hE! íL¬܈¤.J¥pìÐ¥\,(µgŒÚ’„½ éü«ÙÄÝÝÛÌìÌøˆÞ'µ‡œ øé_q‹ÀÖ“ïó]¸°UØS:@ø7ØvÂ]$XrUÛ+Z>ÂdS•Kã¼¢@…y”[?i8,}†™fæ$ßÇË{§œÂã!jk•‡‘tzþ•D<Òèe0ì @xÅ4ƒñ„+,G%Z4ÉyÓp®‹Ðg¢Œ\×\ÐTm¤ 4qz[T €nÅßkíÁ!¥¸€°ä»Lœ-Å'¼ÊyB»ÎQ ?QÑ»ïÿÂ`9ƒ eîø_>›8%Í€^yÃy'낃÷ý¥‹ÆÃÆÍ‰ëLx#å¢N æ”c9•}í›—|%r®›’ o^€à¼9N„Y¯;ÿÄ\¡ƒ|êDpžÇ?œ00~¿‡£²ò¦Äæós)š r?뛄\?ßñs&>$¶™Øç^ë Ëò°V+¦¦‚|„ÿßâ²)GwB$|;"Vè`ü4uâ"§ïºŒ ÿÅÆÍî©Y刨É{WðœdmµN„8%­’“÷¼v¶Ó ÉúOÑd@Vz¹){ƒNXB¢xD½‚”-2©îÔu‰ƒå…xM!ÝM°#.‰eÙ?^ÇØòÀ¶vÞŸ@†…Å .á$Á©i¶ÇáfšHÁþZ2ÄF:nes¿þ&ßïE()ŽRÅ6’Ïà†ˆ“Œ63êNîñPF#¶w¡ÿƒ w­j6]ýê 0HZ¹i-‘Þø±Úˆ1~mT^b·þ%§]VëÝ÷Éü ?¬ 0K±6 4ìÒñ%t³uZ^âÜ£©ˆƒ9?6÷ÍcÕ’÷Ç–eM9çìäþçYöÍ$8¼%ÆFø† 2q±õLÂ06Ƹ³yj˜× —¼Ás.2Œ|Íógs¨ŠÿôqŲ äŽ –‡ Á·wï.r\×(º¢¸Ê7‚Ó»Yde¡› þæ£áç„JI®ÛiCSc¯rm1ãâ\Ïš¶Û†™@v¹âÛ_õôwcÅB@…-xjlåTm÷ó)ÿÐ,V´¤4þ-ö ‚:¯Î)!PØÑá·»ô¤ÌÿØl"D~•[a“¸a²Ë?ÙúmòϤßì.PnŽ‘jõù[hƒù/¾‘á)2,AGËCêk$v™tÔ¼ÌR¿2iHlW •„™Èã…˜ª +m° ¶DÆN2Ÿpá`<‹íßò|¢l  ©x¬‘©dq¤ cïI|J ÍË’b>Faþq›µœg$\ðù”u^išŽØG®eäbÀü-5N(—„‹Ž`† sü#íØ¶Àêˆ+õtKà®:HºÛ}¶ÓÌ¥kuyH­³dd×êìa݃ªŸX­Â¸?Ž›%DTÜ.ÿíAe§DQE™§³OÕkT«yÎ>ÓCøÿÀ¨;“H:±ÝHâSǶ àáa磵hßùÎàìxSjLâ=ÃGádªG àjÃ’m˜,É[ÎÐòàØƒÃHãÇv‚"‰ ã0}G¨ìªØO(T²V‹±ke»WÒ§òFã}õ SK¦>±¼Ì\âz ÙK½Òk6#Ó)¿Ç=‹óf¡½ÞÜF9ÐFù¿ŸüêýDï0º¯Š#p‹™ákЋ(ä"Y£v»Œ‡ï­µF–Ò& ô° }7v6݈'wƒ¥$O =À–;-°òXÏ «b;¶0†Å $^!Æ5ãÒ£¸uDADÔ¼h4Õã c_™ç?¨Ûä`+íÀ€sŠË;8Pþ –Éý}Ej¦]+ ‰3¾‚” ·hGSÙGy”ÅÏÝÏëŒæÇûËç·T@ªë`&è½Àt“À×î÷º ´Å9V§ŠÅrxL=„P@¡FiÝŽ§y³|ÔÝu«¡ô~jTW35ųÏúcãý¶GU^d,x¬# Å_ÌHS£¥Y*¡&àŧªnŠÓ!ÔÌŽ‹ûÁÔn÷ZSuœê†[‡ý†ŒE–F ã€ðx4L %ûÁEš*Z `ÛJXî#,>¼Ó©(HªÒ¼1BÁHÚ ‹®EMpï¼½C ÝY³Áîôœü¶ ª×ߟñõ¢F ‰Ÿ†Oý<FYW)„°³HHÒyÚ‹ïýðƒ,ºL%cħ¦oS÷jPØøÙ\ÙT u@˜ü^K˶„N¤¸²rÞe›' nèΧ¤‰Ô"ÿÝLd'íyX4sç®U„ª5ÁÑ n¼uB+®!½;±K-rI§ûÁMˆêYR)2ò•É‚øþßb»ñà€€\d3*;ñx1Ó#B´ó:žü/yX¹\ú«ÿ¡šk‡Ú ´µ†9’¦…ƒåΧl#nEÞÑw^l€ÁÁƒ²Òzéù?]ÂiÓ½´50`*š[XgÉø“õð”Ð ¤çFpÚrJ£¦t{™·aU̵C ÄÿÓ;VXÂþz¹ìBtúg¨Bÿ= þw»ñ‘[þà…‰61$Œ¦†)z"úýöC‹‹ªÄ¨³N“àò˜ˆ;ĨÈiÌßõ˜û¿ïÝï´à@5Z3Gã¦,ûË0ëGški†i”}P@ÞèV.®Qÿ{€8‘ÀaÔ–Ø VÅ…òÏqõлàú`—×ñE¹320ð¥C"~ØÆÑd.ï÷î÷ÖÇ0ØàYwÅÕ@gR,â»Á“Áøv0pаÀêÕ§>X¬6‘ÀEà.wŒkxÜ'è‹9jN¯ÿ®5LVñá3“‡®/ô,}=K“Ú˜[âú¬ÜWùœÐC€)øªéןUÉðî±é[f‰%¥{²wÒqç<œ|±9/O×5´óåËl"ѲÆx8%ÂAAYF(‹àfj sܤ³Á  M€h[„rƒWŸ¡É•¿dV?štyT›b€Á¼|\ñ7`ûÁþðÀ_ÓXkók»:%`”Ä“a†ú}Nü6ë7¬¶ÜÏ\EkUi*r3ó,v³èAÆ‚zÜul]Ät45-üË|ÅHj½LÜ=à0,Nc°ÖÆ—9)ü-ýP f¶ø8 ’] y±®ÎLKi‰E¡¬ß2¸þl4%Egõ¼÷@™½ëüòRh¯S’*­÷Ï—òÙ ø³.ƒLÍûàÞGÛàf*:¨´õòjC=±)èÁK8!”dáoÑÓ=þe‡óiõ@*ÐèóŽ z~¾ËŸÀMŒOèKÚ¶ü—ŸÐâX–iCœÞ‚êȈxÔ‹l¼ÃŽÆ0½”(òÔXÏ‹›8LVYŠ:Yˆ¼ìžYéÁ€±Òª¨7Ó¨îOn0P ¼¾fG“+ZXt{m°¤:`íþÏSƒƒ‹ SswWuôíìhdÉ“ˆâv…Éû ž‹¸­ê©iRߨS¦À×@[€«\ßÍ!¬ÿï©Î•ç'»i& ©P¸M³·¨µŸÐÂêM}±Ót"'jC³/‚¶ÊæýV´k»ø!c¶† Ú?º—@‡Äƒ¼WÅÛ+ ´0TØ¿±v1Õí Q½™acèˆ b‹sL( _ž‡ªæP0gþ¯$d J( é ¯ÐD‘/Fú޽ ZËýnv‘ç½RvŠDæÏ©Tøö„¯œí%¾{?:EiË`\Ëe…D™Á<ÅIOçdö©l±i{—jÒ"П¡¾[§,—ãíëN¡*X+Ø~ºWúð„ušef>ÛMÚ¾ÈDA´ô}5~¯¥^2&J;¨8­îj‘†EžÊ‡ð¸´?MP”qÜFqšm”¸ƒP_llá8ðѦîèJ ¡§¥¡×ðþT1¾&íЩ`Õ¨>8bpÇ›Dþ*†¬â“ËÊúefÕÝbAÉšJÐü-Y %âÚ!¨–5Žö^ºŽ3õÂð;}§Å䃤ýâÇ~dÉg¬7zyëƒeÊá»ÄqT(úkC‚¡”6‚°O&÷ñ[ƒååQ+ßÀœÌTVèf êg±¢_÷£ [Ò"ÉÄ"ã@Qñ0£‚¾G¥L´^Îöê[ð•¶„«M•¾iX¿ÂQñÏÙqÃÿ Qò@è¨C'±£øO ;Ì%2Dÿ mÇÎ$ ¶È稔ÌáIl$ CèOÅ Ú´g’x¢ñá^‡*Л(` ùïR´JB¦Û4pé#ã𣎜ˇ¶´0>Ór›! $H´g =¯ék}JÆÃm`£†•ÇRH¹?îr€†<=óPq7ñøŒæˆIØj–û‚‰C"îLq1³§ÿs´j¢_…w½Ýë°Ù)P‘ÿ ^Á¤ã©–%CAP ²ZèÎ=Ä ÌªÆ Ø61DëÓ«%¤¿¦‰ ïUQwh_ÿekZ¤›‹ûN«Ý@A‚>«/‡óC¥50²§[§Ö`OI¦!f"ïÖ®& ƒ~!a()#`Ðê"‡ €å¸ T-ð´Hr¥Å¿·†/º ˜€½R2Q(h'ÿ˜;úäP¿ÿ†¬S~ QR¸Þ£~þŠó…†ž%ò÷0#1'÷òÄi³$›ŠÃîO©AÎl G`Œ‡d– Uì*Õ 0ý@4­¸è ¹i"Ù‹žó»ûû™Ãå Ù³nÕFåQÅ19 Ä%6Y3S€%ä=Ÿ AhF `™fÁX;ᵘG ´U]t­“¾ª¸ñ¥ÐV÷±ì^DÁ&±Ì `¬¤þk@ùý½ A(/ÀLå|u„(5÷‡}ßù–kZ¢O[ÔKÿ‰'´'àǹ?Öú¡Ûß‚0PÃðj—a "ùjØJ„•ÁN®¥†x&\£Èìø“ÂcÁˆL,‰@9 7ćÏ32.Ü¥x NŸÞ®~ª©Iu P¥t0eU¦žˆ+U  aøó$HöèÝÍ´ÿúÞ‰ù)Ø*Â…{÷¨µY~-ˆ¦ú´«„kæâxï…ë0ëRx­Ô”í@ÁÐ÷F$ò8, Át¹’²¼qÝš²Í¢­O˜Ä§1[\ÞKjÖ“D̸)’7™7q‹ãôåÿž1¤÷o^/Ýæ þYàƒú›ü=?DˆX ¡º>[ƒÙªÑ±çœ=qÒÂÖ*! tOhü˜×Îû/› ©æõåQ¿fô[a |h¯d³Æø]ÖÀgzs ÝL'þ;‡åA˜èºb=ËÿÂl\ ÂE¾Ïñ¡ƒx‚`kɆ¢PÕqò™b"X(qXðº-Ô/6°%פ$Ø\d„4öÆõ¹ìW˜ ¹„ pãcž jˆ‹ßäýö´ø‹Î¦S#×XeÞÓ´0$öŸ;N¾’‚ÞÇäûáþïtÞPÀf¯YŸ 푱‰VǺsé•§Î5È”¸3™¾ñHèßn²üá*t¼˜í¯J«0˜àhFüè‘„8‹ÝK5H°t½›ðøœaU¯6't ƒ!¯tªšðN/å<€¨–5ª(‰Õ Ã[Z0¿dB«{mŸ^? Z½~[K㔓¯Ùˆ4 ÀúÑYÕå½IxÊUo´yó+¿*`­ë¢ZA èp‘œVÛîæ «‡’¯D깈‰5䨺è1¨æºßú¨#M*µ›å'ßÀn—Ÿ7—"ÀDÿØ@’b½¯.yUÄ‚ | ˆ^G(׈ëxLOiGe9b&0ä 6ж¿"Ù+#ê‚>­|Q?¿–ÞÇg¸Ø[µxYn¼Íù¤_Q‰^c±gúwÞGøÄxí .2U¾ýïCBó÷á˜Ü#á>ʤ½ù_ €ÞÌ'hº÷Té˜cË©V÷¶\pÆ^¡ü˜F Û¼ôºN8ä¡@|£Óˆ Ô±ž;Ðeè,êªFy¨vÐX`VI÷c„19n+H2&ÂÆXÂS CMȼnž Ö»_YÂô4ƒ˜ 2?ÁJ–,Qm•”r–-¥A§É¬[q§Ò„ ßá#èT#ÿo‹ ÙŒ rœG ÿÑ?¶” ÆÉQnFj"ù#+¿á* Ó騂zú‚¹Ti™"7F޹—¼‰=§.?±ø~fÖÜ ÷Ç|s»6ÒG; ú>K$áØo­®äîrùN(iϨ@KçùBñ*KÛÉD—¬´ˆÄ±¢€Ø'þs‘ F>žž\ÂÊ ¨O'ä~+M,˜sî ¼OE¼zKA¬~Gï4?ÝA°‘LPp›N€J^ÿØ_’³4’Ç`Òp£ŠEÓ !ëú‹PPRJÚ®«%°ëë±á ¹!EWȽ]÷¾ žÓ! ‚²bá©u6ê6²‰›”¬Jcãjš‰à÷œ,xä›R»2²AP B63c’{ü)9Á•F F<;-Xôc’jˆ’rlÞÿ”1IÒr„”ßü'O+ç“ ‘40✰Tøû;â Â%¼Ðþ¨{gCÖv<´C»”m'Ák#YØ_Fåâ h„`R`P 5¶áúÃH2”=±] P`Å÷÷³…A0*ËŠ3Û¨½MO‚X¼…ìL‰f{Q„éC-Ú7^9¢üáÜ8Ÿ¡þÝï¬QBäßõ¥þ+0uGæ­³y4Ç^*U*bQí6¤QFAtÞ²œS2@~”Àæ­íú`ëÌò²© þÅ ˜(Ge”œðµvFwTM™žuºªùO>ìâ)_òb½Lùdñ!F E‡ŠÆ˜Æ(ô˜f¥Ýa1|ÊyhlJHe1#"XW6 i€K²Œú½1Ž*LjŽO·ðB†!.“Þ1—2˜¨UjÌ=Ká-.j+vµ ÑliäŽû""¤ö³<Èm9m¶™ûLa\¿ãÔàFÑÊï7HN¢íNaY‚$F©áÆ»]é(·i>(¤œl(íþ\~<Ýws2MI ja~“uRv–ƸJÑÑšÖyæð²Ãø?eFçánÚÛš"‹fpQ¾Ÿ 6é8V¾¨}¥ E¡¥XÁø~äî% Àjóœ£HÄJ€PãÒ+oRh.þc–¦¶‰/\Ù‘<ÊSwf‰aÂ[]7?¡£Â/8_†mY@b “ª0b:xRŽí8n3èpxEÌÍ3ˆŠEƼ¤PK'áÏMðñãâ•}4ÿ…BMÓl§Éº¸»@“©±ø*ú¨†NlB0{2 ¸‰àhbrs‘ Ø»Y¢¼Ÿ=Ÿ²þ1M·¾ûS«MÀ‘+ÙôI¶uŸb°O9“üA†jΰ'†* (+[÷‚(ÿÝêà\FXZÌ»½7ö;ï¸ã@«üÃ]t¶ùDH>·ù‰¼­G;`ʸ’Üýkîàe!’*mÕd¦óW¸þªSBíæL@ÃÃN3Á"åЮ“äÿÂ$Œ×»߈‹§Éãž“ÚQÅÆ6£¦fuëFè€çøƒS3Úâü_ªéx%™s©n2Þz)ï–Úºê6:'5"¨N"eÉ=hÊØ==)¯ û_qÛ¶P–¤‚1ÁóŒ á­>åÀl ?„ýÌäP8¥¢ ¯ÿÒðü?O:4C©—†•S j†O–_ó¿átàn»·ùmëgçò˜´´Ã߀ídÉ|Ú¬ÇÌsˆý%w’|˜Ig‚WuZÔÑÔ7o'ëõH|ê Ê iå#jW².[¯wþ~”4ïÔ!ØÉà¬ÕCl̇’ðþÌÕÜúó® À“瀞'aÆnZ‚>"|9mŒ×rk7ü¯£X4ïõÉ‹X¼u8áÿyXA'ý¼R:•bA"¬%˜7„D 66W€CÀôéArøæ\ Å?êÒ¶ ®KV“\$…¶Hq¡ô^¡FƇ; k,½uÞãccÇè’Í>8È 4pòä#TyÕŠH§êfØÈ‘Ñ·¦â*hTÓÅ×8Æ’žü”yrOOóxfž®*`ØáņáD' §à¢ˆüX¬á–á=¢†£•[é”kþY¦?°HñoZ0M±t0`A œ202<³Ã‰Äœèi`à/ÚµÈ áØ0€ÍåêC5lßÿÿ45±¦-s\$÷é7u®¼\-s›P- Å#ŸÅ¸ò7äŠU®ï'Š8LXá .O&îfÉì:Ý4$¹€Ížˆû‰HÈûp¿Mïß>Ñ·šÓ}Ö:¥þ¢›Hò@sÉ‘Oáÿ°ÞÛ{hžõ¥võ›ßMÐq‘ ŽËÒ<ÅZÑ8C,Ž0`#WžÛb“ @01ÐÐ)…k8 ÛI„e=+íV\¿­&òO„”‚ªU3 ¥3:›Üåtñ¢lDº¿RÈß­e}tG¢J²»·¡ü$ý×Ri—0¢á9IZ`¤ö$ bQC±­w !øˆî¥À…y Ý.\ÆçÄüï(#z-;f ù7ÐɉïMù= áRŒÂV'ÖÆ² >ß•:”\þU€­Jò‚QD3‰.·Q {´ [€ÞXîÐ&j .K¼g§@S1ñQí¼ …Éà\hHÄ‘šç@˜XäI뺂fóˆæû±9i;E xwÿC/-†ÝކþBl…ÃEŸœ‡sÐÑá§s¹›icƒÆ»âÎ=.]h‹«ôyª g«½µ½•h½Åmâÿ­•†ŠâÔVáçôކ 4€Í´ã}cŠdÇ,b¹–œ_kéŒ÷/S#)…Dâ„'‚™?înmnš)}6ùû™½†uôêlkg0éŠÛžÜ×6 Láþk¤t¹‰)1¨ ×OËšX ‘ë¨ñùü̱£ŽOñ•¹Çgâ9~;ÛÐ&á(ešB2w¼Þx\Á¡å­øxYÅ~æVé1hq£¦+ÂQcNcG*ÉèKy¼ äüX` ÊÇ7Ó@ä\»ýÿû‘€rà^ÿdµ÷°Žó6íør úÝi£÷hZ5ñsb‡Ÿ|2£ÿC2 SeYåªI ­d‹`ãˆþO×re Ä¡štƒ¶¨+ŽúÐT˜åÞ¿+<!¨„J .¹ûÁÃüø EhWžPÔ$90°qgüaëp<Ÿÿ'êDà²÷ÝÜÛï·_÷ø%¹øQT ô(CºwW*HY_n«‡q3¬ª4VŸÿò]ßá:r¯Ì•*¼%ƒ²†ZYtA©xŠx@†®† s÷† á-zOhœ¢?ã>T!¤<¯FE÷¯x€Æ, éøg ¤õ×$?à¦2&2¬y‚(`îeaȺð}ñŒ«Á¯&,ëÑÑW¦ ΞؘÁØ7Eÿ^ÿÿó"t†âœ4¦ ($11ÇÿÿÇ$61óÛ¡¦Á:Ú½S)ÿ¶á€Ñ×ÿv$C¢_ÿ&\O“÷ìwt¬2‡¹Ôz’Ö<ƒÉFV2ì~'Óí «{нïm¹?U}ï¥ò{®™eðÊ™G¿þ%Xhd\`þ ËÀ;ëØ÷u¼ÌÈ›eÅZbQ¹ÇÏP?#7† ØñÊ7º +Ñ¢ïà:Â6%¿+ Š9WŠCHàx¬Vþo?åóI3„?þa*ª¿áÕ¾;hXœépKá®ÐáÆ¨í“Î<"4„Bäe]Ÿ?†‚æÜv3㲞kÞ ;l“ÂAbòA\‡xrjIƇœ6°Á‘¯vÀW” /‰_;Q“}÷ }Î2<>¢Æä E»eï\.YÔ{ΕžÆ&¹ ö ¤&G%'B®$©hMCOÊÆ¿¸ÉR*.ñ‚òÊ|*ÀXÝaHGÊ2¼­šÒœ©½‚¸èeAIÂãa2à0Æyuït¾õIðpc¡âoS´-i¸I ¾-Xm¿½{•mjšd£À  L?ËÉîV<$5]ù/Ue{8èD(}i©ß¦™ÃÃÖÿúnÂÜ‹±~:™úeìT´ü;£“ôY/²¯Øü46˜r•X7ðôÞûÇDMî"öØÜýï}zÕÅQbÅë¥Ý½k‰0&$ÓÂ-–å>!:[ÊfNbÄ`¹K„³*; M_Ã*†¼á†´_åf@À¡Ô"r c pCºð^Wbdv X¥÷"ì_‰M̽çaâÛC ˆž•Ð8ˆUQÄ»ÄìqãøÜ¢Hï|r—œ<¦½Â<ØÝÿç”ÌXñíA«Ïíoþ1<¼‹¡å>L†mz ­Dï8ØáÆ€ïI¹›c‰“w†n±øq±O^Ä„*$æ_'×á?"&çe‰yÿ†TŠÜ¥Xþ¸‹”æS•EüR$Xº”Ìø–e;k>¢mi0»MΣáfèvL"ëã¨òadº —Ñž.ªªÏØ¡;þ`øŽ1èþª¿Qå“÷¼9Ç:ãœBûóÿáÄhX•rímÇýQþt±ðÏ€8V3ù=1?=z†G_žƒ¹HôÅ\}¤ ~ŠŠTÐ.ÀÐ 'ØB¬Hr¡z¡zBS¤;ìKo2`{ š´.™¾äŠøR¦>tíÞO!D-±¿mË8Ý¿6F\ejÈöȇi3æCš³3ÿ‚·„®wÅÞ ðøì1ªñv™MŠ&óñŠJ´´t ÊA¡Æ0~¸à‘m@"J¸—ÁNÊ )·êë'ÿ„ne=ËÞI˜02ÀŒŸZâüÑñ”ˆW0}ùõɦÙ»Œ¡P¿Š$¬"¹Dþ v |‰'+4=Ÿ eALí\©k0$¢Û[Ïø%`v–ŠÍFöÄ2Y‰dç)ÐVšA}E6FÆ££@ß³ðFÍR¶aü·ðò›ZPcÒPLÓÁ¡]?Ô©íµô­….3×w ãÿ@õã:$ m"+ñ³Ïq •‚ ÔJ¤\K¬-e}dÃÉ0wt`Ëßâ¡¶ËLnžÒU5…MÜM›Ó¡Kú‹ã¾æú–< C{»¾WäðhœÔä)²ªââþ1= ¿Ï€ß^þ–'> ‡Ñ Øpv©./Â{N½ ix%'ÿËÀ{ԌߚWyiß½:òu”ú #FÅl°4Àlÿsw†ÏÞ;`gª”DÖ¤ =צòõ•ñDÄNÓ>r5FÒVsFÐÞç_.Oˆ’±¶yÚΡEö‹P%(ÄYídÑR/õï^‘%¶Ù*ûMªvQâCÐåŒíe½sb±ÄZµ”“ÈQ¬£Œ 2¾ :½5‚–¤Jï©rùH냫ÀΧ¿pdPà9Õ`ñ¢=¾Éäj†£R¯T0ž1å™ì )Å•¡WÅEø1ås+Càùû‚͚㕳¯Ýaé]²çՌӇxL‰¾|?[ÁFƒÎßÍ«uð%4R2›×1ˆŒX 9Ü–ALò²{®þ¿óþéÈ4ˆy¬%‰†*‘”4·…þ—æˆT]Jïþûqt -æ Ú}GëšY”ÜÊí·ùÆ $YñÊ¢ëî0ÙÂWE–£ÀD+ôy §)ƒ1d…bÊŠOîI8hÕF|¥õO—¨IÀÖ¢TÉ'”ya£2Œ…¥ÄïÅ_ß#T1½í *@)P¢ÅŒ@à_¨\/ê(Iû.:õè÷c ¡nз—äå€âC¾ÁðÙ?UžÑá`°D£Xºê“_ÃùIÆË°]éj–¨éàÂn‚j– ,\µöÇOô% ”â…$’v$XR3ñ 2I bjÀ·c}”s€(lH"’=Ãìj#'Lcÿ'„Áy˜&`sjõïùx Ôµ9ÉÛ£’ ÓQÁZqq¢AwU½ûA³ÇîÎ üJmHyC ¤‰ÛÇÉíP/ ‚ |<•8*¥@=¨À–%HÈsžüÄsåḀÌ §<ú™‹½çáz¶*‚ê‹c ¨mzXlCõOóñûìÍÇæ®Nã&Ì·4¸cf)~Æ×úÉœïÞaÉk$'þ«}“þ#~ÿ ÛìÖ¹Y8;þ¾:ÆI2Æu+ØC|x“çûÝ›Ò L»*ÞAòØãÇ ðŸŸ–6ÓÍ„lòɧÈÑBȳ1øGs’P jvg\E 1‰œãpþ S Žy²™ ˆÌ7ÖŸÕ™®>‚øæ ä^1Š}‡†Ö›"j—¤ð3IUË8`XÞœ“Ä&„#?i@l.…ýNu2{JênàãŽ)YAà9¾à«$].è ÕWÿr„Ç®tGåQÄ–ÿ¸Ðó0Œá+ƒY°H34W¦ I´§sW€à/ßƃ@© uÂj¢Î‡¢Çc¼Zħ™®kÐÎñé ƒé §Ñr}HÛ;|T^ÿæÊ4 «´†ƒ˜# µBiþO$ \ilßÊæ è­\xÁÅìMÉ(Ÿ§Lx¿0[ "Ù½ ×qJ¶ø +OŒë+&nlZÇAŸ Ph)+9:ª ´vájžÀü)d¾Áù)bBH“ƒ†>n~yÁlåÞpå‘uàu4 á¼pŠN‘0p?éø®ªÛK=’1éËJ”\<™žÝÃÜ4uÁÔ±ªæ´s_Ø“‹(u. SJ RyõÏÜ`Wxl ‹ î\{”Ýñ‰‚¤"ð“Áä„f€ïfµ¡ïüÐO“Òl†l5ñ«Í^ÐoýF ŠÞ¶Íùÿ™õZíªªå6kD >ÏËZdƒ–ZbE&e¬Ì~BKʱG\:Ý:Š ¯VbØ‘6Å®þ}.ÂÑÇFßÑÆ`u)²ß.%õãÐÃH³´Îß{©@ép>ê<}ÿ ™á÷~;ð¸U öy`*¬ÚºWª0à¥ï²Þ$lzx£dCAœ™ÚUwRÙ®ªɱP÷O±“ÊP¢,dˆj¤ÅEÇ©)ÅEw@S"ºzÚçbC­6‰[Ç…B'r—Ä€û„;àÂϳRQ¥ƒÂÁ¨ó‡µ µ ¸1ÁqmߊÕy„ñ\Vú…&ð7MÀÒpPui =†$'—%ëÙ¼Ûó"dʵ=Dõ–Ùâ¢.³ÎypIáÚÅÄ êðhÊo餆åÕj{Ë3Àáp³ýÛª¡p4`¬2RÕFÔaáaCç‹.ØÕfu†œõж Á8+››f=Ä8«„n¿\ SK˜[NÝš-t°‚Q# ü‹äñ×*¼ øºeæ@€©TËý¤ŠÂ«•åÂYð¾[€ú5/ÿU´¹¡äšABËZÈ*àÒ5M–EqG2ÓÚX hÀšE‡3àÿr!nO9ÒH¬1ì¶òà¹-Pn”d £„¨K̼7¢p‹Ä ü°Ë@•3×ÛNl8Û¤!b|ŠÐ1©»#²Š %zWzó¾V;mÁ[ ²Åçž±ðý`N%…îŒ-d€$hßgkJͯÌ$Û‚¢ów@œ66’žBùN•„’ô+‰~ ÚgYç}½#‚£÷œ#gÿ€î*¼9Žœ4—:˜Ÿ]ä‚wÅqØ;y>¡×{ûⲆ½ÛNOW×~?/OÌÊ[¡'“Ý*lÙCHk /?"2€ŒÍ¬«ÿãÞØÈÙö)”6›üÃÑt~Éøü4áˆlÙ»š+1/ô9—¾™Þ¥@Ý(·¸ë·b2Ö¢qQL¿„, B's­!–&†O¿%°ð@Œd6ˆûã7ýcT–ß÷[‘ÿvá6Àã”Ç-;¸!±Ù#}ð”‹é¡¦Eú\TêÇ?`W'¯^×Ó’:7 Po?üJ+ ƒtÖß¡R}4ó9pCEì¼$£mJ1ÏÞ@ÁqЖŠÉD_\€Ã Rª›&Aµ§4Žõ™ÑYMuÑAĺ—ÙµÁæîÛ] ™/$«Yøwr¶xYþÔ· Nð”8ÊqÐm´5EÞ¶\LUÛ w˜ šÙ§H†Z !ä(¾©ŽŸWòyJÔ€”¢\¯•0¦8}ŸÝ „¢·=Þs¨'!SBÑö“·@0¿{ïzþ ‚J­7¼&l›#”­JÄg‘’k΃÷Ì*ãnº­Ñ}1ThŸEì@Ôá¶Ñµ/w5?5j'PUˆš*6BÁ‡QþmríÚÌ“H¡Œ]>d5MNÙ¯äðÚŒÀà¦È éY¹C |²4l|¯CÀ­ÌR+&y7Pªæ«R>J`:Y—_ ºÉ€3ìx»j8íϲ“¨;røþeQZãšž°Þ»§š]D€ñ¡´ë¨q˜{øš6HÅ9‚ï7ù<*0*Ad% Vœ@UY|æ5¶\7vzÐîÔ/Ö0íŽ~T>üe,s0½Ê»A%ô.8Ô½ºBgDÀj+°8%'0X a88&hi¢ ÏäÁô~ú h™rÈ–ÐZä¶b[ƒW¿I´¨6g³K— Šá–Zö™–ÆÚÐV™XÝì´t‹‚`±Í§RÎûÞ€f‚ÔNVÃà«+o ‡™ÒìþOH,›PWl¼Í-Ô(ðÅÏdøqFGÛ¾ó¶Ow¼Bt¸=r×a©ÈMíUÑÚGØÅv°*XL=«üÃÔédÑ$ÿÁÖ¼4±Ã?´öùzŒí´Ôå˜ÑÆìßM üúˆ÷ uIÆïyD‡MØÌtK†¬M7£W:<`óÁ3"ŠkÆÙh¸Ë{¶gÛóA"щ߂Æ@u×òUsƒ¿F¶{©˜Dà V&#Z™s'§ÕI;õ«àØøœë±ŒŸ}(Ô¯L nÝnÞ2z·XΣŸÇôóYŒR¡Z½ò6bvƒ€ž ÙŽëÍÒyé +a'…^’|ŸÞ¦£Z>›|u§Ý£Ñôòæ“^wëÞþ¡‘ÔÇš7mŠz“ÏäûÕDPÌÄ*-9ªËägCò}}ƒZ"5¦¼pK:öÞeèu\NfiŒˆ˜ò¨|gÔ,dñGÜ æWŒ‰=æí¼ˆoŒ™<ê˜%í Ý»C[ñŒ<0¡úc‚Wr{߉hÔ×@¤*^ë~‚j(ÎÃðÁ¸> !+Ô ÙÆÅG- ûeNŠŽ•Åo·“õèl@TàÉRÛmM8<·¦'K¹cø›¶fi•œ¢¤Ðùä̦3|ÕÒ‡°zŒ¬+'%ò¢ñi÷9®>3E’¸ÖÕ¦ÇB íLÇ|‹aª÷.‰¿6+â!iËbÀÉù“chU÷åWÃÝ¡X†m? ê.O©KŸ1ÜØEROï†DÄ?~'0@ªéÌv05æ]©˜„@O¡‡à’‚áMòò\BÔÿ /Œ Ø’tô^OA¦h8$À?¨{árÖ¦](m?Ý¿ëEŠ™D×UQ{káÛØ}Fèb}ÝN””6&ª¸ÃãHÄØ×mí Æ’ïc Øâ±7ÿì0OM½@iL%Ýl.Ù üQsQsùzõžÿ‘ûðêsIñ¼Fj|7Î +h€ ‡«0{[ŽðÿxÆ-Œˆ ‡’ðòü'ÖÅDIq»ŽÿL§…™1\"çÿ·1_Qr¨¤ ëk7Ëvl³ƒÜÇ!ë¶;h$G Ù — 3˜$v¢(ñUm†…Bà©wvSU,&Gù¸¯I°¨°N,)š¢¸Ñ‘ì¹ûcvÀM à´¡‡]!œ™É]½ßí……Äœ8:H®Xf¹ ³ã½0žçÅx­êZ¶1&ñ欩C–­Âßfx0é–[_ÑÁ˜ ˜Ú0yØ‘bXJ–ø8ip%ÑžÔ¼×ñfq¦•xhK)ý?Éà¼ã0!”O`)ª!ªh1º}Áƒ—*ˆA¹«/æUŒ”*¥ºXpxc¥‘ó*©8‰Œ`à)]ÅÅu0\2¶:°áóØUMI_ßÛèpûì;`‡:j^ì¯O¬pÛmE˜eºÜ’îzv£¶œµú¦Æ¸<8p;©D>$nø `4hW®§qánçd§OÂ߀܂C—õ]]Ž\”×?] ~¬óµ¦ßèüoUÄHaÕ×a÷&ä“ä1V_*½fiþOÅŽd)Awò.'Æ—•4-V^ºá£Cð·Î“ð»;ɪ{¹6éç¨ÕÀýe± y‚ÆÆ Ëöhˆ¿¥1ÇoTnX}®[™3ð.Xz–¨ Ò4×ÉîV¸%¤¡’ú¯pW‘NÐ ŠÞø“ÈV±CFÆN=$@‡+|O¹v`K]WÛ•yÚ*»ÈØû"¹›ã[´YåèÊz̘ñ¥V„GÓE0Ö„e1˜˜´ûŸÕ€BWQ­ ÿŸ¯ªÔ 2\˜n_¨·Qc{†©‹MDaÔ{è 3ò„…Û_±¨QÀj€Ý‚=ýÙr”2€W௦p2(-ëzÃâ '²ÿ¼ç†Ä½LuŽB?`L„ßþ ] T­£Ò¹þ„¹–Isí5~ÁþÏoÌ2P 'wæ5çúR¡¥VÏa$v®Û_Ç5»†!×'ƾa î¦ÓÞ4yM¦ž¾MËýÁ&÷ݲŠ57“÷u c²=xu!äNv`màŸA}¤!“Å™LšÕVCô¥Ð`‘]üáRª—u\ë(×ÑÿÒ‡|jLó,²y'Åš&î`׺٢Ap]9y в± –™HÂú¯vgQp 0/ `ó˜à´—ƆN·‡'J¥#hª?3Ø|Áµ² Ðæí:ÚŒ¢ÚwÛeç]Þð~£°ÕÜùŸÉçE ¶5îÙàYâ7DsàÐ5Ÿ‡tâaȆU$Üôþc6kéžzB~»|×°ºýY’©cR@zи/¥+Îw‡N0vpˆQ“……e½j–?ÍBªü¼ƺAH´?Ƕk¸ÑËý äbž”s"›öïHqƒ¡bQÚü­ãkäðDP$ØÀ˜¸„z3Љ,ÀpWÊsa.=ã¥îaù!¥¢´‡“·?ƒà3¯€¼9µd.*ß, 9šŽ¼Ƨ†Nð¨jÑáGœ†ãCå:Ußó3›cüŸGåY$¥¼mÔ­ù(F,À®˜«€ÏÖ9Å¡AÑ‘ûb]þO0ò´! 1$Zñð n“c¯7wp¬h^œƒ¸– ÖB­÷4íõÞ0Þ¶o°FóFM>±kÅhD—.x! a™ô’q] ¯Ž.»ùp‡?p£eô€[¾òÃÉ,80PéCt³rßïdÆh¢°Oæöëì°Ù&F³6a—úµ¹BjïÂ0E†ëÿÄ^bÙÀ'«rŸúî£8Öç&}úœsžR~õ'L²+Çã &Š;IGëmœ ™Ó)Ê/ÁÝtä³ÛÈêš ?øâ ?Ÿ\ãrà}œoç>À6¼b|âBëDøÍßCþd [1™€ºZ/GûÃ?­ W2-ÌO_„q½žöç‡åîöÐ9Ã,Ÿ ÇZ<(° ¾½§½DS&>N÷è}Û»°07½ï}z¾¾«7xí*Þé ÞAº£hå\³(.k‚ya—"Wì9 òŸ“ÇÕñxÚb|bô~#<™NÅhKPaqr®,;e#ð\“ Ic'A}'lê'ïâr/x®|òÞ`[øu¥°*åJÅin¥¡?øEÏèVÃ+X¦l»±QUà“m¾¾-”ÛÀ÷p¦ßÿ % °·Òaù^`˜Ò‹ 3©N ¾=ãèõóðJ¸arJ Sœ²zjøuž^î¢ÙçoÃÄQ¡"ScŽ¡JH¡þOy|)ŠÜÏжsdeþtaûà~ûÙk‚þ‡I©ÄMWéû(GðÄIœ\¤^à:éæ—€Ž–ü#ç#} §Æ¿9˜"³!u§_®sáÀM¬ï08½ñ€ÚýÛ ^ ë­7çø{Fm2·Ø9ß`„ø{DXáòäœßà›=Õðºn0ïóÿÆ®4é/kWo4²VZ¶õØ‘Çú#Æ"’è·ÿÌÁžƒlœ­¹)mY¨—jè.WÕ<™:ßNé±GÿZNúð¢Ñc¾ÿG̃ SçjÛ°Q–µt˜¡ û¶E¼ù¼pö0fšW¸@#g·LèØúd Y`ßå_’%U{?g3©Åá¾ C4ôù “ÚH£[n±ñ²³‡‚êS*'à&äÆÊQ:ê•BÅ%tˆ¤¤[ ½W|8-瀧pêju›ç8óÃtö¸!*u=ÿ-M=2R9lnèû ÙŸ‡„‰´0êcXÝÍ+ÙX«¸ïɲÿæ%|Õãˆ6X‚ÔüLPÄQïù<*0hH*˜À¤‹•ÜÀ7ÁÆò-ö6àÙ‡ª_n4ê£Ó‘¶Õ@bwYŒ,˜’_Ö UAn¶ìü‰œ,8®ºƒ°?#÷QàX €eQ®‹¡óWO‡F–gíßÌT 3^•¾âá¿/’ƒáïÚÊè YKhDŠ¢ù]Þ½¨í tKn~€AZeÿÛ8¦?Ä6¾*}(,ªîr)àiaûÚ7©Æ§‡qª¿V¶Á 4kßoÊÇÃKSjòûÚ&Ž{ií¤ð›fÎkI"äI·‹ –퀑_¸Ü!þò^t cŠ#Àª{ê*ϯúßËðßšI=%Cur‡ÌÎ' 1\M;û­ö%8EÆj¹|3#(êúX¢8i ¨ùáxÉÐ\üXÏÆ\#/wŠÛk/-Ýùb6ÉTÝ=±[Þ|ü¼Œš^ê[ãgpEa㉞ÚJ?-*Ì’JÙ2TüVˆ4¦ =î›ùr!¿Œ 4&z„ìfÜw-¬‚$“ÎDîÜm" G¢5yå‹„@ÑÖÞ8y•v_øAŒ'ðVá}Ì0`NhDœ;@Cé•E «‘z¢Cc\V66¶'”0Oeüb¿†¢æŠ=Žkð«;×´„AøßP@ûú¿øW ‚Ô»|A•ñuFÄèÿÁ*ÃÂÆ ð>1 |”]?ÿ'úxj8jÒJâÒYkÿ•Cˆ*ÆŽá6›޾G‚¬=*ÜK0îDÍz J¦ýN¸! öá ‘>%40é¬5Ÿ§Z»ÿ_«õØ(Þí“ùdÿÕ÷zW€ ÷ÇôQËÎ^/›FÅßÔº4ê6 ,K\$G\u¥–J{Ôà´Ã˜–Õ 7brÕÆ*þõe}œ7Ö¤WLJ¿—˜0Á{Ÿ¢&¹Äe¦Vˆª?Ü4Ô Viq³b g-k µ•ñ‰(QÑ‚2‚ ¨ qÐh.×ëB`%'ìà¸wðËŽ² ز¬àIì4帯€Gj´¬³NXÈ!Ø›¶ì(³H6s8æ¼ÐyNÀê?Š$€à7 $£ì6v24¹èz8†^ÿÑí:rƒm1{GÇþ*Á€Î‹ö@A“ÞŸN…6 ÑÄ7þw£+ìƒÝ4²3ÿ낼é™oì]0õˆ+׿°×ÓÖÔÇã±¾LØ¿–ç¿Ät(j°QÑ P÷ñ²â ráۇK3vbf#×//'Ãf/Ç…“&[ŒÿtOW Ô7.¼ÏY,Xž+q ðáPîˆäˆ•öÛ¬¦LKXDï0Aÿ·vRä#ä»§%Àð¹úbX¤\¸@P#»¿-)“í›ÂDAÎmUËç ‡\9xà½Ãh, oÎ 窇ŸõÙêƒÿúþæá¤õ²ƒv·PN”LJyøÙ÷®þZ„ 0ÇâáZë®ùÓ´?«äð1 0Q‚ jÕ@Ü—XCÂá+Dç:à+j ÃâÒËÈóølY¸8¸P€2SÏ“ÌØ`ÌÐê.€9F€ %ËbÆjQFéB=~ìäÝùqÂ+&|ÍŽ§õ;¨y€­ì È 1;mÅy«¦ÅW‹H°E$ý,\«þ …«üVöŸË¹+¯›{Éê>Á€Ã ÒqØÂò8 ë¤ò1A„ÇÁб*q@ܖΟ ÊúrÆw)«¼ß ’–ñJ–Q–OZÄÅø=°Tƒ †ƒ,°`Ø  -N "ã’X)"Çm,›Î<Ÿ³È5´Ô ªG‘ ¶Ðj>Ø Dä‰[ C‰RÃhê/» Wn°ú?òx’ 8±BŠÌEÜoôܶ𠬱i5ÌÐeÈ>#ÛF»Ò“Þ^Z.éœf¿O}w%ÿ·ÌÑ/ͦ†ÿÿüõ{?ûܲ¿þOuó^ÿ\>zÛfÿÿÚoÝ:×NsóÒ¿wŸ —¹êúý«õ=]ýúë|žõª÷¿ž·gûÔ5«J¬ÿßâ±Åcž!ß ]îO;þ½ðK"—þ§: |E7ïŸúëI}³PãTÿÿwŸü™Ø¿ÿÿøN‰ QEˆtðmü‘QP¿õ×ÂQÓÅm þ ØDö4ì@jK§ùz÷×é÷Ç« d ?žüÐ4rÿ <‹îü¿Yg[ü8©ŽŒZ.8Wÿ @…ìýxtÿÆG¸wžJWî¯æpô…ƒ¥€¼ …¶4W §øü5Ã˃¸:å6ïr{¥n;Mø/µ¯†SÀ_µ±qóËX%ó!=|èhŒÊÊþÓqVé½ï'ï©01´Ë}SŒµëW¶áiÒ¼æhe3ãÑX9Q†¿4L¿Œ±áü !… οdí%=aÏ@|Ö ÙL¯ú嚢!¡(J¤°PpbÆðí¦\–XãBÁ­m€p<ôòÖµþÝù°#ŠRî°Àʤ¹ÑœÉÑIð¡Tå»S Ü_ÆF ]Á.”-“¤N!´JáPÌ‘â; ¸}‚O[ÙÎ Ò¡Áµº@çÿ³÷S°ã'b.Déu&‰\7Q¢n³ ¡ž0 ó[J€hVì¡1'£Ëÿ4:WÓù[¿³Ëž˜O÷áå='ÛÝ¥e˜@ci}] ë'ã?íï¹Ì/æQתYmÔõb‡°H^_p/ÁÎ(‡= ºõZD—ß´Q’‚-îÍV Ð;¯:ó¯8\@tnšåI[ "¥JÚ%ÌàD“dÏä@¹/Ž·e„`„YçüŸ 蘦½L¼0Ëå¿Î°Cccv³û>‡Cù™KAdñ–ˆþ!Ã×Eð@÷ §g¥‡ÿõI<šŸ~ ’qo£,÷§¤ã³i¬¨4žg§ÖAÁ–ÌnW^°ûoÐ@E…{œÅWþp0@zÿÈöHXâ§ÏTÁ‡!/`¨hZ“àÕØÚz툴S.Aÿáhös²ÒÂáÞ›Å<ž¡{‹˜°cR=äžRh%@Š$†¡1i9‹¤‡OéÓ “º»˜+ö‚!†LI¼Øºæ"*ˆ“Ç (¸@¥©àx@×HA¥VU},Ì.1¿Æ²îOh‡ ¥³Vî¿:Î? hžƒÌ¬†0¤Vá)dÄ=^È­x+Ct.¿ >`‚S6äîÞ«ëÛ«)Z¬Ùóq2Öd/IþMÞ%?òÞ`Ñ›~ûަkSYAÕz‚>6¿mÔTCH‹¼×6¤6wÞ¥ë:ú‹“Êî‘÷n¡ ª)®-»®"/Y3\G?dî³òFŽ ¾ë—ˆ”?¿L®LÞÝ^äöyÈE1ÊDðf¾tT¬}Ÿÿk¹^4]xæm8`Æ#€ÎLmãù~1ÆŸ .µ;ów~ó㟡ÿ qKÔ{YŽŠ³„6ŠkÒÿƒ uXÙu¸üZ cÆ4S\íFßp_ú¨T‡Ðkµëˆ­à—@{ilvÿãžÆ Á@Þ˜]¼ÐBäã`Žúœ÷‚ ÛQz%…‹á 쯷̕:Ÿ85¹›fÝ2¬£ÝÉYWÊë#…)Ÿã7zw.xÉ'¬©¶1&XD˜(rìš1;±œw™0_øùU ·øŸEÈ5`%>í{Gðz-ÔŽ”øi3’AؼÛ›Âöžådgÿzs4·a‚¥ùàã-FTq.^&³ PªOó D§û¢•3á'æìB8?d8‘È y¿×èô›½Ðeà©„X*;÷(—®ý.ZY‚ŠÐæä9 õ¼ kŠzD—#V,ñúR›g¬·†$£k”HÙ+°Ô¼!¾>Éþ€äϓőצbИ‹tØKŽŒôexU\²Ãr«Ž†8†.KG{tap}FQö¨ €¢°vâ\Z"+^½ipMvšè£¢qaCìa”•a•¹m& 'úÉè¯á…• £EÐvžŠÑ§f|æ0>˜‚VÛÿÇÒá‡Éhé (mw\`‚ýNïTÍÿþÑowwß~TÄ(÷[ƒðl¯¹B5=Ó9ù²Lo>AÖÇ`1u©j˜ñ¨t¸uañÊÇêD)áI­$Ê!ÂX©˜)׬ݒA6!i.Z~Ƨ9à²ÓúdQÁr8Y‘Ó"¬@ïòOÁ‹¯zÑÍ_±â¬jû@ˆ³ÃRiíR]ÿ¸Ýïɦ‰l¿9÷—œcÛÒ F(õr{z¶Åî”ÈDËs%ÅÒL3A%)LþÇt­É+"罯œ $º`]?ÿDÃ-‡iâyØ…ÁØ´aÄîÀqÿThŸíò;Gý_ü•ü€†÷ŠxÔü3 ?ÐlTpVà`ÇÜwf͆WBÎ}@ ÚùÿìçÛ (‹IÖ¯Ä& Z,ƒ³.¡0ý0FEâ ug4ä ?1Ò߯:iüž Ž@~hÕ—ú‹¥/R©Ñ;ÊÍN=äÖù lÏa£X}‡î;% Ô:üùÛMG²yüüøEÝœ?R¼ Ϧõú¯±eïvç¾&ÉœAo~¼§"çŽlöÁ#ÃPpMýuévúa%©®Má;‰füÑM½Dw{ÝK‚}¹u¨ÈÌ·¸z_ØÊ{iØ·*„ÛÕWûI þ´ÐüJ ‰PÞê‹§MÎÆþ>š|v‘ÿvú¬5a·.©˜CDÿåÅŽ†Ø^ìn4™±Ieó1þ»–7¶îòˆÅL™qô°ûâ9Äôˆ d5ZŒW+Ä.ƒÃôö ¶ç즓J÷øAµGPéB"…’ÍKò·?ð—‹È«Â$¼v.Èn(¾R÷û kBrßð‚{³´;’+SþÚŒ¼oØ2í‹!GÀYè;c÷N7'î´bQí+5ê%Îo‡E§Ê‡šþÓÌÐa‡(Àäú¾»Bg÷v Àßèo´±&«à³«MhºûÓûÕ•ýôžSÚ/Ú '8èó{ [Ål÷²Ô9D_ÕˆXÃx'uëö†ùyÏèdËNĘQZÛû\uÈw\2å…~¦”q€l¶èˆ¹ÊGCñêW à¨ãiòŒ˜\ñS›ì8l:T RΰÎß® lN±å ü•Jü9«ÑxV±êïB œH¨nÕ"¦54 §`v ²Ç©®9¤¸™FÍúþJ¬|Á‘jíûÿƒu¤=Æ6§æJÿ'É­XÈ€Ù§/Ñê8"¤©þÈÙ6쀹»»–É |j¡‡ñ`:yŽ<1UFŒ&¤ƒÀ z´_—Ô¡JÎz¸ÃíI³C1L´ÉR® úxªÀïÎÁD¿YÿpðL3÷ü6YUÐÄ>ÿAßÁjªjc·Ely ™ûÎÓCN™˜û#ÿ¥`BÛ$5^Ú´±»×ºÉg-ç?'³hˆãPï—®áVŒM®{ø7)Ž/2ó×;±¦Oý~êÖÿ¥„„m!!H]݇‚é?—ÝôA¸i$kx-ëóXqßú kE­Æ›Âž8ÇIÂ:FWûÉS¿ëµ<à\õ Cþþ¿\ÝÞliý(qs2(ÖþK¨CÉÇhÓX[ZýƒàRh5 XáÀã •d°>Ð5ƒX¸TZÍÑWE¨h§ð±™$Þtu³ùAŸ'ƒÐl‹‚aš«¾Ä¦l2çÿò}:;`œµm+uFÔžá ¨P"3 ð@؇€(èŒmŒ¦F] À¡&£àà˜ä­Yθø‹I±&ÆŠëËë¿x@¦ˆA\ñ_sŒbÿâÞb™ždºœH 8¢~T«%eÊ ííäµý¬‚1nª¤Žt9`¢È˜/L]Eñf:žzçÙ˜­!Õ-ï6þ¢}ÿ£œL•Œ2ÿdþÇ”"Qvî›»ˆiK–îH„WúOMf§#Ø¿'ï’+wÃÂQ[ïiÄòo+¨šrÃ8`r~î½t¯ß_¦ÕÝÝãñ)лiÓ“ê“ÂSüVÇt2 ]„ÛsÁŠ%¹–|=“×ùxêFf‚#(lÎ_Ž{BlorEýèï±K²ˆKþ³ç/«A@.3¾Ìb7â¼aàr·¬œø,LJoˈyq‡ænjÕY_Á•½ú§)¯ø~ûØäè\@ƒLiw7‹÷Óo}U—wù·}´Ð"½ïð¼¬g^!\2½pì–Už*?ð…@A‚­Ÿ%â£nÁ…Pï±ò3jküí.0:ª†·£òîP¢Z­ï¾”Bô̬gQKœýòêOë'Ö¹çþ•„7{ÜþãAÉÿТ&†±¶û¬hílcZE!¶É.öŒ¤‹¸Õ«S%9ápn‘ŸS7áºÁX¹@ôì nñR©³%“AŸB†Õ×ã)“¾àÿËèÐÂ<ɘQÔ4újÐ ½tÖã> ¹Ð —ƒ¥r¿ˆVˆr†™[VÉ?_JÈ¿CÖoGç'9©c†ž°Çð¿èý<¶ÃÊdÞN*ÀÓxsâa˜@ô ¼ô,“K€LcÕÿ¼ÑÇ"Ò³©mcA‡ì€ú6säœô2BöÃee@±šžÚÍ…ë»'äX@” f¬±R9 Y0@SÆ2äKHüÄD\ |fÍÕGöx혀ýöJªyè|v û¤ÆÙø.²í§§ù“­Á›DyÏ-’9“WG¨ÓMüêê—ƒ* ý4Õùþgì{vË.®]æ«A—üg»Á€ó`„ `¬‚”.š›CãÌγžàY8'n÷m7Ö‚àEÄ¿»ÝÍ$÷á!Ð"ûÀÐÅÞ—&TJR&®Ô0½‚¨hO½:¯T]#›ÏÝß'æ90$g1:0®¤y_ñ§# O4N'=o^½÷¯NOº û]W ìpvu9Ò)˜CÿÁ…Žö4ÛD¬ëñ¼^÷zk2ÉÇg×½·“adîõËð9Bø*½áNÕ™p§P>•±´”(¬¸|Xì¶%¢³Ñ—xn팔nVØŽ+ã¹lnw »t4?wýÊ…ïÁc9*…]%¨xunfáØpw ƒ¸QÓîÿøœsËàQ¹$n÷?qˆ]£'©³‹^q "¸ß—uJ.ÖxáO±1AVÌ nô7{cÙ`ö˜§áÓËÏpíõmYAUï°ÍDüÆ€Û¿iJ ±1Zш²àJö¶–þò|2¬³&%‘ËâV6$+ÐZÀm'„»?s^æ ^‘ ³8€¹›ƒ¨Ot7dÀ`1Ó'Ç âÖk÷‰Õ ·kß>Íÿ'ûÿp@€‹}áE Éë<˜Ý6ãZÁý ù>«LrÃj×ë¯Ç¦|‰Âl4þ 0·E4ÕÖ7îçð5 [.þ²¼xOüƒá†Í ãËOØiÒí{¾<«LŒž¥œuø÷›3½·7 ¢Í*»Ù²D.BéC!¦Sˆ?„Ñ @§Ð«yÆÀFï—…¨ €Ã€Ãå,h+ÅÆ ^¸yºä¤¹X½Õ¸Ù~¡ÚyBÿ]q0ÙSžxÀ3ÿΧOŽ%Õ;·ü#àA¥ñn®'1u§À?n¬é"h·Lp颯7“6ø.J Ï¿ÄëëJŠìsÂÊOAˆ]aŽ6ï{sàÅ>[m oLb*´x³œ:(  Šnl´­ç¹Â§@P†‚´=Zy ª›Ü+µ-¬`l­çéÉý(@‚|Êaã°Ùx IÁä8<°àÑr—UÈ÷þ¾ H®‰îùk#]p˜Äë_Ã…‚ëÛŽC‚$¸ÿé¤Ù^0Fãʾ=“ÿAáÆ`–#;’Bì÷Ãs ³©‹.1Õ[ ,!šÜ¶ÕF€ä d.¥@ žHØLDLîqu"Àš ±˜¸BÒf¦yeàdPOJÓ¸|׋Vù+æað#+±>¸Ê‡ u—E!È9âÄüA€àQƒ°XƒXº…h8ÿ'æ£Nà²:Ós–ë“2ÛãK ±X@š §ðw0ã &;#Z÷ †Ä%Tª½X¤pÀ))çØ]Ïú7 ÿm—ö ©w?ÀU@Ž$vùžÌ¸ÌGS$€YÔâÐ'~ ÝWY®rWÜIϵ$ÑÊ4ʰ¤bõð žlt™ê ¡Ü€ô1å¯Ó\ž2&‚·˜Û=ÑT¹@þÏ=ËnŠc¶o±G6—a`T /ºª_æ¦ÓÜ/' –Îiü_¯—1\õᎠ‡Z^€1Ò¿£ØÃ ˆ€Ùyݼb§ Owó 2ÞýÃöÜ­¿SÜø0ã=oÈ-®îGY3ÔßG[W…¬c"EáÁ_YÇA€ ñõóƒT9ÒßWü©wOÔ»Ïßá£ö{rÆ•o|ü96²€3y?½|¾a%¥Pt爿utãq†ý“TÜà”Ñsn¦×k¬ÿæ™»Á$Z°wJºàK·ü¦Z»)€Ê¶>’©ª›FåÛ¥->I.§hëêÊh|_†øFqÊ—Vο'Ú«Ä>Ÿ+n6ïr÷†ww¹Ô=¬Ê(½qþ6˜•ÛCŠÞ÷‚nçPx&º2Åì¿Â”··w•‹Û»×Æ^ð¤ ïV^£ ÝÞèã±Æýòöñ»ß_}à 4p7Ôh' B=×G¶+ODXU’Åê!v/ÂO?7Í!ƒ%3nÿÒ y[ßÜc± Âôû©çÑÚ5•)“ò&Ô)êîp$] ZÆJÀ_Ð>GZ_Ÿ|R¨s6¶®­gó{aæ;-,Cèµ%‚"í$Âð"k4ô{Í_„/Æ/rŽc¨dò®M” àÌ¢0W¡+6-OB“I"–ñ—$úu À¦ Ýä¢ ÏÒÆçÓOìbó÷Ú¸BoÁAíBvºoâÕ²ÚºïOiZÄc4 \÷¤ÛãG­×~•ê`ÀR(ØìbzGb7D‹ºúkMî±XÊ—Ô!1Ë?«?†üåQu´C/©RC3ôãæº7ÈlÞÖ¢ zÌ ¯¶ÈÈÓ˜0Ðm;À ¡ •üÐõ?mØ’ëà¦Ì,[C¹ñ.ŠD2= z'¤"ʧdÆWûRûÒw:Äó© ËÿLaB@Žê÷VH#—ÙáäÓøj`Ó|³¯@b¢Úåî”ÍjfùÞZ»PÖ¿£ŽÜŸÑƾŽ<´QQG¼Œ!Õr|ÃA4!íH MAùØèb»ÜêX:ÿÖ‚`É»j¤†FQÐ÷e?€!qWý„@Ú0" ‘ß'sUR`Ê’¸Z¼t|lH¬*ð·î¾j7ÞbæÇêØÌ¯ªŠš0O¦òØÇÙvërßÛ âa)Ö+66دaÉú±ðÉJ²á¾0TüÜC”ÄVbF¾n5ŒêjÃ(ðÉOˆ„a³+]ëJ¦ö­‰…$4f?é‡jl+íA­XYÈ\”t°†T¦N¦Baeœ‰ÝÒƒ¢ˆµ^9TdOn€Õä‘Aho_â»C®ú›Íê ‡ƒ°£ÆÌ5ú#à¯qÈÇ‹&¢_ÈÕ|G ® ¶¼n‚r˜¯-æ.|žÿ‡R ÓQ(Ég¾ÌŸ×ï¨`+ PÎ~åkÿþ4V›?±ÀÃð¬tçe+ Ï`;­éòuÔíû¹„ƒ¨wÆÚÂì®HÓ];ol6¦ˆp½µŠ/® ×Þó/ÃHmU¸/îæÎð–[™L{Ê‹ âê6ð?M>}g È„¨çþ ? àosïtZTz n†o'{,—?}!ã›|õ6È-˜œ¥Gu=¿ ûû|ÛÑ›L0ânšËÑRµÿ…´v1¢Õ(uÄÀ&½çÿ¥ÂØ4ä•÷‰rÞí×C7¹ÿe{±"0maa®ÆM;ø'U€Ö#A”ÜËÿ¶øˆÌîËñ¼‚$/¿Ý½iÍZÿÛS%A –ÔZÚ&„^Iý*ŒÔðp¾Î wšßZÇŒu·{Ë„cjÎíœÅ„—~õdo+\gŒ¯o,n+?»ù¥Qv/Olß§lÉ|9`€þOKñ³&‡ 7 ¾~9s-uCü!ªi9™ÐmN'„¯îîøS—ww{ß­ôã`à>âw{»¿ŒÝ㩊­Ýïº~1=Ò3^î•å¢b/Nîî9?gýßâW+ó`@r›ñíØwtîx_‡°3|ÚœøÁÝ"ÚñArÛ½òRÇò~ÕXE,€‰5{~ ·”l}™jÜýxSQ{Of÷ˆn&Mø¤b‹Ôh¥Ä¹ë« èƒÁ[äA; ˜õÖsÌufÚÁMj?&”Þ<ž®ês¶=fêUŸmÞ|k)Ò)ª;(`5ª±¯ƒ]0ùïa•h‡ê¨Oüúð¶Û¶•®f?à©ô¨Á&›à´ƒxY$e Û쀫xm7Ûá™#µ†÷Ú-¸U?Æ]»ÕP~g– ‹X®RámqI§xÞ;ö¼!`ñœ‚ȺDjg¹¥vzÖMq>©Ó# -i9“öÁzp2¸´÷ ‡¦geÒ“Ñ}h±Ú1Q±5I‡Œ\8‚ôE«þ2 fŸñÐO¤Þ˜ .1°ô3¬ó¤»FS¢ˆô‘Ãj¢_¬:‡ËÿÂ‘PSÙýî€ó†í`„-±1—įä|ú0$rúzÉì²£1CH¢Ù]jy+j óÃìù$ÿâ°d€Ð‚YÌ?ü²¨r”*!”™c8¼x$joñpˆXZwñs–:>»r«É?£Âðúy1¸3ÀGæÔi êÍ‹^X%•%ŽÕ/ú xH¶”N/»ïgú ð¯T‘D ƒSŽüÊ9‡QŸwQ,~­Ëx#6ã†ATªÍƒÍBÜ„ˆhIb®‚ÉO®€’Âp_ ^(Ī:V þ.ÿÿ‹&‹òy¥ŠL+#øsà:ûíNHà ۥô©]ƒó®Z-sáBJ¡‚2 ]Åõ0°üíœú9'3“s9­P©ðù{þ9,ƒæO—û\« pÙ³ÄYHRæÒ–tJ,K‚üðÀ©t~>>VâÓÒ“Ì8. Báðè9 Gó,oÌ;‹7ßwGä09db¶~ %¥÷@‡~˜æÃñùì¶éÃÆ×ó0â¼Ïè×DR³1‚’lÆsߣ”Ûü”Þâ8nän³¯ÓÝ?økhëà6îz ¥»­ûõ…¸KüÀõÝnöçÑY±~Ãp¼Ý4ã^ôú ÉÅû¦îô÷|Ð5Ä …ŸÄpákía<î-xCXfô5¨ljŸ·ÿáÈðï³f~„îuwÃø1¸ê£òðU¶ZPI#:¥CÃi•ðÏòÒ å=?Ä¿xÿuM"*=4^®•ÂWOC£.x˜ÑÿE»7\%o"“÷Ó|Üíz¼}k̬- xô3*AÌM¾{ºÓ,Õ‹tÀ~ñÑíB”Ïar- À+)MÀ÷Âþ>‘ÙÔK“ˆ¢yT·Ebè¯ê¡,±•’ÊïÐ&›`KœªÈõ äÍÛa Óo—PíJÇT£!†Ù[õRA‰Ê-Á€íH7¯mÂÑ¡}'¿JåÇL½jìeƒzZ‰U‰ý²à·t%ºV£"ÿî Üuկ˵ä_×{¡<±½ì÷t:ÉîïÝÚ·ÅìnÛ¶û§·ÞëÜÿÆ+Ü­O m©¸­ßwñ›îïvšfëçîþ1;Ln0:²'í2Uc•‰TuªE»ð‹, ¯rnè4onþ2$ \—Ú̳ÃH¶)"€Æ†%·°4[UôR2Ö^p@iÇ—~È"[Jü ÷rSfÂu§±gE§âT8ƒï†2.v±ð@ÐöÄ*O"Ù›NÈ#á\ˆ^ÑáÆ²¤ž—¿‰ƒ¿Oý¯ù¼Å_”/"M¾^v ™¢ÌÉ ªøñË33˜#ÐרèM(Ò´h^Ÿ'ô.F5@hô‘§+JÔ¶æ÷Q€®îv{w»ßDmU‹ƒîµxãf 妘:ð±§|œ=>We{'ü0,hR)­&'ئ ªvØ€€)÷Û›Õ¶<efRûò Pº ë)°9<¯ŠÎì`_Ø |lhûRÏÛŠÌÀëÐGaœVçwÿx׃­!’7Óÿ\h·¾Îk %ª:Vùßv Fú4€ÎuÂa 2çž¿íWª©Sïõ_*}ëÏYtÁ'ý£â·a¦þ3_è15Ki,/zN1ê¼VäÞŸÉöCâchoVØêÓä¸D®åcI¬'èùÒ ¿¥ÜÔ ³fÚ‰û’b ¨åè|LàPý)1Çz?l*¸Þÿÿ ÉF»´-·Ðϸ@-îV¿úù'§Eêÿÿþyê%Bÿ« è#TÀ—q\ÝÈA^ÿCÒ/ô+ˆ"¥Ë|ËúÞ‹¯Ë¯øØ"Ð[ÄræýÃm„_ðïU4?0?ý“êJvá¿V† G߽ɞÿþ8ŸX{^ñÛùiðWß*Ü?GíAHÎÜÿ>O÷ôù"­éþÕ­«îÒ°¦ËÛ|#xøæÆC;DM˜‘»¼Ÿ%ËAHÐHN€ÉPë`JÚE/}jB !>][Ä#f¶U84é.öµ¦ßéë⮆ۼ¹ýVmï^ºß/¡W—M Úñ2ÓÞÊ+ðWwEJ÷Ýÿýö îî÷»´“×ÄÝïwÕ`šTŽS¢djÛëÂ9‹Û½çÙ ö ·w·Ë•Ú n÷ÞîîÛâé„®îîî+?laÇNëL?w˜úÝ×ùúÒõãë¢{ßJ“¿ ôu785ÜçÜe—ä{•GŒç±w}ÚwüfT/º•WòŽ÷ÆPêN¤ö%·œJ­~ªÏTÞô¦Æófï—œ­Ñ†Jˆ€ìªÐÀ{Ø{¹1¿Í›yjé5 A¹2NÕrîöDZGFvHͧú÷«A;pwý¶!ŽÄÔ œL,@/üKQ¿ {žqÔCbq°;Ù?Dwÿ÷ÿ©8érÉCzU’U ‘s `'×Ï<"¹šø?GM9cæs¥zknÖÕ¤õ“÷¹E&C68èGc+¢‹^6Ñ¡\ÿÂo—Žh ø$ðÍóÝð¢¥=C‹qøCËÆÃ÷»3Áeêš6ÝŽF±\<Á!’­óÅÞý7~T6 L3’iïãúï/ÌC°pÀ«”lFI·çSO@¿–Úšþ„4Áã;¼Í$W¼—îþ(¢¡Ýœ”€„¹º~[l‡?‰Ad°ZÍ \ð„h’”¦±¶ £½VÑ?¹§ Õ^÷×@•ÞÞïÛbÐXIEf»´Å)Cg‚ )S Àûaº~[ Bcîáù/b®ñ3û0tè)ò²~~Ÿµ&6?ï÷ëžð£g¿ï!érpD÷Qí‹kWƃùM­£„°F®¨³ú¾¯%Ž?ÿ«þïÔj¼Á¡vÚ3î l‹¥Ç»é—ˆxU\¨¼ãÉäoš 3gva#º™SŒìÆ1Ùœ^5夯Ë~ã+WÞ-YQrªº‰ICy?DƉ$¥±ø7Á¡ÖrÓì[$ðØ- ƒRÁØÆ¶B‰mâÔCн‰pŸ÷e5ÁªA¨wAÀ` €Ç>&Ý·œîûÛ¤ótÌŠ(8êݯÍBáÔ b½ª× «¥V B`ø< ª[Ù ¸¢´ÏÃç&+¼Ñ4UõÚ¢‹…B¥÷ß½˜uçõ¤*Ì©jù^­Eñû`ô¡´ K’±tÑÜžaÃÎC œŠÕ(‡í(ï˜]D^£¬®úõf"ˆp£1þ)ó1Ù)m :.üwšª‚ð<a׬$ÀÂ’|6ÃCĵ X%P<Ò/? „@-.8\ÝŒÀspè°‰Ù‘WS®Ã˜éƎ󗇑eÿ†!lÀLÎz?¡ñŒ$og ¿ð•Ægäá_Oó Wµn MÛŸhcªÌX ç|u(˜mWV-zá«Îª´O°Å·vAôo†gýQ ¿·âò¢¦D¦À}`Ö•´ ¨„ ÎZÍÌPöô¿ïµÅCÚ÷ÝÞ§êt3U¶;ÝÃØë¤7™¬ ûkx+áÖm~€ü{€ŒOl„ë „·ÿû ÁN»A|þÛõ¦ß_†|Òº’ýþ­ôª-^óJ\£,H¼@;| ÍU‹m?÷ûËi˜*€õÇh`+Ѧ`ÀÁ¶š ˜ØwXÑÁàŒŒô/ªôFµ¾»Òð•éÞôú|~çZ{«¾Ó¾Æv*L™}ü¹r÷{·´KŸýË¥ºv“y?“°Sww»ÝÝßáÒ1»'wcA;¿…6;)ûŠÝÏû»»à³žùx­ìVúY;‘ ½Þïw¿Œ[»»ŠÞîïÖÈ9;»»½ÏÇMÆŽ6­#aVðÂü ‹BheÈÑ¡ÆÚx„fÀŸlÌùPx'vÝîχ¸ö®ÝÆ7‡\{tn~óa‰:9Û½ wÑíñÑ¡è"¤¯×µDGjì÷މ5*èìÁ/ÆhÂÔüÿ ,Ópå©)mÛ„Ìþ¦Hαgþo¯Œ)‹Í:ÑfgC¿¶›ë•]/ýߊlb즩­Æ9é„vÙ¨dI·Ú6èHÅ’­0h.\·‹zRå1nOvÐ}ø‚¾V)ÛNþ"><9Y»¿„5í†×¤òLš÷+à üzý#yòHBÀjÈâYîÂÿ$Ào³,Œt}½´*ËX5°•íU§A–#Õ­šºæ¦*zæÿæCŽðAÀ˜U"á:«h¸Ö$Èø“À{Øwu»KsÛ]àý»äðšM\;ÊWâ`'G&•}âãlê FÄÓ‹ú—òdÉLÇßÓQ­=H‘tÿël\X0»»$I‡ãÖgÐÿŠÚ@æOcôL?ìJ˜gl|o!øÃFTü°DdŽKǰn ÔÙùcH:_“ç­”)L7yí6³æíäK¬9cö)¹m‰”`)†DœnŒü^éÖߺر]ùeÓóŒ-pDÓ7è>w„ í=UoÁîñR*Œ>¥0‘ï­ùȃì½óu0U€ñ iù=úÃ3äë8ÓþOæ)ŽcàÌlÁ–]/¡cƒ•HïûÿŒè×ÉáÐZÊ8ÁxòA­ÿØõ· *EÇ6c™™Äæš*øðÍÀ;GmOí(ÿN¢ 3°Bþ5ÞÂ`´(ªH¾îóVõÜÙ8^æRx" Ž(5`¸4V[ÎæCO‹dN{·woLO]Þ\$@V’[—<ØÖnfÆÄY­b´Þ×­/e|ÓªŠÔf&–.ÔOµnïÖ>1T Jפ—í dZ SCot|4õuîûþ 5õþø%ÇÈLÀóˆBἦw$q~ç.ÚÜ/Á¨¶Ñ’II× AJ°ÑÆÊÄÇ`‹Ë̿ºNíô5°?Ñ<Ë»Ãþðކ|vãZÞ·më×Ê 4(Êê¤8  €$ bü’V˜I_ÿ‡“ɨdÏ„rÊ Z:wš¨|cd³„ŸôØôJÿ´÷w‰{öí»eõÞ¶Wõ¡>Ïäþ¾’Tkï·zÛx­í¦o_z/à·wîõíbÝØ%³r±o~†]çÒ#Ý=ß?é…%c·;oww{סҤîîôÞ\èowq[ëã7r°ïßwx!ú—„ÍW㻄ÈhFÞw?á/1ÿòçCa¼}Ýùù¬e²º®3.ÑûýŠ Ö UŽ©;¾×ª ˆšDEüØí×—ZÿÁ[±£5˺´2ÇŽø*õ•æÄsi²õ1[‹FÆœ_;¥oÁ e@NæÖÃqÛˆþïÞþ1;N=O5c#ïÐ'+5Ð4×߾ܯ]8ÝõÕ}$>‡BVÙþí[&§[(Ì!«Þ†UÉÚLj‡)åÍË–¼b8€îúaܨA·’ßék¿â oW#4.>“PƒxxE¯Ÿyè0ï•ù~/¶8EÂáX&@úPqm©Ì¼ÿø~¼ùc›"ÌdÜ-AÆG v˜r ,Ò«L˜)]áþÁû}|kÃÑ•£­LÆÕà/=~“a€_{"³-®K‹+ñåhk u%GUÈ;õ”{ȽÞ˘eÇYWöAä8Rr͵C+v›•q­â¶oͱ×¹Q8.KXРⲇ¸\9[; q€x^£-2~P‚è­Ð³©c¡è Ïò0ª]UI”Š_ 9èi.èþOq ÅÜ„8ÛNn°h–Êâ.X¬Q/ˆâ ãÀðÿ'ä„û ÀqGþÛ¥í0ÓèÀÛ¬-š#¥à¡B¼~ãvd2žºªèÍâFD­’$eŽy©1„m¹Ç>CE7‹ÈñHþÚ“{àÁ›õR©£¾¾­Iøƒ £p€4ñÜÔ "ú0]î«4Å @æCø2r£ßÜk?ÞÐÜR¼OàêóMUO%=vV+õzê4¯4gSª­sšÈª.Ì`59Ùçî8eÉá0BE…ÃRDÅŒWÍœvÕS* é*-Kwm:yi˜°2ow2d p»0CµðùBŠ\Z«óªÂ¨°ôÅoDöæ8“Á(TB”)íÁc²±ÜÙÃli‚ôèî‚!á°Ä`½‰EŒ€µHH€Êaeh “~@żžàPPh†hSGùÇž9 ¯àö¾…æôc±URQñcÓgÂÚø²¹¤ª.z˜çæÌèãÀó! xzߟ¿\5a#Hoùߺ‡sÑ4_¨z\ðý`£ ˜ª'åÛWÊÓ}HòiˇìüÁ‰@3@ôû a½‡Jì–ÏûøxÿÞÿ¯)@_ÂБ?VºþÔïÚÌBýWæÿ£þ4™ÿ«¯’kµÿ‡`HZ&¸®ÈÙð¶èB.©‘‹ä‡{^¾;»u¿÷_Ý»Ãpq:äöY$q¢ Æ|W=¢°Îò§÷­êóÕ:ÿÇîÆì$ã*ÕÞýÛ½:ìT¾›Üðê½x¦òýÜì5Ð¥y}Þõ¯ ÞúSÃÉ}êL]ß½ï(ÁÍòÒjOݧپ?{¹`XRÝü(®‰÷wwqº$^²Çâµ·ÄÜC`÷÷ýöôáÀ,|¸yJ’ÑÏ.? 1ªü:úïÜ£·c-ŠïÃ;DYŒ§þ˜SotßÔe³4·[o}ÞSÛ”ÞëE_¾‡mÏîîƒw}xŠ0¢Çо®ÎkÇ©]ëÿÏÕ®;hâ©7/ÝÈû»÷ 8øÒ=ñÛa3=ÓÐWa¼ûÚ‚FÍx± UÿáI˜¾ïm6pänÆ~šJ|Ãbÿ¯‚¦íÒ ±ƒ£j.3˜V¶ ;d¹Í[‹{¥Tƒa%&ø'P´Q â É×¥…=½Œ™GÞ<Û¯íY¶ÄMn½ôoûM¡7boL¼öøº‚›ïeMÞ÷׈QŠ}Ñ´#ÅïÅ£8xД~=:4h‚¿ËN÷v:Æ;”;iƉ6Œֽ”dõ#Än"—í÷c5-ÅU 8¥I’V¾5^B*y‡mæSËx^˜ËE¿ Z'ÑB8Ö@'ºPþI JÇN 2(»fL÷ÇÒŽ( FøÛ4‡¤Ð‘|uuóµqG3§þ„dóÅýø`y ¹]ÓÇ÷©Í‘ꆿ%=Tâ_Éå2H\ãl±†cÛ#bY¹Áó½Èªº,'番â(h¾åÇm¶5F§Ý­ñ9?¬å±CŠd<#(5 iIï:à±PÜÂʼ3R#ù¹RÖÙF6wäxýoÏ•FV/ÈÓÕ-°P¯(œ²7I\p.Ú.ZåRq(uE Sñ㊾úM†¸j\Ýì Ñý"1¯áî:©4KgÃVŒ:…Y¨èœ9aРݘ­0z"¶ ?¦(iGµŸ—¢‰È1{¾ÒÁ€?ë3_ZÔ ÜO¾ÝÎÕÅwÇ â˜Ã|©éÁÏ^“Wêþ{ƒô8|ë-Iÿ…è#¤6?o"ÉlÔ·•¹G¤þ΂Nèq– h8ncÃwÍ"9ºæ‘Þ?ßû³z0ÃØ J±N~×@·MÇêÉëvúˆg\^&îiìl3ªlÄF÷v‚x˃7 PÒZó1ÌÇ„¥ .uþOõÛ¥ñT÷®ýÛvRðUm8aYD¼ÏÏØïY>ïWÓ„-‡û¶†÷{×2ßNîþ‡ÒˆûÝß½1wK»'ðLîxìýîµS»øÅtÝ醚gw¤A+ ŒÙþCy§Ugߣµ³‰ë up„a¼?qÀÝÖŠ vì²+¡´ !»Ge¼'ÞîøþÛ»Ö¯'ÁÝßw<+áBÉ÷ÝÛ åƒ^>â\»»•%>ÆXow•‹À¡žOv¼·8ûè.AÓ…¶„è„ü¦Ë‹ŽUÉäöš[üóÍ\ 37aïûȺ¾.ðZÓ{ Ær~¾ÝŒýˆÓtÅ«4?ãÓdç„^85](o•ÚÞÜÿ×<¾‚L;îjR0êMU¡µ,0…÷j¡3VrGë†Õ4ïgVkE÷xôÑÉ´@«s]¹°‹}ŒOQ)d©<ún4©ZhRKB>d1œÀä­„­·ðLòÀC‡ ÅåñÞ3tòã½֮뾼ô­]åþŠÞçôÉê¨R†Í&ðÉ„JÚÈ'©j”iœ·=èªi%Ás"¬îb2@ƬÈBb?'ä{‚&±¯Uß HB%kClþ¿¤žŽ1ØkTí,q!¸ÇF¼ž !Àh'bžù‚N“ŠÔcd²|`‘!áz€· "°¶vMëÎADÑ_Æ´õ†óµö™YQHäÿÔï«qlAV]ʼõ…ùk^é1—¹F©¾‘Z"†ô5ölüó5ÜyfÂÃ8àÆÕ‚q‚û´‘0ÕïÅ¥k¯Ø——Y©…™¯}9»…SEyzµÒDÁInûÆ–¤@Š6:7ëáæÃS! 5ë×Ĩ;#ÿ¦-‚Ÿø ¿ÉÖ¹³ Âv>ᘧ,5ò}‘T¸Ûk3ÅÏ¿ J'¾lÏ0Ó-PNÊYõ|: ÇË"à Ë((kgp:Mh*+H–‚›)Èn£]g¦-Ǻ Ù@( šÁ¹n\»Ë@¼2 ¢ˆV(Ú >_˜Â„ÈÊ8>ši­-6)ÿÚƒ÷Sð+rŸÞ¢Aسj-þd€â¬ÂûÉlAuR‚²vGYª• à~xm9ë\Ó]s:›OË–ƒù{δ‹@?˜`œ£*#‹#ì(Ùœñĸwö¶/£ó‡l÷dð!°@h°FZÞNÊðÅN4éÜ Ù¤ïÍ)åRU^§=~s¿_7ÿûk~9(/ƒtkp"?IÞ²ƒþ`Âø_¦î+Ö”ò—øx޹RѹBÉ?¥Ðö®À¡¾Š”ºóþCWÏw{·è ˜†Ñfÿ†J1ï®<rßœÄ,K0’ ÃóA2eã£OåÖ‡KkBÅ/ IÄ¿ d‹ÿÛ•¢òøvo+2y¤bÏY°cwlqHÀÓΈזæ3ÞüÒü>° ´á#›Bvµï-°ÃáYq¬fß)󚱂=?þ–Dì\!˜\Á 3þwþèà‹”ThQwËBç4³ X./sPعlýða7øù¿­”<ŠçïšfÑ‚­éîîî+«áiŸmÛ}lnÇëA {ìiÂWÁcvà1ÿ€ÓòŽ ^¢=xBå3Ökê‚%»ð^¦ú~æt 9TvÜijgà¢23x¾÷å~Õ½½vë/www/ÝÞòóStOvBŒotEƒÖïr˜±ìÿÂŒ‚é{»×½ïÇR·€fß¼þöø#Ýîî+¹ ´â[ž3;[ýß¹P»ïLØ$ww;õ¢ŽO¸‡¾‡<ÑÜ' ¿2ü'½¿Bñˆøyd†–’£k"60á÷ñ¢n“sÞè#ÛåõXþÛ¾Ûïà¥Ý–‘ÜÓ×ã±ÍÅnïMÿýÄ4Ý%}Ÿ×cú0@ß Òï\iGpXü$™ãBÖ¾gû{ŸžÏ  v û´µŒ¿û{åÛ ¬ D0<iixB–ëâ!oü0¶IÞ½ôî&ô‹û¿p›ÝÞÿ ©Ø¼!p‰Öôþû woG,mù ‚óXiÛzcZ´…Óyçüm‘Ÿ*ýn^Bïµë¼¼ŸÜ¸ÄOO¯¬ËÏ×üÈtšL p•Ÿ/ð„BÁÕ±+Ï8ÒˆTpªeÉï[Šu›²jˆaH¹D¡©ox±vÞxè¢úkÜŒ~O‰Œ03þÌŸÉŒáxÌkj+ _PHÓ‡ôT)GŒH/Iš6m4!ÁȦ¨Ç³˜§–'ˆV/l´'þežqIÁV_Qrþ¾ÿÉyÓz‚š‹ÖfÄÓëÈ¿|}2„8Öš†#fi@z8*÷qÖWnò¥9¯ðœ$‘29µ7ÜkÂèZk”«&2UW_IS“Æ¢ÈÏQâ1Má ËGâ±X‰«íÍÝÆÕ×½³¡…DR€2î\>ÆkÐîb+Ýß§^ §…>ˆ·Æupv¨YWµk¡FÕ5^âXeòbŒi¯ým–É´ýDF!Ûp=ÂCþN×R2÷¾§Kî€@Èî¯Ag𓥪ú)@ò’¯  j…fILsO9Ï9úòÕ–Ìp6~5}«¼Á˜ÍÍs§ï}kÀûwÅñ?ƒ«ÐI¤dÁwÔÙ2x±à¥„ÇŽ ‚©=ÝF.øC„µFT\ Ò–¨©Mªê¤ìZÉKP\È“.vS.+Û f KË8§Ó¶ÏæY€^¸ÂdNÓƒB\ŸòÓˆ|(´ ù¸JŽyÜÄ[„ZÝ ‰Ôï}¯jn$97xöd¡øªÀYõñ'5YÚšr1U.0¡^\—ˆsQÂQ&›KAƪ`.* \î_“ÜEpý:ÎNÝÁ‡°¹ˆÆ£PûµÙJpňò ÏW3×ïü:’ËŠßj$—ëÒ­Å·([¿ÝìÐògyØÄ`NR¬M~Á3R¿Ð%´Ö=fÎ|"ÙoKaÚø~}ÝÜ¶ŠŠÃ¡êF¹¿<8ÏÙ6FJ_a²‹ÍBÈx &̤; øS+`d·P„n¦Sª;Ø =jÜ?Ÿœ)ÐÃð8ÇS}€¶î# ;æ¬\‚`ësn Ž~þT×Éú­‚néÛwÊmI±—w{»»½ïïãog1Ûï»W¾[ÿ‚Ýí  \ùj_7H¿v7}ß{×c/–›¦÷w{G[¢ž‚ŠîYÊD€¸]ˆßwx>g&'ç¹{½îþ^ ³¢¬+ 43É’²ü´MyöïrЬÇJ¾·nù¤D›¼Ÿß¸Ù—¶â7-7¹aá;íï~ÁlÉÕï{ü)”5&:=¸«O7ËàžxVû¦Vø&Që˜E—=|!CJZŒ2 ÊÚôÆÊˆn#@¡þúÖnsÍHewkõ§~. b 7O.”}‚váb ¼þ.Z`n:\¤MJZU•‚†²©O”³Ö<‚;ü£çì6³9O .feA²A|ì!>rl”sÍvqŠ”{¯;d‚¿g¦÷ ¼ª5Í‘6=Žå™0µG|nž*Çš<åêÒ,Eæèïò~ËT›Ñ½+;ØR:ƒöÚ ÇBNn´P7tÒ¹°Ã^N¸ÙÎÈ fê•©\¤îÎuÙÉþÓ¦Ú‰¨ ‡‹ØÁß–HgȱU)BåÕnÎNçe64^ÝyÏõ2)¯‚νï}|_t¤5R[Úx%íe¯v‚¼>®fž»ŠTÕÞ‚}¡äÆ¡"‚òì%¸HóÜ5IPVŸqx×’Ö2üŸ¤›0nDӮ㔡¹v?“Ôª¬æsÔ¡[ àÖÒå¥Ê÷iêOx£qÉâô‘ “壺ÖBdöñ'=,4Ë|”]lÿµâ—ËÜç7œÍ=xfÞÁˆl„2pd0‚pÛöœÇ=uXÕI?sS©3•îAù½%€ôöœ…J‚7ë'… @à\.¾újŸ`”:Žâ±[’8{´¹¦GV#НJW¯$Ã ß "ãfk5êò€nZŠÈ!“PaÝó]õ¥äãí9ʪ.EµÅGí·29ÅKññwƒ[ÅÞWÝDð¹ Ë­ÿ0€°ží¨Õo“ïkªÞœm¬Ó·ô@L Õ©ñ…/h,©bªâ4Ó/þÒÄ“(°p§}¿´z}`QhA)¤O#õ ´Ý[RЀH¾½Õ”S/ðÖ/–°#]ÇZ² áþ“þWÔ>Zm`”ôn‚üÿÿîýb)Éñ5C§Í}çü2ïU÷WÀ›øÏ7ü'ã8Fа31w@€xOÌÔP¹òëoEÎ+]É/Ìô3pËð³¡öî7nAŒD̓@UWüéÄðߌÁZ$á|^Q`ø}`JÓYô1ã–ᵪœÅ覎ñ/Lz"Œ!ÏýjpÒ4ž5ñn+Ã3ã•Ø‹õ®¬hðÓaŒCõÎp |9EŒÛûÓa‚Kîv®&.ŸÏ¤äŸ/7x¯N¹y·}V+0c×öÿÞîN@hÈ’MÂ5>*Ìãc–*ä £ƒ…Y[.ÍVžê¸Èi,ß ÓåY•|=»(mJùÍqïEБ-hÍ¿Ñßjfò!¬l»ÓÖÐbƒÍÎð(nÆïêá-îåfÝuQ²ë*tÉ(ùyÙ.믮·t 7|Ô®îú­„7¼ù<»Ýbø¯»ŽÓÁ'/úù1½ü—>_ÄmÏ›Ûü=wwzWŸ »ü•÷ãà‘³®Ûzn>ñ‡ Ôo~1¥@nÙ¼}»ýËíîõt=ÝÜV+v¡Ç¥¿¬¶ñ^ôNîçï}9£½—C[e÷s÷­÷{¾ª¿“„Ò&Ûlìo] ÞXJŠ6™ñYmnK¥­ÇïÎûšŒüûಂÇDCFá:ñkûš|­Ö4ÕMŸß+y£šü¾xçï¨U­D†~D^Û¦#ÛgÓÞ,†wstå®V¶HÝ•<¥ŸŒp-]¡ùÂHž­†pÚ«_ïæï[a>å£L@÷ʧ·ÿ×φPܲ!ݘ{ǭµmÝD7wÕä¯}¢·+ƒMM¬v–Ñ¡¤4`øÝ¨ÕÖ:µÃ ôþ¸“Ýò±˜õÆGðxioÖ‡ü…Šá͸xn]}ì°7°&^ÃÙSwL  §WÕöùB¹£á‰M&^6ÿr0›±§>7‚$ Éë004+B`·#VF#¬M>]!Î:ŒÑ‚_ø‰ï½>q’2¨ÛÆ`اh¤GÔˆ›i™{µÈp§†` êÃÕÉ4¡¦sƒæwÁÚ5Ê”Ãñбn"=¢4ƒª_%e~€º €Ò›Ö+»‹¬ŸŠÈšOÑYnV;#ÐÂEöWÝÒyþ ¯}ïäC…3pR}Z@ÁsøÁ;gÀàw" Ôšð@Cû/º!À _ÐÚÔâÅáJz„X³ ¸ÜwVR‚MU`ã!Fª.±X»Jª«Y‹b^/iW6o)oˆ¾÷¼Øcçö€êzeó`%á±Ý-Ùþ15"p4ê'‡ƇKH › ‰¨(ù¹ó`1^¡n‚‘qÅâ¶îÑóƒD.¿(ÞjÿßÈ­Q‚ƒýs›"ƒŠ€ïs’³‡°•ÿÔ©›Í#šå"¼µ`P~·Ñbð¼âÅMU/êƒc`¸)c!Ó‚Ò5 € å1›Æ§$F%VI‘&UL@)Ì¤Ø 0FÈÔ¤ÂÆ-CÁÆe·'Å.‰®mÂ`¤ƒR')Ñ_e*Kž´ž ?úZ…±ó‡7ñ²ªD*¦¼±`ß'¿óâô\7ÑÿÏ‹¸np|ÌøÜ£€ða (>U>N¨:Ôáûjß 8FH{÷ü7.+·TÞœ¿|³g`ΤŸ\96ˆL½Ömãg;Óü-LJõhbawRÜRJ‹Â=÷U#mÀpHð¯[‚KsÂzÿúÚƒ5Ù|¿ÐRp úºH`3¡2O1¶2‡„=ãÿŒxwúÞ7ÀÌ/$AÊÝ÷Ž€s‘L½?’´Ö¥á>î d cµk'õV "¼æ/»›ÑUå÷ã.øÐ¯ØÊ"Øåx†Ñxì#TÐç ‚hÞï^p»Éí}‹½ßrÇÁ*wÞçïó3ã¶ŸcÓ»½ÆmØ£G*<„ziøQÚ ÀE­—Õoî6/¿·8€ÝNûð£knïåïx¬€ßaK¼gÎö+NÞîo¥¡‹ŠßIî÷w¶ÈÆEotNîöiåÂN! òá D%»ïÊww»~„'¾îò}Öÿ{»ù·‚­'r±Â½ÝŸ A[˜Çe4»ûb›+íüDkn×)q qö^ ªš³ÛûÈÕD‘Óå¥ïŽZbŒ1ˆÊÑÊ'=ßlb‘¬Ì|™¼öóÂÍÀ =öÌkH ùºÜDòù{C*,!±¡l}9XE – ŸZNbP&úÒà¡Á9›«½ƒ}àyòZØ]µ½D1͢ב_Ê0NgÍi#DUVáÞv[0‘‚Ç)»Ýv|XÃÃýºŽÓåó>¼Ì·–û‚†Ü´b{¶Ìž¾ù>ve TZá°&-†³á•M²j¿ý]“û¼³0΋d#wï ØJµ)ƦAzƒ²˜¢'Ü"~ÐnË+ÿºE v(þOKuþIü ]ÞïtNõÈO¾ïR’ۿܽe'ˆÙ%üÝï†OÀ–ðÒ+$Á®²è@±HÀ]Èößû Ñ"äW WÇEà žiÍfàKÃ4«èÿqbà€~¨"½F<ÀU[t}!ìÅ€½%AŽ·« ÈÆµ¨»ÅÞ«%úý|8±†ÄúnÖQƒõ>`$4aœi÷G–Ÿi!ÿˆE «¢R$KÄôèßGø¥\f-€Gú櫽>¼$Aó!ò)›Õ„¹d €JÜöÌšÉæiUÉ !kwB‘Ç$IWºû+öÀ‹nízÂx 6ô{sLðb`^«üðLû~°0[«Òë‡ü/çìv‡s*Ì%CüëwWîÿô¿tàÿׯÿ6ùiÓÖ—ÿýngH¾ë—çZØ›÷+ú÷Á=X”œN…õÖé ¤Øun†p†)ç€ Rl@¼m Ã-쇥u*Ffz: ËLx‚h鵆ՊMPþZ¼·¿BozWÞ›­~/»zo{ê”'»ß+«Í{êõï'ÃYþµöL þ;oîÓíÖ“Ýß¡˜:€·‡ì;Ç~îôÑ?üõ|ÉÇZ¤~û/»±øìÚý–®çϱÜß7pï¦äÕÉ¿A}û½ßZ8ûîîîÖXv2îîîåç§MÏJ_Ç]÷v²¤ïØË¹rÜ„±FìvËmý N\ËŽÒHš95ƒ"ØÎïÝ £Ça4Ç/øåïl> p«ÅHrÅÕ \øÔrø‰{û%îιeý»¿ˆÞ|½¿z áî «{‰™úµðï€óÆÈ.J3»é߯ʜ°é+¸ÏIø~Á"ŸÝ½M“õ-!„áùxˆ»^KoQÁ ò»´›Í˜9>i¦ÂpkÏöëøŸÑ¾;œÞŠÖŠVPLªî»};ï]+F»õî‡"íuv+ˆg½µCêˆz¼¸ÕšÁØ]E锜•‹Ø/W(ƒÆ@€;%ÔóûdZÐ)vBä@v[ëXÀÅÙìKúpiÿ ·[ŽŸìÉõõ¡¡àÍþ ÷ ¡!sn M5œBB Ø©ÓÜXø‡¦·² EÜ€ˆ »ùJW¾ŠX‚§~‹Ç»»Þåç¹|—{ê_ï,®¦&OÏý˜I*$W d’Õ¨}Lõ¬iÁE`ƒ"f*V»‘³44œ–µ@ÀEU•‹©hÑ`‹øà™=âɲ­:§µXH‘Ae]sà¸íŽ,9”€ƒ8:ã*åêì$ÍÏü"pP˲@çP;Ý´5¸.J¸ëÎs´ˆÄ 3Mʼn)aGZ—ëU[ýéË•ZöíA[Åë]j[x©[Á¸å­AKT°v zu7¨TÚyFÍu¹0`¿]¬¢ËÌÿ›Ä²“ýp´ γÿÈÆ%ö5´ñ¶2–úu0dÙ)«P÷ŸV³?‡bÿT¬ÀoÀ‘œnÿ×pCìó鮸k€ïY_'ëX#©\·7×¾ÓTbæÆܔԆ:9%†óáŸJTYÿºËü7OÉ›·ƒiitœrbŸçõ§¨š„sçLÌtß'˜šE× D óc½ÿú¯`@+ëekÂØø- CØÔÏ xc?Wö2ËÁÑ÷(gèÆŠ5ô3jûã]„®ßw×ï£lsý=KõYiñ÷rþOçý¿ƛtíuêZš+x¯Çeód¿Ïþ ÞûÁJ¬Zâ·¿°—vî¸BîƒÝéowŒ¾÷¿>>ßÍ»ï”%{{»ù¯}vw{»»Ýß<"Ù0¥?ï _}ßà§q•»º©ï[N‰{ü<Ÿx+Ö;YâbØâ²áÅÝØÆBÿà•÷÷}ë{„ï{·/kkmYo}Þ*íwOOY}æüfø+o§ K2 '|~5ão·g B ¦Ü9Gå˜;e"à­6 „JÄ!yïãé™@ªŽo¬˜x·Û‰ Eͺ±½Þ8¿‚ÐÓ¥zÉô׊°ÞhTâÆÉþÞ.Pñ¢Æ`ÂWÍuâ™]às|uóéÀ›Ó!ÙøÅåj¬©$oõpaÛ3êˆ(Ï£?jIèú¹KUŒj¿`£¹{Ë}|Nöîû\ÓnU®ßµX$¼æ$I¯‰¤FGEõ¬îkŠ·‘öÝ·°ÞÇnTM+ÐXÅc×Êe‰D@{§?ø# Lb[« a¨@X8‘ú&×+vü !ùÁã,š-ã€Ð¸‹Æšú‚Œ?ácŽþíßHáÈ_AÙ“cS¿&Òáè€öd½`Qôg5h<ØHhíÕ¨,tÇÑ…(Åœ`œ°L1£ƒ¦åîlL&³;Fliïü.Di$™Ì²äR‘‰¼¾«5î÷XN+~î™d Uå_­¸'Š-·Z-BO, G ©ŒZîBÄX¹©(¹CRs¶H|X1àø6\Þì$Vá4ÍèÁÞÁˆ›@­€àHpPZËÁÍÈ(“bi®O|„¡¨²/߮Ӳ‹Ùæ¾Öüf¥zýðzªOâý!PUê ^)ê!ü9í!Š””ôŽÿêïx•Z9¯ò ®PçiÖ‚Ð#Lu¼äH÷@Ñùëÿ¡Òê«„Ä„+XN‘.Æ’©AP+€+6ÕqŠv†ôÃwFÿ›¾ûÜT¬µ7·nù?}Bâ%䯼‘‰[z¶e0æÕûW Ãîµp3û  ïê3ðžrÖž¿¸%c"_xÛ³î¢$ŠnIBƒß½ûoúçØºo|W]!]Í—·xcB´ÜÕ_z*â·|x  à/…µ@´`4JD¼ø–äϽÄȾù1­SÆ=eÌt^oDb»F»íº´ìŸ'«ºõ§ÄäïD$O± ôí¿¡[»gÿѬœBÅ;—@Rû"û¾>Çq ¹ñì_E ïw»Ôðÿá;Ýïzè~tÛÝ+¡Ñ6w–Žý—´îÿ»½ËíÝÞ¯^åÃç»»úw/µà–Üš1]÷Ãá;Ú½:ãöÃNð¥ m.:dç´i5Ûૹ0*ʾÌiÛã]¾Þüù½tWŸ¾¡$b$h Ó¡‹½ó䟮áÈ:yg:_qÚ %aØ”– ~q]Ôávÿ‚M@(!Þ)ÔM™InBa‡8¼{S´›Ü/A׊Þ?DèßDV"¾WØÐÄ =ðç/«*ñ^^÷ø.ÞÒWËïw×–÷ù.H«g,R.w> â®×„nk\d0{¾Z3¤yÊÞíKð”LˆCt?îê6sÅ`W»£< ÏSÜezcdöfÙV+‹¾Âv¡Ž‘ÐþÈ;3öSñ‘mã¾ÎOëù•ÄÔõ)©7Ê aМ{kÈöpô·ºL/á°>“ÀÑNDQ·K¡ÃpÖ;žZ½íþ²A3xF¤ w¥ŽkþÉûj£ˆ6Iá5§Õþâõ@¸Å “íÂ0¡8 Õu¡zIò¢ìüá}§ìä+Ÿá®-mä}@[ÝH ÿŽ]°VT#Ù»ƒpÔÄu^ƒý˜#€F÷n*ä)9÷zùê#B¿p*ߣPÒŠkà k{éD0Õ`bÖm|=-)Þç¿'…–B8U–`ë…ZPäMðßA6øšÇ6iü$jè–Â1UÇçÿ¬°ÈÐDÕLŸžƒ-kpkÛô"Äþ$p/‚5·ŒðæÕÂâ4@NÀù˜±˜ –ëš^ eI1 y‡Úl2zúü;Ó{ªê×î/Ç=³"ñ W½ÏÁ-T…£IxêèΫŠÞRظÖq‚P ÝRc-E@[àqŒ\ð?Ænñ̇œ?i5†ç@¿ƒñe¦n‰•!šºI£9rV.ÿa;±ÕòûòÝèò~šo¶ñ ÷{¿f´iö´+{¾ýÆqºW.xËí·}îîúÞÓÂùq÷}Vÿ“ß®¸&»Þ÷Õ®AåÞïNòò÷\>ÆV«òóþëE{ß™]7½¡Óû…Uwî{Åp\­åÇ¿øµ†˜”Y¾ORw}îè €ví]ŒkÛyÞÞÜœsêGtí´; ð„PU õ“¢ ‰¹ÿÛg±8iUn#~µÑë»$«“¾õøëéÞîƒ.=‡Â[½ß÷»Ó\V}X0쯪}0U8ÚFO`ó<²=Þ–d„¹Wܺ[oé.BøM[xÞD€Â)þ7û.uËÅ7ie~cÃ'|ÀÖ:c¢°»ÇRƒkœkî¶@¨saðS);µNÈñÑFºªI±Ty (:‚Õ|Á ,CÕ«†eÉ貃ÂÅÅaaŠ8)‡l«g`<Ú#:„€Î#ÂZAkCE.)>Mk¤®Æ•9^Ügª/¢ˆHtkìty–éÎLAÇçìXRÈÅX'PÒWŒõlV9ß3VzÍ%„Ió^°L•ý0'…$˜®BRZ¾NÏ`57t[É5y 0qB6³Õ“ k¥‘äwC‚Àˆ AMÀû¼u ì¿ä*Ïo/vàDÇ×®—“gØl64hò<@€Úºž×ÿˆÁì©­àÍÃð}îÄl›øƒîó¾°ÝÎQyÈoáÁ³­çò>Îô3?¤ä,šèÐúnÔ `7ÚÛKŽ(¾öÿåÌËU¨ #@‘á õâOw¿kâÉêEÈéùI(ÿ±{ Q±ƒç}HÖ‘Òܽ%QƒÕ\°êÑ£EÕdù7L,ÕcB# Cê{™µ¼ºÉæ5è €øQ½¾hÇÿÙòzúŠR¬‚hÔl‡Z…'ò%ak~ŽÁ×òCIë†OÖ¾±+!>+{ÝÓðG?žÿDñ5‹ÖÚ¾ »¹ñà#MÎêr`›°;^À®“NÃg @›°nNÍgÂ2žŒy1„üë+–Œšà81Éý; ®åM¿À7kyì ܈Ýýß±QAŸˆ~÷™'AwcOtB®pÚ2å÷úoîâŽâGÜVï{õé«Þ÷=QKÕ¨º{ºV]¾T'Þ´&ÉÜV÷:›ýi›,ojB¸ÅçãDÚõt]ËK\Þ+näý_mÜV {Ýù|«µ•#®[Þ]{]|’3/Óú|VÈßsÃèz ¶Ìà@ÂÛÒ×שd6vÊ{ZV 9R¿w㆞âü2JøQµöŠÏ.¹>ýÉw·©7½}:_„\\_VÉ׿ºgû_ÚÖ˜ÄzCO?Œw„îœ:æYÄE*1õ cY‘`¶ì­Jkz¨'‡ÊØÄP^s¬´ýÑîb"ÆS¨”÷Ü~©4bð…‘Užõy&+¯a¥b«'€°™,‹R åþÀ˜rTq¾°ø°s†+é]ClÂÌÆ@*2tÄÍ‹»!e‚Pœ5HS0Q uÁ`fZƒ*…íl{eáÃ0&¹Pi<"ƒaV( „j**æ󶈧’¤Eà¢åvŒ ŸA$À­˜i*æ]mf.ìêaø Õ?Å?Ïâ'%¥ó)±%@ÜL—‚Õ mÇÒ‡ÎUì¤ìK¾á›á6D-ÈKé¬ù£½‰- ’ñ‚8B.`z» f`¤ý#53ÍösF3ÕLaWï‚Ýg`/+##ä‡~c ðJ ö<UŠ–;Ëò{Bò´Ee²Èõçšh˜h&½A¯]x'_óË›­ûbŒÍ}¯óãqTð+¶-ÔÇ¥é÷Š÷Ôk"«Ó›í9ûY¸_Ñ’ÐJù·Oª™í)FÃ0pðàxy×@‡£-JüX¹Uš²;hyø/þ:`kM0-•åáèsúxt:û(`@ãÿœ£n¨9Pp&`8öX½/›x$| ¾”Ö›Ój̯[eÿm¤ÉrÛ½¿»ö¿·|ÿ¡ÜÙ›ïwðû¼V÷{»½è¿á$ù{Ýé¬N›ÞžOº¬%{Wi7Ú ·ÞñU¹ÎÅÚúìïwŸ8zÉ÷ic¬îí<°yé?÷A^ï‚N[ï{E¾_âyfî\×MJÇ/, üîùimpÉõô1"VÔKéðúý¦÷Áé¿ø(ª•`6?×U—‡U< ÿnÞ¹^?E+'´´ÞÝ17w{Þ¯.òÖõÝX5¦m3ÁñMñ®äß…úƒ¬×ð_(Ó‘ËßL Ø£,ÍÑ&ÖóÆYW?Ž®*úåõI~(þ ˜hzýódZVVO,’RCÉvæD0ààX#‡ˆ¨ ¬æN¯ÇK ZÄ/Óüž1DLÛ²&qï“UÀ9=Ÿ|š=™F*˜÷§b¤1"1zeÏþO   &@RÄ@»±·%(üH9ä'ÿpùøûždÆ‚¨Ù©|Á©±Ô@c> Á’tI^5á­ ©dez…2`„9Hñ ÉäÙP™5o1žõžð&½’ÇËÚ‘·ÁjF½´H" 0g»ˆäĵÖ(˜ —]Ã%æJYŒ –Ù`»É"†Ol×–™jBg&j98‡AÚP€"ÔÝr6éõ(ØGž%«3ñ F—3-6±h„N³”2ìQ¨R­šk“Áˆ-h àä64(„¡CÏ &<+Dî%§¿×?Ê úÀx‡+š>…˜,ÓB}™bÞm[„õ÷þÿ½¿z®[Ü”ŠEüÞéÂ'J›[$ÔG—ïqöƒfÖT7ݸ–÷ÜÕ4ÈÓ*Ò±|¸3P©®ß„nÛñùpú¡TÉ©.¬C&@H}+rAòõö"ò¼Ð=§éÓw7p»[<ÝUUV@¿ö®?v,±­\Úç’ Z7$”î%àƒñýs×>‰‡ï£÷uýBÇÐ"f{1í{¥'dÃ(öê{}uo5¯^ Ÿc’ Ư ·̾1àžà/ʽíz1`o…5à%å„x$ˆÄR0A¿°<ÉÈ!¿¥Âlq+;xþ•Í׺Šçþ~¸†•¶ä¡_W‚YÇ×tì®e»²~ªæ±ÐcÑ)..ñ[¨ñ‹/Ëäö÷B‹¥lØ}gz ¦™ EÝuëM €c¯ÇßZûžæÀ‰øÇò-}Öi ÜÌX>Ár—šÓ#5¾'{»»},¤jûZ-ï“×­ïrwã}ãû¶Õ]\ªÝ7½þdîÔE£9{ß‚haÅäâ½ú~ÒNãO&û?ÛaÛ»å…ÉC×°ac¨,ÝÄîq¦†rù¡ðõJ6>…¿ /¯›Þ©08r=?=×f¿¸%gþë}ö·÷ûäŸíµn#yPš5Ò N¦:w•…ÁMíîúuÕðSº'ýÙ¢¹A¾`DÒfÛú¢xа‘ƒ‚b…¦ çvš7b]>bŠˆ¾6ï ý­lö‚ÁAªwC*3€(ÙLEĘÚ¨…xÑ>¦p‚$vøN¹¦qæ\)\KXMèšÉ„|ÔHÙØš;Ì'@Ü&Ò®ª‡Ð@gŒÍñëŠäB¤BCè@€Ä óí€R;ìŽAÃÚPò È ¤‹@±Ð_ žØy…„ÚL«ü0ÔÅ.Mã×þˆ%Ã/3™!¸·û¾Ûmà-.R]!c®ÃQù„.\“e–"4«ämû Wt®úŒžXdA­Ð+ô„¦XÒlœG|`èGß—osO¤øJg¿ÂébóáÞ„ÿ»ÅiñÔÇÖ ~¡=ÖÞ»äÿ|$J¡…¸K…²°`ÛxCPéa3U=¢É¿cŒ%fÌÉŸO!Sqìmúc4Q2ÓbùQý_™Ôh‡°×ëP¢›¼e¶f—¿ÐØÊ |hÅS³>o—C‹JàŒøD»Ú½{I}yRx÷‹IvÍ »¦$¤h:ŒIš"Þsíô AG†=åÏñWRý‚tä¥"Û|UvÄO–×·ojF!1„ýç-£îO¢‰Úïµ¹5i‚VHí°3$.X?±Ï—¸®+µ±Óú¦ K“}©X&¯¿i÷±;Zº3åß¹[i·š-öDî_÷쌕·µ½éÕ°J”9<ã÷Ÿi_²Q9á­†¡3^ÐÂz‚_™²c‚ië“åOÄ%áý¢€þ‰vŸ¾ß°J[Õêj1Ó—i?E­ï#lWû"{ûkr_y>ÿ' ¦Š E" Âoôú)wûµL“˜½Í·w»Û»´!‡n_ûöLVïØ"¼¿¯~ý’ú÷½²2Ý?~ýûöDÂ9‰ÞˆHýˆX!fæ“Có­ÚÒ>Ÿ± ÝÜ}ǯÙ)äÛ{sÇFz^Þû3.™_Àßd»—×}íÓ|ð˜@uÓéôÁ Ew~Ÿ²'FÛ-7½ü–;AǤñ üÁDt‰UÉž{hLï.÷Ù‚ùò°D@ WW#þ<Ü–w·.’zjZd꿺BŠO°¹zÏFœ6S7ä´¸„Ù75O9Í¡—(#ØAÑÒòàXÄu:OÌüCFQ? k öÊY_«âKe Å0·„p¼ª¼8Ø^YLÚÓÌå(Ž/o‡/ Ü7¤£dìHjO ä”iÙ4O‹!j€dö†fJ•Ý­H;ØSïùyK,cE󉆼O´eÀ?¨S$Fêáïj0{ ž ‡ôNÍ KÜ¡”:ÃÔ›€¨`#ÆÅÑ@Æ×Ä𪹄n&D!ñܧjÏ 7ÖÈ®›ñ(¡Àaº[m ¯L‡R3kØfÈAò BõL£ ŒOÞ› |*ioðŠCNºí‘ŽFg(Ð2Ñ]ãávÚGqGPÂF*ßûœïÇ ¿á A†ã°‡©ý#Ù@*Öλ_Ÿ,®"A“ÞÆ ¢&DE3‘¤aTAÔ=¢.YÝ„@sc·=™øq9¾,Oíè/KûöûíÐ$mϽdóTˆ0X½q›ê¡T•<^M›%Î!íž^º#B/ãqîà[¬2"mò9ŠÆÃw®v>Ûã+¹ ` /”èý×l ¥`Î%úŒ ŸùšTÄerÙäÄ8.ÿôõK×âÝÆ@³:‹) Àœ:ÙQ£5nÛïͺ¸–óëáÙÍQJ•ÈÕãï"^Éð"¸™¯€D<¦ŸÖ(]Þ³!¸«Ì@jEÁU¯b6dÑ›¯à‘ 5ƒ’Ú€pÚ¦R;£HÓúi؇Ó%m•å*D0Z„Åñd(“ Gn–Äô¨‹»HaH(ØÎ:èáX!æFüo‚-3VõèïÀ—tÈ…Kj B ÊEºA}ÜÖ?3G¨%e¸:MØ€¦×SÐ6 G‹!äd+àÆ¢ ¯bY‘FÒ¿-·‰:^9bjîÏàNL×#6Ü¡ulYf¡;í8)MxVn+íðDª œ˜Ñ•áµÛ»XoRëê‚w¥gÑŸÇáÇG°‚xå(5Ÿ?á :‘æk›ÒÊ ÖÔV\!@ìŒÍ•À€6fäƒPÛ· ›¦ÉI°%)šAŠ)ƒ] [zYqŸ0¡c „h%k!м­„oÛñcþѦŒóøAÇÂA#¡ž³Ý«CJzϺúC¼ÛO¢Ç§;ä(5þJûáuqâÌ$!-Èö者0s©™?/òÌİw6$lw·1VƒÁY–„†ð+ê~Ýö\h4ÌÙË“ž'½êÃÑñŸÞôF´Š ÑKá:ÿª’ÑÚ?ScÜ·[¥MæT¡~ñh6‘ÚÐX R§بÆ:4Š©Ë°A›)¶ºc º]UþÎ:¼Eæ;5þ<”A'®e¥è÷(â› ¢—ÝÃR44{qĘDuR¤ß™ðf¯ ø1\ÓR7Çó1’ òÅèØ 7FŒ5úàƒHCCR{ýK0±·^‘6Ï¿aÑàîOàï~d„³ªP»ñ‡…… _fÀÅ>{8Ö`E‚ `ò¥/áõ8üFäýGò7`NÑ›A§1°¡ŽHR£Œûªîÿ•i´ƒ*¯„kÑƶ·?þ Y  rx¢NäÃ~ô{¯2Ò]`ÞüC/>aéM€Dë™®|¯È¿ÃÔ¨®h»K¡kŒP¯_‡-øi ¥jœÊÐãÄð%o?±6ËîíD^WýSîmÁZ'ˆÿ§ñ1÷©Àçýgç^¡+WËí.¢ÒjldÞù² ëŽ;‚²z:x‹ìŽÞk_éZó"zj¬+U¾5hÏ,Ç„d'|§Ò# NüÕüG€§Ù»B_0Ÿ¾(yÆñ†S±JA"H b»ÁfÄn¶zã® ùIÜ?K BäÑ/Vy¹Ý>c¿±Pâ „$dLw$vŠ–-Çx‚Ó}³)[lýOKß°óŒŽy¿‹pŽÝª%ooqmÁwP‚"=®þøåÄ•±’„DÐ(-à‘L}ñ¢[9yúI #5Óm¿ïÂíÛøAHdü<¯'ãð'z_åü rÉâÞk—¤{±[rûïÞ0¿[&„7>óÔc#×Õ&DûXÌtA«—¦Z´ßAï:(ë#èìýŸLã6ÚðÃW¥ªÇzà}#b¡€ÌM\{.{ ·/l›¶°Œ;w]t@ev_, %J hï~=W„×SZdƒÑÐxÑ¡fÛàßœG ö¶i†xßA„­Vx²hf/› ˜À*üÎò[Š:YX1½´p¾ß=.ÐÆá9X|SÂv&¸EDQ„!ÝL¢__šœµ+ ºh”>êUû_òGzö`¨àμ±RcñÕp‘øã‘œ³Ú‚tK„Æ«iñ»?Äw!÷¿{v¿}±¸Å7¥×G‚d3;©Oû ~ÆŒ2‹øð·a~±ìsÅ/¨ª?ëI"ü_wëúï^ÀÞߤ6³}çÂXÐ_wPDÙÏÔIÖé%*Ž É[q‰ %ô8=xHŸS(ã+<‚ñ†.v{]ý¼5 ¾?–Æ9$žóîHÐÇ s^ŒÅ-€ÎÕ”ý ;_ {á{ªp‚¶å'HÊ_Æ XE£ïsAar›mœ ÀÔ¥ö…‚æefCé7›÷g è“Ê¿8>ãÔìcQèiéa­íøTÈa["AükÝ@:ïäÍâV]¿ÃT/>#ìä/ï…Óº?ËRx侀uf¿®ž"¨[?ËË—Ñatñç@@¹³.a#/ïͶðWÀì•٥̘+>@ÞîÞWÃï }¬_—…Û¡¢B#G‹gÆ3f01fƺ`ThâPîê…™DE <®Œwõ¦UMB@ª X ûò3Èe{ÚÙï”NûyÛs&wѰË_O®¼,¯ðSÂ?ïî$Ž2r÷—ñ°p^¦Ëp’# ÞµêmƒÜd\!Nœ@Ž8vrñ-¢çR("dl€¥úüÞÔ·ª,˜<ð0ûh™ØPDÄr¨H®=HÎk@áeÝBhrù­>‰«£ó–Æñƒ#¸Rpžï]>óòÞ‹ …àô¶ÒW9Z}<Ÿò!þ{Ücv,Õ5Z“ÐFý¢Ù¬à  ÀxŒÒÃIpfꇳ¢t3-Ýi¨æíµ ùÒÐnÚeý/vPHýns:CŠÈñÿ½ŸÀDÚãÜbo!ÃVþS|0„q2ë”à˜]¾Ò78›­ÒqŠÒâm5#ÁH¥‹ŠühtjÄJ ´ôîDw Ý …95 µxY¼0³¯¼;õyéóþ3r3æ× ç²PY_ãwX[ßäWrWfW¹ÁáþÿÉbÒ F0þ_qt’©‡0ì»ÿ##MÛ¨X{"ó ! @ +Ú>gx“#â‰B[Iîjd1j޾³#»Ç3ÔÞË„5ƒ«vVínù#†T÷/¯Qä"œ¢Ù.˜v55‡Ž~ÂG&Ý=d[ïß!QR½ÜŽht•,9"„=¼Ùa"7Ë‹ Ž‹a3ºŠ\& w±Ùͽ*WǶ(xEVçñ¥#šx¿¦{Ä@`y%¼ßÁù JR2›_—É#éé5õ/ÆY½ý ;ˆ‰cÃ.›‹ððK)œ¿ÛñZ°‚ÅöŠtÍeÂjøâqÍÜSÃ'X$úK˸ȤiŒ=dÛ{ƒAZ!nÂ-:-FòÃpuįè:Ò®gø‡ùñHt ²på…9?ýŸ2rX³œ¹,±åû¤ií¹ü«1MzË Ýþê ã:Á¨mYÕ‘¾·ÏÐ0ª›ûP›ÀÜ‘8C|Y0 ?Ceý D" >Ü«üÐ…†VþiœÈ ɳšÜ{‹F»:øèÓ“î‚êþ0B!]úù»êfoJ™£…ë%XÌó6þ]O~€ß¶o¢¿=ÍÛH(ÐÕàÎг±õúPDÉZ¿Ø÷iÆÆOlvÍ,‰ €¹^!˜4a @ÁbàÐðT0ÿ.JÎ܃ÏÈ©Ùq·AÖ¯‹Ù@ÆF1EA{¨NÌŠ#†^íˆðÁñ $ pbfpÄœH¢ðãí¤{4“ BÌ/…‡bÁG c¥«Éò±Í0ùf0ace¥Ù¾¤oƒ«i^çòA]–¯É K(&˜ à'5ð åü_Õ| /!«ÂÆr:ÚtÜZà7<[a÷Z‚_Ëy¸9㛆AEžð±23±3ÐͺyÇð¸½èÿQ¯ÊÈ Œ¤…иqÔ4jvUŸêzÍëvþ Ã¸öÀåÊÒ«²Æ»jÁ'0’u$^ŸkÍÛ}çÿ‚W³tl)A :~!ØÂ<<ƒ`Ô2]r° 2"h7ŸÆ´óƒ4óÚo@ç}Ýú=“Íý>MÐ÷9z¼š]ìê Ù‚³%7ô¯#B;Íþ5gßñŽÊð‡€Û‹À›Ø¢wXÉ/ jÆ0 MHž+Ÿd«àOV6xÈ É“ƒ«9”Yd²àÉ4¡6hþ¸èHgQ5å~aœ (›QÝ÷Ðu’Ž}3[òÔqBŒkO´cŽ{+è4 ™ÚˆÇqÀ^»'S^êÆ/¸oI Bbì’·MbÜ¥`n|D9Ð5ÉW¸}˜OÚ¬T¾ÒŒ ˜IB6†JhâP—¤Ô©ƒË’êÚ]p´)À®1O ×,Vcž,ß@Hf¹n;’]URŽØÁwÆ-„¸Û½‘èM™4ÉÂ~3ÁL8Ðîaü>}Ápqu/Îñ`LÖ| «¾mûpƒ`a.ñ¡§C8JáäzðŠáUq‘š(0=€W£@OÍ5 x:_x=3÷ ¯Í#¥ßo‚çCØ|ñfˆ4Ɔ4k˜ A)tU¾ÏÙ‰Gè`Üp(ñ¼÷Ù…ßÝ™sŠzÂõû¿F/0„ræJjŠÁX e~ïØå,ä™3'‡Hhñ¶žTÁQ±…ÿw ©çÞÕ §QŸÂé& 0°B9£YìA³Z:·Œóì¦-\žÖ6×òШ. ÒOÜ@ãÙnã·ãÑÂûB0Ì)^Ò¡Æ3ꪟn!€¬¶î÷v/àæ,ÐwÚ¼ÿA×C@2U†mµðÿ© Ð÷?|S±—1¸-Ó¾±ßHÞXt» ‘´¤Òà'™"üGžÿ§×e&ÅŽì~`F;†0' `¿o9èy.†T§á{móŽ5ÏÁ¿¼Å¸’¡þÒ‹ˆÿx¹f¥.Á³°¤ü[Ã~FMLÚ Ûà¡»¸;w–;æ ˆâcw¾Å§©SA…­ßˆpþ$h»=§)Pø˜€æ­Õ¤ÿ‘&J…F…ÁJæwøµáa¡Ð¸€ ð‘F­ËÊ_.8$‹tÀ÷€”Šª‰ÆS¦ð?ñúÓRŸáw",<`@(l¥/‹}] ª’UÑiXÛ[ÌW}ƒÕu mêµçñô ˆ R¤‚½–õ‹N2¥û#”zéu¬}}æaÔg ¨@n6’PlÖõ~O(Åk†8ÿÙÈœYê¶a æÑuJ¥ĉ3½pœþÚ»ŠÝ*Aƒ[~.™õâL¸sŠ1n\wâ¼CŽ|øŸ†mð›OaÊv:?ʈ<F ËÃŽ¶&I„gYéö(€sSV¢B>IiïbtžØ²r|qi÷ËIû·Az©†ÒŽi­B‹IpözÔ± ‡G$`”Ér ¸€W9‹†Áa‚‹ua'±=´…ªiÒÞ…+ i1  „À ¡ó]PÒaµ–5`µpï %9£@`gÜÃÔÃúC–Ö€߇·JãæÄ0Å\+ÄhMc=u™É0ÕÄmÁÚ«q¥b èbtU"Â¥u½l@*…>8Bïó?ás&k>&19æ7ó79 g}ì®NÂFnq«ä—ÑE§«gJîXudƒÀ´†€ |¸³ÁL2Y!!”fN\ ÖÛÈTÊNAG* J ‘˜45¥P¼ÁQ¢"iïÑvÖB瓨ˆ¤íáÂì [2h>Ù€  lÿL—¯Zÿ Õ uÑ#ܽr^`欯yQ·§  Ø=೸¯ºQ­vý.Šp¦ „2 1]ÍÝÜÿØCHàb.¨€„Ýõ¾«ÄOl.ËÕb†Å)ä7£6"Má § x$A37ŠŒw¹Äpé—÷q¬ípïW­“vnã>9‰¸¢ÀH­©u>®¾ãTf6k÷Í<†Q!Oüô©Ìë|`¸ V#*ñŽÈlNÖ€'ÜP °" #ç¢.E¸É%À7º5ï\%€„7tÿ®éíÛÀ›øê:ñä¡ïçy‚ÃÓ€o™vÄH_ÿ¨@%È|x%AM˜Ÿ?¸&xnýðn$ÿmÿn(©!Ña‡ÆÃ÷b3É}h}AV%•#|Z59»ð´;©ïÜq¸Ýd¥¯T…ËØÔ6œ¾¯·ŠÆšÈ…¹{½ŸB¿ ·þ8ƒ"‡Âàqínä&ϱ'OTš¢ÂãµìQÈ$z~$Œ¥¼\¼½!QT±9ª«ÇpzÁé`àyNd{Š7qX­~ÂôÐrD ·Í‚¹j¾Ô ÖcûW¤å~mÄÅAH>‰Ü üT3ò½qÉ›k– $T‡Tx:Ê=ª,ŒÙÖô£²¶ôp»iâh$´#q¨J¥Ve) ¸:ÌÇ3šUµ K¬x ª2U÷fav3¬x_ð‚Ž*/þÍ!y’ [ꔆ „ Ô.ÆÈ¬/ç€âPUðfÏR_ ã:p0ª†C  IŒ³á÷X>b£1ŒUÂJM†°»I Âd(!…%-Iʶ²¢·€üó"¾ ѱ£…|Ì9æùtIæ>ò ¦l•kXe]¦¡Ä< @øHXk#Šyw€›H•Æmf¥ø!~I4âbGfà³ `ÙO6.QÍÛø]4¥!1AS‹Îíu˶@¨'1{+’m$…b¸¹ðëwê¹3¸N!fÈÁ×È#rúHq´{s}Å}ÍØ‹ðçŸøD†a„„Pï&‡Ÿ%ƒA«ƒµ±d´) SûÉ?ÆAZí¸’óåø¶ÞSÕ”–¡¨Ú¡:ÀékÅîó·‹…¡LÁªŽþ¼þ>jR®À_pß"øè(`««}²¶K3ƒ—«^¾óÓ‰vUpè,D­³8••½ÜA%Ê®§Éœ(17Í‚18ÀG - ½m= á{NQL4ö`†.«ù…Ólƒ@€Ð¤$*^æM¥°»i€Õ ‰ˆ 8 rÎ\6|Qqï—s£¬)*²ˆKè,g·Ï&‹mÁF qËe²Æ$`Xí„„†’X0(XlQ”°Ü fëJhþÁ­Ž° ª±¤Ë•MŽ:ñ8¨­Èð<µ¬ê³?(`@ƒ¯2Ù¯ay¢ºR0£eÄ £¥QƇ ,üb3²2¡Ý#ìn fº¼ÿ ‡„A³ #ç³5ÀÈ"QR€ü-^‘d¢:ÈvÆëñvïÚQH¥X=À”‡Ò—´å}>x»ä ª•Æž8—žšãŠ(()F•)À9”ת‹q¢bÞªŽ*pÊÕ÷!'¯|ÐáP°`` ÀIÆÃ¢´sC-ƒÿ}tœP5'DOÀ.] ×y1›ÿpß'€öZLsh­°=©µZpXcl©”¾„_nD_Œ>n]ܱ–ÆÇ)Ò¿ s­g 4·½2”n`EH¾ÓâÂABžÚ»ÁÕŠ´G’Šì Åb\ú¶$æ ùæ6;Š•Ìƒ  4ÀNˆ%' ¤Ós ,Y`±—†àÄî§òÿœ™`¡cè­«B/fv­,"›æ!’Iª#+Ÿ=ЄÅpòøŒÿýVY Éä£cS’ò]ìϕיc-ý¬ÜfÅÂ1lê4ñ`Lm2.¹lr¶ß`¼,þ€°öKFß³ÃØJÎHFéïÁÔØZ7RÐÃìA¾ãœQÁðÑÿpö>càYâù72ƒÒäëÿ~ßÖYþ¡¹T°z5-ˆW%Ó‘KÚ»4‚V½=µï/HÆ3%A€ü†xûèo_øfj‘VåÚÙš Í¿Ï|Sçc´Cœp¢½ÞA¥Óî næäÿ>«Ð_l80, «“ÙS$*(( ¿Ü#wîï»â«»Šâ¸˜á³… Y öÁm{sñéÅx­§øå·|j…ö8­J|ÁïÝŠÜQžå\üw¼ƒ˜U"pàhNïÅ©Säª`»wßv»¤)Ê$@yp1ß:È ýì[ž."Ô‡¸Ö—þ2JûZeªG®Ç ð÷™Vk]…p!@z7§WÒÑÙlªœ2ÕèÓ° O2'XŒ{j¬´÷†îÀ—„é4ðz‰Ng‚ÏA» …LJà”xY øk^M4±€•u¹þòs`F‘Ã.ÎiÞXÒ+‚GÂG¤ý°ÐwïÞÀè-d{„Ñ—Ïb(b÷†šÄr+SnÌpùÙzÄ·ÃoE N¬4ýl1#E/™XûQÁü³Z[³Q«µ¹Èj-­”¢–aJc8Ù€FLÎó%BpnAGñÏX˜?¡b÷RëÂ3/X•øA«Å“<*J37,eÄ„%%6-ŠfIƒgJùNÀV3›ÿO©e©ãLFÊGñgØ£K…1A14 –Ä €Áær‘éÀÛÖí)bd Y϶Z5BAFx<ð~@ P€j©Ç1ÐkBäÇL1³cvV÷í×þ÷ô°ÀmßàlüÏ)g-.£ñµ§ñ£,c"`j…^»‹y^6ðºXxøÀ#ø#n{è¬ UüFÌ!œ0[ ‡IÚ^'µkz9’Ű@Ù¸7@FfNðÛ<›àõŽ ¿Ë¿Ãéì`ÁÞ-³<‚Åh¿Ì0¦äCÎi÷+dÑoZ½ÛK‹° @ö>Û~ n𰪋Šbûa3Ä!§¬P^àŽïÿŒÈ¤ƒÙâöÁ˜,»îûßTFq¥(Ÿj×ùkÐñÀ†îö0H«ßzpºJiá1¡ðøOeÍULSá}#HÑ×n>Ñdª÷!UVzP¢èA! ütaFtóprFN•ŸTmg0ä.ÛFg8QÝî3veÛß±š“! ñkІ½Ø¸ÀÀ"È8'! *¤ÌñC!<Ù|¸ì.ª6ãåŠ!ÃÀ3þ.xAFÞ©ÝÔ/ó…ûÅ(i·D·1YÞl1Ð÷Qãžê*½áþ~Ä”hry¿ZxXÂ2‰ÂĀÎôJÂKf>ƒ×†z%WqÒ+ãâœÊà íLÛd]e S}ﺬk-· ;ú?÷ÄbÅçRÃ)OwcÅlŽmjªT|PÚ ÿöÂ7tÿ=„ºOPÞw•  Å˦_á™Tü…Ûÿ&T\VïW7»ŠÈXѾb±i]𤼱âÚáUl¹ÅqròárA%-]…Ý£¦i•%Ec ª÷Bz£ðÎÍÂg(’¢G£z.³!ÀH–X_qP¢ñ[ºOK †’Œ¬ð™ pÑ +´ Œ$k Ù+q ­\¤!Vwò¿VD?yyÓEc„‚ º‡\Hç¨a—175ª“Rb®ý5ï |šÛ¶¿“ „‚¤âÊ*›âå÷A(P:Q(™ópQ¾9£JJ Ww!€¯B$.Âô¾dãÂîâ&‰VCdÐ ?Fo?èJC€n­«Û@ý ~-1êG $ÅhŒWð²êB£'<ƒõ%Š\¡¤é 2ç+ª)ó<[ÝÞZ1|æ"ºn9,Lb—L¾ù ð†ù¸çM!ôÆŽßíàŒÊÒ8JÁvúUB*Adp3êÎñý…ûq„÷qŒíÓP[KÜøɉiž+H ïU³±í gdW ³ßåÛ¨úšî°gVZ48#¨ôÀ¶ðcꢀ¿OÜkVP[áQ°Hj1ï ÷ ˆê.Ó‹®T®¦×Ö«uÿ…ï·xŽn+sãÎpW“ávÓQ¬b •,2Cd‚˽õ[š6‹]1ˆ©ÔM®ŠñFf@¡nÚÁöè¥äŽð´¼­-܀޳[>È0åâä: ˆÈi¶8šAfÂKwÆV´¬‡`´tº²5e"òÿ»Å”YQC®˜¦L¿ä‡4!F-öƒK>zæÞïrfN°Ó/·}§R„††‚C; /¶=‚`‘v-¯®¬4ÜÆ41.cñ•îd€ñ;æD0² ‚–CêBNG“I7¸/áö•܃ƒØ4¦ùùÆ)ø^¿*PÞ«äR:³¿nÍ»ä¬:!Ç0;$UÙNæ¼÷Œð±­LÇùf(è‰jfó^\Ë·µ¼Ánéç|SéêQKHTaÁÅ.QI“ÀÃÒó ¶)”vK/´ =ÚfŠrz£–bã*M,nùl, ½ÂîÕ7‰¾˜ë÷È|/owÂeyK7ð&ùõÎnÝC#šnÆÆ‚·šŠ`1_­ô$NòU†Ú%1M¦l Ê÷,e &KÈ›zM¨5 .±ÿÉüíZ;ºl44í”[þpG9}Ïv¨¨ Ö.^„H™cš²î¬SâP`xÕîþ¬2u7¯÷@쥙Eò(öŽåwóihDñÙ{ »~:„±œ $1H…].h&ù2éÝÜú•' $@Í*s[6õ ƒs@ @Ç.ä."%à‰œìp½%`”(6’ˆÁáã=eê°zÆÉÛž#¶=àìŸv³G†Vir¤ò ­Z¹…Ñ,(cÃH(‡ÿ/Ýpˆò 0<áE¹}”IK€²´ ŠëòáÑsa…‚f„¿¬:W jÖë(XÈ)YÌ Åz¹®ˆ0d¦uwPLƒ¶·îþab$ Åa1i0ê0gU¨>X‚_¶HgBøD¦íꃵ³>®* `À‡ IùK4·‚¿)Ö½¨]¶ð ¥$"…ÞX—l÷i°Øí¤ Nðn§¦:æÏÑrt*<І0pQ¤0G3jXN|FUÀÄÉHòÊv ä„P˜·8väoxû?…`ØP‘„ÁQræÓ›½kìƒ|ö &U^ó^_úk¼:pXíÚ*PÍEãrçŒo~y"Ë €–ŠEæég¼ñ°{´ÂšŠ“%ü~¬£&KÑ)‡XúfLõU3çšx‹¹º>:ÓîK“"jæ *NØE7ŒïŸôÇ¿¦PP‡“Y~YbE¬4ÊÇÏ>¢œˆ_v@`õ½ýóþ6˜Á 1¼Öõß`×vÄwÐ`ûúæ , Wè¢K¼¨Æ.Á¸§J,TŒhg× WŽÂä?›(Ø*¥§ëœÁ±a ¤k¾ú'‚½¦ÖþÇH>»-¾XbÏâ..ÖöHÛ°bÂ}3œ0atìqqƒ‘üm¥°ì¿ NrÀîüf×»„z ìbmѵˆ*I:RÛâRY{9ÿ8d"r·"±]PF¤e~  lö'‡|Vüî'Îð‹Tÿ ¥êNórñ/éÛ¹‹aR‡7\_ÓNI*L‹A ¢5'°t4”·ÆÙ¡Ÿße2M5ª?ûgÄ`Úp$8"<“³TßH…©Xb€Õ E¸øDBøÍáZö€.Rxc>¢{„ ÍB\}Ö‹Œ˜p¬Âk@ ]MÞ—tìvÉù0»¼·‹mZ\Îî|“ð½(f†ìÄÆ‹CWHج(Ç7v!µóaÔ2õüF!¸™+¯@éó#ÌõØD¯ÜLñ€1וoìXþC¦;€3<ò…ïn—ê%á?7bNô÷~¡©ÛЂ·ºqÈÞ-èqSusñ2X€ûÕKáöžFoµ|Ñ8ã)¡Á¸K•ÃzбkÐQDÄ‹"«æl1ÿÏ* ûëĸa0¬g?I?ÔåCáÆ n Ÿî: )OsQ5ÇK ÌP ¥&?6ÔïÕoÌîljðºiH|,,åEŒDv#™°­2+?[ªòáE‰$ÂNŸq8¡sÜ®¤Ç„qì  ¹¤À*×AÕIÀЩOÞê~wªö‰¶tÙ¾„LÆ¢ÕrCL ´éx]¦Á " ‚Aà˜Ä²Ü\ €ß» éɈîçу+%+jåû±YD%P•`dD<Ä™H©¸ð€;U&Æûȸ&þ£f”N¦}2 ­—<;bBH~S`¶! ²ªì …„ðÊB¡D—ÛF^l¤ô !$À÷¨‚ˆ·¨ÃWQö•MYÆ )Þx…ô –jghµMcÅ–ò(¤€oœ¯mpÚMÀa‡c§Ðzj†'O/÷³á}0f 0\fÞvÑ(J`¸©cž¾ŸKÖ ¶ð¬àŒh%á!@¢6¯£7åôKB(²¾bYçŨ£3bLÊè²­øf.UhÅß@He1ük]÷Y»»Æî⾫ûþ‹ œ Û¿pšš×«ž õø&í÷FŽòÛI¦—rëÅ+@ÀIÛ϶Eð„G¯Û­©ý!uQªYß3ªƒo׆§¡D?¾‚vãÂ|>“cåБ?¡Â$Ü2ú(uzÊH¯ëØû„n(NÉð‚źoñêŸÀ>ø ’×Ú—¸iø‘”%¡Åؾo†ÜPEH&5®h©G°~yÀÐèO0‚ÖhB¸ä=xz,9 å¹Ç‹áÂßWRýÓ܊Ħ¿…ƒ€ ˜&pfJqí“¿†ÆÁÂØj>Ç¡)ÚÂC¡fJLzT€&©Œ‚ô[9Áä̌נ:"SU4¥i£} ¾HÀX ?î)+硇ÀÕ¡»|úXÇ— œX$†›Ë=ÈàNMì{ËOoa»hž<¼/M Ù4¸-Z˜„€¸]µ-yymÑ? ΙÐé8_ åüÑÊ‹•ÏÄ„¶´÷]" ñ>ªÏ¯x¯’` Ü´;ê× F£!ðÿIú‹Þ›ñ9¸OohÌ"RÜ,f,i2UCå#°6UbËä íù™§Bûñq32˜iLc#¦1tJf¢§ ¿’˜Éž P™ÇeáæÜ>·‡áÜ[8`)3Šø­î$÷âTh%¦óxŸ—aus°ê!PJ¢S ‚õR³ë™«©…%à…6 È[+úÍïÿý¼Oùp¥,É'ÆM¸€»é‹gÃ¥õyü5ÖÃB¬Á¼¼{òÇ2À#àk_þ¶Û DÈ·îPn}~º±„Òûôˈ7rç ¬.ʨYùœ‡¶[AÑp»>rÓT“Ú~"Lx' (Ðnq¦3BØz®4ƒ)Ød&w\&U/°fvä  ýqñ†*ŽÝyq˜™ƒXxÌã…ÓHhÓCƒ¸]ÏÏN ó¿+U­ÍOúÖ“°ó>J5dV)A— çþ \Õ‡¼þš÷¸½Aey‡°LÔ_ªöD…jy°V;Æ+Ç‹$VhÂÆX¸F±tÒ£ÉÁ_ ŠÝw)A0ÄÎ=ƈ°È¦†‹5¤ZÿOÛ‹.å•ÄÓÑÈž §V9Apms2g?‹Ø5Œ‘ –uà´ÿµ A̼#vó†]âr ¤¡øå(n;ª±]ýw/[•c„B7î©$±-î:]DÛÑÿf¯B¼k™|\¶!æváÕä³ÉÏ`#87$ø¡ÆbÀ!*Í‹Êub’”ˆŒ(_ºR™cxFõŸÖƒ÷=Š(U ˜>ÛŽ‚é¶ Ã¤ãÂAçŸ&ðÈb;ò-©€W™¨¯Àr/ ¯:šlT™”ü$»¢[ ¢/Søè0!üÑÅ‚?d…„—mLº”ˬÉT. ”Ù“¡/¤L?í¸WU(¡j¤£ sgUWUSbÉ10Z¸žƒ¶+2/áX¼L¤3YfxQ9î(êÂÆ$qÚ®‚|ã‚@°ÎwNRZgñ]†&L3DM?CaãÏ7€$Î¥݅uÍì½0M•[ ŽRvÓ³)Á)Ø#³(wD–qßÂö˜pÔ ·¤²k,4èŽn#Õİæ¢äAgðíVR¤Ä˜ÌÅë5.£1rLJ`›û%ÙNÈ…HÕgJqƒC‡†3ªWãLhQ@9çÂ-°˜‚R ˆQ,²­ÔòÈÏ_‚oìü„q#nIh‘o,À¯Æ|ˆ ÊSFsžvTœáG†ñ£¥Ü-ü\K‰„C²n^!ˆ»üøF 4¡b´}¯òÁ«âŠ÷Æl\åò4øÐ ¹Rß–?ÈлEã†÷ÿ"S0*nðÒNŒ­âõpñæ0²å.*ù¡OeñAIÞ0e`H^úF/(º¢Ö¬Œ.Ób®†(mÇ¢€*×Ô{Àp‹Œ…> PYI’ò®áÀë•'ãA ª¨“.—OáÀj,ºs#gê ¸$kðÅ$“  §Ê°1àù;4¾,Œ6 6\ÚШj¸½¦¿„‡0Ý ßXéÊG8züýG¹;Ìã€UDØå¡^ýùsÌ£>”x%(êÃÎp¼AâLáyÑð»MP` ND4Řds‚S%¢ÓœP*á Ú6&Ç#Ž&p4ecürrK'ZOÖá ÂFTJ›±ƒ1KÓ®OÌ (ƒ )¹U¤Êy5 †Á“$Ój-~_‰ܰ‘ÕÛÔW·TåÉ?üSx~ïyˆ–V’^e=ÌOeؼðú#+é}Ö–2]f<Ú g{ÞÄÑø”Î0j^üþö¡`õð_Y¼‡–Âc@ÿï+_ïÙS–»þKA16ß·u‹»Ò ð»ÉPO-{}?åò1ñÀ­«Z•B;rÆjP¹\Y.ÿõ6E…Èð)ƒ_²°ŸÐ~-§¦žbë¼±ž °ŒCð¬qÇ–<p T á¶kEð»åØ_³”0Ð-å}7þÀÀ‡Jƒ»¸:_ã%ôˆC8×p~Ý‘ñ=+.Z@ú|±9×ðÇF#6»®0TÿˆÔEŠU &9‰Tžw ŠP•‰Ýpÿ>CÙýf”K¼ÁSS»úï/4y|G'Ò—¬~ßçôQœ,]ZyÒñ§Ït¾œ@CiĉGÏÀ}¾<`èd3ã0Ï/µ~®{›å„ØÐ`H Vǯ!\Éf ®Î „>©€_–Ôûp’¨DÕj/¯a{­6‚¹"ŒÎÒ ­ÕΕ!ÿü,´ À†DbNHD+`³µ&ÜÂójPí˜f,‚¥“lÎ!…©xºÖ^ îvuI¼dÁé?¦ÂŽý06QÖó빯6C¡øÁæU~áF«Ú›*¢˜“…™ÁÆaW‹‰ ÐNµLè8*HÕjÂŽ{‰{‘ûÁÃ#8ËSŽŸˆ óŽ ÿ ;#":#êè/{d(×ðß«½ü.‹9ŒÐ.X#“h¨Ümü²2±!.⺣Á]!pð‹Ø®âî]ôlÉÅ8?åb:¾K & ¶úݶ“ðêåi]³Òž#K·—8Ö˜°JF½Àp9‚R›"ì°lÄ –2aÐãËjr¨¡Wÿ,Q—¼"ݺ¾ñOšb”MŸ›e-$p^c {ÑSå ÂLÐÃ熆~âLá>àLJßÄxfÖÕ$_˜ß /GÓDÄt›ì‚0⢀l¯!]“»L¡xÀ‰µF›W$V°KütG‹§&u7ÂÐi‡ÄÕÖÀ”DW¦ïÎÿÆ%„`ü6ÍùÑGDÊŸ÷ÒnäÛWÕäÞc럪 ®¾¼›™ ‰ Áº#¯U«$Š€­ô–½î=¾÷¼é$¬›Nx‘ô$Ü6|>°ùظÐ@ŸÏ·„…ùÅ rÖrikqíc0]èw<“¥†J1/vîy¥Äºu‡²ð~”…͇;"·¾ͳ…¨ÌX™_ñ]ÈO#ÇÅÆ2IcÆ¡ðäd‘³&â  X­–ò¢³H…»`9¾„øçRóçduË=µSJ$µüDYƒAò†ØZ€‰+É@aš:®Jjy²§‰Çë»>þ¸4‰  T£ÃÔ4‚>P›±l‚@(‚¢Ö/{«DùöYÁ˜ÐWQuîýM†P˜=h"H0ÀðªÜžån†ªÔ~ãNæäO ÙŸBc¾1ÌP Ëù7?ÿ³ÿ3i7áþ!ÑÿÅÄó6HyÃÄM ŒW2{ràï&! ¹Ÿ‡€œxhJ&N™`É Æ ±;g€d 9aM«´'âí´z}À;ðÆkçU<ëw +yx³¶«²¾B=‘š3â¬xP—Äô~UøOy®·‹†±šÜ™ÚV„[ãT…—hHˆÓ@È—|—ú/)’Lå^4 ¿9äyM*Ûtn#! i¿K…wÜøaWÐw¬jGvJÊùŒ®@Úï+³‰ÇOL nE€Ú£Ò‰±/LH8þ˜Ä¼ºÁ,PŸ…UQ*ÂH¡§lRÙ-xÿì=%¶Ž ºª­{0º ‰óu·dž¬z­¨-ÓxD5¸Ï&ÁøSQz}÷÷SÁR‹\gV´'©rƳn4Ë-›&XR²Ã÷¸îQu¯0°èÚwß+>"¸èÜÂ&Œ¶±9ÒéP®V$8¹sèñ w*g9Ä#¼¿‰üP8MÈ­-V0|T-ÐŽm•ÐLè8:4A§•…„ä;®b)©CèMÕíBê“hª3,–¤VIú‡kfì3/­ÓN™ÇV"JbÝUGÅnâçT¤È¬Ò…Úß–Í1ú”+ÎÖÊ¿>\Ý ØÛu–÷Îú ¶éPÄøûãá?9Bú÷¦a)O±¸[Î à—ÇÿPX²)¨Iq« 6{€”7Í/Û‰nñþy.‹‡ºŸán¼K>¢\/5"ìRU²È˜ R**ç×R¾‡ò øRû¸$Œ†¡IÎk[î~Iÿßs~@»ØÛ‚ŠÕ` KM˜’Áøêê¨|T:r›Žê8šf :‚Sí‹Ü9¨œ"hçà°»¾./h\S±E92œnÜÐS‹x2 ¸¥ºUU’uöü›Î[‡f¥‹o30›+#ØþFkEÙ©Û3_1øU˜ºa¤EåA{ñÒ–Ó:zÙf|൷òS2–‡Mrž:Xê|¡Êe/…Û·––ÍLj-rÝ•*eHô¹n•B´Õ¢…#-Ã¥€Ö·Ž:±]3Œ6Žîb ¹½—÷¥Òz”pI£H¶‡rEb6g?«_¼'X;¿Üìx8 Ζ§’åDá±ÁPæ >=ï‡ ü…–<}æÒ\àiÿ—V /Êš_‹ô î•/|/µ5‚Â!Qõ.S¤0×ÔÊa{ HâÊ=å¦~5û¤8Ú.òѱåCPÆk´.ûçJî®[¸¯.‰º .&™yþÃ,óEâÊòö‡–Y…4(ÁŒóHÅÔz±'“TŸƒ¿mÖ> *å .^2ñœ[âEÈ”rN¯/€p”¡$,Š#_Ã$ñt+ämgÄðj4Üûa_ (–Ayköo…‡!ŸíBÀ FåÍðºN 8ce1d þ Iî9k‘Gáz¨a'%ÆÕ˜´°º¯Èî ZafésÁ’"uzME<Ùþâ:Tœ7Š ¥Öžÿ]{æ7ÁÀoJHùëì0—và[ø2XÿÿÌ2¯|ýDDXÄ gÍHèO¨œR’ÖÞ–{“ïò$$—_>6ÚFÍÂNаŒš¶jè|3~­»çK ʰ¼«àCù2]ƒÝ¦cõõö;bÇr”ë©ØçÑÎI™›i@È~Þ-Qhì *ôZo^ƒAv­ Èt*4iS¤×§ÂÉ“¤º¤ÔW–K/·øÄ̤xöu–;ð™ÐÝÏøOïù/ƒ…N5Ò(…ƒ¹Œ³Ç ïWñ™¯ÛÇ“˜/w3Bl8rNm¶fME"àÅd;aõŒÿÈÚV]!ð|>t^Â&ðÿ ¯(VÊîî éJHî@-i¼<£WêüŠñØÙ"鉵èÇk¾¿ókÌŽxTKwTøÂ§š_ìæÛ@íÜK£9o0›Ç@Μ®ìœV½•×çúÈR`¾ÂéºØí®B<r!Jf¤nø`?í^Âå¢sËn!Ú‚=5˜iDUŒƒÃc).¶ o ½&ÎWAýÒLÆ<ÚcÌs{Œü ¬ÌÅxhkeò 2)7]W·»Ä‚1³­u dÓ˜‰‰.¯ §SÄ„ŽÛZˆVÑ᧤ ÂFT€ñà :ïŽ$q°cc }eÙ–HD>`S=1.’¿[$N²·P½•T¯ãæº<¬Õ¼ÙlÍ2ëÒ·,qTÝf¾E¼c£ª9à˰°IIÁÉzá•YàÉÂÂÙnâH8ÂØ)Àû]j­û°txÚð«ßð»m†Í“c ¦jÂc)“#‰Ð4·KL¡>¢a |"èmjùº”ö.…ñØ–Vén1^qæ>O<¶?peåh7[Ú ?ß5ª¼ŽÑu,j>Œ¨$lsî‡<)ù\güÔ$›e/÷ö;A9ß ¤xè È#¥GYå)f‡Reñ›‚Ø7Ƹ0€¥¼rͽcTÚÖ|¼àÂôëc×V(\¸½wZ¨*üZÄ(ø›ˆ“ÍÂ,–—ÊÚ@¼—À­Y5üĶ2ÙCYŒK8bíÜ òg£ÊW/5Ý,O [‡x*4œu3€U wÆFúD–/Jø»ÂÊ‘“‡¼1G‚¾Òן,ç FÇþXÆý¸L9±<¦Þ7‚Ío¸,§Ž…ÝF…G ŸÑ1º?<4y¦ë-AîÆvª+{U Ôð4…ÝCgò2œ‚’‹¾éÖ&oW‡•j³Œ&ýxeÒ_ÐCo\(Å3`´d’£rÌS}k)&Ìy{…Ö À Dõ<,a(õû,É»,(¬ŠVQ“þã%§…€;f%÷‹ŠÏ¢·u5¨¸ àø˜Â›f"b»ÂÀ×!»ƒ¥ew‘@¨( 2èAg¤ã™'íqÞ#À=múºÍfoˆ‚¤n…t <³K:¼}ê!¡œP˜ÊnŒt(iLÔS®8à”&z$3*›g㈭§w†)c*ÄD©T)*.¾=EBºL@o¢ #Wf­É©øép»,¿âU!Éÿ ùÒ@O ÞÐê(i jˆïïñ“üigôÄcš[dM$n"–€Îh…%½B§ç)¤3Ê,µfÜ n×ݘ ¦]Æñ1XjÂ6åñ¦ž°{KÔ†„µðÏ® ÜIeV&z|ÑYß(Yçá,/IT˜·ÆùñsrÔþVŽK§×>cÈBs0 BŃ]ÏÁq®Rñ)ÂYŸ_„ņƒ/g%³?O-„ŽÎ‹“!0zÂkÁXÊçjø_n$Q¨„k&Ô«DÏ×\†Œ>¾~Þ/ÃÉÚbÐJŒBx øøaQšõµlÌx]$ÇBƒ$Dë'ƒBËÈÿ‡UKßIxìÊÿ`¤ÇùÄÆ´_0 áè!¨a3÷?*´ó‚” ’B•÷Â?\yXajÕ¾ó²ßö–h݉âK†Ÿª§6e„l€t£„®eN¦Éÿà‡Ð;°pWµ8_ÑYA~àã$|(' )O ÚÌ ÊÙ Wbgê’‡mÓ¤Ýõ†Ò?þºº/ó·¤vÄhå\;O©”冰¾öhÉÐØ²l'Æöö†!ŽMå ¿cÙüí4 ¯5æL4Ÿ<TbLôî KCXÿç á¼hé sæ¾’*˜/?AÇ©]¡Ê«¿Z;ÌN8Q‰ö ¤k…×ÁeQü¡iif­`Á_„€]qæ†Eßd± Ñ´B‹z#aúD27Ë„D• x¨pÈMÆdP¿GmŽãÙàÙóLIï Ä;äíÝù0‰ƒûÝ ®USìýnü´ŠF?lšd˜®_j ‘D$ˆ}ê“-ÛWÈrçkƒ±“ü,-KCÄT$¼H‘¬ìÄÒNÕKóÿŽßð±ÌHèhú h.©×0©óÚ…qЧ²ë‚¤”|Fvãr^‘J~^ȸ$NÿFÁ6q‹…¼±ý°2 ¾œ^ª¤]Ô"{ d °ÌïIQ¹_{fÎÕ±›è7;DÀéë ¥N ã³Y½Ú ´~55Åôàk¶•[ éFNýC_î±”×3@r‚[€ÛË/ Né+ú„âÐ_¤ ALJ\êqB±¢Î÷ÊÊÄŽˆKÄÍ^O ;<`e3p—ðºÖ(à¡û€C¿Îéýù?/ïŒ3¡+ŽÆïæýÇ)ÁŽ• ÇöŒ?Äk¹uñ$1ì‰ÏrAèv× äÎÈù?Ÿðº¶Ç‚Àfs‚T_ë=ÜvÒPrûŸü:†WòÄ‚ké^ýB±ÃX=éˆ}ûü~¼±g ¤×âžý%K…Âûee»Þ1·‘„q&B> Êæ@$ëðÿG––™0uóBâ&+,r‚6ôκƒ>ÈÀ§Ò§©Ö@ÖrD2ˆP¾¹\8¡4áóDl§kòŒ™.WÈm¼x†æëJátÚe$†©p9@5$|ü$LI\dp< ‹bøøÇK $ÔžrLäH ¢špðvOÆÈ@몖a…â Cšž¿HK„Þ6QŸ.ÿá•ÿûd%wÛ|/´ Á³arp<µäƒ5¦áH¢4°*¹"þhBi±à° W3ÿ¦` p*ÚýâϵQÍ%ñxxX&ˆøú 2‹ö" sæS0«Iþ2À¢rÑ2…ïn$bEp¹¹ˆ“q1DgÄBIY1ûµ}2ÕTŠ’ãõøSw•!Ŝߗ¼#ÓOUj4"#înó¾Á‘\e߇Pœ¦Ÿ/„MfZþ ƒŠXëľà·)×ÃÈ„Æî易±BäŸÜå.+,I¦Åƒ/à=m37VÕH'©‚³‡ÁP^Î--M¤Ã6«Š ÝVbì°‘9ÛÛ&4È 0±w¾@„‚¬y74.Ûc‡ª h*ŠÀšYŒôóÈéRpûåç}†ݳCAg †¯66 õÂí´$å $„ÄáÔ@À%M …ñæN㩳í8õþ3ö~Pà,9r  µµX,zL"Æ…Í„‚L¼,:CuUEÎAA]¿ÝŒHñ&WapXùv‡²ê&6 ’ @¦ ƒ)Xž=Ȩ[£*³°RóÒ%¯‹ã¶aÏСv“„˜}J.]Ôq[/$ ½€’$ÙÒðÕÓýœt‘G2Æ&0Uƒ××ü é«­_ñ¡Ä0m>åËÿŒaÛË3À0*îOÿ„ÇS8ÛÌ%ß™~¿•£hbIa‚€1âÁ(åÆxÿΰ·+pÃ>À˜œÒ*3àTÚaùcá9“Ǽ€8r„`/@Ðíæ éñ<·˜©L‚£b¾_òA-÷à=áî9¾ipèÀH ß´/ÌצAB+›ƒ¯Þ$À:ù®„ûIûA„dK\å¤ò"℈†p»mÇ’9ÏàÇ],vŽp>ù!ÇŠ<Þ“á{è â¿w»âLfÉÙ{¹þߥáw³Bh§lO4Lg‡ß ¶ÐÖ8?€¥èݱ·Ùã5úçq°ºMÖÐþ(F"XÎïŸËÎ`建 °Ö»>¸ÉºØþ˜…'°uÀ3¯ôßräÙ鿺(‘´ !®ñ Z¨cÁÿÄ/mî#U©C±Jè%&°â£,¸@€þœ¸ÊÉÐ`}âàÈJÀ— þbÝè÷Æ‚Dg"íH@7`[êûúó¡©-µûMr-±•áÆgyòØ@k‡Ç‚7À»(‡Í%° žØϸ¸&ÇÍÁ¦ØuSì»D)õ¼¶#}¾`¿h¢›-ÚÕÑ,Ò>ã™!ÐÛˆ‘@'Ìh€ åáPoSGmc (³ê[@ÏÉÁU"ÖZqšH‰üïì"òW˜+ýÈH†Èäçk ð»âDp‚àƒ™õþ¸nd!ï=DHF´¬òã«ý¼@^ÔDìXiÜ*‡€ýôH‹Œœ;eħ/?lUÈïÐ]&Ž0Š8x&¡ZÎ]¹ìóœùÁ 0‚éÓ”ñÞHc¾+wáU{Ý2…d ¨KŒáý'È 4.«Ræ1]†‡b‚ðàÜDàP ‹¥>$–øÆPS ”Vee° ô¨`#ÊeHuÅ}·»{O¥¡ŠáÌ7ä?I è%û–¢>ËN>UÇ?Ÿ1ðÀtñn¿‘JÁ™þóÂ:!ÐPgl#`q Lð`ŠoXõR6óš 8CŒ‡÷Ô§ãlÑpð/SØ:À«õ€ôÓ›5ÑLi_@‡\›³î ÌÒȸ$ÏÞ»vŒ¡·½BCR“‚Ö"2ݶ¯ÒK-•‚š‚Fi%¸0uþ=øŸQœ$Ò¯JÛÒEãf]·b’nÒNt–ú²w{o?F±»¼þ>ÙÒJ“{»óù7wºßB.‡«n†l :|–Õ njaÉmPš}ᾕäÕdô^BJˆ³çn×™&ª„ðbÌ?n°«ËÕ>‚õlÔjxòÎþ!6¶˜™nÇÂCF Rw¬"€ª×ŽhcÂòN°ƒ}ND—"à—iMfñ\À#a¤“dXWßÜ)7·¦Öì½u1¦‘yãg( ,µ•9{j^á†Õ?Ö?i¸¦ÎÊ‘€1¤?ZI÷Ä:ŒS–€2j?´f¡Ìhb ²9|_ÌRãÕ{ÙY/'†ZÑÆ•7bêCÜàû ï@™@UTŸ¸uB=mt–ø1Tr`™[kÛÑ)ÅÜ<ŠÃÍÝÔƒÁ—GÍ"å †YåJH9þßBD³Èà˜Qß(Hðð$JH4ºÏ¨ÈØK…h.+°›ýÞ§q%Z¿Ig>7 „cŽAÇ› LqùÕ)ò,^¾úV3‚ àXfÆ ШTH.M? ¿ ä‹¡gŠê-âûÂÔ~8ؾ# ¤lÁÁ!PZÇ _ºž÷xéS—§v°1]*&á:v7g„æ…oo Â^ᚃqMpUß_·^nÞ/âÈÒŒñÔˆ -@r~~= _t@•þÔO(!k¹>S+_#6V¢Ù·[©l.žƒBÄ;wwuÔç›)® Œ°€ ©Ɉb…‚p‚R8Ü$ ‡D—æ&C"$~ÏœY–‘­ áYÖù˜òrÈ+**u(`j$>Qš†.ü<&”‘@gp¡¢ \©/‘Ô6‚fk }§Ô NCñv"ߥö¶] th§ˆôÂÙe5š:(Toüý)yµ$]ÍC£a [<œ$ Ùsæ›]OóR{öÜÑØ»B,3Ϋ‚BozÆ.äóo:,ÓlÓ´Èl/‘›óZˆ‹»øGtº>yþö¶^o‡øú¬¾¥±²ÉËV< üRÆ>`=¡¶Àô\Ö`Ü/p¿+XËâ£`ß@%«Oû2®ÿ%)n†›{w¾E@¡¾÷wuL®î—ˆðÝ÷r±vÝû¹›,€Mîз©Û(†´½» .\™(e žÞ;œ`–™w°1š%·~óÆ›&NͺO&Ó»Ÿò"´1$ŸS3ÿ. ¤  \ž³þÄÄSCÚ˜i—gvžA~K¥YÐ2ºDš«i€Lå _Š"®ÂP•I¼aOä @FÚ,†VÜ›1¼·‚Í,,*[ U(%êŽþÒʬ»þ ,2€£ËÒ{QÑ.ÓËšøî-ÙR cžgÛZU8!4P,àÑ‚íd¬nâ³cavš  R HP\Sé¼U”²g9ÎpÌ?’QÂkù =‡úºðw`6l•kÃÊ2æßp[Wï_~;ûüéf%a&¸ °¢Nƒ ÓÜêµs¾WãÍôù„ï|:Šü¹u¿Žáèu O¿ÿ]÷‚óåÜrŒ•Om4øx–¬¸÷t.ÑN1 ìx½;Øc¾ÓÄf µ®ß…µñé{ÖÀÒwc·™ðÚs}Œ%á¹þà—ÆÎ ¸&·€…­Ÿ¿žgñ¨Ø_·&€ ÝÈëp`É/pÐ~U^´"£e$M¨ë×ýÞD¬|¥ÐO¯Þïvã:n”CKrdºGŠ"³".ª1ܼ*γ4e$`MMÅ’”ˆdSsˆœûbð’ªø´!ü‰”ƒŠ®wWsO¼ùÛ¾ q3{ÐõÙíîgT”>¦L†ü­ZHÌÒÜ8õÞ0KMòY2N“Û m´ë ’þŸЇ& a.xúڼî?TWÄÍ™Öó¬r}OÜ}HœLzÂõHáY à›jU®,§…ƒa$$»øY^rc&ŠË®Æµ’@X¨PLhH bÓ€ÿ/BÄ(A˜Æƒƒ@Öç°jåàÍéúŽBé$¦,³…àÒµd€T»®d»€äÏU¾l@Ðè*Šßq?ŸTìÛ ¤ÔW\Ü¡¶%â¥E¦u%d9Z`˜9ž'È!O @˜…ÆmùòÔ*†ÿƒò[B±[ó9Àd¡vš …  fÇa_šR^ñh¶{Ý Åd&`K†K‰[éã]Wj@~™ôAƒó…BààáåPDÜá£ý˜eŒ…`¯—hwÁ¿¾òšpŒÍV¸)C‰D%`xÌÛ|ξ¯‚>”¢n‡ÿ h¼4¶r ÉXlFyé¼ÐjZ‘ù¼Ö±-9é϶+rTj NÌkøFÉ]ÞxB®NŸAm'³# A¹ƒçó@@ºWcoÐ4‰ñ XŸ´ ((Û¹¢Áð>ðà=wîNÂ<¶ð‹¨àCê°=²MïÀ2p^±J÷Fè]Ýé;ê÷ŠÅ&³ŸjÏã¦d&{Xñov«Äîîô÷`…R¯B¦¦íÔ§¬ÌxOJ_Éw¼Bâ¥b ºA¤I™Wo$_ ×SCÃÛ`ÕÁŽœ/Ó% NP¾"| @}Ññ¦cP+«ÔþäDûÔ²)‹ ººüKrc ™ò+á}ÜV;i1PùóÈDÞì3 Ýý¯ªßñxŒS/:ø]¶ðZKÄ–ã+yåKAÓGƒyœ·Èk‚9áUkØJ`ÐZ#DÜ0› A‰@–\ìSƒ(¡éL+ ‚vôÅF-=Þû ,Uhlèî¤ ‰¸qÖ]p ø@nC8!øcáXøPÚ@í½®Uw¨ÉF(`‡ -` d XAktÊH‡9­[³w裦&g»²®[Ýê²S„ÖîÝWnúÖ&û»éæî<.„<¾+ËvRcô©Öûm“xû¢ýÒ½¬Zm‚fH-êM-ŸòâùÒl\J´Vcó]¸Alaç¼áÍØ µ9b¡P뎦 ÷áå#|@Iïëý·?7$"yo2Ÿˆ9UÚb÷îâ"à}Zò¿æ¼¯Yð6ùhÐË{nVŽ+Êœõ¬Gñý{ñ ˜)â¥)» ¹³…¥ м¶ìÎD"AzƒB2æðºI:>0ƒø†‚*(6ªìZfÓP»iÙÂãÂðÀÉa6FÃB+m`nc ³Z¢j,©”a窠§ ßÕzåER‚k1•¨UãÚG(€ŸçHÀÙ¸†ïþ£xeZ†Fzxµ•šÔtsn¿‹Þ[Úy—ßëvÕï„b¼|`÷‚‹6e7ìîî﻾ðXœd©À.|)Mp;8 .Âý㟎‚nÑëIK>›Ä_b<ñ ­` ò¨`#ÄF)GŽAàÉÑi’)¾÷QL0©%µá)nüwužÌ&øþaƒß¯ß¹Æ~EsL]ÐL DB˜zOûGPá«ÔªÇDDŽŽ(?^]%·™oäiU˜ƒ”ˆ,Go²æµ2Œ7.ÊÜÎ|9XI–:$±Ã'(ÛiÊ‚: J)ÊÌSˆ0iåÂî^GÔaÈhú„V¯}À,g6FcäŸÄî{ù]Ô¦p ëî±ýÿz0¸µoˆï{¦‰‘+w~ämDtÌÈ ªØ¤À”Ú‘%Ïï­SÄÞòæç¼(û–?8€Ü‘]ûÝÑúæÄ<ùx¹ä|ØÊÀOÞQ÷ööÛ6;GÏ/B2îHäKdgýŸáÃñÀ"Ÿ-ïÓÇ–»¹8‰ˈ¨ö@c$ÓRTÔP!¦éööüLCf›ÎŒ‰ª,`ƒƒã>´Ô…»¸‚nB'$8¾€c+œ8!Yï$¯ƒQp? ˞Ξ˜~™d¬Kh|sÞ¯xAfºl®ÈÑf&7>Þ-íà?ßÀlvúQ;到—®f\GsÔûï°»y£;´Î“²"H}>H/~9nïr]ß¾v)BŠ„|ž6(Så>wۡкQ Ý%a4ñ„*˜7JaÞþ>Wð»Û×<2…ßR"žÓ›ªÓâ® ÛÌr— ü¶¶¢(1ìÜ^? iðD’™À´}á_”^2ôô»¼$C®,Ëc‚Fè{M`<^õáZÂÊ-Œ#ýÓ+þ;Óxð)7ÿËÑ1y}ñ)ÑuǺÕã‰àï¦çÉ4.UbÂé&(ñã¡À˜Õ<¸2²wìÞ`utŽ,—Fñ:’ôÏWÿ‰ è@jfŠÔ^5à4a|5gŸõÌ #ÕwXú€Ù°%¾_ë<ÀWcÏX…[±©RFf9×KYÃÚú€kÓ*ºŸ•óitЋw1…üg²çîéɵ¶3k¸!L€Ž•¶ûÃ1Î>QðÙq±€ªç™]ø“ômî.û ë~JvÝÅÔÌ^TðÎñËF£ˆCx«ÎD˜w[·N•ˆåádA °ƒµ»…Û]q‰ëް€“Õí0Mi!å]ŸñtØi¸kDè8Âj]Æ–oü^£dŠØ{ièì[¾†UÎ̧ë ñÌ”aÓ`f|w:¶B?Åà° AVƒ~éH Þ¥‹v»#YüàŒ™â»O\(mÒ8 \×’p‹âÈp~]S¢{·ÂîÞN0Ÿ"<ýŽf+÷2Î&{D÷ls¾ÀöÿŽ:Ô¹~oÀ)Ï ·ÞýT¯æ $ø÷žV—œƒÌ,SÆ õ—úQ ŽOجŽÁfoÿ¥×ã·LnÕÈãgm¬ÉêfßwÅu‚fî_½ïB¾W®±‰ó)wâvÍwïÆ$÷w»Ì›CÙý÷»î<ápDðlŸÛpŒ–íÀüv(Æ8Ѐö¡,aßr[O×['ÄóŸ·éõÍý¸Øè7;ìs6å-xÏò–›…éæù–þ'BÏÜ0!ñNF !ÄgBÙ–š¥r$ÞR…Sc Aa`ýÜàb[Ptˆøš¬p¾Cy‘WɆëI÷™,Qýÿ‚ˆÀ Šß„C©ñh9Á¼SçPÉ*ÁAw €¡¦üN+Ú|/´pf@D#M9†lÏgðˆblg3N«º“ÌT#Ü{çŽ×½ü‹çŒ¸ÿŒÕÐR>±„&$›nÛëïú@-Ѿßû?À æýšë¿„Ø@:Ñ„$TÀÔÊ ë;ÿ÷Êhfa¬dóï{Ê~/ê±}¥ØU¶ßer/ÞêàЕ¸^Z¬»&åT¸çï6Štý¶  ÔÈüµŸOÆ8-ö‘¤sqñ^|þîÃcÕG–ÜzÂxÄŸ^½TÄÜrôcááBk¥üÒ™‹€!rÝú÷ê— ô*½àŠù|¤ø9ÒŠî÷¶{adÇÍ ØþŸ:D~G‡+h^U#„÷ãi^!Ìv‡¿ W£Èû Ë%öY8ϸ¢ ½Òƒo/=ÌéÆT˜1ļ@<•±ÝÑ‚¬¶Fº"Ø®Ö27&f¶ðQ†®F7Oyàþê^#¢5gDBÂ…¹…• ­ze¾t)4} þ ?Ál°¤Âœ­Ø8VbäIœ†8½½ˆ´þi°fBÄG: IŲv\»ƒÕ“€Ã ìI2ÛA<ÐöaéFÚµ¾ßæ *J“­E*ÊG×,âÃDt €ICí# Aí>PBÄóÚ¼"ñ“‹Z¤y‘v*MÏ ýŧ“Cjm¢cp«í3´øcU¦tÅßD~îù´¦ÐÉØõA ƒd| ñsàXñ,?.HAbº:ÀO½¡ë‚i¦¤Ó‚dÀ#õ¼CÄçq¿÷Áz¬ò¢}wñ:BwNÉ~î ]ù¶Ñe»lCã'ŽØÔ™”ŽV'v×ï;øôO**jÙB­Û㘮¾å75’ýI†½ÊZ«Ç+ð>—g „aêlRk¯ï;x*dG¿šÈd´Êödú o‚Á&ÏxœÿØ0jÁéÞºÚ[/U²7{¨QÞî~ûÞóûå´äWBk£×Gû½¾Ä%ë_tžùóXBïx¬íßz¢«½$¥—wp‹¹Ov+!ÅÞ´LŽ”a ©ÄO´OÂ>+|û©×öØ¦Ššµ¬.Õ‚òDZ3̃ùo¬z}†èxDtï ‹  XÑ€ªtÀUAIÏYÁŸ=¾q"JÖƒÚô³wÏ…Úl*0æ†qϲ‹¹”r2¡·©æ@!øÙU„Ð } O¼ióB~MpºiC ˜Te‡ä ø 9à"€3*€3MЃÒÏ=ÁG:oô¢øMo9;q^ˆ/{“ ùÔL(#Z Md¿6¨*•V9îëZ*-D¶ù@o†£voÃþZè$NÛyþQ ´~׋åÖ½lîVóFž ZU`À“ˆ Šs„ŠUàK¦¡ˆ„6´û7Z¦îöì×wÍ]=7á$ö®îç/b›»ß.Dí‹Nöîøø…íŸÆs\?¯´€\C;â6ãÄ8$`ƒC4Pã ÀÐ@@½"i`Pºðß„àr@ƒ(½%%hEfc’ K(ÆVÃß Šf !€OCe®qÆÅ„ß 0 ¤‚ĈL/ Y]¨n0|*C¼ZZ¤ÄÁž3<ñÛ‘-åz.B'Âé$4(ALS V*äL•“Ø ÐPƒÈ̃¾ôãs¯¸Ý¢žxéYÊV¿íÄLôÔŠðEMÚÃØÀ`?%q\ij9ÑøwrÞîòO1ññ€GoçòßÁ9¸ÿ$˰Nü»  Ÿ9£I‚+hcAøæí@ÎÈÀ“Eú{éÍÄj^j8XùŸñðd€ÑE?J"!#g¿‘,$G ?øLáv.pO¤{µ^g³™¯ß–ëT½©6ò6É–\„Q$BŽî<_’6~yï¶€‘Ûçò"¥3ÿþv×À~ð ÒzºmNZæ ¯úÈ[ÜJM£ýÓ­ðüpyñ?Vîªp«›˜=‚[æ=d \R·»ëÓÓä‘Q@ 뛵kp[°#n0aa_o¶J½©Ð4yÿä¶ ´I;Üô`JÔ]0ZÄ5®âÿ8^¾—£î‡ôNÍ KÜ¡”:ÃÔ›€!âd¹iÁšŸ’ûÝ$ºÉöÝ×PÔÏX!›|N*‰1ÀÉÿCÇFmˆ4¹ZZ#¶ÒË‹ìÏ^ôh3—܃ PˆdXÖ¥#®!˜ž´×l-šˆ=›ÙƒªE»ÔÏúÒ;V…x#Ñi¶O'ýX% †ËMÅ; _ ¡•Lãtþ: J „ýËÿÿB^Hå®0I ½6êíí™´KΗ˚T뮘ºw¤ô¼Ðcx@IÞëšv[–®L†×ŸW(}Tã0fCôK4¨È3–G{‚<ûUïgÏ뱨o$îºòãé’P X°ê$~ëß 0TˆµŒ°ž ûÒ;¹'%å^ö[ž¥"úm5â PТ«pÖ0öE•uÖ¡‰-3úï÷ ¶ñaS͘?¼çûóÃÇêïncÚ_'†¢¦&2_þzÀƒyI¦2þlXE§•$:ùîÿÙ@޶ê·È2Y“ßWÝ…F“{ÜãÄðUÑÀ”ÃY‚濈ìç) À¶OFgp(‚0§—< ìFÀ2;‡ßqöö+lŸåXQ*Uí;r Í6à7H<l!¦´.ù7”ƒ'ÝwÜ…²4™-AbA9Ilqá2üø?ý%öòº_øC£?Jûßú÷ù¹hÿÿ‰Ñ;£¦\»£xušzÝ]´Aõj½{FêN0µÑ/ÞýBvÏöÙ_ltÿBäÑ»~‘9—$?߯üTjöö‘à‘Göºa9ÒCM´7Éûù¯~¡&Sßû™÷¿Z¯ÿ«i7Õý™&.¡5[™˜É—þÿ­­W¯™›mþk{’oéô\u^"Ùli²uÈåôÕn©añwŸš—uï§ÚÑï¥ÏS1þOjûûCŠ`þ¯¦èÕ• öß2Ö_U`¤”4:L‘q¬ mÑï­ÚAâùƒ3°•€5£ý ˆýœÐAnß„IpK±e:›; èƒñãð`Ó†mK÷wüør.^2ä2½ý|0 zAwü%eƒWøe4:ñƒÔ.ãìïŒ&Ï ~°}»òzjÛ‡Ôª¢µùFÀ˜i8‡†V-X ©ÿØmð£a5êW†ÐÒÿ]_m„`t^á‹®üžõyY²K‡³é]*Îâœ0˜üž¯YT¡1_KV@@ž¼=îV#ª-¬7Bh}VKÓ 1 ­1;KÓ/þVS;(êîF³¼H·,-è~O)‰iC¥ôñÓŒ·Ÿ¿b¿Ÿ5ž¿ÕLÖL™?§‚=ÖžÉÍ1?Ù‚¡+Zéý•úa¬¿AŒâ±ÓtRp2Ä1°{à}Ôž®éŠ\i¨Ú¹«ê•ÝÕÿ†X5mÕ¤aëGü|£@Ð`8þ”3£¿2‰ä™7øš ŒXwq~ö—ø\uT\ŸžÒŠö><6µÇ‡mÆé½{‚À‰©æ§c…Œ¸;çŠLûœXƒ93³™ºvFË °ÔW\LŽ© :g@T³qˆ»‚Î,‚4‡‡Ãu5O„שh…ògø–·3³Ža  ÷DA÷›ÛŒö¥ìÿn6Iº*ŸïëŽL'c½+ÍÉ+w”@ëIð–ß.u ÏŽâY/}õ¤ îXZfi3ûæÝë‰Í¶™È7æØÁ™s¿&± ¯°ŸÁ5·J\Jùdÿ]|—–=ÿk_Üû½ß©1˜øônèÝ÷;7_ÂY6IÞáuM÷–M ŠÝó¥u¢#¡tTÓña y ¦G%Û5ÒoÍ•C+«×µÑw•IvVhe…êUt×`™íºc©=›´ú„“îe­»]7êY’á§QWŸÞûU=Yëò}5ý%–ïßJÕLÉ´¦Sâ6ÆS<ñ¦Dµ¯.c/U·OôýÔF÷ZGíé®þ½»”1,^í]^q÷Ïì1c ,º4krè…©áG}2¸%Ö69RF]ᦓ uN8zMˆŽSÊ47ÿ^Ééâ[)ýÂÑç2Hä.f àÄ áçn‘Aóìú¡¤ÙLÍ›ƒ. Êc5RMÆL„ìvž¤²þ0Aq¾Ç ÿ21¥ú5xˆó²Ìפႌ‘ñvÇBú²Á-kï_W–f$&Ïÿ9“ÑM0º(ùG´ {ÃÓ:ÊA8¿.öè$$Ã?W“Ó¦{h`6!*Aw­ŠRåÏž°`ú'òxr0YxØàQ±±ÿåZ‡pª®øŽÛ°õò]­ª²OÑ=óKcÉõiajÒ|÷À«{˜IYNÿÉëÒcô«¾ØêÆÄùøêgB†° ÛÛ›ó‚‡iåú9‡µØ+²1‚i 1q•ó°j|žé¾|¬ª&†——¼q±'õ²ŽfsN<|…ÿ˜8ËETÖÎO´TØ¢¶4ŸLÉé¨låTéÞÿâg¯ ˜äú{ ^@ÿ´éÍÙ†‘ÖϾ„‘?Òí%ÙÝæDxt>GªÌcœ¾gO¾ÓfǾº=öÓ›ØøSÎ;œm l4ËE8E¨¾|JŸ Ä< ±¾¥ôâ|ÔDŽY€‰Ä†ɀ%^…ðæ@±³ÓqNŠ3‚O·ð»@ÞŠ‚áŸ\¸LS‡/Ä¥UN:-Ðoñ{nãƒAãâhÐ6jZDÀËÅ=ÓÛ7~þ.øn2ÃKz‰–ž÷Ü!w»î÷FøìpÜ“`ÞüðÆ‹ûÇã/#æŽMmŠ~å1šµ.¾ðâò4,¼}‘àǽÎ È©UÖá)fî÷`Ï•Ø(Ì$WEÉJïâ7!:Í7ï _²™Œè¥ll—ïÉ»äÿ¼mj§÷iÛbòêÊ'4?ÇÝv«V¨È®èeÉX×}l°N¦64žc,eQB"V\zB±Ûþ;t‹1ôdoEÐýò#¨OøáÇkt#VŠÃÒ/õRj¯Iª]#IªÖ¶Éc÷tŸzõz¶³W>ÏK„螊_ë%1a–§å3¨JgºmÒM}¯ñv›ŸFO2« wm9"Í߯ÓôÂöÎAçôÓ$±¤[/Ûyi±»Ö TÞ[޵ƒJïí_ž¼=ûïpûÍh©2Ž×:ž|g‡ûK cG#2G*Fex0>÷ŽÀ$ ÔàõN¼S1a/Éþ†×ß@ aˆ©p/“ï|cÇ70c°6qqáìl1qkꤞßkæ}ö›­´üî‰&7µªÌܳ“ÇìAçh<;ŽøÏ?¶“ø"Zü@þJ@&Þ+?ù,ªÎjÇέ}oþOõ°ú(ÀŸjpq×$Ç„†Ha ½è4Ñ4@Œ\ý>,'й»=Pó§ÿĨê°T’¸Üm*BtÀ#z°Þýô}Cõ¡þ)±ÝmÏôA„2mN‰GN¥íw<åV#¹±°ó狀0ŸÕŸw]ËAb„µ:IU¯oID¦¦¿ñ©)>;~éwA¬Io µ.DÈ@T±Â¸ÑGÿÇ̹zòá†,$:Ðâ,.Tϵݔæ¿lb°kX 5ç{oNÌÐ-p‰ðXb¹^eµžØ,±í›UeÑ:ŒF¾ø–ªPxmÁŽº(Æ8Þ|,í›~Q£ æ°ÏËÂW Û³'êÔ­¢9—“öJJ§vñÒã'µK™¹œüžÈšØb?N í É»ý‡aȾé}áeÌUð“Å@¹ÈP_Àã(fàø¬Cøø1ÕÝú÷}©«5>1'h{÷↋ÉxøŒ±³9<ÃF"Œ @B;%ŽCùÁµRO2%ùR³#_ðŒ7§2f_`­ÕUS{¶LCæÖOÎû1´Ü•j ÆÞbþŽÀn“ËDïÄ7=,Þïáí<ƒ XÂiÊAåGâ t•›6ãÞã9sž¥Ç¾“êðòonEàžôÞ›4˜ø´3ãa'¬‰k LÙ‡- ù©÷H²3 w˜?w¿¡–°JÞÔà€pÓ“[7N†4O-׊¸Fíè㈬,³! ] ëîð1#¯ƒ•©IqÛ§àŽÄ*¼&†,À=†‚>~ Û¶ZŒ¡²[r§XFî}ŽËCF¯ží÷±Ëj×é9;„¶S®vfÞæã¸ï_Eìc¦(Ÿ^!ÌÙñï{ÉûzfQ´ÝÒt™Bi[môöDïÝ´î\d÷»—ué>%=,nNúñ<ÔØËMh%¦‰åÿCÏè• ÆršÇ9£§v¢ÝÒÏ4Ÿ¯í\5¬4Z”eëèt0ŽÅ¨Âðn6 8bö¦oÓþ´_‚TÒ–û3 ÃÖ÷ 3Ê2àÆ„ 今 {åò}ª´ê!sŽüPCðn• •Åøÿ“õW°vò"Ñ…ß ºüß;‡ÂÙú ˜š¿ßC1ói]ú½¦ì;`0Ì7Ϫ ³ìA¬œ€oê\#ý7|÷¯¹®$˜ Y§*yŒ£»ÜmöT{óf’ˆ Â^<˜È§¥·ë'«^ Óê™ÌÈØ¿ÐA. ûëpéG‰ÀÞí¦á‹tÅ秪—þ£IŒŸr¼þÖ„¦2…zq OuÂÖíY@þÓÅFàõøÚh²:T—Nr „Ø@‘üwçï üC@l ¦;]7þ7Ò'ï~„NÖý̽œm›çèÔlX¥=IÃÀó²ˆáïñ¶L²øŒ)Œ…UvoÀ•î'å j¯Oäý|¾ sýÿ»RákE3þÞ‚œe€E„€F×<mþÇÀØ °6ÌÌ2´O’&fV<0WmÌhjƒúØ£+,¸äK‘¬¢¢'ô5ƒùj¨vïiÑÚ£†ÆíçHºœIÃv/“Ò«P©‘%ľÅÀ-‰®"%Íg²¼”ÃÈçó½xÝÀAêä×Ûùˆ¼ÑãûÀ8„m£«š¡/ŸÅ=™®ˆtsÿðìh2Pe ­eýªÚ^tæWõ€»L²>ÿØÒÐàý”Ç  ôÚÌj™üú©ûÙðßöË™—Jú ¸Ts‚­r7ìÃÍo2¿¢’PÍbõ>g…!%~RßÊk1Ï4Njð[\g $¹2 ê>ýÇÂ…»ùw‘lIÁÒ;}Z͈ÿŸ’+w î$yø…{<ØéJôƒ†“ñpÞbÜ>V5w‘ÿ“¡;9 †ç`‚ªÒ‚þñ ×ûzRXs´ÌŒ®õÈKÚ1 ñY²#‹s{Ü>“ÄxGÜœIü}Õ¯y£Ôµ§j =Ey*Ÿjí½UzZ„‰zE¥±Â=A¹ö¼^‘®²à‡6—»Rà¯ð”¹Þ“H½Æ–…ʉì]é$I<ƒµáq Ûïc“®Ú_“žØ»¤ö‹¿„i«H„â\9Ò¿„'Vk¿Ù†Ÿ›¿bó°öîúºgDSH±¿B¯Jƒf<'õã÷Ÿ&CtÙxúLဠyA…â îæ[ÇhgŒ”Š5"!©ÓD^ø ¼ËÎçÕSЗ•è'¶7Ž:…§à–vÐË;í'}F^~Û½ ™qàxjRªÔ'A–Vk?wC½;?±1¦†Ãv±Ã‚óCôÉ#íëÂ|ê ¨Ç¯ö¨£ï{ ÐÎÄ‘BjI=4÷^ß›ÃJ׋h4›Œ~ÇFˆˆ4›Oý2§ÞO%'­%–÷AÑ?UîMÒ%+ˆ}W%‘ÃKŸúnÖ—êIR:7ÜFpÛÁÁîIÐRzÒM‚›º¯(•île£‹÷OVÚÂO>ˆþOõL1A¦ú l_¯ú1×àÛœéæX®Ów&eK.$@ôvöp/µQ¶ú™k1¦îžAñNµÙÕ ýÓÕ0äµ/ýöV“–7¸}&¹iÍL!#¶Ÿëxg€ýUcüÿÂÏø}B,ÿ¯ÒºøuAvAŒº ò/ÇNæ¹Íï­±·`ˆeøEÿ€å¥‰@vq3û‰·èºÇpý©}§ú¶Ï`'éÖŸ¤‹Ö¯| mÔ´ZEœº@írÉ“ú|jƦœt‡ã,¨öç, j— ›:IRbõÖ„ÅÊ®…õãZJ3„_âc·¯º7ÓöcKÀÊÿî›ú‘oG4ZÒé:#@s‚?åµÇ†WCïH"8Öêẑ¯o‘¦ðÿößø ï»¶Oƒfƒ¿Éû*ä.%”½Ä /P5ö5eüt½UÙËV°ê– €¿Hj£xB³ÅtZ:ö9øô¯b¬jU®á¢ ¡¿ðîi"à 8Ë›2ó¨eûVM‡ù?µHaf°@wQPÞx$Ÿo ÇMæw‡f&9ñ°1˜¿Ê(oWßîðù±jðMùúV(ldl~™& Wv5¨_â~K©[¶‹ m­qºž¬ÐÚtB%€8€Á¸=ÅJ˜<lÂ5Q›Ço’¢Øu­§ÆÄÄ0, žúãQƒ“ë5j¹RDááøVƒÒ9ó¾¾ BË+ À(6ÔcT›¢ú`˜ï(2 ¾)¯ß»m7s™%êôP£˜¿A‡ÑÅõUÉß÷Ç'†3K+‡;Zo/W:VîØ†œC6ÑWŽþ¡Š³ÚòFßc± ÆÂ> 6š‚V=ª±²¯>úMú¦ýÂ)Ë7=b\wåõuÉ´´ÛÔ®ä^ø±þMT‡òÔÀ£‚?7A¼Fô¬n"qGa[d0pl‚SP3.XË17–Ë[ ™ÁXL!Ö¢ª F°jÈz§KY¼Y°÷dQÈV©Ç=(ƒtŠW‰òúk&²eĨ*Sfôó\o'6U–šFDaDù5pš?Ad$þá”@–ŽôÇ_WïÝËÇ5™hîá䮢åË\ ¬Ð±» ~1 t‹Žþé— n|gwglG¢»GÄzTø‹‘UÖ†×^GñÖåLØÏK€®—]¡Ô¼\E¤kìUßáÈïòü#„XF ¶C^ük%q’}ý›º[Edÿ¶Â6²ÅE40N‘Ä`U¯"V0¯ÂVV1ÞÒx`{¼?#.Q<¢3Vn?ZNŠÕÓnÝM1׌¶‚l¿Ÿ• ëR¦šc6Àlj×n¹çCk®¸8 gyRƒüUr†ÜP4¦ éð®u¥=Î0´èkAÌpk0kSRØ¡ûJ ¶ÇÁÿÊæ;¿@Šƒv1¡£±Þ‘pÆŸøôÏKÐN­Û ¾ü# @Øõ눀íŒV†ußl,º—q´uãìoBuálrº¶/,éá‚ó…ök{ÑQ¼ÿh»foM.Õ?R5ÇHËeûu–ŸBéÈLŠg…ËÞ²í:eãtï¡›¿†”ò<.´;rÝ6éØ œ= ûÚÀü`ظìp?„ÿõ@ån ÐoérT´MÇ.d1ㆩÆ^2fŽë•kÁÓÿƧµèÌô)/_Üë§È BÜ颼Ÿö˜G·ëÿ€ø>ù}L]9˜ÿ 6†«·ðR†ž ݾA°h¢äØ×P‰¢FãRào¦D(ü½Äi òšs¨­xÈ´¸›=*?ƒj¢»ÿ¹Æ7€Ãˆl?èžGTØßNBà }k[0J2}?_Çh¥ù‘÷Œìê[Xeeaÿ´ðó`ÌHƶ̭3¯8’èwõGæƒ=õE±Àð¢aüeöQd 1kH wG-z”Q‰ŸÉí+J4«ÂmnN´r·ÎZ/‰ÁÏÎ _ú¦ÏŒè£þ ¬éŽaËhý±…±r„,HÿøÔÔ$O‚; V\„Øÿ kÉ2!¯¿‡‡Õ×ü?¾;)|ê(ñ±ºéyLp•eÇ$Ùé‚ÕïLaau/¶'ñÿþãfš­äZèŸVãã)ççÓ¨ªä glI å?\Ëk^¿ R6.TÀþùCع-qð<ûovÞwÿô5§¬è³¹ªYþnçÒilsCïGè êïÞ<ì7Ñ¿•ÿ¿Ó;ž÷ìÞ“Æ3à>3@fþc€mʬɒ°¶÷ ®6Åô ÅÂÚ!ºjðûä‘,júÉÓê[± Áè~øÅb9XOŠœ/ò~Ý´5§ACHf`C¾ï¿žï+bµ¥¾T„5]ü¯ÔjxÙn¬4v1²VƒˆíÒ K‰kNÚÔÚä)ñ„©rn5X›ç`«Dh÷ÁXpøÖOÄé^º¥vP%é¹mÇ ]–LW÷t5]H-Fü ªð@L`r2KLÊ-³V‹/®<Ž«áöȉXVÞñ¾È@ýÐìÆ7£VdFc°€:à‡JçâpƒOœ%øXá²êñ®¾ã?ç•l~ÎSA=WqÄp`——g‚°ŸA{:bVV¥ÃÚAr~)™DG”hKeö¢‰`FBU5ÅÔÆ®2÷‡ó}LPá@„º2ÙeÓ› ,SÝõíÅÿ¢ê¼Þ ‚¥²Q¬Æ×ª¶ÖŒÝajÞ!îä£ÜÉ$WðY»ÝÝÞ+E›¨ë¿t¹q¾;I‘œ£|ð³˜þ!;¿rÓIJÍËwÏ ©éB 6~ œ¸o=,k„ÕÜv+ ‘¯Í³>®ý]¶ËšÍ¶‘3@:oÖÆ¶Ë>^Ár©ACˆïaN]d­qŽy[ØæójN*«âTŬˆÑÃ}/@¢Z°oD(÷©ÐßU™ §¸ËÒò]Šsö *º~¦êÄëž—¡³:–æøÌHQá›Àxa ˆwmkmºÇUS±co:Š;ACî—à…²‘Pù”¬v† š–!(}1­ âõÄãp¬ñ`’t\†:^XÙÃzæï„~u/J7JHi®}‰È»ÌEp m‰¯M¢Ê‘ °CVŸ¸KAÇïÚ™›\'š[®pÀê*må ö·c|¾tŠÔ‘¹Š¤ÌŸÚt#c'– !Hê³b*F‹”øîQbˆÊÄ(6N¨d}„"js /¯w‰X>‹è¶/zO~ùý‰°òHx/˜ËHtçzj}ÿö÷ý ö2ÑÅ8kô>´g7@1îî;ï¾Èÿ¨ÒàÉ'ô›ñÁ}RŒ!/&ñ¡óf;À*ÿ=K¹±ï7òþ-oÕÇ #BmZÌ,¿CÖ/€J£Ð¥¯«ÇEtÕ`©ôÓ%8ƒ°Ã©»@ ÀŸC ?§l@þ¼ýDÇZ¸Fƒkžtfô¶ÒÎð™¦1­m¶ 4ÔÛS 5&´òvYÑÐlb¼-Ve_ka]ƒ·ßn~u¬° zø&¦éæÔ6Î./=L ý#5¢Õ ZW×usë˸/%@vãü›Ö7X>° Ò$lh¨‚ÿWbÊg{-FxÊ™ô/ô)ö"\*Ék»ž€€S-æ·sGž3Y}Ç~|ú~\òéJûá˜G4Ø¢þ 8ÝJ:¸<¶Ó#}ûÜ07GršRŸ¬D^t/žhxGýï°‹:Ÿê:{¶ÇMÕ•€£¤ã#ëa‡ëpÿ‹úT݉9î Ò‡{’åëcÎ6ÍíUŽØ´š¹ Ʋ6_vf4~ãwA ]˜”è'ßCñ·¬l¥ƒ*Ò­ª\~;S]·Þ¯:w"ÁÙ’cÄò§ýxצeÓëPE>_ÙŠD2Lé~á‡vªÀÝÒVê[¤A?ë€ãÔzn–»9ZË5Þ¼f“Û Ÿ)¬f60geÒz4¢ëz7לN^;ìjÓ< g`B<-ô#aé ´$As˜§@!ÞÅ*…4dÔŸr$ eôͤ_¨¸9 dŸ¡ÛD»,·Ñ1,õÞ­°Û[TŠø†ïËeRÏ…Cê9ñZ©º¥ëV N޼ñh 63ÞE?B`fåJ”áRT©¨ ½'‚!ÀÀó¡ŠQDPöETd­Åæ4c_'Ï‚˜!bݤAtê´Ç¨[ÊYèH6Pn9R@ÙrxZ„Ø"IO¾Øl¡ð$1¥ºƒ½~0§¹ïwè=Á·Fº¯1¾‡A+ûo™Dî/ÿF!VÙ 8×ùjJçÝî‘cÅ@?V/ sŸ ·PPW‡¬oÉCÄÃáÇ­šp¢Ü™„œªì_•ÙÙ9)ÞDHñ‹€óÄK$‚ˆ†Ø²àŒ¡¨ìÄÏÔÝ=XৈM¹'G±Ÿ]Rö2x²8£#dÏŽ©lGýô ï¤ÓO- p7®mî]¢l‘S’¸¬V+ø¬Æ…nîüãoGÏ»ËËŽÛJžîR7îõZ¹ñT ¢ë†Hße Á4{Û„>ãíÔÐ5Yú»™Þ[ðŽÑòØ3[W»½­ˆYhìOS¶¤¨L†jOõz­xS>%û^’æn]¸Åþ¯ºÅÚ>`¿ÙPì ½ œ¼¨)£yîû”Qq-`]¸¦0ÚÅ£²Ä‰œƒm¿±Pxfæ.Uæþ•UXöìl›<^—»¢×C)[ëW$nëm:r¤cèBg'ç€M1ÓÃÝìÊà |!* šPÀãÆ9OÆ= Í›ç ×bç_¹…šº=¶±qø8· `q ‘ØÊddTi¥ЬN½MؤŠV§½ê‹uÂëÇmP‹rÌBßCv„Ö|e“ThlLIBzn†ÆÊ±+—z []jD$*cCE -蚩ꨥèyý‚jcg=ë¹´[}§­ô¸((å'wt&6.ðÀOÅ÷žf‡]úD„èTøê‡ª+ÁùÆ|÷J¬ZK>?ºˆâ)ß{­iköˆ¹?»±P•žœa=Ø2ÐTé "Ó7¡?ÅÂ7)ý¼éútL$®üldþO]Óã0Šõ†(+ ð“ùýú¶'þQÀh.Wþï0›‡5Ù€Eøå›O½Ú×êÆ¤@ÎÏükvÿÝ“ýÙ|¡ûk¢_ñÆÊ}ªƒøÐüÒF­e#¿tBŽ„xDnU´èl;PA¨ËüÔ@2MAÞ?Ç^ÆFH–AÝþ*¨JL{E›¼ÈÔ p&ü~ÿÉ÷Dj YÆ à¢¢Õ5·+í§A=¨œéK ìj €jÜvþsm  ª’:ÅØÔŠ?øu<.õ¾^}é§fÍú0ÙÖëå¾OۤƘ¹Ìpß[Æ¢‚GŸéÓ-jðïÂS^Åi ¹½=ý²[ÕÊ ‘j¦Dy•x1×þƒ¨J.hñ}¦à8áÆ™fx§F|^Þ´ä8‡QÿÆÂ ›³_f§¶ÜŸÆe‡††=bÍñ€uªW?ý–ÍÁÆHlþ¼i[ݘ¨q뼟¹S%rèú˜u¸è|5‡[§öÑ×4?v¡ÆX1ã'$'¡¿£Œ¤QøÕFb¶ßödåžÜž7Ø{a±äPÆÿáKGžÍ%вÕ#„mð;¼#÷ý­3ƒ ·-Ć ÷wª4o>×AOt¬':ÝCÃÛüªÇåý´Ö×lß(ÅåH2DZö˜·Á­>‹]G]M„ÆÈžj“¦ñƒúê«õ¦ha„`Iúã#cþû«{ *“€qfÂVüÀÜÒ5+û#etkxãBÓy¼ÅB¦íu¶éó¢ê©Lœxú¢«·,„ eA猧’Õ )V•nä…E~¹ÐÏ'ÊfòÄsø­kóSˆ¢“Ói±±lÏìñï¯'Š †…Q G¼²Ș«<*ƒq¯„ oj½}MKR¡ˆÃ4$#©[Lcüôà ú²éûã;ùD&(7ˆáp½*ÝŒŒ œ…[^Šm⹦ûpØl&Ä$·¼Ñ¶¸ ß Eü!œ€fØ<§öBYþ›'@¼.9 Pušî4â2úÏÅù.lº–bµÁhEõ\ `EUß!¸„@W؆x%©ï}ž6½yG§åâ³ü+RouÞ8ÉþÈxʬ&|ˆ}¿· L{]QííŽNÊîE½7¢?â)×cmì¨ÍßZr“E#b•RÍ’æ›þQæ¯SFî{î óãÙL[·Œ2åâO«ð\ Ô¤)}ñmu!”ª,„R½>ÓpC>†å£ ¤›p/[bÔ2¼z7‰d:„7¿ˆd#œ‚<ú@š·è'4©ô:œ3âè|¸Ä6¼ö)?ÈÞgæóz]\JOu{ y!é"¢ÁýïBÐÝéX òþŸyŒŸÓxʳdPyaÚøŸ¡wôV‘õøùj®†3„xõ†i½™ñ°&š\$Î ,àÓº±sô5' 0 ›_&†j±ÛgbϯÊq5ã"˜—ûY:cóÐIL+ DõBUs)¡ÕÐ"¡P˽ñT#'FÞeäö“ðSBcL7a¤:Øh@úˆ› `Öázþ®®¨¥cüO†)ï‡'±G'Õ¾YºpâOÖ ³.7ª+ ü_ ì¦…vOÓÚ|»OkiÓåÇÜ¶Ë û½÷¨û¡!zyÃ]Û!DNl rÂå^Ö© •st"¦t´uxO )˜2¨~¼w,]ÇÆD {#N4 n¦·dviònÌtóåŸúZnUXt—t±ÐØÝRÉýÛö„2ÓÛÖO"¶ë¶e#2?a8D­°&ú^=úeŠWæk¥‡^Ø#3V˜Ë µäÓPGšðÃÃËꤎÌPLHÅs;õÓlY]£ý†[yÀkUºa»Ä£àì/è[b‰¤7g&^F@þôÞ!Áj>Œ¶ð“Z_]d}U‚õM€¾F@ë¤wî-7;zÅÏZô¸\¥fŠó¨MYz¯>´5) ¬â=‡ˆ6 JIy†¹´ŽU*9š³ Óz‘`Z3ò{I¾1€XTçSçþ^m?ÃwÿnÏ‚o_ƺ@)ª¨ÿiM(wpO³p_ ?yäüzÖšÙÊ +A«ß÷ÇÿPÎæ\…ÕYF‹4×÷¶2pV:*•hS÷áúÍhÔys1•Õ{ýv7™{¨XØ-WQOª¡0oQûa¶ƒÕU¿n=Ègp÷žðG'ÖŠ˜ % Þ[Ô0Zð› ìr¸a¸ëZ£æÚ¶ 3L«ìã¹@€â½Yì¾5–žöûÔ…›=MÝn|]”{Â%Û_ðÿâ*÷t2ë§òÁ#[×–†bWl;Zø>™—ÔÅ·¼à‡õÊ»Ce ch€„&Y”&\¤Ñ'ó_½ÐÉÿÐÔÀ¥-þûz/gâßJ¦ueaмg ‡ÔaõÿÜd\|Ô÷öãesGÖßå³ð‘ƒãH í§ ¢Äê_ô¦6¹Þ¯/â>-ÄHs™Ëûa÷™åY–ýÈk¦p‘n · –Ëà ½¾ÜÅÌhTa1ÑhªÐ¤Š‘vV¥­RÔz1¤;•múCa~@qoL˜¨A[™àŽ0OޏøãzOžÜá6àQÄ誕 n«2dobm6yu2íÓeßÝOÛíÄ›xn«ËKka!ƒT´gD_¹ö(Øs=sÜE+€í4ñZ™ kõt:ʶЩ§údµ@9kx§†w·Üc5ëKuZt.n1v5©•µ,7k+Æ¿´Vƒö ˆ^üö@únfú/Žˆ繇_qªÙNýäaô|Tá µÎpˆq–.®µô–àqCÔûÑ%СþR åÁ³ÀM{Wíû¿8F3¯‚¨Ãù•`€1 åõØ)Dš¡ˆr‡µÈ‹XëÙ Z/;Éïê7ð€ïÔâW]êÑENäi Ë ³zÂs ¡ù>õ$Ix¸r [í6Ó|;RÆREvš}‘|$L"½¬ÐÖHWÄ.õ“ªPc’ÿÐSŒqyQŠ)ÑÖ³û gOÇÞWä­N ® ´Cš>ŠŽa¼Mаâü»+Óv¦í×¶ vÞÖ}Åà*×mGî% éíššÂù}h&#m‘ç…™+†k(p¸\šKšwÝ+p^E~8œ8)<q83úoÿù6ýÿ¤xÅÅ…‹ˆ6@‡öþa…éf1c­À,@‰°€…_‡°üä*·}p(å&˜X;˜Câ@ÁÁæð4Â쟴®ÎÌrëé[έ Ì|÷UÛfO0‰ ; yznÏwQ<àêÏfÂ|nmÞõó~_Û[?J+Þ_¨è‡ÕÇ^q¿:-䪖÷í‚?³§_¢ºtžO~š!jõI‚d 3(¢@8}ª¬_Éù4©þ»é\(z0'¡é.?ÖàñöæŠ \%l#O¤ëe T"XÜ¢ ÓNºtMd\X È»é6­t"k/— ]&î1µ$‹Ÿ¡GÐÉœ0”!ìg`©mߘåi’wÌÉ™¦ŸÑ-¶4Ñ´òÎe;hŸ]¨I¾æ“¢øE9™ï=…^Ù5ƒW’ɶ•h‡¯+ÿ)ŒP8Š¢lQb _Å’®ʸb8þ» Ó’Éý£‹§˜hÏQ³gÿqM·Ø‡†žîZÜ-äm™‘U·¦–3Âíí3Jt×õ}×$Dó&u™$rsÔöê<,©—'Ùc†°W“ñ˜Øýè~³ÒOîäé!Aj×p ¶«Ä×F‹¬:#ô@¢Ò 0v·„ÿî°½Ä|ù›Ož=«R°K%±†N·E+Ûúî1mFåþÙÈ5gŒýÔÿˆàÉ÷g nÝSðšn9xØIÞ}˜$pŠ( Ó BÔáb° þ¶®Üžþ¹fš‹ޏ>ú 4Èväy#¾1ïåû›U`ö´´°±osÙß”¢|žî© ÓÓïû'+:ІË&CHmI,ÚâŠÔ|cf÷­!’ð5> £X¦@ý¼Qó~µ¢XÛàšÂG<(¸% 5À& ѽ0ÜVL†PÑ1Ù­ƒMIξ6äJ#ÔºÆ~RÑpZc°m$ÚbäNÄÇ _b¥AƶE ÝÚ²¹óü)ÌX¦çÇ ñDr À<çL})3¢Q1“[þkIPÕÙÒµCrhääzÅÁÍ ·h‚?vîµSOô|ï8Xm}ä÷uÆ®99.½-8]ü©†ß¡’µé8³``qÌVÀ±,T¿eug–nZ=‚x!hfÆr›w„¢¸0c9%³…(Å÷Gwü–Ñ(©?þƒ«"OºŽ {½ß¥-ž/W½Ph½m”ëî“éÏ×o´e¤¯y‡ÑtŠù7×lFš¥ñÑeé¥UMgð)Ã>&~ò1vMÿãciàåzį6üf v\jôÉ&·ïûzùX¾“±ó!»¶jÇ *#[pŸ×”Z[g„šhïî`οëQdÃŽšÔTÏNø°[|ŸÕc^»{TØË¬ @}™5<Üé\¡š*O5× ¸Ù“¿ÐD}$‰§ö ôÆ®$Fž,iö ÃSq¥ÜŽo%Ç1¯°%Øë.Z®¨k§¼Œiy[eáýõœ&§2#å Ï㺴åCý‚ëÌJª—ÉDl?õ͹-V½ÄFÞï&z®lö}ý<ö Êú÷· ÇÎ%A“ìa_œ'Á7ƒñú C´< ]nþ¶Ù₉Œø!<°·©]ДP’ÆwÊ÷)ÍMëqR("v(¨\€´·^\J²|¡ãYù>(*°’2y{ÌK>tvâ“n–W›%¼Ü_ˆnhàuŠ!ÂAê×J¯“Â@…É:Þ‘pW´¼ÔXã’V¨‡ ZiUíK¥ã¾ÌKÞ“3¸ËOwwœy®;/j”Æç1t¼]ìrÑÇ„9³i[·Éi¦«‘wF°æ¢ÆÀ«ê»ËDxî[ióæ‚wºÄ:Kd´¬õÐB¬BAÀ‹ 0ê}”h™²-)»¸DØ•£ú¾h4zÞ£Ó„%ÖÁÅwÒc‘ êýIœÀ·“ëºÌ…ùH1÷½×lT4¦R£n*+ô3?¡Á$tHïA!½Ô×T¦œÖ?hæcX>[*¹Щ¡¥0ð{è þíØ¡IkÔ=.¨Ó´0#g•B¹ÄëÆ6$1›´ 8P*#¨naP‘àŸá±±Ù"dqÁƒŠ½õ ¡ø‡Pœ›U1KØ[bX!ÙD£r~ÀÕxÊ8`'T¹0ôŸâ˜Ÿ›œ+J?sVÕÏE àÁ'b–#3H9A™ŠðƒÆÎè‰Q@³‰˜Np½žÐ ®^2lc*ÎR°úØ‹?ïò,@¨rËX)æÄ€˜Öì ~·x@“p`;nÞPìª\fR޳Ð-A ´;g²Àª©é…¨DA»‡×†Š †é\<‰£ûktÓ8`:´¹W*!*èÊè5ð]$¦|ìi‰ Ö¨‚´"ÛXN›Ó<6·}2GZý/Ýk¨Kqøýï“Ý|"­ €) m°Ø†@Ô L´R%bà—A—ò ¾GÀIQQ£ÂûŸ{Ò¶;¢ì9å˹BíµN„¬@¶‚3„ƒ]d¥õ&Úaª»W7NØÁªôÊuíŠ0 •íÜ?D½ÔC°ƒ?ü;ƒã× à‰à`:S ö‚3‡¬ç˜H¯€ÞN­§¹úÜkŽ›=kTIÔt޵㢊 UWësZŸâd2è¿Ói߯Ǭ͟!–ãxÈ=v˜b^a¯ÈÍË…NW–³u9Û1hqƒý0Œ&÷Ëxa|åB‹C5¬2oõÿÑÑw[ˆV¦ ɶ gÔ÷EôÎY@r~šÐØBåk}¾»Ui²ÃNËMp3hvŠè{‘ç–ïßtk0` Eû§_¢Å¾O¿Ã,‹òUŽ~~(ej.ÿñ¨"ĄŠN ;­ H=Ós¸3 &’ÌTT {ýøØÌû¥½³3]:0mÆ«ç£àûÛ8Õž ¿>'«¶/ýjaîÒKýæÀýg‘ +‚P™ åéH‡¿<-ÔÀ×½ðµ4¸Käõv¨(˜Zó'ëc³€}µý¦…¾­•Gi4`Ÿk?¦4Ä5&*A£ƒ–ÕõæCW>Ñl>î¬B9yL‘TÇ`Át __µCÓ¸ËWs$Ú3.™éîÅ' ðFŽuÂ$±Á¢“úT‹-»"^qpK€i¡×ªÏŒ×‡Áð/Ù Æà]ªhlôasW˜¢Â/ˆFpš;z„‰*¨zÏë«V„iG Í=÷Áýé…é“í>n7âf¸Ûôa•°0uOYÔY>Õèk~ QFÈ­Ý÷4Cq e|u›Ä4LJõ׊>VoSüC’äbç“ê¯&p<ЯºÃFEyþ› Õføp`†œŠ-P¬¸Eù=¦»—…@¼ðS •báb?§pûÊÃŒgd‹& 8yˆ ýW†'ßý K™K©øW@‰ÒÒñÈ(–.¯µwgȇßìØ¬‡]V¨ªhñ¯x¾¡O[f®zfðà˜Kwôǹ±ü\O Ã_½¸@˸$À¾Ï†Nbpå&ò-z%ñëÓ ”Ç™9¬Ä¦@öh³oŽ( |:/”Ù\X³((È@ÇR íRâ‘N5K´$‹¯õ´_¦Œ^.¸‚çï»»À+†¨âlÁŒf¢±Jâ¾ßÛTÁ\V‚õ-V8ˆ%ƒ¾ ªF‡küÒ;J•-æ>´Rþ‹hñ÷ݦ²1á&¢¼Ôó Âu[¾•= MVò²®N´7'ŒgÆBq½–õZç×q]תÐó ,/¥5/jªÁ¸æâc^+´ñæâÒîè ³ZÓa¨¨6 óÐl©ÜU-]q9ÿÙD|¹ÈJJŸ›-,#” m>T®Aæ—ZB¾_Hv5½²™iwFçoÁ_-Òa_•+ÃIa ƒ$¬„,¶TfÔ¥Ši'í¤“íÊ+ñü¾ù²–PžŒ¿°Ôñ^%èÕ äÿìS`J’PM*EÝè˜wx-ÀÙ¹ÙB¨‚íÞ2Ãk ì¹»‡™èoĨ”3EÑ„®+8¾%݉ ×©<ǯôªUáéîЅဵ=‡Ž‰u«ßé^:ðÊ~›ÝÞ3GÐ*R[Ž‹v‹W¦‡A„\¶§Ü¿ (ù\4]„d púd? ’ÀHÐålm ,®kìb™1|,¼°0N-[J<²€j‰=1Ãw¢´\øwy(`ì¹Ålow{ËTÄS”ŠQ€ÕÈû]_—É÷Þ!ÈÅqPDnOà§dƒ/ÜâF‡y6ï?y?m{÷íäúݱÛÝmÑC–äúTœJ*: Ð-ˆêr@9ÊíÒaƒ ³Ã)h:¯ò•¥z+èt v–6ä o%ÖˆC..ÀKúÓ½wëÃ]CýÞ7BzT£ÿ!*Ê€ ûOñ@«+)᥺=<?Fã¯ CQ pÓ¦ïß'ÿAìØ¡“6ædHú›ø¥™‡b²øh¾1HhîpÚŸ.®E÷ý޵»–±îŒ¾Kà#U3`®÷ÚìÇjjtA5`ZΖ9_F‰Ñu¸t­È‡NÏm-úÕ݃Մš“úvà +@[ì`E'Q¿Õ„ήC‡él¯“é¿Á3À}ª1±›©V6 ¯ÀBÿ£fê•Pâ¿û,ð&œÔ¥¡¡i c‹?»Ü!C€êÇýÆÆ²Z&HÞŠc 7Z¼Lžè,<]Š{½<ª ÆìNþõ0]Ï $Ã1(z“×S1è/ }°d4¸F­Æ{Æ•²;äRð·kÝpÿ/Ì›>B#R̸譗÷¤2 âc$æàÊÕ=#ƒ<»¢ÑF þ—·±!:˜dL‡úa¡£¡ªÓè¡YÏ„qÓž°ÚZW“û¤†ªt`cbä«<áu´4ÃÄgâƒÇ€õdÑÉhQš.T^)’ëú{bYʇëÀ*Ê3Ÿo(Öµ+J§—ã£_×Ö«OA7!’T7ó§‹ó5ÙŠˆ`ù,Q‹2qê¿“ä×3ç¡wvË­½“5†A¢Ÿ‚M®õ‡¯to“Ù&I0¡­¦eVb ÌWŽ×£ÕÊr‡®$ŠEÂ[]›çPöäòñ%1.OAÕÂÏ 'ÀHþ¢ØŸX2åÈËû8éën¹=µª2‹’œöŽÈoJ%*hh?'R´$R*zö54ª 'ްÊ[Þƒ"i]›‚àõÈgF±6w<“&1.Ó¯½—è£, ]»tþ°¨E÷?©™R=z:»¢{^ÐyG€ÍR.f3uŒ àÅ~UôóB… ]ËDLÀ…³bƒ«.Õþ¼ºCqʦϾtâÛFvøThßúÓ^u¹G¤D^ÈtªÄÐY^sô¢þosaþãYwƒ<(êl–`0©{ñ˜Æ~eU¶ ‚3‚ ¶ KÞªÌyËìï ÍT_h `¶À ä¸Y’ñ)„2ÅÿéØTáPI“îâ¼ …=¦›ƒË©3äÔ4ËÁA²x^ç‘]™ä<ý¬ƒîÔsˆKHîUÜ1%„Ø -â¡Ì|D:ÔWŽžÊú(Æ|é<‚J-)„AŠöΨݦ¤„¼ƒ$Iñ¡X„$À– ›,F-…ÙOã="q®Y‡ÜoàJ‡M{2åø†y‡Œ³‚fÂE€™…‹+1[oùžF¤`**ª=X.¨[D²a;ÉÁ4õÞÑÒ K½ÅXcÙºÚŒ Žú½ŠEmŠ¿“üh“ Dù[Òë£kÖ•J?šD*Óäÿ€˜j½M“!jëàãÉ t^'þqÕW©2Èwì~³ÝßãA¼¸ÞÁCŽÓo®PQÉÂÕßCjÓ^­P±ÇB·»Šï6'9ÌL¦-_›Ô²6jU´™³ `ÐUííý4×÷4±]ô¸…w¾ô‹8'jVî²TS|Á”ƒ|¸’Gô»áÓD%œ‚ð£û>z…aS¾›4’_¥j÷#F~_bR šIOnú^ãrfŽ&qËÛ­Ô–×i¯­0¦ÓC¦^ƒ"7"uE-ÃÄ"ßtdHôÞ2ÐÕm÷7Þ„ãCá®ÓËÆ1Ú|?ÏðvC Ù=#œ3ã™hz áD§ 3N‡ÓÁþÓ:Høv.*ðéõ¬˜èn R·‡‘4Pð³ JÆ‚>ÕP–YúÈ„‹ê V ô6–ž(ì)„èþOý¡ÜûʼÁrh쥄.E Q1ñÈ®*…b{Ú|Éžˆê|s 1-½5F $ȾÅ裂]µb¥BeìL6G^€îš)¦7he–õi„îX•½”´“éütÁ²º ѸÈ‘’¢-ô~